WM Common Library

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

目前為 2014-09-04 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/416/16185/WM%20Common%20Library.js

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