// 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);
