Advertisement
izk666

script.js

Apr 16th, 2016
218
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. (function(OC, window, $, undefined) {
  2.     'use strict';
  3.  
  4.     $(document).ready(function() {
  5.  
  6.         $(document.body).click(function(e){
  7.             var $command_box = $('#commands_popup');
  8.             if (!$command_box.has(e.target).length) { // if the click was not within $command_box
  9.                 //$command_box.hide();
  10.                 $command_box.slideUp(100);
  11.                 $('.activerow').removeClass('activerow');
  12.             }
  13.            
  14.         });
  15.  
  16.         // this passwords object holds all our passwords
  17.         var Passwords = function(baseUrl) {
  18.             this._baseUrl = baseUrl;
  19.             this._passwords = [];
  20.             this._activePassword = undefined;
  21.         };
  22.  
  23.         Passwords.prototype = {
  24.             load: function(id) {
  25.                 var self = this;
  26.                 this._passwords.forEach(function(password) {
  27.                     if (password.id === id) {
  28.                         password.active = true;
  29.                         self._activePassword = password;
  30.                     } else {
  31.                         password.active = false;
  32.                     }
  33.                 });
  34.             },
  35.             getActive: function() {
  36.                 return this._activePassword;
  37.             },
  38.             removeActive: function() {
  39.                 var index;
  40.                 var deferred = $.Deferred();
  41.                 var id = this._activePassword.id;
  42.                 this._passwords.forEach(function(password, counter) {
  43.                     if (password.id === id) {
  44.                         index = counter;
  45.                     }
  46.                 });
  47.  
  48.                 if (index !== undefined) {
  49.                     // delete cached active password if necessary
  50.                     if (this._activePassword === this._passwords[index]) {
  51.                         delete this._activePassword;
  52.                     }
  53.  
  54.                     this._passwords.splice(index, 1);
  55.  
  56.                     $.ajax({
  57.                         url: this._baseUrl + '/' + id,
  58.                         method: 'DELETE'
  59.                     }).done(function() {
  60.                         deferred.resolve();
  61.                     }).fail(function() {
  62.                         deferred.reject();
  63.                     });
  64.                 } else {
  65.                     deferred.reject();
  66.                 }
  67.                 return deferred.promise();
  68.             },
  69.             removeByID: function(id) {
  70.                 var index = id;
  71.                 var deferred = $.Deferred();
  72.                
  73.                 if (index !== undefined) {
  74.                     // delete cached active password if necessary
  75.                     if (this._activePassword === this._passwords[index]) {
  76.                         delete this._activePassword;
  77.                     }
  78.  
  79.                     this._passwords.splice(index, 1);
  80.  
  81.                     $.ajax({
  82.                         url: this._baseUrl + '/' + id,
  83.                         method: 'DELETE'
  84.                     }).done(function() {
  85.                         deferred.resolve();
  86.                     }).fail(function() {
  87.                         deferred.reject();
  88.                     });
  89.                 } else {
  90.                     deferred.reject();
  91.                 }
  92.                 return deferred.promise();
  93.             },
  94.             create: function(password) {
  95.  
  96.                 var deferred = $.Deferred();
  97.  
  98.                 $.ajax({
  99.                     url: this._baseUrl,
  100.                     method: 'POST',
  101.                     contentType: 'application/json',
  102.                     data: JSON.stringify(password)
  103.                 }).done(function(password) {
  104.                     deferred.resolve();
  105.                 }).fail(function() {
  106.                     deferred.reject();
  107.                 });
  108.                 return deferred.promise();
  109.             },
  110.             getAll: function() {
  111.                 return this._passwords;
  112.             },
  113.             loadAll: function() {
  114.                 var deferred = $.Deferred();
  115.                 var self = this;
  116.                 $.get(this._baseUrl).done(function(passwords) {
  117.                     self._activePassword = undefined;
  118.                     self._passwords = passwords;
  119.                     deferred.resolve();
  120.                 }).fail(function() {
  121.                     deferred.reject();
  122.                 });
  123.                 return deferred.promise();
  124.             },
  125.             updateActive: function(index, loginname, website, address, pass, notes, sharewith, category, deleted, changedDate) {
  126.                
  127.                 var sharewithArr = [];
  128.                 if ($.isArray(sharewith)) {
  129.                     sharewithArr = sharewith;
  130.                 } else if (!sharewith) {
  131.                     sharewithArr = "";
  132.                 } else {
  133.                     sharewithArr = sharewith.split(', ');
  134.                 }
  135.                
  136.                 if (changedDate == undefined) {
  137.                     // this needs to stay here for users who are updating from <= v16.2; creation dates are used as source
  138.                     var d = new Date();
  139.                     // date as YYYY-MM-DD
  140.                     var changedDate = d.getFullYear()
  141.                         + '-' + ('0' + (d.getMonth() + 1)).slice(-2)
  142.                         + '-' + ('0' + d.getDate()).slice(-2);
  143.                 }
  144.  
  145.                 if (!pass) {
  146.                     pass = ' ';
  147.                 }
  148.                 var password = {
  149.                     'id' : index,
  150.                     'website': website,
  151.                     'pass': pass,
  152.                     'loginname': loginname,
  153.                     'address': address,
  154.                     'category': category,
  155.                     'notes': notes,
  156.                     'sharewith' : sharewithArr,
  157.                     'deleted': deleted,
  158.                     'datechanged' : changedDate
  159.                 };
  160.  
  161.                 return $.ajax({
  162.                     url: this._baseUrl + '/' + password.id,
  163.                     method: 'PUT',
  164.                     contentType: 'application/json',
  165.                     data: JSON.stringify(password)
  166.                 });
  167.             }
  168.         };
  169.  
  170.         // this categories object holds all our categories
  171.         var Categories = function(baseUrl) {
  172.             this._baseUrl = baseUrl;
  173.             this._categories = [];
  174.         };
  175.  
  176.         Categories.prototype = {
  177.             load: function(id) {
  178.                 var self = this;
  179.                 this._categories.forEach(function(category) {
  180.                     if (category.id === id) {
  181.                         category.active = true;
  182.                         self._activecategory = category;
  183.                     } else {
  184.                         category.active = false;
  185.                     }
  186.                 });
  187.             },
  188.             removeByID: function(id) {
  189.                 var index = id;
  190.                 var deferred = $.Deferred();
  191.                
  192.                 if (index !== undefined) {
  193.  
  194.                     this._categories.splice(index, 1);
  195.  
  196.                     $.ajax({
  197.                         url: this._baseUrl + '/' + id,
  198.                         method: 'DELETE'
  199.                     }).done(function() {
  200.                         deferred.resolve();
  201.                     }).fail(function() {
  202.                         deferred.reject();
  203.                     });
  204.                 } else {
  205.                     deferred.reject();
  206.                 }
  207.                 return deferred.promise();
  208.             },
  209.             create: function(category) {
  210.                 var deferred = $.Deferred();
  211.  
  212.                 $.ajax({
  213.                     url: this._baseUrl,
  214.                     method: 'POST',
  215.                     contentType: 'application/json',
  216.                     data: JSON.stringify(category)
  217.                 }).done(function(category) {
  218.                     deferred.resolve();
  219.                 }).fail(function() {
  220.                     deferred.reject();
  221.                 });
  222.                 return deferred.promise();
  223.             },
  224.             getAll: function() {
  225.                 return this._categories;
  226.             },
  227.             loadAll: function() {
  228.                 var deferred = $.Deferred();
  229.                 var self = this;
  230.                 $.get(this._baseUrl).done(function(categories) {
  231.                     self._categories = categories;
  232.                     deferred.resolve();
  233.                 }).fail(function() {
  234.                     deferred.reject();
  235.                 });
  236.                 return deferred.promise();
  237.             }
  238.         };
  239.  
  240.         // this holds our settings
  241.         var Settings = function(baseUrl) {
  242.             this._baseUrl = baseUrl;
  243.             this._settings = [];
  244.         };
  245.  
  246.         Settings.prototype = {
  247.             load: function() {
  248.                 var deferred = $.Deferred();
  249.                 var self = this;
  250.                 $.ajax({
  251.                     url: this._baseUrl,
  252.                     method: 'GET',
  253.                     async: false
  254.                 }).done(function( settings ) {
  255.                     self._settings = settings;
  256.                 }).fail(function() {
  257.                     deferred.reject();
  258.                 });
  259.                 return deferred.promise();
  260.             },
  261.             getKey: function(key) {
  262.                 for (var k in this._settings)
  263.                 {
  264.                     if (k == key)
  265.                         return this._settings[k];
  266.                 }
  267.             },
  268.             getAll: function() {
  269.                 return this._settings;
  270.             }
  271.         };
  272.  
  273.         // this will be the view that is used to update the html
  274.         var View = function(passwords) {
  275.             this._passwords = passwords;
  276.         };
  277.  
  278.         View.prototype = {
  279.             renderContent: function() {
  280.                 var source_passwords = $('#template-passwords-old').html();
  281.                 var template_passwords = Handlebars.compile(source_passwords);
  282.                 var html_passwords = template_passwords({
  283.                     passwords: this._passwords.getAll()
  284.                 });
  285.                 $('#PasswordsTableTestOld').html(html_passwords);
  286.  
  287.                 // check for legacy versions, where loginname and notes (and so forth) weren't included in the db properties column yet
  288.                 // prior to v17
  289.                 var table = document.getElementById('PasswordsTableTestOld');
  290.                 if (table) {
  291.                     for (var i = 0; i < table.rows.length; i++) {
  292.                         resetTimer(true);
  293.                         // test for login names (= [1]), should not exist since they're serialized in properties column
  294.                         // but if they do, website (= [0]) must be filled too, gives error on >= v18 otherwise
  295.                         if (table.rows[i].cells[0].textContent != '' && table.rows[i].cells[1].textContent != '') {
  296.                             var updateReq = true;
  297.                         }
  298.                     }
  299.                     if (updateReq) {
  300.                         $('#update_start_btn').click(function() {
  301.                             updateStart(passwords);
  302.                         });
  303.                         updateRequired();
  304.                         return false;
  305.                     } else {
  306.                         $('#PasswordsTableTestOld').hide();
  307.                         // following doesn't work on IE7
  308.                         $('#PasswordsTableTestOld').remove();
  309.                     }
  310.                 }
  311.  
  312.                 // building new table
  313.                 source_passwords = $('#template-passwords-serialize').html();
  314.                 template_passwords = Handlebars.compile(source_passwords);
  315.                 var html_passwords_serialize = template_passwords({
  316.                     passwords: this._passwords.getAll()
  317.                 });
  318.  
  319.                 // decode HTML: convert (&quot;) to (") and (&amp;) to (&) and so forth
  320.                 html_passwords_serialize = $('<textarea/>').html(html_passwords_serialize).text();
  321.                 var rows = html_passwords_serialize.split('<br>');
  322.                
  323.                 formatTable(false, rows);
  324.  
  325.                 $('tr').click(function(event) {
  326.                     $('tr').removeClass('activerow');
  327.                 });
  328.  
  329.                 $('.btn_commands_open').click(function(event) {
  330.                     event.stopPropagation(); // or else this will hide the box
  331.                     $('tr').removeClass('activerow');
  332.  
  333.                     if ($('#commands_popup').css('display') == 'block') {
  334.                         $('#commands_popup').slideUp(150);
  335.                         return false;
  336.                     }
  337.  
  338.                     var $row = $(this).closest('tr');
  339.                     var $cell = $(this).closest('td');
  340.                     $row.addClass('activerow');
  341.                     $('#commands_popup').hide();
  342.  
  343.                     // set values
  344.                     $('#cmd_id').val($row.attr('attr_id'));
  345.                     $('#cmd_type').val($cell.attr('type'));
  346.                     $('#cmd_value').val($row.attr('attr_' + $cell.attr('type')));
  347.                     $('#cmd_website').val($row.attr('attr_website'));
  348.                     $('#cmd_address').val($row.attr('attr_address'));
  349.                     $('#cmd_loginname').val($row.attr('attr_loginname'));
  350.                     $('#cmd_pass').val($row.attr('attr_pass'));
  351.                     $('#cmd_notes').val($row.attr('attr_notes'));
  352.                     $('#cmd_sharedwith').val($row.attr('attr_sharedwith'));
  353.                     $('#cmd_category').val($row.attr('attr_category'));
  354.                     $('#cmd_deleted').val($row.hasClass('is_deleted'));
  355.                     if ($row.hasClass('is_sharedby')) {
  356.                         $('#btn_edit').hide();
  357.                         $('#commands_popup input').css('width', '170px');
  358.                     } else {
  359.                         $('#btn_edit').show();
  360.                         $('#commands_popup input').css('width', '80px');
  361.                     }
  362.  
  363.                     if ($('#app-navigation').css('position') == 'absolute') {
  364.                         var left = $(this).position().left;
  365.                     } else {
  366.                         var left = $(this).position().left + $('#app-navigation').width();
  367.                     }
  368.                     left = left + $('.btn_commands_open').width() - ($('#commands_popup').width() / 2);
  369.                     var top = $(this).position().top + $('#header').height() + 25;
  370.                     $('#commands_popup').css('left', left + 'px');
  371.                     $('#commands_popup').css('top', top + 'px');
  372.                     $('#commands_popup').slideDown(150);
  373.                 });
  374.  
  375.                 // colour picker from the great https://github.com/bgrins/spectrum
  376.                 $("#colorpicker").spectrum({
  377.                     color: '#eeeeee',
  378.                     showInput: true,
  379.                     showButtons: false,
  380.                     showInitial: false,
  381.                     allowEmpty: false,
  382.                     showAlpha: false,
  383.                     showPalette: true,
  384.                     showPaletteOnly: false,
  385.                     togglePaletteOnly: false,
  386.                     showSelectionPalette: false,
  387.                     clickoutFiresChange: true,
  388.                     hideAfterPaletteSelect: true,
  389.                     containerClassName: 'category_colorpicker',
  390.                     replacerClassName: 'category_colorpicker',
  391.                     preferredFormat: 'hex',
  392.                     palette: [
  393.                         ["#000000","#444444","#666666","#999999","#cccccc","#eeeeee","#f3f3f3","#ffffff"],
  394.                         ["#ff0000","#ff9900","#ffff00","#00ff00","#00ffff","#0000ff","#9900ff","#ff00ff"],
  395.                         ["#f4cccc","#fce5cd","#fff2cc","#d9ead3","#d0e0e3","#cfe2f3","#d9d2e9","#ead1dc"],
  396.                         ["#ea9999","#f9cb9c","#ffe599","#b6d7a8","#a2c4c9","#9fc5e8","#b4a7d6","#d5a6bd"],
  397.                         ["#e06666","#f6b26b","#ffd966","#93c47d","#76a5af","#6fa8dc","#8e7cc3","#c27ba0"],
  398.                         ["#cc0000","#e69138","#f1c232","#6aa84f","#45818e","#3d85c6","#674ea7","#a64d79"],
  399.                         ["#990000","#b45f06","#bf9000","#38761d","#134f5c","#0b5394","#351c75","#741b47"],
  400.                         ["#660000","#783f04","#7f6000","#274e13","#0c343d","#073763","#20124d","#4c1130"]
  401.                     ],
  402.                     move: function(color) {
  403.                         $('#cat_colour').val(color.toHexString());
  404.                     },
  405.                     change: function(color) {
  406.                         $('#cat_colour').val(color.toHexString());
  407.                     },
  408.                     hide: function(color) {
  409.                         $('#cat_colour').val(color.toHexString());
  410.                     }
  411.                 });
  412.  
  413.                 // use ZeroClipboard to copy the password
  414.                 $('#app-settings').attr("ZeroClipboard", isFlashEnabled());
  415.  
  416.                 if ($('#app-settings').attr("ZeroClipboard") == 'true') {
  417.                     var client = new ZeroClipboard($('#btn_copy'));
  418.                     client.on( 'ready', function(event) {
  419.                         client.on( 'copy', function(event) {
  420.                             event.clipboardData.setData('text/plain', $('#cmd_value').val());
  421.                         });
  422.                         client.on( 'aftercopy', function(event) {
  423.                             //alert('Copied text to clipboard: ' + event.data['text/plain']);
  424.                             $('#zeroclipboard_copied').slideDown(50);
  425.                             setTimeout(function() {
  426.                                 $('#zeroclipboard_copied').slideUp(100);
  427.                             }, 1500);
  428.                         });
  429.                     });
  430.                     client.on('error', function(event) {
  431.                         //alert('Clipboard error: "' + event.name + '" - ' + event.message);
  432.                         $('#app-settings').attr("ZeroClipboard", false);
  433.                         ZeroClipboard.destroy();
  434.                     });
  435.                 } else {
  436.                     $('#btn_copy').click(function() {
  437.                         var typeTitle = '';
  438.                         switch ($('#cmd_type').val()) {
  439.                             case 'website':
  440.                                 typeTitle = t('passwords', 'Website or company');
  441.                                 break;
  442.                             case 'loginname':
  443.                                 typeTitle = t('passwords', 'Login name');
  444.                                 break;
  445.                             case 'pass':
  446.                                 typeTitle = t('passwords', 'Password');
  447.                                 break;
  448.                         }
  449.                         $('#commands_popup').slideUp(100);
  450.                         $('.activerow').removeClass('activerow');
  451.                         window.prompt(typeTitle + ':', $('#cmd_value').val());
  452.                     });
  453.                 }
  454.  
  455.                 $('#btn_invalid_sharekey').click(function() {
  456.                     OCdialogs.alert(t('passwords', 'You do not have a valid share key, to decrypt this password. Ask the user that shared this password with you, to reshare it.'), t('passwords', 'Invalid share key'), function() { return false; }, true);
  457.                 });
  458.                
  459.                 $('#btn_edit').click(function() {
  460.                     var typeVar = '';
  461.                     var typeTitle = '';
  462.                     switch ($('#cmd_type').val()) {
  463.                         case 'website':
  464.                             typeVar = 'website';
  465.                             typeTitle = t('passwords', 'Website or company');
  466.                             break;
  467.                         case 'loginname':
  468.                             typeVar = 'loginname';
  469.                             typeTitle = t('passwords', 'Login name');
  470.                             break;
  471.                         case 'pass':
  472.                             typeVar = 'password';
  473.                             typeTitle = t('passwords', 'Password');
  474.                             break;
  475.                     }
  476.                     popUp(typeTitle, $('#cmd_value').val(), typeVar, $('#cmd_address').val(), $('#cmd_website').val(), $('#cmd_loginname').val());
  477.                     $('#accept').click(function() {
  478.                         var newvalue = $('#new_value_popup').val();
  479.  
  480.                         if (typeVar == 'website') {
  481.                             if ($('#new_address_popup').val() != ''
  482.                                 && $('#new_address_popup').val().substring(0,7).toLowerCase() != 'http://'
  483.                                 && $('#new_address_popup').val().substring(0,8).toLowerCase() != 'https://'
  484.                                 && $('#new_address_popup').val().substring(0,4).toLowerCase() != 'www.')
  485.                             {
  486.                                 if (isUrl($('#new_address_popup').val())) {
  487.                                     // valid URL, so add http
  488.                                     $('#new_address_popup').val('http://' + $('#new_address_popup').val());
  489.                                     // now check if valid
  490.                                     if (!isUrl($('#new_address_popup').val())) {
  491.                                         $('#popupInvalid').show();
  492.                                         $('#new_address_popup').select();
  493.                                         return false;
  494.                                     }
  495.                                 } else {
  496.                                     $('#popupInvalid').show();
  497.                                     $('#new_address_popup').select();
  498.                                     return false;
  499.                                 }
  500.                             }
  501.  
  502.                             var newaddress = $('#new_address_popup').val();
  503.                         }
  504.  
  505.                         var passwords = new Passwords(OC.generateUrl('/apps/passwords/passwords'));
  506.  
  507.                         if ($('#keep_old_popup').prop('checked') == true) {
  508.                             // save row to trash bin first
  509.                             var d = new Date();
  510.                             // date as YYYY-MM-DD
  511.                             var changedDate = d.getFullYear()
  512.                                 + '-' + ('0' + (d.getMonth() + 1)).slice(-2)
  513.                                 + '-' + ('0' + d.getDate()).slice(-2);
  514.                             var pass_old = $('#cmd_pass').val();
  515.                             var password = {
  516.                                 'website': $('#cmd_website').val(),
  517.                                 'pass': pass_old,
  518.                                 'loginname': $('#cmd_loginname').val(),
  519.                                 'address': $('#cmd_address').val(),
  520.                                 'category': $('#cmd_category').val(),
  521.                                 'notes': $('#cmd_notes').val(),
  522.                                 'deleted': '1'
  523.                             };
  524.                             passwords.create(password).done(function() {
  525.                                 var passwords2 = new Passwords(OC.generateUrl('/apps/passwords/passwords'));
  526.                                 var view = new View(passwords2);
  527.                                 passwords2.loadAll().done(function() {
  528.                                     view.renderContent();
  529.                                 });
  530.                             }).fail(function() {
  531.                                 OCdialogs.alert(t('passwords', 'Error: Could not create password.'), t('passwords', 'Save'), function() { return false; }, true);
  532.                                 return false;
  533.                             });
  534.                         }
  535.  
  536.                         // overwrite proper field(s) with new value(s)
  537.                         switch (typeVar) {
  538.                             case 'website':
  539.                                 $('#cmd_website').val(newvalue);
  540.                                 $('#cmd_address').val(newaddress);
  541.                                 break;
  542.                             case 'loginname':
  543.                                 $('#cmd_loginname').val(newvalue);
  544.                                 break;
  545.                             case 'password':
  546.                                 $('#cmd_pass').val(newvalue);
  547.                                 break;
  548.                         }
  549.  
  550.                         var success = passwords.updateActive($('#cmd_id').val(), $('#cmd_loginname').val(), $('#cmd_website').val(), $('#cmd_address').val(), $('#cmd_pass').val(), $('#cmd_notes').val(), $('#cmd_sharedwith').val(), $('#cmd_category').val(), $('#cmd_deleted').val());
  551.                         if (success) {
  552.                             var passwords = new Passwords(OC.generateUrl('/apps/passwords/passwords'));
  553.                             var view = new View(passwords);
  554.                             passwords.loadAll().done(function() {
  555.                                 view.renderContent();
  556.                             })
  557.  
  558.                             // building new table
  559.                             var source_passwords = $('#template-passwords-serialize').html();
  560.                             var template_passwords = Handlebars.compile(source_passwords);
  561.                             var html_passwords_serialize = template_passwords({
  562.                                 passwords: passwords.getAll()
  563.                             });
  564.                             html_passwords_serialize = html_passwords_serialize.replace(/&quot;/g, '"');
  565.                             var rows = html_passwords_serialize.split('<br>');
  566.                             formatTable(false, rows);
  567.                         } else {
  568.                             OCdialogs.alert(t('passwords', 'Error: Could not update password.'), t('passwords', 'Save'), function() { return false; }, true);
  569.                         }
  570.                         removePopup();
  571.                     });
  572.                     return false;
  573.                 });
  574.  
  575.                 $('#app-settings-content').hide();
  576.  
  577.                 // reset timer on hover (for touch screens)
  578.                 $('#idleTimer').mouseenter(function(event) {
  579.                     resetTimer(true);
  580.                 });
  581.                 $('#countSec').mouseenter(function(event) {
  582.                     resetTimer(true);
  583.                 });
  584.  
  585.                 $('#back_to_passwords').click(function() {
  586.                     $('#section_table').show(200);
  587.                     $('#section_categories').hide(100);
  588.                 });
  589.  
  590.                 $("#CategoriesTableContent").on("click", "td", function() {
  591.  
  592.                     var $cell = $(this);
  593.                     var $row = $cell.closest('tr');
  594.                     var rows = $row.closest('table').find('tr').length;
  595.                     var cat_id = $row.find('.catTable_id').text();
  596.                     var cat_name = $row.find('.catTable_name').text();
  597.                     var is_trash = $cell.hasClass('icon-delete');
  598.  
  599.                     if (is_trash) {
  600.                         OCdialogs.confirm(t('passwords', 'This will delete the category') + " '" + cat_name + "'. " + t('passwords', 'Are you sure?'), t('passwords', 'Category'), function(res) {
  601.                             if (res) {
  602.                                 var categories = new Passwords(OC.generateUrl('/apps/passwords/categories'));
  603.                                 categories.removeByID(cat_id).done(function() {
  604.                                     setTimeout(function() {
  605.                                         categories = new Categories(OC.generateUrl('/apps/passwords/categories'));
  606.                                         categories.loadAll().done(function() {
  607.                                             renderCategories(categories);
  608.                                         }).fail(function() {
  609.                                             OCdialogs.alert(t('passwords', 'Error: Could not load categories.'), t('passwords', 'Passwords'), function() { return false; }, true);
  610.                                         });
  611.                                     }, 500);
  612.                                 }).fail(function() {
  613.                                     OCdialogs.alert(t('passwords', 'Error: Could not delete category.'), t('passwords', 'Category'), function() { return false; }, true);
  614.                                 });
  615.                             }
  616.                         });
  617.                     }
  618.                 });
  619.  
  620.                 $('#cat_add').click(function() {
  621.                     if ($('#cat_name').val().length == 0) {
  622.                         return false;
  623.                     }
  624.                     var categories = new Categories(OC.generateUrl('/apps/passwords/categories'));
  625.                     var cat_name = $('#cat_name').val();
  626.                     var cat_colour = $('#cat_colour').val();
  627.                     cat_colour = cat_colour.replace('#', '');
  628.                     var category = {
  629.                         categoryName: cat_name,
  630.                         categoryColour: cat_colour
  631.                     };
  632.                     var success = categories.create(category);
  633.  
  634.                     if (success) {
  635.                         $('#cat_name').val('');
  636.                         $('#cat_colour').val('#eeeeee');
  637.                         $('#colorpicker').spectrum('set', 'eeeeee');
  638.                         setTimeout(function() {
  639.                             categories = new Categories(OC.generateUrl('/apps/passwords/categories'));
  640.                             categories.loadAll().done(function() {
  641.                                 renderCategories(categories);
  642.                             }).fail(function() {
  643.                                 OCdialogs.alert(t('passwords', 'Error: Could not load categories.'), t('passwords', 'Passwords'), function() { return false; }, true);
  644.                             });
  645.                         }, 500);
  646.                     } else {
  647.                         OCdialogs.alert(t('passwords', 'Error: Could not create category.'), t('passwords', 'Categories'), function() { return false; }, true);
  648.                     }
  649.                 });
  650.  
  651.                 $('#sidebarClose').click(function() {
  652.                     $('#sidebarRow').val('');
  653.                     $('#app-content-wrapper').attr('class', '');
  654.                     $('#app-sidebar-wrapper').hide(100);
  655.                 });
  656.  
  657.                 $('#delete_trashbin').click(function(event) {
  658.  
  659.                     OCdialogs.confirm(t('passwords', 'This will permanently delete all passwords in this trash bin.') + ' ' + t('passwords', 'Are you sure?'), t('passwords', 'Trash bin'),
  660.                         function(confirmed) {
  661.                             if (confirmed)  {
  662.  
  663.                                 $('#PasswordsTableContent tr').each(function() {
  664.                                     var $row = $(this);
  665.                                     var is_deleted = $row.hasClass('is_deleted');
  666.                                     var id = $row.attr('attr_id');
  667.                                     var passwords = new Passwords(OC.generateUrl('/apps/passwords/passwords'));
  668.                                     if (is_deleted) {
  669.                                         passwords.removeByID(id).done(function() {
  670.                                             $row.remove();
  671.                                             formatTable(true); // reset counters and show 'trash is empty'
  672.                                         }).fail(function() {
  673.                                         });
  674.                                     }
  675.                                 });
  676.  
  677.                                 setTimeout(function() {
  678.                                     alert(t('passwords', 'Deletion of all trashed passwords done.'));
  679.                                 }, 3000);
  680.                             }
  681.                     }, true);
  682.                 });
  683.  
  684.                 $('#PasswordsTableContent td').click(function(event) {
  685.                     var $cell = $(this);
  686.                     var $row = $cell.closest('tr');
  687.                     var is_strength = $cell.hasClass('cell_strength');
  688.                     var is_date = $cell.hasClass('cell_datechanged');
  689.                     var is_notes = $cell.hasClass('icon-notes');
  690.                     var is_category = $cell.hasClass('cell_category');
  691.                     var is_info = $cell.hasClass('icon-info');
  692.                     var is_share = $cell.hasClass('icon-share');
  693.                     var is_sharedby = $cell.hasClass('icon-shared');;
  694.                     var is_sharedto = $cell.hasClass('icon-public');
  695.                     var is_trash = $cell.hasClass('icon-delete');
  696.                     var is_restore = $cell.hasClass('icon-history');
  697.                     var active_table = $('#app-settings').attr("active-table");
  698.                     var $cmd_buttons = $cell.find('.btn_commands_open');
  699.  
  700.                     if ($cell.html().substr(0, 6) == '******') {
  701.                         $cmd_buttons.click();
  702.                         return false;
  703.                     }
  704.  
  705.                     var passwords = new Passwords(OC.generateUrl('/apps/passwords/passwords'));
  706.  
  707.                     // popUp function works with parameters:
  708.                     // popUp(title, value, type, address_value, website, username);
  709.  
  710.                     if (is_category) {
  711.                         popUp(t('passwords', 'Category'), $row.attr('attr_category'), 'category', '', $row.attr('attr_website'), $row.attr('attr_loginname'));
  712.                         $('#accept').click(function() {
  713.                             $row.attr('attr_category', $('#new_value_popup select').val());
  714.                             var success = passwords.updateActive($row.attr('attr_id'), $row.attr('attr_loginname'), $row.attr('attr_website'), $row.attr('attr_address'), $row.attr('attr_pass'), $row.attr('attr_notes'), $row.attr('attr_sharedwith'), $row.attr('attr_category'), $row.hasClass('is_deleted'));
  715.                             if (success) {
  716.                                 var view = new View(passwords);
  717.                                 passwords.loadAll().done(function() {
  718.                                     view.renderContent();
  719.                                 })
  720.                                 // building new table
  721.                                 var source_passwords = $('#template-passwords-serialize').html();
  722.                                 var template_passwords = Handlebars.compile(source_passwords);
  723.                                 var html_passwords_serialize = template_passwords({
  724.                                     passwords: passwords.getAll()
  725.                                 });
  726.                                 html_passwords_serialize = html_passwords_serialize.replace(/&quot;/g, '"');
  727.                                 var rows = html_passwords_serialize.split('<br>');
  728.                                 formatTable(false, rows);
  729.                             } else {
  730.                                 OCdialogs.alert(t('passwords', 'Error: Could not update password.'), t('passwords', 'Save'), function() { return false; }, true);
  731.                             }
  732.                             removePopup();
  733.                         });
  734.                         return false;
  735.                     }
  736.  
  737.                     if (is_share || is_sharedto) {
  738.                         popUp(t('passwords', 'Share'), $row.attr('attr_sharedwith'), 'share', '', $row.attr('attr_website'), $row.attr('attr_loginname'));
  739.                         $('#accept').click(function() {
  740.                             var sharearray = [];
  741.                             $("#new_value_popup input:checked").each(function() {
  742.                                 sharearray.push($(this).val());
  743.                             });
  744.  
  745.                             var success = passwords.updateActive($row.attr('attr_id'), $row.attr('attr_loginname'), $row.attr('attr_website'), $row.attr('attr_address'), $row.attr('attr_pass'), $row.attr('attr_notes'), sharearray, $row.attr('attr_category'), $row.hasClass('is_deleted'));
  746.                             if (success) {
  747.                                 if (sharearray.length == 0) {
  748.                                     $row.attr('attr_sharedwith', '');
  749.                                     $cell.removeClass('icon-public');
  750.                                     $cell.addClass('icon-share');
  751.                                     $cell.find('div').removeClass('is_sharedto');
  752.                                     $cell.find('div span').text(0);
  753.                                 } else {
  754.                                     $row.attr('attr_sharedwith', sharearray.join(','));
  755.                                     $cell.removeClass('icon-share');
  756.                                     $cell.addClass('icon-public');
  757.                                     $cell.find('div').addClass('is_sharedto');
  758.                                     $cell.find('div span').text(sharearray.length);
  759.                                 }
  760.                             } else {
  761.                                 OCdialogs.alert(t('passwords', 'Error: Could not update password.'), t('passwords', 'Save'), function() { return false; }, true);
  762.                             }
  763.                             removePopup();
  764.                         });
  765.                         return false;
  766.                     }
  767.  
  768.                     if (is_sharedby) {
  769.                         OCdialogs.info(t('passwords', 'Shared by %s').replace('%s', uid2displayname($row.attr('sharedby'))) + '.', t('passwords', 'Share'), function() { return false; }, true);
  770.                         return false;
  771.                     }
  772.  
  773.                     if (is_notes) {
  774.                         popUp(t('passwords', 'Notes'), $row.attr('attr_notes'), 'notes', '', $row.attr('attr_website'), $row.attr('attr_loginname'), $row.hasClass('is_sharedby'));
  775.                         $('#accept').click(function() {
  776.                             $row.attr('attr_notes', $('#new_value_popup').val());
  777.                             var success = passwords.updateActive($row.attr('attr_id'), $row.attr('attr_loginname'), $row.attr('attr_website'), $row.attr('attr_address'), $row.attr('attr_pass'), $row.attr('attr_notes'), $row.attr('attr_sharedwith'), $row.attr('attr_category'), $row.hasClass('is_deleted'));
  778.                             if (success) {
  779.                                 if ($row.attr('attr_notes').length == 0) {
  780.                                     $cell.removeClass('has-note');
  781.                                 } else {
  782.                                     $cell.addClass('has-note');
  783.                                 }
  784.                             } else {
  785.                                 OCdialogs.alert(t('passwords', 'Error: Could not update password.'), t('passwords', 'Save'), function() { return false; }, true);
  786.                             }
  787.                             removePopup();
  788.                         });
  789.                         return false;
  790.                     }
  791.  
  792.                     if (is_info || is_strength || is_date) {
  793.                         if ($('#sidebarRow').val() == $row.attr('attr_id')) {
  794.                             $('#sidebarRow').val('');
  795.                             $('#app-content-wrapper').attr('class', '');
  796.                             $('#app-sidebar-wrapper').hide(100);
  797.                         } else {
  798.                             showSidebar($row);
  799.                         }
  800.                         return false;
  801.                     }
  802.  
  803.                     if (is_trash) {
  804.                         if (active_table == 'active') {
  805.                             // no sharedwith, so a share will be stopped when the owner deletes the password
  806.                             var success = passwords.updateActive($row.attr('attr_id'), $row.attr('attr_loginname'), $row.attr('attr_website'), $row.attr('attr_address'), $row.attr('attr_pass'), $row.attr('attr_notes'), '', $row.attr('attr_category'), "1");
  807.                             if (success) {
  808.                                 var wasShared = $row.attr('attr_sharedwith') != null && $row.attr('attr_sharedwith') != '';
  809.                                 $row.addClass('is_deleted');
  810.                                 $row.hide();
  811.                                 $row.find('.icon-info').removeClass('icon-info').addClass('icon-history');
  812.                                 // remove shared state
  813.                                 $row.attr('attr_sharedwith', '');
  814.                                 $row.find('.icon-public').removeClass('icon-public').addClass('icon-share');
  815.                                 $row.find('is_sharedto span').text(0);
  816.                                 $row.find('.is_sharedto').removeClass('is_sharedto');
  817.  
  818.                                 formatTable(true);
  819.                                 if (wasShared) {
  820.                                     OCdialogs.info(t('passwords', 'The password was moved to the trash bin.') + ' ' + t('passwords', 'This password is no longer shared anymore.'), t('passwords', 'Trash bin'), function() { return false; }, true);
  821.                                 } else {
  822.                                     OCdialogs.info(t('passwords', 'The password was moved to the trash bin.'), t('passwords', 'Trash bin'), function() { return false; }, true);
  823.                                 }
  824.                             } else {
  825.                                 OCdialogs.alert(t('passwords', 'Error: Could not update password.'), t('passwords', 'Trash bin'), function() { return false; }, true);
  826.                             }
  827.                            
  828.                         // from trash, remove from database
  829.                         } else {
  830.                             OCdialogs.confirm(t('passwords', 'This will delete the password for') + " '" + $row.attr('attr_website') + "' " + t('passwords', "with user name") + " '" + $row.attr('attr_loginname') + "'. " + t('passwords', 'Are you sure?'), t('passwords', 'Trash bin'), function(res) {
  831.                                 if (res) {
  832.                                     var view = new View(passwords);
  833.                                     passwords.removeByID($row.attr('attr_id')).done(function() {
  834.                                         // now removed from db, so delete from DOM
  835.                                         $row.remove();
  836.                                         formatTable(true);
  837.                                     }).fail(function() {
  838.                                         OCdialogs.alert(t('passwords', 'Error: Could not delete password.'), t('passwords', 'Trash bin'), function() { return false; }, true);
  839.                                     });
  840.                                 }
  841.                             });
  842.                         }
  843.                     }
  844.  
  845.                     if (is_restore) {
  846.                         var success = passwords.updateActive($row.attr('attr_id'), $row.attr('attr_loginname'), $row.attr('attr_website'), $row.attr('attr_address'), $row.attr('attr_pass'), $row.attr('attr_notes'), '', $row.attr('attr_category'), "0");
  847.                         if (success) {
  848.                             $cell.removeClass('icon-history').addClass('icon-info');
  849.                             $row.removeClass('is_deleted');
  850.                             $row.hide();
  851.                             formatTable(true);
  852.                         } else {
  853.                             OCdialogs.alert(t('passwords', 'Error: Could not update password.'), t('passwords', 'Trash bin'), function() { return false; }, true);
  854.                         }
  855.                     }
  856.                 });                
  857.             },
  858.             renderNavigation: function() {
  859.  
  860.                 // set settings
  861.                 var settings = new Settings(OC.generateUrl('/apps/passwords/settings'));
  862.                 settings.load();
  863.                 if ((settings.getKey('backup_allowed').toLowerCase() == 'true') == false) {
  864.                     // was already hidden with CSS at default for IE7 and lower
  865.                     // Now remove it from DOM (doesn't work on IE7 and lower)
  866.                     $('#app-settings-backup').remove();
  867.                 } else {
  868.                     // So show it
  869.                     $('#app-settings-backup').show();
  870.                 }
  871.                 $('#app-settings').attr('timer', settings.getKey('timer'));
  872.                 $('#app-settings').attr('days-orange', settings.getKey('days_orange'));
  873.                 $('#app-settings').attr('days-red', settings.getKey('days_red'));
  874.                 if ((settings.getKey('icons_allowed').toLowerCase() == 'true')) {
  875.                     $('#app-settings').attr('icons-show', settings.getKey('icons_show').toLowerCase() == 'true');
  876.                     $('#app-settings').attr('icons-service', settings.getKey('icons_service'));
  877.                 } else {
  878.                     $('#app-settings').attr('icons-show', 'false');
  879.                 }
  880.                 $('#app-settings').attr('hide-passwords', settings.getKey('hide_passwords').toLowerCase() == 'true');
  881.                 $('#app-settings').attr('hide-usernames', settings.getKey('hide_usernames').toLowerCase() == 'true');
  882.                 $('#app-settings').attr('hide-attributes', settings.getKey('hide_attributes').toLowerCase() == 'true');
  883.  
  884.                 // set timer if set by user
  885.                 if ($('#app-settings').attr('timer') != '0') {
  886.                     resetTimer(false);
  887.                 }
  888.  
  889.                 // edit categories
  890.                 $('#editCategories').click(function() {
  891.                     $('#app-settings-content').hide(200);
  892.                     $('#sidebarClose').click();
  893.                     $('#section_table').hide(200);
  894.                     $('#section_categories').show(300);
  895.                 });
  896.  
  897.                 // move all to trash
  898.                 $('#trashAll').click(function() {
  899.                     trashAllPasswords(Passwords);
  900.                 });
  901.  
  902.                 // download backup
  903.                 $('#backupDL').click(function() {
  904.                     backupPasswords();
  905.                 });
  906.  
  907.                 // CSV import
  908.                 $('#upload_csv').on('change', function(event) {
  909.                     uploadCSV(event);
  910.                 });
  911.                 $('#importStart').click(function() {
  912.                     checkCSVsettings();
  913.                     importCSV();
  914.                 });
  915.                 $('#importCancel').click(function() {
  916.                     $('#CSVtableDIV').hide();
  917.                     $('#CSVcontent').text('');
  918.                     $('#CSVheadersBtn').removeClass('CSVbuttonOff');
  919.                     $('#CSVquotationmarksBtn').removeClass('CSVbuttonOff');
  920.                     $('#CSVescapeslashBtn').removeClass('CSVbuttonOff');
  921.                     $('#CSVsplit_rnBtn').removeClass('CSVbuttonOff');
  922.                 });
  923.                 $('#CSVheadersBtn').click(function() {
  924.                     $('#CSVheadersBtn').toggleClass('CSVbuttonOff');
  925.                     renderCSV(false);
  926.                 });
  927.                 $('#CSVquotationmarksBtn').click(function() {
  928.                     $('#CSVquotationmarksBtn').toggleClass('CSVbuttonOff');
  929.                     renderCSV(false);
  930.                 });
  931.                 $('#CSVescapeslashBtn').click(function() {
  932.                     $('#CSVescapeslashBtn').toggleClass('CSVbuttonOff');
  933.                     renderCSV(false);
  934.                 });
  935.                 $('#CSVsplit_rnBtn').click(function() {
  936.                     $('#CSVsplit_rnBtn').toggleClass('CSVbuttonOff');
  937.                     renderCSV(false);
  938.                 });
  939.                 $('#selectAll').click(function() {
  940.                     var checkboxes = $('#CSVtable td').find(':checkbox');
  941.                     checkboxes.prop('checked', true);
  942.                 });
  943.                 $('#selectNone').click(function() {
  944.                     var checkboxes = $('#CSVtable td').find(':checkbox');
  945.                     checkboxes.prop('checked', false);
  946.                 });
  947.                
  948.  
  949.                 // clear search field
  950.                 $('#search_clear').click(function() {
  951.                     $('#search_text').val('');
  952.                     $('#search_text').keyup();
  953.                 });
  954.  
  955.                 // search function
  956.                 $('#search_text').keyup(function() {
  957.                     $('#list_active').click(); // first to active list; filter must not work on trashed items
  958.                     var $rows = $('#PasswordsTableContent tr').not('thead tr').not('.is_deleted');
  959.                     var val = $.trim($(this).val()).replace(/ +/g, ' ').toLowerCase();
  960.  
  961.                     // filter
  962.                     $rows.show().filter(function() {
  963.                         var text = $(this).text().replace(/\s+/g, ' ').toLowerCase();
  964.                         return !~text.indexOf(val);
  965.                     }).hide();
  966.                 });
  967.  
  968.                 // click on other list item
  969.                 $('#list_active').click(function() {
  970.                     $('#list_active').addClass('active');
  971.                     $('#list_trash').removeClass('active');
  972.                     $('#app-settings').attr("active-table", 'active');
  973.                     $('#cleartrashbin').hide();
  974.                     $('#PasswordsTableContent tbody tr').show();
  975.                     $('#PasswordsTableContent tbody tr.is_deleted').hide();
  976.                     formatTable(true);
  977.                 });
  978.                 $('#list_trash').click(function() {
  979.                     $('#list_active').removeClass('active');
  980.                     $('#list_trash').addClass('active');
  981.                     $('#app-settings').attr("active-table", 'trashbin');
  982.  
  983.                     $('#PasswordsTableContent tbody tr').hide();
  984.                     $('#PasswordsTableContent tbody tr.is_deleted').show();
  985.  
  986.                     if ($(".menu_passwords_trashbin").text() > 0) {
  987.                         $('#cleartrashbin').show();
  988.                     }
  989.                     formatTable(true);
  990.                 });
  991.  
  992.                 // clean up website: https://www.Google.com -> google.com
  993.                 $('#new_website').focusout(function() {
  994.                     if ((this.value.substring(0,7).toLowerCase() == 'http://'
  995.                             || this.value.substring(0,8).toLowerCase() == 'https://'
  996.                             || this.value.substring(0,4).toLowerCase() == 'www.')
  997.                         && $('#new_address').val() == '') {
  998.                         $('#new_address').val(this.value.toLowerCase());
  999.                         $('#new_website').val(strip_website(this.value).toLowerCase());
  1000.                     }
  1001.                    
  1002.                 });
  1003.                 // try to set a domain entry on website field
  1004.                 $('#new_address').focusout(function() {
  1005.                     if ((this.value.substring(0,7).toLowerCase() == 'http://'
  1006.                             || this.value.substring(0,8).toLowerCase() == 'https://'
  1007.                             || this.value.substring(0,4).toLowerCase() == 'www.')
  1008.                         && $('#new_website').val() == '') {
  1009.                         $('#new_website').val(URLtoDomain(this.value));
  1010.                     }
  1011.                 });
  1012.  
  1013.                 // create a new password
  1014.                 var self = this;
  1015.                 $('#new_password_add').click(function() {
  1016.  
  1017.                     if ($('#new_username').val() == ''
  1018.                         || $('#new_website').val() == ''
  1019.                         || $('#new_password').val() == '')
  1020.                     {
  1021.                         OCdialogs.alert(t('passwords', 'Fill in the website, user name and password.'), t('passwords', 'Add password'), function() { return false; }, true);
  1022.                         return false;
  1023.                     }
  1024.  
  1025.                     if ($('#new_address').val() != ''
  1026.                         && $('#new_address').val().substring(0,7).toLowerCase() != 'http://'
  1027.                         && $('#new_address').val().substring(0,8).toLowerCase() != 'https://'
  1028.                         && $('#new_address').val().substring(0,4).toLowerCase() != 'www.')
  1029.                     {
  1030.                         if (isUrl($('#new_address').val())) {
  1031.                             // valid URL, so add http
  1032.                             $('#new_address').val('http://' + $('#new_address').val());
  1033.                             // now check if valid
  1034.                             if (!isUrl($('#new_address').val())) {
  1035.                                 OCdialogs.alert(t('passwords', 'Fill in a valid URL in the first field.') + ' ' + t('passwords', 'Note: This field is optional and can be left blank.'), t('passwords', 'Add password'), function() { return false; }, true);
  1036.                                 $('#new_address').select();
  1037.                                 return false;
  1038.                             }
  1039.                         } else {
  1040.                             OCdialogs.alert(t('passwords', 'Fill in a valid URL in the first field.') + ' ' + t('passwords', 'Note: This field is optional and can be left blank.'), t('passwords', 'Add password'), function() { return false; }, true);
  1041.                             $('#new_address').select();
  1042.                             return false;
  1043.                         }
  1044.                     }
  1045.  
  1046.                     var d = new Date();
  1047.                     // date as YYYY-MM-DD
  1048.                     var changedDate = d.getFullYear()
  1049.                         + '-' + ('0' + (d.getMonth() + 1)).slice(-2)
  1050.                         + '-' + ('0' + d.getDate()).slice(-2);
  1051.  
  1052.                     var password = {
  1053.                         'website': $('#new_website').val(),
  1054.                         'pass': $('#new_password').val(),
  1055.                         'loginname': $('#new_username').val(),
  1056.                         'address': $('#new_address').val(),
  1057.                         'category': $('#new_category select').val(),
  1058.                         'notes': $('#new_notes').val(),
  1059.                         'deleted': '0'
  1060.                     };
  1061.  
  1062.                     self._passwords.create(password).done(function() {
  1063.  
  1064.                         $('#new_username').val('');
  1065.                         $('#new_website').val('');
  1066.                         $('#new_password').val('');
  1067.                         $('#new_address').val('');
  1068.                         $('#new_notes').val('');
  1069.                         $('#new_category select').val(0);
  1070.                         $('#new_category select').attr('style', '');
  1071.                         $('#generate_strength').text('');
  1072.                         $('#generate_passwordtools').fadeOut(250);
  1073.                         $('#gen_length').val('30');
  1074.  
  1075.                         var passwords = new Passwords(OC.generateUrl('/apps/passwords/passwords'));
  1076.                         var view = new View(passwords);
  1077.                         passwords.loadAll().done(function() {
  1078.                             view.renderContent();
  1079.                         })
  1080.  
  1081.                         // building new table
  1082.                         var source_passwords = $('#template-passwords-serialize').html();
  1083.                         var template_passwords = Handlebars.compile(source_passwords);
  1084.                         var html_passwords_serialize = template_passwords({
  1085.                             passwords: passwords.getAll()
  1086.                         });
  1087.                         html_passwords_serialize = html_passwords_serialize.replace(/&quot;/g, '"');
  1088.                         var rows = html_passwords_serialize.split('<br>');
  1089.                         formatTable(false, rows);
  1090.  
  1091.                     }).fail(function() {
  1092.                         OCdialogs.alert(t('passwords', 'Error: Could not create password.'), t('passwords', 'Add password'), function() { return false; }, true);
  1093.                         return false;
  1094.                     });
  1095.  
  1096.                 });
  1097.                
  1098.                 // ### change category
  1099.                 // for select box in 'new password' part
  1100.                 $('#new_category').change(function() {
  1101.                     if ($('#new_category select').val() == '(change)') {
  1102.                         $('#new_category select').val(0);
  1103.                         $('#new_category select').attr('style', '');
  1104.                         $('#editCategories').click();
  1105.                     } else if ($('#new_category select').val() != 0) {
  1106.                         var bg = $('#new_category').find(':selected').attr('bg');
  1107.                         var fg = $('#new_category').find(':selected').attr('fg')
  1108.                         $('#new_category select').attr('style', 'color: #' + fg + ' !important; background-color: ' + bg + ' !important; font-weight: bold !important;');
  1109.                     } else {
  1110.                         // 'none' chosen
  1111.                         $('#new_category select').attr('style', '');
  1112.                     }
  1113.                 });
  1114.                 // for select box above search bar
  1115.                 $('#nav_category_list').change(function() {
  1116.  
  1117.                     if ($('#nav_category_list select').val() == '(change)') {
  1118.                         $('#nav_category_list select').val(0);
  1119.                         $('#nav_category_list select').attr('style', '');
  1120.                         if ($('#app-settings').attr("active-table") == 'active') {
  1121.                             $('#list_active').click();
  1122.                         } else {
  1123.                             $('#list_trash').click();
  1124.                         }
  1125.                         $('#editCategories').click();
  1126.                     } else if ($('#nav_category_list select').val() != 0) {
  1127.                         $('#list_active').click(); // first to active list; filter must not work on trashed items
  1128.                         var bg = $('#nav_category_list').find(':selected').attr('bg');
  1129.                         var fg = $('#nav_category_list').find(':selected').attr('fg')
  1130.                         $('#nav_category_list select').attr('style', 'color: #' + fg + ' !important; background-color: ' + bg + ' !important;');
  1131.                         // filter
  1132.                         var $rows = $('#PasswordsTableContent tr').not('thead tr').not('.is_deleted');
  1133.                         var val = $('#nav_category_list select option:selected').text();
  1134.                         $rows.show().filter(function() {
  1135.                             var text = $(this).find('.cell_category').text();
  1136.                             return !~text.indexOf(val);
  1137.                         }).hide();
  1138.                     } else {
  1139.                         // 'none' chosen
  1140.                         $('#nav_category_list select').attr('style', '');
  1141.                         if ($('#app-settings').attr("active-table") == 'active') {
  1142.                             $('#list_active').click();
  1143.                         } else {
  1144.                             $('#list_trash').click();
  1145.                         }
  1146.                     }
  1147.                 });
  1148.  
  1149.                 // allow tabs in textareas (notes)
  1150.                 $("textarea").keydown(function(e) {
  1151.                     var $this, end, start;
  1152.                     if (e.keyCode === 9) {
  1153.                         start = this.selectionStart;
  1154.                         end = this.selectionEnd;
  1155.                         $this = $(this);
  1156.                         $this.val($this.val().substring(0, start) + "\t" + $this.val().substring(end));
  1157.                         this.selectionStart = this.selectionEnd = start + 1;
  1158.                         return false;
  1159.                     }
  1160.                 });
  1161.  
  1162.                 // calculate strength
  1163.                 $("#new_password").keyup(function() {
  1164.                     strength_str(this.value, false);
  1165.                 });
  1166.  
  1167.                 // select whole password when entering field
  1168.                 $('#new_password').click(function() {
  1169.                     this.select();
  1170.                 });
  1171.  
  1172.                 // generate password
  1173.                 $('#new_generate').click(function() {
  1174.                     var popup_exist = ($('#gen_length_popup').val() > 0)
  1175.  
  1176.                     if (!popup_exist) {
  1177.                         // show options
  1178.                         $('#generate_passwordtools').fadeIn(500);
  1179.                         document.getElementById('generate_passwordtools').scrollIntoView();
  1180.                     }
  1181.  
  1182.                     var lower_checked = $('#gen_lower').prop('checked');
  1183.                     var upper_checked = $('#gen_upper').prop('checked');
  1184.                     var numbers_checked = $('#gen_numbers').prop('checked');
  1185.                     var special_checked = $('#gen_special').prop('checked');
  1186.                     var length_filled = $('#gen_length').val();
  1187.                     var generate_new = '';
  1188.  
  1189.                     if (!isNumeric(length_filled) || length_filled.length == 0 || length_filled < 4) {
  1190.                         OCdialogs.alert(t('passwords', 'Fill in a valid number as length with a minimum of 4.'), t('passwords', 'Generate password'), function() { return false; }, true);
  1191.                         return false;
  1192.                     }
  1193.                     if (!lower_checked && !upper_checked && !numbers_checked && !special_checked) {
  1194.                         OCdialogs.alert(t('passwords', 'Select at least one option to generate a password.'), t('passwords', 'Generate password'), function() { return false; }, true);
  1195.                         return false;
  1196.                     }
  1197.  
  1198.                     // run
  1199.                     generate_new = generatepw(lower_checked, upper_checked, numbers_checked, special_checked, length_filled);
  1200.                    
  1201.                     // calculate strength
  1202.                     strength_str(generate_new, false);
  1203.  
  1204.                     // fill in
  1205.                     if (popup_exist) {
  1206.                         $('#new_value_popup').val(generate_new);
  1207.                         $("#generate_strength").text('');
  1208.                         $('#generate_passwordtools').hide();
  1209.                     } else {
  1210.                         $('#new_password').val(generate_new);
  1211.                     }
  1212.                 });
  1213.  
  1214.             },
  1215.             render: function() {
  1216.                 $('#loading').show();
  1217.                 $('#PasswordsTable').hide();
  1218.                 this.renderNavigation();
  1219.                 this.renderContent();
  1220.                 $('#loading').hide();
  1221.                 $('#PasswordsTable').show();
  1222.                 $('#list_active').click();
  1223.             }
  1224.         };
  1225.  
  1226.         var settings = new Settings(OC.generateUrl('/apps/passwords/settings'));
  1227.         var passwords = new Passwords(OC.generateUrl('/apps/passwords/passwords'));
  1228.         var view = new View(passwords);
  1229.         var categories = new Categories(OC.generateUrl('/apps/passwords/categories'));
  1230.        
  1231.         settings.load();
  1232.  
  1233.         // disable context menu if set by admin
  1234.         if (settings.getKey('disable_contextmenu').toLowerCase() == 'true') {
  1235.             this.addEventListener('contextmenu', function(ev) {
  1236.                 ev.preventDefault();
  1237.                 OCdialogs.info(t('passwords', 'The context menu is disabled by your administrator.'), t('passwords', 'Context menu'), function() { return false; }, true);
  1238.                 return false;
  1239.             }, false);
  1240.         }
  1241.  
  1242.         // reset timer on activity (mouse move)
  1243.         if (settings.getKey('timer') != 0) {
  1244.             this.addEventListener("mouseover", function() {
  1245.                 if ($('#app-settings').attr('timer') != '0') {
  1246.                     resetTimer(true);
  1247.                 }
  1248.             });
  1249.         }
  1250.  
  1251.         categories.loadAll().done(function() {
  1252.             renderCategories(categories);
  1253.         }).fail(function() {
  1254.             OCdialogs.alert(t('passwords', 'Error: Could not load categories.'), t('passwords', 'Passwords'), function() { return false; }, true);
  1255.         });
  1256.  
  1257.         passwords.loadAll().done(function() {
  1258.             view.render();
  1259.         }).fail(function() {
  1260.             OCdialogs.alert(t('passwords', 'Error: Could not load passwords.'), t('passwords', 'Passwords'), function() { return false; }, true);
  1261.         });
  1262.     });
  1263.  
  1264.  
  1265. })(OC, window, jQuery);
  1266.  
  1267. function renderCategories(categories) {
  1268.     this._categories = categories;
  1269.  
  1270.     var source_categories = $('#template-categories').html();
  1271.     var template_categories = Handlebars.compile(source_categories);
  1272.     var html_categories = template_categories({
  1273.         categories: this._categories.getAll()
  1274.     });
  1275.     $('#CategoriesTableContent').html(html_categories);
  1276.     $('#new_category').html(''); // reset list in New Password part
  1277.     $('#nav_category_list').html(''); // reset list in navigation entry (below trash bin)
  1278.  
  1279.     // fill table
  1280.     var table = document.getElementById('CategoriesTableContent');
  1281.  
  1282.     if (table.rows.length == 0) {
  1283.         $('#emptycategories').show();
  1284.         $('#new_category').append('<p>(' + t('passwords', 'No categories') + ')</p><br>');
  1285.     } else {
  1286.         $('#emptycategories').hide();
  1287.         var cat_select = '<select>' // for new passwords
  1288.         cat_select += '<option value=0>(' + t('passwords', 'None') + ')</option>';
  1289.         for (var i = 0; i < table.rows.length; i++) {
  1290.             var catID = table.rows[i].cells[1].textContent;
  1291.             var catText = table.rows[i].cells[3].textContent;
  1292.             var catColorBG = table.rows[i].cells[4].textContent || 'fff';
  1293.             var catColorFG = getContrastYIQ(catColorBG)
  1294.             // this cannot be done with handlebars; foreground colour is missing
  1295.             table.rows[i].cells[0].innerHTML = '<div class="category" style="color:#' + catColorFG + ';background-color:#' + catColorBG + ';">' + catText + '</div';
  1296.             cat_select += '<option value=' + catID + ' bg=#' + catColorBG + ' fg=' + catColorFG + '>' + catText + '</option>';
  1297.         }
  1298.         cat_select += '<option value=(change)>(' + t('passwords', 'Edit categories') + ')</option>';
  1299.         cat_select += '</select>';
  1300.         $('#new_category').append(cat_select);
  1301.         $('#nav_category_list').html(cat_select);
  1302.     }
  1303. }
  1304. function isNumeric(n) {
  1305.     return !isNaN(parseFloat(n)) && isFinite(n);
  1306. }
  1307.  
  1308. function formatTable(update_only, rows) {
  1309.  
  1310.     var active_table = $('#app-settings').attr("active-table");
  1311.     var hide_usernames = $('#app-settings').attr("hide-usernames") == 'true';
  1312.     var hide_passwords = $('#app-settings').attr("hide-passwords") == 'true';
  1313.     var hide_attributes = $('#app-settings').attr("hide-attributes") == 'true';
  1314.  
  1315.     // btn_commands_newline
  1316.     // will put the popup button with edit/copy/share buttons on a new line in the cell
  1317.     //
  1318.     // btn_commands_inline
  1319.     // will put the popup button with edit/copy/share buttons right next to the value in the cell
  1320.  
  1321.     if (!update_only) {
  1322.  
  1323.         // clear first
  1324.         $('#PasswordsTableContent tbody').html('');
  1325.         $('#ShareUsers').html('');
  1326.  
  1327.         var is_sharedby = false;
  1328.         var is_sharedto = false;
  1329.  
  1330.         for (var i = 0; i < rows.length - 1; i++) {
  1331.  
  1332.             var thisRow = rows[i].trim();
  1333.  
  1334.             // escape line feed (for notes):
  1335.             thisRow = thisRow.replace(/\n/g, '\\n');
  1336.             // escape tabs (for notes)
  1337.             thisRow = thisRow.replace(/\t/g, '\\t');
  1338.             // escape backslash:
  1339.             thisRow = thisRow.replace(/\\/g, '\\\\');
  1340.             // fix row format for sharing:
  1341.             thisRow = thisRow.replace(/\", ,/g, '\",');
  1342.  
  1343.             try {
  1344.                 var row = JSON.parse('{' + thisRow + '}');
  1345.             } catch (e1) {
  1346.                 try {
  1347.                     // error possibly due to quotation mark,
  1348.                     // so escape it and try again:
  1349.                     thisRow = escapeJSON(thisRow);
  1350.                     var row = JSON.parse('{' + thisRow + '}');
  1351.                 } catch (e2) {
  1352.                     alert(e1 + ' (row ' + i + '):\n{' + thisRow + '}');
  1353.                     continue;
  1354.                 }
  1355.             }
  1356.  
  1357.             if (row.id == 0) {
  1358.                 var uid = row.website;
  1359.                 var uid_escaped = uid.replace(/[^a-zA-Z 0-9]+/g, '');
  1360.                 var displayname = row.user_id;
  1361.                 $('#ShareUsersTableContent').append('<tr>' +
  1362.                     '<td class="share_uid">' + uid + '</td>' +
  1363.                     '<td class="share_displayname">' + displayname + '</td>' +
  1364.                     '</tr>'
  1365.                 );
  1366.                 if (displayname != $('#expandDisplayName').text()) { // do not include yourself
  1367.                     if (uid != displayname) {
  1368.                         $('#ShareUsers').append('<label><input type="checkbox" value=' + uid + '><div class="share_avatar avatar_' + uid_escaped + '"></div><span><strong>' + displayname + '</strong> (' + uid + ')</span></label><br>');
  1369.                     } else {
  1370.                         $('#ShareUsers').append('<label><input type="checkbox" value=' + uid + '><div class="share_avatar avatar_' + uid_escaped + '"></div><span>' + displayname + '</span></label><br>');
  1371.                     }
  1372.                     $('.avatar_' + uid_escaped).avatar(uid, 32);
  1373.                 }
  1374.                 continue;
  1375.             }
  1376.  
  1377.             if (row.user_id != $('head').attr('data-user')) {
  1378.                 is_sharedby = true;
  1379.             } else {
  1380.                 is_sharedby = false;
  1381.             }
  1382.  
  1383.             if (row.deleted == 1) {
  1384.                 if (is_sharedby) {
  1385.                     var html_row = '<tr class="is_deleted is_sharedby" sharedby="' + row.user_id + '" ';
  1386.                 } else {
  1387.                     var html_row = '<tr class="is_deleted" ';
  1388.                 }
  1389.             } else {
  1390.                 if (is_sharedby) {
  1391.                     var html_row = '<tr class="is_sharedby" sharedby="' + row.user_id + '" ';
  1392.                 } else {
  1393.                     var html_row = '<tr ';
  1394.                 }
  1395.             }
  1396.  
  1397.             if (row.datechanged == '0000-00-00') {
  1398.                 row.datechanged = '1970-01-01'; // UNIX :)
  1399.             }
  1400.             if (typeof row.strength == 'undefined') {
  1401.                 row.strength = -1;
  1402.             }
  1403.             if (typeof row.sharedwith != 'undefined' && row.sharedwith != '') {
  1404.                 is_sharedto = true;
  1405.                 html_row += 'attr_sharedwith="' + row.sharedwith + '" ';
  1406.             } else {
  1407.                 is_sharedto = false;
  1408.             }
  1409.             html_row += 'attr_id="' + row.id + '" '
  1410.                         + 'attr_website="' + row.website + '" '
  1411.                         + 'attr_address="' + row.address + '" '
  1412.                         + 'attr_loginname="' + row.loginname + '" '
  1413.                         + 'attr_pass="' + row.pass + '" '
  1414.                         + 'attr_category="' + (row.category || '0') + '" '
  1415.                         + 'attr_notes="' + row.notes.replace(/\\n/g, '\n').replace(/\\t/g, '\t') + '" '
  1416.                         + 'attr_datechanged="' + row.datechanged + '" '
  1417.                         + 'attr_strength="' + strength_int2str(row.strength) + '" '
  1418.                         + 'attr_length="' + row.length + '" '
  1419.                         + 'attr_lower="' + row.lower + '" '
  1420.                         + 'attr_upper="' + row.upper + '" '
  1421.                         + 'attr_number="' + row.number + '" '
  1422.                         + 'attr_special="' + row.special + '">'
  1423.  
  1424.             // start website
  1425.             if (isUrl(row.website) || row.address != '') {
  1426.                 html_row += '<td type="website" sorttable_customkey=' + row.website + ' class="is_website cell_website">';
  1427.                 var show_icons = ($('#app-settings').attr("icons-show") == 'true');
  1428.  
  1429.                 // set real website url if available
  1430.                 if (row.address != '') {
  1431.                     if (row.address.substr(0, 7) == "http://" || row.address.substr(0, 8) == "https://") {
  1432.                         var websiteURL = row.address;
  1433.                     } else {
  1434.                         var websiteURL = 'http://' + row.address;
  1435.                     }
  1436.                 } else {
  1437.                     var websiteURL = 'http://' + row.website;
  1438.                 }
  1439.  
  1440.                 if (show_icons) {
  1441.                     var icons_service = $('#app-settings').attr("icons-service");
  1442.                     if (icons_service == 'ddg') { // DuckDuckGo
  1443.                         html_row += '<a href="' + websiteURL + '" target="_blank"><img class="websitepic" src="https://icons.duckduckgo.com/ip2/' + URLtoDomain(websiteURL) + '.ico">' + row.website + '</a>';
  1444.                     }
  1445.                     if (icons_service == 'ggl') { // Google
  1446.                         html_row += '<a href="' + websiteURL + '" target="_blank"><img class="websitepic" src="https://www.google.com/s2/favicons?domain=' + URLtoDomain(websiteURL) + '">' + row.website + '</a>';
  1447.                     }
  1448.                 } else {
  1449.                     html_row += '<a href="' + websiteURL + '" target="_blank">' + row.website + '</a>';
  1450.                 }
  1451.             } else { // no valid website url
  1452.                 html_row += '<td type="website" sorttable_customkey=' + row.website + ' class="cell_website">' + row.website; // or else doesn't align very well
  1453.             }
  1454.  
  1455.             if (row.website.length > 45) {
  1456.                 html_row += '<div class="btn_commands_newline">' +
  1457.                             '<input class="btn_commands_open" type="button">' +
  1458.                         '</div>' +
  1459.                         '</td>';
  1460.             } else {
  1461.                 html_row += '<div class="btn_commands_inline">' +
  1462.                             '<input class="btn_commands_open" type="button">' +
  1463.                         '</div>' +
  1464.                         '</td>';
  1465.             }
  1466.             // end website
  1467.  
  1468.             // start loginname
  1469.             if (hide_usernames) {
  1470.                 html_row += '<td type="loginname" sorttable_customkey=' + escapeHTML(row.loginname, false) + ' class="hidden_value">' +
  1471.                             '******' +
  1472.                             '<div class="btn_commands_inline">' +
  1473.                                 '<input class="btn_commands_open" type="button">' +
  1474.                             '</div></td>';
  1475.             } else { // place button before value for very long login names
  1476.                 html_row += '<td type="loginname" sorttable_customkey=' + escapeHTML(row.loginname, false) + ' class="cell_username">' +
  1477.                             '<div class="btn_commands_inline">' +
  1478.                                 '<input class="btn_commands_open" type="button">' +
  1479.                             '</div>' +
  1480.                             escapeHTML(row.loginname, true) +
  1481.                             '</td>';
  1482.             }
  1483.             // end loginname
  1484.  
  1485.             // start password
  1486.             if (row.pass == 'oc_passwords_invalid_sharekey') {
  1487.                 html_row += '<td class="cell_password">' +
  1488.                             '<input id="btn_invalid_sharekey" type="button" value="' + t('passwords', 'Invalid share key') + '"></td>';
  1489.             } else if (hide_passwords) {
  1490.                 html_row += '<td type="pass" sorttable_customkey=' + escapeHTML(row.pass, false) + ' class="hidden_value">' +
  1491.                             '******' +
  1492.                             '<div class="btn_commands_inline">' +
  1493.                                 '<input class="btn_commands_open" type="button">' +
  1494.                             '</div></td>';
  1495.             } else { // place button before value for very long passwords
  1496.                 html_row += '<td type="pass" sorttable_customkey=' + escapeHTML(row.pass, false) + ' class="cell_password">' +
  1497.                             '<div class="btn_commands_inline">' +
  1498.                                 '<input class="btn_commands_open" type="button">' +
  1499.                             '</div>' +
  1500.                             escapeHTML(row.pass, true) +
  1501.                             '</td>';
  1502.             }
  1503.  
  1504.             if (!hide_attributes) {
  1505.                 // start strength
  1506.                 html_row += '<td sorttable_customkey=' + 1 / row.strength + ' class="' + strength_int2class(row.strength) + ' cell_strength">' +
  1507.                                 strength_int2str(row.strength) +
  1508.                             '<div class="btn_commands_inline">' +
  1509.                                 '(' + row.strength + ')' +
  1510.                             '</div></td>';
  1511.                 // end strength
  1512.                 // start date
  1513.                 var d = new Date(row.datechanged);
  1514.                 html_row += '<td sorttable_customkey=' + date2sortkey(d) + ' class="' + date2class(d) + ' cell_datechanged"><span>' +
  1515.                                 date2str(d, true) +
  1516.                             '</span><div class="btn_commands_inline dateChanged">' +
  1517.                                 date2str(d, false) +
  1518.                             '</div></td>';
  1519.                 // end date
  1520.             } else {
  1521.                 $('#column_strength').hide();
  1522.                 $('#column_datechanged').hide();
  1523.             }
  1524.  
  1525.             // category
  1526.             if (!is_sharedby) {
  1527.                 var cat_set = false;
  1528.                 if (!row.category || row.category == 0) {
  1529.                     html_row += '<td class="icon-category cell_category"></td>';
  1530.                 } else {
  1531.                     if ($('#CategoriesTableContent').html() == '') {
  1532.                         // categories not yet populated, give second to load
  1533.                         setTimeout(function() {
  1534.                         }, 1000);
  1535.                     }
  1536.                     $('#CategoriesTableContent tr').each(function() {
  1537.                         var cat_id = $(this).find('.catTable_id').text();
  1538.                         if (cat_id == row.category) {
  1539.                             var cat_bg = $(this).find('.catTable_bg').text();
  1540.                             var cat_fg = getContrastYIQ(cat_bg);
  1541.                             var cat_name = $(this).find('.catTable_name').text();
  1542.                             html_row += '<td class="cell_category"><div class="category" style="color:#' + cat_fg + ';background-color:#' + cat_bg + ';">' + cat_name + '</div></td>';
  1543.                             cat_set = true;
  1544.                         }
  1545.                     });
  1546.                     if (!cat_set) {
  1547.                         html_row += '<td class="icon-category cell_category"></td>';
  1548.                     }
  1549.                 }
  1550.             } else {
  1551.                 html_row += '<td></td>';
  1552.             }
  1553.  
  1554.  
  1555.             // notes
  1556.             if (row.notes) {
  1557.                 html_row += '<td class="icon-notes has-note"></td>';
  1558.             } else {
  1559.                 html_row += '<td class="icon-notes"></td>';
  1560.             }
  1561.  
  1562.             // sidebar (info icon)
  1563.             if (row.deleted == 1) {
  1564.                 // replace the info-icon by a revert icon when a password is in trash
  1565.                 html_row += '<td class="icon-history"></td>';
  1566.             } else {
  1567.                 html_row += '<td class="icon-info"></td>';
  1568.             }
  1569.  
  1570.             // share
  1571.             if (is_sharedby) {
  1572.                 html_row += '<td class="icon-shared" title="' + t('passwords', 'Shared by %s').replace('%s', row.user_id) + '"><div class="sharedto_count"><span>0</span></div></td>';
  1573.             } else if ($('#app-settings').attr('sharing-allowed') == 'yes') {
  1574.                 if (is_sharedto) {
  1575.                     html_row += '<td class="icon-public" title="' + t('passwords', 'Shared to %s').replace('%s', row.sharedwith) + '"><div class="sharedto_count is_sharedto"><span>' + row.sharedwith.split(",").length + '</span></div></td>';
  1576.                 } else {
  1577.                     html_row += '<td class="icon-share"><div class="sharedto_count"><span>0</span></div></td>';
  1578.                 }
  1579.             } else {
  1580.                 html_row += '<td></td>';
  1581.             }
  1582.  
  1583.             // delete
  1584.             if (!is_sharedby) {
  1585.                 html_row += '<td class="icon-delete"></td>';
  1586.             } else {
  1587.                 html_row += '<td></td>';
  1588.             }
  1589.  
  1590.             html_row += '</tr>';
  1591.  
  1592.             $('#PasswordsTableContent tbody').append(html_row);
  1593.         }
  1594.     }
  1595.  
  1596.     var total = $('#PasswordsTableContent tbody tr').length;
  1597.     var deleted = $('#PasswordsTableContent tbody tr.is_deleted').length;
  1598.  
  1599.     // update counters
  1600.     $('#emptycontent').hide();
  1601.     $('#emptytrashbin').hide();
  1602.     $('#PasswordsTable').show();
  1603.     $('.menu_passwords_active').text(total - deleted);
  1604.     $('.menu_passwords_trashbin').text(deleted);
  1605.     if (active_table == 'active' && total - deleted == 0) {
  1606.         $('#emptycontent').show();
  1607.         $('#PasswordsTable').hide();
  1608.     }
  1609.     if (active_table == 'trashbin' && deleted == 0) {
  1610.         $('#delete_trashbin').hide();
  1611.         $('#emptytrashbin').show();
  1612.         $('#PasswordsTable').hide();
  1613.     }
  1614. }
  1615.  
  1616. function strength_func(Password) {
  1617.  
  1618.     var charInStr;
  1619.     var strength_calc;
  1620.     var passwordLength;
  1621.     var hasLowerCase;
  1622.     var hasUpperCase;
  1623.     var hasNumber;
  1624.     var hasSpecialChar1;
  1625.     var hasSpecialChar2;
  1626.     var hasSpecialChar3;
  1627.     var hasSpecialChar4;
  1628.     var charInt;
  1629.  
  1630.     passwordLength = Password.length;
  1631.  
  1632.     strength_calc = 0;
  1633.  
  1634.     // check length
  1635.     switch(true) {
  1636.         case passwordLength >= 8:
  1637.             //strength_calc = 1;
  1638.             break;
  1639.         case passwordLength <= 4:
  1640.             // password smaller than 5 chars is always bad
  1641.             return 0;
  1642.             break;
  1643.     }
  1644.  
  1645.     // loop ONCE through password
  1646.     for (var i = 1; i < passwordLength + 1; i++) {
  1647.        
  1648.         charInStr = Password.slice(i, i + 1);
  1649.         charInt = charInStr.charCodeAt(0);
  1650.  
  1651.         switch(true) {
  1652.             case charInt >= 97 && charInt <= 122:
  1653.                 if (!hasLowerCase) {
  1654.                     strength_calc = strength_calc + 1;
  1655.                     hasLowerCase = true;
  1656.                 }
  1657.                 break;
  1658.             case charInt >= 65 && charInt <= 90:
  1659.                 if (!hasUpperCase) {
  1660.                     strength_calc = strength_calc + 1;
  1661.                     hasUpperCase = true;
  1662.                 }
  1663.                 break;
  1664.             case charInt >= 48 && charInt <= 57:
  1665.                 if (!hasNumber) {
  1666.                     strength_calc = strength_calc + 1;
  1667.                     hasNumber = true;
  1668.                 }
  1669.                 break;
  1670.             case charInt >= 33 && charInt <= 47:
  1671.                 if (!hasSpecialChar1) {
  1672.                     strength_calc = strength_calc + 1;
  1673.                     hasSpecialChar1 = true;
  1674.                 }
  1675.                 break;
  1676.             case charInt >= 58 && charInt <= 64:
  1677.                 if (!hasSpecialChar2) {
  1678.                     strength_calc = strength_calc + 1;
  1679.                     hasSpecialChar2 = true;
  1680.                 }
  1681.                 break;
  1682.             case charInt >= 91 && charInt <= 96:
  1683.                 if (!hasSpecialChar3) {
  1684.                     strength_calc = strength_calc + 1;
  1685.                     hasSpecialChar3 = true;
  1686.                 }
  1687.                 break;
  1688.             case charInt >= 123 && charInt <= 255:
  1689.                 if (!hasSpecialChar4) {
  1690.                     strength_calc = strength_calc + 1;
  1691.                     hasSpecialChar4 = true;
  1692.                 }
  1693.                 break;
  1694.         }
  1695.  
  1696.     }
  1697.    
  1698.     strength_calc = strength_calc + (Math.floor(passwordLength / 8) * ((hasLowerCase ? 1 : 0) + (hasUpperCase ? 1 : 0) + (hasNumber ? 1 : 0) + (hasSpecialChar1 ? 1 : 0) + (hasSpecialChar2 ? 1 : 0) + (hasSpecialChar3 ? 1 : 0) + (hasSpecialChar4 ? 1 : 0)));
  1699.    
  1700.     var power = 6;
  1701.     strength_calc = strength_calc + Math.round(Math.pow(passwordLength, power) / Math.pow(10, power + 1));
  1702.  
  1703.     return strength_calc;
  1704.  
  1705. }
  1706.  
  1707. function generatepw(lower, upper, number, special, length_chars) {
  1708.  
  1709.     var length_calc = Math.floor(length_chars / (lower + upper + number + special));
  1710.  
  1711.     var Wlower = "";
  1712.     var Wupper = "";
  1713.     var Wnumber = "";
  1714.     var Wspecial = "";
  1715.  
  1716.     if (lower) {
  1717.         Wlower = random_characters(0, length_calc);
  1718.     }
  1719.     if (upper) {
  1720.         Wupper = random_characters(1, length_calc);
  1721.     }
  1722.     if (number) {
  1723.         Wnumber = random_characters(2, length_calc);
  1724.     }
  1725.     if (special) {
  1726.         Wspecial = random_characters(3, length_calc);
  1727.     }
  1728.  
  1729.     var ww = "" + Wlower + Wupper + Wnumber + Wspecial;
  1730.  
  1731.     // e.g. length 27 with all 4 options = 6 char for every option (24) so 3 remaining
  1732.     // so fill up, starting with special, then number, then upper, then lower:
  1733.     var difference = length_chars - length_calc * (lower + upper + number + special);
  1734.     if (special) {
  1735.         ww = ww + random_characters(3, difference);
  1736.     } else if (number) {
  1737.         ww = ww + random_characters(2, difference);
  1738.     } else if (upper) {
  1739.         ww = ww + random_characters(1, difference);
  1740.     } else if (lower) {
  1741.         ww = ww + random_characters(0, difference);
  1742.     }
  1743.  
  1744.     // do a Fisher-Yates shuffle
  1745.     var a = ww.split("");
  1746.     var n = a.length;
  1747.  
  1748.     for (var i = n - 1; i > 0; i--) {
  1749.         var j = Math.floor(Math.random() * (i + 1));
  1750.         var tmp = a[i];
  1751.         a[i] = a[j];
  1752.         a[j] = tmp;
  1753.     }
  1754.  
  1755.     ww = a.join("");
  1756.  
  1757.     return ww;
  1758.  
  1759. }
  1760.  
  1761. function random_characters(char_kind, size_wanted) {
  1762.  
  1763.     var allowed = "";
  1764.     var text = "";
  1765.  
  1766.     switch (char_kind) {
  1767.         // No | l I 1 B 8 0 O o due to reading ability
  1768.         case 0:
  1769.             allowed = "abcdefghijkmnpqrstuvwxyz";
  1770.             break;
  1771.         case 1:
  1772.             allowed = "ACDEFGHJKLMNPQRSTUVWXYZ";
  1773.             break;
  1774.         case 2:
  1775.             allowed = "2345679";
  1776.             break;
  1777.         case 3:
  1778.             allowed = "!@#$%^&*()_+~[]{}:;?><,./-=";
  1779.             break;
  1780.     }
  1781.  
  1782.     for (var i = 0; i < size_wanted; i++)
  1783.     text += allowed.charAt(Math.floor(Math.random() * allowed.length));
  1784.  
  1785.     return text;
  1786. }
  1787.  
  1788. function uid2displayname(uid) {
  1789.     var displayname = uid;
  1790.     $('#ShareUsersTableContent tr').each(function() {
  1791.         var uid_list = $(this).find('.share_uid').text();
  1792.         var displayname_list = $(this).find('.share_displayname').text();
  1793.         if (uid == uid_list) {
  1794.             displayname = displayname_list;
  1795.         }
  1796.     });
  1797.     return displayname;
  1798. }
  1799. function strength_int2str(integer) {
  1800.  
  1801.     if (integer == -1) {
  1802.         return '???';
  1803.     }
  1804.  
  1805.     switch (true) {
  1806.         case (integer < 8):
  1807.             return t('passwords', 'Weak');
  1808.             break;
  1809.         case (integer < 15):
  1810.             return t('passwords', 'Moderate');
  1811.             break;
  1812.         default: // everything >= 15
  1813.             return t('passwords', 'Strong');
  1814.     }
  1815. }
  1816. function strength_int2class(integer) {
  1817.  
  1818.     if (integer == -1) {
  1819.         return false;
  1820.     }
  1821.  
  1822.     switch (true) {
  1823.         case (integer < 8):
  1824.             return 'red';
  1825.             break;
  1826.         case (integer < 15):
  1827.             return 'orange';
  1828.             break;
  1829.         default: // everything >= 15
  1830.             return 'green';
  1831.     }
  1832. }
  1833. function strength_str2class(str) {
  1834.  
  1835.     switch (str) {
  1836.         case t('passwords', 'Weak'):
  1837.             return 'red';
  1838.             break;
  1839.         case t('passwords', 'Moderate'):
  1840.             return 'orange';
  1841.             break;
  1842.         case t('passwords', 'Strong'):
  1843.             return 'green';
  1844.     }
  1845. }
  1846. function bool2class_str(bool) {
  1847.  
  1848.     switch (parseInt(bool)) {
  1849.         case 0:
  1850.             return ["red", t('passwords', 'No')];
  1851.             break;
  1852.         case 1:
  1853.             return ["green", t('passwords', 'Yes')];
  1854.     }
  1855. }
  1856. function date2sortkey(dateChanged) {
  1857.     return (dateChanged.getFullYear()
  1858.         + ('0' + (dateChanged.getMonth() + 1)).slice(-2)
  1859.         + ('0' + dateChanged.getDate()).slice(-2)) / 10000000;
  1860. }
  1861. function date2class(dateChanged) {
  1862.  
  1863.     var dateToday = new Date();
  1864.     var diffInDays = Math.floor((dateToday - dateChanged) / (1000 * 60 * 60 * 24));
  1865.  
  1866.     var days_orange = $('#app-settings').attr("days-orange");
  1867.     var days_red = $('#app-settings').attr("days-red");
  1868.  
  1869.     if (diffInDays > days_red - 1) {
  1870.         return 'red'; // default: > 365 days
  1871.     } else if (diffInDays > days_orange - 1) {
  1872.         return 'orange'; // default: 150-364 days
  1873.     } else if (diffInDays < days_orange) {
  1874.         return 'green'; // < default: 150 days
  1875.     }
  1876. }
  1877. function date2str(dateChanged, timeAgo) {
  1878.  
  1879.     if (isNaN(dateChanged)) {
  1880.         return '???';
  1881.     }
  1882.  
  1883.     var language = $('html').attr('lang');
  1884.  
  1885.     if (!timeAgo) {
  1886.         var month = dateChanged.getMonth() + 1;
  1887.         var date_str;
  1888.         var month_str;
  1889.         switch (month) {
  1890.             case 1:
  1891.                 month_str = t('passwords', 'January');
  1892.                 break;
  1893.             case 2:
  1894.                 month_str = t('passwords', 'February');
  1895.                 break;
  1896.             case 3:
  1897.                 month_str = t('passwords', 'March');
  1898.                 break;
  1899.             case 4:
  1900.                 month_str = t('passwords', 'April');
  1901.                 break;
  1902.             case 5:
  1903.                 month_str = t('passwords', 'May');
  1904.                 break;
  1905.             case 6:
  1906.                 month_str = t('passwords', 'June');
  1907.                 break;
  1908.             case 7:
  1909.                 month_str = t('passwords', 'July');
  1910.                 break;
  1911.             case 8:
  1912.                 month_str = t('passwords', 'August');
  1913.                 break;
  1914.             case 9:
  1915.                 month_str = t('passwords', 'September');
  1916.                 break;
  1917.             case 10:
  1918.                 month_str = t('passwords', 'October');
  1919.                 break;
  1920.             case 11:
  1921.                 month_str = t('passwords', 'November');
  1922.                 break;
  1923.             case 12:
  1924.                 month_str = t('passwords', 'December');
  1925.                 break;
  1926.         }
  1927.  
  1928.         if (language == 'en') {
  1929.             // format: 14th March 2011, most Brittish according to https://www.englishclub.com/vocabulary/time-date.htm
  1930.             var suffix;
  1931.             switch (dateChanged.getDate()) {
  1932.                 case 1:
  1933.                 case 21:
  1934.                 case 31:
  1935.                     suffix = 'st';
  1936.                     break;
  1937.                 case 2:
  1938.                 case 22:
  1939.                     suffix = 'nd';
  1940.                     break;
  1941.                 case 3:
  1942.                 case 23:
  1943.                     suffix = 'rd';
  1944.                     break;
  1945.                 default:
  1946.                     suffix = 'th';
  1947.                     break;
  1948.             }
  1949.             date_str = dateChanged.getDate() + '<sup>' + suffix + '</sup> ' + month_str + ' ' + dateChanged.getFullYear();
  1950.         } else if (language == 'nl') {
  1951.             // Dutch: 14 maart 2015
  1952.             date_str = dateChanged.getDate() + ' ' + month_str + ' ' + dateChanged.getFullYear();
  1953.         } else if (language == 'de') {
  1954.             // German: 14. März 2015
  1955.             date_str = dateChanged.getDate() + '. ' + month_str + ' ' + dateChanged.getFullYear();
  1956.         } else if (language == 'es') {
  1957.             // Spanish: 14 de marzo de 2015
  1958.             date_str = dateChanged.getDate() + ' de ' + month_str + ' de ' + dateChanged.getFullYear();
  1959.         } else if (language == 'ca') {
  1960.             // Catalan: 14 de març de 2015
  1961.             if ((month_str[0] == 'a') || (month_str[0] == 'o')) {
  1962.                 date_str = dateChanged.getDate() + ' d\'' + month_str + ' de ' + dateChanged.getFullYear();
  1963.             } else {
  1964.                 date_str = dateChanged.getDate() + ' de ' + month_str + ' de ' + dateChanged.getFullYear();
  1965.             }
  1966.  
  1967.         } else {
  1968.             // all others: March 14, 2015
  1969.             date_str = month_str + ' ' + dateChanged.getDate() + ', ' + dateChanged.getFullYear();
  1970.         }
  1971.  
  1972.         return date_str;
  1973.  
  1974.     }
  1975.  
  1976.     var dateToday = new Date();
  1977.  
  1978.     var diffInDays = Math.floor((dateToday - dateChanged) / (1000 * 60 * 60 * 24));
  1979.     switch (diffInDays) {
  1980.         case -1:
  1981.         case 0:
  1982.             return t('passwords', 'today');
  1983.             break;
  1984.         case 1:
  1985.             if (language == 'es') {
  1986.                 return 'hace ' + diffInDays + ' ' + t('passwords', 'day ago');
  1987.             } else if (language == 'ca') {
  1988.                 return 'fa ' + diffInDays + ' ' + t('passwords', 'day ago');
  1989.             } else {
  1990.                 return diffInDays + ' ' + t('passwords', 'day ago');
  1991.             }
  1992.             break;
  1993.         default:
  1994.             if (language == 'es') {
  1995.                 return 'hace ' + diffInDays + ' ' + t('passwords', 'days ago');
  1996.             } else if (language == 'ca') {
  1997.                 return 'fa ' + diffInDays + ' ' + t('passwords', 'days ago');
  1998.             } else {
  1999.                 return diffInDays + ' ' + t('passwords', 'days ago');
  2000.             }
  2001.     }
  2002. }
  2003.  
  2004.  
  2005. function strength_str(passw, return_string_only) {
  2006.  
  2007.     if (!return_string_only) {
  2008.         if (passw == '') {
  2009.             $("#generate_strength").text('');
  2010.             return false;
  2011.         }
  2012.  
  2013.         $("#generate_strength").removeClass("red");
  2014.         $("#generate_strength").removeClass("orange");
  2015.         $("#generate_strength").removeClass("green");
  2016.     }
  2017.    
  2018.     switch (strength_func(passw)) {
  2019.         case 0:
  2020.         case 1:
  2021.         case 2:
  2022.         case 3:
  2023.         case 4:
  2024.         case 5:
  2025.         case 6:
  2026.         case 7:
  2027.             if (return_string_only) { return t('passwords', 'Weak'); }
  2028.             $("#generate_strength").text(t('passwords', 'Strength') + ': ' + t('passwords', 'Weak').toLowerCase() + ' (' + strength_func(passw) + ')');
  2029.             $("#generate_strength").addClass("red");
  2030.             break;
  2031.         case 8:
  2032.         case 9:
  2033.         case 10:
  2034.         case 11:
  2035.         case 12:
  2036.         case 13:
  2037.         case 14:
  2038.             if (return_string_only) { return t('passwords', 'Moderate'); }
  2039.             $("#generate_strength").text(t('passwords', 'Strength') + ': ' + t('passwords', 'Moderate').toLowerCase() + ' (' + strength_func(passw) + ')');
  2040.             $("#generate_strength").addClass("orange");
  2041.             break;
  2042.         default: // everything >= 15
  2043.             if (return_string_only) { return t('passwords', 'Strong'); }
  2044.             $("#generate_strength").text(t('passwords', 'Strength') + ': ' + t('passwords', 'Strong').toLowerCase() + ' (' + strength_func(passw) + ')');
  2045.             $("#generate_strength").addClass("green");
  2046.     }
  2047.  
  2048.     $("#generate_strength_popup").text($("#generate_strength").text());
  2049.     $("#generate_strength_popup").attr("class", $("#generate_strength").attr("class"));
  2050.  
  2051. }
  2052. function escapeHTML(text, only_brackets) {
  2053.     if (typeof text !== 'undefined') {
  2054.         if (only_brackets) {
  2055.             return text.replace(/</g,'&lt;').replace(/>/g,'&gt;');
  2056.         } else {
  2057.             return text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  2058.         }
  2059.     } else {
  2060.         return text;
  2061.     }
  2062. }
  2063. function escapeJSON(text) {
  2064.  
  2065.     // unquote element names and values first with just a random string
  2066.     text = text.replace(/", "/g, ',uJ94dFpJv36usjxQS56SL3Lv77H25cE3 ');
  2067.     text = text.replace(/" : "/g, ' :uJ94dFpJv36usjxQS56SL3Lv77H25cE3 ');
  2068.  
  2069.     // FIX FOR API
  2070.     // "properties": "\"loginname\": \"foo\", \"address\": \"\", \"notes\": \"\"",
  2071.     // needs to be read as:
  2072.     // "loginname": "foo", (not "loginname&quot;: &quot;foo",)
  2073.     // "address": "", (not "address&quot;: &quot;",)
  2074.     // "notes": "", (not "notes&quot;: &quot;",)
  2075.     text = text.replace(/\": \"/g, ' :uJ94dFpJv36usjxQS56SL3Lv77H25cE3 ');
  2076.  
  2077.     text = text.substr(1);
  2078.     text = text.replace(/.$/g, '');
  2079.     // now escape HTML characters (in usernames, passwords, notes)
  2080.     text = $('<textarea/>').text(text).html();
  2081.     text = text.replace(/\"/g, '&quot;');
  2082.     // and change string back to valid JSON
  2083.     text = text.replace(/,uJ94dFpJv36usjxQS56SL3Lv77H25cE3 /g, '", "');
  2084.     text = text.replace(/ :uJ94dFpJv36usjxQS56SL3Lv77H25cE3 /g, '" : "');
  2085.     text = '"' + text + '"';
  2086.  
  2087.     return text;
  2088. }
  2089. function isUrl(url) {
  2090.  
  2091.     // not starting with a whitespace char or / or $ or . or ? or #
  2092.     // overall no spaces allowed
  2093.     // at least 1 char before and 2 chars after a dot
  2094.     // test for ^[^\s/$.?#]\S{1,}\.[a-z]{2,}$
  2095.     url = url.toLowerCase();
  2096.     var strRegex = '^[^\\s/$.?#]\\S{1,}\\.[a-z]{2,}$';
  2097.  
  2098.     var re = new RegExp(strRegex);
  2099.  
  2100.     return re.test(url);
  2101. }
  2102. function strip_website(website) {
  2103.  
  2104.     var convert = website;
  2105.  
  2106.     if (!isUrl(website)) {
  2107.         return website;
  2108.     }
  2109.    
  2110.     if (convert.substr(0, 8) == "https://") {
  2111.         convert = convert.substr(8, convert.length - 8);
  2112.     };
  2113.  
  2114.     if (convert.substr(0, 7) == "http://") {
  2115.         convert = convert.substr(7, convert.length - 7);
  2116.     };
  2117.    
  2118.     if (convert.substr(0, 4) == "www.") {
  2119.         convert = convert.substr(4, convert.length - 4);
  2120.     };
  2121.  
  2122.     return convert;
  2123. }
  2124. function URLtoDomain(website) {
  2125.  
  2126.     var domain;
  2127.     // remove protocol (http, ftp, etc.) and get domain
  2128.     if (website.indexOf("://") > -1) {
  2129.         domain = website.split('/')[2];
  2130.     }
  2131.     else {
  2132.         domain = website.split('/')[0];
  2133.     }
  2134.  
  2135.     // remove port number
  2136.     domain = domain.split(':')[0];
  2137.  
  2138.     // remove unwanted wwww. for sorting purposes
  2139.     if (domain.substr(0, 4) == "www.") {
  2140.         domain = domain.substr(4, domain.length - 4);
  2141.     };
  2142.  
  2143.     return domain;
  2144. }
  2145. function markRow(row) {
  2146.     var rows = $('tr', $('#PasswordsTableContent'));
  2147.     rows.eq(row).animate( { backgroundColor: '#ffa' }, 400, function() {
  2148.         $(this).animate( { backgroundColor: 'none' }, 3000);
  2149.     });
  2150. }
  2151.  
  2152. function backupPasswords() {
  2153.  
  2154.     // No support in IE
  2155.     if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) {
  2156.         OCdialogs.alert(t('passwords', 'This function is unsupported on your browser. Use a modern browser instead.'), t('passwords', 'Download Backup'), function() { return false; }, true);
  2157.         return false;
  2158.     }
  2159.  
  2160.     OCdialogs.confirm(t('passwords', 'This will download an unencrypted backup file, which contains all your passwords.') + ' ' + t('passwords', 'This file is fully compatible with other password services, such as KeePass, 1Password and LastPass.') + ' ' + t('passwords', 'Are you sure?'), t('passwords', 'Download Backup'),
  2161.         function(confirmed) {
  2162.             if (confirmed) {
  2163.                 var d = new Date();
  2164.                 var textToWrite = '"Website","Username","Password","FullAddress","Notes"\r\n';
  2165.  
  2166.                 var deleted_too = window.confirm(t('passwords', 'Would you like to backup deleted passwords too?'));
  2167.  
  2168.                 $('#PasswordsTableContent tbody tr').each(function() {
  2169.                     var $row = $(this);
  2170.  
  2171.                     // possible entries:   
  2172.                     // $row.attr('attr_id')
  2173.                     // $row.attr('attr_loginname')
  2174.                     // $row.attr('attr_website')
  2175.                     // $row.attr('attr_address')
  2176.                     // $row.attr('attr_pass')
  2177.                     // $row.attr('attr_notes')
  2178.                     // $row.attr('attr_category')
  2179.  
  2180.                     if (!$row.hasClass('is_deleted') || deleted_too) {
  2181.                         textToWrite += '"' + $row.attr('attr_website').replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '","'
  2182.                             + $row.attr('attr_loginname').replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '","'
  2183.                             + $row.attr('attr_pass').replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '","'
  2184.                             + $row.attr('attr_address').replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '","'
  2185.                             + $row.attr('attr_notes').replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'
  2186.                             + '\r\n';
  2187.                     }
  2188.                 });
  2189.  
  2190.                 var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});
  2191.                 var d = new Date();
  2192.  
  2193.                 // filename as YYYYMMDD_backup.txt
  2194.                 var fileNameToSaveAs = d.getFullYear()
  2195.                                      + ('0' + (d.getMonth() + 1)).slice(-2)
  2196.                                      + ('0' + d.getDate()).slice(-2)
  2197.                                      + '_backup.csv';
  2198.  
  2199.                 var downloadLink = document.createElement("a");
  2200.                 downloadLink.download = fileNameToSaveAs;
  2201.                 downloadLink.innerHTML = "Download File";
  2202.                
  2203.                 if (window.webkitURL != null) {
  2204.                     // Chrome allows the link to be clicked
  2205.                     // without actually adding it to the DOM.
  2206.                     downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
  2207.                 } else {
  2208.                     // Firefox requires the link to be added to the DOM
  2209.                     // before it can be clicked.
  2210.                     downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
  2211.                     downloadLink.onclick = destroyClickedElement;
  2212.                     downloadLink.style.display = "none";
  2213.                     document.body.appendChild(downloadLink);
  2214.                 }
  2215.  
  2216.                 downloadLink.click();
  2217.                 downloadLink = '';
  2218.  
  2219.                 // collapse settings part in navigation pane
  2220.                 $('#app-settings-content').hide();
  2221.             }
  2222.         }, true);
  2223.  
  2224. }
  2225. function destroyClickedElement(event) {
  2226.     document.body.removeChild(event.target);
  2227. }
  2228. function uploadCSV(event) {
  2229.  
  2230.     if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
  2231.         OCdialogs.alert(t('passwords', 'This function is unsupported on your browser. Use a modern browser instead.'), t('passwords', 'Import CSV File'), function() { return false; }, true);
  2232.         return false;
  2233.     }
  2234.  
  2235.     //Retrieve the first (and only!) File from the FileList object
  2236.     var f = event.target.files[0];
  2237.  
  2238.     if (!f) {
  2239.         OCdialogs.alert('No file loaded', t('passwords', 'Import CSV File'), function() { return false; }, true);
  2240.         $('#upload_csv').replaceWith($('#upload_csv').clone(true).val(''));
  2241.         return false;
  2242.     } else if (f.name.substr(f.name.length - 4, 4).toLowerCase() != '.csv') {
  2243.         // validate file
  2244.         OCdialogs.alert(t('passwords', 'This is not a valid CSV file.') + ' ' + ('passwords', 'Only files with CSV as file extension are allowed.'), t('passwords', 'Import CSV File'), function() { return false; }, true);
  2245.         $('#upload_csv').replaceWith($('#upload_csv').clone(true).val(''));
  2246.         return false;
  2247.     } else {
  2248.  
  2249.         var r = new FileReader();
  2250.        
  2251.         r.onload = function(event) {
  2252.             var fileContent = event.target.result;
  2253.             $('#CSVcontent').text(fileContent);
  2254.             renderCSV(true);
  2255.         }
  2256.         r.readAsText(f); // = execute
  2257.     }
  2258.  
  2259.     $('#CSVtableDIV h2').text(t('passwords', 'Import CSV File') + ": '" + f.name + "' (~" + Math.round(f.size / 1024) + ' kB)');
  2260.     $('#CSVpreviewTitle').text(t('passwords', 'Preview') + ":");
  2261.  
  2262.     // reset upload file field
  2263.     $('#upload_csv').replaceWith($('#upload_csv').clone(true).val(''));
  2264.  
  2265. }
  2266. function renderCSV(firstTime) {
  2267.  
  2268.     // clear first
  2269.     $('#CSVtable').empty();
  2270.     $('#CSVerrorfield').empty();
  2271.  
  2272.     var contents = $('#CSVcontent').text();
  2273.     contents = contents.replace(/"\n/g,"\"\"\n").replace(/"\r\n/g,"\"\"\r\n");
  2274.  
  2275.     if ($('#CSVsplit_rnBtn').hasClass('CSVbuttonOff')) {
  2276.         var count = (contents.match(/\n/g) || []).length + 1;
  2277.         var lines = contents.split('\n');
  2278.     } else {
  2279.         var count = (contents.match(/\r\n/g) || []).length + 1;
  2280.         var lines = contents.split('\r\n');
  2281.     }
  2282.     if ($('#CSVheadersBtn').hasClass('CSVbuttonOff')) {
  2283.         var startRow = 0;
  2284.     } else {
  2285.         var startRow = 1;
  2286.     }
  2287.     if ($('#CSVquotationmarksBtn').hasClass('CSVbuttonOff')) {
  2288.         var quotationMarks = false;
  2289.         var splitDelimiter = ',';
  2290.     } else {
  2291.         var quotationMarks = true;
  2292.         var splitDelimiter = '","';
  2293.     }
  2294.     if ($('#CSVescapeslashBtn').hasClass('CSVbuttonOff')) {
  2295.         var escapeSlash = false;
  2296.     } else {
  2297.         var escapeSlash = true;
  2298.     }
  2299.  
  2300.     if (count < 1) {
  2301.         InvalidCSV(t('passwords', 'This file contains no passwords.'));
  2302.     }
  2303.  
  2304.     var countColumns = 0;
  2305.  
  2306.     for (var i = startRow; i < lines.length; i++) {
  2307.         // i = 1 with headers, so skip i = 0 (headers tested before)
  2308.  
  2309.         // loop once to check if all lines contain at least 3 values
  2310.        
  2311.         if (lines[i] != '') {
  2312.             if (quotationMarks && lines[i].substr(0, 1) != '"') {
  2313.                 InvalidCSV(t('passwords', 'This file contains one or more values without quotation marks.'));
  2314.             }
  2315.  
  2316.             var line = lines[i].split(splitDelimiter);
  2317.  
  2318.             if (countColumns < line.length) {
  2319.                 countColumns = line.length;
  2320.             }
  2321.             if (line.length < 3) {
  2322.                 InvalidCSV(t('passwords', 'This file contains one or more lines with less than 3 columns.'));
  2323.             }
  2324.             if (quotationMarks) {
  2325.                 // line is "value1","value2","value3"
  2326.                 // and thus cut first and last '"'
  2327.                 lines[i] = lines[i].substr(1, lines[i].trim().length - 2);
  2328.             }
  2329.         } else {
  2330.             count = count - 1;
  2331.         }
  2332.     }
  2333.  
  2334.     // create tableheads
  2335.     var CSVtable = $('#CSVtable');
  2336.     var tableHead = ''
  2337.  
  2338.     tableHead = '<thead><tr><td id="CSVcolumnCheck">' + t('passwords', 'Import') + ':</td>'
  2339.     for (i = 1; i < countColumns + 1; i++) {
  2340.         tableHead = tableHead +
  2341.             '<td id="CSVcolumnTD' + i + '">' +
  2342.                 '<select id="CSVcolumn' + i + '">' +
  2343.                     '<option value="website">' + t('passwords', 'Website or company') + '</option>' +
  2344.                     '<option value="username">' + t('passwords', 'Login name') + '</option>' +
  2345.                     '<option value="password">' + t('passwords', 'Password') + '</option>' +
  2346.                     '<option value="url">' + t('passwords', 'Full URL') + '</option>' +
  2347.                     '<option value="notes">' + t('passwords', 'Notes') + '</option>' +
  2348.                     '<option value="empty" selected>(' + t('passwords', 'Do not import') + ')</option>' +
  2349.                 '</select>' +
  2350.             '</td>';
  2351.  
  2352.     }
  2353.     tableHead = tableHead + '</tr></thead>';
  2354.     if (countColumns != 0) {
  2355.         CSVtable.append(tableHead);
  2356.     }
  2357.  
  2358.     $('#CSVcolumn1 option').eq(0).prop('selected', true);
  2359.     $('#CSVcolumn2 option').eq(1).prop('selected', true);
  2360.     $('#CSVcolumn3 option').eq(2).prop('selected', true);
  2361.     $('#CSVcolumn4 option').eq(3).prop('selected', true);
  2362.     $('#CSVcolumn5 option').eq(4).prop('selected', true);
  2363.    
  2364.  
  2365.     var tableBody = '<tbody>';
  2366.     for (i = startRow; i < lines.length; i++) {
  2367.         if (lines[i] != '') {
  2368.             var line = lines[i].split(splitDelimiter);
  2369.            
  2370.             tableBody = tableBody + '<tr><td><input type="checkbox" id="CSVcheckRow' + i + '" checked></td>';  
  2371.  
  2372.             for (var j = 0; j < countColumns; j++) {
  2373.                 if (typeof line[j] !== 'undefined') {
  2374.                     if (escapeSlash) {
  2375.                         tableBody = tableBody + '<td><textarea disabled>' + escapeHTML(line[j]).replace(/\\"/g, '"').replace(/\\\\/g, '\\') + '</textarea></td>';
  2376.                      } else {
  2377.                         tableBody = tableBody + '<td><textarea disabled>' + escapeHTML(line[j]) + '</textarea></td>';
  2378.                     }
  2379.                 }
  2380.             }
  2381.             tableBody = tableBody + '</tr>';
  2382.         }
  2383.     }
  2384.     tableBody = tableBody + '</tbody>';
  2385.     CSVtable.append(tableBody);
  2386.  
  2387.     if (firstTime) {
  2388.         // align to horizontal center
  2389.         $('#CSVtableDIV').show();
  2390.         var CSVtableDIVWidth = document.getElementById('CSVtableDIV').clientWidth;
  2391.         var browserWidth = Math.max(
  2392.             document.body.scrollWidth, document.documentElement.scrollWidth,
  2393.             document.body.offsetWidth, document.documentElement.offsetWidth,
  2394.             document.body.clientWidth, document.documentElement.clientWidth
  2395.         );
  2396.         var browserHeight = Math.max(
  2397.             document.body.scrollHeight, document.documentElement.scrollHeight,
  2398.             document.body.offsetHeight, document.documentElement.offsetHeight,
  2399.             document.body.clientHeight, document.documentElement.clientHeight
  2400.         );
  2401.         $('#CSVtableScroll').css('maxHeight', browserHeight * 0.45);
  2402.         document.getElementById("CSVtableDIV").style.left = (browserWidth - CSVtableDIVWidth) / 2 + "px";
  2403.         document.getElementById("CSVtableDIV").style.top = "20px";
  2404.         $('#CSVtableDIV').hide();
  2405.         $('#app-settings-content').hide();
  2406.         $('#CSVtableDIV').show(400);
  2407.     }
  2408.  
  2409.     // set title of preview + password count
  2410.     if ((count - startRow) == 1) {
  2411.         $('#CSVpreviewTitle').text(t('passwords', 'Preview') + ' (' + (count - startRow) + ' ' + t('passwords', 'Password').toLowerCase() + '):');
  2412.     } else {
  2413.         $('#CSVpreviewTitle').text(t('passwords', 'Preview') + ' (' + (count - startRow) + ' ' + t('passwords', 'Passwords').toLowerCase() + '):');
  2414.     }
  2415.  
  2416.     $('#CSVcolumnCount').val(countColumns);
  2417.  
  2418.     checkCSVsettings();
  2419.  
  2420. }
  2421. function checkCSVsettings() {
  2422.  
  2423.     var countColumns = $('#CSVcolumnCount').val();
  2424.  
  2425.     var selectedOptions = '';
  2426.     var multipleColumnError = false;
  2427.     var hasWebsite = false;
  2428.     var hasUsername = false;
  2429.     var hasPassword = false;
  2430.  
  2431.     for (var i = 0; i < countColumns; i++) {
  2432.         $('#CSVcolumnTD' + i).removeClass('CSVcolumnInvalid');
  2433.         selectedOptions = selectedOptions + $('#CSVcolumn' + i).val();
  2434.         if ($('#CSVcolumn' + i).val() == 'website') {
  2435.             hasWebsite = true;
  2436.         }
  2437.         if ($('#CSVcolumn' + i).val() == 'username') {
  2438.             hasUsername = true;
  2439.         }
  2440.         if ($('#CSVcolumn' + i).val() == 'password') {
  2441.             hasPassword = true;
  2442.         }
  2443.     }
  2444.  
  2445.     // check website on multiple occurences
  2446.     if ((selectedOptions.match(/website/g) || []).length > 1) {
  2447.         for (var i = 0; i <= countColumns; i++) {
  2448.             if ($('#CSVcolumn' + i).val() == 'website') {
  2449.                 $('#CSVcolumnTD' + i).addClass('CSVcolumnInvalid');
  2450.                 multipleColumnError = true;
  2451.             }
  2452.         }
  2453.     }
  2454.     // check username on multiple occurences
  2455.     if ((selectedOptions.match(/username/g) || []).length > 1) {
  2456.         for (i = 0; i < countColumns; i++) {
  2457.             if ($('#CSVcolumn' + i).val() == 'username') {
  2458.                 $('#CSVcolumnTD' + i).addClass('CSVcolumnInvalid');
  2459.                 multipleColumnError = true;
  2460.             }
  2461.         }
  2462.     }
  2463.     // check password on multiple occurences
  2464.     if ((selectedOptions.match(/password/g) || []).length > 1) {
  2465.         for (i = 0; i < countColumns; i++) {
  2466.             if ($('#CSVcolumn' + i).val() == 'password') {
  2467.                 $('#CSVcolumnTD' + i).addClass('CSVcolumnInvalid');
  2468.                 multipleColumnError = true;
  2469.             }
  2470.         }
  2471.     }
  2472.     // check url on multiple occurences
  2473.     if ((selectedOptions.match(/url/g) || []).length > 1) {
  2474.         for (i = 0; i < countColumns; i++) {
  2475.             if ($('#CSVcolumn' + i).val() == 'url') {
  2476.                 $('#CSVcolumnTD' + i).addClass('CSVcolumnInvalid');
  2477.                 multipleColumnError = true;
  2478.             }
  2479.         }
  2480.     }
  2481.     // check notes on multiple occurences
  2482.     if ((selectedOptions.match(/notes/g) || []).length > 1) {
  2483.         for (i = 0; i < countColumns; i++) {
  2484.             if ($('#CSVcolumn' + i).val() == 'notes') {
  2485.                 $('#CSVcolumnTD' + i).addClass('CSVcolumnInvalid');
  2486.                 multipleColumnError = true;
  2487.             }
  2488.         }
  2489.     }
  2490.  
  2491.     // check if no red text appeared, else disable import button
  2492.     if ($('#CSVerrorfield').text() != '') {
  2493.         $('#importStart').css ('opacity', 0.5);
  2494.         $('#importStart')[0].disabled = true;
  2495.         throw new Error('Invalid values. Check red text.');
  2496.     } else {
  2497.         $('#importStart').css ('opacity', 1);
  2498.         $('#importStart')[0].disabled = false;
  2499.     }
  2500.     if (multipleColumnError) {
  2501.         throw new Error('Invalid values. Check red text.');
  2502.     }
  2503.  
  2504. }
  2505. function importCSV() {
  2506.  
  2507.     var countColumns = $('#CSVcolumnCount').val();
  2508.     var websiteColumn = -1;
  2509.     var loginColumn = -1;
  2510.     var passwordColumn = -1;
  2511.     var urlColumn = -1;
  2512.     var notesColumn = -1;
  2513.  
  2514.     for (var i = 1; i <= countColumns; i++) {
  2515.         if ($('#CSVcolumn' + i).val() == 'website') {
  2516.             websiteColumn = i;
  2517.         }
  2518.         if ($('#CSVcolumn' + i).val() == 'username') {
  2519.             loginColumn = i;
  2520.         }
  2521.         if ($('#CSVcolumn' + i).val() == 'password') {
  2522.             passwordColumn = i;
  2523.         }
  2524.         if ($('#CSVcolumn' + i).val() == 'url') {
  2525.             urlColumn = i;
  2526.         }
  2527.         if ($('#CSVcolumn' + i).val() == 'notes') {
  2528.             notesColumn = i;
  2529.         }
  2530.     }
  2531.  
  2532.     if (websiteColumn == -1 || loginColumn == -1 || passwordColumn == -1) {
  2533.         OCdialogs.alert(t('passwords', 'Fill in the website, user name and password.'), t('passwords', 'Import CSV File'), function() { return false; }, true);
  2534.         throw new Error('Select a column for website, username and password.');
  2535.     }
  2536.  
  2537.     var CSVtable = document.getElementById('CSVtable');
  2538.     var loginCSV = '';
  2539.     var websiteCSV = '';
  2540.     var urlCSV = '';
  2541.     var passwordCSV = '';
  2542.     var notesCSV = '';
  2543.     var passarray = [];
  2544.     var d = new Date();
  2545.     // date as YYYY-MM-DD
  2546.     var changedDate = d.getFullYear()
  2547.         + '-' + ('0' + (d.getMonth() + 1)).slice(-2)
  2548.         + '-' + ('0' + d.getDate()).slice(-2);
  2549.  
  2550.     for (var r = 1; r < CSVtable.rows.length; r++) {
  2551.         if ($('#CSVcheckRow' + r).is(":checked")) {
  2552.             for (var c = 0; c <= countColumns; c++) {
  2553.                 if (c == websiteColumn) {
  2554.                     websiteCSV = CSVtable.rows[r].cells[c].textContent;
  2555.                 }
  2556.                 if (c == loginColumn) {
  2557.                     loginCSV = CSVtable.rows[r].cells[c].textContent;
  2558.                 }
  2559.                 if (c == passwordColumn) {
  2560.                     passwordCSV = CSVtable.rows[r].cells[c].textContent;
  2561.                 }
  2562.                 if (c == urlColumn) {
  2563.                     urlCSV = CSVtable.rows[r].cells[c].textContent;
  2564.                 }
  2565.                 if (c == notesColumn) {
  2566.                     notesCSV = CSVtable.rows[r].cells[c].textContent;
  2567.                 }
  2568.             }
  2569.  
  2570.             urlCSV = urlCSV.toLowerCase();
  2571.  
  2572.             // validate URL, must have protocol like http(s)                       
  2573.             if (urlCSV != ''
  2574.                 && urlCSV.substring(0, 7).toLowerCase() != 'http://'
  2575.                 && urlCSV.substring(0, 8).toLowerCase() != 'https://')
  2576.             {
  2577.                 if (isUrl(urlCSV)) {
  2578.                     // valid ULR, so add http
  2579.                     urlCSV = 'http://' + urlCSV;
  2580.                     // now check if valid
  2581.                     if (!isUrl(urlCSV)) {
  2582.                         OCdialogs.alert(t('passwords', 'This is not a valid URL, so this value will not be saved:') + ' ' + urlCSV, t('passwords', 'Import CSV File'), function() { return false; }, true);
  2583.                         urlCSV = '';
  2584.                     }
  2585.                 } else {
  2586.                     OCdialogs.alert(t('passwords', 'This is not a valid URL, so this value will not be saved:') + ' ' + urlCSV, t('passwords', 'Import CSV File'), function() { return false; }, true);
  2587.                     urlCSV = '';
  2588.                 }
  2589.             }
  2590.  
  2591.             passarray.push({
  2592.                 'website': websiteCSV,
  2593.                 'pass': passwordCSV,
  2594.                 'loginname': loginCSV,
  2595.                 'address': urlCSV,
  2596.                 'category': 0,
  2597.                 'notes': notesCSV,
  2598.                 'deleted': '0'
  2599.             });
  2600.         }
  2601.     }
  2602.     $('#CSVtableDIV').hide();
  2603.     $('#CSVprogressDIV').show();
  2604.     $('#CSVprogressActive').val(0);
  2605.     $('#CSVprogressTotal').val(passarray.length);
  2606.     importPassword(passarray);
  2607. }
  2608.  
  2609. function importPassword(array) {
  2610.     var password = array[0];
  2611.     if (array.length > 0) {
  2612.         array.shift();
  2613.         $('#CSVprogressActive').val($('#CSVprogressTotal').val() - array.length);
  2614.         var done = ($('#CSVprogressActive').val() / $('#CSVprogressTotal').val()) * 100;
  2615.         $('#CSVprogressDone').css('width', done + '%');
  2616.         $('#CSVprogressText1').text(password.website);
  2617.         $('#CSVprogressText2').text($('#CSVprogressActive').val() + ' / ' + $('#CSVprogressTotal').val() + ' (' + Math.round(done) + '%)');
  2618.         var success = $.ajax({
  2619.             url: OC.generateUrl('/apps/passwords/passwords'),
  2620.             method: 'POST',
  2621.             contentType: 'application/json',
  2622.             data: JSON.stringify(password),
  2623.             success: function(data) {
  2624.                 if (array.length == 0) {
  2625.                     setTimeout(function() {
  2626.                         alert(t('passwords', 'Import of passwords done. This page will now reload.'));
  2627.                         location.reload(true);
  2628.                     }, 500);
  2629.                 }
  2630.                 importPassword(array);
  2631.             },
  2632.             error: function(data) {
  2633.                 setTimeout(function() {
  2634.                     alert(t('passwords', "Error: The password of website '%s' cannot be imported. However, the import progress will continue.").replace('%s', password.website));
  2635.                 }, 500);
  2636.             }
  2637.         });
  2638.     }
  2639. }
  2640.  
  2641. function InvalidCSV(error_description) {
  2642.     if ($('#CSVerrorfield').text().indexOf(error_description) == -1) {
  2643.         $('#CSVerrorfield').append('<p>' + error_description + '</p>')
  2644.     }
  2645. }
  2646.  
  2647. function popUp(title, value, type, address_value, website, username, sharedby) {
  2648.     var ShareUsersAvailable = ($('#ShareUsers').html() != '');
  2649.     $('#popup').html('');
  2650.     $('#overlay').remove();
  2651.     $('#popup').remove();
  2652.     $('<div/>', {id: 'overlay'}).appendTo($('#app'));
  2653.     $('#overlay').delay(200).css('opacity', 0.6);
  2654.     $('<div/>', {id: 'popup'}).appendTo($('#app'));
  2655.     $('<div/>', {id: 'popupTitle'}).appendTo($('#popup'));
  2656.     $('<span/>', {text:website}).appendTo($('#popupTitle'));
  2657.     $('<br/>').appendTo($('#popupTitle'));
  2658.     $('<span/>', {text:t('passwords', 'Login name') + ': ' + username, id:"popupSubTitle"}).appendTo($('#popupTitle'));
  2659.  
  2660.     $('<div/>', {id: 'popupContent'}).appendTo($('#popup'));
  2661.     if (type == 'share') {
  2662.         if (ShareUsersAvailable) {
  2663.             $('<p/>', {text:t('passwords', 'Choose one or more users and press Share.')}).appendTo($('#popupContent'));
  2664.         }
  2665.     } else {
  2666.         if (!sharedby) {
  2667.             $('<p/>', {text:t('passwords', 'Enter a new value and press Save to keep the new value.\nThis cannot be undone.')}).appendTo($('#popupContent'));
  2668.         }
  2669.         $('<br/>').appendTo($('#popupContent'));
  2670.         $('<p/>', {text:title + ':'}).appendTo($('#popupContent'));
  2671.     }
  2672.     if (type == 'notes') {
  2673.         $('<textarea/>', {id:"new_value_popup", rows:"5"}).val(value).appendTo($('#popupContent'));
  2674.         // allow tabs in textareas (notes)
  2675.         $("textarea").keydown(function(e) {
  2676.             var $this, end, start;
  2677.             if (e.keyCode === 9) {
  2678.                 start = this.selectionStart;
  2679.                 end = this.selectionEnd;
  2680.                 $this = $(this);
  2681.                 $this.val($this.val().substring(0, start) + "\t" + $this.val().substring(end));
  2682.                 this.selectionStart = this.selectionEnd = start + 1;
  2683.                 return false;
  2684.             }
  2685.         });
  2686.  
  2687.     } else if (type == 'category') {
  2688.         $('#popupContent').append('<div id="new_value_popup">' + $('#new_category').html() + '</div>');
  2689.         if ($('#new_category').html().indexOf(t('passwords', 'No categories')) == -1) {
  2690.             $('#new_value_popup select option').last().remove(); // no Edit categories
  2691.             $('#new_value_popup select').val(value);
  2692.         }
  2693.         $('#popupContent').append('<button id="editCategoriespopup">' + t('passwords', 'Edit categories') + '</button>');
  2694.         $('#editCategoriespopup').click(function() {
  2695.             removePopup();
  2696.             $('#app-settings-content').hide(200);
  2697.             $('#sidebarClose').click();
  2698.             $('#section_table').hide(200);
  2699.             $('#section_categories').show(400);
  2700.         });
  2701.  
  2702.     } else if (type == 'share') {
  2703.         if (!ShareUsersAvailable) {
  2704.             $('<p/>', {text:t('passwords', 'There are no users available you can share with.') + '\n' + t('passwords', 'LDAP is unsupported in this version.')}).appendTo($('#popupContent'));
  2705.         } else {
  2706.             $('#popupContent').append('<div class="share_scroll"><div id="new_value_popup">' + $('#ShareUsers').html() + '</div></div>');
  2707.             if (typeof value != 'undefined') {
  2708.                 var sharedusers = value.split(',');
  2709.                 $.each(sharedusers, function(index, value2) {
  2710.                     $('#new_value_popup input[value=' + value2 + ']').attr('checked', true);
  2711.                 });
  2712.             }
  2713.         }
  2714.  
  2715.     } else {
  2716.         $('<input/>', {type:'text', id:"new_value_popup", autocorrect:'off', autocapitalize:'off', spellcheck:'false'}).val(value).appendTo($('#popupContent'));
  2717.         if (type == 'password') {
  2718.             $('#new_value_popup').addClass('password_field');
  2719.             $('<p id="generate_strength_popup"></p>').appendTo($('#popupContent'));
  2720.            
  2721.             $('<input>', {type:'checkbox', id:"gen_lower_popup"}).prop("checked", $('#gen_lower').is(":checked")).appendTo($('#popupContent'));
  2722.             $('<label/>', {for:'gen_lower_popup',text:t('passwords', 'Lowercase characters')}).appendTo($('#popupContent'));
  2723.             $('<br/>').appendTo($('#popupContent'));
  2724.            
  2725.             $('<input>', {type:'checkbox', id:"gen_upper_popup"}).prop("checked", $('#gen_upper').is(":checked")).appendTo($('#popupContent'));
  2726.             $('<label/>', {for:'gen_upper_popup',text:t('passwords', 'Uppercase characters')}).appendTo($('#popupContent'));
  2727.             $('<br/>').appendTo($('#popupContent'));
  2728.            
  2729.             $('<input>', {type:'checkbox', id:"gen_numbers_popup"}).prop("checked", $('#gen_numbers').is(":checked")).appendTo($('#popupContent'));
  2730.             $('<label/>', {for:'gen_numbers_popup',text:t('passwords', 'Numbers')}).appendTo($('#popupContent'));
  2731.             $('<br/>').appendTo($('#popupContent'));
  2732.            
  2733.             $('<input>', {type:'checkbox', id:"gen_special_popup"}).prop("checked", $('#gen_special').is(":checked")).appendTo($('#popupContent'));
  2734.             $('<label/>', {for:'gen_special_popup',text:t('passwords', 'Punctuation marks')}).appendTo($('#popupContent'));
  2735.             $('<br/>').appendTo($('#popupContent'));
  2736.            
  2737.             $('<input/>', {type:'text', id:"gen_length_popup", value:$('#gen_length').val()}).appendTo($('#popupContent'));
  2738.             $('<label/>', {text:t('passwords', 'characters')}).appendTo($('#popupContent'));
  2739.             $('<br/>').appendTo($('#popupContent'));
  2740.            
  2741.             $('<button/>', {id:'new_generate_popup', text:t('passwords', 'Generate password')}).appendTo($('#popupContent'));  
  2742.             $('<br/>').appendTo($('#popupContent'));
  2743.  
  2744.         } else if (type == 'website') {
  2745.             $('<br/><br/>').appendTo($('#popupContent'));
  2746.             $('<p/>', {text:t('passwords', 'Full URL (optional)') + ':'}).appendTo($('#popupContent'));
  2747.             $('<input/>', {type:'text', id:"new_address_popup", autocorrect:'off', autocapitalize:'off', spellcheck:'false'}).val(address_value).appendTo($('#popupContent'));
  2748.             $('<p/>', {id:"popupInvalid", text:t('passwords', 'Fill in a valid URL.') + ' ' + t('passwords', 'Note: This field is optional and can be left blank.')}).appendTo($('#popupContent'));
  2749.         }
  2750.  
  2751.         if (type == 'website' || type == 'loginname' || type == 'password') {
  2752.             $('<input>', {type:'checkbox', id:"keep_old_popup"}).prop("checked", 'true').appendTo($('#popupContent'));
  2753.             $('<label/>', {for:'keep_old_popup', id:"keep_old_popuplbl", text:t('passwords', 'Move old value to trash bin')}).appendTo($('#popupContent'));
  2754.         }
  2755.  
  2756.     }
  2757.  
  2758.     $('<div/>', {id: 'popupButtons'}).appendTo($('#popup'));   
  2759.     $('<button/>', {id:'cancel', text:t('passwords', 'Cancel')}).appendTo($('#popupButtons'));
  2760.     if (!sharedby) {
  2761.         if (type == 'share' && ShareUsersAvailable) {
  2762.             $('<button/>', {id:'accept', text:t('passwords', 'Share')}).appendTo($('#popupButtons'));
  2763.         } else {
  2764.             $('<button/>', {id:'accept', text:t('passwords', 'Save')}).appendTo($('#popupButtons'));
  2765.         }
  2766.     }
  2767.  
  2768.     // Popup
  2769.     $('#overlay').click(function() {
  2770.         removePopup();
  2771.     });
  2772.     $('#cancel').click(function() {
  2773.         removePopup();
  2774.     });
  2775.     if (type == 'password') {
  2776.         strength_str($("#new_value_popup").val(), false);
  2777.         $('#generate_strength').text('');
  2778.         $('#generate_passwordtools').hide();
  2779.  
  2780.         $("#new_value_popup").keyup(function() {
  2781.             strength_str(this.value, false);
  2782.             $('#generate_strength').text('');
  2783.         });
  2784.         $('#gen_lower_popup').change(function() {
  2785.             $('#gen_lower').prop("checked", $('#gen_lower_popup').is(":checked"));
  2786.         });
  2787.         $('#gen_upper_popup').change(function() {
  2788.             $('#gen_upper').prop("checked", $('#gen_upper_popup').is(":checked"));
  2789.         });
  2790.         $('#gen_numbers_popup').change(function() {
  2791.             $('#gen_numbers').prop("checked", $('#gen_numbers_popup').is(":checked"));
  2792.         });
  2793.         $('#gen_special_popup').change(function() {
  2794.             $('#gen_special').prop("checked", $('#gen_special_popup').is(":checked"));
  2795.         });
  2796.         $('#gen_length_popup').change(function() {
  2797.             $('#gen_length').val($('#gen_length_popup').val());
  2798.         });
  2799.         $('#new_generate_popup').click(function() {
  2800.             $('#new_generate').click();
  2801.         });
  2802.     }
  2803.  
  2804.     // no focus, too annoying for iPad and iPhone
  2805.     // $('#new_value_popup').focus();
  2806.     // $('#new_value_popup').select();
  2807.  
  2808.     // align to vertical center
  2809.     var popupHeight = document.getElementById('popup').clientHeight;
  2810.     var browserHeight = Math.max(
  2811.         document.body.scrollHeight, document.documentElement.scrollHeight,
  2812.         document.body.offsetHeight, document.documentElement.offsetHeight,
  2813.         document.body.clientHeight, document.documentElement.clientHeight
  2814.     );
  2815.     if (browserHeight > popupHeight) {
  2816.         document.getElementById("popup").style.top = (browserHeight - popupHeight) / 4 + "px"; 
  2817.     }
  2818.  
  2819.     $('#popupTitle').click(); // for deactivating the active row
  2820. }
  2821. function removePopup() {
  2822.     $('#overlay').css('opacity', 0);
  2823.     $('#popup').hide(200);
  2824.     $('#popup').css('top', '0');
  2825.     setTimeout(function() {
  2826.         $('#overlay').remove();
  2827.         $('#popup').remove();
  2828.     }, 300);
  2829. }
  2830. function trashAllPasswords(Passwords) {
  2831.  
  2832.     var passwords = new Passwords(OC.generateUrl('/apps/passwords/passwords'));
  2833.     var doneTotal = 0;
  2834.  
  2835.     $('#PasswordsTableContent tbody tr').each(function() {
  2836.         var $row = $(this);
  2837.         var $cell = $row.closest('td.icon-info');
  2838.         if (!$row.hasClass('is_deleted')) {
  2839.             // // no sharedwith, so a share will be stopped when the owner deletes the password
  2840.             var success = passwords.updateActive($row.attr('attr_id'), $row.attr('attr_loginname'), $row.attr('attr_website'), $row.attr('attr_address'), $row.attr('attr_pass'), $row.attr('attr_notes'), '', $row.attr('attr_category'), '1');
  2841.             if (success) {
  2842.                 doneTotal++;
  2843.                 $row.attr('class', 'is_deleted');
  2844.                 $cell.removeClass('icon-info');
  2845.                 $cell.addClass('icon-history');
  2846.                 $row.hide();
  2847.                 formatTable(true);
  2848.             } else {
  2849.                 OCdialogs.alert(t('passwords', 'Error: Could not update password.'), t('passwords', 'Save'), function() { return false; }, true);
  2850.             }
  2851.         }
  2852.     });
  2853.  
  2854.     if (doneTotal > 0) {
  2855.         OCdialogs.info(t('passwords', 'All passwords were moved to the trash bin.'), t('passwords', 'Trash bin'), function() { return false; }, true);
  2856.     } else {
  2857.         OCdialogs.info(t('passwords', 'There are no passwords to be moved.'), t('passwords', 'Trash bin'), function() { return false; }, true);
  2858.     }
  2859. }
  2860. function showSidebar($row) {
  2861.    
  2862.     $('#app-content-wrapper').attr('class', 'content-wrapper-sidebar');
  2863.  
  2864.     // set top relatively to header (adaptive to future header changes by core team, or for skin users)
  2865.     document.getElementById('app-sidebar-wrapper').style.top = document.getElementById('header').clientHeight + 35 + 'px';
  2866.  
  2867.     $('#app-sidebar-wrapper').show(200);
  2868.     $('#sidebarRow').val($row.attr('attr_id'));
  2869.  
  2870.     $('#sidebarWebsite').text($row.attr('attr_website'));
  2871.     $('#sidebarAddress').text($row.attr('attr_address'));
  2872.     if ($('#sidebarAddress').text() == '') {
  2873.         $('#sidebarAddress').text('(' + t('passwords', 'None') + ')');
  2874.     }
  2875.     $('#sidebarUsername').text($row.attr('attr_loginname'));
  2876.  
  2877.     $('#sidebarLength').text($row.attr('attr_length'));
  2878.  
  2879.     $('#sidebarStrength').text($row.attr('attr_strength') + ' (' + strength_func($row.attr('attr_pass')) + ')');
  2880.  
  2881.     $('#sidebarStrength').attr('class', strength_str2class($row.attr('attr_strength')));
  2882.     $('#sidebarStrength').addClass('rightCol');
  2883.  
  2884.     var lower = bool2class_str($row.attr('attr_lower'));
  2885.     $('#sidebarLower').attr('class', lower[0]);
  2886.     $('#sidebarLower').text(lower[1]);
  2887.     $('#sidebarLower').addClass('rightCol');
  2888.  
  2889.     var upper = bool2class_str($row.attr('attr_upper'));
  2890.     $('#sidebarUpper').attr('class', upper[0]);
  2891.     $('#sidebarUpper').text(upper[1]);
  2892.     $('#sidebarUpper').addClass('rightCol');
  2893.  
  2894.     var number = bool2class_str($row.attr('attr_number'));
  2895.     $('#sidebarNumber').attr('class', number[0]);
  2896.     $('#sidebarNumber').text(number[1]);
  2897.     $('#sidebarNumber').addClass('rightCol');
  2898.  
  2899.     var special = bool2class_str($row.attr('attr_special'));
  2900.     $('#sidebarSpecial').attr('class', special[0]);
  2901.     $('#sidebarSpecial').text(special[1]);
  2902.     $('#sidebarSpecial').addClass('rightCol');
  2903.  
  2904.     var d = new Date($row.attr('attr_datechanged'));
  2905.     $('#sidebarChanged').html(date2str(d, false) + ' (' + date2str(d, true) + ')');
  2906.     $('#sidebarChanged').attr('class', date2class(d));
  2907.     $('#sidebarChanged').addClass('rightCol');
  2908.  
  2909.     $('#sidebarNotes').text($row.attr('attr_notes'));
  2910.     if ($('#sidebarNotes').text() == '') {
  2911.         $('#sidebarNotes').text('(' + t('passwords', 'None') + ')');
  2912.     }
  2913.  
  2914.     var cat_attribute = $row.attr('attr_category');
  2915.     $('#sidebarCategories').text('');
  2916.     $('#CategoriesTableContent tr').each(function() {
  2917.         var cat_id = $(this).find('.catTable_id').text();
  2918.         if (cat_id == cat_attribute) {
  2919.             var cat_name = $(this).find('.catTable_name').text();
  2920.             $('#sidebarCategories').text(cat_name);
  2921.         }
  2922.     });
  2923.     if ($('#sidebarCategories').text() == '') {
  2924.         $('#sidebarCategories').text('(' + t('passwords', 'None') + ')');
  2925.     }
  2926.  
  2927. }
  2928. function resetTimer(kill_old) {
  2929.  
  2930.     if ($('#app-settings').attr('timer') == 0) {
  2931.         return false;
  2932.     }
  2933.  
  2934.     var settimer = $('#app-settings').attr('timer');
  2935.     var session_timeout = $('#app-settings').attr('session-timeout');
  2936.  
  2937.     $('#idleTimer').show(500);
  2938.     $('#outerRing').show(500);
  2939.  
  2940.     $('#countSec').text(int2time(settimer, false));
  2941.  
  2942.     if (settimer < 61) {
  2943.         $('#countSec').css('font-size', '30px');
  2944.     } else {
  2945.         $('#countSec').css('font-size', '16px');
  2946.     }
  2947.  
  2948.     if (kill_old) {
  2949.         clearInterval(intervalID);
  2950.     }
  2951.  
  2952.     intervalID = setInterval(function() {
  2953.         settimer = settimer - 1;
  2954.         session_timeout = session_timeout - 1;
  2955.  
  2956.         $('#countSec').text(int2time(settimer, false));
  2957.         $('#session_lifetime').text(session_timeout);
  2958.  
  2959.         if (settimer < 61) {
  2960.             $('#countSec').css('font-size', '30px');
  2961.         } else {
  2962.             $('#countSec').css('font-size', '16px');
  2963.         }
  2964.         // kill on 0, 'click' on logoff entry in top right menu
  2965.         if (settimer == 0 || session_timeout == 0) {
  2966.             $('#PasswordsTable table td').hide();
  2967.         }
  2968.         if (settimer <= 0) {
  2969.             clearInterval(intervalID);
  2970.             alert(t('passwords', 'You will be logged off due to inactivity of %s seconds.').replace('%s', $('#app-settings').attr('timer'))
  2971.                     + '\n\n'
  2972.                     + t('passwords', "You can change the timer settings in the '%s' menu.").replace('%s', t('core', 'Personal'))
  2973.                 );
  2974.             window.location = document.getElementById('logout').href;
  2975.         }
  2976.         if (session_timeout <= 0) {
  2977.             alert(t('passwords', 'You will be logged off due to expiration of your session cookie (set to %s minutes).').replace('%s', int2time($('#app-settings').attr('session-timeout'), true)));
  2978.             window.location = document.getElementById('logout').href;
  2979.         }
  2980.     }, 1000);
  2981.  
  2982. }
  2983. function int2time(integer, always_as_minutes) {
  2984.     if (typeof integer !== 'undefined') {
  2985.         if (integer < 61 && !always_as_minutes) {
  2986.             return integer;
  2987.         } else {
  2988.             return new Date(null, null, null, null, null, integer).toTimeString().match(/\d{2}:\d{2}:\d{2}/)[0].substr(3, 5);
  2989.         }
  2990.     }
  2991. }
  2992. function getContrastYIQ(hexcolor) {
  2993.     // adapted from https://24ways.org/2010/calculating-color-contrast
  2994.     // this great function converts RGB into YIQ first: https://en.wikipedia.org/wiki/YIQ
  2995.     if (hexcolor.length == 3) {
  2996.         // convert #eee to #eeeeee
  2997.         hexcolor = hexcolor.substr(0, 1) + hexcolor.substr(0, 1)
  2998.             + hexcolor.substr(1, 1) + hexcolor.substr(1, 1)
  2999.             + hexcolor.substr(2, 1) + hexcolor.substr(2, 1);
  3000.     }
  3001.     var r = parseInt(hexcolor.substr(0, 2), 16);
  3002.     var g = parseInt(hexcolor.substr(2, 2), 16);
  3003.     var b = parseInt(hexcolor.substr(4, 2), 16);
  3004.     var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
  3005.     return (yiq >= 128) ? '000000' : 'ffffff';
  3006. }
  3007.  
  3008. // the following functions are needed to update the
  3009. // database for users with version older than v17
  3010. // (encrypt loginname, address and password properties)
  3011. function updateRequired() {
  3012.     $('#section_update').show();
  3013.     $('#section_table').hide();
  3014.     $('#section_categories').hide();
  3015. }
  3016. function updateStart(passwords) {
  3017.  
  3018.     $('#update_start_btn').hide();
  3019.     $('#update_progress').show();
  3020.  
  3021.     var table = document.getElementById('PasswordsTableTestOld');
  3022.     // initiate progress bar
  3023.     $('#update_progress_active').val(0);
  3024.     $('#update_progress_total').val(table.rows.length);
  3025.     var done = ($('#update_progress_active').val() / $('#update_progress_total').val()) * 100;
  3026.     $('#update_progress_done').css('width', done + '%');
  3027.     $('#update_progress_text').text($('#update_progress_active').val() + ' / ' + $('#update_progress_total').val() + ' (' + Math.round(done) + '%)')
  3028.  
  3029.     var all_success = true;
  3030.  
  3031.     for (var i = 0; i < table.rows.length; i++) {
  3032.  
  3033.         $('#update_progress_active').val(i + 1);
  3034.         done = ($('#update_progress_active').val() / $('#update_progress_total').val()) * 100;
  3035.         $('#update_progress_done').css('width', done + '%');
  3036.         $('#update_progress_text').text($('#update_progress_active').val() + ' / ' + $('#update_progress_total').val() + ' (' + Math.round(done) + '%)')
  3037.  
  3038.         var website = table.rows[i].cells[0].textContent;
  3039.         var loginname = table.rows[i].cells[1].textContent;
  3040.         var pass = table.rows[i].cells[2].textContent;
  3041.         var creation_date = table.rows[i].cells[4].textContent;
  3042.         var db_id = table.rows[i].cells[5].textContent;
  3043.         var address = table.rows[i].cells[7].textContent;
  3044.         var notes = table.rows[i].cells[8].textContent;
  3045.         var deleted = table.rows[i].cells[9].textContent;
  3046.  
  3047.         if (loginname != '') {
  3048.             var success = passwords.updateActive(db_id, loginname, website, address, pass, notes, '', 0, deleted, creation_date);
  3049.             if (!success) {
  3050.                 all_success = false;
  3051.             }
  3052.         }
  3053.     }
  3054.  
  3055.     setInterval(function() {
  3056.         if (all_success && done == 100) {
  3057.             $('#update_progress').hide();
  3058.             $('#update_done').show();
  3059.             $('#update_done_btn').click(function() {
  3060.                 location.reload(true);
  3061.             });
  3062.         } else if (done == 100) {
  3063.             alert(t('passwords', 'Error: Could not update password.'));
  3064.         }
  3065.     }, 1000);
  3066.  
  3067. }
  3068. function updateDone() {
  3069.     $('#section_update').hide();
  3070.     $('#section_table').show();
  3071.     $('#section_categories').show();
  3072. }
  3073. function isFlashEnabled() {
  3074.     var hasFlash = false;
  3075.     try {
  3076.         var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
  3077.         if (fo) {
  3078.             hasFlash = true;
  3079.         }
  3080.     }
  3081.     catch (e) {
  3082.         if (navigator.mimeTypes ["application/x-shockwave-flash"] != undefined) {
  3083.             hasFlash = true
  3084.         }
  3085.     }
  3086.     return hasFlash;
  3087. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement