Executing code in the webpage context from Chrome extensions

Posted: February 11th, 2012 | Author: | Filed under: Javascript | Tags: , , , | No Comments »

Content scripts on Chrome extensions run on a different context than the page they’re running on. They can access and manipulate the DOM, but they don’t have access to any variables of functions defined in the webpage.

It is, however, possible to inject new <script> tags into the page and execute code from there. Yet, passing data from the content script scope and getting data back isn’t very convenient – which is why I wrote a small function to make that easier.

To pass data into the webpage context, the inject function takes a function as its last arguments and passes all the arguments preceding it to that function. To get data back from the webpage context I’m taking advantage of the fact that appendChild is synchronous and blocks execution until the script is finished executing by replacing the <script> innerText with the return value.

Do note that all the data is exchanged in JSON format, so only primitive values and simple array/objects can be passed. When the code causes an exception, its stringified and thrown in the content script context (a magic isException property is used for that, so be careful not to use it for other purposes).

Here’s the function (written in CoffeeScript. For those of you still using JavaScript, the JavaScript version is available at the gist):

The usage:

Hope you found that useful. Enjoy.


Default arguments in JavaScript

Posted: July 30th, 2011 | Author: | Filed under: Javascript | Tags: , | No Comments »

I’ve thought today on a nice and clean way of handling default function arguments in JavaScript. Because the arguments object in JavaScript defines getters and setters that also interact with the functions’s formal arguments, changing its values also changes the value of the local argument variables. For example:

function foo(bar) {
    arguments[0] = "baz";
    alert(bar); // bar is now "baz", not "qux"
}
foo("qux");

What that means is that we can pass the arguments object to another function, which can take care of setting default values and by that also modify our local argument variables:

window.setDefault = function (args) {
   for (var i = args.length + 1; i<arguments.length; i++) {
       args[i-1] = arguments[i];
   }
};
// Minified:
// window.setDefault=function(a){for(var b=a.length+1;b<arguments.length;b++)a[b-1]=arguments[b]}

(raw, minified (48 bytes))

Than, to use it:

function foo(bar, baz, qux) {
    setDefault(arguments, "bar", "baz", "qux");
    console.log(bar, baz, qux); // "a", "baz", "qux"
}
foo("a");

HTML escaping in Underscore.js templates

Posted: July 20th, 2011 | Author: | Filed under: Javascript | Tags: , , , | No Comments »

When displaying values in HTML templates, you would usually want to escape special HTML characters so that they’re displayed properly and to protect against XSS.

I’ve changed my local Underscore.js (and opened a pull request at Underscore.js’s github) to add support for _.escape() and a new <%== ... %> template syntax (notice the double equal signs) which escapes the value for displaying it in HTML.

If you want to add support for that, you can either apply my commit or simply use this code (make sure to load that after Underscore.js):

_.templateSettings.encode = /<%==([\s\S]+?)%>/g;
_.extend(_, {
	// Taken from Backbone.js's escapeHTML()
	escape: function(string) {
			return (''+string).replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
	},
	template: function(str, data) {
		var c  = _.templateSettings;
		var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
			'with(obj||{}){__p.push(\'' +
			str.replace(/\\/g, '\\\\')
				.replace(/'/g, "\\'")
				.replace(c.encode, function(match, code) {
					return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
				})
				.replace(c.interpolate, function(match, code) {
					return "'," + code.replace(/\\'/g, "'") + ",'";
				})
				.replace(c.evaluate || null, function(match, code) {
					return "');" + code.replace(/\\'/g, "'")
						.replace(/[\r\n\t]/g, ' ') + "__p.push('";
				})
				.replace(/\r/g, '\\r')
				.replace(/\n/g, '\\n')
				.replace(/\t/g, '\\t')
				+ "');}return __p.join('');";
		var func = new Function('obj', tmpl);
		return data ? func(data) : func;
	}
});

(raw, minified (835 bytes))

If you prefer and have no backward-compatibility issues with it, you can change the <%= ... %> syntax to be escaped by default, and use the double equal sign for cases you do want unescaped HTML. Use that code instead:

_.extend(_.templateSettings, {
	encode: /<%=([\s\S]+?)%>/g,
	interpolate : /<%==([\s\S]+?)%>/g
});
_.extend(_, {
	// Taken from Backbone.js's escapeHTML()
	escape: function(string) {
			return (''+string).replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
	},
	template: function(str, data) {
		var c  = _.templateSettings;
		var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
			'with(obj||{}){__p.push(\'' +
			str.replace(/\\/g, '\\\\')
				.replace(/'/g, "\\'")
				.replace(c.interpolate, function(match, code) {
					return "'," + code.replace(/\\'/g, "'") + ",'";
				})
				.replace(c.encode, function(match, code) {
					return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
				})
				.replace(c.evaluate || null, function(match, code) {
					return "');" + code.replace(/\\'/g, "'")
						.replace(/[\r\n\t]/g, ' ') + "__p.push('";
				})
				.replace(/\r/g, '\\r')
				.replace(/\n/g, '\\n')
				.replace(/\t/g, '\\t')
				+ "');}return __p.join('');";
		var func = new Function('obj', tmpl);
		return data ? func(data) : func;
	}
});

(raw, minified (878 bytes))

P.S. Can anyone think of a nicer syntax than <%== ... %>? I don’t really like it, but I didn’t come up with anything better…


“Singleton” initializer in Javascript

Posted: July 19th, 2011 | Author: | Filed under: Javascript, Web Development | Tags: , , | No Comments »

The nature of javascript makes it quite easy to have singleton-like behavior without actually using the singleton design pattern – simply define an object with some properties and use that. No need to do anything more than that, really – there’s no reason to create a prototyped function, create an instance and keep a reference to it when javascript gives you a much more natural and elegant solution.

For example:

var Foo = {
  bar: 123,
  a: function() { /* ... */ }
  /* ... */
};
// Simply use Foo as a "singleton"

However, one thing that is missing from the singleton pattern is that the constructor is called only once, and only at the moment someone requests an instance, so it doesn’t run any code to initialize the object unless actually necessary.

Here’s my way of doing that in javascript:

var ObjectInitializer = function(object) {
  var initialized = false;
  return function() {
    if (!initialized) {
      object.initialize();
      initialized = true;
    }
    return object;
  };
};

To use that, simply add an initialize property on the object with the code to execute when the object is requested the first time, and pass it to ObjectInitializer, which’ll return an anonymous function that calls the initialize method the first time its accessed and returns the object, or simply returns the object otherwise.

Example:

var Foo = ObjectInitializer({
  a: 3,
  b: 2,
  initialize: function() {
    this.b += this.a;
  },
  increase: function() {
    this.b++;
    return this;
  }
});

var foo = Foo();
alert(foo.b); // 5
foo.increase();
alert(foo.b); // 6
var bar = Foo();
alert(bar.increase().b); // 7
alert(Foo().increase().b); // 8

Recursive Backbone.Model’s toJSON()

Posted: July 18th, 2011 | Author: | Filed under: Javascript, Web Development | Tags: , , , , | No Comments »

If you’re using collections and models as attributes for other models, calling toJSON() on Backbone.Model objects isn’t very useful as it returns the models and collections as-is. I’ve opened an issue requesting to make it recursive, but it seems like its not going to be added.

If anyone still wants this functionality, you can use that modified version of Backbone.Model.prototype.toJSON that I added to the issue:

Backbone.Model.prototype.toJSON = function() {
    if (this._isSerializing) {
        return this.id || this.cid;
    }
    this._isSerializing = true;
    var json = _.clone(this.attributes);
    _.each(json, function(value, name) {
        _.isFunction(value.toJSON) && (obj[name] = value.toJSON());
    });
    this._isSerializing = false;
    return json;
}

jQuery :focus selector expression

Posted: September 22nd, 2010 | Author: | Filed under: Javascript, jQuery, Web Development | Tags: , , , , , | No Comments »

Just a quick code snippet I wrote for someone on IRC: (should work, but wasn’t really tested)

// Support for browsers that doesn't set document.activeElement
if (typeof document.activeElement === 'undefined') {
	$(document)
		.focusin(function(e){ document.activeElement = e.target; })
		.focusout(function(){ document.activeElement = null; });
}
// Adding a ':focus' expression
jQuery.expr[':'].focus = function(a){ return a === document.activeElement; }

You can use it like that:

$('input:focus'); // Find a focused input.
$(...).is(':focus'); // Test whether an element is focused or not.
$(document.activeElement); // Get the currently focused element as a jQuery object.
$(':focus'); // Same as last one, but MUCH MUCH slower and resource intensive.

jQuery-UI multicomplete widget (based on autocomplete)

Posted: July 11th, 2010 | Author: | Filed under: Javascript, jQuery, Web Development | Tags: , , , , , | 5 Comments »

After writing my $.fn.autocomplete function to allow using jQuery’s UI autocomplete with multiple selections, I’ve rewritten it as a widget that extends the autocomplete widget. Basically, it works the exact same as autocomplete – other than allowing to select multiple values separated with a comma.

A few things were improved/changed in that version:

  • All options are settable, including all the callbacks that I’m modifying. your callbacks will be called first, and returning false from them will have effect.
  • All methods/getters/setters are available via multicomplete() (e.g. $(..).multicomplete(‘option’, ‘source’, ['foo', 'bar'])). When using multicomplete, setting/getting them via .autocomplete() will no longer work.
  • Events are prefixed with multicomplete instead of autocomplete (e.g. $(..).bind(‘multicompletesearch’, function(){…})). Using “autocomplete” as the prefix won’t work.
  • When you pass an array as the source, items that were already selected won’t show up in the autocomplete list.
  • When you pass a URL as the source, a new parameter named “selected” is passed in the AJAX request so your server-side code can know which items are already selected and not return them.

It should be noted that most of the handling is still done with jQuery-UI’s autocomplete, my multicomplete code just extends that widget and changes a few things. Everything should work as expected, and I don’t foresee any issues when new versions of jQuery UI are out – unless they make radical changes.

So here’s the demo (only works on the single post page):

Source code of demo:

$('#suggest').multicomplete({
	source: ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "perl"]
});

Source code of the multicomplete widget:

(function($ ,$a, $p){ // getting $p as a parameter doesn't require me to "var $p=..." and saves a two bytes ;-)  ("var " versus ",x" in argument list [when minifier is shrinking variables])
	$p=$a.prototype;
	$.widget('ui.multicomplete', $a, {
		// Search for everything after the last "," instead of the whole input contents
		_search: function(value){
			$p._search.call(this, value.match(/s*([^,]*)s*$/)[1]);
		},
		// Overwrite _trigger to make custom handling for events while still allowing user callbacks
		// Setting my own callbacks on this.options or binding using .bind() doesn't allow me to properly handle user callbacks, as this must be called AFTER the user callbacks are executed (which isn't possible by bind()ing when this.options[] is set)
		_trigger: function(type, event, data) {
			// call "real" triggers
			var ret = $p._trigger.apply(this, arguments);
			// When its select event, and user callback didn't return FALSE, do my handling and return false
			if (type == 'select' && ret !== false) {
				// When a selection is made, replace everything after the last "," with the selection instead of replacing everything
				var val=this.element.val();
				this.element.val(val.replace(/[^,]+$/,(val.indexOf(',') != -1 ?' ':'')+data.item.value + ', '));
				ret = false;
			}
			// Force false when its the focus event - parent should never set the value on focus
			return (type == 'focus' ? false : ret);
		},
		_create:function(){
			var self=this;
			// When menu item is selected and TAB is pressed focus should remain on current element to allow adding more values
			this.element.keydown(function(e){
				self.menu.active && e.keyCode == $.ui.keyCode.TAB && e.preventDefault();
			});
			$p._create.call(this);
		},
		_initSource: function() {
			// Change the way arrays are handled by making autocomplete think the user sent his own source callback instead of an array
			// The way autocomplete is written doesn't allow me to do it in a prettier way :(
			if ( $.isArray(this.options.source) ) {
				var array = this.options.source, self = this;
				this.options.source = function( request, response ) {
					response( self.filter(array, request) ); // Use our filter() and pass the entire request object so the filter can tell what's currently selected
				};
			}

			// call autocomplete._initSource to create this.source function according to user input
			$p._initSource.call(this);

			// Save a copy of current source() function, than new source() sets request.selected and delegate to original source
			var _source = this.source;
			this.source = function(request, response) {
				request.selected = this.element.val().split(/s*,s*/);
				request.selected.pop(); // don't include the term the user is currently writing as selected
				_source(request, response);
			};
			// TODO: instead of overwritting this.source, I can overwrite _search which is easier, but than I'll have to repeat jQuery-UI's code that might change
		},

		// Like $.ui.autocomplete.filter, but excludes items that are already selected
		filter: function(array, request) {
			return $.grep($a.filter(array, request.term),function(value){return $.inArray(value, request.selected) == -1;});
		}
	});
})(jQuery, jQuery.ui.autocomplete);

Minified:
(function($,f,g){g=f.prototype;$.widget(‘ui.multicomplete’,f,{_search:function(a){g._search.call(this,a.match(/s*([^,]*)s*$/)[1])},_trigger:function(a,b,c){var d=g._trigger.apply(this,arguments);if(a==’select’&&d!==false){var e=this.element.val();this.element.val(e.replace(/[^,]+$/,(e.indexOf(‘,’)!=-1?’ ‘:”)+c.item.value+’, ‘));d=false}return(a==’focus’?false:d)},_create:function(){var a=this;this.element.keydown(function(e){a.menu.active&&e.keyCode==$.ui.keyCode.TAB&&e.preventDefault()});g._create.call(this)}, _initSource:function(){if($.isArray(this.options.source)){var c=this.options.source,self=this;this.options.source=function(a,b){b(self.filter(c,a))}}g._initSource.call(this);var d=this.source;this.source=function(a,b){a.selected=this.element.val().split(/s*,s*/);a.selected.pop();d(a,b)}},filter:function(b,c){return $.grep(f.filter(b,c.term),function(a){return $.inArray(a,c.selected)==-1})}})})(jQuery,jQuery.ui.autocomplete);

Download: Full code with comments (3141 bytes) or Minified code (932 bytes)

Released under the MIT and GPL licenses.

P.S. I would like to thank ajpiano from #jquery (at freenode) for suggesting me to write this as a widget. I’ve learned a few things on jQuery/jQuery-UI in the process and I’ll definitely start writing some of my code as widgets, I really like the way they works.

Read the rest of this entry »


jQuery-UI autocomplete with multiple selections

Posted: July 10th, 2010 | Author: | Filed under: Javascript, jQuery, Web Development | Tags: , , , | 2 Comments »

For one of our projects we needed to have an auto-complete input field with multiple selections, separated with a comma.

I found quite a few plugins that can do that, but preferred to use the autocomplete feature that’s built-in to jQuery UI. To get it working, all that’s needed is modifying some events/callbacks (select, focus, source and keydown).

I’ve wrapped everything as $.fn.multiselect, that modifies what’s needed and lets the built-in autocomplete handle the rest.

You can pass it the same options as you would pass to autocomplete(), other than “select” and “focus” which are overwritten by multicomplete. It should be noted that you have to work with autocomplete() if you want to use their methods/getters/setter (e.g. $(..).autocomplete( “option”, “delay”, 500 ) or $(..).autocomplete(“close”);)

Update: this was rewritten as a widget. That version works too, but has some limitations. you should probably use the widget version instead.

You can see it in action here (try typing “a”):
(The demo only works when viewing the single-post page)

Source code of demo:

$('#suggest').multicomplete({
	source: ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "perl"]
});

Source code for $.fn.multicomplete:

(function($){
	$.fn.multicomplete = function(opt) {
		var $t = $(this);
		// When menu item is selected and TAB is pressed, focus should remain on current element to allow adding more values
		$t.bind('keydown', function(e) {
			if ($t.data('autocomplete').menu.active && e.keyCode == $.ui.keyCode.TAB) {
				e.preventDefault();
			}
		});

		// Call autocomplete() with our modified select/focus callbacks
		$t.autocomplete($.extend(opt,{
			// When a selection is made, replace everything after the last "," with the selection instead of replacing everything
			select: function(event,ui) {
				this.value = this.value.replace(/[^,]+$/,(this.value.indexOf(',') != -1 ?' ':'')+ui.item.value + ', ');
				return false;
			},
			// Disable replacing value on focus
			focus: function(){return false;}
		}));

		// Get the "source" callback that jQuery-UI prepared
		var $source = $t.data('autocomplete').source;

		// Modify the source callback to change request.term to everything after the last ",", than delegate to $source
		$t.autocomplete('option', 'source', function(request, response) {
			request.term = request.term.match(/s*([^,]*)s*$/)[1]; // get everything after the last "," and trim it
			$source(request, response);
		});
	};
})(jQuery);

Minified:
(function($){$.fn.multicomplete=function(c){var d=$(this);d.bind(‘keydown’,function(e){if(d.data(‘autocomplete’).menu.active&&e.keyCode==$.ui.keyCode.TAB){e.preventDefault()}}); d.autocomplete($.extend(c,{select:function(a,b){this.value=this.value.replace(/[^,]+$/,(this.value.indexOf(‘,’)!=-1?’ ‘:”)+b.item.value+’, ‘);return false},focus:function(){return false}}));var f=d.data(‘autocomplete’).source;d.autocomplete(‘option’,'source’,function(a,b){a.term=a.term.match(/s*([^,]*)s*$/)[1];f(a,b)})}})(jQuery);

Download: Full code with comments (1263 bytes) or Minified code (512 bytes)

Released under the MIT and GPL licenses.
Read the rest of this entry »


Greasemonkey user script: Experts Exchange

Posted: December 25th, 2009 | Author: | Filed under: Greasemonkey scripts, Javascript | Tags: | No Comments »

For those of you who doesn’t know, experts-exchange.com usually charges money for viewing solutions, but if you come from Google search results you can view them for free (my guess is they wanted the text in the answers to get indexed, in order to get extra traffic from search keywords that only appear in the answers and not in the questions. showing the answers only to Google bot would be considered cloaking and harm the user experience, so they do it in order to avoid getting banned from Google).

The only problem is that they show the answers after a long long long list of categories, and you have to scroll down a lot in order to see them.

I wrote a small (3 lines) greasemonkey script to remove all the junk and make it more usable by hiding the categories list, the fake hidden answers and let you see the content you really want to see:

Download / Install / View source

You need to be using Firefox and have the Greasemonkey extension installed (direct link to .xpi file) to use this.

The code is those 3 lines:

var ans = document.getElementsByClassName('answers');
ans[0].parentNode.replaceChild(ans[1],ans[0]);
for (var c in {'squareSignUp':1,'qStats':1,'relatedSolutions':1,'allZonesMain':1,'lightImage':1})document.getElementsByClassName(c)[0].parentNode.removeChild(document.getElementsByClassName(c)[0]);

Simple and quite useful.


Greasemonkey user script: Better torrentz.com

Posted: December 25th, 2009 | Author: | Filed under: Greasemonkey scripts, Javascript | Tags: , | 2 Comments »

I recently started downloading a TV show using torrentz.com, and got really annoyed at how hard it is to download every episode.

You have to search for it, open the torrentz.com page for it, pick one of the trackers, go to the tracker site,  download the .torrent file from there, go back to torrentz.com page, click the “µTorrent compatible list” link, copy the trackers list from there to utorrent, and go back to search for the next episode.

I see no reason not to have everything directly on the torrentz.com page for the torrent, so I created a Greasemonkey script to to do just that.

The script changes (some of the) links to the trackers page in direct links to the .torrent file (while hiding the referrer, because some trackers didn’t like other sites linking directly to the .torrent file), the direct links to .torrent files are changed to be colored in black.

It also adds a big “download .torrent” link above the links to the trackers, with the first .torrent file it finds and changes the (useless) list of trackers URLs with a textarea that you can copy directly to uTorrent using some AJAX.

I find it quite useful and time saving, and thought someone out there might like it too:

Download / Install / View source

You need to be using Firefox and have the Greasemonkey extension installed (direct link to .xpi file) to use this.