NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name GM_config // @namespace http://userscripts.org/users/23652 // @description GreaseMonkey Script Configurator // @include http://* // @include https://* // @include file:* // @copyright JoeSimmons & Sizzlemctwizzle & IzzySoft // @version 1.2.58 // @license LGPL version 3 or any later version; http://www.gnu.org/copyleft/lgpl.html // ==/UserScript== /* Instructions --------------------------------------------------------------------------------------------------- GM_config is cross-browser compatible. To use it in a Greasemonkey/Tampermonkey/Violentmonkey script, you can just @require it. If you can't @require it, you will need to manually include the code at the beginning of your user script. In non-Greasemonkey, stored settings will only be accessible on the same domain on which they were saved. ---------------------------------------------------------------------------------------------------------------- */ /* CHANGELOG -------------------------------------------------------------------------- 1.2.58 - added a 'slider' type; it allows adjusting by sliding it with your mouse - re-wrote the entire code 1.2.57 - Fixed TypeError with the "options" setting of a "select" field The "options" setting needs to be a JSON object, not an array 1.2.56 - started keeping changelog in source - added type "password" - fixed error when user doesn't define any sections ------------------------------------------------------------------------------------- */ var GM_config = { storage : 'GM_config', init : function () { var i, settings, arg, css, stored, kid, l = arguments.length; // loop through GM_config.init() arguments for (i = 0; i < l; i += 1) { arg = arguments[i]; switch (typeof arg) { case 'object': { for (var j in arg) { // could be a callback functions or settings object switch (j) { case "open": { GM_config.onOpen=arg[j]; delete arg[j]; break; // called when frame is gone } case "close": { GM_config.onClose=arg[j]; delete arg[j]; break; // called when settings have been saved } case "save": { GM_config.onSave=arg[j]; delete arg[j]; break; // store the settings objects } default: { settings = arg; } } } break; } case 'function': { GM_config.onOpen = arg; break; // passing a bare function is set to open // could be custom CSS or the title string } case 'string': { if (arg.indexOf('{') !== -1 && arg.indexOf('}') !== -1) { css = arg; } else { GM_config.title = arg; } break; } default: { break; } } } if (!GM_config.title) { GM_config.title = 'Settings - Anonymous Script'; // if title wasn't passed through init() } // give the script a unique saving ID for non-firefox browsers GM_config.storage = GM_config.title.replace(/\W+/g, '').toLowerCase(); stored = GM_config.read(); // read the stored settings GM_config.passed_values = {}; for (i in settings) { GM_config.doSettingValue(settings, stored, i, null, false); if (settings[i].kids) { for (kid in settings[i].kids) { GM_config.doSettingValue(settings, stored, kid, i, true); } } } GM_config.values = GM_config.passed_values; GM_config.settings = settings; if (css) { GM_config.css.stylish = css; } }, // ------------------------------------------------------------------------------------------------------------------- open: function () { if (document.evaluate("//iframe[@id='GM_config']",document,null,9,null).singleNodeValue) return; // Create frame document.body.appendChild((GM_config.frame=GM_config.create('iframe',{id:'GM_config', style:'position: fixed; top: 0; left: 0; opacity: 0; display: none; z-index: 999999; width: 75%; height: 75%; max-height: 95%; max-width: 95%; border:3px ridge #000000; overflow: auto;'}))); GM_config.frame.src = 'about:blank'; // In WebKit src cant be set until it is added to the page GM_config.frame.addEventListener('load', function () { var obj = GM_config, doc = this.contentDocument, frameBody = doc.getElementsByTagName('body')[0], create=obj.create, settings=obj.settings, anch, secNo; obj.frame.contentDocument.getElementsByTagName('head')[0].appendChild(create('style',{type:'text/css',textContent:obj.css.basic + "\n\n" + obj.css.stylish})); // Add header and title frameBody.appendChild(create('div', {id:'header',className:'config_header block center', innerHTML:obj.title})); // Append elements anch = frameBody; // define frame body secNo = 0; // anchor to append elements for (var i in settings) { var type, field = settings[i], value = obj.values[i], section = (field.section ? field.section : ["Main Options"]), headerExists = doc.evaluate(".//div[@class='section_header_holder' and starts-with(@id, 'section_')]", frameBody, null, 9, null).singleNodeValue; if (typeof field.section !== "undefined" || headerExists === null) { anch = frameBody.appendChild(create('div', {className:'section_header_holder', id:'section_'+secNo, kids:new Array( create('a', {className:'section_header center', href:"javascript:void(0);", id:'c_section_kids_'+secNo, textContent:section[0], onclick:function (){GM_config.toggle(this.id.substring(2));}}), create('div', {id:'section_kids_'+secNo, className:'section_kids', style:obj.getValue('section_kids_'+secNo, "")==""?"":"display: none;"}) )})); if (section[1]) anch.appendChild(create('p', {className:'section_desc center',innerHTML:section[1]})); secNo++; } anch.childNodes[1].appendChild(GM_config.addToFrame(field, i, false)); } // Add save and close buttons frameBody.appendChild(obj.create('div', {id:'buttons_holder', kids:new Array( obj.create('button',{id:'saveBtn',textContent:'Save',title:'Save options and close window',className:'saveclose_buttons',onclick:function (){GM_config.close(true)}}), obj.create('button',{id:'cancelBtn', textContent:'Cancel',title:'Close window',className:'saveclose_buttons',onclick:function (){GM_config.close(false)}}), obj.create('div', {className:'reset_holder block', kids:new Array( obj.create('a',{id:'resetLink',textContent:'Restore to default',href:'#',title:'Restore settings to default configuration',className:'reset',onclick:obj.reset}) )}))})); obj.center(); // Show and center it window.addEventListener('resize', obj.center, false); // Center it on resize if (obj.onOpen) obj.onOpen(); // Call the open() callback function // Close frame on window close window.addEventListener('beforeunload', function (){GM_config.remove(this);}, false); }, false); }, // ------------------------------------------------------------------------------------------------------------------- close: function (save) { if (save) { var type, fields = GM_config.settings, typewhite=/radio|text|hidden|password|checkbox/; for (f in fields) { var field = GM_config.frame.contentDocument.getElementById('field_'+f), kids=fields[f].kids; if ( typewhite.test(field.type) ) { type = field.type; } else { type = field.tagName.toLowerCase(); } GM_config.doSave(f, field, type); if (kids) for (var kid in kids) { var field = GM_config.frame.contentDocument.getElementById('field_'+kid); if ( typewhite.test(field.type) ) { type=field.type; } else { type=field.tagName.toLowerCase(); } GM_config.doSave(kid, field, type, f); } } if (GM_config.onSave) GM_config.onSave(); // Call the save() callback function GM_config.save(); } if (GM_config.frame) { GM_config.remove(GM_config.frame); } delete GM_config.frame; if (GM_config.onClose) { GM_config.onClose(); // Call the close() callback function } }, // ------------------------------------------------------------------------------------------------------------------- set: function (name,val) { GM_config.values[name] = val; }, // ------------------------------------------------------------------------------------------------------------------- get: function (name) { return GM_config.values[name]; }, // ------------------------------------------------------------------------------------------------------------------- isGM: (typeof window.opera === "undefined" && typeof window.chrome === "undefined" && typeof GM_info === "object" && typeof GM_registerMenuCommand === "function"), // ------------------------------------------------------------------------------------------------------------------- log: function (str) { if (this.isGM) return GM_log(str); else if (window.opera) return window.opera.postError(str); else return console.log(str); }, // ------------------------------------------------------------------------------------------------------------------- getValue : function (name, d) { var r, def = (typeof d !== "undefined" ? d : ""); switch (this.isGM === true) { case true: r = GM_getValue(name, def); break; case false: r = localStorage.getItem(name) || def; break; } return r; }, // ------------------------------------------------------------------------------------------------------------------- setValue : function (name, value) { switch (this.isGM === true) { case true: GM_setValue(name, value); break; case false: localStorage.setItem(name, value); break; } }, // ------------------------------------------------------------------------------------------------------------------- deleteValue : function (name) { switch (this.isGM === true) { case true: GM_deleteValue(name); break; case false: localStorage.removeItem(name); break; } }, // ------------------------------------------------------------------------------------------------------------------- save: function (store, obj) { try { var val = JSON.stringify(obj || GM_config.values); GM_config.setValue((store||GM_config.storage),val); } catch(e) { GM_config.log("GM_config failed to save settings!\n" + e); } }, // ------------------------------------------------------------------------------------------------------------------- read: function (store) { var val = GM_config.getValue((store || GM_config.storage), '{}'); switch (typeof val) { case "string": var rval = JSON.parse(val); break; case "object": var rval = val; break; default: var rval = {}; } return rval; }, // ------------------------------------------------------------------------------------------------------------------- reset: function (e) { e.preventDefault(); var type, obj = GM_config, fields = obj.settings; for (f in fields) { var field = obj.frame.contentDocument.getElementById('field_'+f), kids=fields[f].kids; if (field.type=='radio'||field.type=='text'||field.type=='checkbox') type=field.type; else type=field.tagName.toLowerCase(); GM_config.doReset(field, type, null, f, null, false); if (kids) for (var kid in kids) { var field = GM_config.frame.contentDocument.getElementById('field_'+kid); if (field.type=='radio'||field.type=='text'||field.type=='checkbox') type=field.type; else type=field.tagName.toLowerCase(); GM_config.doReset(field, type, f, kid, true); } } }, // ------------------------------------------------------------------------------------------------------------------- addToFrame : function (field, i, k) { var elem, obj = this, anch = this.frame, value = obj.values[i], Options = field.options, label = field.label, create = obj.create, isKid = (k !== null && k === true), kid, kids; switch (field.type) { case 'textarea': elem = create(isKid ? "span" : "div", {title:field.title||'', kids:new Array( create('span', {textContent:label, className:'field_label'}), create('textarea', {id:'field_'+i,innerHTML:value, cols:(field.cols?field.cols:20), rows:(field.rows?field.rows:2)}) ), className: 'config_var'}); break; case 'radio': var boxes = new Array(); for (var j = 0,len = Options.length; j<len; j++) { boxes.push(create('span', {textContent:Options[j]})); boxes.push(create('input', {value:Options[j], type:'radio', name:i, checked:Options[j]==value?true:false})); } elem = create(isKid ? "span" : "div", {title:field.title||'', kids:new Array( create('span', {textContent:label, className:'field_label'}), create('span', {id:'field_'+i, kids:boxes}) ), className: 'config_var'}); break; case 'select': var options = [], j; if (Object.prototype.toString.call(Options) === '[object Array]') { for (j in Options) { options.push( create('option', {textContent : Options[j], value : j, selected: (j === value)}) ); } } else { options.push( create("option", {textContent : 'Error - "options" needs to be a JSON object.', value : 'error', selected : 'selected'})); } elem = create(isKid ? "span" : "div", {title:field.title||'', kids:new Array( create('span', {textContent:label, className:'field_label'}), create('select',{id:'field_'+i, kids:options}) ), className: 'config_var'}); break; case 'checkbox': elem = create(isKid ? "span" : "div", {title:field.title||'', kids:new Array( create('label', {textContent:label, className:'field_label', "for":'field_'+i}), create('input', {id:'field_'+i, type:'checkbox', value:value, checked:value}) ), className: 'config_var'}); break; case 'button': var tmp; elem = create(isKid ? "span" : "div", {kids:new Array( (tmp=create('input', {id:'field_'+i, type:'button', value:label, size:(field.size?field.size:25), title:field.title||''})) ), className: 'config_var'}); if (field.script) obj.addEvent(tmp, 'click', field.script); break; case 'hidden': elem = create(isKid ? "span" : "div", {title:field.title||'', kids:new Array( create('input', {id:'field_'+i, type:'hidden', value:value}) ), className: 'config_var'}); break; case 'password': elem = create(isKid ? "span" : "div", {title:field.title||'', kids:new Array( create('span', {textContent:label, className:'field_label'}), create('input', {id:'field_'+i, type:'password', value:value, size:(field.size?field.size:25)}) ), className: 'config_var'}); break; case 'slider': elem = create(isKid ? "span" : "div", {title:field.title||'', kids:new Array( create('span', {textContent:label, className:'field_label'}), create('input', {id:'field_'+i, type:'text', value:value, size:(field.size?field.size:25)}) ), className: 'config_var'}); break; default: elem = create(isKid ? "span" : "div", {title:field.title||'', kids:new Array( create('span', {textContent:label, className:'field_label'}), create('input', {id:'field_'+i, type:'text', value:value, size:(field.size?field.size:25)}) ), className: 'config_var'}); break; } if (field.kids) { kids = field.kids; for (kid in kids) elem.appendChild(obj.addToFrame(kids[kid], kid, true)); } return elem; }, // ------------------------------------------------------------------------------------------------------------------- doSave : function (f, field, type, oldf) { var isNum=/^[\d\.]+$/, set = oldf ? GM_config.settings[oldf]["kids"] : GM_config.settings; switch (type) { case 'text': GM_config.values[f] = ((set[f].type=='text') ? field.value : ((isNum.test(field.value) && ",int,float".indexOf(","+set[f].type)!=-1) ? parseFloat(field.value) : false)); if (set[f]===false) { alert('Invalid type for field: '+f+'\nPlease use type: '+set[f].type); return; } break; case 'hidden': case 'password': GM_config.values[f] = field.value.toString(); break; case 'textarea': GM_config.values[f] = field.value; break; case 'checkbox': GM_config.values[f] = field.checked; break; case 'select': GM_config.values[f] = field.options[field.selectedIndex].value; break; case 'span': var radios = field.getElementsByTagName('input'); if (radios.length>0) for (var i=radios.length-1; i>=0; i--) { if (radios[i].checked) GM_config.values[f] = radios[i].value; } break; } }, // ------------------------------------------------------------------------------------------------------------------- doSettingValue : function (settings, stored, i, oldi, k) { var set = k!=null && k==true && oldi!=null ? settings[oldi]["kids"][i] : settings[i]; if (",save,open,close".indexOf(","+i) == -1) { // The code below translates to: // if a setting was passed to init but wasn't stored then // if a default value wasn't passed through init() then use null // else use the default value passed through init() // else use the stored value try { var value = (stored[i]==undefined ? (set["default"]==undefined ? null : set["default"]) : stored[i]); } catch(e) { var value = (stored[i]=="undefined" ? (set["default"]=="undefined" ? null : set["default"]) : stored[i]); } // If the value isn't stored and no default was passed through init() // try to predict a default value based on the type if (value === null) { switch (set["type"]) { case 'radio': case 'select': value = set.options[0]; break; case 'checkbox': value = false; break; case 'int': case 'float': value = 0; break; default: value = (typeof stored[i]=="function") ? stored[i] : ""; } } } GM_config.passed_values[i] = value; }, // ------------------------------------------------------------------------------------------------------------------- doReset : function (field, type, oldf, f, k) { var isKid = k!=null && k==true, obj=GM_config, set = isKid ? obj.settings[oldf]["kids"][f] : obj.settings[f]; switch (type) { case 'text': field.value = set['default'] || ''; break; case 'hidden': case 'password': field.value = set['default'] || ''; break; case 'textarea': field.value = set['default'] || ''; break; case 'checkbox': field.checked = set['default'] || false; break; case 'select': if (set['default']) { for (var i=field.options.length-1; i>=0; i--) if (field.options[i].value==set['default']) field.selectedIndex=i; } else field.selectedIndex=0; break; case 'span': var radios = field.getElementsByTagName('input'); if (radios.length>0) for (var i=radios.length-1; i>=0; i--) { if (radios[i].value==set['default']) { radios[i].checked=true; } } break; } }, // ------------------------------------------------------------------------------------------------------------------- values : {}, settings : {}, // ------------------------------------------------------------------------------------------------------------------- css: { basic : 'body {background:#FFFFFF;}\n' + '.indent40 {margin-left:40%;}\n' + '* {font-family: arial, tahoma, sans-serif, myriad pro;}\n' + '.field_label {font-weight:bold; font-size:12px; margin-right:6px;}\n' + '.block {display:block;}\n' + '.saveclose_buttons {\n' + 'margin:16px 10px 10px 10px;\n' + 'padding:2px 12px 2px 12px;\n' + '}\n' + '.reset, #buttons_holder, .reset a {text-align:right; color:#000000;}\n' + '.config_header {font-size:20pt; margin:0;}\n' + '.config_desc, .section_desc, .reset {font-size:9pt;}\n' + '.center {text-align:center;}\n' + '.section_header_holder {margin-top:8px;}\n' + '.config_var {margin:0 0 4px 0; display:block;}\n' + '.config_var {font-size: 13px !important;}\n' + '.section_header {font-size:13pt; background:#414141; color:#FFFFFF; border:1px solid #000000; margin:0;}\n' + '.section_desc {font-size:9pt; background:#EFEFEF; color:#575757; border:1px solid #CCCCCC; margin:0 0 6px 0;}\n' + 'input[type="radio"] {margin-right:8px;}', stylish : '' }, // ------------------------------------------------------------------------------------------------------------------- create: function (a,b) { var ret=window.document.createElement(a); if (b) for (var prop in b) { if (prop.indexOf('on')==0) ret.addEventListener(prop.substring(2),b[prop],false); else if (prop=="kids" && (prop=b[prop])) for (var i=0; i<prop.length; i++) ret.appendChild(prop[i]); else if (",style,accesskey,id,name,src,href,for".indexOf(","+prop.toLowerCase())!=-1) ret.setAttribute(prop, b[prop]); else ret[prop]=b[prop]; } return ret; }, // ------------------------------------------------------------------------------------------------------------------- center: function () { var node = GM_config.frame, style = node.style, beforeOpacity = style.opacity; if (style.display=='none') style.opacity='0'; style.display = ''; style.top = Math.floor((window.innerHeight/2)-(node.offsetHeight/2)) + 'px'; style.left = Math.floor((window.innerWidth/2)-(node.offsetWidth/2)) + 'px'; style.opacity = '1'; }, // ------------------------------------------------------------------------------------------------------------------- run: function () { var script = GM_config.getAttribute('script'), func; if (script && typeof script === 'string' && script !== '') { func = new Function(script); window.setTimeout(func, 0); } }, // ------------------------------------------------------------------------------------------------------------------- addEvent: function (el, ev, scr) { el.addEventListener(ev, function () { typeof scr === 'function' ? window.setTimeout(scr, 0) : eval(scr) }, false); }, // ------------------------------------------------------------------------------------------------------------------- remove: function (el) { if (el && el.parentNode) el.parentNode.removeChild(el); }, // ------------------------------------------------------------------------------------------------------------------- toggle : function (e) { var node=GM_config.frame.contentDocument.getElementById(e); node.style.display=(node.style.display!='none')?'none':''; GM_config.setValue(e, node.style.display); } }; //* EXAMPLE CODE BELOW -------------------------- GM_config.init("Test", { "one" : { "label" : "Option One", "type" : "checkbox", "default" : false }, "two" : { "label" : "Option Two", "type" : "checkbox", "default" : false }, "three" : { "label" : "Option Three", "type" : "password" }, "four" : { "label" : "Option Four", "type" : "select", "options" : { "one" : "One", "two" : "Two" } } }); GM_config.open(); //------------------------------------------------- */