|
|
I think we need one of these. It would be very useful.
$x - by Johan Sundströmfunction $x(xpath, root) { // From Johan Sundström
var doc = root ? root.evaluate ? root : root.ownerDocument : document, next;
var got = doc.evaluate(xpath, root||doc, null, null, null), result = [];
while(next = got.iterateNext())
result.push(next);
return result;
}remove - by Peter Bunyanfunction remove(element) {
if (element)
element.parentNode.removeChild(element);
}
|
|
|
xpathExec
function xpathExec(xpath, func) {
var result = document.evaluate(xpath, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0; i < result.snapshotLength; i++) {
if (result.snapshotLength == 1) return func(result.snapshotItem(i), arguments[2], arguments[3]);
else func(result.snapshotItem(i), arguments[2], arguments[3]);
}
}
// example
xpathExec("//td[@class='ebcPpl']", replaceInItem, "PayPal", "PP");
|
|
|
Is it possible to include a js-library file in my script? For example, prototype.js or others.js written by myself. |
|
|
Quick answer Aprhen: No, not yet. It is being discussed and code is being written. You can read more about it on the greasemonkey mailinglist. |
|
|
If your intent is to write functions to be used as standard libraries, you guys might want to comment your functions a little better. A description of what the function does and what variables it expects would be really helpful. For example, Peter, your $x function seems pretty incomprehensible (at leas with my limited JS expertise). A more descriptive name would probably be in order too. I don't mean to be critical, I just think that would make it a lot easier for others to make use of your code. |
|
|
Much of the appeal of the $x function is its short name ;) Also, it's pretty self-documenting if you've done JS XPath scripting before, though obviously less so if you haven't. It returns an array of elements matching an XPath expression and takes an optional root element for that expression. |
|
|
It's not a good idea to include a js file from local disk for security reasons. How about include js from remote site? |
|
|
How is a remote source more trusted than a local? |
|
|
Including stuff from remote sites is a privacy issue – the logs for that server will track your browsing history (well, the pages where those scripts are applied). I'd just cut-and-paste for now. |
|
|
Instead of using Jasper's
$x("//a[@target='_blank']").forEach(function(v, i, a) {
v.removeAttribute('target');
});
forEach, and some other useful array methods seems to be relatively unknown, so I'm including a tutorial that explains them all. |
|
|
Arvid, thanks for the tutorial, it's great. Always love learning something new. |
|
|
Here is something I use for creating elements in place of the a bit too verbose for my taste createEl({n: nodename, a: {attr1: val, attr2: val}, c: [child1, child2], evl: {type: eventlistener_type, f: eventlistener_function, bubble: bool}}, appendTo)
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 (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;
}
//example usage, not tested :)
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);
//instead of writing:
var ol, li;
ol = document.createElement('ol');
ol.setAttribute('class', 'some_list');
ol.setAttribute('id', 'my_list');
li = document.createElement('li');
li.textContent = "first point";
li.addEventListener('click', function() { alert('click'); }, true);
ol.appendChild(li);
li = document.createElement('li');
li.textContent = "second point";
ol.appendChild(li);
li = document.createElement('li');
li.textContent = "third point";
ol.appendChild(li);
The function might be too heavyweigth for some scripts, and the syntax might be a bit too unreadable. But I've taken a fondness of it. Here's another piece of code I use for de/serializing objects and saving them with serialize/deserialize
function deserialize(name, def) {
return eval(GM_getValue(name, (def ? def : '({})')) );
}
function serialize(name, val) {
GM_setValue(name, uneval(val));
}
//example
var settings = {a: 1, b: 2, c: 3};
serialize('test', settings);
var _settings = deserialize('test');
// now "settings == _settings" should be true
For selecting one element with XPath:
$xs
function $xs(xpath, root) {
var doc = root ? root.evaluate ? root : root.ownerDocument : document, next;
return doc.evaluate(xpath, root||doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
(yay, no double linebreaks in pre-tags anymore!) |
|
|
Elegant code! A true pleasure to behold! My mastery of Javascript is much lesser. Arvid, do you know any other good tutorials? I mean about using objects the way your functions do? |
|
|
I copied some of these into http://wiki.greasespot.net/Code_Snippets and tidied that page up some. Forums are great for discussing this stuff, but the wiki is probably a better reference. |
|
|
Can someone make this post a sticky, please? |
|
|
Lior: I'm not sure about making this thread sticky. I think the wiki is better as a reference. This thread is useful for discussions (e.g. pros and cons of different implementations of $x()), but I'm not sure that warrants stickiness. |
|
|
Henrik: I see what you mean. I simply noticed that I was coming back to this thread a lot (even bookmarked it yesterday), and that it contained info that was not in the Wiki (as far as I know). It's your call. |
|
|
Lior: Update the wiki ;) |
|
|
In the $x function I like to add the line
|
|
|
Henrik: Got it. :-) BTW, my programming improved tremendously from careful reading of these snippets.
|
|
|
Here is my full (and almost insane) $x, it remove the necessity of using forEach on the methods of the returned nodes
function $x(xpath) {
xpath=xpath.replace(/((^|\|)\s*)([^/|]+)/g,'$2//$3').replace(/([^.])\.(\w*)/g,'$1[@class="$2"]').replace(/#(\w*)/g,'[@id="$1"]').replace(/\/\[/g,'/*[');
var got=document.evaluate(xpath,document,null,null,null), result=[];
if (next=got.iterateNext()) {
for(var i in next.wrappedJSObject)
if(typeof result[i] =='undefined') result[i]=Function('a=arguments;this.forEach(function(n) {try {if(n.'+i+') n.'+i+'(a[0],a[1],a[2],a[3],a[4])}catch(e){}})');
result.push(next);
while(next=got.iterateNext()) result.push(next);
}
return result;
}
it allows for things like
although it doesn't work for
as it says that addEventListener is undefined some proding shows that it shows up in:
does anyone have anyidea why this would be? It is driving me insane |
|
|
How about the autoupdate code? Here's a pretty minimal version assembled from various sources on here:
// Script Header
//
// @version 1.2
...
...
// code
function autoUpdateFromUserscriptsDotOrg(SCRIPT) {
try {
if (!GM_getValue) return; // Older version of Greasemonkey. Can't run.
// avoid a flood of dialogs e.g. when opening a browser with multiple tabs set to homepage
// and a script with * includes or opening a tabgrop
var DoS_PREVENTION_TIME = 2 * 60 * 1000;
var isSomeoneChecking = GM_getValue('CHECKING', null);
var now = new Date().getTime();
GM_setValue('CHECKING', now.toString());
if (isSomeoneChecking && (now - isSomeoneChecking) < DoS_PREVENTION_TIME) return;
// check daily
var ONE_DAY = 24 * 60 * 60 * 1000;
var lastChecked = GM_getValue('LAST_CHECKED', null);
if (lastChecked && (now - lastChecked) < ONE_DAY) return;
GM_xmlhttpRequest({
method: 'GET',
url: SCRIPT.url + '?source', // don't increase the 'installed' count just for update checks
onload: function(result) {
if (!result.responseText.match(/@version\s+([\d.]+)/)) return; // did not find a suitable version header
var theOtherVersion = parseFloat(RegExp.$1);
if (theOtherVersion <= parseFloat(SCRIPT.version)) return; // no updates or older version on userscripts.orge site
if (window.confirm('A new version ' + theOtherVersion + ' of greasemonkey script "' + SCRIPT.name + '" is available.\nYour installed version is ' + SCRIPT.version + ' .\n\nUpdate now?\n')) {
GM_openInTab(SCRIPT.url); // better than location.replace as doing so might lose unsaved data
}
}
});
GM_setValue('LAST_CHECKED', now.toString());
} catch (ex) {
}
}
// usage example
autoUpdateFromUserscriptsDotOrg({
name: 'RSS+Atom Feed Subscribe Button Generator',
url: 'http://userscripts.org/scripts/source/688.user.js',
version: "1.2",
});
|
|
|
EDIT: Junk Blocker your totally right, thanks for pointing that out to me. Using your example will artificially increase the install count every time it checks for updates. A less antisocial way would be to check http://userscripts.org/scripts/source/688 for updates instead |
|
|
No, it won't. See this post by Henrik Nyh
url: SCRIPT.url + '?source', // don't increase the 'installed' count just for update checks
i.e. appending '?source' to the source url is an alternate way of avoiding increasing the count. :) |
|
|
I just made a makeMenuToggle function and posted it to the wiki. Quite useful! It generalizes the case where you add a menu item to toggle some persisted variable. |