Greasy Fork is available in English.

WM Common Library

A collection of useful functions and objects, some of which are specific to the Wall Manager family of scripts.

Este script no debería instalarse directamente. Es una biblioteca que utilizan otros scripts mediante la meta-directiva de inclusión // @require https://update.greatest.deepsurf.us/scripts/765/2185/WM%20Common%20Library.js

  1. // ==UserScript==
  2. // @name WM Common Library
  3. // @namespace pt wm common library
  4. // @description A collection of useful functions and objects, some of which are specific to the Wall Manager family of scripts.
  5. // @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/
  6. // @version 3.0.1.10
  7. // @copyright Charlie Ewing except where noted
  8. // ==/UserScript==
  9.  
  10. (function(){
  11. var sandbox=this;
  12.  
  13. //***************************************************************************************************************************************
  14. //***** Greasemonkey and Browser Type Validation
  15. //***************************************************************************************************************************************
  16.  
  17. // is Greasemonkey running
  18. sandbox.isGM = (typeof GM_getValue != 'undefined' && typeof GM_getValue('a', 'b') != 'undefined');
  19. sandbox.isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
  20.  
  21. //***************************************************************************************************************************************
  22. //***** Global Enumerated Values
  23. //***************************************************************************************************************************************
  24.  
  25. //enumerated string equal to script that does nothing
  26. sandbox.jsVoid="javascript:void(0)";
  27.  
  28. //time enums
  29. sandbox.second=1000;
  30. sandbox.minute=second*60;
  31. sandbox.hour=minute*60;
  32. sandbox.day=hour*24;
  33.  
  34. //***************************************************************************************************************************************
  35. //***** Data Type Verification
  36. //***************************************************************************************************************************************
  37.  
  38. //return true if o is undefined
  39. sandbox.isUndefined=function(o){try{return ((typeof o)=="undefined");}catch(e){log("wmLibrary.isUndefined: "+e);}};
  40.  
  41. //return true if o is a string
  42. sandbox.isString=function(o){try{return ((typeof o)=="string");}catch(e){log("wmLibrary.isString: "+e);}};
  43.  
  44. //return true if o is not undefined
  45. sandbox.exists=function(o){try{return (!isUndefined(o));}catch(e){log("wmLibrary.exists: "+e);}};
  46.  
  47. // Returns true if object o is an array
  48. sandbox.isArray=function(o){try{return (o instanceof Array);}catch(e){log("wmLibrary.isArray: "+e);}};
  49.  
  50. // Returns true if object o is an array and has a length > 0
  51. sandbox.isArrayAndNotEmpty=function(o){try{return isArray(o) && o.length>0;}catch(e){log("wmLibrary.isArrayAndNotEmpty: "+e);}};
  52.  
  53. // Returns true if object o is an object but not an array
  54. sandbox.isObject=function(o){try{return (((typeof o)=="object") && !isArray(o));}catch(e){log("wmLibrary.isObject: "+e);}};
  55.  
  56. //return true if o is undefined
  57. //sandbox.isNaN=function(o){try{return (o.toString()==="NaN");}catch(e){log("wmLibrary.isNaN: "+e);}};
  58.  
  59. //return integer value of object
  60. sandbox.val=function(o){try{return parseInt(o);}catch(e){log("wmLibrary.val: "+e);}};
  61.  
  62. sandbox.calcTime=function(timer) {try{
  63.  
  64. if ((typeof timer)=="integer") return timer;
  65. if (timer.match(/^(\d)/)) return val(timer);
  66.  
  67. //debug.print(timer);
  68. var t=2; //defaults to 2 minutes on error
  69. //check for U:# time format (u = millisecond count)
  70. if (timer.toLowerCase().startsWith("u:")) {
  71. t=parseInt(timer.toLowerCase().split("u:")[1]||"");
  72. return t;
  73. }
  74. //check for s:# (s = second count)
  75. if (timer.toLowerCase().startsWith("s:")) {
  76. t=parseInt(timer.toLowerCase().split("s:")[1]||"")||0;
  77. return t*1000;
  78. }
  79. //check for t:#D:#H:#M:#S time format
  80. if (timer.toLowerCase().startsWith("t:")){
  81. var fnNumberFromHMSDate = function(i,l) {
  82. var teststring = "(\\d)*?"+l;
  83. var test = new RegExp(teststring,"i");
  84. var testret = test.exec(i);
  85. //debug.print([i,teststring,testret]);
  86. return parseInt((testret||["0"])[0]);
  87. };
  88. t=timer.toLowerCase().split("t:")[1];
  89. //it should now be in "1d:2h:5m:30s" format
  90. var d = fnNumberFromHMSDate(t,"d");
  91. var h = fnNumberFromHMSDate(t,"h");
  92. var m = fnNumberFromHMSDate(t,"m");
  93. var s = fnNumberFromHMSDate(t,"s");
  94. //debug.print([d,h,m,s]);
  95. return ((s*second)+(m*minute)+(h*hour)+(d*day));
  96. }
  97. //do originally programmed time words
  98. switch(timer) {
  99. case "off": return 0; break; //off
  100. case "tenth": t = 0.1; break; // 6 seconds
  101. case "sixth": t = 0.1666667; break; // 10 seconds
  102. case "third": t = 0.3333333; break; // 20 seconds
  103. case "half": t = 0.5; break; // 30 seconds
  104. case "one": t = 1; break; // 1 minute
  105. case "two": t = 2; break; // 2 minutes
  106. case "three": t = 3; break; // 3 minutes
  107. case "four": t = 4; break; // 4 minutes
  108. case "five": t = 5; break; // 5 minutes
  109. case "ten": t = 10; break; // 10 minutes
  110. case "fifteen": t = 15; break; // 15 minutes
  111. case "thirty": t = 30; break; // 30 minutes
  112. case "hour": t = 60; break; // 1 hour
  113. case "2hour": t = 60*2; break; // 2 hours
  114. case "3hour": t = 60*3; break; // 3 hours
  115. case "4hour": t = 60*4; break; // 4 hours
  116. case "8hour": t = 60*8; break; // 8 hours
  117. case "12hour": t = 60*12; break; // 12 hours
  118. case "18hour": t = 60*18; break; // 18 hours
  119. case "24hour": t = 60*24; break; // 1 day
  120. case "36hour": t = 60*36; break; // 1.5 days
  121. case "48hour": t = 60*48; break; // 2 days
  122. case "30s2m": t = (Math.random() * 1.5) + 0.5; break; // random between 30s and 2m
  123. case "2m5m": t = (Math.random() * 3) + 2; break; // random between 2m and 5m
  124. case "5m10m": t = (Math.random() * 5) + 5; break; // random between 5m and 10m
  125. }
  126. return Math.round((t*60000)+(Math.random()*(t*100)));
  127. }catch(e){log("wmLibrary.calcTime: "+e);}};
  128.  
  129. //comprehensive convert anything to a boolean value
  130. sandbox.cBool = function(x){try{
  131. //log(x||"undefined");
  132. //capture undefined
  133. if (!exists(x)) return false;
  134. //capture nulls
  135. if (x==null) return false;
  136. //capture checkboxes
  137. if (exists(x.checked)) x=x.checked;
  138. //capture objects with value property
  139. if (exists(x.value)) x=x.value;
  140. //capture boolean values
  141. if ((typeof x)=="boolean") return x;
  142. //capture non-null objects
  143. if (isObject(x)) return true;
  144. //capture arrays
  145. if (isArray(x)) return true;
  146. //capture text
  147. if (typeof x=="string") {
  148. var trueVal=x;
  149. if (exists(x.toLowerCase)) trueVal=x.toLowerCase();
  150. switch(trueVal){
  151. case "1": case "true": case "yes": case "checked": return true; break;
  152. case "0": case "false": case "no": case "unchecked": return false; break;
  153. }
  154. }
  155. //default
  156. return Boolean(x);
  157. }catch(e){log("wmLibrary.cBool: {x="+x+"}: "+e);}};
  158.  
  159. //***************************************************************************************************************************************
  160. //***** Logging
  161. //***************************************************************************************************************************************
  162.  
  163. // cross-browser log function, turns the log variable into a function
  164. // originally from FVWM by Joe Simmons
  165. // now also catches the WM debug window first
  166. sandbox.log=function(){try{
  167. var fx, debug=this.debug;
  168. if (exists(debug)) fx=debug.print;
  169. else if (isGM) fx=GM_log;
  170. else if (window.opera) fx=opera.postError;
  171. else fx=console.log;
  172. if (fx) {var args=arguments, self=this; setTimeout(function(){fx.apply(self,args);},0); }
  173. }catch(e){console.log("WmLibrary.log: "+e);}};
  174.  
  175. //***************************************************************************************************************************************
  176. //***** Style Sheet Creation
  177. //***************************************************************************************************************************************
  178.  
  179. //append css style to the header
  180. //supply a name and this function will force that style sheet to have an id attribute equal to the name supplied
  181. //supply a doc object and the stylesheet will be put in that document instead of this one
  182. sandbox.addGlobalStyle=function(css,name,doc) {try{var head, style;head = (doc||document).getElementsByTagName('head')[0];if (!head) { return; };style = (doc||document).createElement('style');style.type = 'text/css';style.innerHTML = css;head.appendChild(style); if(name||null) style.setAttribute("id",name);}catch(e){log("wmLibrary.addGlobalStyle: "+e);}};
  183.  
  184. //***************************************************************************************************************************************
  185. //***** Mouse Events
  186. //***************************************************************************************************************************************
  187.  
  188. //click specified DOM element
  189. sandbox.click=function(e) {try{if(!e && typeof e=='string') e=document.getElementById(e);if(!e) return;var evObj = e.ownerDocument.createEvent('MouseEvents');evObj.initMouseEvent("click",true,true,e.ownerDocument.defaultView,0,0,0,0,0,false,false,false,false,0,null);e.dispatchEvent(evObj);}catch(e){log("wmLibrary.click: "+e);}};
  190.  
  191. //pretend to put the mouse over specified DOM element
  192. sandbox.mouseover=function(e) {try{if(!e && typeof e=='string') e=document.getElementById(e);if(!e) return;var evObj = e.ownerDocument.createEvent('MouseEvents');evObj.initMouseEvent("mouseover",true,true,e.ownerDocument.defaultView,0,0,0,0,0,false,false,false,false,0,null);e.dispatchEvent(evObj);}catch(e){log("wmLibrary.mouseover: "+e);}};
  193.  
  194. //***************************************************************************************************************************************
  195. //***** DOM Creation/Manipulation
  196. //***************************************************************************************************************************************
  197.  
  198. //return a DOM element by ID with optional alternate root document
  199. sandbox.$=function(ID,root) {try{return (root||document).getElementById(ID);}catch(e){log("wmLibrary.$: "+e);}};
  200.  
  201. //return new DOM element a, with parameters b, and children c
  202. sandbox.createElement=function(a,b,c) {try{
  203. if(a=="text") {return document.createTextNode(b);};
  204. var ret=document.createElement(a.toLowerCase());
  205. if(b) for(var prop in b) {
  206. if(prop.indexOf("on")==0) {
  207. ret.addEventListener(prop.substring(2),b[prop],false);
  208. } else if (
  209. ",style,accesskey,id,name,src,href,which,rel,action,method,value,data-ft".indexOf(","+prop.toLowerCase())!=-1
  210. ) {
  211. ret.setAttribute(prop.toLowerCase(), b[prop]);
  212. /*} else if (
  213. !exists(ret[prop.toLowerCase()])
  214. } {
  215. ret.setAttribute(prop.toLowerCase(), b[prop]);*/
  216. } else {
  217. ret[prop]=b[prop];
  218. }
  219. }
  220. if(c) c.forEach(
  221. function(e) {
  222. if (e) ret.appendChild(e);
  223. }
  224. );
  225. return ret;
  226. }catch(e){log("wmLibrary.createElement: "+e);}};
  227.  
  228. //return document.location.pathname
  229. sandbox.getDocName=function() {try{return document.location.pathname;}catch(e){log("wmLibrary.getDocName: "+e);}};
  230.  
  231. //remove specified DOM element
  232. sandbox.remove=function(e) {try{var node=(typeof e=='string')?$(e):e; if(node && node.parentNode) node.parentNode.removeChild(node); node=null;}catch(e){log("wmLibrary.remove: "+e);}};
  233.  
  234. //return selected nodes using xpath, with additional parameters
  235. sandbox.selectNodes=function(xPath,params){try{params=(params||{});var doc = (params.doc||document), node = (params.node||doc); return doc.evaluate(xPath,node,null,(params['type']||6),null);}catch(e){log("wmLibrary.selectNodes: "+e);}};
  236.  
  237. //return single selected node using xpath, with additional parameters
  238. sandbox.selectSingleNode=function(xPath,params){try{params=params||{}; params['type']=9;return selectNodes(xPath,params).singleNodeValue;}catch(e){log("wmLibrary.selectSingleNode: "+e);}};
  239.  
  240. //for the selected nodes using xpath and additional parameters, perform passed function
  241. sandbox.forNodes=function(xPath,params,fx){try{if(!fx) return;var nodes = selectNodes(xPath,params);if (nodes.snapshotLength) {for (var i=0,node;(node=nodes.snapshotItem(i));i++) {fx(node);}}nodes=null;}catch(e){log("wmLibrary.forNodes: "+e);}};
  242.  
  243. //fetch the selected elements from an html select multi into an array
  244. //this fetches the ELEMENT not its value
  245. sandbox.getSelectedOptions=function(elem){try{
  246. var ret=[];
  247. for (var i=0; i<elem.options.length; i++) {
  248. if (elem.options[i].selected) ret.push(elem.options[i]);
  249. }
  250. return ret;
  251. }catch(e){log("wmLibrary.getSelectedOptions: "+e);}};
  252.  
  253. //fetch the selected values from an html select multi into an array
  254. //this fetches the VALUE not the element
  255. sandbox.getSelectedOptionValues=function(elem){try{
  256. var ret=[];
  257. for (var i=0; i<elem.options.length; i++) {
  258. if (elem.options[i].selected) ret.push(elem.options[i].value);
  259. }
  260. return ret;
  261. }catch(e){log("wmLibrary.getSelectedOptionValues: "+e);}};
  262.  
  263. //attach an array of elements to a node
  264. sandbox.appendChildren = function(node,arr){try{for (var i=0,len=arr.length;i<len;i++){node.appendChild(arr[i]);};}catch(e){log("wmLibrary.appendChildren: "+e);}};
  265.  
  266. //create a set of options for a selection list based on an array
  267. sandbox.optionsFromArray = function(arr){try{var ret=[];for (var i=0,len=arr.length;i<len;i++) {ret.push(createElement("option",{value:arr[i],textContent:arr[i]}));};return ret;}catch(e){log("wmLibrary.optionsFromArray: "+e);}};
  268.  
  269. //select an element from a dropdown box with a certain value
  270. sandbox.selectDropDownElement = function(obj,value){try{var node = selectSingleNode(".//option[@value='"+value+"']",{node:obj});if (node) node.selected=true;}catch(e){log("wmLibrary.selectDropDownElement: "+e);}};
  271.  
  272. //return the value of a dropdown's selected inded
  273. sandbox.valueOfSelect = function(obj){try{return obj.options[obj.selectedIndex].value;}catch(e){log("wmLibrary.valueOfSelect: "+e);}};
  274.  
  275. //hides all snapshots or iterations in an xpathResult object
  276. sandbox.hideNodes=function(xPath,params) {try{forNodes(xPath,params,function(item){item.style.display="none";});}catch(e){log("wmLibrary.hideNodes: "+e);}};
  277.  
  278. //unhides all snapshots or iterations in an xpathResult object
  279. sandbox.showNodes=function(xPath,params) {try{forNodes(xPath,params,function(item){item.style.display="";});}catch(e){log("wmLibrary.showNodes: "+e);}};
  280.  
  281. //move element up
  282. sandbox.elementMoveUp=function(e){try{
  283. //if this element has a parent
  284. if (e.parentNode) {
  285. //and its not the first child
  286. if (e.parentNode.firstChild!=e){
  287. //move it to just before its previous sibling
  288. e.parentNode.insertBefore(e,e.previousSibling);
  289. }
  290. }
  291. return e;
  292. }catch(e){log("wmLibrary.elementMoveUp: "+e);}};
  293.  
  294. //move element down
  295. sandbox.elementMoveDown=function(e){try{
  296. //if this element has a parent
  297. if (e.parentNode) {
  298. //and its not the last child
  299. if (e.parentNode.lastChild!=e){
  300. //if the next sibling IS the last child
  301. if (e.parentNode.lastChild==e.nextSibling){
  302. //just move it to the bottom
  303. e.parentNode.appendChild(e);
  304. } else {
  305. //insert it between the next sibling and the next next sibling
  306. e.parentNode.insertBefore(e,e.nextSibling.nextSibling);
  307. }
  308. }
  309. }
  310. return e;
  311. }catch(e){log("wmLibrary.elementMoveDown: "+e);}};
  312.  
  313. //move element up to top of container
  314. sandbox.elementMoveTop=function(e){try{
  315. //if this element has a parent
  316. if (e.parentNode) {
  317. //and its not the first child
  318. if (e.parentNode.firstChild!=e){
  319. //move it to the top of the container
  320. e.parentNode.insertBefore(e,e.parentNode.firstChild);
  321. }
  322. }
  323. return e;
  324. }catch(e){log("wmLibrary.elementMoveTop: "+e);}};
  325.  
  326. //move element up to top of container
  327. sandbox.elementMoveBottom=function(e){try{
  328. //if this element has a parent
  329. if (e.parentNode) {
  330. //and its not the first child
  331. if (e.parentNode.lastChild!=e){
  332. //move it to the bottom of the container
  333. e.parentNode.appendChild(e);
  334. }
  335. }
  336. return e;
  337. }catch(e){log("wmLibrary.elementMoveBottom: "+e);}};
  338.  
  339. //sort an element's children by an attribute
  340. sandbox.elementSortChildren=function(e,by){try{
  341. by=by||"name";
  342. if (e && e.childNodes) {
  343. //pack into an array
  344. var ret=[];
  345. for (var n=0;n<e.childNodes.length;n++) {
  346. ret.push(e.childNodes[n]);
  347. }
  348. //sort the array
  349. ret.sort(function(a,b){return a[by]>b[by]});
  350. //fix order of display
  351. for (var n=0;n<ret.length;n++) {
  352. e.appendChild(ret[n]);
  353. }
  354. //clean up
  355. ret=null;
  356. }
  357. }catch(e){log("wmLibrary.elementSortChildren: "+e);}};
  358.  
  359. //remove all of a node's child nodes
  360. sandbox.removeAllChildren=function(e){
  361. var node=e.childNodes[0];
  362. while (node) {
  363. remove(node);
  364. node=e.childNodes[0];
  365. }
  366. };
  367.  
  368. //return the real url of a location
  369. sandbox.realURL=function() {try{var u=window.location.href, host=window.location.host, protocol=window.location.protocol+"//", hash=window.location.hash;if(hash!="" && (/#\/.*\.php/).test(hash)) u=protocol+host+hash.split("#")[1];else if(hash!="" && hash.find("#")) u=u.split("#")[0];if (u.substr(-1) === "#") u=u.split("#")[0];return u;}catch(e){log("wmLibrary.realURL: "+e);}};
  370.  
  371. // compile and return the true x,y scroll offset onscreen of an element in Firefox
  372. sandbox.trueScrollOffset = function(o){try{
  373. var offset={left:o.scrollLeft,top:o.scrollTop}, parentOffset=null;
  374. if (!(o==document.body) && !(0==document.documentElement) && o.parentNode) parentOffset=trueScrollOffset(o.parentNode);
  375. if (parentOffset) {
  376. offset.left+=parentOffset.left||0;
  377. offset.top+=parentOffset.top||0;
  378. }
  379. return offset;
  380. }catch(e){log("wmLibrary.trueScrollOffset: "+e);}},
  381.  
  382. // compile and return the true x,y offset onscreen of an element in Firefox
  383. sandbox.trueOffset = function(o){try{
  384. var offset={left:o.offsetLeft,top:o.offsetTop}, parentOffset=null;
  385. if (o.offsetParent) parentOffset=trueOffset(o.offsetParent);
  386. if (parentOffset) {
  387. offset.left+=parentOffset.left||0;
  388. offset.top+=parentOffset.top||0;
  389. }
  390. return offset;
  391. }catch(e){log("wmLibrary.trueOffset: "+e);}},
  392.  
  393. //force a page to transition to new location s even if changing the document location does not work
  394. sandbox.linkTo = function(s) {try{
  395. var link=document.body.appendChild(createElement("a",{href:s,target:"_top"}));
  396. click(link);
  397. }catch(e){log("wmLibrary.linkTo: "+e);}};
  398.  
  399. //***************************************************************************************************************************************
  400. //***** Date/Time
  401. //***************************************************************************************************************************************
  402.  
  403. //return a unix timestamp
  404. sandbox.timeStamp=function(){try{return (new Date()).getTime();}catch(e){log("wmLibrary.timeStamp: "+e);}};
  405.  
  406. //return a facebook timestamp without millisecond data
  407. sandbox.timeStampNoMS=function(){try{var t=timeStamp().toString(); return t.substr(0,t.length-3);}catch(e){log("wmLibrary.timeStampNoMS: "+e);}};
  408.  
  409. //returns a guaranteed unique timestamp in base36 prefixed with an underscore
  410. sandbox.unique=function(){try{var now=timeStamp();var newnow=now;while (newnow==now){newnow=timeStamp();} return "_"+(newnow.toString(36));}catch(e){log("wmLibrary.unique: "+e);}};
  411.  
  412. //***************************************************************************************************************************************
  413. //***** String Prototype Additions
  414. //***************************************************************************************************************************************
  415.  
  416. //return true if string starts with s
  417. sandbox.String.prototype.startsWith = function(s) {try{if (this.length<s.length) return false; else return (this.substring(0,s.length)===s)}catch(e){log("wmLibrary.String.prototype.startsWith: "+e);}};
  418.  
  419. //return true if string ends with s
  420. sandbox.String.prototype.endsWith = function(s) {try{if (this.length<s.length) return false; else return (this.substring(this.length-s.length,s.length)===s)}catch(e){log("wmLibrary.String.prototype.endsWith: "+e);}};
  421.  
  422. //return true if string contains s
  423. sandbox.String.prototype.find = function(s) {try{
  424. return (this.indexOf(s) != -1);
  425. }catch(e){log("wmLibrary.String.prototype.find: "+e);}};
  426. sandbox.String.prototype.contains = function(s) {return this.find(s);};
  427.  
  428. //inserts string s into this string at position startIndex
  429. sandbox.String.prototype.insert = function(s,startIndex) {try{
  430. return this.substr(0,startIndex)+s+this.substr(startIndex,this.length-startIndex);
  431. }catch(e){log("wmLibrary.String.prototype.insert: "+e);}};
  432.  
  433. //pads the string with space or a specific character, on the left
  434. //strings already longer than totalLength are not changed
  435. sandbox.String.prototype.padLeft = function(totalLength,c) {try{
  436. c=(c||" ").charAt(0);
  437. if (totalLength>0){
  438. return (totalLength<=this.length)?this:
  439. c.repeat((totalLength-this.length))+this;
  440. }
  441. }catch(e){log("wmLibrary.String.prototype.padLeft: "+e);}};
  442.  
  443. //pads the string with space or a specific character, on the left
  444. //strings already longer than totalLength are not changed
  445. sandbox.String.prototype.padRight = function(totalLength,c) {try{
  446. c=(c||" ").charAt(0);
  447. if (totalLength>0){
  448. return (totalLength<=this.length)?this:
  449. this+c.repeat((totalLength-this.length));
  450. }
  451. }catch(e){log("wmLibrary.String.prototype.padright: "+e);}};
  452.  
  453. //return the string as an array of characters
  454. sandbox.String.prototype.toCharArray = function() {try{
  455. return this.split(/(.|\n|\r)/g);
  456. }catch(e){log("wmLibrary.String.prototype.toCharArray: "+e);}};
  457.  
  458. //return the passed string minus spaces
  459. sandbox.String.prototype.noSpaces = function(s) {try{return (this.replace(/\s+/g,''));}catch(e){log("wmLibrary.String.prototype.noSpaces: "+e);}};
  460.  
  461. //return the passed string with word first letters capitalized
  462. sandbox.String.prototype.upperWords = function(s) {try{return (this+'').replace(/^(.)|\s(.)/g, function($1){return $1.toUpperCase();});}catch(e){log("wmLibrary.String.prototype.upperWords: "+e);}};
  463.  
  464. //return the passed string repeated n times
  465. sandbox.String.prototype.repeat = function(n) {try{return new Array(n+1).join(this);}catch(e){log("wmLibrary.String.prototype.repeat: "+e);}};
  466.  
  467. //return the passed string minus line breaks
  468. sandbox.String.prototype.noLineBreaks = function(s) {try{return (this.replace(/(\r\n|\n|\r)/gm," "));}catch(e){log("wmLibrary.String.prototype.noLineBreaks: "+e);}};
  469.  
  470. //return the passed string without beginning or ending quotes
  471. sandbox.String.prototype.unQuote = function() {try{return this.replace(/^"|"$/g, '');}catch(e){log("wmLibrary.String.prototype.unQuote: "+e);}};
  472.  
  473. //return the passed string without beginning or ending quotes
  474. sandbox.String.prototype.quote = function() {try{return "\""+this+"\"";}catch(e){log("wmLibrary.String.prototype.quote: "+e);}};
  475.  
  476. //return the passed string without beginning or ending brackets
  477. sandbox.String.prototype.unBracket = function() {try{return this.replace(/^\[|\]$/g, '');}catch(e){log("wmLibrary.String.prototype.unBracket: "+e);}};
  478.  
  479. //return the passed string without beginning spaces
  480. sandbox.String.prototype.trimStart = function(){try{
  481. return this.replace(/^\s\s*/, '');
  482. }catch(e){log("wmLibrary.String.prototype.trimStart: "+e);}};
  483.  
  484. //return the passed string without ending spaces
  485. sandbox.String.prototype.trimEnd = function(){try{
  486. return this.replace(/\s\s*$/, '');
  487. }catch(e){log("wmLibrary.String.prototype.trimEnd: "+e);}};
  488.  
  489. //return the passed string without beginning or ending spaces
  490. sandbox.String.prototype.trim = function(){try{
  491. return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  492. }catch(e){log("wmLibrary.String.prototype.trim: "+e);}};
  493.  
  494. //assuming passed string is a url parameter list, return named parameter's value or ""
  495. //this works great for both search and hash parts
  496. //do not pass a document.location without first splitting off search and hash parts
  497. sandbox.String.prototype.getUrlParam = function(s) {try{
  498. var r=this.removePrefix("#").removePrefix("?").split("&");
  499. for (var p=0,param;(param=r[p]);p++){
  500. if ( param.startsWith(s+"=") || param==s ) {
  501. return (param.split("=")[1]||null);
  502. }
  503. }
  504. return null;
  505. }catch(e){log("wmLibrary.String.prototype.getUrlParam: "+e);}};
  506.  
  507. //return passed string with word added to end. words are separated by spaces
  508. //alternately accepts an array of words to add
  509. sandbox.String.prototype.addWord= function(word){try{
  510. if (!isArray(word)) word=[word];
  511. var words = this.split(" ");
  512. var ret=this;
  513. for (var w=0,len=word.length;w<len;w++){
  514. if (!words.inArray(word[w])) ret=ret+" "+word;
  515. }
  516. return ret;
  517. }catch(e){log("wmLibrary.String.prototype.addWord: "+e);}};
  518.  
  519. //return passed string minus specified word
  520. //alternately accepts an array of words to remove
  521. sandbox.String.prototype.removeWord= function(word){try{
  522. if (!isArray(word)) word=[word];
  523. var words=this.split(" ");
  524. var ret;
  525. for (var w=0,len=word.length;w<len;w++){
  526. ret = words.removeByValue(word[w]);
  527. }
  528. return ret.join(" ");
  529. }catch(e){log("wmLibrary.String.prototype.removeWord: "+e);}};
  530.  
  531. //return true if passed string contains word
  532. sandbox.String.prototype.containsWord= function(word){try{return this.split(" ").inArray(word);}catch(e){log("wmLibrary.String.prototype.containsWord: "+e);}};
  533.  
  534. //return passed string with word replaced with word2
  535. sandbox.String.prototype.replaceWord= function(word,word2){try{return this.split(" ").replace(word,word2).join(" ");}catch(e){log("wmLibrary.String.prototype.replaceWord: "+e);}};
  536.  
  537. //return passed string with word toggled
  538. sandbox.String.prototype.toggleWord= function(word){try{if (this.containsWord(word)) return this.removeWord(word); return this.addWord(word);}catch(e){log("wmLibrary.String.prototype.toggleWord: "+e);}};
  539.  
  540. //return passed string with word toggled based on a boolean input
  541. sandbox.String.prototype.toggleWordB = function(bool,word){try{
  542. return this[(bool?"add":"remove")+"Word"](word);
  543. }catch(e){log("wmLibrary.String.prototype.toggleWordB: "+e);}};
  544.  
  545. //return passed string with word swapped for another based on a boolean input
  546. //if bool==true then we return string including word1 and excluding word2
  547. //else we return string including word2 and excluding word1
  548. sandbox.String.prototype.swapWordB = function(bool,word1,word2){try{
  549. return this.replaceWord((bool?word2:word1),(bool?word1:word2));
  550. }catch(e){log("wmLibrary.String.prototype.swapWordB: "+e);}};
  551.  
  552. //return passed string minus prefix of s if it exists
  553. sandbox.String.prototype.removePrefix = function(s){try{if (this.startsWith(s)) {return this.substring(s.length);} else return this;}catch(e){log("wmLibrary.String.prototype.removePrefix: "+e);}};
  554.  
  555. //return passed string minus suffix of s if it exists
  556. sandbox.String.prototype.removeSuffix = function(s){try{if (this.endsWith(s)) {return this.substring(0,this.length-s.length);} else return this;}catch(e){log("wmLibrary.String.prototype.removeSuffix: "+e);}};
  557.  
  558. // visual basic alternate for string.toLowerCase()
  559. sandbox.String.prototype.lcase = function() {try{return this.toLowercase();}catch(e){log("wmLibrary.String.prototype.lcase: "+e);}};
  560.  
  561. // visual basic alternate for string.toUpperCase()
  562. sandbox.String.prototype.ucase = function() {try{return this.toUppercase();}catch(e){log("wmLibrary.String.prototype.ucase: "+e);}};
  563. // copy the calling string to the clipboard (IE or GM)
  564. sandbox.String.prototype.toClipboard = function() {try{
  565. if (window.clipboardData){
  566. window.clipboardData.setData("Text", this);
  567. } else if (unsafeWindow) {
  568. try{
  569. unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
  570. } catch(e){
  571. log("wmLibrary.String.prototype.toClipboard: Cannot enable privelege 'UniversalXPConnect'. Be sure that 'signed.applets.codebase_principal_support' is set to true in 'about:config'");
  572. }
  573. const clipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
  574. clipboardHelper.copyString(this);
  575. } else {
  576. log("wmLibrary.String.prototype.toClipboard: Cannot perform task");
  577. }
  578. } catch(e){log("wmLibrary.String.prototype.toClipboard: "+e);}};
  579.  
  580. //replaces all instances of {x} with passed argument x
  581. //or arguments[0][x] when the first argument is an array
  582. sandbox.String.prototype.format = function() {try{
  583. var ret = this;
  584. var args=arguments; //use argument mode
  585. if (isArray(args[0])) args=args[0]; //switch to array mode
  586. for (var i = 0; i < args.length; i++) {
  587. var re = new RegExp('\\{'+i+'\\}', 'gi');
  588. ret = ret.replace(re, args[i]);
  589. }
  590. return ret;
  591. }catch(e){log("wmLibrary.String.prototype.format: "+e);}};
  592.  
  593. //similar to String.format, except that instances of {%x} are replaced
  594. //instead of instances of {x}
  595. sandbox.String.prototype.format2 = function() {try{
  596. var ret = this;
  597. var args=arguments; //use argument mode
  598. if (isArray(args[0])) args=args[0]; //switch to array mode
  599. for (var i=0; i < args.length; i++) {
  600. var re = new RegExp('\\{%'+i+'\\}', 'gi');
  601. ret = ret.replace(re, args[i]);
  602. }
  603. return ret;
  604. }catch(e){log("wmLibrary.String.prototype.format2: "+e);}};
  605.  
  606. //returns true if the string is zero-length
  607. sandbox.String.prototype.isEmpty = function() {try{
  608. return this.length==0;
  609. }catch(e){log("wmLibrary.String.prototype.isEmpty: "+e);}};
  610.  
  611. //format a JSON string with linebreaks and indents
  612. //with optional indent number to set indent length in spaces
  613. //default intent is a tab character
  614. sandbox.String.prototype.formatJSON = function(indent) {try{
  615. indent=(indent)?(" ").repeat(indent):"\t";
  616. //first lets convert the supposed JSON string to an actual object
  617. //so we can validate that it is of good format
  618. var topObj=JSON.parse(this);
  619. //if we got this far, it is valid
  620. //make a function to spell our our branches
  621. var writeBranch=function(obj,name,level){
  622. var ret="";
  623. //start our output string
  624. ret+=(level)?indent.repeat(level):"";
  625. ret+=(name)?JSON.stringify(name)+": ":"";
  626. ret+=(isArray(obj))?
  627. "["+((!obj.isEmpty())?
  628. "\n":
  629. ""
  630. ):
  631. (isObject(obj))?
  632. "{"+((!methodsToArray(obj).isEmpty())?
  633. "\n":
  634. ""
  635. ):
  636. "";
  637. //draw the inside object(s)
  638. var c=0;
  639. if (isArray(obj)) for (var i=0,len=obj.length;i<len;i++){
  640. //write arrays out
  641. if (i>0) ret+=",\n";
  642. ret+=writeBranch(obj[i],null,level+1);
  643. } else if (isObject(obj)) for (var i in obj){
  644. if (c>0) ret+=",\n";
  645. //write objects out
  646. ret+=writeBranch(obj[i],i,level+1);
  647. c++;
  648. } else {
  649. //branch is not an object or array
  650. ret+=JSON.stringify(obj);
  651. }
  652. //end our output string
  653. ret+=(isArray(obj))?
  654. ((!obj.isEmpty())?
  655. "\n"+((level)?
  656. indent.repeat(level):
  657. ""
  658. ):
  659. ""
  660. )+"]":
  661. (isObject(obj))?
  662. ((!methodsToArray(obj).isEmpty())?
  663. "\n"+((level)?
  664. indent.repeat(level):
  665. ""
  666. ):
  667. ""
  668. )+"}":
  669. "";
  670. //back to previous branch
  671. return ret;
  672. }
  673. //start writing the branches
  674. return writeBranch(topObj,null,0);
  675. }catch(e){log("wmLibrary.String.prototype.formatJSON: "+e);}};
  676.  
  677. //returns the longested quoted text within the calling string
  678. sandbox.String.prototype.longestQuoteWithin = function() {try{
  679. var p=0, c=0, s="", a=0, b=0, l=0;
  680. while (p<this.length){
  681. a=this.indexOf('"', p);
  682. if (a!=-1) {
  683. p=a+1;
  684. b=this.indexOf('"',p);
  685. if (b!=-1) {
  686. p=b+1;
  687. l=b-a;
  688. if (l>c) {
  689. c=l;
  690. s=this.substr(a+1,l-1);
  691. }
  692. } else {
  693. p=this.length;
  694. }
  695. } else {
  696. p=this.length;
  697. }
  698. }
  699. return s;
  700. }catch(e){log("wmLibrary.String.prototype.longestQuoteWithin: "+e);}};
  701.  
  702. //***************************************************************************************************************************************
  703. //***** Array Prototype Additions
  704. //***************************************************************************************************************************************
  705.  
  706. //returns true if the array is zero-length
  707. sandbox.Array.prototype.isEmpty = function() {try{
  708. return this.length==0;
  709. }catch(e){log("wmLibrary.Array.prototype.isEmpty: "+e);}};
  710.  
  711. //return passed array with element x and element y swapped
  712. sandbox.Array.prototype.swap = function (x,y) {try{
  713. var b = this[x];
  714. this[x] = this[y];
  715. this[y] = b;
  716. return this;
  717. }catch(e){log("wmLibrary.Array.prototype.swap: "+e);}};
  718.  
  719. //return true if a value exists in the array
  720. //with optional startIndex
  721. //and optional count which specifies the number of elements to examine
  722. sandbox.Array.prototype.inArray = function(value,startIndex,count) {try{
  723. startIndex=startIndex||0;
  724. if (startIndex>=this.length) {
  725. //log("wmLibrary.Array.prototype.inArray: Error: startIndex out of bounds");
  726. return false;
  727. }
  728. if (exists(count) && count<1) {
  729. //log("wmLibrary.Array.prototype.inArray: Error: count is less than 1");
  730. return false;
  731. }
  732. var c=0;
  733. for(var i=this.length-1; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i--) {
  734. c++;
  735. if(this[i]==value) return true;
  736. }
  737. return false;
  738. }catch(e){log("wmLibrary.Array.prototype.inArray: "+e);}};
  739.  
  740. //alias for inArray
  741. sandbox.Array.prototype.contains = function(value,startIndex,count) {return this.inArray(value,startIndex,count);};
  742.  
  743. //return the location of a value in an array
  744. //with optional startIndex
  745. //and optional count which specifies the number of elements to examine
  746. sandbox.Array.prototype.inArrayWhere = function(value,startIndex,count) {try{
  747. startIndex=startIndex||0;
  748. if (startIndex>=this.length) {
  749. //log("wmLibrary.Array.prototype.inArrayWhere: Error: startIndex out of bounds");
  750. return -1;
  751. }
  752. if (exists(count) && count<1) {
  753. //log("wmLibrary.Array.prototype.inArrayWhere: Error: count is less than 1");
  754. return -1;
  755. }
  756. var c=0;
  757. for(var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++) {
  758. c++;
  759. if(this[i]==value) return i;
  760. }
  761. return -1;
  762. }catch(e){log("wmLibrary.Array.prototype.inArrayWhere: "+e);}};
  763. //alias for inArrayWhere
  764. sandbox.Array.prototype.indexOf = function(value,startIndex,count) {return this.inArrayWhere(value,startIndex,count);};
  765.  
  766. //return the location of the last occurence of value in an array
  767. //with optional startIndex
  768. //and optional count which specifies the number of elements to examine
  769. sandbox.Array.prototype.lastIndexOf = function(value,startIndex,count) {try{
  770. startIndex=startIndex||0;
  771. if (startIndex>=this.length) {
  772. //log("wmLibrary.Array.prototype.lastIndexOf: Error: startIndex out of bounds");
  773. return -1;
  774. }
  775. if (exists(count) && count<1) {
  776. //log("wmLibrary.Array.prototype.lastIndexOf: Error: count is less than 1");
  777. return -1;
  778. }
  779. var c=0;
  780. for(var i=this.length; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i++) {
  781. c++;
  782. if(this[i]==value) return i;
  783. }
  784. return -1;
  785. }catch(e){log("wmLibrary.Array.prototype.lastIndexOf: "+e);}};
  786.  
  787. //return true if the location of value is 0
  788. sandbox.Array.prototype.startsWith = function(value){return this.inArrayWhere(value)===0;}
  789.  
  790. //return the last value in an array
  791. sandbox.Array.prototype.last = function() {try{return this[this.length - 1];}catch(e){log("wmLibrary.Array.prototype.last: "+e);}};
  792.  
  793. //return true if the content of the last index is equal to value
  794. sandbox.Array.prototype.endsWith = function(value){return this.last()===value;}
  795.  
  796. //return the array will spaces removed from every element
  797. sandbox.Array.prototype.noSpaces = function() {try{for(var i=0,l=this.length; i<l; i++) {this[i]=this[i].noSpaces();}; return this;}catch(e){log("wmLibrary.Array.prototype.noSpaces: "+e);}};
  798.  
  799. //remove the first instance of a value in an array
  800. //now accepts an array of values to remove
  801. //removes the first instance of every item in array passed
  802. //returns the calling array
  803. sandbox.Array.prototype.removeByValue = function(values) {try{
  804. if (!isArray(values)) values=[values];
  805. for (var i=0,len=values.length; i<len;i++) {
  806. var e=this.inArrayWhere(values[i]);
  807. if(e>=0)this.splice(e,1);
  808. }
  809. return this;
  810. }catch(e){log("wmLibrary.Array.prototype.removeByValue: "+e);}};
  811.  
  812. //replace all instances of a value in an array
  813. //returns the calling array
  814. sandbox.Array.prototype.replaceAll = function(val, val2) {try{
  815. var i=this.inArrayWhere(val);
  816. while(i>=0) {
  817. this[i]=val2;
  818. i=this.inArrayWhere(val,i+1);
  819. }
  820. return this;
  821. }catch(e){log("wmLibrary.Array.prototype.replaceAll: "+e);}};
  822.  
  823. //remove all instances of a value in an array
  824. //now accepts an array of values to remove
  825. //returns the calling array
  826. sandbox.Array.prototype.removeAllByValue = function(values) {try{
  827. if (!isArray(values)) values=[values];
  828. for (var i=0,len=values.length; i<len;i++) {
  829. var e=this.inArrayWhere(values[i]);
  830. while (e>=0){
  831. if(e>=0)this.splice(e,1);
  832. e=this.inArrayWhere(values[i],e+1);
  833. }
  834. }
  835. return this;
  836. }catch(e){log("wmLibrary.Array.prototype.removeAllByValue: "+e);}};
  837.  
  838. //replace the first instance of a value in an array
  839. //returns the calling array
  840. sandbox.Array.prototype.replace = function(val, val2) {try{var i=this.inArrayWhere(val);if(i>=0)this[i]=val2;return this;}catch(e){log("wmLibrary.Array.prototype.replace: "+e);}};
  841.  
  842.  
  843. //remove element i of an array
  844. //returns the calling array
  845. sandbox.Array.prototype.remove = function(i) {try{this.splice(i,1); return this;}catch(e){log("wmLibrary.Array.prototype.remove: "+e);}};
  846.  
  847. //remove elements beyond specified new size
  848. //or add elements to fill the new size equal to defaultValue
  849. sandbox.Array.prototype.resize = function(newSize,defaultValue) {try{
  850. if (this.length>newSize) {
  851. this.splice(newSize,this.length-newSize);
  852. } else {
  853. for (var i=this.length;i<newSize;i++){
  854. this[i]=defaultValue;
  855. }
  856. }
  857. return this;
  858. }catch(e){log("wmLibrary.Array.prototype.resize: "+e);}};
  859.  
  860. //return a random element of an array
  861. sandbox.Array.prototype.pickRandom = function () {try{var i=Math.floor(Math.random()*this.length); return this[i];}catch(e){log("wmLibrary.Array.prototype.pickRandom: "+e);}};
  862.  
  863. //sorts an array by string length, priming it for text searches, putting longest strings first so that "pea" is not found before "peanut"
  864. //returns the calling array
  865. sandbox.Array.prototype.optimize = function(){try{if (this.length>1) {for (var i=this.length-1;i>0;i--) {for (var i2=i-1;i2>0;i2--){if (this[i].length > this[i2].length) {var b=this[i]; this[i]=this[i2]; this[i2]=b; b=null;}}}}; return this;}catch(e){log("wmLibrary.Array.prototype.optimize: "+e);}};
  866.  
  867. //returns a shallow copy of the calling array
  868. sandbox.Array.prototype.clone = function(){try{
  869. var ret=[];
  870. if (this.length) for (var i=0,len=this.length; i<len; i++){
  871. ret.push(this[i]);
  872. }
  873. return ret;
  874. }catch(e){log("wmLibrary.Array.prototype.clone: "+e);}};
  875.  
  876. //reverses the elements of an array
  877. //with optional startIndex
  878. //and optional count which limits the reverse section
  879. //if startIndex+count is greater than the length of the array
  880. //then only the available section is reversed
  881. //returns the calling array
  882. sandbox.Array.prototype.reverse = function(startIndex,count){try{
  883. startIndex=startIndex||0;
  884. if (startIndex>=this.length) {
  885. //log("wmLibrary.Array.prototype.reverse: Error: startIndex out of bounds");
  886. return -1;
  887. }
  888. if (exists(count) && count<1) {
  889. //log("wmLibrary.Array.prototype.reverse: Error: count is less than 1");
  890. return -1;
  891. }
  892. var endIndex=(exists(count))?startIndex+count:this.length-1;
  893. if (endIndex>this.length-1) endIndex=this.length-1;
  894. while (startIndex>endIndex){
  895. this.swap(startIndex,endIndex);
  896. startIndex++;
  897. endIndex--;
  898. }
  899. return this;
  900. }catch(e){log("wmLibrary.Array.prototype.reverse: "+e);}};
  901.  
  902. //sets a range of elements in the array to the defaultValue
  903. //returns the calling array
  904. sandbox.Array.prototype.clear = function(startIndex,count,defaultValue){try{
  905. if (count>0 && this.length>startIndex) {
  906. for (var i=startIndex,len=this.length; (i<len && i<(startIndex+count)); i++){
  907. this[i]=defaultValue;
  908. }
  909. }
  910. return this;
  911. }catch(e){log("wmLibrary.Array.prototype.clear: "+e);}};
  912.  
  913. //copies elements from this array to a destination destArray
  914. //starting in this array at sourceIndex
  915. //and pasting into the destArray at destIndex
  916. //where length is the number of elements to copy
  917. //pasting beyond the higher bounds of the destArray simply increases the array size
  918. //returns the calling array
  919. sandbox.Array.prototype.copy = function(sourceIndex,destArray,destIndex,length){try{
  920. if (!isArray(destArray)) {
  921. log("wmLibrary.Array.prototype.copy: Error: destArray is not an array");
  922. return this;
  923. }
  924. if (sourceIndex >= this.length) {
  925. //log("wmLibrary.Array.prototype.copy: Error: sourceIndex out of bounds");
  926. return this;
  927. }
  928. for (var i=0; i<length; i++){
  929. destArray[destIndex+i]=this[sourceIndex+i];
  930. }
  931. return this;
  932. }catch(e){log("wmLibrary.Array.prototype.copy: "+e);}};
  933.  
  934. //copies all elements from this array to a destination destArray
  935. //pasting into the destArray at destIndex
  936. //pasting beyond the higher bounds of the destArray simply increases the array size
  937. //returns the calling array
  938. sandbox.Array.prototype.copyTo = function(destArray,destIndex){try{
  939. if (!isArray(destArray)) {
  940. log("wmLibrary.Array.prototype.copyTo: Error: destArray is not an array");
  941. return this;
  942. }
  943. for (var i=0, len=this.length; i<len; i++){
  944. destArray[destIndex+i]=this[i];
  945. }
  946. return this;
  947. }catch(e){log("wmLibrary.Array.prototype.copyTo: "+e);}};
  948.  
  949. //returns an array containing elements from the current array where the element has parameter p equal to value v
  950. sandbox.Array.prototype.selectByParam = function(p,v) {try{var ret=[]; for(i=0;i<this.length;i++) if(this[i][p]==v) ret.push(this[i]); return ret;}catch(e){log("wmLibrary.Array.prototype.selectByParam: "+e);}};
  951.  
  952. //returns the element matched by matchFunc, or null
  953. //with optional start index
  954. //and optional count to limit the number of searched elements
  955. sandbox.Array.prototype.find = function(matchFunc,startIndex,count) {try{
  956. startIndex=startIndex||0;
  957. if (startIndex>=this.length) {
  958. //log("wmLibrary.Array.prototype.find: Error: startIndex out of bounds");
  959. return null;
  960. }
  961. if (exists(count) && count<1) {
  962. //log("wmLibrary.Array.prototype.find: Error: count is less than 1");
  963. return null;
  964. }
  965. var c=0;
  966. for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
  967. c++;
  968. if (matchFunc(this[i])) {
  969. return this[i];
  970. break;
  971. }
  972. }
  973. return null;
  974. }catch(e){log("wmLibrary.Array.prototype.find: "+e);}};
  975.  
  976. //returns the index of element matched by matchFunc, or -1
  977. //with optional startIndex
  978. //and optional count which specifies the number of elements to check
  979. sandbox.Array.prototype.findIndex = function(matchFunc,startIndex,count) {try{
  980. startIndex=startIndex||0;
  981. if (startIndex>=this.length) {
  982. //log("wmLibrary.Array.prototype.findIndex: Error: startIndex out of bounds");
  983. return -1;
  984. }
  985. if (exists(count) && count<1) {
  986. //log("wmLibrary.Array.prototype.findIndex: Error: count is less than 1");
  987. return -1;
  988. }
  989. var c=0;
  990. for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
  991. c++;
  992. if (matchFunc(this[i])) {
  993. return i;
  994. break;
  995. }
  996. }
  997. return -1;
  998. }catch(e){log("wmLibrary.Array.prototype.findIndex: "+e);}};
  999.  
  1000. //returns all elements matched by matchFunc, or null
  1001. //with optional start index
  1002. //and optional count to limit the number of elements searched
  1003. sandbox.Array.prototype.findAll = function(matchFunc,startIndex,count) {try{
  1004. startIndex=startIndex||0;
  1005. var ret=[];
  1006. if (startIndex>=this.length) {
  1007. //log("wmLibrary.Array.prototype.findAll: Error: startIndex out of bounds");
  1008. return null;
  1009. }
  1010. if (exists(count) && count<1) {
  1011. //log("wmLibrary.Array.prototype.findAll: Error: count is less than 1");
  1012. return null;
  1013. }
  1014. var c=0;
  1015. for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
  1016. c++;
  1017. if (matchFunc(this[i])) {
  1018. ret.push(this[i]);
  1019. }
  1020. }
  1021. return (isArrayAndNotEmpty(ret))?ret:null;
  1022. }catch(e){log("wmLibrary.Array.prototype.findAll: "+e);}};
  1023.  
  1024. //returns true if all elements in the array match the matchFunc
  1025. //with optional start index
  1026. //and optional count to limit the number of elements searched
  1027. sandbox.Array.prototype.trueForAll = function(matchFunc,startIndex,count) {try{
  1028. startIndex=startIndex||0;
  1029. if (startIndex>=this.length) {
  1030. //log("wmLibrary.Array.prototype.trueForAll: Error: startIndex out of bounds");
  1031. return false;
  1032. }
  1033. if (exists(count) && count<1) {
  1034. //log("wmLibrary.Array.prototype.trueForAll: Error: count is less than 1");
  1035. return false;
  1036. }
  1037. var c=0;
  1038. for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
  1039. c++;
  1040. if (!matchFunc(this[i])) {
  1041. return false;
  1042. }
  1043. }
  1044. return true;
  1045. }catch(e){log("wmLibrary.Array.prototype.trueForAll: "+e);}};
  1046.  
  1047. //returns true if array contains an element matched by the matchFunc
  1048. //with optional startIndex
  1049. //and optional count which specifies the number of elements to check
  1050. sandbox.Array.prototype.exists = function(matchFunc,startIndex,count) {try{
  1051. return this.findIndex(matchFunc,startIndex,count)!=-1;
  1052. }catch(e){log("wmLibrary.Array.prototype.exists: "+e);}};
  1053.  
  1054. //returns the last element matched by matchFunc, or null
  1055. //with optional start index
  1056. //and optional count to limit the number of searched elements
  1057. sandbox.Array.prototype.findLast = function(matchFunc,startIndex,count) {try{
  1058. startIndex=startIndex||0;
  1059. if (startIndex>=this.length) {
  1060. //log("wmLibrary.Array.prototype.findLast: Error: startIndex out of bounds");
  1061. return null;
  1062. }
  1063. if (exists(count) && count<1) {
  1064. //log("wmLibrary.Array.prototype.findLast: Error: count is less than 1");
  1065. return null;
  1066. }
  1067. var c=0;
  1068. for (var i=this.length; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i--){
  1069. c++;
  1070. if (matchFunc(this[i])) {
  1071. return this[i];
  1072. break;
  1073. }
  1074. }
  1075. return null;
  1076. }catch(e){log("wmLibrary.Array.prototype.findLast: "+e);}};
  1077.  
  1078. //returns the last element matched by matchFunc, or -1
  1079. //with optional start index
  1080. //and optional count which specifies the number of elements to check
  1081. sandbox.Array.prototype.findLastIndex = function(matchFunc,startIndex,count) {try{
  1082. startIndex=startIndex||0;
  1083. if (startIndex>=this.length) {
  1084. //log("wmLibrary.Array.prototype.findLastIndex: Error: startIndex out of bounds");
  1085. return -1;
  1086. }
  1087. if (exists(count) && count<1) {
  1088. //log("wmLibrary.Array.prototype.findLastIndex: Error: count is less than 1");
  1089. return -1;
  1090. }
  1091. var c=0;
  1092. for (var i=this.length; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i--){
  1093. c++;
  1094. if (matchFunc(this[i])) {
  1095. return i;
  1096. break;
  1097. }
  1098. }
  1099. return -1;
  1100. }catch(e){log("wmLibrary.Array.prototype.findLastIndex: "+e);}};
  1101.  
  1102. //***************************************************************************************************************************************
  1103. //***** JSON/OBJECT Construction and Matching
  1104. //***************************************************************************************************************************************
  1105.  
  1106. //returns the merge of any number of JSON objects passed as unnamed arguments
  1107. sandbox.mergeJSON_long = function(){try{
  1108. var ret = {};
  1109. //for each JSON object passed
  1110. for (var a=0,len=arguments.length;a<len;a++) {
  1111. //for each element in that object
  1112. for (var v in arguments[a]) {
  1113. if (!exists(ret[v])) {
  1114. //simply copy the element to the return value
  1115. ret[v] = arguments[a][v];
  1116. } else {
  1117. if ((typeof arguments[a][v])=="object") {
  1118. //merge the two elements, preserving tree structure
  1119. ret[v] = mergeJSON(ret[v], arguments[a][v]);
  1120. } else {
  1121. //overwrite simple variable
  1122. ret[v] = arguments[a][v];
  1123. }
  1124. }
  1125. }
  1126. }
  1127. //the problem here is that its way too recursive and jams firefox often
  1128. return ret;
  1129. }catch(e){log("wmLibrary.mergeJSON: "+e);}};
  1130.  
  1131. sandbox.mergeJSON = function(){try{
  1132. var ret = {};
  1133. //for each JSON object passed
  1134. for (var a=0,len=arguments.length;a<len;a++) {
  1135. //for each element in that object
  1136. for (var v in arguments[a]) {
  1137. //replace the initial element with that of the next
  1138. ret[v] = arguments[a][v];
  1139. }
  1140. //the problem here is that only the top level branches are preserved
  1141. }
  1142. return ret;
  1143. }catch(e){log("wmLibrary.mergeJSON: "+e);}};
  1144.  
  1145. //returns all members of an array that have a specified parameter with a specified value
  1146. //sandbox.matchByParam=function(arr,param,value){try{var ret=[];for (var i=0,e;(e=arr[i]);i++){if (e[param]==value) ret.push(e);};return ret;}catch(e){log("wmLibrary.matchByParam: "+e);}};
  1147. //returns all members of an array that have a specified parameter with a specified value
  1148. //now accepts input of array or object
  1149. //can now specify output of array or object
  1150. sandbox.matchByParam=function(o,param,value,outputType){try{
  1151. if(!exists(outputType)) outputType="array";
  1152. var inputType=(isArray(o))?"array":((typeof o) == "object")?"object":"unknown";
  1153. var ret=(outputType=="object")?{}:[]; //default to array on error
  1154.  
  1155. switch(inputType){
  1156. case "array": for (var i=0,e;(e=o[i]);i++){
  1157. switch(outputType){
  1158. case "array": if (e[param]==value) ret.push(e); break;
  1159. case "object": if (e[param]==value) ret[i]=e; break;
  1160. }
  1161. };break;
  1162.  
  1163. case "object": for (var i in o){
  1164. var e=o[i];
  1165. switch(outputType){
  1166. case "array": if (e[param]==value) ret.push(e); break;
  1167. case "object": if (e[param]==value) ret[i]=e; break;
  1168. }
  1169. };break;
  1170. }
  1171. return ret;
  1172. }catch(e){log("wmLibrary.matchByParam: "+e);}};
  1173.  
  1174. //sorts the methods of an object by method 'id' or method 'value'
  1175. //beware this may mangle some objects
  1176. sandbox.sortCollection=function(o,by){
  1177. var a=[];
  1178. for (var i in o){
  1179. a.push({id:i,value:o[i]});
  1180. }
  1181. a.sort(function(a,b){return a[by]>b[by];});
  1182. var ret={};
  1183. for (var i=0;i<a.length;i++){
  1184. ret[a[i].id]=a[i].value;
  1185. }
  1186. return ret;
  1187. };
  1188.  
  1189. // Collect all the values from parameter p in object o, traversing kids nodes
  1190. sandbox.getBranchValues=function(o,p){try{
  1191. var ret={};
  1192. for(var i in o) {
  1193. //get value p for object o's element i
  1194. if (p=="id"){ //special case for fetching a list of ID's
  1195. if (exists(o[i][p])) ret[i]=o[i][p];
  1196. else ret[i]=i;
  1197. } else if (p=="."){ //special case for fetching a list of all objects without a tree structure
  1198. ret[i]=o[i];
  1199. }
  1200.  
  1201. else if (exists(o[i][p])) ret[i]=o[i][p];
  1202. //if object o has kids, then get all the values p inside that kid k
  1203. if (o[i].kids) ret=mergeJSON(ret,getBranchValues(o[i].kids,p));
  1204. }
  1205. return ret;
  1206. }catch(e){log("wmLibrary.getBranchValues: "+e);}};
  1207.  
  1208. //convert an object's methods to an array, storing the method's key on the object as an id
  1209. sandbox.methodsToArray = function(o) {try{var ret=[]; for (var i in o) {o[i].id=o[i].id||i; ret.push(o[i])}; return ret;}catch(e){log("wmLibrary.methodsToArray: "+e);}};
  1210.  
  1211. //convert an array of objects to methods of an object using either the object's ai or name as its key
  1212. sandbox.arrayToMethods = function(a) {try{var ret={}; for (var i=0;i<a.length;i++) ret[ a[i].id||a[i].name ]=a[i]; return ret;}catch(e){log("wmLibrary.arrayToMethods: "+e);}};
  1213.  
  1214. //convert an object's methods to an array of those method names
  1215. sandbox.methodNames = function(o) {try{var ret=[]; for (i in o) ret.push(i); return ret;}catch(e){log("wmLibrary.methodNames: "+e);}};
  1216.  
  1217. //copy parts from one object to another
  1218. //used for extending one object with parts from another
  1219. //by John Resig
  1220. sandbox.extend = function(a,b) {try{
  1221. for ( var i in b ) {
  1222. //collect setter/getter functions
  1223. var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
  1224. //copy setter/getter functions
  1225. if ( g || s ) {
  1226. if ( g ) a.__defineGetter__(i, g);
  1227. if ( s ) a.__defineSetter__(i, s);
  1228. } else a[i] = b[i]; //copy vars
  1229. }
  1230. return a;
  1231. }catch(e){log("wmLibrary.extend: "+e);}};
  1232.  
  1233. //***************************************************************************************************************************************
  1234. //***** WM Specific Functions
  1235. //***************************************************************************************************************************************
  1236.  
  1237. //returns an object suitable for accText data based on an array, and allowing an idPrefix and textSuffix
  1238. sandbox.createAccTextFromArray = function(arr,idPrefix,textSuffix){try{var ret={};if (arr) {for (var i=0,len=arr.length;i<len;i++){ret[(idPrefix||'')+arr[i].noSpaces().toLowerCase()]=arr[i].upperWords()+(textSuffix||'');}};return ret;}catch(e){log("wmLibrary.createAccTextFromArray: "+e);}};
  1239.  
  1240. //writes a message to the hash section of the document location, or redirects to a location that can accept a new hash section
  1241. sandbox.sendMessage=function(s,hwnd,flag){try{
  1242. hwnd = (hwnd||window.top);
  1243. if (exists(hwnd)) try {hwnd.location.hash = s;} catch(e){
  1244. if (flag==1) hwnd.location.href = "http://apps.facebook.com/?#"+s;
  1245. else hwnd.location.href = "http://www.facebook.com/reqs.php?#"+s;
  1246. }
  1247. }catch(e){log("wmLibrary.sendMessage: "+e);}};
  1248.  
  1249. //flags for menu building function
  1250. sandbox.MENU_ID_ENFORCE_NAME=1; //causes menuFromData to return lowercase nospace names as the id instead of the calculated id
  1251.  
  1252. //inserts one or more menu option blocks based upon a data object
  1253. //marking all new items in the newitem list above as green so users can easily find your changes
  1254. sandbox.menuFromData=function(data,menuNode,newItemList,idPrefix,flags){try{
  1255. flags=(flags||0); newItemList=(newItemList||[]);
  1256. if (data) for (var m=0,len=data.length; m<len; m++) {
  1257. var text = data[m]["name"].upperWords(), event = (data[m]["event"]||"Unsorted").upperWords();
  1258. var outid = (flags==MENU_ID_ENFORCE_NAME)?data[m].name.noSpaces().toLowerCase():(data[m]["id"]||data[m]["name"]).noSpaces().toLowerCase();
  1259. var thisMenu; if( !(thisMenu=(menuNode["optblock"+event]||null) ) ) {thisMenu=(menuNode["optblock"+event]={type:"optionblock",label:event,kids:{} });}
  1260. thisMenu.kids[idPrefix+outid]={type:"checkbox",label:text,newitem:newItemList.inArray(idPrefix+outid)};
  1261. }
  1262. }catch(e){log("wmLibrary.menuFromData: "+e);}};
  1263.  
  1264. //returns a list of search strings from a data object containing id's names and events, already optimized for searching
  1265. sandbox.searchFromData=function(data,idPrefix){try{
  1266. idPrefix=(idPrefix||"");
  1267. var ret = [];
  1268. for (var m=0,mat;(mat=data[m]);m++){
  1269. ret.push(idPrefix+(mat.id||mat.name));
  1270. }
  1271. ret.optimize();
  1272. return ret;
  1273. }catch(e){log("wmLibrary.searchFromData: "+e);}};
  1274.  
  1275. //returns a list of materials from a data object containing id's names and events, already optimized for searching
  1276. sandbox.matListFromData=function(data){try{
  1277. var ret = [];
  1278. for (var m=0,mat;(mat=data[m]);m++){
  1279. ret.push(mat.name);
  1280. } ret.optimize();
  1281. return ret;
  1282. }catch(e){log("wmLibrary.matListFromData: "+e);}};
  1283.  
  1284. //returns a valid accText object from a data object containing id's names and events
  1285. sandbox.accTextFromData=function(data,idPrefix,textSuffix,flags){try{idPrefix=(idPrefix||""); textSuffix=(textSuffix||"");var ret={}; for (var m=0,mat;(mat=data[m]);m++){ret[idPrefix+((flags==MENU_ID_ENFORCE_NAME)?mat.name:(mat.id||mat.name)).noSpaces().toLowerCase()]=(mat.name+textSuffix).upperWords();} return ret;}catch(e){log("wmLibrary.accTextFromData: "+e);}};
  1286.  
  1287. //***************************************************************************************************************************************
  1288. //***** Sidekick Object
  1289. //***************************************************************************************************************************************
  1290.  
  1291. //sidekick specific functions
  1292. sandbox.Sidekick={
  1293. //init
  1294. tabID:null,
  1295. status:0,
  1296. nopopLink:"",
  1297.  
  1298. //attempts to dock the sidekick script to the wm host script
  1299. //params takes an object that contains the following parameters:
  1300. //appID(string), version(string), skType(integer),
  1301. //name(string), thumbSource(string or array),
  1302. //flags(object), icon(string), desc(string),
  1303. //addFilters(object),
  1304. //alterLink(object), accText(object),
  1305. //tests(array) and menu(object)
  1306. dock: function(params){try{
  1307. //find the dock node on this page
  1308. var door=$('wmDock');
  1309. if (!door) {
  1310. //does not exist, wait and try again later
  1311. window.setTimeout(function(){Sidekick.dock(params);}, 1000);
  1312. return;
  1313. }
  1314. //detect if a sidekick for this app is already docked
  1315. var doorMark=$('wmDoor_app'+params.appID);
  1316. if (doorMark && (params.skType==doorMark.getAttribute("value")) ) {
  1317. //a sidekick of this level is already here, cancel docking
  1318. return;
  1319. }
  1320. //setup defaults for a few of the expected parameters
  1321. params.thumbsSource=(params.thumbsSource||"app_full_proxy.php?app"+params.appID);
  1322. params.desc=(params.desc||params.name+" Sidekick (ver "+params.version+")");
  1323.  
  1324. //create a block of data to attach to the dock
  1325. var attString=JSON.stringify(params);
  1326. door.appendChild(
  1327. doorMark=createElement('div',{id:'wmDoor_app'+params.appID,'data-ft':attString,value:(params.skType||0)})
  1328. );
  1329. //doorMark.setAttribute("skType",(params.skType||0));
  1330. //confirm(doorMark.getAttribute("skType"));
  1331. //ring the buzzer so the host knows the package is ready
  1332. window.setTimeout(function(){click(door);},1000);
  1333. }catch(e){log("wmLibrary.Sidekick.dock: "+e);}},
  1334. //receive and process messages
  1335. //msg code 1 is a packet from the wm host containing data about the post we are processing
  1336. //that packet must contain at least the tab/window ID with which the WM host can access that tab again
  1337. //msg code 3 is a packet from this or a deeper iframe window about the return value for this post
  1338. //because Chrome returns NULL at event.source on msg 1, we now have to rethink
  1339. receiveMessage: function(event) {try{
  1340. if (isObject(event.data)) {
  1341. var data=event.data; //just shorten the typing
  1342. if (data.channel=="WallManager"){
  1343. log(JSON.stringify(data));
  1344. switch (data.msg) {
  1345. case 1: //get init data from wm host
  1346. //if (!Sidekick.tabID)
  1347. Sidekick.tabID=data.tabID;
  1348. log("Sidekick hears host...");
  1349. //
  1350. break;
  1351. case 3: //get message from child
  1352. if (Sidekick.tabID) {
  1353. log("Sidekick hears iframe...");
  1354. //send our status packet back to wm
  1355. Sidekick.status=data.status;
  1356. Sidekick.nopopLink=data.nopopLink||null;
  1357. //update the stored data about this post
  1358. var skChannel = getOptJSON("skChannel")||{};
  1359. skChannel[Sidekick.tabID]={
  1360. tabID:Sidekick.tabID,
  1361. status:Sidekick.status,
  1362. nopopLink:Sidekick.nopopLink,
  1363. };
  1364. log(JSON.stringify(skChannel));
  1365. setOptJSON("skChannel",skChannel);
  1366. } else {
  1367. //have not yet recieved tabID package from wm, wait a sec
  1368. setTimeout(function(){Sidekick.receiveMessage(event);},1000);
  1369. }
  1370. break;
  1371. }
  1372. }
  1373. }
  1374. }catch(e){log("wmLibrary.Sidekick.receiveMessage: "+e);}},
  1375.  
  1376. //disable the listener started below
  1377. unlisten: function(params){try{
  1378. window.removeEventListener("message", Sidekick.receiveMessage, false);
  1379. }catch(e){log("wmLibrary.Sidekick.unlisten: "+e);}},
  1380. //turn on the listener which can receive messages from wm host (if this window = window.top) or from iframes
  1381. listen: function(params){try{
  1382. window.addEventListener("message", Sidekick.receiveMessage, false);
  1383. }catch(e){log("wmLibrary.Sidekick.listen: "+e);}},
  1384.  
  1385. //listen for changes to the skChannel variable and report those changes to WM whenever docked
  1386. openChannel: function(){try{
  1387. var dump=$("wmDataDump");
  1388. if (dump) {
  1389. var skData=getOpt("skChannel");
  1390. setOpt("skChannel","");
  1391. if (skData) dump.appendChild(createElement('div',{'data-ft':skData}));
  1392. }
  1393. setTimeout(Sidekick.openChannel,1000);
  1394. }catch(e){log("wmLibrary.Sidekick.openChannel: "+e);}},
  1395.  
  1396. //send a status code from the deepest iframe to the topmost frame so that it can be passed back with data the top window already has
  1397. sendStatus: function(status,link){try{
  1398. if (exists(window.top)) {
  1399. window.top.postMessage({
  1400. channel:"WallManager",
  1401. msg:3,
  1402. status:status,
  1403. nopopLink:(link?link:''),
  1404. },"*");
  1405. } else {
  1406. //window.top is hidden to us from this location
  1407. contentEval('window.top.postMessage({"channel":"WallManager","msg":3,"status":'+status+',"link":"'+(link?link:'')+'"},"*");');
  1408. }
  1409. }catch(e){log("wmLibrary.Sidekick.sendStatus: "+e);}},
  1410. };
  1411.  
  1412. //***************************************************************************************************************************************
  1413. //***** Visual Effects
  1414. //***************************************************************************************************************************************
  1415.  
  1416. //slides element e toward the specified destination offset
  1417. //specify [t, l, r, b] top, left, right, and bottom as the final offset
  1418. //specify s as the number of MS the move should loop on
  1419. //specify p as the number of pixels to move per interval
  1420. sandbox.slide=function(e,t,l,r,b,s,p) {try{
  1421. s=s||50;p=p||10;
  1422.  
  1423. var top= e.style.top; top=parseInt(top); top=(isNaN(top))?0:top;
  1424. var bottom = e.style.bottom; bottom=parseInt(bottom); bottom=(isNaN(bottom))?0:bottom;
  1425. var left= e.style.left; left=parseInt(left); left=(isNaN(left))?0:left;
  1426. var right = e.style.right; right=parseInt(right); right=(isNaN(right))?0:right;
  1427.  
  1428. p1=(p>Math.abs(t))?Math.abs(t):p;
  1429. if(t>0) {e.style.top = (top+p1)+"px";t-=p1;}
  1430. else if (t<0) {e.style.top = (top-p1)+"px";t+=p1;}
  1431.  
  1432. p1=(p>Math.abs(l))?Math.abs(l):p;
  1433. if(l>0) {e.style.left = (left+p1)+"px";l-=p1;}
  1434. else if (l<0) {e.style.left = (left-p1)+"px";l+=p1;}
  1435.  
  1436. p1=(p>Math.abs(r))?Math.abs(r):p;
  1437. if(r>0) {e.style.right = (right+p1)+"px";r-=p1;}
  1438. else if (r<0) {e.style.right = (right-p1)+"px";r+=p1;}
  1439.  
  1440. p1=(p>Math.abs(b))?Math.abs(b):p;
  1441. if(b>0) {e.style.bottom = (bottom+p1)+"px";b-=p1;}
  1442. else if (b<0) {e.style.bottom = (bottom-p1)+"px";b+=p1;}
  1443.  
  1444. if (t!=0||l!=0||r!=0||b!=0) window.setTimeout(function(){slide(e,t,l,r,b,s,p);},s);
  1445. }catch(e){log("wmLibrary.slide: "+e);}};
  1446.  
  1447. //***************************************************************************************************************************************
  1448. //***** URL Encode/Decode
  1449. //***************************************************************************************************************************************
  1450.  
  1451. //url encode/decode functions nicely wrapped from webtoolkit
  1452. sandbox.Url = {
  1453. // public method for url encoding
  1454. encode : function (string) {try{return escape(this._utf8_encode(string));}catch(e){log("wmLibrary.Url.encode: "+e);}},
  1455. // public method for url decoding
  1456. decode : function (string) {try{return this._utf8_decode(unescape(string));}catch(e){log("wmLibrary.Url.decode: "+e);}},
  1457. // private method for UTF-8 encoding
  1458. _utf8_encode : function (string) {
  1459. string = string.replace(/\r\n/g,"\n");
  1460. var utftext = "";
  1461. for (var n = 0; n < string.length; n++) {
  1462. var c = string.charCodeAt(n);
  1463.  
  1464. if (c < 128) {
  1465. utftext += String.fromCharCode(c);
  1466. }
  1467. else if((c > 127) && (c < 2048)) {
  1468. utftext += String.fromCharCode((c >> 6) | 192);
  1469. utftext += String.fromCharCode((c & 63) | 128);
  1470. }
  1471. else {
  1472. utftext += String.fromCharCode((c >> 12) | 224);
  1473. utftext += String.fromCharCode(((c >> 6) & 63) | 128);
  1474. utftext += String.fromCharCode((c & 63) | 128);
  1475. }
  1476. }
  1477. return utftext;
  1478. },
  1479. // private method for UTF-8 decoding
  1480. _utf8_decode : function (utftext) {
  1481. var string = "";
  1482. var i = 0;
  1483. var c = c1 = c2 = 0;
  1484. while ( i < utftext.length ) {
  1485. c = utftext.charCodeAt(i);
  1486. if (c < 128) {
  1487. string += String.fromCharCode(c);
  1488. i++;
  1489. }
  1490. else if((c > 191) && (c < 224)) {
  1491. c2 = utftext.charCodeAt(i+1);
  1492. string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  1493. i += 2;
  1494. }
  1495. else {
  1496. c2 = utftext.charCodeAt(i+1);
  1497. c3 = utftext.charCodeAt(i+2);
  1498. string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  1499. i += 3;
  1500. }
  1501. }
  1502. return string;
  1503. }
  1504. };
  1505.  
  1506. //***************************************************************************************************************************************
  1507. //***** GM Local Storage Commands
  1508. //***************************************************************************************************************************************
  1509.  
  1510. // set an option
  1511. sandbox.setOpt=function(opt,value){try{GM_setValue(opt,value);}catch(e){log("wmLibrary.setOpt: "+e);}}
  1512.  
  1513. // Get a stored option
  1514. sandbox.getOpt=function(opt){try{return GM_getValue(opt);}catch(e){log("wmLibrary.getOpt: "+e);}}
  1515.  
  1516. // set an option
  1517. sandbox.setOptJSON=function(opt,value){try{GM_setValue(opt,JSON.stringify(value));}catch(e){log("wmLibrary.setOptJSON: "+e);}}
  1518.  
  1519. // Get a stored option
  1520. sandbox.getOptJSON=function(opt){try{var v=GM_getValue(opt, '{}');return JSON.parse(v);}catch(e){log("wmLibrary.getOptJSON: "+e+" opt is:"+opt+", data is:"+v);}}
  1521.  
  1522.  
  1523. //***************************************************************************************************************************************
  1524. //***** 2D Math
  1525. //***************************************************************************************************************************************
  1526.  
  1527. // add two points or vectors
  1528. sandbox.addPoints = function(p0, p1){try{
  1529. var p2=mergeJSON(p0); //copy p0
  1530. for (var v in p1) p2[v]=(p2[v]||0)+(p1[v]||0);
  1531. return p2;
  1532. }catch(e){log("wmLibrary.addPoints: "+e);}},
  1533.  
  1534. //***************************************************************************************************************************************
  1535. //***** Delays and Repeaters
  1536. //***************************************************************************************************************************************
  1537.  
  1538. // shortform for window.setTimeout(x,0)
  1539. sandbox.doAction = function(f) {try{setTimeout(f,0);}catch(e){log("doAction: "+e);}};
  1540.  
  1541. //repeat a function fn a number of times n with a delay of 1 second between calls
  1542. sandbox.signal = function(fn,n){try{
  1543. if (n>0) {
  1544. doAction(fn);
  1545. setTimeout(function(){signal(fn,n-1);},1000);
  1546. }
  1547. }catch(e){log("wmLibrary.signal: "+e);}};
  1548.  
  1549. //***************************************************************************************************************************************
  1550. //***** Enum Creation
  1551. //***************************************************************************************************************************************
  1552.  
  1553. // create an unprotected enumeration list
  1554. sandbox.Enum = function() {try{for (var i in arguments) {this[arguments[i]] = i;}}catch(e){log("Enum.init: "+e);}};
  1555.  
  1556. //create an unprotected enumeration list of binary flags
  1557. sandbox.EnumFlags = function() {try{for (var i in arguments) {this[arguments[i]] = Math.pow(2,i);}}catch(e){log("EnumFlags.init: "+e);}};
  1558.  
  1559. //***************************************************************************************************************************************
  1560. //***** Pop-ups
  1561. //***************************************************************************************************************************************
  1562.  
  1563. //create a centered iframe to display multiline text in a textarea
  1564. //with optional isJSON flag which will format JSON strings with indents and linebreaks
  1565. sandbox.promptText = function(s,isJSON){try{
  1566. if (isJSON) s=s.formatJSON(4);
  1567. var newFrame;
  1568. document.body.appendChild((newFrame=createElement('iframe',{style:'position:fixed; top:0; left:0; display:none !important; z-index:999; width:75%; height:75%; max-height:95%; max-width:95%; border:1px solid #000000; overflow:auto; background-color:white;'})));
  1569. newFrame.src = 'about:blank'; // In WebKit src cant be set until it is added to the page
  1570. newFrame.addEventListener('load', function(){
  1571. var frameBody = this.contentDocument.getElementsByTagName('body')[0];
  1572. var close=function(){try{
  1573. remove(newFrame);
  1574. delete newFrame;
  1575. }catch(e){log("wmLibrary.promptText.close: "+e);}};
  1576. // Add save and close buttons
  1577. frameBody.appendChild(
  1578. createElement("textArea",{textContent:s,style:"height:90%;width:100%;"})
  1579. );
  1580. frameBody.appendChild(
  1581. createElement("div", {id:"buttons_holder"}, [
  1582. createElement('button',{id:"closeBtn", textContent:"Close",title:"Close window",onclick:close}),
  1583. ])
  1584. );
  1585. var center=function(){try{
  1586. var style=newFrame.style;
  1587. var node=newFrame;
  1588. style.display = '';
  1589. style.top = Math.floor((window.innerHeight/2)-(node.offsetHeight/2)) + 'px';
  1590. style.left = Math.floor((window.innerWidth/2)-(node.offsetWidth/2)) + 'px';
  1591. }catch(e){log("wmLibrary.promptText.center: "+e);}};
  1592. center();
  1593. window.addEventListener('resize', center, false); // Center it on resize
  1594.  
  1595. // Close frame on window close
  1596. window.addEventListener('beforeunload', function(){newFrame.remove(this);}, false);
  1597. }, false);
  1598. }catch(e){log("wmLibrary.promptText: "+e);}};
  1599.  
  1600. //***************************************************************************************************************************************
  1601. //***** Text To Script
  1602. //***************************************************************************************************************************************
  1603.  
  1604. //force code to be run outside the GM sandbox
  1605. sandbox.contentEval = function(source) {try{
  1606. // Check for function input.
  1607. if ('function' == typeof source) {
  1608. // Execute this function with no arguments, by adding parentheses.
  1609. // One set around the function, required for valid syntax, and a
  1610. // second empty set calls the surrounded function.
  1611. source = '(' + source + ')();'
  1612. }
  1613.  
  1614. // Create a script node holding this source code.
  1615. var script = document.createElement('script');
  1616. script.setAttribute("type", "application/javascript");
  1617. script.textContent = source;
  1618.  
  1619. // Insert the script node into the page, so it will run, and immediately
  1620. // remove it to clean up.
  1621. document.body.appendChild(script);
  1622. document.body.removeChild(script);
  1623. }catch(e){log("wmLibrary.contentEval: "+e);}};
  1624.  
  1625. //***************************************************************************************************************************************
  1626. //***** RegExp Construction
  1627. //***************************************************************************************************************************************
  1628.  
  1629. //convert an array to a pipe delimited RegExp group
  1630. sandbox.arrayToRegExp = function(a) {try{
  1631. var ret="";
  1632. if (isArrayAndNotEmpty(a)) {
  1633. ret="(";
  1634. for (var i=0,len=a.length; i<len;i++){
  1635. ret=ret+a[i];
  1636. if (i<(len-1)) ret=ret+"|";
  1637. }
  1638. ret=ret+")";
  1639. }
  1640. return ret;
  1641. }catch(e){log("wmLibrary.arrayToRegExp: "+e);}};
  1642.  
  1643. //takes an integer range and converts it to a regular expression
  1644. //which can search for that number range in a string
  1645. sandbox.integerRangeToRegExp = function(params) {try{
  1646. params=params||{};
  1647. var min=params.min.toString(), max=params.max.toString();
  1648. var ret="";
  1649.  
  1650. //on the odd case that both min and max values were equal
  1651. if (max==min) return max;
  1652. //count shared digits we can omit from complex regexp
  1653. var numSharedDigits=0;
  1654. if (min.length==max.length) {
  1655. for (var n=max.length;n>0;n--){
  1656. if (max.substring(0,n) == min.substring(0,n)) {
  1657. numSharedDigits=n;
  1658. break;
  1659. }
  1660. }
  1661. }
  1662. var shared=max.substring(0,numSharedDigits);
  1663. //crop the min and max values
  1664. min=min.removePrefix(shared);
  1665. max=max.removePrefix(shared);
  1666.  
  1667. //move the shared stuff to the front of the test
  1668. ret+=shared+"(";
  1669.  
  1670. //count the digits
  1671. var minDigits=min.length;
  1672. var maxDigits=max.length;
  1673.  
  1674. //set some flags
  1675. var isSingleDigit=(minDigits==1 && maxDigits==1);
  1676. var isVariableDigits=(minDigits != maxDigits);
  1677. //using 1 to 4444 as a range
  1678. //calculate maximum range tests
  1679. //ie: 444x 44xx 4xxx
  1680. if (maxDigits>1){
  1681. ret+=max.substr(0,maxDigits-1)+"[0-"+max.substr(maxDigits-1,1)+"]";
  1682. for (var n=(maxDigits-2); n>0; n--) {
  1683. if (max.substr(n,1)!="0") {
  1684. ret+="|"+max.substr(0,n)+"[0-"+(val(max.substr(n,1))-1)+"]"+("\\d").repeat((maxDigits-1)-n);
  1685. }
  1686. }
  1687. }
  1688.  
  1689. //calculate intermediate range tests
  1690. //ie: 1xxx, 1xx, 1x
  1691. for (var n=maxDigits;n>1;n--){
  1692. //check if min and max both use this digit
  1693. if (minDigits==n && maxDigits==n) {
  1694. //as neither bound would be put out of range
  1695. //and the bounds are not equal
  1696. if ((min.substr(0,1)!="9") && (max.substr(0,1)!="1") && (val(max.substr(0,1))>(val(min.substr(0,1))+1))) {
  1697. ret+="|["+(val(min.substr(0,1))+1)+"-"+(val(max.substr(0,1))-1)+"]"+("\\d").repeat(n-1);
  1698. }
  1699. //detect if min uses this digit
  1700. } else if (minDigits==n) {
  1701. //as long as it does not start with 9
  1702. if (min.substr(0,1)!="9") {
  1703. ret+="|["+(val(min.substr(0,1))+1)+"-9]"+("\\d").repeat(n-1);
  1704. }
  1705. break;
  1706. //detect if max uses this digit
  1707. } else if (maxDigits==n) {
  1708. //as long as it does not start with 1
  1709. if (max.substr(0,1)!="1") {
  1710. ret+="|[1-"+(val(max.substr(0,1))-1)+"]"+("\\d").repeat(n-1);
  1711. }
  1712. } else {
  1713. //they do not use this digit
  1714. //is it BETWEEN their digit counts
  1715. if (n > minDigits) {
  1716. ret+="|[1-9]"+("\\d").repeat(n-1);
  1717. }
  1718. }
  1719. }
  1720. //calculate minimum range tests
  1721. //ie: [1-9]
  1722. if (minDigits>1){
  1723. ret+="|"+min.substr(0,minDigits-1)+"["+min.substr(minDigits-1,1)+"-9]";
  1724. for (var n=(minDigits-2); n>0; n--) {
  1725. if (min.substr(n,1)!="9") {
  1726. ret+="|"+min.substr(0,n)+"["+(val(min.substr(n,1))+1)+"-9]"+("[0-9]").repeat((minDigits-1)-n);
  1727. }
  1728. }
  1729. } else {
  1730. //single digit min
  1731. if (maxDigits>minDigits) {
  1732. ret+="|["+min+"-9]";
  1733. } else {
  1734. //both min and max are single digits
  1735. ret+="|["+min+"-"+max+"]";
  1736. }
  1737. }
  1738. //fix same start and end range issues
  1739. for (var i=0;i<=9;i++){
  1740. ret=ret.replace(new RegExp("\\["+i+"-"+i+"\\]","gi"),i);
  1741. }
  1742. ret=ret.replace(new RegExp("\\[0-9\\]","gi"),"\\d");
  1743. return ret+")";
  1744. }catch(e){log("wmLibrary.integerRangeToRegExp: "+e);}};
  1745.  
  1746. //***************************************************************************************************************************************
  1747. //***** Typing Simulation
  1748. //***************************************************************************************************************************************
  1749.  
  1750. sandbox.simulateKeyEvent = function(character,byCode) {
  1751. var evt = document.createEvent("KeyboardEvent");
  1752. (evt.initKeyEvent || evt.initKeyboardEvent)("keypress", true, true, window,
  1753. 0, 0, 0, 0,
  1754. 0, ((byCode||null) || character.charCodeAt(0)) )
  1755. var canceled = !body.dispatchEvent(evt);
  1756. if(canceled) {
  1757. // A handler called preventDefault
  1758. alert("canceled");
  1759. } else {
  1760. // None of the handlers called preventDefault
  1761. alert("not canceled");
  1762. }
  1763. };
  1764.  
  1765. sandbox.typeText = function(s) {
  1766. for (var i=0,len=s.length; i<len; i++){
  1767. simulateKeyEvent(s.substr(i,1));
  1768. log(s.substr(i,1));
  1769. }
  1770. };
  1771.  
  1772. sandbox.typeEnter = function() {
  1773. simulateKeyEvent(null,13);
  1774. };
  1775.  
  1776. /*formatting notes
  1777. format a number to x decimal places
  1778. number.toFixed(x);
  1779.  
  1780. convert to hexidecimal
  1781. number.toString(16);
  1782.  
  1783. //try something like this to get your own header details
  1784. define your own parseHeaders function
  1785. var fileMETA = parseHeaders(<><![CDATA[
  1786. // ==UserScript==
  1787. // @name My Script
  1788. // @namespace http://www.example.com/gmscripts
  1789. // @description Scripting is fun
  1790. // @copyright 2009+, John Doe (http://www.example.com/~jdoe)
  1791. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  1792. // @version 0.0.1
  1793. // @include http://www.example.com/*
  1794. // @include http://www.example.org/*
  1795. // @exclude http://www.example.org/foo
  1796. // @require foo.js
  1797. // @resource resourceName1 resource1.png
  1798. // @resource resourceName2 http://www.example.com/resource2.png
  1799. // @uso:script scriptid
  1800. // ==/UserScript==
  1801. ]]></>.toString());
  1802.  
  1803. //include jquery stuff
  1804. // ==UserScript==
  1805. // @name jQuery Example
  1806. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
  1807. // ==/UserScript==
  1808. */
  1809.  
  1810. //a custom collection wrapper
  1811. //this pretty much mimics collections in visual basic
  1812. //with a lot of collection methods added from other systems
  1813. var jsCollection=function(objOrArray){
  1814. var self=this;
  1815. this.items={};
  1816. //return an item from this collection by index or key
  1817. this.__defineGetter__("item",function(indexOrKey){try{
  1818. return this.items[indexOrKey]||null;
  1819. }catch(e){log("jsCollection.item: "+e);}});
  1820.  
  1821. //return the count of items in this collection
  1822. this.__defineGetter__("count",function(){try{
  1823. var ret=0;
  1824. for (var e in this.items) ret++;
  1825. return ret;
  1826. }catch(e){log("jsCollection.count: "+e);}});
  1827. //return true if the count of items in this collection is 0
  1828. this.__defineGetter__("isEmpty",function(){try{
  1829. return this.count==0;
  1830. }catch(e){log("jsCollection.isEmpty: "+e);}});
  1831.  
  1832. //remove all items from this collection
  1833. this.clear=function(){try{
  1834. while(this.items[0]) delete this.items[0];
  1835. }catch(e){log("jsCollection.clear: "+e);}};
  1836. //return the index of the first occurence of obj
  1837. this.indexOf=function(obj){try{
  1838. var c=0;
  1839. for (var i in this.items){
  1840. if (this.items[i]===obj) {
  1841. return c;
  1842. break;
  1843. }
  1844. c++;
  1845. }
  1846. return -1;
  1847. }catch(e){log("jsCollection.indexOf: "+e);}};
  1848.  
  1849. //return the key of the first occurence of obj
  1850. this.keyOf=function(obj){try{
  1851. for (var i in this.items){
  1852. if (this.items[i]===obj) {
  1853. return i;
  1854. break;
  1855. }
  1856. }
  1857. return -1;
  1858. }catch(e){log("jsCollection.keyOf: "+e);}};
  1859.  
  1860. //returns true if obj occurs in this collection
  1861. this.contains=function(obj){try{
  1862. return this.indexOf(obj)!=-1;
  1863. }catch(e){log("jsCollection.contains: "+e);}};
  1864. //returns true if an item in this collection has key = key
  1865. this.containsKey=function(key){try{
  1866. return exists(this.items[key]);
  1867. }catch(e){log("jsCollection.containsKey: "+e);}};
  1868.  
  1869. //remove an item from the collection by index or key
  1870. this.remove=function(indexOrKey){try{
  1871. delete this.items[indexOrKey];
  1872. }catch(e){log("jsCollection.remove: "+e);}};
  1873. //add an item to the collection
  1874. //with optional key which defaults to unique()
  1875. //with optional before which is an object to match
  1876. //with optional after which is an object to match
  1877. this.add=function(item,key,before,after){try{
  1878. key=key||unique();
  1879. if (before && this.indexOf(before)!=-1) {
  1880. var ret={};
  1881. for (var i in this.items){
  1882. if (this.items[i]===before) {
  1883. ret[key]=item;
  1884. }
  1885. ret[i]=this.items[i];
  1886. }
  1887. this.items=ret;
  1888. } else if (after && this.indexOf(after)!=-1) {
  1889. var ret={};
  1890. for (var i in this.items){
  1891. ret[i]=this.items[i];
  1892. if (this.items[i]===after) {
  1893. ret[key]=item;
  1894. }
  1895. }
  1896. this.items=ret;
  1897. } else {
  1898. this.items[key]=item;
  1899. }
  1900. }catch(e){log("jsCollection.add: "+e);}};
  1901. //shortform to add an item
  1902. //after an item
  1903. //with optional key
  1904. this.insertAfter=function(item,after,key){try{
  1905. this.add(item,key,null,after);
  1906. }catch(e){log("jsCollection.insertAfter: "+e);}};
  1907.  
  1908. //shortform to add an item
  1909. //before an item
  1910. //with optional key
  1911. this.insertBefore=function(item,before,key){try{
  1912. this.add(item,key,before,null);
  1913. }catch(e){log("jsCollection.insertBefore: "+e);}};
  1914. //shortform to add an item
  1915. //with optional key
  1916. this.append=function(item,key){try{
  1917. this.add(item,key);
  1918. }catch(e){log("jsCollection.append: "+e);}};
  1919. //shortform to add an item
  1920. //to the beginning of the collection
  1921. //with optional key
  1922. this.prepend=function(item,key){try{
  1923. this.add(item,key,(this.items[0]||null));
  1924. }catch(e){log("jsCollection.prepend: "+e);}};
  1925.  
  1926. //add an array of items
  1927. //with optional before and after
  1928. this.addRange=function(itemArray,before,after){try{
  1929. if (before && this.indexOf(before)!=-1) {
  1930. var ret={};
  1931. for (var i in this.items){
  1932. if (this.items[i]===before) {
  1933. for (var a=0,len=itemArrayLength;a<len;a++){
  1934. ret[unique()]=itemArray[a];
  1935. }
  1936. }
  1937. ret[i]=this.items[i];
  1938. }
  1939. this.items=ret;
  1940. } else if (after && this.indexOf(after)!=-1) {
  1941. var ret={};
  1942. for (var i in this.items){
  1943. ret[i]=this.items[i];
  1944. if (this.items[i]===after) {
  1945. for (var a=0,len=itemArrayLength;a<len;a++){
  1946. ret[unique()]=itemArray[a];
  1947. }
  1948. }
  1949. }
  1950. this.items=ret;
  1951. } else {
  1952. for (var a=0,len=itemArrayLength;a<len;a++){
  1953. this.items[unique()]=itemArray[a];
  1954. }
  1955. }
  1956. }catch(e){log("jsCollection.addRange: "+e);}};
  1957.  
  1958. //shortform to add an array of items
  1959. this.appendRange=function(itemArray){try{
  1960. this.addRange(itemArray);
  1961. }catch(e){log("jsCollection.appendRange: "+e);}};
  1962. //shortform to add an array of items
  1963. //to the beginning of the collection
  1964. this.prependRange=function(itemArray){try{
  1965. this.addRange(itemArray,(this.items[0]||null));
  1966. }catch(e){log("jsCollection.prependRange: "+e);}};
  1967.  
  1968. //add a copy of item
  1969. //with optional before or after
  1970. this.addCopy=function(item,before,after){try{
  1971. this.add(item,null,before,after);
  1972. }catch(e){log("jsCollection.addCopy: "+e);}};
  1973.  
  1974. //add multiple copies of item
  1975. //with optional before and after
  1976. this.addCopies=function(item,count,before,after){try{
  1977. var ret=[];
  1978. for (var i=0;i<count;i++) ret.push(item);
  1979. this.addRange(item,before,after);
  1980. }catch(e){log("jsCollection.addCopies: "+e);}};
  1981.  
  1982. //return the collection converted to an array
  1983. this.toArray=function(){try{
  1984. return methodsToArray(this.items);
  1985. }catch(e){log("jsCollection.toArray: "+e);}};
  1986.  
  1987. //return the index of item with key=key
  1988. this.indexOfKey=function(key){try{
  1989. return this.indexOf(this.items[key]||null);
  1990. }catch(e){log("jsCollection.indexOfKey: "+e);}};
  1991. //return the key of the item at index=index
  1992. this.keyOfIndex=function(index){try{
  1993. var c=0;
  1994. for (var i in this.items){
  1995. if (c==index) return i;
  1996. c++;
  1997. }
  1998. }catch(e){log("jsCollection.keyOfIndex: "+e);}};
  1999.  
  2000. //use passed data on creation to create initial items
  2001. if (objOrArray){
  2002. if (isArrayAndNotEmpty(objOrArray)){
  2003. for (var i=0,len=objOrArray.length;i<len;i++){
  2004. this.add(objOrArray[i],i);
  2005. }
  2006. } else if (isObject(objOrArray)) {
  2007. for (var i in objOrArray){
  2008. this.items[i]=objOrArray[i];
  2009. }
  2010. }
  2011. }
  2012. //return self for external use
  2013. return this;
  2014. };
  2015.  
  2016. })();