WM Common Library

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

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