// 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);;
// insert characters in a textarea or text input field
// special characters are enclosed in {}; use {{} for the { character itself
(function($){

$.fn.sendkeys = function (x){
	return this.each( function(){
		var localkeys = $.extend({}, $.metadata && $(this).metadata()['sendkeys'], $(this).data('sendkeys')); // allow for element-specific key functions
		var o = this;
		// IE puts the insertion point at the start of the field on focus. We need to correct that
		// we'll record the actual selection in $.data('sendkeys.selection')
		// Opera is flaky: it impelements both document.selection and element.setSelectionRange, so we have to exclude that
		if (document.selection && !this.setSelectionRange && !$.data(this, 'sendkeys.selection')){
			var rng = this.createTextRange();
			rng.collapse(false); // append to end
			$.data(this, 'sendkeys.selection', rng);
			$(this).bind('beforedeactivate', function(){
				$.data(this, 'sendkeys.selection', document.selection.createRange());
			}).bind('focus', function(){
				$.data(this, 'sendkeys.selection').select();
			});
		}
		this.focus();
		x.replace(/{[^}]*}|[^{]+/g, function(s){
			(localkeys[s] || $.fn.sendkeys.defaults[s] || simplechar).call(o, s);
		});
		$(o).trigger({type: 'sendkeys', which: x});
	});
}; // sendkeys

// for IE, which isn't smart enough to constrain a selection to a single element
function constrainAndRecord (rng, o){
	// Stupid IE can't treat these the same
	if (o.tagName == 'TEXTAREA'){
		var orng = rng.duplicate();
		orng.moveToElementText(o);
	}else{
		orng = o.createTextRange();
	}
	if (orng.compareEndPoints('StartToStart', rng) == 1) rng.setEndPoint('StartToStart', orng);
	if (orng.compareEndPoints('EndToEnd', rng) == -1) rng.setEndPoint('EndToEnd', orng);
	rng.select();
	$.data(o, 'sendkeys.selection', rng);	
} // constrainAndRecord

if ($('<input>')[0].setSelectionRange){
	// Standards
	var _simplechar = function(c){
		var val = this.value;
		var start =this.selectionStart;
		this.value = val.substring(0, start)+ c + val.substring(this.selectionEnd);
		this.setSelectionRange(start+c.length, start+c.length);
	};
	var backspace = function(c){
		var start = this.selectionStart, end = this.selectionEnd;
		var val = this.value;
		if (start != end){
			this.value = val.substring(0,start) + val.substring(end);
			this.setSelectionRange(start, start);
		}else if (start) {
			// start == end and not start != 0
			this.value = val.substring(0, start-1)+val.substring(start);
			this.setSelectionRange(start-1, start-1);
		}
	};
	var rightarrow = function(c){
		this.setSelectionRange(this.selectionStart+1, this.selectionStart+1);
	};
	var leftarrow = function(c){
		this.setSelectionRange(this.selectionStart-1, this.selectionStart-1);
	};
	var del = function(c){
		var start = this.selectionStart, end = this.selectionEnd;
		var val = this.value;
		if (start != end){
			this.value = val.substring(0,start) + val.substring(end);
			this.setSelectionRange(start, start);
		}else{
			this.value = val.substring(0, start)+val.substring(start+1);
			this.setSelectionRange(start, start);
		}
	};
	var all = function(c){
		this.setSelectionRange(0, this.value.length);
	};
}else if (document.selection){
	// IE
	_simplechar = function(c){
		// add the char c to the selection
		var rng = $.data(this, 'sendkeys.selection'); // assume o already has the focus
		rng.text = c;
		rng.collapse(false); // move to end of selection
		constrainAndRecord(rng, this);
	};
	backspace = function (c){
		var rng = $.data(this, 'sendkeys.selection');
		if (rng.text.length == 0) rng.moveStart('character',-1);
		constrainAndRecord(rng, this);
		rng.text = "";
		constrainAndRecord(rng, this); // need to constrain before and after. I don't trust IE
	};
	rightarrow = function(c){
		var rng = $.data(this, 'sendkeys.selection');
		rng.move('character', 1);
		constrainAndRecord(rng, this);
	}
	leftarrow = function(c){
		var rng = $.data(this, 'sendkeys.selection');
		rng.move('character', -1);
		constrainAndRecord(rng, this);
	}
	del = function (c){
		var rng = $.data(this, 'sendkeys.selection');
		if (rng.text.length == 0) rng.moveEnd('character',1);
		constrainAndRecord(rng, this);
		rng.text = "";
		constrainAndRecord(rng, this); // need to constrain before and after. I don't trust IE
	};
	all = function(c){
		var rng = this.createTextRange();
		constrainAndRecord(rng, this);
	};
}else{
	// selections not supported. Just append the character
	_simplechar = function(c){
		this.value += c;
	};
	backspace = del = function(c){
		this.value = ""; 
	}
	rightarrow = leftarrow = all = function(){};
}

// simulate a keypress (not keydown/keyup) for simplechar. Any other key has to be done by hand in the code
simplechar = function(c){
	_simplechar.call(this,c);
	for (var i =0; i < c.length; ++i){
		var x = c.charCodeAt(i);
		$(this).trigger({type: 'keypress', keyCode: x, which: x, charCode: x});
	}
}

// add the functions publicly so they can be overridden
$.fn.sendkeys.defaults = {
	'{backspace}': backspace,
	'{rightarrow}': rightarrow,
	'{leftarrow}': leftarrow,
	'{del}': del,
	'{selectall}' : all,
	'{{}': function() {simplechar.call(this, '{')},
	'simplechar': simplechar // make it publicly callable
};

})(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
// dependencies: jquery.ui.subclass.js (mine), ui.core.js, effects.core.js (from jQuery UI 1.7)
// 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.

// Modified: 2009-05-19

(function($){
	$.ui.widget.subclass('ui.textpopup', {
		_init: function(){
			var self = this;
			if (this.options.hideOnOutsideClick) this.hideOnOutsideClick();
			var trigger = this.options.trigger;
			if (trigger == 'self'){
				trigger = this.element;
			}
			if (trigger) this.trigger = $(trigger).bind('focus click', function (){ self.show(); return false; });
		},
		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.options.show.call(box, self.options.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');
			self.options.hide.call(box, self.options.speed);
			box.queue(function(){self._trigger('hidden'); box.dequeue()});
		},
		box: function(){
			// lazy create
			return this.theBox || this._create();
		},
		_create: function(){
			var self = this, box = $('<div/>').addClass(this.options['class']).hide();
			box.css({position: 'absolute', width: 'auto', zIndex: 100}).
			    css(position[this.options.position.charAt(0)][0]).
					css(position[this.options.position.charAt(1)][1]).
					keydown(function(e) {
						if (e.keyCode == $.ui.keyCode.ESCAPE) {
							self.element.focus();
							self.hide();
						}
					});
			this.theBox = box;
			box.data('textpopup', this);
			this._fill(box);
			this._attach();
			this._trigger('created', 0, box);
			return box;
		},
		_fill: function(box){
			// virtual method to put something in the box
		},
		_attach: function(){
			// create a positioned wrapper to allow the textbox to be positioned relative to it
			var pos = this.element.position();
			if (this.options.position.indexOf('r') > -1) pos.left += this.element.outerWidth();
			$('<div style="position: absolute; z-index: 100;"/>').insertAfter(this.element).append(this.box()).
				css(pos).height(this.element.outerHeight());
		},
		// hides the box for any click outside it
		// TODO: make a similar function for the esc key, for keyboard accessibility
		hideOnOutsideClick: function(){
			var self = this;
			var $body = $('body');
			var hider = function(e) { if(!self._isClickInside(e)) self.hide(); };
			this.after('show', function(){
				$body.bind('click', hider);
			});
			this.before('hide', function(){
				$body.unbind('click',hider);
			});
		},
		destroy: function() {
			this.box().parent().remove();
			this.theBox = undefined;
		},
		// returns true if the event e is a click inside the box , the original element or the triggering elements
		_isClickInside: function(e){
			var keepers = $([]).add(this.trigger).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 = {
		t: [{bottom: '100%'}, {top: '0%'}],
		b: [{top: '100%'}, {bottom: '0%'}],
		l: [{right: '100%'}, {left: '0%'}],
		r: [{left: '100%'}, {right: '0%'}]
	};
	
	// 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; notice that this is a class variable
		_fill: function(box){
			// TODO: add a loading graphic
			var self = this;
			if (!self.html){
				var url = self.options.url;
				if (/^data:[^,]*,/.test(url)){
					setHTML(decodeURIComponent(url.replace(/^data:[^,]*,/, '')));
				}else{
					$.get(url, setHTML);
				}
			}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();
				}
			}
		}
	});
	
	$.ui.ajaxpopup.subclass('ui.hebrewKeyboard', {
		_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;
				}
			});
		},
		options: {
			url: '/inc/keyboard.html'
		}	
	});

})(jQuery);
;
;(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();
			websearch.setSiteRestriction(form.find('[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.submit(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
		}
	});

})(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);
;
