Configure

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

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/38750/253186/Configure.js

  1. // ==UserScript==
  2. // @name Configure
  3. // @description A library to help you set up configure in greasemonkey script.
  4. // @namespace eight04.blogspot.com
  5. // @version 2.2.0
  6. // @grant GM_setValue
  7. // @grant GM_getValue
  8. // @homepageURL https://github.com/eight04/GM_config
  9. // @supportURL https://github.com/eight04/GM_config/issues
  10. // @attribution sizzlemctwizzle (https://github.com/sizzlemctwizzle/GM_config)
  11. // @attribution Joe Simmons (https://gf.qytechs.cn/en/scripts/1884-gm-config)
  12. // ==/UserScript==
  13. var GM_config = function(){
  14.  
  15. "use strict";
  16.  
  17. var config = {
  18. title: null,
  19. settings: null,
  20. local: false
  21. }, dialog, css, GM_config;
  22.  
  23. function addChild(e, children) {
  24. if (!children) {
  25. return;
  26. }
  27.  
  28. if (!Array.isArray(children)) {
  29. children = [children];
  30. }
  31.  
  32. var i;
  33. for (i = 0; i < children.length; i++) {
  34. if (typeof children[i] == "string") {
  35. children[i] = document.createTextNode(children[i]);
  36. }
  37. e.appendChild(children[i]);
  38. }
  39. }
  40.  
  41. function element(tag, attr, children) {
  42. var e, key, key2;
  43.  
  44. e = document.createElement(tag);
  45.  
  46. if (attr) {
  47. for (key in attr) {
  48. if (typeof attr[key] == "boolean") {
  49. if (attr[key]) {
  50. e.setAttribute(key, "");
  51. } else {
  52. e.removeAttribute(key);
  53. }
  54.  
  55. } else if (key == "event") {
  56. for (key2 in attr[key]) {
  57. e["on" + key2] = attr[key][key2];
  58. }
  59.  
  60. } else {
  61. e.setAttribute(key, attr[key]);
  62. }
  63. }
  64. }
  65.  
  66. addChild(e, children);
  67.  
  68. return e;
  69. }
  70.  
  71. function frag(children) {
  72. var fragment = document.createDocumentFragment();
  73. addChild(fragment, children);
  74. return fragment;
  75. }
  76.  
  77. function getValue(key) {
  78. if (config.local) {
  79. key = location.hostname + "/" + key;
  80. }
  81. var value = GM_getValue(key);
  82. if (GM_getValue(key + "/type") == "object") {
  83. value = JSON.parse(value);
  84. }
  85. return value;
  86. }
  87.  
  88. function setValue(key, value) {
  89. if (config.local) {
  90. key = location.hostname + "/" + key;
  91. }
  92. if (typeof value == "object") {
  93. GM_setValue(key + "/type", "object");
  94. value = JSON.stringify(value);
  95. }
  96. GM_setValue(key, value);
  97. }
  98.  
  99. function read() {
  100. var key, s;
  101. config.local = GM_getValue(location.hostname, false);
  102. for (key in config.settings) {
  103. s = config.settings[key];
  104. s.value = getValue(key, s.type);
  105. if (s.value == null) {
  106. s.value = s.default;
  107. }
  108. }
  109. }
  110.  
  111. function save() {
  112. var key, s;
  113. GM_setValue(location.hostname, config.local);
  114. for (key in config.settings) {
  115. s = config.settings[key];
  116. if (s.value == null) {
  117. setValue(key, s.default);
  118. } else {
  119. setValue(key, s.value);
  120. }
  121. }
  122. }
  123.  
  124. function destroyDialog() {
  125. dialog.element.classList.remove("config-dialog-ani");
  126. setTimeout(function() {
  127. document.body.classList.remove("config-dialog-open");
  128. document.body.style.paddingRight = "";
  129. dialog.element.parentNode.removeChild(dialog.element);
  130. dialog = null;
  131. }, 220);
  132. }
  133.  
  134. function createDialog(title) {
  135. var iframe = element("iframe", {"class": "config-dialog-content"});
  136. var modal = element("div", {"class": "config-dialog", "tabindex": "-1"}, [
  137. element("style", null, "body.config-dialog-open { padding-right: " + (window.innerWidth - document.documentElement.offsetWidth) + "px; }"),
  138. iframe
  139. ]);
  140.  
  141. var head = element("div", {"class": "config-dialog-head"}, title);
  142. var body = element("div", {"class": "config-dialog-body"});
  143. var footer = element("div", {"class": "config-dialog-footer form-inline"});
  144.  
  145. var style = element("style", null, getConfigCssString());
  146.  
  147. document.body.classList.add("config-dialog-open");
  148. document.body.appendChild(modal);
  149.  
  150. function manipulateIframe() {
  151. var doc = iframe.contentDocument;
  152. doc.head.appendChild(style);
  153. doc.body.appendChild(head);
  154. doc.body.appendChild(body);
  155. doc.body.appendChild(footer);
  156. }
  157.  
  158. iframe.contentWindow.onload = manipulateIframe;
  159.  
  160. manipulateIframe();
  161.  
  162. function render() {
  163. var body = iframe.contentDocument.body,
  164. w = body.offsetWidth,
  165. h = body.scrollHeight;
  166.  
  167. iframe.style.width = w + "px";
  168. iframe.style.height = h + "px";
  169. modal.focus();
  170.  
  171. modal.classList.add("config-dialog-ani");
  172. }
  173.  
  174. return {
  175. element: modal,
  176. head: head,
  177. body: body,
  178. footer: footer,
  179. render: render
  180. };
  181. }
  182.  
  183. function close(saveFlag) {
  184. var key, s;
  185.  
  186. if (!dialog) {
  187. return;
  188. }
  189. destroyDialog();
  190.  
  191. for (key in config.settings) {
  192. s = config.settings[key];
  193. if (saveFlag) {
  194. switch (s.type) {
  195. case "number":
  196. s.value = +s.element.value;
  197. break;
  198.  
  199. case "checkbox":
  200. s.value = s.element.checked;
  201. break;
  202.  
  203. case "radio":
  204. s.value = s.element.querySelector("input:checked").value;
  205. break;
  206.  
  207. case "select":
  208. if (!s.multiple) {
  209. s.value = s.element.value;
  210. } else {
  211. s.value = Array.prototype.map.call(
  212. s.element.selectedOptions,
  213. function(ele){
  214. return ele.value;
  215. }
  216. );
  217. }
  218. break;
  219.  
  220. default:
  221. s.value = s.element.value;
  222. }
  223. // Create inputs
  224. }
  225. // Create inputs
  226. s.element = null;
  227. }
  228.  
  229. if (saveFlag) {
  230. save();
  231. if (GM_config.onload) {
  232. GM_config.onload();
  233. }
  234. }
  235.  
  236. if (GM_config.onclose) {
  237. GM_config.onclose(saveFlag);
  238. }
  239. }
  240.  
  241. function getConfigCssString() {
  242. 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}";
  243. }
  244.  
  245. function getCssString() {
  246. 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}";
  247. }
  248.  
  249. function setupDialogValue (reset, imports) {
  250. var key, setting, value;
  251.  
  252. for (key in config.settings) {
  253. setting = config.settings[key];
  254.  
  255. if (reset) {
  256. value = setting.default;
  257. } else {
  258. if (imports && imports[key] != undefined) {
  259. value = imports[key];
  260. } else {
  261. value = setting.value;
  262. }
  263. }
  264.  
  265. switch (setting.type) {
  266. case "number":
  267. setting.element.value = value.toString();
  268. break;
  269.  
  270. case "checkbox":
  271. setting.element.checked = value;
  272. break;
  273.  
  274. case "radio":
  275. setting.element.querySelector("[value=" + value + "]").checked = true;
  276. break;
  277.  
  278. case "select":
  279. if (!setting.multiple) {
  280. setting.element.querySelector("[value=" + value + "]").selected = true;
  281. } else {
  282. while (setting.element.selectedOptions.length) {
  283. setting.element.selectedOptions[0].selected = false;
  284. }
  285. value.forEach(function(value){
  286. setting.element.querySelector("[value=" + value + "]").selected = true;
  287. });
  288. }
  289. break;
  290.  
  291. default:
  292. setting.element.value = value;
  293. break;
  294. }
  295. }
  296. }
  297.  
  298. function createInputs(dialog) {
  299. var key, s, group;
  300.  
  301. for (key in config.settings) {
  302. s = config.settings[key];
  303.  
  304. if (s.type == "textarea") {
  305. s.element = element("textarea", {"id": key});
  306. s.element.classList.add("form-control");
  307. group = [
  308. element("label", {"for": key}, s.label),
  309. s.element
  310. ];
  311. } else if (s.type == "radio") {
  312. s.element = element("fieldset", null, [element("legend", null, s.label)].concat(Object.keys(s.options).map(function(optKey){
  313. return element("label", {class: "radio"}, [
  314. element("input", {type: "radio", name: key, value: optKey}),
  315. s.options[optKey]
  316. ]);
  317. })));
  318. group = [
  319. s.element
  320. ];
  321. } else if (s.type == "select") {
  322. s.element = element(
  323. "select",
  324. {class: "form-control", multiple: !!s.multiple},
  325. Object.keys(s.options).map(function(optKey){
  326. return element(
  327. "option",
  328. {value: optKey},
  329. s.options[optKey]
  330. );
  331. })
  332. );
  333. group = element("label", null, [
  334. s.label,
  335. s.element
  336. ]);
  337. } else {
  338. s.element = element("input", {"id": key, "type": s.type});
  339.  
  340. switch (s.type) {
  341. case "number":
  342. s.element.classList.add("form-control");
  343. group = [
  344. element("label", {"for": key}, s.label),
  345. s.element
  346. ];
  347. break;
  348. case "checkbox":
  349. group = element("div", {"class": "checkbox"}, [
  350. s.element,
  351. element("label", {"for": key}, s.label)
  352. ]);
  353. break;
  354. default:
  355. s.element.classList.add("form-control");
  356. group = [
  357. element("label", {"for": key}, s.label),
  358. s.element
  359. ];
  360. }
  361. }
  362.  
  363. dialog.body.appendChild(
  364. element("div", {"class": "form-group"}, group)
  365. );
  366. }
  367. }
  368.  
  369. function createFooter(dialog) {
  370. var local = config.local;
  371.  
  372. dialog.footer.appendChild(frag([
  373. element("button", {"class": "btn-default", event: {
  374. click: function () {
  375. config.local = local;
  376. close(true);
  377. }
  378. }}, "Save"),
  379.  
  380. element("button", {"class": "btn-default", event: {
  381. click: function() {
  382. close();
  383. }
  384. }}, "Cancel"),
  385.  
  386. element("button", {class: "btn-default", event: {
  387. click: function() {
  388. setupDialogValue(true);
  389. }
  390. }}, "Default"),
  391.  
  392. element("label", {class: "radio"}, [
  393. element("input", {type: "radio", name: "working-scope", checked: !local, event: {
  394. change: function () {
  395. local = !this.checked;
  396. }
  397. }}),
  398. "Global setting"
  399. ]),
  400.  
  401. element("label", {class: "radio"}, [
  402. element("input", {type: "radio", name: "working-scope", checked: local, event: {
  403. change: function () {
  404. local = this.checked;
  405. }
  406. }}),
  407. "On " + location.hostname
  408. ])
  409. ]));
  410. }
  411.  
  412. function exportSetting() {
  413. var exports = JSON.stringify(getConfigObj());
  414. prompt("Copy:", exports);
  415. }
  416.  
  417. function importSetting() {
  418. var imports = prompt("Paste your setting:"), setting;
  419. if (!imports) {
  420. return;
  421. }
  422. try {
  423. setting = JSON.parse(imports);
  424. } catch (err) {
  425. alert("Invalid JSON!");
  426. return;
  427. }
  428. setupDialogValue(false, setting);
  429. }
  430.  
  431. function createHead(dialog) {
  432. dialog.head.appendChild(frag([
  433. element("button", {class: "btn-sm", event: {
  434. click: exportSetting
  435. }}, "Export"),
  436. element("button", {class: "btn-sm", event: {
  437. click: importSetting
  438. }}, "Import")
  439. ]));
  440. }
  441.  
  442. function open() {
  443. if (!css) {
  444. css = element("style", {"id": "config-css"}, getCssString());
  445. document.head.appendChild(css);
  446. }
  447.  
  448. if (!dialog) {
  449. dialog = createDialog(config.title);
  450.  
  451. // Create head
  452. createHead(dialog);
  453.  
  454. // Create inputs
  455. createInputs(dialog);
  456.  
  457. // Setup values
  458. setupDialogValue();
  459.  
  460. // Create footer
  461. createFooter(dialog);
  462.  
  463. // Render
  464. dialog.render();
  465. }
  466. }
  467.  
  468. function getConfigObj(key) {
  469. var con;
  470.  
  471. if (typeof key == "string") {
  472. return config.settings[key].value;
  473. } else {
  474. if (typeof key == "object") {
  475. con = key;
  476. } else {
  477. con = {};
  478. }
  479. for (key in config.settings) {
  480. con[key] = config.settings[key].value;
  481. }
  482. return con;
  483. }
  484. }
  485. function setup(options, loadCallback) {
  486. GM_config.init(GM_info.script.name, options);
  487. GM_config.onload = loadCallback;
  488. GM_registerMenuCommand(GM_info.script.name + " - Configure", GM_config.open);
  489. loadCallback();
  490. }
  491.  
  492. GM_config = {
  493. init: function(title, settings) {
  494. config.title = title;
  495. config.settings = settings;
  496. read();
  497. return GM_config.get();
  498. },
  499. open: open,
  500. close: close,
  501. get: getConfigObj,
  502. setup: setup
  503. };
  504.  
  505. return GM_config;
  506. }();
  507.  

QingJ © 2025

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