/**
* jQuery Cookie plugin
*
* Copyright (c) 2010 Klaus Hartl (stilbuero.de)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
jQuery.cookie = function (key, value, options) {

    // key and at least value given, set cookie...
    if (arguments.length > 1 && String(value) !== "[object Object]") {
        options = jQuery.extend({}, options);

        if (value === null || value === undefined) {
            options.expires = -1;
        }

        if (typeof options.expires === 'number') {
            var days = options.expires, t = options.expires = new Date();
            t.setDate(t.getDate() + days);
        }

        value = String(value);

        return (document.cookie = [
            encodeURIComponent(key), '=',
            options.raw ? value : encodeURIComponent(value),
            options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
            options.path ? '; path=' + options.path : '',
            options.domain ? '; domain=' + options.domain : '',
            options.secure ? '; secure' : ''
        ].join(''));
    }

    // key and possibly options given, get cookie...
    options = value || {};
    var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent;
    return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null;
};



//** All Levels Navigational Menu- (c) Dynamic Drive DHTML code library: http://www.dynamicdrive.com
//** Script Download/ instructions page: http://www.dynamicdrive.com/dynamicindex1/ddlevelsmenu/
//** Usage Terms: http://www.dynamicdrive.com/notice.htm

//** Current version: 2.2 See changelog.txt for details
//** Fixes for IE by Cameron Gregory, http://www.flamingtext.com/

if (typeof dd_domreadycheck=="undefined") //global variable to detect if DOM is ready
	var dd_domreadycheck=false;
if (typeof ft_initonce=="undefined")
	var ft_initonce=false;

var ddlevelsmenu={

enableshim: true, //enable IFRAME shim to prevent drop down menus from being hidden below SELECT or FLASH elements? (tip: disable if not in use, for efficiency)

arrowpointers:{
	downarrow: ["http://cdn1.ftimg.com/css/menu/arrow-down.gif", 11,7], //[path_to_down_arrow, arrowwidth, arrowheight]
	rightarrow: ["http://cdn1.ftimg.com/css/menu/arrow-right.gif", 12,12], //[path_to_right_arrow, arrowwidth, arrowheight]
	showarrow: {toplevel: true, sublevel: true} //Show arrow images on top level items and sub level items, respectively?
},
hideinterval: 500, //delay in milliseconds before entire menu disappears onmouseout.
effects: {enableswipe: true, enablefade: true, duration: 200},
httpsiframesrc: "/css/blank.htm", //If menu is run on a secure (https) page, the IFRAME shim feature used by the script should point to an *blank* page *within* the secure area to prevent an IE security prompt. Specify full URL to that page on your server (leave as is if not applicable).

///No need to edit beyond here////////////////////

topmenuids: [], //array containing ids of all the primary menus on the page
topitems: {}, //object array containing all top menu item links
subuls: {}, //object array containing all ULs
lastactivesubul: {}, //object object containing info for last mouse out menu item's UL
topitemsindex: -1,
ulindex: -1,
hidetimers: {}, //object array timer
shimadded: false,
nonFF: !/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent), //detect non FF browsers
getoffset:function(what, offsettype){
	return (what.offsetParent)? what[offsettype]+this.getoffset(what.offsetParent, offsettype) : what[offsettype];
},

getoffsetof:function(el){
	el._offsets={left:this.getoffset(el, "offsetLeft"), top:this.getoffset(el, "offsetTop")};
},

getwindowsize:function(){
	this.docwidth=window.innerWidth? window.innerWidth-10 : this.standardbody.clientWidth-10;
	this.docheight=window.innerHeight? window.innerHeight-15 : this.standardbody.clientHeight-18;
},

gettopitemsdimensions:function(){
	for (var m=0; m<this.topmenuids.length; m++){
		var topmenuid=this.topmenuids[m];
		for (var i=0; i<this.topitems[topmenuid].length; i++){
			var header=this.topitems[topmenuid][i];
			var submenu=document.getElementById(header.getAttribute('rel'));
			header._dimensions={w:header.offsetWidth, h:header.offsetHeight, submenuw:submenu.offsetWidth, submenuh:submenu.offsetHeight};
		}
	}
},

isContained:function(m, e){
	var e=window.event || e;
	var c=e.relatedTarget || ((e.type=="mouseover")? e.fromElement : e.toElement);
	while (c && c!=m)try {c=c.parentNode;} catch(e){c=m;}
	if (c==m)
		return true;
	else
		return false;
},

addpointer:function(target, imgclass, imginfo, BeforeorAfter){
	var pointer=document.createElement("img")
	pointer.src=imginfo[0]
	pointer.style.width=imginfo[1]+"px"
	pointer.style.height=imginfo[2]+"px"
	if(imgclass=="rightarrowpointer"){
		pointer.style.left=target.offsetWidth-imginfo[2]-2+"px"
	}
	pointer.className=imgclass
	var target_firstEl=target.childNodes[target.firstChild.nodeType!=1? 1 : 0] //see if the first child element within A is a SPAN (found in sliding doors technique)
	if (target_firstEl && target_firstEl.tagName=="SPAN"){
		target=target_firstEl //arrow should be added inside this SPAN instead if found
	}
	if (BeforeorAfter=="before")
		target.insertBefore(pointer, target.firstChild)
	else
		target.appendChild(pointer)
},

css:function(el, targetclass, action){
	var needle=new RegExp("(^|\\s+)"+targetclass+"($|\\s+)", "ig")
	if (action=="check")
		return needle.test(el.className)
	else if (action=="remove")
		el.className=el.className.replace(needle, "")
	else if (action=="add" && !needle.test(el.className))
		el.className+=" "+targetclass
},

addshimmy:function(target){

var ie6=false;
if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)){
	var v=new Number(RegExp.$1) // capture x.x portion and store as a number
	ie6=(v>=6 && v < 7);
}
	var shim=(!window.opera)? document.createElement("iframe") : document.createElement("div") //Opera 9.24 doesnt seem to support transparent IFRAMEs
	shim.className="ddiframeshim"
	shim.setAttribute("src", (ie6 || location.protocol=="https:")? this.httpsiframesrc : "about:blank")
	shim.setAttribute("frameborder", "0")
	target.appendChild(shim)
	try{
		shim.style.filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
	}
	catch(e){}
	return shim
},

positionshim:function(header, submenu, dir, scrollX, scrollY){
	if (header._istoplevel){
		var scrollY=window.pageYOffset? window.pageYOffset : this.standardbody.scrollTop
		var topgap=header._offsets.top-scrollY
		var bottomgap=scrollY+this.docheight-header._offsets.top-header._dimensions.h
		if (topgap>0){
			this.shimmy.topshim.style.left=scrollX+"px"
			this.shimmy.topshim.style.top=scrollY+"px"
			this.shimmy.topshim.style.width="99%"
			this.shimmy.topshim.style.height=topgap+"px" //distance from top window edge to top of menu item
		}
		if (bottomgap>0){
			this.shimmy.bottomshim.style.left=scrollX+"px"
			this.shimmy.bottomshim.style.top=header._offsets.top + header._dimensions.h +"px"
			this.shimmy.bottomshim.style.width="99%"
			this.shimmy.bottomshim.style.height=bottomgap+"px" //distance from bottom of menu item to bottom window edge
		}
	}
},

hideshim:function(){
	this.shimmy.topshim.style.width=this.shimmy.bottomshim.style.width=0
	this.shimmy.topshim.style.height=this.shimmy.bottomshim.style.height=0
},


buildmenu:function(mainmenuid, header, submenu, submenupos, istoplevel, dir){
	header._master=mainmenuid; //Indicate which top menu this header is associated with
	header._pos=submenupos; //Indicate pos of sub menu this header is associated with
	header._istoplevel=istoplevel;
	if (istoplevel){
		this.addEvent(header, function(e){
		ddlevelsmenu.hidemenu(ddlevelsmenu.subuls[this._master][parseInt(this._pos)]);
		}, "click");
	}
	this.subuls[mainmenuid][submenupos]=submenu;
	header._dimensions={w:header.offsetWidth, h:header.offsetHeight, submenuw:submenu.offsetWidth, submenuh:submenu.offsetHeight};
	this.getoffsetof(header);
	submenu.style.left=0;
	submenu.style.top=0;
	submenu.style.visibility="hidden";
	this.addEvent(header, function(e){ //mouseover event
		if (!ddlevelsmenu.isContained(this, e)){
			var submenu=ddlevelsmenu.subuls[this._master][parseInt(this._pos)];
			if (this._istoplevel){
				ddlevelsmenu.css(this, "selected", "add");
				clearTimeout(ddlevelsmenu.hidetimers[this._master][this._pos]);
			}
			ddlevelsmenu.getoffsetof(header);
			var scrollX=window.pageXOffset? window.pageXOffset : ddlevelsmenu.standardbody.scrollLeft;
			var scrollY=window.pageYOffset? window.pageYOffset : ddlevelsmenu.standardbody.scrollTop;
			var submenurightedge=this._offsets.left + this._dimensions.submenuw + (this._istoplevel && dir=="topbar"? 0 : this._dimensions.w);
			var submenubottomedge=this._offsets.top + this._dimensions.submenuh;
			//Sub menu starting left position
			var menuleft=(this._istoplevel? this._offsets.left + (dir=="sidebar"? this._dimensions.w : 0) : this._dimensions.w);
			if (submenurightedge-scrollX>ddlevelsmenu.docwidth){
				menuleft+= -this._dimensions.submenuw + (this._istoplevel && dir=="topbar" ? this._dimensions.w : -this._dimensions.w);
			}
			menuleft-=1;//eugene:this is to align with our menu separator border
			submenu.style.left=menuleft+"px";
			//Sub menu starting top position
			var menutop=(this._istoplevel? this._offsets.top + (dir=="sidebar"? 0 : this._dimensions.h) : this.offsetTop);
			if (submenubottomedge-scrollY>ddlevelsmenu.docheight){ //no room downwards?
				if (this._dimensions.submenuh<this._offsets.top+(dir=="sidebar"? this._dimensions.h : 0)-scrollY){ //move up?
					menutop+= - this._dimensions.submenuh + (this._istoplevel && dir=="topbar"? -this._dimensions.h : this._dimensions.h)
				}
				else{ //top of window edge
					menutop+= -(this._offsets.top-scrollY) + (this._istoplevel && dir=="topbar"? -this._dimensions.h : 0)
				}
			}
			submenu.style.top=menutop+"px"
			if (ddlevelsmenu.enableshim && (ddlevelsmenu.effects.enableswipe==false || ddlevelsmenu.nonFF)){ //apply shim immediately only if animation is turned off, or if on, in non FF2.x browsers
				ddlevelsmenu.positionshim(header, submenu, dir, scrollX, scrollY)
			}
			else{
				submenu.FFscrollInfo={x:scrollX, y:scrollY}
			}
			ddlevelsmenu.showmenu(header, submenu, dir)
		}
	}, "mouseover");
	this.addEvent(header, function(e){ //mouseout event
		var submenu=ddlevelsmenu.subuls[this._master][parseInt(this._pos)]
		if (this._istoplevel){
			if (!ddlevelsmenu.isContained(this, e) && !ddlevelsmenu.isContained(submenu, e)) //hide drop down ul if mouse moves out of menu bar item but not into drop down ul itself
				ddlevelsmenu.hidemenu(submenu)
		}
		else if (!this._istoplevel && !ddlevelsmenu.isContained(this, e)){
			ddlevelsmenu.hidemenu(submenu)
		}

	}, "mouseout");
},

setopacity:function(el, value){
	el.style.opacity=value
	if (typeof el.style.opacity!="string"){ //if it's not a string (ie: number instead), it means property not supported
		el.style.MozOpacity=value
		if (el.filters){
			el.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity="+ value*100 +")"
		}
	}
},

showmenu:function(header, submenu, dir){
	if (this.effects.enableswipe || this.effects.enablefade){
		if (this.effects.enableswipe){
			var endpoint=(header._istoplevel && dir=="topbar")? header._dimensions.submenuh : header._dimensions.submenuw
			submenu.style.width=submenu.style.height=0
			submenu.style.overflow="hidden"
		}
		if (this.effects.enablefade){
			this.setopacity(submenu, 0) //set opacity to 0 so menu appears hidden initially
		}
		submenu._curanimatedegree=0
		submenu.style.visibility="visible"
		clearInterval(submenu._animatetimer)
		submenu._starttime=new Date().getTime() //get time just before animation is run
		submenu._animatetimer=setInterval(function(){ddlevelsmenu.revealmenu(header, submenu, endpoint, dir)}, 10)
	}
	else{
		submenu.style.visibility="visible"
	}
},

revealmenu:function(header, submenu, endpoint, dir){
	var elapsed=new Date().getTime()-submenu._starttime //get time animation has run
	if (elapsed<this.effects.duration){
		if (this.effects.enableswipe){
			if (submenu._curanimatedegree==0){ //reset either width or height of sub menu to "auto" when animation begins
				submenu.style[header._istoplevel && dir=="topbar"? "width" : "height"]="auto"
			}
			submenu.style[header._istoplevel && dir=="topbar"? "height" : "width"]=(submenu._curanimatedegree*endpoint)+"px"
		}
		if (this.effects.enablefade){
			this.setopacity(submenu, submenu._curanimatedegree)
		}
	}
	else{
		clearInterval(submenu._animatetimer)
		if (this.effects.enableswipe){
			submenu.style.width="auto"
			submenu.style.height="auto"
			submenu.style.overflow="visible"
		}
		if (this.effects.enablefade){
			this.setopacity(submenu, 1)
			submenu.style.filter=""
		}
		if (this.enableshim && submenu.FFscrollInfo) //if this is FF browser (meaning shim hasn't been applied yet
			this.positionshim(header, submenu, dir, submenu.FFscrollInfo.x, submenu.FFscrollInfo.y)
	}
	submenu._curanimatedegree=(1-Math.cos((elapsed/this.effects.duration)*Math.PI)) / 2
},

hidemenu:function(submenu){
	if (typeof submenu._pos!="undefined"){ //if submenu is outermost UL drop down menu
		this.css(this.topitems[submenu._master][parseInt(submenu._pos)], "selected", "remove")
		if (this.enableshim)
			this.hideshim()
	}
	clearInterval(submenu._animatetimer)
	submenu.style.left=0
	submenu.style.top="-1000px"
	submenu.style.visibility="hidden"
},


addEvent:function(target, functionref, tasktype) {
	if (target.addEventListener)
		target.addEventListener(tasktype, functionref, false);
	else if (target.attachEvent)
		target.attachEvent('on'+tasktype, function(){return functionref.call(target, window.event)});
},

domready:function(functionref){ //based on code from the jQuery library
	if (dd_domreadycheck){
		functionref()
		return
	}
	// Mozilla, Opera and webkit nightlies currently support this event
	if (document.addEventListener) {
		// Use the handy event callback
		document.addEventListener("DOMContentLoaded", function(){
			document.removeEventListener("DOMContentLoaded", arguments.callee, false )
			functionref();
			dd_domreadycheck=true;
		}, false )
	}
	else if (document.attachEvent){
		// If IE and not an iframe
		// continually check to see if the document is ready
		if ( document.documentElement.doScroll && window == window.top) (function(){
			if (dd_domreadycheck){
				functionref()
				return
			}
			try{
				// If IE is used, use the trick by Diego Perini
				// http://javascript.nwbox.com/IEContentLoaded/
				document.documentElement.doScroll("left")
			}catch(error){
				setTimeout( arguments.callee, 0)
				return;
			}
			//and execute any waiting functions
			functionref();
			dd_domreadycheck=true
		})();
	}
	if (document.attachEvent && parent.length>0) //account for page being in IFRAME, in which above doesn't fire in IE
		this.addEvent(window, function(){functionref()}, "load");
},

filterSubuls:function(subuls){
	var ret = [];
	for (var c=0; c<subuls.length; c++){
		if(!$(subuls[c]).hasClass("notSubmenu"))
			ret.push(subuls[c]);
		else{
			var h = $(".logo_menu_list").height() - parseInt($(".logo_menu_preview").css('padding-top')) - parseInt($(".logo_menu_preview").css('padding-bottom'));
			$(".logo_menu_preview").height(h);
		}
	}
	return ret;
},
init:function(mainmenuid, dir){
	if (ft_initonce)
		return;
	ft_initonce=true;
	this.standardbody=(document.compatMode=="CSS1Compat")? document.documentElement : document.body
	this.topitemsindex=-1
	this.ulindex=-1
	this.topmenuids.push(mainmenuid)
	this.topitems[mainmenuid]=[] //declare array on object
	this.subuls[mainmenuid]=[] //declare array on object
	this.hidetimers[mainmenuid]=[] //declare hide entire menu timer
	if (this.enableshim && !this.shimadded){
		this.shimmy={};
		this.shimmy.topshim=this.addshimmy(document.body); //create top iframe shim obj
		this.shimmy.bottomshim=this.addshimmy(document.body); //create bottom iframe shim obj
		this.shimadded=true;
	}
	var menubar=document.getElementById(mainmenuid);
	var alllinks=menubar.getElementsByTagName("a");
	this.getwindowsize();
	for (var i=0; i<alllinks.length; i++){
		if (alllinks[i].getAttribute('rel')){
			this.topitemsindex++;
			this.ulindex++;
			var menuitem=alllinks[i];
			this.topitems[mainmenuid][this.topitemsindex]=menuitem; //store ref to main menu links
			var dropul=document.getElementById(menuitem.getAttribute('rel'));
			document.body.appendChild(dropul) //move main ULs to end of document
			dropul.style.zIndex=2000;; //give drop down menus a high z-index
			dropul._master=mainmenuid;  //Indicate which main menu this main UL is associated with
			dropul._pos=this.topitemsindex; //Indicate which main menu item this main UL is associated with
			this.addEvent(dropul, function(){ddlevelsmenu.hidemenu(this)}, "click");
			var arrowclass=(dir=="sidebar")? "rightarrowpointer" : "downarrowpointer";
			var arrowpointer=(dir=="sidebar")? this.arrowpointers.rightarrow : this.arrowpointers.downarrow;
			if (this.arrowpointers.showarrow.toplevel)
				this.addpointer(menuitem, arrowclass, arrowpointer, (dir=="sidebar")? "before" : "after");
			this.buildmenu(mainmenuid, menuitem, dropul, this.ulindex, true, dir);; //build top level menu
			dropul.onmouseover=function(e){
				clearTimeout(ddlevelsmenu.hidetimers[this._master][this._pos]);
			};
			this.addEvent(dropul, function(e){ //hide menu if mouse moves out of main UL element into open space
				if (!ddlevelsmenu.isContained(this, e) && !ddlevelsmenu.isContained(ddlevelsmenu.topitems[this._master][parseInt(this._pos)], e)){
					var dropul=this;
					if (ddlevelsmenu.enableshim)
						ddlevelsmenu.hideshim();
					ddlevelsmenu.hidetimers[this._master][this._pos]=setTimeout(function(){
						ddlevelsmenu.hidemenu(dropul);
					}, ddlevelsmenu.hideinterval);
				}
			}, "mouseout");
			var subuls=dropul.getElementsByTagName("ul");
			subuls = this.filterSubuls(subuls);
			for (var c=0; c<subuls.length; c++){
				this.ulindex++;
				var parentli=subuls[c].parentNode;
				if (this.arrowpointers.showarrow.sublevel)
					this.addpointer(parentli.getElementsByTagName("a")[0], "rightarrowpointer", this.arrowpointers.rightarrow, "before");
				this.buildmenu(mainmenuid, parentli, subuls[c], this.ulindex, false, dir); //build sub level menus
			}
		}
	} //end for loop
	this.addEvent(window, function(){ddlevelsmenu.getwindowsize(); ddlevelsmenu.gettopitemsdimensions()}, "resize")
},

setup:function(mainmenuid, dir){
	this.domready(function(){ddlevelsmenu.init(mainmenuid, dir)})
}

};
var oldonerror = window.onerror;
window.onerror = function(errorMessage, url, line) {
  var loggerUrl = "http://www.flamingtext.com/general/jserror.cgi";
  var parameters = "?description=" + escape(errorMessage)
      + "&amp;url=" + encodeURIComponent(url)
      + "&amp;line=" + encodeURIComponent(line)
      + "&amp;parent_url=" + encodeURIComponent(document.location.href)
      + "&amp;user_agent=" + encodeURIComponent(navigator.userAgent);

  /** Send error to server */
  //window.alert("blah:"+line+":"+errorMessage);
  new Image().src = loggerUrl + parameters;
  window.onerror=oldonerror;
};
(function($){
	
	var langurl = function(lang) {
		var fthost="flamingtext.com";
		// turn flamingtext.com, ar -> ar.flamingtext.com
		// turn ft5.flamingtext.com, ar -> ar.ft5.flamingtext.com
		
		if(typeof(lang) =='undefined')
			return encodeURI("http://"+fthost);
		
		var first = "";
		var len = fthost.length - ".flamingtext.com".length;
		if (len <= 0) {
			//PRODUCTION!!
			// can't do this until cookie problem solved.
			//if (str.equals("hi-in"))
				//return "www.flamingtext.in";
		} else {
			first = fthost.substring(0,len)+".";
		}
		return encodeURI("http://" + first + lang + ".flamingtext.com");
		
	};
	
	//langpick widget
	$.widget( "ui.langpick", {
		options: {
			langs:[
				{lang:"English"
				,en_lang:"English"
				// don't remove index.html !
				,url:langurl()//"http://flamingtext.com/index.html"
				,flag:undefined
				}
				,
				{lang:"&rlm;العربية&rlm;"
				,en_lang:"Arabic"
				//,url:"http://ar.flamingtext.com/"
				,url:langurl("ar")
				,flag:"http://cdn1.ftimg.com/images/icons/countries/png/ae.png"
				}
				,
				{lang:"中文"
				,en_lang:"Chinese"
				,url:langurl("zh-cn")//"http://zh-cn.flamingtext.com/"
				,flag:"http://cdn1.ftimg.com/images/icons/countries/png/cn.png"
				}
				,
				{lang:"Français"
				,en_lang:"French"
				,url:langurl("fr")//"http://fr.flamingtext.com/"
				,flag:"http://cdn1.ftimg.com/images/icons/countries/png/fr.png"
				}
				,
				{lang:"Deutsch"
				,en_lang:"German"
				,url:langurl("de")//"http://de.flamingtext.com/"
				,flag:"http://cdn1.ftimg.com/images/icons/countries/png/de.png"
				}
				,
				{lang:"हिन्दी"
				,en_lang:"Hindi"
				,url:langurl("hi-in")//"http://hi-in.flamingtext.com/"
				,flag:"http://cdn1.ftimg.com/images/icons/countries/png/in.png"
				}
				,
				{lang:"日本語"
				,en_lang:"Japanese"
				,url:langurl("ja")//"http://ja.flamingtext.com/"
				,flag:"http://cdn1.ftimg.com/images/icons/countries/png/jp.png"
				}
				,
				{lang:"Português"
				,en_lang:"Portuguese"
				,url:langurl("pt")//"http://pt.flamingtext.com/"
				,flag:"http://cdn1.ftimg.com/images/icons/countries/png/br.png"
				}
				,
				{lang:"Русский"
				,en_lang:"Russian"
				,url:langurl("ru")//"http://ru.flamingtext.com/"
				,flag:"http://cdn1.ftimg.com/images/icons/countries/png/ru.png"
				}
				,
				{lang:"Español"
				,en_lang:"Spanish"
				,url:langurl("es")//"http://es.flamingtext.com/"
				,flag:"http://cdn1.ftimg.com/images/icons/countries/png/es.png"
				}
			]
			,ui:{
				showOutline:false
				,html: "English &raquo;"
				,showFlag:false
				,showEnTooltip:true
				,min_width:130
			
			}
			,header:"Select Language"
			,open:function(e,ui){
				var p = ui.preview;
			}
			,close:function(e,ui){
				var p = ui.preview;
			}
			,positionFn:function(e,ui){
				var p = ui.preview;
				var b = ui.initButton;
				
				var pWidth = p.outerWidth();
				var pHeight = p.outerHeight();
				/*
				//default: screen center
				var offset = b.offset();
				var top = ($(window).height() -pHeight)/2 + $(window).scrollTop() - offset.top + "px";
				var left = ($(window).width() -pWidth)/2 + $(window).scrollLeft() - offset.left + "px";
				//center of screen
				p.css({
					top		:top
					,left		:left
				});
				*/
				///*
				//Alternative positioning: below element
				var b = ui.initButton;
				var offset = b.offset();
				var h = b.outerHeight();
				var w = b.outerWidth();
				p.css({
					top	:offset.top+h+"px"
					,left	:offset.left-pWidth+w+"px"
				});
				//*/
			}
			,columns:2
		},
		_create: function() {

			this.preview = undefined;
			this.totalLangs = this.options.langs.length;
			this.columns=[];
			this.column_width=0;
			
			this._buildUI();
		},
		_ui:function(){
			return {
				initButton:this.clickButton
				,preview:this.preview
			};
		},
		_buildUI:function(){
			var self = this;
			var o = this.options;
			
			/*this.widget().css({
				position:'relative'
				,float:'left'
			});*/
			var main = $("<span></span>");
			this.widget().html(main);
			
			this.clickButton = $("<a href=\"\" class=\"lp_init_button\" title=\"Select Language\">"+this.options.ui.html+"</a>").appendTo(main);
			
			//this is needed for ie6, otherwise it will stretch the language picker to whole body width
			var lp_placeholder = $("<div style=\"width:0px;\"></div>").appendTo('body');
			this.preview = $("<div class=\"lp_preview_dialog\"></div>").appendTo(lp_placeholder);
			
			this._buildLangPreview();
			
			if ($.isFunction(o.open)){
				this.clickButton.click(function(e){
					e.preventDefault();
					e.stopImmediatePropagation();
					if(self.preview.hasClass('lp_open')){
						self._close();
					}
					else{
						self._open();
					}
				});
			}
			
			
			this.preview.hide().css({visibility:'visible'});
		},
		_buildLangPreview: function(){
			var o = this.options;
			this._createMain();
			this._createHeader();
			this._createBody();
			this._createFooter();
			this._setWidths();//for cross-browser
		}
		,_createMain: function(){
			var o = this.options;
			this.preview_outline = $("<div class=\"lp_preview_dialog_outline\"></div>").appendTo(this.preview);
			this._showOutline();
			
			this.preview_container = $("<div class=\"lp_preview_dialog_container\"></div>").appendTo(this.preview_outline);
		}
		,_showOutline:function(){
			var o = this.options;
			
			//dont show for IE8 and lower
			if(navigator.appVersion.indexOf("MSIE")!=-1 && parseFloat(navigator.appVersion.split("MSIE")[1])<9){
				o.ui.showOutline = false;
			}			
			
			if(!o.ui.showOutline){
				this.preview_outline.css({
					padding:'0px'
					,border:'0px'
				});
			}
		}
		,_createHeader: function(){
			var o = this.options;
			this.preview_header = $("<div class=\"lp_preview_header\">"+o.header+"</div>").appendTo(this.preview_container);
			o.ui.min_width = o.ui.min_width>this.preview_header.width() ? o.ui.min_width : this.preview_header.width();
		}
		,_createBody: function(){
			var o = this.options;
			
			this.preview_body = $("<div class=\"lp_preview_body\"></div>").appendTo(this.preview_container);
			this._createColumns();
			var column = $("<div style=\"clear:both;\"></div>").appendTo(this.preview_body);
			o.ui.min_width = o.ui.min_width>this.preview_body.width() ? o.ui.min_width : this.preview_body.width();
		}
		,_createFooter: function(){
			var self = this;
			var o = this.options;
			
			this.preview_footer = $("<div class=\"lp_preview_footer\"></div>").appendTo(this.preview_container);
			
			//var close = $("<a href=\"\" class=\"lp_preview_close\">Close</a>").appendTo(this.preview_footer);
			var close = $("<input type=\"button\" class=\"lp_preview_close\" value=\"Close\"/>").appendTo(this.preview_footer);
			close.click(function(e){
				e.preventDefault();
				self._close();
			});
			o.ui.min_width = o.ui.min_width>this.preview_footer.width() ? o.ui.min_width : this.preview_footer.width();
		}
		,_open: function(){
			var self = this;
			var o = this.options;
			
			//position on open, because user might have changed window size
			if ($.isFunction(o.positionFn)){
				this._trigger('positionFn', null, this._ui());
			}
			
			if ($.isFunction(o.open)){
				self._trigger('open', null, self._ui());
			}
			
			this.preview.show();//css({visibility:'visible'});
			this.preview.addClass('lp_open');
			
			$(document).bind('click.langpick',{lp:self}, function(e){
				if (!$(e.target).parents().andSelf().is(e.data.lp.preview)) {
		                	e.data.lp._close();
		        	}
			});
			$(document).bind('keyup.langpick',{lp:self}, function(e){
				var lp = e.data.lp;
				if (e.keyCode == 27 && lp.preview.hasClass('lp_open')){
					lp._close();
				}
			});
		}
		,_close: function(){
			var self = this;
			var o = this.options;
			if ($.isFunction(o.close)){
				self._trigger('close', null, self._ui());
			}

			this.preview.hide();//css({visibility:'hidden'});
			this.preview.removeClass('lp_open');
			//remove all bound events
			$(document).unbind('.langpick');
		}
		,_createColumns: function(){
			var self = this;
			var o = this.options;
			
			o.columns = o.columns>this.totalLangs? this.totalLangs : o.columns;
			
			var langsInColumn = Math.ceil(this.totalLangs / o.columns);
			//console.log(this.totalLangs +";"+ this.options.columns +";"+ langsInColumn);
			var endIndex = 0;
			var langsProcessed = 0;
			for (var i=0;i<o.columns;i++){
				var startIndex = endIndex;
				var endIndex = startIndex + langsInColumn;

				//console.log(startIndex +";"+ endIndex);
				var langs = this.options.langs.slice(startIndex,endIndex);
				this._createColumn(i,langs);
				
				//compute number of langs in next column
				langsProcessed += langsInColumn;
				var langsLeft = this.totalLangs - langsProcessed;
				langsInColumn = Math.ceil(langsLeft / (o.columns-(i+1)));
			}
			
			$.each(self.columns, function(i,c){
				c.css('width',self.column_width+"px");
			});
			
			
			
			
		}
		,_setWidths: function(){
			var self = this;
			var o = this.options;
			//set this explicitly for cross-browser gradients display
			//this ensures hasLayout is set for gradients to work in IE
			var width = self.column_width*o.columns > o.ui.min_width ? self.column_width*o.columns : o.ui.min_width;
			this.preview_header.css('width',width+"px");
			this.preview_body.css('width',width+"px");
			this.preview_footer.css('width',width+"px");
			this.preview.css('width',"auto");//.width(width);
		}
		,_createColumn: function(i, langs){
			var column = $("<div class=\"lp_column\"></div>").appendTo(this.preview_body);
			var o = this.options;
			var self = this;
			
			var list = $("<ul class=\"lp_langUl\"></ul>").appendTo(column);
			$.each(langs, function(i,lang){
				var li = $("<li class=\"lp_langLi\"></li>");
				var item = $("<div class=\"lp_langLiItem\"></div>").appendTo(li);
	
				if(o.ui.showFlag){
					var flag = $("<div class=\"lp_langLiItemFlag\"></div>").appendTo(item);
					if (typeof lang.flag!='undefined' && lang.flag.length){						
						var img = $("<img class=\"lp_langLiItemFlagImg\" src=\""+lang.flag+"\"/>").appendTo(flag);
					}
				}
				
				var text = $("<a href=\""+lang.url+"\" class=\"lp_langLiItemText\">"+lang.lang+"</a>").appendTo(item);
				if(o.ui.showEnTooltip){
					var tooltip=$("<span class=\"lp_langTooltip\">"+lang.en_lang+"</span>");
					item.append(tooltip);
					text.hover(function(e){
						$(this).parent().find('.lp_langTooltip').show();
					}
					,function(e){
						$(this).parent().find('.lp_langTooltip').hide();
					});
				}
				
				text.click(function(e){
					e.preventDefault();
					$(this).parent().find('.lp_langTooltip').hide();
					self._close();
					window.location = encodeURI(lang.url+"/lc");
				});
				
				list.append(li);
			});
			
			this.column_width = (this.column_width>column.width())? this.column_width : column.width();
			this.columns.push(column);
		}

	});

})(jQuery);
function log(msg) {
	setTimeout(function() {
		throw new Error(msg);
	}, 0);
};
$(document).ready(function(){
        
        $(".logo_menu_preview").hide();
        $(".logo_menu_preview > div").hide();

	var prevCat = "cat_All"; 

        $(".logo_menu_list li").hover(function(){
        	$(this).closest(".logo_menu_list").find('a.lastVisited').removeClass('lastVisited');
                $(".logo_menu_preview .preview_"+prevCat).hide();
                var cat = prevCat =  $(this).attr('cat');
                var preview = getCatPreview(cat);
                preview.show();
		$(".logo_menu_preview").show();
        },
        function(){
        	$(this).children('a').addClass('lastVisited');
        });
        
        function getCatPreview(cat){
	        var preview = $('.preview_'+cat);
	        
	        if(preview.length)
	        	return preview;
        	
        	var previews = $(".logo_menu_preview");
        	var obj = previewCats[cat];
        	
        	preview = $("<div class=\"preview_"+cat+"\" style=\"display: none;\"></div>").appendTo(previews);
        	var note = $("<div class=\"logo_menu_preview_note\"><p>Examples from this category:</p></div>").appendTo(preview);
        	
        	for(var i=0; i<obj.l.length;i++){
	        	var link = $("<a href=\""+obj.u+"\"></a>").appendTo(preview);
	        	var img = $("<img src=\"http://cdn1.ftimg.com/images/logos/220x60/"+lang+"/"+obj.l[i]+"\"/>").appendTo(link);        	
        	}
        	return preview;
        }
});
var isIE6 = navigator.userAgent.toLowerCase().indexOf('msie 6') != -1;
var isIE7 = navigator.userAgent.toLowerCase().indexOf('msie 7') != -1;

var MODE_IMAGEBOT=1;
var APP_WEB="web";
var APP_CHROME="chrome";

var ft = ft || {};

if (!ft.dynamicForms) {
	ft.dynamicForms = {};
}


function DynamicForm(id,mode,app,updateImmediately,fthost) {
	this.id=id;
	this.mode=mode;
	this.app = app || APP_WEB;
	this.ftExtension=null;
	this.host = fthost;
	this.formChange=true;
	this.proxyFormId="proxyform-"+id;
	this.proxyForm= document.getElementById(this.proxyFormId);
	if (!this.proxyForm) {
		log("proxyform not found:"+this.proxyFormId);
	}
	//this.oldFormParams=""; //moved to updateHandler
	this.updateImmediately=updateImmediately;
	
	if (this.app == APP_CHROME) {
		this.host=this.host || "http://www.flamingtext.com";
	}
	
	this.docReady = false;
	
	this.registerChangeEvents();
	this.init();
	
	ft.dynamicForms[id] = this;
	this.test = function() {
		window.alert("this is dynamicform.test()");
		logger.info("ftExtension");
		logger.info(ftExtension);
		ftExtension.test();
		logger.info("called");
	};
}

DynamicForm.prototype.registerChangeEvents = function() {
	if (!isIE6) {
		var df = this;
		
		var changeFunction = function(evt){
			//console.log($(this).prop('type') + " " + evt.type + " event");
			var df = evt.data.df;
			
			if(df.docReady){
				//console.log("doc ready: call checkFormChange");
				//this will help not making too many unneccessary image requests when 2+ parameters change quickly
				setTimeout(function(){
					df.updateHandler.checkFormChange();
				},100);
			}
			else{
				df.updateImmediately = true;
				//console.log("doc NOT ready: call checkFormChange when ready");
				$(document).ready(function() {
					df.updateHandler.checkFormChange();
				});
			}
			//var str = $(this).attr('id')+":fired "+evt.type;
			//console.log(str);
		};
		
		//use live event so that we capture changes made before document.ready
		$(":input",$("#proxyform-"+df.id)).live('change', {df: df}, changeFunction);
		$('textarea,select,input:text',$("#proxyform-"+df.id)).live('keyup', {df: df}, changeFunction);
	}
}

DynamicForm.prototype.init = function() {
	var df = this;
	//when ready
	$(document).ready(function() {
		df.docReady = true;
		
		df.updateHandler = new UpdateHandler(df);
		df.updateHandler.init();
		
		setupBoxes();
		
		if (df.mode == MODE_IMAGEBOT) {
			var changeLogo = function(evt) {
				//window.alert("change:"+$(this)+":"+$(this).val()+":"+id);
				//window.alert("XX"+ "&text="+$("#"+id+"-text").val());
				ftExtension.showLogoPopup(df.id,"script="+$(this).val()+"&text="+$("#"+df.id+"-text").val());
			}
			var initChangeLogo = function() {
				$("#"+df.id+"-changeLogo").change(changeLogo);
			};
			initChangeLogo();
			

		}
		
		if(!isIE6 && df.updateImmediately){
			df.updateHandler.checkFormChange();
		}
		//console.log("id:"+"#proxyform-"+df.id+"selector:"+$('input:text',$("#proxyform-"+df.id)).attr('id'));
	});
};


function UpdateHandler(df) {
	//log ("update handler");
	var ret= {
		dynamicForm: df,
		id: df.id,
		mode: df.mode, // 0 for standard, 1 for svg-edit
		currentHttpRequest: null,
		working: false,
		originalImageSrc: null,
		logoPreviewDivId: "logoPreview-"+df.id,
		imageId: "logoImage-"+df.id,
		statusImageId: "statusImage-"+df.id,
		statusElement: null,
		errorStatusId: "errorStatus-"+df.id,
		imageNoteId: "logoImageNote-"+df.id,
		imageElement: null,
		maxHttpRequestTime: 20000, //timeout for ajax call
		oldFormParams:"",
		currentFormParams:"",
		params:"",
		
		init: function() {
			this.statusElement =  document.getElementById(this.statusImageId);
			this.imageNote = $("#"+this.imageNoteId);
			this.fullSizePreview = false;
			
			if (!this.statusElement) {
				log ("unable to find status element:"+this.statusImageId);
			}
			if (this.mode != MODE_IMAGEBOT) {
				this.imageElement =  document.getElementById(this.imageId);
				if (this.imageElement) {
					var me = this;
					this.imageElement.onload=function() {me.imageLoaded();};
					this.imageElement.onerror=function() {me.imageErrored();};
					this.originalImageSrc = this.imageElement.src;
				} else {
					log ("unable to find image element:"+this.imageId);
				}
			}
			
			
			this.logoPreviewDiv = document.getElementById(this.logoPreviewDivId);
			this._initScroll();
		},

		_initScroll: function(){
			var ie = navigator.appVersion.indexOf("MSIE")!=-1;
			var ieVersion = parseFloat(navigator.appVersion.split("MSIE")[1]);
			
			//does not work for ie7
			if(!ie || ieVersion>=8){
				var top = $(this.logoPreviewDiv).offset().top - parseFloat($(this.logoPreviewDiv).css('marginTop').replace(/auto/, 0));
				$(window).bind('scroll.ft',{self:this},function (e) {
					var updateHandler = e.data.self;
					//if we are in a scalled mode
					if(!updateHandler.fullSizePreview){
						var preview = $(updateHandler.logoPreviewDiv);
						var winY = $(this).scrollTop();
						if (winY >= top) {
							var w = $(".logoPreviewWrapper").outerWidth();
							preview.addClass('logoPreviewFixed');
							preview.css({width:w-2});
						} else{
							preview.removeClass('logoPreviewFixed');
						}
					}
					return false;
				});
				$(window).trigger('scroll.ft');//in case we go away and click back
				this._scaleImage();
			}
			
		},
		updateImage: function(src,params,data) {
			//log("updateImage:mode="+this.mode);
			if (this.imageElement)
				this.imageElement.src = src;
 			else {
				if (this.mode == MODE_IMAGEBOT) {
					updateFtLogo(this.id,src,params,data);
				}
				this.clearStatus();
 				this.working=false;
				if (this.statusElement)
 					this.statusElement.src="http://cdn1.ftimg.com/x.gif";
			}
		},

		failed: function(str) {
			this.setStatus(str);
			log(str);
			this.working=false;
			this.statusElement.src="http://cdn1.ftimg.com/fail.gif"
		},

		imageLoaded: function() {
			//log("image loaded");
			this._scaleImage();
 			this.clearStatus();
 			this.working=false;
 			this.statusElement.src="http://cdn1.ftimg.com/x.gif";
		},
		//scale does both full size as well as scaling depending on the mode
		//this.fullSizePreview - true if mode is "full size image preview", false if mode is "scaled image preview"
		_scaleImage: function(){
			this.imageNote.html("");
			$(this.imageElement).width('auto');
			$(this.imageElement).height('auto');

			//add a note
			if($(this.imageElement).width() > 560 || $(this.imageElement).height() > 200){
				this._addFullSizeNote(!this.fullSizePreview);
			}

			//scaled preview mode
			if(!this.fullSizePreview){
				var scale = 1, scaleW = 1, scaleH = 1;
				var w = $(this.imageElement).width();
				var h = $(this.imageElement).height();
				
				if(w > 560)
					scaleW = 560/w;
				if(h > 200)
					scaleH = 200/h;
				scale = Math.min(1,scaleW,scaleH);
				if(scale<1){
					$(this.imageElement).width(w*scale);
					$(this.imageElement).height(h*scale);
				}
				
				$(window).trigger('scroll.ft');
			}
			else{
				$(this.logoPreviewDiv).removeClass('logoPreviewFixed');
			}
			//set height of the wrapper to be same as previewDiv, so that it looks good when scrolling
			$(".logoPreviewWrapper").height($(this.logoPreviewDiv).height());
		}
		,_addFullSizeNote: function(fullsize){
			var self = this;
			var alert;
			
			if(fullsize){
				alert = $("<div><img src=\"http://cdn1.ftimg.com/images/alert.png\"/> Note: Preview image has been scaled. </div>");
			}
			else{
				alert = $("<div><img src=\"http://cdn1.ftimg.com/images/alert.png\"/> Note: Image shown at full size. </div>");
			}
			var link = $("<a href=\"#\"> Toggle preview size</a>");
			link.bind('click.ft',{df:self, fullsize:fullsize}, function(e){
				e.preventDefault();
				var df = e.data.df;
				df.fullSizePreview = fullsize;
				df._scaleImage();
			});
			this.imageNote.html("");
			this.imageNote.append(alert);
			this.imageNote.append(link);
		}
		,ready: function() {
			return !this.working;
		},
		// Not supportted in IE6
		abortCurrentHttpRequest: function() {
			this.working=false;
			if (!isIE6 && this.currentHttpRequest) this.currentHttpRequest.abort();
		},
		setStatus: function(str) {
			var xx = document.getElementById(this.errorStatusId);
			if (xx)
				xx.innerHTML="<font size='-1' color='red'>"+str+"</font>"
			//else log("no errorStatus div");
		},

		imageErrored:  function() {
 			this.setStatus("Image Errored");
 			this.working=false;
 			this.statusElement.src="http://cdn1.ftimg.com/fail.gif";
		},

		clearStatus: function() {
			var xx = document.getElementById(this.errorStatusId);
			if (xx)
				xx.innerHTML="";
		},

		setWorking: function(val) {
			this.working=val;
		},
		
		checkFormChange: function(){
			//log("Caller:"+arguments.callee.caller.toString());
			var reqAvail = this._isHttpRequestAvailable();
			var imageOutdated = this._isImageOutdated();
			//console.log("_checkFrame:reqAvail="+reqAvail+";imageOutdated="+imageOutdated);
			if(reqAvail && imageOutdated){
				this._getNewImage();
				return true;
			}
			return false;
		
		},
		_isHttpRequestAvailable: function (){			
			var ms = this.httpRequestStartTime ? ((new Date().getTime())-this.httpRequestStartTime) : 0;
			//log("ms="+ms);
			if(!this.working || ms >= this.maxHttpRequestTime){
				return true;
			}
			return false;
		},
		_getFtHost: function() {
			if (df.host) {
				return df.host;
			}
			var ret;
			ret='http://'+document.location.hostname;
			if(document.location.port!=80){
				ret+=":"+document.location.port;
			}
			return ret;
		},
		_isImageOutdated: function (){
			this.currentFormParams = this._getFtHost();
			this.currentFormParams+='/net-fu/image_output.cgi?';
			this.params=buildParams(df.proxyForm,{},df.id+"-");
			this.currentFormParams += this.params + 'imageoutput=true';
			if (this.mode == MODE_IMAGEBOT) {
				this.currentFormParams += '&_dataurl=true';
			}
			
			//console.log("this.currentFormParams="+this.currentFormParams+";this.oldFormParams="+this.oldFormParams+";df.updateImmediately"+df.updateImmediately);
			if(this.currentFormParams != this.oldFormParams){
				return true;
			}
			return false;
		},
		_getNewImage: function() {
			var updateHandler=this;
			// ray: only one http request at a time - so we don't need worry about async image update problem any more
			if (!isIE6 && this.currentHttpRequest) this.currentHttpRequest.abort();
			//TODO: remove this
			//currently happens on ie9 in imagebot when logo options are opened in properties panel and change is triggered for the first time
			if(this.currentFormParams.indexOf('script=')==-1)
				return;
			this.currentHttpRequest =$.ajax({
				//type:"get",
				url:updateHandler.currentFormParams,
				cache:false,
				//async: true, //needed?
				dataType: "json",
				beforeSend: function(req){
					req.updateHandler=updateHandler;
					req.params=updateHandler.params;
					req.updateHandler.setWorking(true);
					req.updateHandler.statusElement.src="http://cdn1.ftimg.com/running.gif";
					req.updateHandler.clearStatus();
					req.updateHandler.oldFormParams=updateHandler.currentFormParams;
				},
				success: function (json_imgObject, textStatus, req) {
					req.updateHandler.setWorking(false);
					//if something changed while this request was processing we need to generate new image and dont update preview
					if(!req.updateHandler.checkFormChange()){
						if (json_imgObject.src) {
							//log("src:"+json_imgObject.src);
							req.updateHandler.updateImage(json_imgObject.src,req.params,json_imgObject.data);
						} else if (json_imgObject.error) {
							req.updateHandler.failed(json_imgObject.error);
						} else {
							req.updateHandler.failed("bad response from server");
						}
					}
				},
				error: function(req,textStatus) {
					//if (req.status) {
						log("error:"+req.status+":"+req.responseText);
						req.updateHandler.failed("http error:"+req.status);
					//}
				}
			});
		}
		

	};
	//log ("done update handler");
	return ret;

}

String.prototype.startsWith = function(str)
{return (this.match("^"+str)==str)};

function log(msg) {
	setTimeout(function() {
		throw new Error(msg);
	}, 0);
}
function logProperties(msg,obj) {
for (var i=0; i < obj.length;i++) {
	element = obj[i];
	name=element.name;
	value= element.value;
	log(msg+":"+name+"="+value);
}
}


function setupBoxes() {
	//log("setup boxes");
	$(".box_title_link, .group_title").find('a').click(function(e){
		e.preventDefault();
	});
	//show/hide based on if we have 'active' class on the title
	$(".box_inner, .box_group").each(function(){
		var isActive = $(this).parent().find(".box_title_link, .group_title").eq(0).hasClass('active');
		if(isActive)
			$(this).show();
		else
			$(this).hide();
	});
	//hide summary for now
	$(".box_summary").hide();
	
	//click handler
	$(".box_title_link, .group_title").click(function(){
		var fn = function(p,hidden){
			p.toggleClass("active");
			var isActive = p.hasClass('active');
			if(isActive)
				p.find('img').prop('src', "http://cdn1.ftimg.com/images/minus.png");
			else
				p.find('img').prop('src', "http://cdn1.ftimg.com/images/plus.png");
			//need to find closest .box_title now since .box_title_link is a child of it
			if(hidden){//for other frames in animator set css because slideToggle does not work when in hidden div
				if(isActive)
					p.closest(".box_title, .group_title").next().css("display","block");
				else
					p.closest(".box_title, .group_title").next().css("display","none");
			}
			else
				p.closest(".box_title, .group_title").next().slideToggle("fast");
		}
		var p = $(this).closest(".box_title_link, .group_title");
		fn(p);
		var isActive = p.hasClass('active');
	
		//if we on animator page
		var curFrame = $("#currentFrameId").length? parseInt($("#currentFrameId").val()) : false;
		if(typeof(curFrame)=='number'){
			var classList = p.attr('class').split(/\s+/);
			var clazz = undefined;
			$.each( classList, function(i, item){
				if (item.indexOf('ft_') == 0) {
					clazz=item;
					return false;
				}
			});
			if(clazz){
				var otherFrames=$("."+clazz).not(p).filter(function(i){
					if(isActive)
						return !$(this).hasClass('active');
					else
						return $(this).hasClass('active');
				});
				$.each(otherFrames,function(i,f){
					fn($(f), true);
				});
			}
		}
	});
}
//el - a parrent element withing which to take parameters, can be a form or a div(in case for animator)
//exclude - an object with names to be excluded 
//eg exclude = {url:1, msSleep:1};
//replaceParam - sometimes we want to strip something from param name, eg "frame0", or "frame\d", ie can use regex here
//retruns params with & at the end, as most of the time we need it anyway

function buildParams(el, exclude, replaceParam){
	var params = "";
	var inputs = $(":input", $(el)).filter(function(index){
		return !exclude[$(this).attr('name')];
	});//find all inputs within el and filter them
	
	$.each(inputs,function(i,v){
		var name = $(v).attr('name');
		var value = $(v).val();
		var add=true;

		if (name && name !=""){
		 	if(replaceParam){
				var re = new RegExp(replaceParam);
				name=name.replace(re,"");
			}
			if (name == "ext")
				value="png";
			else if (name == "extAnim")
				value="gif";
			else if ($(v).prop('type')=="checkbox") {
				value = $(v).prop('checked')?"on":"off";
			}
			else if ($(v).prop('type')=="radio") {
				if (!$(v).prop('checked'))
					add=false;
			}
			if (add)
				params+=name+"="+encodeURIComponent(value)+"&";
			}
	});
	return params;
}
//$(document).ready(setupBoxes);
/*
//dont do this for now.
//simply use val function of hintedInput widget
//reasons if we use myjQuery for all code this will make extra comparison for each val() call
//if we use it only for hintedInput elements then its same as calling $(el).hintedInput("val")

var myjQuery = jQuery.sub();
var orig = jQuery.fn.val;
myjQuery.fn.val = function(newVal) {
	if (typeof newVal == 'undefined' && $(this).data('hintedInput') && !!$(this).hintedInput("isHintOn")) {
		//return empty string instead of hint value
		return "";
	}
	
	//call the original jQuery method
	return orig.apply( this, arguments );
};
*/
(function($){
	//hintedInput
	$.widget("ft.hintedInput", {
		options: {
			hintText:"Type your text here"
			,hintCss:{
				color:"#555"
			}
		}
		,_create: function() {
			if($(this.element).prop('type')!='text')
				return;
			//prepare css to use when hint is not displayed
			this._buildNoHintCss();
			
			$(this.element).bind('focus.hintedInput',{self:this},function(e) {
				e.data.self._hideHint();
			});
			$(this.element).bind('blur.hintedInput',{self:this},function(e) {
				e.data.self._showHint();
			});
			this._showHint();
		}
		,_buildNoHintCss: function(){
			var css = {};
			var el = $(this.element);
			$.each(this.options.hintCss,function(k,v){
				css[k] = el.css(k);
			});
			this.noHintCss = css;
		}
		,_hideHint:function(){
			if(this.hintOn){
				$(this.element).val("").css(this.noHintCss);
				this.hintOn = false;
			}
		}
		,_showHint:function(){
			if($(this.element).val() == ""){
				$(this.element).val(this.options.hintText).css(this.options.hintCss);
				this.hintOn = true;
			}
			else
				this.hintOn = false;
		}
		,isHintOn:function(){
			return this.hintOn;
		}
		,val:function(newVal){
			if(typeof newVal != 'undefined')
				return $(this.element).val(newVal);
			if(this.hintOn)
				return "";
			return $(this.element).val();
		}
		,destroy:function(){
			this.element
				.removeClass('hintedInput')
				.removeData('hintedInput')
				.unbind('.hintedInput');
			$(document).unbind('.hintedInput');
			$.Widget.prototype.destroy.apply(this,arguments);

		}
	});
})(jQuery);
function dcf(fn, fni) {
window.setTimeout('cf("'+fn+'","'+fni+'");',50);
}
function cf(fn,fni) {
var y="";
n=document.getElementById(fn);
x=n.value;
for(i=0;i<x.length;i++){
 if(x.charAt(i)==' ')y=y+'+';
 else y=y+x.charAt(i);
}

var previewW = (typeof(window.dontUseMeAskCameron)!='undefined')? 140 : 330;
var previewH = (typeof(window.dontUseMeAskCameron)!='undefined')? 32 : 75;

if(document.images)if (document.images[n])document.images[n].src='http://cdn1.ftimg.com/fonts/preview/'+previewW+'x'+previewH+'/'+y+'.png';
else document.getElementById(fni).src ='http://cdn1.ftimg.com/fonts/preview/'+previewW+'x'+previewH+'/'+y+'.png';
}
;

var FontsManager = function(opts){
	var queue = [];
	var count = 0;
	var defaults = {
		maxCalls:5
	};
	var options = $.extend({},defaults,opts);
	var isReady = function(){
		return count<options.maxCalls;
	};
	this.callCompleted = function(id){
		count--;
		queue[id]=undefined;
		setTimeout(function(){
			if(isReady()){
				//run from the beginning
				var i=0;
				while(i<queue.length && typeof(queue[i])=='undefined')
					i++;
				if(i<queue.length){
					queue[i]();
				}
				//setTimeout(function(){queue.shift()();},0);
			}
		},10);
		//console.log('complete:'+count+";"+queue.length);
	};
	
	this.run = function(id,fn){
		var runFn = function(){
			count++;
			fn();
		};
		if(isReady())
			runFn();
		else
			queue[id] = runFn;//overwrite with latest
		//console.log('run:'+count+";"+queue.length);
	};
};

(function($){
	//fontManager
	$.widget("ft.fontmanager", {
		options: {
			idPrepend: "font_"
			,oldvalue: null
			,statusImageValue: "http://cdn1.ftimg.com/x.gif"
			,loadingImg: "http://cdn1.ftimg.com/running.gif"
			,errorMessageValue: null
			,url: ''
			,disabled: false
			,maxHttpRequestTime: 20000
			,gettingNewImageLabel: ""//Getting new image
			,inputs:[]
			,conditionalInputs:{
				"script":{
					"banner":{colorTextR:0,colorTextG:0,colorTextB:204,sunkenText:false,textMode:1,textBorder:0,halignText:0,valignText:0}
					,"plain-logo":{colorTextR:0,colorTextG:0,colorTextB:204}
				}
			}
			,getImgOnInit: false
			,defaultText:"abc"
			,fontname:""
			,fontsManager:undefined
		},
		
		_create: function() {
			this._initFontManager();
		},
		
		_initFontManager: function(){
			var o = this.options;

			this.statusDiv = this._initSiblingsDiv("statusDiv", "fontStatusMessage");
			this.errorsDiv = this._initSiblingsDiv("errorsDiv", "fontErrorsMessage");		
			this.imageLoaded = true;
			this.working = false;
			
			$(this.widget()).bind('fontChanged',{self:this}, function(ev){ev.data.self._checkFrame();});
			
			this._initInputs();
			
			
			this._registerChangeEvents();
			this._initPreviewImage();//calls this._checkFrame();
		},
		_initInputs: function(clazz){
			var self = this;
			var o = this.options;
			this.inputs = $([]);
			$.each(o.inputs, function(i,v){
				var jq = $(v);
				if(jq.length)
					self.inputs = self.inputs.add(jq);
			});
		},
		_initSiblingsDiv: function(id,clazz){
			var ret = $(this.element).siblings(clazz);
			if(!ret.length){
				ret = $("<div id=\""+this.options.idPrepend+id+"\" class=\""+clazz+"\"></div>");
				$(this.element).closest('div').append(ret);
			}
			return ret;
		},
		_registerChangeEvents: function (){
			var self = this;
			this.fontmanagerCount = $(":ft-fontmanager").length;
			
			$.each(this.inputs, function(i,v){
				self._registerChangeEvent($(v));
			});
			//if it is a select
			this.inputs.filter('select').each(function(){
				self._registerKeyUpEvent($(this));
			});
			
		},
		_registerChangeEvent: function (el){
			var o = this.options;
			
			el.bind('change.font.changed',{fm:this, fm_count:this.fontmanagerCount}, function(event){
				if(o.disabled)
					return false;

				var fm = event.data.fm;
				
				if(o.fontsManager){
					//console.log('change - run now');
					o.fontsManager.run(event.data.fm_count, function(){$(fm.widget()).trigger('fontChanged');});
				}
				else{
					var n = parseInt($(this).attr("delaycheck")) + event.data.fm_count*100;
					if(!n)
						n = event.data.fm_count*100;
					//totalcalls++;
					//console.log("fm:"+event.data.fm_count+";n="+n+";calls="+totalcalls);
					if(n){
						setTimeout(function(){$(fm.widget()).trigger('fontChanged');},n);
					}
					else{
						$(fm.widget()).trigger('fontChanged');
					}
				}
				//fm._devChange(this);
			});
			
			//for some types we want other than basic 'change' handler
			var type = el.prop('type');
			switch (type){
				case "textarea":
				case "text":
					//console.log($(el).attr('id') + " registering keypress event");
					this._registerKeyUpEvent(el);
					break;
			}			
		},
		_registerKeyUpEvent: function (el){
			var o = this.options;
			el.unbind('keyup.font.changed').bind('keyup.font.changed', {fm:this}, function(event){
				//log("key pressed"+$(this).attr('id'));
				if(o.disabled)
					return false;
				//setTimeout(function(){$(event.data.fm.widget()).trigger('fontChanged');}, event.data.fm_count*50);
				//event.data.fm._devChange(this);
				
				//this is needed because we bind multiple keyups to this but only the last one executes
				$(this).trigger('change.font.changed');
			});			
		},

		_initPreviewImage: function(){
			var o = this.options;
			var self = this;
			this.previewImage = $(this.element);
			this.statusImage = $("#"+this.options.idPrepend+"statusImage");
			//this.errorMessage = $("#"+this.options.idPrepend+"errorMessage");
			
			if (this.previewImage.length){
				this.previewImage.unbind("load error").bind({
					load: function() {
						self._imageLoaded();
					},
					error: function() {
						self._imageErrored();
					}
				});
				if(o.url)
					this._setUrl(o.url);
			}
			
			if (this.statusImage.length){
				this.statusImage.prop('src',o.statusImageValue);
			}
			//if (this.errorMessage.length)
			//	this.errorMessage.html(o.errorMessageValue);
			
			if(o.getImgOnInit){
				//this._checkFrame();
				if(o.fontsManager)
					o.fontsManager.run(this.fontmanagerCount,function(){$(self.element).trigger('fontChanged');});
				else
					$(this.element).trigger('fontChanged');
			}
				
		},
		
		_imageLoaded: function (){
			var o = this.options;
			this._clearStatus();
			this.imageLoaded=true;
			o.statusImageValue = "http://cdn1.ftimg.com/x.gif";
			if (this.statusImage.length)
				this.statusImage.prop('src',o.statusImageValue);
		},
		_clearStatus: function (){
			this.options.errorMessageValue = "";
			//if (this.errorMessage.length)
			//	this.errorMessage.html(this.options.errorMessageValue);
			if (this.errorsDiv.length)
				this.errorsDiv.html(this.options.errorMessageValue).css({'display':'none'});
		},
		_setStatus: function (str){
			this.options.errorMessageValue = str;
			//if (this.errorMessage.length)
			//	this.errorMessage.html("<font size='-1' color='red'>"+this.options.errorMessageValue+"</font>");
			if (this.errorsDiv.length)
				this.errorsDiv.html("<font size='-1' color='red'>"+">>> " + this.options.errorMessageValue+"</font>").css({'display':'block'});
		},
		_failed: function (str){
			var o = this.options;
			this._setStatus(str);
			this.imageLoaded=true;
			o.statusImageValue = "http://cdn1.ftimg.com/fail.gif";
			if (this.statusImage.length)
				this.statusImage.prop('src',o.statusImageValue);
		},
		_imageErrored: function (str){
			var o = this.options;
			this._setStatus("Image Errored: " + str);
			this.imageLoaded=true;
			o.statusImageValue = "http://cdn1.ftimg.com/fail.gif";
			if (this.statusImage.length)
				this.statusImage.prop('src',o.statusImageValue);
		},
		_outdated: function(){
			var reqAvail = this._isHttpRequestAvailable();
			var imageOutdated = this._isImageOutdated();
			return (reqAvail && imageOutdated);
		},
		//return true if new image is generated, flase otherwise
		_checkFrame: function (verification_call){
			if(this._outdated()){
				//console.log("reqAvail && imageOutdated");
				this._getNewImage(verification_call);
				return true;
			}
			else if(this.options.fontsManager && !verification_call)
				this.options.fontsManager.callCompleted(this.fontmanagerCount);
			
			return false;
		},		
		_isHttpRequestAvailable: function (){			
			var ms = this.httpRequestStartTime ? ((new Date().getTime())-this.httpRequestStartTime) : 0;
			//log("ms="+ms);
			if(!this.working || ms >= this.options.maxHttpRequestTime){
				return true;
			}
			return false;
		},
		_isImageOutdated: function (){
			var o = this.options;
			this.currentParams = "/net-fu/image_output.cgi?";
			this.currentParams += this._buildParams();
			this.currentParams += "fontname="+encodeURIComponent(this.options.fontname)+"&"
			this.currentParams += "imageoutput=true";
			//this.currentParams = this._buildParams();
			//log("currentParams:"+this.currentParams);
			//log("oldvalue     :"+this.options.oldvalue);
			if(this.currentParams != this.options.oldvalue){
				return true;
			}
			return false;
		},
		_buildParams:function(){
			var self = this;
			var params = "";
			var replaceParam = this.options.idPrepend;
			$.each(this.inputs,function(i,v){
				var name = $(v).attr('name');
				var value = $(v).val();
				var add=true;

				if (name && name !=""){
				 	if(replaceParam){
						var re = new RegExp(replaceParam);
						name=name.replace(re,"");
					}
					if (name == "ext")
						value="png";
					else if (name == "extAnim")
						value="gif";
					else if ($(v).prop('type')=="checkbox") {
						value = $(v).prop('checked')?"on":"off";
					}
					else if ($(v).prop('type')=="radio") {
						if (!$(v).prop('checked'))
							add=false;
					}
					if (add)
						if(name == "text" && value=='')
							value = self.options.defaultText;
						params+=name+"="+encodeURIComponent(value);
						params+="&textBorder=0";
						params+="&"+self._processConditionalInputs(name,value);
					}
			});
			return params;
		},
		_processConditionalInputs:function(name, value){
			var ret = "";
			
			if(this.options.conditionalInputs[name]){
				var conditionalValues = this.options.conditionalInputs[name];
				if (conditionalValues[value]){
					var inputs = conditionalValues[value];
					$.each(inputs, function(inp,val){
						ret+=inp+"="+encodeURIComponent(val)+"&";
					});
				}
			}
			return ret;
		},
		_getNewImage: function (verification_call){
			var self = this;
			var o = this.options;
			//log("getting new image");
			
			var jqxhr = $.ajax({type:"get",
				url:this.currentParams,
				async: true,
				beforeSend: function(req){
					self.imageLoaded=false;
					self._setWorking(true);
					self.httpRequestStartTime = new Date().getTime();
				},
				timeout:o.maxHttpRequestTime,
				cache:false,
				dataType: "json",
				success: function (json_imgObject, textStatus, req) {
					self._setWorking(false);
					//console.log("getNewImage");
					//if something changed while this request was processing we need to generate new image and dont update preview
					if(!self._checkFrame(true)){
						if (json_imgObject.src) {
							self.options.url=json_imgObject.src;
							self._setUrl(self.options.url);
						} else if (json_imgObject.error) {
							self._failed(json_imgObject.error);
						} else {
							self._failed("bad response from server");
						}
					}
				},
				error: function(req,textStatus,exc) {
					self._setWorking(false);
					//console.log("error:"+textStatus+":"+exc);
					self._failed("error:"+textStatus);
				}
			});
			if(!verification_call && o.fontsManager){
				jqxhr.always(function(){o.fontsManager.callCompleted(this.fontmanagerCount);});
			}
			
			
			o.statusImageValue = o.loadingImg;
			if(this.statusImage.length){
				this.statusImage.prop("src", o.statusImageValue);
			}
			
			this._clearStatus();
			o.oldvalue=this.currentParams;
		},
		_setWorking: function (isWorking){
			var o = this.options;
			//url is no longer valid
			if(isWorking){
				this.working = true;
				this.statusDiv.html(o.gettingNewImageLabel).css({'display':'block'});
			}
			else{
				this.working = false;
				this.statusDiv.html("").css({'display':'none'});
			}		
		},
		_setUrl: function (value){
			var o = this.options;
			o.url = value;
			if(this.previewImage.length)
				this.previewImage.prop("src", o.url);
		},

		
		_setOption: function( key, value ) {
			$.Widget.prototype._setOption.apply( this, arguments );
			if(key=="url"){
				this._setUrl(value);
			}
		},

		hasError: function(){
			if(this.errorsDiv.html()!="")
				return true;
			return false;
		},
		recheckFont: function(){
			this.options.oldvalue = null;
			this._checkFrame();
		}
	});

})(jQuery);

$(document).ready(function(){
	//Set a cookie to remember number of fonts per page on page refresh
	$(".ft-font-controls-drop-down").show();
	$(".ft-font-controls").find("select").bind('change',function(){
		var fontsPp = $(this).val();
		$.cookie('fontsPp', null);
		var domain = document.domain;
		var pos = domain.indexOf('flamingtext');
		if (pos > -1)
		{
		domain = domain.substring(pos);
		}
		else{domain = 'flamingtext.com'}
		$.cookie('fontsPp', fontsPp, {path: '/', domain: '.' + domain});
		$(this).closest('form').submit();
	});
	//Set a cookie to remember the preview string to use
	$('#font_text').bind('change',function(){
		var previewString = $(this).val();
		$.cookie('previewString', null);
		var domain = document.domain;
		var pos = domain.indexOf('flamingtext');
		if (pos > -1)
			domain = domain.substring(pos);
		else
			domain = 'flamingtext.com';
		$.cookie('previewString', escape(previewString), {path: '/', domain: '.' + domain});
	});
	
	if($('#font_text').length){
		var font_text = $.cookie('previewString');
		if(font_text)
			$('#font_text').val(unescape(font_text));
	}
	
	$(".ft-font-specific-logo-suggestion-img").bind('load',function(){
		
		$(this).siblings(".ft-font-specific-loadingDiv").remove();
	});
	
	$(".ft-font-preview-size").find('a').click(function(e){e.preventDefault();});
});

(function($){
	//datapicker widget
	$.widget( "ui.datapicker", {
		options:{
			tags:{
				names:{type:"list"
					,values:[
						{name:"new",title:"New"}
					]
					,showInLeftPanel:true
				}
				,alphabetical:{type:"table"
					, values:[
						{name: "a", title:"A"}
						,{name: "b", title:"B"}
						,{name: "c", title:"C"}
						,{name: "d", title:"D"}
						,{name: "e", title:"E"}
						,{name: "f", title:"F"}
						,{name: "g", title:"G"}
						,{name: "h", title:"H"}
						,{name: "i", title:"I"}
						,{name: "j", title:"J"}
						,{name: "k", title:"K"}
						,{name: "l", title:"L"}
						,{name: "m", title:"M"}
						,{name: "n", title:"N"}
						,{name: "o", title:"O"}
						,{name: "p", title:"P"}
						,{name: "q", title:"Q"}
						,{name: "r", title:"R"}
						,{name: "s", title:"S"}
						,{name: "t", title:"T"}
						,{name: "u", title:"U"}
						,{name: "v", title:"V"}
						,{name: "w", title:"W"}
						,{name: "x", title:"X"}
						,{name: "y", title:"Y"}
						,{name: "z", title:"Z"}
						,{name: "0", title:"#"}
					]
					, columns:5
					,showInTopPanel:true
					,showInLeftPanel:false
				}
			}
			,position:function(e,ui){
				//default is center of the screen	
				var p = ui.main;
				var pWidth = p.outerWidth();
				var pHeight = p.outerHeight();
				var top = ($(window).height() -pHeight)/2 + $(window).scrollTop() + "px";
				var left = ($(window).width() -pWidth)/2 + $(window).scrollLeft() + "px";
				p.css({
					top	:top
					,left	:left
				});
			
			} //where to display this widget, use ui.main to update top/left
			,ui:{
				header:"Data Picker"
				,showOutline:false
				,min_width:200
				,footer:{
					autocloseLabel:"Auto Close"
					,okayLabel:"Okay"
					,cancelLabel:"Cancel"
				}
				,previousTagLabel: "Previous"
				,topPanel:{
					show:false
					,showAlphabet:true
					,search:{
						enabled:true
						,searchLabel:"Search"
						,goLabel: "Go"
						,runAsYouType:true
						,searchAll:true//if false searches withing current tag; TODO: ui element(checkbox) to change this value
					}
					,closeLabel: "Close"
				}
				,leftPanel:{
					specialTags:{
						previous:{enabled:false,tagname:"dp_tag_previous",ui:"Previous"}
						,search:{enabled:true,tagname:"dp_tag_search",ui:"Search"}
					}
				}
				,rightPanel:{
					containerClass:"dp_div"
					,pagination:{
						itemsPerPage:10
						,pageLinks:5// so will show  something like: << < 7 8 9 10 11 > >>
					}
				}
			}
			//,showPreviousTag:true
			//,previousTagName: "dp_tag_previous"
			,controlElement:null //element that opens/closes this dialog
			,init:function(){}//to be defined in specific implementations
			,select:function(){}//to be defined in specific implementations
			,cancel:function(){}//to be defined in specific implementations
			,close:function(){}//to be defined in specific implementations
			,failed:function(){}//to be defined in specific implementations
			,autoclose:true
			,cacheAjaxResults:false //TODO
			,origValue:undefined//original value
			,initialTag:undefined//tag to select on init
		}
		,_create: function(){
			//list and describe variables here
			this.preview = undefined;//jquery object representing whole datapicker
			this.preview_outline = undefined;//jquery object representing outline (child of this.preview)
			this.preview_container = undefined;//jquery object representing container (child of this.preview_outline)
			this.preview_header = undefined;//jquery object representing header (child of this.preview_container)
			this.preview_body = undefined;//jquery object representing body (child of this.preview_container)
			this.preview_footer = undefined;//jquery object representing footer (child of this.preview_container)
			
			this.left_panel = undefined;//jquery object representing left panel of the body (child of this.preview_body)
			this.right_panel = undefined;//jquery object representing right panel of the body (child of this.preview_body)
			
			this.tags = undefined; //jquery object representing all the tags
			this.autoclose;//booolean if autoclose is set
			
			this.selectedTag = undefined;//currently selected tag
			this.previousTag = undefined;//previously selected tag
			this.selectedItem = undefined;//currently selected item
			this.prevItem = undefined;//previously selected item
			//this.previousItems = $([]);//jquery object representing previously selected items
			
			this.origValue = (this.options.origValue)? this.options.origValue : undefined;// original value
			this.initialTag = (this.options.initialTag)? this.options.initialTag : undefined;// initialTag value
			this.currentTag = this.initialTag;// selectedItem's tag
			this.items = {};//key = category name, value = dom element representing right_panel contents
			this.fn_queue = {};//queue of function to run when item is ready, ie user has to trigger 'item_ready' manually
			this.tagsProcessed = {};//processed tags
			this.working=false;
			
			this.imagebot = (typeof(window.dontUseMeAskCameron)!='undefined')? window.dontUseMeAskCameron : {};
			
			this._buildUI();
			this._cssFix();
			
			//user-specific behaviour(like updating other plugin-related objects, eg hidden field which stores the value of the selected font)
			if ($.isFunction(this.options.init)){
				this._trigger('init', null, this._ui());
			}
		}
		,_init: function(){}
		,_ui: function(){
			var self = this;

			var orig = {
				value:this.prevItem? this.prevItem.attr('dp_value'):this.origValue
				,escapedValue: self._escapedValue(this.prevItem? this.prevItem.attr('dp_value'):this.origValue)
				,tagname: this.initialTag
				,img_url: this.prevItem? this.prevItem.find('img').attr('src'):""
			};
			var selected = {
				value:this.selectedItem? this.selectedItem.attr('dp_value'):this.origValue
				,escapedValue: self._escapedValue(this.selectedItem? this.selectedItem.attr('dp_value'):this.origValue)
				,tagname: this.currentTag
				,img_url: this.selectedItem? this.selectedItem.find('img').attr('src'):""
			};
			
			return {
				main: this.preview
				,selected: selected
				,original: orig
				,controlElement:this.options.controlElement //in case we need to update it as well
				,escapeFn:this._escapedValue
			};
		}
		,_buildUI: function(){
			var o = this.options;
			this._createMain();
			this._createHeader();
			this._createBody();
			this._createFooter();
			
			//this._setWidths();//for cross-browser
			
			this._initControlElement();
			
			//hide now
			this.preview.hide();
			this.preview.css({visibility:'visible'});
		}
		,_cssFix: function(){
			var ie = navigator.appVersion.indexOf("MSIE")!=-1;
			var ieVersion = parseFloat(navigator.appVersion.split("MSIE")[1]);
			var ie9 = ie && ieVersion>=9 && ieVersion<10;
			
			if(ie9){
				this.preview_footer.add(this.preview_header).css({filter:"none"});
			
			}
			
		}
		,_initControlElement: function(){
			var o = this.options;
			var self = this;
			var c = o.controlElement;
			if(typeof(c) !='undefined' && c.length){
				c.addClass("dp_control_element");
				c.click(function(e){
					e.preventDefault();
					
					//is this opened now, get value here, because it will be different after $(document).trigger('click.datapicker')
					var open = self.preview.hasClass('dp_open');
					//this is to make sure click is fired for other(and this one) pickers to close
					$(document).trigger('click.datapicker');
					//this is to prevent form being submitted by input type:image
					e.stopImmediatePropagation();					
					if(open){
						self._close();
					}
					else{
						self._open();
					}
				});
			
			}
		}
		,_close: function(cancelled){
			var self = this;
			var o = this.options;
			if ($.isFunction(o.close)){
				self._trigger('close', null, self._ui());
			}
			
			if(!cancelled)
				this._addToPreviousItems();
			this.initialTag = this.currentTag;
			
			var key = this._getKey(this.currentTag);
			this.items[key].pageWithSelection = this.items[key].pageWithSelectionUnconfirmed;
			
			//this.preview.css({visibility:'hidden'});
			this.preview.hide();
			this.preview.removeClass('dp_open');
			//remove all bound events from the document
			$(document).unbind('.datapicker');
		}
		,_open: function(){
			var self = this;
			var o = this.options;

			//postion on open as window might have moved/resized
			if ($.isFunction(o.position)){
				this._trigger('position', null, this._ui());
			}
			
			if ($.isFunction(o.open)){
				self._trigger('open', null, self._ui());
			}
			
			//this.preview.css({visibility:'visible'});
			this.preview.show();
			this.preview.addClass('dp_open');
			
			$(document).bind('click.datapicker',{dp:self}, function(e){
				if (!$(e.target).parents().andSelf().is(e.data.dp.preview)) {
		                	e.data.dp._close();
		        	}
			});
			$(document).bind('keyup.datapicker',{dp:self}, function(e){
				var dp = e.data.dp;
				if (e.keyCode == 27 && dp.preview.hasClass('dp_open')){
					dp._close();
				}
			});
			
			this._selectTag_string(this.initialTag);
			
		}
		,_cancel:function(){
			var self = this;
			var o = this.options;
			if ($.isFunction(o.cancel)){
				self._trigger('cancel', null, self._ui());
			}
			if(typeof(this.selectedItem) != 'undefined')
				this.selectedItem.removeClass('selectedItem');
			
			//restore orig value here
			this.selectedItem = this.prevItem;
			this.currentTag = this.initialTag;
			
			//ensure we have correct item for selectedItem css class
			this.right_panel.find('.selectedItem').removeClass('selectedItem');
			
			var val = (typeof(this.selectedItem)!='undefined' && this.selectedItem.length)? this.selectedItem.attr('dp_value') : this.origValue;
			if(val){
				var key = this._getKey(this.currentTag);
				var pageWithSelection = this.items[key].pageWithSelection ||0;
				this.items[key].pageWithSelectionUnconfirmed = pageWithSelection;
				var page = this.items[key].pages[pageWithSelection].cache;
								
				this.selectedItem = page.find('[dp_value="'+val+'"]').addClass('selectedItem');
				this.right_panel.find('[dp_value="'+val+'"]').addClass('selectedItem');
			}
			
			
			this._close(true);
		}
		,_failed:function(status,empty,tag){
			var self = this;
			var o = this.options;
			//user-specific behaviour(like updating other plugin-related objects, eg hidden field which stores the value of the selected font)
			if ($.isFunction(o.failed)){
				self._trigger('failed', null, self._ui());
			}
			
			if(typeof empty != 'boolean')
				empty=true;
			if(empty && this.selectedTag.is(tag))
				this._tagListEmpty(tag);
				
			if(tag.attr('tagname')!=o.ui.leftPanel.specialTags.search.tagname)
				throw new Error("datapicker failed: "+status);
		}
		,_showSearchHint:function(tag){
			var key = this._getKey(tag);
			var div = this._tagListEmpty(tag).html("Please type in the search box");
			var emptyDiv = $("<div></div>");
			this.items[key] = {
				pageWithSelection:0//if we have selected item in this tag, this is the page the item is in
				,pageWithSelectionUnconfirmed:0//currently selected item's page
				,currentPage:0//currently displayed page
				,container:emptyDiv
				,pages:[{items:["noresults"],cache:emptyDiv.append(div.clone())}]
			};
		}
		,_saveSearchEmpty:function(status,empty,tag, searchTerm){
			var key = this._getKey(tag, searchTerm);
			var div = this._tagListEmpty(tag);
			var emptyDiv = $("<div></div>");
			this.items[key] = {
				pageWithSelection:0//if we have selected item in this tag, this is the page the item is in
				,pageWithSelectionUnconfirmed:0//currently selected item's page
				,currentPage:0//currently displayed page
				,container:emptyDiv
				,pages:[{items:["noresults"],cache:emptyDiv.append(div.clone())}]
			};
		}
		,_tagListEmpty:function(tag){
			this.shownSearch="";
			var str = "Category empty.";
			if(tag.attr('tagname')==this.options.ui.leftPanel.specialTags.search.tagname)
				str = "No search results";
			var div = $("<div class=\"dp_failed_div\">"+str+"</div>");
			this.right_panel.html(div);
			return div;
		}
		,_createMain: function(){
			var o = this.options;
						
			this.main = $("<div class=\"dp_main\"></div>").appendTo('body');
			this.preview = $("<div class=\"dp_preview_dialog\"></div>").appendTo(this.main);
			//make it draggable, but disallow dragging dp_preview_body because it has scroll bars which need to be dragged
			this.preview.draggable({cancel:".dp_preview_body"});
			
			this._buildOutline();
			this.preview_container = $("<div class=\"dp_preview_dialog_container\"></div>").appendTo(this.preview_outline);
		}
		,_buildOutline:function(){
			var o = this.options;
			this.preview_outline = $("<div class=\"dp_preview_dialog_outline\"></div>").appendTo(this.preview);
			
			//dont show for IE8 and lower
			if(o.ui.showOutline && 
				(this.imagebot.length || (navigator.appVersion.indexOf("MSIE")!=-1 && parseFloat(navigator.appVersion.split("MSIE")[1])<9))
				){
				o.ui.showOutline = false;
			}
			
			if(!o.ui.showOutline){
				this.preview_outline.css({
					padding:'0px'
					,border:'0px'
				});
			}
		}
		,_createHeader: function(){
			var o = this.options;
			var self = this;
			this.preview_header = $("<div class=\"dp_preview_header\">"+o.ui.header+"</div>").appendTo(this.preview_container);
			var close = $("<div class=\"dp_preview_dialog_close\"></div>").appendTo(this.preview_header);
			var img = $("<img src='http://cdn1.ftimg.com/images/stock_delete.png' title='"+o.ui.topPanel.closeLabel+"'/>").appendTo(close);

			close.bind('click.datapicker',{self:self}, function(e){
				e.data.self._close();
			});
			
			
			o.ui.min_width = o.ui.min_width>this.preview_header.width() ? o.ui.min_width : this.preview_header.width();
		}
		,_createBody: function(){
			var o = this.options;
			
			this.preview_body = $("<div class=\"dp_preview_body\"></div>").appendTo(this.preview_container);
			/*this.preview_body.css({
				width:o.css.dimension.width
				,height:o.css.dimension.height
			});
			*/
			this.tags = $([]);
			
			this._createTopPanel();
			this._createLeftPanel();
			this._createRightPanel();
			
			o.ui.min_width = o.ui.min_width>this.preview_body.width() ? o.ui.min_width : this.preview_body.width();
			var body_h = (o.ui.topPanel.show? this.top_panel.outerHeight():0) + this.left_panel.outerHeight();
			this.preview_body.css('height',body_h);
				
		}
		,_createTopPanel: function(){
			var o = this.options;
			if(!o.ui.topPanel.show){
				var tags = o.ui.leftPanel.specialTags;
				for(var key in tags){
					tags[key].enabled=false;
				}
				return;
			}
			this.top_panel = $("<div class=\"dp_top_panel\"></div>").appendTo(this.preview_body);
			
			this._buildSearchPanel();
			this._buildTopPanelAlphabet();
		}
		,_buildSearchPanel:function(){
			var o = this.options;
			if(!o.ui.topPanel.search.enabled){
				o.ui.leftPanel.specialTags.search.enabled=false;
				return;
			}
			this.top_panel_search = $("<div class=\"dp_top_panel_search\"></div>").appendTo(this.top_panel);
			this.search_field = $("<input class=\"search_field\" value=\"\"/>").appendTo(this.top_panel_search);
			this.search_field.hintedInput({
				hintText:"Search"
				,hintCss:{
					color:"#555"
				}
			});
			if(o.ui.topPanel.search.runAsYouType){
				this._registerSearchFieldChanges();
			}
			/*//no go button for now
			var go = $("<input class=\"go_btn\" type=\"button\" value=\""+o.ui.topPanel.search.goLabel+"\"/>").appendTo(this.top_panel_search);
			go.bind('click.datapicker',{self:this},function(e){
				//e.data.self._runSearch();
				e.data.self.search_field.trigger('change.datapicker');
			});
			*/
		}
		,_registerSearchFieldChanges:function(){
			this.search_field.bind('change.datapicker',{self:this}, function(e){
				var val = $(this).hintedInput("val");
				if(val=="") return;

				var self = e.data.self;
				if((!self.runningXhr && (!self.shownSearch || self.shownSearch!=val))//shown search results are for a different search term
					||(self.runningXhr && self.runningXhr.searchTerm != encodeURIComponent(val))//current ajax call search is different
					||self.selectedTag.attr('tagname')!=self.options.ui.leftPanel.specialTags.search.tagname//we are on a different tag
				){
					if(self.runningXhr)
						self.runningXhr.abort();
					e.data.self._runSearch();
				}
				
			}).bind('keyup.datapicker',{self:this}, function(e){
				var val = $(this).val();
				var delay = 100;
				//its slow for 1-letter searches, so lets delay it more, ie we assume user will type more letters
				if(val.length<2)
					delay=500;
				
				setTimeout(function(){
					e.data.self.search_field.trigger('change.datapicker');
				},delay);//to reduce number of calls

			});
		}
		,_runSearch:function(){
			var searchTagname = this.options.ui.leftPanel.specialTags.search.tagname;
			this._selectTag_string(searchTagname);
		}
		,_buildTopPanelAlphabet: function(){
			var alphabet = this.options.tags.alphabetical;
			if(alphabet.showInTopPanel && this.options.ui.topPanel.showAlphabet){
				var container = $("<div class=\"dp_top_panel_alphabet\"></div>").appendTo(this.top_panel);
				var letters = alphabet.values;
				if(letters && letters.length){
					var ul = $("<ul class=\"dp_top_panel_alphabet_ul\"></ul>").appendTo(container);
					var self = this;
					$.each(letters, function(i,c){
						if(!c.title){
							c.title = c.name;
						}
						var li = $("<li tagname=\""+c.name+"\" title=\""+c.title+"\">"+c.title+"</li>").appendTo(ul);
						self._addTag(li);
					});
				
				}
			}
		}
		,_createLeftPanel: function(){
			var self = this;
			var o = this.options;
			this.left_panel = $("<div class=\"dp_left_panel\"></div>").appendTo(this.preview_body);
			/*this.left_panel.css({
				width:o.css.dimension.leftPanel.width
				,height:o.css.dimension.leftPanel.height
			});
			*/
			this._addTags();
		}
		,_addTags: function(){
			var self = this;
			var o = this.options;
			var tags = o.tags;
			
			this._addSpecialTags();
			
			$.each(tags, function(k,tag){
				if(tag.showInLeftPanel){
					var t = tag.type;
					
					if(t=="list"){
						self._buildTagList(tag);
					}
					else if(t=="table"){
						self._buildTagTable(tag);
					}
				}
			});
		
		}
		,_addSpecialTags:function(){
			var o = this.options;
			
			var haveTags = false;
			var tags = o.ui.leftPanel.specialTags;
			for(var key in tags){
				var t = tags[key];
				if(t.enabled){
					haveTags = true;
					break;
				}
			}
			if(!haveTags)
				return;
			
			
			var div = $("<div class=\"dp_tag_div\"></div>").appendTo(this.left_panel);
			var ul = $("<ul class=\"dp_tag_list\"></ul>").appendTo(div);
			for(var key in tags){
				var t = tags[key];
				if(t.enabled){
					t.items = $();
					var tag = $("<li tagname=\""+t.tagname+"\" title=\""+t.ui+"\">"+t.ui+"</li>").appendTo(ul);
					this._addTag(tag);
				}	
			}
		}
		,_buildTagList:function(tag){
			var self = this;
			var o = this.options;
			if(tag.values && tag.values.length){
				var div = $("<div class=\"dp_tag_div\"></div>").appendTo(this.left_panel);
				var ul = $("<ul class=\"dp_tag_list\"></ul>").appendTo(div);
				$.each(tag.values, function(i,c){
					if(!c.title)
						c.title = c.name;
					var li = $("<li tagname=\""+c.name+"\" title=\""+c.title+"\">"+c.title+"</li>").appendTo(ul);
					self._addTag(li);
				});
			}
		}
		,_buildTagTable:function(tag){
			var self = this;
			var o = this.options;
			if(typeof tag.values != 'undefined' && tag.values.length){
				var div = $("<div class=\"dp_tag_div\"></div>").appendTo(this.left_panel);
				var table = $("<table class=\"dp_tag_table\"></table>").appendTo(div);
				var tbody = $("<tbody></tbody>").appendTo(table);
				var columns = tag.columns;
				var rows = Math.ceil(tag.values.length / columns);
				
				for(var ir = 0; ir<rows;ir++){
					var tr = $("<tr></tr>").appendTo(tbody);
					for(var ic = 0; ic<columns;ic++){
						var index = ir*columns+ic;
						if(index < tag.values.length){
							var c = tag.values[index];
							if(typeof(c.title) == 'undefined'){
								c.title = c.name;
							}
							//console.log("index:"+index+";len:"+tag.values.length+";val:"+c.name);
							var td = $("<td tagname=\""+c.name+"\" title=\""+c.title+"\">"+c.title+"</td>").appendTo(tr);
							self._addTag(td);
						}
						else break;
					}
				}
			}
		}
		,_addTag:function(tag){
			var self = this;
			var o = this.options;
			tag.click(function(e){
				self._selectTag($(this));
			});
			this.tags = this.tags.add(tag);
		}
		,_selectTag_string: function(tag){
			if(typeof(tag)=='string' && tag.length){
				var $tag = this.tags.filter('[tagname="'+tag+'"]');
				this._selectTag($tag);
			}
		}
		,_isSpecialTag:function(tagname){
			var tags = this.options.ui.leftPanel.specialTags;
			for(var t in tags){
				if(tagname == tags[t].tagname)
					return true;
			}
			return false;
		}
		,_selectTag:function(tag){
			var self = this;
			var o = this.options;
			var key = this._getKey(tag);
			
			if(typeof(this.selectedTag)=='undefined' || !this.selectedTag.is(tag)
				|| (this.items[key] && this.items[key].currentPage != this.items[key].pageWithSelection)
				||(tag.attr('tagname')==o.ui.leftPanel.specialTags.search.tagname && this.shownSearch != this.search_field.hintedInput("val"))){
				//do NOT update previous tag if we are already on search tag
				if(!this.selectedTag || (tag.attr('tagname')!=o.ui.leftPanel.specialTags.search.tagname && !this.selectedTag.is(tag)))
					this.previousTag = this.selectedTag;
				//TODO: save all specialTag results
				/*
				if(typeof(this.previousTag)!='undefined' && this._isSpecialTag(this.previousTag.attr('tagname'))){
					//clone this because right_panel is about to be emptied, but we need to remember these items(with events)
					//this.previousItems = this.previousItems.clone(true,true);
					var specialTag = o.ui.leftPanel.specialTags[this._getSpecialTagKeyByTagname(this.previousTag.attr('tagname'))];
					specialTag.items = specialTag.items.clone(true,true);
				}*/
				this.selectedTag = tag;
				
				this.right_panel.empty();
				this._setLoading(true,tag);
				
				if(this.selectedTag.attr('tagname')==o.ui.leftPanel.specialTags.search.tagname)
					this.shownSearch = this.search_field.hintedInput("val")||this.shownSearch||"";

				var key = this._getKey(tag);
				var page = undefined;
				if(this.items[key]){
					this.items[key].currentPage = this.items[key].pageWithSelection;
					var currentPage = this.items[key].currentPage;
					page = this.items[key].pages[currentPage].cache;
				}
						
				if(o.cacheAjaxResults && typeof(this.selectedTag)!='undefined' && this.selectedTag.attr('tagname') != o.ui.leftPanel.specialTags.previous.tagname //dont cache "previous" tag
					&& typeof(page)!='undefined'){
					
					//if we have same item in 2+ tags, ensure we always have 1 selectedItem(in the current tag) and 'selectedItem' class is updated correctly
					//eg.Featured, select chrominium,then open Light, chrominium is there too so it is set as selectedItem(as part of first time fetch)
					//now when we go back to Featured it uses cache. At this point we need to ensure selectedItem is reset to the cached item in Featured tag,
					//otherwise it will not be selected in right panel on display.
					if(this.selectedItem){
						
						var newSelectedItem = page.find('[dp_value="'+self.selectedItem.attr('dp_value')+'"]');
						if(newSelectedItem.length){
							this.selectedItem.removeClass('selectedItem');
							this.selectedItem = newSelectedItem.addClass("selectedItem");
							this.currentTag = this.initialTag = this.selectedTag.attr('tagname');
						}
					}
					
					this._showItems(this.selectedTag);
				}
				else{
					this._getData(tag,(this.selectedItem && this.selectedItem.length)?this.selectedItem.attr('dp_value'):this.origValue);
				}
				
				
				this.tags.not(tag).removeClass('selectedTag');
				tag.addClass('selectedTag');
			}
		}
		,_saveItems:function(opts){
			var values = opts.values;//array
			var tag = opts.tag;
			
			var container = $("<div></div>");
			if(this.options.ui.rightPanel.containerClass)
				container.addClass(this.options.ui.rightPanel.containerClass);
			
			var key = this._getKey(tag,opts.searchTerm);
			var itemsPerPage = this.options.ui.rightPanel.pagination.itemsPerPage;
			var size = values.length;
			var pages = Math.ceil(size/itemsPerPage);
			
			var arr = [];
			for(var i=0;i<pages;i++){
				var minIndex = i*itemsPerPage;
				var maxIndex = Math.min(minIndex+itemsPerPage,size);
				arr[i] = {
					items: values.slice(minIndex,maxIndex)
					,cache:undefined
				}
			}
			
			this.items[key] = {
				pageWithSelection:0//if we have selected item in this tag, this is the page the item is in
				,pageWithSelectionUnconfirmed:0//currently selected item's page
				,currentPage:0//currently displayed page
				,container:container
				,pages:arr
			};
		}
		,_getKey:function(tag, searchTerm){
			var key = $.type(tag)=="string"?tag:tag.attr('tagname');
			if(key==this.options.ui.leftPanel.specialTags.search.tagname){
				var srch = searchTerm ? searchTerm:(this.search_field.hintedInput("val")||this.shownSearch||"");
				key+="&q="+srch;
			}
			//console.log("key:"+key);
			return key;
		}
		,_loadItems:function(key,currentPage){
			var page = this.items[key].pages[currentPage];
			var items = page.cache.children();
			var self = this;
			$.each(items,function(i,item){
				if(!$(item).hasClass('dp_item_ready'))
					self._loadItem($(item));
			});
			
			
		}
		,_loadItem:function(div){}//to de defined by extending classes
		,_showItems:function(tag){
			if(!tag)
				tag = this.selectedTag;
			if(!this.selectedTag.is(tag))
				return;
			var key = this._getKey(tag);
			if(!$.isPlainObject(this.items[key]) || !this.items[key].pages.length)
				throw new Error("Items are not saved,key:"+key);
			
			var currentPage = this.items[key].currentPage;
			var page = this.items[key].pages[currentPage];
			
			//page not cached
			if(typeof(page.cache) == 'undefined')
				this._cachePage(tag,key,currentPage);
			
			
			//other category might have been selected while this has not loaded
			if(this.selectedTag.is(tag)){
				this._displayItems(key,currentPage);
				
				if(this.options.ui.leftPanel.specialTags.search.tagname == tag.attr('tagname'))
					this.shownSearch = this.search_field.hintedInput("val")||this.shownSearch;
			}
			
		}
		,_displayItems:function(key,currentPage){
			var page = this.items[key].pages[currentPage];
			var items = page.cache;
			
			if(typeof(items)=='undefined')
				throw new Error("items cache for page "+currentPage+" is empty");
			
			
			var paginationPanel = this._buildPagination(this.items[key].pages.length, this.items[key].currentPage,key);
			
			//this may be delayed untill all(or first) items are actually loaded
			this._itemsLoaded(this.selectedTag, (items.length?false:true));
			
			var clone = items.clone(true,true).css({
				position:''
				,top:''
				,visibility:''
			});
			
			this.right_panel.empty()
				.append(this._buildInfoPanel(items))
				.append(paginationPanel)
				.append(clone)
				.append(paginationPanel.clone(true,true));
			
			this._loadItems(key,currentPage);
		}
		,_cachePage: function(tag,key,currentPage){
			var page = this.items[key].pages[currentPage];
			var container = this.items[key].container.clone();
			
			if(container && container.length){
				var self = this;
				container.css({
					position:'absolute'
					,top:'-10000px'
					,visibility:'hidden'
				}).appendTo(this.main);
				var items = page.items;
				
				//build dom and assign events
				$.each(items, function(i,item){
					var $item = self._buildItem(item, tag, container);//defined by extending class
					self._bindItemEvents($item,key,currentPage);
					
					var sel = (self.selectedItem && self.selectedItem.length)?self.selectedItem.attr('dp_value'):self.origValue;
					if(sel && item==sel){
						//if we have selectedItem in this tag too, reassing selectedItem
						if(self.selectedItem)
							self.selectedItem.removeClass('selectedItem');
						self.currentTag = self.initialTag =  tag.attr('tagname');
						self.selectedItem = $item.addClass("selectedItem");
					}
				});
				
				page.cache = container;
			}
			
		
		}
		,_buildItem:function(item, tag, container){}//how to display an item - defined by extending class
		,_buildInfoPanel:function(items){
			/*var n = items.children().length;
			var ret = $('<div class="dp_items_info"></div>');
			var pageMsg = $('<div class="dp_items_info_totalMsg">Total: '+n+' item'+(n>1?'s':'')+'</div>').appendTo(ret);
			*/
			
			if(this.options.ui.leftPanel.specialTags.search.tagname != this.selectedTag.attr('tagname'))
				return;
			
			var ret = $('<div class="dp_items_info"></div>');
			var pageMsg = $('<div class="dp_items_info_totalMsg">Search: "'+this.shownSearch+'"</div>').appendTo(ret);
			
			return ret;
		}
		,_buildPagination:function(len, currentPage, key){
			var ret = $();
			if(len<2)
				return ret;
			
			var pageToShow = currentPage+1;
			
			var pageLinks = this.options.ui.rightPanel.pagination.pageLinks;
			
			ret = $('<div class="dp_pagination"></div>');
			var pageMsg = $('<div class="dp_pagination_pageMsg">Page '+pageToShow+' of '+len+'</div>').appendTo(ret);
			
			var buttons = $('<div class="dp_pagination_buttons"></div>').appendTo(ret);
			//if(currentPage!=0)
				//var first = $('<button class="dp_pagination_button first_page" value="0">First</button>').appendTo(buttons);
			var prev = $('<button class="dp_pagination_button prev_page" value="'+(currentPage==0?len-1:currentPage-1)+'">&lt;</button>').appendTo(buttons);
			
			/*
			var pageLinksToTheLeft = Math.floor((pageLinks-1)/2);
			var pageLinksToTheRight = pageLinks - pageLinksToTheLeft -1;
			//max pages to the left and to the right of selected page
			if(currentPage-pageLinksToTheLeft<0){
				pageLinksToTheLeft = currentPage-1;
				pageLinksToTheRight = Math.min(pageLinks - pageLinksToTheLeft -1,len-currentPage);
			}
			else if(currentPage+pageLinksToTheRight>len){
				pageLinksToTheRight = len-currentPage;
				pageLinksToTheLeft = Math.min(pageLinks - pageLinksToTheRight -1,currentPage-1);
			}
			this._buildPageLinksLeft(pageLinksToTheLeft,currentPage,len,buttons);
			var selected_button = $('<button class="dp_pagination_button selected_page" value="'+currentPage+'">'+pageToShow+'</button>').appendTo(buttons);
			this._buildPageLinksRight(pageLinksToTheRight,currentPage,len,buttons);
			*/
			var next = $('<button class="dp_pagination_button next_page" value="'+(currentPage==len-1?0:currentPage+1)+'">&gt;</button>').appendTo(buttons);
			//if(currentPage!=len-1)
			//	var last = $('<button class="dp_pagination_button last_page" value="'+(len-1)+'">Last</button>').appendTo(buttons);
			
			buttons.children('button').bind('click.datapicker',{self:this, currentPage:currentPage, key:key},function(e){
				if(e.data.currentPage == parseInt($(this).val()))
					return;
				e.stopPropagation();
				
				var self = e.data.self;
				self._setLoading(true,self.selectedTag);
				self.items[e.data.key].currentPage = parseInt($(this).val());
				self._showItems(self.selectedTag);
			});
			
			
			return ret;
		}
		/*
		,_buildPageLinksLeft:function(n,pageToShow,pages,buttons){
			if(pageToShow-n>1)
				var dots = $('<span class="dp_pagination_dots">..</span>').appendTo(buttons);
			while(n>0){
				var button = $('<button class="dp_pagination_button" value="'+(pageToShow-n)+'">'+(pageToShow-n)+'</button>').appendTo(buttons);
				n--;
			}
		}
		,_buildPageLinksRight:function(n,pageToShow,pages,buttons){
			var i = 1;
			while(i<=n){
				var button = $('<button class="dp_pagination_button" value="'+(pageToShow+i)+'">'+(pageToShow+i)+'</button>').appendTo(buttons);
				i++;
			}
			if(pageToShow+n<pages)
				var dots = $('<span class="dp_pagination_dots">..</span>').appendTo(buttons);

		}*/
		,_bindItemEvents: function(item,key,currentPage){
			var self = this;
			item.bind('item_ready',{dp:self}, function(e){
				$(this).addClass('dp_item_ready');
				var dp = e.data.dp;
				//ensure right panel has updated(ready) item
				var val = $(this).attr('dp_value');
				var displayedItem = dp.right_panel.find('[dp_value="'+val+'"]');
				if(displayedItem.length){
					var clone = $(this).clone(true,true);
					var prev = displayedItem.prev();
					var parent = displayedItem.parent();
					displayedItem.remove();
					
					if(prev.length)
						clone.insertAfter(prev);
					else
						parent.prepend(clone);
				}
				
				if(dp.fn_queue[val]){
					while(dp.fn_queue[val].length){
						dp.fn_queue[val].shift()();//shift and run the fn from the queue
					}
				}
			});
			
			item.bind('click.datapicker',{dp:self, item:item, key:key,currentPage:currentPage},function(e){
				//NOTE: we use 'item' not $(this), because we want to select in items cache.
				//items cache will be cloned wiht data/events so this way the correct item is used within a handler
				
				e.data.dp.items[e.data.key].pageWithSelectionUnconfirmed = e.data.currentPage;
				e.data.dp._selectItem(e.data.item);
			
			});
		}
		,_setLoading:function(loading, tag){
			if(loading){
				var searching = this.options.ui.leftPanel.specialTags.search.tagname == tag.attr('tagname');
				var msgText="Loading: "+tag.attr('title');;
				if(searching)
					msgText = "Searching: <p>\""+ this.search_field.val()+"\"</p>";
				
				var loadingDiv = $("<div class=\"dp_loading_div\"></div>").appendTo(this.right_panel);
				var msg = $("<div class=\"dp_loading_message\">"+msgText+"</div>").appendTo(loadingDiv);
				var img = $("<img class=\"dp_loading_indicator\" src=\"/images/loading.gif\"/>").appendTo(loadingDiv);
				this.working=true;
			}
			else if(tag.attr('tagname') == this.selectedTag.attr('tagname')){
				this.right_panel.empty();
				this.working=false;
			}
		}
		,_getData: function(tag,selectItem){
			var self = this;
			var o = this.options;
			
			if(this._getSpecialTag(tag))
				return true;
			
			return false;
		}
		,_getSpecialTagKeyByTagname: function(tagname){
			var tags = this.options.ui.leftPanel.specialTags;
			for(var t in tags){
				if(tagname == tags[t].tagname)
					return t;
			}
			return undefined;
		}
		,_getSpecialTag: function(tag){
			var self = this;
			var o = this.options;
			if(this._isSpecialTag(tag.attr('tagname'))){
				var specialTag = o.ui.leftPanel.specialTags[this._getSpecialTagKeyByTagname(tag.attr('tagname'))];
				
				//this.previousItems = this.previousItems.clone(true,true);
				this._setLoading(false,tag);
				var div = $("<div class=\"dp_preview_list\"></div>").appendTo(this.right_panel);
				//this.previousItems.appendTo(div);
				specialTag.items.appendTo(div);
				return true;
			}
			return false;
		}
		,_getItems: function(tag, data){ //to be overriden
		}
		,_itemsLoaded: function(tag, empty){
			this._setLoading(false,tag);
			if(empty){
				this._tagListEmpty(tag);
			}
		}
		,_selectItem: function(item){//can be added-to(by overriding and calling $.ui.datapicker.prototype._selectItem.apply(this, arguments)) but common behaviour is here
			var self = this;
			var o = this.options;
			
			if(typeof(this.selectedItem) != 'undefined')
				 this.selectedItem.removeClass('selectedItem');
			
			this.selectedItem = item;
			this.selectedItem.addClass('selectedItem');
			this.currentTag = this.selectedTag.attr('tagname');
			
			//update on the right_panel too
			this.right_panel.find('[dp_value="'+item.attr('dp_value')+'"]').addClass('selectedItem').siblings().removeClass('selectedItem');
			
			if(this.autoclose)
				this._close();
				
			//user-specific behaviour(like updating other plugin-related objects, eg hidden field which stores the value of the selected font)
			if ($.isFunction(o.select)){
				//var ui = self._ui();//this gives ui object as it now, ie not guaranteed to be same when item is ready
				this._ready(item,function(){
					//ensure we still have this item selected
					//this may be false in case we selected other item while this one is still loading(ie not ready)
					if(self.selectedItem.is(item))
						self._trigger('select', null, self._ui());
				});
			}
		}
		,_ready:function(item,fn){
			if(this._isItemReady(item))
				fn();
			else{
				var val = item.attr('dp_value');
				if(!this.fn_queue[val])
					this.fn_queue[val] = [];
				this.fn_queue[val].push(fn);
			}
		}
		,_isItemReady:function(item){
			return item.hasClass('dp_item_ready');
		}
		,_addToPreviousItems:function(){
			if(typeof(this.selectedItem)=='undefined')
				return;
			this.prevItem = this.selectedItem;//.clone(true,true);
			//this.prevItem.removeClass('selectedItem');
			
			if(!this.options.ui.leftPanel.specialTags.previous.enabled)
				return;
			
			var dp_value = this.prevItem.attr('dp_value');
			var previousTag = this.options.ui.leftPanel.specialTags.previous;
			var items = previousTag.items;
			
			//if(this.previousItems.filter('[dp_value="'+escapedVal+'"]').length)
			//use the following instead of the line above because '[attr="val"]' selector does not handle special chars in variable, eg Pee's Celtic Italic
			if(items.filter(function(){return $(this).attr("dp_value")==dp_value;}).length)
				return;//do not add
			
			//add to the beginning
			//this.previousItems.unshift(this.prevItem);
			//this.previousItems = this.previousItems.clone(true,true);
			//this.previousItems = this.prevItem.add(this.previousItems);

			previousTag.items = this.prevItem.add(items.clone(true,true));
		}
		,_escapedValue: function(val){
			if(typeof(val)!="string")
				return "";
			var ret = "";
			for(var i=0;i<val.length;i++){
				if(val.charAt(i)==' ')
					ret+='+';
				else
					ret+=val.charAt(i);
			}
			return encodeURIComponent(ret);
		}
		,_createRightPanel: function(){
			var self = this;
			var o = this.options;
			this.right_panel = $("<div class=\"dp_right_panel\"></div>").appendTo(this.preview_body);
			var right_panel_width = this.preview_body.width()-this.left_panel.outerWidth();
			this.right_panel.css('width',right_panel_width+"px");
			
			/*this.right_panel.css({
				width:o.css.dimension.rightPanel.width
				,height:o.css.dimension.rightPanel.height
			});*/
		}
		,_createFooter: function(){
			var self = this;
			var o = this.options;
			
			this.preview_footer = $("<div class=\"dp_preview_footer\"></div>").appendTo(this.preview_container);
			/*this.preview_footer.css({
				width:o.css.dimension.bottomPanel.width
				,height:o.css.dimension.bottomPanel.height
			});
			*/
			this._createAutoclose();
			this._createFooterButtons();
			
			o.ui.min_width = o.ui.min_width>this.preview_footer.width() ? o.ui.min_width : this.preview_footer.width();
		}
		,_createAutoclose: function(){
			var self = this;
			var o = this.options;
			
			this.autoclose = (typeof(o.autoclose)=='boolean')? o.autoclose : true;
			
			var div = $("<div class=\"dp_autocloseDiv\"></div>").appendTo(this.preview_footer);
			
			var checkbox = $("<input type=\"checkbox\" class=\"dp_autocloseDiv\"/>").appendTo(div);
			checkbox.prop("checked", this.autoclose);
			checkbox.bind('change.datapicker',{dp:self}, function(e){
				var dp = e.data.dp;
				dp.autoclose = !!$(this).prop("checked");
			});
			
			var label = $("<span class=\"dp_autocloseLabel\">"+o.ui.footer.autocloseLabel+"</span>").appendTo(div);
			label.bind('click.datapicker',{cb:checkbox}, function(e){
				e.data.cb.trigger('click').trigger('change');//need to trigger change for ie6
			});

		}
		,_createFooterButtons:function(){
			var self = this;
			var o = this.options;
			
			var div = $("<div class=\"dp_footerButtons\"></div>").appendTo(this.preview_footer);
						
			var cancel = $("<a href=\"\" class=\"dp_preview_button_cancel\">"+o.ui.footer.cancelLabel+"</a>").appendTo(div);
			cancel.bind('click.datapicker',{dp:self},function(e){
				e.preventDefault();
				var dp = e.data.dp;
				dp._cancel();
			});
			
			var okay = $("<input type=\"button\" class=\"dp_preview_button_okay\" value=\""+o.ui.footer.okayLabel+"\"/>").appendTo(div);
			okay.bind('click.datapicker',{dp:self},function(e){
				var dp = e.data.dp;
				dp._close();
			});
		
		}
		,_setWidths: function(){
			var self = this;
			var o = this.options;
			//set this explicitly for cross-browser gradients display
			//this ensures hasLayout is set for gradients to work in IE
			var width =  o.ui.min_width;
			this.preview_header.css('width',width+"px");
			this.preview_body.css('width',width+"px");
			this.preview_footer.css('width',width+"px");
			this.preview.css('width',"auto");//.width(width);
		}
		,destroy: function(){
			var self = this;
			self._close();
			self.element
				.removeClass("dp_control_element")
				.unbind('.datapicker')
				.removeData('datapicker');
			return self;
		}
	});
	
	//ajaxpicker widget, extends datapicker
	$.widget( "ui.ajaxpicker", $.extend({},$.ui.datapicker.prototype, {
		options:{
			ajaxCategory: "fonts"
			,ajaxFailed:function(){} //function to run when ajax fails
			,ajaxHost:""
			,ajaxCgi:"/net-fu/ajax.cgi"
		}
		,_create: function(){
			var opts = this.options;
			//ensure we have default datapicker options(and override them)
			//note: we do recursive merge here
			this.options = $.extend(true,{},$.ui.datapicker.prototype.options, opts);
			//this.element.data('ui.datapicker', this.element.data('ui.ajaxpicker'));
			$.ui.datapicker.prototype._create.apply(this, arguments);
			}
		,_getData: function(tag){
			var self = this;
			var o = this.options;
			var tag_str = tag.attr('tagname');
			var searching = o.ui.leftPanel.specialTags.search.tagname == tag_str && this.search_field.hintedInput("val")!="";
			
			if(o.ui.leftPanel.specialTags.search.tagname == tag_str && this.search_field.hintedInput("val")==""){
				this._showSearchHint(tag);
				return;
			}
			
			//if we selected "Previous" tag dont query
			if(!searching && $.ui.datapicker.prototype._getData.apply(this, arguments))
				return;
			
			var queryTag = encodeURIComponent(tag_str);
			var querySearch = "";
			if(searching){
				if(o.ui.topPanel.search.searchAll)
					queryTag = encodeURIComponent("_all");// for now, but can be any tag
				else
					queryTag = encodeURIComponent(this.previousTag.attr('tagname'));//use previous because we just switched to "search" tag from it.
				querySearch = encodeURIComponent(this.search_field.hintedInput("val"));
			}
			
			var context = {self:self, tag:tag, searching:searching, searchTerm:(this.search_field ? this.search_field.hintedInput("val"):"")};
			var u=o.ajaxHost;
			//if (this.imagebot.length)
				//u = this.imagebot.getConfigManager().getConfig().ftHost;
			u+=o.ajaxCgi+"?c="+o.ajaxCategory+"&t="+queryTag;
			if(searching)
				u+="&q="+querySearch;
			
			this.runningXhr = $.ajax({url:u,async:true, context:context, cache:false, dataType:"json",
				success:function(result) {
					this.self.runningXhr = undefined;
					if (result.values) {
						this.self._saveItems({values:result.values,tag:this.tag,searchTerm:this.searchTerm});
						if(this.searching && encodeURIComponent(this.searchTerm)!=encodeURIComponent(this.self.search_field.val())){
							this.self.search_field.trigger('change');
							return;
						}
						else
							this.self._showItems(this.tag);
					} else if (result.error && this.searching) {
						this.self._saveSearchEmpty(result.error, true, this.tag, this.searchTerm);
					} else if (result.error) {
						this.self._ajaxFailed(result.error, true, this.tag);
					} else {
						this.self._ajaxFailed("bad response from server", true, this.tag);
					}
				},
				error: function(req,textStatus) {
					//log("error:"+req.status+":"+textStatus);
					this.self._ajaxFailed("http error:"+textStatus, true, this.tag);
				}
			});
			this.runningXhr.searchTerm = querySearch;
		}
		,_ajaxFailed: function(str,empty, tag){
			var o = this.options;
			var self = this;
			this.runningXhr = undefined;
			if ($.isFunction(o.ajaxFailed)){
				self._trigger('ajaxFailed', null, str);
			}
			
			$.ui.datapicker.prototype._failed.apply(this, arguments);
		}
		,destroy: function(){
			var self = this;
			$.ui.datapicker.prototype.destroy.apply(this, arguments);
			self.element.unbind('.ajaxpicker').removeData('ajaxpicker');
			return self;
		}
	}));
	
	//fontpicker widget, extends ajaxpicker
	$.widget( "ui.fontpicker", $.extend({},$.ui.ajaxpicker.prototype, {
		options:{
			/*trying center of the screen(default of the datapicker)
			position:function(e,ui){
				var offsetElement = ui.controlElement.closest('div.formEntry');
				var offset = offsetElement.offset();
				var oWidth = offsetElement.outerWidth();
				var oHeight = offsetElement.outerHeight();
				
				var top = offset.top + oHeight + "px";
				var left = offset.left + "px";
				//bellow div.formEntry
				ui.main.css({
					top	:top
					,left	:left
				});
			}
			,*/ajaxCategory: "fonts" //used by ajaxpicker for ajax query parameter
			,fontFailed: function(str){} //nothing special do to, just pass the error to datapicker
			,origValue:"font"
			,autoclose:true
			,cacheAjaxResults:true
			,ui:{
				header:"Select Font"
				,topPanel:{
					show:true
				}
				,rightPanel:{
					containerClass:"fonts_div"
					,pagination:{
						itemsPerPage:10
						,pageLinks:5// so will show  something like: << < 7 8 9 10 11 > >>
					}
				}
			}
			,showPreviousTag:false
			,initialTag:"cool"
			//,loadingMessageTmpl:"<div class=\"dp_loading_message\">Loading {Tag} Fonts</div>"
			//,template:"<ul>{{each(i, font) fonts}}<li>${font}</li>{{/each}}</ul>" //do this way later
		}
		,_create: function(){
			var opts = this.options;
			//ensure we have default datapicker options(and override them)
			//note: we do recursive merge here
			this.options = $.extend(true,{},$.ui.ajaxpicker.prototype.options, opts);
			//this.element.data('ui.ajaxpicker', this.element.data('ui.fontpicker'));
			$.ui.ajaxpicker.prototype._create.apply(this, arguments);
		}
		,_buildItem:function(item, tag, container){
			var fontDiv= $("<div dp_value=\""+item+"\" title=\""+item+"\"></div>").appendTo(container);
			var escapedVal = this._escapedValue(item);
			var img = $("<img class=\"ft-font-image\" width=\"330\" height=\"75\" >").appendTo(fontDiv);
			img.error({self:this,div:fontDiv,value:item,escapedVal:escapedVal, tag:tag},function(e){
				e.data.div.remove();
				e.data.self._ajaxFailed("failed to load font:"+e.data.escapedVal+".png(original value:"+e.data.value+")",false, e.data.tag);
			});
			img.bind('load.datapicker', {div:fontDiv}, function(e){
				$(this).unbind('load.datapicker');
				e.data.div.trigger('item_ready');
			});
			return fontDiv;
		}
		,_loadItem:function(div){
			var val = div.attr('dp_value');
			var escapedVal = this._escapedValue(val);
			$('img',div).attr('src',"http://cdn1.ftimg.com/fonts/preview/330x75/"+escapedVal+".png");
		}
		,_ajaxFailed: function(str,empty,tag){
			var o = this.options;
			var self = this;
			if ($.isFunction(o.fontFailed)){
				self._trigger('fontFailed', null, str);
			}
			
			$.ui.datapicker.prototype._failed.apply(this, arguments);
		}		
		,destroy: function(){
			var self = this;
			$.ui.ajaxpicker.prototype.destroy.apply(this, arguments);
			self.element.unbind('.fontpicker').removeData('fontpicker');
			return self;
		}
	}));
	
	
	
	//patternpicker widget, extends ajaxpicker
	$.widget( "ui.patternpicker", $.extend({},$.ui.ajaxpicker.prototype, {
		options:{
			tags:{
				alphabetical:{enabled:false} //no alpha for patterns
			}
			/*trying center of the screen(default of the datapicker)
			,position:function(e,ui){
				var offsetElement = ui.controlElement.closest('div.formEntry');
				var offset = offsetElement.offset();
				var oWidth = offsetElement.outerWidth();
				var oHeight = offsetElement.outerHeight();
				
				var top = offset.top + oHeight + "px";
				var left = offset.left + "px";
				//bellow div.formEntry
				ui.main.css({
					top	:top
					,left	:left
				});
			}*/
			,ajaxCategory: "patterns" //used by ajaxpicker for ajax query parameter
			,patternFailed: function(str){} //nothing special do to, just pass the error to datapicker
			,origValue:"pattern"
			,autoclose:true
			,cacheAjaxResults:true
			,ui:{
				header:"Select Pattern"
				,rightPanel:{
					containerClass:"patterns_div"
					,pagination:{
						itemsPerPage:15
						,pageLinks:5// so will show  something like: << < 7 8 9 10 11 > >>
					}
				}
			}
			,showPreviousTag:false
			,initialTag:"standard"
		}
		,_create: function(){
			var opts = this.options;
			//ensure we have default datapicker options(and override them)
			//note: we do recursive merge here
			this.options = $.extend(true,{},$.ui.ajaxpicker.prototype.options, opts);
			//this.element.data('ui.ajaxpicker', this.element.data('ui.patternpicker'));
			$.ui.ajaxpicker.prototype._create.apply(this, arguments);
		}
		,_buildItem:function(item, tag, container){
			var patternDiv= $("<div dp_value=\""+item+"\" title=\""+item+"\"></div>").appendTo(container);
			var escapedVal = this._escapedValue(item);
			var img = $("<img class=\"ft-font-image\" width=\"100\" height=\"40\" >").appendTo(patternDiv);
			img.error({self:this,div:patternDiv,value:item,escapedVal:escapedVal, tag:tag},function(e){
				e.data.div.remove();
				e.data.self._ajaxFailed("failed to load pattern:"+e.data.escapedVal+".png(original value:"+e.data.value+")",false, e.data.tag);
			});
			img.bind('load.datapicker', {div:patternDiv}, function(e){
				$(this).unbind('load.datapicker');
				e.data.div.trigger('item_ready');
			});
			return patternDiv;
		}
		,_loadItem:function(div){
			var val = div.attr('dp_value');
			var escapedVal = this._escapedValue(val);
			$('img',div).attr('src',"http://cdn1.ftimg.com/images/patterns/100x40/"+escapedVal+".png");
		}
		,_escapedValue:function(val){
			if(typeof(val)!='string' || val.length==0)
				return "";
			var r='';
			for(i=0;i<val.length;i++){
				c=val.charAt(i);
				if(c==' ')r+='_';
				else if(c=='(')r+='_';
				else if(c==')')r+='_';
				else if(c=='$')r+='_';
				else if(c=='#')r+='_';
				else if(c=='?')r+='_';
				else if(c=='%%')r+='_';
				else if(c=='&')r+='_';
				else if(c=='\'')r+='_';
				else if(c=='+')r+='_';
				else r+=c;
			}
			return r;
		}
		,_ajaxFailed: function(str,empty, tag){
			var o = this.options;
			var self = this;
			if ($.isFunction(o.patternFailed)){
				self._trigger('patternFailed', null, str);
			}
			
			$.ui.datapicker.prototype._failed.apply(this, arguments);
		}
		,destroy: function(){
			var self = this;
			$.ui.ajaxpicker.prototype.destroy.apply(this, arguments);
			self.element.unbind('.patternpicker').removeData('patternpicker');
			return self;
		}
	}));
	
	
	//gradientnpicker widget, extends ajaxpicker
	$.widget( "ui.gradientpicker", $.extend({},$.ui.ajaxpicker.prototype, {
		options:{
			tags:{
				alphabetical:{enabled:false} //no alpha for patterns
			}
			/*trying center of the screen(default of the datapicker)
			,position:function(e,ui){
				var offsetElement = ui.controlElement.closest('div.formEntry');
				var offset = offsetElement.offset();
				var oWidth = offsetElement.outerWidth();
				var oHeight = offsetElement.outerHeight();
				
				var top = offset.top + oHeight + "px";
				var left = offset.left + "px";
				//bellow div.formEntry
				ui.main.css({
					top	:top
					,left	:left
				});
			}*/
			,ajaxCategory: "gradients" //used by ajaxpicker for ajax query parameter
			,gradientFailed: function(str){} //nothing special do to, just pass the error to datapicker
			,origValue:"gradient"
			,autoclose:true
			,cacheAjaxResults:true
			,ui:{
				header:"Select Gradient"
				,rightPanel:{
					containerClass:"gradients_div"
					,pagination:{
						itemsPerPage:24
						,pageLinks:5// so will show  something like: << < 7 8 9 10 11 > >>
					}
				}
			}
			,showPreviousTag:false
			,initialTag:"standard"
		}
		,_create: function(){
			var opts = this.options;
			//ensure we have default datapicker options(and override them)
			//note: we do recursive merge here
			this.options = $.extend(true,{},$.ui.ajaxpicker.prototype.options, opts);
			//this.element.data('ui.ajaxpicker', this.element.data('ui.gradientpicker'));
			$.ui.ajaxpicker.prototype._create.apply(this, arguments);
		}
		,_buildItem:function(item, tag, container){
			var patternDiv= $("<div dp_value=\""+item+"\" title=\""+item+"\"></div>").appendTo(container);
			var escapedVal = this._escapedValue(item);
			var img = $("<img class=\"ft-font-image\" width=\"100\" height=\"20\" >").appendTo(patternDiv);
			img.error({self:this,div:patternDiv,value:item,escapedVal:escapedVal, tag:tag},function(e){
				e.data.div.remove();
				e.data.self._ajaxFailed("failed to load gradient:"+e.data.escapedVal+".png(original value:"+e.data.value+")",false, e.data.tag);
			});
			img.bind('load.datapicker', {div:patternDiv}, function(e){
				$(this).unbind('load.datapicker');
				e.data.div.trigger('item_ready');
			});
			return patternDiv;
		}
		,_loadItem:function(div){
			var val = div.attr('dp_value');
			var escapedVal = this._escapedValue(val);
			$('img',div).attr('src',"http://cdn1.ftimg.com/images/gradients/100x20/"+escapedVal+".png");
		}
		,_escapedValue:function(val){
			if(typeof(val)!='string' || val.length==0)
				return "";
			var r='';
			for(i=0;i<val.length;i++){
				c=val.charAt(i);
				if(c==' ')r+='_';
				else r+=c;
			}
			return r;
		}
		,_ajaxFailed: function(str,empty,tag){
			var o = this.options;
			var self = this;
			if ($.isFunction(o.gradientFailed)){
				self._trigger('gradientFailed', null, str);
			}
			
			$.ui.datapicker.prototype._failed.apply(this, arguments);
		}
		,destroy: function(){
			var self = this;
			$.ui.ajaxpicker.prototype.destroy.apply(this, arguments);
			self.element.unbind('.gradientpicker').removeData('gradientpicker');
			return self;
		}
	}));
	
	//logopicker widget, extends ajaxpicker
	$.widget( "ui.logopicker", $.extend({},$.ui.ajaxpicker.prototype, {
		options:{
			tags:{
				alphabetical:{enabled:false} //no alpha for patterns
			}
			/*trying center of the screen(default of the datapicker)
			,position:function(e,ui){
				var offsetElement = ui.controlElement.closest('.box_inner');
				var offset = offsetElement.offset();
				var oWidth = offsetElement.outerWidth();
				var oHeight = offsetElement.outerHeight();
				
				var top = offset.top + oHeight + "px";
				var left = offset.left + "px";
				//bellow div.formEntry
				ui.main.css({
					top	:top
					,left	:left
				});
			}*/
			,ajaxCategory: "logos" //used by ajaxpicker for ajax query parameter
			,logoFailed: function(str){} //nothing special do to, just pass the error to datapicker
			,origValue:"logo"
			,autoclose:true
			,cacheAjaxResults:true
			,ui:{
				header:"Select Logo"
				,topPanel:{
					show:true
					,showAlphabet:false
				}
				,rightPanel:{
					containerClass:"logos_div"
					,pagination:{
						itemsPerPage:10
						,pageLinks:5// so will show  something like: << < 7 8 9 10 11 > >>
					}
				}
			}
			,showPreviousTag:false
			,initialTag:"Featured"
			,fontname:"agate"
			,text:"agate"
		}
		,_create: function(){
			var opts = this.options;
			//ensure we have default datapicker options(and override them)
			//note: we do recursive merge here
			this.options = $.extend(true,{},$.ui.ajaxpicker.prototype.options, opts);
			$.ui.ajaxpicker.prototype._create.apply(this, arguments);
		}
		,_buildItem:function(item, tag, container){
			var logo = logoEntries[item];
			var logoDiv = $("<div dp_value=\""+item+"\" title=\""+item+"\"></div>").appendTo(container);
			//logoDiv.addClass((i%2==0)?"logoPickerEvenPreview":"logoPickerOddPreview");
			if(logo)
				var logoDivText = $("<p class=\"logo_div_text\">"+(logo.title?logo.title:"&nbsp;")+"</p>").appendTo(logoDiv);
			var escapedVal = this._escapedValue(item);
			var loading_img = $("<img class=\"ft-loading-image\" src=\"http://cdn1.ftimg.com/images/loading.gif\">").appendTo(logoDiv);
			
			var img = $("<img class=\"ft-logo-image\"/>").css({width:'',height:''}).appendTo(logoDiv);
			img.bind('load.dp',{self:this,div:logoDiv,logo:logo,dp_value:item,tag:tag},function(e){
				$(this).attr('logo_loaded','true');
				
				var div = e.data.div;
				div.children().remove('.logo_div_text, .ft-loading-image');
				
				var logo = e.data.logo;
				if(logo && logo.bgClass)
					div.addClass(logo.bgClass);
					
				if(logo && logo.title)
					div.attr('title',logo.title);
				
				var h = $(this).outerHeight();
				var w = $(this).outerWidth();
				
				var divh = 75;//div.height();
				var divw = 340;//div.width();
				
				var scale = 1, scaleW = 1, scaleH = 1;
				if (w>divw)
					scaleW = divw/w;
				if (h>divh)
					scaleH = divh/h;
				scale = Math.min(1,scaleW,scaleH);
				
				if(scale<1){
					w= w*scale; h=h*scale;
					$(this).width(w);
					$(this).height(h);
				}
				
				var top = Math.round((divh - h)/2) + "px";
				var left = Math.round((divw - w)/2) + "px";
				$(this).css({
					top	:top
					,left	:left
				});
				//unbind as we need to do this once only.
				$(this).unbind('load.dp');
				
				div.trigger('item_ready');
				
			});
			img.error({self:this,div:logoDiv,value:item,escapedVal:escapedVal, tag:tag},function(e){
				e.data.div.remove();
				e.data.self._ajaxFailed("failed to load logo:"+e.data.escapedVal,false,e.data.tag);
			});
			return logoDiv;
		}
		,_loadItem:function(div){
			var val = div.attr('dp_value');
			if(!val)//empty tag - ie no results from search
				return;
			var escapedVal = this._escapedValue(val);
			var img = $('img',div);
			var logo = logoEntries[val];
			var o = this.options;
			
			var context = {self:this, img:img,div:div,logo:logo, tag:this.selectedTag};
			var u=o.ajaxHost+"/net-fu/image_output.cgi?script="+encodeURIComponent(escapedVal)+"&text="+encodeURIComponent(o.text)+"&fontname="+encodeURIComponent(o.fontname)+"&imageoutput=true";
			$.ajax({url:u,async:true, context:context, cache:false, dataType:"json",
				success:function(result) {
					if (result.src) {
						this.img.attr({'src':result.src});//,title:this.logo.title});
					} else if (result.error) {
						this.div.remove();
						this.self._ajaxFailed("failed to load image:"+result.error,false,this.tag);
					} else {
						this.div.remove();
						this.self._ajaxFailed("bad response from server",false,this.tag);
					}
				},
				error: function(req,textStatus) {
					this.div.remove();
					this.self._ajaxFailed("http error:"+textStatus,false, this.tag);
				}
			});
			
		}
		,_escapedValue:function(val){
			if(typeof(val)!='string' || val.length==0)
				return "";
			var r='';
			for(i=0;i<val.length;i++){
				c=val.charAt(i);
				if(c==' ')r+='_';
				else r+=c;
			}
			return r;
		}
		,_ajaxFailed: function(str,empty, tag){
			var o = this.options;
			var self = this;
			if ($.isFunction(o.logoFailed)){
				self._trigger('logoFailed', null, str);
			}
			
			$.ui.datapicker.prototype._failed.apply(this, arguments);
		}
		,destroy: function(){
			var self = this;
			$.ui.ajaxpicker.prototype.destroy.apply(this, arguments);
			self.element.unbind('.logopicker').removeData('logopicker');
			return self;
		}
	}));
	
	
	/*
	//helper
	$.extend({
		//use ft_ to reduce chances of conflicting with native jquery method with same name
		ft_renderTemplate:function(container, template, data ) {
			$(container).empty();
			$.tmpl(template, data).appendTo(container);
		}
	
	});*/
	
})(jQuery);
/*
---This carousel widget is used on the front page of Flamingtext.com---
*/

(function($) { 
	$.widget("ui.carousel", { 
		options: {  
			  startIndex: 0,  
			  selector: ".ft-carousel-content",
			  contentDelayTime: 5000 /*(ms)*/
			},
		
		play: function() {
			this._timerFunction();
		},
		stop: function() {
			clearInterval(this.timer);
			$(window).unbind('blur');
		},
		resumeListeners: function() {
			var self = this;
			jQuery(window)
				.blur(function() {
					$(window).unbind('focus').focus(function() {
					 	self._timerFunction();
					});
					clearInterval(self.timer);
				});
		},
		_create: function() {
			this._setupWidget();		
		},
		
		_setupWidget: function() {
			this.current = this.options.startIndex;
			this.navDiv = $(this.element).children().eq(0);
			this.arrow = this.navDiv.children().eq(0);
			this.contentDiv = $(this.options.selector);

			this._setupNav();
			this._setupContent();
		},
		_setupNav: function() {
			this.navItems = this.navDiv.children().eq(1).children();

			for(var i=0; i < this.navItems.length;i++){
				var item = this.navItems.get(i);
				this._process($(item),i);
			}
			this._initTimer();
		},
		_setupContent: function() {
			$(this.element).children().eq(1).html(this.contentDiv);
		},
		_process: function(item,i) {
			item.bind('click',{self:this, i:i},function(e){
				var self = e.data.self;
				clearInterval(self.timer);
				self._select(e.data.i);
			});
		},
		
		_select: function(navItem) {
			this._moveArrow(navItem);
		},
		_initTimer: function() {
			this.resumeListeners();
			this._timerFunction();
		},
		_timerFunction: function() {
			var self = this;
			self.timer = window.setInterval(function(){
				if(self.current < (self.navItems.length-1) )
					{
					self._select(self.current+1);
					}
				else{self._select(0);}
				}, self.options.contentDelayTime
			);
		},

		_updateContent: function(navItem) {
			this.contentDiv.eq(this.current).hide();
			this.contentDiv.eq(navItem).show();
		},

		_moveArrow: function(navItem) {
			var self = this;
			var newPosY = -5 + 74 * navItem;
			
			this.arrow.animate({
					top : newPosY
				}, "fast", function(){
					self._updateContent(navItem);
					self.current = navItem;
					}
			);
		}
		  
	});  
      
})(jQuery);  

