// David Mevthin's overlabel plugin
// http://scott.sauyet.com/thoughts/archives/2007/03/31/overlabel-with-jquery/#comment-24144
// class overlabel-apply should have position: absolute and left: appropriate for the item
(function($){
$.fn.overlabel = function() {
  this.each(function(){
    var label = $(this);
    $("#"+(this.htmlFor || label.attr('for') || "ID-NOT-FOUND"))
      .focus(function(){ label.css("text-indent", "-9999px"); })
      .blur(function(){ label.css ("text-indent", this.value ? "-9999px" : "0px"); })
      .trigger("blur")
      .length && label.addClass("overlabel-apply");
  });
}
})(jQuery);;
// Cross-broswer implementation of text ranges and selections
// documentation: http://bililite.com/blog/2011/01/11/cross-browser-and-selections/
// Version: 1.1
// Copyright (c) 2010 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

(function(){

bililiteRange = function(el, debug){
	var ret;
	if (debug){
		ret = new NothingRange(); // Easier to force it to use the no-selection type than to try to find an old browser
	}else if (document.selection){
		// Internet Explorer
		ret = new IERange();
	}else if (window.getSelection && el.setSelectionRange){
		// Standards. Element is an input or textarea 
		ret = new InputRange();
	}else if (window.getSelection){
		// Standards, with any other kind of element
		ret = new W3CRange()
	}else{
		// doesn't support selection
		ret = new NothingRange();
	}
	ret._el = el;
	ret._textProp = textProp(el);
	ret._bounds = [0, ret.length()];
	return ret;
}

function textProp(el){
	// returns the property that contains the text of the element
	if (typeof el.value != 'undefined') return 'value';
	if (typeof el.text != 'undefined') return 'text';
	if (typeof el.textContent != 'undefined') return 'textContent';
	return 'innerText';
}

// base class
function Range(){}
Range.prototype = {
	length: function() {
		return this._el[this._textProp].replace(/\r/g, '').length; // need to correct for IE's CrLf weirdness
	},
	bounds: function(s){
		if (s === 'all'){
			this._bounds = [0, this.length()];
		}else if (s === 'start'){
			this._bounds = [0, 0];
		}else if (s === 'end'){
			this._bounds = [this.length(), this.length()];
		}else if (s === 'selection'){
			this.bounds ('all'); // first select the whole thing for constraining
			this._bounds = this._nativeSelection();
		}else if (s){
			this._bounds = s; // don't error check now; the element may change at any moment, so constrain it when we need it.
		}else{
			var b = [
				Math.max(0, Math.min (this.length(), this._bounds[0])),
				Math.max(0, Math.min (this.length(), this._bounds[1]))
			];
			return b; // need to constrain it to fit
		}
		return this; // allow for chaining
	},
	select: function(){
		this._nativeSelect(this._nativeRange(this.bounds()));
		return this; // allow for chaining
	},
	text: function(text, select){
		if (arguments.length){
			this._nativeSetText(text, this._nativeRange(this.bounds()));
			if (select == 'start'){
				this.bounds ([this._bounds[0], this._bounds[0]]);
				this.select();
			}else if (select == 'end'){
				this.bounds ([this._bounds[0]+text.length, this._bounds[0]+text.length]);
				this.select();
			}else if (select == 'all'){
				this.bounds ([this._bounds[0], this._bounds[0]+text.length]);
				this.select();
			}
			return this; // allow for chaining
		}else{
			return this._nativeGetText(this._nativeRange(this.bounds()));
		}
	},
	insertEOL: function (){
		this._nativeEOL();
		this._bounds = [this._bounds[0]+1, this._bounds[0]+1]; // move past the EOL marker
		return this;
	}
};


function IERange(){}
IERange.prototype = new Range();
IERange.prototype._nativeRange = function (bounds){
	var rng;
	if (this._el.tagName == 'INPUT'){
		// IE 8 is very inconsistent; textareas have createTextRange but it doesn't work
		rng = this._el.createTextRange();
	}else{
		rng = document.body.createTextRange ();
		rng.moveToElementText(this._el);
	}
	if (bounds){
		if (bounds[1] < 0) bounds[1] = 0; // IE tends to run elements out of bounds
		if (bounds[0] > this.length()) bounds[0] = this.length();
		if (bounds[1] < rng.text.replace(/\r/g, '').length){ // correct for IE's CrLf wierdness
			// block-display elements have an invisible, uncounted end of element marker, so we move an extra one and use the current length of the range
			rng.moveEnd ('character', -1);
			rng.moveEnd ('character', bounds[1]-rng.text.replace(/\r/g, '').length);
		}
		if (bounds[0] > 0) rng.moveStart('character', bounds[0]);
	}
	return rng;					
};
IERange.prototype._nativeSelect = function (rng){
	rng.select();
};
IERange.prototype._nativeSelection = function (){
	// returns [start, end] for the selection constrained to be in element
	var rng = this._nativeRange(); // range of the element to constrain to
	var len = this.length();
	if (document.selection.type != 'Text') return [len, len]; // append to the end
	var sel = document.selection.createRange();
	try{
		return [
			iestart(sel, rng),
			ieend (sel, rng)
		];
	}catch (e){
		// IE gets upset sometimes about comparing text to input elements, but the selections cannot overlap, so make a best guess
		return (sel.parentElement().sourceIndex < this._el.sourceIndex) ? [0,0] : [len, len];
	}
};
IERange.prototype._nativeGetText = function (rng){
	return rng.text.replace(/\r/g, ''); // correct for IE's CrLf weirdness
};
IERange.prototype._nativeSetText = function (text, rng){
	rng.text = text;
};
IERange.prototype._nativeEOL = function(){
	if (typeof this._el.value != 'undefined'){
		this.text('\n'); // for input and textarea, insert it straight
	}else{
		this._nativeRange(this.bounds()).pasteHTML('<br/>');
	}
};
// IE internals
function iestart(rng, constraint){
	// returns the position (in character) of the start of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after
	var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness
	if (rng.compareEndPoints ('StartToStart', constraint) <= 0) return 0; // at or before the beginning
	if (rng.compareEndPoints ('StartToEnd', constraint) >= 0) return len;
	for (var i = 0; rng.compareEndPoints ('StartToStart', constraint) > 0; ++i, rng.moveStart('character', -1));
	return i;
}
function ieend (rng, constraint){
	// returns the position (in character) of the end of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after
	var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness
	if (rng.compareEndPoints ('EndToEnd', constraint) >= 0) return len; // at or after the end
	if (rng.compareEndPoints ('EndToStart', constraint) <= 0) return 0;
	for (var i = 0; rng.compareEndPoints ('EndToStart', constraint) > 0; ++i, rng.moveEnd('character', -1));
	return i;
}

// an input element in a standards document. "Native Range" is just the bounds array
function InputRange(){}
InputRange.prototype = new Range();
InputRange.prototype._nativeRange = function(bounds) {
	return bounds || [0, this.length()];
};
InputRange.prototype._nativeSelect = function (rng){
	this._el.setSelectionRange(rng[0], rng[1]);
};
InputRange.prototype._nativeSelection = function(){
	return [this._el.selectionStart, this._el.selectionEnd];
};
InputRange.prototype._nativeGetText = function(rng){
	return this._el.value.substring(rng[0], rng[1]);
};
InputRange.prototype._nativeSetText = function(text, rng){
	var val = this._el.value;
	this._el.value = val.substring(0, rng[0]) + text + val.substring(rng[1]);
};
InputRange.prototype._nativeEOL = function(){
	this.text('\n');
};

function W3CRange(){}
W3CRange.prototype = new Range();
W3CRange.prototype._nativeRange = function (bounds){
	var rng = document.createRange();
	rng.selectNodeContents(this._el);
	if (bounds){
		w3cmoveBoundary (rng, bounds[0], true, this._el);
		rng.collapse (true);
		w3cmoveBoundary (rng, bounds[1]-bounds[0], false, this._el);
	}
	return rng;					
};
W3CRange.prototype._nativeSelect = function (rng){
	window.getSelection().removeAllRanges();
	window.getSelection().addRange (rng);
};
W3CRange.prototype._nativeSelection = function (){
		// returns [start, end] for the selection constrained to be in element
		var rng = this._nativeRange(); // range of the element to constrain to
		if (window.getSelection().rangeCount == 0) return [this.length(), this.length()]; // append to the end
		var sel = window.getSelection().getRangeAt(0);
		return [
			w3cstart(sel, rng),
			w3cend (sel, rng)
		];
	}
W3CRange.prototype._nativeGetText = function (rng){
	return rng.toString();
};
W3CRange.prototype._nativeSetText = function (text, rng){
	rng.deleteContents();
	rng.insertNode (document.createTextNode(text));
	this._el.normalize(); // merge the text with the surrounding text
};
W3CRange.prototype._nativeEOL = function(){
	var rng = this._nativeRange(this.bounds());
	rng.deleteContents();
	var br = document.createElement('br');
	br.setAttribute ('_moz_dirty', ''); // for Firefox
	rng.insertNode (br);
	rng.insertNode (document.createTextNode('\n'));
	rng.collapse (false);
};
// W3C internals
function nextnode (node, root){
	//  in-order traversal
	// we've already visited node, so get kids then siblings
	if (node.firstChild) return node.firstChild;
	if (node.nextSibling) return node.nextSibling;
	if (node===root) return null;
	while (node.parentNode){
		// get uncles
		node = node.parentNode;
		if (node == root) return null;
		if (node.nextSibling) return node.nextSibling;
	}
	return null;
}
function w3cmoveBoundary (rng, n, bStart, el){
	// move the boundary (bStart == true ? start : end) n characters forward, up to the end of element el. Forward only!
	// if the start is moved after the end, then an exception is raised
	if (n <= 0) return;
	var node = rng[bStart ? 'startContainer' : 'endContainer'];
	if (node.nodeType == 3){
	  // we may be starting somewhere into the text
	  n += rng[bStart ? 'startOffset' : 'endOffset'];
	}
	while (node){
		if (node.nodeType == 3){
			if (n <= node.nodeValue.length){
				rng[bStart ? 'setStart' : 'setEnd'](node, n);
				// special case: if we end next to a <br>, include that node.
				if (n == node.nodeValue.length){
					// skip past zero-length text nodes
					for (var next = nextnode (node, el); next && next.nodeType==3 && next.nodeValue.length == 0; next = nextnode(next, el)){
						rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
					}
					if (next && next.nodeType == 1 && next.nodeName == "BR") rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
				}
				return;
			}else{
				rng[bStart ? 'setStartAfter' : 'setEndAfter'](node); // skip past this one
				n -= node.nodeValue.length; // and eat these characters
			}
		}
		node = nextnode (node, el);
	}
}
var     START_TO_START                 = 0; // from the w3c definitions
var     START_TO_END                   = 1;
var     END_TO_END                     = 2;
var     END_TO_START                   = 3;
// from the Mozilla documentation, for range.compareBoundaryPoints(how, sourceRange)
// -1, 0, or 1, indicating whether the corresponding boundary-point of range is respectively before, equal to, or after the corresponding boundary-point of sourceRange. 
    // * Range.END_TO_END compares the end boundary-point of sourceRange to the end boundary-point of range.
    // * Range.END_TO_START compares the end boundary-point of sourceRange to the start boundary-point of range.
    // * Range.START_TO_END compares the start boundary-point of sourceRange to the end boundary-point of range.
    // * Range.START_TO_START compares the start boundary-point of sourceRange to the start boundary-point of range. 
function w3cstart(rng, constraint){
	if (rng.compareBoundaryPoints (START_TO_START, constraint) <= 0) return 0; // at or before the beginning
	if (rng.compareBoundaryPoints (END_TO_START, constraint) >= 0) return constraint.toString().length;
	rng = rng.cloneRange(); // don't change the original
	rng.setEnd (constraint.endContainer, constraint.endOffset); // they now end at the same place
	return constraint.toString().length - rng.toString().length;
}
function w3cend (rng, constraint){
	if (rng.compareBoundaryPoints (END_TO_END, constraint) >= 0) return constraint.toString().length; // at or after the end
	if (rng.compareBoundaryPoints (START_TO_END, constraint) <= 0) return 0;
	rng = rng.cloneRange(); // don't change the original
	rng.setStart (constraint.startContainer, constraint.startOffset); // they now start at the same place
	return rng.toString().length;
}

function NothingRange(){}
NothingRange.prototype = new Range();
NothingRange.prototype._nativeRange = function(bounds) {
	return bounds || [0,this.length()];
};
NothingRange.prototype._nativeSelect = function (rng){ // do nothing
};
NothingRange.prototype._nativeSelection = function(){
	return [0,0];
};
NothingRange.prototype._nativeGetText = function (rng){
	return this._el[this._textProp].substring(rng[0], rng[1]);
};
NothingRange.prototype._nativeSetText = function (text, rng){
	var val = this._el[this._textProp];
	this._el[this._textProp] = val.substring(0, rng[0]) + text + val.substring(rng[1]);
};
NothingRange.prototype._nativeEOL = function(){
	this.text('\n');
};

})();;
// insert characters in a textarea or text input field
// special characters are enclosed in {}; use {{} for the { character itself
// documentation: http://bililite.com/blog/2008/08/20/the-fnsendkeys-plugin/
// Version: 2.0
// Copyright (c) 2010 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

(function($){

$.fn.sendkeys = function (x, opts){
	return this.each( function(){
		var localkeys = $.extend({}, opts, $(this).data('sendkeys')); // allow for element-specific key functions
		// most elements to not keep track of their selection when they lose focus, so we have to do it for them
		var rng = $.data (this, 'sendkeys.selection');
		if (!rng){
			rng = bililiteRange(this).bounds('selection');
			$.data(this, 'sendkeys.selection', rng);
			$(this).bind('mouseup.sendkeys', function(){
				// we have to update the saved range. The routines here update the bounds with each press, but actual keypresses and mouseclicks do not
				$.data(this, 'sendkeys.selection').bounds('selection');
			}).bind('keyup.sendkeys', function(evt){
				// restore the selection if we got here with a tab (a click should select what was clicked on)
				if (evt.which == 9){
					// there's a flash of selection when we restore the focus, but I don't know how to avoid that.
					$.data(this, 'sendkeys.selection').select();
				}else{
					$.data(this, 'sendkeys.selection').bounds('selection');
				}	
			});
		}
		this.focus();
		if (typeof x === 'undefined') return; // no string, so we just set up the event handlers
		x.replace(/\n/g, '{enter}'). // turn line feeds into explicit break insertions
		  replace(/{[^}]*}|[^{]+/g, function(s){
			(localkeys[s] || $.fn.sendkeys.defaults[s] || $.fn.sendkeys.defaults.simplechar)(rng, s);
		  });
		$(this).trigger({type: 'sendkeys', which: x});
	});
}; // sendkeys


// add the functions publicly so they can be overridden
$.fn.sendkeys.defaults = {
	simplechar: function (rng, s){
		rng.text(s, 'end');
		for (var i =0; i < s.length; ++i){
			var x = s.charCodeAt(i);
			// a bit of cheating: rng._el is the element associated with rng.
			$(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x});
		}
	},
	'{{}': function (rng){
		$.fn.sendkeys.defaults.simplechar (rng, '{')
	},
	'{enter}': function (rng){
		rng.insertEOL();
		var b = rng.bounds();
		rng.select();
		var x = '\n'.charCodeAt(0);
		$(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x});
	},
	'{backspace}': function (rng){
		var b = rng.bounds();
		if (b[0] == b[1]) rng.bounds([b[0]-1, b[0]]); // no characters selected; it's just an insertion point. Remove the previous character
		rng.text('', 'end'); // delete the characters and update the selection
	},
	'{del}': function (rng){
		var b = rng.bounds();
		if (b[0] == b[1]) rng.bounds([b[0], b[0]+1]); // no characters selected; it's just an insertion point. Remove the next character
		rng.text('', 'end'); // delete the characters and update the selection
	},
	'{rightarrow}':  function (rng){
		var b = rng.bounds();
		if (b[0] == b[1]) ++b[1]; // no characters selected; it's just an insertion point. Move to the right
		rng.bounds([b[1], b[1]]).select();
	},
	'{leftarrow}': function (rng){
		var b = rng.bounds();
		if (b[0] == b[1]) --b[0]; // no characters selected; it's just an insertion point. Move to the left
		rng.bounds([b[0], b[0]]).select();
	},
	'{selectall}' : function (rng){
		rng.bounds('all').select();
	}
};

})(jQuery);
// Copyright (c) 2009 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
(function($){
// create the master widget
$.widget("ui.widget",{
	// Aspect Oriented Programming tools from Justin Palmer's article
	yield: null,
	returnValues: { },
	before: function(method, f) {
		var original = this[method];
		this[method] = function() {
			f.apply(this, arguments);
			return original.apply(this, arguments);
		};
	},
	after: function(method, f) {
		var original = this[method];
		this[method] = function() {
			this.returnValues[method] = original.apply(this, arguments);
			return f.apply(this, arguments);
		}
	},
	around: function(method, f) {
		var original = this[method];
		this[method] = function() {
			var tmp = this.yield;
			this.yield = original;
			var ret = f.apply(this, arguments);
			this.yield = tmp;
			return ret;
		}
	}
});

// from http://groups.google.com/group/comp.lang.javascript/msg/e04726a66face2a2 and
// http://webreflection.blogspot.com/2008/10/big-douglas-begetobject-revisited.html
var object = (function(F){
	return (function(o){
			F.prototype = o;
			return new F();
	});
})(function (){});

// create a widget subclass
var OVERRIDE = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; 
$.ui.widget.subclass = function subclass(name){
	$.widget(name); // Slightly inefficient to create a widget only to discard its prototype, but it's not too bad
	name = name.split('.');
	var widget = $[name[0]][name[1]], superclass = this, superproto = superclass.prototype;
	
	
	var proto = arguments[0] = widget.prototype = object(superproto); // inherit from the superclass
	$.extend.apply(null, arguments); // and add other add-in methods to the prototype
	widget.subclass = subclass;

	// Subtle point: we want to call superclass init and destroy if they exist
	// (otherwise the user of this function would have to keep track of all that)
	for (key in proto) if (proto.hasOwnProperty(key)) switch (key){
		case '_create':
			var create = proto._create;
			proto._create = function(){
				superproto._create.apply(this);
				create.apply(this);
			};
		break;
		case '_init':
			var init = proto._init;
			proto._init = function(){
				superproto._init.apply(this);
				init.apply(this);
			};
		break;
		case 'destroy':
			var destroy = proto.destroy;
			proto.destroy = function(){
				destroy.apply(this);
				superproto.destroy.apply(this);
			};
		break;
		case 'options':
			var options = proto.options;
			proto.options = $.extend ({}, superproto.options, options);
		break;
		default:
			if ($.isFunction(proto[key]) && $.isFunction(superproto[key]) && OVERRIDE.test(proto[key])){
				proto[key] = (function(name, fn){
					return function() {
						var tmp = this._super;
						this._super = superproto[name];
						try { var ret = fn.apply(this, arguments); }   
						finally { this._super = tmp; }					
						return ret;
					};
				})(key, proto[key]);
			}
		break;
	}
};
})(jQuery);
๏ปฟ// textpopup and hebrew keyboard widgets
// Version: 1.4
// dependencies: jquery.ui.subclass.js (mine), ui.core.js, effects.core.js (from jQuery UI 1.8)
// Copyright (c) 2010 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

// Modified: 2010-11-23

(function($){
	$.ui.widget.subclass('ui.textpopup', {
		_init: function(){
			var self = this;
			this._hideOnOutsideClick(this.options.hideOnOutsideClick);
			// if options.position is an object suitable for passing to $.fn.position (field 'my' is defined) then use it; otherwise use the string shortcuts
			// if we use the string shortcuts, make sure we make a copy rather than changing the original
			this._position = this.options.position.my ? this.options.position : $.extend({},position[this.options.position]);
			// the position should usually be relative to the containing box or the element, but the user can override that.
			if (!this._position.of) this._position.of = this.options.box || this.element;
			if (!this._position.collision) this._position.collision = 'none';
			// turn the speed into an array to be used with Function.apply
			this._speed = $.isArray(this.options.speed) ? this.options.speed : [this.options.speed];
			var trigger = this.options.trigger;
			if (trigger == 'self'){
				trigger = this.element;
			}
			if (this._triggerElement) $(trigger).unbind('.textpopup'); // don't forget to remove the old bindings 
			// note that for elements that can get focus, self.show will be called twice (but we test for :visible so it doesn't animate twice)
			if (trigger) this._triggerElement = $(trigger).bind('focus.textpopup click.textpopup', function (){
				self.show(); 
				// allow the default behavior
			});
			// bug inducing note: this._trigger is the function, this._triggerElement is the element
		},
		position: function(){
			var display = this.box().css('display');
			this.box().css({display: 'block', visibility: 'hidden'}).
				position(this._position).
				css({display: display, visibility: 'visible'});
		},
		show: function(){
			// See http://wiki.codetalks.org/wiki/index.php/Docs/Keyboard_navigable_JS_widgets for manipulating tabindex
			var self = this, box = self.box().attr('tabindex', 0);
			if (box.is(':visible, :animated')) return;
			self.position();
			self.options.show.apply(box, this._speed);
			box.queue(function(){self._trigger('shown'); box.dequeue()});
		},
		hide: function(){
			// having a hidden box with a tabindex bothers the browser to no end
			var self = this, box = self.box().removeAttr('tabindex');
			if (box.is(':hidden')) return;
			self.options.hide.apply(box, this._speed);
			box.queue(function(){self._trigger('hidden'); box.dequeue()});
		},
		box: function(){
			// lazy create
			return this.theBox || this._createBox();
		},
		_createBox: function(){
			var self = this;
			var css = this.options.box ? {display: 'inline-block'} : {position: 'absolute', display: 'none'};
			var box = $('<div/>').
				appendTo(this.options.box || 'body').
				css(css).
				addClass(this.options['class']).
				keydown(function(e) {
					if (e.keyCode == $.ui.keyCode.ESCAPE) {
						self.element.focus();
						if (self.options.hideOnOutsideClick) self.hide();
					}
				});
			this.theBox = box;
			box.data('textpopup', this);
			this._fill(box);
			this._trigger('created', 0, box);
			return box;
		},
		_fill: function(box){
			// virtual method to put something in the box
		},
		// hides the box for any click outside it. fails for clicks in textboxes, since the click does not bubble up to the body
		_hideOnOutsideClick: function(flag){
			var self = this;
			var $body = $('body');
			if (flag){
				var hider = function(e){ if(!self._isClickInside(e)) self.hide(); };
				this.after('show', function(){
					$body.unbind('click.textpopup', hider); // we don't want to double hide
					$body.bind('click.textpopup', hider);
				});
				this.before('hide', function(){
					$body.unbind('click.textpopup', hider);
				});
			}else{
				$body.unbind ('click.textpopup');
			}
		},
		destroy: function() {
			this.box().remove();
			if (this._triggerElement) this._triggerElement.unbind ('.textpopup');
			$('body').unbind('.textpopup');
			this.theBox = undefined;
		},
		// returns true if the event e is a click inside the box , the original element or the triggering elements
		_setOption: function(key, value) {
			this._super(key, value);
			if (key == 'trigger' || 'hideOnOutsideClick' || 'position' || 'speed') this._init;
			if (key == 'class') this.box().attr('class', value);
		},
		_isClickInside: function(e){
			var keepers = $([]).add(this._triggerElement).add(this.box()).add(this.element);
			for (var elem = e.target; elem; elem = elem.parentNode) if (keepers.index(elem) > -1) return true;
			return false;
		},
		options: {
			show: $.fn.show,
			hide: $.fn.hide,
			speed: 'slow',
			hideOnOutsideClick: true,
			position: 'tl',
			trigger: 'self',
			'class': 'ui-textpopup-box'
		}
	});

	// position for the textpopup relative to the input box. rt means right side, aligned to top; tr means top side, aligned to right
	var position = {
		tl: {my: 'left bottom', at: 'left top'},
		tr: {my: 'right bottom ', at: 'right top'},
		bl: {my: 'left top', at: 'left bottom'},
		br: {my: 'right top', at: 'right bottom'},
		lt: {my: 'right top', at: 'left top'},
		rt: {my: 'left top', at: 'right top'},
		lb: {my: 'right bottom', at: 'left bottom'},
		rb: {my: 'left bottom', at: 'right bottom'}
	};
	
	// a textpopup that loads its HTML from an external (Ajax) , fixed file (it's loaded once when needed at first, then saved)
	// defaults.url must be defined
	// includes a hack to allow data: urls even in IE (checks the url for 'data:,' then treats it specially)
	$.ui.textpopup.subclass('ui.ajaxpopup', {
		_html: undefined, // lazy load the code
		_fill: function(box){
			var self = this;
			if (!self._html){
				box.append($(self.options.busy));
				var url = self.options.url;
				if (/^data:[^,]*,/.test(url)){
					setHTML(decodeURIComponent(url.replace(/^data:[^,]*,/, '')));
				}else{
					$.get(url, setHTML, 'text');
				}
			}else{
				box.html(self._html);
			}
			function setHTML(data){
				self._html = data.replace(/<style(\S|\s)*style>/, function(style){
					$('head').append(style); // styles only go in the head, and don't need to be appended more than once
					return '';
				});
				box.html(self._html);
				if (box.is(':animated, :visible')){ // restart the effect
					box.stop(true, true).hide();
					self.show();
				}
			}
		},
		options: {
			busy: '<img src="http://bililite.com/images/busy/wait22.gif" />'
		}
	});
	
	var keymap = {
		81: '"',
		87: "'",
		69: 'ืง',
		82: 'ืจ',
		84: 'ื',
		89: 'ื',
		85: 'ื',
		73: 'ื',
		79: 'ื',
		80: 'ืค',
		91: ':',
		93: ';',
		65: 'ืฉ',
		83: 'ื',
		68: 'ื',
		70: 'ื',
		71: 'ืข',
		72: 'ื',
		74: 'ื',
		75: 'ื',
		76: 'ื',
		59: 'ืฃ',
		39: ',',
		90: 'ื',
		88: 'ืก',
		67: 'ื',
		86: 'ื',
		66: 'ื ',
		78: 'ื',
		77: 'ืฆ',
		44: 'ืช',
		46: 'ืฅ',
		47: '.'
	};
	// add the lower cases
	for (var c in keymap) if (c >= 65 && c <= 90) keymap[parseInt(c)+97-65] = keymap[c];

	$.ui.ajaxpopup.subclass('ui.hebrewKeyboard', {
		_capsLock: false,
		_fill: function(box){
			var self = this;
			this._super(box);
			box.click(function(e){
				if ($(e.target).is('.key')) {
					self.element.sendkeys(e.target.title);
					return false;
				}
			});
			this.element.keypress(function(evt){
				if (self._capsLock && !evt.metaKey && !evt.ctrlKey && keymap[evt.which]){
					self.element.sendkeys(keymap[evt.which]);
					return false;
				}
			}).keyup(function(evt){
				if (evt.which == $.ui.keyCode.CAPS_LOCK){
					self._capsLock = !self._capsLock;
					self.box().find('.capsLock').text(self._capsLock ? self.options.capslockOn : self.options.capslockOff);
				}
				if (self._capsLock){
					self.box().find('.k'+evt.which).removeClass('hover');
				}
			}).keydown(function(evt){
				if (self._capsLock){
					self.box().find('.k'+evt.which).addClass('hover');
				}
			});
		},
		options: {
			url: '/inc/keyboard.html',
			capslockOff: 'Press the Caps Lock key to use the physical keyboard',
			capslockOn: 'Press the Caps Lock key to restore the physical keyboard'
		}	
	});

})(jQuery);
;
// googleSearch: popup results from the Google search API
// Version 1.1
// Copyright (c) 2011 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

;(function ($){
	$.ui.ajaxpopup.subclass('ui.googleSearch', {
		_init: function (){
			var self = this, form = self.element.closest('form');
			self.contents = this.options.contents;
			var websearch = new google.search.WebSearch();
			// allow using the new (google/cse) or old (google/search) parameters
			websearch.setSiteRestriction(form.find('[name="as_sitesearch"],[name="sitesearch"]').val());
			websearch.setLinkTarget (google.search.Search.LINK_TARGET_SELF);
			websearch.setSearchCompleteCallback(self, function(){
				// create the backup url from the original form
				self.searchurl = form.attr('action')+'?'+form.serialize();
				// if the ajax search can't find it, use full Google (why google.com/search finds things that google.com?q=site:xxx doesn't is beyond me)
				if (websearch.results.length == 0) window.location = self.searchurl;
				self.contents = $.map(websearch.results, function(x) {return x.html} );
				self._trigger('found', 0, [websearch.results]);
				self.show();
			});
			form.bind('submit.googleSearch', function(evt){
				websearch.execute (self.element.val());
				return false;
			});
		},
		show: function(){
			this.box().find('a.moreresults').attr('href', this.searchurl);
			this.box().find('div.resultsdiv').html(this.contents);
			this._super();
		},
		options: {
			url: '/inc/search.html',
			trigger: null,
			contents: $([]) // the initial value of the search results DIV. Could be set to a loading... image
		},
		destroy: function(){
			var form = this.element.closest('form');
			form.unbind ('.googleSearch');
		}
	});

})(jQuery);
;
(function($){

//lazy evaluate the scrolling element (needs to be evaluated after the body element exists)
var html;

// default for this plugin, outerWidth/Height  and offset is to include padding and border but exclude margin. This adjusts the measurements if the options are set to something different
function propPx(el,opts, what){
	return	Math.round(
		(!opts.padding && -parseFloat(el.css('padding'+what) || 0)) +
		(!opts.border && -parseFloat(el.css('border'+what+'Width') || 0)) +
		(opts.margin && parseFloat(el.css('margin'+what) || 0))
	);
}

// find the effective size of the window; the clientHeight/Width less the size of the element. This gives the width/height of the rectangle that the top left corner of the element can go in and still
// leave room for the entire element
function constrainPx (el, opts, what){
	return document.documentElement['client'+what] - el['outer'+what]() -
		(what == 'Height' ?
			propPx(el,opts,'Top')+propPx(el,opts,'Bottom') :
			propPx(el,opts,'Left')+propPx(el,opts,'Right')
		);
}

$.fn.scrollIntoView = function(opts, easing, fn){
	// FF3 needs to scroll html; body.scrollHeight < html.scrollHeight
	// Opera9 needs to scroll html (body works but flashes); body < html
	// Chrome1 needs to scroll body; body >= html
	// Safari3 needs to scroll body; body >= html
	// IE7 in quirksmode needs to scroll body; body > html
	// IE7 in standards mode needs to scroll html; body < html
	html = html || ($('body')[0].scrollHeight >= $('html')[0].scrollHeight ? $('body')[0] : $('html')[0]);
	if (typeof opts != 'object') opts = {duration: opts, easing: easing, complete: fn}; 
	opts = $.extend({}, $.fn.scrollIntoView.defaults, $.metadata && this.metadata()['scrollIntoView'], opts);
	if (opts.complete){
		// the animate is done on the html element; the callback needs to be done on the target.
		var complete = opts.complete, self = this[0];
		opts.complete = function (){complete.apply(self, arguments);};
	}
	if (opts.margin) opts.border = true; // make sure the properties are logically consistent
	if (opts.border) opts.padding = true;
	var offset = this.offset(); // offset includes padding and border. We need to adjust
	offset.top -= propPx(this, opts, 'Top');
	offset.left -= propPx(this, opts, 'Left');
	var h = Math.max(0, constrainPx(this, opts, 'Height')), // if the size is negative then the element will not fit.  Just scroll so the top left corner is maximally visible 
		w = Math.max(0, constrainPx(this, opts, 'Width'));
		$(html).animate({
		scrollTop: Math.min(offset.top, Math.max(html.scrollTop, offset.top-h)),
		scrollLeft: Math.min(offset.left, Math.max(html.scrollLeft, offset.left-w))
	}, opts);
	return this;
};
$.fn.scrollIntoView.defaults = {
	padding: true,
	border: true,
	margin: false
};
})(jQuery);
;

