WM Config Interface

Creates a configuration interface which allows users to set and get options easily.

Tento skript by nemal byť nainštalovaný priamo. Je to knižnica pre ďalšie skripty, ktorú by mali používať cez meta príkaz // @require https://update.greatest.deepsurf.us/scripts/410/1287/WM%20Config%20Interface.js

  1. // ==UserScript==
  2. // @name WM Config Interface
  3. // @namespace configtemp
  4. // @description Creates a configuration interface which allows users to set and get options easily.
  5. // @require https://greatest.deepsurf.us/scripts/407-wm-common-library/code/WM%20Common%20Library.user.js?version=1284
  6. // @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/
  7. // @version 2.0.0.13
  8. // @copyright Charlie Ewing
  9. // ==/UserScript==
  10.  
  11. //this script is based on GM_config by JoeSimmons, sizzlemctwizzle, and izzysoft
  12. //this script requires some functions in the WM Common Library
  13.  
  14. (function(){
  15.  
  16. //config element types
  17. this.ConfigElementTypes = {
  18. checkbox:{defaultValue:false,valueMember:"checked"},
  19. text:{defaultValue:""},
  20. section:{defaultValue:"none",valueMember:"data-ft"}, //closed
  21. separator: {defaultValue:"none",valueMember:"data-ft"}, //closed
  22. optionblock: {doNotSave:true}, //not to be stored
  23. textarea: {defaultValue:""},
  24. radio: {defaultValue:false},
  25. select: {defaultValue:""}, //none selected
  26. button: {doNotSave:true}, //not to be stored
  27. "button_highlight": {doNotSave:true}, //not to be stored
  28. "button_selectmulti": {doNotSave:true}, //not to be stored
  29. "button_selectprefix": {doNotSave:true}, //not to be stored
  30. hidden: {defaultValue:false},
  31. link: {defaultValue:false}, //not to be stored
  32. tabcontrol: {doNotSave:true}, //not to be stored
  33. tabelement: {doNotSave:true}, //not to be stored
  34. tabbody: {defaultValue:"none",valueMember:"data-ft"}, //closed
  35. tab: {defaultValue:"none",valueMember:"data-ft"}, //closed
  36. "float": {defaultValue:0.0},
  37. "long": {defaultValue:0},
  38. "int": {defaultValue:0},
  39. colorbox: {defaultValue:"black"},
  40. "message": {doNotSave:true}, //not to be stored
  41. selecttime: {defaultValue:""},
  42. };
  43.  
  44. //instanceable config system
  45. this.Config = function(params){
  46. var self = this;
  47.  
  48. //defaults
  49. this.storageName = "settings";
  50. this.css = "";
  51. this.logo = null;
  52. this.onOpen = null;
  53. this.onClose = null;
  54. this.onSave = null;
  55. this.settings = {};
  56. this.title = "Settings - Anonymous Script";
  57. this.fields = {}; //list of ConfigField objects
  58. this.values = {}; //saveable list of data
  59. this.sectionsAsTabs = false;
  60. this.separatorsAsTabs = false;
  61. this.useScrollIntoView = false;
  62. this.confirms = {save:true,cancel:true,"import":true, restore:true};
  63.  
  64. //init
  65. this.init = function(params) {try{
  66. params=params||{};
  67.  
  68. //get special params first
  69. if (exists(params.css)) {
  70. this.css.user = params.css;
  71. delete params.css;
  72. }
  73. //set params
  74. for (var p in params){
  75. this[p]=params[p];
  76. }
  77.  
  78. //get values
  79. this.values=this.read();
  80. //configure values and fields table
  81. this.configure();
  82. }catch(e){log("Config.init: "+e);}};
  83.  
  84. //read vars from local storage
  85. this.read = function(params) {try{
  86. params=params||{};
  87. return getOptJSON(params.storageName||this.storageName)||{};
  88. }catch(e){log("Config.read: "+e);}};
  89. //write vars to local storage
  90. this.write = function(params) {try{
  91. params=params||{};
  92. setOptJSON(params.storageName||this.storageName)||{};
  93. }catch(e){log("Config.write: "+e);}};
  94. //convert the tree structure to a 2D table of fields
  95. //get saved and default values as well as element types
  96. //pass destroy:true to clear the fields list and start over
  97. //pass reset:true to set all values to their defaults
  98. this.configure = function(params) {try{
  99. params=params||{};
  100. //destroy current config if requested
  101. if (params.destroy) this.fields={};
  102. //traverse the entire settings tree
  103. //OR just the passed settings param
  104. var settings = params.settings || this.settings;
  105. for (var field in settings) {
  106. //copy branch to 2D table
  107. data = settings[field];
  108. this.fields[field] = data;
  109. //setup default values
  110. data["default"] = (exists(data["default"]))?data["default"]:ConfigElementTypes[data.type].defaultValue;
  111. //set value for saveable types only
  112. if (!(ConfigElementTypes[data.type].doNotSave||false)){
  113. if (params.reset||false) {
  114. //set the value to its default value
  115. data.value = data["default"];
  116. this.values[field]=data.value;
  117. } else {
  118. if (exists(this.values[field])) {
  119. //a value exists, copy it to the field
  120. data.value = this.values[field];
  121. } else {
  122. //a value does not yet exist, create one
  123. data.value = data["default"];
  124. this.values[field]=data.value;
  125. }
  126. }
  127. }
  128. //traverse children if needed
  129. if (exists(data.kids)) {
  130. this.configure({
  131. settings:data.kids, //pass children
  132. parent:this.fields[field], //link parent
  133. reset:params.reset, //pass the reset command
  134. });
  135. }
  136. }
  137. }catch(e){log("Config.configure: "+e);}};
  138. //take additional settings data and append it to a specific field
  139. this.append = function(params) {try{
  140. params=params||{};
  141. //detect branch
  142. var branch = (exists(params.branch))?this.fields[params.branch].kids:this.settings;
  143. //copy data to specified branch
  144. for (var field in params.data) {
  145. branch[field]=params.data[field];
  146. }
  147.  
  148. //reconfigure just the passed branch
  149. this.configure({
  150. settings: params.data, //pass the new children
  151. parent: params.branch||null, //link parent
  152. });
  153. //return the branch to which we were appended
  154. return branch;
  155. }catch(e){log("Config.append: "+e);}};
  156. //open the config menu in an iframe
  157. this.open = function(params) {try{
  158. //confirm(this.sectionsAsTabs);
  159. params=params||{};
  160. //check if already open
  161. if (document.evaluate("//iframe[@id='Config']",document,null,9,null).singleNodeValue) return;
  162. //create iframe
  163. document.body.appendChild(
  164. this.frame = createElement("iframe",{
  165. id:"Config",
  166. style:(
  167. "position:fixed;"+
  168. "top:0; left:0;"+
  169. "opacity:0;"+
  170. "display:none !important;"+
  171. "z-index:9998;"+
  172. "width:75%; height:75%;"+
  173. "max-height:95%; max-width:95%;"+
  174. "border:1px solid #000000;"+
  175. "overflow:auto;"+
  176. ""//leave here
  177. )
  178. })
  179. );
  180. //load a blank document into our frame
  181. this.frame.src = "about:blank";
  182. //when it loads add our config page
  183. var self=this;
  184. this.frame.addEventListener("load", function(){
  185.  
  186. self.frameDoc=this.contentDocument;
  187. frameBody = this.contentDocument.getElementsByTagName("body")[0];
  188. //select display settings
  189. //use the passed field name as the top of our settings
  190. //or select the entire thing
  191. var settings=exists(params.field)?self.fields[params.field].kids:self.settings;
  192. //set up our frame's css
  193. self.frame.contentDocument.getElementsByTagName("head")[0].appendChild(
  194. createElement("style",{
  195. type: "text/css",
  196. textContent: self.css.basic + self.css.user
  197. })
  198. );
  199.  
  200. //add header and title
  201. frameBody.appendChild(
  202. createElement("div",{
  203. id: "header",
  204. className: "config_header block center",
  205. innerHTML: self.title
  206. })
  207. );
  208.  
  209. //append elements
  210. var prevSibling=null; //<-this part allows tabs at the top level
  211. for (var i in settings) {
  212. var newElem = self.addToFrame(settings[i], i, true, prevSibling, null).elem;
  213. prevSibling=frameBody.appendChild(newElem);
  214. }
  215.  
  216. //add config toolbar
  217. frameBody.appendChild(
  218. createElement('div', {id:'buttons_holder'}, [
  219. createElement('button',{
  220. //id:'saveBtn',
  221. textContent:'Save',
  222. title:'Save options and close window',
  223. //className:'saveclose_buttons',
  224. onclick:function(){self.close({doSave:true});}
  225. }),
  226. createElement('button',{
  227. //id:'cancelBtn',
  228. textContent:'Cancel',
  229. title:'Close window',
  230. //className:'saveclose_buttons',
  231. onclick:function(){self.close({doSave:false});}
  232. }),
  233. createElement('button', {
  234. //id:'resetBtn',
  235. textContent:'Restore to default',
  236. title:'Restore settings to default configuration',
  237. //className:'saveclose_buttons',
  238. onclick:function(){self.reset();}
  239. }),
  240. createElement('button', {
  241. //id:'resetBtn',
  242. textContent:'Import Settings',
  243. title:'Import saved settings as text.',
  244. //className:'saveclose_buttons',
  245. onclick:function(){self.importSettings();}
  246. }),
  247. createElement('button', {
  248. //id:'resetBtn',
  249. textContent:'Export Settings',
  250. title:'Export settings as text for storage elsewhere.',
  251. //className:'saveclose_buttons',
  252. onclick:function(){self.exportSettings();}
  253. })
  254. ])
  255. );
  256. //add some whitespace to the bottom of the page
  257. frameBody.appendChild(
  258. createElement("span",{className:"bigSpacer"})
  259. );
  260. // Show and center it
  261. self.center();
  262. // Center it on resize
  263. window.addEventListener('resize', function(){self.center();}, false);
  264. //call the onOpen function if available
  265. if (!(params.noEvents||false)) if (self.onOpen) doAction(self.onOpen);
  266. // Close frame on window close
  267. window.addEventListener('beforeunload', function(){
  268. remove(this);
  269. }, false);
  270. }, false);
  271. }catch(e){log("Config.open: "+e);}};
  272.  
  273. this.close = function(params) {try{
  274. params=params||{};
  275. //update the values list
  276. if (params.doSave||false) {
  277. var ask=this.confirms.save;
  278. if (params.noConfirm || !ask || (ask && confirm("Save options?"))) {
  279. var fields = this.fields, values = this.values;
  280. for (var f in fields) {
  281. var field=fields[f];
  282. var valueMember=ConfigElementTypes[field.type].valueMember||"value";
  283. var elem=this.frame.contentDocument.getElementById('field_'+f);
  284. if (elem) {
  285. //if (f.contains("interval")) debug.print(["before",values[f],field.value]);
  286. values[f]=(["value","checked"].inArray(valueMember))?elem[valueMember]:elem.getAttribute(valueMember);
  287. field.value=values[f];
  288. //if (f.contains("interval")) debug.print(["after",values[f],field.value]);
  289. } else {
  290. log("cannot find element: "+f);
  291. }
  292. }
  293. this.save();
  294. // Call the onSave function if available
  295. if (this.onSave) doAction(this.onSave);
  296. }
  297. } else {
  298. //ask to cancel without save
  299. var ask=this.confirms.cancel;
  300. if (!(params.noConfirm || !ask || (ask && confirm("Close without saving?")))) return;
  301. }
  302. //destroy the iframe and forget it
  303. if (this.frame) remove(this.frame);
  304. delete this.frame;
  305. //call the onClose function if available
  306. if (!params.noEvents && this.onClose) doAction(this.onClose);
  307. }catch(e){log("Config.close: "+e);}};
  308. this.reload=function(){
  309. this.close({noEvents:true, doSave:false, noConfirm:true})
  310. this.open({noEvents:true});
  311. };
  312.  
  313. this.set = function(name,val) {try{
  314. this.values[name] = val;
  315. this.fields[name].value = val;
  316. }catch(e){log("Config.set: "+e);}};
  317.  
  318. this.get = function(name) {try{
  319. return this.values[name];
  320. }catch(e){log("Config.get: "+e);}};
  321.  
  322. /* unused functions
  323. getValue : function(name, def) {try{ return (this.isGM?GM_getValue:(function(name,def){return localStorage.getItem(name)||def}))(name, def||""); }catch(e){log("Config.getValue: "+e);}},
  324.  
  325. setValue : function(name, value) {try{ return (this.isGM?GM_setValue:(function(name,value){return localStorage.setItem(name,value)}))(name, value||""); }catch(e){log("Config.setValue: "+e);}},
  326. */
  327. this.save = function(storageName) {try{
  328. var shrunk={}, fields=this.fields;
  329.  
  330. //clone the values data to preserve items we no longer have
  331. //fields for
  332. var shrunk=mergeJSON(this.values);
  333. //do not store values that match the default value
  334. for (var f in fields) {
  335. if ((ConfigElementTypes[fields[f].type].doNotSave||false) || (fields[f].value==fields[f]["default"])) {
  336. delete shrunk[f];
  337. }
  338. }
  339. setOptJSON((storageName||this.storageName),shrunk);
  340. }catch(e){log("Config.save: "+e);}};
  341.  
  342. this.reset = function() {try{
  343. var ask=this.confirms.restore;
  344. if (!ask || (ask && confirm("Reset all values to defaults?"))) {
  345. this.configure({reset:true});
  346. this.save();
  347. this.reload();
  348. }
  349. }catch(e){log("Config.reset: "+e);}};
  350.  
  351. this.selectBlock = function(e) {try{
  352. var boxes = selectNodes(".//input[@type='checkbox']",{type:6,node:e.parentNode||$(e,$("Config").contentDocument).parentNode,doc:$("Config").contentDocument});
  353. for (var i=0,box;(box=boxes.snapshotItem(i)); i++) box.checked=true;
  354. //http://i55.tinypic.com/6ih93q.png
  355. }catch(e){log("Config.selectBlock: "+e);}};
  356.  
  357. this.deselectBlock = function(e) {try{
  358. var boxes = selectNodes(".//input[@type='checkbox']",{type:6,node:e.parentNode||$(e,$("Config").contentDocument).parentNode,doc:$("Config").contentDocument});
  359. for (var i=0,box;(box=boxes.snapshotItem(i)); i++) box.checked=false;
  360. //http://i55.tinypic.com/2lk2xyw.png
  361. }catch(e){log("Config.deselectBlock: "+e);}};
  362.  
  363. //highlights option menu elements in an array, first clearing any elements provided in clearFirst array
  364. this.highlightElements =function(options,clearFirst) {try{
  365. if (clearFirst) for (var i=0;i<clearFirst.length; i++) if (box=$('field_'+clearFirst[i],$("Config").contentDocument) ) box.parentNode.className=box.className.replace(" highlight","");
  366. if (options) for (var i=0;i<options.length; i++) if (box=$('field_'+options[i],$("Config").contentDocument) ) box.parentNode.className=box.className.replace(" highlight","")+" highlight";
  367. }catch(e){log("Config.highlightElements: "+e);}};
  368.  
  369. //selects option menu elements in an array, first clearing any elements provided in clearFirst array
  370. this.selectElements = function(options,clearFirst) {try{
  371. if (clearFirst) for (var i=0;i<clearFirst.length; i++) if (box=$('field_'+clearFirst[i],$("Config").contentDocument)) box.checked=true;
  372. if (options) for (var i=0;i<options.length; i++) if (box=$('field_'+options[i],$("Config").contentDocument)) box.checked=true;
  373. }catch(e){log("Config.selectElements: "+e);}};
  374.  
  375. //selects option menu elements with a certain prefix, first clearing any elements starting with text appearing in clearFirst
  376. //this button only selects elements at the level the button appears or deeper in the option menu tree
  377. this.selectElementsByPrefix =function(prefix,clearPrefix) {try{
  378. if (clearPrefix){
  379. forNodes(".//*[starts-with(@id, 'field_"+clearPrefix+"')]",{doc:$("Config").contentDocument}, function(box){box.checked=false;});
  380. };
  381.  
  382. if (prefix){
  383. alert("prefix:"+prefix);
  384. forNodes(".//*[starts-with(@id, 'field_"+prefix+"')]",{doc:$("Config").contentDocument}, function(box){alert("checking"); box.checked=true;});
  385. };
  386. }catch(e){log("Config.selectElementsByPrefix: "+e);}};
  387.  
  388. this.addToFrame = function(field, i, k, prevSibling, objReference) {try{
  389. var isForms2Ready = (exists(jsForms) && exists(jsForms.colorPicker));
  390. var elem, elemObj, nextElem;
  391. var anch = this.frame;
  392. var Options = field.options;
  393. var isKid = k!=null && k===true;
  394. var now = timeStamp();
  395. //prefetch some stuff
  396. var title = field.title||"", label = field.label||"", value = field.value;
  397. //check that it is switched on by time
  398. if (exists(field.startDate)) {
  399. if ((now-Date.parse(field.startDate))<0) return; //not started yet
  400. }
  401. //check that it has not been switched off
  402. if (exists(field.endDate)) {
  403. if ((now-Date.parse(field.endDate))>0) return; //already ended
  404. }
  405.  
  406. var styleItems=[];
  407. //check if options change separators and sections to tabs
  408. var swapType=
  409. (this.sectionsAsTabs && field.type=="section")?"tab":
  410. (this.separatorsAsTabs && field.type=="separator")?"tab":
  411. field.type;
  412. //check if previous element was a tab
  413. var tabHeader, tabParent;
  414. if (prevSibling!=null && prevSibling.className.contains('tab_container')) tabParent = prevSibling;
  415.  
  416. /*if (swapType!="tab" && tabParent) {
  417. //detect if a tab is selected
  418. var tabSelected=selectSingleNode("./span/a[contains(@class,'tab_selected')]",{node:tabParent,doc:this.frameDoc});
  419. if (!tabSelected) {
  420. //detect the first tab element
  421. var firstTab=selectSingleNode("./span/a",{node:tabParent,doc:this.frameDoc});
  422. if (firstTab) {
  423. //toggle that tab
  424. //var e = firstTab.id.removePrefix("tab_");
  425. //this.toggleTab(e);
  426. click(firstTab);
  427. }
  428. }
  429. }*/
  430. //draw it
  431. var counterHolder=null;
  432. switch(swapType) {
  433. case 'section':
  434. elem = createElement('div', {title:title, className: 'section_header_holder'},[
  435. createElement('a', {className:'section_header center text_border_sec hottracking', href:"javascript:void(0);", onclick:function(){self.toggle(i,true);}},[
  436. createElement("span",{textContent:label}),
  437. counterHolder=createElement("span",{className:"counter"})
  438. ]),
  439. nextElem = createElement('div', {id:'field_'+i, className:'section_kids',"data-ft":value, style:"display:"+((value=="none")?"none":"block")+";"})
  440. ]);
  441. styleItems[0]=elem;
  442. break;
  443. case 'separator':
  444. elem = createElement("div", {title:title||'', className: 'separator section_header_holder'},[
  445. createElement('div', {id:'field_'+i+'_all',className:'field_label block_select_all littleButton oddBlue',type:((field.hideSelectAll)?'hidden':'button'),title:'Select All',onclick:function(){self.selectBlock(this);},style:'float:right; margin-top:4px;'},[createElement("span",{className:"resourceIcon checkAll16"})]),
  446. createElement('div', {id:'field_'+i+'_none',className:'field_label block_select_none littleButton oddBlue',type:((field.hideSelectAll)?'hidden':'button'),title:'Select None',onclick:function(){self.deselectBlock(this);},style:'float:right; margin-top:4px;'},[createElement("span",{className:"resourceIcon uncheckAll16"})]),
  447. styleItems[0]=createElement('a', {className:'separator_label text_border_sep hottracking', href:"javascript:void(0);", textContent:label, onclick:function(){self.toggle(i,true);}}),
  448. counterHolder=createElement("span",{className:"counter"}),
  449. styleItems[1]=(nextElem=createElement('div', {id:'field_'+i, className:'section_kids',"data-ft":value, style:"display:"+((value=="none")?"none":"block")+";"}))
  450. ]);
  451. break;
  452. case 'optionblock':
  453. elem = createElement("div", {title:title||'', className: 'config_var underline'},[
  454. createElement('label', {textContent:label, className:'optionblock_label', "for":'field_'+i}),
  455. createElement('div', {id:'field_'+i+'_all',className:'field_label block_select_all littleButton oddBlue',type:((field.hideSelectAll)?'hidden':'button'),title:'Select All',onclick:function(){self.selectBlock(this);}},[createElement("span",{className:"resourceIcon checkAll16"})]),
  456. createElement('div', {id:'field_'+i+'_none',className:'field_label block_select_none littleButton oddBlue',type:((field.hideSelectAll)?'hidden':'button'),title:'Select None',onclick:function(){self.deselectBlock(this);}},[createElement("span",{className:"resourceIcon uncheckAll16"})]),
  457. createElement('br'),
  458. createElement('input', {id:'field_'+i, type:'hidden', value:''}),
  459. ]);
  460. styleItems[0]=elem;
  461. break;
  462. case 'textarea':
  463. elem = createElement("span", {title:title||'', className: 'config_var'},[
  464. createElement('span', {textContent:label, className:'field_label'}),
  465. createElement('textarea', {id:'field_'+i,innerHTML:value, cols:(field.cols?field.cols:20), rows:(field.rows?field.rows:2)})
  466. ]);
  467. styleItems[0]=elem;
  468. break;
  469. case 'radio':
  470. var boxes = [];
  471. for (var j = 0,len = Options.length; j<len; j++) {
  472. boxes.push(createElement('span', {textContent:Options[j]}));
  473. boxes.push(createElement('input', {value:Options[j], type:'radio', name:i, checked:Options[j]==value?true:false}));
  474. }
  475. elem = createElement("span", {title:title||'', className: 'config_var '+(field.format||'block')},[
  476. createElement('span', {textContent:label, className:'field_label'}),
  477. createElement('span', {id:'field_'+iboxes},boxes)
  478. ]);
  479. styleItems[0]=elem;
  480. break;
  481. case 'select':
  482. var options = [];
  483. if (isObject(Options)) for (var j in Options) options.push(createElement('option',{textContent:Options[j],value:j,selected:(j==value)}));
  484. else options.push(createElement("option", {textContent:"Error - options needs to be an object type, not an array.",value:"error",selected:"selected"}));
  485.  
  486. elem = createElement(isKid ? "span" : "div", {title:title||'', className: 'config_var'},[
  487. createElement('span', {textContent:label, className:'field_label'}),
  488. createElement('select',{id:'field_'+i},options)
  489. ]);
  490. styleItems[0]=elem;
  491. break;
  492. case 'button':
  493. var tmp;
  494. elem = createElement("span", {className: 'config_var '+(field.format||'inline')},[
  495. (tmp=createElement('input', {id:'field_'+i, type:'button', value:label, size:(field.size?field.size:25), title:title||''}))
  496. ]);
  497. if (field.script) self.addEvent(tmp, 'click', field.script);
  498. styleItems[0]=elem;
  499. break;
  500. case 'button_highlight':
  501. var tmp;
  502. elem = createElement("span", {className: 'config_var '+(field.format||'inline')},[
  503. createElement('input', {id:'field_'+i, type:'button', value:label, size:(field.size?field.size:25), title:title||'',onclick:function(){self.highlightElements(field.options,field.clearfirst);}})
  504. ]);
  505. styleItems[0]=elem;
  506. break;
  507. case 'button_selectmulti':
  508. var tmp;
  509. elem = createElement("span", {className: 'config_var '+(field.format||'inline')},[
  510. createElement('input', {id:'field_'+i, type:'button', value:label, size:(field.size?field.size:25), title:title||'',onclick:function(){self.selectElements(field.options,field.clearFirst);}})
  511. ]);
  512. styleItems[0]=elem;
  513. break;
  514. case 'button_selectprefix':
  515. var tmp;
  516. elem = createElement("span", {className: 'config_var '+(field.format||'inline')},[
  517. createElement('input', {id:'field_'+i, type:'button', value:label, size:(field.size?field.size:25), title:title||'',onclick:function(){self.selectElementsByPrefix(field.prefix,field.clearPrefix);}})
  518. ]);
  519. styleItems[0]=elem;
  520. break;
  521. case 'hidden':
  522. elem = createElement("span", {title:title||'', className: 'config_var'},[
  523. createElement('input', {id:'field_'+i, type:'hidden', value:value})
  524. ]);
  525. styleItems[0]=elem;
  526. break;
  527. case 'link':
  528. elem = createElement("span", {title:title||'', className: (field.format||'block')},[
  529. createElement('a', {id:'field_'+i, href:field.href, title:title||'', textContent:field.label, target:'_blank', className:'field_label link_label'+(field.newitem?' newopt':'')})
  530. ]);
  531. styleItems[0]=elem;
  532. break;
  533.  
  534. case 'message':
  535. elem = createElement("span", {title:title||'', className: (field.format||'block')},[
  536. createElement('span', {id:'field_'+i, title:title||'', textContent:field.textContent, className:'field_label message_text'+(field.newitem?' newopt':'')})
  537. ]);
  538. styleItems[0]=elem;
  539. break;
  540. case 'selecttime':
  541. //creates an interval from some text input
  542. var timevalue = calcTime(value);
  543. var timedays = parseInt(timevalue/day);
  544. var timehours = parseInt((timevalue-(timedays*day))/hour);
  545. var timeminutes = parseInt((timevalue-(timehours*hour)-(timedays*day))/minute);
  546. var timeseconds = parseInt((timevalue-(timeminutes*minute)-(timehours*hour)-(timedays*day))/second);
  547. var daynode, hournode, minutenode, secondnode, returnnode;
  548. var fnCalcTime = function(){
  549. returnnode.value = "t:"+daynode.value+"d:"+hournode.value+"h:"+minutenode.value+"m:"+secondnode.value+"s"
  550. };
  551.  
  552. elem = createElement("span", {title:title||'', className: 'config_var'},[
  553. createElement('span', {textContent:label, className:'field_label'}),
  554. returnnode=createElement('input', {id:'field_'+i,value:value,type:"hidden"}),
  555. daynode=createElement('input', {value:timedays, type:"number", onchange:fnCalcTime,size:2}),
  556. createElement('span', {textContent:"d", className:'field_label'}),
  557. hournode=createElement('input', {value:timehours, type:"number", onchange:fnCalcTime,size:2}),
  558. createElement('span', {textContent:"h", className:'field_label'}),
  559. minutenode=createElement('input', {value:timeminutes, type:"number", onchange:fnCalcTime,size:2}),
  560. createElement('span', {textContent:"m", className:'field_label'}),
  561. secondnode=createElement('input', {value:timeseconds, type:"number", onchange:fnCalcTime,size:2}),
  562. createElement('span', {textContent:"s", className:'field_label'}),
  563. ]);
  564. styleItems[0]=elem;
  565. break;
  566. //deprecated
  567. case 'tabcontrol':
  568. elem = createElement("div",{title:title||'', className: 'tab_container'});
  569. styleItems[0]=elem;
  570. break;
  571. case 'tabelement':
  572. var value2 = self.values[field.key];
  573. elem = createElement("span",{title:title||'',className:'tab_element'},[
  574. createElement('a', {id:'tab_'+field.key,className:'tab_header text_border_sec hottracking'+((value2=='block')?" tab_selected":""),href:"javascript:void(0);", textContent:label, onclick:function(){self.toggleTab(field.key);}})
  575. ]);
  576. styleItems[0]=elem;
  577. break;
  578. case 'tabbody':
  579. elem = createElement("div",{id:'field_'+i,title:title||'',"data-ft":value,style:("display:"+value+";"),className:'tab_body'});
  580. styleItems[0]=elem;
  581. break;
  582. //end deprecated
  583. case 'tab': //tab shortform
  584. var objRef=null;
  585. if (null && isForms2Ready) {
  586. //build tabs using the jsForms library
  587. //objReference should be a jsForms.tabControl object
  588. if (objReference) {
  589. //we have a reference to a jsForms.tabControl object
  590. objRef=objReference;
  591. } else {
  592. //need to realize a tabControl for this tab
  593. objRef=new jsForms.tabControl({
  594. dock:"fill",
  595. preventAutoSelectTab:true,
  596. });
  597. }
  598. //add our tab as a jsForms.tabPage
  599. var thisTab = objRef.addTab({
  600. text:label,
  601. });
  602. //prepare for child elements to be added
  603. elem=objRef.node; //always return the tabControl
  604. nextElem=thisTab.pageNode;
  605. //set tab and tab page to be stylized
  606. styleItems[0]=thisTab.buttonNode;
  607. styleItems[1]=nextElem;
  608. } else {
  609. //build tabs using this library
  610. //create tab container if needed
  611. if (!tabParent){
  612. tabParent = createElement("div",{className:'tab_container'},[
  613. tabHeader=createElement("div",{className:'tab_header_container'})
  614. ]);
  615. }
  616. //ALWAYS return the tabContainer element so the next
  617. //element can join with it if needed
  618. elem=tabParent;
  619.  
  620. //create tab header if needed
  621. if (!tabHeader){
  622. tabHeader = tabParent.firstChild; //assume no header
  623. if (!tabHeader.className.contains('tab_header_container')) {
  624. tabParent.appendChild(tabHeader=createElement("div",{className:'tab_header_container'}) );
  625. }
  626. }
  627.  
  628. //create tab element
  629. tabHeader.appendChild(
  630. styleItems[0]=createElement("span",{className:'tab_element '},[
  631. createElement('a', {id:'tab_'+i,className:'tab_header text_border_sec hottracking'+((value=='block')?" tab_selected":""),href:"javascript:void(0);", onclick:function(){self.toggleTab(i);}},[
  632. createElement("span",{textContent:label}),
  633. counterHolder=createElement("span",{className:"counter"})
  634. ])
  635. ])
  636. );
  637. //create tab body
  638. tabParent.appendChild(
  639. styleItems[1]=(nextElem=createElement("div",{id:'field_'+i,title:title||'',"data-ft":value,style:("display:"+value+";"),className:'tab_body'}) )
  640. );
  641. }
  642. break;
  643.  
  644. case 'text':
  645. case 'float':
  646. case 'long':
  647. case 'int':
  648. case 'colorbox':
  649. var box;
  650. if (isForms2Ready && field.type=="colorbox") {
  651. var o,l;
  652. elem = createElement(isKid ? "span" : "div", {title:title||'', className: 'config_var tablerow'},[
  653. createElement('span', {textContent:label, className:'field_label tablecell'}),
  654. box=(o=jsForms.createElement("colorPicker",{
  655. dropDownSize:{height:"200px"},
  656. text:value,
  657. })).node
  658. ]);
  659. o.textNode.id="field_"+i;
  660. box.className="tablecell";
  661. } else {
  662. elem = createElement(isKid ? "span" : "div", {title:title||'', className: 'config_var'},[
  663. createElement('span', {textContent:label, className:'field_label'}),
  664. box=createElement('input', {id:'field_'+i, type:'text', value:value, size:(field.size?field.size:25)})
  665. ]);
  666. }
  667. styleItems[0]=elem;
  668. if (field.type=="colorbox" && !isForms2Ready){
  669. box.style.setProperty("background-color",value,"important");
  670. box.style.color="white";
  671. box.style.textShadow="-1px -1px 1px #000000, 1px 1px 1px #000000, 1px -1px 1px #000000, -1px 1px 1px #000000";
  672. box.style.fontWeight="bold";
  673. box.onchange=function(){
  674. this.style.setProperty("background-color",this.value,"important");
  675. }
  676. }
  677. break;
  678.  
  679. default:
  680. case 'checkbox':
  681. elem = createElement("span", {title:title||'', className: 'config_var '+(field.format||'block')},[
  682. createElement('label', {textContent:label, className:'field_label', "for":'field_'+i}),
  683. createElement('input', {id:'field_'+i, type:'checkbox', value:value, checked:value})
  684. ]);
  685. styleItems[0]=elem;
  686. }
  687. //cleanup
  688. tabContainer=null; tabParent=null;
  689.  
  690. //add special classes and styles
  691. if (styleItems.length) for (si=0,silen=styleItems.length;si<silen;si++){
  692. styleItems[si].className+=(field.newitem?' newopt'+((field.newitem===true)?"":field.newitem):'')+" "+(field.css||"");
  693. if(field.backgroundColor) styleItems[si].style.backgroundColor=field.backgroundColor;
  694. if(field.fontColor) styleItems[si].style.color=field.fontColor;
  695. }
  696.  
  697. //add its kids
  698. var newCount=(field.newitem?1:0);
  699. if (field.kids) {
  700. var kids=field.kids,prev;
  701. for (var kid in kids) {
  702. var childRet = this.addToFrame(kids[kid], kid, true, prev, objRef);
  703. (nextElem||elem).appendChild(prev=childRet.elem);
  704. newCount=newCount+childRet.newCount
  705. }
  706. }
  707. //display the new item count for this branch
  708. if (newCount && (swapType=="tab" || swapType=="section" || swapType=="separator") && counterHolder){
  709. counterHolder.textContent=newCount;
  710. counterHolder.style.display="inline-block";
  711. counterHolder.title=newCount+" new items in this section.";
  712. counterHolder.className+=" newOpt"; //make it green
  713. }
  714.  
  715. //return this branch and its new item count
  716. return {elem:elem, newCount:newCount};
  717. }catch(e){log("Config.addToFrame: "+e);}};
  718.  
  719. this.exportSettings = function(){try{
  720. var v = JSON.stringify(this.values)
  721. prompt("Copy and save these settings with a text editor such as notepad.",v);
  722. }catch(e){log("Config.exportSettings: "+e);}};
  723.  
  724. this.importSettings = function(){try{
  725. var ask=this.confirms.import
  726. if (!ask || (ask && confirm("This will overwrite your current settings. Are you sure?"))) {
  727. var v = prompt("Paste saved settings below.",null);
  728. if (v!=null && v!=""){
  729. v=JSON.parse(v);
  730. if (v) {
  731. this.values = v;
  732. this.configure();
  733. this.save();
  734. alert("Please refresh your browser to use the new settings.");
  735. } else {
  736. alert("Could not import settings!");
  737. }
  738. }
  739. }
  740. }catch(e){log("Config.importSettings: "+e);}};
  741.  
  742. this.css = {
  743. basic: 'body {background:#FFFFFF;}\n' +
  744. '.indent40 {margin-left:40%;}\n' +
  745. '* {font-family: arial, tahoma, sans-serif, myriad pro;}\n' +
  746. '.field_label {font-weight:bold; font-size:12px; margin-right:6px;}\n' +
  747. '.block {display:block;}\n' +
  748. '.saveclose_buttons {margin:16px 10px 10px 10px;padding:2px 12px 2px 12px;}\n' +
  749. '.reset, #buttons_holder, .reset a {text-align:right; color:#000000;}\n' +
  750. '.config_header {font-size:20pt; margin:0;}\n' +
  751. '.config_desc, .section_desc, .reset {font-size:9pt;}\n' +
  752. '.center {text-align:center;}\n' +
  753. '.config_var {margin:0 0 4px 0; display:block;}\n' +
  754. 'input[type="radio"] {margin-right:8px;}\n'+
  755.  
  756.  
  757. "body {color: buttontext !important; margin:0 !important; background:buttonface !important;}\n"+
  758. ".logo {width:128px; height:74px;}\n"+
  759.  
  760. '.section_header {font-size:13pt; background:#414141; color:#FFFFFF; border:1px solid #000000; margin:0;}\n' +
  761. ".section_header {border:1px solid #000000; border-radius: 5px 5px 5px 5px; color:white !important;background:buttonshadow !important; display:block; font-size: 15px !important; font-weight: 700 !important; line-height:23px !important;text-decoration:none !important;}\n"+
  762. '.section_desc {font-size:9pt; background:#EFEFEF; color:#575757; border:1px solid #CCCCCC; margin:0 0 6px 0;}\n' +
  763. ".separator_label {border:1px solid #007195; border-radius: 5px 5px 5px 5px; font-size: 15px !important; line-height:19px !important; font-weight: 700 !important; color:white !important; text-decoration:none !important; margin:0 !important; padding:1px 0 1px 6px !important; display:block !important; background:buttonshadow !important;}\n"+
  764. ".section_header_holder {border-radius: 5px 5px 5px 5px;padding:0 6px 0 6px !important; margin-bottom:6px !important; }\n"+
  765. ".section_kids {background:buttonface !important;border-radius: 0 0 5px 5px;border: 1px solid #000000 !important;border-top:0 !important;padding: 0 6px 6px !important;margin: 0 6px 0 6px !important;}\n"+
  766. "#header {font-size:18px !important;}\n"+
  767. "div.config_var span.config_var {display:inline-block !important; margin-left:10px !important;}\n"+
  768. "div.config_var {margin:0 !important; padding: 2px 0 2px 0 !important;}\n"+
  769. ".optionblock_label {display:inline-block; font-size:16px !important; font-weight:bold; padding-top:10px; color:buttontext;}\n"+
  770. //".block_select_all {margin-top:4px; background: #ccffff url('http://i55.tinypic.com/6ih93q.png') no-repeat center;width:17px;height:17px;border-radius: 2px 2px 2px 2px;}\n"+
  771. //".block_select_none {margin-top:4px; background: #ccffff url('http://i55.tinypic.com/2lk2xyw.png') no-repeat center;width:17px;height:17px;border-radius: 2px 2px 2px 2px;}\n"+
  772. ".field_label {font-size:11px !important;}\n"+
  773. ".link_label {line-height:19px; position:relative;z-index:0;padding:2px 6px 2px 6px; border-radius: 0px 25px 0px 25px / 0px 100px 0px 100px; margin-left:6px !important; text-decoration:none;border: 1px solid black; color:black !important; background:#EFF2F7 !important;}\n"+
  774. ".link_label:hover, .link_label:active {z-index:1; background:#D8DFEA !important;}\n"+
  775. "span.field_label:not([class*=\"separator\"]) {margin-right:8px !important;} label.field_label {margin:0 !important;}\n"+
  776. "span > label.field_label {margin-right:0 !important;}\n"+
  777. "select, input[type=\"text\"], textarea {background-color:window !important; color:windowtext !important; border:none !important;}\n"+
  778.  
  779. ".tab_element {display:inline-block !important;}\n"+
  780. ".tab_header {background:buttonshadow !important;color:buttonface !important; padding:2px 6px;border:1px solid #000000; border-radius: 5px 5px 0 0; margin:0 !important; font-size: 13px !important; line-height:19px !important; font-weight: 700 !important; text-decoration:none !important;position:relative;z-index:0;}\n"+
  781. ".hottracking:hover {background:highlight !important;}\n"+
  782. ".tab_body {padding: 10px !important;margin: 0 !important; background:buttonface !important;border-radius: 0 5px 5px 5px;border: 1px solid #000000 !important;display:none;position:relative;z-index:1;top:0px;}\n"+
  783. ".tab_selected {background:buttonface !important;border-bottom:0 !important;color:black !important; z-index:2; text-shadow:none !important;}\n"+
  784.  
  785. ".inline {display:inline-block;}\n"+
  786. ".block {display:block;}\n"+
  787. ".tablerow {display:table-row;}\n"+
  788. ".tablecell {display:table-cell;}\n"+
  789. ".floatright {float:right;}\n"+
  790. ".floatleft {float:left;}\n"+
  791.  
  792. ".underline {border-bottom:1px solid #70BAFF;}\n"+
  793. ".overline {border-bottom:1px solid #70BAFF;}\n"+
  794. ".hidden {display:none;}\n"+
  795. ".unreleased, .ghost, .ended {opacity:0.25;}\n"+
  796. ".highlight {background:#94BC41 !important; color:black;}\n"+
  797. ".green {background:green !important;color:black;}\n"+
  798. ".red {background:darkred !important;color:black;}\n"+
  799. ".blue {background:royalblue !important;color:black;}\n"+
  800. ".orange {background:darkorange !important;color:black;}\n"+
  801. ".yellow {background:gold !important;color:black;}\n"+
  802. ".silver {background:silver !important;color:black;}\n"+
  803. ".gray {background:gray !important;color:black;}\n"+
  804. ".white {background:white !important;color:black;}\n"+
  805. ".black {background:black !important;color:white;}\n"+
  806. ".box {border:1px solid silver;}\n"+
  807. ".newopt {background:#027B09 !important;}\n"+
  808. ".newopt1 {background:green !important;}\n"+
  809. ".newopt2 {background:darkred !important;}\n"+
  810. ".newopt3 {background:royalblue !important;}\n"+
  811.  
  812. ".text_border_sep {font-family:tahoma; text-shadow: 0 0 1px black, 0 0 1px black, 0 0 1px black, 0 0 1px black;text-transform:uppercase; font-weight:900 !important;}\n"+
  813. ".text_border_sec {font-family:tahoma; text-shadow: 0 0 1px black, 0 0 1px black, 0 0 1px black, 0 0 1px black;text-transform:uppercase; font-weight:900 !important;}\n"+
  814.  
  815. "#buttons_holder {bottom: 0; position: fixed; right: 0; z-index: 1;}\n"+
  816. ".bigSpacer {height:50px; display:block;}\n"+
  817.  
  818. //little button div
  819. ".littleButton {background-color:threedshadow; border-radius:5px; margin:1px; display:inline-block; vertical-align:middle;}\n"+
  820. ".littleButton:hover {background-color:highlight !important;}\n"+
  821. ".littleButton>img {position:relative; display:block;}\n"+
  822. ".littleButton.oddOrange {background-color:#FF9968;}\n"+
  823. ".littleButton.oddBlack {background-color:#82976E;}\n"+
  824. ".littleButton.oddBlue {background-color:#51D1EA;}\n"+
  825. ".littleButton.oddGreen {background-color:#B7E54F;}\n"+
  826. ".counter {margin-left: 5px; margin-right:-5px; font-size: .75em; padding: 2px; border: 1px solid black; border-radius: 4px; position: relative; top: -1em; display: none; line-height:1em; text-shadow:none; color:white;}\n"+
  827. "", //leave here
  828. user: ""
  829. };
  830.  
  831. this.center = function() {try{
  832. var node = this.frame;
  833. if (!node) return;
  834. var style = node.style, beforeOpacity = style.opacity;
  835. if(style.display=='none') style.opacity='0';
  836. style.display = '';
  837. style.top = Math.floor((window.innerHeight/2)-(node.offsetHeight/2)) + 'px';
  838. style.left = Math.floor((window.innerWidth/2)-(node.offsetWidth/2)) + 'px';
  839. style.opacity = '1';
  840. }catch(e){log("Config.center: "+e);}};
  841.  
  842. this.addEvent = function(el,ev,scr) {try{
  843. el.addEventListener(ev, function() { typeof scr == 'function' ? doAction(scr) : eval(scr) }, false);
  844. }catch(e){log("Config.addEvent: "+e);}};
  845.  
  846. this.toggle = function(e,newMode) {try{
  847. var node=this.frame.contentDocument.getElementById((newMode)?'field_'+e:e);
  848. node.style.display=(node.style.display!='none')?'none':'block';
  849. node.setAttribute("data-ft",node.style.display);
  850. if (this.useScrollIntoView && node.style.display!='none') node.parentNode.scrollIntoView(true);
  851. //Config.setValue(e, node.style.display);
  852. }catch(e){log("Config.toggle: "+e);}};
  853.  
  854. this.toggleTab = function(e) {try{
  855. var tabBodyNode=this.frame.contentDocument.getElementById('field_'+e);
  856. var tabHeaderNode=this.frame.contentDocument.getElementById('tab_'+e);
  857.  
  858. //unselect selected tabs
  859. var tabCtrl = tabBodyNode.parentNode;
  860. var tabs=selectNodes("./span[contains(@class,'tab_element')]/a[contains(@class,'tab_selected')] | ./div[contains(@class,'tab_header_container')]/span[contains(@class,'tab_element')]/a[contains(@class,'tab_selected')]",{node:tabCtrl,doc:$("Config").contentDocument});
  861. if (tabs) for (var i=0,tab;(tab=tabs.snapshotItem(i));i++) {
  862. var id=tab.id.substring(4);
  863. tab.className = tab.className.replace(' tab_selected','');
  864. var node=$('field_'+id,$("Config").contentDocument);
  865. node.style.display='none';
  866. node.setAttribute("data-ft","none");
  867. tab=null;
  868. }
  869.  
  870. //select the tab
  871. tabHeaderNode.className += " tab_selected";
  872. tabBodyNode.style.display="block";
  873. tabBodyNode.setAttribute("data-ft","block");
  874.  
  875. //bring into view
  876. if (this.useScrollIntoView) tabHeaderNode.scrollIntoView(true);
  877.  
  878. //cleanup
  879. tabBodyNode = null;tabHeaderNode=null;tabCtrl=null;tabs=null;
  880. }catch(e){log("Config.toggleTab: "+e);}};
  881.  
  882. //initialize
  883. if (exists(params)) {
  884. this.init(params);
  885. }
  886. log("Config initialized");
  887. return self;
  888. };
  889. })();