WM Config Interface

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

As of 14.04.2014. See ბოლო ვერსია.

ეს სკრიპტი არ უნდა იყოს პირდაპირ დაინსტალირებული. ეს ბიბლიოთეკაა, სხვა სკრიპტებისთვის უნდა ჩართეთ მეტა-დირექტივაში // @require https://update.greatest.deepsurf.us/scripts/419/1301/WM%20Config%20Interface.js.

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