config.js 27 KB

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