/*
 * jQuery JavaScript Library v1.3.2
 * http://jquery.com/
 *
 * Copyright (c) 2009 John Resig
 * Dual licensed under the MIT and GPL licenses.
 * http://docs.jquery.com/License
 *
 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
 * Revision: 6246
 */
(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
/*
 * Sizzle CSS Selector Engine - v0.9.3
 *  Copyright 2009, The Dojo Foundation
 *  Released under the MIT, BSD, and GPL Licenses.
 *  More information: http://sizzlejs.com/
 */
(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML='   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();/**
 * Cookie plugin
 *
 * Copyright (c) 2006 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
 *
 */

/**
 * Create a cookie with the given name and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String name The name of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */

/**
 * Get the value of a cookie with the given name.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String name The name of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function(name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options = $.extend({}, options); // clone object since it's unexpected behavior if the expired property were changed
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // NOTE Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            /**
               Changed by Stephan Beal: traverse the cookies in reverse order, since i've seen
               the cookie get updated by appending to the list, instead of replacing, and the GET
               then returns the first instance.
            */
            //for (var i = cookies.length-1; i >= 0 ; --i) {
            for( var i in cookies )
            {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};
/*
 * jQuery Form Plugin
 * version: 2.28 (10-MAY-2009)
 * @requires jQuery v1.2.2 or later
 *
 * Examples and documentation at: http://malsup.com/jquery/form/
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
;(function($) {

/*
    Usage Note:
    -----------
    Do not use both ajaxSubmit and ajaxForm on the same form.  These
    functions are intended to be exclusive.  Use ajaxSubmit if you want
    to bind your own submit handler to the form.  For example,

    $(document).ready(function() {
        $('#myForm').bind('submit', function() {
            $(this).ajaxSubmit({
                target: '#output'
            });
            return false; // <-- important!
        });
    });

    Use ajaxForm when you want the plugin to manage all the event binding
    for you.  For example,

    $(document).ready(function() {
        $('#myForm').ajaxForm({
            target: '#output'
        });
    });

    When using ajaxForm, the ajaxSubmit function will be invoked for you
    at the appropriate time.
*/

/**
 * ajaxSubmit() provides a mechanism for immediately submitting
 * an HTML form using AJAX.
 */
$.fn.ajaxSubmit = function(options) {
    // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
    if (!this.length) {
        log('ajaxSubmit: skipping submit process - no element selected');
        return this;
    }

    if (typeof options == 'function')
        options = { success: options };

    var url = $.trim(this.attr('action'));
    if (url) {
	    // clean url (don't include hash vaue)
	    url = (url.match(/^([^#]+)/)||[])[1];
   	}
   	url = url || window.location.href || ''

    options = $.extend({
        url:  url,
        type: this.attr('method') || 'GET'
    }, options || {});

    // hook for manipulating the form data before it is extracted;
    // convenient for use with rich editors like tinyMCE or FCKEditor
    var veto = {};
    this.trigger('form-pre-serialize', [this, options, veto]);
    if (veto.veto) {
        log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
        return this;
    }

    // provide opportunity to alter form data before it is serialized
    if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
        log('ajaxSubmit: submit aborted via beforeSerialize callback');
        return this;
    }

    var a = this.formToArray(options.semantic);
    if (options.data) {
        options.extraData = options.data;
        for (var n in options.data) {
          if(options.data[n] instanceof Array) {
            for (var k in options.data[n])
              a.push( { name: n, value: options.data[n][k] } );
          }
          else
             a.push( { name: n, value: options.data[n] } );
        }
    }

    // give pre-submit callback an opportunity to abort the submit
    if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
        log('ajaxSubmit: submit aborted via beforeSubmit callback');
        return this;
    }

    // fire vetoable 'validate' event
    this.trigger('form-submit-validate', [a, this, options, veto]);
    if (veto.veto) {
        log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
        return this;
    }

    var q = $.param(a);

    if (options.type.toUpperCase() == 'GET') {
        options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
        options.data = null;  // data is null for 'get'
    }
    else
        options.data = q; // data is the query string for 'post'

    var $form = this, callbacks = [];
    if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
    if (options.clearForm) callbacks.push(function() { $form.clearForm(); });

    // perform a load on the target only if dataType is not provided
    if (!options.dataType && options.target) {
        var oldSuccess = options.success || function(){};
        callbacks.push(function(data) {
            $(options.target).html(data).each(oldSuccess, arguments);
        });
    }
    else if (options.success)
        callbacks.push(options.success);

    options.success = function(data, status) {
        for (var i=0, max=callbacks.length; i < max; i++)
            callbacks[i].apply(options, [data, status, $form]);
    };

    // are there files to upload?
    var files = $('input:file', this).fieldValue();
    var found = false;
    for (var j=0; j < files.length; j++)
        if (files[j])
            found = true;

	var multipart = false;
//	var mp = 'multipart/form-data';
//	multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);

    // options.iframe allows user to force iframe mode
   if (options.iframe || found || multipart) {
       // hack to fix Safari hang (thanks to Tim Molendijk for this)
       // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
       if (options.closeKeepAlive)
           $.get(options.closeKeepAlive, fileUpload);
       else
           fileUpload();
       }
   else
       $.ajax(options);

    // fire 'notify' event
    this.trigger('form-submit-notify', [this, options]);
    return this;


    // private function for handling file uploads (hat tip to YAHOO!)
    function fileUpload() {
        var form = $form[0];

        if ($(':input[name=submit]', form).length) {
            alert('Error: Form elements must not be named "submit".');
            return;
        }

        var opts = $.extend({}, $.ajaxSettings, options);
		var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);

        var id = 'jqFormIO' + (new Date().getTime());
        var $io = $('<iframe id="' + id + '" name="' + id + '" src="about:blank" />');
        var io = $io[0];

        $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });

        var xhr = { // mock object
            aborted: 0,
            responseText: null,
            responseXML: null,
            status: 0,
            statusText: 'n/a',
            getAllResponseHeaders: function() {},
            getResponseHeader: function() {},
            setRequestHeader: function() {},
            abort: function() {
                this.aborted = 1;
                $io.attr('src','about:blank'); // abort op in progress
            }
        };

        var g = opts.global;
        // trigger ajax global events so that activity/block indicators work like normal
        if (g && ! $.active++) $.event.trigger("ajaxStart");
        if (g) $.event.trigger("ajaxSend", [xhr, opts]);

		if (s.beforeSend && s.beforeSend(xhr, s) === false) {
			s.global && $.active--;
			return;
        }
        if (xhr.aborted)
            return;

        var cbInvoked = 0;
        var timedOut = 0;

        // add submitting element to data if we know it
        var sub = form.clk;
        if (sub) {
            var n = sub.name;
            if (n && !sub.disabled) {
                options.extraData = options.extraData || {};
                options.extraData[n] = sub.value;
                if (sub.type == "image") {
                    options.extraData[name+'.x'] = form.clk_x;
                    options.extraData[name+'.y'] = form.clk_y;
                }
            }
        }

        // take a breath so that pending repaints get some cpu time before the upload starts
        setTimeout(function() {
            // make sure form attrs are set
            var t = $form.attr('target'), a = $form.attr('action');

			// update form attrs in IE friendly way
			form.setAttribute('target',id);
			if (form.getAttribute('method') != 'POST')
				form.setAttribute('method', 'POST');
			if (form.getAttribute('action') != opts.url)
				form.setAttribute('action', opts.url);

            // ie borks in some cases when setting encoding
            if (! options.skipEncodingOverride) {
                $form.attr({
                    encoding: 'multipart/form-data',
                    enctype:  'multipart/form-data'
                });
            }

            // support timout
            if (opts.timeout)
                setTimeout(function() { timedOut = true; cb(); }, opts.timeout);

            // add "extra" data to form if provided in options
            var extraInputs = [];
            try {
                if (options.extraData)
                    for (var n in options.extraData)
                        extraInputs.push(
                            $('<input type="hidden" name="'+n+'" value="'+options.extraData[n]+'" />')
                                .appendTo(form)[0]);

                // add iframe to doc and submit the form
                $io.appendTo('body');
                io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
                form.submit();
            }
            finally {
                // reset attrs and remove "extra" input elements
				form.setAttribute('action',a);
                t ? form.setAttribute('target', t) : $form.removeAttr('target');
                $(extraInputs).remove();
            }
        }, 10);

        var nullCheckFlag = 0;

        function cb() {
            if (cbInvoked++) return;

            io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);

            var ok = true;
            try {
                if (timedOut) throw 'timeout';
                // extract the server response from the iframe
                var data, doc;

                doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;

                if ((doc.body == null || doc.body.innerHTML == '') && !nullCheckFlag) {
                    // in some browsers (cough, Opera 9.2.x) the iframe DOM is not always traversable when
                    // the onload callback fires, so we give them a 2nd chance
                    nullCheckFlag = 1;
                    cbInvoked--;
                    setTimeout(cb, 100);
                    return;
                }

                xhr.responseText = doc.body ? doc.body.innerHTML : null;
                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
                xhr.getResponseHeader = function(header){
                    var headers = {'content-type': opts.dataType};
                    return headers[header];
                };

                if (opts.dataType == 'json' || opts.dataType == 'script') {
                    var ta = doc.getElementsByTagName('textarea')[0];
                    xhr.responseText = ta ? ta.value : xhr.responseText;
                }
                else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
                    xhr.responseXML = toXml(xhr.responseText);
                }
                data = $.httpData(xhr, opts.dataType);
            }
            catch(e){
                ok = false;
                $.handleError(opts, xhr, 'error', e);
            }

            // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
            if (ok) {
                opts.success(data, 'success');
                if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
            }
            if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
            if (g && ! --$.active) $.event.trigger("ajaxStop");
            if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');

            // clean up
            setTimeout(function() {
                $io.remove();
                xhr.responseXML = null;
            }, 100);
        };

        function toXml(s, doc) {
            if (window.ActiveXObject) {
                doc = new ActiveXObject('Microsoft.XMLDOM');
                doc.async = 'false';
                doc.loadXML(s);
            }
            else
                doc = (new DOMParser()).parseFromString(s, 'text/xml');
            return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
        };
    };
};

/**
 * ajaxForm() provides a mechanism for fully automating form submission.
 *
 * The advantages of using this method instead of ajaxSubmit() are:
 *
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
 *    is used to submit the form).
 * 2. This method will include the submit element's name/value data (for the element that was
 *    used to submit the form).
 * 3. This method binds the submit() method to the form for you.
 *
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 * passes the options argument along after properly binding events for submit elements and
 * the form itself.
 */
$.fn.ajaxForm = function(options) {
    return this.ajaxFormUnbind().bind('submit.form-plugin',function() {
        $(this).ajaxSubmit(options);
        return false;
    }).each(function() {
        // store options in hash
        $(":submit,input:image", this).bind('click.form-plugin',function(e) {
            var form = this.form;
            form.clk = this;
            if (this.type == 'image') {
                if (e.offsetX != undefined) {
                    form.clk_x = e.offsetX;
                    form.clk_y = e.offsetY;
                } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
                    var offset = $(this).offset();
                    form.clk_x = e.pageX - offset.left;
                    form.clk_y = e.pageY - offset.top;
                } else {
                    form.clk_x = e.pageX - this.offsetLeft;
                    form.clk_y = e.pageY - this.offsetTop;
                }
            }
            // clear form vars
            setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 10);
        });
    });
};

// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
    this.unbind('submit.form-plugin');
    return this.each(function() {
        $(":submit,input:image", this).unbind('click.form-plugin');
    });

};

/**
 * formToArray() gathers form element data into an array of objects that can
 * be passed to any of the following ajax functions: $.get, $.post, or load.
 * Each object in the array has both a 'name' and 'value' property.  An example of
 * an array for a simple login form might be:
 *
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 *
 * It is this array that is passed to pre-submit callback functions provided to the
 * ajaxSubmit() and ajaxForm() methods.
 */
$.fn.formToArray = function(semantic) {
    var a = [];
    if (this.length == 0) return a;

    var form = this[0];
    var els = semantic ? form.getElementsByTagName('*') : form.elements;
    if (!els) return a;
    for(var i=0, max=els.length; i < max; i++) {
        var el = els[i];
        var n = el.name;
        if (!n) continue;

        if (semantic && form.clk && el.type == "image") {
            // handle image inputs on the fly when semantic == true
            if(!el.disabled && form.clk == el) {
            	a.push({name: n, value: $(el).val()});
                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
            }
            continue;
        }

        var v = $.fieldValue(el, true);
        if (v && v.constructor == Array) {
            for(var j=0, jmax=v.length; j < jmax; j++)
                a.push({name: n, value: v[j]});
        }
        else if (v !== null && typeof v != 'undefined')
            a.push({name: n, value: v});
    }

    if (!semantic && form.clk) {
        // input type=='image' are not found in elements array! handle it here
        var $input = $(form.clk), input = $input[0], n = input.name;
        if (n && !input.disabled && input.type == 'image') {
        	a.push({name: n, value: $input.val()});
            a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
        }
    }
    return a;
};

/**
 * Serializes form data into a 'submittable' string. This method will return a string
 * in the format: name1=value1&amp;name2=value2
 */
$.fn.formSerialize = function(semantic) {
    //hand off to jQuery.param for proper encoding
    return $.param(this.formToArray(semantic));
};

/**
 * Serializes all field elements in the jQuery object into a query string.
 * This method will return a string in the format: name1=value1&amp;name2=value2
 */
$.fn.fieldSerialize = function(successful) {
    var a = [];
    this.each(function() {
        var n = this.name;
        if (!n) return;
        var v = $.fieldValue(this, successful);
        if (v && v.constructor == Array) {
            for (var i=0,max=v.length; i < max; i++)
                a.push({name: n, value: v[i]});
        }
        else if (v !== null && typeof v != 'undefined')
            a.push({name: this.name, value: v});
    });
    //hand off to jQuery.param for proper encoding
    return $.param(a);
};

/**
 * Returns the value(s) of the element in the matched set.  For example, consider the following form:
 *
 *  <form><fieldset>
 *      <input name="A" type="text" />
 *      <input name="A" type="text" />
 *      <input name="B" type="checkbox" value="B1" />
 *      <input name="B" type="checkbox" value="B2"/>
 *      <input name="C" type="radio" value="C1" />
 *      <input name="C" type="radio" value="C2" />
 *  </fieldset></form>
 *
 *  var v = $(':text').fieldValue();
 *  // if no values are entered into the text inputs
 *  v == ['','']
 *  // if values entered into the text inputs are 'foo' and 'bar'
 *  v == ['foo','bar']
 *
 *  var v = $(':checkbox').fieldValue();
 *  // if neither checkbox is checked
 *  v === undefined
 *  // if both checkboxes are checked
 *  v == ['B1', 'B2']
 *
 *  var v = $(':radio').fieldValue();
 *  // if neither radio is checked
 *  v === undefined
 *  // if first radio is checked
 *  v == ['C1']
 *
 * The successful argument controls whether or not the field element must be 'successful'
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.  If this value is false the value(s)
 * for each element is returned.
 *
 * Note: This method *always* returns an array.  If no valid value can be determined the
 *       array will be empty, otherwise it will contain one or more values.
 */
$.fn.fieldValue = function(successful) {
    for (var val=[], i=0, max=this.length; i < max; i++) {
        var el = this[i];
        var v = $.fieldValue(el, successful);
        if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
            continue;
        v.constructor == Array ? $.merge(val, v) : val.push(v);
    }
    return val;
};

/**
 * Returns the value of the field element.
 */
$.fieldValue = function(el, successful) {
    var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
    if (typeof successful == 'undefined') successful = true;

    if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
        (t == 'checkbox' || t == 'radio') && !el.checked ||
        (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
        tag == 'select' && el.selectedIndex == -1))
            return null;

    if (tag == 'select') {
        var index = el.selectedIndex;
        if (index < 0) return null;
        var a = [], ops = el.options;
        var one = (t == 'select-one');
        var max = (one ? index+1 : ops.length);
        for(var i=(one ? index : 0); i < max; i++) {
            var op = ops[i];
            if (op.selected) {
				var v = op.value;
				if (!v) // extra pain for IE...
                	v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
                if (one) return v;
                a.push(v);
            }
        }
        return a;
    }
    return el.value;
};

/**
 * Clears the form data.  Takes the following actions on the form's input fields:
 *  - input text fields will have their 'value' property set to the empty string
 *  - select elements will have their 'selectedIndex' property set to -1
 *  - checkbox and radio inputs will have their 'checked' property set to false
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
 *  - button elements will *not* be effected
 */
$.fn.clearForm = function() {
    return this.each(function() {
        $('input,select,textarea', this).clearFields();
    });
};

/**
 * Clears the selected form elements.
 */
$.fn.clearFields = $.fn.clearInputs = function() {
    return this.each(function() {
        var t = this.type, tag = this.tagName.toLowerCase();
        if (t == 'text' || t == 'password' || tag == 'textarea')
            this.value = '';
        else if (t == 'checkbox' || t == 'radio')
            this.checked = false;
        else if (tag == 'select')
            this.selectedIndex = -1;
    });
};

/**
 * Resets the form data.  Causes all form elements to be reset to their original value.
 */
$.fn.resetForm = function() {
    return this.each(function() {
        // guard against an input with the name of 'reset'
        // note that IE reports the reset function as an 'object'
        if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
            this.reset();
    });
};

/**
 * Enables or disables any matching elements.
 */
$.fn.enable = function(b) {
    if (b == undefined) b = true;
    return this.each(function() {
        this.disabled = !b;
    });
};

/**
 * Checks/unchecks any matching checkboxes or radio buttons and
 * selects/deselects and matching option elements.
 */
$.fn.selected = function(select) {
    if (select == undefined) select = true;
    return this.each(function() {
        var t = this.type;
        if (t == 'checkbox' || t == 'radio')
            this.checked = select;
        else if (this.tagName.toLowerCase() == 'option') {
            var $sel = $(this).parent('select');
            if (select && $sel[0] && $sel[0].type == 'select-one') {
                // deselect all other options
                $sel.find('option').selected(false);
            }
            this.selected = select;
        }
    });
};

// helper fn for console logging
// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
    if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
        window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,''));
};

})(jQuery);
/**
    Adds the object jQuery.html, which contains a set of factory
    functions for generating new HTML elements. See the docs
    in the code below.

    License: Public Domain
    Author: Stephan Beal (wanderinghorse.net)
*/
jQuery.extend({
/**
An object containing a set of factory functions for generating new
HTML elements. It is intended to be used like this:

Old:

var x = jQuery("<span></span>");

New:

var x = jQuery.html.span();

It just looks nicer, not having all the HTML littering the code,
and it's less distracting in syntax-highlighted environments
(IMO), where the HTML strings have different colors than the
real code.
*/
html:
{
    /** Returns jQuery('<'+tag+'></'+tag+'>') */
    tag:function(tag) { return jQuery('<'+tag+'></'+tag+'>');},

    /* Headers and separators. */
    hN:function(n) { return this.tag('h'+n);},
    h1:function(n) { return this.hN(1);},
    h2:function(n) { return this.hN(2);},
    h3:function(n) { return this.hN(3);},
    br:function() { return jQuery('<br/>'); },
    hr:function() { return jQuery('<hr/>'); },
    nbsp:function() { return this.span().append('&nbsp;'); },

    /* Blocks/containers. */
    div:function() { return this.tag('div');},
    pre:function() { return this.tag('pre');},
    blockquote:function() { return this.tag('blockquote');},
    iframe:function(){ return jQuery("<iframe src='about:blank'/>"); },

    /* General text formatting. */
    span:function() { return this.tag('span');},
    tt:function() { return this.tag('tt');},
    b:function() { return this.tag('b');},
    strong:function() { return this.tag('strong');},
    em:function() { return this.tag('em');},
    i:function() { return this.tag('i');},
    strike:function() { return this.tag('strike');},
    u:function() { return this.tag('u');},
    sup:function() { return this.tag('sup');},
    sub:function() { return this.tag('sub');},

    /* Tables */
    /* table() returns a table with a tbody. Some browsers automatically create
    a tbody entry if not is specified, some do not. Some might allow creation
    of a standalone tbody to insert into a table, whereas others might not.
    So... we just create it all together.
    */
    td:function() { return this.tag('td');},
    th:function() { return this.tag('th');},
    thead:function() { return this.tag('thead');},
    /**
        Returns a new jQuery TR object with member functions
        td() and th(), each of which creates a new TD resp. TH
        jQuery object, appends it to this TR element, and returns it.
    */
    tr:function()
    {
        var tr = this.tag('tr');
        var self = this;
        function td()
        {
            return self.td().appendTo(this);
        };
        function th()
        {
            return self.th().appendTo(this);
        };
        tr.td = td;
        tr.th = th;
        return tr;
    },
    /**
        Returns a new jQuery TABLE object with a member function
        tr() which creates a new row, appends to the TABLE,
        and returns it.
    */
    table:function()
    {
        var tbl = jQuery('<table><thead></thead><tbody></tbody></table>');
        var self = this;
        function tr()
        {
            var x = self.tr();
            x.appendTo(this);
            return x;
        };
        function thead()
        {
            return jQuery('thead',this);
        }
        function tbody()
        {
            return jQuery('tbody',this);
        }
        tbl.thead = thead;
        tbl.tbody = tbody;
        tbl.tr = tr;
        return tbl;
    },

    /* Lists: */
    li:function(n) { return this.tag('li');},
    /**
        Returns a new jQuery UL object with a member function
        li() which creates a new LI element, appends to the UL,
        and returns it.
    */
    ul:function(n)
    {
        var l = this.tag('ul');
        var self = this;
        function li()
        {
            return self.li().appendTo(this);
        };
        l.li = li;
        return l;
    },
    /**
        Returns a new jQuery OL object with a member function
        li() which creates a new LI element, appends to the OL,
        and returns it.
    */
    ol:function(n)
    {
        var l = this.tag('ol');
        var self = this;
        function li()
        {
            return self.li().appendTo(this);
        };
        l.li = li;
        return l;
    },

    /* Input */
    form:function() { return this.tag('form'); },
    input:function(inputType) { return jQuery("<input type='"+inputType+"'></input>"); },
    button:function() { return this.input('button');},
    /* i just couldn't bring myself to call this text(). */
    textfield:function() { return this.input('text');},
    checkbox:function() { return this.input('checkbox');},
    radio:function() { return this.input('radio');},
    hidden:function() { return this.input('hidden');},
    /**
        Returns a new jQuery SELECT object with a member function
        option() which creates a new option element, appends to the
        SELECT, and returns it.
    */
    select:function()
    {
        var sel = this.tag('select');
        var self = this;
        function option()
        {
            return self.option().appendTo(this);
        };
        sel.option = option;
        return sel;
    },
    option:function() { return this.tag('option');},
    textarea:function() { return this.tag('textarea');},
    fieldset:function() { return this.tag('fieldset');},

    /** Misc. */
    img:function() { return this.tag('img');},
    /* Returns an "inactive" link, with onclick='return false;' and href='#'. */
    a:function() { /*return this.tag('a');*/
    //return jQuery("<a href='#' onclick='return false;'></a>");
        function _false() { return false; };
        return this.tag('a').attr('href','#').click(_false);
    }
}/*html*/
});
/**
   A collection of routines for use with jQuery.  Unlike jQuery
   plugins, which are designed to work on multiple result objects,
   all of these routines are intended for single-element selections,
   and therefore they are not implement as plugins.

   Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
   License: Public Domain.
*/

/**
   Binds jqCheck, which must be-a jQuery checkbox[-compatible]
   object, to a boolean property of obj. When the checkbox is
   checked, the property is set to a true value, else a false
   value.

   obj may be either an array or an object. prop must be a string (or
   other legal property index). The optional callback function is
   called with (obj,prop,currentValue) on each change, where
   currentValue is of unspecified type but carries a boolean value
   representing the checked state of jqCheck.

   Only the callback argument may be null. If any args are not what
   they need to be then an exception is thrown.

   jqCheck's checked state is set based on the value of obj[prop], but
   no change() event is triggered by calling this function, so the
   callback is not called by this function (but will be triggered when
   the checkbox changes).
   
   e.g.
   
   HTML:
   
   <input type='checkbox' id='MyOption'></input>
   
   Script:
   
   jQuery_checkbox_bind_property( $('#MyOption'),
        Application.options,
        'myOption',
        function(obj, prop, val ){alert(prop+'='+val);}
        );
        
    When the checkbox is (de)selected, Application.options.myOption
    will be set to the current value and the inlined callback function
    will be called and passed (obj,prop,checked), where checked is a
    boolean expression representing the checked state (do not rely on
    the specific TYPE of checked, only its value).

*/
function jQuery_checkbox_bind_property( jqCheck, obj, prop, callback )
{
    if( !jqCheck
        || !jqCheck.addClass
        || !obj
        || !prop )
    {
        throw new Error('jQuery_checkbox_bind_property() requires very specific arguments. Please RTFM!');
    }
    jqCheck.change(function(evt)
               {
                   var self = jQuery(this);
                   var x = self.attr('checked') ? 1 : 0;
                   obj[prop] = x;
                   if( callback )
                   {
                       callback( obj, prop, x );
                   }
               })
        .attr('checked',obj[prop] ? 1 : 0);
    return jqCheck;
};

/**
   Creates a wrapper jQuery object for a checkbox.
   Requires that cb be a jQuery object holding
   a SINGLE Checkbox item. _opt is an object
   containing key/value pairs in this set:

   label:"Label for the checkbox" (default='')

   The label acts as a proxy for the checkbox, such that
   clicking on the label will click the checkbox. As a
   special case, if (null===label) then cb.prev()
   is used as the label and that object gets moved
   into the generated wrapper object.

   callback:function(value,callbackData){} (default=null)

   is called when either the checkbox or label are clicked.
   The arguments are the current boolean value of the checkbox
   and the object set as _opt.data (may be null).
 
   callbackData:any (default={})

   is passed to the callback function as the second parameter.

   selectedClass:'css-class-name' (default='toggle-is-enabled')

   When cb is checked, the generated wrapper object gets
   this CSS class added to it. When it is unchecked, this class
   is removed.

   wrapperType:'tag' (default='span')

   determines the HTML tag type of the returned wrapper object. Must
   be a legal container type for the other arguments (e.g. SPAN is not
   legal if the label contains block elements).

   All but the callback and (possibly) label arguments can be reasonably
   defaulted. If no callback is set then the CSS changes will be
   applied but the client will have no way to interact with the
   change event.

   Returns the wrapper object created to contain the
   checkbox and its buddy.

   The returned object will have the opt.selectedClass if cb is
   checked at the time this function is called.

   e.g.:

   HTML:

   <span>This is the label if opt.label is null:</span>
   <input type='checkbox' id='MyOption'/>

   JS:

   jQuery_checkbox_wrapper( jQuery('#MyOption'),{
       //label:"My option", // default=use $(this).prev()
       callback:function(val,cbdata){alert('Option toggled!');}
    });
    
    Assuming we default the 'label' argument, the returned
    object would look something like this:

    <span [class=opt.selectedClass]>cb.prev() cb</span>
*/
function jQuery_checkbox_wrapper(cb,opt)
{
    var opt = jQuery.extend({
            label:null,
            selectedClass:'toggle-is-enabled',
            callback:null,
            callbackData:{},
            wrapperType:'span',
            cssClass:null,
            wrapperClass:null
        },opt ? opt : {}
        );
    var holder = jQuery.html.tag( opt.wrapperType );
    if( opt.cssClass ) holder.addClass(opt.cssClass);
    if( null === opt.label ) opt.label = cb.prev();
    cb.replaceWith(holder);
    var lbl = jQuery(opt.label);
    holder.append( lbl )
        .append('&nbsp;')
        .append(cb)
        ;
    if( opt.wrapperClass ) holder.addClass(opt.wrapperClass);
    function reclass(val)
    {
        if( 'checked' == val ) holder.addClass( opt.selectedClass );
        else holder.removeClass( opt.selectedClass );
    };
    reclass(cb.attr('checked'));
    cb.change( function()
    {
        var x = cb.attr('checked') ? 1 : 0;
        reclass(x);
        if( opt.callback ) opt.callback(x, opt.callbackData);
    } );
    lbl.click( function(){
                  cb.click().change();
                });
    return holder;
}

/**
   Expects sel to be a single jQuery SELECT element. It adds a list
   OPTION objects to the selection object and ties a callback to it.
   It is untested with multi-select lists, but "should" work.

   opt is a properties object with these properties:

   opt.items: the array of key/label pairs. For each item, an OPTION
   element is created, where value=key and label is the displayed text.

   opt.selectedValue: is an arbitrary value. If a VALUE in the items list
   compares == to a given item in the list, then that item is marked
   as selected.

   opt.callbackData: is an optional argument to pass to the callback function.

   opt.callback: is a function which is called and passed
   (currentValue,optObj,callbackData), where optObj is the OPTION
   object which caused the event, currentValue is
   optObj.attr('value'), and callbackData is taken from
   opt.callbackData.
*/
function jQuery_select_elem_wrapper(sel,opt)
{
    opt = jQuery.extend({
            callback:null,
            callbackData:{},
            selectedValue:undefined,
            items:{'0':'Fill out the _opt.items property for jQuery_select_elem_wrapper()!'}
        }, opt ? opt : {}
        );
    sel.empty();
    var isArray = (undefined === opt.items['length']) ? false : true;
    for( var i in opt.items )
    {
        var o = jQuery('<option></option>');
        o.attr('value',i);
        o.append( opt.items[i] );
        if( undefined !== opt.selectedValue )
        {
            if( i == opt.selectedValue )
            {
                o.attr('selected','selected');
            }
        }
        sel.append(o);
    }
    if( opt.callback )
    {
        sel.change(function(){
                       jQuery('option:selected',jQuery(this))
                           .each(function(){
                                     var o = jQuery(this);
                                     opt.callback(o.attr('value'), o, opt.callbackData);
                                 });
                   });
    }
    return sel;
};
/*
    http://www.JSON.org/json2.js
    2009-06-29

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html

    This file creates a global JSON object containing two methods: stringify
    and parse.

        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or '&nbsp;'),
                        it contains the characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the object holding the key.

            For example, this would serialize Dates as ISO strings.

                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }

                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'


            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'


        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });

            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });


    This is a reference implementation. You are free to copy, modify, or
    redistribute.

    This code should be minified before deployment.
    See http://javascript.crockford.com/jsmin.html

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    NOT CONTROL.
*/

/*jslint evil: true */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/

// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

var JSON = JSON || {};

(function () {

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf()) ?
                   this.getUTCFullYear()   + '-' +
                 f(this.getUTCMonth() + 1) + '-' +
                 f(this.getUTCDate())      + 'T' +
                 f(this.getUTCHours())     + ':' +
                 f(this.getUTCMinutes())   + ':' +
                 f(this.getUTCSeconds())   + 'Z' : null;
        };

        String.prototype.toJSON =
        Number.prototype.toJSON =
        Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0 ? '[]' :
                    gap ? '[\n' + gap +
                            partial.join(',\n' + gap) + '\n' +
                                mind + ']' :
                          '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    k = rep[i];
                    if (typeof k === 'string') {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0 ? '{}' :
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
                        mind + '}' : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                     typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());


if( jQuery )
{
/**
    Creates an exceedingly basic "hide/show" widget. As input it requires
    a container element (e.g. a DIV) which itself contains EXACTLY TWO
    child elements (which may themselves contain children). The first
    element is used as a label and the second as content. When the label
    is clicked, the content area "klappt zu" (it closes) and when the label
    is clicked again the content are "klappt auf" (it opens). It's similar
    to an accordion view, but restricted to one element. A multi-view
    accordion could be implemented on top of this behaviour.

    The input DOM elements are moved around and placed inside a TABLE
    element so that this code can more easily control the overall layout.

    The opt object is an optional set of properties in this form:

    {
        labelClass:'cssClassOfLabelElement',
        labelClosedClass:'cssClassAddedToLabelWhenClosed'
        contentClass:'cssClassOfContentArea',
        startClosed:bool (default=false), // if true, content area starts closed
        fadeSpeed:'fast', // Controls speed of show/hide of othe content area.
                          // Any animation fade speed supported by jQuery.
        tableAttr:{ // attributes applied to the generated TABLE
                   // defaults are something like:
                   border:0,
                   cellspacing:0,
                   cellpadding:0
                }
    }

    Note that the various CSS classes are not applied directly to the
    client-supplied elements, but to the wrapper elements which make
    up the KlappZuDing.

    Author: Stephan Beal (http://wanderinghorse.net)

    License: Public Domain, unless your copy of jQuery is GPL, in which
    case this code must derive the GPL.
*/
(function($){
    $.fn.klappZuDing = function( opt )
    {
        var csspre = 'klappZuDing-';
        opt = $.extend( true,
            {
                labelClass:csspre+'label',
                labelClosedClass:csspre+'label-closed',
                contentClass:csspre+'content',
                fadeSpeed:'fast',
                startClosed:false,
                beforeShow:null,
                beforeHide:null,
                tableAttr:{border:'0',
                           cellspacing:'0',
                           cellpadding:'0'
                }
            }, opt ? opt : {}
        );
        //var self = this;
        var ch = this.children();
        if( ch.length != 2 )
        {
            throw new Error("KlappZuDing requires that the container element have exactly two children!");
        }
        var lbl = $(ch.get(0));
        var c = $(ch.get(1));
        var tbl = jQuery.html.table();
        tbl.attr(opt.tableAttr);
        var trHead = tbl.tr();
        var lblTD = trHead.td()
        .addClass( opt.labelClass )
        .append(lbl)
        ;
        tbl.tr().td()
            .addClass( opt.contentClass )
            .append(c)
        ;
        this.append(tbl);
        var opened = !opt.startClosed;
        function doclick()
        {
            opened = opened ? false : true;
            if( opened )
            {
                lblTD.removeClass( opt.labelClosedClass );
                if( opt.beforeShow ) opt.beforeShow();
                c.slideDown( opt.fadeSpeed );
            }
            else
            {
                lblTD.addClass( opt.labelClosedClass );
                if( opt.beforeHide ) opt.beforeHide();
                c.slideUp( opt.fadeSpeed );
            }
        };
        trHead.click( doclick );
        // we don't use trHead.click() here to avoid undue animation on elems during init:
        if( !opt.startClosed ) c.show();
        else
        {
            lblTD.addClass( opt.labelClosedClass );
            c.hide();
        }
        return this;
    };
})(jQuery);
};


/**
	An extremely primitive color picker, designed to be used with a jQuery selector, as in:

	<div id='MyColorSelector'></div>

	$('#MyColorSelector').addColorPicker();

	it creates a set of "color blotches" elements and uses jQuery(this).append() to add them
	to the current element. If it is called outside of a jQuery context then it will throw
	an exception.

	Arguments:

	props = an optional array of key/val pairs:

	props.colors = array of colors (hex, rgb(), or (null or 'transparent')). The default
	contains a hex-encoded "premium, hand-picked selection" of common
	reds/yellows/blues, plus null (treated as transparent).

	props.blotchElemType: Element Type for each color blotch (default='span')

	props.blotchClass: CSS class for each element (default='ColorBlotch')

	props.clickCallback: a callback tied to each blotch, called when the blotch
	is clicked. It is passed a single color argument (hex-encoded or rgb(r,g,b)
	or null, as defined in .colors). For the special transparent color, the callback
	is passed the string 'transparent'. The default callback does nothing.

	props.iteractionCallback: function(target,elem,color,iterationNumber) is
	called after each blotch is append()ed. It is passed the target jQuery
	object, the blotch jQuery object, current color (same encoding as in
	.colors), and the current iteration count (starts at 0 and increments 1
	per blotch added). This can be used to gain some control over the layout,
	e.g. by inserting a <br/> every 5 iterations. e.g.:
		iterationCallback: function(tgt,elem,i) { if( !((i+1)%5) ) tgt.append('<br/>') }
	The default callback is null.


	props.fillString: a string which gets inserted into all
	non-transparent color blotches.

	props.fillStringX: a string which gets inserted into
	transparent blotches.


	Peculiarities of the implementation:

	- each "cell" of the selector is populated with a single
	&nbsp; UNLESS the color is (null or 'transparent'), in which
	case a '?' is used (this is to avoid visual confusion with a
	blotch of the same background container as the target
	element. If you don't like this, you can use the
	iterationCallback to change the content using jQuery's .text()
	or .html() functions.


	Code home page:
	
	http://wanderinghorse.net/computing/javascript/jquery/colorpicker/

	License: Public Domain

	Author: stephan beal (http://wanderinghorse.net/home/stephan/)

	Terse revision history (newest at the top):

	20070712:
	- integrated changes/comments from J�rn Zaefferer.
	- renamed func: braindeadColorSelector() to addColorPicker(), because that's
		really what the function does.

	20070711: initial release
*/
jQuery.fn.addColorPicker = function( props ) {
	if( ! props ) { props = []; }
	props = jQuery.extend({
		blotchElemType: 'span',
		blotchClass:'ColorBlotch',
		clickCallback: function(ignoredColor) {},
		iterationCallback: null,
		fillString: '&nbsp;',
		fillStringX: '?',
		colors: [
			'transparent', '#ffffff','#d0d0d0','#777777','#000000', // monochromes
			'#ffaaaa','#ff00ff', '#ff0000','#aa0000','#9000ff', // reds
			'#ff6c00', '#ffff00', '#ffbb00', '#f0e68c','#d2b229', // browns/oranges/yellows
			'#aaffaa','#00ff00','#00aa00','#6b8e23','#007700', // greens
			'#bbddff','#00ffdd', '#aaaaff','#0000ff','#0000aa' // blues
			]
	}, props);
	var count = props.colors.length;
	for( var i = 0; i < count; ++i ) {
		var color = props.colors[i];
		if( ! color ) color='transparent';
		var elem = jQuery('<'+props.blotchElemType+'/>')
			.addClass( props.blotchClass )
			.css( 'background-color',color); // jq bug: chaining here fails if color is null b/c .css() returns (new String('transparent'))!
		elem.html( ('transparent' == color) ? props.fillStringX : props.fillString );
		if( props.clickCallback ) {
			elem.click( function() { props.clickCallback(jQuery(this).css('background-color')); });
		}
		this.append( elem );
		if( props.iterationCallback ) props.iterationCallback( this, elem, color, i );
	}
	return this;
};

/**

    A basic event management class.

    License: Public Domain

    Author: stephan beal (http://wanderinghorse.net/home/stephan/)
*/
function EventManager()
{
    this.$evmimpl = {
        /** Next available event listener ID. */
	nextID:0,
        /**
        Each event binding is stored in here, in the format demonstrated
        by the dummy/placeholder element.
        */
	map:{
	    '$dummy/placeholder$':[{ID:0,callback:function(eventKey,payload,cbData){},cbdata:null}]
	}
    };
    return this;
};

EventManager.prototype = {
    /**
        Adds an event listener for a named event type. The arguments are:

        eventKey (string) is the type of event to listen to.

        callback is the function to call when the event is fired. It is passed
        (eventKey,eventArguments,cbData). The exact expectations of the eventArguments
        type depends on the event type. Some pass a single argument object and some
        pass on all client arguments as an array.

        cbData is passed on to the callback when the event is fired.

        When the event is fired, callback(eventKey,eventPayload,cbData) is called.

        Returns an opaque handle value which can be passed to unlisten() to deregister
        the listener. Note that the handles for all different event names are unique, regardless
        of their associated eventKey. They will all evaluate to a true value. If this app is
        used long enough, it is possible that the IDs overflow and repeat, but that would require
        a HUGE number (billions) of listeners to be created in the life of the app.
    */
    listen:function(eventKey,callback,cbData)
    {
        var ei = this.$evmimpl;
        if( ! ei.map[eventKey] ) ei.map[eventKey] = {};
        var ar = ei.map[eventKey];
        if( undefined === arguments.callee.nextID )
        {
            arguments.callee.nextID = 0;
        }
        var id = (++arguments.callee.nextID);
        //var key = eventKey+"_"+id;
        //ar[key] = {
        ei.map[id] = ar[id] = {
            ID:id,
            eventKey:eventKey,
            callback:callback,
            cbData:cbData
        };
        return id;
    },
    /**
        Unregisters an event listener which was registered with
        listen(). The eventKey is an event type and listenerID is the value
        returned from the corresponding call to listen().
    */
    unlisten:function(listenerID)
    {
        var ei = this.$evmimpl;
        var x = ei.map[listenerID];
        if( x )
        {
            ar = ei.map[x.eventKey];
            if( ar )
            {
                ar[x.ID] = undefined;
                delete ar[x.ID];
            }
            delete x;
            delete ei.map[listenerID];
        }
    },
    /**
        Fires a named event and calls the registered
        callbacks (see listen()) with the arguments:
        (eventKey,payload,cbData), where cbData is specified
        in the call to listen().

        To disable the firing of events, set jqApp.fire.disabled=true.
        Set it to false to re-enable them.
    */
    fire:function(eventKey,payload)
    {
        if( arguments.callee.disabled ) return false;
        var ar = this.$evmimpl.map[eventKey];
        if(ar) for( var id in ar )
        {
            var x = ar[id];
            if( x && x.callback )
            {
                x.callback( eventKey, payload, x.cbData );
            }
            else
            {
                delete ar[id];
            }
        }
    }
};
/**
   Causes ChildClass to inherit from ParentClass, in the conventional
   OO sense of the word inherit.

   Unlike real-world inheritance, ParentClass must outlive ChildClass.

   Derived from code posted here:

   http://groups.google.com/group/v8-users/browse_thread/thread/adfc2978ee519b42

   Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
   License: Public Domain

    How to use this:

    function MyType(){...}

    function MySubType(){...}
    extendClass( MySubType, MyType );


    After that:

    var sub = new MySubType(...);
    print(sub instanceof MyType ); // true

    and sub will (mostly) have access to properties inherited from MyType.

    But there are a few catches...

    With this approach, for OVERRIDDEN member funcs to work properly, they
    must be defined AFTER calling extendClass(), because
    ChildClass.prototype is overwritten by extendClass().

    So, proper usage is (apparently):

    function MyClass()
    {
        var av = Array.prototype.slice.apply(arguments,[0]);
        arguments.callee._superConstructor_.call( this, av );
        ...
    };
    extendClass( MyClass, MyInterface );//must go right after the ctor!

    MyClass.prototype.overriddenFunc = function(){...}; 


    Subclass ctors can call their superclass ctor with:

    var av = Array.prototype.slice.apply(arguments,[0]);
    arguments.callee._superConstructor_.call( this, av );

    or:

    MyType._superConstructor_.call( this, av );

    And they can reference superclass functions with:

    this._superClass_.funcName.call(this,[args])

    Calling them like this:

    this._superClass_.funcName(args);

    does not work polymorphically (not sure why). That is,
    if the called function calls another overridden function,
    any overridden impl of that second function won't get
    picked up (the parent impl will be selected).

    For this all to work the functions must have been
    defined outside of the ctor, e.g.:

    BaseType.prototype.funcName = function(){...};

    rather than in the ctor. Not quite sure why that is.

    RETURNS: ChildClass

    To help avoid some of the confusion which goes along
    with the limitations of this approach, ChildClass
    gets a new member called extendPrototype(), which
    is used like this:

    ChildClass.extendPrototype({
        func1:function(){...},
        prop1:....
    });

    Because extendClass() returns ChildClass, the two can
    be combined like:

    extendClass( Child, Parent ).extendPrototype({...});

*/
function extendClass( ChildClass, ParentClass )
{
    if( 1 == arguments.length )
    {
        return arguments.callee.apply( this, [this,ChildClass] );
    }
    function TempClass() {};
    TempClass.prototype = ParentClass.prototype;
    ChildClass.prototype = new TempClass();
    ChildClass.prototype.constructor = ChildClass;
    ChildClass._superConstructor_ = ParentClass;
    //ChildClass._superClass_ = ParentClass.prototype;
    ChildClass.prototype._superClass_ = ParentClass.prototype;
    function _extendPrototype( TheClass, obj )
    {
        for( var k in obj )
        {
            TheClass.prototype[k] = obj[k];
        }
    };

    ChildClass.extendPrototype = function( obj )
    {
        return _extendPrototype( ChildClass, obj );
    };
    return ChildClass;
};

if(0)
{ /* test/demo. Don't try this from a browser (where print() has very different behaviour)!
    */
    function MyType()
    {
        var av = Array.prototype.slice.apply(arguments,[0]);
        print("ctor MyType(",av.join(','),')');
        this.prop1 = 1;
        this.func = function(){
            for( var k in this )
            {
                var v = this[k];
                if( v instanceof Function ) print("this["+k+"] =function()");
                else print("this["+k+"] =",v);
            }
        };
    };
    MyType.prototype.str = function(){ return "part1";}
    function MySubType()
    {
        var av = Array.prototype.slice.apply(arguments,[0]);
        arguments.callee._superConstructor_.call( this, av );
        print("ctor MySubType(",av.join(','),')');
        this.prop2 = 2;
        var self = this;
        this.str = function() { return self._superClass_.str()+"part2";}
    };
    if(0)
    {
        MySubType.extendClass = extendClass;
        MySubType.extendClass( MyType );
    }
    else
    {
        extendClass( MySubType, MyType );
    }
    function assert(ex)
    {
        if( ! ex ) throw new Error("assertion failed!");
        return ex;
    }
    var x = new MySubType(7,3,11);
    x.func();
    print("x.str() =",x.str());
    print( "x.prop1:x.prop2 =",x.prop1 + ":" + x.prop2 );
    print( "x instanceof MyType ==",x instanceof MyType );
    var x2 = new MySubType(11,7,3);
    x2.prop2 = 32;
    print('x2.prop2='+x2.prop2,
        'x.prop2='+x.prop2,
            assert(!(x === x2)),
           assert(x === x),
           assert(x2 instanceof MyType)
           );
};
/**
Common JS code for BYOO, the Build Your Own Ogre(R)
tool.

Author: Stephan Beal (http://wanderinghorse.net)

BYOO Home Page:

  http://wanderinghorse.net/gaming/ogre/byoo

Ogre is a registered trademark of Steve Jackson Games
(http://www.sjgames.com). This material is used here in
accordance with the SJ Games online policy:

  http://www.sjgames.com/general/online_policy.html

BYOO is not a trademark: the trailing (R) symbol belongs only
to the word Ogre.


Requirements:

- jQuery 1.3

- Several jQuery plugins and other 3rd-party JS code gets
included by jqApp/include/js-packer.php.

Other Notes:

- This is not particularly clean code. Much of it is being
genericized into a separate framework. If that ever reaches
a useful point, i'd like to port BYOO over to that.

BYOO License:

    This game aid is the original creation of Stephan Beal and is
    released for free distribution, and not for resale, under the
    permissions granted in the Steve Jackson Games Online Policy
    (http://www.sjgames.com/general/online_policy.html).

    The source code itself is in the Public Domain unless your
    copy of jQuery is GPL, in which case BYOO must inherit
    the GPL license. That said, it is not clear whether
    the GPL and the SJG Online Policy are compatible.

END LICENSE
*/


/** trim() prototypes taken from

http://www.somacon.com/p355.php

(Public Domain)
String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g,"");
};
String.prototype.ltrim = function() {
	return this.replace(/^\s+/,"");
};
String.prototype.rtrim = function() {
	return this.replace(/\s+$/,"");
};

if( 0 && ! jQuery.prototype.data )
{
    jQuery.prototype.data = function(k,v)
    {
        if( undefined === this.$$data )
        {
            this.$$data = {};
        }
        if(undefined == v)
        {
            return this.$$data[k];
        }
        this.$$data[k] = v;
    };
}
*/


if(0)
{/* doesn't seem to work how i would like it to... */
/**
onclick handler to load firebug lite on the fly:

javascript:var firebug=document.createElement('script');
firebug.setAttribute('src','http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js');
document.body.appendChild(firebug);
(function(){if(window.firebug.version){firebug.init();}else{setTimeout(arguments.callee);}})();
void(firebug);
*/
    if( 'firebug' in this )//undefined !== firebug )
    {
        (function()
        {
            var repl = ['get','post','getJSON','ajax'];
            for( var i in repl )
            {
                var fn = repl[i];
                var oldF = jQuery[fn];
                jQuery[fn] = function()
                {
                    var av = Array.prototype.slice.apply(arguments,[0]);
                    var xh = arguments.callee.nonFirebug.call( this, av );
                    if( xh ) firebug.watchXHR(xh);
                    return xh;
                };
                jQuery[fn].nonFirebug = oldF;
            }
        })();
    }
};


if(0 && !Array.prototype.indexOf)
{
/*
This prototype is provided by the Mozilla foundation and
is distributed under the MIT license.
http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
*/
  Array.prototype.indexOf = function(elt /*, from*/)
  {
    var len = this.length;
    var from = Number(arguments[1]) || 0;
    from = (from < 0)
         ? Math.ceil(from)
         : Math.floor(from);
    if (from < 0)
      from += len;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}

/**
    Creates an array of numbers in the range
    [min,max], skipping step numbers between
    each entry.
*/
function byoo_create_numlist(min,max,step)
{
    if(undefined===step) step = 1;
    if(undefined===max) max = 9;
    if(undefined===min) min = 0;
    var a = [];
    for( var i = min; i <= max; i+=step)
    {
        a[''+i] = i;
    }
    return a;
};

/**
BYOO is the main application object.
*/
BYOO = {
Version:'2009 Sept 6 @ 00:23 CET',

/** The current Ogre object. */
TheOgre:null,
/* Event Manager */
ev:
{
    /**
        Events get bound, unbound, and fired here.
    */
    mgr:(new EventManager()),
    /**
        Key for BYOO status message events, fired by
        BYOO.status().

        The argument is expected to be a single string or jQuery
        object.
    */
    messageEvent:'message',
    /**
        Key for BYOO asynchronous exception events.
    */
    exceptionEvent:'exception'
},
/** All Ogres, in both JSON and Ogre formats. */
AllOgres:{
    json:null,
    list:{},
    /**
        Returns the number of items in the json list.
    */
    count:function()
    {
        var c = 0;
        var inp = this.json || this.list;
        for( var i in inp ) ++c;
        return c;
    }
},
/** All Gear, in both JSON and Gear formats. */
AllGear:
{
    json:null,
    list:{},
    /**
        Returns the number of items in the json list.
    */
    count:function()
    {
        var c = 0;
        var inp = this.json || this.list;
        for( var i in inp ) ++c;
        return c;
    }
},
/** History of selected Ogres, stored as an array
of Ogre objects with arbitrary keys. */
History:[],
/**
    Place to store instances of widgets.
*/
Applets:{},
/**
  Widget/plugin classes go here.
*/
Widget:{},
/**
    List of callbacks for BYOO.selectOgre() notifications.
*/
OgreListeners:[],
/**
    Placeholder for the Ogre Editor object.
*/
Editor:null,
/**
    Parts of URLs, used for configuring certain URL-based
    actions.
*/
URLs:{
    /**
        Directory prefix for all "widgets" - JS code
        conforming to certain conventions for use as
        parts of the BYOO UI.
    */
    widgetPrefix:'widgets/',
    /**
        Directory prefix for all "page widgets" - php/html snippets
        for importing into the main content area.
    */
    pagePrefix:'pages/',
    /**
        AJAX command to save an Ogre.
    */
    save:'ajax-save-ogre.php',
    /**
        A URL returning the Ogre DB as JSON data.
    */
    jsonOgres:'json-ogre.php',
    /**
    A URL returning the Gear DB as JSON data.
    */
    jsonGear:'json-gear.php'
},
/**
    List of all icons used for Ogre counters.

    .prefix = the URL prefix to apply to counters.

    .list = array of entries, with numeric indexes.
*/
Icons:{prefix:'counters/',
       list:null},
/**
    Holds commonly-used CSS classes.
*/
CSS:{
    currentOgreLink:'byoo-current-ogre-link',
    ogreIDClass:'byoo-Ogre-ID-',
    ogreCounter:'byoo-ogre-counter',
    hasUnsavedChanges:'byoo-has-unsaved-changes',
    gearIDClass:'byoo-Gear-ID-'
},
/** Holds all sorts of options for the app. */
Options:{
    /**
        "Cisco's Constant" is a central part of the
        Ciscos Algorithm for calculating Ogre costs.
    */
    CiscosConstant:5.357,
    /** Current counter color. */
    selectedColor:'white',
    /** Whether to show Ogre history or not. */
    showHistory:true,
    /** Fading speed for various show/hide operations. */
    fadeTime: 300, /* use jQuery.support.opacity? */
    /** Directory prefix for all Ogre counters (Ogre.Icon field). */
    countersPrefix:'counters/',
    /** How many record forms to show on the records page. */
    recordFormCount:3,
    /** Used by the Hide Legalities checkbox and cookie. */
    hideLegalities:false,
    /**
       The default ogre selection handler checks if this is set. If it
       is, it call it and passes it the selected Ogre. If it is not
       set, it performs some default action (probably showing the
       record sheet).
    */
    currentOgreHandler:null,
    /**
       A list of colors, intended for use as Ogre counter colors.
       They must be in a format compatible with the requirements of
       the colorpicker jQuery plugin.
    */
    colors:[/*'transparent',*/ '#ffffff','#d0d0d0', /*monochromes*/
            '#ffaaaa','#ff00ff', '#ff0000', /*reds*/
            '#ffff00', '#ffbb00', '#f0e68c','#d2b229', /* browns/oranges/yellows */
            '#aaffaa','#00ff00',/* greens */
            '#bbddff','#00ffdd', '#aaaaff' /* blues*/
            ],
    /**
       Cookie options, in a format compatible with jQuery.cookie().
    */
    cookieOpt:{
        path:'/', //'/gaming/ogre/byoo',
        /** Default lifetime of cookies, in days. */
        expires:30
        }
    }
};

BYOO.Icons.list =
[
    'Default.png',
    'Default-nolabel.png',
    'Doppelsoldner.png',
    'Doppelsoldner-nolabel.png',
    'Fencer.png',
    'Fencer-nolabel.png',
    'Flower.png',
    'Flower-nolabel.png',
    'FreeLove.png',
    'FreeLove-nolabel.png',
    'Lancer.png',
    'Lancer-nolabel.png',
    'MarkI.png',
    'MarkI-nolabel.png',
    'MarkII.png',
    'MarkII-nolabel.png',
    'MarkIII.png',
    'MarkIII-nolabel.png',
    'MarkIIIb.png',
    'MarkIIIb-nolabel.png',
    'MarkIIIx.png',
    'MarkIIIx-nolabel.png',
    'MarkIV.png',
    'MarkIV-nolabel.png',
    'MarkV.png',
    'MarkV-nolabel.png',
    'MarkVI.png',
    'MarkVI-nolabel.png',
    'Ninja.png',
    'Ninja-nolabel.png',
    'Spartan.png',
    'Spartan-nolabel.png'
];


/**
The default icon to use for Ogres
where Ogre.Icon is empty.
*/
BYOO.Icons.defaultIcon = 'Default-nolabel.png';

do
{
    /**
        Rewrite BYOO.Icons.list so that the
        keys and values are the same. This simplifies
        getting the proper values into and out of
        selection list objects.
    */
    var ar = BYOO.Icons.list;
    BYOO.Icons.list = [];
    for( var i in ar )
    {
        var f = ar[i];
        BYOO.Icons.list[f] = f;
    }
}while(false);

/**
Size of Ogre icons in "small" contexts, e.g.
the Ogre selection list.

The default icons we use are 48x48.
*/
BYOO.Icons.miniSize = 24;
/**
   Adds a listener function which gets called when
   selectOgre() is called.
*/
BYOO.addOgreListener = function(callback,data)
{
    var li = BYOO.OgreListeners;
    li[li.length] = {
    callback:callback,
    data:data
    };
    return li.length;
};
BYOO.removeOgreListener = function(id)
{
    delete BYOO.OgreListeners[id];
};

BYOO.log = function()
{
    var av = Array.prototype.slice.apply(arguments,[0]);
    var t = BYOO.log.target ;//$('#BYOOLog');
    if( ! t || !t.length )
    {
        t = $('#BYOOLog');
        if( ! t || !t.length ) return false;
    }
    var c = av[0];
    if( ! (c.html instanceof Function)) c = jQuery(c);
    BYOO.log.list.push(c);
    var wr = $("<span class='log-line-marker'></span>");
    wr.append( c.html() ).append('<br/>');
    t.prepend( wr );
    if( BYOO.log.list.length > BYOO.log.list.limit )
    {
        BYOO.log.list.pop();
        $('.log-line-marker:last',t).remove();
    }
};
BYOO.log.list = [];
BYOO.log.list.limit = 10;
BYOO.log.target = null;
BYOO.ev.mgr.listen( BYOO.ev.messageEvent, function(key,msg){ BYOO.log( msg ); } );

/**
    Tries to find all external Anchor elements
    in the given jQuery selector scope, and
    refines them a bit to distinguish them from
    application links.
*/
BYOO.externalizeLinks = function(scope)
{
    var sels = [ "a[href*='://']", "[href^='/']" ];
    for( var i in sels )
    {
        var sel = sels[i];
        var anc = scope ? jQuery(sel) : jQuery(sel,scope);
        anc.attr('target','_new')
            .addClass('external-link')
        ;
    }
};

/**
   Sets the main content area to the given content. Returns c.

   c may be either a jQuery object or a string.
*/
BYOO.setMainContent = function(c)
{
    var tgt = jQuery('#BYOOMainArea');
    if( ! c ) c = '(empty)';
    /*if( ! c.hide ) c =jQuery(c); // fails in jQuery code :? */
    tgt.empty();
    if( c.fadeIn )
    {
        c.hide();
        tgt.append(c);
        c.fadeIn( BYOO.Options.fadeTime,
            function()
            {
                BYOO.externalizeLinks(tgt);
            }
         );
    }
    else
    {
        tgt.hide();
        tgt.append(c);
        //if(c.show) c.show();
        tgt.fadeIn( BYOO.Options.fadeTime );
    }
    jQuery("a",tgt).addClass('whatthehell').css('font-size','1.5em').attr('target','_new');
    //tgt.hide().append(c).fadeIn( BYOO.Options.fadeTime );
    //tgt.append(c);
    return c;
};

/**
   Like BYOO.setMainContent(), but loads the content
   using jQuery.get(url). The URL should return
   something which we can pass to BYOO.setMainContent()
   (e.g. HTML).
*/
BYOO.loadMainContent = function(url)
{
    try
    {
        jQuery.get(url,{},
               function(data){
                   BYOO.setMainContent(data);
               });
    }
    catch(e)
    {
        BYOO.message("Error loading url ["+ur+"]: "+e);
    }
};

/**
Loads the given URL, which must return JavaScript
code
*/
BYOO.loadScript = function(url,callback)
{
    try
    {
        jQuery.getScript(url,
                function(data){
                    if( callback ) callback(data);
                });
    }
    catch(e)
    {
        BYOO.message("Error loading url ["+ur+"]: "+e);
    }
};

/**
    Tries to load the script file:

    BYOO.URLs.widgetPrefix+'BYOO.Widget.'name+'.js'

    By convention, the script should have a class
    called BYOO.Widget.NAME with the following
    required interface:

    CLASS.prototype.getView() must return a jQuery object
    which is the "view" for the widget. That is, the UI
    component. The view is opaque to the framework, though
    the widget may publish conventions for clients to make
    direct use of.

    Most widgets will find CLASS.prototype.setOgre(ogre)
    useful as a place to initialize their view.

    If callback is set then it is called and passed an
    instance of the widget (possibly new, possibly
    recycled).

*/
BYOO.loadWidget = function(name,callback)
{
    var old = BYOO.Applets[name];
    if( undefined !== old )
    {
        BYOO.message("Recycling Widget: "+name);
        if( callback ) callback(old);
        return;
    }
    var cname = 'BYOO.Widget.'+name;
    var url = BYOO.URLs.widgetPrefix+cname+'.js';
    BYOO.message("Trying to load new Widget type: "+name);
    BYOO.loadScript(url,function()
    {
        try
        {
            var ctor = BYOO.Widget[name];
            if( ctor instanceof Function )
            {
                BYOO.message("Loaded new Widget type: "+name);
                var obj = new ctor();
                BYOO.Applets[name] = obj;
                if( callback ) callback(obj);
            }
            else
            {
                throw new Error("Loaded Widget from ["+url+"], but it does not contain class BYOO.Widget."+name+"!");
            }
        }
        catch(e)
        {
            BYOO.message("Exception loading widget class ["+name+"]:",''+e);
            // throw e;// we "should" do this to let firebug catch it for us.
        }
        //BYOO.Applets[name] = 
        //if( callback ) callback(
    });
};
/**
   Sets the current Ogre (which may be null) for the application. Any
   callbacks added via BYOO.addOgreListener() are called and passed
   (o,data), where data is assigned via the second parameter of
   BYOO.addOgreListener().
*/
BYOO.selectOgre = function( o )
{
    BYOO.TheOgre = o;
    if( ! o )
    {
        jQuery('.byoo-requires-ogre:visible').fadeOut(BYOO.Options.fadeTime);
    }
    else
    {
        jQuery('.byoo-requires-ogre:hidden').fadeIn(BYOO.Options.fadeTime);
    }
    var li = BYOO.OgreListeners;
    for( var i in li )
    {
        var c = li[i];
        if( c.callback )
        {
            c.callback( o, c.data );
        }
    }
    BYOO.updateHistory(o);
};

/**
   Replaces one entry in the global/in-memory Ogre db.
   This can be used to replace the internal cache entry
   for individual re-loaded Ogres.

   If ogre is not in the list (we check based on its
   ID field) then it is appended.
*/
BYOO.replaceOgreJSON = function(json)
{
    var origID = json['_OriginalID'];
    var theo = null;
    if( origID )
    {
        //alert("Replacing old ID ["+origID+"] with ["+json.ID+"]");
        delete json['_OriginalID'];
        theo = BYOO.AllOgres.list[origID];
        delete BYOO.AllOgres.list[origID];
        delete BYOO.AllOgres.json[origID];
        BYOO.AllOgres.json[json.ID] = json;
        if( theo )
        {
            theo.markChangedState(false);
            theo.ID = json.ID;
            theo = BYOO.revertOgre( theo );
        }
        else
        {
            theo = new BYOO.Ogre(json);
        }
        BYOO.AllOgres.list[json.ID] = theo;
    }
    else
    {
        var ogid = json.ID;
        if( ! ogid ) return false;
        var oldid = BYOO.TheOgre ? BYOO.TheOgre.ID : 0;
        BYOO.AllOgres.json[ogid] = json;
        var theo = BYOO.AllOgres.list[ogid];
        if( theo )
        {
            BYOO.revertOgre( theo );
        }
        else
        {
            theo = BYOO.AllOgres.list[ogid] = new BYOO.Ogre(json);
        }
    }
/*    if( oldid && (oldid == theo.ID) )
    { // force update for listeners
        BYOO.selectOgre( theo );
    }*/
    var msg = "Replaced "+theo.getLabel()+" from JSON state."
    //+(json.toSource ? ":\n<tt>"+json.toSource()+"</tt>" : ".")
    ;
    BYOO.message(msg);
    return theo;
};



/**
Returns an IMG jQuery object for the given
image. It has the class BYOO.CSS.ogreCounter
added to it, and if BYOO.Options.selectedColor
is not null then the IMG's background is set to
that color.
*/
BYOO.getCounterImg = function(ico,css)
{
    if( ! ico ) return null;
    var img = jQuery('<img></img>');
    img.css('border','1px outset black');
    /*if(css) for( var k in css )
    {
        img.css(k,css[k]);
};
*/
    if(css) img.css(css);
    var fn = BYOO.Icons.prefix+ico;
    img.attr('src',fn);
    img.addClass(BYOO.CSS.ogreCounter);
    if( BYOO.Options.selectedColor )
    {
        img.css('background-color',BYOO.Options.selectedColor);
    }
    return img;
};

/**
Returns a string in the form "ooo ooo", where
the number of "o"s == count, and the "o"s will
be grouped in sets of groupBy elements. with
a separator between each.
*/
BYOO.getDots = function(count,groupBy)
{
    if( undefined === groupBy ) groupBy = 3;
    var a = [];
    var total = 0;
    while( total < count )
    {
        for( var t = 0; (total < count) && (t < groupBy); ++t, ++total)
        {
            a.push('O');
        }
        if( total < count ) a.push('&nbsp;');
    }
    return a.join('');
};

/**
    Blinks the given jQuery object(s)
    the given number of times, using the given
    fade out/in speed.

    Default:

    times == 3
    speed == (BYOO.Options.fadeTime/2)
*/
BYOO.flash = function(jq,times,speed)
{
    if( ! BYOO.Options.fadeTime ) return;
    if( undefined === times ) times = 3;
    if( undefined === speed ) speed = BYOO.Options.fadeTime/2;
    var low = 0.33;
    var high = 1.0;
    var at = 1;
    function fader()
    {
        jq.fadeTo( speed, high, function()
        {
            if( (++at <= times) )
            {
                jq.fadeTo( speed, low, fader );
            }
        });
    };
    jq.fadeTo( speed, low, fader );
};

BYOO.Gear = function(json)
{
    if( json ) for( var k in BYOO.Gear.fields )
    {
        var f = BYOO.Gear.fields[k];
        if( undefined !== json[f] )
        {
            var v = json[f];
            if(0)
            { // This check backfires on Opera 9, where "Infantry Bay" is converted to "Infinity"!!!
            // Thanks to SJ himself for pointing that out (though he didn't know what caused it).
                var ncheck = parseFloat(v);
                if( isNaN(ncheck) )//|| ((''+v).indexOf(' ') >= 0) )
                /* kludge for strings ^^^^^^ which start with a number. */
                {
                    this[f] = v;
                }
                else
                {
                    this[f] = ncheck;
                }
            }
            else
            {
                this[f] = v;
            }
        }
    }

    return this;
};

BYOO.Gear.fields = ['ID',
                    'SortOrder',
                    'Name',
                    'ShortName',
                    'Attack',
                    'Range',
                    'Defense',
                    'Ciscos',
                    'IsOfficial',
                    'Notes',
                    'CountPerRow',
                    'CountGroupBy',
                    'CountDefault',
                    'CountHintMin',
                    'CountHintMax',
                    'CountHintDefault',
                    'CountHintStep'
                    ];


/**
    Returns a CSS class name which is unique to
    gear objects with this object's ID.
*/
BYOO.Gear.prototype.getIDClass = function()
{
    return BYOO.CSS.gearIDClass+this.ID;
};
/**
   Returns a string describing a BYOO.Gear object in a simple/compact
   format. If count is set it must be an integer describing how many
   of the gear items are represented. Set count to a false value to
   leave off the count part of the record.
*/
BYOO.Gear.prototype.getRecordLine = function(count,verbose)
{
    var a = [];
    if( count )
    {
        a.push( parseInt(count)+'x' );
    }
    if( verbose )
    {
        //a.push( this.getLabel() );
        a.push( this.Name );
        a.push( '('+this.ShortName+')' );
    }
    else
    {
        a.push( this.ShortName );
    }
    if( this.Attack )
    {
        a.push('ATT '+this.Attack+'/'+this.Range);
    }
    if( this.Defense )
    {
        a.push('DEF '+this.Defense);
    }
    if( verbose && count )
    {
        a.push( ': '+BYOO.getDots(count,this.CountGroupBy ? this.CountGroupBy : 3));
    }
    return a.join(' ');
};

/** For internal use only.

    Creates a SELECT widget for a specific Ogre/Gear combination,
    for setting the count. Changes to the object are updated in
    ogre's state. If ogre does not contain a gear object of this type
    then 'count' gear items of this type are added to it.
*/
BYOO.Gear.prototype._createGearCountSelector = function(ogre,count)
{
    var span = jQuery.html.span();
    //span.addClass( this.getIDClass() );
    var f = jQuery.html.select();
    f.addClass(BYOO.Ogre.inputFieldClass);
    if( undefined === count ) count = this.CountDefault || this.CountHintDefault;
    var min = this.CountHintMin;
    var max = this.CountHintMax;
    if( count > max ) max = count;
    else if( count < min ) min = count;
    var list = [];
    for( var i = min; i <= max; ++i )
    {
        list[i] = i;
    }
    var fpre = ogre.getFormFieldName('Gear')+'['+this.ID+']';
    f.attr('name',fpre+'[Count]');
    if(1)
    {
        var hid = jQuery.html.hidden();
        hid.attr('name',fpre+'[ID]');
        hid.attr('value',this.ID);
        span.append(hid);
    }
    /** We need to find the holder object record in ogre.Gear... */
    /*
    var holder = null;
    for( var i in ogre.Gear )
    {
        var gent = ogre.Gear[i];
        if( gent.ID != this.ID ) continue;
        holder = gent;
        break;
    }*/
    var holder = ogre.Gear[this.ID];

    var self = this;
    if( ! holder )
    {
        ogre.Gear[this.ID] = holder = {ID:this.ID, Count:count, JSObj:self};
        BYOO.message("Added new Gear to Ogre: "+this.Name);
    }

//     function isgeardirty(ogre,jgear,gearHolder)
//     {
//         //var json = BYOO.AllOgres.json[ogre.ID];
//         //var jgear = json ? json.Gear : null;
//         if( ! jgear )
//         {
//             BYOO.message("Internal error: Gear editor selector cannot find json store for Ogre "+ogre.getLabel());
//             return false;
//         }
//         //BYOO.message("dirty check: x"+count+'<br/>',jgear.toSource(),'<br/>',ogre.Gear.toSource());
//         //if( jgear.length != ogre.Gear.length ) return true;
//         for( var i in jgear )
//         {
//             var onegear = jgear[i];
//             if( onegear.ID != gearHolder.ID ) continue;
//             var og = ogre.Gear[onegear.ID];
//             //BYOO.message("dirty check: x"+gearHolder.Count,':<br/>',onegear.toSource(),'<br/>',og.toSource());
//             if( ! og || (og.Count != onegear.Count) )
//             {
//                 return true;
//             }
//             return false;
//             // FIXME? do need to check for new gear?
//         }
//         // new Gear item
//         return true;
//     };

    jQuery_select_elem_wrapper(f,{
        selectedValue:count,
        items:list,
        callback:function(val,opt,data)
        {
            holder.Count = parseInt(val);
            BYOO.message(self.ShortName,'count set to',val);
//             var json = BYOO.AllOgres.json[ogre.ID];
//             var jgear = json ? json.Gear : null;
//             if( ! jgear )
//             {
//                 BYOO.message("Internal error: Gear editor selector cannot find json store for Ogre "+ogre.getLabel());
//                 return;
//             }
//             //if( isgeardirty(ogre, jgear, holder) )
//             if( ogre.checkDirty(true) )
//             {
//                 //ogre.markChangedState(true);
//                 span.addClass( BYOO.CSS.hasUnsavedChanges );
//             }
//             else
//             {
//                 span.removeClass( BYOO.CSS.hasUnsavedChanges );
//                 //ogre.checkDirty(true);
//             }
        }
    });
    span.append(f);
    return span;
};
/**
    Gets an editor widget (a jQuery object) which is configured
    to edit this gear count for the given ogre. The gear is
    added to ogre if it doesn't already have a gear item of this
    type.
*/
BYOO.Gear.prototype.getEditorForOgre = function(ogre,count)
{
    if( undefined === count ) count = 0;
    var c = jQuery.html.span();
    c.append( this._createGearCountSelector(ogre,count) )
        .addClass( this.getIDClass() )
        .append( jQuery.html.nbsp() )
        .append( this.getRecordLine(0,true) )
        ;
    return c;
};

/**
    Creates a jQuery object associated with the given
    ogre. The widget contains a list of all Gear items
    NOT in the Ogre. If a gear item is selected for addition
    via this widget's UI then callback(ogre,gear,opt) is called,
    where ogre is the argument passed to this function, gear
    is the global instance of the selected Gear object,
    and opt is the OPTION element which triggered the call.
    The caller may wish to call opt.remove() to remove the option
    from the list. The callback is not called if the Ogre
    already has the selected gear item.
*/
BYOO.Gear.getGearAdder = function(ogre,callback)
{
    var sp = jQuery.html.span();
    var sel = jQuery.html.select();
    var gearKey = 'gearObject';
    var gcount = 0;
    for( var gid in BYOO.AllGear.list )
    {
        var g = BYOO.AllGear.list[gid];
        if( ogre.Gear[g.ID] ) continue;
        ++gcount;
        var opt = jQuery.html.option();
        opt.attr('value',gid);
        //opt.append( g.getLabel() );//getLabel() stylizes some items, and Firefox can't show them in the SELECT part!
        opt.append( g.getRecordLine(0,true) );
        sel.append(opt);
    }
    if( ! gcount )
    {
        var opt = jQuery.html.option()
            .attr('value',-1)
            .append("Nothing left to add!")
            ;
        sel.append( opt );
    }
    else
    {
        var btn = jQuery.html.button();
        btn.attr('value','Add');
        var self = this;
        btn.click(function()
        {
            var opt = jQuery("option:selected:first",sel);
            //var o = opt.data( dataKey );
            var gid = parseInt( opt.attr('value') );
            var g = gid ? BYOO.AllGear.list[gid] : null;
            if( g )
            {
                var ex = ogre.Gear[g.ID];
                if( ex )
                {
                    BYOO.message("Ogre already has Gear item: "+g.getLabel());
                }
                else
                {
                    ogre.checkDirty(true);
                    if(callback) callback( ogre, g, opt );
                }
            }
        });
    }
    sp.append( sel )
        //.append( jQuery.html.nbsp() )
        .append( btn );
    return sp;
};

/**
   Wraps str in a span with the class byoo-sjg-official.
*/
BYOO.sjgOfficialize = function(str)
{
    return "<span class='byoo-sjg-official'>"+str+"</span>";
};

/**
   Returns a string (possibly HTML)
   with a label for this Ogre.
*/
BYOO.Gear.prototype.getLabel = function()
{
    if( 0 == this.IsOfficial ) return this.Name;
    return "<span class='byoo-sjg-official'>"
    + this.Name
    + '</span>';
};


BYOO.Ogre = function(json)
{
    if( json )
    {
        var numreg = /^\d+(\.\d+)?$/;
        for( var k in BYOO.Ogre.fields )
        {
            var f = BYOO.Ogre.fields[k];
            delete this[f]; // ensure clean slate on json load
            var v = json[f];
            if( undefined !== v )
            {
                var ncheck = parseFloat(v);
                if( ! isNaN(ncheck) && numreg.test(v) )
                {
                    this[f] = ncheck;
                }
                else
                {
                    this[f] = v;
                }
            }
        }
    }
    var sg = json ? json['Gear'] : null;
    //delete this['Gear'];
    this.Gear = [];
    if( sg )
    {
        for( var k in sg )
        {
            var g = sg[k];
            var gid = parseInt(g['ID']);
            var c = parseInt(g['Count']);
            this.Gear[gid] = { ID:gid,
                             JSObj:BYOO.AllGear.list[gid],
                             Count:c };
        }
    }
    return this;
};
/** Only serializable fields go here, not including Gear. */
BYOO.Ogre.fields = ['ID',
                    'Name',
                    'Nickname',
                    'LastUpdated',
                    'Icon',
                    'SizeClass',
                    'MP',
                    'TUMP',
                    'Ciscos',
                    'CiscosFudge',
                    'IsOfficial',
                    'Notes',
                    'Submitter',
                    'SubmitterInfo'
                    ];
/** Fields for which we want to consider a change to be "dirty". */
BYOO.Ogre.dirtyFields = ['Name',
                        'Nickname',
                        'Icon',
                        'MP',
                        'TUMP',
                        'SizeClass',
                        'Submitter',
                        'SubmitterInfo',
                        'Notes'];
/**
    Internal function to add a unique-per-Ogre-ID class
    to the given jquery object(s).
*/
BYOO.Ogre.prototype.applyCSS = function(jqObject)
{
    jqObject.addClass(this.getIDClass());
};

/**
   Marks this ogre has "clean" (no unsaved changes).
*/
BYOO.Ogre.prototype.markChangedState = function(dirty)
{
    var li = this.getLinksTo();
    //this.dirty = dirty;
    if( dirty ) li.addClass( BYOO.CSS.hasUnsavedChanges );
    else li.removeClass( BYOO.CSS.hasUnsavedChanges );
    return dirty;
};

/**
Applies the given Ogre object to the given jQuery target element. The
contents of jq are replaced with the record form.  If verbose is true
then submitter info and notes (if any) are appended to the record form.
*/
BYOO.applyRecord = function(jq,ogre,verbose)
{
    jq.addClass('byoo-ogre-record')
    .empty();
    if( ! ogre )
    {
        jq.append("No Ogre selected.");
        return jq;
    }
    jq.append( ogre.getCounterImg() )
    .append(' ')
    .append( jQuery('<b></b>').append(ogre.getLabel()) )
    ;
    if( ogre.Nickname )
    {
        jq.append(' (<i>'+ogre.Nickname+'</i>)');
    }
    var fmt = function(x) { return Math.round(x*10)/10; };
    jq.append( "<br/><b>Size Class:</b> "+ogre.SizeClass+'<br/>' )
        .append( '<b>Ciscos:</b> '+fmt(ogre.Ciscos))
        .append( ' <b>Approx. Armor Points:</b> '+fmt(ogre.Ciscos/10))
        .append( ' <b>Approx. SJG VP:</b> '+fmt(ogre.Ciscos*0.6)+'<br/>')
    ;
    if( ogre.Gear.length )
    {
        jq.append('<b>Weapons/Equipment:</b><br/>');
    }
    for( var k in ogre.Gear )
    {
        var x = ogre.Gear[k];
        var g = x.JSObj; //BYOO.AllOgres.json[ogre.ID];
        jq.append( jQuery.html.tt().append( g.getRecordLine(x.Count,true) ) )
            .append('<br/>')
            ;
    }
    jq.append("<b>"+ogre.MP+" MP, "
              + ogre.TUMP+" Treads/MP = "
              + (ogre.MP*ogre.TUMP)
              + " treads</b><br/>");
    var m = ogre.MP;
    for( ; m > 0; --m )
    {
        jq.append( '<tt>MP '+m+'&nbsp;&nbsp;'
                   +BYOO.getDots( ogre.TUMP, 5 )
                   +'</tt><br/>');
    }
    if( ! verbose )
    {
        return jq;
    }
    if( ogre.Submitter)
    {
        jq.append('<b>Submitted to BYOO by:</b> '+ogre.Submitter);
        if( ogre.SubmitterInfo )
        {
            jq.append(' ('+ogre.SubmitterInfo+')');
        }
        jq.append('<br/>');
    }
    if( "" != ogre.Notes )
    {
        jq.append("<b>Notes:</b><br/>")
        .append("<div>"+ogre.Notes+"</div>");
    }
    return jq;
};
/**
   Returns a string (possibly HTML)
   with a label for this Ogre.
*/
BYOO.Ogre.prototype.getLabel = function()
{
    if( 0 == this.IsOfficial ) return this.Name;
    return "<span class='byoo-sjg-official'>"
    + this.Name
    + '</span>';
};
/**
    NYI.
*/
BYOO.Ogre.prototype.set = function(key,val)
{
    if( ! (key in BYOO.Ogre.dirtyFields) ) return undefined;
    if( ! this.isDirty )
    {
        var json = BYOO.AllOgres.json[this.ID];
        if( json[key] != this[key] )
        {
            this.isDirty = true;
            //this.markChangedState(true);
        }
    }
    return this[key] = val;
};

/**
   Recalculates this Ogre's Ciscos value. Sets this.Ciscos
   and returns that value.
*/
BYOO.Ogre.prototype.calcCiscos = function()
{
    var c = 0;
    c = 2 * (this.MP * this.TUMP);
    if( this.Gear.length ) for( var k in this.Gear )
    {
        var x = this.Gear[k];
        var g = BYOO.AllGear.list[x.ID];
        c += (x.Count * g.Ciscos);
    }
    if( c )
    {
        c = c * BYOO.Options.CiscosConstant;
        c = c / (10 - this.MP);
    }
    return this.Ciscos = c;
};

// BYOO.Ogre.prototype.checkDirtyGear = function()
// {
//     var json = BYOO.AllOgres.json[this.ID];
//     var jgear = json ? json.Gear : null;
//     if( ! jgear )
//     {
//         BYOO.message("Internal error: Gear editor selector cannot find json store for Ogre "+ogre.getLabel());
//         return false;
//     }
// 
//     for( var i in jgear )
//     {
//         var gjs = jgear[i];
//         var og = this.Gear[gjs.ID];
//         //alert(gjs.toSource()+"\n"+og.toSource());
//         if( ! og || (og.Count != gjs.Count) )
//         {
//             return true;
//         }
//         // FIXME: we need to check for new gear.
//     }
//     return false;
// };

/**
   Checks the "dirty" (unsaved changes) state. It updates links to
   this object to reflect that state.
*/
BYOO.Ogre.prototype.checkDirty = function()
{
    //if( ! arguments.length ) alsoGear = true;
    var json = BYOO.AllOgres.json[this.ID];
    if( ! json )
    {
        if(0)
        {
            throw new Error("Ogre.checkDirty() (Ogre #"+this.ID
                            +") could not find corresponding BYOO.AllOgres.json[] entry!");
        }
        else
        {
            // this is likely a stale link to an Ogre which got a new ID when it was saved.
            return false;
        }
    }
    var dirty = false;
    var flist = BYOO.Ogre.dirtyFields;
    for( var i in flist )
    {
        var f = flist[i];
        if( json[f] == this[f] ) continue;
        //alert("Setting dirty due to field ["+f+"]\n["+this.json[f]+"] != ["+this[f]+"]");
        dirty = true;
        break;
    }
//     if( ! dirty && alsoGear )
//     {
//         dirty = this.checkDirtyGear();
//         //BYOO.message("checkDirty(false
//     }

    return this.markChangedState(dirty);
};

/**
    Returns a string in the form:

    "this.Name (this.Nickname)"

    but the nickname part is left out if (!this.Nickname).

    Intended for use as the 'title' attribute for
    links.
*/
BYOO.Ogre.prototype.getTitle = function()
{
    var lbl = this.Name
        +(this.Nickname?(" ("+this.Nickname+")"):"")
    ;
    if( 0 && this.dirty )
    { /* need to iron out some other details first. */
        lbl += " [Has unsaved changes!]";
    }
    return lbl;
};

/**
    (Tries to) return a list of all links to
    this Ogre object which were created via
    Ogre.getLinkObj().
*/
BYOO.Ogre.prototype.getLinksTo = function()
{
    return jQuery("a."+this.getIDClass());
};
/**
    Tries to replace all links to this ogre
    with updated info (current icon and name).
//     Links created with getLinkObj() are compatible
    with this function. If the link object
    contains an IMG with the class BYOO.CSS.ogreCounter
    then it is also updated with the current counter
    image.
*/
BYOO.Ogre.prototype.updateExternalLinks = function()
{
    var ckey = this.getIDClass();
    var li = this.getLinksTo();
    if(0) BYOO.message('updateLinks(): '+BYOO.CSS.ogreIDClass+':'+ckey+':'+li.length);
    var ogre = this;
    li.each(function(i)
    {
        var a = jQuery(li[i]);
        if(0)
        { /* this would be much simpler than the following approach, but
            it doesn't accomodate customizations to the link objects, like
            those done in the main Ogre list.
            */
            a.replaceWith( ogre.getLinkObj() );
        }
        else
        {
            a.attr('title',ogre.getTitle());
            var im = jQuery('img.'+BYOO.CSS.ogreCounter,a);
            im.attr('src',BYOO.Icons.prefix+ogre.Icon);
            jQuery('span.link-label',a).html(ogre.getLabel());
        }
        BYOO.flash(a,2,'medium');
    });
};

/**
   BYOO.getCounterImg(this.Icon), using a default icon if
   no other is set. css is an optional object containing
   CSS properties for the image.
*/
BYOO.Ogre.prototype.getCounterImg = function(css)
{
    return BYOO.getCounterImg(this.Icon ? this.Icon : BYOO.Icons.defaultIcon, css);
};

/**
   Default callback for BYOO.Ogre.getLinkObj().
*/
BYOO.Ogre.getLinkObjCallback = function(og,data)
{
    BYOO.selectOgre(og);
};

/**
   Returns a new jQuery '<A>' object which acts as a link to activate
   this Ogre. When clicked, the given callback is called and passed
   the this Ogre object. If callback is null then a default callback
   is used which calls BYOO.selectOgre(this).

    opt may optionally be a set of key/value pairs

    {
        addBR:boolean = if true, then a BR is appended to the link.

        addIcon:boolean = if true, then a mini-icon is prepended to the link.

        iconSize:integer = the size of the image (if addIcon is true), defaults
        to BYOO.Icons.miniSize.
    }

   The returned object has these properties set:

   - CSS class BYOO.CSS.ogreIDClass+(this.ID)

   - 'title' = this.getTitle();

   - 'href' = '#' and the click op returns false.
*/
BYOO.Ogre.prototype.getLinkObj = function(callback,opt)
{
    opt = jQuery.extend({
        addIcon:false,
        addBR:false,
        iconSize:BYOO.Icons.miniSize
    },opt ? opt : {});
    var j = jQuery("<a href='#' onClick='false;'></a>");
    var ogre = this;
    var t = ogre.getTitle();
    j.attr('title',t);
    if( ! callback ) callback = BYOO.Ogre.getLinkObjCallback;
    j.click(function(){
                callback(ogre);
                return false;
            });
    /**
        Reminder: we use a SPAN here only so that we can
        more reliably update links post-facto.
    */
    var sp = jQuery('<span></span>')
        .addClass('link-label')
      .append(ogre.getLabel());
    j.append( sp );
    if(opt.addIcon)
    {
        var img = this.getCounterImg({width:opt.iconSize,height:opt.iconSize});
        if(img) j.prepend(img);
    }
    if(opt.addBR)
    {
        j.append('<br/>');
    }
    ogre.applyCSS(j);//.addClass(BYOO.CSS.ogreIDClass+ogre.ID);
    return j;
};

/**
   Creates a countersheet for this Ogre, with the given
   number of rows and calls (both of which have reasonable
   defaults.

   Returns a new jQuery element which contains the countersheet.
*/
BYOO.Ogre.prototype.createCounterSheet = function(rows,cols)
{
    if( ! rows ) rows = 5;
    if( ! cols ) cols = 8;
    var div = jQuery('<div></div>');
    var img = this.getCounterImg();
    img.attr('title',img.attr('src').replace(/.+\//g,''));
    for( var r = 0; r < rows; ++r )
    {
        for( var c = 0; c < cols; ++c )
        {
            div.append(img.clone());
        }
        div.append('<br/>');
    }
    return div;
};

/**
   Creates a record sheet for this Ogre. If verbose is true then the
   Ogre's notes and submitter info are included on the form.

   Returns a new jQuery element which contains the record sheet.
*/
BYOO.Ogre.prototype.createRecordSheet = function(verbose)
{
/** Reminder: we use a TABLE instead of a DIV because
    the latter's border expands to the full allowed width. */
    var tb = jQuery('<table><tbody></tbody></table>');
    var tr = jQuery('<tr></tr>');
    var td = jQuery('<td></td>');
    tr.append(td);
    tb.append(tr);
    BYOO.applyRecord(td,this,verbose);
    return tb;
};

/**
    Returns a string in the form
    "Ogres[this.ID][fld]"

    for use as the name for an input field assigned
    to an Ogre property. When such fields are submitted
    to PHP, PHP creates an object tree which looks something
    like:

    'Ogres' => [
            Ogre.ID => {ID:32,Name:...,...},
            otherOgre.ID =>{ID:1,Name...}
            ]

    which is easy to then parse back to a format we can
    save to the db.
*/
BYOO.Ogre.prototype.getFormFieldName = function(fld)
{
    return 'Ogres['+this.ID+']['+fld+']';
};
/**
    Not yet completely implemented.

    Creates a jQuery element representing and editor
    for the given property of this object.

    Supported properties:

    - Name
    - Nickname
    - Icon

    The opt param is an optional object of metaproperties
    for the editor element:

    If opt.callback is set then it is called after
    a change event, and passed (theOgre, prop, inputElement),
    where inputElement is the object returned by this
    function.
*/
BYOO.Ogre.prototype.getPropFormElem = function(prop,opt)
{
    if( (undefined === prop) || (undefined === this[prop]) ) return null;
    if( ! opt ) opt = {};
    var fp = arguments.callee.fields;
    var e = arguments.callee.fields[prop];
    var c = e.factory(this);
    var inp = $('.'+BYOO.Ogre.inputFieldClass,c);//('input'==c.attr('type')) ? c : $('input',c);
    var _opt = opt ? opt : {};
    var ogre = this;
    if( inp.length )
    {
        inp.removeClass(BYOO.Ogre.inputFieldClass);
        inp.attr('name', /*prop?*/this.getFormFieldName(prop) );
        //inp.attr('value',this[prop]);
        inp.change( function()
        {
            //ogre[prop] = $(this).attr('value');
            if( opt.callback )
            {
                opt.callback(ogre,prop,$(this));
            }
            if( e.defaultCallback )
            {
                e.defaultCallback( ogre, prop, $(this) );
            }
        });
    }
    return c;
};
BYOO.Ogre.inputFieldClass = 'ogre-property-input-field';
/**
Returns the string BYOO.CSS.ogreIDClass+this.ID.
*/
BYOO.Ogre.prototype.getIDClass = function()
{
    return BYOO.CSS.ogreIDClass+this.ID;
};


/**
   Compares the internal state of the given property of
    this ogre against its original json copy. If the
    values differ, tgt, which must be-a jQuery element gets the
   CSS class BYOO.CSS.hasUnsavedChanges added to it. If they are the
   same, the CSS class is removed.

   If tgt is null or undefined then this.getLinksTo() is used.
*/
BYOO.Ogre.prototype.flagChanged = function(prop,tgt)
{
    if( (undefined === tgt) || (null === tgt) ) tgt = this.getLinksTo();
    var json = BYOO.AllOgres.json[this.ID];
    if( ! json )
    {
      throw new Error("Could not find internal JSON store for Ogre #"+this.ID+"!");
    }
    if( (this[prop] != json[prop]) )
    {
        tgt.addClass(BYOO.CSS.hasUnsavedChanges);
    }
    else
    {
        tgt.removeClass(BYOO.CSS.hasUnsavedChanges);
    }
};

/** For internal use only.

   Creates a container with a SELECT in it. list is the
   list of items to add to the list.

   Returns a jQuery object.
*/
BYOO.Ogre.prototype._createSelectionEditor = function(prop,list)
{
    var e = $('<span></span>');
    var f = $('<select></select>');
    f.addClass(BYOO.Ogre.inputFieldClass);
    var self = this;
    jQuery_select_elem_wrapper(f,{
        selectedValue:this[prop],
        items:list,
        callback:function(val,opt,data)
        {
            self[prop] = val;
            self.checkDirty();
            self.flagChanged(prop,e);
            var msg = $('<span></span>');
            msg.append( self.getLabel() )
                .append(' '+prop+' set to ')
                //.append('&nbsp;');
                .append( val )
            ;
            BYOO.message(msg);
        }
    });
    this.flagChanged(prop,e);
    e.append(f);
    return e;
};

/** For internal use only.

props = this object's property to proxy.

opt = optional set of properties:
   label = label text (default=prop)
   size = length of the text field (default=20 or so)
*/
BYOO.Ogre.prototype._createTextField = function(prop,opt)
{
    opt = jQuery.extend({
        label:prop,
        size:20
    }, opt ? opt : {});
    var e = jQuery('<span></span>');
    var f = jQuery("<input type='text'></input>");
    var passons = ['size'];
    for( var i in passons )
    {
        var k = passons[i];
        f.attr(k,opt[k]);
    }
    f.attr( 'value', this[prop] );
    f.addClass(BYOO.Ogre.inputFieldClass);
    var self = this;
    f.change(function(){
        var val = f.attr('value');
        self[prop] = val;
        self.checkDirty();
        self.flagChanged(prop,e);
        if( (''+val).length < 20 )
        {
            BYOO.message('Ogre.'+prop+' set to '+self[prop]);
        }
        else
        {
            BYOO.message('Ogre.'+prop+' updated.');
        }
    });
    this.flagChanged(prop,e);
    e.append('<b>'+opt.label+':</b>&nbsp;');
    e.append(f);
    return e;
};

/** For internal use only.

   Creates a container with a textarea in it. opt is
   an optional key/val object with args for the TEXTAREA
   field.

   Returns a jQuery object.
*/
BYOO.Ogre.prototype._createTextArea = function(prop,opt)
{
    opt = jQuery.extend({
        label:prop,
        cols:72,
        rows:8
    }, opt ? opt : {});
    var e = jQuery('<div></div>');
    var f = jQuery('<textarea></textarea>');
    var passons = ['rows','cols'];
    for( var i in passons )
    {
        var k = passons[i];
        f.attr(k,opt[k]);
    }
    f.attr( 'value', this[prop] );
    f.addClass(BYOO.Ogre.inputFieldClass);
    var self = this;
    f.change(function(){
        var val = f.attr('value');
        self[prop] = val;
        self.checkDirty();
        self.flagChanged(prop,e);
        BYOO.message('Ogre.'+prop+' set.');
    });
    this.flagChanged(prop,e);
    e.append('<b>'+opt.label+':</b><br/>')
        .append(f);
    return e;
};


/**
    Factories for creating editor elements for
    individual Ogre properties.
*/
BYOO.Ogre.prototype.getPropFormElem.fields =
{
    ID:
    {
        factory:function(ogre)
        {
            return jQuery('<span></span>').append( ogre.ID );
        }
    },
    Name:
    {
        defaultCallback:function(ogre,prop,fldjq) {ogre.updateExternalLinks();},
        factory:function(ogre)
        { return ogre._createTextField('Name'); }
    },
    Nickname:
    {
        defaultCallback:function(ogre,prop,fldjq) {ogre.updateExternalLinks();},
        factory:function(ogre)
        { return ogre._createTextField('Nickname'); }
    },
    Icon:
    {
        defaultCallback:function(ogre,prop,fldjq) {ogre.updateExternalLinks();},
        factory:function(ogre)
        {
            var e = $('<span></span>');
            var ico = ogre.getCounterImg();
            e.append(ico);
            var f = $('<select></select>');
            f.addClass(BYOO.Ogre.inputFieldClass);
            jQuery_select_elem_wrapper(f,{
                selectedValue:ogre.Icon,
                items:BYOO.Icons.list,
                callback:function(val,opt,data)
                {
                    var i = BYOO.Icons.list[val];
                    ogre.Icon = i;
                    ogre.checkDirty();
                    ogre.flagChanged('Icon',e);
                    var msg = $('<span>Ogre.Icon set to</span>');
                    msg.append('&nbsp;');
                    if(false)
                    {/*
                        Konqueror 4.2 ignores the image sizes set here, but
                        honors them in the Ogre list view?
                    */
                        var dim = BYOO.Icons.miniSize;
                        msg.append( ogre.getCounterImg({width:dim,height:dim}) );
                    }
                    else msg.append( i );
                    BYOO.message(msg);
                    ico.attr('src',BYOO.Icons.prefix+i);
                }
            });
            ogre.flagChanged('Icon',e);
            e.append(f);
            return e;
        }
    },
    SizeClass:{
        factory:function(ogre)
        { return ogre._createSelectionEditor('SizeClass',byoo_create_numlist(5,9));}
    },
    'MP':{
    factory:function(ogre)
    {return ogre._createSelectionEditor('MP',byoo_create_numlist(1,4));}
    },
    'TUMP':{
        factory:function(ogre)
        {return ogre._createSelectionEditor('TUMP',byoo_create_numlist(1,30));}
    },
    Ciscos:{label:'Ciscos',
        factory:function(ogre)
        {
            return jQuery('<span></span>').addClass('achtung').append( "NYI!" );
        }
    },
    'CiscosFudge':{
        factory:function(ogre)
        {
            return jQuery('<span></span>').addClass('achtung').append( "NYI!" );
        }
    },
    'IsOfficial':
    {
        factory:function(ogre)
        {
            return jQuery('<span></span>').append( ogre.IsOfficial ? "yes" : "no" );
        }
    },
    'Notes':
    {
        factory:function(ogre)
        { return ogre._createTextArea('Notes',
                                      {rows:8,
                                       cols:72,
                                       label:"Notes"}
                    );
        }
    },
    'Submitter':
    {
        factory:function(ogre)
        { return ogre._createTextField('Submitter'); }
    },
    'SubmitterInfo':
    {
        factory:function(ogre)
        { return ogre._createTextField('SubmitterInfo',{size:40}); }
    }

};
/**
    Creates a object which contains this Ogre's
    data in a JSON-friendly structure.
*/
BYOO.Ogre.prototype.createJSONable = function()
{
    var json = {};
    for( var k in BYOO.Ogre.fields )
    {
        var f = BYOO.Ogre.fields[k];
        var v = ''+this[f];
        json[f] = v;
    }
    json.Gear = [];
    for( var k in this.Gear )
    {
        var cg = this.Gear[k];
        var jg = {ID:cg.ID, Count:cg.Count};
        json.Gear.push( jg );
    }
    return json;
};

/**
    Clones this Ogre, but does not update BYOO.AllOgres.
    As part of the cloning process, the (ID, Name) fields
    are changed to [try to] make them unique. Likewise IsOfficial
    will bet set to 0.
*/
BYOO.Ogre.prototype.clone = function()
{
    var c = new this.constructor(this);
    var top = 10000000;
    var d = new Date();
    c.ID = 'NEW_'+d.getTime()+'_'+d.getMilliseconds()+'_'+Math.ceil((Math.random()*top%top));
    c.Name = 'Copy of '+c.Name;
    c.IsOfficial = 0;
    return c;
};

BYOO.createNewOgre = function(from)
{
    if(from)
    {
        var newog = from.clone();
        newjs = newog.createJSONable();
        BYOO.AllOgres.json[newog.ID] = newjs;
        BYOO.AllOgres.list[newog.ID] = newog;
        return newog;
    }
    // TODO: create a blank entry.
    return null;
};

/**
   Loads all Ogre objects from the remote server. If jq is not null it must be-a jQuery
   object. It is modified to reflect the loading state.

   If callback is not null then it must be a function, which is called
   after the load is complete and is passed no arguments.

   On success the callback will be called and BYOO.AllOgres.* will be
   populated.
*/
BYOO.loadOgres = function(callback)
{
    jQuery('#BYOOOgreList').empty();
    BYOO.AllOgres.list = null;
    BYOO.AllOgres.json = null;
    BYOO.selectOgre(null);
    var oldLabel = null;
    BYOO.clearHistory();
    jQuery.getJSON(BYOO.URLs.jsonOgres,
        function(data)
        {
            var jli = {};
            var oli = {};
            var count = 0;
            for( var i in data.Ogres )
            {
                ++count;
                var json = data.Ogres[i];
                jli[json.ID] = json;
                oli[json.ID] = new BYOO.Ogre(json);
            }
            BYOO.AllOgres.json = jli;
            BYOO.AllOgres.list = oli;
            if( callback )
            {
                callback();
            }
        });
};

/**
    Returns true if the Ogre DB state has been loaded.
*/
BYOO.isLoaded = function()
{
    //alert('BYOO.AllOgres.list.length='+BYOO.AllOgres.list.length);
    return (BYOO.AllOgres.json.length ==
            BYOO.AllOgres.list.length)
        ? true
        : false;
};
/**
    Loads the Gear from the DB and calls the given
    callback on success, passing it no parameters.
    The loaded Gear is in BYOO.AllGear.json (in the
    original JSON form) and BYOO.AllGear.list (high-level
    Gear objects). The keys of those arrays are the
    IDs of the contained Gear data.
*/
BYOO.loadGear = function(callback)
{
    var oldLabel = null;
    jQuery.getJSON(BYOO.URLs.jsonGear,
        function(data)
        {
            var jar = data.Gear;
            BYOO.AllGear.json = {};
            /** Map BYOO.AllGear.json to BYOO.Gear
                objects in BYOO.AllGear.list.
            */
            for( var k in jar )
            {
                var g = jar[k];
                var gid = g['ID'];
                BYOO.AllGear.json[gid] = g;
                BYOO.AllGear.list[gid] = new BYOO.Gear(g);
            }
            if(callback) callback();
        });
};

/**
   Reverts the given Ogre from its cached JSON state. Returns
   ogre on success, null on error.
*/
BYOO.revertOgre = function(ogre)
{
    if( ! ogre || !ogre.ID) return ogre;
    var js = BYOO.AllOgres.json[ogre.ID];
    if( ! js ) return null;
    ogre.markChangedState( false );
/*    var oldid = BYOO.TheOgre ? BYOO.TheOgre.ID : 0;
*/
    BYOO.Ogre.apply( ogre, [js] );
    /*alert("Reverted Ogre "+ogre.Name);*/

/*    if( oldid && (oldid == ogre.ID) )
    { // force update for listeners
        BYOO.selectOgre( ogre );
    }
*/
    return ogre;
};


BYOO.message = function(str)
{
//     if( ! BYOO.message.target )
//     {
//         BYOO.message.target = jQuery('#BYOOStatusLine');
//     }
    //var t = BYOO.message.target;
    var msg = jQuery.html.span();
    msg.append( (new Date()).toLocaleTimeString() )
        .append(':&nbsp;');
    for( var i = 0; i < arguments.length; ++i )
    {
        var a = arguments[i];
        if( ! (a.html instanceof Function) ) a = jQuery.html.span().append(''+a);
        msg.append(a)
            .append( jQuery.html.nbsp() )
        ;
    }
    //t.empty().append( $(msg).html() ); /* why won't append(msg) do the trick? */
    //BYOO.flash(t,1);
    BYOO.ev.mgr.fire( BYOO.ev.messageEvent, msg );
};
BYOO.message.target = null;

BYOO.updateHistory = function(ogre)
{
    if( undefined === ogre ) ogre = BYOO.TheOgre;
    if( ! ogre ) return false;
    var tgt = jQuery('#BYOOHistory');
    for( var i in BYOO.History )
    {
        var o = BYOO.History[i];
        if( o.ID == ogre.ID )
        {
            BYOO.flash( $('.'+ogre.getIDClass(),tgt), 2 );
            return;
        }
    }
    jQuery('#BYOOHistoryOps').show();
    BYOO.History[BYOO.History.length] = ogre;
    var link = ogre.getLinkObj();
    link.hide();
    tgt.append( link )
        .append(' &nbsp; ')/* the nbsp is a workaround for konqueror. */
    ;
    link.addClass(BYOO.CSS.currentOgreLink);
    link.fadeIn(BYOO.Options.fadeTime);
    BYOO.flash(link,2,"medium");
    return true;
};

BYOO.clearHistory = function(ogre)
{
    jQuery('#BYOOHistory')
    .empty();
    BYOO.History = [];
    jQuery('#BYOOHistoryOps').hide();
}


/**
   Listens for the selected Ogre and changes the current page
   to that Ogre.
*/
BYOO.addOgreListener( function(o,data) {
    if(o)
    {
        //BYOO.message("Selected Ogre: "+o.getLabel());
        if(1)
        { /* Eye candy to mark links to the current Ogre.
             Can be turned off without hurting the app.
          */
            var key = BYOO.CSS.currentOgreLink;
            var pre = BYOO.CSS.ogreIDClass;
            jQuery("a."+BYOO.CSS.currentOgreLink)
                .removeClass(BYOO.CSS.currentOgreLink);
            o.getLinksTo().addClass(BYOO.CSS.currentOgreLink);
        }
        if( BYOO.Options.currentOgreHandler )
        {
            BYOO.Options.currentOgreHandler(o);
        }
        else
        {
            BYOO.setMainContent( o.createRecordSheet(true) );
        }
    }
    else
    {
        /*BYOO.message("No Ogre is currently selected.");*/
    }
});
/**
    Loads url via HTTP GET and replaces the content of
    the tgt jQuery object with that content. getopt may
    be an object of properties for jQuery.get(). The optional
    callback is called without parameters after the GET
    request returns successfully (it is not called if
    the GET fails).
*/
BYOO.getPage = function( url, tgt, getopt, callback )
{
    if( tgt instanceof String ) tgt = $(tgt);
    BYOO.message("Loading remote page ["+url+"]...");
    if( ! getopt ) getopt = {};
    jQuery.get(url,
                getopt,
                function(data,status){
                    BYOO.message("Loaded remote page ["+url+"]");
                    if( data && tgt)
                    {
                        //var d = $(data);/*causes a crash or endless loop in jQ for some constructs? */
                        //d.hide();
                        tgt.empty().append( data );
                        BYOO.externalizeLinks(tgt);
                        //d.show(BYOO.Options.fadeTime);
                    }
                    if( callback )
                    {
                        callback();
                    }
                });
};

/**
   If passed two args it sets a cookie to the given value, else it returns
   the value of the given cookie (or null if (!cookie)).
   Uses BYOO.Options.cookieOpt for the cookie options.
*/
BYOO.cookie = function(cookie,val)
{
    if(1)
    {
        return (arguments.length > 1)
        ? jQuery.cookie(cookie, val, BYOO.Options.cookieOpt)
        : (cookie ? jQuery.cookie(cookie) : null);
    }
    else
    {
        return (arguments.length > 1)
        ? (jQuery.ajax({
                url:'ajax-set-cookie.php',
                data:('name='+escape(cookie)+'&value='+escape(val)),
                cache:false
                       }),true)
        : jQuery.cookie(cookie);
    }
};

/**
 Creates a countersheet for all available Ogre counters.
*/
function byoo_show_mega_countersheet()
{
    var c = jQuery('<div></div>');
    var howMany = 2;
    var pos = 0;
    var perRow = 4;
    for( var i in BYOO.Icons.list )
    {
        var str = BYOO.Icons.list[i];
        var img = BYOO.getCounterImg( str );
        img.attr('title',img.attr('src').replace(/.+\//g,''));
        c.append(img.clone());
        for( var x = 0; x < (howMany-1); ++x )
        {
            c.append(img.clone());
        }
        if( pos++ && (0 == (pos % perRow)) ) c.append('<br/>');
    }
    return BYOO.setMainContent( c );
}

/**
 Creates a countersheet for the given Ogre, or
 for all Ogres if (!ogre).
*/
function byoo_show_countersheet(ogre)
{
    BYOO.Options.currentOgreHandler = function(o){ byoo_show_countersheet(o); };
    if( ! ogre )
    {
        return byoo_show_mega_countersheet();
    }
    var c = ogre.createCounterSheet();
    return BYOO.setMainContent( c );
}

/**
 Displays the given Ogre, or BYOO.TheOgre if (!ogre).
*/
function byoo_show_ogre(ogre)
{
    if( ! ogre ) ogre = BYOO.TheOgre;
    if( ! ogre )
    {
        BYOO.message("No Ogre selected.");
        return false;
    }
    ogre.calcCiscos(); // in case editor changed it.
    var c = ogre.createRecordSheet(true);
    BYOO.Options.currentOgreHandler = function(o){ byoo_show_ogre(o); };
    return BYOO.setMainContent( c );
}

/**
 Displays BYOO.Options.recordFormCount record sheets for the given
 Ogre, or BYOO.TheOgre if (!ogre).
*/
function byoo_show_record_sheets(ogre)
{
    if( ! ogre ) ogre = BYOO.TheOgre;
    var key = 'OgreRecordForm';
    ogre.calcCiscos(); // in case editor changed it.
    function update(ap)
    {
        ap.opt.count = BYOO.Options.recordFormCount;
        ap.setOgre( ogre );
        BYOO.Options.currentOgreHandler = function(o){ byoo_show_ogre(o); };
        BYOO.setMainContent( ap.getView() );
    }
    BYOO.loadWidget(key,update);
}

/**
   Displays all Ogre Gear using BYOO.setMainContent().
*/
BYOO.listGear = function()
{
    var tbl = jQuery.html.table();
    tbl.attr('border','1');
    var fld = [
        {lbl:'Name',
            align:'left',
            f:function(g){
            var l = g.getLabel();
            if( g.ShortName ) l += ' ('+g.ShortName+')';
            return l;
            }},
        {lbl:'Attack',align:'center',f:function(g){return g.Attack;}},
        {lbl:'Range',align:'center',f:function(g){return g.Range;}},
        {lbl:'Defense',align:'center',f:function(g){return g.Defense;}},
        {lbl:'Ciscos',align:'center',f:function(g){return g.Ciscos;}},
        {lbl:'Notes',align:'left',f:function(g){return g.Notes ? g.Notes : '&nbsp;';}}
    ];
    var tr = jQuery.html.tr();// NOT tbl.tr(): it needs to be put into tbl.thead()
    tr.addClass('even');
    for( var i in fld )
    {
        var wr = fld[i];
        var td = tr.th();
        td.append(wr.lbl);
    }
    tbl.thead().append(tr);
    var even = true;
    for( var k in BYOO.AllGear.json )
    { /* reminder: we use the json list instead of the BYOO.Gear list because
       it's sorted like we want it. */
        var j = BYOO.AllGear.json[k];
        var g = BYOO.AllGear.list[j.ID];
        even = even ? false : true;
        tr = tbl.tr().addClass( even ? 'even' : 'odd' );
        for( var i in fld )
        {
            var wr = fld[i];
            tr.td()
                .attr('align', wr.align || 'left')
                .append(wr.f(g));
        }
    }
    /*tbl.flexigrid({height:'auto',striped:false}); */
    BYOO.setMainContent(tbl);
};
/**
    Should be called once after all application data
    has been loaded.
*/
function byoo_setup_after_load()
{
    // FIXME: what happens at startup should be determined by the client
    // code (i.e. index.php).
    var tgt = $('#BYOOOgreListTarget');
    tgt.empty()
        .hide()
        .css('font-size','80%')
        .css('border','1px inset #444444')
    ;
    function setview(wid)
    {
        wid.populateList(BYOO.AllOgres.list);
        wid.updateView();
        tgt.append( wid.getView() );
        tgt.show();
        BYOO.flash(tgt,1,'fast');
        BYOO.message('Loaded and ready.');
    };
    BYOO.loadWidget( 'OgreList',setview);
};
