Translated using Weblate (Turkish)
[phpmyadmin.git] / js / config.js
blob266bfcbd2ade7a7405b04b899a01a64f87048be9
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
2 /**
3  * Functions used in configuration forms and on user preferences pages
4  */
6 var configInlineParams;
7 var configScriptLoaded;
9 /**
10  * checks whether browser supports web storage
11  *
12  * @param type the type of storage i.e. localStorage or sessionStorage
13  *
14  * @returns bool
15  */
16 function isStorageSupported (type, warn) {
17     try {
18         window[type].setItem('PMATest', 'test');
19         // Check whether key-value pair was set successfully
20         if (window[type].getItem('PMATest') === 'test') {
21             // Supported, remove test variable from storage
22             window[type].removeItem('PMATest');
23             return true;
24         }
25     } catch (error) {
26         // Not supported
27         if (warn) {
28             Functions.ajaxShowMessage(Messages.strNoLocalStorage, false);
29         }
30     }
31     return false;
34 /**
35  * Unbind all event handlers before tearing down a page
36  */
37 AJAX.registerTeardown('config.js', function () {
38     $('.optbox input[id], .optbox select[id], .optbox textarea[id]').off('change').off('keyup');
39     $('.optbox input[type=button][name=submit_reset]').off('click');
40     $('div.tabs_contents').off();
41     $('#import_local_storage, #export_local_storage').off('click');
42     $('form.prefs-form').off('change').off('submit');
43     $(document).off('click', 'div.click-hide-message');
44     $('#prefs_autoload').find('a').off('click');
45 });
47 AJAX.registerOnload('config.js', function () {
48     var $topmenuUpt = $('#topmenu2.user_prefs_tabs');
49     $topmenuUpt.find('li.active a').attr('rel', 'samepage');
50     $topmenuUpt.find('li:not(.active) a').attr('rel', 'newpage');
51 });
53 // default values for fields
54 var defaultValues = {};
56 /**
57  * Returns field type
58  *
59  * @param {Element} field
60  */
61 function getFieldType (field) {
62     var $field = $(field);
63     var tagName = $field.prop('tagName');
64     if (tagName === 'INPUT') {
65         return $field.attr('type');
66     } else if (tagName === 'SELECT') {
67         return 'select';
68     } else if (tagName === 'TEXTAREA') {
69         return 'text';
70     }
71     return '';
74 /**
75  * Enables or disables the "restore default value" button
76  *
77  * @param {Element} field
78  * @param {boolean} display
79  */
80 function setRestoreDefaultBtn (field, display) {
81     var $el = $(field).closest('td').find('.restore-default img');
82     $el[display ? 'show' : 'hide']();
85 /**
86  * Marks field depending on its value (system default or custom)
87  *
88  * @param {Element} field
89  */
90 function markField (field) {
91     var $field = $(field);
92     var type = getFieldType($field);
93     var isDefault = checkFieldDefault($field, type);
95     // checkboxes uses parent <span> for marking
96     var $fieldMarker = (type === 'checkbox') ? $field.parent() : $field;
97     setRestoreDefaultBtn($field, !isDefault);
98     $fieldMarker[isDefault ? 'removeClass' : 'addClass']('custom');
102  * Sets field value
104  * value must be of type:
105  * o undefined (omitted) - restore default value (form default, not PMA default)
106  * o String - if field_type is 'text'
107  * o boolean - if field_type is 'checkbox'
108  * o Array of values - if field_type is 'select'
110  * @param {Element} field
111  * @param {String}  fieldType  see {@link #getFieldType}
112  * @param {String|Boolean}  value
113  */
114 function setFieldValue (field, fieldType, value) {
115     var $field = $(field);
116     switch (fieldType) {
117     case 'text':
118     case 'number':
119         $field.val(value);
120         break;
121     case 'checkbox':
122         $field.prop('checked', value);
123         break;
124     case 'select':
125         var options = $field.prop('options');
126         var i;
127         var imax = options.length;
128         for (i = 0; i < imax; i++) {
129             options[i].selected = (value.indexOf(options[i].value) !== -1);
130         }
131         break;
132     }
133     markField($field);
137  * Gets field value
139  * Will return one of:
140  * o String - if type is 'text'
141  * o boolean - if type is 'checkbox'
142  * o Array of values - if type is 'select'
144  * @param {Element} field
145  * @param {String}  fieldType returned by {@link #getFieldType}
146  * @type Boolean|String|String[]
147  */
148 function getFieldValue (field, fieldType) {
149     var $field = $(field);
150     switch (fieldType) {
151     case 'text':
152     case 'number':
153         return $field.prop('value');
154     case 'checkbox':
155         return $field.prop('checked');
156     case 'select':
157         var options = $field.prop('options');
158         var i;
159         var imax = options.length;
160         var items = [];
161         for (i = 0; i < imax; i++) {
162             if (options[i].selected) {
163                 items.push(options[i].value);
164             }
165         }
166         return items;
167     }
168     return null;
172  * Returns values for all fields in fieldsets
173  */
174 // eslint-disable-next-line no-unused-vars
175 function getAllValues () {
176     var $elements = $('fieldset input, fieldset select, fieldset textarea');
177     var values = {};
178     var type;
179     var value;
180     for (var i = 0; i < $elements.length; i++) {
181         type = getFieldType($elements[i]);
182         value = getFieldValue($elements[i], type);
183         if (typeof value !== 'undefined') {
184             // we only have single selects, fatten array
185             if (type === 'select') {
186                 value = value[0];
187             }
188             values[$elements[i].name] = value;
189         }
190     }
191     return values;
195  * Checks whether field has its default value
197  * @param {Element} field
198  * @param {String}  type
199  * @return boolean
200  */
201 function checkFieldDefault (field, type) {
202     var $field = $(field);
203     var fieldId = $field.attr('id');
204     if (typeof defaultValues[fieldId] === 'undefined') {
205         return true;
206     }
207     var isDefault = true;
208     var currentValue = getFieldValue($field, type);
209     if (type !== 'select') {
210         isDefault = currentValue === defaultValues[fieldId];
211     } else {
212         // compare arrays, will work for our representation of select values
213         if (currentValue.length !== defaultValues[fieldId].length) {
214             isDefault = false;
215         } else {
216             for (var i = 0; i < currentValue.length; i++) {
217                 if (currentValue[i] !== defaultValues[fieldId][i]) {
218                     isDefault = false;
219                     break;
220                 }
221             }
222         }
223     }
224     return isDefault;
228  * Returns element's id prefix
229  * @param {Element} element
230  */
231 // eslint-disable-next-line no-unused-vars
232 function getIdPrefix (element) {
233     return $(element).attr('id').replace(/[^-]+$/, '');
236 // ------------------------------------------------------------------
237 // Form validation and field operations
240 // form validator assignments
241 var validate = {};
243 // form validator list
244 var validators = {
245     // regexp: numeric value
246     regExpNumeric: /^[0-9]+$/,
247     // regexp: extract parts from PCRE expression
248     regExpPcreExtract: /(.)(.*)\1(.*)?/,
249     /**
250      * Validates positive number
251      *
252      * @param {boolean} isKeyUp
253      */
254     validatePositiveNumber: function (isKeyUp) {
255         if (isKeyUp && this.value === '') {
256             return true;
257         }
258         var result = this.value !== '0' && validators.regExpNumeric.test(this.value);
259         return result ? true : Messages.error_nan_p;
260     },
261     /**
262      * Validates non-negative number
263      *
264      * @param {boolean} isKeyUp
265      */
266     validateNonNegativeNumber: function (isKeyUp) {
267         if (isKeyUp && this.value === '') {
268             return true;
269         }
270         var result = validators.regExpNumeric.test(this.value);
271         return result ? true : Messages.error_nan_nneg;
272     },
273     /**
274      * Validates port number
275      */
276     validatePortNumber: function () {
277         if (this.value === '') {
278             return true;
279         }
280         var result = validators.regExpNumeric.test(this.value) && this.value !== '0';
281         return result && this.value <= 65535 ? true : Messages.error_incorrect_port;
282     },
283     /**
284      * Validates value according to given regular expression
285      *
286      * @param {boolean} isKeyUp
287      * @param {string}  regexp
288      */
289     validateByRegex: function (isKeyUp, regexp) {
290         if (isKeyUp && this.value === '') {
291             return true;
292         }
293         // convert PCRE regexp
294         var parts = regexp.match(validators.regExpPcreExtract);
295         var valid = this.value.match(new RegExp(parts[2], parts[3])) !== null;
296         return valid ? true : Messages.error_invalid_value;
297     },
298     /**
299      * Validates upper bound for numeric inputs
300      *
301      * @param {boolean} isKeyUp
302      * @param {int} maxValue
303      */
304     validateUpperBound: function (isKeyUp, maxValue) {
305         var val = parseInt(this.value, 10);
306         if (isNaN(val)) {
307             return true;
308         }
309         return val <= maxValue ? true : Functions.sprintf(Messages.error_value_lte, maxValue);
310     },
311     // field validators
312     field: {
313     },
314     // fieldset validators
315     fieldset: {
316     }
320  * Registers validator for given field
322  * @param {String}  id       field id
323  * @param {String}  type     validator (key in validators object)
324  * @param {boolean} onKeyUp  whether fire on key up
325  * @param {Array}   params   validation function parameters
326  */
327 // eslint-disable-next-line no-unused-vars
328 function registerFieldValidator (id, type, onKeyUp, params) {
329     if (typeof validators[type] === 'undefined') {
330         return;
331     }
332     if (typeof validate[id] === 'undefined') {
333         validate[id] = [];
334     }
335     if (validate[id].length === 0) {
336         validate[id].push([type, params, onKeyUp]);
337     }
341  * Returns validation functions associated with form field
343  * @param {String}  fieldId     form field id
344  * @param {boolean} onKeyUpOnly  see registerFieldValidator
345  * @type Array
346  * @return array of [function, parameters to be passed to function]
347  */
348 function getFieldValidators (fieldId, onKeyUpOnly) {
349     // look for field bound validator
350     var name = fieldId && fieldId.match(/[^-]+$/)[0];
351     if (typeof validators.field[name] !== 'undefined') {
352         return [[validators.field[name], null]];
353     }
355     // look for registered validators
356     var functions = [];
357     if (typeof validate[fieldId] !== 'undefined') {
358         // validate[field_id]: array of [type, params, onKeyUp]
359         for (var i = 0, imax = validate[fieldId].length; i < imax; i++) {
360             if (onKeyUpOnly && !validate[fieldId][i][2]) {
361                 continue;
362             }
363             functions.push([validators[validate[fieldId][i][0]], validate[fieldId][i][1]]);
364         }
365     }
367     return functions;
371  * Displays errors for given form fields
373  * WARNING: created DOM elements must be identical with the ones made by
374  * PhpMyAdmin\Config\FormDisplayTemplate::displayInput()!
376  * @param {Object} errorList list of errors in the form {field id: error array}
377  */
378 function displayErrors (errorList) {
379     var tempIsEmpty = function (item) {
380         return item !== '';
381     };
383     for (var fieldId in errorList) {
384         var errors = errorList[fieldId];
385         var $field = $('#' + fieldId);
386         var isFieldset = $field.attr('tagName') === 'FIELDSET';
387         var $errorCnt;
388         if (isFieldset) {
389             $errorCnt = $field.find('dl.errors');
390         } else {
391             $errorCnt = $field.siblings('.inline_errors');
392         }
394         // remove empty errors (used to clear error list)
395         errors = $.grep(errors, tempIsEmpty);
397         // CSS error class
398         if (!isFieldset) {
399             // checkboxes uses parent <span> for marking
400             var $fieldMarker = ($field.attr('type') === 'checkbox') ? $field.parent() : $field;
401             $fieldMarker[errors.length ? 'addClass' : 'removeClass']('field-error');
402         }
404         if (errors.length) {
405             // if error container doesn't exist, create it
406             if ($errorCnt.length === 0) {
407                 if (isFieldset) {
408                     $errorCnt = $('<dl class="errors"></dl>');
409                     $field.find('table').before($errorCnt);
410                 } else {
411                     $errorCnt = $('<dl class="inline_errors"></dl>');
412                     $field.closest('td').append($errorCnt);
413                 }
414             }
416             var html = '';
417             for (var i = 0, imax = errors.length; i < imax; i++) {
418                 html += '<dd>' + errors[i] + '</dd>';
419             }
420             $errorCnt.html(html);
421         } else if ($errorCnt !== null) {
422             // remove useless error container
423             $errorCnt.remove();
424         }
425     }
429  * Validates fields and fieldsets and call displayError function as required
430  */
431 function setDisplayError () {
432     var elements = $('.optbox input[id], .optbox select[id], .optbox textarea[id]');
433     // run all field validators
434     var errors = {};
435     for (var i = 0; i < elements.length; i++) {
436         validateField(elements[i], false, errors);
437     }
438     // run all fieldset validators
439     $('fieldset.optbox').each(function () {
440         validateFieldset(this, false, errors);
441     });
442     displayErrors(errors);
446  * Validates fieldset and puts errors in 'errors' object
448  * @param {Element} fieldset
449  * @param {boolean} isKeyUp
450  * @param {Object}  errors
451  */
452 function validateFieldset (fieldset, isKeyUp, errors) {
453     var $fieldset = $(fieldset);
454     if ($fieldset.length && typeof validators.fieldset[$fieldset.attr('id')] !== 'undefined') {
455         var fieldsetErrors = validators.fieldset[$fieldset.attr('id')].apply($fieldset[0], [isKeyUp]);
456         for (var fieldId in fieldsetErrors) {
457             if (typeof errors[fieldId] === 'undefined') {
458                 errors[fieldId] = [];
459             }
460             if (typeof fieldsetErrors[fieldId] === 'string') {
461                 fieldsetErrors[fieldId] = [fieldsetErrors[fieldId]];
462             }
463             $.merge(errors[fieldId], fieldsetErrors[fieldId]);
464         }
465     }
469  * Validates form field and puts errors in 'errors' object
471  * @param {Element} field
472  * @param {boolean} isKeyUp
473  * @param {Object}  errors
474  */
475 function validateField (field, isKeyUp, errors) {
476     var args;
477     var result;
478     var $field = $(field);
479     var fieldId = $field.attr('id');
480     errors[fieldId] = [];
481     var functions = getFieldValidators(fieldId, isKeyUp);
482     for (var i = 0; i < functions.length; i++) {
483         if (typeof functions[i][1] !== 'undefined' && functions[i][1] !== null) {
484             args = functions[i][1].slice(0);
485         } else {
486             args = [];
487         }
488         args.unshift(isKeyUp);
489         result = functions[i][0].apply($field[0], args);
490         if (result !== true) {
491             if (typeof result === 'string') {
492                 result = [result];
493             }
494             $.merge(errors[fieldId], result);
495         }
496     }
500  * Validates form field and parent fieldset
502  * @param {Element} field
503  * @param {boolean} isKeyUp
504  */
505 function validateFieldAndFieldset (field, isKeyUp) {
506     var $field = $(field);
507     var errors = {};
508     validateField($field, isKeyUp, errors);
509     validateFieldset($field.closest('fieldset.optbox'), isKeyUp, errors);
510     displayErrors(errors);
513 function loadInlineConfig () {
514     if (!Array.isArray(configInlineParams)) {
515         return;
516     }
517     for (var i = 0; i < configInlineParams.length; ++i) {
518         if (typeof configInlineParams[i] === 'function') {
519             configInlineParams[i]();
520         }
521     }
524 function setupValidation () {
525     validate = {};
526     configScriptLoaded = true;
527     if (configScriptLoaded && typeof configInlineParams !== 'undefined') {
528         loadInlineConfig();
529     }
530     // register validators and mark custom values
531     var $elements = $('.optbox input[id], .optbox select[id], .optbox textarea[id]');
532     $elements.each(function () {
533         markField(this);
534         var $el = $(this);
535         $el.on('change', function () {
536             validateFieldAndFieldset(this, false);
537             markField(this);
538         });
539         var tagName = $el.attr('tagName');
540         // text fields can be validated after each change
541         if (tagName === 'INPUT' && $el.attr('type') === 'text') {
542             $el.on('keyup', function () {
543                 validateFieldAndFieldset($el, true);
544                 markField($el);
545             });
546         }
547         // disable textarea spellcheck
548         if (tagName === 'TEXTAREA') {
549             $el.attr('spellcheck', false);
550         }
551     });
553     // check whether we've refreshed a page and browser remembered modified
554     // form values
555     var $checkPageRefresh = $('#check_page_refresh');
556     if ($checkPageRefresh.length === 0 || $checkPageRefresh.val() === '1') {
557         // run all field validators
558         var errors = {};
559         for (var i = 0; i < $elements.length; i++) {
560             validateField($elements[i], false, errors);
561         }
562         // run all fieldset validators
563         $('fieldset.optbox').each(function () {
564             validateFieldset(this, false, errors);
565         });
567         displayErrors(errors);
568     } else if ($checkPageRefresh) {
569         $checkPageRefresh.val('1');
570     }
573 AJAX.registerOnload('config.js', function () {
574     setupValidation();
578 // END: Form validation and field operations
579 // ------------------------------------------------------------------
581 // ------------------------------------------------------------------
582 // Tabbed forms
586  * Sets active tab
588  * @param {String} tabId
589  */
590 function setTab (tabId) {
591     $('ul.tabs').each(function () {
592         var $this = $(this);
593         if (!$this.find('li a[href="#' + tabId + '"]').length) {
594             return;
595         }
596         $this.find('li').removeClass('active').find('a[href="#' + tabId + '"]').parent().addClass('active');
597         $this.parent().find('div.tabs_contents fieldset').hide().filter('#' + tabId).show();
598         var hashValue = 'tab_' + tabId;
599         location.hash = hashValue;
600         $this.parent().find('input[name=tab_hash]').val(hashValue);
601     });
604 function setupConfigTabs () {
605     var forms = $('form.config-form');
606     forms.each(function () {
607         var $this = $(this);
608         var $tabs = $this.find('ul.tabs');
609         if (!$tabs.length) {
610             return;
611         }
612         // add tabs events and activate one tab (the first one or indicated by location hash)
613         $tabs.find('li').removeClass('active');
614         $tabs.find('a')
615             .on('click', function (e) {
616                 e.preventDefault();
617                 setTab($(this).attr('href').substr(1));
618             })
619             .filter(':first')
620             .parent()
621             .addClass('active');
622         $this.find('div.tabs_contents fieldset').hide().filter(':first').show();
623     });
626 function adjustPrefsNotification () {
627     var $prefsAutoLoad = $('#prefs_autoload');
628     var $tableNameControl = $('#table_name_col_no');
629     var $prefsAutoShowing = ($prefsAutoLoad.css('display') !== 'none');
631     if ($prefsAutoShowing && $tableNameControl.length) {
632         $tableNameControl.css('top', '55px');
633     }
636 AJAX.registerOnload('config.js', function () {
637     setupConfigTabs();
638     adjustPrefsNotification();
640     // tab links handling, check each 200ms
641     // (works with history in FF, further browser support here would be an overkill)
642     var prevHash;
643     var tabCheckFnc = function () {
644         if (location.hash !== prevHash) {
645             prevHash = location.hash;
646             if (prevHash.match(/^#tab_[a-zA-Z0-9_]+$/)) {
647                 // session ID is sometimes appended here
648                 var hash = prevHash.substr(5).split('&')[0];
649                 if ($('#' + hash).length) {
650                     setTab(hash);
651                 }
652             }
653         }
654     };
655     tabCheckFnc();
656     setInterval(tabCheckFnc, 200);
660 // END: Tabbed forms
661 // ------------------------------------------------------------------
663 // ------------------------------------------------------------------
664 // Form reset buttons
667 AJAX.registerOnload('config.js', function () {
668     $('.optbox input[type=button][name=submit_reset]').on('click', function () {
669         var fields = $(this).closest('fieldset').find('input, select, textarea');
670         for (var i = 0, imax = fields.length; i < imax; i++) {
671             setFieldValue(fields[i], getFieldType(fields[i]), defaultValues[fields[i].id]);
672         }
673         setDisplayError();
674     });
678 // END: Form reset buttons
679 // ------------------------------------------------------------------
681 // ------------------------------------------------------------------
682 // "Restore default" and "set value" buttons
686  * Restores field's default value
688  * @param {String} fieldId
689  */
690 function restoreField (fieldId) {
691     var $field = $('#' + fieldId);
692     if ($field.length === 0 || defaultValues[fieldId] === undefined) {
693         return;
694     }
695     setFieldValue($field, getFieldType($field), defaultValues[fieldId]);
698 function setupRestoreField () {
699     $('div.tabs_contents')
700         .on('mouseenter', '.restore-default, .set-value', function () {
701             $(this).css('opacity', 1);
702         })
703         .on('mouseleave', '.restore-default, .set-value', function () {
704             $(this).css('opacity', 0.25);
705         })
706         .on('click', '.restore-default, .set-value', function (e) {
707             e.preventDefault();
708             var href = $(this).attr('href');
709             var fieldSel;
710             if ($(this).hasClass('restore-default')) {
711                 fieldSel = href;
712                 restoreField(fieldSel.substr(1));
713             } else {
714                 fieldSel = href.match(/^[^=]+/)[0];
715                 var value = href.match(/=(.+)$/)[1];
716                 setFieldValue($(fieldSel), 'text', value);
717             }
718             $(fieldSel).trigger('change');
719         })
720         .find('.restore-default, .set-value')
721         // inline-block for IE so opacity inheritance works
722         .css({ display: 'inline-block', opacity: 0.25 });
725 AJAX.registerOnload('config.js', function () {
726     setupRestoreField();
730 // END: "Restore default" and "set value" buttons
731 // ------------------------------------------------------------------
733 // ------------------------------------------------------------------
734 // User preferences import/export
737 AJAX.registerOnload('config.js', function () {
738     offerPrefsAutoimport();
739     var $radios = $('#import_local_storage, #export_local_storage');
740     if (!$radios.length) {
741         return;
742     }
744     // enable JavaScript dependent fields
745     $radios
746         .prop('disabled', false)
747         .add('#export_text_file, #import_text_file')
748         .on('click', function () {
749             var enableId = $(this).attr('id');
750             var disableId;
751             if (enableId.match(/local_storage$/)) {
752                 disableId = enableId.replace(/local_storage$/, 'text_file');
753             } else {
754                 disableId = enableId.replace(/text_file$/, 'local_storage');
755             }
756             $('#opts_' + disableId).addClass('disabled').find('input').prop('disabled', true);
757             $('#opts_' + enableId).removeClass('disabled').find('input').prop('disabled', false);
758         });
760     // detect localStorage state
761     var lsSupported = isStorageSupported('localStorage', true);
762     var lsExists = lsSupported ? (window.localStorage.config || false) : false;
763     $('div.localStorage-' + (lsSupported ? 'un' : '') + 'supported').hide();
764     $('div.localStorage-' + (lsExists ? 'empty' : 'exists')).hide();
765     if (lsExists) {
766         updatePrefsDate();
767     }
768     $('form.prefs-form').on('change', function () {
769         var $form = $(this);
770         var disabled = false;
771         if (!lsSupported) {
772             disabled = $form.find('input[type=radio][value$=local_storage]').prop('checked');
773         } else if (!lsExists && $form.attr('name') === 'prefs_import' &&
774             $('#import_local_storage')[0].checked
775         ) {
776             disabled = true;
777         }
778         $form.find('input[type=submit]').prop('disabled', disabled);
779     }).submit(function (e) {
780         var $form = $(this);
781         if ($form.attr('name') === 'prefs_export' && $('#export_local_storage')[0].checked) {
782             e.preventDefault();
783             // use AJAX to read JSON settings and save them
784             savePrefsToLocalStorage($form);
785         } else if ($form.attr('name') === 'prefs_import' && $('#import_local_storage')[0].checked) {
786             // set 'json' input and submit form
787             $form.find('input[name=json]').val(window.localStorage.config);
788         }
789     });
791     $(document).on('click', 'div.click-hide-message', function () {
792         $(this)
793             .hide()
794             .parent('.group')
795             .css('height', '')
796             .next('form')
797             .show();
798     });
802  * Saves user preferences to localStorage
804  * @param {Element} form
805  */
806 function savePrefsToLocalStorage (form) {
807     var $form = $(form);
808     var submit = $form.find('input[type=submit]');
809     submit.prop('disabled', true);
810     $.ajax({
811         url: 'prefs_manage.php',
812         cache: false,
813         type: 'POST',
814         data: {
815             'ajax_request': true,
816             'server': CommonParams.get('server'),
817             'submit_get_json': true
818         },
819         success: function (data) {
820             if (typeof data !== 'undefined' && data.success === true) {
821                 window.localStorage.config = data.prefs;
822                 window.localStorage.configMtime = data.mtime;
823                 window.localStorage.configMtimeLocal = (new Date()).toUTCString();
824                 updatePrefsDate();
825                 $('div.localStorage-empty').hide();
826                 $('div.localStorage-exists').show();
827                 var group = $form.parent('.group');
828                 group.css('height', group.height() + 'px');
829                 $form.hide('fast');
830                 $form.prev('.click-hide-message').show('fast');
831             } else {
832                 Functions.ajaxShowMessage(data.error);
833             }
834         },
835         complete: function () {
836             submit.prop('disabled', false);
837         }
838     });
842  * Updates preferences timestamp in Import form
843  */
844 function updatePrefsDate () {
845     var d = new Date(window.localStorage.configMtimeLocal);
846     var msg = Messages.strSavedOn.replace(
847         '@DATE@',
848         Functions.formatDateTime(d)
849     );
850     $('#opts_import_local_storage').find('div.localStorage-exists').html(msg);
854  * Prepares message which informs that localStorage preferences are available and can be imported or deleted
855  */
856 function offerPrefsAutoimport () {
857     var hasConfig = (isStorageSupported('localStorage')) && (window.localStorage.config || false);
858     var $cnt = $('#prefs_autoload');
859     if (!$cnt.length || !hasConfig) {
860         return;
861     }
862     $cnt.find('a').on('click', function (e) {
863         e.preventDefault();
864         var $a = $(this);
865         if ($a.attr('href') === '#no') {
866             $cnt.remove();
867             $.post('index.php', {
868                 'server': CommonParams.get('server'),
869                 'prefs_autoload': 'hide'
870             }, null, 'html');
871             return;
872         } else if ($a.attr('href') === '#delete') {
873             $cnt.remove();
874             localStorage.clear();
875             $.post('index.php', {
876                 'server': CommonParams.get('server'),
877                 'prefs_autoload': 'hide'
878             }, null, 'html');
879             return;
880         }
881         $cnt.find('input[name=json]').val(window.localStorage.config);
882         $cnt.find('form').trigger('submit');
883     });
884     $cnt.show();