GM_config (eight's version)

A library to help you set up configure in greasemonkey script.

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/7212/156587/GM_config%20%28eight%27s%20version%29.js

  1. /* exported GM_config */
  2.  
  3. /*
  4. Copyright 2009+, GM_config Contributors (https://github.com/sizzlemctwizzle/GM_config)
  5.  
  6. GM_config Contributors:
  7. Mike Medley <medleymind@gmail.com>
  8. Joe Simmons
  9. Izzy Soft
  10. Marti Martz
  11.  
  12. GM_config is distributed under the terms of the GNU Lesser General Public License.
  13.  
  14. GM_config is free software: you can redistribute it and/or modify
  15. it under the terms of the GNU Lesser General Public License as published by
  16. the Free Software Foundation, either version 3 of the License, or
  17. (at your option) any later version.
  18.  
  19. This program is distributed in the hope that it will be useful,
  20. but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. GNU Lesser General Public License for more details.
  23.  
  24. You should have received a copy of the GNU Lesser General Public License
  25. along with this program. If not, see <http://www.gnu.org/licenses/>.
  26. */
  27.  
  28. // ==UserScript==
  29. // @name GM_config (eight's version)
  30. // @description A library to help you set up configure in greasemonkey script.
  31. // @namespace eight04.blogspot.com
  32. // @version 2.2.0
  33. // @grant GM_setValue
  34. // @grant GM_getValue
  35. // @license LGPL version 3 or any later version; http://www.gnu.org/copyleft/lgpl.html
  36. // @copyright 2015+, eight <https://github.com/eight04/GM_config>
  37. // @homepageURL https://github.com/eight04/GM_config
  38. // @supportURL https://github.com/eight04/GM_config/issues
  39. // @attribution sizzlemctwizzle (https://github.com/sizzlemctwizzle/GM_config)
  40. // @attribution Joe Simmons (https://gf.qytechs.cn/en/scripts/1884-gm-config)
  41. // ==/UserScript==
  42.  
  43. var GM_config = function(){
  44.  
  45. "use strict";
  46.  
  47. var config = {
  48. title: null,
  49. settings: null,
  50. local: false
  51. }, dialog, css, GM_config;
  52.  
  53. function addChild(e, children) {
  54. if (!children) {
  55. return;
  56. }
  57.  
  58. if (!Array.isArray(children)) {
  59. children = [children];
  60. }
  61.  
  62. var i;
  63. for (i = 0; i < children.length; i++) {
  64. if (typeof children[i] == "string") {
  65. children[i] = document.createTextNode(children[i]);
  66. }
  67. e.appendChild(children[i]);
  68. }
  69. }
  70.  
  71. function element(tag, attr, children) {
  72. var e, key, key2;
  73.  
  74. e = document.createElement(tag);
  75.  
  76. if (attr) {
  77. for (key in attr) {
  78. if (typeof attr[key] == "boolean") {
  79. if (attr[key]) {
  80. e.setAttribute(key, "");
  81. } else {
  82. e.removeAttribute(key);
  83. }
  84.  
  85. } else if (key == "event") {
  86. for (key2 in attr[key]) {
  87. e["on" + key2] = attr[key][key2];
  88. }
  89.  
  90. } else {
  91. e.setAttribute(key, attr[key]);
  92. }
  93. }
  94. }
  95.  
  96. addChild(e, children);
  97.  
  98. return e;
  99. }
  100.  
  101. function frag(children) {
  102. var fragment = document.createDocumentFragment();
  103. addChild(fragment, children);
  104. return fragment;
  105. }
  106.  
  107. function getValue(key) {
  108. if (config.local) {
  109. key = location.hostname + "/" + key;
  110. }
  111. var value = GM_getValue(key);
  112. if (GM_getValue(key + "/type") == "object") {
  113. value = JSON.parse(value);
  114. }
  115. return value;
  116. }
  117.  
  118. function setValue(key, value) {
  119. if (config.local) {
  120. key = location.hostname + "/" + key;
  121. }
  122. if (typeof value == "object") {
  123. GM_setValue(key + "/type", "object");
  124. value = JSON.stringify(value);
  125. }
  126. GM_setValue(key, value);
  127. }
  128.  
  129. function read() {
  130. var key, s;
  131. config.local = GM_getValue(location.hostname, false);
  132. for (key in config.settings) {
  133. s = config.settings[key];
  134. s.value = getValue(key, s.type);
  135. if (s.value == null) {
  136. s.value = s.default;
  137. }
  138. }
  139. }
  140.  
  141. function save() {
  142. var key, s;
  143. GM_setValue(location.hostname, config.local);
  144. for (key in config.settings) {
  145. s = config.settings[key];
  146. if (s.value == null) {
  147. setValue(key, s.default);
  148. } else {
  149. setValue(key, s.value);
  150. }
  151. }
  152. }
  153.  
  154. function destroyDialog() {
  155. dialog.element.classList.remove("config-dialog-ani");
  156. setTimeout(function() {
  157. document.body.classList.remove("config-dialog-open");
  158. document.body.style.paddingRight = "";
  159. dialog.element.parentNode.removeChild(dialog.element);
  160. dialog = null;
  161. }, 220);
  162. }
  163.  
  164. function createDialog(title) {
  165. var iframe = element("iframe", {"class": "config-dialog-content"});
  166. var modal = element("div", {"class": "config-dialog", "tabindex": "-1"}, [
  167. element("style", null, "body.config-dialog-open { padding-right: " + (window.innerWidth - document.documentElement.offsetWidth) + "px; }"),
  168. iframe
  169. ]);
  170.  
  171. var head = element("div", {"class": "config-dialog-head"}, title);
  172. var body = element("div", {"class": "config-dialog-body"});
  173. var footer = element("div", {"class": "config-dialog-footer form-inline"});
  174.  
  175. var style = element("style", null, getConfigCssString());
  176.  
  177. document.body.classList.add("config-dialog-open");
  178. document.body.appendChild(modal);
  179.  
  180. function manipulateIframe() {
  181. var doc = iframe.contentDocument;
  182. doc.head.appendChild(style);
  183. doc.body.appendChild(head);
  184. doc.body.appendChild(body);
  185. doc.body.appendChild(footer);
  186. }
  187.  
  188. iframe.contentWindow.onload = manipulateIframe;
  189.  
  190. manipulateIframe();
  191.  
  192. function render() {
  193. var body = iframe.contentDocument.body,
  194. w = body.offsetWidth,
  195. h = body.scrollHeight;
  196.  
  197. iframe.style.width = w + "px";
  198. iframe.style.height = h + "px";
  199. modal.focus();
  200.  
  201. modal.classList.add("config-dialog-ani");
  202. }
  203.  
  204. return {
  205. element: modal,
  206. head: head,
  207. body: body,
  208. footer: footer,
  209. render: render
  210. };
  211. }
  212.  
  213. function close(saveFlag) {
  214. var key, s;
  215.  
  216. if (!dialog) {
  217. return;
  218. }
  219. destroyDialog();
  220.  
  221. for (key in config.settings) {
  222. s = config.settings[key];
  223. if (saveFlag) {
  224. switch (s.type) {
  225. case "number":
  226. s.value = +s.element.value;
  227. break;
  228.  
  229. case "checkbox":
  230. s.value = s.element.checked;
  231. break;
  232.  
  233. case "radio":
  234. s.value = s.element.querySelector("input:checked").value;
  235. break;
  236.  
  237. case "select":
  238. if (!s.multiple) {
  239. s.value = s.element.value;
  240. } else {
  241. s.value = Array.prototype.map.call(
  242. s.element.selectedOptions,
  243. function(ele){
  244. return ele.value;
  245. }
  246. );
  247. }
  248. break;
  249.  
  250. default:
  251. s.value = s.element.value;
  252. }
  253. // Create inputs
  254. }
  255. // Create inputs
  256. s.element = null;
  257. }
  258.  
  259. if (saveFlag) {
  260. save();
  261. if (GM_config.onload) {
  262. GM_config.onload();
  263. }
  264. }
  265.  
  266. if (GM_config.onclose) {
  267. GM_config.onclose(saveFlag);
  268. }
  269. }
  270.  
  271. function getConfigCssString() {
  272. return "*,*:before,*:after{box-sizing:border-box}a{transition:all .2s linear}a:link,a:visited{color:#707070}a:hover{color:#0a53f8}a:active{color:#0a53f8}.link{color:#707070;text-decoration:underline;cursor:pointer}body{font-size:16px;font-family:\"Helvetica Neue\",Helvetica,Arial,\"微軟正黑體\",sans-serif;color:#3d3d3d;padding:0;margin:0;line-height:1}h1,h2,h3,h4,h5,h6{margin-top:30px;margin-bottom:15px;font-weight:bold}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-size:70%;color:#8a8a8a}h1 .head-reset,h2 .head-reset,h3 .head-reset,h4 .head-reset,h5 .head-reset,h6 .head-reset{font-size:16px;font-weight:normal}h1{font-size:260%}h2{font-size:215%;border-bottom:1px solid #808080;padding-bottom:.25em}h3{font-size:170%}h4{font-size:125%}h5{font-size:100%}h6{font-size:85%}p{margin-top:.9375em;margin-bottom:.9375em;line-height:1.55}input,textarea,select,button{font-size:inherit;font-family:inherit;line-height:inherit;margin:0;padding:0;vertical-align:middle;height:2em;color:inherit;background-color:transparent;border:none}input:focus,textarea:focus,select:focus,button:focus{outline:none}::-moz-placeholder,:-moz-placeholder{opacity:1}:invalid{outline:none;box-shadow:none}textarea{height:auto;line-height:1.55;padding-top:.225em;padding-bottom:.225em}select{cursor:pointer;line-height:1.55}select[multiple]{height:auto}button{cursor:pointer;text-align:center;background-image:none}button::-moz-focus-inner{border:0;padding:0}img{vertical-align:text-bottom;max-width:100%;max-height:100%}code{font-family:Menlo,Monaco,Consolas,\"Courier New\",\"細明體\",monospace;background-color:#f0f0f0;font-size:90%;padding:.25em}pre{margin-top:1em;margin-bottom:1em;font-family:Menlo,Monaco,Consolas,\"Courier New\",\"細明體\",monospace}small{font-size:90%}hr{border:none;border-top:1px solid #808080;margin:15px 0}::selection{background-color:rgba(255,169,46,0.4)}::-moz-selection{background-color:rgba(255,169,46,0.4)}fieldset,legend{border:0 none;margin:0;padding:0}input[type=\"checkbox\"],input[type=\"radio\"]{padding:0}.row-gap>fieldset>legend{position:relative;top:15px}input.ng-invalid,textarea.ng-invalid,select.ng-invalid,input.ng-invalid:hover,textarea.ng-invalid:hover,select.ng-invalid:hover,input.ng-invalid:focus,textarea.ng-invalid:focus,select.ng-invalid:focus{border-color:#cb1b1b}input[type=\"checkbox\"].ng-invalid,input[type=\"radio\"].ng-invalid,input[type=\"checkbox\"].ng-invalid:hover,input[type=\"radio\"].ng-invalid:hover,input[type=\"checkbox\"].ng-invalid:focus,input[type=\"radio\"].ng-invalid:focus{box-shadow:0 0 0 1px #cb1b1b}::-webkit-input-placeholder{color:#c9c9c9}::-moz-placeholder{color:#c9c9c9}:-ms-input-placeholder{color:#c9c9c9}:-moz-placeholder{color:#c9c9c9}.form-group{margin-top:1em;margin-bottom:1em}.row>.form-group{margin:0}label,legend{vertical-align:bottom}label+.form-control,legend+.form-control,label+.input-group,legend+.input-group,label+.radio,legend+.radio,label+.checkbox,legend+.checkbox,label .form-control,legend .form-control,label .input-group,legend .input-group,label .radio,legend .radio,label .checkbox,legend .checkbox{margin-top:.3em}.form-control{border:1px solid #808080;border-radius:.1875em;display:block;width:100%;line-height:1;display:inline-block;padding-left:.5em;padding-right:.5em;color:#707070;transition:.2s all linear}.form-control:hover{border-color:#ccc}.form-control:focus{border-color:#0a53f8;color:#242424}.form-control[disabled]{background-color:#f0f0f0;border-color:#ccc;cursor:not-allowed}.form-control[disabled]:hover,.form-control[disabled]:active{border-color:#ccc}.radio,.checkbox{position:relative}.radio input[type=\"radio\"],.checkbox input[type=\"radio\"],.radio input[type=\"checkbox\"],.checkbox input[type=\"checkbox\"]{color:inherit;position:absolute;width:auto;height:auto;top:0;bottom:0;left:0;margin:auto 0}.radio label,.checkbox label,label.radio,label.checkbox{display:table;padding-left:1.5em;cursor:pointer;line-height:1.55;transition:.2s all linear}.radio label:hover,.checkbox label:hover,label.radio:hover,label.checkbox:hover{color:#707070}.radio+.radio,.radio+.checkbox,.checkbox+.radio,.checkbox+.checkbox{margin-top:0}.form-inline input,.form-inline select,.form-inline button,.form-inline .radio,.form-inline .checkbox,.form-inline fieldset,.form-inline .form-group{display:inline-block;vertical-align:middle;width:auto;margin:0}.btn-default{border:1px solid #808080;border-radius:.1875em;transition-property:border-color,box-shadow;transition-duration:.2s;transition-timing-function:linear;padding-left:.5em;padding-right:.5em;min-width:4.25em}.btn-default:hover{border-color:#ccc}.btn-default:focus{color:#3d3d3d;border-color:#0a53f8}.btn-default:active{border-color:#0a53f8;box-shadow:inset .12em .12em .5em #dedede}.btn-default[disabled]{background-color:#f0f0f0;border-color:#ccc;cursor:not-allowed}.btn-default[disabled]:hover,.btn-default[disabled]:active{border-color:#ccc}.btn-sm{border:1px solid #808080;border-radius:.1875em;transition-property:border-color,box-shadow;transition-duration:.2s;transition-timing-function:linear;width:2em;line-height:.8}.btn-sm:hover{border-color:#ccc}.btn-sm:focus{color:#3d3d3d;border-color:#0a53f8}.btn-sm:active{border-color:#0a53f8;box-shadow:inset .12em .12em .5em #dedede}.btn-sm[disabled]{background-color:#f0f0f0;border-color:#ccc;cursor:not-allowed}.btn-sm[disabled]:hover,.btn-sm[disabled]:active{border-color:#ccc}.btn-close{width:1em;height:1em;vertical-align:baseline;color:#707070}.btn-close:hover{color:inherit}.btn-circle{display:inline-block;width:1.25em;height:1.25em;text-align:center;line-height:1.25;font-size:80%;vertical-align:baseline;border-radius:50%;border:1px solid #707070;margin:-1px 0;color:#707070}.btn-circle:hover{color:inherit;border-color:inherit}.btn-block{display:block;width:100%}.btn-group{display:block;display:inline-block;border:1px solid #808080;border-radius:.1875em}.btn-group>*{display:table-cell;vertical-align:middle;white-space:nowrap}.btn-group>*{border-width:0 1px;border-radius:0;margin-right:-1px}.btn-group>*:first-child{margin-left:-1px}body{display:inline-block;padding:30px;overflow:hidden}.config-dialog-head{font-weight:bold;font-size:120%}.config-dialog-head .btn-sm{font-size:50%;font-weight:normal;width:auto;padding:0 2px;float:right}.config-dialog-head .btn-sm+*{margin-right:5px}.config-dialog-footer.form-inline{white-space:nowrap}.config-dialog-footer.form-inline>*+*{margin-left:15px}.config-dialog-footer.form-inline label.radio{padding:4px 0 1px 18px}";
  273. }
  274.  
  275. function getCssString() {
  276. return ".config-dialog-open{overflow:hidden}.config-dialog{position:fixed;top:0;left:0;right:0;bottom:0;vertical-align:middle;text-align:center;background:rgba(0,0,0,0.5);overflow:auto;z-index:99999;opacity:0;transition:opacity .2s linear;white-space:nowrap}.config-dialog:before{content:\"\";display:inline-block;height:100%;vertical-align:middle}.config-dialog-ani{opacity:1}.config-dialog-content{text-align:left;display:inline-block;width:90%;vertical-align:middle;background:white;margin:30px 0;box-shadow:0 0 30px black;border-width:0;transition:transform .2s linear;transform:translateY(-20px)}.config-dialog-ani .config-dialog-content{transform:none}";
  277. }
  278.  
  279. function setupDialogValue (reset, imports) {
  280. var key, setting, value;
  281.  
  282. for (key in config.settings) {
  283. setting = config.settings[key];
  284.  
  285. if (reset) {
  286. value = setting.default;
  287. } else {
  288. if (imports && imports[key] != undefined) {
  289. value = imports[key];
  290. } else {
  291. value = setting.value;
  292. }
  293. }
  294.  
  295. switch (setting.type) {
  296. case "number":
  297. setting.element.value = value.toString();
  298. break;
  299.  
  300. case "checkbox":
  301. setting.element.checked = value;
  302. break;
  303.  
  304. case "radio":
  305. setting.element.querySelector("[value=" + value + "]").checked = true;
  306. break;
  307.  
  308. case "select":
  309. if (!setting.multiple) {
  310. setting.element.querySelector("[value=" + value + "]").selected = true;
  311. } else {
  312. while (setting.element.selectedOptions.length) {
  313. setting.element.selectedOptions[0].selected = false;
  314. }
  315. value.forEach(function(value){
  316. setting.element.querySelector("[value=" + value + "]").selected = true;
  317. });
  318. }
  319. break;
  320.  
  321. default:
  322. setting.element.value = value;
  323. break;
  324. }
  325. }
  326. }
  327.  
  328. function createInputs(dialog) {
  329. var key, s, group;
  330.  
  331. for (key in config.settings) {
  332. s = config.settings[key];
  333.  
  334. if (s.type == "textarea") {
  335. s.element = element("textarea", {"id": key});
  336. s.element.classList.add("form-control");
  337. group = [
  338. element("label", {"for": key}, s.label),
  339. s.element
  340. ];
  341. } else if (s.type == "radio") {
  342. s.element = element("fieldset", null, [element("legend", null, s.label)].concat(Object.keys(s.options).map(function(optKey){
  343. return element("label", {class: "radio"}, [
  344. element("input", {type: "radio", name: key, value: optKey}),
  345. s.options[optKey]
  346. ]);
  347. })));
  348. group = [
  349. s.element
  350. ];
  351. } else if (s.type == "select") {
  352. s.element = element(
  353. "select",
  354. {class: "form-control", multiple: !!s.multiple},
  355. Object.keys(s.options).map(function(optKey){
  356. return element(
  357. "option",
  358. {value: optKey},
  359. s.options[optKey]
  360. );
  361. })
  362. );
  363. group = element("label", null, [
  364. s.label,
  365. s.element
  366. ]);
  367. } else {
  368. s.element = element("input", {"id": key, "type": s.type});
  369.  
  370. switch (s.type) {
  371. case "number":
  372. s.element.classList.add("form-control");
  373. group = [
  374. element("label", {"for": key}, s.label),
  375. s.element
  376. ];
  377. break;
  378. case "checkbox":
  379. group = element("div", {"class": "checkbox"}, [
  380. s.element,
  381. element("label", {"for": key}, s.label)
  382. ]);
  383. break;
  384. default:
  385. s.element.classList.add("form-control");
  386. group = [
  387. element("label", {"for": key}, s.label),
  388. s.element
  389. ];
  390. }
  391. }
  392.  
  393. dialog.body.appendChild(
  394. element("div", {"class": "form-group"}, group)
  395. );
  396. }
  397. }
  398.  
  399. function createFooter(dialog) {
  400. var local = config.local;
  401.  
  402. dialog.footer.appendChild(frag([
  403. element("button", {"class": "btn-default", event: {
  404. click: function () {
  405. config.local = local;
  406. close(true);
  407. }
  408. }}, "Save"),
  409.  
  410. element("button", {"class": "btn-default", event: {
  411. click: function() {
  412. close();
  413. }
  414. }}, "Cancel"),
  415.  
  416. element("button", {class: "btn-default", event: {
  417. click: function() {
  418. setupDialogValue(true);
  419. }
  420. }}, "Default"),
  421.  
  422. element("label", {class: "radio"}, [
  423. element("input", {type: "radio", name: "working-scope", checked: !local, event: {
  424. change: function () {
  425. local = !this.checked;
  426. }
  427. }}),
  428. "Global setting"
  429. ]),
  430.  
  431. element("label", {class: "radio"}, [
  432. element("input", {type: "radio", name: "working-scope", checked: local, event: {
  433. change: function () {
  434. local = this.checked;
  435. }
  436. }}),
  437. "On " + location.hostname
  438. ])
  439. ]));
  440. }
  441.  
  442. function exportSetting() {
  443. var exports = JSON.stringify(getConfigObj());
  444. prompt("Copy:", exports);
  445. }
  446.  
  447. function importSetting() {
  448. var imports = prompt("Paste your setting:"), setting;
  449. if (!imports) {
  450. return;
  451. }
  452. try {
  453. setting = JSON.parse(imports);
  454. } catch (err) {
  455. alert("Invalid JSON!");
  456. return;
  457. }
  458. setupDialogValue(false, setting);
  459. }
  460.  
  461. function createHead(dialog) {
  462. dialog.head.appendChild(frag([
  463. element("button", {class: "btn-sm", event: {
  464. click: exportSetting
  465. }}, "Export"),
  466. element("button", {class: "btn-sm", event: {
  467. click: importSetting
  468. }}, "Import")
  469. ]));
  470. }
  471.  
  472. function open() {
  473. if (!css) {
  474. css = element("style", {"id": "config-css"}, getCssString());
  475. document.head.appendChild(css);
  476. }
  477.  
  478. if (!dialog) {
  479. dialog = createDialog(config.title);
  480.  
  481. // Create head
  482. createHead(dialog);
  483.  
  484. // Create inputs
  485. createInputs(dialog);
  486.  
  487. // Setup values
  488. setupDialogValue();
  489.  
  490. // Create footer
  491. createFooter(dialog);
  492.  
  493. // Render
  494. dialog.render();
  495. }
  496. }
  497.  
  498. function getConfigObj(key) {
  499. var con;
  500.  
  501. if (typeof key == "string") {
  502. return config.settings[key].value;
  503. } else {
  504. if (typeof key == "object") {
  505. con = key;
  506. } else {
  507. con = {};
  508. }
  509. for (key in config.settings) {
  510. con[key] = config.settings[key].value;
  511. }
  512. return con;
  513. }
  514. }
  515. function setup(options, loadCallback) {
  516. GM_config.init(GM_info.script.name, options);
  517. GM_config.onload = loadCallback;
  518. GM_registerMenuCommand(GM_info.script.name + " - Configure", GM_config.open);
  519. loadCallback();
  520. }
  521.  
  522. GM_config = {
  523. init: function(title, settings) {
  524. config.title = title;
  525. config.settings = settings;
  526. read();
  527. return GM_config.get();
  528. },
  529. open: open,
  530. close: close,
  531. get: getConfigObj,
  532. setup: setup
  533. };
  534.  
  535. return GM_config;
  536. }();
  537.  

QingJ © 2025

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