|
|
(9 intermediate revisions by 5 users not shown) |
Line 1: |
Line 1: |
| __TOC__
| | #REDIRECT [[:Category:Coding Tips]] |
| | |
| == jQuery in Greasemonkey scripts using the metadata key @require ==
| |
| | |
| '' '''UPDATE:''' there was an old way to accomplish this by injecting a script tag into the head that loaded jquery on each page, that method can be found [http://www.joanpiedra.com/jquery/greasemonkey/ here] for archival purposes. It is no longer suggested because it relies on the browser to cache the script. the new method is superior, and simplier.''
| |
| | |
| Greasemonkey 0.8 introduce the @require metadata key that can be used to load JavaScript libraries
| |
| into you user-script. See [http://wiki.greasespot.net/Metadata_block#.40require @require] in the Scripting Reference for more info.
| |
| | |
| Simply point the @require key to a location of the jQuery library.
| |
| Greasemonkey will download it and store it together with your user-script in a subdirectory under your gm_scripts directory.
| |
| The user-script then uses this local copy to speed up it's loading and to save some network traffic.
| |
| | |
| A self explaining example.
| |
| <code><pre>
| |
| // ==UserScript==
| |
| // @name jQueryPlay
| |
| // @namespace http://www.example.com/jQueryPlay/
| |
| // @description Plays around with jQuery. Simply appends " more text." to string in the element with id sometext.
| |
| // @include http://domainToOperateOn.com/
| |
| // @require http://code.jquery.com/jquery-latest.js
| |
| // ==/UserScript==
| |
| | |
| (function() {
| |
| // This will be executed onLoad
| |
| // Append some text to the element with id #someText using the jQuery library.
| |
| $("#someText").append(" more text.");
| |
| }());
| |
| </pre></code>
| |
| | |
| To update the library simply uninstall the user-script and install it again.
| |
| | |
| == document.getElementById helper ==
| |
| | |
| <pre>function $() {
| |
| if (arguments.length==1) return document.getElementById(arguments[0]);
| |
| var z=[], i=0, el;
| |
| while(el=document.getElementById(arguments[i++]))
| |
| if (el)
| |
| z.push(el);
| |
| return z;
| |
| }</pre>
| |
| | |
| Example usage:
| |
| | |
| $("header").innerHTML = "Halloa!"; // Single element
| |
| $("header","footer","content").forEach(function(element) { // multiple elements
| |
| element.parentNode.removeChild(element);
| |
| });
| |
| | |
| == XPath helper ==
| |
| | |
| Run a particular [[XPath]] expression <code>p</code> against the context node <code>context</code> (or the document, if not provided) with this overloaded XPath helper.
| |
| | |
| Returns the results as an array, unless a value of <code>false</code> is passed in.
| |
| | |
| <pre>function $x() {
| |
| var x='', // default values
| |
| node=document,
| |
| type=0,
| |
| fix=true,
| |
| i=0,
| |
| toAr=function(xp){ // XPathResult to array
| |
| var final=[], next;
| |
| while(next=xp.iterateNext())
| |
| final.push(next);
| |
| return final
| |
| },
| |
| cur;
| |
| while (cur=arguments[i++]) // argument handler
| |
| switch(typeof cur) {
| |
| case "string":x+=(x=='') ? cur : " | " + cur;continue;
| |
| case "number":type=cur;continue;
| |
| case "object":node=cur;continue;
| |
| case "boolean":fix=cur;continue;
| |
| }
| |
| if (fix) { // array conversion logic
| |
| if (type==6) type=4;
| |
| if (type==7) type=5;
| |
| }
| |
| if (!/^\//.test(x)) x="//"+x; // selection mistake helper
| |
| if (node!=document && !/^\./.test(x)) x="."+x; // context mistake helper
| |
| var temp=document.evaluate(x,node,null,type,null); //evaluate!
| |
| if (fix)
| |
| switch(type) { // automatically return special type
| |
| case 1:return temp.numberValue;
| |
| case 2:return temp.stringValue;
| |
| case 3:return temp.booleanValue;
| |
| case 8:return temp.singleNodeValue;
| |
| case 9:return temp.singleNodeValue;
| |
| }
| |
| return fix ? toAr(temp) : temp;
| |
| }</pre>
| |
| | |
| Example usage (with [[Coding_tips#array.forEach|Array.forEach]]):
| |
| | |
| <pre>
| |
| var paragraphs = $x("//p", XPathResult.ORDERED_NODE_SNAPSHOT_TYPE), i=1;
| |
| paragraphs.forEach(function(paragraph) { // Loop over every paragraph
| |
| paragraph.innerHTML = "This is paragraph #"+(i++);
| |
| });
| |
| | |
| var content=$x("//div[@class='center story']", XPathResult.FIRST_ORDERED_NODE_TYPE);
| |
| content.innerHTML="time to eat dinner!";
| |
| </pre>
| |
| | |
| '''Note:''' When you specify a context node, you '''NORMALLY''' need to use a [[XPath#Relative_paths|relative XPath expression]]. However, this function takes care of that technicality.
| |
| | |
| == Get elements by CSS selector ==
| |
| | |
| Lets you run an XPath using the concise CSS style selectors such as .class and #id, it also adds a much needed method of comparing the ends of strings, $=.
| |
| <code><pre>
| |
| function $$(xpath,root) {
| |
| xpath = xpath
| |
| .replace(/((^|\|)\s*)([^/|\s]+)/g,'$2.//$3')
| |
| .replace(/\.([\w-]+)(?!([^\]]*]))/g, '[@class="$1" or @class$=" $1" or @class^="$1 " or @class~=" $1 "]')
| |
| .replace(/#([\w-]+)/g, '[@id="$1"]')
| |
| .replace(/\/\[/g,'/*[');
| |
| str = '(@\\w+|"[^"]*"|\'[^\']*\')';
| |
| xpath = xpath
| |
| .replace(new RegExp(str+'\\s*~=\\s*'+str,'g'), 'contains($1,$2)')
| |
| .replace(new RegExp(str+'\\s*\\^=\\s*'+str,'g'), 'starts-with($1,$2)')
| |
| .replace(new RegExp(str+'\\s*\\$=\\s*'+str,'g'), 'substring($1,string-length($1)-string-length($2)+1)=$2');
| |
| var got = document.evaluate(xpath, root||document, null, 5, null), result=[];
| |
| while (next = got.iterateNext())
| |
| result.push(next);
| |
| return result;
| |
| }
| |
| </pre></code>
| |
| | |
| Example usage:
| |
| $$('#title')[0].innerHTML='Greased';
| |
| $$('a[@href $= "user.js"]').forEach(function (a) {
| |
| a.innerHTML='check it out a script';
| |
| });
| |
| $$('a[@href ^= "http"]').forEach(function (a) {
| |
| a.innerHTML += ' (external)';
| |
| });
| |
| | |
| == Conditional logging ==
| |
| | |
| Used to easily toggle sending debug messages to the console. Passes all arguments on to [http://www.getfirebug.com/logging.html console.log], but only if <code>console</code> is defined (for backward compatibility) and <code>DEBUG</code> is <code>true</code>.
| |
| | |
| Code and example usage:
| |
| | |
| const DEBUG = true;
| |
|
| |
| var links = document.links;
| |
| debug("Links: %o", links);
| |
|
| |
| function debug() {
| |
| if (DEBUG && console) {
| |
| console.log.apply(this, arguments);
| |
| }
| |
| }
| |
| | |
| ======Another way...======
| |
| ...to do it would be this:
| |
| | |
| const DEBUG = 1;
| |
|
| |
| console =
| |
| {
| |
| log : function (text) { if( DEBUG ) unsafeWindow.console.log( text ); },
| |
| info : function (text) { if( DEBUG ) unsafeWindow.console.info( text ); },
| |
| warn : function (text) { if( DEBUG ) unsafeWindow.console.warn( text ); },
| |
| error : function (text) { if( DEBUG ) unsafeWindow.console.error( text ); }
| |
| }
| |
| | |
| which allows you to just support more functions from the [[Useful_Tools_for_Script_Writers#FireBug|Firebug]] console if you want and use it with unchanged syntax.
| |
| | |
| == DOM node manipulation ==
| |
| | |
| === Use .innerHTML to create DOM structure ===
| |
| The non W3 standard setter method [http://developer.mozilla.org/en/docs/DOM:element.innerHTML <code>.innerHTML</code>] can be used to concisely create DOM structure in trivial userscripts. The following is a complete branding script to show that Greasemonkey has run on a host.
| |
| | |
| host = document.location.host;
| |
| dummyDiv = document.createElement('div');
| |
| dummyDiv.innerHTML = '<div><span style="color: red">Greased: ' + host + '</span></div>';
| |
| document.body.insertBefore(dummyDiv.firstChild, document.body.firstChild);
| |
| | |
| Note above that <code>dummyDiv</code> is only needed as a holder for its <code>.firstChild</code>.
| |
| | |
| Here is a helper function that reuses the <code>dummyDiv</code>:
| |
| | |
| function firstNodeOf(html){
| |
| firstNodeOf.dummyDiv.innerHTML = html;
| |
| return firstNodeOf.dummyDiv.firstChild;
| |
| }
| |
| firstNodeOf.dummyDiv = document.createElement('div');
| |
| | |
| With this helper, one can write the following trivial script:
| |
| | |
| document.body.appendChild(firstNodeOf('<div><span style="color: red">END OF PAGE</span></div>');
| |
| | |
| '''STRONGLY NOTE:''' <code>.innerHTML</code> seems to be sensitive to <code>document.contentType</code>. When the type is <code>text/plain</code> the <code>.innerHTML</code> setter does not parse its argument into DOM nodes, but instead returns #text nodes. The setter seems to work fine for types such as <code>text/html</code> or <code>application/xhtml+xml</code> but where and how it works is undocumented.
| |
| | |
| '''Advantages:''' Concise and and probably more efficient than any hand coded approach.
| |
| | |
| '''Disadvantages:''' Other than the <code>text/plain</code> probem, <code>.innerHTML</code> is not a W3 standard and many people don't like non-standard code.
| |
| | |
| '''Upshot:''' Until [http://developer.mozilla.org/en/docs/E4X ECMAScript for XML (E4X)] becomes availble in user scripts, this is a useful hack for non <code>text/plain</code> pages.
| |
| ==== Notes ====
| |
| * <code>.innerHTML</code> [http://developer.mozilla.org/en/docs/DOM:element.innerHTML#Notes cannot be used to create parts of a <code>TABLE</code>]. E.g.
| |
| | |
| tab = firstNodeOf('<table><tr><td> ... </td></tr></table>'); // WORKS
| |
| tr = firstNodeOf('<tr><td> ... </td></tr>'); // DOESN'T WORK
| |
| td = firstNodeOf('<td> ... </td>'); // DOESN'T WORK
| |
| td.innerHTML = ' ... '; // WORKS
| |
| | |
| So, if a script builds a table from a HTML string, it has to be done as a whole rather than in parts.
| |
| | |
| === Build a DOM node with attributes ===
| |
| | |
| Creates a new DOM node with the given attributes.
| |
| | |
| function createElement(type, attributes){
| |
| var node = document.createElement(type);
| |
| for (var attr in attributes) if (attributes.hasOwnProperty(attr)){
| |
| node.setAttribute(attr, attributes[attr]);
| |
| }
| |
| return node;
| |
| }
| |
| Example usage:
| |
| | |
| var styles = createElement('link', {rel: 'stylesheet', type: 'text/css', href: basedir + 'style.css'});
| |
| document.body.previousSibling.appendChild(styles);
| |
| | |
| === Remove DOM node ===
| |
| | |
| function remove(element) {
| |
| element.parentNode.removeChild(element);
| |
| }
| |
| | |
| === Insert node after node ===
| |
| | |
| function insertAfter(newNode, node) {
| |
| return node.parentNode.insertBefore(newNode, node.nextSibling);
| |
| }
| |
| | |
| This works because even if <code>node</code> is the last node, <code>nextSibling</code> returns <code>null</code> so <code>insertBefore</code> puts the new node at the end.
| |
| | |
| Example usage:
| |
| | |
| var link = document.getElementById("the_link");
| |
| var icon = document.createElement("img");
| |
| icon.src = "…";
| |
| insertAfter(icon, link);
| |
| | |
| === Hijacking browser properties ===
| |
| | |
| Sometimes you want to cook your own browser constants, for instance change the value of navigator.userAgent. Getters are good for that:
| |
| | |
| var real = window.navigator.userAgent;
| |
| var lie = function() { return real + " Macintosh"; };
| |
| unsafeWindow.navigator.__defineGetter__("userAgent", lie);
| |
| | |
| === Extending the DOM with missing functions ===
| |
| | |
| Other times you might want to emulate proprietary functionality of another browser, for instance when a site uses such features:
| |
| | |
| var getter = function() { return this.textContent; };
| |
| var setter = function(t) { return this.textContent = t; };
| |
| unsafeWindow.HTMLElement.prototype.__defineGetter__("innerText", getter);
| |
| unsafeWindow.HTMLElement.prototype.__defineSetter__("innerText", setter);
| |
| | |
| Here is a function to get outerHTML without using unsafeWindow (note: the above isn't working with FF3 and GM 0.8, anyway).
| |
| | |
| function outerHTML(el){
| |
| // create range of element
| |
| var rng=document.createRange();
| |
| rng.selectNode(el);
| |
| // create temp span around element
| |
| var tempSpan=document.createElement("span");
| |
| rng.surroundContents(tempSpan);
| |
| // what we came for
| |
| var oHTML=tempSpan.innerHTML;
| |
| // remove temp span
| |
| rng.selectNodeContents(tempSpan);
| |
| var frag = rng.cloneContents();
| |
| tempSpan.parentNode.replaceChild(frag, tempSpan);
| |
| // remove range
| |
| rng.detach();
| |
| // output
| |
| return oHTML;
| |
| }
| |
| | |
| === Advanced createElement for creating hierarchies of elements ===
| |
| Creates an element with attributes as well as child elements with their own attributes and children. Function should be called with arguments in the form of the following hash (note that "child1", "child2" should be hashes of the same structure):
| |
| createEl({n: nodename, a: {attr1: val, attr2: val}, c: [child1, child2], evl: {type: eventlistener_type, f: eventlistener_function, bubble: bool}}, appendTo)
| |
| | |
| <code><pre>
| |
| function createEl(elObj, parent) {
| |
| var el;
| |
| if (typeof elObj == 'string') {
| |
| el = document.createTextNode(elObj);
| |
| }
| |
| else {
| |
| el = document.createElement(elObj.n);
| |
| if (elObj.a) {
| |
| attributes = elObj.a;
| |
| for (var key in attributes) if (attributes.hasOwnProperty(key)) {
| |
| if (key.charAt(0) == '@')
| |
| el.setAttribute(key.substring(1), attributes[key]);
| |
| else
| |
| el[key] = attributes[key];
| |
| }
| |
| }
| |
| if (elObj.evl) {
| |
| el.addEventListener(elObj.evl.type, elObj.evl.f, elObj.evl.bubble);
| |
| }
| |
| if (elObj.c) {
| |
| elObj.c.forEach(function (v, i, a) { createEl(v, el); });
| |
| }
| |
| }
| |
| if (parent)
| |
| parent.appendChild(el);
| |
| return el;
| |
| }
| |
| </pre></code>
| |
| | |
| Example usage:
| |
| <code><pre>
| |
| createEl({n: 'ol', a: {'@class': 'some_list', '@id': 'my_list'}, c: [
| |
| {n: 'li', a: {textContent: 'first point'}, evl: {type: 'click', f: function() {alert('first point');}, bubble: true}},
| |
| {n: 'li', a: {textContent: 'second point'}},
| |
| {n: 'li', a: {textContent: 'third point'}}
| |
| ]}, document.body);
| |
| </pre></code>
| |
| | |
| == GET a URL with callback function ==
| |
| | |
| Retrieves <code>url</code> using HTTP GET, then calls the function <code>cb</code> with the response text as its single argument.
| |
| | |
| function get(url, cb) {
| |
| GM_xmlhttpRequest({
| |
| method: "GET",
| |
| url: url,
| |
| onload: function(xhr) { cb(xhr.responseText); }
| |
| });
| |
| }
| |
| | |
| Example usage:
| |
| function inform(text) {
| |
| alert("The HTML of the page: \n" + text);
| |
| }
| |
|
| |
| get("http://www.google.com", inform);
| |
| | |
| == POST data to a URL with callback function ==
| |
| | |
| Sends <code>data</code> to <code>url</code> using HTTP POST, then calls the function <code>cb</code> with the response text as its single argument.
| |
| | |
| function post(url, data, cb) {
| |
| GM_xmlhttpRequest({
| |
| method: "POST",
| |
| url: url,
| |
| headers:{'Content-type':'application/x-www-form-urlencoded'},
| |
| data:encodeURI(data),
| |
| onload: function(xhr) { cb(xhr.responseText); }
| |
| });
| |
| }
| |
| | |
| Example usage:
| |
| | |
| post('http://www.flash-mx.com/mm/viewscope.cfm', 'userid=joe&password=guessme', function(text) {
| |
| alert('HTML of the page:' + text)
| |
| })
| |
| | |
| == better Settings Class ==
| |
| | |
| This code allows you to set and get also object values. Because of having problems with long integers, i also tranlated integers into srings
| |
| | |
| | |
| function Settingsobject(){
| |
| this.prefix="";
| |
| this.default={};
| |
| }
| |
| Settingsobject.prototype.set=function(name, value){
| |
| if(typeof value == "boolean")
| |
| value = value ? "{b}1" : "{b}0";
| |
| else if(typeof value == "string")
| |
| value = "{s}" + value;
| |
| else if(typeof value == "number")
| |
| value = "{n}" + value;
| |
| else
| |
| value = "{o}" + value.toSource();
| |
| GM_setValue(this.prefix+""+name, value);
| |
| }
| |
| Settingsobject.prototype.get=function(name){
| |
| var value=GM_getValue(this.prefix+""+name, this.default[name] || "{b}0")
| |
| if(!value.indexOf)
| |
| return value;
| |
| if(value.indexOf("{o}")==0){
| |
| try{
| |
| return eval("("+value.substr(3)+")");
| |
| }catch(e){
| |
| GM_log("Error while calling variable "+name+" while translating into an object: \n\n"+e+"\n\ncode:\n"+value.substr(3))
| |
| return false;
| |
| }
| |
| }
| |
| if(value.indexOf("{b}")==0)
| |
| return !!parseInt(value.substr(3));
| |
| if(value.indexOf("{n}")==0)
| |
| return parseFloat(value.substr(3));
| |
| if(value.indexOf("{s}")==0)
| |
| return value.substr(3);
| |
| return value;
| |
| }
| |
| Settingsobject.prototype.register=function(name, defaultvalue){
| |
| this.default[name]=defaultvalue;
| |
| return true;
| |
| }
| |
| | |
| Example usage:
| |
| var globalSettings=new Settingsobject();
| |
| globalSettings.prefix="global.";
| |
| | |
| The Prefix is good for seperating between accounts.
| |
| | |
| | |
| Setting variable ''x'' to 1:
| |
| globalSettings.register("x", 0);
| |
| globalSettings.set("x",1);
| |
| | |
| Yo do not have to register a variable, but so you can set a default value and you have an overfiew over all variables in your Settings.default object.
| |
| | |
| == RegExp escape string ==
| |
| | |
| Escapes regexp meta characters in a string.
| |
| | |
| function escapeRegExp(s) {
| |
| return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
| |
| }
| |
| | |
| Example usage:
| |
| | |
| var re = new RegExp("^" + escapeRegExp("fo*bar") + "$");
| |
| "fo*bar".match(re); // Matches
| |
| "foobar".match(re); // Doesn't match
| |
| | |
| == !important Style ==
| |
| | |
| Appends !important to each rule then adds the CSS to the page letting you override the default formatting.
| |
| | |
| function addStyle(css) {
| |
| GM_addStyle(css.replace(/;/g,' !important;'));
| |
| }
| |
| | |
| | |
| Example usage:
| |
| | |
| addStyle('a {text-decoration:none;}');
| |
| | |
| | |
| == Make menu toggle ==
| |
| | |
| A common pattern is to [[GM_registerMenuCommand|register a menu command]] that toggles some script variable that is persisted using [[GM_getValue|GM_get]]/[[GM_setValue|setValue]]. This function abstracts that functionality.
| |
| | |
| function makeMenuToggle(key, defaultValue, toggleOn, toggleOff, prefix) {
| |
| // Load current value into variable
| |
| window[key] = GM_getValue(key, defaultValue);
| |
| // Add menu toggle
| |
| GM_registerMenuCommand((prefix ? prefix+": " : "") + (window[key] ? toggleOff : toggleOn), function() {
| |
| GM_setValue(key, !window[key]);
| |
| location.reload();
| |
| });
| |
| }
| |
| | |
| The first argument is the key used with GM_get/setValue and is also the variable which will hold the current value. The second argument is the default value.
| |
| | |
| The third and fourth arguments are the text to be displayed in the menu for toggling on and toggling off, respectively. The fifth argument is an optional prefix for those menu items.
| |
| | |
| Only one menu command is added, that will toggle the option.
| |
| | |
| Example usage:
| |
| | |
| makeMenuToggle("linkify_emails", true, "Include e-mail addresses", "Exclude e-mail addresses", "Linkify");
| |
|
| |
| if (linkify_emails)
| |
| process_emails_too();
| |
| | |
| '''Note''' that after changing the value, the page is reloaded so that the script runs again with the changed options, and to update the menu. This is not recommended if the user might have unsaved input on the page in question. In such a case, consider adding a <code>prompt</code> or re-running some method instead. Since menu items can't be edited/removed without reloading the page, one would likely also want two separate menu items instead of a toggle.
| |
| | |
| == Serialize/deserialize for GM_getValue ==
| |
| | |
| Used to store and retrieve multiple values (typically as a serialized hash) in a single [[GM_getValue]] slot.
| |
| | |
| function deserialize(name, def) {
| |
| return eval(GM_getValue(name, (def || '({})')));
| |
| }
| |
|
| |
| function serialize(name, val) {
| |
| GM_setValue(name, uneval(val));
| |
| }
| |
| | |
| Example usage:
| |
| | |
| var settings = {a: 1, b: 2, c: 3};
| |
| serialize('test', settings);
| |
| var _settings = deserialize('test');
| |
| // now "settings == _settings" should be true
| |
| | |
| | |
| == make an array persistent in globalStorage ==
| |
| | |
| [http://developer.mozilla.org/en/docs/DOM:Storage DOM:Storage] is available in Firefox 2 and up.
| |
| | |
| /**
| |
| Makes an given Array persistent in the globalStorage Object. It will not work
| |
| with arrays that get additional mutator functions after it was made persistent.
| |
|
| |
| @param {String} name - name of the property of this that is the Array
| |
| @param {String} domain - domain parameter of globalStorage
| |
| @param {Array{String, String, ...}} add_mutator - additional non standard (JavaScript 1.7) functions
| |
| */
| |
| function makeArrayPersistent(_name, _domain, _add_mutator){
| |
| //workaround for scripts that work on pages stored on file://
| |
| var domain = _domain || ".localdomain";
| |
|
| |
| //if the array is not defined yet we define it and fill it
| |
| //with values stored in globalStorage
| |
| if(!this[_name]){
| |
| var evalStr = String(globalStorage[domain][_name]);
| |
| this[_name] = eval(evalStr);
| |
| //if it is defined allready we store it in globalStorage
| |
| }else{
| |
| globalStorage[domain][_name] = uneval(this[_name]);
| |
| }
| |
|
| |
| //Watch will intercept asignments to this[_name] and store the new array in
| |
| //globalStorage. The original array in globalStorage is discarded.
| |
| this.watch(_name, function(_prop, _oldVal, _newVal){
| |
| globalStorage[domain][_name] = uneval(_newVal);
| |
| return _newVal;
| |
| });
| |
|
| |
| //see a few lines below
| |
| ["push", "pop", "reverse", "shift",
| |
| "sort", "splice", "unshift"].forEach(makeMutatorFunctionGlobal);
| |
|
| |
| //you can supply additional functions that will be wrapped
| |
| if(_add_mutator)
| |
| _add_mutator.forEach(makeMutatorFunctionGlobal);
| |
|
| |
| //member functions that alter the array itself are wrapped. The wrapper will
| |
| //call the mutator function and store the altered array in globalStorage
| |
| function makeMutatorFunctionGlobal(_f){
| |
| this[_name][_f] = function(){
| |
| var f = this[_name][_f];
| |
| return function(){
| |
| var retVal;
| |
| retVal = f.apply(this, arguments);
| |
| globalStorage[domain].trolls = uneval(this);
| |
| return retVal;
| |
| }
| |
| }();
| |
| }
| |
| }
| |
| | |
| === Example Usage ===
| |
| | |
| We create an array in window.
| |
| var a = [1, 4, 3, 2];
| |
| | |
| The following would not work:
| |
| | |
| function baz(){
| |
| var b;
| |
| makeArrayPersistent("b", "example.org");
| |
| }
| |
| | |
| We cant keep track of variables that that are not kept track of by JavaScript itself.
| |
| | |
| makeArrayPersistent("a", "example.org");
| |
| | |
| We have to supply the variables name due to watch. <code>this["a"]</code> equals <code>this.a</code> in our case. If you want to store an object in a different scope you can use <code>makeArrayPersistent.call(some.object, "a", "example.org");</code>. The domain has to match the domain of the site where your script is injected too. Read up on <code>globalObject</code> for details.
| |
| | |
| this.a.push(5);
| |
| | |
| Now a equals <code>[1, 4, 3, 2, 5]</code>. It is stored in <code>globalObject</code> that way.
| |
| | |
| this.a.sort();
| |
| | |
| And now it's <code>[1, 2, 3, 4, 5]</code>, stored again.
| |
| | |
| delete this.a;
| |
| | |
| Now the array does not exist in this anymore. But it is kept in <code>globalObject</code>. In the next line a will be defined in this and filled from <code>globalObject</code>.
| |
| | |
| makeArrayPersistent("a", "dexhome.homelinux.org");
| |
| | |
| And <code>this.a</code> equals <code>[1, 2, 3, 4, 5]</code> again.
| |
| | |
| If the user of your script got the same page open in two tabs the two scripts will start to fight over <code>globalStorage</code> and you will lose data. You have to alter <code>makeMutatorFunctionGlobal</code> with some meaningfull logic to counter this. The idea is to retrieve the array from <code>globalStorage</code>, combine it with the local copy and run the wrapped mutator function. Afterward it's stored in <code>globalStorage</code> again.
| |
| | |
| ==Waiting for something==
| |
| Sometimes a script has to wait for some AJAX to finish before it can run this lets you do that
| |
| function wait(c,f){
| |
| if (c()) f()
| |
| else window.setTimeout(function (){wait(c,f)},300,false);
| |
| }
| |
| Example usage:
| |
| wait(
| |
| function(){return count==0},
| |
| function(){alert('allfound')}
| |
| );
| |
| | |
| == Add commas to numbers==
| |
| Numbers look more readable with commas, the following function will add them to every integer in a string.
| |
| <pre>function commafy(num) {
| |
| var str = (num+"").split("."),
| |
| dec=str[1]||"",
| |
| num=str[0].replace(/(\d)(?=(\d{3})+\b)/g,"$1,");
| |
| return (dec) ? num+'.'+dec : num;
| |
| }</pre>
| |
| Examples:
| |
| <pre> commafy("123456789.12345");
| |
| //gives: "123,456,789.12345"</pre>
| |
| | |
| == Make script accessible to Firebug ==
| |
| [[Useful_Tools_for_Script_Writers#FireBug|Firebug]]'s console is very useful but can't access functions in a script this can make debugging them tiresome. The following code will run the entire script in the page so you can mess about with it from Firebug.
| |
| | |
| function a() {return a.caller.toString().replace(/([\s\S]*?return;){2}([\s\S]*)}/,'$2')}
| |
| document.body.appendChild(document.createElement('script')).innerHTML=a();
| |
| return;
| |
| | |
| == Embed a function in the current page ==
| |
| | |
| function embedFunction(s) {
| |
| document.body.appendChild(document.createElement('script')).innerHTML=s.toString().replace(/([\s\S]*?return;){2}([\s\S]*)}/,'$2');
| |
| }
| |
| | |
| Example:
| |
| function helloWorld() { alert("hello world"); }
| |
| ...
| |
| embedFunction(helloWorld);
| |
| varBody = document.getElementsByTagName("body")[0];
| |
| varBody.innerHTML = varBody.innerHTML+ '<a title="Click me" href="javascript:helloWorld();">Click for hello world</a>';
| |
| | |
| == Dump the properties of an object ==
| |
| | |
| This function will allow you to easily dump the properties of an object and their values to the firebug console, or anywhere else you like. I know it is long, but it also does nice formatting on the output, helping it to be readable
| |
| | |
| /* dumpObj courtesy of Scott Van Vliet
| |
| *
| |
| * http://weblogs.asp.net/skillet/archive/2006/03/23/440940.aspx
| |
| *
| |
| * usage:
| |
| * params: all are optional except obj
| |
| *
| |
| * obj -> OBJECT your object to dump
| |
| * name -> STRING the name of your object if you want it printed
| |
| * maxDepth -> NUMBER the maximum nested objects that will be dumped. Defaults to 0, because
| |
| * it grows exponentially, so most of the time, it will be better and
| |
| * easier to read if you just call dump on the child object manually.
| |
| * format -> BOOLEAN whether to format the output (default=1). Turn this of if you have to
| |
| * HTML content from it, to keep the indent low.
| |
| * indent ↓
| |
| * tabsize ↓
| |
| * depth ↓
| |
| * tabs -> these are for internal communication when recursing.., if you plan on touching
| |
| * them, you shouldn't need documentation...lol
| |
| *
| |
| * The formatting works well only if viewed in a monospaced font. This means good in the firebug
| |
| * console, and probably bad in alert boxes, unless you set your chrome to use a monospace font...
| |
| *
| |
| * modifications by Naja Melan ( najamelan<AT>gmail )
| |
| *
| |
| **/
| |
|
| |
| function dump( obj, name, maxDepth, format, indent, tabsize, depth, tabs )
| |
| {
| |
| if( typeof obj == "undefined" ) return "dumpObj: No object was passed in!\n";
| |
| if( typeof maxDepth == "undefined" ) maxDepth = 0;
| |
| if( typeof name == "undefined" ) name = "<root object>";
| |
| if( typeof format == "undefined" ) format = 1;
| |
| if( typeof indent == "undefined" ) indent = "";
| |
| if( typeof tabSize == "undefined" ) tabSize = 8;
| |
| if( typeof depth == "undefined" ) depth = 0;
| |
| if( typeof tabs == "undefined" ) tabs = "";
| |
|
| |
| if( typeof obj != "object" ) return obj;
| |
|
| |
| var child = null,
| |
| output = [];
| |
|
| |
| output.push( indent + name + "\n" );
| |
|
| |
| if( format )
| |
| {
| |
| indent += " ";
| |
|
| |
| var maxLength = 0;
| |
| for( var item in obj )
| |
| if( item.toString().length > maxLength ) maxLength = item.toString().length;
| |
| }
| |
|
| |
| for( var item in obj )
| |
| {
| |
| try
| |
| {
| |
| child = obj[item];
| |
| }
| |
|
| |
| catch (e)
| |
| {
| |
| child = "<Unable to Evaluate>";
| |
| }
| |
|
| |
| if( format )
| |
| {
| |
| var numSp = maxLength - item.toString().length + 1,
| |
| tabs = "";
| |
|
| |
| while( --numSp > 0 ) tabs += " ";
| |
| }
| |
|
| |
| if( typeof child == "object" )
| |
| {
| |
| if( depth >= maxDepth )
| |
| output.push( indent + item + tabs + ": <object, max depth reached>\n" );
| |
|
| |
| else
| |
| {
| |
| try
| |
| {
| |
| var temp = dump( child, item, maxDepth, format, indent, tabsize, depth + 1, tabs );
| |
| }
| |
| catch( e )
| |
| {
| |
| output.push( indent + item + tabs + ": <object could not be iterated, Error name: '" +
| |
| e.name + "'. Error message: '" + e.message + "'>\n" );
| |
| temp = null;
| |
| }
| |
|
| |
| if( temp == indent + item + "\n" )
| |
| output.push( indent + item + tabs + ": <object, only has built-in properties>\n" );
| |
|
| |
| else if( temp )
| |
| {
| |
| output.push( " \n" );
| |
| output.push( temp );
| |
| output.push( "\n------------------------------------------------------------------------<end of " +
| |
| item + ">---------------------------------------------- \n \n" );
| |
| }
| |
| }
| |
| continue;
| |
| }
| |
|
| |
| else
| |
| {
| |
| if( format )
| |
| {
| |
| var intro = indent + item,
| |
| length = intro.length + numSp + 1,
| |
| indent2 = " ";
| |
|
| |
| while( --length > 0 ) indent2 += " ";
| |
| }
| |
|
| |
| else
| |
| {
| |
| var intro = indent + item,
| |
| tabs = indent2 = "";
| |
| }
| |
|
| |
| output.push( intro + tabs + ": " +
| |
| ( ( !format )? child : child.toString().replace( /({)\n ( \[native code\])\n(})/,
| |
| "$1$2 $3" ).replace( /(\r\n|[\r\n]+|<br ?\/?>)/gm, "$1" + indent2 + tabs ) ) + "\n" );
| |
| }
| |
| }
| |
| return output.join( "" );
| |
| }
| |
| | |
| == Force Links to Open in Another Window ==
| |
| | |
| Useful when you have altered the page in a custom way (i.e. via user input) and need to preserve the customized page without repainting form fields or customized nodes. This assumes only anchor links, not button or JavaScript induced links:
| |
| <pre> var anchorTags=document.links;
| |
| for (var i=anchorTags.length - 1; i >= 0;--i)
| |
| anchorTags[i].target="_blank";
| |
| </pre>
| |
| | |
| == nextSibling function that skips whitespace ==
| |
| | |
| <pre>function nextSibling(startSib){
| |
| var nextSib;
| |
| if (!(nextSib=startSib.nextSibling))
| |
| return false;
| |
| while (nextSib.nodeType!=1)
| |
| if (!(nextSib=nextSib.nextSibling))
| |
| return false;
| |
| return nextSib;
| |
| }</pre>
| |
| | |
| === Example Usage ===
| |
| <pre>if (nextSib=nexSibling(element))
| |
| alert(nextSib.textContent);</pre>
| |
| | |
| [[Category:Code snippets]]
| |