WM Common Library

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

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

  1. // ==UserScript==
  2. // @name WM Common Library
  3. // @namespace tempcommon
  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.1.2
  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 Object.prototype.toString.call(o)==="[object 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 so that words which contain another word in the array are placed before that other word
  864. //such as "pea" must come AFTER "peanut", and "great american race" must come BEFORE "american"
  865. //the sort is case-insensitive
  866. sandbox.Array.prototype.fixOrder = function(){
  867. var compareFunc = function(a,b){
  868. var s1=a.toLowerCase(), s2=b.toLowerCase();
  869. if (s1.contains(s2)) return -1; //when a contains b, a must come first
  870. else if (s2.contains(s1)) return 1 //when b contains a, b must come first
  871. else return 0; //no order change is required
  872. };
  873. this.sort(compareFunc);
  874. return this;
  875. };
  876.  
  877. //alias for the previous function
  878. sandbox.Array.prototype.optimize = sandbox.Array.prototype.fixOrder;
  879.  
  880. //returns a shallow copy of the calling array
  881. sandbox.Array.prototype.clone = function(){try{
  882. return this.slice(0);
  883. }catch(e){log("wmLibrary.Array.prototype.clone: "+e);}};
  884.  
  885. //reverses the elements of an array
  886. //with optional startIndex
  887. //and optional count which limits the reverse section
  888. //if startIndex+count is greater than the length of the array
  889. //then only the available section is reversed
  890. //returns the calling array
  891. sandbox.Array.prototype.reverse = function(startIndex,count){try{
  892. startIndex=startIndex||0;
  893. if (startIndex>=this.length) {
  894. //log("wmLibrary.Array.prototype.reverse: Error: startIndex out of bounds");
  895. return -1;
  896. }
  897. if (exists(count) && count<1) {
  898. //log("wmLibrary.Array.prototype.reverse: Error: count is less than 1");
  899. return -1;
  900. }
  901. var endIndex=(exists(count))?startIndex+count:this.length-1;
  902. if (endIndex>this.length-1) endIndex=this.length-1;
  903. while (startIndex>endIndex){
  904. this.swap(startIndex,endIndex);
  905. startIndex++;
  906. endIndex--;
  907. }
  908. return this;
  909. }catch(e){log("wmLibrary.Array.prototype.reverse: "+e);}};
  910.  
  911. //sets a range of elements in the array to the defaultValue
  912. //returns the calling array
  913. sandbox.Array.prototype.clear = function(startIndex,count,defaultValue){try{
  914. if (count>0 && this.length>startIndex) {
  915. for (var i=startIndex,len=this.length; (i<len && i<(startIndex+count)); i++){
  916. this[i]=defaultValue;
  917. }
  918. }
  919. return this;
  920. }catch(e){log("wmLibrary.Array.prototype.clear: "+e);}};
  921.  
  922. //copies elements from this array to a destination destArray
  923. //starting in this array at sourceIndex
  924. //and pasting into the destArray at destIndex
  925. //where length is the number of elements to copy
  926. //pasting beyond the higher bounds of the destArray simply increases the array size
  927. //returns the calling array
  928. sandbox.Array.prototype.copy = function(sourceIndex,destArray,destIndex,length){try{
  929. if (!isArray(destArray)) {
  930. log("wmLibrary.Array.prototype.copy: Error: destArray is not an array");
  931. return this;
  932. }
  933. if (sourceIndex >= this.length) {
  934. //log("wmLibrary.Array.prototype.copy: Error: sourceIndex out of bounds");
  935. return this;
  936. }
  937. for (var i=0; i<length; i++){
  938. destArray[destIndex+i]=this[sourceIndex+i];
  939. }
  940. return this;
  941. }catch(e){log("wmLibrary.Array.prototype.copy: "+e);}};
  942.  
  943. //copies all elements from this array to a destination destArray
  944. //pasting into the destArray at destIndex
  945. //pasting beyond the higher bounds of the destArray simply increases the array size
  946. //returns the calling array
  947. sandbox.Array.prototype.copyTo = function(destArray,destIndex){try{
  948. if (!isArray(destArray)) {
  949. log("wmLibrary.Array.prototype.copyTo: Error: destArray is not an array");
  950. return this;
  951. }
  952. for (var i=0, len=this.length; i<len; i++){
  953. destArray[destIndex+i]=this[i];
  954. }
  955. return this;
  956. }catch(e){log("wmLibrary.Array.prototype.copyTo: "+e);}};
  957.  
  958. //returns an array containing elements from the current array where the element has parameter p equal to value v
  959. 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);}};
  960.  
  961. //returns the element matched by matchFunc, or null
  962. //with optional start index
  963. //and optional count to limit the number of searched elements
  964. sandbox.Array.prototype.find = function(matchFunc,startIndex,count) {try{
  965. startIndex=startIndex||0;
  966. if (startIndex>=this.length) {
  967. //log("wmLibrary.Array.prototype.find: Error: startIndex out of bounds");
  968. return null;
  969. }
  970. if (exists(count) && count<1) {
  971. //log("wmLibrary.Array.prototype.find: Error: count is less than 1");
  972. return null;
  973. }
  974. var c=0;
  975. for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
  976. c++;
  977. if (matchFunc(this[i])) {
  978. return this[i];
  979. break;
  980. }
  981. }
  982. return null;
  983. }catch(e){log("wmLibrary.Array.prototype.find: "+e);}};
  984.  
  985. //returns the index of element matched by matchFunc, or -1
  986. //with optional startIndex
  987. //and optional count which specifies the number of elements to check
  988. sandbox.Array.prototype.findIndex = function(matchFunc,startIndex,count) {try{
  989. startIndex=startIndex||0;
  990. if (startIndex>=this.length) {
  991. //log("wmLibrary.Array.prototype.findIndex: Error: startIndex out of bounds");
  992. return -1;
  993. }
  994. if (exists(count) && count<1) {
  995. //log("wmLibrary.Array.prototype.findIndex: Error: count is less than 1");
  996. return -1;
  997. }
  998. var c=0;
  999. for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
  1000. c++;
  1001. if (matchFunc(this[i])) {
  1002. return i;
  1003. break;
  1004. }
  1005. }
  1006. return -1;
  1007. }catch(e){log("wmLibrary.Array.prototype.findIndex: "+e);}};
  1008.  
  1009. //returns all elements matched by matchFunc, or null
  1010. //with optional start index
  1011. //and optional count to limit the number of elements searched
  1012. sandbox.Array.prototype.findAll = function(matchFunc,startIndex,count) {try{
  1013. startIndex=startIndex||0;
  1014. var ret=[];
  1015. if (startIndex>=this.length) {
  1016. //log("wmLibrary.Array.prototype.findAll: Error: startIndex out of bounds");
  1017. return null;
  1018. }
  1019. if (exists(count) && count<1) {
  1020. //log("wmLibrary.Array.prototype.findAll: Error: count is less than 1");
  1021. return null;
  1022. }
  1023. var c=0;
  1024. for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
  1025. c++;
  1026. if (matchFunc(this[i])) {
  1027. ret.push(this[i]);
  1028. }
  1029. }
  1030. return (isArrayAndNotEmpty(ret))?ret:null;
  1031. }catch(e){log("wmLibrary.Array.prototype.findAll: "+e);}};
  1032.  
  1033. //returns true if all elements in the array match the matchFunc
  1034. //with optional start index
  1035. //and optional count to limit the number of elements searched
  1036. sandbox.Array.prototype.trueForAll = function(matchFunc,startIndex,count) {try{
  1037. startIndex=startIndex||0;
  1038. if (startIndex>=this.length) {
  1039. //log("wmLibrary.Array.prototype.trueForAll: Error: startIndex out of bounds");
  1040. return false;
  1041. }
  1042. if (exists(count) && count<1) {
  1043. //log("wmLibrary.Array.prototype.trueForAll: Error: count is less than 1");
  1044. return false;
  1045. }
  1046. var c=0;
  1047. for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
  1048. c++;
  1049. if (!matchFunc(this[i])) {
  1050. return false;
  1051. }
  1052. }
  1053. return true;
  1054. }catch(e){log("wmLibrary.Array.prototype.trueForAll: "+e);}};
  1055.  
  1056. //returns true if array contains an element matched by the matchFunc
  1057. //with optional startIndex
  1058. //and optional count which specifies the number of elements to check
  1059. sandbox.Array.prototype.exists = function(matchFunc,startIndex,count) {try{
  1060. return this.findIndex(matchFunc,startIndex,count)!=-1;
  1061. }catch(e){log("wmLibrary.Array.prototype.exists: "+e);}};
  1062.  
  1063. //returns the last element matched by matchFunc, or null
  1064. //with optional start index
  1065. //and optional count to limit the number of searched elements
  1066. sandbox.Array.prototype.findLast = function(matchFunc,startIndex,count) {try{
  1067. startIndex=startIndex||0;
  1068. if (startIndex>=this.length) {
  1069. //log("wmLibrary.Array.prototype.findLast: Error: startIndex out of bounds");
  1070. return null;
  1071. }
  1072. if (exists(count) && count<1) {
  1073. //log("wmLibrary.Array.prototype.findLast: Error: count is less than 1");
  1074. return null;
  1075. }
  1076. var c=0;
  1077. for (var i=this.length; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i--){
  1078. c++;
  1079. if (matchFunc(this[i])) {
  1080. return this[i];
  1081. break;
  1082. }
  1083. }
  1084. return null;
  1085. }catch(e){log("wmLibrary.Array.prototype.findLast: "+e);}};
  1086.  
  1087. //returns the last element matched by matchFunc, or -1
  1088. //with optional start index
  1089. //and optional count which specifies the number of elements to check
  1090. sandbox.Array.prototype.findLastIndex = function(matchFunc,startIndex,count) {try{
  1091. startIndex=startIndex||0;
  1092. if (startIndex>=this.length) {
  1093. //log("wmLibrary.Array.prototype.findLastIndex: Error: startIndex out of bounds");
  1094. return -1;
  1095. }
  1096. if (exists(count) && count<1) {
  1097. //log("wmLibrary.Array.prototype.findLastIndex: Error: count is less than 1");
  1098. return -1;
  1099. }
  1100. var c=0;
  1101. for (var i=this.length; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i--){
  1102. c++;
  1103. if (matchFunc(this[i])) {
  1104. return i;
  1105. break;
  1106. }
  1107. }
  1108. return -1;
  1109. }catch(e){log("wmLibrary.Array.prototype.findLastIndex: "+e);}};
  1110.  
  1111. //***************************************************************************************************************************************
  1112. //***** JSON/OBJECT Construction and Matching
  1113. //***************************************************************************************************************************************
  1114.  
  1115. //returns the merge of any number of JSON objects passed as unnamed arguments
  1116. sandbox.mergeJSON_long = function(){try{
  1117. var ret = {};
  1118. //for each JSON object passed
  1119. for (var a=0,len=arguments.length;a<len;a++) {
  1120. //for each element in that object
  1121. for (var v in arguments[a]) {
  1122. if (!exists(ret[v])) {
  1123. //simply copy the element to the return value
  1124. ret[v] = arguments[a][v];
  1125. } else {
  1126. if ((typeof arguments[a][v])=="object") {
  1127. //merge the two elements, preserving tree structure
  1128. ret[v] = mergeJSON(ret[v], arguments[a][v]);
  1129. } else {
  1130. //overwrite simple variable
  1131. ret[v] = arguments[a][v];
  1132. }
  1133. }
  1134. }
  1135. }
  1136. //the problem here is that its way too recursive and jams firefox often
  1137. return ret;
  1138. }catch(e){log("wmLibrary.mergeJSON: "+e);}};
  1139.  
  1140. sandbox.mergeJSON = function(){try{
  1141. var ret = {};
  1142. //for each JSON object passed
  1143. for (var a=0,len=arguments.length;a<len;a++) {
  1144. var o=arguments[a];
  1145. //for each element in that object
  1146. for (var v in o) {
  1147. //replace the initial element with that of the next
  1148. ret[v] = o[v];
  1149. }
  1150. //the problem here is that only the top level branches are preserved
  1151. }
  1152. return ret;
  1153. }catch(e){log("wmLibrary.mergeJSON: "+e);}};
  1154.  
  1155. //returns all members of an array that have a specified parameter with a specified value
  1156. //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);}};
  1157. //returns all members of an array that have a specified parameter with a specified value
  1158. //now accepts input of array or object
  1159. //can now specify output of array or object
  1160. sandbox.matchByParam=function(o,param,value,outputType){try{
  1161. if(!exists(outputType)) outputType="array";
  1162. var inputType=(isArray(o))?"array":((typeof o) == "object")?"object":"unknown";
  1163. var ret=(outputType=="object")?{}:[]; //default to array on error
  1164.  
  1165. switch(inputType){
  1166. case "array": for (var i=0,e;(e=o[i]);i++){
  1167. switch(outputType){
  1168. case "array": if (e[param]==value) ret.push(e); break;
  1169. case "object": if (e[param]==value) ret[i]=e; break;
  1170. }
  1171. };break;
  1172.  
  1173. case "object": for (var i in o){
  1174. var e=o[i];
  1175. switch(outputType){
  1176. case "array": if (e[param]==value) ret.push(e); break;
  1177. case "object": if (e[param]==value) ret[i]=e; break;
  1178. }
  1179. };break;
  1180. }
  1181. return ret;
  1182. }catch(e){log("wmLibrary.matchByParam: "+e);}};
  1183.  
  1184. //sorts the methods of an object by method 'id' or method 'value'
  1185. //beware this may mangle some objects
  1186. sandbox.sortCollection=function(o,by){
  1187. var a=[];
  1188. for (var i in o){
  1189. a.push({id:i,value:o[i]});
  1190. }
  1191. a.sort(function(a,b){return a[by]>b[by];});
  1192. var ret={};
  1193. for (var i=0;i<a.length;i++){
  1194. ret[a[i].id]=a[i].value;
  1195. }
  1196. return ret;
  1197. };
  1198.  
  1199. // Collect all the values from parameter p in object o, traversing kids nodes
  1200. sandbox.getBranchValues=function(o,p){try{
  1201. var ret={};
  1202. for(var i in o) {
  1203. //get value p for object o's element i
  1204. if (p=="id"){ //special case for fetching a list of ID's
  1205. if (exists(o[i][p])) ret[i]=o[i][p];
  1206. else ret[i]=i;
  1207. } else if (p=="."){ //special case for fetching a list of all objects without a tree structure
  1208. ret[i]=o[i];
  1209. }
  1210.  
  1211. else if (exists(o[i][p])) ret[i]=o[i][p];
  1212. //if object o has kids, then get all the values p inside that kid k
  1213. if (o[i].kids) ret=mergeJSON(ret,getBranchValues(o[i].kids,p));
  1214. }
  1215. return ret;
  1216. }catch(e){log("wmLibrary.getBranchValues: "+e);}};
  1217.  
  1218. //convert an object's methods to an array, storing the method's key on the object as an id
  1219. 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);}};
  1220.  
  1221. //convert an array of objects to methods of an object using either the object's ai or name as its key
  1222. 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);}};
  1223.  
  1224. //convert an object's methods to an array of those method names
  1225. sandbox.methodNames = function(o) {try{var ret=[]; for (i in o) ret.push(i); return ret;}catch(e){log("wmLibrary.methodNames: "+e);}};
  1226.  
  1227. //copy parts from one object to another
  1228. //used for extending one object with parts from another
  1229. //by John Resig
  1230. sandbox.extend = function(a,b) {try{
  1231. for ( var i in b ) {
  1232. //collect setter/getter functions
  1233. var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
  1234. //copy setter/getter functions
  1235. if ( g || s ) {
  1236. if ( g ) a.__defineGetter__(i, g);
  1237. if ( s ) a.__defineSetter__(i, s);
  1238. } else a[i] = b[i]; //copy vars
  1239. }
  1240. return a;
  1241. }catch(e){log("wmLibrary.extend: "+e);}};
  1242.  
  1243. //***************************************************************************************************************************************
  1244. //***** WM Specific Functions
  1245. //***************************************************************************************************************************************
  1246.  
  1247. //returns an object suitable for accText data based on an array, and allowing an idPrefix and textSuffix
  1248. sandbox.createAccTextFromArray=function(arr,keyPrefix,textSuffix){
  1249. var ret={};
  1250. if (arr) {
  1251. for (var i=0,len=arr.length;i<len;i++){
  1252. o=arr[i];
  1253. ret[(keyPrefix||'')+o.noSpaces().toLowerCase()]=o.upperWords()+(textSuffix||'');
  1254. }
  1255. }
  1256. return ret;
  1257. };
  1258.  
  1259. //writes a message to the hash section of the document location, or redirects to a location that can accept a new hash section
  1260. sandbox.sendMessage=function(s,hwnd,flag){try{
  1261. hwnd = (hwnd||window.top);
  1262. if (exists(hwnd)) try {hwnd.location.hash = s;} catch(e){
  1263. if (flag==1) hwnd.location.href = "http://apps.facebook.com/?#"+s;
  1264. else hwnd.location.href = "http://www.facebook.com/reqs.php?#"+s;
  1265. }
  1266. }catch(e){log("wmLibrary.sendMessage: "+e);}};
  1267.  
  1268. //flags for menu building function
  1269. sandbox.MENU_ID_ENFORCE_NAME=1; //causes menuFromData to return lowercase nospace names as the id instead of the calculated id
  1270.  
  1271. //inserts one or more menu option blocks based upon a data object
  1272. //marking all new items in the newitem list above as green so users can easily find your changes
  1273. sandbox.menuFromData=function(data,menuNode,newItemList,idPrefix,flags){try{
  1274. flags=(flags||0); newItemList=(newItemList||[]);
  1275. if (data) for (var m=0,len=data.length; m<len; m++) {
  1276. var text = data[m]["name"].upperWords(), event = (data[m]["event"]||"Unsorted").upperWords();
  1277. var outid = (flags==MENU_ID_ENFORCE_NAME)?data[m].name.noSpaces().toLowerCase():(data[m]["id"]||data[m]["name"]).noSpaces().toLowerCase();
  1278. var thisMenu; if( !(thisMenu=(menuNode["optblock"+event]||null) ) ) {thisMenu=(menuNode["optblock"+event]={type:"optionblock",label:event,kids:{} });}
  1279. thisMenu.kids[idPrefix+outid]={type:"checkbox",label:text,newitem:newItemList.inArray(idPrefix+outid)};
  1280. }
  1281. }catch(e){log("wmLibrary.menuFromData: "+e);}};
  1282.  
  1283. //returns a list of search strings from a data object containing id's names and events, already optimized for searching
  1284. sandbox.searchFromData=function(data,idPrefix){try{
  1285. idPrefix=(idPrefix||"");
  1286. var ret = [];
  1287. for (var m=0,mat;(mat=data[m]);m++){
  1288. ret.push(idPrefix+(mat.id||mat.name));
  1289. }
  1290. ret.optimize();
  1291. return ret;
  1292. }catch(e){log("wmLibrary.searchFromData: "+e);}};
  1293.  
  1294. //returns a list of materials from a data object containing id's names and events, already optimized for searching
  1295. sandbox.matListFromData=function(data){try{
  1296. var ret = [];
  1297. for (var m=0,mat;(mat=data[m]);m++){
  1298. ret.push(mat.name);
  1299. } ret.optimize();
  1300. return ret;
  1301. }catch(e){log("wmLibrary.matListFromData: "+e);}};
  1302.  
  1303. //returns a valid accText object from a data object containing id's names and events
  1304. 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);}};
  1305.  
  1306. //***************************************************************************************************************************************
  1307. //***** Sidekick Object
  1308. //***************************************************************************************************************************************
  1309.  
  1310. //sidekick specific functions
  1311. sandbox.Sidekick={
  1312. //init
  1313. tabID:null,
  1314. status:0,
  1315. nopopLink:"",
  1316.  
  1317. //attempts to dock the sidekick script to the wm host script
  1318. //params takes an object that contains the following parameters:
  1319. //appID(string), version(string), skType(integer),
  1320. //name(string), thumbSource(string or array),
  1321. //flags(object), icon(string), desc(string),
  1322. //addFilters(object),
  1323. //alterLink(object), accText(object),
  1324. //tests(array) and menu(object)
  1325. dock: function(params){try{
  1326. //find the dock node on this page
  1327. var door=$('wmDock');
  1328. if (!door) {
  1329. //does not exist, wait and try again later
  1330. window.setTimeout(function(){Sidekick.dock(params);}, 1000);
  1331. return;
  1332. }
  1333. //detect if a sidekick for this app is already docked
  1334. var doorMark=$('wmDoor_app'+params.appID);
  1335. if (doorMark && (params.skType==doorMark.getAttribute("value")) ) {
  1336. //a sidekick of this level is already here, cancel docking
  1337. return;
  1338. }
  1339. //setup defaults for a few of the expected parameters
  1340. params.thumbsSource=(params.thumbsSource||"app_full_proxy.php?app"+params.appID);
  1341. params.desc=(params.desc||params.name+" Sidekick (ver "+params.version+")");
  1342.  
  1343. //create a block of data to attach to the dock
  1344. var attString=JSON.stringify(params);
  1345. door.appendChild(
  1346. doorMark=createElement('div',{id:'wmDoor_app'+params.appID,'data-ft':attString,value:(params.skType||0)})
  1347. );
  1348. //doorMark.setAttribute("skType",(params.skType||0));
  1349. //confirm(doorMark.getAttribute("skType"));
  1350. //ring the buzzer so the host knows the package is ready
  1351. window.setTimeout(function(){click(door);},1000);
  1352. }catch(e){log("wmLibrary.Sidekick.dock: "+e);}},
  1353. //receive and process messages
  1354. //msg code 1 is a packet from the wm host containing data about the post we are processing
  1355. //that packet must contain at least the tab/window ID with which the WM host can access that tab again
  1356. //msg code 3 is a packet from this or a deeper iframe window about the return value for this post
  1357. //because Chrome returns NULL at event.source on msg 1, we now have to rethink
  1358. receiveMessage: function(event) {try{
  1359. if (isObject(event.data)) {
  1360. var data=event.data; //just shorten the typing
  1361. if (data.channel=="WallManager"){
  1362. log(JSON.stringify(data));
  1363. switch (data.msg) {
  1364. case 1: //get init data from wm host
  1365. //if (!Sidekick.tabID)
  1366. Sidekick.tabID=data.tabID;
  1367. log("Sidekick hears host...");
  1368. //
  1369. break;
  1370. case 3: //get message from child
  1371. if (Sidekick.tabID) {
  1372. log("Sidekick hears iframe...");
  1373. //send our status packet back to wm
  1374. Sidekick.status=data.status;
  1375. Sidekick.nopopLink=data.nopopLink||null;
  1376. //update the stored data about this post
  1377. var skChannel = getOptJSON("skChannel")||{};
  1378. skChannel[Sidekick.tabID]={
  1379. tabID:Sidekick.tabID,
  1380. status:Sidekick.status,
  1381. nopopLink:Sidekick.nopopLink,
  1382. };
  1383. log(JSON.stringify(skChannel));
  1384. setOptJSON("skChannel",skChannel);
  1385. } else {
  1386. //have not yet recieved tabID package from wm, wait a sec
  1387. setTimeout(function(){Sidekick.receiveMessage(event);},1000);
  1388. }
  1389. break;
  1390. }
  1391. }
  1392. }
  1393. }catch(e){log("wmLibrary.Sidekick.receiveMessage: "+e);}},
  1394.  
  1395. //disable the listener started below
  1396. unlisten: function(params){try{
  1397. window.removeEventListener("message", Sidekick.receiveMessage, false);
  1398. }catch(e){log("wmLibrary.Sidekick.unlisten: "+e);}},
  1399. //turn on the listener which can receive messages from wm host (if this window = window.top) or from iframes
  1400. listen: function(params){try{
  1401. window.addEventListener("message", Sidekick.receiveMessage, false);
  1402. }catch(e){log("wmLibrary.Sidekick.listen: "+e);}},
  1403.  
  1404. //listen for changes to the skChannel variable and report those changes to WM whenever docked
  1405. openChannel: function(){try{
  1406. var dump=$("wmDataDump");
  1407. if (dump) {
  1408. var skData=getOpt("skChannel");
  1409. setOpt("skChannel","");
  1410. if (skData) dump.appendChild(createElement('div',{'data-ft':skData}));
  1411. }
  1412. setTimeout(Sidekick.openChannel,1000);
  1413. }catch(e){log("wmLibrary.Sidekick.openChannel: "+e);}},
  1414.  
  1415. //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
  1416. sendStatus: function(status,link){try{
  1417. if (exists(window.top)) {
  1418. window.top.postMessage({
  1419. channel:"WallManager",
  1420. msg:3,
  1421. status:status,
  1422. nopopLink:(link?link:''),
  1423. },"*");
  1424. } else {
  1425. //window.top is hidden to us from this location
  1426. contentEval('window.top.postMessage({"channel":"WallManager","msg":3,"status":'+status+',"link":"'+(link?link:'')+'"},"*");');
  1427. }
  1428. }catch(e){log("wmLibrary.Sidekick.sendStatus: "+e);}},
  1429. };
  1430.  
  1431. //***************************************************************************************************************************************
  1432. //***** Visual Effects
  1433. //***************************************************************************************************************************************
  1434.  
  1435. //slides element e toward the specified destination offset
  1436. //specify [t, l, r, b] top, left, right, and bottom as the final offset
  1437. //specify s as the number of MS the move should loop on
  1438. //specify p as the number of pixels to move per interval
  1439. sandbox.slide=function(e,t,l,r,b,s,p) {try{
  1440. s=s||50;p=p||10;
  1441.  
  1442. var top= e.style.top; top=parseInt(top); top=(isNaN(top))?0:top;
  1443. var bottom = e.style.bottom; bottom=parseInt(bottom); bottom=(isNaN(bottom))?0:bottom;
  1444. var left= e.style.left; left=parseInt(left); left=(isNaN(left))?0:left;
  1445. var right = e.style.right; right=parseInt(right); right=(isNaN(right))?0:right;
  1446.  
  1447. p1=(p>Math.abs(t))?Math.abs(t):p;
  1448. if(t>0) {e.style.top = (top+p1)+"px";t-=p1;}
  1449. else if (t<0) {e.style.top = (top-p1)+"px";t+=p1;}
  1450.  
  1451. p1=(p>Math.abs(l))?Math.abs(l):p;
  1452. if(l>0) {e.style.left = (left+p1)+"px";l-=p1;}
  1453. else if (l<0) {e.style.left = (left-p1)+"px";l+=p1;}
  1454.  
  1455. p1=(p>Math.abs(r))?Math.abs(r):p;
  1456. if(r>0) {e.style.right = (right+p1)+"px";r-=p1;}
  1457. else if (r<0) {e.style.right = (right-p1)+"px";r+=p1;}
  1458.  
  1459. p1=(p>Math.abs(b))?Math.abs(b):p;
  1460. if(b>0) {e.style.bottom = (bottom+p1)+"px";b-=p1;}
  1461. else if (b<0) {e.style.bottom = (bottom-p1)+"px";b+=p1;}
  1462.  
  1463. if (t!=0||l!=0||r!=0||b!=0) window.setTimeout(function(){slide(e,t,l,r,b,s,p);},s);
  1464. }catch(e){log("wmLibrary.slide: "+e);}};
  1465.  
  1466. //***************************************************************************************************************************************
  1467. //***** URL Encode/Decode
  1468. //***************************************************************************************************************************************
  1469.  
  1470. //url encode/decode functions nicely wrapped from webtoolkit
  1471. sandbox.Url = {
  1472. // public method for url encoding
  1473. encode : function (string) {try{return escape(this._utf8_encode(string));}catch(e){log("wmLibrary.Url.encode: "+e);}},
  1474. // public method for url decoding
  1475. decode : function (string) {try{return this._utf8_decode(unescape(string));}catch(e){log("wmLibrary.Url.decode: "+e);}},
  1476. // private method for UTF-8 encoding
  1477. _utf8_encode : function (string) {
  1478. string = string.replace(/\r\n/g,"\n");
  1479. var utftext = "";
  1480. for (var n = 0; n < string.length; n++) {
  1481. var c = string.charCodeAt(n);
  1482.  
  1483. if (c < 128) {
  1484. utftext += String.fromCharCode(c);
  1485. }
  1486. else if((c > 127) && (c < 2048)) {
  1487. utftext += String.fromCharCode((c >> 6) | 192);
  1488. utftext += String.fromCharCode((c & 63) | 128);
  1489. }
  1490. else {
  1491. utftext += String.fromCharCode((c >> 12) | 224);
  1492. utftext += String.fromCharCode(((c >> 6) & 63) | 128);
  1493. utftext += String.fromCharCode((c & 63) | 128);
  1494. }
  1495. }
  1496. return utftext;
  1497. },
  1498. // private method for UTF-8 decoding
  1499. _utf8_decode : function (utftext) {
  1500. var string = "";
  1501. var i = 0;
  1502. var c = c1 = c2 = 0;
  1503. while ( i < utftext.length ) {
  1504. c = utftext.charCodeAt(i);
  1505. if (c < 128) {
  1506. string += String.fromCharCode(c);
  1507. i++;
  1508. }
  1509. else if((c > 191) && (c < 224)) {
  1510. c2 = utftext.charCodeAt(i+1);
  1511. string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  1512. i += 2;
  1513. }
  1514. else {
  1515. c2 = utftext.charCodeAt(i+1);
  1516. c3 = utftext.charCodeAt(i+2);
  1517. string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  1518. i += 3;
  1519. }
  1520. }
  1521. return string;
  1522. }
  1523. };
  1524.  
  1525. //***************************************************************************************************************************************
  1526. //***** GM Local Storage Commands
  1527. //***************************************************************************************************************************************
  1528.  
  1529. // set an option
  1530. sandbox.setOpt=function(opt,value){try{GM_setValue(opt,value);}catch(e){log("wmLibrary.setOpt: "+e);}}
  1531.  
  1532. // Get a stored option
  1533. sandbox.getOpt=function(opt){try{return GM_getValue(opt);}catch(e){log("wmLibrary.getOpt: "+e);}}
  1534.  
  1535. // set an option
  1536. sandbox.setOptJSON=function(opt,value){try{GM_setValue(opt,JSON.stringify(value));}catch(e){log("wmLibrary.setOptJSON: "+e);}}
  1537.  
  1538. // Get a stored option
  1539. 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);}}
  1540.  
  1541.  
  1542. //***************************************************************************************************************************************
  1543. //***** 2D Math
  1544. //***************************************************************************************************************************************
  1545.  
  1546. // add two points or vectors
  1547. sandbox.addPoints = function(p0, p1){try{
  1548. var p2=mergeJSON(p0); //copy p0
  1549. for (var v in p1) p2[v]=(p2[v]||0)+(p1[v]||0);
  1550. return p2;
  1551. }catch(e){log("wmLibrary.addPoints: "+e);}},
  1552.  
  1553. //***************************************************************************************************************************************
  1554. //***** Delays and Repeaters
  1555. //***************************************************************************************************************************************
  1556.  
  1557. // shortform for window.setTimeout(x,0)
  1558. sandbox.doAction = function(f) {try{setTimeout(f,0);}catch(e){log("doAction: "+e);}};
  1559.  
  1560. //repeat a function fn a number of times n with a delay of 1 second between calls
  1561. sandbox.signal = function(fn,n){try{
  1562. if (n>0) {
  1563. doAction(fn);
  1564. setTimeout(function(){signal(fn,n-1);},1000);
  1565. }
  1566. }catch(e){log("wmLibrary.signal: "+e);}};
  1567.  
  1568. //***************************************************************************************************************************************
  1569. //***** Enum Creation
  1570. //***************************************************************************************************************************************
  1571.  
  1572. // create an unprotected enumeration list
  1573. sandbox.Enum = function() {try{for (var i in arguments) {this[arguments[i]] = i;}}catch(e){log("Enum.init: "+e);}};
  1574.  
  1575. //create an unprotected enumeration list of binary flags
  1576. sandbox.EnumFlags = function() {try{for (var i in arguments) {this[arguments[i]] = Math.pow(2,i);}}catch(e){log("EnumFlags.init: "+e);}};
  1577.  
  1578. //***************************************************************************************************************************************
  1579. //***** Pop-ups
  1580. //***************************************************************************************************************************************
  1581.  
  1582. //create a centered iframe to display multiline text in a textarea
  1583. //with optional isJSON flag which will format JSON strings with indents and linebreaks
  1584. sandbox.promptText = function(s,isJSON){try{
  1585. if (isJSON) s=s.formatJSON(4);
  1586. var newFrame;
  1587. 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;'})));
  1588. newFrame.src = 'about:blank'; // In WebKit src cant be set until it is added to the page
  1589. newFrame.addEventListener('load', function(){
  1590. var frameBody = this.contentDocument.getElementsByTagName('body')[0];
  1591. var close=function(){try{
  1592. remove(newFrame);
  1593. delete newFrame;
  1594. }catch(e){log("wmLibrary.promptText.close: "+e);}};
  1595. // Add save and close buttons
  1596. frameBody.appendChild(
  1597. createElement("textArea",{textContent:s,style:"height:90%;width:100%;"})
  1598. );
  1599. frameBody.appendChild(
  1600. createElement("div", {id:"buttons_holder"}, [
  1601. createElement('button',{id:"closeBtn", textContent:"Close",title:"Close window",onclick:close}),
  1602. ])
  1603. );
  1604. var center=function(){try{
  1605. var style=newFrame.style;
  1606. var node=newFrame;
  1607. style.display = '';
  1608. style.top = Math.floor((window.innerHeight/2)-(node.offsetHeight/2)) + 'px';
  1609. style.left = Math.floor((window.innerWidth/2)-(node.offsetWidth/2)) + 'px';
  1610. }catch(e){log("wmLibrary.promptText.center: "+e);}};
  1611. center();
  1612. window.addEventListener('resize', center, false); // Center it on resize
  1613.  
  1614. // Close frame on window close
  1615. window.addEventListener('beforeunload', function(){newFrame.remove(this);}, false);
  1616. }, false);
  1617. }catch(e){log("wmLibrary.promptText: "+e);}};
  1618.  
  1619. //***************************************************************************************************************************************
  1620. //***** Text To Script
  1621. //***************************************************************************************************************************************
  1622.  
  1623. //force code to be run outside the GM sandbox
  1624. sandbox.contentEval = function(source) {try{
  1625. // Check for function input.
  1626. if ('function' == typeof source) {
  1627. // Execute this function with no arguments, by adding parentheses.
  1628. // One set around the function, required for valid syntax, and a
  1629. // second empty set calls the surrounded function.
  1630. source = '(' + source + ')();'
  1631. }
  1632.  
  1633. // Create a script node holding this source code.
  1634. var script = document.createElement('script');
  1635. script.setAttribute("type", "application/javascript");
  1636. script.textContent = source;
  1637.  
  1638. // Insert the script node into the page, so it will run, and immediately
  1639. // remove it to clean up.
  1640. document.body.appendChild(script);
  1641. document.body.removeChild(script);
  1642. }catch(e){log("wmLibrary.contentEval: "+e);}};
  1643.  
  1644. //***************************************************************************************************************************************
  1645. //***** RegExp Construction
  1646. //***************************************************************************************************************************************
  1647.  
  1648. //convert an array to a pipe delimited RegExp group
  1649. sandbox.arrayToRegExp = function(a) {try{
  1650. var ret="";
  1651. if (isArrayAndNotEmpty(a)) {
  1652. ret="(";
  1653. for (var i=0,len=a.length; i<len;i++){
  1654. ret=ret+a[i];
  1655. if (i<(len-1)) ret=ret+"|";
  1656. }
  1657. ret=ret+")";
  1658. }
  1659. return ret;
  1660. }catch(e){log("wmLibrary.arrayToRegExp: "+e);}};
  1661.  
  1662. //takes an integer range and converts it to a regular expression
  1663. //which can search for that number range in a string
  1664. sandbox.integerRangeToRegExp = function(params) {try{
  1665. params=params||{};
  1666. var min=params.min.toString(), max=params.max.toString();
  1667. var ret="";
  1668.  
  1669. //on the odd case that both min and max values were equal
  1670. if (max==min) return max;
  1671. //count shared digits we can omit from complex regexp
  1672. var numSharedDigits=0;
  1673. if (min.length==max.length) {
  1674. for (var n=max.length;n>0;n--){
  1675. if (max.substring(0,n) == min.substring(0,n)) {
  1676. numSharedDigits=n;
  1677. break;
  1678. }
  1679. }
  1680. }
  1681. var shared=max.substring(0,numSharedDigits);
  1682. //crop the min and max values
  1683. min=min.removePrefix(shared);
  1684. max=max.removePrefix(shared);
  1685.  
  1686. //move the shared stuff to the front of the test
  1687. ret+=shared+"(";
  1688.  
  1689. //count the digits
  1690. var minDigits=min.length;
  1691. var maxDigits=max.length;
  1692.  
  1693. //set some flags
  1694. var isSingleDigit=(minDigits==1 && maxDigits==1);
  1695. var isVariableDigits=(minDigits != maxDigits);
  1696. //using 1 to 4444 as a range
  1697. //calculate maximum range tests
  1698. //ie: 444x 44xx 4xxx
  1699. if (maxDigits>1){
  1700. ret+=max.substr(0,maxDigits-1)+"[0-"+max.substr(maxDigits-1,1)+"]";
  1701. for (var n=(maxDigits-2); n>0; n--) {
  1702. if (max.substr(n,1)!="0") {
  1703. ret+="|"+max.substr(0,n)+"[0-"+(val(max.substr(n,1))-1)+"]"+("\\d").repeat((maxDigits-1)-n);
  1704. }
  1705. }
  1706. }
  1707.  
  1708. //calculate intermediate range tests
  1709. //ie: 1xxx, 1xx, 1x
  1710. for (var n=maxDigits;n>1;n--){
  1711. //check if min and max both use this digit
  1712. if (minDigits==n && maxDigits==n) {
  1713. //as neither bound would be put out of range
  1714. //and the bounds are not equal
  1715. if ((min.substr(0,1)!="9") && (max.substr(0,1)!="1") && (val(max.substr(0,1))>(val(min.substr(0,1))+1))) {
  1716. ret+="|["+(val(min.substr(0,1))+1)+"-"+(val(max.substr(0,1))-1)+"]"+("\\d").repeat(n-1);
  1717. }
  1718. //detect if min uses this digit
  1719. } else if (minDigits==n) {
  1720. //as long as it does not start with 9
  1721. if (min.substr(0,1)!="9") {
  1722. ret+="|["+(val(min.substr(0,1))+1)+"-9]"+("\\d").repeat(n-1);
  1723. }
  1724. break;
  1725. //detect if max uses this digit
  1726. } else if (maxDigits==n) {
  1727. //as long as it does not start with 1
  1728. if (max.substr(0,1)!="1") {
  1729. ret+="|[1-"+(val(max.substr(0,1))-1)+"]"+("\\d").repeat(n-1);
  1730. }
  1731. } else {
  1732. //they do not use this digit
  1733. //is it BETWEEN their digit counts
  1734. if (n > minDigits) {
  1735. ret+="|[1-9]"+("\\d").repeat(n-1);
  1736. }
  1737. }
  1738. }
  1739. //calculate minimum range tests
  1740. //ie: [1-9]
  1741. if (minDigits>1){
  1742. ret+="|"+min.substr(0,minDigits-1)+"["+min.substr(minDigits-1,1)+"-9]";
  1743. for (var n=(minDigits-2); n>0; n--) {
  1744. if (min.substr(n,1)!="9") {
  1745. ret+="|"+min.substr(0,n)+"["+(val(min.substr(n,1))+1)+"-9]"+("[0-9]").repeat((minDigits-1)-n);
  1746. }
  1747. }
  1748. } else {
  1749. //single digit min
  1750. if (maxDigits>minDigits) {
  1751. ret+="|["+min+"-9]";
  1752. } else {
  1753. //both min and max are single digits
  1754. ret+="|["+min+"-"+max+"]";
  1755. }
  1756. }
  1757. //fix same start and end range issues
  1758. for (var i=0;i<=9;i++){
  1759. ret=ret.replace(new RegExp("\\["+i+"-"+i+"\\]","gi"),i);
  1760. }
  1761. ret=ret.replace(new RegExp("\\[0-9\\]","gi"),"\\d");
  1762. return ret+")";
  1763. }catch(e){log("wmLibrary.integerRangeToRegExp: "+e);}};
  1764.  
  1765. //***************************************************************************************************************************************
  1766. //***** Typing Simulation
  1767. //***************************************************************************************************************************************
  1768.  
  1769. sandbox.simulateKeyEvent = function(character,byCode) {
  1770. var evt = document.createEvent("KeyboardEvent");
  1771. (evt.initKeyEvent || evt.initKeyboardEvent)("keypress", true, true, window,
  1772. 0, 0, 0, 0,
  1773. 0, ((byCode||null) || character.charCodeAt(0)) )
  1774. var canceled = !body.dispatchEvent(evt);
  1775. if(canceled) {
  1776. // A handler called preventDefault
  1777. alert("canceled");
  1778. } else {
  1779. // None of the handlers called preventDefault
  1780. alert("not canceled");
  1781. }
  1782. };
  1783.  
  1784. sandbox.typeText = function(s) {
  1785. for (var i=0,len=s.length; i<len; i++){
  1786. simulateKeyEvent(s.substr(i,1));
  1787. log(s.substr(i,1));
  1788. }
  1789. };
  1790.  
  1791. sandbox.typeEnter = function() {
  1792. simulateKeyEvent(null,13);
  1793. };
  1794.  
  1795. /*formatting notes
  1796. format a number to x decimal places
  1797. number.toFixed(x);
  1798.  
  1799. convert to hexidecimal
  1800. number.toString(16);
  1801.  
  1802. //try something like this to get your own header details
  1803. define your own parseHeaders function
  1804. var fileMETA = parseHeaders(<><![CDATA[
  1805. // ==UserScript==
  1806. // @name My Script
  1807. // @namespace http://www.example.com/gmscripts
  1808. // @description Scripting is fun
  1809. // @copyright 2009+, John Doe (http://www.example.com/~jdoe)
  1810. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  1811. // @version 0.0.1
  1812. // @include http://www.example.com/*
  1813. // @include http://www.example.org/*
  1814. // @exclude http://www.example.org/foo
  1815. // @require foo.js
  1816. // @resource resourceName1 resource1.png
  1817. // @resource resourceName2 http://www.example.com/resource2.png
  1818. // @uso:script scriptid
  1819. // ==/UserScript==
  1820. ]]></>.toString());
  1821.  
  1822. //include jquery stuff
  1823. // ==UserScript==
  1824. // @name jQuery Example
  1825. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
  1826. // ==/UserScript==
  1827. */
  1828.  
  1829. //a custom collection wrapper
  1830. //this pretty much mimics collections in visual basic
  1831. //with a lot of collection methods added from other systems
  1832. var jsCollection=function(objOrArray){
  1833. var self=this;
  1834. this.items={};
  1835. //return an item from this collection by index or key
  1836. this.__defineGetter__("item",function(indexOrKey){try{
  1837. return this.items[indexOrKey]||null;
  1838. }catch(e){log("jsCollection.item: "+e);}});
  1839.  
  1840. //return the count of items in this collection
  1841. this.__defineGetter__("count",function(){try{
  1842. var ret=0;
  1843. for (var e in this.items) ret++;
  1844. return ret;
  1845. }catch(e){log("jsCollection.count: "+e);}});
  1846. //return true if the count of items in this collection is 0
  1847. this.__defineGetter__("isEmpty",function(){try{
  1848. return this.count==0;
  1849. }catch(e){log("jsCollection.isEmpty: "+e);}});
  1850.  
  1851. //remove all items from this collection
  1852. this.clear=function(){try{
  1853. while(this.items[0]) delete this.items[0];
  1854. }catch(e){log("jsCollection.clear: "+e);}};
  1855. //return the index of the first occurence of obj
  1856. this.indexOf=function(obj){try{
  1857. var c=0;
  1858. for (var i in this.items){
  1859. if (this.items[i]===obj) {
  1860. return c;
  1861. break;
  1862. }
  1863. c++;
  1864. }
  1865. return -1;
  1866. }catch(e){log("jsCollection.indexOf: "+e);}};
  1867.  
  1868. //return the key of the first occurence of obj
  1869. this.keyOf=function(obj){try{
  1870. for (var i in this.items){
  1871. if (this.items[i]===obj) {
  1872. return i;
  1873. break;
  1874. }
  1875. }
  1876. return -1;
  1877. }catch(e){log("jsCollection.keyOf: "+e);}};
  1878.  
  1879. //returns true if obj occurs in this collection
  1880. this.contains=function(obj){try{
  1881. return this.indexOf(obj)!=-1;
  1882. }catch(e){log("jsCollection.contains: "+e);}};
  1883. //returns true if an item in this collection has key = key
  1884. this.containsKey=function(key){try{
  1885. return exists(this.items[key]);
  1886. }catch(e){log("jsCollection.containsKey: "+e);}};
  1887.  
  1888. //remove an item from the collection by index or key
  1889. this.remove=function(indexOrKey){try{
  1890. delete this.items[indexOrKey];
  1891. }catch(e){log("jsCollection.remove: "+e);}};
  1892. //add an item to the collection
  1893. //with optional key which defaults to unique()
  1894. //with optional before which is an object to match
  1895. //with optional after which is an object to match
  1896. this.add=function(item,key,before,after){try{
  1897. key=key||unique();
  1898. if (before && this.indexOf(before)!=-1) {
  1899. var ret={};
  1900. for (var i in this.items){
  1901. if (this.items[i]===before) {
  1902. ret[key]=item;
  1903. }
  1904. ret[i]=this.items[i];
  1905. }
  1906. this.items=ret;
  1907. } else if (after && this.indexOf(after)!=-1) {
  1908. var ret={};
  1909. for (var i in this.items){
  1910. ret[i]=this.items[i];
  1911. if (this.items[i]===after) {
  1912. ret[key]=item;
  1913. }
  1914. }
  1915. this.items=ret;
  1916. } else {
  1917. this.items[key]=item;
  1918. }
  1919. }catch(e){log("jsCollection.add: "+e);}};
  1920. //shortform to add an item
  1921. //after an item
  1922. //with optional key
  1923. this.insertAfter=function(item,after,key){try{
  1924. this.add(item,key,null,after);
  1925. }catch(e){log("jsCollection.insertAfter: "+e);}};
  1926.  
  1927. //shortform to add an item
  1928. //before an item
  1929. //with optional key
  1930. this.insertBefore=function(item,before,key){try{
  1931. this.add(item,key,before,null);
  1932. }catch(e){log("jsCollection.insertBefore: "+e);}};
  1933. //shortform to add an item
  1934. //with optional key
  1935. this.append=function(item,key){try{
  1936. this.add(item,key);
  1937. }catch(e){log("jsCollection.append: "+e);}};
  1938. //shortform to add an item
  1939. //to the beginning of the collection
  1940. //with optional key
  1941. this.prepend=function(item,key){try{
  1942. this.add(item,key,(this.items[0]||null));
  1943. }catch(e){log("jsCollection.prepend: "+e);}};
  1944.  
  1945. //add an array of items
  1946. //with optional before and after
  1947. this.addRange=function(itemArray,before,after){try{
  1948. if (before && this.indexOf(before)!=-1) {
  1949. var ret={};
  1950. for (var i in this.items){
  1951. if (this.items[i]===before) {
  1952. for (var a=0,len=itemArrayLength;a<len;a++){
  1953. ret[unique()]=itemArray[a];
  1954. }
  1955. }
  1956. ret[i]=this.items[i];
  1957. }
  1958. this.items=ret;
  1959. } else if (after && this.indexOf(after)!=-1) {
  1960. var ret={};
  1961. for (var i in this.items){
  1962. ret[i]=this.items[i];
  1963. if (this.items[i]===after) {
  1964. for (var a=0,len=itemArrayLength;a<len;a++){
  1965. ret[unique()]=itemArray[a];
  1966. }
  1967. }
  1968. }
  1969. this.items=ret;
  1970. } else {
  1971. for (var a=0,len=itemArrayLength;a<len;a++){
  1972. this.items[unique()]=itemArray[a];
  1973. }
  1974. }
  1975. }catch(e){log("jsCollection.addRange: "+e);}};
  1976.  
  1977. //shortform to add an array of items
  1978. this.appendRange=function(itemArray){try{
  1979. this.addRange(itemArray);
  1980. }catch(e){log("jsCollection.appendRange: "+e);}};
  1981. //shortform to add an array of items
  1982. //to the beginning of the collection
  1983. this.prependRange=function(itemArray){try{
  1984. this.addRange(itemArray,(this.items[0]||null));
  1985. }catch(e){log("jsCollection.prependRange: "+e);}};
  1986.  
  1987. //add a copy of item
  1988. //with optional before or after
  1989. this.addCopy=function(item,before,after){try{
  1990. this.add(item,null,before,after);
  1991. }catch(e){log("jsCollection.addCopy: "+e);}};
  1992.  
  1993. //add multiple copies of item
  1994. //with optional before and after
  1995. this.addCopies=function(item,count,before,after){try{
  1996. var ret=[];
  1997. for (var i=0;i<count;i++) ret.push(item);
  1998. this.addRange(item,before,after);
  1999. }catch(e){log("jsCollection.addCopies: "+e);}};
  2000.  
  2001. //return the collection converted to an array
  2002. this.toArray=function(){try{
  2003. return methodsToArray(this.items);
  2004. }catch(e){log("jsCollection.toArray: "+e);}};
  2005.  
  2006. //return the index of item with key=key
  2007. this.indexOfKey=function(key){try{
  2008. return this.indexOf(this.items[key]||null);
  2009. }catch(e){log("jsCollection.indexOfKey: "+e);}};
  2010. //return the key of the item at index=index
  2011. this.keyOfIndex=function(index){try{
  2012. var c=0;
  2013. for (var i in this.items){
  2014. if (c==index) return i;
  2015. c++;
  2016. }
  2017. }catch(e){log("jsCollection.keyOfIndex: "+e);}};
  2018.  
  2019. //use passed data on creation to create initial items
  2020. if (objOrArray){
  2021. if (isArrayAndNotEmpty(objOrArray)){
  2022. for (var i=0,len=objOrArray.length;i<len;i++){
  2023. this.add(objOrArray[i],i);
  2024. }
  2025. } else if (isObject(objOrArray)) {
  2026. for (var i in objOrArray){
  2027. this.items[i]=objOrArray[i];
  2028. }
  2029. }
  2030. }
  2031. //return self for external use
  2032. return this;
  2033. };
  2034.  
  2035. })();