fork of GM_config

A lightweight, reusable, cross-browser graphical settings framework for inclusion in user scripts.

目前为 2022-12-01 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/455782/1123449/fork%20of%20GM_config.js

  1. /*
  2. Copyright 2009+, GM_config Contributors (https://github.com/sizzlemctwizzle/GM_config)
  3.  
  4. GM_config Collaborators/Contributors:
  5. Mike Medley <medleymind@gmail.com>
  6. Joe Simmons
  7. Izzy Soft
  8. Marti Martz
  9. Adam Thompson-Sharpe
  10.  
  11. GM_config is distributed under the terms of the GNU Lesser General Public License.
  12.  
  13. GM_config is free software: you can redistribute it and/or modify
  14. it under the terms of the GNU Lesser General Public License as published by
  15. the Free Software Foundation, either version 3 of the License, or
  16. (at your option) any later version.
  17.  
  18. This program is distributed in the hope that it will be useful,
  19. but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. GNU Lesser General Public License for more details.
  22.  
  23. You should have received a copy of the GNU Lesser General Public License
  24. along with this program. If not, see <https://www.gnu.org/licenses/>.
  25. */
  26.  
  27. // ==UserScript==
  28. // @exclude *
  29. // @author Mike Medley <medleymind@gmail.com> (https://github.com/sizzlemctwizzle/GM_config)
  30. // @icon https://raw.githubusercontent.com/sizzlemctwizzle/GM_config/master/gm_config_icon_large.png
  31.  
  32. // ==UserLibrary==
  33. // @name fork of GM_config
  34. // @description A lightweight, reusable, cross-browser graphical settings framework for inclusion in user scripts.
  35. // @copyright 2009+, Mike Medley (https://github.com/sizzlemctwizzle)
  36. // @license LGPL-3.0-or-later; https://raw.githubusercontent.com/sizzlemctwizzle/GM_config/master/LICENSE
  37.  
  38. // @homepageURL https://openuserjs.org/libs/sizzle/GM_config
  39. // @homepageURL https://github.com/sizzlemctwizzle/GM_config
  40. // @supportURL https://github.com/sizzlemctwizzle/GM_config/issues
  41.  
  42. // ==/UserScript==
  43.  
  44. // ==/UserLibrary==
  45.  
  46.  
  47. // The GM_config constructor
  48. function GM_configStruct() {
  49. // call init() if settings were passed to constructor
  50. if (arguments.length) {
  51. GM_configInit(this, arguments);
  52. this.onInit();
  53. }
  54. }
  55. // This is the initializer function
  56. function GM_configInit(config, args) {
  57. // Initialize instance variables
  58. if (typeof config.fields == "undefined") {
  59. config.fields = {};
  60. config.onInit = config.onInit || function() {};
  61. config.onOpen = config.onOpen || function() {};
  62. config.onSave = config.onSave || function() {};
  63. config.onClose = config.onClose || function() {};
  64. config.onReset = config.onReset || function() {};
  65. config.isOpen = false;
  66. config.title = 'User Script Settings';
  67. config.css = {
  68. basic: [
  69. "#GM_config * { font-family: arial,tahoma,myriad pro,sans-serif; }",
  70. "#GM_config { background: #FFF; }",
  71. "#GM_config input[type='radio'] { margin-right: 8px; }",
  72. "#GM_config .indent40 { margin-left: 40%; }",
  73. "#GM_config .field_label { font-size: 12px; font-weight: bold; margin-right: 6px; }",
  74. "#GM_config .radio_label { font-size: 12px; }",
  75. "#GM_config .block { display: block; }",
  76. "#GM_config .saveclose_buttons { margin: 16px 10px 10px; padding: 2px 12px; }",
  77. "#GM_config .reset, #GM_config .reset a," +
  78. " #GM_config_buttons_holder { color: #000; text-align: right; }",
  79. "#GM_config .config_header { font-size: 20pt; margin: 0; }",
  80. "#GM_config .config_desc, #GM_config .section_desc, #GM_config .reset { font-size: 9pt; }",
  81. "#GM_config .center { text-align: center; }",
  82. "#GM_config .section_header_holder { margin-top: 8px; }",
  83. "#GM_config .config_var { margin: 0 0 4px; }",
  84. "#GM_config .section_header { background: #414141; border: 1px solid #000; color: #FFF;",
  85. " font-size: 13pt; margin: 0; }",
  86. "#GM_config .section_desc { background: #EFEFEF; border: 1px solid #CCC; color: #575757;" +
  87. " font-size: 9pt; margin: 0 0 6px; }"
  88. ].join('\n') + '\n',
  89. basicPrefix: "GM_config",
  90. stylish: ""
  91. };
  92. }
  93. if (args.length == 1 &&
  94. typeof args[0].id == "string" &&
  95. typeof args[0].appendChild != "function") var settings = args[0];
  96. else {
  97. // Provide backwards-compatibility with argument style intialization
  98. var settings = {};
  99. // loop through GM_config.init() arguments
  100. for (var i = 0, l = args.length, arg; i < l; ++i) {
  101. arg = args[i];
  102. // An element to use as the config window
  103. if (typeof arg.appendChild == "function") {
  104. settings.frame = arg;
  105. continue;
  106. }
  107. switch (typeof arg) {
  108. case 'object':
  109. for (var j in arg) { // could be a callback functions or settings object
  110. if (typeof arg[j] != "function") { // we are in the settings object
  111. settings.fields = arg; // store settings object
  112. break; // leave the loop
  113. } // otherwise it must be a callback function
  114. if (!settings.events) settings.events = {};
  115. settings.events[j] = arg[j];
  116. }
  117. break;
  118. case 'function': // passing a bare function is set to open callback
  119. settings.events = {onOpen: arg};
  120. break;
  121. case 'string': // could be custom CSS or the title string
  122. if (/\w+\s*\{\s*\w+\s*:\s*\w+[\s|\S]*\}/.test(arg))
  123. settings.css = arg;
  124. else
  125. settings.title = arg;
  126. break;
  127. }
  128. }
  129. }
  130. /* Initialize everything using the new settings object */
  131. // Set the id
  132. if (settings.id) config.id = settings.id;
  133. else if (typeof config.id == "undefined") config.id = 'GM_config';
  134. // Set the title
  135. if (settings.title) config.title = settings.title;
  136. // Set the custom css
  137. if (settings.css) config.css.stylish = settings.css;
  138. // Set the frame
  139. if (settings.frame) config.frame = settings.frame;
  140. // Set the event callbacks
  141. if (settings.events) {
  142. var events = settings.events;
  143. for (var e in events)
  144. config["on" + e.charAt(0).toUpperCase() + e.slice(1)] = events[e];
  145. }
  146. // Create the fields
  147. if (settings.fields) {
  148. var stored = config.read(), // read the stored settings
  149. fields = settings.fields,
  150. customTypes = settings.types || {},
  151. configId = config.id;
  152. for (var id in fields) {
  153. var field = fields[id];
  154. // for each field definition create a field object
  155. if (field)
  156. config.fields[id] = new GM_configField(field, stored[id], id,
  157. customTypes[field.type], configId);
  158. else if (config.fields[id]) delete config.fields[id];
  159. }
  160. }
  161. // If the id has changed we must modify the default style
  162. if (config.id != config.css.basicPrefix) {
  163. config.css.basic = config.css.basic.replace(
  164. new RegExp('#' + config.css.basicPrefix, 'gm'), '#' + config.id);
  165. config.css.basicPrefix = config.id;
  166. }
  167. }
  168. GM_configStruct.prototype = {
  169. // Support old method of initalizing
  170. init: function() {
  171. GM_configInit(this, arguments);
  172. this.onInit();
  173. },
  174. // call GM_config.open() from your script to open the menu
  175. open: function () {
  176. // Die if the menu is already open on this page
  177. // You can have multiple instances but you can't open the same instance twice
  178. var match = document.getElementById(this.id);
  179. if (match && (match.tagName == "IFRAME" || match.childNodes.length > 0)) return;
  180. // Sometimes "this" gets overwritten so create an alias
  181. var config = this;
  182. // Function to build the mighty config window :)
  183. function buildConfigWin (body, head) {
  184. var create = config.create,
  185. fields = config.fields,
  186. configId = config.id,
  187. bodyWrapper = create('div', {id: configId + '_wrapper'});
  188. // Append the style which is our default style plus the user style
  189. head.appendChild(
  190. create('style', {
  191. type: 'text/css',
  192. textContent: config.css.basic + config.css.stylish
  193. }));
  194. // Add header and title
  195. bodyWrapper.appendChild(create('div', {
  196. id: configId + '_header',
  197. className: 'config_header block center'
  198. }, config.title));
  199. // Append elements
  200. var section = bodyWrapper,
  201. secNum = 0; // Section count
  202. // loop through fields
  203. for (var id in fields) {
  204. var field = fields[id],
  205. settings = field.settings;
  206. if (settings.section) { // the start of a new section
  207. section = bodyWrapper.appendChild(create('div', {
  208. className: 'section_header_holder',
  209. id: configId + '_section_' + secNum
  210. }));
  211. if (Object.prototype.toString.call(settings.section) !== '[object Array]')
  212. settings.section = [settings.section];
  213. if (settings.section[0])
  214. section.appendChild(create('div', {
  215. className: 'section_header center',
  216. id: configId + '_section_header_' + secNum
  217. }, settings.section[0]));
  218. if (settings.section[1])
  219. section.appendChild(create('p', {
  220. className: 'section_desc center',
  221. id: configId + '_section_desc_' + secNum
  222. }, settings.section[1]));
  223. ++secNum;
  224. }
  225. // Create field elements and append to current section
  226. section.appendChild((field.wrapper = field.toNode()));
  227. }
  228. // Add save and close buttons
  229. bodyWrapper.appendChild(create('div',
  230. {id: configId + '_buttons_holder'},
  231. create('button', {
  232. id: configId + '_saveBtn',
  233. textContent: 'Save',
  234. title: 'Save settings',
  235. className: 'saveclose_buttons',
  236. onclick: function () { config.save() }
  237. }),
  238. create('button', {
  239. id: configId + '_closeBtn',
  240. textContent: 'Close',
  241. title: 'Close window',
  242. className: 'saveclose_buttons',
  243. onclick: function () { config.close() }
  244. }),
  245. create('div',
  246. {className: 'reset_holder block'},
  247. // Reset link
  248. create('a', {
  249. id: configId + '_resetLink',
  250. textContent: 'Reset to defaults',
  251. href: '#',
  252. title: 'Reset fields to default values',
  253. className: 'reset',
  254. onclick: function(e) { e.preventDefault(); config.reset() }
  255. })
  256. )));
  257. body.appendChild(bodyWrapper); // Paint everything to window at once
  258. config.center(); // Show and center iframe
  259. window.addEventListener('resize', config.center, false); // Center frame on resize
  260. // Call the open() callback function
  261. config.onOpen(config.frame.contentDocument || config.frame.ownerDocument,
  262. config.frame.contentWindow || window,
  263. config.frame);
  264. // Close frame on window close
  265. window.addEventListener('beforeunload', function () {
  266. config.close();
  267. }, false);
  268. // Now that everything is loaded, make it visible
  269. config.frame.style.display = "block";
  270. config.isOpen = true;
  271. }
  272. // Change this in the onOpen callback using this.frame.setAttribute('style', '')
  273. var defaultStyle = 'bottom: auto; border: 1px solid #000; display: none; height: 75%;'
  274. + ' left: 0; margin: 0; max-height: 95%; max-width: 95%; opacity: 0;'
  275. + ' overflow: auto; padding: 0; position: fixed; right: auto; top: 0;'
  276. + ' width: 75%; z-index: 9999;';
  277. // Either use the element passed to init() or create an iframe
  278. if (this.frame) {
  279. this.frame.id = this.id; // Allows for prefixing styles with the config id
  280. this.frame.setAttribute('style', defaultStyle);
  281. buildConfigWin(this.frame, this.frame.ownerDocument.getElementsByTagName('head')[0]);
  282. } else {
  283. // Create frame
  284. document.body.appendChild((this.frame = this.create('iframe', {
  285. id: this.id,
  286. style: defaultStyle
  287. })));
  288. // In WebKit src can't be set until it is added to the page
  289. this.frame.src = 'about:blank';
  290. // we wait for the iframe to load before we can modify it
  291. var that = this;
  292. this.frame.addEventListener('load', function(e) {
  293. var frame = config.frame;
  294. if (frame.src && !frame.contentDocument) {
  295. // Some agents need this as an empty string for newer context implementations
  296. frame.src = "";
  297. } else if (!frame.contentDocument) {
  298. that.log("GM_config failed to initialize default settings dialog node!");
  299. }
  300. var body = frame.contentDocument.getElementsByTagName('body')[0];
  301. body.id = config.id; // Allows for prefixing styles with the config id
  302. buildConfigWin(body, frame.contentDocument.getElementsByTagName('head')[0]);
  303. }, false);
  304. }
  305. },
  306. save: function () {
  307. var forgotten = this.write();
  308. this.onSave(forgotten); // Call the save() callback function
  309. },
  310. close: function() {
  311. // If frame is an iframe then remove it
  312. if (this.frame.contentDocument) {
  313. this.remove(this.frame);
  314. this.frame = null;
  315. } else { // else wipe its content
  316. this.frame.innerHTML = "";
  317. this.frame.style.display = "none";
  318. }
  319. // Null out all the fields so we don't leak memory
  320. var fields = this.fields;
  321. for (var id in fields) {
  322. var field = fields[id];
  323. field.wrapper = null;
  324. field.node = null;
  325. }
  326. this.onClose(); // Call the close() callback function
  327. this.isOpen = false;
  328. },
  329. set: function (name, val) {
  330. this.fields[name].value = val;
  331. if (this.fields[name].node) {
  332. this.fields[name].reload();
  333. }
  334. },
  335. get: function (name, getLive) {
  336. var field = this.fields[name],
  337. fieldVal = null;
  338. if (getLive && field.node) {
  339. fieldVal = field.toValue();
  340. }
  341. return fieldVal != null ? fieldVal : field.value;
  342. },
  343. write: function (store, obj) {
  344. if (!obj) {
  345. var values = {},
  346. forgotten = {},
  347. fields = this.fields;
  348. for (var id in fields) {
  349. var field = fields[id];
  350. var value = field.toValue();
  351. if (field.save) {
  352. if (value != null) {
  353. values[id] = value;
  354. field.value = value;
  355. } else
  356. values[id] = field.value;
  357. } else
  358. forgotten[id] = value;
  359. }
  360. }
  361. try {
  362. this.setValue(store || this.id, this.stringify(obj || values));
  363. } catch(e) {
  364. this.log("GM_config failed to save settings!");
  365. }
  366. return forgotten;
  367. },
  368. read: function (store) {
  369. try {
  370. var rval = this.parser(this.getValue(store || this.id, '{}'));
  371. } catch(e) {
  372. this.log("GM_config failed to read saved settings!");
  373. var rval = {};
  374. }
  375. return rval;
  376. },
  377. reset: function () {
  378. var fields = this.fields;
  379. // Reset all the fields
  380. for (var id in fields) fields[id].reset();
  381. this.onReset(); // Call the reset() callback function
  382. },
  383. create: function () {
  384. switch(arguments.length) {
  385. case 1:
  386. var A = document.createTextNode(arguments[0]);
  387. break;
  388. default:
  389. var A = document.createElement(arguments[0]),
  390. B = arguments[1];
  391. for (var b in B) {
  392. if (b.indexOf("on") == 0)
  393. A.addEventListener(b.substring(2), B[b], false);
  394. else if (",style,accesskey,id,name,src,href,which,for".indexOf("," +
  395. b.toLowerCase()) != -1)
  396. A.setAttribute(b, B[b]);
  397. else
  398. A[b] = B[b];
  399. }
  400. if (typeof arguments[2] == "string")
  401. A.innerHTML = arguments[2];
  402. else
  403. for (var i = 2, len = arguments.length; i < len; ++i)
  404. A.appendChild(arguments[i]);
  405. }
  406. return A;
  407. },
  408. center: function () {
  409. var node = this.frame;
  410. if (!node) return;
  411. var style = node.style,
  412. beforeOpacity = style.opacity;
  413. if (style.display == 'none') style.opacity = '0';
  414. style.display = '';
  415. style.top = Math.floor((window.innerHeight / 2) - (node.offsetHeight / 2)) + 'px';
  416. style.left = Math.floor((window.innerWidth / 2) - (node.offsetWidth / 2)) + 'px';
  417. style.opacity = '1';
  418. },
  419. remove: function (el) {
  420. if (el && el.parentNode) el.parentNode.removeChild(el);
  421. }
  422. };
  423. // Define a bunch of API stuff
  424. (function() {
  425. var isGM = typeof GM_getValue != 'undefined' &&
  426. typeof GM_getValue('a', 'b') != 'undefined',
  427. setValue, getValue, stringify, parser;
  428. // Define value storing and reading API
  429. if (!isGM) {
  430. setValue = function (name, value) {
  431. return localStorage.setItem(name, value);
  432. };
  433. getValue = function(name, def){
  434. var s = localStorage.getItem(name);
  435. return s == null ? def : s
  436. };
  437. // We only support JSON parser outside GM
  438. stringify = JSON.stringify;
  439. parser = JSON.parse;
  440. } else {
  441. setValue = GM_setValue;
  442. getValue = GM_getValue;
  443. stringify = typeof JSON == "undefined" ?
  444. function(obj) {
  445. return obj.toSource();
  446. } : JSON.stringify;
  447. parser = typeof JSON == "undefined" ?
  448. function(jsonData) {
  449. return (new Function('return ' + jsonData + ';'))();
  450. } : JSON.parse;
  451. }
  452. GM_configStruct.prototype.isGM = isGM;
  453. GM_configStruct.prototype.setValue = setValue;
  454. GM_configStruct.prototype.getValue = getValue;
  455. GM_configStruct.prototype.stringify = stringify;
  456. GM_configStruct.prototype.parser = parser;
  457. GM_configStruct.prototype.log = window.console ?
  458. console.log : (isGM && typeof GM_log != 'undefined' ?
  459. GM_log : (window.opera ?
  460. opera.postError : function(){ /* no logging */ }
  461. ));
  462. })();
  463. function GM_configDefaultValue(type, options) {
  464. var value;
  465. if (type.indexOf('unsigned ') == 0)
  466. type = type.substring(9);
  467. switch (type) {
  468. case 'radio': case 'select':
  469. value = options[0];
  470. break;
  471. case 'checkbox':
  472. value = false;
  473. break;
  474. case 'int': case 'integer':
  475. case 'float': case 'number':
  476. value = 0;
  477. break;
  478. default:
  479. value = '';
  480. }
  481. return value;
  482. }
  483. function GM_configField(settings, stored, id, customType, configId) {
  484. // Store the field's settings
  485. this.settings = settings;
  486. this.id = id;
  487. this.configId = configId;
  488. this.node = null;
  489. this.wrapper = null;
  490. this.save = typeof settings.save == "undefined" ? true : settings.save;
  491. // Buttons are static and don't have a stored value
  492. if (settings.type == "button") this.save = false;
  493. // if a default value wasn't passed through init() then
  494. // if the type is custom use its default value
  495. // else use default value for type
  496. // else use the default value passed through init()
  497. this['default'] = typeof settings['default'] == "undefined" ?
  498. customType ?
  499. customType['default']
  500. : GM_configDefaultValue(settings.type, settings.options)
  501. : settings['default'];
  502. // Store the field's value
  503. this.value = typeof stored == "undefined" ? this['default'] : stored;
  504. // Setup methods for a custom type
  505. if (customType) {
  506. this.toNode = customType.toNode;
  507. this.toValue = customType.toValue;
  508. this.reset = customType.reset;
  509. }
  510. }
  511. GM_configField.prototype = {
  512. create: GM_configStruct.prototype.create,
  513. toNode: function() {
  514. var field = this.settings,
  515. value = this.value,
  516. options = field.options,
  517. type = field.type,
  518. id = this.id,
  519. configId = this.configId,
  520. labelPos = field.labelPos,
  521. create = this.create;
  522. function addLabel(pos, labelEl, parentNode, beforeEl) {
  523. if (!beforeEl) beforeEl = parentNode.firstChild;
  524. switch (pos) {
  525. case 'right': case 'below':
  526. if (pos == 'below')
  527. parentNode.appendChild(create('br', {}));
  528. parentNode.appendChild(labelEl);
  529. break;
  530. default:
  531. if (pos == 'above')
  532. parentNode.insertBefore(create('br', {}), beforeEl);
  533. parentNode.insertBefore(labelEl, beforeEl);
  534. }
  535. }
  536. var retNode = create('div', { className: 'config_var',
  537. id: configId + '_' + id + '_var',
  538. title: field.title || '' }),
  539. firstProp;
  540. // Retrieve the first prop
  541. for (var i in field) { firstProp = i; break; }
  542. var label = field.label && type != "button" ?
  543. create('label', {
  544. id: configId + '_' + id + '_field_label',
  545. for: configId + '_field_' + id,
  546. className: 'field_label'
  547. }, field.label) : null;
  548. switch (type) {
  549. case 'textarea':
  550. retNode.appendChild((this.node = create('textarea', {
  551. innerHTML: value,
  552. id: configId + '_field_' + id,
  553. className: 'block',
  554. cols: (field.cols ? field.cols : 20),
  555. rows: (field.rows ? field.rows : 2)
  556. })));
  557. break;
  558. case 'radio':
  559. var wrap = create('div', {
  560. id: configId + '_field_' + id
  561. });
  562. this.node = wrap;
  563. for (var i = 0, len = options.length; i < len; ++i) {
  564. var radLabel = create('label', {
  565. className: 'radio_label'
  566. }, options[i]);
  567. var rad = wrap.appendChild(create('input', {
  568. value: options[i],
  569. type: 'radio',
  570. name: id,
  571. checked: options[i] == value
  572. }));
  573. var radLabelPos = labelPos &&
  574. (labelPos == 'left' || labelPos == 'right') ?
  575. labelPos : firstProp == 'options' ? 'left' : 'right';
  576. addLabel(radLabelPos, radLabel, wrap, rad);
  577. }
  578. retNode.appendChild(wrap);
  579. break;
  580. case 'select':
  581. var wrap = create('select', {
  582. id: configId + '_field_' + id
  583. });
  584. this.node = wrap;
  585. for (var i = 0, len = options.length; i < len; ++i) {
  586. var option = options[i];
  587. wrap.appendChild(create('option', {
  588. value: option,
  589. selected: option == value
  590. }, option));
  591. }
  592. retNode.appendChild(wrap);
  593. break;
  594. default: // fields using input elements
  595. var props = {
  596. id: configId + '_field_' + id,
  597. type: type,
  598. value: type == 'button' ? field.label : value
  599. };
  600. switch (type) {
  601. case 'checkbox':
  602. props.checked = value;
  603. break;
  604. case 'button':
  605. props.size = field.size ? field.size : 25;
  606. if (field.script) field.click = field.script;
  607. if (field.click) props.onclick = field.click;
  608. break;
  609. case 'hidden':
  610. break;
  611. default:
  612. // type = text, int, or float
  613. props.type = 'text';
  614. props.size = field.size ? field.size : 25;
  615. }
  616. retNode.appendChild((this.node = create('input', props)));
  617. }
  618. if (label) {
  619. // If the label is passed first, insert it before the field
  620. // else insert it after
  621. if (!labelPos)
  622. labelPos = firstProp == "label" || type == "radio" ?
  623. "left" : "right";
  624. addLabel(labelPos, label, retNode);
  625. }
  626. return retNode;
  627. },
  628. toValue: function() {
  629. var node = this.node,
  630. field = this.settings,
  631. type = field.type,
  632. unsigned = false,
  633. rval = null;
  634. if (!node) return rval;
  635. if (type.indexOf('unsigned ') == 0) {
  636. type = type.substring(9);
  637. unsigned = true;
  638. }
  639. switch (type) {
  640. case 'checkbox':
  641. rval = node.checked;
  642. break;
  643. case 'select':
  644. rval = node[node.selectedIndex].value;
  645. break;
  646. case 'radio':
  647. var radios = node.getElementsByTagName('input');
  648. for (var i = 0, len = radios.length; i < len; ++i)
  649. if (radios[i].checked)
  650. rval = radios[i].value;
  651. break;
  652. case 'button':
  653. break;
  654. case 'int': case 'integer':
  655. case 'float': case 'number':
  656. var num = Number(node.value);
  657. var warn = 'Field labeled "' + field.label + '" expects a' +
  658. (unsigned ? ' positive ' : 'n ') + 'integer value';
  659. if (isNaN(num) || (type.substr(0, 3) == 'int' &&
  660. Math.ceil(num) != Math.floor(num)) ||
  661. (unsigned && num < 0)) {
  662. alert(warn + '.');
  663. return null;
  664. }
  665. if (!this._checkNumberRange(num, warn))
  666. return null;
  667. rval = num;
  668. break;
  669. default:
  670. rval = node.value;
  671. break;
  672. }
  673. return rval; // value read successfully
  674. },
  675. reset: function() {
  676. var node = this.node,
  677. field = this.settings,
  678. type = field.type;
  679. if (!node) return;
  680. switch (type) {
  681. case 'checkbox':
  682. node.checked = this['default'];
  683. break;
  684. case 'select':
  685. for (var i = 0, len = node.options.length; i < len; ++i)
  686. if (node.options[i].textContent == this['default'])
  687. node.selectedIndex = i;
  688. break;
  689. case 'radio':
  690. var radios = node.getElementsByTagName('input');
  691. for (var i = 0, len = radios.length; i < len; ++i)
  692. if (radios[i].value == this['default'])
  693. radios[i].checked = true;
  694. break;
  695. case 'button' :
  696. break;
  697. default:
  698. node.value = this['default'];
  699. break;
  700. }
  701. },
  702. remove: function(el) {
  703. GM_configStruct.prototype.remove(el || this.wrapper);
  704. this.wrapper = null;
  705. this.node = null;
  706. },
  707. reload: function() {
  708. var wrapper = this.wrapper;
  709. if (wrapper) {
  710. var fieldParent = wrapper.parentNode;
  711. fieldParent.insertBefore((this.wrapper = this.toNode()), wrapper);
  712. this.remove(wrapper);
  713. }
  714. },
  715. _checkNumberRange: function(num, warn) {
  716. var field = this.settings;
  717. if (typeof field.min == "number" && num < field.min) {
  718. alert(warn + ' greater than or equal to ' + field.min + '.');
  719. return null;
  720. }
  721. if (typeof field.max == "number" && num > field.max) {
  722. alert(warn + ' less than or equal to ' + field.max + '.');
  723. return null;
  724. }
  725. return true;
  726. }
  727. };
  728. // Create default instance of GM_config
  729. var GM_config = new GM_configStruct();