Dette scriptet burde ikke installeres direkte. Det er et bibliotek for andre script å inkludere med det nye metadirektivet // @require https://update.greasyfork.org/scripts/765/2185/WM%20Common%20Library.js
// ==UserScript==
// @name WM Common Library
// @namespace pt wm common library
// @description A collection of useful functions and objects, some of which are specific to the Wall Manager family of scripts.
// @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/
// @version 3.0.1.10
// @copyright Charlie Ewing except where noted
// ==/UserScript==
(function(){
var sandbox=this;
//***************************************************************************************************************************************
//***** Greasemonkey and Browser Type Validation
//***************************************************************************************************************************************
// is Greasemonkey running
sandbox.isGM = (typeof GM_getValue != 'undefined' && typeof GM_getValue('a', 'b') != 'undefined');
sandbox.isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
//***************************************************************************************************************************************
//***** Global Enumerated Values
//***************************************************************************************************************************************
//enumerated string equal to script that does nothing
sandbox.jsVoid="javascript:void(0)";
//time enums
sandbox.second=1000;
sandbox.minute=second*60;
sandbox.hour=minute*60;
sandbox.day=hour*24;
//***************************************************************************************************************************************
//***** Data Type Verification
//***************************************************************************************************************************************
//return true if o is undefined
sandbox.isUndefined=function(o){try{return ((typeof o)=="undefined");}catch(e){log("wmLibrary.isUndefined: "+e);}};
//return true if o is a string
sandbox.isString=function(o){try{return ((typeof o)=="string");}catch(e){log("wmLibrary.isString: "+e);}};
//return true if o is not undefined
sandbox.exists=function(o){try{return (!isUndefined(o));}catch(e){log("wmLibrary.exists: "+e);}};
// Returns true if object o is an array
sandbox.isArray=function(o){try{return (o instanceof Array);}catch(e){log("wmLibrary.isArray: "+e);}};
// Returns true if object o is an array and has a length > 0
sandbox.isArrayAndNotEmpty=function(o){try{return isArray(o) && o.length>0;}catch(e){log("wmLibrary.isArrayAndNotEmpty: "+e);}};
// Returns true if object o is an object but not an array
sandbox.isObject=function(o){try{return (((typeof o)=="object") && !isArray(o));}catch(e){log("wmLibrary.isObject: "+e);}};
//return true if o is undefined
//sandbox.isNaN=function(o){try{return (o.toString()==="NaN");}catch(e){log("wmLibrary.isNaN: "+e);}};
//return integer value of object
sandbox.val=function(o){try{return parseInt(o);}catch(e){log("wmLibrary.val: "+e);}};
sandbox.calcTime=function(timer) {try{
if ((typeof timer)=="integer") return timer;
if (timer.match(/^(\d)/)) return val(timer);
//debug.print(timer);
var t=2; //defaults to 2 minutes on error
//check for U:# time format (u = millisecond count)
if (timer.toLowerCase().startsWith("u:")) {
t=parseInt(timer.toLowerCase().split("u:")[1]||"");
return t;
}
//check for s:# (s = second count)
if (timer.toLowerCase().startsWith("s:")) {
t=parseInt(timer.toLowerCase().split("s:")[1]||"")||0;
return t*1000;
}
//check for t:#D:#H:#M:#S time format
if (timer.toLowerCase().startsWith("t:")){
var fnNumberFromHMSDate = function(i,l) {
var teststring = "(\\d)*?"+l;
var test = new RegExp(teststring,"i");
var testret = test.exec(i);
//debug.print([i,teststring,testret]);
return parseInt((testret||["0"])[0]);
};
t=timer.toLowerCase().split("t:")[1];
//it should now be in "1d:2h:5m:30s" format
var d = fnNumberFromHMSDate(t,"d");
var h = fnNumberFromHMSDate(t,"h");
var m = fnNumberFromHMSDate(t,"m");
var s = fnNumberFromHMSDate(t,"s");
//debug.print([d,h,m,s]);
return ((s*second)+(m*minute)+(h*hour)+(d*day));
}
//do originally programmed time words
switch(timer) {
case "off": return 0; break; //off
case "tenth": t = 0.1; break; // 6 seconds
case "sixth": t = 0.1666667; break; // 10 seconds
case "third": t = 0.3333333; break; // 20 seconds
case "half": t = 0.5; break; // 30 seconds
case "one": t = 1; break; // 1 minute
case "two": t = 2; break; // 2 minutes
case "three": t = 3; break; // 3 minutes
case "four": t = 4; break; // 4 minutes
case "five": t = 5; break; // 5 minutes
case "ten": t = 10; break; // 10 minutes
case "fifteen": t = 15; break; // 15 minutes
case "thirty": t = 30; break; // 30 minutes
case "hour": t = 60; break; // 1 hour
case "2hour": t = 60*2; break; // 2 hours
case "3hour": t = 60*3; break; // 3 hours
case "4hour": t = 60*4; break; // 4 hours
case "8hour": t = 60*8; break; // 8 hours
case "12hour": t = 60*12; break; // 12 hours
case "18hour": t = 60*18; break; // 18 hours
case "24hour": t = 60*24; break; // 1 day
case "36hour": t = 60*36; break; // 1.5 days
case "48hour": t = 60*48; break; // 2 days
case "30s2m": t = (Math.random() * 1.5) + 0.5; break; // random between 30s and 2m
case "2m5m": t = (Math.random() * 3) + 2; break; // random between 2m and 5m
case "5m10m": t = (Math.random() * 5) + 5; break; // random between 5m and 10m
}
return Math.round((t*60000)+(Math.random()*(t*100)));
}catch(e){log("wmLibrary.calcTime: "+e);}};
//comprehensive convert anything to a boolean value
sandbox.cBool = function(x){try{
//log(x||"undefined");
//capture undefined
if (!exists(x)) return false;
//capture nulls
if (x==null) return false;
//capture checkboxes
if (exists(x.checked)) x=x.checked;
//capture objects with value property
if (exists(x.value)) x=x.value;
//capture boolean values
if ((typeof x)=="boolean") return x;
//capture non-null objects
if (isObject(x)) return true;
//capture arrays
if (isArray(x)) return true;
//capture text
if (typeof x=="string") {
var trueVal=x;
if (exists(x.toLowerCase)) trueVal=x.toLowerCase();
switch(trueVal){
case "1": case "true": case "yes": case "checked": return true; break;
case "0": case "false": case "no": case "unchecked": return false; break;
}
}
//default
return Boolean(x);
}catch(e){log("wmLibrary.cBool: {x="+x+"}: "+e);}};
//***************************************************************************************************************************************
//***** Logging
//***************************************************************************************************************************************
// cross-browser log function, turns the log variable into a function
// originally from FVWM by Joe Simmons
// now also catches the WM debug window first
sandbox.log=function(){try{
var fx, debug=this.debug;
if (exists(debug)) fx=debug.print;
else if (isGM) fx=GM_log;
else if (window.opera) fx=opera.postError;
else fx=console.log;
if (fx) {var args=arguments, self=this; setTimeout(function(){fx.apply(self,args);},0); }
}catch(e){console.log("WmLibrary.log: "+e);}};
//***************************************************************************************************************************************
//***** Style Sheet Creation
//***************************************************************************************************************************************
//append css style to the header
//supply a name and this function will force that style sheet to have an id attribute equal to the name supplied
//supply a doc object and the stylesheet will be put in that document instead of this one
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);}};
//***************************************************************************************************************************************
//***** Mouse Events
//***************************************************************************************************************************************
//click specified DOM element
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);}};
//pretend to put the mouse over specified DOM element
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);}};
//***************************************************************************************************************************************
//***** DOM Creation/Manipulation
//***************************************************************************************************************************************
//return a DOM element by ID with optional alternate root document
sandbox.$=function(ID,root) {try{return (root||document).getElementById(ID);}catch(e){log("wmLibrary.$: "+e);}};
//return new DOM element a, with parameters b, and children c
sandbox.createElement=function(a,b,c) {try{
if(a=="text") {return document.createTextNode(b);};
var ret=document.createElement(a.toLowerCase());
if(b) for(var prop in b) {
if(prop.indexOf("on")==0) {
ret.addEventListener(prop.substring(2),b[prop],false);
} else if (
",style,accesskey,id,name,src,href,which,rel,action,method,value,data-ft".indexOf(","+prop.toLowerCase())!=-1
) {
ret.setAttribute(prop.toLowerCase(), b[prop]);
/*} else if (
!exists(ret[prop.toLowerCase()])
} {
ret.setAttribute(prop.toLowerCase(), b[prop]);*/
} else {
ret[prop]=b[prop];
}
}
if(c) c.forEach(
function(e) {
if (e) ret.appendChild(e);
}
);
return ret;
}catch(e){log("wmLibrary.createElement: "+e);}};
//return document.location.pathname
sandbox.getDocName=function() {try{return document.location.pathname;}catch(e){log("wmLibrary.getDocName: "+e);}};
//remove specified DOM element
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);}};
//return selected nodes using xpath, with additional parameters
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);}};
//return single selected node using xpath, with additional parameters
sandbox.selectSingleNode=function(xPath,params){try{params=params||{}; params['type']=9;return selectNodes(xPath,params).singleNodeValue;}catch(e){log("wmLibrary.selectSingleNode: "+e);}};
//for the selected nodes using xpath and additional parameters, perform passed function
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);}};
//fetch the selected elements from an html select multi into an array
//this fetches the ELEMENT not its value
sandbox.getSelectedOptions=function(elem){try{
var ret=[];
for (var i=0; i<elem.options.length; i++) {
if (elem.options[i].selected) ret.push(elem.options[i]);
}
return ret;
}catch(e){log("wmLibrary.getSelectedOptions: "+e);}};
//fetch the selected values from an html select multi into an array
//this fetches the VALUE not the element
sandbox.getSelectedOptionValues=function(elem){try{
var ret=[];
for (var i=0; i<elem.options.length; i++) {
if (elem.options[i].selected) ret.push(elem.options[i].value);
}
return ret;
}catch(e){log("wmLibrary.getSelectedOptionValues: "+e);}};
//attach an array of elements to a node
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);}};
//create a set of options for a selection list based on an array
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);}};
//select an element from a dropdown box with a certain value
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);}};
//return the value of a dropdown's selected inded
sandbox.valueOfSelect = function(obj){try{return obj.options[obj.selectedIndex].value;}catch(e){log("wmLibrary.valueOfSelect: "+e);}};
//hides all snapshots or iterations in an xpathResult object
sandbox.hideNodes=function(xPath,params) {try{forNodes(xPath,params,function(item){item.style.display="none";});}catch(e){log("wmLibrary.hideNodes: "+e);}};
//unhides all snapshots or iterations in an xpathResult object
sandbox.showNodes=function(xPath,params) {try{forNodes(xPath,params,function(item){item.style.display="";});}catch(e){log("wmLibrary.showNodes: "+e);}};
//move element up
sandbox.elementMoveUp=function(e){try{
//if this element has a parent
if (e.parentNode) {
//and its not the first child
if (e.parentNode.firstChild!=e){
//move it to just before its previous sibling
e.parentNode.insertBefore(e,e.previousSibling);
}
}
return e;
}catch(e){log("wmLibrary.elementMoveUp: "+e);}};
//move element down
sandbox.elementMoveDown=function(e){try{
//if this element has a parent
if (e.parentNode) {
//and its not the last child
if (e.parentNode.lastChild!=e){
//if the next sibling IS the last child
if (e.parentNode.lastChild==e.nextSibling){
//just move it to the bottom
e.parentNode.appendChild(e);
} else {
//insert it between the next sibling and the next next sibling
e.parentNode.insertBefore(e,e.nextSibling.nextSibling);
}
}
}
return e;
}catch(e){log("wmLibrary.elementMoveDown: "+e);}};
//move element up to top of container
sandbox.elementMoveTop=function(e){try{
//if this element has a parent
if (e.parentNode) {
//and its not the first child
if (e.parentNode.firstChild!=e){
//move it to the top of the container
e.parentNode.insertBefore(e,e.parentNode.firstChild);
}
}
return e;
}catch(e){log("wmLibrary.elementMoveTop: "+e);}};
//move element up to top of container
sandbox.elementMoveBottom=function(e){try{
//if this element has a parent
if (e.parentNode) {
//and its not the first child
if (e.parentNode.lastChild!=e){
//move it to the bottom of the container
e.parentNode.appendChild(e);
}
}
return e;
}catch(e){log("wmLibrary.elementMoveBottom: "+e);}};
//sort an element's children by an attribute
sandbox.elementSortChildren=function(e,by){try{
by=by||"name";
if (e && e.childNodes) {
//pack into an array
var ret=[];
for (var n=0;n<e.childNodes.length;n++) {
ret.push(e.childNodes[n]);
}
//sort the array
ret.sort(function(a,b){return a[by]>b[by]});
//fix order of display
for (var n=0;n<ret.length;n++) {
e.appendChild(ret[n]);
}
//clean up
ret=null;
}
}catch(e){log("wmLibrary.elementSortChildren: "+e);}};
//remove all of a node's child nodes
sandbox.removeAllChildren=function(e){
var node=e.childNodes[0];
while (node) {
remove(node);
node=e.childNodes[0];
}
};
//return the real url of a location
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);}};
// compile and return the true x,y scroll offset onscreen of an element in Firefox
sandbox.trueScrollOffset = function(o){try{
var offset={left:o.scrollLeft,top:o.scrollTop}, parentOffset=null;
if (!(o==document.body) && !(0==document.documentElement) && o.parentNode) parentOffset=trueScrollOffset(o.parentNode);
if (parentOffset) {
offset.left+=parentOffset.left||0;
offset.top+=parentOffset.top||0;
}
return offset;
}catch(e){log("wmLibrary.trueScrollOffset: "+e);}},
// compile and return the true x,y offset onscreen of an element in Firefox
sandbox.trueOffset = function(o){try{
var offset={left:o.offsetLeft,top:o.offsetTop}, parentOffset=null;
if (o.offsetParent) parentOffset=trueOffset(o.offsetParent);
if (parentOffset) {
offset.left+=parentOffset.left||0;
offset.top+=parentOffset.top||0;
}
return offset;
}catch(e){log("wmLibrary.trueOffset: "+e);}},
//force a page to transition to new location s even if changing the document location does not work
sandbox.linkTo = function(s) {try{
var link=document.body.appendChild(createElement("a",{href:s,target:"_top"}));
click(link);
}catch(e){log("wmLibrary.linkTo: "+e);}};
//***************************************************************************************************************************************
//***** Date/Time
//***************************************************************************************************************************************
//return a unix timestamp
sandbox.timeStamp=function(){try{return (new Date()).getTime();}catch(e){log("wmLibrary.timeStamp: "+e);}};
//return a facebook timestamp without millisecond data
sandbox.timeStampNoMS=function(){try{var t=timeStamp().toString(); return t.substr(0,t.length-3);}catch(e){log("wmLibrary.timeStampNoMS: "+e);}};
//returns a guaranteed unique timestamp in base36 prefixed with an underscore
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);}};
//***************************************************************************************************************************************
//***** String Prototype Additions
//***************************************************************************************************************************************
//return true if string starts with s
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);}};
//return true if string ends with s
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);}};
//return true if string contains s
sandbox.String.prototype.find = function(s) {try{
return (this.indexOf(s) != -1);
}catch(e){log("wmLibrary.String.prototype.find: "+e);}};
sandbox.String.prototype.contains = function(s) {return this.find(s);};
//inserts string s into this string at position startIndex
sandbox.String.prototype.insert = function(s,startIndex) {try{
return this.substr(0,startIndex)+s+this.substr(startIndex,this.length-startIndex);
}catch(e){log("wmLibrary.String.prototype.insert: "+e);}};
//pads the string with space or a specific character, on the left
//strings already longer than totalLength are not changed
sandbox.String.prototype.padLeft = function(totalLength,c) {try{
c=(c||" ").charAt(0);
if (totalLength>0){
return (totalLength<=this.length)?this:
c.repeat((totalLength-this.length))+this;
}
}catch(e){log("wmLibrary.String.prototype.padLeft: "+e);}};
//pads the string with space or a specific character, on the left
//strings already longer than totalLength are not changed
sandbox.String.prototype.padRight = function(totalLength,c) {try{
c=(c||" ").charAt(0);
if (totalLength>0){
return (totalLength<=this.length)?this:
this+c.repeat((totalLength-this.length));
}
}catch(e){log("wmLibrary.String.prototype.padright: "+e);}};
//return the string as an array of characters
sandbox.String.prototype.toCharArray = function() {try{
return this.split(/(.|\n|\r)/g);
}catch(e){log("wmLibrary.String.prototype.toCharArray: "+e);}};
//return the passed string minus spaces
sandbox.String.prototype.noSpaces = function(s) {try{return (this.replace(/\s+/g,''));}catch(e){log("wmLibrary.String.prototype.noSpaces: "+e);}};
//return the passed string with word first letters capitalized
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);}};
//return the passed string repeated n times
sandbox.String.prototype.repeat = function(n) {try{return new Array(n+1).join(this);}catch(e){log("wmLibrary.String.prototype.repeat: "+e);}};
//return the passed string minus line breaks
sandbox.String.prototype.noLineBreaks = function(s) {try{return (this.replace(/(\r\n|\n|\r)/gm," "));}catch(e){log("wmLibrary.String.prototype.noLineBreaks: "+e);}};
//return the passed string without beginning or ending quotes
sandbox.String.prototype.unQuote = function() {try{return this.replace(/^"|"$/g, '');}catch(e){log("wmLibrary.String.prototype.unQuote: "+e);}};
//return the passed string without beginning or ending quotes
sandbox.String.prototype.quote = function() {try{return "\""+this+"\"";}catch(e){log("wmLibrary.String.prototype.quote: "+e);}};
//return the passed string without beginning or ending brackets
sandbox.String.prototype.unBracket = function() {try{return this.replace(/^\[|\]$/g, '');}catch(e){log("wmLibrary.String.prototype.unBracket: "+e);}};
//return the passed string without beginning spaces
sandbox.String.prototype.trimStart = function(){try{
return this.replace(/^\s\s*/, '');
}catch(e){log("wmLibrary.String.prototype.trimStart: "+e);}};
//return the passed string without ending spaces
sandbox.String.prototype.trimEnd = function(){try{
return this.replace(/\s\s*$/, '');
}catch(e){log("wmLibrary.String.prototype.trimEnd: "+e);}};
//return the passed string without beginning or ending spaces
sandbox.String.prototype.trim = function(){try{
return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}catch(e){log("wmLibrary.String.prototype.trim: "+e);}};
//assuming passed string is a url parameter list, return named parameter's value or ""
//this works great for both search and hash parts
//do not pass a document.location without first splitting off search and hash parts
sandbox.String.prototype.getUrlParam = function(s) {try{
var r=this.removePrefix("#").removePrefix("?").split("&");
for (var p=0,param;(param=r[p]);p++){
if ( param.startsWith(s+"=") || param==s ) {
return (param.split("=")[1]||null);
}
}
return null;
}catch(e){log("wmLibrary.String.prototype.getUrlParam: "+e);}};
//return passed string with word added to end. words are separated by spaces
//alternately accepts an array of words to add
sandbox.String.prototype.addWord= function(word){try{
if (!isArray(word)) word=[word];
var words = this.split(" ");
var ret=this;
for (var w=0,len=word.length;w<len;w++){
if (!words.inArray(word[w])) ret=ret+" "+word;
}
return ret;
}catch(e){log("wmLibrary.String.prototype.addWord: "+e);}};
//return passed string minus specified word
//alternately accepts an array of words to remove
sandbox.String.prototype.removeWord= function(word){try{
if (!isArray(word)) word=[word];
var words=this.split(" ");
var ret;
for (var w=0,len=word.length;w<len;w++){
ret = words.removeByValue(word[w]);
}
return ret.join(" ");
}catch(e){log("wmLibrary.String.prototype.removeWord: "+e);}};
//return true if passed string contains word
sandbox.String.prototype.containsWord= function(word){try{return this.split(" ").inArray(word);}catch(e){log("wmLibrary.String.prototype.containsWord: "+e);}};
//return passed string with word replaced with word2
sandbox.String.prototype.replaceWord= function(word,word2){try{return this.split(" ").replace(word,word2).join(" ");}catch(e){log("wmLibrary.String.prototype.replaceWord: "+e);}};
//return passed string with word toggled
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);}};
//return passed string with word toggled based on a boolean input
sandbox.String.prototype.toggleWordB = function(bool,word){try{
return this[(bool?"add":"remove")+"Word"](word);
}catch(e){log("wmLibrary.String.prototype.toggleWordB: "+e);}};
//return passed string with word swapped for another based on a boolean input
//if bool==true then we return string including word1 and excluding word2
//else we return string including word2 and excluding word1
sandbox.String.prototype.swapWordB = function(bool,word1,word2){try{
return this.replaceWord((bool?word2:word1),(bool?word1:word2));
}catch(e){log("wmLibrary.String.prototype.swapWordB: "+e);}};
//return passed string minus prefix of s if it exists
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);}};
//return passed string minus suffix of s if it exists
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);}};
// visual basic alternate for string.toLowerCase()
sandbox.String.prototype.lcase = function() {try{return this.toLowercase();}catch(e){log("wmLibrary.String.prototype.lcase: "+e);}};
// visual basic alternate for string.toUpperCase()
sandbox.String.prototype.ucase = function() {try{return this.toUppercase();}catch(e){log("wmLibrary.String.prototype.ucase: "+e);}};
// copy the calling string to the clipboard (IE or GM)
sandbox.String.prototype.toClipboard = function() {try{
if (window.clipboardData){
window.clipboardData.setData("Text", this);
} else if (unsafeWindow) {
try{
unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
} catch(e){
log("wmLibrary.String.prototype.toClipboard: Cannot enable privelege 'UniversalXPConnect'. Be sure that 'signed.applets.codebase_principal_support' is set to true in 'about:config'");
}
const clipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
clipboardHelper.copyString(this);
} else {
log("wmLibrary.String.prototype.toClipboard: Cannot perform task");
}
} catch(e){log("wmLibrary.String.prototype.toClipboard: "+e);}};
//replaces all instances of {x} with passed argument x
//or arguments[0][x] when the first argument is an array
sandbox.String.prototype.format = function() {try{
var ret = this;
var args=arguments; //use argument mode
if (isArray(args[0])) args=args[0]; //switch to array mode
for (var i = 0; i < args.length; i++) {
var re = new RegExp('\\{'+i+'\\}', 'gi');
ret = ret.replace(re, args[i]);
}
return ret;
}catch(e){log("wmLibrary.String.prototype.format: "+e);}};
//similar to String.format, except that instances of {%x} are replaced
//instead of instances of {x}
sandbox.String.prototype.format2 = function() {try{
var ret = this;
var args=arguments; //use argument mode
if (isArray(args[0])) args=args[0]; //switch to array mode
for (var i=0; i < args.length; i++) {
var re = new RegExp('\\{%'+i+'\\}', 'gi');
ret = ret.replace(re, args[i]);
}
return ret;
}catch(e){log("wmLibrary.String.prototype.format2: "+e);}};
//returns true if the string is zero-length
sandbox.String.prototype.isEmpty = function() {try{
return this.length==0;
}catch(e){log("wmLibrary.String.prototype.isEmpty: "+e);}};
//format a JSON string with linebreaks and indents
//with optional indent number to set indent length in spaces
//default intent is a tab character
sandbox.String.prototype.formatJSON = function(indent) {try{
indent=(indent)?(" ").repeat(indent):"\t";
//first lets convert the supposed JSON string to an actual object
//so we can validate that it is of good format
var topObj=JSON.parse(this);
//if we got this far, it is valid
//make a function to spell our our branches
var writeBranch=function(obj,name,level){
var ret="";
//start our output string
ret+=(level)?indent.repeat(level):"";
ret+=(name)?JSON.stringify(name)+": ":"";
ret+=(isArray(obj))?
"["+((!obj.isEmpty())?
"\n":
""
):
(isObject(obj))?
"{"+((!methodsToArray(obj).isEmpty())?
"\n":
""
):
"";
//draw the inside object(s)
var c=0;
if (isArray(obj)) for (var i=0,len=obj.length;i<len;i++){
//write arrays out
if (i>0) ret+=",\n";
ret+=writeBranch(obj[i],null,level+1);
} else if (isObject(obj)) for (var i in obj){
if (c>0) ret+=",\n";
//write objects out
ret+=writeBranch(obj[i],i,level+1);
c++;
} else {
//branch is not an object or array
ret+=JSON.stringify(obj);
}
//end our output string
ret+=(isArray(obj))?
((!obj.isEmpty())?
"\n"+((level)?
indent.repeat(level):
""
):
""
)+"]":
(isObject(obj))?
((!methodsToArray(obj).isEmpty())?
"\n"+((level)?
indent.repeat(level):
""
):
""
)+"}":
"";
//back to previous branch
return ret;
}
//start writing the branches
return writeBranch(topObj,null,0);
}catch(e){log("wmLibrary.String.prototype.formatJSON: "+e);}};
//returns the longested quoted text within the calling string
sandbox.String.prototype.longestQuoteWithin = function() {try{
var p=0, c=0, s="", a=0, b=0, l=0;
while (p<this.length){
a=this.indexOf('"', p);
if (a!=-1) {
p=a+1;
b=this.indexOf('"',p);
if (b!=-1) {
p=b+1;
l=b-a;
if (l>c) {
c=l;
s=this.substr(a+1,l-1);
}
} else {
p=this.length;
}
} else {
p=this.length;
}
}
return s;
}catch(e){log("wmLibrary.String.prototype.longestQuoteWithin: "+e);}};
//***************************************************************************************************************************************
//***** Array Prototype Additions
//***************************************************************************************************************************************
//returns true if the array is zero-length
sandbox.Array.prototype.isEmpty = function() {try{
return this.length==0;
}catch(e){log("wmLibrary.Array.prototype.isEmpty: "+e);}};
//return passed array with element x and element y swapped
sandbox.Array.prototype.swap = function (x,y) {try{
var b = this[x];
this[x] = this[y];
this[y] = b;
return this;
}catch(e){log("wmLibrary.Array.prototype.swap: "+e);}};
//return true if a value exists in the array
//with optional startIndex
//and optional count which specifies the number of elements to examine
sandbox.Array.prototype.inArray = function(value,startIndex,count) {try{
startIndex=startIndex||0;
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.inArray: Error: startIndex out of bounds");
return false;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.inArray: Error: count is less than 1");
return false;
}
var c=0;
for(var i=this.length-1; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i--) {
c++;
if(this[i]==value) return true;
}
return false;
}catch(e){log("wmLibrary.Array.prototype.inArray: "+e);}};
//alias for inArray
sandbox.Array.prototype.contains = function(value,startIndex,count) {return this.inArray(value,startIndex,count);};
//return the location of a value in an array
//with optional startIndex
//and optional count which specifies the number of elements to examine
sandbox.Array.prototype.inArrayWhere = function(value,startIndex,count) {try{
startIndex=startIndex||0;
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.inArrayWhere: Error: startIndex out of bounds");
return -1;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.inArrayWhere: Error: count is less than 1");
return -1;
}
var c=0;
for(var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++) {
c++;
if(this[i]==value) return i;
}
return -1;
}catch(e){log("wmLibrary.Array.prototype.inArrayWhere: "+e);}};
//alias for inArrayWhere
sandbox.Array.prototype.indexOf = function(value,startIndex,count) {return this.inArrayWhere(value,startIndex,count);};
//return the location of the last occurence of value in an array
//with optional startIndex
//and optional count which specifies the number of elements to examine
sandbox.Array.prototype.lastIndexOf = function(value,startIndex,count) {try{
startIndex=startIndex||0;
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.lastIndexOf: Error: startIndex out of bounds");
return -1;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.lastIndexOf: Error: count is less than 1");
return -1;
}
var c=0;
for(var i=this.length; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i++) {
c++;
if(this[i]==value) return i;
}
return -1;
}catch(e){log("wmLibrary.Array.prototype.lastIndexOf: "+e);}};
//return true if the location of value is 0
sandbox.Array.prototype.startsWith = function(value){return this.inArrayWhere(value)===0;}
//return the last value in an array
sandbox.Array.prototype.last = function() {try{return this[this.length - 1];}catch(e){log("wmLibrary.Array.prototype.last: "+e);}};
//return true if the content of the last index is equal to value
sandbox.Array.prototype.endsWith = function(value){return this.last()===value;}
//return the array will spaces removed from every element
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);}};
//remove the first instance of a value in an array
//now accepts an array of values to remove
//removes the first instance of every item in array passed
//returns the calling array
sandbox.Array.prototype.removeByValue = function(values) {try{
if (!isArray(values)) values=[values];
for (var i=0,len=values.length; i<len;i++) {
var e=this.inArrayWhere(values[i]);
if(e>=0)this.splice(e,1);
}
return this;
}catch(e){log("wmLibrary.Array.prototype.removeByValue: "+e);}};
//replace all instances of a value in an array
//returns the calling array
sandbox.Array.prototype.replaceAll = function(val, val2) {try{
var i=this.inArrayWhere(val);
while(i>=0) {
this[i]=val2;
i=this.inArrayWhere(val,i+1);
}
return this;
}catch(e){log("wmLibrary.Array.prototype.replaceAll: "+e);}};
//remove all instances of a value in an array
//now accepts an array of values to remove
//returns the calling array
sandbox.Array.prototype.removeAllByValue = function(values) {try{
if (!isArray(values)) values=[values];
for (var i=0,len=values.length; i<len;i++) {
var e=this.inArrayWhere(values[i]);
while (e>=0){
if(e>=0)this.splice(e,1);
e=this.inArrayWhere(values[i],e+1);
}
}
return this;
}catch(e){log("wmLibrary.Array.prototype.removeAllByValue: "+e);}};
//replace the first instance of a value in an array
//returns the calling array
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);}};
//remove element i of an array
//returns the calling array
sandbox.Array.prototype.remove = function(i) {try{this.splice(i,1); return this;}catch(e){log("wmLibrary.Array.prototype.remove: "+e);}};
//remove elements beyond specified new size
//or add elements to fill the new size equal to defaultValue
sandbox.Array.prototype.resize = function(newSize,defaultValue) {try{
if (this.length>newSize) {
this.splice(newSize,this.length-newSize);
} else {
for (var i=this.length;i<newSize;i++){
this[i]=defaultValue;
}
}
return this;
}catch(e){log("wmLibrary.Array.prototype.resize: "+e);}};
//return a random element of an array
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);}};
//sorts an array by string length, priming it for text searches, putting longest strings first so that "pea" is not found before "peanut"
//returns the calling array
sandbox.Array.prototype.optimize = function(){try{if (this.length>1) {for (var i=this.length-1;i>0;i--) {for (var i2=i-1;i2>0;i2--){if (this[i].length > this[i2].length) {var b=this[i]; this[i]=this[i2]; this[i2]=b; b=null;}}}}; return this;}catch(e){log("wmLibrary.Array.prototype.optimize: "+e);}};
//returns a shallow copy of the calling array
sandbox.Array.prototype.clone = function(){try{
var ret=[];
if (this.length) for (var i=0,len=this.length; i<len; i++){
ret.push(this[i]);
}
return ret;
}catch(e){log("wmLibrary.Array.prototype.clone: "+e);}};
//reverses the elements of an array
//with optional startIndex
//and optional count which limits the reverse section
//if startIndex+count is greater than the length of the array
//then only the available section is reversed
//returns the calling array
sandbox.Array.prototype.reverse = function(startIndex,count){try{
startIndex=startIndex||0;
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.reverse: Error: startIndex out of bounds");
return -1;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.reverse: Error: count is less than 1");
return -1;
}
var endIndex=(exists(count))?startIndex+count:this.length-1;
if (endIndex>this.length-1) endIndex=this.length-1;
while (startIndex>endIndex){
this.swap(startIndex,endIndex);
startIndex++;
endIndex--;
}
return this;
}catch(e){log("wmLibrary.Array.prototype.reverse: "+e);}};
//sets a range of elements in the array to the defaultValue
//returns the calling array
sandbox.Array.prototype.clear = function(startIndex,count,defaultValue){try{
if (count>0 && this.length>startIndex) {
for (var i=startIndex,len=this.length; (i<len && i<(startIndex+count)); i++){
this[i]=defaultValue;
}
}
return this;
}catch(e){log("wmLibrary.Array.prototype.clear: "+e);}};
//copies elements from this array to a destination destArray
//starting in this array at sourceIndex
//and pasting into the destArray at destIndex
//where length is the number of elements to copy
//pasting beyond the higher bounds of the destArray simply increases the array size
//returns the calling array
sandbox.Array.prototype.copy = function(sourceIndex,destArray,destIndex,length){try{
if (!isArray(destArray)) {
log("wmLibrary.Array.prototype.copy: Error: destArray is not an array");
return this;
}
if (sourceIndex >= this.length) {
//log("wmLibrary.Array.prototype.copy: Error: sourceIndex out of bounds");
return this;
}
for (var i=0; i<length; i++){
destArray[destIndex+i]=this[sourceIndex+i];
}
return this;
}catch(e){log("wmLibrary.Array.prototype.copy: "+e);}};
//copies all elements from this array to a destination destArray
//pasting into the destArray at destIndex
//pasting beyond the higher bounds of the destArray simply increases the array size
//returns the calling array
sandbox.Array.prototype.copyTo = function(destArray,destIndex){try{
if (!isArray(destArray)) {
log("wmLibrary.Array.prototype.copyTo: Error: destArray is not an array");
return this;
}
for (var i=0, len=this.length; i<len; i++){
destArray[destIndex+i]=this[i];
}
return this;
}catch(e){log("wmLibrary.Array.prototype.copyTo: "+e);}};
//returns an array containing elements from the current array where the element has parameter p equal to value v
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);}};
//returns the element matched by matchFunc, or null
//with optional start index
//and optional count to limit the number of searched elements
sandbox.Array.prototype.find = function(matchFunc,startIndex,count) {try{
startIndex=startIndex||0;
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.find: Error: startIndex out of bounds");
return null;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.find: Error: count is less than 1");
return null;
}
var c=0;
for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
c++;
if (matchFunc(this[i])) {
return this[i];
break;
}
}
return null;
}catch(e){log("wmLibrary.Array.prototype.find: "+e);}};
//returns the index of element matched by matchFunc, or -1
//with optional startIndex
//and optional count which specifies the number of elements to check
sandbox.Array.prototype.findIndex = function(matchFunc,startIndex,count) {try{
startIndex=startIndex||0;
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.findIndex: Error: startIndex out of bounds");
return -1;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.findIndex: Error: count is less than 1");
return -1;
}
var c=0;
for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
c++;
if (matchFunc(this[i])) {
return i;
break;
}
}
return -1;
}catch(e){log("wmLibrary.Array.prototype.findIndex: "+e);}};
//returns all elements matched by matchFunc, or null
//with optional start index
//and optional count to limit the number of elements searched
sandbox.Array.prototype.findAll = function(matchFunc,startIndex,count) {try{
startIndex=startIndex||0;
var ret=[];
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.findAll: Error: startIndex out of bounds");
return null;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.findAll: Error: count is less than 1");
return null;
}
var c=0;
for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
c++;
if (matchFunc(this[i])) {
ret.push(this[i]);
}
}
return (isArrayAndNotEmpty(ret))?ret:null;
}catch(e){log("wmLibrary.Array.prototype.findAll: "+e);}};
//returns true if all elements in the array match the matchFunc
//with optional start index
//and optional count to limit the number of elements searched
sandbox.Array.prototype.trueForAll = function(matchFunc,startIndex,count) {try{
startIndex=startIndex||0;
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.trueForAll: Error: startIndex out of bounds");
return false;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.trueForAll: Error: count is less than 1");
return false;
}
var c=0;
for (var i=startIndex,len=this.length; (i<len && (!exists(count) || (exists(count) && c<count))); i++){
c++;
if (!matchFunc(this[i])) {
return false;
}
}
return true;
}catch(e){log("wmLibrary.Array.prototype.trueForAll: "+e);}};
//returns true if array contains an element matched by the matchFunc
//with optional startIndex
//and optional count which specifies the number of elements to check
sandbox.Array.prototype.exists = function(matchFunc,startIndex,count) {try{
return this.findIndex(matchFunc,startIndex,count)!=-1;
}catch(e){log("wmLibrary.Array.prototype.exists: "+e);}};
//returns the last element matched by matchFunc, or null
//with optional start index
//and optional count to limit the number of searched elements
sandbox.Array.prototype.findLast = function(matchFunc,startIndex,count) {try{
startIndex=startIndex||0;
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.findLast: Error: startIndex out of bounds");
return null;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.findLast: Error: count is less than 1");
return null;
}
var c=0;
for (var i=this.length; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i--){
c++;
if (matchFunc(this[i])) {
return this[i];
break;
}
}
return null;
}catch(e){log("wmLibrary.Array.prototype.findLast: "+e);}};
//returns the last element matched by matchFunc, or -1
//with optional start index
//and optional count which specifies the number of elements to check
sandbox.Array.prototype.findLastIndex = function(matchFunc,startIndex,count) {try{
startIndex=startIndex||0;
if (startIndex>=this.length) {
//log("wmLibrary.Array.prototype.findLastIndex: Error: startIndex out of bounds");
return -1;
}
if (exists(count) && count<1) {
//log("wmLibrary.Array.prototype.findLastIndex: Error: count is less than 1");
return -1;
}
var c=0;
for (var i=this.length; (i>=startIndex && (!exists(count) || (exists(count) && c<count))); i--){
c++;
if (matchFunc(this[i])) {
return i;
break;
}
}
return -1;
}catch(e){log("wmLibrary.Array.prototype.findLastIndex: "+e);}};
//***************************************************************************************************************************************
//***** JSON/OBJECT Construction and Matching
//***************************************************************************************************************************************
//returns the merge of any number of JSON objects passed as unnamed arguments
sandbox.mergeJSON_long = function(){try{
var ret = {};
//for each JSON object passed
for (var a=0,len=arguments.length;a<len;a++) {
//for each element in that object
for (var v in arguments[a]) {
if (!exists(ret[v])) {
//simply copy the element to the return value
ret[v] = arguments[a][v];
} else {
if ((typeof arguments[a][v])=="object") {
//merge the two elements, preserving tree structure
ret[v] = mergeJSON(ret[v], arguments[a][v]);
} else {
//overwrite simple variable
ret[v] = arguments[a][v];
}
}
}
}
//the problem here is that its way too recursive and jams firefox often
return ret;
}catch(e){log("wmLibrary.mergeJSON: "+e);}};
sandbox.mergeJSON = function(){try{
var ret = {};
//for each JSON object passed
for (var a=0,len=arguments.length;a<len;a++) {
//for each element in that object
for (var v in arguments[a]) {
//replace the initial element with that of the next
ret[v] = arguments[a][v];
}
//the problem here is that only the top level branches are preserved
}
return ret;
}catch(e){log("wmLibrary.mergeJSON: "+e);}};
//returns all members of an array that have a specified parameter with a specified value
//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);}};
//returns all members of an array that have a specified parameter with a specified value
//now accepts input of array or object
//can now specify output of array or object
sandbox.matchByParam=function(o,param,value,outputType){try{
if(!exists(outputType)) outputType="array";
var inputType=(isArray(o))?"array":((typeof o) == "object")?"object":"unknown";
var ret=(outputType=="object")?{}:[]; //default to array on error
switch(inputType){
case "array": for (var i=0,e;(e=o[i]);i++){
switch(outputType){
case "array": if (e[param]==value) ret.push(e); break;
case "object": if (e[param]==value) ret[i]=e; break;
}
};break;
case "object": for (var i in o){
var e=o[i];
switch(outputType){
case "array": if (e[param]==value) ret.push(e); break;
case "object": if (e[param]==value) ret[i]=e; break;
}
};break;
}
return ret;
}catch(e){log("wmLibrary.matchByParam: "+e);}};
//sorts the methods of an object by method 'id' or method 'value'
//beware this may mangle some objects
sandbox.sortCollection=function(o,by){
var a=[];
for (var i in o){
a.push({id:i,value:o[i]});
}
a.sort(function(a,b){return a[by]>b[by];});
var ret={};
for (var i=0;i<a.length;i++){
ret[a[i].id]=a[i].value;
}
return ret;
};
// Collect all the values from parameter p in object o, traversing kids nodes
sandbox.getBranchValues=function(o,p){try{
var ret={};
for(var i in o) {
//get value p for object o's element i
if (p=="id"){ //special case for fetching a list of ID's
if (exists(o[i][p])) ret[i]=o[i][p];
else ret[i]=i;
} else if (p=="."){ //special case for fetching a list of all objects without a tree structure
ret[i]=o[i];
}
else if (exists(o[i][p])) ret[i]=o[i][p];
//if object o has kids, then get all the values p inside that kid k
if (o[i].kids) ret=mergeJSON(ret,getBranchValues(o[i].kids,p));
}
return ret;
}catch(e){log("wmLibrary.getBranchValues: "+e);}};
//convert an object's methods to an array, storing the method's key on the object as an id
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);}};
//convert an array of objects to methods of an object using either the object's ai or name as its key
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);}};
//convert an object's methods to an array of those method names
sandbox.methodNames = function(o) {try{var ret=[]; for (i in o) ret.push(i); return ret;}catch(e){log("wmLibrary.methodNames: "+e);}};
//copy parts from one object to another
//used for extending one object with parts from another
//by John Resig
sandbox.extend = function(a,b) {try{
for ( var i in b ) {
//collect setter/getter functions
var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
//copy setter/getter functions
if ( g || s ) {
if ( g ) a.__defineGetter__(i, g);
if ( s ) a.__defineSetter__(i, s);
} else a[i] = b[i]; //copy vars
}
return a;
}catch(e){log("wmLibrary.extend: "+e);}};
//***************************************************************************************************************************************
//***** WM Specific Functions
//***************************************************************************************************************************************
//returns an object suitable for accText data based on an array, and allowing an idPrefix and textSuffix
sandbox.createAccTextFromArray = function(arr,idPrefix,textSuffix){try{var ret={};if (arr) {for (var i=0,len=arr.length;i<len;i++){ret[(idPrefix||'')+arr[i].noSpaces().toLowerCase()]=arr[i].upperWords()+(textSuffix||'');}};return ret;}catch(e){log("wmLibrary.createAccTextFromArray: "+e);}};
//writes a message to the hash section of the document location, or redirects to a location that can accept a new hash section
sandbox.sendMessage=function(s,hwnd,flag){try{
hwnd = (hwnd||window.top);
if (exists(hwnd)) try {hwnd.location.hash = s;} catch(e){
if (flag==1) hwnd.location.href = "http://apps.facebook.com/?#"+s;
else hwnd.location.href = "http://www.facebook.com/reqs.php?#"+s;
}
}catch(e){log("wmLibrary.sendMessage: "+e);}};
//flags for menu building function
sandbox.MENU_ID_ENFORCE_NAME=1; //causes menuFromData to return lowercase nospace names as the id instead of the calculated id
//inserts one or more menu option blocks based upon a data object
//marking all new items in the newitem list above as green so users can easily find your changes
sandbox.menuFromData=function(data,menuNode,newItemList,idPrefix,flags){try{
flags=(flags||0); newItemList=(newItemList||[]);
if (data) for (var m=0,len=data.length; m<len; m++) {
var text = data[m]["name"].upperWords(), event = (data[m]["event"]||"Unsorted").upperWords();
var outid = (flags==MENU_ID_ENFORCE_NAME)?data[m].name.noSpaces().toLowerCase():(data[m]["id"]||data[m]["name"]).noSpaces().toLowerCase();
var thisMenu; if( !(thisMenu=(menuNode["optblock"+event]||null) ) ) {thisMenu=(menuNode["optblock"+event]={type:"optionblock",label:event,kids:{} });}
thisMenu.kids[idPrefix+outid]={type:"checkbox",label:text,newitem:newItemList.inArray(idPrefix+outid)};
}
}catch(e){log("wmLibrary.menuFromData: "+e);}};
//returns a list of search strings from a data object containing id's names and events, already optimized for searching
sandbox.searchFromData=function(data,idPrefix){try{
idPrefix=(idPrefix||"");
var ret = [];
for (var m=0,mat;(mat=data[m]);m++){
ret.push(idPrefix+(mat.id||mat.name));
}
ret.optimize();
return ret;
}catch(e){log("wmLibrary.searchFromData: "+e);}};
//returns a list of materials from a data object containing id's names and events, already optimized for searching
sandbox.matListFromData=function(data){try{
var ret = [];
for (var m=0,mat;(mat=data[m]);m++){
ret.push(mat.name);
} ret.optimize();
return ret;
}catch(e){log("wmLibrary.matListFromData: "+e);}};
//returns a valid accText object from a data object containing id's names and events
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);}};
//***************************************************************************************************************************************
//***** Sidekick Object
//***************************************************************************************************************************************
//sidekick specific functions
sandbox.Sidekick={
//init
tabID:null,
status:0,
nopopLink:"",
//attempts to dock the sidekick script to the wm host script
//params takes an object that contains the following parameters:
//appID(string), version(string), skType(integer),
//name(string), thumbSource(string or array),
//flags(object), icon(string), desc(string),
//addFilters(object),
//alterLink(object), accText(object),
//tests(array) and menu(object)
dock: function(params){try{
//find the dock node on this page
var door=$('wmDock');
if (!door) {
//does not exist, wait and try again later
window.setTimeout(function(){Sidekick.dock(params);}, 1000);
return;
}
//detect if a sidekick for this app is already docked
var doorMark=$('wmDoor_app'+params.appID);
if (doorMark && (params.skType==doorMark.getAttribute("value")) ) {
//a sidekick of this level is already here, cancel docking
return;
}
//setup defaults for a few of the expected parameters
params.thumbsSource=(params.thumbsSource||"app_full_proxy.php?app"+params.appID);
params.desc=(params.desc||params.name+" Sidekick (ver "+params.version+")");
//create a block of data to attach to the dock
var attString=JSON.stringify(params);
door.appendChild(
doorMark=createElement('div',{id:'wmDoor_app'+params.appID,'data-ft':attString,value:(params.skType||0)})
);
//doorMark.setAttribute("skType",(params.skType||0));
//confirm(doorMark.getAttribute("skType"));
//ring the buzzer so the host knows the package is ready
window.setTimeout(function(){click(door);},1000);
}catch(e){log("wmLibrary.Sidekick.dock: "+e);}},
//receive and process messages
//msg code 1 is a packet from the wm host containing data about the post we are processing
//that packet must contain at least the tab/window ID with which the WM host can access that tab again
//msg code 3 is a packet from this or a deeper iframe window about the return value for this post
//because Chrome returns NULL at event.source on msg 1, we now have to rethink
receiveMessage: function(event) {try{
if (isObject(event.data)) {
var data=event.data; //just shorten the typing
if (data.channel=="WallManager"){
log(JSON.stringify(data));
switch (data.msg) {
case 1: //get init data from wm host
//if (!Sidekick.tabID)
Sidekick.tabID=data.tabID;
log("Sidekick hears host...");
//
break;
case 3: //get message from child
if (Sidekick.tabID) {
log("Sidekick hears iframe...");
//send our status packet back to wm
Sidekick.status=data.status;
Sidekick.nopopLink=data.nopopLink||null;
//update the stored data about this post
var skChannel = getOptJSON("skChannel")||{};
skChannel[Sidekick.tabID]={
tabID:Sidekick.tabID,
status:Sidekick.status,
nopopLink:Sidekick.nopopLink,
};
log(JSON.stringify(skChannel));
setOptJSON("skChannel",skChannel);
} else {
//have not yet recieved tabID package from wm, wait a sec
setTimeout(function(){Sidekick.receiveMessage(event);},1000);
}
break;
}
}
}
}catch(e){log("wmLibrary.Sidekick.receiveMessage: "+e);}},
//disable the listener started below
unlisten: function(params){try{
window.removeEventListener("message", Sidekick.receiveMessage, false);
}catch(e){log("wmLibrary.Sidekick.unlisten: "+e);}},
//turn on the listener which can receive messages from wm host (if this window = window.top) or from iframes
listen: function(params){try{
window.addEventListener("message", Sidekick.receiveMessage, false);
}catch(e){log("wmLibrary.Sidekick.listen: "+e);}},
//listen for changes to the skChannel variable and report those changes to WM whenever docked
openChannel: function(){try{
var dump=$("wmDataDump");
if (dump) {
var skData=getOpt("skChannel");
setOpt("skChannel","");
if (skData) dump.appendChild(createElement('div',{'data-ft':skData}));
}
setTimeout(Sidekick.openChannel,1000);
}catch(e){log("wmLibrary.Sidekick.openChannel: "+e);}},
//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
sendStatus: function(status,link){try{
if (exists(window.top)) {
window.top.postMessage({
channel:"WallManager",
msg:3,
status:status,
nopopLink:(link?link:''),
},"*");
} else {
//window.top is hidden to us from this location
contentEval('window.top.postMessage({"channel":"WallManager","msg":3,"status":'+status+',"link":"'+(link?link:'')+'"},"*");');
}
}catch(e){log("wmLibrary.Sidekick.sendStatus: "+e);}},
};
//***************************************************************************************************************************************
//***** Visual Effects
//***************************************************************************************************************************************
//slides element e toward the specified destination offset
//specify [t, l, r, b] top, left, right, and bottom as the final offset
//specify s as the number of MS the move should loop on
//specify p as the number of pixels to move per interval
sandbox.slide=function(e,t,l,r,b,s,p) {try{
s=s||50;p=p||10;
var top= e.style.top; top=parseInt(top); top=(isNaN(top))?0:top;
var bottom = e.style.bottom; bottom=parseInt(bottom); bottom=(isNaN(bottom))?0:bottom;
var left= e.style.left; left=parseInt(left); left=(isNaN(left))?0:left;
var right = e.style.right; right=parseInt(right); right=(isNaN(right))?0:right;
p1=(p>Math.abs(t))?Math.abs(t):p;
if(t>0) {e.style.top = (top+p1)+"px";t-=p1;}
else if (t<0) {e.style.top = (top-p1)+"px";t+=p1;}
p1=(p>Math.abs(l))?Math.abs(l):p;
if(l>0) {e.style.left = (left+p1)+"px";l-=p1;}
else if (l<0) {e.style.left = (left-p1)+"px";l+=p1;}
p1=(p>Math.abs(r))?Math.abs(r):p;
if(r>0) {e.style.right = (right+p1)+"px";r-=p1;}
else if (r<0) {e.style.right = (right-p1)+"px";r+=p1;}
p1=(p>Math.abs(b))?Math.abs(b):p;
if(b>0) {e.style.bottom = (bottom+p1)+"px";b-=p1;}
else if (b<0) {e.style.bottom = (bottom-p1)+"px";b+=p1;}
if (t!=0||l!=0||r!=0||b!=0) window.setTimeout(function(){slide(e,t,l,r,b,s,p);},s);
}catch(e){log("wmLibrary.slide: "+e);}};
//***************************************************************************************************************************************
//***** URL Encode/Decode
//***************************************************************************************************************************************
//url encode/decode functions nicely wrapped from webtoolkit
sandbox.Url = {
// public method for url encoding
encode : function (string) {try{return escape(this._utf8_encode(string));}catch(e){log("wmLibrary.Url.encode: "+e);}},
// public method for url decoding
decode : function (string) {try{return this._utf8_decode(unescape(string));}catch(e){log("wmLibrary.Url.decode: "+e);}},
// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
};
//***************************************************************************************************************************************
//***** GM Local Storage Commands
//***************************************************************************************************************************************
// set an option
sandbox.setOpt=function(opt,value){try{GM_setValue(opt,value);}catch(e){log("wmLibrary.setOpt: "+e);}}
// Get a stored option
sandbox.getOpt=function(opt){try{return GM_getValue(opt);}catch(e){log("wmLibrary.getOpt: "+e);}}
// set an option
sandbox.setOptJSON=function(opt,value){try{GM_setValue(opt,JSON.stringify(value));}catch(e){log("wmLibrary.setOptJSON: "+e);}}
// Get a stored option
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);}}
//***************************************************************************************************************************************
//***** 2D Math
//***************************************************************************************************************************************
// add two points or vectors
sandbox.addPoints = function(p0, p1){try{
var p2=mergeJSON(p0); //copy p0
for (var v in p1) p2[v]=(p2[v]||0)+(p1[v]||0);
return p2;
}catch(e){log("wmLibrary.addPoints: "+e);}},
//***************************************************************************************************************************************
//***** Delays and Repeaters
//***************************************************************************************************************************************
// shortform for window.setTimeout(x,0)
sandbox.doAction = function(f) {try{setTimeout(f,0);}catch(e){log("doAction: "+e);}};
//repeat a function fn a number of times n with a delay of 1 second between calls
sandbox.signal = function(fn,n){try{
if (n>0) {
doAction(fn);
setTimeout(function(){signal(fn,n-1);},1000);
}
}catch(e){log("wmLibrary.signal: "+e);}};
//***************************************************************************************************************************************
//***** Enum Creation
//***************************************************************************************************************************************
// create an unprotected enumeration list
sandbox.Enum = function() {try{for (var i in arguments) {this[arguments[i]] = i;}}catch(e){log("Enum.init: "+e);}};
//create an unprotected enumeration list of binary flags
sandbox.EnumFlags = function() {try{for (var i in arguments) {this[arguments[i]] = Math.pow(2,i);}}catch(e){log("EnumFlags.init: "+e);}};
//***************************************************************************************************************************************
//***** Pop-ups
//***************************************************************************************************************************************
//create a centered iframe to display multiline text in a textarea
//with optional isJSON flag which will format JSON strings with indents and linebreaks
sandbox.promptText = function(s,isJSON){try{
if (isJSON) s=s.formatJSON(4);
var newFrame;
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;'})));
newFrame.src = 'about:blank'; // In WebKit src cant be set until it is added to the page
newFrame.addEventListener('load', function(){
var frameBody = this.contentDocument.getElementsByTagName('body')[0];
var close=function(){try{
remove(newFrame);
delete newFrame;
}catch(e){log("wmLibrary.promptText.close: "+e);}};
// Add save and close buttons
frameBody.appendChild(
createElement("textArea",{textContent:s,style:"height:90%;width:100%;"})
);
frameBody.appendChild(
createElement("div", {id:"buttons_holder"}, [
createElement('button',{id:"closeBtn", textContent:"Close",title:"Close window",onclick:close}),
])
);
var center=function(){try{
var style=newFrame.style;
var node=newFrame;
style.display = '';
style.top = Math.floor((window.innerHeight/2)-(node.offsetHeight/2)) + 'px';
style.left = Math.floor((window.innerWidth/2)-(node.offsetWidth/2)) + 'px';
}catch(e){log("wmLibrary.promptText.center: "+e);}};
center();
window.addEventListener('resize', center, false); // Center it on resize
// Close frame on window close
window.addEventListener('beforeunload', function(){newFrame.remove(this);}, false);
}, false);
}catch(e){log("wmLibrary.promptText: "+e);}};
//***************************************************************************************************************************************
//***** Text To Script
//***************************************************************************************************************************************
//force code to be run outside the GM sandbox
sandbox.contentEval = function(source) {try{
// Check for function input.
if ('function' == typeof source) {
// Execute this function with no arguments, by adding parentheses.
// One set around the function, required for valid syntax, and a
// second empty set calls the surrounded function.
source = '(' + source + ')();'
}
// Create a script node holding this source code.
var script = document.createElement('script');
script.setAttribute("type", "application/javascript");
script.textContent = source;
// Insert the script node into the page, so it will run, and immediately
// remove it to clean up.
document.body.appendChild(script);
document.body.removeChild(script);
}catch(e){log("wmLibrary.contentEval: "+e);}};
//***************************************************************************************************************************************
//***** RegExp Construction
//***************************************************************************************************************************************
//convert an array to a pipe delimited RegExp group
sandbox.arrayToRegExp = function(a) {try{
var ret="";
if (isArrayAndNotEmpty(a)) {
ret="(";
for (var i=0,len=a.length; i<len;i++){
ret=ret+a[i];
if (i<(len-1)) ret=ret+"|";
}
ret=ret+")";
}
return ret;
}catch(e){log("wmLibrary.arrayToRegExp: "+e);}};
//takes an integer range and converts it to a regular expression
//which can search for that number range in a string
sandbox.integerRangeToRegExp = function(params) {try{
params=params||{};
var min=params.min.toString(), max=params.max.toString();
var ret="";
//on the odd case that both min and max values were equal
if (max==min) return max;
//count shared digits we can omit from complex regexp
var numSharedDigits=0;
if (min.length==max.length) {
for (var n=max.length;n>0;n--){
if (max.substring(0,n) == min.substring(0,n)) {
numSharedDigits=n;
break;
}
}
}
var shared=max.substring(0,numSharedDigits);
//crop the min and max values
min=min.removePrefix(shared);
max=max.removePrefix(shared);
//move the shared stuff to the front of the test
ret+=shared+"(";
//count the digits
var minDigits=min.length;
var maxDigits=max.length;
//set some flags
var isSingleDigit=(minDigits==1 && maxDigits==1);
var isVariableDigits=(minDigits != maxDigits);
//using 1 to 4444 as a range
//calculate maximum range tests
//ie: 444x 44xx 4xxx
if (maxDigits>1){
ret+=max.substr(0,maxDigits-1)+"[0-"+max.substr(maxDigits-1,1)+"]";
for (var n=(maxDigits-2); n>0; n--) {
if (max.substr(n,1)!="0") {
ret+="|"+max.substr(0,n)+"[0-"+(val(max.substr(n,1))-1)+"]"+("\\d").repeat((maxDigits-1)-n);
}
}
}
//calculate intermediate range tests
//ie: 1xxx, 1xx, 1x
for (var n=maxDigits;n>1;n--){
//check if min and max both use this digit
if (minDigits==n && maxDigits==n) {
//as neither bound would be put out of range
//and the bounds are not equal
if ((min.substr(0,1)!="9") && (max.substr(0,1)!="1") && (val(max.substr(0,1))>(val(min.substr(0,1))+1))) {
ret+="|["+(val(min.substr(0,1))+1)+"-"+(val(max.substr(0,1))-1)+"]"+("\\d").repeat(n-1);
}
//detect if min uses this digit
} else if (minDigits==n) {
//as long as it does not start with 9
if (min.substr(0,1)!="9") {
ret+="|["+(val(min.substr(0,1))+1)+"-9]"+("\\d").repeat(n-1);
}
break;
//detect if max uses this digit
} else if (maxDigits==n) {
//as long as it does not start with 1
if (max.substr(0,1)!="1") {
ret+="|[1-"+(val(max.substr(0,1))-1)+"]"+("\\d").repeat(n-1);
}
} else {
//they do not use this digit
//is it BETWEEN their digit counts
if (n > minDigits) {
ret+="|[1-9]"+("\\d").repeat(n-1);
}
}
}
//calculate minimum range tests
//ie: [1-9]
if (minDigits>1){
ret+="|"+min.substr(0,minDigits-1)+"["+min.substr(minDigits-1,1)+"-9]";
for (var n=(minDigits-2); n>0; n--) {
if (min.substr(n,1)!="9") {
ret+="|"+min.substr(0,n)+"["+(val(min.substr(n,1))+1)+"-9]"+("[0-9]").repeat((minDigits-1)-n);
}
}
} else {
//single digit min
if (maxDigits>minDigits) {
ret+="|["+min+"-9]";
} else {
//both min and max are single digits
ret+="|["+min+"-"+max+"]";
}
}
//fix same start and end range issues
for (var i=0;i<=9;i++){
ret=ret.replace(new RegExp("\\["+i+"-"+i+"\\]","gi"),i);
}
ret=ret.replace(new RegExp("\\[0-9\\]","gi"),"\\d");
return ret+")";
}catch(e){log("wmLibrary.integerRangeToRegExp: "+e);}};
//***************************************************************************************************************************************
//***** Typing Simulation
//***************************************************************************************************************************************
sandbox.simulateKeyEvent = function(character,byCode) {
var evt = document.createEvent("KeyboardEvent");
(evt.initKeyEvent || evt.initKeyboardEvent)("keypress", true, true, window,
0, 0, 0, 0,
0, ((byCode||null) || character.charCodeAt(0)) )
var canceled = !body.dispatchEvent(evt);
if(canceled) {
// A handler called preventDefault
alert("canceled");
} else {
// None of the handlers called preventDefault
alert("not canceled");
}
};
sandbox.typeText = function(s) {
for (var i=0,len=s.length; i<len; i++){
simulateKeyEvent(s.substr(i,1));
log(s.substr(i,1));
}
};
sandbox.typeEnter = function() {
simulateKeyEvent(null,13);
};
/*formatting notes
format a number to x decimal places
number.toFixed(x);
convert to hexidecimal
number.toString(16);
//try something like this to get your own header details
define your own parseHeaders function
var fileMETA = parseHeaders(<><![CDATA[
// ==UserScript==
// @name My Script
// @namespace http://www.example.com/gmscripts
// @description Scripting is fun
// @copyright 2009+, John Doe (http://www.example.com/~jdoe)
// @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @version 0.0.1
// @include http://www.example.com/*
// @include http://www.example.org/*
// @exclude http://www.example.org/foo
// @require foo.js
// @resource resourceName1 resource1.png
// @resource resourceName2 http://www.example.com/resource2.png
// @uso:script scriptid
// ==/UserScript==
]]></>.toString());
//include jquery stuff
// ==UserScript==
// @name jQuery Example
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
// ==/UserScript==
*/
//a custom collection wrapper
//this pretty much mimics collections in visual basic
//with a lot of collection methods added from other systems
var jsCollection=function(objOrArray){
var self=this;
this.items={};
//return an item from this collection by index or key
this.__defineGetter__("item",function(indexOrKey){try{
return this.items[indexOrKey]||null;
}catch(e){log("jsCollection.item: "+e);}});
//return the count of items in this collection
this.__defineGetter__("count",function(){try{
var ret=0;
for (var e in this.items) ret++;
return ret;
}catch(e){log("jsCollection.count: "+e);}});
//return true if the count of items in this collection is 0
this.__defineGetter__("isEmpty",function(){try{
return this.count==0;
}catch(e){log("jsCollection.isEmpty: "+e);}});
//remove all items from this collection
this.clear=function(){try{
while(this.items[0]) delete this.items[0];
}catch(e){log("jsCollection.clear: "+e);}};
//return the index of the first occurence of obj
this.indexOf=function(obj){try{
var c=0;
for (var i in this.items){
if (this.items[i]===obj) {
return c;
break;
}
c++;
}
return -1;
}catch(e){log("jsCollection.indexOf: "+e);}};
//return the key of the first occurence of obj
this.keyOf=function(obj){try{
for (var i in this.items){
if (this.items[i]===obj) {
return i;
break;
}
}
return -1;
}catch(e){log("jsCollection.keyOf: "+e);}};
//returns true if obj occurs in this collection
this.contains=function(obj){try{
return this.indexOf(obj)!=-1;
}catch(e){log("jsCollection.contains: "+e);}};
//returns true if an item in this collection has key = key
this.containsKey=function(key){try{
return exists(this.items[key]);
}catch(e){log("jsCollection.containsKey: "+e);}};
//remove an item from the collection by index or key
this.remove=function(indexOrKey){try{
delete this.items[indexOrKey];
}catch(e){log("jsCollection.remove: "+e);}};
//add an item to the collection
//with optional key which defaults to unique()
//with optional before which is an object to match
//with optional after which is an object to match
this.add=function(item,key,before,after){try{
key=key||unique();
if (before && this.indexOf(before)!=-1) {
var ret={};
for (var i in this.items){
if (this.items[i]===before) {
ret[key]=item;
}
ret[i]=this.items[i];
}
this.items=ret;
} else if (after && this.indexOf(after)!=-1) {
var ret={};
for (var i in this.items){
ret[i]=this.items[i];
if (this.items[i]===after) {
ret[key]=item;
}
}
this.items=ret;
} else {
this.items[key]=item;
}
}catch(e){log("jsCollection.add: "+e);}};
//shortform to add an item
//after an item
//with optional key
this.insertAfter=function(item,after,key){try{
this.add(item,key,null,after);
}catch(e){log("jsCollection.insertAfter: "+e);}};
//shortform to add an item
//before an item
//with optional key
this.insertBefore=function(item,before,key){try{
this.add(item,key,before,null);
}catch(e){log("jsCollection.insertBefore: "+e);}};
//shortform to add an item
//with optional key
this.append=function(item,key){try{
this.add(item,key);
}catch(e){log("jsCollection.append: "+e);}};
//shortform to add an item
//to the beginning of the collection
//with optional key
this.prepend=function(item,key){try{
this.add(item,key,(this.items[0]||null));
}catch(e){log("jsCollection.prepend: "+e);}};
//add an array of items
//with optional before and after
this.addRange=function(itemArray,before,after){try{
if (before && this.indexOf(before)!=-1) {
var ret={};
for (var i in this.items){
if (this.items[i]===before) {
for (var a=0,len=itemArrayLength;a<len;a++){
ret[unique()]=itemArray[a];
}
}
ret[i]=this.items[i];
}
this.items=ret;
} else if (after && this.indexOf(after)!=-1) {
var ret={};
for (var i in this.items){
ret[i]=this.items[i];
if (this.items[i]===after) {
for (var a=0,len=itemArrayLength;a<len;a++){
ret[unique()]=itemArray[a];
}
}
}
this.items=ret;
} else {
for (var a=0,len=itemArrayLength;a<len;a++){
this.items[unique()]=itemArray[a];
}
}
}catch(e){log("jsCollection.addRange: "+e);}};
//shortform to add an array of items
this.appendRange=function(itemArray){try{
this.addRange(itemArray);
}catch(e){log("jsCollection.appendRange: "+e);}};
//shortform to add an array of items
//to the beginning of the collection
this.prependRange=function(itemArray){try{
this.addRange(itemArray,(this.items[0]||null));
}catch(e){log("jsCollection.prependRange: "+e);}};
//add a copy of item
//with optional before or after
this.addCopy=function(item,before,after){try{
this.add(item,null,before,after);
}catch(e){log("jsCollection.addCopy: "+e);}};
//add multiple copies of item
//with optional before and after
this.addCopies=function(item,count,before,after){try{
var ret=[];
for (var i=0;i<count;i++) ret.push(item);
this.addRange(item,before,after);
}catch(e){log("jsCollection.addCopies: "+e);}};
//return the collection converted to an array
this.toArray=function(){try{
return methodsToArray(this.items);
}catch(e){log("jsCollection.toArray: "+e);}};
//return the index of item with key=key
this.indexOfKey=function(key){try{
return this.indexOf(this.items[key]||null);
}catch(e){log("jsCollection.indexOfKey: "+e);}};
//return the key of the item at index=index
this.keyOfIndex=function(index){try{
var c=0;
for (var i in this.items){
if (c==index) return i;
c++;
}
}catch(e){log("jsCollection.keyOfIndex: "+e);}};
//use passed data on creation to create initial items
if (objOrArray){
if (isArrayAndNotEmpty(objOrArray)){
for (var i=0,len=objOrArray.length;i<len;i++){
this.add(objOrArray[i],i);
}
} else if (isObject(objOrArray)) {
for (var i in objOrArray){
this.items[i]=objOrArray[i];
}
}
}
//return self for external use
return this;
};
})();