config.js 24 KB

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