Scriptish 0.1b4 Can Update Your User Scripts

For a long time now both user script authors and Greasemonkey users have wanted Greasemonkey to be able to update user scripts. In absence of this feature user script authors have resorted to rolling their own code to do the updating, which has lead to a minefield of problems ranging from simple javascript errors to causing long downtimes on userscripts.org. Besides the obvious risks that third-party user script updaters have of not working, over using bandwidth, and harming the uptime of important websites, there is also a variability of design and usefulness which means it's hard for a user to know things that they should know, like:

  1. Which of their installed user scripts are update-able?
  2. Which of their installed user scripts have updates available?
  3. Where will they be notified of updates for there user scripts?
  4. What do they need to do in order to update a user script?
  5. How can they disable updates for a user script?

Hopefully it's now plain to see why a user script engine must update user scripts, and yet none that I know of do. Greasemonkey for Firefox does not. Google Chrome runs user scripts natively, but also does not update them. Those two engines are by far the most popular, but there are a few others, all of which do not update user scripts..

Well late last year I started discussing this feature with Olivier Cornu for his fork of Greasemonkey which he called Webmonkey. Months later I decided to start a Greasemonkey fork of my own, which I call Scriptish. Then, as this year went by I discovered Mozilla's plans for the new Addon manager and the stars seemed to align.

So a couple of days ago I finally got around to implementing user script updating in Scriptish, and released the feature in Scriptish 0.1b4 last night; now I'd like to discuss it a little bit for those that are interested.

User Script Updating in Scriptish

@updateURL

You may specify a URL for Scriptish to check for updates using the @updateURL metadata block key. The @updateURL must be a secure url (ie: starts with "https://"), otherwise it will be ignored.

The optional use of ".meta.js"

To save bandwidth, and improve response times, you may optionally provide a ".meta.js" version of your user script as the @updateURL, which should be the same as the user script, but only containing the user script's metadata block. If you provide a @updateURL that ends with ".meta.js", then Scriptish will check then ".meta.js" file for updates, and download an update from the same url where ".meta.js" is replaced with ".user.js".

The option to use the download URL as the update URL if no @updateURL was provided.

If you go to the options window for Scriptish you will see a preference which allows you to use a user script's download URL as the update URL if there was no @updateURL defined; this option is turned off by default.

Using ".meta.js" by default from userscripts.org

If the update URL that should be used to check for updates is a userscripts.org URL, then the ".meta.js" version of the user script will be used by default.

The GM_updatingEnabled constant

All versions of Scriptish that provide user script updating will include a GM_updatingEnabled constant in the user script sandbox, this means that if you are a user script author that has implemented a updater for your script already, then your code can check if this variable is defined and equal to 'true' before attempting to check for updates.

Conclusion

A user script engine that updates user scripts has been long desired, so I'm proud to introduce the feature for the first time in Scriptish, and I hope you give it a try!

Webxtend

Something that I don't think many web developers and extension developers seem to be picking up on, but yet some are, is what I'm calling webxtending, which is providing APIs that allow their users to extend their website/webapp or extension with a browser extension. To illustrate this idea take the Gmail-Greasemonkey API, this is a front-end JS API that a user script can easily access to be notified of Gmail's custom events, change state variables, and perform actions with the Gmail interface (afaik the api used to work, and some parts do not work at the moment). This was a wonderful idea because it allowed user script authors to save a lot of development time, also maintenance time, and write more cpu efficient scripts. Furthermore I imagine such a API would be extremely useful to internal development, it would at least reduce the chance of sloppiness.

Webxtending doesn't always mean providing a Javascript API though, it often simply means writing semantic code by using microformats, and structuring the markup in a logical manner, taking into account how others may want to change the UI, and making those changes as easy for them to make as possible. This means using the id attribute even when you don't need it, because others may, but it also means thinking carefully about every facet of the structure. XUL overlays used in Firefox extension development are a good example of this structural extendibility idea.

Webxtend is a gold standard quality requirement, if your website or browser extension is not webxtendable then it's not gold standard quality. =p

UserScript: Showing Page Titles & URLs in Google Analytics

Often, when you're using Google Analytics, you want to be able to segment by the page's title, but there is no default way to do this with Google Analytics.

Well, a couple of hours ago I started catching up on my reading in Google Reader, and I noticed a blog post from LunaMetrics titled "Showing Page Titles & URLs in Google Analytics", and that post pointed out exactly how to segment by page title.

So, I took a couple of minutes and wrote a userscript to add this dimension to the 'segment by' drop down with a userscript so that you can easily segment by page title, without having to think about how to do this. Here's the code:

This userscript is available on userscripts.org as well, here: Google Analytics Page Title Segmentation

Enjoy!

The Principle of JavaScript Illumination

Every web developer knows that their site should work when the site's users disables JavaScript, but I constantly find this principal is violated when it is simple thing to obey. When web UI developers violate this principal it shows an extreme laziness and a failure to understand good practices on their part; this can greatly worsen the user experience. So, I'd like to articulate what I'm going to call The Principle of JavaScript Illumination which is an idea I haven't seen articulated yet, but that everyone knows in their gut they should do, and yet I hardly ever see this principal adhered to in practice.

Definition

The Principle of JavaScript Illumination is a simple one, if some user interface element will not work when JavaScript is disabled, then have JavaScript add the element.

Implications

If you have a user interface (UI) element that uses JavaScript, then make sure there is a default functionality that will work when JavaScript is disabled. If the UI element cannot work at all without JavaScript, then you should make JavaScript add the element, this way if JavaScript is disabled then the user will not see UI elements that will not work, causing a bad user experience.

Examples

Facebook Chat

When you log in to Facebook with JavaScript disabled, the chat widget is displayed to you on the bottom right of the screen, but it doesn't do anything, it's completely useless to you and it's just in the way, so why the heck is it displayed to the user?

Kampyle

Kampyle provides some widget code for anyone to put on their site which adds a "Give Feedback" button to the bottom right of the user's screen, which the user can click on to give feedback for the site. The trouble is, when JS is disabled, or when the user uses NoScript to block assets from the kampyle.com domain, then the button doesn't work (see ROIRevolution's site for an example of this), so why the heck is it displayed to the user?

Conclusion

Be a good web citizen, and a smart web developer by obeying The Principle of JavaScript Illumination, because it's the right thing to do and because you're users will thank you for it.

Using Web Workers Today

I did a little reading on Web Workers a few months ago, and I recently used them in a little test I was asked to perform, so I thought I would share what I've discovered since I don't see much in Google search results about how to use Web Workers in a cross browser fashion with graceful degradation.

Web Workers

For those of you that do not know what Web Workers are I suggest you read the Web Workers Draft Recommendation, but in brief they provide web developers with the ability to create thread-like operations.

This is obviously useful for long operations. The standard way of dealing with laborious tasks like these in the past was to split up the work with multiple setTimeout() calls.

setTimeout

Before Web Workers, setTimeout() had to be used to split up long operations in to multiple small operations, because you only had a single thread, so this had to be done in order to avoid having an unresponsive UI or triggering a unresponsive script warning.

Current Browser Support For Web Workers

Currently the latest versions of Firefox, Chrome, and Safari all support Web Workers; IE and Opera do not support Web Workers.

So how do you detect if a browser supports Web Workers programmatically? this is how:

if( Worker /*check for support*/ )
// Web Workers are supported
else
// Web Workers are not supported

Minor Note For Safari

While Safari supports Web Workers it requires that strings be used when passing messages, which is something that Firefox and Chrome do not require. Firefox and Chrome both stringify anything not already a string that is crossing the bridge to or from a Web Worker, but Safari does not do this, so you must. This is a simple thing to do, but one that you must remember, always use the JSON object to JSON.stringify when sending a message and JSON.parse when receiving a message (if web workers are supported, then so will the JSON object be).

Supporting Browsers That Do Not Support Web Workers

The biggest hurdle for most to implement Web Workers today is the fact that not all browsers support them, but the fact is that it is simple to degrade gracefully, thus allowing those users that are using browsers that support web workers to start reaping the rewards, and allowing you to improve your site and be prepared for when IE and Opera finally come around.

So what is the best way to handle the browsers that do not support Web Workers? well I see two options:

  1. Maintain a secondary script to be used when Web Workers cannot be, and add it to the page by creating a script element and appending it to the document.body.
  2. Make the Web Worker friendly to being used in a window scope instead of a Web Worker's scope so that it can be simply added to page like I describe in option 1.

Option 2 will be the better approach in most cases I suspect, because it means that two separate files with two slightly different methods of achieving the same task do not need to be maintained. In order to implement option 2 two there are some things to consider:

  • You cannot use functions like postMessage, onmessage, or importScripts while in the window scope.
  • You will need to split the work up with setTimeout (which is available in the Web Worker scope as well) to avoid an unresponsive UI and unresponsive script warnings.
  • You should assume that your Web Worker will be used in the window scope, so do not pollute your global scope.

There are a couple of ways to determine if you are not in a Web Worker's scope, as to avoid using the postMessage, onmessage, and importScripts functions (which are available to Web Workers) when your script is run in the window scope (postMessage has another purpose in the window scope). The first method is this:

if(typeof importScripts == "function") {
// web worker scope
}
else {
// not web worker scope, assume window scope
}

The other method is to check if you are in the window scope:

if(typeof window != "undefined") {
// window scope
}
else {
// not window scope, assume web worker scope
}

Or, combining the two methods above:

if(typeof window == "undefined" && typeof importScripts == "function") {
// web worker scope
}
else {
// another scope
}

These methods will allow us to avoid using postMessage, onmessage, or other methods we use while in the scope of a Web Worker.

To avoid polluting the global space normal tactics apply, I would simply suggest wrapping all of the code in an anonymous automatically executed function, like so:

(function(){
// this is an anonymous automatically executed function
})()

Putting these methods together, we would get web workers like so:

(function(){
var winScope = true;
if(typeof window == "undefined" && typeof importScripts == "function") {
// web worker scope
winScope = false;
}

if(!winScope) {
onmessage = function() {
postMessage("blah");
}
}
else {
alert("blah");
}
})()

Except that real Web Workers would do interesting things, and use setTimeout.

Goo.gl Now Open For General Use

Screen shot of Goo.gl fixed up with the userscript mentioned below

Three days ago Google announced it's release of Goo.gl a url shortener which is more reliable and trust worthy (to a user) than other services such as tinyurl.com, tr.im, bit.ly, etc.

Now I think all web publishers should maintain their own url shortener which only they can create urls for and control, for example I bought the domain evold.ca, and use the subdomain r.evold.ca as my url shortener. I primarily use this domain to shorten urls to my other domains, like erikvold.com, but I can and do occasionally use it to create short urls for domains that are not mine. As a user, when I see a blog post that I want to share, I first check if the publisher has published a short url for the page, which any smart web publisher does, but for the other web publishers (the majority atm) I have to create a short url, which I can now use Goo.gl to do.

The problem is that Goo.gl does not allow users to use it to shorten links directly. Currently, the Google URL Shortener is only officially available via the Google Toolbar and FeedBurner.

Well, I noticed that this problem was solved yesterday by a Google Chrome extension called goo.gl shortener. Amit Agarwal drew my attention to this extension in a blog post he wrote yesterday, where he pointed out the important javascript parts needed to create goo.gl short urls. So I took this information and opened Goo.gl up with a userscript, a ubiquity command, and a jetpack to help everyone create short urls with Goo.gl, check them out!

Click Tracking JavaScript Library Now Available

Purpose

To help web publishers easily track link clicks the correct way, across browser platforms, with tools like Google Analytics (GA) and Google Website Optimizer (GWO).

Typically a web publisher will want to do something like track all external links on a website with GA, setup a GWO test where the conversion is a click through, or do both on the same page for a few pages of their site. This javascript library was designed to make tasks such as these simple.

About

clickTrackingLib

Adding the click-tracking-lib.js or the click-tracking-lib-with-presets.js to a web page provides you with a clickTrackingLib javascript object. Once you provide clickTrackingLib with some match objects, you can then have clickTrackingLib scan a single, set, or all document links to determine if the link(s) should have tracking functions applied to it or not, and do so if needed. If one or more tracking functions are applied to a link, then a delay function will be applied to the link (this can be disabled too of course, and the delay can user defined but the default is 99 ms).

The Delay Function

The delay function is meant to delay the user from being redirected to a new page for just a bit to allow time for say a Google Analytics image request to complete, and avoid being canceled by the user's browser. It can be disabled, but it is added by with a 99 sec delay by default, which can be customized, as the very last onClick event listener, after all of the desired provided Match object trackingFunc's are applied.

The delay function technically only sets a timeout to redirect the user after x ms, but in order for the delay to work the link's default functionality, namely redirecting the user, needs to be disabled, if the link doesn't just open in a new tab (target="_blank" and target="_new" are checked for). To disable the default functionality the link's onclick attribute must be set to onclick="return false;" which I figured should be fine because all well made websites won't use that attribute except to make it equal to "return false;" anyhow. If your site is not well made, then you should think about fixing it or having it fixed for you, but you can still use this library by simply not using the delay function and implementing it by some other means.

Presets

There are a number of presets available if you use click-tracking-lib-with-presets.js or click-tracking-presets.js; presets are predefined match functions for common use cases, such as matching all internal links, or external links, or download links. They can be used as they written, you could cut out the ones you won't use, or simply use them as examples when you define your own custom match objects.

The can be accessed from the clickTrackingLib.getMatchPreset( name ) method. The possible name values and the meanings behind them are as follows:

  • 'rel-external': matches rel-external links
  • 'external-hostname': matches external hostname links
  • 'all-external': matches all rel-external and external hostname links
  • 'filetypes': matches file names: (docx*|xlsx*|pptx*|exe|zip|pdf|xpi|rar|xls)
  • 'non-rel-external': matches non-rel-external links
  • 'internal-hostname': matches internal hostname links
  • 'all-internal': matches non-rel-external links or internal hostname links

Match Objects

Any object that has a 'match(link):boolean' method and 'trackingFunc(event,link):void' method defined.

match(link):boolean

This method will take a link element as input, run some test on it and return true or false, if true is returned then the Match object's trackingFunc will be added to the link.

trackingFunc(event,link):void

This method will be provided with two input values; event is the click event object, and link is a reference to the link element. You can obtain a reference to this from the event object, but there is some cross browser logic you would need to implement, so I thought a direct reference to the link would be handy. The function should not return anything.

new clickTrackingLib._clickTrackingLib();

If you want to create multiple clickTrackingLib objects, that contain different sets of match object arrays, then you need to create a new one, like so:

var myNewClickTrackingObj = new clickTrackingLib._clickTrackingLib();

This will give you a new object, myNewClickTrackingObj, which is like clickTrackingLib in all but two ways. First myNewClickTrackingObj will not have a _clickTrackingLib() method, and second it will not have a getMatchPreset() method, even if clickTrackingLib.getMatchPreset() was defined. This is because you don't need a bunch of objects in memory with those two methods, only one, which is the singleton clickTrackingLib object.

How To Use

  1. Download either click-tracking-lib-with-presets.js or click-tracking-lib.js (see ABOUT fore more information) from the project repository at GitHub.
  2. Minify it with one of the following options:
  3. Upload the minified script to your site.
  4. Add <script src=".." type="text/javascript"></script> for the script to the desired pages.
  5. Write some code:
    • define some match objects (just an object with a match method and trackingFunc method)
    • Add match objects to your clickTrackingLib object
    • Call the clickTrackingLib.attachTrackingFunctions( links, delay ) when desired.
      • links: can be a single anchor element, an array of anchor elements, or falsely so that the default is used, which is document.links.
      • delay: should be a positive integer or falsey, in the latter case no delay function will be added to the links scanned, in the former case a delay function will be applied with the specified delay value.

[More]

Would You Like To Know More?

This is just a last moment idea inspired by the movie Starship Troopers which flashed in to my mind a couple of days ago as I was thinking about Jetpacks for the Jetpack for Learning Design Challenge. It is primarily a Microformat/Link Type proposal for rel-info with some ideas and prototypes demonstrating how it could be used.

Abstract

When you are reading something on the web there are usually pages which exist elsewhere on the web that would provide background information, and further reading if you desired to dig deeper into the subject(s) you are reading about. Sometimes some of those pages are linked to from the article you are on, but often the link wouldn't make sense in the context of the content, and using a link tag isn't very useful to a user, so the link is not made.

I propose a rel-info link type, even if just as a Microformat, for 'a' and 'link' tags to provide a link to context-sensitive information.

With rel-info a user can read more about the Movie that was discussed at Netflix or IMDB easily for example, because the publisher (or a script the user subscribes to, more on this later) provided a rel-info link which the user can easily find and follow with a Jetpack, Firefox extension, or some other browser extension.

Proposal

When rel-info links are provided on a page browser extensions can easily extract them from the page, which can lead to a large number of use cases, of which I am sure I have only just scratched the surface. There is another side to this coin however which is that there are a large number of ways that rel-info links might be added to a page.

Let's explore the latter case, first the person who published the page could add rel-info links, but then browser extensions could add rel-info links, they could also search for and remove rel-info links. A user could subscribe to a person's or institution's suggestion feed or script which adds and/or removes rel-info links to pages, along with using a AI based browser extensions to add rel-info links.

When the rel-info links are going to be consumed there are some other interesting possibilities, for example a browser extension could filter the links by a HTML 5 'data-topic' attribute by keeping a list of topics that the user dislikes or likes, the domain could also be used for filtering. A browser extension could allow the user to mark pages as read so that they are removed from pages in the future as another example.

Prototypes

To test this link type out with a Jetpack I first had to add it to some pages, so I decided to write a couple of UserScripts to do this demonstrating how they can be dynamically added to pages by anyone even users themselves. The first userscript was to add the rel-info link type to IMDB links, and another to add the rel-info link type to Wikipedia links.

If you install the above UserScripts then you should find rel="info" or rel="... info" on your IMDB and Wikipedia links. Once you have done that you can check out and install the "Would You Like To Know More?" Jetpack. This is a slidebar Jetpack, and when you open this slidebar the focused tab is then scanned for rel-info links, if any are found then they are displayed in a list for you to choose from, if you click one of the provided links a new tab will be opened for the new page.

These UserScripts and the Jetpack take advantage of HTML 5 'data-' attributes, by adding a 'data-source' attribute to the rel-info links added on the userscript side, which the jetpack was written to check for and display if found. This is an example of how HTML 5 'data-' attributes can be used with the rel-info link type which I propose to create more interesting results.

The Daily UserScript: Google Images Smush It Link

Today's userscript adds "Smush It!" links to Google Images results, which makes use of the Yahoo! YSlow Smush.it web service.

The Unpleasantry:

When I'm searching through images on Google Images and I find something that I want to use, I naturally want to smush it using Yahoo! YSlow Smush.it, but to do that I have first get the url of the image, then go to Smush.it, paste the image url and hit the "Smush" button.

The Alleviation:

Once you install the "Google Images Smush It Link" UserScript there will be a link added to each image result which will enable you to quickly smush the image.

Screen Shots:


Before: Google Image search result before installing the Google Images Smush It Link userscript.

After: Google Image search result after installing the Google Images Smush It Link userscript.

After: Google Image search result after clicking the provided 'Smush It!' link.

Greasemonkey Tip: Simulating a click on a DOM element without the click() method

While I was working on today's userscript for Google Reader I had to clone a node, and when I did that the mouse events were not part of the clone, so the clone wasn't really a clone..

I thought about it for a moment and decided to simulate a click of the original node when the clone was clicked, but the problem was that I had relied on libraries for this in the past, which is a good idea in general, but I didn't want to add code that is meant for other browsers to my userscript. So I did some searching, and didn't find any results that helped me at all after > 1 hours. So after I had it with searching, I took a look at the jQuery code, and got lost, then switched to the YUI 3 Event library code and found my answer. So I thought I would put it on the web for the next guy.

Simulating a click:

[More]

More Entries

© Erik Vold 2007-2012. Contact Erik Vold. Top ^