GM_config_sync

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

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址