new function() {
// compiled on Tue 11/09/2010 19:20:23.67

// ***
// jquery.!!presentation.js
// workaround for a jQuery bug, where load events are not fired
// if there are no ready events in queue
$(nop);
var presentation_globals = function() {
  var endSlashRegex = /\w+\//;
  var g = {
    body: null,
    url: function() {
      // initialize with standard Engine URLs
      var result = {
        host: location.host + estradaCustom.sitePath + '/',
        base: ($('script[src$=/scripts/core.js]').attr('src') || '')
          .replace(/scripts\/core.js$/, '')
      };
      result['fullHost'] = location.protocol + '//' + result.host;
      return result;
    } (),
    gridTheme: null,
    getLayoutSource: function() {
      var html = '<html><head>'
      html += '<base href="' + location.href + '" />'
      $.each(map(document.getElementsByTagName('link')), function() {
        var l = outerHtml(this);
        l = l.replace(/media=(")*screen(")*/, '');
        html += l;
      });
      html += '</head><body class="' + document.body.className + '">';
      html += document.getElementById('l-page').innerHTML.replace(/'/g, '&#146;');
      html += '</body></html>';
      return html;

      function outerHtml(n) {
        return n.outerHTML ? n.outerHTML : function() {
          var parent = n.parentNode;
          var el = document.createElement(parent.tagName);
          el.appendChild(n);
          var shtml = el.innerHTML;
          parent.appendChild(n);
          return shtml;
        } ()
      }

      function newWindow() {
        var w = window.open();
        w.document.write(html);
        w.document.close();
      }
    },
    uiHandler: {
      add: function(bucket, key, node) {
        var me = this;
        if (!me.items[bucket]) {
          me.items[bucket] = {};
        }
        me.items[bucket][key] = node;
      },
      getStorageKeys: function(node) {
        var bucket = node.parents('[id]:last').attr('id');
        var key = $.trim($(node).attr('className').replace('content-query', ''));
        if (!key) {
          key = node.attr('className');
        }
        if (bucket && key) {
          return { bucket: bucket, key: key };
        }
        return null;
      },
      items: {}
    },
    recurrenceIndicator: function() {
      $('div.range', g.layout.areas.content).addClass('recurrence-indicator');
    },
    addRecurrence: function(node) {
      g.layout.addContent(node);
    },
    behaviors: {},
    layout: {
      addContent: function(node) {
        g.layout.add('content', node);
      },
      add: function(area, node) {
        g.layout.areas[area].append(node);
      },
      areas: {},
      func: []
    },
    global: {},
    extension: function() {
      var regex = /.+\.(\w+)\/?/;
      var match = regex.exec(window.location);
      if (match) {
        return match[1];
      }
      return null;
    } (),
    query: function() {
      if (!(window.location.search && (window.location.search.length > 1))) {
        return {};
      }
      var result = {};
      var q = window.location.search.substr(1);
      var pairs = q.split('&');
      for (var i = 0; i < pairs.length; i++) {
        var keyvalue = pairs[i].split('=');
        result[keyvalue[0]] = (keyvalue.length == 2) ? keyvalue[1] : '';
      }
      return result;
    } (),
    meta: function() {
      var g = this;
      var result = {};
      $('meta').each(function() {
        var name = this.name;
        if (name != 'robots') {
          if (name == 'view') {
            var items = this.content.split(',');
            result[name] = {
              name: items[0],
              title: items[1]
            };
          }
          else {
            result[name] = this.content;
          }
        }
      });
      return result;
    } (),
    initialize: function(layout) {
      var g = this;
      layout.bind('content-copy-placement').to(document);
    },
    authorDisabled: function() {
      $(function() {
        $('a').each(function() {
          var link = $(this);
          var href = link.attr('href');
          link.attr('href', $.createUrl(href, 'noauthoring=1'));
        });
      });
    }
  };

  g.gridTheme = g.url.base + '../themes/jqGrid/steel/';

  g.urlState = function() {
    var registered = [];
    var prevHref = window.location.href;
    var prevState = '';
    var g = {
      state: function(val) {
        if (val) {
          window.location.hash = val;
          prevHref = window.location.href;
          prevState = g.state();
        }
        else {
          if (!window.location.hash) {
            return '';
          }
          return window.location.hash.substr(1);
        }
      },
      onchange: function(callback) {
        registered.push(callback);
      },
      monitor: function() {
        function check() {
          if (window.location.href != prevHref) {
            prevHref = window.location.href;
            $.each(registered, function() {
              this({ prev: prevState, state: g.state() });
            });
          }
        }
        setInterval(check, 100);
      } ()
    };
    prevState = g.state();
    return g;
  } ();

  g.isAdminRequest = function() {
    return (g.extension != null && g.extension == 'admin');
  } ();

  return g;
} ();

plugin('presentation', function() {
  return presentation_globals;
}, true);

$.presentationHelper = $.presentation();


// ***
// jquery.!a-ui-handler.js

var global_ui_handler = {
	DATA_KEY: 'ui_handler_data',
	setQuery: function (query, name, value) {
		if (!query || query.length == 0) {
			query = "?";
		}
		else {
			query += "&";
		}
		query += encodeURIComponent(name) + "=" + encodeURIComponent(value);
		return query;
	},
	// the collection of registered renderers
	handlers: {},
	// This is the renderer that is used when no renderer is found for the
	// specified key or the key is not specified
	// also, this renderer is merged with all renderers to supply missing/optional
	// members
	handler: {
		titleCapitalized: 'Default',
		title: 'default',
		className: 'default',
		selector: '.content ul:first',
		itemLoader: null,
		cqRender: function(config) {
		  var node = config.queryJNode;
			var g = global_ui_handler;
			g.cqConfig = config;
      g.handler.render(node, g);
		},
		render: function(node, params) {
      $('form', node).remove();
		}
	}
};


// The main method has two modes:
// * registration mode, which is used to register a custom renderer
// * processing mode, which is used to process an element or collection of elements
// The modes are chosen transparently by using different call signatures.
//
// Registration usage:
// * key:String -- a string key that identifies a custom renderer
// * renderer:CustomRendererObject -- an object that has all the methods and
//   properties, necessary to create a custom renderer
// Progessing usage -- call with no parameters on the node that is to be
// processed
plugin('uiHandler', function(key, handler) {
	var g = global_ui_handler;
	if (!key) {
		// processing mode
		process(this);
	} 
	else if (handler) {
		// registration mode
		g.handlers[key] = handler;
	}
	else {
	  if (typeof(key) == 'string') {
	    return getUiHandler(key);
	  }
	  else {
	    process(this, key);  // key = params
	  }
	}
	return this;
	
	function process(jq, params) {
	  if (!jq.size) {
		  handlerRender(document, params);
		  return;
		}
	  jq.each(function() {
	    handlerRender(this, params);
	  });
	}
	
	function handlerRender(node, params) {
	 
	  $('.ui-handler > span.key', node).each(function() {
	    var n = $(this);
	    if (n.length > 0) {
	      var key = n.text();
	      if (key && (key.length > 0)) {
	        var node = n.parent();
	        n.remove();
	        var handler = $.extend({}, g.default_handler,	g.handlers[key] || {});
	        handler.render && handler.render(node, params);
	      }
	      else {
	        n.remove();
	      }
	    }
	  });
	}
	
	function getUiHandler(key) {
		//return g.handlers[key] ? g.handlers[key] : null;
		var handler = g.handlers[key];
		if (handler) {
		  return $.isFunction(handler) ? handler() : handler;
		}
		return null;
	}
	
	/*
	function getUiHandler(key) {
		return $.extend({}, g.default_handler,
				g.handlers[key] || {});
	}
	*/

});


// ***
// jquery.!b-ui-grid.js
// Grid renderer

var global_ui_grid = {
  DATA_KEY: 'ui_handler_grid',
  imgpath: $.presentation().gridTheme + 'images/',
  dialogDefaults: {
    top: 0,
    left: 0,
    //height: 350,
    modal: false,
    drag: false,
    closeicon: 'ico-close.gif',
    imgpath: $.presentation().gridTheme + 'images/',
    url: null,
    mtype: "POST",
    closeAfterAdd: false,
    clearAfterAdd: true,
    closeAfterEdit: false,
    reloadAfterSubmit: true,
    onInitializeForm: null,
    beforeInitData: null,
    beforeShowForm: null,
    afterShowForm: null,
    beforeSubmit: null,
    afterSubmit: null,
    onclickSubmit: null,
    afterComplete: null,
    onclickPgButtons: null,
    afterclickPgButtons: null,
    editData: {},
    recreateForm: false,
    addedrow: "first"
  },
  gridDialog: {
    create: function(params) {

      var empty = {};
      var tParams = $.extend(empty, global_ui_grid.dialogDefaults, params || {});
      var dialog = {};
      $('#' + tParams.gridId).each(function() {
        var $t = this;
        var gID = tParams.modalId ? tParams.modalId : $("table:first", $t.grid.bDiv).attr("id");
        var IDs = { themodal: 'mod-' + gID, modalhead: 'hd' + gID, modalcontent: 'cnt' + gID };
        var frmgr = "FrmGrid_" + gID;
        var frmtb = "TblGrid_" + gID;
        var frm = $("<form name='FormPost' id='" + frmgr + "' class='FormGrid'></form>");
        var tbl = $("<table id='" + frmtb + "' class='EditTable' cellspacing='0' cellpading='0' border='0'><tbody></tbody></table>");
        $.gridModal.createModal(IDs, frm, tParams, $t.grid.hDiv, $t.grid.hDiv);
        dialog.node = $('#' + IDs.themodal);
        tParams.className && dialog.node.addClass(tParams.className);
        var zindex = dialog.node.css('z-index');
        zindex = zindex ? zindex - 5 : 900;
        dialog.node.css({ 'z-index': zindex });
        tParams.close && $('.jqmClose', dialog.node).click(function() {
          tParams.close();
        });
        dialog.view = function() {
          $.gridModal.viewModal("#" + IDs.themodal, { modal: tParams.modal });
        };
      });
      return dialog;
    },
    remove: function(dialog) {
      dialog.node.remove();
    }
  },
  gridDefaults: {
    scrollrows: true,
    shrinkToFit: false,
    rowNum: 10,
    rowList: [10, 20, 30, 40, 50],
    imgpath: $.presentation().gridTheme + 'images',
    sortname: 'id',
    viewrecords: true,
    sortorder: "desc",
    caption: "Grid",
    height: '100%',
    gridComplete: function(jqGrid) {
      var g = global_ui_grid;
      if (jqGrid.handler && jqGrid.handler.gridId) {
        if (jqGrid.settings.maxHeight) {
          var grid = $('#' + jqGrid.handler.gridId);
          var gridParent = grid.parent();
          var p = gridParent;
          var height = gridParent.outerHeight();
          while (height == 0) {
            p = p.parent();
            if (!p) break;
            height = p.outerHeight();
          }
          if (height > jqGrid.settings.maxHeight) {
            gridParent.css({ height: jqGrid.settings.maxHeight + 'px', 'overflow-y': 'auto' });
            if (!$.support.boxModel) {
              if (!jqGrid.settings.width || isNaN(qGrid.settings.width)) {
                grid.width('98%');
              }
            }
          }
        }
      }
      this.uiGridComplete && this.uiGridComplete(jqGrid);
    }
  },
  setGridDimensions: function(handler) {
    var g = global_ui_grid;
    var gridEl = $(handler.gridParams.gridEl);
    var width = $(handler.gridParams.gridEl).css('width').replace('px', '');
    var grid = $('#' + handler.gridId);
    var holder = grid.parent().parent();
    holder.addClass('grid-holder');
    if (!handler.gridParams.width) {
      //g.gridParams && (g.gridParams.maxWidth = gridEl.parent().innerWidth());
      var p = gridEl.parent();
      var w = p.innerWidth();
      if (w == 0) {
        var strWidth = p.css('width').replace('px', '');
        strWidth && (w = (isNaN(strWidth) ? 0 : parseInt(strWidth)));
        while (w == 0) {
          p = p.parent();
          if ((p == null) || (p.length == 0) || (p.attr('nodeName').toLowerCase() == 'html')) break;
          strWidth = p.css('width').replace('px', '');
          strWidth && (w = (isNaN(strWidth) ? 0 : parseInt(strWidth)));
        }
      }
      handler.gridParams.maxWidth = w;
    }
    else {
      if (!isNaN(handler.gridParams.width)) {
        // set width of ui-handler div to enable centering of ui grid
        if (!isNaN(width)) {
          width = parseInt(width) + 2;
          holder.css('width', width + 'px');
        }
        else {
          holder.css('width', width);
          $('> *', holder).css('width', width);
        }
      }
    }
  },
  navDefaults: {
    add: false,
    edit: false,
    del: false,
    refresh: false,
    search: false
  },
  editDefaults: {
    height: 280,
    reloadAfterSubmit: false
  },
  addDefaults: {},
  deleteDefaults: {
    reloadAfterSubmit: false
  },
  searchDefaults: {},
  gridParams: null,
  gridId: null,
  handler: function() {
    return new handlerObject();
    function handlerObject() {
      var o = {
        titleCapitalized: 'Grid',
        title: 'grid',
        className: 'grid',
        selector: '.content ul:first',
        render: function(node, uiFrom) {
          var g = global_ui_grid;
          var me = this;
          var uiParamNode = $('.ui-params', node);
          var jsonParams = (uiParamNode.length > 0) ? eval("(" + uiParamNode.text() + ")") : {};
          if (!uiFrom) uiFrom = {gridParams: {}};
          var tmpGridParams = {};
          tmpGridParams.uiGridComplete = uiFrom.gridParams.gridComplete;
          $.each(uiFrom.gridParams, function(key, value) {
            if (key != 'gridComplete') {
              tmpGridParams[key] = value;
            }
          });
          me.gridParams = $.extend({}, g.gridDefaults, jsonParams, tmpGridParams || {});
          uiParamNode.remove();
          var strParam = $('.ui-grid-nav-params', node).text();
          strParam = (strParam && strParam.length > 0) ? strParam : '{}';
          var navParams = $.extend(g.navDefaults, eval("(" + strParam + ")"), uiFrom.navButtons || {});
          if (me.gridParams.datatype == 'xmlstring') {
            me.gridParams.datastr = node.toXML();
            node.empty();
          }
          me.gridId = uiFrom.gridId = 'l-grid-' + $.guid();
          if (!me.gridParams.handler) {
            me.gridParams.handler = {};
          }
          me.gridParams.handler.gridId = me.gridId;
          var gridNode = node.dom('table').attr('id', me.gridId).addClass('scroll');
          me.gridParams.gridEl = gridNode[0];
          g.setGridDimensions(me);
          if (!uiFrom.noPager) {
            me.gridParams.pager = $.div().attr('id', me.gridId + '-pager').addClass('grid-nav').insertAfter(gridNode);
          }
          if (uiFrom.topNavigation) {
            var topNav = $.div().addClass('grid-nav top-nav');
            gridNode.before(topNav);
          }
          me.gridParams.pager = node.find('.grid-nav');
          gridNode.jqGrid(me.gridParams).navGrid(me.gridParams.pager,
		        navParams,
		        g.editDefaults,
		        g.addDefaults,
            g.deleteDefaults,
            g.searchDefaults
		      ).gridCustomButtons(me.gridParams.pager, uiFrom.customButtons);
        }
      };
      return o;
    }
    
  }
};

$.uiHandler('ui-grid', global_ui_grid.handler);

plugin('uiGridDialog', function() {
  return global_ui_grid.gridDialog;
}());

plugin('uiGridDialogDefaults', function() {
  return global_ui_grid.dialogDefaults;
}());

plugin('gridCustomButtons', function(pager, customButtons) {
  var nav = $(this);
  for (var key in customButtons) {
    var caption = customButtons[key].caption ? customButtons[key].caption : key;
    var o = $.extend(customButtons[key], {caption: caption, title: key});
    nav.navButtonAdd(pager.attr('id'), o);
  }
});

// ***
// jquery.!jqGrid.common.js
 /**
 * jqGrid common function
 * Tony Tomov tony@trirand.com
 * http://trirand.com/blog/ 
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
**/ 
// Modal functions

plugin('gridModal', function() {

  var o = {
    showModal: function(h) {
	    h.w.show();
    },
    closeModal: function(h) {
	    h.w.hide();
	    if(h.o) { h.o.remove(); }
    },
    createModal: function(aIDs, content, p, insertSelector, posSelector, appendsel) {
	    var clicon = p.imgpath ? p.imgpath+p.closeicon : p.closeicon;
	    var mw  = document.createElement('div');
	    jQuery(mw).addClass("modalwin").attr("id",aIDs.themodal);
	    var mh = jQuery('<div id="'+aIDs.modalhead+'"><table width="100%"><tbody><tr><td class="modaltext">'+p.caption+'</td> <td style="text-align:right" ><a href="javascript:void(0);" class="jqmClose">'+(clicon!=''?'<img src="' + clicon + '" border="0"/>':'X') + '</a></td></tr></tbody></table> </div>').addClass("modalhead");
	    var mc = document.createElement('div');
	    jQuery(mc).addClass("modalcontent").attr("id",aIDs.modalcontent);
	    jQuery(mc).append(content);
	    mw.appendChild(mc);
	    var loading = document.createElement("div");
	    jQuery(loading).addClass("loading").html(p.processData||"");
	    jQuery(mw).prepend(loading);
	    jQuery(mw).prepend(mh);
	    jQuery(mw).addClass("jqmWindow");
	    if (p.drag) {
		    jQuery(mw).append("<img  class='jqResize' src='"+p.imgpath+"resize.gif'/>");
	    }
	    if(appendsel===true) { jQuery('body').append(mw); } //append as first child in body -for alert dialog
	    else { jQuery(mw).insertBefore(insertSelector);	}
	    if(p.left ==0 && p.top==0) {
		    var pos = [];
		    pos = findPos(posSelector) ;
		    p.left = pos[0] + 4;
		    p.top = pos[1] + 4;
	    }
	    if (p.width == 0 || !p.width) {p.width = 300;}
	    if(p.height==0 || !p.width) {p.height =200;}
	    if(!p.zIndex) {p.zIndex = 950;}
	    /*
	    var height = (p.height==='string') ? p.height : p.height+'px';
	    jQuery(mw).css({top: p.top+"px",left: p.left+"px",width: p.width+"px",height: height, zIndex:p.zIndex});
	    */
	    jQuery(mw).css({top: p.top+"px",left: p.left+"px",width: p.width+"px",height: p.height+"px", zIndex:p.zIndex});
	    return false;
    },
    viewModal: function(selector,o){
	    o = jQuery.extend({
		    toTop: true,
		    overlay: 10,
		    modal: false,
		    onShow: $.gridModal.showModal,
		    onHide: $.gridModal.closeModal
	    }, o || {});
	    jQuery(selector).jqm(o).jqmShow();
	    return false;
    },
    hideModal: function(selector) {
	    jQuery(selector).jqmHide();
    },
    DnRModal: function(modwin,handler){
	    jQuery(handler).css('cursor','move');
	    jQuery(modwin).jqDrag(handler).jqResize(".jqResize");
	    return false;
    },
    info_dialog: function(caption, content, c_b, pathimg) {
	    var cnt = "<div id='info_id'>";
	    cnt += "<div align='center'><br />"+content+"<br /><br />";
	    cnt += "<input type='button' size='10' id='closedialog' class='jqmClose EditButton' value='"+c_b+"' />";
	    cnt += "</div></div>";
	    $.gridModal.createModal({
		    themodal:'info_dialog',
		    modalhead:'info_head',
		    modalcontent:'info_content'},
		    cnt,
		    { width:290,
		    height:120,drag: false,
		    caption:"<b>"+caption+"</b>",
		    imgpath: pathimg,
		    closeicon: 'ico-close.gif',
		    left:250,
		    top:170 },
		    '','',true
	    );
	    $.gridModal.viewModal("#info_dialog",{
		    onShow: function(h) {
			    h.w.show();
		    },
		    onHide: function(h) {
			    h.w.hide().remove();
			    if(h.o) { h.o.remove(); }
		    },
		    modal :true
	    });
    }
  }
  return o;

  //Helper functions
  function findPos(obj) {
	  var curleft = curtop = 0;
	  if (obj.offsetParent) {
		  do {
			  curleft += obj.offsetLeft;
			  curtop += obj.offsetTop; 
		  } while (obj = obj.offsetParent);
		  //do not change obj == obj.offsetParent 
	  }
	  return [curleft,curtop];
  };
  function isArray(obj) {
	  if (obj.constructor.toString().indexOf("Array") == -1) {
		  return false;
	  } else {
		  return true;
	  }
  };
  // Form Functions
  function createEl(eltype,options,vl,elm) {
	  var elem = "";
	  switch (eltype)
	  {
		  case "textarea" :
				  elem = document.createElement("textarea");
				  if(!options.cols && elm) {jQuery(elem).css("width","99%");}
				  jQuery(elem).attr(options);
				  if(vl == "&nbsp;" || vl == "&#160;") {vl='';} // comes from grid if empty
				  jQuery(elem).val(vl);
				  break;
		  case "checkbox" : //what code for simple checkbox
			  elem = document.createElement("input");
			  elem.type = "checkbox";
			  jQuery(elem).attr({id:options.id,name:options.name});
			  if( !options.value) {
				  vl=vl.toLowerCase();
				  if(vl.search(/(false|0|no|off|undefined)/i)<0 && vl!=="") {
					  elem.checked=true;
					  elem.defaultChecked=true;
					  elem.value = vl;
				  } else {
					  elem.value = "no";
				  }
				  jQuery(elem).attr("offval","off");
			  } else {
				  var cbval = options.value.split(":");
				  if(vl == cbval[0]) {
					  elem.checked=true;
					  elem.defaultChecked=true;
				  }
				  elem.value = cbval[0];
				  jQuery(elem).attr("offval",cbval[1]);
			  }
			  break;
		  case "select" :
			  elem = document.createElement("select");
			  var msl = options.multiple==true ? true : false;
			  if(options.value) {
				  var ovm = [];
				  if(msl) {jQuery(elem).attr({multiple:"multiple"}); ovm = vl.split(","); ovm = jQuery.map(ovm,function(n){return jQuery.trim(n)});}
				  if(typeof options.size === 'undefined') {options.size =1;}
				  if(typeof options.value == 'string') {
					  var so = options.value.split(";"),sv, ov;
					  jQuery(elem).attr({id:options.id,name:options.name,size:Math.min(options.size,so.length) });
					  for(var i=0; i<so.length;i++){
						  sv = so[i].split(":");
						  ov = document.createElement("option");
						  ov.value = sv[0]; ov.innerHTML = jQuery.htmlDecode(sv[1]);
						  if (!msl &&  sv[1]==vl) ov.selected ="selected";
						  if (msl && jQuery.inArray(jQuery.trim(sv[1]), ovm)>-1) {ov.selected ="selected";}
						  elem.appendChild(ov);
					  }
				  } else if (typeof options.value == 'object') {
					  var oSv = options.value;
					  var i=0;
					  for ( var key in oSv) {
						  i++;
						  ov = document.createElement("option");
						  ov.value = key; ov.innerHTML = jQuery.htmlDecode(oSv[key]);
						  if (!msl &&  oSv[key]==vl) ov.selected ="selected";
						  if (msl && jQuery.inArray(jQuery.trim(oSv[key]),ovm)>-1) ov.selected ="selected";
						  elem.appendChild(ov);
					  }
					  jQuery(elem).attr({id:options.id,name:options.name,size:Math.min(options.size,i) });
				  }
                  jQuery(elem).css("width","98%");
			  }
			  break;
		  case "text" :
			  elem = document.createElement("input");
			  elem.type = "text";
			  vl = jQuery.htmlDecode(vl);
			  elem.value = vl;
			  if(!options.size && elm) {
				  jQuery(elem).css({width:"98%"});
			  }
			  jQuery(elem).attr(options);
			  break;
		  case "password" :
			  elem = document.createElement("input");
			  elem.type = "password";
			  vl = jQuery.htmlDecode(vl);
			  elem.value = vl;
			  if(!options.size && elm) { jQuery(elem).css("width","99%"); }
			  jQuery(elem).attr(options);
			  break;
		  case "image" :
			  elem = document.createElement("input");
			  elem.type = "image";
			  jQuery(elem).attr(options);
			  break;
	  }
	  return elem;
  };
  function checkValues(val, valref,g) {
	  if(valref >=0) {
		  var edtrul = g.p.colModel[valref].editrules;
	  }
	  if(edtrul) {
		  if(edtrul.required === true) {
			  if( val.match(/^s+$/) || val == "" )  return [false,g.p.colNames[valref]+": "+jQuery.jgrid.edit.msg.required,""];
		  }
		  // force required
		  var rqfield = edtrul.required === false ? false : true;
		  if(edtrul.number === true) {
			  if( !(rqfield === false && isEmpty(val)) ) {
				  if(isNaN(val)) return [false,g.p.colNames[valref]+": "+jQuery.jgrid.edit.msg.number,""];
			  }
		  }
		  if(edtrul.minValue && !isNaN(edtrul.minValue)) {
			  if (parseFloat(val) < parseFloat(edtrul.minValue) ) return [false,g.p.colNames[valref]+": "+jQuery.jgrid.edit.msg.minValue+" "+edtrul.minValue,""];
		  }
		  if(edtrul.maxValue && !isNaN(edtrul.maxValue)) {
			  if (parseFloat(val) > parseFloat(edtrul.maxValue) ) return [false,g.p.colNames[valref]+": "+jQuery.jgrid.edit.msg.maxValue+" "+edtrul.maxValue,""];
		  }
		  if(edtrul.email === true) {
			  if( !(rqfield === false && isEmpty(val)) ) {
			  // taken from jquery Validate plugin
				  var filter = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i;
				  if(!filter.test(val)) {return [false,g.p.colNames[valref]+": "+jQuery.jgrid.edit.msg.email,""];}
			  }
		  }
		  if(edtrul.integer === true) {
			  if( !(rqfield === false && isEmpty(val)) ) {
				  if(isNaN(val)) return [false,g.p.colNames[valref]+": "+jQuery.jgrid.edit.msg.integer,""];
				  if ((val % 1 != 0) || (val.indexOf('.') != -1)) return [false,g.p.colNames[valref]+": "+jQuery.jgrid.edit.msg.integer,""];
			  }
		  }
		  if(edtrul.date === true) {
			  if( !(rqfield === false && isEmpty(val)) ) {
				  var dft = g.p.colModel[valref].datefmt || "Y-m-d";
				  if(!checkDate (dft, val)) return [false,g.p.colNames[valref]+": "+jQuery.jgrid.edit.msg.date+" - "+dft,""];
			  }
		  }
	  }
	  return [true,"",""];
  };
  // Date Validation Javascript
  function checkDate (format, date) {
	  var tsp = {};
	  var result =  false;
	  var sep;
	  format = format.toLowerCase();
	  //we search for /,-,. for the date separator
	  if(format.indexOf("/") != -1) {
		  sep = "/";
	  } else if(format.indexOf("-") != -1) {
		  sep = "-";
	  } else if(format.indexOf(".") != -1) {
		  sep = ".";
	  } else {
		  sep = "/";
	  }
	  format = format.split(sep);
	  date = date.split(sep);
	  if (date.length != 3) return false;
	  var j=-1,yln, dln=-1, mln=-1;
	  for(var i=0;i<format.length;i++){
		  var dv =  isNaN(date[i]) ? 0 : parseInt(date[i],10); 
		  tsp[format[i]] = dv;
		  yln = format[i];
		  if(yln.indexOf("y") != -1) { j=i; }
		  if(yln.indexOf("m") != -1) {mln=i}
		  if(yln.indexOf("d") != -1) {dln=i}
	  }
	  if (format[j] == "y" || format[j] == "yyyy") {
		  yln=4;
	  } else if(format[j] =="yy"){
		  yln = 2;
	  } else {
		  yln = -1;
	  }
	  var daysInMonth = DaysArray(12);
	  var strDate;
	  if (j === -1) {
		  return false;
	  } else {
		  strDate = tsp[format[j]].toString();
		  if(yln == 2 && strDate.length == 1) {yln = 1;}
		  if (strDate.length != yln || tsp[format[j]]==0 ){
			  return false;
		  }
	  }
	  if(mln === -1) {
		  return false;
	  } else {
		  strDate = tsp[format[mln]].toString();
		  if (strDate.length<1 || tsp[format[mln]]<1 || tsp[format[mln]]>12){
			  return false;
		  }
	  }
	  if(dln === -1) {
		  return false;
	  } else {
		  strDate = tsp[format[dln]].toString();
		  if (strDate.length<1 || tsp[format[dln]]<1 || tsp[format[dln]]>31 || (tsp[format[mln]]==2 && tsp[format[dln]]>daysInFebruary(tsp[format[j]])) || tsp[format[dln]] > daysInMonth[tsp[format[mln]]]){
			  return false;
		  }
	  }
	  return true;
  }
  function daysInFebruary (year){
	  // February has 29 days in any year evenly divisible by four,
      // EXCEPT for centurial years which are not also divisible by 400.
      return (((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0))) ? 29 : 28 );
  }
  function DaysArray(n) {
	  for (var i = 1; i <= n; i++) {
		  this[i] = 31;
		  if (i==4 || i==6 || i==9 || i==11) {this[i] = 30;}
		  if (i==2) {this[i] = 29;}
	  } 
	  return this;
  }

  function isEmpty(val)
  {
	  if (val.match(/^s+$/) || val == "")	{
		  return true;
	  } else {
		  return false;
	  } 
  }
  function htmlEncode (value){
      return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
  }
}());

// ***
// jquery.ajah.js
/*
================================================================================
jquery.ajah.js
Provides ajax capability to work with html results.  
Taylored to work with estrada presentation behaviors.

Uses the options available to the jQuery ajax call.
Documentation Link: http://docs.jquery.com/Ajax/jQuery.ajax#options

================================================================================
*/

// ajah without callbacks processing.
plugin('ajah', function(url, params, callback, errorCallback, method, cache) {
  var settings;
  if (url.url) {
    settings = url;
  }
  else {
    settings = {
      url: url,
      params: params,
      callback: callback,
      errorCallback: errorCallback,
      method: method,
      cache: cache
    };
  }
  settings = $.extend({
    followRedirect: false,
    callback: nop,
    errorCallback: nop,
    removeIndicator: false,
    indicator: false,
    indicatorNode: null,
    indicatorMsg: 'Processing...',
    indicatorPos: 'over'
  }, settings);

  var methodExplicit = false;
  // get selector
  var off = settings.url.indexOf(' ');
  var selector;
  if (off >= 0) {
    selector = settings.url.slice(off, settings.url.length);
    settings.url = settings.url.slice(0, off);
  }

  // Default to a GET request
  if (settings.method) {
    methodExplicit = true;
  }
  else {
    settings.method = 'GET';
  }

  // If the second parameter was provided
  if (settings.params) {
    // If it's a function
    if ($.isFunction(settings.params)) {
      // We assume that it's the callback
      settings.errorCallback = settings.callback;
      settings.callback = settings.params;
      settings.params = null;
      // Otherwise, build a param string
    }
    else {
      if (!methodExplicit) settings.method = 'POST';
    }
  }
  var indicatorNode, spinner;
  if (settings.indicator) {
    indicatorNode = settings.indicatorNode ? settings.indicatorNode : $(document.body);
    spinner = indicatorNode.spinner({
      position: settings.indicatorPos,
      hide: false,
      text: settings.indicatorMsg,
      className: (settings.indicatorClass ? settings.indicatorClass : 'processing-big')
    });
  }

  // Request the remote document
  $.ajax({ url: settings.url, type: settings.method, dataType: 'html', data: settings.params,
    cache: settings.cache,
    beforeSend: function(xhr) {
      // this is a special header that lets the Engine know that it's
      // dealing with an XMLHttpRequest object, which always follows the
      // redirects. Thus, in case of a redirect, the Engine will return
      // a text/plain document with the URL as its contents
      (settings.method == 'POST' || settings.followRedirect) && xhr.setRequestHeader("x-puny-xhr", "alas");

      // add this header to force fresh content in IE
      xhr.setRequestHeader("If-Modified-Since", new Date());
    },
    complete: function(res, status) {
      // If successful, inject the HTML into all the matched elements
      if (status == 'success' || status == 'notmodified') {
        if ((settings.method == 'POST' || settings.followRedirect) && res.getResponseHeader('Content-Type')
                      .indexOf('text/plain') == 0) {
          settings.callback.call({}, status, res.responseText, spinner);
          removeSpinner();
          return;
        }
        var html = res.responseText;
        processResult(html, res, settings.callback);
        removeSpinner();
      }
      else {
        var html = res.responseText;
        processResult(html, res, ajahError);
      }
    },
    error: function(res, status) {
      ajahError(res, spinner)
    }
  });

  return this;

  function removeSpinner() {
    settings.removeIndicator && indicatorNode && indicatorNode.spinner().remove();
  }

  function ajahError(res, spin) {
    if (spinner) {
      if (this.html) {
        var errorNode = this.find('div.error');
        if (errorNode.length == 0) {
          errorNode = this;
        }
        spinner.messageHtml(errorNode.html());
      }
      else {
        spinner.messageText(res.statusText);
      }
    }
    settings.errorCallback.call(this, res, spin);
  }

  function processResult(html, rs, callback) {
    // make sure the document has loaded
    $(function() {
      var prefix = guid();
      var startPos = html.indexOf('body>');
      if (startPos >= 0) {
        startPos += 5;
        var endPos = html.indexOf('</body', startPos);
        if (endPos >= 0) {
          html = html.substring(startPos, endPos)
                .replace(/id(\s)*=(\s)*'/g, 'id=\'' + prefix + '-');
          var store = $(document.body).div().css({ position: 'absolute',
            left: '-1972px', width: '10px', height: '10px',
            overflow: 'hidden'
          }).html(html);
          if (selector) {
            store.find(selector).each(function() {
              callback.call($(this), rs, spinner);
            });
          }
          else {
            store.each(function() {
              callback.call(store, rs, spinner);
            });
          }
          store[0] && document.body.removeChild(store[0]);
        }
      }
    });
  }

}, true);

$.ajah2 = $.ajah;
// ***
// jquery.box.js
// calculates total size (including margins, border, and padding) of an element
// returns an object that contains "width" and "height" members

// TODO(dglazkov): add margin/padding information
// TODO(dglazkov): fix the problem with this in IE (does not correctly
//                calculate)
plugin('box', function() {
  var w = this.width();
  var h = this.height();
  var e = this[0];
  
  var style = (document.defaultView
    && document.defaultView.getComputedStyle
    && document.defaultView.getComputedStyle(e, null)) || e.currentStyle
    || e.style;
    
  return {
    width: w + cv(style, 'paddingLeft') + cv(style, 'paddingRight')
      + cv(style, 'marginLeft') + cv(style, 'marginRight')
      + (style.borderLeftStyle == 'none' ?
        0 : cv(style, 'borderLeftWidth'))
      + (style.borderRightStyle == 'none' ?
        0 : cv(style, 'borderRightWidth')),
    height: h + cv(style, 'paddingTop') + cv(style, 'paddingBottom')
      + cv(style, 'marginTop') + cv(style, 'marginBottom')
      + (style.borderTopStyle == 'none' ?
        0 : cv(style, 'borderTopWidth'))
      + (style.borderBottomStyle == 'none' ?
        0 : cv(style, 'borderBottomWidth'))
  };
  

  function cv(s, n) {
    var p = parseInt(s[n].replace(/px$/, ''));
    if (isNaN(p)) {
      // It's either 'auto', or I give up
      return 0;
    }
    return p;
  };
    
});
// ***
// jquery.calendar-grid.js
  
// basic calendar grid, provides rendering of a calendar grid and general
// event handling framework, using "inversion of control" pattern

// uses plugins:
// * table

// TODO(dglazkov): add support for the "year" grid
// TODO(dglazkov): add support for invalid ranges (one at first)
// TODO(dglazkov): assign correct classes to cells (in-range, invalid, today,
// and selected)

// global scope object
var calendar_grid_globals = {
  KEY: 'calendar-grid',
  // global storage for all calendar instances
  //all: {},
  // default settings for the calendarGrid. The supplied settings are always
  // merged into these
  default_settings: {
    // called when the grid loads (initially or as a result of setting
    // changing)
    // * this -- Grid Object Model (GOM), an object that encapsulates
    //   manipulation of the grid
    // * params -- parameters of the grid (either defaults or passed via change
    //   invocation)
    load: nop,
    // called when a cell in the grid is selected
    // * this -- TODO(dglazkov): figure out what it is
    // * date -- a Date object, initialized to the date selected
    // * invalid -- true if the selected date is inside of the invalid range(s),
    //   false otherwise
    select: nop,
    // specifies whether and how to show the week labels. Valid values:
    // * 'abbr' -- show abbreviated labels, S, M, T, etc.
    // * 'full' -- show full labels: Monday, Tuesday, etc.
    // * 'none' or any other value -- do not show labels
    labels: 'none',
    showMonthName: false,
    className: 'calendar-grid'
  },
  // day-of-week labels for the grid
  labels: {
    abbr: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    full: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
           'Saturday']
  },
  firstDayOfWeek: function(date) {
    var dow = date.getDay();
    if (dow) {
      var adjusted = this.zeroDate(date);
      adjusted.setDate(date.getDate() - dow);
      return adjusted;
    }
    return new Date(date.getTime());
  },
  zeroDate: function(date) {
    var adjusted = new Date(date.getTime());
    adjusted.setHours(0);
    adjusted.setMinutes(0);
    adjusted.setSeconds(0);
    adjusted.setMilliseconds(0);
    return adjusted;
  },
  getMonthsSpan: function(start, end) {
	  var sDate, eDate;   
    var d1 = start.getFullYear() * 12 + start.getMonth();
    var d2 = end.getFullYear() * 12 + end.getMonth();
    var sign;
    var months = 0;

    if (start == end) {
        months = 0;
    } 
    else if (d1 == d2) {      //same year and month
        months = Math.ceil((end.getDate() - start.getDate())/this.getLastDayOfMonth(start));
    } 
    else {
        if (d1 <  d2) {
            sDate = start;
            eDate = end;
            sign = 1;
        } else {
            sDate = end;
            eDate = start;
            sign = -1;
        }
        
        var lastday = this.getLastDayOfMonth(sDate);
        var sAdj = lastday - sDate.getDate();
        var eAdj = eDate.getDate();
        var adj = Math.ceil((sAdj + eAdj)/lastday -1);
        
        months = ((d2 - d1) + adj) * sign;
    }
    return months;
  },
  rangeCoversMonth: function(range) {
    if ((range.start.getDate() == 1) && isLastDay(range.start, range.end)) {
			return this.getMonthsSpan(range.start, range.end);
		}
		return 0;
  
    function isLastDay(start, end) {
		var test = new Date(end.getTime());
		test.setDate(test.getDate() + 1);
		var diff = test.getMonth() - end.getMonth();
		return ((diff == 1) || (diff == -11));
	}
  },
  createMonthRange: function(date, range, context) {
    var start = this.zeroDate(date);
    start.setDate(1);
    var end = new Date(start.getTime());
    end.setMonth(end.getMonth() + range);
    end.setDate(end.getDate() - 1);
    context.start = start;
    context.end = this.zeroDate(end);
    return context;
  },
  createWeekRange: function(date, range, context) {
    var start = this.zeroDate(date);
    var day = start.getDay();
    start.setDate(start.getDate() - day);
    var end = new Date(start.getTime());
    end.setDate(end.getDate() + (7*range)-1);
    context.start = start;
    context.end = end;
    return context;
  },
  createDayRange: function(date, range, context) {
    var start = this.zeroDate(date);
    var end = new Date(start.getTime());
    end.setDate(end.getDate() + (range-1));
    end.setHours(23, 59, 59);
    context.start = start;
    context.end = end;
    return context;
  },
  getDaysSpan: function(start, end) {
	  var offset = start.getTimezoneOffset() - end.getTimezoneOffset();
	  return (Math.floor(((end.getTime() - start.getTime()) + (offset*60*1000)) / (1000*60*60*24)));
  },
  getLastDayOfMonth: function(date) {
	var d = new Date(date.getFullYear(), date.getMonth() + 1, 0);
	return d.getDate();
  },
  ATTR: ['id="', 1,
         '" class="', 3, '"'],
  CELL: ['<div class="day"><span>', 1, '</span></div><div class="cell"></div>']
  //CELL: ['<div class="day" onclick="$.calendarGrid(\'', 1, '\').select(this)" ><span>', 3, '</span></div><div class="cell"></div>']
  /*
  ATTR: ['onclick="$.calendarGrid(\'', 1, '\').select(this)" id="', 3,
         '" class="', 5, '"'],
  CELL: ['<div class="day"><span>', 1, '</span></div><div class="cell"></div>']
  */
};

// jQuery module: calendarGrid
// returns CalendarGrid instance
// * id -- string id of the calendar, by which it can be referenced
//   (optional, an internal global id is assigned if not provided)
// * settings -- initial settings of the calendar, optional, see
//   calendar_grid.default_settings for the list of available parameters
// * params -- initial parameters for the grid (optional, DefaultParams is
//   is used if not provided)
plugin('calendarGrid', function(id, settings, params) {
  
  var target = $(this);
  var g = calendar_grid_globals;
  var instance = target.data(g.KEY);
  if (!instance) {
    var complete_settings = $.extend({}, g.default_settings, settings);
    var complete_params = $.extend({}, new DefaultParams(), params);
    
    instance = new CalendarGrid(id, target, complete_settings,
                                        complete_params);
    target.data(g.KEY, instance);
  }
  return instance;

  
  // default parameters for the calendarGrid. Using function/object notation
  // because dates will need to be initialized every time
  function DefaultParams() {
    
    // default range is:
    // * start == first day of the current month,
    // * end == last day of the current month
    g.createMonthRange(new Date(), 1, this);
    
    // default "invalid" ranges array is empty
    this.invalid = [];
  }
  
  // calendar grid instance
  // calling the constructor creates the grid
	function CalendarGrid(id, node, settings, params) {
		// lookup table: from cell id to date
		var dates = {};
		// lookup table: from date ticks to cell id;
		var cells = {};
		// indicates whether the range sliding uses month or explicit range
		// mode: 0 month, 1 days  span: number of days or months
		var rangeincrement = {mode: -1, monthSpan: 0, daySpan: 0};
		var renderMonthName = settings.showMonthName;

		// generate initial calendar grid:
		var table = node.table('class="' + settings.className + '"');
		// first, the labels
		$(g.labels[settings.labels] || ear).each(function() {
			table.cell(this);
		})
		table.row();
		setRangeIncrement(params);
		renderDays(params);
    
		function setRangeIncrement(params) {
		  //params && params.rangeincrement && console.info(params.rangeincrement.mode);
			if (!params.rangeincrement || (params.rangeincrement.mode == -1)) {
				if ((rangeincrement.monthSpan = g.rangeCoversMonth(params)) > 0) {
					rangeincrement.mode = 0;
				}
				else { 
					rangeincrement.mode = 1;
				}
			}
			else {
			  rangeincrement.mode = params.rangeincrement.mode;
			}
		}
		
		// changes the parameters of the grid
		// - new_params -- new parameters. If new_params is a number, moves range
		//   relative to current range (number can be positive or negative)
		this.change = function(new_params, callback) {
			if (new_params != null) {
				if (!isNaN(new_params)) {
					if (new_params != 0) {
						$.extend(params, slide(new_params));
					}
				}
				else {
					$.extend(params, new_params);
					setRangeIncrement(params);
				}
			}
			else {
				rangeincrement.mode = 0;
				g.createMonthRange(g.zeroDate(new Date()), 1, params);
			}
      table.clear(true);
			renderDays(params);
			callback && callback(params);
			// move relative to the current range
			function slide(n) {
				if (rangeincrement.mode == 0) {
					// change by months
					var start = new Date(params.start.getTime());
					start.setMonth(start.getMonth() + (n*rangeincrement.monthSpan));
					return g.createMonthRange(start, rangeincrement.monthSpan, {});
				} 
				// change by explicit range
				var start;
				var end;
				if (n > 0) {
					start = g.zeroDate(params.end);
					start.setDate(start.getDate() + 1);
					end = new Date(start.getTime());
					end.setDate(end.getDate() + (rangeincrement.daySpan*n));
				}
				else {
					end = g.zeroDate(params.start);
					end.setDate(end.getDate() - 1);
					start = new Date(end.getTime());
					start.setDate(start.getDate() + (rangeincrement.daySpan*n));
				}
				return { start: start, end: end };
			}
		}
    
		// removes the grid
		this.remove = function() {
		}
    
		this.select = function(node) {
			settings.select.call({}, dates[$(node).parent().attr('id')]);
		}
		
		this.reset = function() {
		
		}
    
		function renderDays(params) {
			rangeincrement.daySpan =  g.getDaysSpan(params.start, params.end);
			cells = {};
			var cdate = new Date();
			var today = [3];
			today[0] = cdate.getDate();
			today[1] = cdate.getMonth();
			today[2] = cdate.getFullYear();
			var completeRange = {};
			var date = g.firstDayOfWeek(params.start);
			completeRange['start'] = new Date(date.getTime());
			var lastDay = g.getLastDayOfMonth(date);
			var calMonth = 0;
			var startTicks = params.start.getTime();
			var endTicks = params.end.getTime();
			while(date <= params.end) {
				for(var j = 0; j < 7; j++) {
					var d = date.getDate();
					if (d == 1) {
						lastDay = g.getLastDayOfMonth(date);
						calMonth = date.getMonth();
					}
					// set up attributes (table id and unique id for the cell)
					
					
					var cell_id = g.ATTR[1] = guid();
					var ticks = date.getTime();
					dates[cell_id] = new Date(ticks);
					cells[ticks] = cell_id;
					// TODO(dglazkov): set correct class names
					g.ATTR[3] = ((ticks >= startTicks) && (ticks <= endTicks))
						? 'in-range'
						: 'out-range';
					if ((d == today[0]) 
							&& (date.getMonth() == today[1]) 
								&& (date.getFullYear() == today[2])) {
						g.ATTR[3] += ' today';
					}
					// set up cell content
				  g.CELL[1] = (renderMonthName && 
						(
							(d == 1) || 
							((d == lastDay) && (date.getMonth() != calMonth))
						)) 
						? $.dates.format(date, "MMM") + " " + d
						: d;
					if (renderMonthName && (calMonth == 0) && (d == 1)) {
						g.CELL[1] += ", " + $.dates.format(date, "yyyy");
					}
					table.cell(g.CELL.join(''), g.ATTR.join(''));
					date.setDate(d + 1);
				}
				table.row();
			}
			// render initial view of the table
			table.render();
			$('div.day', table.node()).click(function(ev) {
			  instance.select(this);
			});
			completeRange['end'] = new Date(date.setTime(date.getTime()-1000));
			params['completeRange'] = completeRange;
			params['rangeincrement'] = rangeincrement;
			var gom = new GOM(cells);
			if (params.selected) {
			  var cell = gom.cell(params.selected);
			  cell && cell.parent().addClass('current');
			}
			$('.day span', table.node()).hover(
			  function() {
			    $(this).addClass('on');
			  },
			  function() {
			    $(this).removeClass('on');
			  }
			);
			// invoke load handler
			settings.load.call(gom, params);
		}
	}
  
  // Grid Object Model (GOM)
  // exposes usable structure of the calendar grid without revealing too much
  function GOM(cells) {
    // provides access to the inside of a calendar grid cell
    // * date -- a Date() instance
    // returns:
    // * null if the provided date is not inside of the range, currently
    //   displayed by the grid
    // * a jQuery object of the cell. What is returned is not the actual cell,
    //   but rather a container where you can put stuff
    this.cell = function(date) {
      var ticks = g.zeroDate(date).getTime();
      var cell = cells[ticks];
      if (cell) {
        if (typeof(cell) == 'string') {
          // cache result of jQuery traversal prior to returning
          return cells[ticks] = $('#' + cell + ' div.cell:first');
        }
        return cell;
      }
    }
  }
  
  // IMPORTANT: The entire DateMatrix is currently not used  
  // represents the two-dimensional array (matrix) that contains the references
  // to nodes and provides abilities to find a node based on the Date and
  // vise versa
  function DateMatrix() {
    this._matrix = [];
    this.selected = {
      equals: function(year, month, day) {
        if (!month) {
          return year == this._getValue();
        }
        return this.year == year && this.month == month && this.day == day;
      },
      set: function(o, id) {
        this.year = o.year;
        this.month = o.month;
        this.day = o.day;
        this.id = id;
        this._value = null;
      },
      clear: function() {
        this.year = null;
        this.month = null;
        this.id = null;
        this.day = null;
        this._value = null;
      },
      get: function() {
        return this.year ?
          ((this.month + 1) + "/" + this.day + "/" + this.year) : "";
      },
      _getValue: function() {
        if (!this._value) {
          this._value = new Date(this.year, this.month, this.day).valueOf();
        }
        return this._value;
      }
    };
    this.rlookup = {
      reset: function() {
        this._table = {};
      },
      set: function(year, month, day, id) {
        this._table[id] = { year: year, month: month, day: day };
      },
      get: function(id) {
        return this._table[id];
      }
    }
  }
    
  DateMatrix.prototype.months = ["January", "February", "March", "April", "May",
                               "June", "July", "August", "September", "October",
                               "November", "December"];
  
  DateMatrix.prototype.initWeek =  function() {
    this._week && this._matrix.push(this._week);
    this._week = [];
  };
  
  DateMatrix.prototype.initDay = function(node) {
    var id = node.id;
    this._week.push(id);
  };
  
  DateMatrix.prototype.initMonth = function(context) {
    this._matrix.push(this._week);
    this._title = context.month;
  };
  
  DateMatrix.prototype.delta = function(delta) {
    var m = this.month + delta % 12;
    var yd = parseInt(delta / 12);
    if (m > 11) {
      m = 0;
      yd++;
    }
    if (m < 0) {
      m = 11;
      yd--;
    }
    this.populate(this.year + yd, m);
  };
  
  
  DateMatrix.prototype.select = function(node) {
    var day = parseInt(node.innerHTML);
    var id = this.selected.id;
    if (id) {
      var old = document.getElementById(id);
      old.className = old.className.replace(/ e-selected/, "");
    }
    node.className += " e-selected";
    this.selected.set(this.rlookup.get(node.id), node.id);
  };
  
  DateMatrix.prototype.populate = function(year, month) {
    this.rlookup.reset();
    this.year = year;
    this.month = month;
    this._title.innerHTML = this.months[month] + " " + this.year;
    var date = new Date(year, month, 1);
    var now = new Date();
    now =
      (new Date(now.getFullYear(), now.getMonth(), now.getDate())).valueOf();
    var dow = date.getDay();
    var d = dow > 0 ? -dow : -7;
    var week = 0;
    var other;
    var selected;
    var node;
    var value;
    var m;
    for(var week = 0; week < 6; week++) {
      for(dow = 0; dow < 7; dow++) {
        date.setDate(++d);
        d = date.getDate();
        m = date.getMonth();
        value = date.valueOf();
        node = document.getElementById(this._matrix[week][dow]);
        node.innerHTML = d;
        selected = this.selected.equals(value);
        this.rlookup.set(date.getFullYear(), m, date.getDate(), node.id);
        if (selected) this.selected.id = node.id;
        other = m != month;
        node.className = (other ? "e-other-month" : "e-day") +
          (value == now ? "-now" : "") + (selected ? " e-selected" : "")
      }
    }
  }
});

// ***
// jquery.calendar-phrase.js
// provides UI for query form

var calendar_phrase = {
  // default options
  defaults: {
    // called when the phrase is ready to load
    load: nop,
    config: null,
    node: null,
    state: true,
    mode: false,
    range: true,
    reset: true,
    search: true,
    filter: true,
    gridId: 0
  }
};

// TODO(kew): add documentation
plugin('calendarPhrase', function(options) {
  
  var g = calendar_phrase;
  var global = query_form_phrase;
  var queryFormSettings = $.extend({}, g.defaults, options || {});
  
  var defaults = {
		name: "div",
		className: 'phrase',
		phrase: null,
		node: null
	};
	
	var queryPhrase = {
		phrase: null,
		config: queryFormSettings.config,
		reload: nop,
		gridId: queryFormSettings.gridId,
		calendarView: 0,
		currentDate: new Date(),
		initialized: false
	};
	
	queryPhrase.node = queryFormSettings.node;
	queryPhrase.filtering = global.filterManager(queryFormSettings, queryPhrase, {
		node: queryPhrase.node,
		className: 'filter item ',
		postUrlParams: {}
	});
	
	queryPhrase.calState = new stateManager(queryPhrase, {
		node: queryFormSettings.node,
		className: 'head'
	});
	queryPhrase.phrase = queryPhrase.calState.Phrase;

	var tools = queryPhrase.phrase.getPhraseNode().div().addClass('tools');
	
	if (queryFormSettings.mode) {
		queryPhrase.mode = new modeManager(queryPhrase, {
			phrase: queryPhrase.phrase,
			name: 'div',
			className: 'mode'
		});
		queryPhrase.state.mode = queryPhrase.mode.state;
	}
	
	if (queryFormSettings.range) {
		if (queryPhrase.config.range) {
			queryPhrase.calRange = new rangeManager(queryPhrase, {
				phrase: queryPhrase.phrase,
				name: 'div',
				className: 'navigation'
			});
			queryPhrase.calState.range = queryPhrase.calRange.state;
			queryPhrase.calState.increment = queryPhrase.calRange.increment.state;
		}
	}
	
	if (queryFormSettings.reset) {
		queryPhrase.reset = new resetManager(queryPhrase, {
			phrase: queryPhrase.phrase,
			name: 'div'
		});
	}
	
	if (queryFormSettings.search) {
		if (queryPhrase.config.search) {
			queryPhrase.search = new searchManager(queryPhrase, {
				phrase: queryPhrase.phrase,
				name: 'span'
			});
		}
	}
	
	queryPhrase.gridParamsReady = function() {
	  queryFormSettings.node.removeClass('month-view').removeClass('week-view')
	    .removeClass('day-view').removeClass('custom-view');
	  switch (queryPhrase.calendarView) {
	    case 0:
	      queryFormSettings.node.addClass('month-view');
	      break;
	    case 1:
	      queryFormSettings.node.addClass('week-view');
	      break;
	    case 2:
	      queryFormSettings.node.addClass('day-view');
	      break;
	    default:
	      queryFormSettings.node.addClass('custom-view');
	      break;
	  }

	  queryPhrase.filters && queryPhrase.filters.render();
	  if (queryPhrase.filters || queryPhrase.search) {
	    queryPhrase.phrase.node($.div().addClass('clear-float'));
	  }
	  queryPhrase.calRange && queryPhrase.calRange.render();
    queryPhrase.mode && queryPhrase.mode.render();
    queryPhrase.phrase.node($.div().addClass('clear-float'));
    if (!queryPhrase.initialized) {
      queryPhrase.initialized = true;
	    
	  }
	}
	
	return queryPhrase;
			
	function stateManager(view, options) {
		var me = this;
		options = $.extend({}, defaults, options);
		var holder = options.phrase ? options.phrase : options.node;
		var state;
		this.range = null;
		this.mode = null;
		this.increment = null;
		this.Phrase;
		var node;
		
		var phrase = holder.phrase({
			name: options.name,
			className: options.className,
			load: function() {
				me.Phrase = this;
				node = me.Phrase.getPhraseNode();
				this.phrase({
					load: function() {
						this.text('');
						state = this;
					}
				});
			}
		});
		
		this.render = function() {
		  state.clear();
		  if (view.calendarView == 0) return;
			state.text('Show ' + view.config.handler.title);
			if (me.range) {
				state.node(me.range);
			}
			if (me.increment) {
				state.node(me.increment);
			}
			if (me.mode) {
				state.text(' ');
				state.node(me.mode);
			}
			state.period();
		}
	}
	
	function resetManager(view, options) {
		var me = this;
		var node;
		var state;
		this.Phrase;
		
		options = $.extend({}, defaults, options);
		var holder = options.phrase ? options.phrase : options.node;	
		var phrase = holder.phrase({
			name: options.name,
			className: options.className,
			load: function() {
				me.Phrase = this;
				node = me.Phrase.getPhraseNode();
				this.phrase({
					load: function() {
						this.text('');
						state = this;
					}
				});
			}
	  });
	  
	  this.render = function() {
	    me.Phrase.clear();
			state.text('Reset range to this');
			state.link('month', '#today', 'navigation', function() {
				view.gridChange();
				return false;
			});
			var area = state.phrase({
				name: 'span',
				className: 'area-change-increment',
				load: function() {
					state.phrase({
						name: 'span',
						load: function() {
							this.text(', ');
							this.node(view.calRange.increment.titleMonthsChange);
						}
					});
					state.text(" or ");
					state.node(view.calRange.increment.titleChange).semicolon();
				}
			});
		}
	}
	
	function modeManager(view, options) {
	  var me = this;
	  var rendered = false;
		var node;
		this.current = null;
		this.range = null
		this.titleChange = null;
		this.state = $('<span class="state-mode" />');
		this.Phrase;
		var month, week, day;
		var listgridPhrase;
		var changeListGrid;
		var currentListGrid;
		
		options = $.extend({}, defaults, options);
		var holder = options.phrase ? options.phrase : options.node;	
		var phrase = holder.phrase({
			name: options.name,
			className: options.className,
			load: function() {
				me.Phrase = this;
				node = me.Phrase.getPhraseNode;
		    this.phrase({
				  name: 'span', 
				  className: 'view-list-grid',
				  load: function() {
						this.text('');
						listgridPhrase = this;
					}
				});
				this.phrase({
				  name: 'span', 
				  className: 'view-span month current',
				  load: function() {
						this.text('');
						month = this;
					}
				});
				this.phrase({
				  name: 'span', 
				  className: 'view-span week',
				  load: function() {
						this.text('');
						week = this;
					}
				});
				this.phrase({
				  name: 'span', 
				  className: 'view-span day',
				  load: function() {
						this.text('');
						day = this;
					}
				});
			}
	  });
	  
    this.render = function() {
      if (!rendered) {
        rendered = true;
        var cur = view.config.mode || 'calendar';
		    listgridPhrase.button('View As ' + ((cur == 'list') ? 'Calendar' : 'List'), 'change-listgrid', 
          function(done, postParams) {
            var newMode = (view.config.mode == 'list') ? 'calendar' : 'list';
            if (view.isDirty) {
              view.gridChange(view.currentDate, newMode);
            }
            else {
              $.superGrid(queryFormSettings.gridId).changemode({mode: newMode});
            }
            if (newMode == 'calendar') {
              view.state.removeRange();
            }
            $('.change-listgrid .text', listgridPhrase.getPhraseNode()).text('View As ' + ((newMode == 'list') ? 'Calendar' : 'List'));
            //done && done();
          }, false);
		    month.link('Month', '#month', 'view-month', function() {
		      view.calendarView = 0;
		      view.gridChange(view.currentDate);
		      viewHandler($(this).parent());
			    
		    });
		    week.link('Week', '#week', 'view-week', function() {
		      view.calendarView = 1;
		      view.gridChange(view.currentDate);
		      viewHandler($(this).parent());
		    });
		    day.link('Day', '#day', 'view-day', function() {
		      view.calendarView = 2;
		      view.gridChange(view.currentDate, 'list');
		      viewHandler($(this).parent());
		    });
	    }
		}
		  
	  function viewHandler(newCurrent) {
	    $('.current', me.Phrase.getPhraseNode()).removeClass('current');
	    newCurrent.addClass('current');
	  }
	  
	  this.dayView = function(date) {
	    view.currentDate = date;
	    $('a', day.getPhraseNode()).click();
	  }
	  		
		this.update = function(mode) {
			view.config.mode = mode;
			$('.change-listgrid .text', listgridPhrase.getPhraseNode()).text('View As ' + ((mode == 'list') ? 'Calendar' : 'List'));
		}
		
	}
	
	function searchManager(view, options) {
	  var me = this;
		var node;
		var state;
		this.Phrase;
		
		options = $.extend({}, defaults, options);
		var holder = options.phrase ? options.phrase : options.node;	
		var phrase = holder.phrase({
			name: options.name,
			className: options.className,
			load: function() {
				me.Phrase = this;
				node = me.Phrase.getPhraseNode();
			}
	  });
	  			
		this.render = function() {
			me.Phrase.link('Search ' + view.config.handler.title + '.', '#search', 'search');
			$(".search", me.Phrase).searchPicker(
				{
					title: view.config.search.label,
					config: view.config,
					handler: view.config.handler,
					range: view.config.range,
					getParams: function() {
						return {config: view.config};
					}
				});
		}
	}
	
	function rangeManager(view, options) {
	  var me = this;
	  var rendered = false;
		var node;
		this.current = {};
		this.picker = null;
		this.state = $('<span class="state-range" />');
		this.monthSpot = null;
		this.Phrase;
		var rangeArea = $('<span class="range item"></span>').eHover();
		this.rangeLink = $('<a href="#range" />').click(function() {
		  $(this).rangePicker('rangepicker', 
				{
					type: 'date',
					mode: 'dialog',
					stayCurrent: true,
					clickPick: true,
					refresh: function() {
					  return view.config.range.start.value.get();
					},
					select: function(date) {
						view.gridChange(date);
					}
				});
			return false;
		});
		rangeArea.append(me.rangeLink);
		
		options = $.extend({}, defaults, options);
		var holder = options.phrase ? options.phrase : options.node;	
		var phrase = holder.phrase({
			name: options.name,
			className: options.className,
			load: function() {
				me.Phrase = this;
				node = me.Phrase.getPhraseNode();
			}
	  });
	  
	  this.render = function() {
	    //me.Phrase.clear();
			if (!rendered) {
			  rendered = true;
			  me.Phrase.link('', '#previous', 'previous', function() {
				  view.gridChange(-1);
			  });
			  me.Phrase.link('', '#next', 'next', function() {
				  view.gridChange(1);
			  });
			  me.Phrase.node(rangeArea);
			}
			else {
			  me.increment.updateTitle();
			}
		}
		
		this.increment = {
			toggle: function(multiples) {
				var r = {rangeincrement: {}};
				for (var key in me.current.rangeincrement) {
					r.rangeincrement[key] = me.current.rangeincrement[key];
				}
				for (var key in me.current) {
					if (!me.current[key].mode) {
						r[key] = me.current[key];
					}
				}
				r.rangeincrement.mode = 
					(r.rangeincrement.mode == 0) 
						? (multiples 
							? (r.rangeincrement.monthSpan > 1 
								? 1
								: -1)
							: (r.rangeincrement.monthSpan > 1 
								? -1
								: 1))
						: -1;
				if (r.rangeincrement.mode == -1) {
					r = calendar_grid_globals.createMonthRange(
						r.start, 
						(multiples ? r.rangeincrement.monthSpan : 1), 
						r)
				}
				view.gridChange(r);
			},
			state: $('<span class="state-increment" />'),
			titleChange: $('<a href="#increments" class="change-increment">Day</a>'),
			titleMonthsChange: $('<a href="#increments1" class="m-change-increment">Multiples</a>'),
			updateTitle: function() {
			  if ((view.calendarView == 0) && 
			      (me.current.rangeincrement.monthSpan == 1)) {
			    me.rangeLink.text($.dates.format(me.current.start, 'MMM yyyy'));
			  }
			  else {
			    me.rangeLink.text($.dates.rangeFormatter(me.current.start, me.current.end, true));
			  }
			}
		}
		
		me.increment.titleChange.click(function() {
			me.increment.toggle(false);
		});
		me.increment.titleMonthsChange.click(function() {
			me.increment.toggle(true);
		});
		
		function getTitle(start, end) {
			var title = "";
			if (start && !start.getYear) {
				start = new Date(Date.parse(start));
			}
			if (end && !end.getYear) {
				end = new Date(Date.parse(end));
			}
			if (!start && !end) {
				title = " across all dates ";
			}
			else if (!start) {
				title = " before " + 
					$.dates.format(end, "M/d/yyyy");
			}
			else if (!end) {
				title = " after " + 
					$.dates.format(start, "M/d/yyyy");
			}
			else {
				title = " between " + 
					$.dates.format(start, "M/d/yyyy") +
					" and " +
					$.dates.format(end, "M/d/yyyy");
			}
			return title;
		}
		this.update = function(rangeincrement, start, end, complete_start, complete_end) {
			if (((me.current.start != start) || (me.current.end != end)) 
						|| ((view.mode == 'calendar') && !view.config.range.extended )){
				view.config.loadRequired = true;
			}
			me.current.start = start;
			me.current.end = end;
			me.current.rangeincrement = rangeincrement;
			view.config.range.set(start, end, complete_start, complete_end);
			me.increment.updateTitle();
			if (view.config.mode == 'list') {
			  view.filtering.refreshRange();
			}
		  !view.currentDate && (view.currentDate = start);
		}
	}
});

// ***
// jquery.category-picker.js
// using sheet, provides a dialog box for picking categories

/*
settings:
	select(itemArray) - callback when 'ok' button is selected
		itemArray - array of currently selected category values
	inheritUp - true | false
		specifies that if selected, all ancestor categories are also selected.
	inheritDown - true | false
		specifies that if selected, all descendant categories are also selected.

*/
		

var category_picker = {
	CATEGORYPICKER_KEY: 'category_picker',
	defaults: {
		title: 'Select Categories',
		select: nop,
		inheritUp: true,
		inheritDown: false,
		categories: null,
		close: nop,
		more: false
	}
};

plugin('categoryPicker', function(settings) {
	var g = category_picker;
	var srcNode = this;
	settings = $.extend({}, g.defaults, settings);
	var picker = $(this).data(g.CATEGORYPICKER_KEY);
	if (!picker) {
		picker = new categorySheet(); 
		$(this).data(g.CATEGORYPICKER_KEY, picker);
	}
	else {
		picker.open();
	}
	return picker;
	
	function categorySheet() { 
	  var me = this;
	  var areas = [];
	  var results;
		var picker;
		
		function initialize(slot) {
		  results = new categoryResults(settings.inheritUp, settings.inheritDown);
		  picker = new categoryPicker(results);
		  if (settings.categories) {
			  $.each(settings.categories, function() {
				  areas.push(picker.parseSelect(this));
			  });
		  }
		  else {
			  $(srcNode).each(function() {
				  var nodeName = $(this).get(0).nodeName.toLowerCase();
				  switch (nodeName) {
					  case "select":
						  areas.push(picker.parseSelect(this));
						  $(this).remove();
						  break;
					  default:
						  break;
				  }
			  });
		  }
	  }
		

		this.open = function() {
			$(srcNode).sheet({
			  volatile: false,
			  width: '535px',
			  title: settings.title,
			  load: function(done, manager) {
			    var slot = this;
			    window.setTimeout(function() {
            initialize(slot);
            slot.phrase({
				      load: function() {
					      this.button('CANCEL', function() {
						      manager.close();
						      settings.close();
					      });
					      this.button('OK', 'ok', function(done) {
						      manager.enabled(false);
						      manager.close();
						      settings.select(results.getSelected());
						      return true;
					      });
				      }
			      });
			      categorySheetHandler(slot, areas);
				    done && done();
          }, 0);
				  return true;
			  },
			  close: function() {
				  settings.close();
			  }
		  });
		}
		
		this.clear = function() {
		  results.clear();
		  settings.select(results.getSelected());
		}
		
		me.open();

		
		function categorySheetHandler(workArea, areas) {
		  
			$(workArea).addClass("category-picker");
			if (picker.count > 100) {
				$(workArea).parent().addClass("large");
			}
			else if (picker.count > 50) {
				$(workArea).parent().addClass("medium");
			}
			else {
				$(workArea).parent().addClass("small");
			}
			$(workArea).cols(
				{
					one: { className: "pickerarea" },
					two: { className: "resultsarea" }
				});

			var outer = $('.pickerarea', workArea).div().addClass('outer-scroll');
			picker.area = outer.div().addClass('scrollarea');
			outer = $('.resultsarea', workArea).div().addClass('outer-scroll');
			results.area = outer.div().addClass('scrollarea');
			$(areas).each(function() {
				picker.area.append(this);
			});
			picker.area.treeview({
				collapsed: true,
				animated: "medium",
				persist: "location"
			});
			results.init();
		}
	}
	
	function categoryPicker(results) {
		var me = this;
		this.area = null;
		this.count = 0;
	
		this.parseSelect = function(select) {
			var nbsp = String.fromCharCode(160);
			var holder = $('<div class="category-segment" />');
			holder.div().addClass("title").text(select.title);
			var curr = holder;
			var prevLevel = 0;
			var inputName = $(select).attr("name");
			$("option", select).each(function() {
				var item = getItem($(this));
				if (item) {
					item.value = inputName + '.' + item.value;
					var cnt = item.level - prevLevel;
					if (cnt != 0) {
						if (cnt > 0) {
							for (;cnt > 0; cnt--) {
								curr = curr.dom("ul");
							}
						}
						else if (cnt < 0){
							while (curr && (cnt < 0)) {
								if (curr.attr("nodeName").toLowerCase() == "ul") {
									++cnt;
								}
								curr = curr.parent();
							}
						}
						prevLevel = item.level;
					}
					var id = guid();
					curr = curr.attr("nodeName").toLowerCase() == "li" 
						? curr.parent().dom("li").addClass("item-checkbox")
						: curr.dom("li").addClass("item-checkbox");
					curr.dom("a").attr("name", id);
					var checkbox = curr.dom("div").attr("id", id).addClass("checkbox").click(function() {
						results.change($(this));
					});
					if (item.inherited) {
						curr.dom("span").addClass('inherited-flag');
						curr.addClass('inherited');
						checkbox.addClass('inherited');
						results.inherited.push(id);
					}
					curr.dom("span").addClass(item.value).text(item.text);
					if (item.selected) {
						results.defaultSelected.push(id);
					}
					
					++me.count
				}
			});

			return holder;
			
			function getItem(node) {
				var text = node.text();
				var value = node.attr('value');
				if (text == '-- select --') return null;
				var selected = node.attr('selected') ? node.attr('selected') : false;
				var inherited = node.hasClass('inherited');
				var e = text.split(nbsp);
				return {level: e.length, 
					text: e[e.length-1], 
					value: value, 
					selected: selected,
					inherited: inherited
				};
			}
		}
	
	}
	
	function categoryResults(inheritUp, inheritDown) {
		var me = this;
		var results = {};
		var regx = new RegExp('checked-disabled');
		this.area;
		var renderArea;
		this.defaultSelected = [];
		this.inherited = [];
		
		this.init = function() {
			me.area.div().addClass("title").text("Current Selection");
			renderArea = me.area.div();
			jQuery.each(me.defaultSelected, function() {
				me.change($('#' + this));
			});
		}
		
		this.change = function(jNode) {
			if (jNode.hasClass('checked-disabled') || jNode.hasClass('inherited')) {
				return;
			}
			if (!jNode.attr("id")) {
				jNode.attr("id", guid());
			}
			var id = jNode.attr("id");
			if (!results[id]) {
				add(jNode, true, id);
			}
			else {
				remove(jNode, true, id);
			}
			render();
		}
		
		this.getSelected = function() {
			var selected = {};
			var item;
			for (var key in results) {
				item = results[key];
				var v = item.value.split('.');
				var name = v[0];
				var value = v.slice(1).join('');
				var items = selected[name];
				if (!items) {
					items = [];
					selected[name] = items;
				}
				if (settings.more) {
				  items.push({text: item.text, value: value});
				}
				else {
				  items.push(value);
				}
			}
			return selected;
		}
		
		this.clear = function() {
		  for (var key in results) {
		    item = results[key];
		    $(item.checkbox).click();
		  }
		}
		
		function render() {
			var item;
			var items = [];
			for (var key in results) {
				item = results[key];
				items.push([item.text, item.checkbox]);
			}
			var idx = 0;
			renderArea.empty();
			if (items.length > 0) {
				items.sort();
				$(items).each(function(idx) {
					item = this;
					var a = renderArea.div().addClass('item-selected');
					var id = "remove-" + $(item[idx,1]).attr("id");
					a.dom("dom").attr("id", id).attr("title", "Remove Category")
						.addClass("remove")
						.click(function() {
							var id = $(this).attr("id").replace("remove-","");
							$((results[id]).checkbox).click();
						})
						.dom('b');
					a.dom("span").addClass("title")
						.dom("a").attr("href", "#" + $(item[idx,1]).attr("id"))
						.attr("title", "Go to Category")
						.text(this[idx, 0])
						.click(function() {
							var id = $(this).attr("href").replace("#", "");
							$(results[id].checkbox).parents().filter(".expandable")
								.find(">.hitarea")
								.each(function() {
									$(this).click();
								});

						});
				});
			}
		}
		
		function add(node, updateTree, id) {
			if (!id) {
				id = node.attr("id");
			}
			var parent = node.parent();
			var item = $('span:first', parent);
			if ((inheritUp || inheritDown) && updateTree) removeCheckedAncestor(parent);
			results[id] = {checkbox: node, text: item.text(), value: item.attr('className')};
			node.removeClass("checked-disabled");
			node.addClass("checkbox-checked");
			parent.addClass("checked");
			if (inheritUp && updateTree) {
				setAncestors(parent, true);
				setDescendants(parent, false);
			}
			if (inheritDown && updateTree) setDescendants(parent, true);
		}
		
		function remove(node, updateTree, id) {
			if (!id) {
				id = node.attr("id");
			}
			delete results[id];
			node.removeClass("checkbox-checked");
			var parent = node.parent();
			parent.removeClass("checked");
			if (inheritUp && updateTree) setAncestors(parent, false);
			if (inheritDown && updateTree) setDescendants(parent, false);
		}
		
		function isChecked(node) {
			return node.hasClass("checkbox-checked") || node.hasClass("checked");
		}
		
		function setDescendants(node, checked) {
		
			descend(node);
			function descend(el) {
				$("li", el).each(function() {
					var checkbox = $(".checkbox:first", this);
					if (checkbox) {
						if (isChecked(checkbox)) {
							remove(checkbox, false);
						}
						if (checked) {
							checkbox.addClass("checked-disabled");
						}
						else {
							checkbox.removeClass("checked-disabled");
						}
					}
					descend($(this));
				});
			}
		}
		
		function setAncestors(node, checked) {
			node.parents("li.item-checkbox").each(function() {
				var name = $(this).attr("nodeName").toLowerCase();
				var checkbox = $(">.checkbox", this);
				if (checkbox) {
					if (isChecked(checkbox)) {
						remove(checkbox, false);
					}
					checkboxNode = checkbox.get(0);
					if (checked) {
						checkboxNode.className += " checked-disabled";
					}
					else {
						checkboxNode.className = checkboxNode.className.replace(regx, "");
					}
				} 
			});
		}
		
		function removeCheckedAncestor(node) {
			node.parents("li").each(function() {
				if (isChecked($(this))) {
					var checkbox = $(".checkbox:first", this);
					if (checkbox) {
						remove(checkbox, true);
						return true;
					}
				}
			});
		}
	}
});
// ***
// jquery.content-query.js
// General content query processor

var global_content_query = {
	DATA_KEY: 'content_query_data',
	setQuery: function (query, name, value) {
		if (!query || query.length == 0) {
			query = "?";
		}
		else {
			query += "&";
		}
		//query += escape(name) + "=" + escape(value);
		query += encodeURIComponent(name) + "=" + encodeURIComponent(value);
		return query;
	}
};


// The main method has two modes:
// * registration mode, which is used to register a custom handler
// * processing mode, which is used to process a content query (or potential
//   content query)
// The modes are chosen transparently by using different call signatures.
//
// Registration usage:
// * key:String -- a string key that identifies a custom handler
// * handler:CustomhandlerObject -- an object that has all the methods and
//   properties, necessary to create a custom handler
// Progessing usage -- call with no parameters on the node that is to be
// processed
plugin('contentQuery', function(layoutHelper, complete) {
  var g = global_content_query;


  // processing mode
  // for each query inside, process content query
  // $('.content-query:has(div.ui-handler>span.key)').each(function() {
  $('div.content-query:has(>div.ui-handler>span.key),div.content-query:has(>form>div.ui-handler>span.key)').each(function() {
    var cq = $(this);
    var cqForm = $('>form', cq);
    var config = new queryConfig(cq, cqForm);
    config.layoutHelper = layoutHelper;
    if (config.handler) {
      if (config.handler.itemLoader) {
        if (config.handler.itemLoader(config, cq)) {
          // remove query markup from DOM, because we don't need it anymore
          cq.empty();
        }
      }
      else if (config.handler.clearQuery) {
        cq.empty();
      }
      config.handler.cqRender(config);
      /*
      // exit and set to continue after layout is finished
      // this will (perceptually) speed up loading of the page
      window.setTimeout(function() {
        config.handler.cqRender(config);
      }, 0);
      */
    }
  });
  complete();

  function queryKey(node) {
    var key;
    var data;
    var uiParams;
    var paramsRequired = false;
    if (node && node.length > 0) {
      var keyNode = $("span.key", node);
      key = keyNode.text();
      data = node.find('.ui-data');
      if (data.length == 0) data = null;

      var uiParamNode = node.find('.ui-params');
      uiParams = (uiParamNode.length > 0) ? eval("(" + uiParamNode.text() + ")") : {};

      var parent = keyNode.parent();
      if (parent.hasClass('ui-handler')) {
        paramsRequired = parent.hasClass('params-required');
        parent.remove();
      }
      else {
        keyNode.remove();
      }
    }
    // allow override vi query string parameter
    var qKey = $.authoring().query.handler;
    if (qKey) {
      key = qKey;
    }
    var handler = (key && (key.length > 0)) ? $.uiHandler(key) : global_ui_handler.handler;

    return { key: key, handler: handler, dataNode: data, uiParams: uiParams, paramsRequired: paramsRequired };
  }

  function queryConfig(queryNode, formNode) {
    var me = this;
    var querySpecial = "";
    this.baseUrl = window.location.pathname;
    this.mode = "";
    this.loadRequired = true;
    this.loadDisabled = false;
    this.hasData = false;
    this.queryJNode = queryNode;
    this.head = "";

    var k = (formNode.length > 0) ? queryKey(formNode) : queryKey(queryNode);
    if (!k.handler) {
      return;
    }
    this.key = k.key;
    this.handler = k.handler;
    this.dataNode = k.dataNode;
    this.UiParams = k.uiParams;
    this.paramsRequired = k.paramsRequired;
    
    formNode.remove();

    var allDateRange = {
      start: null,
      end: null
    };

    urlQuery = new queryString();
    querySpecial = urlQuery.setQuery('draft-preview', querySpecial);

    this.items = null;
    this.itemMap = null;

    // range
    this.range = {
      isEmpty: true,
      start: null,
      end: null,
      extended: null,
      set: function(start, end, extended_start, extended_end) {
        this.start.value.set(start);
        this.end.value.set(end);
        if (extended_start && extended_end) {
          this.extended = {
            start: new dataValue(this.start),
            end: new dataValue(this.end)
          }
          this.extended.start.set(extended_start)
          this.extended.end.set(extended_end)
        }
        else {
          this.extended = null;
        }
      },
      init: function() {
        this.start = new configItem(null, null);
        this.start.label = "Start";
        this.end = new configItem(null, null);
        this.end.label = "End";
      }
    };

    //categories
    this.categories = {
      list: null,
      set: function(name, value) {
        me.categories.list[name].value.set(value);
      },
      toQuery: function(q) {
        for (var key in me.categories.list) {
          var item = me.categories.list[key];
          q = item.value.toQuery(q);
        }
        return q;
      },
      isEmpty: function() {
        for (var key in me.categories.list) {
          var item = me.categories.list[key];
          if (!item.isEmpty) {
            return false;
          }
        }
        return true;
      },
      isSelectedEmpty: function() {
        for (var key in me.categories.list) {
          var item = me.categories.list[key];
          if (!item.value.isEmpty) {
            return false;
          }
        }
        return true;
      },
      clear: function() {
        for (var key in me.categories.list) {
          var item = me.categories.list[key];
          item.value.set(null);
        }
      },
      getSelected: function() {
        selected = [];
        var list = me.categories.list;
        for (var key in list) {
          var item = list[key];
          if (!item.value.isEmpty) {
            var select = list[key].input;
            $.each(item.value.get(), function() {
              var title = $('option[value=' + this + ']', select).text();
              selected.push(title);
            });
          }
        }
        return selected;
      }
    };

    if (formNode) {
      // range
      me.range.start = new configItem($('.range input.from', formNode));
      if (!me.range.start.label) me.range.start.label = "Start";
      me.range.end = new configItem($('.range input.to', formNode));
      if (!me.range.end.label) me.range.end.label = "End";
      // descendants
      var descendantsContainer = $('fieldset.descendants', formNode);
      if (descendantsContainer.length > 0) {
        this.descendants = new configItem($('input', descendantsContainer), true);
        this.descendants.containerNode = descendantsContainer;
      }
      // search
      this.search = new configItem($('input.search', formNode), true);
      //this.search = new configItem($('.content-query input.search'), true);
      if (!this.search.label) this.search.label = "Search";
      // categories
      var categoryItems = $('select.filter', formNode);
      if (categoryItems.length > 0) {
        me.categories.list = {};
      }
      categoryItems.each(function() {
        var select = $(this);
        if (select.attr('title') == '') {
          select.attr('title', select.parent().find('label').text());
        }
        var item = new configItem(select, true);
        me.categories.list[item.name] = item;
      });
    }
    if (me.categories.list) {
      me.categories.selects = [];
      for (var key in me.categories.list) {
        me.categories.selects.push(me.categories.list[key].input);
      }
    }
    if ((me.range.start && !me.range.start.isEmpty) && (me.range.end && !me.range.end.isEmpty)) {
      me.range.isEmpty = false;

      allDateRange.start = new dataValue(me.range.start);
      var d = new Date();
      d.setFullYear(d.getFullYear() - 100);
      allDateRange.start.set(d);

      allDateRange.end = new dataValue(me.range.end);
      var d = new Date();
      d.setFullYear(d.getFullYear() + 100);
      allDateRange.end.set(d);
    }

    // Url used for posting
    this.postUrl = function(params) {
      var q = "";
      if (params && params.query) {
        $.each(params.query, function(key, value) {
          q = g.setQuery(q, key, value);
        });
      }
      if (!me.range.isEmpty) {
        if ((!params || params.range || params.all) && me.range) {
          if (me.range.start) {
            q = me.range.extended
						  ? me.range.extended.start.toQuery(q)
						  : me.range.start.value.toQuery(q);
          }
          if (me.range.end) {
            q = me.range.extended
						  ? me.range.extended.end.toQuery(q)
						  : me.range.end.value.toQuery(q);
          }
        }
        else {
          q = allDateRange.start.toQuery(q);
          q = allDateRange.end.toQuery(q);
        }
      }
      if ((!params || params.search || params.all) && me.search.value) {
        q = me.search.value.toQuery(q);
      }
      if (me.categories.list) {
        q = me.categories.toQuery(q);
      }
      if (me.descendants) {
        q = me.descendants.value.toQuery(q);
      }
      q += ((q.length > 0) ? '&' : '?') + querySpecial;
      return me.baseUrl + q;
    };

    // Generic object for handling inputs.
    function configItem(items, useInput) {
      var i = this;
      this.isEmpty = false;
      this.valueIsEmpty = true;
      if (!items || (items.length == 0)) {
        i.isEmpty = true;
        return;
      }

      this.name = null;
      var otherNames;
      var inputNode;
      if (items) {
        var currentName;
        items.each(function(idx) {
          if (idx == 0) {
            inputNode = this;
            i.name = inputNode.name;
          }
          else {
            if (this.name != i.name) {
              if (!otherNames) otherNames = [];
              otherNames.push(this.name);
            }
          }
        });
      }

      //this.name = null;
      this.type = null;
      this.label = null;
      this.input = null;
      this.dataType = 'string';
      this.moreNames = otherNames;
      this.containerNode = null;

      if (inputNode) {
        //i.name = inputNode.name;
        i.type = inputNode.type;
        if ($(inputNode).hasClass('datetime')) {
          i.dataType = 'datetime';
        }
      }
      this.value = new dataValue(this);

      switch (i.type) {
        case "text":
        case "textarea":
          if (i.dataType == 'datetime') {
            i.value.set($.dates.parse(inputNode.value));
          }
          else {
            i.value.set(inputNode.value);
          }
          break;
        case "select-one":
        case "select-multiple":
          $.each(inputNode.options, function() {
            if (this.selected) {
              i.value.add(this.value);
            }
          });
          break;
      }
      if (useInput) {
        this.input = inputNode;
      }

      var labelNode = $(inputNode).prev("label").get(0);
      if (labelNode) {
        i.label = $(labelNode).text();
      }
    }

    function dataValue(cfgItem) {
      var me = this;
      var val;
      var dataType = cfgItem.dataType;
      this.isEmpty = true;
      this.get = function() {
        return val;
      }
      this.set = function(v) {
        val = v;
        me.isEmpty = (val && val.length > 0) ? false : true;
      }
      this.add = function(v) {
        if (!val || !val.push) {
          val = [];
        }
        val.push(v);
        me.isEmpty = false;
      }
      this.toString = function(format) {
        var retval = "";
        if (!format) format = "";
        if (val) {
          if (val.constructor === Array) {
            var cnt = 0;
            $(val).each(function() {
              ++cnt;
              if (cnt > 1) query += ";";
              query += str(this, format);
            });
          }
          else {
            retval = str(val, format);
          }
        }

        return retval;

        function str(v, format) {
          if (v.getDate) {
            if (!format) format = "M/d/yyyy";
            return $.dates.format(v, format);
          }
          else {
            return v.toString();
          }
        }
      }

      this.toQuery = function(query) {
        if (cfgItem && val) {
          var v = '';
          if (val.constructor === Array) {
            $(val).each(function(idx) {
              v += ((idx == 0) ? '' : ',') + str(this);
            });
          }
          else {
            v = str(val);
          }
          if (v.length > 0) {
            query = g.setQuery(query, cfgItem.name, v);
            if (cfgItem.moreNames) {
              $.each(cfgItem.moreNames, function() {
                query = g.setQuery(query, this, v);
              });
            }
          }
        }

        return query;

        function str(v) {
          if (v.getDate) {
            return $.dates.querydate(v); //$.dates.iso8601date(v);
          }
          else {
            return v.toString();
          };
        }
      };
    }
  }

  function queryString() {
    var me = this;
    if (window.location.search) {
      var list = window.location.search.split("?")[1].split("&");
      for (var i = 0; i < list.length; i++) {
        var pair = list[i].split("=");
        this[pair[0].toLowerCase()] = pair[1];
      }
    }

    this.setQuery = function(key, query) {
      if (me[key]) {
        query += ((query && query.length > 0) ? '&' : '')
					+ key + '=' + me[key];
      }
      return query;
    }
  }
});

// ***
// jquery.cqData.js
plugin('cqData', function(dataNode, dataType, fieldName, format) {
  var selector = '.' + fieldName;
  return $.cqData2(dataNode, dataType, selector, format);
});

plugin('cqData2', function(dataNode, datatype, selector, format) {
  return $.cqData3(dataNode, datatype, selector, format, false);
});

plugin('cqData3', function(dataNode, dataType, selector, format, remove) {
  var fieldNode = dataNode.find(selector + ':first');
  if (remove) {
    fieldNode.remove();
  }
  if (!dataType || (dataType.length == 0)) dataType = 'singlelinetext';
  switch (dataType) {
    case 'url':
      return (fieldNode.length > 0) ? fieldNode : '';
      break;
    case 'singlelinetext':
      if (fieldNode.length == 0) return '';
      if (fieldNode.attr('nodeName').toLowerCase() == 'a') {
        return fieldNode.outerHtml();
      }
      else {
        return fieldNode.text();
      }
      break;
    case 'multilinetext':
      return (fieldNode.length > 0) ? fieldNode.html() : '';
      break;
    case 'datetime':
      if (!format) {
       format = "yyyy-MM-dd";
      }
      if (fieldNode.length == 0) return '';
      var value = fieldNode.find('abbr:first');
      return (value.length > 0) ? $.dates.format($.dates.parse(value.text()), format) : '';
      break;
    case 'image':
      return (fieldNode.length > 0) ? fieldNode.outerHtml() : '';
      break;
    case 'boolean':
      return (fieldNode.length > 0)
        ? ((fieldNode.text().toLowerCase() == 'true') ? true : false)
        : false;
      break;
    case 'number':
      if (fieldNode.length > 0) {
        var val = fieldNode.text();
        try {
          return parseInt(val);
        }
        catch (e) {
          return null;
        }
      }
      return null;
      break;
  }
});



// ***
// jquery.dataHelper.js
var data_globals = function() {
  var o = {
    convert: function(stringData, dataType, defaultValue) {
      switch (dataType) {
        case 'datetime':
          return (stringData && stringData.length > 0) ? $.dates.format($.dates.parse(stringData), format) : (defaultValue ? defaultValue : '');
          break;
        case 'boolean':
          return (stringData && stringData.length > 0)
            ? ((stringData.toLowerCase() == 'true') ? true : false)
            : (defaultValue ? defaultValue : false);
          break;
        case 'int':
          if (stringData && stringData.length > 0) {
            try {
              return parseInt(stringData);
            }
            catch (e) {
            }
          }
          return (defaultValue ? defaultValue : null);
          break;
      }
    },
    convertToBoolean: function(stringData, defaultValue) {
      return o.convert(stringData, 'boolean', defaultValue);
    },
    convertToInt: function(stringData, defaultValue) {
      return o.convert(stringData, 'int', defaultValue);
    }
  };
  return o;
}();
$.dataHelper = data_globals;



// ***
// jquery.date-picker.js
// provides an inline date/time picker

// TODO(dglazkov): implement date picker
// TODO(dglazkov): implement time picker (later)
// TODO(dglazkov): add documentation
plugin('datePicker', function() {
  // TODO(dglazkov): add functionality
});
// ***
// jquery.dates.js
// provides parsing of dates.  Takes a string as input and attempts
// to parse out a date time value.  Returns a date object.

// TODO(nchampion): comment for documentation
//					add aditional datePatterns
//					add two digit year support
//					order date patterns according to hit probability

var dates_global = {
  MilliSecsDays: 1000*60*60*24,
	MilliSecsHours: 1000*60*60,
	MilliSecsSeconds: 1000*60
};

plugin('dates', function() {
	var monthNames = [
		'January',
		'February',
		'March',
		'April',
		'May',
		'June',
		'July',
		'August',
		'September',
		'October',
		'November',
		'December'
	];
	var dayNames = [
		'Sunday',
		'Monday',
		'Tuesday',
		'Wednesday',
		'Thursday',
		'Friday',
		'Saturday'
	];
	var datePatterns = [
		// +++ Required date indicator, create default date
		{	regex: /^\+\+\+$/,
			handler: function() {
       return new Date();
			}
		},
		// yyyy-mm-ddThh:mm:ss
		{	regex: /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/,
			handler: function(strDate, matched) {
			  var d = new Date();
			  d.setDate(1);  // Set to first for now -- prevents javascript date issues
			  d.setMonth(parseInt(matched[2], 10) - 1);
			  d.setFullYear(matched[1]);
			  d.setDate(parseInt(matched[3], 10));
        d.setHours(parseInt(matched[4], 10));
				d.setMinutes(parseInt(matched[5], 10));
				d.setSeconds(parseInt(matched[6], 10));
				return d;
			}
		},
		// yyyy-mm-dd or yyyy/mm/dd
		// 2008-04-12 or 2007/11/30
		{	regex: /(\d{4})[\/-](\d{1,2})[\/-](\d{1,2})/,
			handler: function(strDate, matched) {
				var d = zeroDate(new Date());
				d.setDate(1);
				d.setMonth(parseInt(matched[2], 10) - 1);
				d.setFullYear(matched[1]);
				d.setDate(parseInt(matched[3], 10));
				return checktime(d,strDate);
			}
		},
		// mm/dd/yyyy or mm-dd-yyyy or mm/dd/yy or mm-dd-yy
		// 10/23/2008 or 3-5-1999 or 1/21/08
		{	regex: /(\d{1,2})[\/|-](\d{1,2})[\/|-](\d{2,4})/,
			handler: function(strDate, matched) {
				var d = zeroDate(new Date());
				var y = parseInt(matched[3],10);
				if (y <= 50) { y = "" + (y + 2000); }
				else if (y <=99) { y = "" + (y + 1900);	}
				else if (y < 999) y=""+(y-0+1900);
				d.setDate(1);
				d.setMonth(parseInt(matched[1], 10) - 1);
				d.setFullYear(y);
				d.setDate(parseInt(matched[2], 10));
				return checktime(d,strDate);
			}
		},
		// Mon dd[st,h,rd,nd] hh:mm:ss UTC yyyy (standard unix date command format)
		// Mon Jan  6 15:44:21 PST 1997
		{	regex: /(\w+) +(\d{1,2}) +(\d{1,2}):(\d{1,2}):(\d{1,2}) +(?:\w+) +(\d{4})/i,
			handler: function(strDate, matched) {
				var m, d = new Date();
				if (matched.length == 7) {
					if (matched[6]) {
						d.setFullYear(matched[6]);
					}
				}
				if ((m=getMonth(matched[1])) >= 0) {
				  d.setDate(1);
					d.setMonth(m);
					d.setDate(parseInt(matched[2], 10));
					d.setHours(parseInt(matched[3], 10));
					d.setMinutes(parseInt(matched[4], 10));
					d.setSeconds(parseInt(matched[5], 10));
				} else {
					d = null;
				}
				return d;
			}
		},
		// Month dd[st,h,rd,nd], yyyy or Mon dd[st,h,rd,nd], yyyy
		// September 11, 2001 or Jan 4th, 2009
		{	regex: /(\w+) (\d{1,2})(?:st|nd|rd|th)?,? ?(\d{4})?/i,
			handler: function(strDate, matched) {
				var m, d = new Date();
				d.setDate(1);
				if (matched.length == 4) {
					if (matched[3]) {
						d.setFullYear(matched[3]);
					}
				}
				if ((m=getMonth(matched[1])) >= 0) {
				  d.setDate(parseInt(matched[2], 10));
					d.setMonth(m);
				} else {
					d = null;
				}
				if (d) return checktime(d,strDate);
			}
		},
		// yyyyMMdd.hhmmss
		// 20080412.153421
		{	regex: /(\d{4})(\d{1,2})(\d{1,2})(?:.|T)(\d{1,2})(\d{1,2})(?:\.?)(\d{1,2})/,
			handler: function(strDate, matched) {
				var d = new Date();
				d.setDate(1);
				d.setMonth(parseInt(matched[2], 10) - 1);
				d.setFullYear(matched[1]);
				d.setDate(parseInt(matched[3], 10));
				d.setHours(parseInt(matched[4], 10));
				d.setMinutes(parseInt(matched[5], 10));
				d.setSeconds(parseInt(matched[6], 10));
				return d;
			}
		},
		// dd[st,h,rd,nd]
		// 27th
		{	regex: /^(\d{1,2})(st|nd|rd|th)?$/i, 
			handler: function(strDate, matched) {
				var d = new Date();
				d.setDate(parseInt(matched[1], 10));
				return checktime(d,strDate);
			}
		},
		// Today or Now
		{	regex: /^tod|^now/i,
			handler: function(strDate, matched) { 
				var d = new Date();
				return checktime(d,strDate);
			} 
		},
		// Tomorrow
		{	regex: /^tom/i,
			handler: function(strDate, matched) {
				var d = new Date(); 
				d.setDate(d.getDate() + 1); 
				return checktime(d,strDate);
			}
		},
		// Yesterday
		{	regex: /^yes/i,
			handler: function(strDate, matched) {
				var d = new Date();
				d.setDate(d.getDate() - 1);
				return checktime(d,strDate);
			}
		}
	];      
	function getMonth(monthName) {
		var intCount = monthNames.length;
		var matches, rv = -1;
		for (var idx = 0; idx < intCount; idx++) {
			matches = new RegExp("^" + monthName, "i").test(monthNames[idx]);
			if (matches) {
				rv = idx;
				idx=intCount;
			}
		}
		return rv;
	};
	function zeroDate(date) {
    var adjusted = new Date(date.getTime());
    adjusted.setHours(0);
    adjusted.setMinutes(0);
    adjusted.setSeconds(0);
    adjusted.setMilliseconds(0);
    return adjusted;
  };
	function checktime(dateObj, strDate) {
		var timeRE = /(\d{1,2}):(\d{1,2}):?(\d{1,2})? ?(am|pm)?/i;
		var matched = timeRE.exec(strDate);
		if (matched && matched.length > 1 && matched[1]) {
			var intHours = parseInt(matched[1]);
			if (matched.length > 4 && matched[4]) {
				if (matched[4].toLowerCase().indexOf("am")==0) {
					if (intHours == 12) {
						intHours = 0;
					} else if (intHours > 12) {
						return (null);
					}
				} else if (intHours < 12 && matched[4].toLowerCase().indexOf("pm")==0) {
					intHours += 12;
				}
			}
			dateObj.setHours(intHours);
			dateObj.setMinutes(0);
			dateObj.setSeconds(0);
			if (matched.length > 2 && matched[2]) {
				dateObj.setMinutes(matched[2]);
				dateObj.setSeconds(0);
				if (matched.length > 3 && matched[3]) {
					dateObj.setSeconds(matched[3]);
				}
			}
		}
		return dateObj;
	};
	function checkDate(oDate) {
		if (!oDate) {
			oDate = new Date();
		}
		return(oDate);
	};
	////////////////////////////////////////////////////////////////////////////
	// This function uses the same 'format' strings as the 
	// java.text.SimpleDateFormat class, with minor exceptions.
	// The format string consists of the following abbreviations:
	// 
	// Field        | Full Form          | Short Form
	// -------------+--------------------+-----------------------
	// Year         | yyyy (4 digits)    | yy (2 digits), y (2 or 4 digits)
	// Month        | MMM (name)         | MM (2 digits), M (1 or 2 digits)
	//              | NNN (abbr.)        |
	// Day of Month | dd (2 digits)      | d (1 or 2 digits)
	// Day of Week  | EE (name)          | E (abbr)
	// Hour (1-12)  | hh (2 digits)      | h (1 or 2 digits)
	// Hour (0-23)  | HH (2 digits)      | H (1 or 2 digits)
	// Hour (0-11)  | KK (2 digits)      | K (1 or 2 digits)
	// Hour (1-24)  | kk (2 digits)      | k (1 or 2 digits)
	// Minute       | mm (2 digits)      | m (1 or 2 digits)
	// Second       | ss (2 digits)      | s (1 or 2 digits)
	// AM/PM        | A  (AM or PM)      | a ( a or p)
	//
	// NOTE THE DIFFERENCE BETWEEN MM and mm! Month=MM, not mm!
	// Examples:
	//  "MMM d, y" matches: January 01, 2000
	//                      Dec 1, 1900
	//                      Nov 20, 00
	//  "M/d/yy"   matches: 01/20/00
	//                      9/2/00
	//  "MMM dd, yyyy hh:mm:ssa" matches: "January 01, 2000 12:30:45AM"
	// ------------------------------------------------------------------
	// formatDate (date_object, format)
	// Returns a date in the output format specified.
	/////////////////////////////////////////////////////////////////////////////
	function formatDate(date,format) {
		function LZ(x) {return(x<0||x>9?"":"0")+x}
		format=format+"";
		var result="";
		var i_format=0;
		var c="";
		var token="";
		var y=date.getYear();
		var M=date.getMonth()+1;
		var d=date.getDate();
		var E=date.getDay();
		var H=date.getHours();
		var m=date.getMinutes();
		var s=date.getSeconds();
	
		var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
		// Convert real date parts into formatted versions
		var value=new Object();
		if (y <= 50) { y = "" + (y + 2000); }
		else if (y <=99) { y = "" + (y + 1900);	}
		else if (y < 999) y=""+(y-0+1900);
		else y=""+y;
		value["y"]=""+y;
		value["yyyy"]=y;
		value["yy"]=y.substring(2,4);
		value["M"]=M;
		value["MM"]=LZ(M);
		value["MMM"]=monthNames[M-1];
		value["NNN"]=monthNames[M+11];
		value["d"]=d;
		value["dd"]=LZ(d);
		value["E"]=dayNames[E].substring(0,3);
		value["EE"]=dayNames[E];
		value["H"]=H;
		value["HH"]=LZ(H);
		if (H==0){value["h"]=12;}
		else if (H>12){value["h"]=H-12;}
		else {value["h"]=H;}
		value["hh"]=LZ(value["h"]);
		if (H>11) {value["K"]=H-12;} else {value["K"]=H;}
		value["k"]=H+1;
		value["KK"]=LZ(value["K"]);
		value["kk"]=LZ(value["k"]);
		if (H > 11) { value["A"]="PM"; value["a"]= "p"; }
		else { value["A"]="AM"; value["a"] = "a"; }
		value["m"]=m;
		value["mm"]=LZ(m);
		value["s"]=s;
		value["ss"]=LZ(s);
		while (i_format < format.length) {
			c=format.charAt(i_format);
			token="";
			while ((format.charAt(i_format)==c) && (i_format < format.length)) {
				token += format.charAt(i_format++);
			}
			if (value[token] != null) {	result=result + value[token]; }
			else { result=result + token; }
		}
		return result;
	};
	var datesHandler = {
		parse: function(strDate) {
			var intCount = datePatterns.length;
			var regex, handler, matched;
			for (var idx = 0; idx < intCount; idx++) {
				matched = null;
				regex = datePatterns[idx].regex;
				handler = datePatterns[idx].handler;
				matched = regex.exec(strDate);
				if (matched) {
					return handler(strDate,matched);
				}
			}
			return null;
		},
    relative: function(date, to) {
      to || (to = new Date());
      var diff = (to - date) / 60000;
      if (diff < 1) {
        return "a few seconds ago";
      }
      else if (diff < 2) {
        return "about a minute ago";
      }
      else if (diff < 60) {
        return parseInt(diff, 10) + " minutes ago";
      }
      else {
        diff = diff / 60;
        if (diff < 2) {
          return "an hour ago";
        }
        else if (diff < 24) {
          return parseInt(diff, 10) + " hours ago";
        }
        else {
          diff = diff / 24;
          if (diff < 2) {
            return "a day ago";
          }
          else if (diff < 30) {
            return parseInt(diff, 10) + " days ago";
          }
          else {
            diff = diff / 30;
            if (diff < 2) {
              return "about a month ago";
            }
            else if (diff < 12) {
              return parseInt(diff, 10) + " months ago";
            }
            else {
              diff = diff / 12;
              if (diff < 2) {
                return "about a year ago";
              }
              return parseInt(diff, 10) + " years ago";
            }
          }
        }
      }
      return s;        
    },
		longdate: function(oDate) {
			return formatDate(checkDate(oDate), "EE, MMM dd, yyyy hh:mm:ss A");
		},
		shortdate: function(oDate) {
			return formatDate(checkDate(oDate), "M/d/yyyy");
		},
		iso8601date: function(oDate) {
			return formatDate(checkDate(oDate), "yyyy-MM-ddTHH:mm:ss");
		},
		querydate: function(oDate) {
			return formatDate(checkDate(oDate), "M/d/yyyy hh:mm:ss A");
		},
		format: function(oDate, strFormat) {
			return formatDate(checkDate(oDate), strFormat);
		},
		rangeFormatter: function(from, to, dateonly) {
	    var gcal = calendar_grid_globals;
	    var datestr;
	    if (from && to) {
			  if (from.getTime() == to.getTime()) {
				  datestr = $.dates.format(from, "EE, MMM dd, yyyy");
				  if (!dateonly) {
				    datestr += " @ "
				      + $.dates.format(from, "h:mm A");
				  }
			  }
			  else {
				  var f = gcal.zeroDate(from);
				  var t = gcal.zeroDate(to);
				  if (f.getTime() == t.getTime()) {
					  datestr = $.dates.format(f, "EE, MMM dd, yyyy");
					  if (!dateonly) {
						  datestr += " @ "
						    + $.dates.format(from, "h:mm A")
						    + " - "
						    + $.dates.format(to, "h:mm A");
						}
				  }
				  else {
				    if (dateonly) {
				      datestr = $.dates.format(from, "MMM dd, yyyy")
						    + " - "
						    + $.dates.format(to, "MMM dd, yyyy");
				    }
				    else {
					    datestr = $.dates.format(from, "EE, MMM dd, yyyy h:mm A")
						    + " - "
						    + $.dates.format(to, "EE, MMM dd, yyyy h:mm A");
						}
				  }
			  }
		  }
		  return datestr;
	  }
	};
	return datesHandler;
}(), true);

// ***
// jquery.dom.js
// Simple DOM manipulation

// creates an element by specified name, appends it to current node and returns
// new element's jQuery object
plugin('dom', function(name) {
  var result;
  if (this.size && this.size() > 0) {
    this.each(function() {
      var el = this.appendChild(document.createElement(name));
      (result && $(result).add(el)) || (result = $(el));
    });
  }
  else {
    result = $(document.createElement(name));
  }
  return result;
});


// creates a div, appends it to current node and returns div's jQuery object
plugin('div', function() {
  if (this.size && this.size() > 0) {
    return this.dom('div');
  }
  else {
    return $.dom('div');
  }
});

plugin('domBefore', function(name) {
	var result;
	this.each(function() {
		var el = this.parentNode.insertBefore(document.createElement(name), this);
		(result && $(result).add(el)) || (result = $(el));
	});
	return result;
});

plugin('domAfter', function(name) {
	var result;
	this.each(function() {
		var el = this.parentNode.insertBefore(document.createElement(name), this.nextSibling);
		(result && $(result).add(el)) || (result = $(el));
	});
	return result;
});

// adding form element requires that attributes be set before it is added
// to dom.  
plugin('form', function(name, attrs, label) {
	var result;
	var el;
	if (label && (label.position == 'before') && this[0]) {
		$(this).dom('span').addClass('label').html(label.text);
	}
	try {
		var elstr = '<' + name;
		for (var attr in attrs) {
			elstr += ' ' + attr + '="' + attrs[attr] + '"';
		}
		el = document.createElement(elstr);
	}
	catch(e) {
		el = document.createElement(name);
		for (var attr in attrs) {
			$(el).attr(attr, attrs[attr]);
		}
	}
	if (this[0]) {
		this.each(function() {
			this.appendChild(el);
			(result && $(result).add(el)) || (result = $(el));
		});
	}
	else {
		result = $(el);
	}
	if (label && (label.position == 'after') && this[0]) {
		$(this).dom('span').addClass('label').html(label.text);
	}
	return result;
});

plugin('formfield', function(name, attrs, label, value) {
  if (typeof label == 'string') {
    label = {position: 'before', text: label};
  }
	var result = $.div().addClass('field');
	var el;
	if (label && (label.position == 'before')) {
		result.dom('span').addClass('label').html(label.text);
	}
	try {
		var elstr = '<' + name;
		for (var attr in attrs) {
			elstr += ' ' + attr + '="' + attrs[attr] + '"';
		}
		el = document.createElement(elstr);
	}
	catch(e) {
		el = document.createElement(name);
		for (var attr in attrs) {
			$(el).attr(attr, attrs[attr]);
		}
	}
	value && $(el).val(value);
	result.append(el);
	if (label && (label.position == 'after')) {
		result.dom('span').addClass('label').html(label.text);
	}
	if (this[0]) {
	  $(this).append(result);
	}
	return result;
});


// ***
// jquery.duration-picker.js
/*
duration picker plugin
Settings:
load - function to call 
*/

var duration_picker_globals = {
	duration_default_settings: {
		change: nop,
		validation: nop,
		select: nop,
		refresh: nop,
		label: 'Select Duration',
		orientation: 'vertical',
		mode: 'dialog' // dialog | dialog2 | inline
	},
  daySliderDefaults: {
      length: 30,
      interval: 1,
      defaultValue: 0,
      id: 'daySlider',
      label: 'day'
  },
	hourSliderDefaults: {
      length: 24,
      interval: 1,
      defaultValue: 0,
      id: 'hourSlider',
      label: 'hour'
  },
  minuteSliderDefaults: {
      length: 60,
      interval: 5,
      defaultValue: 0,
      id: 'minSlider',
      label: 'min'
  },
  parse: function(value) {
		if (value.length == 0) {
			return {days: 0, hours: 0, minutes: 0}
		}
		var match = value.match(/^\s*(\d+).(\d+)\s*(\:\s*(\d+))?\s*$/i);
		if (match) {
		  var days = parseInt(match[1]);
			var hours = parseInt(match[2]);
			var minutes = match[4] ? parseInt(match[4]) : 0;
			if (!isNaN(hours) && hours < 25) {
				if (!isNaN(minutes)) {
					if (minutes < 60) {
						return { days: days, hours: hours, minutes: minutes }
					}
				}
			}
		}
		return null;
	}
}
 
plugin("durationPicker", function(id, settings) {
	var dp = duration_picker_globals;
	var el_id = guid(id);
	settings = $.extend({}, dp.duration_default_settings, settings)
	return new Picker(this, settings);
	
	function Picker(jnode, settings) {
		var me = this;
		var picker;
		var id = guid();
		switch (settings.mode) {
			case 'dialog':
			case 'dialog2':
				jnode.sheet({
					width: '250px',
					title: settings.title,
					load: function(done, manager) {
						var holder = this.div().addClass('duration-picker-holder');
					  picker = new durationPicker(holder.div().addClass('duration-picker'), settings);
						holder.phrase({
							load: function() {
								this.button('CANCEL', function() {
									manager.close();
								});
								this.button('OK', 'ok', function(done) {
									manager.enabled(false);
									if (picker.validate()) {
										manager.close();
										settings.select(picker.data());
									}
									if (done) done();
								}, true);
							}
						});
					},
          close: function() {
            settings.close && settings.close();
          },
          volatile: true,
					open: function() {
						if (settings.refresh) {
							var data = settings.refresh();
							if (data) {
								picker.refresh(data);
							}
						}
					}
				});
				break;
			default:
			  picker = new durationPicker(jnode, settings);
        break;
		}
	}

	/*
	******************************************************************************
	* Duration Picker
	******************************************************************************
	*/

	function durationPicker(holder, options) {
		var me = this;
		var input = null;
		var specific = null;
		var timeSliderArea;
		var current;
		
		if (options.value) {
		  if ('days' in options.value) {
		    current = options.value;
		  }
		  else {
		    current = dp.parse(options.value);
		  }
		}
		else {
		  current = db.parse('');
		}
    var timeArea = holder.dom('span').addClass('duration-area');
    
		var input = timeArea.form("input", 
			{
				type: "text", 
				name: "specificTime", 
				className: "duration"
			}, null);
		input.attr('readonly', 'true');
		setInput(current, true);
	  duration_picker_globals.daySliderDefaults.update = function(ev, ui) {
	    current.days = parseInt(ui.value);
	    setInput(current, true);
	  }
	  duration_picker_globals.hourSliderDefaults.update = function(ev, ui) {
	    current.hours = parseInt(ui.value);
	    setInput(current, true);
	  }
	  duration_picker_globals.minuteSliderDefaults.update = function(ev, ui) {
	    current.minutes = parseInt(ui.value);
	    setInput(current, true);
	  }

		timeSliderArea = timeSlider(timeArea, input,
	        [
	          duration_picker_globals.daySliderDefaults,
	          duration_picker_globals.hourSliderDefaults,
	          duration_picker_globals.minuteSliderDefaults
          ]);

		timeArea.hover(
		  function() {
		    $('.daySlider', timeSliderArea).slider('option', 'value', current.days);
		    $('.hourSlider', timeSliderArea).slider('option', 'value', current.hours);
		    $('.minSlider', timeSliderArea).slider('option', 'value', current.minutes);
		    timeSliderArea.show();
		    position(timeSliderArea, input, options.position);
      },
      function() {
        timeSliderArea.hide();
      }
    );
    
	  function timeSlider(area, input, data) {
	    var SLIDER_KEY = 'duration-slider';
	    var slider = area.data(SLIDER_KEY);
	    if (!slider) {
        slider = $.div().addClass('sliderArea duration-slider');
        $.each(data, function(i, item) {
          var s = $('<div class="sliderItem"><div class="label">' + item.label + '</div> <p class="' + item.id + '"></p></div>');
          createSlider(s.find('p'), item); 
          slider.append(s);
        });
        area.data(SLIDER_KEY, slider);
        area.append(slider);
        slider.hide();
      }
      return slider;
    }
    
    function position(node, relativeToNode, mode) {
      var pos = relativeToNode.position();
      switch(mode) {
        case 'left':
          break;
        case 'right':
          var left = pos.left +  relativeToNode.outerWidth();
          var top = pos.top;
          break;
        case 'bottom':
        default:
          var left = pos.left;
          var top = pos.top + relativeToNode.outerHeight();
          break;
      }
      if ($.browser.msie){
        top += 2;
      }
      node.css({left: left, top: top});
    }

    function createSlider(holder, item) {
      holder.slider({
          orientation: duration_picker_globals.duration_default_settings.orientation,
          range: "min",
          min: 0,
          max: item.length - item.interval,
          value: item.defaultValue,
          step: item.interval,
          animate: true,
          slide: item.update       
      });
    }
		
		function setInput(values, update) {
			if (options.update && update) {
				options.update(values);
			}
			input.val(gets(values.days, values.hours, values.minutes));
		}

		function validateTime(updateInput) {
			var result = dp.parse(input.val());
			if (updateInput) {
				if (result) {
					setInput(result, true);
				}
				else {
					setInput();
				}
			}
			return true;
		}
		
		
		function gets(days, hours, minutes) {
			var value = "";
			if (days > 0) {
			  if (days == 1) {
			    value += days + ' day';
			  }
			  else {
			    value += days + ' days';
			  }
			}
			value += ' ' + ((hours <= 9) ? '0' : '') + hours + ':' + ((minutes <= 9) ? '0' : '') + minutes;
			return value;
		}
		
		/*
		function parse(value) {
			if (value.length == 0) {
				return {days: 0, hours: 0, minutes: 0}
			}
			var match = value.match(/^\s*(\d+).(\d+)\s*(\:\s*(\d+))?\s*$/i);
			if (match) {
			  var days = parseInt(match[1]);
				var hours = parseInt(match[2]);
				var minutes = match[4] ? parseInt(match[4]) : 0;
				if (!isNaN(hours) && hours < 25) {
					if (!isNaN(minutes)) {
						if (minutes < 60) {
							return { days: days, hours: hours, minutes: minutes }
						}
					}
				}
			}
			return null;
		}
		*/
	}
});

// ***
// jquery.eaccordion.js


plugin('eaccordion', function(options) {
   
  var settings = $.extend({
      header: 'h3', 
      autoHeight: false, 
      clearStyle: true,
      icons: { 'header': 'ui-icon-triangle-1-e', 'headerSelected': 'ui-icon-triangle-1-s' }
    }, options || {});
  
  var accord = $(this).accordion(settings);
  if (settings.rollover) {
    accord.find(settings.rollover).addClass('rollover-item').hover(
      function(ev) {
        $(this).addClass('l-on');
      },
      function(ev) {
        $(this).removeClass('l-on');
      });
  }
  return accord;
});
// ***
// jquery.error-console.js

var global_error_console = {
	ERROR_CONSOLE_KEY: 'error_console',
	defaults: {
		title: "Errors",
		width: 300,
		height: 300
	}
};

plugin('errorConsole', function(settings) {
	var g = global_error_console;
	var trigger = $(this);
	var err = this.data(g.ERROR_CONSOLE_KEY);
	if (!err) {
		settings = $.extend({}, g.defaults, settings);
		err = new ErrorConsole(trigger.get(0));
		this.data(g.ERROR_CONSOLE_KEY, err);
	}
	return err;
	
	function ErrorConsole(srcNode) {
	
	  var me = this;
		var errors = 0;
		var errconsole;
		var sheet;
		var holder;
		
		if (settings.sheet) {
	    sheet = createSheet();
	  }
	  else {
	    holder = $(srcNode);
	  }
	  
	
		this.Add = function(errormsg) {
			++errors;
			if (!errconsole) {
				errconsole = holder.div().addClass("error-console").dom("ul");
			}
			errconsole.dom("li").html(errormsg);
		}
		
		this.hasErrors = function() {
			return (errors > 0) ? true : false;
		}
		
		this.clear = function() {
			if (errconsole) {
				$(errconsole.parent()).remove();
				errconsole = null;
			}
			errors = 0;
		}
		
		if (settings.msg) {
	    me.Add(settings.msg);
	  }
		
		function createSheet() {
		  $(srcNode).sheet({
        width: settings.width,
        height: settings.height,
        title: settings.title,
        load: function(sdone, manager) {
          holder = this.div().addClass('errors');
          this.div().addClass('submit').phrase({
            load: function() {
              this.button('OK', function() {
                manager.close();
              });
            }
          });
          return false;
        },
        close: function() {
          done();
        },
        open: function() {
        
        }
      });
		}
	}
});

// ***
// jquery.gateway.js
/*
================================================================================
jquery.gateway.js
Converts gateway links to iframes

================================================================================
*/

plugin('gateway', function() {
  $('#httpGateway,.gateway').each(function() {
		var gw = $(this);
		var url;
		if (gw.hasClass('gateway')) {
		  url = gw.text();
		}
		else {
		  url = $('a:first', gw).eq(0).attr('href');
		}
		if (url && url.length > 0) {
		  gw.html('<iframe src="' + url + '" style="border:none;width:100%;height:500px"></iframe>');
		}
	});
}, true);

// ***
// jquery.image-picker.js

var global_image_picker = {
  animate: 100,
  DATA_KEY: 'image-picker',
  IMAGE_PICKER_STORAGE_KEY: 'image-picker-size',
  IMAGE_INFO_KEY: 'image-info',
  defaults: {
    // default width of the dialog box
    width: 300,
    // estimated height of the dialog box
    height: 300,
    type: 'image'
  }
};

plugin('imagepicker', function(options) {
  var g = global_image_picker;
  
  options = $.extend({}, g.defaults, options || {});
  var gA = $.authoring();
  if (!gA) {
    // authoring is not enabled
    return null;
  }
  var trigger = $(this);
  var picker = trigger.data(g.DATA_KEY);
	if (!picker) {
		picker = new imagepicker(trigger); 
		trigger.data(g.DATA_KEY, picker);
	}
	picker.open();
	return picker;
	
	function imagepicker(pTrigger) {

    var COLL_TEMPLATE = [ '<div class="collection"><div id="coll-', 1,
                          '"><a href="#coll-toggle" class="title">', 3, '</a>' +
                          '<div class="content"' +
                          ' id="con-', 5,
                          '" style="display:none;"></div>' +
                          '<div style="clear:both"></div></div></div>' ];
                          
    var PIC_TEMPLATE = [ '<div class="pic"><a id="pic-', 1, '" href="#preview"' +
                         ' onclick="hilite(this);return false;"' +
                         ' ondblclick="select(this);return false;"' +
                         ' longdesc="', 3, '" style="background-image:url(\'', 5,
                         '\')"><span class="frame"><span class="title">', 7,
                         '</span></span></a></div>' ];
                         
    
    
    // preview pane
    var preview = $('#preview');
    // all collections, loaded asynchronously on load
    var colls = [];
    var pic_count = 0;
    // currently selected picture
    var current_pic = 0;
    var sheetMgr;
    
    // currently selected size (thumbnail|small|medium|large|original)
    var current_size = $.storage.user(g.IMAGE_PICKER_STORAGE_KEY) || 'medium';
    // currently selected view (grid|preview)
    var current_view = 'grid';
   
    var views = {
      grid: {
        node: $('#view-grid'),
        load: function() {
          // get current pic
          // open its collection
          // select it
        }
      },
      preview: {
        node: $('#view-preview'),
        load: function() {
          var pic = $('#pic-' + current_pic);
          // unselect current pic in grid
          // don't use toggle, because there could be not a selected pic
          pic.removeClass('selected');
          // determine collection
          renderPreview(pic.data(g.IMAGE_INFO_KEY));
        }
      }
    };

    var size_names = [ 'thumbnail', 'small', 'medium', 'large' ];
    var sizes = {
      thumbnail: {
        node: $('#size-thumbnail'),
        handler: function() {}
      },
      small: {
        node: $('#size-small'),
        handler: function() {}
      },
      medium: {
        node: $('#size-medium'),
        handler: function() {}
      },
      large: {
        node: $('#size-large'),
        handler: function() {}
      },
      original: {
        node: $('#size-original'),
        handler: function() {}
      }
    };
    
    var walks = {
      next: function(info) {
        info = info.next;
        if (!info) {
          return;
        }
        current_pic = info.id;
        renderPreview(info);
      },
      previous: function(info) {
        info = info.previous;
        if (!info) {
          return;
        }
        current_pic = info.id;
        renderPreview(info);
      }
    }
    
    // view switcher
    window.view = function(v) {
      if (current_view == v) {
        return;
      }
      
      views[current_view].node.toggleClass('selected');
      $('#' + current_view).toggle();
      current_view = v;
      var view = views[current_view];
      view.load();
      view.node.toggleClass('selected');
      $('#' + current_view).toggle();
    }
    
    // size switcher
    sizes[current_size].node.toggleClass('selected');
    window.size = function(s) {
      if (current_size == s) {
        return;
      }
      
      sizes[current_size].node.toggleClass('selected');
      current_size = s;
      views[current_view].load();
      $.storage.user(g.IMAGE_PICKER_STORAGE_KEY, s);
      sizes[current_size].node.toggleClass('selected');    
    }
    
    // pic-walker
    window.walk = function(where) {
      walks[where](getInfo());
    }
    
    // collection collapser/expander
    //window.toggle = function(n) {
    function toggle(n) {
      var coll = colls[n];
      if (!coll) return;
      
      coll.content.toggle(g.animation);
      coll.hatch.toggleClass('open');
      if (coll.loaded) return;
      
      // load all pics in collection
      // for simplicity, loading == loaded
      coll.loaded = true;
      var previous;
      $.ajah({
        url: coll.url + ' ol li ul',
        params: null,
        callback: function(status, location) {
          var li = $(this);
          var info = {
            id: pic_count,
            previous: previous,
            coll: coll
          };
          if (previous) {
            previous.next = info;
          }
          li.find('a').each(function() {
            info[this.className] = {
              href: this.href,
              title: this.innerHTML,
              url: this.title
            }
          });
          PIC_TEMPLATE[1] = pic_count;
          PIC_TEMPLATE[3] = info.details.href;
          PIC_TEMPLATE[5] = info.image.href + '?version=2';
          PIC_TEMPLATE[7] = info.image.title;
          coll.content.append(PIC_TEMPLATE.join(''));
          // add pic information
          $('#pic-' + pic_count).data(g.IMAGE_INFO_KEY, info);
          pic_count++;
          previous = info;
        },
        errorCallback: function(status, location) {
          
        },
        indicator: true,
        indicatorNode: coll.content,
        removeIndicator: true
      });
    }
    
    // picture selector (when in grid view)
    window.hilite = function(a) {
      var id = a.id.replace(/pic-/, '');
      $(a).toggleClass('selected');
      if (id == current_pic) {
        current_pic = 0;
        return;
      }
      $('#' + current_pic).removeClass('selected');
      current_pic = id;
    }
    
    window.select = function(a) {
      var id = a.id.replace(/pic-/, '');
      $(a).toggleClass('selected');
      $('#' + current_pic).removeClass('selected');
      current_pic = id;
      
      var info = getInfo();
      var imgValue = (options.type == 'image-tag')
        ? '<img src="' + getUrl(info) + '" alt="' + info.image.title + '" />'
        : getUrl(info);
      options.select(imgValue);
      sheetMgr.close(true);
	  }
	  
	  this.open = function() {
	  
	    pTrigger.sheet({
			  volatile: true,
			  leaveDialog: true,
			  width: '535px',
			  title: 'Select Image',
			  load: function(done, manager) {
			    var slot = $(this);
			    sheetMgr = manager;
			    slot.addClass('image-picker');
          render(slot, done);
				  return true;
			  },
			  close: function() {
				  options.close && options.close();
			  }
		  });
      
      function render(slot, done) {
        var col_count = 0;
        colls = [];
        // get sizes and collections
        $.ajah(gA.url.images + '?page=' + gA.meta.identifier, null,
        function() {
          var d = $(this);
          d.find('ul.sizes dl').each(function(i) {
            var data = {};
            $(this).find('dt').each(function() {
              var k = this.innerHTML;
              $(this).find('+dd').each(function() {
                data[k] = this.innerHTML;
              })
            });
            var size = sizes[size_names[i]];
            if (!size) {
              return;
            }
            $.extend(size, data);

            size.node.parent().show();
          });
          d.find('ul.collections a').each(function() {
            var a = $(this);
            COLL_TEMPLATE[1] = col_count;
            COLL_TEMPLATE[3] = a.html();
            COLL_TEMPLATE[5] = col_count;
            slot.append(COLL_TEMPLATE.join(''));
            colls[col_count] = {
              url: gA.url.images + queryOnly(a.attr('href')),
              hatch: slot.find('#coll-' + col_count), 
              content: slot.find('#con-' + col_count)
            }
            ++col_count;
          });
          if (col_count) {
            slot.find('a.title', slot).each(function(idx) {
              $(this).click(function() {
                toggle(idx);
                return false;
              });
            });
            toggle(0);
          }
          done && done();
        }, function() {
          // TODO(dglazkov): handle collection list fetch failure
        });
      }
    };
    
    // needed for IE, which does not correctly retrieve the attribute
    // it brings over the entire URL, rather than just the query string
    function queryOnly(href) {
      var pos = href.indexOf('?');
      if (!pos) {
        return href;
      }
      
      return href.substring(pos);
    }
    
    function getInfo() {
      var pic = $('#pic-' + current_pic);
      return pic.data(g.IMAGE_INFO_KEY);
    }
    
    function getUrl(info) {
      //var src = info.image.href;
      var src = info.image.url;
      var version = sizes[current_size].version;
      if (version) {
        src += '?version=' + version;
      }
      return src;
    }
    
    function renderPreview(info) {
      preview.css({
        backgroundImage: 'url(\'' + getUrl(info) + '\')'
      });
    }
  }
});
// ***
// jquery.info-box.js
// Provides ability to create popup areas to display information
// on mouse over of elements

/* 
Currently only implements the 'hover' event (mouseover).

The calling program is responsible for the creation of the info box via
the create callback.  The create callback is passed the event that triggered
the hover.  This plugin tracks all created info boxes and is mapped via the 
element id on which the event is set.  The calling program is responsible for
making sure that elements have an id attribute.

*/

var info_box_globals = {
  key: 'INFOBOX_KEY',
	default_settings: {
		// callback for creating the info box 
		// passes the element that the event is triggered on
		create: nop,
		useParentPosition: false,
		useParentClass: "",
		delay: 500
	}
};
 
plugin("infoBox", function(settings) {
	var g = info_box_globals;
	var ibox = this.data(g.key);
  if (!ibox) {
    var complete_settings = $.extend({}, g.default_settings, settings);
	  ibox = new infoBox(this, complete_settings);
    this.data(g.key, ibox);
  }
	return ibox;

	function infoBox(nodes, settings) {
	  var me = this;
		var itemInfo = {};
		var itemTimeoutId;
		var curInfo;
		var bound = false;
    
    this.bind = function() {
      if (!bound) {
        nodes.bind('mouseover.' + g.key, 
          function() {
            var node = $(this);
				    itemTimeoutId = window.setTimeout(function() {
					    itemInfoBox(node, "open");
				    }, complete_settings.delay);
          }).bind('mouseout.' + g.key,
          function() {
            clearTimeout(itemTimeoutId);
				    itemInfoBox($(this), "close");
          }
        );
        bound = true;
      }
    }
    
    this.unbind = function(clear) {
      if (bound) {
        clear && me.clear();
        nodes.unbind('.' + g.key);
        bound = false;
      }
    }
    
    this.bind();
    
    this.clear = function() {
      var id = nodes.attr('id');
      var box = itemInfo[id];
      if (box) {
        box.remove();
        itemInfo[id] = null;
      }
    }
		
		function itemInfoBox(el, mode) {
		  var id = el.attr('id');
		  if (!id || id.length == 0) {
		    id = $.guid();
		    el.attr('id', id);
		  }
			switch(mode) {
				case "open":
					if (!itemInfo[id]) {
						var box = settings.create(el);
						$(box).shadowInner('info-popup');
						itemInfo[id] = box;
					}
					curInfo = itemInfo[id];
					$(curInfo).show();
					$.nodePositioner(curInfo, el);
					break;
				case "close":
					if (curInfo) {
						$(curInfo).hide();
					}
					break;
			}
		};
	};
});


// ***
// jquery.link.js

// edit link
plugin('link', function(settings) {
  var g = $.authoring();
  var slot;
  var sheetManager;
  var close;
  var intRegex = /^(\d+)$/;
  //var urlRegex = /\//;
  var urlRegex = /^\/|http|mailto/;
  var defaultText;
  var errorMsg;
  var valid = false;
  var data = $.extend({url:'', text:'', textoverride: false}, settings.data);
  settings = $.extend({},
    {
      SHEET_KEY: 'link',
      title: 'Link',
      mode: 'dialog',
      width: 500,
      inputName: 'password',
      volatile: true,
      slotClass: 'slate',
      submitTitle: 'ADD',
      load: function(end, manager) {
        // save close so that "assign" could use it
        close = manager.close;
        sheetManager = manager;
        slot = this;
        var holder = this.div().addClass('link');
        var tForm = $.form('form');
        var labelPos = 'before';
        var field = tForm.formfield('input', 
          {type: 'text', name: 'url', 'class': 'url', tabindex: 1},
           'URL:', data.url);
        field.phrase({
          name: 'span',
          load: function() {
            this.button('BROWSE', 'browse', function() {
              $(this).sheet($.authoring.browse_sheet({
                pageId: settings.pageId,
                select: function(data) {
                  tForm.find('input[name=url]').val(data.pageId).change();
                }
              }));
            });
          }
        });
        field.find('input').focus(function() {
          manager.status.clear();
        }).change(function() {
          var inp = $(this);
          var url = inp.val();
          var pid = getInt(url);
          if (pid) {
            tForm.spinner({text: "Validating page..."});
            $.ajah(g.url.pageinfo + '?current=' + g.meta.identifier + '&page=' + pid, 
              function(status, location) {
                var html = $(this);
                var pageinfo = html.find('div.pageinfo');
                if (pageinfo.length) {
                  valid = true;
                  defaultText = pageinfo.find('.title').text();
                  var override = tForm.find('input[name=text-override]');
                  if (!override.attr('checked')) {                
                    tForm.find('input[name=text]').val(defaultText).attr('disabled', 'true');
                  }
                }
                else {
                  html.find('h1').remove();
                  errorMsg = html.html();
                  manager.status.error(errorMsg);
                }
                tForm.spinner().remove();
              }, function(status) {
                // handle update failure 
                tForm.spinner().remove();
                valid = false;
                errorMsg = status;
                manager.status.error(errorMsg);
                end && end(); 
              });
          }
          else {
            tForm.find('div.text-override').hide();
            tForm.find('input[name=text]').removeAttr('disabled');
            tForm.spinner({text: "Validating url..."});
            if (!urlRegex.exec(url)) {
              valid = false;
              errorMsg = 'The url does not appear to be an external url';
              manager.status.error(errorMsg);
            }
            else {
              valid = true;
            }
            tForm.spinner().remove();
          }
        });
        var override = $.div().addClass('text-override');
        override.form('input', 
          {type: 'checkbox', name: 'text-override'}, 
          {position: 'after', text: 'Override Default Link Text'})
          .click(function() {
            if ($(this).attr('checked')) {
              tForm.find('input[name=text]').removeAttr('disabled');
            }
            else {
              tForm.find('input[name=text]').attr('disabled', 'true').val(defaultText);
            }
          });
        tForm.append(override);

        tForm.formfield('input', 
          {type: 'text', name: 'text', 'class': 'text', tabindex: 2}, 
          'Text:', data.text).wrapInner('<div></div>');
        holder.append(tForm);
        /* data passed in for edit, initialize form imput */
        if (data.textoverride) {
          tForm.find('input[name=text]').val(data.text);
          tForm.find('input[name=text-override]').attr('checked', 'true');
        }
        if (data && data.url.length) {
          tForm.find('input[name=url]').change();
        }
        
        slot.parent().div().addClass('submit').phrase({
          load: function() {
            this.button('CANCEL', 'cancel', function() {
              manager.close();
            });
            // Submit
            this.button(settings.submitTitle, 'submit', function() {
              if (!valid) {
                manager.status.error(errorMsg);
                return;
              }
              var url = tForm.find('input[name=url]').val();
              if (!url) {
                manager.status.error('URL is required');
                return;
              }
              var text = tForm.find('input[name=text]').val();
              var override = tForm.find('input[name=text-override]:visible');
              var overrideValue = true;
              if (override.length) {
                overrideValue = override.attr('checked');
              }
              else if (!text.length) {
                manager.status.error('Text is required for non-Estrada page urls');
                return;
              }
              var data = {};
              data['textoverride'] = overrideValue;
              data['text'] = text;
              data['url'] = url;
              settings.submit(data);
              manager.close();
            });
          }
        });
        end();
        return true;
      },
      open: function() {
        slot.show();
        sheetManager.status.clear();
      }
    }, (settings || {}));
  
  this.sheet(settings);
  
  
  function getInt(value) {
    var matched = intRegex.exec(value);
    if (matched) {
      return matched[1];
    }
    return null;
  }
});


// ***
// jquery.list-grid.js
// presents information as a list of items
// uses table for grid

// TODO(kwallace): add documentation

var list_grid_globals = {
  // global storage for all list grid instances
  all: {},
  load: nop,
  default_settings: {
	// callback for creating rendering for each item.  The rendering callback
	// is pagged a GOM object that has the methods needed for rendering.
    render: nop,
    showMonthName: false,
    className: 'list-grid'
  },
  CELL: ['<div class="cell">', '', '</div>']
}

plugin('listGrid', function(id, settings, params) {

	// abbreviate global scope
	var g = list_grid_globals;
	var g_cal = calendar_grid_globals;
	// assign unique id, if not specified
	var instance_id = guid(id);
	var instance = g.all[instance_id];
	if (!instance) {
	    
		// merge provided args with the defaults to complete missing (if any)
		// members
		var complete_settings = $.extend({}, g.default_settings, settings);
		var complete_params = $.extend({}, new DefaultParams(), params);
	    
		return instance = g.all[instance_id] = new ListGrid(instance_id, this, 
			complete_settings, complete_params);
	}
	else {
		instance.renderItems();
	}
	return instance;
	
	function DefaultParams() {
    
		// default range is:
		// * start == first day of the current month,
		// * end == last day of the current month
		g_cal.createMonthRange(new Date(), 1, this);
	}
	
	// list grid instance
	function ListGrid(id, node, settings, params) {
		var me = this;
		var rangeincrement = {mode: -1, monthSpan: 1, daySpan: 0}
		// generate initial calendar grid:
		var table = node.table('class="' + settings.className + '"');
		renderItems(params);
		
		this.renderItems = function() {
			renderItems(params);
		}
		
		function renderItems(params) {
			setRangeIncrement(params);
			rangeincrement.daySpan =  g_cal.getDaysSpan(params.start, params.end);
			// invoke load handler
			params.rangeincrement = rangeincrement;
			settings.load(params);
			// invoke render handler
			settings.render.call(new GOM(), params);
			// render initial view of the table
			table.render();
		}
		
		// Grid Object Model (GOM)
		// exposes usable structure of the list grid without revealing too much
		function GOM() {
			this.row = function() {
				table.row();
			}
			// provides access to the inside of a list grid cell
			this.cell = function(data, className) {
				g.CELL[1] = data;
				var c = table.cell(g.CELL.join(''), 
					className ? 'class="' + className + '"' : '');
			}
			
			this.render = function() {
				table.render();
			}
			
			this.clear = function(keepHead) {
				table.clear(keepHead);
			}
		}
		
		function setRangeIncrement(params) {
			if (!params.rangeincrement || (params.rangeincrement.mode == -1)) {
				if ((rangeincrement.monthSpan = g_cal.rangeCoversMonth(params)) > 0) {
					rangeincrement.mode = 0;
				}
				else { 
					rangeincrement.mode = 1;
				}
			}
			else {
				rangeincrement.mode = params.rangeincrement.mode;
			}
		}
		
		this.change = function(new_params) {
			if (new_params != null) {
				if (!isNaN(new_params)) {
					if (new_params != 0) {
						$.extend(params, slide(new_params));
					}
				}
				else {
					$.extend(params, new_params);
					setRangeIncrement(params);
				}
			}
			else {
				g_cal.createMonthRange(g_cal.zeroDate(new Date()), 1, params);
			}
      table.clear(true);
			renderItems(params);
			// move relative to the current range
			function slide(n) {
				if (rangeincrement.mode == 0) {
					// change by months
					var start = new Date(params.start.getTime());
					start.setMonth(start.getMonth() + (n*rangeincrement.monthSpan));
					return g_cal.createMonthRange(start, rangeincrement.monthSpan, {});
				} 
				// change by explicit range
				var start;
				var end;
				if (n > 0) {
					start = g_cal.zeroDate(params.end);
					start.setDate(start.getDate() + 1);
					end = new Date(start.getTime());
					end.setDate(end.getDate() + (rangeincrement.daySpan*n));
				}
				else {
					end = g_cal.zeroDate(params.start);
					end.setDate(end.getDate() - 1);
					start = new Date(end.getTime());
					start.setDate(start.getDate() + (rangeincrement.daySpan*n));
				}
				return { start: start, end: end };
			}
		}
		 
		// removes the table grid
		this.remove = function() {
			 $('table:first', node).remove();
			g.all[instance_id] = null;
		}
	}
});


// ***
// jquery.list-jqgrid.js
// presents information as a list of items
// uses table for grid

// TODO(kwallace): add documentation

var list_jqgrid_globals = {
  // global storage for all list grid instances
  all: {},
  load: nop,
  default_settings: {
	// callback for creating rendering for each item.  The rendering callback
	// is pagged a GOM object that has the methods needed for rendering.
    render: nop,
    showMonthName: false,
    className: 'list-jqgrid'
  },
  defaults_jqGrid: {
    prmNames: {page:'qa',rows: 'qg'},
    settings: {
      //maxHeight: 350,
      uniqueDisabled: true,
      gridComplete: null,
      customData: null
    }
  },
  getGridParams: function(page, rows) {
    var params = {};
    params[list_jqgrid_globals.defaults_jqGrid.prmNames.page] = page;
    params[list_jqgrid_globals.defaults_jqGrid.prmNames.rows] = rows;
    return params;
  },
  defaultConfig: {
    baseUrl: window.location.pathname,
    postUrl: function(q) {
      return me.baseUrl + q;
    }
  },
  gridPageInfo: function(node) {
    var o;
    $('.cq-params:first', node).each(function() {
      var records = $('.records', this).text();
      var total = $('.totalrecords', this).text();
      var page = $('.page', this).text();
      var size = $('.pagesize', this).text();
      $(this).remove();
      o = {
        initialized: true,
        records: records,
        totalrecords: total,
        page: page,
        rowNum: size
      };
    });
    return o;
  },
	gridPageMgr: function(config) {
	  if (!config) {
	    config = {
	      baseUrl: window.location.pathname,
        postUrl: function(q) {
          return me.baseUrl + q;
        },
        gridPageMgr: {
          gridPageInfo: function() {
  	        return {
	            initialized: false,
	            records: 0,
	            totalrecords: 0,
	            page: 0,
	            rowNum: 0
	          }
	        }
	      }
	    };
	  }
	  else {
	    config['gridPageMgr'] = {
	      loadUrl: function(newUrl) {
	        newUrl && (config.baseUrl = newUrl);
		      return config.postUrl();
		    },
		    gridPageInfo: this.gridPageInfo
	    };
	  }
	  if (!config.getGridParams) {
	    if (config.handler && config.handler.getGridParams) {
	      config['getGridParams'] = config.handler.getGridParams;
	    }
	    else {
	      config['getGridParams'] = function() {
	        var o = {};
	        o[list_jqgrid_globals.defaults_jqGrid.prmNames.page] = '1';
	        return o;
	      };
	    }
	  }
	  return config;
	}
};

plugin('listjqGridGlobals', function() {
  return list_jqgrid_globals;
}(), true);

plugin('listjqGrid', function(id, settings, params) {
	// abbreviate global scope
	var g = list_jqgrid_globals;
	var g_cal = calendar_grid_globals;
	// assign unique id, if not specified
	var instance_id = guid(id);
	var instance = g.all[instance_id];
	if (!instance) {
	    
		// merge provided args with the defaults to complete missing (if any)
		// members
		var complete_settings = $.extend({}, g.default_settings, settings);
		var complete_params = $.extend({}, new DefaultParams(), params);
	    
		return instance = g.all[instance_id] = new ListGrid(instance_id, this, 
			complete_settings, complete_params);
	}
	else {
		instance.renderItems();
	}
	return instance;
	
	function DefaultParams() {
    
		// default range is:
		// * start == first day of the current month,
		// * end == last day of the current month
		g_cal.createMonthRange(new Date(), 1, this);
	}
	
	function LoadcustomMgr(settings, specificLoadCustom) {
	  var me = this;
	  this.loadcustom = function(gridHelper) {
	    me.gridPageInfo = settings.gridPageInfo($(settings.selector, gridHelper.data));
	    //console.info(me.gridPageInfo);
	    settings.gridConfig.beforeLoadCustom && settings.gridConfig.beforeLoadCustom(gridHelper.data);
	    specificLoadCustom && specificLoadCustom(gridHelper, me.gridPageInfo);
	    if (me.gridPageInfo) {
	      var pages = me.gridPageInfo.rowNum ? Math.ceil(me.gridPageInfo.totalrecords/me.gridPageInfo.rowNum) : 1;
	      var lastrec = (me.gridPageInfo.records == me.gridPageInfo.rowNum) 
	        ? me.gridPageInfo.records * me.gridPageInfo.page : me.gridPageInfo.totalrecords;
	      var firstrec = lastrec - (me.gridPageInfo.records - 1);
	      
	      var recordtext = firstrec + ' - ' + lastrec;
	      if (lastrec < me.gridPageInfo.totalrecords) {
	        recordtext += ' of ' + me.gridPageInfo.totalrecords;
	      }
	      recordtext += ' Rows';
	      $('#' + settings.gridConfig.gridId).setGridParam(
		      {
		        records: me.gridPageInfo.records,
		        page: me.gridPageInfo.page,
		        rowNum: me.gridPageInfo.rowNum,
		        lastpage: pages,
		        recordtext: recordtext
		      });
		    $('select.selbox', settings.gridConfig.gridParams.pager).val(me.gridPageInfo.rowNum);
		  }
      gridHelper.done && gridHelper.done();
    }
  }
	
	// list grid instance
	function ListGrid(id, node, settings, params) {
		var me = this;
		var rangeincrement = {mode: -1, monthSpan: 1, daySpan: 0};
		setParams(params);

		var gridConfig = settings.gridConfig;
		var caption = gridConfig.caption;
		gridConfig.gridParams = $.extend({}, gridConfig.gridParams, g.defaults_jqGrid.settings);
		var loadCustomMgr = new LoadcustomMgr(settings, gridConfig.gridParams.loadcustom);

		gridConfig.gridParams.loadcustom = loadCustomMgr.loadcustom;
		gridConfig.gridParams.prmNames = g.defaults_jqGrid.prmNames;
		gridConfig.gridParams.url = settings.loadUrl && settings.loadUrl();
		gridConfig.gridParams.gridComplete = settings.gridComplete;
		gridConfig.gridParams.customData = settings.customData;
		gridConfig.gridParams.rowNum = 0;
		$.uiHandler('ui-grid').render(node, gridConfig);
		
		function setParams(params) {
			setRangeIncrement(params);
			params.rangeincrement = rangeincrement;
			settings.load && settings.load(params);
		}
		
		function setRangeIncrement(params) {
			if (!params.rangeincrement || (params.rangeincrement.mode == -1)) {
				if ((rangeincrement.monthSpan = g_cal.rangeCoversMonth(params)) > 0) {
					rangeincrement.mode = 0;
				}
				else { 
					rangeincrement.mode = 1;
				}
			}
			else {
				rangeincrement.mode = params.rangeincrement.mode;
			}
			rangeincrement.daySpan =  g_cal.getDaysSpan(params.start, params.end);
		}
		
		this.caption = function(title) {
		  caption = title;
		}
		
		this.reset = function() {
		  loadCustomMgr.gridPageInfo.page = 1;
		}
		
		this.change = function(new_params) {
			if (new_params != null) {
				if (!isNaN(new_params)) {
					if (new_params != 0) {
						$.extend(params, slide(new_params));
					}
					else {
					  loadCustomMgr.gridPageInfo.page = 1;
					}
				}
				else {
					$.extend(params, new_params);
				}
			}
			else {
				g_cal.createMonthRange(g_cal.zeroDate(new Date()), 1, params);
			}
			setRangeIncrement(params);
			params['rangeincrement'] = rangeincrement;
			settings.load(params);
			$('#' + settings.gridConfig.gridId).clearGridData().setCaption(caption)
			  .setGridParam(
			    {
			      page: loadCustomMgr.gridPageInfo.page,
			      url: settings.loadUrl(),
			      qa: '1'
			    })
			  .trigger('reloadGrid');
			// move relative to the current range
			function slide(n) {
				if (rangeincrement.mode == 0) {
					// change by months
					var start = new Date(params.start.getTime());
					start.setMonth(start.getMonth() + (n*rangeincrement.monthSpan));
					return g_cal.createMonthRange(start, rangeincrement.monthSpan, {});
				} 
				// change by explicit range
				var start;
				var end;
				if (n > 0) {
					start = g_cal.zeroDate(params.end);
					start.setDate(start.getDate() + 1);
					end = new Date(start.getTime());
					end.setDate(end.getDate() + (rangeincrement.daySpan*n));
				}
				else {
					end = g_cal.zeroDate(params.start);
					end.setDate(end.getDate() - 1);
					start = new Date(end.getTime());
					start.setDate(start.getDate() + (rangeincrement.daySpan*n));
				}
				return { start: start, end: end };
			}
		}
		 
		// removes the table grid
		this.remove = function() {
			 $('table:first', node).remove();
			g.all[instance_id] = null;
		}
	}
});


// ***
// jquery.orderer.js

var global_orderer = {
  DATA_KEY: 'orderer',
  defaults: {
    // default width of the dialog box
    width: 300,
    // estimated height of the dialog box
    height: 300
  }
};

plugin('orderer', function(options) {
  var g = global_orderer;
  
  options = $.extend({}, g.defaults, options || {});
  var gA = $.authoring();
  if (!gA) {
    // authoring is not enabled
    return null;
  }
  var trigger = $(this);
  var order = trigger.data(g.DATA_KEY);
	if (!order) {
		order = new orderer(trigger); 
		trigger.data(g.DATA_KEY, order);
	}
	order.open();
	return order;
	
	function orderer(pTrigger) {
	  var dataNode = options.dataNode;
	  var params = options.gridConfig;
	  options.initializeData('order', dataNode);
	  params.gridParams.datastr = dataNode.outerHtml();
	  //var list = dataNode.find('ul:first');
	  var list = dataNode;
	  this.open = function() {
	    $.uiHandler('ui-grid').render(pTrigger, params);
	    $('#' + params.gridId + ' tr').prepend('<td class="itemOrdering">');
	    $('#' + params.gridId).sortable({
	      //handle: 'td.itemOrdering',
	      items: 'tr',
	      axis: 'y',
	      scroll: true,
	      forcePlaceholderSize: true,
	      placeholder: 'sort-placeholder',
	      update: function(ev, ui) {
	        var id = ui.item.attr('id');
	        var prevId = ui.item.prev().attr('id');
	        if (prevId == '_empty') {
	          list.prepend(dataNode.find('#' + id));
	        }
	        else {
	          $('#' + prevId, dataNode).after($('#' + id, dataNode));
	        }
	      },
	      start: function(ev, ui) {
	         ui.placeholder.append('<td colspan="3"><div class="marker" /></td>');
	      },
	      change: function(ev, ui) {
	      }
	    });
    };
  }
});
// ***
// jquery.passwordchange.js
// change password

plugin('passwordchange', function(settings) {
  var password, password2;
  var slot;
  var sheetManager;
  var close;
  settings = $.extend({},
    {
      title: 'Change Password',
      mode: 'dialog',
      width: 300,
      inputName: 'password',
      volatile: true,
      slotClass: 'slate',
      load: function(end, manager) {
        // save close so that "assign" could use it
        close = manager.close;
        sheetManager = manager;
        slot = this;
        var pw = this.div().addClass('password-change');
        var form = pw.append('<form></form>');
        password = $('<input type="password" name="password" />');
        password2 = $('<input type="password" name="password2" />');
        form.append($.div().addClass('field').append('<span class="label">Password</span>').append(password));
        form.append($.div().addClass('field').append('<span class="label">Re-enter Password</span>').append(password2));
        if (settings.url) {
          form.pushButton("", 
            {
	            container: "div",
	            className: "command",
	            mode: "unconnected",
	            buttons: [
	              {
			            title: "Cancel",
			            className: "cancel",
			            selected: function(button) {
				            close();
			            }
		            },
		            {
			            title: "Change Password",
			            className: "submit",
			            selected: function(button) {
				            var trigger = $(button);
				            var data = {};
				            data['pw'] = password.val();
				            var pw2 = password2.val();
				            if ((data['pw'].length == 0) || (data['pw'] != pw2)) {
				              sheetManager.status.error('Passwords do not match, please try again');
				              password.val('');
				              password2.val('');
				              return;
				            }
				            $.ajah(settings.url, data,
                      function() {
                        slot.hide();
                        sheetManager.status.add($(this).html());
                        end();
                      }, 
                      function() {
                        // TODO(dglazkov): handle workgroup retrieval failure
                        slot.hide();
                        sheetManager.status.error($(this).html());
                        end();
                      });
			            }
		            }
	            ]
            });
        }
        end();
        return true;
      },
      open: function() {
        slot.show();
        sheetManager.status.clear();
      }
    }, (settings || {}));
  
  this.sheet(settings);
});


// ***
// jquery.phrase.js
// provides inline sentence-based UI with dialog triggers

var global_phrase = {
  // default options
  defaults: {
    // called when the phrase is ready to load
    // * this -- builder
    load: nop,
    //className: 'phrase',
    name: 'div'
  },
  els: {}
};

// TODO(dglazkov): consider refactoring to build HTML as a string, then
//                 using .append(html)
// TODO(dglazkov): add documentation
plugin('phrase', function(options) {
  
  var g = global_phrase;
  var opt = $.extend({}, g.defaults, options || {});
  
  var me = this;
  // where the node will be stored
  var n;

  // true when the sentence just started
  var first = true;
  
  opt.load.call({
    text: function(text, c) {
      space();
      var span = node().dom('span').addClass('text').html(text);
      if (c) span.addClass(c);
      return this;
    },
    spot: function(text, c, click) {
      space();
      init(node().dom('button').addClass('spot').html(text), c, click);
      return this;
    },
    link: function(text, url, c, click) {
      space();
      init(node().dom('a').attr('href', url).addClass('link').html(text), c,
           click);
      return this;
    },
    
    button: function(text, c, click, spinner, moreFunc) {
      space();
      var moreNode;
      var o = {
        mainButton: null,
        moreButton: null,
        dropdown: null,
        done: buttonDropDownHandler,
        mouseover: mouseOver,
        mouseout: mouseOut
      };
      o.mainButton = $('<button>').addClass('button').html('<span class="text"><b></b>' + text + '</span>');
      if (moreFunc) {
        o.mainButton.addClass('button-split');
        o.moreButton = $.dom('button').attr('id', guid()).addClass('button-more')
          .click(function(ev) {
              o.dropdown = $(o.moreButton).data('dropdown');
              if (!o.dropdown) {
                o.dropdown = $.div().addClass('button-drop-down l-hide').insertAfter(o.moreButton);
                $(o.moreButton).data('dropdown', o.dropdown);
              }
              moreFunc(ev, o);
              //adjustPosition(o.dropdown, o.moreButton);
              o.dropdown.removeClass('l-hide');
              return false;
            });
      }
      init(o.mainButton, c, click, spinner);
      if (spinner) {
        o.mainButton.append('<span class="loading" style="display:none;"><b></b></span>');
      }
      node().append(o.mainButton);
      o.moreButton && o.mainButton.after(o.moreButton);
      return this;
    },
    period: function() {
      node().dom('span').html('.');
      return this;
    },
    comma: function() {
      node().dom('span').html(',');
      return this;
    },
    semicolon: function() {
      node().dom('span').html(';');
      return this;
    },
    phrase: function(options) {
      if (options.name != 'div') {
			  space();
			}
			var opt = $.extend({}, g.defaults, options || {});
			var area = $(node()).phrase(options);
			return area;
    },
    node: function(n) {
			node().append(n);
			return this;
    },
    getPhraseNode: function() {
      return node();
    },
    clear: function() {
      node().empty();
    }
	});
  
	return this;

  function node() {
    return n || (n = $(me).dom(opt.name).addClass('phrase').addClass(opt.className));
  };

  function space() {
    if (first) {
      first = false;
      return;
    }
    node().dom('span').addClass('space').html(' ');
  };
  
  function init(n, c, click, spinner) {
    //console.info('init: n=' + n + ' c=' + c + ' click=' + click);
    click || typeof(c) == "function" && (click = c, c = null);
    c && n.addClass(c);
    click && n.click(function(ev) {
      if (spinner) {
        var b = $(this);
        b.attr('disabled', true);
        el('text', b).hide();
        el('loading', b).show();
        var postParams = n.data('dropdown-post');
        click.apply(this, [function() {
          b.attr('disabled', false);
          el('loading', b).hide();
          el('text', b).show();
        }, postParams]);
      }
      else {
        this.blur();
        click.apply(this, arguments);
      }
      ev.preventDefault();
    });
  };
  
  function buttonDropDownHandler(params, post, postParams) {
    params.mainButton.data('dropdown-post', postParams);
    post && params.mainButton.click();
    params.moreButton.removeData('dropdown');
    params.dropdown.remove();
  };
  
  function el(name, node) {
    return (global_phrase.els[name] || (global_phrase.els[name] =  node.find('.' + name).eq(0)));
  };

	function mouseOver(node) {
    $(node).addClass('l-hover-on');
  }
  
  function mouseOut(node) {
    $(node).removeClass('l-hover-on');
  }

});

// ***
// jquery.push-button.js

var global_push_button = {
	SEARCH_KEY: 'push_button',
	selected: {},
	defaults: {
		container: "div",  // 'none' | 'div' | 'table'
		buttons: null,
		mode: "single", // single | multiple | unconnected
		canUnselect: false,  // only for single mode
		beforeText: "",
		afterText: "",
		className: "pushbutton"
	},
	button_defaults: {
		on: false,
		title: "",
		beforeText: "",
		afterText: "",
		selected: null,
		className: null,
		id: null,
		name: "button"
	}
};

plugin('pushButton', function(id, settings) {
	var g = global_push_button;
	settings = $.extend({}, g.defaults, settings);
	var holder;
	var isTable = false;
	var append = true;
	switch (settings.container) {
	case "table":
		holder = $(document.createElement("table"));
		isTable = true;
		break;
	case "div":
		holder = $(document.createElement("div"));
		break;
	case "span":
		holder = $(document.createElement("span"));
		break;
	default:
		append = false;
		if (!this[0]) {
			holder = $(document.createElement("div"));
		}
		else {
			holder = this;
		}
	}
	if (append) {
		this.append(holder);
	}
	holder.addClass(settings.className);
	return new pushButton(holder, settings, isTable);
	
	function pushButton(srcNode, settings, isTable) {
		var me = this;
		this.container = srcNode;
		
		var idMap = {};
		var buttons = {};  //node: , selected:
		
		if (isTable) {
			var tbody = srcNode.dom("tbody");
			srcNode = tbody.dom("tr");
		}
		
		this.selected = function() {
			return manager.current;
		}
		
		this.button = function(id) {
			return idMap[id].node
		}
		
		this.updateButtonText = function(id, text) {
			$('span:first', idMap[id].node).html(text);
		}
		
		var manager = {
			mode: "single",
			canUnselect: false,
			current: null,
			select: function(node, selectedFunc) {
				var buttonObj = buttons[$(node).attr("id")];
				if (buttonObj) {
					switch(this.mode) {
						case "single":
							if (this.current) {
								$(this.current.node).attr("disabled", false);
								$(this.current.node).removeClass("e-on");
							}
							if (this.current && (node == this.current.node)) {
								this.current = null;
							}
							else {
								this.current = buttonObj;
								if (!this.canUnselect) {
									$(buttonObj.node).attr("disabled", true);
								}
								$(buttonObj.node).addClass("e-on");
								buttonObj.selected = true;
							}
							break;
						case "multiple":
							if (!this.current) {
								this.current = [];
							}
							var found = false;
							var me = this;
							$(this.current).each(function(idx) {
								if (this.node == node) {
									$(this.node).attr("disabled", false);
									$(this.node).removeClass("e-on");
									this.selected = false;
									me.current.splice(idx, 1);
									found = true;
									return false;
								}
							});
							if (!found) {
								this.current.push(buttonObj);
								$(buttonObj.node).addClass("e-on");
								buttonObj.selected = true;
							}
							break;
					}
					if (buttonObj.selectedFunc) buttonObj.selectedFunc(node);
				}
			}
		};
		
		manager.mode = settings.mode;
		manager.canUnselect = settings.canUnselect;

		if (settings.buttons) {
			var pNode;
			//$(settings.buttons).each(function() {
			for (var key in settings.buttons) {
				var buttonItem = settings.buttons[key];
				var buttonSettings = $.extend({}, g.button_defaults, buttonItem);
				pNode = isTable ? srcNode.dom("td").addClass("cell-"+buttonSettings.className) : srcNode;
				var id = guid();
				if (!buttonSettings.id || buttonSettings.id.length == 0) {
					buttonSettings.id = id;
				}
				if (buttonSettings.beforeText) {
					if (buttonSettings.beforeText.data || buttonSettings.beforeText.nodeName) {
						pNode.append(buttonSettings.beforeText);
					}
					else {
						pNode.dom('span').addClass("text").html(buttonSettings.beforeText);
					}
				}
				var button = pNode.dom(buttonSettings.name).attr("id", id);
        // if "button" element is created, it needs a type="button",
        // because according to HTML4 standard, the default is type="submit",
        // which is going to cause a POST submission of the page
        // Note that only FF3 and Safari correctly support the standard.
        if (buttonSettings.name == 'button') {
          try {
						button.attr("type", "button");
					}
					catch(e) {}
        }
				if (buttonSettings.title.data || buttonSettings.title.nodeName) {
					button.append(buttonSettings.title);
				}
				else {
					button.dom('span').html(buttonSettings.title);;
				}
				button.addClass('button');
				if (buttonSettings.className) {
					button.addClass(buttonSettings.className);
				}
				buttons[id] = {node: button, selected: false, selectedFunc: buttonSettings.selected};
				idMap[buttonSettings.id] = buttons[id];
				if (buttonSettings.on) {
					manager.select(button);
				}
				if (buttonSettings.selected) {
				  $(button).click(function() {
						manager.select(this);
					});
				  /*
					$(button).click(function() {
						manager.select(this);
						buttonSettings.selected(this);
					});
					*/
				}
				if (buttonSettings.afterText) {
					if (buttonSettings.afterText.data || buttonSettings.afterText.nodeName) {
						pNode.append(buttonSettings.afterText);
					}
					else {
						pNode.span().addClass("text").html(buttonSettings.afterText);
					}
				}
			};
		}
		/*
		else {
			$(srcNode).each(function() {
				var button = $(this).dom("button");
			});
		}
		*/
		this.Buttons = idMap;
	}
});

// ***
// jquery.query-form-phrase.js
// provides UI for calendar form

var query_form_phrase = {
  // default options
  defaults: {
    // called when the phrase is ready to load
    load: nop,
    config: null,
    node: null,
    mode: false,
    range: true,
    reset: true,
    search: true,
    filter: true,
    gridId: 0
  },
  managerDefaults: {
    name: "div",
    className: 'phrase',
    phrase: null,
    node: null
  },
  createClear: function(clearer, evaluator) {
    var clear = $('<span class="item2 clear"><a href="#clear">Clear</a></span>');
    clear.css('display', 'none');
    clear.click(function() {
      clearer();
      clear.css('display', 'none');
      return false;
    });
    evaluator(
      function() {
        clear.css('display', '');
      },
      function() {
        clear.css('display', 'none');
      }
    );
    return clear;
  },
  categoryManager: function(view, options) {
    var key = 'categories';
    var node;
    var state;
    var statetext;
    var noneSelected = '<No Categories Selected>';

    var currentSelectedText = $('<span class="current-value" />');
    currentSelectedText.text(noneSelected);
    var hasSelected = false;
    var setIsNotEmpty, setIsEmpty;

    options = $.extend({}, query_form_phrase.managerDefaults, options);

    var currentSelected;
    var picker;
    var filterLink = $('<a href="#filter">Categories</a>').click(function() {
      picker = $(this).categoryPicker(
		    {
		      title: 'Select Categories',
		      more: true,
		      inheritUp: false,
		      inheritDown: true,
		      categories: view.config.categories.selects,
		      select: function(selected) {
		        currentSelected = selected;
		        setCurrentSelectedAsText();
		        //filterLink.infoBox().clear();
		      }
		    });
      return false;
    });

    function setCurrentSelectedAsText() {
      var textValue = '';
      for (var key in currentSelected) {
        var items = currentSelected[key];
        $.each(items, function(idx, item) {
          textValue += ((textValue.length > 0) ? ', ' : '') + item.text
        });
      }
      hasSelected = (textValue.length > 0);
      hasSelected ? setIsNotEmpty() : setIsEmpty();
      if (!hasSelected) {
        textValue = noneSelected;
      }
      currentSelectedText.text(textValue);
      statetext = textValue;
    }

    function infoBoxConfig(evaluator) {
      var o = {
        create: function(el) {
          var box = $.div().addClass('info-popup filter-categories');
          var list = '<ul>';
          evaluator();
          list += '</ul>';
          $(box).append('<div class="title">Current Filter Categories</div>'
		        + list
		      );
          $(el).after(box);
          return box;
        }
      };
      return o;
    };

    function infoBoxFromCurrent() {
      return infoBoxConfig(function() {
        $.each(view.config.categories.getSelected(), function() {
          list += '<li>' + this + '</li>';
        });
      });
    };

    function infoBoxFromQuery() {
      return infoBoxConfig(function() {
        $.each(view.config.categories.getSelected(), function() {
          list += '<li>' + this + '</li>';
        });
      });
    };

    var o = {
      key: key,
      stateLabel: 'Categories',
      Phrase: null,
      isEmpty: function() {
        return !hasSelected;
      },
      render: function(parentNode) {
        var g = query_form_phrase;
        var me = this;
        if (!parentNode) {
          parentNode = options.node;
        }
        view.state.categories = state;
        var className = 'search-filter filter-item' + (me.className ? ' ' + me.className : '');
        var holder = parentNode.div().addClass(className);
        var span = $.dom('span').addClass('categories has-opener item2').eHover();
        span.append(filterLink);
        holder.append(span);
        holder.append(g.createClear(
	        function() {
	          picker.clear();
	        },
	        function(isNotEmpty, isEmpty) {
	          setIsNotEmpty = isNotEmpty;
	          setIsEmpty = isEmpty;
	        }));
        holder.append(currentSelectedText);
        /*
        if (view.config.categories.isSelectedEmpty()) {
        filterLink.infoBox(infoBoxFromQuery()).unbind(true);
        }
        else {
        filterLink.infoBox(infoBoxFromQuery()).bind();
        }
        */
      },
      setValue: function() {
        view.config.categories.clear();
        for (var key in currentSelected) {
          var items = currentSelected[key];
          var value = [];
          $.each(items, function(idx, item) {
            value.push(item.value);
          });
          view.config.categories.set(key, value);
        }
      },
      stateText: function() {
        return statetext;
      }
    };
    return o;

  },
  searchManager: function(view, options) {
    var key = 'search';
    var node;
    var rendered = false;
    var searchText;
    var statetext;
    var isempty = true;

    var o = {
      key: key,
      stateLabel: 'Search Text',
      Phrase: null,
      isEmpty: function() {
        return isempty;
      },
      render: function(parentNode) {
        var g = query_form_phrase;
        var me = this;
        if (!parentNode) {
          parentNode = options.node;
        }
        var className = 'search-filter filter-item' + (me.className ? ' ' + me.className : '');
        var searchArea = parentNode.div().addClass(className);
        searchArea.dom('span').addClass("label").text("Search Text: ");
        searchText = searchArea.form("input", { type: "text", name: "searchText", className: 'text' });
        searchArea.append(g.createClear(
	        function() {
	          searchText.val('');
	        },
	        function(isNotEmpty, isEmpty) {
	          searchText.blur(function() {
	            if (searchText.val().length > 0) {
	              isNotEmpty();
	            }
	            else {
	              isEmpty();
	            }
	          })
	        }));
      },
      singleSubmit: function(submit) {
        searchText.keydown(function(ev) {
          if (ev.which == 13) {
            submit.click();
          }
        });
      },
      value: function() {
        var val = searchText.val();
        statetext = val;
        if (val.length > 0) {
          isempty = false;
          return val;
        }
        else {
          isempty = true;
          return null;
        }
      },
      stateText: function() {
        return statetext;
      }
    };
    return o;
  },
  descendantsManager: function(view, options) {
    var key = 'descendants';
    var node;
    var rendered = false;
    var searchText;
    var statetext;
    var isempty = true;
    var area;

    var o = {
      key: key,
      stateLabel: '',
      Phrase: null,
      isEmpty: function() {
        return isempty;
      },
      render: function(parentNode) {
        var g = query_form_phrase;
        var me = this;
        if (!parentNode) {
          parentNode = options.node;
        }
        var className = 'descendants-filter filter-item' + (me.className ? ' ' + me.className : '');
        area = parentNode.div().addClass(className);
        area.append(view.config.descendants.containerNode);
      },
      value: function() {
        var val = area.find('input.descendants:checked').val();
        statetext = ((val == '0') ? 'Search Current Collection Only' : 'Search Current and Descendant Collections');
        if (val.length > 0) {
          //isempty = false;
          return val;
        }
        else {
          //isempty = true;
          return null;
        }
      },
      stateText: function() {
        return statetext;
      }
    };
    return o;
  },
  viewNavManager: function(view, options) {
    return new function() {
      var me = this;
      var rendered = false;
      var node;
      var namesp = 'viewnav' + $.guid();
      var urls = {};
      this.Phrase;

      if (!options.node) {
        return;
      }
      options = $.extend({}, query_form_phrase.managerDefaults, options);
      var holder = options.node;
      var phrase = holder.phrase({
        name: options.name,
        className: options.className,
        load: function() {
          me.Phrase = this;
          node = me.Phrase.getPhraseNode();
        }
      });

      var g = $.authoring();
      if (g.meta.view) {
        var text = (g.meta.view.name == 'default' && g.meta.view.title == 'Page') ? 'Current' : g.meta.view.title;
        var id = $.guid();
        var nn = node.dom('span').attr('id', id).addClass('viewitem current').text(text);
        urls[id] = window.location.pathname;
        view.caption(caption(nn));
      }
      $('a', options.viewnav).each(function() {
        var link = $(this);
        var className = link.attr('className');
        var text = (className == 'default') ? 'Current' : link.text();
        var id = $.guid();
        var nn = node.dom('span').attr('id', id).addClass('viewitem').text(text);
        urls[id] = link.attr('href');
        bind(nn);
      });
      node.div().addClass('clear-float');
      $(options.viewnav).remove();

      this.render = function() {
        if (!rendered) {
          rendered = true;
        }
      }

      function caption(node) {
        return node.text() + ' ' + view.config.handler.titleCapitalized;
      }

      function viewHandler(newCurrent) {
        $('.current', me.Phrase.getPhraseNode()).removeClass('current').each(function() {
          bind($(this));
        });
        newCurrent.addClass('current').each(function() {
          unbind($(this));
        });
        view.urlChange(urls[newCurrent.attr('id')], caption(newCurrent));
      }

      function bind(node) {
        node.addClass('clickable').bind('click.' + namesp, function() {
          viewHandler($(this));
          return false;
        });
      }

      function unbind(node) {
        node.removeClass('clickable').unbind('click.' + namesp);
      }
    }
  },
  stateManager: function(view, options) {
    if (!options.node) {
      return;
    }
    options = $.extend({}, query_form_phrase.managerDefaults, options);
    var holder = options.node;
    var state;
    var stateAreas = {};
    var stateNode;
    var searchState;
    var hasValue = {};
    var valueTracker = {};
    var resetAvailable = false;

    var phrase = holder.phrase({
      name: options.name,
      className: options.className,
      load: function() {
        state = this;
        stateNode = state.getPhraseNode();
        var head = $.div().addClass('header').text('Search Results');
        searchState = $.div().addClass('search-state');
        state.node(head);
        state.node(searchState);
      }
    });

    function stateArea(key, label) {
      var state = $('<span class="state-item state-' + key + '" />');
      if (label && (label.length > 0)) {
        state.append($('<span class="label">' + label + ': </span>'));
      }
      state.append($('<span class="value" />'));
      stateAreas[key] = state;
    }

    var o = {
      render: function(filterList) {
        $.each(filterList, function(idx, filterItem) {
          stateArea(filterItem.key, filterItem.stateLabel);
        });
      },
      refresh: function() {
        var stateNeeded = false;
        searchState.empty();
        $.each(hasValue, function(key, exists) {
          if (exists) {
            var item = stateAreas[key];
            if (item) {
              stateNeeded = true;
              searchState.append(item);
              item.find('.value').text(valueTracker[key]);
            }
          }
        });
        resetAvailable = stateNeeded;
        if (stateNeeded) {
          stateNode.addClass('state-visible');
        }
        else {
          stateNode.removeClass('state-visible');
        }
      },
      HasValueTracker: function(key, exists, value) {
        hasValue[key] = exists;
        valueTracker[key] = value;
      },
      resetAvailable: function() {
        return resetAvailable;
      },
      removeRange: function() {
        searchState.find('.state-range').remove();
      }
    }
    return o;
  },
  filterManager: function(settings, view, options) {
    var g = query_form_phrase;

    function activeFilters(view) {
      var filterList = [];
      if (settings.search) {
        view.search = g.searchManager(view, {
          name: 'span'
        });
        filterListAdd(view.search);
      }
      if (settings.range) {
        view.range = g.rangeManager(view, {
          name: 'span'
        });
        filterListAdd(view.range);
      }
      if (settings.categories) {
        view.categories = g.categoryManager(view, {
          name: 'div'
        });
        filterListAdd(view.categories);
      }
      if (settings.descendants) {
        view.descendants = g.descendantsManager(view, {
          name: 'div'
        });
        filterListAdd(view.descendants);
      }
      var singleFilter;
      var o = {
        list: filterList,
        isEmpty: (filterList.length == 0),
        render: function(filterArea) {
          $.each(filterList, function(idx, filter) {
            filter.render(filterArea);
            if (filterList.length == 1) {
              singleFilter = filter;
            }
          });
          view.descendants && view.descendants.render(filterArea);
          view.state.render(filterList);
        },
        finalize: function(submitButton) {
          if (singleFilter && singleFilter.singleSubmit) {
            singleFilter.singleSubmit(submitButton);
          }
        },
        setValues: function() {
          $.each(filterList, function(idx, filterItem) {
            if (filterItem.setValue) {
              filterItem.setValue();
            }
            else {
              view.config[filterItem.key].value.set(filterItem.value());
            }
          });
        },
        setState: function() {
          var cnt = 0;
          $.each(filterList, function(idx, filterItem) {
            var exists = !filterItem.isEmpty();
            if (exists) ++cnt;
            view.state.HasValueTracker(filterItem.key, exists, filterItem.stateText());
          });
          if ((cnt > 0) && view.descendants) {
            view.descendants.value();
            view.state.HasValueTracker(view.descendants.key, true, view.descendants.stateText());
          }
          view.state.refresh();
        },
        resetAvailable: function() {
          return view.state.resetAvailable();
        }
      };

      return o;

      function filterListAdd(item) {
        if (filterList.length == 0) {
          item.className = 'filter-item-first';
        }
        filterList.push(item);
      }
    }

    options = $.extend({}, g.managerDefaults, options);
    var filterHandler = activeFilters(view);
    if (filterHandler.isEmpty) {
      return;
    }
    var cmdArea;
    var phraseNode;
    var outer = options.node.div().addClass('filtering');
    var holder = outer.div().addClass('inner');

    view.state = new g.stateManager(view, {
      node: holder,
      className: 'state'
    });
    var sheetManager;
    var phrase = holder.div().addClass('lower').phrase({
      name: 'span',
      className: 'filter item has-opener',
      load: function() {
        phraseNode = this.getPhraseNode();
        var title = 'Search ' + (view.config.handler.titleCapitalized ?
				  view.config.handler.titleCapitalized : 'Collection');
        var filter = $('<a href="#filter">' + title + '</a>');
        filter.click(function() {
          $(this).sheet({
            SHEET_KEY: 'filtering',
            slotClass: 'slate',
            width: "650px",
            title: title,
            load: function(done, manager) {
              var slot = $(this);
              sheetManager = manager;
              var filterArea = slot.div().addClass('filtering');
              filterHandler.render(filterArea);
              cmdArea = $.div().addClass('submit');
              sheetManager.command.append(cmdArea);
              cmdArea.phrase({
                load: function() {
                  this.button('RESET SEARCH', 'reset', function(bdone) {
                    //slot.find('a[href=$#clear]:visible').click();
                    slot.find('span.clear:visible').click();
                    cmdArea.find('button.ok').click();
                    bdone();
                  }, true);
                  this.button('CANCEL', function() {
                    manager.close();
                  });
                  var submitTitle = options.SubmitTitle ? options.SubmitTitle : 'SEARCH';
                  var submit = this.button(submitTitle, 'ok', function(bdone) {
                    sheetManager.enabled(false);
                    sheetManager.status.clear();
                    sheetManager.status.processing('Searching...');
                    filterHandler.setValues();
                    var qdata = view.config.getGridParams();
                    $.ajah(view.config.postUrl({ query: qdata, all: true }), null,
			                function() {
			                  var handler = view.config.handler;
			                  var results = $(handler.selector(), this);
			                  manager.status.processing();
			                  if (results && results.length > 0) {
			                    view.gridChange($(this), function() {
			                      filterHandler.setState();
			                      sheetManager.close();
			                    });
			                  }
			                  else {
			                    sheetManager.status.error('No ' + handler.title + ' found matching search criteria');
			                  }
			                  bdone();
			                }
		                );
                    return true;
                  }, true);
                  filterHandler.finalize(submit.getPhraseNode().find('button.ok'));
                }
              });
              manager.command.append('<div class="sill"></div>');
            },
            open: function() {
              if (filterHandler.resetAvailable()) {
                cmdArea.find('button.reset').show();
              }
              else {
                cmdArea.find('button.reset').hide();
              }
              view.range && view.range.refresh();
            },
            ok: function() {
            },
            close: function() {

            }
          });
          return false;
        });
        phraseNode.append(filter);
      }
    });

    return {
      refreshRange: function() {
        view.range && view.range.refresh();
        filterHandler.setState();
        //view.state.refresh();
      },
      reset: function() {

      }
    };
  },
  rangeManager: function(view, options) {
    var key = 'range';
    var node;
    var state;
    var statetext;
    var noneSelected = '<No Range Selected>';
    var isempty = true;

    var currentSelectedText = $('<span class="current-value" />');
    currentSelectedText.text(noneSelected);
    var hasValue = false;
    var setIsNotEmpty, setIsEmpty;

    options = $.extend({}, query_form_phrase.managerDefaults, options);

    var currentSelected;
    var picker;

    function setCurrentSelectedAsText() {
      var textValue = '';
      if (currentSelected.start && currentSelected.end) {
        textValue = $.dates.format(currentSelected.start, 'M/d/yyyy')
	        + ' - ' + $.dates.format(currentSelected.end, 'M/d/yyyy');
      }
      hasValue = (textValue.length > 0);
      hasValue ? (setIsNotEmpty && setIsNotEmpty()) : (setIsEmpty && setIsEmpty());
      if (!hasValue) {
        textValue = noneSelected;
      }
      currentSelectedText.text(textValue);
      statetext = textValue;
    }

    function getCurrentRange() {
      var range = {
        start: view.config.range.start.value.get(),
        end: view.config.range.end.value.get()
      };
      currentSelected = range;
      setCurrentSelectedAsText();
      return range;
    }

    var o = {
      key: key,
      stateLabel: 'Date Range',
      Phrase: null,
      isEmpty: function() {
        return !hasValue;
      },
      render: function(parentNode) {
        var me = this;
        var g = query_form_phrase;
        if (!parentNode) {
          parentNode = options.node;
        }
        getCurrentRange();
        var className = 'range-filter filter-item' + (me.className ? ' ' + me.className : '');
        var holder = parentNode.div().addClass(className);
        var span = $.dom('span').addClass('range has-opener item2').eHover();
        var link = $('<a href="#range">Range</a>').click(function() {
          picker = $(this).rangePicker('rangepicker',
				    {
				      type: 'range',
				      mode: 'dialog',
				      refresh: function() {
				        return getCurrentRange();
				      },
				      select: function(range) {
				        currentSelected = range;
				        setCurrentSelectedAsText();
				      }
				    });
          return false;
        });
        span.append(link);
        holder.append(span);
        /*
        holder.append(g.createClear(
        function() {
        picker.clear();
        },
        function(isNotEmpty, isEmpty) {
        setIsNotEmpty = isNotEmpty;
        setIsEmpty = isEmpty;
        }));
        */
        holder.append(currentSelectedText);
      },
      setValue: function() {
        view.config.range.set(currentSelected.start, currentSelected.end);
      },
      stateText: function() {
        return statetext;
      },
      update: function(rangeincrement, start, end, complete_start, complete_end) {
        view.config.range.set(start, end, complete_start, complete_end);
      },
      refresh: function() {
        getCurrentRange();
      }
    };
    return o;

    function getTitle(start, end) {
      var title = "";
      if (start && !start.getYear) {
        start = new Date(Date.parse(start));
      }
      if (end && !end.getYear) {
        end = new Date(Date.parse(end));
      }
      if (!start && !end) {
        title = " across all dates ";
      }
      else if (!start) {
        title = " before " +
					$.dates.format(end, "M/d/yyyy");
      }
      else if (!end) {
        title = " after " +
					$.dates.format(start, "M/d/yyyy");
      }
      else {
        title = " between " +
					$.dates.format(start, "M/d/yyyy") +
					" and " +
					$.dates.format(end, "M/d/yyyy");
      }
      return title;
    }

  }
};

// TODO(kew): add documentation
plugin('queryFormPhrase', function(options) {
  var g = query_form_phrase;
  var gA = $.authoring();
  var queryFormSettings = $.extend({}, g.defaults, options || {});
	
	var queryPhrase = $.extend({
		holder: null,
		tools: null,
		state: null,
		config: queryFormSettings.config,
		reload: nop,
		gridId: queryFormSettings.gridId,
		render: null
	}, queryFormSettings.extendView || {});
	if (queryFormSettings.viewnav) {
	  queryPhrase.viewnav = g.viewNavManager(queryPhrase, {
	    phrase: queryPhrase.phrase,
			name: 'div',
			viewnav: queryFormSettings.viewnav,
			className: 'viewnav',
			node: queryFormSettings.node.div().addClass('view-nav-area')
	  });
	}
	else {;
	  if (gA.meta.view && queryPhrase.caption) {
      var text = (gA.meta.view.name == 'default' && gA.meta.view.title == 'Page') 
        ? 'Current' : gA.meta.view.title;
      queryPhrase.caption(caption(text));
    }
	}
	
	queryPhrase.node = queryFormSettings.node;
	queryPhrase.filtering = g.filterManager(queryFormSettings, queryPhrase, {
		node: queryPhrase.node,
		className: 'filter item '
	});
	
	/*

	if (opt.mode) {
		queryPhrase.mode = new modeManager(queryPhrase, {
			phrase: queryPhrase.phrase,
			name: 'span'
		});
		queryPhrase.state.mode = queryPhrase.mode.state;
	}
	
	if (opt.range) {
	  var rangeOptions = $.extend({}, {nextprev: true}, opt.range);
		if (queryPhrase.config.range) {
			queryPhrase.range = new rangeManager(queryPhrase, {
				phrase: queryPhrase.phrase,
				name: 'span',
				nextprev: rangeOptions.nextprev
			});
			queryPhrase.state.range = queryPhrase.range.state;
			queryPhrase.state.increment = queryPhrase.range.increment.state;
		}
	}
	
	*/
	
	queryPhrase.gridParamsReady = function() {  
    if (!queryPhrase.initialized) {
      queryPhrase.initialized = true;
	  }
	}
	
	return queryPhrase;
	
	function caption(text) {
    return text + ' ' + queryPhrase.config.handler.titleCapitalized;
  }
	
	function resetManager(view, options) {
		me = this;
		this.Phrase;
		options = $.extend({}, defaults, options);
		var holder = options.node ? options.node : view.tools;
		var phrase = holder.phrase({
			name: options.name,
			className: 'reset item',
			load: function() {
				me.Phrase = this;
				var reset = $('<a href="#reset">Reset</a>');
				reset.click(function() {
				
				  return false;
				});
				this.node(reset);
			}
		});
	}
	
	function modeManager(view, options) {
		var me = this;
		options = $.extend({}, defaults, options);
		var holder = options.node ? options.node : view.tools;
		this.current = null;
		this.range = null
		this.titleChange = null;
		this.state = $('<span class="state-mode" />');
		this.Phrase;
		
		var phrase = holder.phrase({
			name: options.name,
			load: function() {
				me.Phrase = this;
				this.text('Change view to');
				this.link('View', '#view', 'change-view', function() {
					$.superGrid(opt.gridId).changemode({mode: view.config.mode});
				}).semicolon();
				me.titleChange = $('.change-view', phrase);
			}
		});
		
		this.update = function(mode) {
			if (mode == "calendar") {
				if (!view.viewedInGrid) {
					view.viewedInGrid = true;
					view.config.loadRequired = true;
				}
			}
			view.config.mode = mode;
			switch(view.config.mode) {
				case "calendar":
					me.state.html(' grid');
					me.titleChange.html('list');
					break;
				case "list":
					me.state.html(' list');
					me.titleChange.html('grid');
					break;
			}
		}
	}
});

// ***
// jquery.range-picker.js
/*
rangePicker plugin
Can be configured to be a range picker, date picker, or date/time picker
Settings:
load - function to call 
*/

var range_picker_globals = {
  KEY: 'range-picker',
	range_default_settings: {
		change: nop,
		validation: nop,
		select: nop,
		refresh: nop,
		time: false,
		presetButtons: true,
		label: 'Select Date Range',
		type: 'range', // range | date | time
		mode: 'dialog' // dialog | dialog2 | inline
	},
	picker_default_settings: {
		label: 'Select Date',
		time: false,
		presetButtons: true,
		stayCurrent: false
	},
	pickertime_default_settings: {
		label: 'Select Date and Time',
		time: true,
		presetButtons: true
	},
	pickerjusttime_default_settings: {
		label: 'Select Time',
		time: true,
		presetButtons: false
	},
	timeSliderDefaults: {
      clockIcon: 'images/icon_clock_2.gif',
      orientation: 'vertical'
  },
	hourSliderDefaults: {
      length: 24,
      interval: 1,
      defaultValue: 0,
      id: 'hourSlider',
      label: 'hour'
  },
  minuteSliderDefaults: {
      length: 60,
      interval: 5,
      defaultValue: 0,
      id: 'minSlider',
      label: 'min'
  },
  secondSliderDefaults: {
      length: 60,
      interval: 5,
      defaultValue: 0,
      id: 'secSlider',
      label: 'sec'
  }
}
 
plugin("rangePicker", function(id, settings) {
	var target = $(this);
	var dp = range_picker_globals;
	var instance = target.data(dp.KEY);
	if (!instance) {
	  var type = settings.type ? settings.type : dp.range_default_settings.type;
    var mode = settings.mode ? settings.mode : dp.range_default_settings.mode;
	  instance = new Picker(target, type, mode, settings);
	  target.data(dp.KEY, instance);
	}
	else {
	  instance.open();
	}
	return instance;
	
	function Picker(jnode, type, mode, settings) {
		var me = this;
		var sheetManager;
		var picker;
		var width;
		switch (type) {
			case 'range':
				width =  (settings.time) ? '525px' : '385px';
				settings = $.extend({}, dp.range_default_settings, settings);
				break;
			case 'date':
				width = '200px';
				settings = $.extend({}, dp.picker_default_settings, settings);
				break;
			case 'datetime':
				width = '280px';
				settings = $.extend({}, dp.picker_default_settings, 
					dp.pickertime_default_settings, settings);
				break;
			case 'time':
				width = '280px';
				settings = $.extend({}, dp.picker_default_settings, 
					dp.pickerjusttime_default_settings, settings);
				break;
		}
		var id = guid();
		switch (mode) {
			case 'dialog':
			case 'dialog2':
					jnode.sheet({
					  mode: mode,
						width: width,
						title: settings.title,
						load: function(done, manager) {
						  sheetManager = manager;
							var holder = this.div().addClass('range-picker-holder');
							switch (type) {
								case "range":
									picker = new rangePicker(holder, id, settings);
									break;
								case "time":
						      picker = new timePicker(holder.div().addClass('time-picker'), settings);
						      break;
								default:
									picker = new datePicker(holder.div().addClass('date-picker'), id, settings);
									break;
							}
							holder.phrase({
								load: function() {
									this.button('CANCEL', function() {
										manager.close();
									});
									this.button('OK', 'ok', function(done) {
										manager.enabled(false);
										if (picker.validate()) {
											manager.close();
											settings.select(picker.data());
										}
										if (done) done();
									}, true);
									if (settings.clickPick) {
									  picker.ok = $('button.ok', this.getPhraseNode());
									}
								}
							});
						},
            close: function() {
              settings.close && settings.close();
            },
            //volatile: true,
						open: function() {
							if (settings.refresh) {
								var data = settings.refresh();
								if (data) {
									picker.refresh(data);
								}
							}
						}
				});
				break;
			default:
				switch (type) {
					case "range":
						picker = new rangePicker(jnode, id, settings);
            if (settings.refresh) {
              var data = settings.refresh();
              if (data) {
                picker.refresh(data);
              }
            }
						break;
					case "time":
						picker = new timePicker(jnode, settings);
						break;
					default:
						picker = new datePicker(jnode, id, settings);
						break;
				}
				break;
		}
		
		this.open = function() {
		  sheetManager && sheetManager.open();
		}
	}

	function rangePicker(jnode, id, settings) {
		var me = this;
		var table = jnode.table('class="table-range-picker"');
		table.row();
		var startId = "range-start-" + id;
		var endId = "range-end-" + id;
		table.cell('<div id="' + startId + '" class="date-picker date-area" />');
		table.cell('<div id="' + endId + '" class="date-picker date-area" />');
		table.render();
		picker_settings = dp.picker_default_settings;
		picker_settings.time = settings.time;
		picker_settings.label = "Start";
		var startPicker = new datePicker($("#"+startId), 
			startId, picker_settings,
			null
			/*
			function(date) {
				console.info('startpicker-callback', date, endPicker.date.value);
				if (endPicker.date && endPicker.date.value) {
					if (date.getTime() > endPicker.date.value.getTime()) {
						endPicker.date.update(date, true, true, false);
					}
				}
			}
			*/
			);
		picker_settings.label = "End";
		var endPicker = new datePicker($("#"+endId), 
			endId, picker_settings,
			null
			/*
			function(date) {
				console.info('endpicker-callback', date, startPicker.date.value);
				if (startPicker.date && startPicker.date.value) {
					if (date.getTime() < startPicker.date.value.getTime()) {
						startPicker.date.update(date, true, true, false);
					}
				}
			}
			*/
			);
		this.data = function() {
			var r = {rangeincrement: {}};
			r.start = me.range.start();
			r.end = me.range.end();
			r.rangeincrement.mode = -1;
			return r;
		}
		this.range = { 
			start: function() { return startPicker.date.value; }, 
			end: function() { return endPicker.date.value; }
		};
		
		this.change = function(start, end) {
			startPicker.date.update(start, true, true);
			endPicker.date.update(end, true, true);
		}
		this.refresh = function(range) {
			me.change(range.start, range.end);
		}
		var errorConsole = jnode.errorConsole();
		
		this.validate = function() {
			errorConsole.clear();
			validateRange();
			if (errorConsole.hasErrors()) {
				return false;
			}
			return true;
		}
		
		function validateRange() {
			var startDate = startPicker.date.value;
			var endDate = endPicker.date.value;
			if (startDate > endDate) {
				errorConsole.Add("Start date > end date");
			}
		}
	}
	
	/*
	******************************************************************************
	* Date Picker
	******************************************************************************
	*/
	function datePicker(srcNode, id, settings, callback) {
		var me = this;
		var dateHolder = srcNode;
		var time;
		var label;
		
		if (settings.label) {
			srcNode.div().addClass("label").html(settings.label);
		}
		if (settings.time) {
			srcNode.addClass("date-time-area");
			label = {
				position: "before",
				text: "Date"
			};
		}
		else {
			srcNode.addClass("date-area");
		}
		
		var inputArea = dateHolder.div().addClass("inputs");
		var hintArea = dateHolder.div().addClass("hints");
		var buttonArea = dateHolder.div().addClass("buttons");
		
		var input = inputArea.form("input", 
			{
				type: "text", 
				name: "specificDate", 
				className: "date"
			},
			label);
		var hint = hintArea.div().addClass("hint");
		input.focus(function() {
			hintArea.show();
			validateDate(false);
		});
		input.blur(function() {
			validateDate(true);
			hintArea.hide();
		});
		input.keyup(function() {
			validateDate(false);
		});
		this.validate = function() {
			return true;
		}
		this.data = function() {
			return me.date.value;
		}
		this.refresh = function(date) {
			if (!date.getTime) {
				date = $.dates.parse(date);
			}
			me.date.update(date, true, true, false);
		}
		this.date = {
			value: null,
			update: function(date, changeGrid, selected, noTimeUpdate) {
			  var prevDate = me.date.value;
				me.date.value = date;
				input.val($.dates.shortdate(me.date.value));
				title.text($.dates.format(date, 'MMM yyyy'));
				if (changeGrid) {
					var params = {};
					calendar_grid_globals.createMonthRange(date, 1, params);
					grid.change(params);
				}
				
				if (time) {
				  if (noTimeUpdate) {
				    me.date.value.setHours(prevDate.getHours());
				    me.date.value.setMinutes(prevDate.getMinutes());
				  }
				  else {
				    time.update(me.date.value);
				  }
				}
				
				if (selected) {
					current(me.date.value);
				}
				//if (callback) callback(date);
			},
			updateTime: function(hours, minutes) {
				var updateGrid = false;
				cur = me.date.value;
				if (!cur) {
					cur = new Date();
					updateGrid = true;
				}
				cur.setHours(hours);
				cur.setMinutes(minutes);
				cur.setSeconds(0);
				me.date.update(cur, updateGrid, true, true);
			}
		};
		
		if (!settings.time) {
			buttonArea.pushButton("today", 
				{
					container: "none",
					className: "",
					mode: "unconnected",
					buttons: [
						{
							title: "Today",
							className: "today",
							selected: function() {
								me.date.update(calendar_grid_globals.zeroDate(new Date()), true, true);
							}
						}
					]
				});
		}
		
		hintArea.hide();
		dateHolder.append(inputArea).append(hintArea).append(buttonArea);
		
		if (settings.time) {
			time = new timePicker(srcNode, 
				{
					inputArea: inputArea,
					hintArea: hintArea,
					buttonArea: buttonArea,
					dateUpdate: me.date.update,
					timeUpdate: me.date.updateTime,
					presetButtons: settings.presetButtons,
					position: 'bottom'
				});
		}
		
		var title = $('<button type="button" class="title" />');
		title.click(function() {
			var yearPicker = dateHolder.data("YearMonthPicker");
			if (yearPicker) {
				yearPicker.show();
			}
			else {
				yearPicker = new yearMonthPicker(dateHolder, 
					pickerGrid,
					function(date) {
						change(date);
						settings.stayCurrent && setInput(date);
					}); 
				dateHolder.data("YearMonthPicker", yearPicker);
			}
		});
		
		var pickerGrid = dateHolder.div().addClass("date-picker-grid");
		
		pickerGrid.click(function() {
			title.focus();
			hintArea.hide();
		});
		
		function onChangeSetDate(params) {
		  setInput(params.start);
		}
	
		pickerGrid.pushButton("navigation", 
			{
				container: "table",
				className: "navigation",
				mode: "unconnected",
				buttons: [
					{
						title: "",
						className: "previous",
						selected: function() {
							grid.change(-1, settings.stayCurrent ? onChangeSetDate : null);
						}
					},
					{
						title: title,
						name: "div",
						className: "title"
					},
					{
						title: "",
						className: "next",
						selected: function() {
							grid.change(1, settings.stayCurrent ? onChangeSetDate : null);
						}
					}
				]
			});
		
		var grid = pickerGrid.calendarGrid('calendar-'+id, {
			load: function(params) {
				//current(params.start, this);
				gridCells = this;
				title.text($.dates.format(params.start, 'MMM yyyy'));
				$(".calendar-grid .day", dateHolder).each(function() {
					$(this).hover(
						function() {
							$(this).parent().addClass("mouse-over");
						},
						function() {
							$(this).parent().removeClass("mouse-over");
						}
					);
				});
			},
			select: function(date) {
				//if (settings.validation) settings.validation(date);
				me.date.update(date, false, true, true);
				me.ok && me.ok.click();
			},
			labels: 'abbr'
		});
		
		var gridCells;
		var curCell;
		function current(date, gridObj) {
			if (gridObj) {
				gridCells = gridObj;
				date = me.date.value ? me.date.value : date;
			}
			if (curCell) {
				curCell.removeClass("current");
			}
			curCell = gridCells.cell(date);
			if (curCell) {
				curCell = curCell.parent().addClass("current");
			}
		}
		
		function change(date) {
			var params = {};
			calendar_grid_globals.createMonthRange(date, 1, params);
			grid.change(params);
		}
		
		function setInput(date) {
			if (date != null) {
				me.date.update(date, true, true, false);
			}
			else {
				input.val("");
			}
		}
		function setHint(value) {
			hint.html(value);
		}
		
		function validateDate(updateInput) {
			var result = parse(input.val());
			if (updateInput) {
				if (result) {
					setInput(result);
				}
				else {
					setInput();
				}
			}
			else {
					setHint(result 
						? gets(result)
						: ((input.val().length > 0) ? "is this a valid date?" : ""));
			}
			return true;
		}
		
		function gets(date) {
			var value = "";
			value = (date.getMonth() + 1)
				+ "/"
				+ date.getDate()
				+ "/"
				+ date.getFullYear();
			return value;
		}
		
		function parse(value) {
			var newDate = null;
			if (value.length == 0) {
				return null;
			}
			var match = value.match(/^\s*(\d+)\s*((\/)?\s*(\d+))?\s*((\/)?\s*(\d+)?)?\s*$/i);
			if (match) {
				newDate = new Date();
				var month = parseInt(match[1])-1;
				var day = match[4] ? parseInt(match[4]) : newDate.getDate();
				var year = match[7] ? parseInt(match[7]) : newDate.getFullYear();
				try {
					if (year > 99) {
						newDate.setFullYear(year, month, day);
					}
					else {
						newDate.setYear(year, month, day);
					}
					newDate.setHours(0);
					newDate.setMinutes(0);
					newDate.setSeconds(0);
				}
				catch (e) {
					newDate = null
				}
			}
			return newDate;
		}
	}
	
	/*
	******************************************************************************
	* Year/Month Picker
	******************************************************************************
	*/
	function yearMonthPicker(srcNode, posNode, select) {
		
		var offset = posNode.position();
		var holder = srcNode.parent().div().addClass('year-picker-holder');
		holder.css({
			position: "absolute",
			left: offset.left+"px",
			top: offset.top+"px"
			});
		holder.height(posNode.height());
		holder.width(posNode.width());
		var cells = {};
		var date = new Date();
		var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
		holder.pushButton("navigation", 
			{
				container: "table",
				className: "navigation",
				mode: "unconnected",
				buttons: [
					{
						title: "",
						className: "previous",
						selected: function() {
							date.setFullYear(date.getFullYear()-1);
							title.text(yearTitle(date));
						}
					},
					{
						title: yearTitle(date),
						//name: "div",
						className: "title",
						selected: function() {
							select(new Date(date.getFullYear(), 0, 1));
							close();
						}
					},
					{
						title: "",
						className: "next",
						selected: function() {
							date.setFullYear(date.getFullYear()+1);
							title.text(yearTitle(date));
						}
					}
				]
			});
		var title = $(".navigation .title", holder);
		var CELL = ['<div id="', 1, '" class="cell">', 3, '</div>'];
		var table = holder.table('class="year-picker-grid"');
		table.row();
		for (var i = 0; i < months.length; i++) {
			var cell_id = CELL[1] = guid();
			cells[cell_id] = i;

			CELL[3] = months[i];
			table.cell(CELL.join(''));
			if ((i % 3) == 2) {
				table.row();
			}
		}
		table.render();
		
		$(".year-picker-grid .cell", holder).each(function() {
			$(this).click(function(ev) {
				var month = cells[this.id];
				select(new Date(date.getFullYear(), month, 1));
				close();
			});
			$(this).hover(
				function() {
					$(this).parent().addClass("mouse-over");
				},
				function() {
					$(this).parent().removeClass("mouse-over");
				}
			);
		});
		this.show = function() {
			holder.fadeIn(200);
		}
		
		function close() {
			holder.fadeOut(200);
		}
		
		function yearTitle(date) {
			return $.dates.format(date, "yyyy");
		}
	}
	
	/*
	******************************************************************************
	* Time Picker
	******************************************************************************
	*/

	function timePicker(holder, options) {
		var me = this;
		var hint = null;
		var input = null;
		var specific = null;
		var timeSliderArea;
		
		this.update = function(date) {
			set(date, true);
		}
		var inputArea;
		if (options.mode == 'inline') {
		  inputArea = holder;
		}
		else {
		  if (!options.inputArea) {
		    options.inputArea = holder.div().addClass("inputs");
		  }
		  if (!options.hintArea) {
		    options.hintArea = holder.div().addClass("hints");
		    options.hintArea.div().addClass("hint");
		  }
		  options.buttonArea || (options.buttonArea = holder.div().addClass("buttons"));
		  inputArea = options.inputArea;
		}
    var timeArea = inputArea.dom('span').addClass('time-area');
    
		var input = timeArea.form("input", 
			{
				type: "text", 
				name: "specificTime", 
				className: "time"
			}, null);
	  if (options.readonly) {
	    input.attr('readonly', 'true');
	  }
		if (options.value) {
		  setInput(options.value.hours, options.value.minutes, true);
		}
	  
	  range_picker_globals.hourSliderDefaults.update = function(ev, ui) {
	    var hoursminutes = parse(input.val());
	    setInput(ui.value, hoursminutes.minutes, true);
	  }
	  range_picker_globals.minuteSliderDefaults.update = function(ev, ui) {
	    var hoursminutes = parse(input.val());
	    setInput(hoursminutes.hours, ui.value, true);
	  }
	  
		timeSliderArea = timeSlider(timeArea, input,
	        [
	          range_picker_globals.hourSliderDefaults,
	          range_picker_globals.minuteSliderDefaults
          ]);
		timeArea.hover(
		  function() {
		    var hoursminutes = parse(input.val());
		    $('.hourSlider', timeSliderArea).slider('option', 'value', hoursminutes.hours);
		    $('.minSlider', timeSliderArea).slider('option', 'value', hoursminutes.minutes);
		    timeSliderArea.show();
		    position(timeSliderArea, input, options.position);
      },
      function() {
        timeSliderArea.hide();
      }
    );
    if (options.mode != 'inline') {
		  var hint = $('div:first', options.hintArea);
		  input.focus(function() {
			  options.hintArea.show();
			  validateTime(false);
		  });
		  input.blur(function() {
			  validateTime(true);
			  options.hintArea.hide();
		  });
		  input.keyup(function() {
			  validateTime(false);
		  });
  	
	    if (options.presetButtons) {
		    var choices = options.buttonArea.pushButton("choices", 
			    {
				    container: "none",
				    mode: "disconnected",
				    buttons: [
					    {
						    title: "Now",
						    className: "now",
						    selected: function() {
							    var now = new Date();
							    if (options.dateUpdate) {
								    options.dateUpdate(now, true, true);
							    }
							    set(now, true);
						    }
					    },
					    {
						    title: "Start of day",
						    id: "start",
						    className: "button start",
						    selected: function() {
							    setInput(0, 0, true);
						    }
					    },
					    {
						    title: "End of day",
						    id: "end",
						    className: "button end",
						    selected: function() {
							    setInput(23, 59, true);
						    }
					    }
				    ]
			    });
	    }
	  }
			
	  function timeSlider(area, input, data) {
	    var SLIDER_KEY = 'time-slider';
	    var slider = area.data(SLIDER_KEY);
	    if (!slider) {
        slider = $.div().addClass('sliderArea');
        $.each(data, function(i, item) {
          var s = $('<div class="sliderItem"><div class="label">' + item.label + '</div> <p class="' + item.id + '"></p></div>');
          createSlider(s.find('p'), item); 
          slider.append(s);
        });
        area.data(SLIDER_KEY, slider);
        area.append(slider);
        slider.hide();
      }
      return slider;
    }
    
    function position(node, relativeToNode, mode) {
      var pos = relativeToNode.position();
      switch(mode) {
        case 'left':
          break;
        case 'bottom':
          var left = pos.left;
          var top = pos.top + relativeToNode.outerHeight();
          break;
        case 'right':
        default:
          var left = pos.left +  relativeToNode.outerWidth();
          var top = pos.top;
          break;
      }
      if ($.browser.msie){
        top += 2;
      }
      node.css({left: left+'px', top: top+'px'});
    }

    function createSlider(holder, item) {
      holder.slider({
          orientation: range_picker_globals.timeSliderDefaults.orientation,
          range: "min",
          min: 0,
          max: item.length - item.interval,
          value: item.defaultValue,
          step: item.interval,
          animate: true,
          slide: item.update       
      });
    }
		
		function setInput(hours, minutes, update) {
			if (hours != null) {
				if (options.timeUpdate && update) {
					options.timeUpdate(hours, minutes);
				}
				input.val(gets(hours, minutes));
			}
			else {
				input.val("");
			}
		}
		
		function setHint(value) {
			hint.html(value);
		}

		function validateTime(updateInput) {
			var result = parse(input.val());
			if (updateInput) {
				if (result) {
					setInput(result.hours, result.minutes, true);
				}
				else {
					setInput();
				}
			}
			else {
					setHint(result 
						? gets(result.hours, result.minutes)
						: ((input.val().length > 0) ? "is this a valid time?" : ""));
			}
			return true;
		}
			
		function set(time, update) {
			if (update || (input.val().length == 0)) {
				var sod = new Date(time.getFullYear(), time.getMonth(), time.getDate());
				var eod = (new Date(sod.getTime() + 86340000)).valueOf();
				var sod = sod.valueOf();
				var value = time.valueOf();
				var hours = time.getHours();
				var minutes = time.getMinutes();
				var seconds = time.getSeconds();
				setInput(hours, minutes, update);
			}
		}
		
		
		function gets(hours, minutes) {
			var value = "";
			var ampm = " AM";
			var adjustedHours;
			if (hours == 0) {
				adjustedHours = "12"
			}
			else if (hours < 12) {
				adjustedHours = hours;
			}
			else if (hours == 12) {
				adjustedHours = hours;
				ampm = " PM";
			}
			else if (hours > 12) {
				adjustedHours = (hours - 12);
				ampm = " PM"
			}
			if (adjustedHours <= 9) {
			  value += '0';
			}
			value += adjustedHours + ":";
			if (minutes <= 9) {
				value += "0"
			}
			value += minutes + "";
			return value + ampm;
		}
		function parse(value) {
			if (value.length == 0) {
				return {hours: 12, minutes: 0}
			}
			var match = value.match(/^\s*0*(\d+)\s*(\:\s*0*(\d+))?\s*([a|p]m?)?\s*$/i);
			if (match) {
				var hours = parseInt(match[1]);
				var minutes = match[3] ? parseInt(match[3]) : 0;
				var ampm = match[4];
				if (!isNaN(hours) && hours < 25) {
					if (!isNaN(minutes)) {
						if (minutes < 60) {
							if (ampm) {
								var ispm = ampm.toUpperCase().charAt(0) == "P";
								if (ispm) {
									if (hours != 12) hours += 12;
								}
								else if (hours == 12) hours = 0;
								if (hours < 25) {
									return { hours: hours, minutes: minutes }
								}
							}
							else {
								return { hours: hours, minutes: minutes }
							}
						}
					}
				}
			}
			return null;
		}
	}
});

// ***
// jquery.search-picker.js
// based on sheet, listGrid and spinner, provides a search dialog box

var global_search = {
	SEARCH_KEY: 'search_sheet',
	defaults: {
		title: "Search",
		renderer: null,
		range: null,
		getParams: null
	},
	params: {
		config: null
	}
};

// TODO(dglazkov): add documentation
plugin('searchPicker', function(settings) {
	var g = global_search;
	var target = $(this);
	settings = $.extend({}, g.defaults, settings);
	var search = target.data(g.SEARCH_KEY);
	if (!search) {
		search = new searchSheet(target, settings);
		target.data(g.SEARCH_KEY, search);
	}

	var rangeButtons;
	var searchResults;
	var searchGom;
	var errorConsole;
	var searchText;
	var searchForm;
	
	function searchSheet(trigger, settings) {
		trigger.click(function() {
			trigger.sheet({
			  SHEET_KEY: 'search',
				width: "650px",
				title: settings.title,
				load: function(done, smanager) {
					g.params.config = settings.config;
					settings.close = smanager.close;
					searchHandler($(this), settings);
				},
				open: function() {
					if (settings.getParams) {
						g.params = settings.getParams();
						if (rangeButtons) {
							rangeButtons.updateButtonText('userange', rangeButtonTitle());
						}
					}
				},
				ok: function() {
				},
				close: function() {
					//searchClear(true);
				}
			});
			return false;
		});
	};
	
	function rangeButtonTitle() {
		return "between " + g.params.config.range.start.value.toString("M/d/yyyy")
				+ " and " + g.params.config.range.end.value.toString("M/d/yyyy");
	};
	
	function searchHandler(searchArea, settings) {
		searchArea.addClass('search-picker');
		searchForm = searchArea.dom('form');
		var getter = searchForm.div().addClass("searchGetter");
		getter.div().addClass("words").text("Search for ");
		searchText = getter.form("input", {type: "text", name: "searchText", className: 'text'});
		getter.div().addClass("words").text(" in ");
		var useRange = !g.params.config.range.isEmpty;
		if (useRange) {
			rangeButtons = getter.pushButton("range", 
				{
					buttons: [
						{
							on: true,
							id: 'userange',
							title: rangeButtonTitle(),
							selected: function() {
								useRange = true;
							}
						},
						{	
							id: 'useall',
							title: "all " + settings.renderer.title,
							selected: function() {
								useRange = false;
							}
						}
					]
				});
		}
		else {
			getter.div().addClass("all").text("all " + settings.title);
		}
		errorConsole = $(getter).errorConsole();
		var submit = getter.dom("button")
			.attr("name", "submit")
			.text("Search");
		$(submit).click(function() {
			submitSearch(searchArea, settings, useRange);
			return false;
		});
		
		getter.div().addClass("float-clear");
		searchResults = searchArea.div().addClass("searchResults");
	};
	
	function searchClear(clearInput) {
		errorConsole.clear();
		if (clearInput) searchText.val('');
	}
	
	function submitSearch(searchArea, settings, useRange) {
		searchClear(false);
		var searchVal = searchText.val();
		if (!searchVal || searchVal.length == 0) {
			errorConsole.Add("Enter search value");
		}
		if (errorConsole.hasErrors()) {
			return;
		}
		var config = g.params.config;
		config.search.value.set(searchVal);
		searchResults.spinner({text: "Searching..."});
		$.ajah(config.postUrl({range: useRange, search: true}), null,
			function() {
			  var handler = settings.handler;
				var results = $(handler.selector, this);
				if (results && results.length > 0) {
				  settings.gridChange($(this), function() {
				    settings.close();
				  });
				  settings.state(searchVal);
				}
				else {
					errorConsole.Add('No ' + handler.title + ' found matching search criteria');
				}
				searchResults.spinner().remove();
			}
		);
	};
});
// ***
// jquery.sheet.email-default.js
// implements 'email default' dialog box

$.presentationHelper.behaviors.sheet_emaildefault = function(params) {

  var g = $.authoring();
      
  var o = $.extend({
    title: 'Email Default',
    key: 'email-default',
    className: 'email-default',
    width: 410,
    status: null,
    custom: null,
    emailform: null,
    slotClass: 'slate',
    handler: function(form) {
      var o = {
        to: $('input[name=to]', form).val(),
        subject: $('input[name=subject]', form).val(),
        body: $('textarea[name=body]', form).val()
      };
      return o;
    },
    load: function(end, manager) {
      var status = manager.status;
      var container = $(this);
      var url = g.url.email + '?id=' + o.key + '&pid=' + g.meta.identifier;
      $.ajah(url, function() {
        var html = $('#' + o.key, this);
        if (html.length > 0) {
          process(html);
        }
      });
       
      function process(html) {
        var emailform = html.find('form');
        manager.addoptions({emailform: emailform});
        html.addClass(o.className);
        o.custom && o.custom(html);
      
        $('button[type=submit]', html)
          .append('<span class="loading" style="display:none;"><b></b></span>')
          .click(function() {
            var b = $(this);
            status.clear();
            b.attr('disabled', 'disabled');
            $('.loading', b).show();
            var params = o.handler($('form', html));
            if (o.validate) {
              if (!o.validate(o.status, params)) {
                processingOff(b);
                return false;
              }
            }
            var url = $('form', html).attr('action');
            $.ajah(url, params, function() {
              var result = $('.result', this);
              if (result.length > 0) {
                emailform.hide();
                status.add(result);
              }
              processingOff(b);
            });
            return false;
          });
        container.append(html);
        end();
        
        function processingOff(b) {
          b.attr('disabled', '');
          $('.loading', b).hide();
        }
      }
      return true;
    },
    open: function(options) {
      options.emailform && options.emailform.show();
      options.status && options.status.clear();
    }
  }, params || {});
  return o;
  
};
// ***
// jquery.sheet.email-owner.js
// implements 'email owner' dialog box

$.presentationHelper.behaviors.sheet_emailowner = function() {

  var o = {
    title: 'Email Content Owner',
    key: 'email-owner',
    className: 'email-owner',
    width: 410,
    handler: function(form) {
      var o = {
        name: $('input[name=name]', form).val(),
        email: $('input[name=email]', form).val(),
        body: $('textarea[name=body]', form).val()
      };
      return o;
    },
    custom: function(node) {
      $('textarea[name=body]', node).attr('rows', '5')
          .val('Link to page:\n' + location.href);
    },
    validate: function(status, params) {
      if (params.name.length == 0) {
        status.error('Enter your name.');
        return false;
      }
      if (params.email.length == 0) {
        status.error('Enter your email address.');
        return false;
      }
      if (params.body.length == 0) {
        status.error('Enter body.');
        return false;
      }
      return true;
    }
  };
  return $.presentationHelper.behaviors.sheet_emaildefault(o);
};

// ***
// jquery.sheet.email-page.js
// implements 'email page' dialog box

$.presentationHelper.behaviors.sheet_emailpage = function() {
    
  var o = {
    title: 'Email Page',
    key: 'email-page',
    className: 'email-page',
    width: 410,
    handler: function(form) {
      var o = {
        to: $('input[name=to]', form).val(),
        //subject: $('input[name=subject]', form).val(),
        name: $('input[name=name]', form).val(),
        email: $('input[name=email]', form).val(),
        body: $('textarea[name=body]', form).val()
      };
      return o;
    },
    custom: function(node) {
      $('textarea[name=body]', node).attr('rows', '5')
          .val('Link to page:\n' + location.href);
    },
    validate: function(status, params) {
      if (params.to.length == 0) {
        status.error('Enter "To" email address.');
        return false;
      }
      if (params.name.length == 0) {
        status.error('Enter your name.');
        return false;
      }
      if (params.email.length == 0) {
        status.error('Enter your email address.');
        return false;
      }
      if (params.body.length == 0) {
        status.error('Enter body.');
        return false;
      }
      return true;
    }
  };
  return $.presentationHelper.behaviors.sheet_emaildefault(o);
};
// ***
// jquery.sheet.js
// represents a modal, draggable dialog box. Only one instance of the box exists
// the content in the box is placed into slots, and each slot is activated by
// a trigger: a DOM node that calls sheet.

// only one slot can be visible at a time. On each opening of the sheet, the
// inactive sheets are hidden, and the active sheet is made visible.

    
// TODO(dglazkov): hook up to the resize event to make sure overlay always
//                 covers the entire page
// TODO(dglazkov): implement resizeable dialog box
// TODO(dglazkov): make title shared across sheets, and then make it the
//                 handle for dragging (eliminates the problem with the
//                 scrollbars)
// TODO(dglazkov): finish implementing manager logic: loading, open


// node.sheet(options) is the only valid call for this plugin. Each call does
// the following:
// * identifies the trigger node, or the node at which the 'sheet' method was
//   called
// * if this is a first-time call, remembers the trigger and allocates a slot
//   for it. Then, calls 'load' hook. The method is be used to populate the
//   sheet.
// * otherwise, opens the slot for the trigger.
// * calls 'open' hook
// * if the user closes the sheet by clicking on 'ok' button, calls 'ok' hook
// * if the user closes the sheet by clicking on 'close' button, calls 'cancel'
//   hook


var global_sheet = {
  animate: 100,
  defaults: {
    SHEET_KEY: 'sheet',
    dialog: true,
    resizeable: false,
    // default width of the dialog box
    width: 300,
    // estimated height of the dialog box
    height: null,
    // called when the slot is loading
    // * this -- slot jQuery node
    // * end -- load completion callback.
    // * manager -- a manager object, which has the following methods:
    //    * close() -- closes the sheet
    //    * loading(on) -- if on=true, sets the sheet into "loading"
    //                         state, otherwise, returns back to normal state
    //    * enable(on) - if on=true, enables the bay area of the sheet,
    //                   otherwise disables the bay are of the sheet. The sheet
    //                   is always re-enabled on open.
    //    * open() -- opens the sheet
    // * returns 'false' (or nothing), if the loading is synchronous. If the
    //   loading is asynchronous, the plugin does not consider it being complete
    //   until the 'end' is called
    load: nop,
    // called when dialog box is opened
    open: nop,
    // called when dialog box is closed
    // * this -- slot jQuery node
    // * waiting -- true, if the slot was still loading when the dialog box
    //   closed
    close: nop,
    // called when dialog box is enabled or disabled
    // * this -- slot jQuery node
    // * on -- true, if it's being disabled, false otherwise
    enable: nop,
    // called when the dialog box is closed
    cancel: nop,
    // called when the 'Ok' button is clicked
    ok: nop,
    // specifies whether to hide the "ok" button
    passive: false,
    // if true, the load hook is called every time the sheet opens
    // for this trigger. In other words, the slot is reloaded on each open
    volatile: false,
    leaveDialog: false,
    over: false,
    slotClass: null,
    description: ''
  }
};

// * options -- sheet options. See 'global_sheet.defaults' for detailed
//   description of all options
plugin('sheet', function(options) {
  var g = global_sheet;

  if (options == 'close') {
    return;
  }
  options = $.extend({}, g.defaults, options || {});

  var dialog, overlay;

  var trigger = $(this);
  // immediately remove focus from trigger
  trigger.blur();
  // sheet object -- all information needed for using the sheet
  var sheet = options.volatile ? null : trigger.data(options.SHEET_KEY);
  //console.info(options.SHEET_KEY, sheet);
  if (sheet == null) {
    options = $.extend({}, g.defaults, options || {});
    var id = $.guid();
    dialog = $(document.body).div()
      .css({ position: 'absolute', display: 'none', top: 0, left: 0 })
      .attr('id', 'sheet-' + id)
      .addClass('sheet-ui');
    if (options.dialog) {
      overlay = $(document.body).div()
        .css({ top: '0', left: '0', position: 'absolute', display: 'none' })
        .attr('id', 'overlay-' + id)
        .addClass('sheet-overlay-ui');
    }
    dialog.css({ width: (isNaN(options.width) ? options.width : (options.width + 'px')) });
    sheet = allocate(dialog);
    sheet.dialog = dialog;
    sheet.overlay = overlay;
    sheet.options = options;
    sheet.options.status = function() {
      var g = {
        error: function(msg) {
          g.add(msg, 'error');
        },
        add: function(msg, type) {
          var className = type ? type : 'message';
          g.clear();
          sheet.status().div().addClass('status-result').addClass(className).html(msg);
        },
        clear: function() {
          sheet.status().empty();
        },
        processing: function(message) {
          if (message) {
            sheet.spinner = sheet.status().spinner({ text: message, hide: false });
          }
          else {
            //sheet.status().spinner().remove();
            sheet.spinner && sheet.spinner.remove() && (sheet.spinner = null);
          }
        }
      }
      return g;
    } ();
    !options.volatile && trigger.data(options.SHEET_KEY, sheet);
  }
  else {
    options = sheet.options;
    dialog = sheet.dialog;
    overlay = sheet.overlay;
  }

  // initalize sheet
  if (!sheet.initialized) {
    // perform one-time initialization things

    sheet.title().html(options.title || trigger.text());
    sheet.description((options.description));
    sheet.close().click(function(evt) {
      this.blur();
      close(options.leaveDialog);
      evt.preventDefault();
    });
    sheet.initialized = true;
  }
  if (!sheet.loaded) {
    load() && open();
    options.volatile || (sheet.loaded = true);
  } else {
    options.enable.call(sheet.slot(), true);
    open();
  }

  // attempts to load (synchronously or asynchronously) the contents of the
  // requested slot
  function load() {
    var slot = sheet.slot();
    // Empty slot right away. This is relevant when the slot is volatile
    slot.empty();
    sheet.manager = {
      trigger: trigger,
      dialog: sheet.dialog,
      //open: nop,
      open: function() {
        open(true);
      },
      close: function(leaveDialog) {
        close(leaveDialog);
      },
      loading: nop,
      enabled: function(on) {
        options.enable.call(slot, on);
      },
      status: options.status,
      addoptions: function(moreOptions) {
        sheet.options = $.extend({}, options, moreOptions || {});
        trigger.data(options.SHEET_KEY, sheet);
      },
      command: sheet.command(),
      width: function(width) {
        dialog.css({ width: (isNaN(width) ? width : (width + 'px')) });
      }
    };
    // invoke load hook with the completion callback
    if (options.load.call(slot, function() {
      var w = waiting();
      // clear wait key
      waiting(0);
      // hide spinner
      sheet.spinner().stop().hide();
      if (!w) {
        // this is actually a synchronous execution
        sheet.loaded = true;
      }
      (w == 1) && open();
      //(w < 2) && open();
    },
    sheet.manager)) {
      if (sheet.loaded) {
        // synchronous loading with callback
        return true;
      }
      // asynchronous loading, will wait for "complete" callback
      waiting(1);
      open();
      // not loaded, advise waiting for callback to complete
      return false;
    }
    // synchronous loading, done
    return true;
  };

  // reads or sets the WAIT_KEY. Valid values:
  // * 0 -- no waiting, everything has loaded
  // * 1 -- waiting for loading to complete, the dialog is open
  // * 2 -- waiting for loading to complete, the dialog is closed
  function waiting(value) {
    if (!arguments.length) {
      return sheet.waiting || 0;
    }
    sheet.waiting = value;
    return value;
  };

  // actually opens the dialog box, makes requested slot visible
  // also sets up overlay
  function open(reopen) {
    var w = waiting();
    // step back to waiting=1 so that "open" is called by the async callback
    w > 1 && waiting(1);
    var subject = w ? sheet.spinner() : sheet.bay();
    // cover the entire page
    // open dialog box and requested slot
    overlay && overlay.css({ 'z-index': max_z(), width: width(), height: height() }).show();
    dialog.css('z-index', max_z()).add(sheet.holder).show();
    subject.show();

    $.nodePositioner(dialog, trigger, { over: options.over });

    w || options.open.call(sheet.slot(), options, sheet.manager);
    reopen || (w = 1);
  };

  // creates dialog position CSS object
  function position() {
    var pos = trigger.offset();
    var maxw = document.documentElement.scrollWidth;
    if (pos.left + options.width > maxw) {
      pos.left = Math.max(0, maxw - options.width - 20);
    }
    var maxh = document.documentElement.scrollHeight;
    if (pos.top + options.height > maxh) {
      pos.top = Math.max(0, maxh - options.height - 20);
    }
    return { 'z-index': max_z(), top: pos.top, left: pos.left };
  };

  // closes the dialog
  function close(leaveDialog) {
    var w = waiting();
    if (w) {
      w = waiting(2);
    }
    if (leaveDialog) {
      if (options.volatile) {
        overlay && overlay.remove();
        dialog.remove();
        sheet.holder.remove();
      }
      else {
        if (overlay) {
          sheet.holder.add(overlay).hide();
        }
        else {
          sheet.holder.hide();
        }
      }
    }
    else {
      dialog.fadeOut(g.animate, function() {
        if (options.volatile) {
          overlay && overlay.remove();
          dialog.remove();
          sheet.holder.remove();
        }
        else {
          if (overlay) {
            sheet.holder.add(overlay).hide();
          }
          else {
            sheet.holder.hide();
          }
        }
      });
    }
    options.close.call(trigger, w);
  };

  // width and height taken directly from jquery/ui/ui.dialog.js
  // why work when you can steal?
  function height() {
    if ($.browser.msie && $.browser.version < 7) {
      var scrollHeight = Math.max(document.documentElement.scrollHeight,
                                  document.body.scrollHeight);
      var offsetHeight = Math.max(document.documentElement.offsetHeight,
                                  document.body.offsetHeight);

      if (scrollHeight < offsetHeight) {
        return $(window).height() + 'px';
      } else {
        return scrollHeight + 'px';
      }
    } else {
      return $(document).height() + 'px';
    }
  };

  function width() {
    if ($.browser.msie && $.browser.version < 7) {
      var scrollWidth = Math.max(
        document.documentElement.scrollWidth, document.body.scrollWidth);
      var offsetWidth = Math.max(
        document.documentElement.offsetWidth, document.body.offsetWidth);

      if (scrollWidth < offsetWidth) {
        return $(window).width() + 'px';
      } else {
        return scrollWidth + 'px';
      }
    } else {
      return $(document).width() + 'px';
    }
  };

  // allocates a new holder and supporting chrome
  function allocate(ui, trigger) {
    var holder = ui.div().hide().addClass('holder').html('<div class="top"><div class="close"><a href="#close">Close</a></div><div class="title"></div><div class="sill"></div></div><div class="middle"><div class="description"></div><div class="spinner" style="display:none;"></div><div class="bay" style="display:none"><div class="slot"></div><div class="status"></div><div class="command"></div></div></div><div class="bottom"><div class="handle"></div><div class="spacer"></div><div class="sill"></div></div>');
    holder.attr('id', $.guid());
    if (options.height) {
      holder.find('.slot').css({ height: (isNaN(options.height) ? options.height : (options.height + 'px')) });
    }
    if (options.resizeable) {
      ui.resizable({ alsoResize: '#' + holder.attr('id') + ' .slot' });
    }
    ui.draggable({ handle: 'div.top' })
    return {
      holder: holder,
      trigger: trigger,
      close: function() {
        var close = holder.find('.top .close a');
        this.close = function() { return close; };
        return close;
      },
      slot: function() {
        var slot = holder.find('.slot');
        options.slotClass && slot.addClass(options.slotClass);
        this.slot = function() { return slot; };
        return slot;
      },
      status: function() {
        var status = holder.find('.status');
        this.status = function() { return status; };
        return status;
      },
      command: function() {
        var cmd = holder.find('.command');
        this.cmd = function() { return cmd; };
        return cmd;
      },
      bay: function() {
        var bay = holder.find('.bay');
        this.bay = function() { return bay; };
        return bay;
      },
      title: function() {
        var title = holder.find('.title');
        this.title = function() { return title; };
        return title;
      },
      description: function(text) {
        if (text && text.length) {
          var description = holder.find('.description');
          description.div().addClass('text').html(text);
        }
      },
      spinner: function() {
        var spinner = holder.find('.spinner');
        this.spinner = function() { return spinner; };
        return spinner;
      },
      handle: function() {
        var hanlde = holder.find('.handle');
        this.handle = function() { return handle; };
        return handle;
      }
    };
  };
});
// ***
// jquery.spinner.js
/*
================================================================================
jquery.spinner.js
Provides "in progress" indicators for instances where a page is waiting on
some other operation.  Good for ajax operations.  

================================================================================
*/
var spinner_globals = {
	id: 'spinner_data'
};

plugin('spinner', function(settings) {
  var g = spinner_globals;
  var gA = $.authoring();
  var settings_defaults = {
    cache: false,
    callbacks: null,
    text: "",
    position: 'before', // before | after | over
    hide: true,
    className: null
  };
  // set defaults for non-specified options
  settings = $.extend(settings_defaults, settings);
  var instance = this.data(g.id);
  if (!instance) {
    instance = new Spinner($(this.get(0)), settings.text);
    this.data(g.id, instance);
  }
  instance.open();
  return instance;

  function Spinner(node, text) {
    var zindex = gA.max_z();
    var n = box(node);
    var spinnerHolder;
    switch (settings.position) {
      case 'after':
        spinnerHolder = node.domAfter('div');
        break;
      case 'over':
        spinnerHolder = node.div();
        spinnerHolder.css({
          position: 'absolute',
          top: n.top,
          left: n.left,
          width: n.width,
          height: n.height,
          opacity: 0.80,
          'z-index': zindex
        });
        break;
      default:
        spinnerHolder = node.domBefore('div');
        break;
    }
    spinnerHolder.addClass('e-loading');
    var msg = spinnerHolder.dom('span').addClass('message').html(text).css('z-index', zindex + 1);
    settings.className && msg.addClass(settings.className);
    settings.hide && node.hide();
    //spinnerHolder.fadeIn(200);


    this.open = function() {
      spinnerHolder.fadeIn(200);
    }

    this.node = function() {
      return spinnerHolder;
    }

    this.remove = function() {
      remove();
    };

    this.messageHtml = function(markup) {
      messageArea().html(markup).parent().find('.status-text').remove();
    }

    this.messageText = function(text) {
      messageArea().addClass('status-text').text(text);
    }
    
    function remove() {
      node.removeData(g.id);
      spinnerHolder.remove();
      settings.hide && node.fadeIn(200);
    }

    function messageArea() {
      var msg = spinnerHolder.find('.message');
      var error;
      if (msg.length) {
        msg.remove();
        var n = spinnerHolder;
        n.div().addClass('close-area').dom('span').addClass('close').click(function() {
          //spinnerHolder.hide();
          remove();
        });
        error = n.div().addClass('error');
        n.css({ height: 'auto', opacity: 1 });
      }
      if (!error) {
        error = spinnerHolder.find('.error');
        error.empty();
      }
      return error.div();
    };
  };

  function box(n) {
    var r = $.extend(n.box(), n.offset());
    r.bottom = r.top + r.height;
    r.right = r.left + r.width;
    return r;
  };
});




// ***
// jquery.storage.js
// handles persistent storage
// has two members:
// * session -- represents data, persisted throughout the browser session
// * user -- data, persisted for this user across sessions
// both members have the same three methods:
// * save(name, value) -- associates supplied 'value' and 'name' and stores
//                        them
// * load(name) -- returns a value (or nothing, if doesn't exist), associated
//                 with the supplied 'name'
// * remove(name) -- removes a name/value pair from the storage


var persistence_globals = {
  // this is where Persistence instance is stored
  instance: null,
  user_key: '~~eu',
	session_key: '~~es',
  expires: function() {
		var now = new Date();
		now.setDate(now.getDate() + 30);
		return '; expires=' + now.toUTCString() + ';';
	}(),
  delete_s: '=; expires=Thu, 13 Jul 1972 02:11:00 GMT',
	path: ';path=/;' 
};

plugin('storage', function() {
  
  var g = persistence_globals;
  return (g.instance = new Persistence());
  
	function Persistence() {
		var u = new Storage(g.user_key);
		var s = new Storage(g.session_key);
		var c = 0;
    
    this.user = function(name, value) {
      if (has_value(value)) {
        return save_value(u, name, value);
      }
      return load_value(u, name);
    };
    
    this.session = function(name, value) {
      if (has_value(value)) {
        return save_value(s, name, value);
      }
      return load_value(s, name);
    };

		parse(document.cookie, ';', '=', function(k, v) {
      // parse either and increment success counter
      ((k == g.user_key && load(k, v, u))
      || (k == g.session_key && load(k, v, s))) && c++;
      // make sure to stop parsing after both 
      return c > 1;
		});
    
    function has_value(value) {
      return value || typeof(value) == 'boolean';
    };
    
    function load_value(where, name) {
      return where[name];
    };
    
    function save_value(where, name, value) {
      where[name] = value;
      where.save();
      return value;
    };
    
		function parse(d, d1, d2, action) {
			var pairs = d.split(d1);
			for(var i = 0; i < pairs.length; i++) {
				var pair = pairs[i].split(d2);
				if (pair.length == 2) {
					if (action(trim(pair[0]), trim(pair[1]))) break;
				}
			}
			return true;
		};
		
		function load(k, v, o) {
			return parse(v, '|', '~', function(k, v) { o[k] = v;});
		};
	
		function trim(s) {
			return s.replace(/^\s+|\s+$/g, '');
		};
  }
	
	
	function remove(key) {
		if (!key) {
			remove(g.user_key);
			remove(g.session_key);
			return;
		}
		document.cookie = key + g.delete_s;
	};
		
	function Storage(key) {
    
    this.save = function() {
      var a = [];
      for(var k in this) {
        var v = this[k];
        var t = typeof(v);
        if (t == 'string'
            || t == 'number'
            || t == 'boolean'
            || t == 'datetime') {
          a.push(k + '~' + v);
        }
      }
      document.cookie = key + '=' + a.join('|') +
        (key == g.user_key ? g.expires : '') + g.path;
    };
	};
	
}(), true);
// ***
// jquery.super-grid.js
// Combines calendarGrid, listGrid, and spinner into a switchable presentation
// of data, making it viewable as a calendar grid or a list grid

// TODO(dglazkov): add documentation

var super_grid_globals = {
	all: {},
	default_settings: {
		// valid modes: calendar|list
		mode: "calendar",
		// called when the grid loads (initially or as a result of setting
		// changing)
		// * this -- Grid Object Model (GOM), an object that encapsulates
		//   manipulation of the grid
		// * params -- parameters of the grid (either defaults or passed via change
		//   invocation)
		calLoad: nop,
		calRender: nop,
		listLoad: nop,
		listRender: nop,
		showMonthName: true,
		modeChange: nop
	}
}
 
plugin("superGrid", function(id, settings) {
	var g = super_grid_globals;
	// assign unique id, if not specified
	id = guid(id);
	var instance = g.all[id];
	if (!instance) {
		 var complete_settings = $.extend({}, g.default_settings, settings);
		instance = g.all[id] = new superGrid(this, complete_settings);
	}
	return instance;

	function superGrid(jnode, complete_settings) {
		var me = this;
		this.settings = complete_settings;
		var paramsInUse;
		var spinner;
		var cal;
		var list;
		var cur;
		var gom;
		var handler;
		
		createGrid(false, false);
				
		function createGrid(loadNeeded, modeChanged) {
			switch (me.settings.mode) {
			  case "list":
					if (!list) {
						list = new grid(jnode);
						var gridId = guid();
						handler = me.settings.handler;
						var config = handler.cqConfig;
						list_jqgrid_globals.gridPageMgr(config);
		        var settings = $.extend({
		            selector: handler.selectorRoot,
				        gridConfig: $.extend({}, handler.gridConfig),
		            load: function(params) {
		              paramsInUse = params;
		              me.settings.listLoad(params);
				        },
				        gridComplete: handler.gridComplete,
				        customData : handler.customData()
			        }, config.gridPageMgr);
						list.grid = list.container.listjqGrid(gridId, settings, paramsInUse)
						list.node = $('#' + handler.gridConfig.gridParams.gridId);
					}
					else if (loadNeeded) {
					  if (!handler.hasCustomData()) {
						  list.grid.change(paramsInUse);
						}
					}
					me.settings.modeChange(me.settings.mode);
					cur = list;
					break;
				case "calendar":
				default:
					if (!cal) {
						cal = new grid(jnode);
						cal.grid = cal.container.calendarGrid('calendar', 
							{
								// load is called in context of the calenderGrid GOM
								// so 'this' is the calendarGrid GOM
								load: function(params) {
									modeChanged = true;
									paramsInUse = params;
									cal.node = cal.container.find("table");
									spinner = cal.node.spinner({text: "Loading..."});
									gom = new GOM(me, this);
									me.settings.calLoad.call(gom, params);
								},
								select: function(date, invalid) {
								  me.settings.select(date);
								},
								labels: "full",
								showMonthName: me.settings.showMonthName
							},
							paramsInUse);
					}
					else if (loadNeeded) {
						cal.grid.change(paramsInUse);
					}
					me.settings.modeChange(me.settings.mode);
					cur = cal;
					break;
			}
			cur.container.show();
			if (modeChanged) {
				me.settings.modeChange(me.settings.mode);
			}
		}
		
		function grid(jnode) {
			this.grid;
			this.node;
			this.container = $('<div class="gridcontainer" />').appendTo(jnode);
		}
		
		this.change = function(params, mode) {
		  if (mode) {
		    paramsInUse = params;
		    me.settings.mode = mode;
		    cur.container.hide();
		    cur.grid.reset();
		    createGrid(true, true);
		  }
		  else {
			  cur.grid.change(params);
			}
		}
		
		this.data = function(newData) {
		
		}
		
		this.changemode = function(params) {
			cur.container.hide();
			switch(me.settings.mode) {
				case "calendar":
					me.settings.mode = "list";
					break;
				case "list":
					me.settings.mode = "calendar";
					break;
			}
      createGrid(true, true);
		}
		
		function GOM(sgrid, cgom) {
			var supergrid = sgrid;
			var calgom = cgom;
			
			this.push = function(data) {
				var cell = calgom.cell(data.date);
				if (cell) {
					var eh = $('<div></div>');
					if (data.id) {
						eh.attr('id', data.id);
					}
					eh.addClass('e-item');
					if (data.className) {
						eh.addClass(data.className);
					}
					cell.append(eh);
					supergrid.settings.calRender.call(eh, data);
				}
			}
			
			this.end = function() {
				spinner.remove();
			}
		}
	}
});

// ***
// jquery.table.js
// a handy table creator. Generates HTML markup for a table
// When instantiated, automatically creates a thead row, assuming that all
// cells are going to be th. If you want to skip the head row, call .row()
// immediately after creating an instance

// TODO(dglazkov): add remove() method, which allows to remove top or bottom X
// rows

// table templates. HTML construction is done by joining arrays, which is
// faster than concatenating strings.
var table_templates = {
  //TABLE : [ '<table ', 1, '>', 3,'</table>' ],
  TABLE : [ '<table ', 1, '></table>' ],
  TR: [ '<tr>', 1, '</tr>'],
  TH: [ '<th ', 1, '>', 3, '</th>'],
  TD: [ '<td ', 1, '>', 3, '</td>']
};

// creates a table object at the specified node. The object can then be
// manipulated to add cells, rows, and render. Nothing is rendered initially.
// * attrs -- string of table attributes
plugin('table', function(attrs) {
  
  var table = this.data('table_maker');
  if (!table) {
    table = new Table(this, attrs);
    this.data('table_maker', table);
  }
  return table;
  
  // Table object is at the heart of the table creator
  // * attrs -- string of table attributes
  function Table(node, attrs) {
    
    // the table node (created on first render)
    var table;
    // template for current row. Starts with TH, all following rows with TD
    var row_template = table_templates.TH;
    // true if rendering head row, false otherwise
    var head = true;
    // collection of row HTML strings
    var rows = [];
    // collection of cell HTML strings
    var cells = [];
    
    this.node = function() {
      return table;
    }
    
    // starts a new row
    // if a row has no cells, this call has no effect
    // returns self
    this.row = function() {
      if (cells.length) {
        table_templates.TR[1] = cells.join('');
        rows.push(table_templates.TR.join(''));
        cells = [];
      }
      if (head) {
        row_template = table_templates.TD;
        head = false;
      } 
      return this;
    };
    
    // creates a cell in the table in the current row
    // * content -- content of the cell (optional)
    // * attrs -- attribute value for the cell (optional)
    // returns self
    this.cell = function(content, attrs) {
      row_template[1] = attrs || '';
      row_template[3] = content || '';
      cells.push(row_template.join(''));
      return this;
    };
    
    // renders the table based on accumulated cell/row info and purges the
    // buffer. If the table already existed, appends to the table
    // returns self
    this.render = function() {
      if (!rows.length) {
        if (cells.length) {
          this.row();
        } else {
          return this;
        }
      }
      var html = rows.join('');
      if (!table) {
				table_templates.TABLE[1] = attrs || '';
				table = $(table_templates.TABLE.join(''));
				table.append(html);
				node.append(table);
				/*
        table_templates.TABLE[1] = attrs || '';
        table_templates.TABLE[3] = html;
        node.append(table_templates.TABLE.join(''));
        table = $('>table:first', node);
        */
      } else {
        table.append(html);
      }
      rows = [];
      return this;
    };
    
    // clears table contents. This method does not remove the actual table
    // element. Use standard jQuery methods if you want to completely zap the
    // table.
    // * keepHead -- if true, the operation keeps the head of the table.
    this.clear = function(keepHead) {
      if (table) {
        if (keepHead) {
          $('tr:has(td)', table).remove();
        } else {
          row_template = table_templates.TH;
          head = true;
          table.empty();
        }
      }
      return this;
    };
  };
});


// ***
// jquery.tabledom.js
// a handy table creator. Generates HTML markup for a table
// When instantiated, automatically creates a thead row, assuming that all
// cells are going to be th. If you want to skip the head row, call .row()
// immediately after creating an instance


// creates a table object at the specified node. The object can then be
// manipulated to add cells, rows, and render. Nothing is rendered initially.
// * attrs -- string of table attributes
plugin('tabledom', function(attrs) {
  
  /*
  var table = this.data('tabledom_maker');
  if (!table) {
    table = new Table(this, attrs);
    this.data('tabledom_maker', table);
  }
  return table;
  */
  return new Table(this, attrs);
  
  // Table object is at the heart of the table creator
  // * attrs -- string of table attributes
  function Table(node, attrs) {
    if (!attrs) attrs = '';
    // the table node (created on first render)
    var table = $('<table ' + attrs + '></table>');
    node.append(table);
    // true if rendering head row, false otherwise
    var head = true;
    var row;
    
    this.node = function() {
      return table;
    }
    
    // starts a new row
    // if a row has no cells, this call has no effect
    // returns self
    this.row = function() {
      if (row && row.length) {
        table.append(row);
        row = null;
      }
      if (head) {
        head = false;
      }
      return this;
    };
    
    // creates a cell in the table in the current row
    // * content -- content of the cell (optional)
    // * attrs -- attribute value for the cell (optional)
    // returns self
    this.cell = function(content, attrs) {
      if (!row) {
        row = $('<tr>');
      }
      var c;
      if (!attrs) attrs = '';
      if (head) {
        c = $('<th ' + attrs + '/>');
      }
      else {
        c = $('<td ' + attrs + '/>');
      }
      if ($.isString(content)) {
        c.html(content);
      }
      else {
        c.append(content);
      }
      row.append(c);
      return this;
    }; 
    
    // clears table contents. This method does not remove the actual table
    // element. Use standard jQuery methods if you want to completely zap the
    // table.
    // * keepHead -- if true, the operation keeps the head of the table.
    this.clear = function(keepHead) {
      if (table) {
        if (keepHead) {
          $('tr:has(td)', table).remove();
        } 
        else {
          head = true;
          table.empty();
        }
      }
      return this;
    };
  };
});


// ***
// jquery.tilt.js
// layout handler

// creates a table object at the specified node. The object can then be
// manipulated to add cells, rows, and render. Nothing is rendered initially.
// * attrs -- string of table attributes
var tilt_globals = {
	defaults: {
	  id: 'tilt_data',
	  selector: 'document.body',
	  defaultPageType: 'inner'
	},
	gel: function(id) {
	  return document.getElementById(id);
  }
};

plugin('tilt', function(settings) {
  var g = tilt_globals;
  
  var globals = 'var gel = tilt_globals.gel;'
  eval(globals);

  var o = {
    ready: function(settings, callback) {
      if ($.isFunction(settings)) {
        callback = settings;
        settings = {};
      }
      settings = $.extend({}, g.defaults, settings, {});
      $('html').addClass('l-before-layout');
      $(function() {
        var node = $(settings.selector);
        var layout = Layout(settings);
        node.data(settings.id, layout);
        $.contentQuery(layout, function() {
          callback && callback(layout);
        });
      });
    },
    globals: 'var gel = $.tilt.gel;',
    gel: tilt_globals.gel
  };
  return o;
  
  
  
  function Layout(settings) {
    var g = $.authoring();
    var body = $(document.body);
    var p = $.presentationHelper;
    p.body = body;
    var o = {
      body: body,
      complete: function(finalize) {
        //$('html').removeClass('l-before-layout').addClass('l-layout-complete');
        /* process in order and each completes before next is processed */
        var pLf = $.presentationHelper.layout.func;
        var cnt = pLf.length;
        if (cnt > 0) {
          processLayouts(0);
        }
        else {
          done();
        }
        
        function processLayouts(i) {
          pLf[i](o, function() {       
            ++i;
            if (i < cnt) {
              processLayouts(i);
            }
            else {
              done();
            }
          });
        }
        
        function done() {
          body.find('>div.content-query').remove();
          $('html').removeClass('l-before-layout').addClass('l-layout-complete');
          finalize && finalize();
        }
        
      },
      getEl: function(selector) {
        return $(selector).eq(0);
      },
      authoring: g,
      pageType: function() {
        var pagetype = g.meta.pagetype;
        if (!pagetype) {
		      pagetype = settings.defaultPageType;
		    }
		    body.addClass('l-' + pagetype);
		    var view = g.meta.view;
		    view && body.addClass('l-' + view.name);
		    return pagetype;
      }(),
      viewnav: function() {
        var viewnav = gel('view-navigation');
		    if (viewnav) {
		      return $('ul', viewnav).get(0);
		    }
		    return null;
      }(),
      foreach: function(coll, func) {
        var retval = false;
        if ($.isArray(coll)) {
          $.each(coll, function(idx) {
            if (func(this, idx, coll)) {
              retval = true;
              return false;
            }
          });
        }
        else {
          var children = $(coll).children();
          children.each(function(idx) {
            if (func(this, idx, coll)) {
              retval = true;
              return false;
            }
          });
        }
        return retval;
      },
      grab: function(node, buckets, level) {
        level || (level = 0);
        var result = {};
        var evalNode = $(node);
        while (level > 0) {
          --level;
          evalNode = evalNode.children();
        }
        o.foreach(evalNode, function(el, idx, target) {
          push(el, getClassNameBucket(el));
        });

        return result;
        
        function push(el, bucket) {
          if (bucket) {
            var items = result[bucket];
            if (!items) {
              items = [];
              result[bucket] = items;
            }
            items.push(el);
          }
        }
        
        function getClassNameBucket(el) {
          var classes = el.className.split(' ');
			    for(var className in buckets) {
				    if (o.foreach(classes, function(nodeClassName, idx, target) {
					    return nodeClassName == className;
				    })) {
					    return buckets[className];
				    }
			    }
		    }
      },
      getElements: function(arrayOfIds, node)	{
        !node && (node = document);
		    var result = {};
		    $.each(arrayOfIds, function() {
		      var item = this;
			    var o = node.getElementById(item);
			    if (o) {
				    result[item] = o;
			    }
			    else {
				    result.complete = true;
			    }
		    })
		    result.complete = !result.complete;
		    result.contains = function(matches) {
			    return foreach(matches, function(match) {
				    if (result[match]) {
					    return true;
				    }
			    });
		    }
		    return result;
	    },
	    remove: function(n) {
	      remove(n);
	    }
    };
    return o;
  };
	
	function remove(n) {
	  if (n) {
		  if (n.remove) {
		    n.remove();
		  }
		  else {
		    $(n).remove();
		  }
		}
	};

}(), true);


// ***
// jquery.tiny-mce.js
// TinyMCE dynamic loader. Provides ability to add rich text editing
// capabilities without having to specify TinyMCE in the HEAD element


// TODO(dglazkov): add support for editor options.
// TODO(dglazkov): provide more elegant disable/enable presentation. Currently,
//                 the editor is simply hidden with visibility: hidden
// TODO(dglazkov): add support for removing editor

var global_tinymce = {
  // at this point, the editor DLL must be on the same domain
  BASE_URL: estradaCustom.sitePath + '/assets/TinyMce.axd',
  EDITOR_KEY: 'tinyMce',
  defaults: {
		theme : "advanced",
		skin : "o2k7",
    gecko_spellcheck: true,
		skin_variant : "black",
    // Add "-" to custom plugins to load them remotely
    //plugins : "table,advlink,insertdatetime,preview,searchreplace,print,contextmenu,paste,fullscreen,xhtmlxtras,-imagepicker",
    plugins : "table,advlink,insertdatetime,preview,searchreplace,print,contextmenu,paste,fullscreen,xhtmlxtras,imagepicker,media",
		height: "400px", width: "630px",
    safari_warning : false,
    cleanup_on_startup : false,
    convert_urls: false,
    relative_urls: true,
    strict_loading_mode: true,
    theme_advanced_buttons1: "cut,copy,paste,pastetext,pasteword,cleanup,removeformat,separator,search,replace,separator,undo,redo,separator,styleselect,formatselect",
    theme_advanced_buttons2: "bold,italic,underline,strikethrough,sub,sup,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,bullist,numlist,outdent,indent,separator,link,unlink,anchor,image,imagepicker,charmap",
    theme_advanced_buttons3 : "tablecontrols,separator,hr,visualaid,code,separator,fullscreen,separator,print,media",
		theme_advanced_toolbar_location : "top",
		theme_advanced_toolbar_align : "left",
    theme_advanced_path_location : "bottom",
    plugin_insertdate_dateFormat : "%Y-%m-%d",
    plugin_insertdate_timeFormat : "%H:%M:%S",
		theme_advanced_resize_horizontal : false,
    theme_advanced_resizing : false
  },
  default_options: {
    // called when the editor initialized and rendered for the first time
    ready: nop,
    // called when the content is submitted (triggered via 'submit' command)
    submit: nop
  },
  // true if tinyMCE was already loaded
  loaded: false
};

// tinyMce plugin:
// * cmd -- optional command. Available commands:
//    * load -- initializes the editor at the given node (optional)
//    * disable -- temporarily disables the editor
//    * enable -- temporarily enables the editor
//    * submit -- trigger submit event in the editor
//    * reset -- resets content of the editor to the original input value
plugin('tinyMce', function(cmd, options, config) {
  var node = this;
  if (node.length == 0) return;
  
  var g = global_tinymce;
  var gA = $.authoring();
  
  if (typeof(cmd) != 'string') {
		config = options;
    options = cmd;
    cmd = 'load'; // load event
    config = $.extend({}, g.defaults, config || {});
  }
  if (g.loaded) {
    init();
    return;
  }
  
  // this sequence in part is from:
  // http://tinymce.moxiecode.com/punbb/viewtopic.php?id=10419
  $.extend(window, {
    // specify tinyMCE base 
    tinyMCEPreInit: {
      suffix: '',
      // has to be just http URI. It sets the base of tinymce library, 
      // later is used by tinyMCE in XHR loading other scipts, for example
      // plugins
      base: g.BASE_URL
    },
    // today (v3.0.8), there's a bug that incorrectly sets up (and fails)
    // content ready event for IE during loading
    // setting this to true bypasses this branch
    tinyMCE_GZ: { loaded: true }
  });
  
  $.getScript(g.BASE_URL + '/tiny_mce.js', function() {
    // Add image picker plugin
    // This is the debug version, loads image picker from a special directory
    // You need to set it up to point to tinymce plugin in Org.Web.TinyMce
    // project
    //tinymce.PluginManager.load('imagepicker',
    //  '/tinymce/plugins/imagepicker/editor_plugin.js?'
    //  + new Date().getTime().toString());

    // tell tinymce that the DOM has been already loaded
    // and then it will use XHR to load other scipts (by ex. plugins).
    window.tinymce.dom.Event._pageInit();
    init();
    g.loaded = true;
  });
 
  function init() {
    var idx = 0;
    $.each(node, function() {
      var tmce;
      if (!(tmce = node.data(g.EDITOR_KEY))) {
        node.data(g.EDITOR_KEY, 1);
        // create editor instance
        editor = new tinymce.Editor(this.id = guid(this.id), config);
        editor.onPostRender.add(function() {
          var o = $.extend({}, g.default_options, options || {});
          o.ready();
          node.data(g.EDITOR_KEY, {
            editor: editor,
            options: o
          });
          exec(tmce, node);
        });
        editor.render();
        return;
      }
      if (tmce == 1) return;
      exec(tmce, node);
    });
  };
  
  function exec(tmce, node) {
    switch(cmd) {
      case 'disable':
        $(tmce.editor.getContainer()).css({
          visibility: 'hidden'
        });
        break;
      case 'submit':
        tmce.options.submit(tmce.editor.getContent());
        break;
      case 'enable':
        $(tmce.editor.getContainer()).css({
          visibility: 'visible'
        });
        break;
      case 'reset':
        var e = tmce.editor;
        e.execCommand('mceAddUndoLevel');
        e.setContent(node.val());
        break;
    }
  };
});

plugin('tinyMceExt', function() {
  var node = this;
  if (node.length == 0) return;
  
  var g = global_tinymce;
  var gA = $.authoring();
  
  // get custom tinymce editor config if set
  /*
  var config;
	try {
		config = custom_editor_config;
	}
	catch(e) {
		config = null;
	}
	*/
  var config = $.extend({}, g.defaults, gA.getEditorConfig());
  if (g.loaded) {
    init();
    return;
  }
  
  // this sequence in part is from:
  // http://tinymce.moxiecode.com/punbb/viewtopic.php?id=10419
  $.extend(window, {
    // specify tinyMCE base 
    tinyMCEPreInit: {
      suffix: '',
      // has to be just http URI. It sets the base of tinymce library, 
      // later is used by tinyMCE in XHR loading other scipts, for example
      // plugins
      base: g.BASE_URL
    },
    // today (v3.0.8), there's a bug that incorrectly sets up (and fails)
    // content ready event for IE during loading
    // setting this to true bypasses this branch
    tinyMCE_GZ: { loaded: true }
  });
  $.getScript(g.BASE_URL + '/tiny_mce.js', function() {
    // tell tinymce that the DOM has been already loaded
    // and then it will use XHR to load other scipts (by ex. plugins).
    var cnt = 1000;
    ready();
    function ready() {
      --cnt;
      if (!window.tinymce && (cnt > 0)) {
        window.setTimeout(ready, 100);
      }
      else {
        window.tinymce.dom.Event._pageInit();
  	    init();
  	    g.loaded = true;
      }
    }
  });
  function init() {
    tinyMCE.init(config);
    $.each(node, function() {
      var textArea = $(this);
      if (textArea.attr('id') == '') {
        textArea.attr('id', guid(this.id));
      }
      var editorId = textArea.attr('id');
      tinyMCE.execCommand('mceAddControl', false, textArea.attr('id'));
    });
  };
});
// ***
// jquery.toXml.js

plugin('toXML', function() {
  var toXML = function(node)
  {
    var out = '';
    var attributes = '';
    var content = '';
    out += '<' + node.nodeName;
    if (node.childNodes)
    {
      for (var i = 0; i < node.childNodes.length; i++)
      {
        switch(node.childNodes[i].nodeType)
        {
          case 1:     // ELEMENT_NODE
            content += toXML(node.childNodes[i]);
            break;
          case 2:     // ATTRIBUTE_NODE
            attributes += ' ' + node.childNodes[i].nodeName  + '="'
                              + node.childNodes[i].nodeValue + '"';
            break;
          case 3:     // TEXT_NODE
          case 4:     // CDATA_SECTION_NODE
          case 5:     // ENTITY_REFERENCE_NODE
          case 6:     // ENTITY_NODE
          case 7:     // PROCESSING_INSTRUCTION_NODE
          case 8:     // COMMENT_NODE
          case 9:     // DOCUMENT_NODE
          case 10:    // DOCUMENT_TYPE_NODE
          case 11:    // DOCUMENT_FRAGMENT_NODE
          case 12:    // NOTATION_NODE
            content += node.childNodes[i].nodeValue;
            break;
        }
      }
    }
    out += attributes;
    if (content.length > 0)
    {
      out += '>' + content;
      out += '</' + node.tagName + '>';
    }
    else
    {
      out += '/>';
    }
    return out;
  }
  var out = '';
  if (this.length > 0) {
    if (typeof XMLSerializer == 'function' ||
        typeof XMLSerializer == 'object')
    {
      var xs = new XMLSerializer();
      this.each(function() { out += xs.serializeToString(this); });
    }
    else if (this[0].xml !== undefined)
    {
      this.each(function() { out += this.xml; });
    }
    else
    {
      if (this.length > 0)
      {
        this.each( function() { out += toXML(this); } );
      }
    }
  }
  return out;
});


// ***
// jquery.ui-article.js
// Publication renderer

var global_handler_article = {
	DATA_KEY: 'handler_article_data',
	
	handler: {
		titleCapitalized: 'Article',
		title: 'article',
		className: 'article',
		selector: '.content ul:first',
		
		cqRender: function(config) {
		  var me = this;
		  var node = config.queryJNode.parent();
			config.loadDisabled = true;
			me.cqConfig = config;
      me.render(node);
		},
		render: function(node) {
		  var me = this;
		  $('.article .f-abstract:first').each(function() {
		    var field = $(this);
		    field.find('.abstract:hasText').each(function() {
		      field.addClass('float-abstract');
				  field.shadow('f-abstract');
		    });
			});
		}
	}
};

$.uiHandler('article', function() {
  var g = global_handler_article;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  return handler;
}());

// ***
// jquery.ui-calendar.js
// Calendar renderer

var global_handler_calendar = {
	DATA_KEY: 'handler_calendar_data',
	gridConfig: {
	  gridParams: {
	    caption: 'Event List View',
      datatype: 'custom',
      //loadonce: true,
      colNames:['Title', 'Event Date', 'Abstract'],
      colModel:[
        {name:'title', width:'33%'}, //225
        {name:'range', width:'26%'}, //175
        {name:'abstract', width:'41%'} //275
      ],
      rowNum:10000,
      rowList:[],
      sortable: false,
      height: '100%',
      width: '100%',
      loadcustom: function(gridHelper) {
        var g = global_handler_calendar;
        $(g.handler.selectorRoot + g.handler.selectorData, gridHelper.data).each(function() {
          var item = g.handler.itemCreate($(this));
          gridHelper.newRow($(this).attr('className'));
          gridHelper.addCellWrap($(item.title).html());
          gridHelper.addCellWrap($.dates.rangeFormatter(item.date, item.ends));
          gridHelper.addCellWrap(item.abstract);
          gridHelper.addRow();
        });
      }
    },
    gridId: null,
    navButtons: {},
    customButtons: {}
	},
	calendarGridLoad: function(context) {
		if (context.config.items) {
			$.each(context.config.items, function() {
				var multiple = daysdiffByDate(this.ends, this.date);
				if (multiple) {
					this.className += " e-multiple";
				}
				context.sgrid.push(this);
				if (multiple) {
				  var saved = new Date();
					saved.setTime(this.date.getTime());
					this.date.setDate(this.date.getDate()+1);
					var start = context.g_cal.zeroDate(this.date);
					while (start <= this.ends) {
						context.sgrid.push(this);
						this.date.setDate(this.date.getDate()+1);
						start = context.g_cal.zeroDate(this.date);
					}
					this.date.setTime(saved.getTime());
				}
			});
		}
		context.sgrid.end();
		$(".e-item").infoBox(
			{
				//useParentPosition: true,
				//useParentClass: '.e-item',
				create: function(el) {
				  var id = el.attr('id');
					var item = context.config.itemMap[id];
					if (item) {
						var box = $.div().addClass('info-popup');
						var datestr = context.g_ev.rangeFormatter(item.date, item.ends)
						$(el).append(box);
						$(box).append(
							'<div class="title">' + $(item.title).text() + '</div>'
							+ '<div class="times">' + datestr + '</div><div class="abstract">'
							+ item.abstract + '</div>'
						);
						return box;
					}
					return null;
				}
			}
		);
		function daysdiffByDate(date1, date2) {
			return ((date1 != date2) && (date1.getDate() != date2.getDate()));
		}
		function daysdiffBySpan(date1, date2) {
			// The number of milliseconds in one day
			var ONE_DAY = 1000 * 60 * 60 * 24;
			// Convert both dates to milliseconds
			var date1_ms = date1.getTime();
			var date2_ms = date2.getTime();
			// Calculate the difference in milliseconds
			var difference_ms = Math.abs(date1_ms - date2_ms);
			// Convert back to days and return
			return Math.floor(difference_ms/ONE_DAY);
		}
	},
	handler: {
		titleCapitalized: 'Events',
		title: 'events',
		className: 'calendar',
		selectorRoot: 'div.content-query.event',
		selectorData: ' > ul li',
		selector: function() {
	    return this.selectorRoot + this.selectorData;
		},
		customData: function() {
		  return global_handler_calendar.dataNode;
		},
		hasCustomData: function() {
		  var g = global_handler_calendar;
		  if (g.dataNode) {
		    g.gridHelper.populate(g.dataNode);
		    return true;
		  }
		  return false;
		},
		getGrid: function() {
		  return global_handler_calendar.grid;
		},
		gridComplete: function(jqGrid) {
		  var g = global_handler_calendar;
		  g.grid = jqGrid;
		  g.gridHelper = jqGrid.gridHelper;

		},
		itemClearer: function(config) {
			config.items = [];
		},
		itemLoader: function(config, node) {
      if (!config.ready) return;
			var items = new Array(node.length);
			var itemMap = {};
			var create = config.handler.itemCreate;
			$(node).each(function(idx) {
				var item = create($(this));
				items[idx] = item;
				itemMap[item.id] = item;
			});
			config.items = items;
			config.itemMap = itemMap;
		},
		// Called to render each item.
		// Parameters:
		// * this -- jQuery object of where the item should be renderered
		// * view -- either "calendar" or "list"
		// * item -- an opaque object, representing one item in the query
		item: function(view, item) {
			switch(view) {
				case "calendar":
				  this.append('<span class="time">' + $.dates.format(item.date, 'h:mma') 
				    + '</span><span class="title">' + item.title.innerHTML + '</span>');
					break;
				case "list":
				default:
					this.cell(item.title.innerHTML, 'title ' 
						+ (item.className ? ' ' + item.className : ''));
					this.cell($.dates.format(item.date, "MMM dd, yyyy hh:mma"), 'start-date');
					this.cell($.dates.format(item.ends, "MMM dd, yyyy hh:mma"), 'end-date');
					this.cell(item.abstract, 'abstract');
					break;
			}
		},
		// Called when the content query config and handler have been determined
		// by default, takes the items from markup.
		// Parameters:
		// * this -- jQuery object of the main ".content-query" node
		// * config -- the configuration object
		// * done -- the callback to call when loading is complete
		// return -- true, if the loading is asynchronous. If the loading is
		// asynchronous, the content query processing will suspend until the "done"
		// call back is called
		load: function(config, done) {
			if (!config.loadDisabled && config.loadRequired) {
			  config.ready = true;
				var me = this;
				config.loadRequired = false;
				$.ajah(config.postUrl(), null,
					function() {
						me.loadData(config, $(this));
						done();
						
					},
					function() {
						
					}
				);
			}
			else {
				done();
			}
		},
		loadData: function(config, dataNode) {
		  var me = this;
		  var eventList = dataNode.find(me.selectorRoot + me.selectorData);
			if (eventList.length > 0) {
				config.handler.itemLoader(config, eventList);
			}
		},
		// Called to create each item.
		// Parameters:
		// * this -- jQuery object of the corresponding item in the content query
		// return -- an opaque object, representing on item in the query
		itemCreate: function(node) {
			var id = guid();
			var requiresEdit = false;
			// title
			//var fieldNode = $("a.title:first", node);
			var fieldNode = node.find('a.title:first');
			var title = document.createElement("div");
			fieldNode.clone().prependTo(title);
			// start date
			var date;
			//fieldNode = $("span abbr.start-date", node);
			fieldNode = node.find('abbr.start-date');
			if (fieldNode.length == 0) {
					requiresEdit = true;
					date = new Date();
			}
			else {
				date = $.dates.parse(fieldNode.attr("title"));
			}
			// ends date
			var ends;
			//fieldNode = $("span abbr.end-date", node);
			fieldNode = node.find('abbr.end-date');
			if (fieldNode.length == 0) {
					requiresEdit = true;
					ends = new Date();
			}
			else {
				ends = $.dates.parse(fieldNode.attr("title"));
			}
			// abstract
			//fieldNode = $("p.abstract:first", node);
			fieldNode = node.find('p.abstract:first');
			var abstract = (fieldNode.length > 0) ? fieldNode.html() : "";
			
			var className;
			if (requiresEdit) {
				className = 'e-requires-edit';
			}
			return {
				id: id,
				className: className,
				title: title,
				date: date,
				ends: ends,
				abstract: abstract
			};
		},
		// Called to create the overall presentation.
		// Parameters:
		// * this -- jQuery object of where the item should be rendered
		// * config -- the configuration object
		// * handler -- the current renderer object
		// * items -- the initial set of items to render (this set is what was
		//            delivered with markup)
		//render: function(config, renderer, items) {
		cqRender: function(config) {
		  var me = this;
		  var g_cal = calendar_grid_globals;
			var g = global_handler_calendar;
			me.cqConfig = config;
			var g_ev = global_handler_event;
			var handler = config.handler;
			var node = config.queryJNode;
			config.queryJNode.addClass(config.handler.className);
			
			var view = $.calendarPhrase(
				{
				  search: !config.search.isEmpty,
				  range: !config.range.isEmpty,
				  categories: !config.categories.isEmpty(),
					config: config,
					node: node,
					mode: true,
					gridId: config.key + guid()
				});
      view.config.getGridParams = function() {
		    return list_jqgrid_globals.getGridParams(1, global_handler_calendar.gridConfig.gridParams.rowNum);
		  };
			view.viewedInGrid = false;
			view.gridChange = function(range, mode) {
			  function getRange(date) {
			    var realRange = {rangeincrement: {mode: 1}};
			    switch(view.calendarView) {
			      case 0:
			        g_cal.createMonthRange(date, 1, realRange);
			        realRange.rangeincrement.mode = 0;
              break;
            case 1:
			        g_cal.createWeekRange(date, 1, realRange);
			        break;
			      case 2:
			        g_cal.createDayRange(date, 1, realRange);
			        break;
			      default:
			        break;
			    }
			    return realRange;
			  }
			  g.dataNode = null;
			  config.hasData = false;
				if (range) {
				  if (range.getDate) {
				    view.currentDate = range;
				    range = getRange(view.currentDate);
				  }
				  else if (isNaN(range)) {
				    // search initiated 
				    view.isDirty = true;
				    config.hasData = true;
				    g.dataNode = range;
				    mode(); // mode is the done() 
				    range = getRange(view.currentDate);
				    done = mode;
				    mode = 'list';
				  }
				  else {
				    view.currentDate = null;
				  }
				}
				else {
				  view.currentDate = null;
				}
				range['selected'] = view.currentDate;
				$.superGrid(view.gridId).change(range, mode);

			}
			view.reload = function() {
				config.loadRequired = true;
				view.gridChange(0);
			}
			$(config.queryJNode).superGrid(view.gridId, {
			  handler: handler,
				mode: "calendar",
				showMonthName: true,
				modeChange: view.mode.update,
				select: function(date) {
				  view.mode.dayView(date);
				},
				calLoad: function(params, skipLoad) {
					view.calRange.update(params.rangeincrement, params.start, params.end, params.completeRange.start, params.completeRange.end);
					view.gridParamsReady();
					view.isDirty = false;
					var sgrid = this;
					var context = {
					  config: config,
					  sgrid: sgrid,
					  g: g,
					  g_ev: g_ev,
					  g_cal: g_cal
					};
					if (config.hasData) {
					  g.calendarGridLoad(context);
					}
					else {
					  handler.load(config, function() {
					    g.calendarGridLoad(context);
					  });
					}
				},
				calRender: function(data) {
					handler.item.call(this, "calendar", data);
				},
				listLoad: function(params) {
				  if (!config.hasData) {
					  view.calRange.update(params.rangeincrement, params.start, params.end);
					  view.gridParamsReady();
					}
				},
				list: global_handler_calendar.list
			});
		}
	}
};

$.uiHandler('events', function() {
  var g = global_handler_calendar;
  var handler = g.handler;
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = g.gridConfig;
  return handler;
}());

// ***
// jquery.ui-confirm.js

plugin('uiConfirm', function(settings) {
       
  settings = $.extend(
    {
      msg: '',
      title: 'Confirm',
      ok: 'OK',
      cancel: 'CANCEL',
      width: 400,
      height: 200,
      confirm: nop,
      negative: nop
    }, settings || {});

  var slot;
  var close;
  var o = {
    SHEET_KEY: 'uiconfirm',
    title: settings.title,
    width: settings.width,
    volatile: true,
    load: function(end, manager) {
      // save close so that "assign" could use it
      slot = $(this);
      close = manager.close;
      var holder = this.div().addClass('ui-confirm').addClass('slate');
      holder.html(settings.msg);
      slot.phrase({
        load: function() {
          if (settings.ok && settings.ok.length) {
            this.button(settings.ok, 'ok', function(done) {
              var button = $(this);
              close();
              settings.confirm();
            }, true);
          }
          if (settings.cancel && settings.cancel.length) {
            this.button(settings.cancel, 'cancel', function(done) {
              close();
              settings.negative();       
            }, true);
          }
        }
      });
      settings.load && settings.load.call(slot, end, manager);
      return false;
    }
  };
  return o;
});

plugin('uiInfo', function(msg) {
  $(this).sheet($.uiConfirm({
    title: 'Alert',
    cancel: '',
    msg: msg
  }));
});
// ***
// jquery.ui-cq-grid.js
// generic content query grid handler

var global_handler_cq_grid = {
	DATA_KEY: 'handler_cq_grid_data',
	gridConfig: {
	  gridParams: {
	    caption: '',
	    colNames:['_'],
      colModel:[
        {name:'title', width: 'auto'}
      ],
      datatype: 'custom',
      loadcustom: function(gridHelper) {
        var g = global_handler_cq_grid;
        g.gridHelper = gridHelper;
        $(gridHelper.handler.selectorRoot + gridHelper.handler.selectorData, gridHelper.data).each(function() {
          var item = $(this);
          gridHelper.newRow(item.attr('className'));
          $.each(gridHelper.colModel, function() {
            var c = this;
            gridHelper.addCellWrap($.cqData(item, c.datatype, c.name, c.dataformat));
          });
          gridHelper.addRow();
        });
      }
    },
    gridId: null,
    navButtons: {},
    customButtons: {},
    topNavigation: true
  },
	handler: {
		titleCapitalized: 'Grid',
		title: 'grid',
		className: 'cq-grid',
		selectorRoot: '.ui-data',
		selectorData: ' > ul > li',
		selector: function() {
		  return this.selectorRoot + this.selectorData;
		},
		//clearQuery: true,
		gridConfig: {},
		cqRender: function(config) {
		  var g = global_handler_cq_grid;
		  var me = this;
		  me.selectorRoot = config.queryJNode.attr('nodeName').toLowerCase() 
		    + '.' + config.queryJNode.attr('class').replace(' ', '.');
		  me.gridConfig.gridParams.handler = {
		    selectorRoot: me.selectorRoot,
		    selectorData: me.selectorData
		  };
		  me.cqConfig = config;
		  var node = config.queryJNode;
		  if (config.paramsRequired) {
		    node.empty();
		  }
		  me.view = $.queryFormPhrase(
			  {
			    config: config,
			    node: config.queryJNode,
			    categories: !config.categories.isEmpty(),
			    search: !config.search.isEmpty,
			    mode: false,
			    range: !config.range.isEmpty,
			    viewnav: config.layoutHelper.viewnav,
			    descendants: (config.descendants != null),
			    extendView: {
			      caption: function(title) {
			        me.gridConfig.gridParams.caption = title;
			      }
			    }
			  });

      me.view.gridChange = function(data, done) {
        if (data) {
          g.gridHelper && g.gridHelper.populate(data);
          done();
        }
        else {
          me.gridConfig.grid.change(0);
        }
      }
      me.view.urlChange = function(url, title) {
        config.gridPageMgr.loadUrl(url);
        me.itemClearer && me.itemClearer(config);
        if (title) {
          me.gridConfig.grid.caption(title);
        }
        me.gridConfig.grid.change(0);
      }
      
      me.render(node);

    },
		// Called to create the overall presentation.
		// Parameters:
		// * node -- the node containing the 'data' markup for the grid
		// * params -- object containing grid config parameters
		render: function(node) {
		  var me = this;
		  var g = global_handler_cq_grid;
		  var gA = $.authoring();
		  var gridId = me.cqConfig.key + guid();
		  list_jqgrid_globals.gridPageMgr(me.view.config);
		  var settings = $.extend({
        selector: me.selectorRoot,
        gridConfig: function() {
          me.gridConfig.gridParams = $.extend(true, me.gridConfig.gridParams, me.cqConfig.UiParams);
          return me.gridConfig;
        } (),
        load: function(params) {
          !me.cqConfig.range.isEmpty && me.view.range.update(params.rangeincrement, params.start, params.end);
          me.view.gridParamsReady && me.view.gridParamsReady();
        }
      }, me.view.config.gridPageMgr);
      me.gridConfig.gridParams.url = me.cqConfig.postUrl();
      me.gridConfig.grid = me.cqConfig.queryJNode.listjqGrid(gridId, settings);
		}
	}
};

$.uiHandler('cq-grid', function() {
  var g = global_handler_cq_grid;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = $.extend(true, {}, g.gridConfig);
  return handler;
} ());

// ***
// jquery.ui-custom-grid.js
// generic content query grid handler

var global_handler_custom_grid = {
	DATA_KEY: 'handler_custom_grid_data',
	gridConfig: {
	  gridParams: {
      datatype: 'custom',
      loadonce: true,
      loadcustom: function(gridHelper, gridPageInfo) {
        var g = global_handler_custom_grid;
        g.gridHelper = gridHelper;
        var cnt = 0;
        $(g.handler.selectorData, gridHelper.data).each(function() {
          ++cnt;
          var item = $(this);
          gridHelper.newRow(item.attr('className'));
          $.each(g.gridConfig.gridParams.colModel, function() {
            var c = this;
            gridHelper.addCellWrap($.cqData(item, c.datatype, c.name, c.dataformat));
          });
          gridHelper.addRow();
        });
        gridHelper.data.remove();
        gridPageInfo.records = cnt;
        gridPageInfo.totalrecords = cnt;
        gridPageInfo.initialized = true;
      },
      gridComplete: function() {
      }
    },
    gridId: null,
    navButtons: {},
    customButtons: {},
    topNavigation: false
  },
	handler: {
		titleCapitalized: 'Grid',
		title: 'grid',
		className: 'cq-grid',
		selectorRoot: '',
		selectorData: '> li',
		selector: function() {
		  return this.selectorRoot + this.selectorData;
		},
		gridConfig: {},
		// Called to create the overall presentation.
		// Parameters:
		// * node -- the node containing the 'data' markup for the grid
		// * params -- object containing grid config parameters
		render: function(node) {
		  //console.info(node, node.html());
		  var me = this;
		  var g = global_handler_custom_grid;
		  var gA = $.authoring();
		  var gridId = 'customgrid' + guid();
		  var dataNode = node.find('.ui-data');
		  g.handler.selectorRoot = node.attr('nodeName').toLowerCase() 
		    + '.' + node.attr('class').replace(' ', '.');
		  var uiParamNode = $('.ui-params', node);
      var jsonParams = (uiParamNode.length > 0) ? eval("(" + uiParamNode.text() + ")") : {};
      uiParamNode.remove();
		  var settings = {
        selector: me.selectorRoot,
        customData: dataNode,
        gridConfig: function() {
          g.gridConfig.gridParams = $.extend(true, g.gridConfig.gridParams, jsonParams);
          return g.gridConfig;
        }(),
        gridPageInfo: function() {
          return {
            initialized: false,
            records: 0,
            totalrecords: 0,
            page: 1,
            rowNum: 30
          };
        }
      };
      var gridNode = $('<div class="grid-holder"></div>');
      node.before(gridNode);
      gridNode.listjqGrid(gridId, settings);
		}
	}
};

$.uiHandler('custom-grid', function() {
  var g = global_handler_custom_grid;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = $.extend(true, {}, g.gridConfig);
  return handler;
} ());

// ***
// jquery.ui-event.js
// event handler

var global_handler_event = {
	DATA_KEY: 'handler_event_data',
	rangeFormatter: function(from, to) {
	  var gcal = calendar_grid_globals;
	  var datestr;
	  if (from && to) {
			if (from.getTime() == to.getTime()) {
				datestr = $.dates.format(from, "EE, MMM dd, yyyy")
				+ " @ "
				+ $.dates.format(from, "h:mm A");
			}
			else {
				var f = gcal.zeroDate(from);
				var t = gcal.zeroDate(to);
				if (f.getTime() == t.getTime()) {
					datestr = $.dates.format(f, "EE, MMM dd, yyyy")
						+ " @ "
						+ $.dates.format(from, "h:mm A")
						+ " - "
						+ $.dates.format(to, "h:mm A");
				}
				else {
					datestr = $.dates.format(from, "EE, MMM dd, yyyy h:mm A")
						+ " - "
						+ $.dates.format(to, "EE, MMM dd, yyyy h:mm A");
				}
			}
		}
		return datestr;
	},
	handler: {
		titleCapitalized: 'Event',
		title: 'event',
		className: 'event',
				
		cqRender: function(config) {
		  var me = this;
		  var g = global_handler_event;
			var gcal = calendar_grid_globals;
			var selector = '.' + config.key;
			$(selector).addClass(this.className);
			$(selector + ' .range').each(function() {
			  var ev = $(this);
				var dateFields = $('span.from,span.to', ev);
				ev.empty();
				ev.append(dateFields);
				var from, to;
				var jFrom =  $('abbr.from', ev);
				if (jFrom.length > 0) {
					from = $.dates.parse(jFrom.attr('title'));
				}
				var jTo = $('abbr.to', ev);
				if (jTo.length > 0) {
					to = $.dates.parse(jTo.attr('title'));
				}
				$('abbr', ev).remove();
				
				$('.from', ev).addClass('date-formatted').text(g.rangeFormatter(from, to));
				
				//$.authoring.content.position();
				
				// if main content, put abstract right above it
				$('#l-content .main').each(function() {
					var ab = $('#l-content .abstract');
					$(this).before(ab);
					ab.shadow('abstract')
				});
			});
		}
	}
};

$.uiHandler('event', function() {
  var g = global_handler_event;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  return handler;
}());

// ***
// jquery.ui-googlesearch.js
// event handler

var global_handler_googlesearch = {
  DATA_KEY: 'handler_googlesearch_data',
  handler: {
    titleCapitalized: 'googlesearch',
    title: 'googlesearch',
    className: 'googlesearch',
    selectorRoot: 'div.googlesearch',
    cqRender: function(config) {
      var me = this;
      var g = global_handler_googlesearch;
      var ph = $.presentationHelper;
      if (ph.query.query) {
        $(window).load(function() {
          $('input.gsc-input').focus().val(decodeURIComponent(ph.query.query));
          $('input.gsc-search-button').click();
        });
      }
    }
  }
};

$.uiHandler('googlesearch', function() {
  var g = global_handler_googlesearch;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  return handler;
}());


// ***
// jquery.ui-httpgateway.js
// event handler

var global_handler_httpgateway = {
	DATA_KEY: 'handler_httpgateway_data',
	handler: {
		titleCapitalized: 'HttpGateway',
		title: 'httpgateway',
		className: 'httpgateway',
		selectorRoot: 'div.httpgateway',
		cqRender: function(config) {
		  var me = this;
		  var g = global_handler_httpgateway;
      $(g.handler.selectorRoot).each(function() {
        var gw = $(this);
        var url = gw.find('div.url').text();
		    gw.html('<iframe src="' + url + '" style="border:none;width:100%;height:500px"></iframe>');
      });
		}
	}
};

$.uiHandler('httpgateway', function() {
  var g = global_handler_httpgateway;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  return handler;
}());

// ***
// jquery.ui-image-map.js
// Image Map handler

var global_handler_image_map = {
	DATA_KEY: 'handler_image_map_data',
	handler: {
		titleCapitalized: 'Image',
		title: 'image',
		className: 'image-map',
		selector: '.content ul:first',
		itemClearer: null,
		itemLoader: null,
		item: null,
		itemCreate: function() {
			
		},
		// Called when the content query config and handler have been determined
		// by default, takes the items from markup.
		// Parameters:
		// * this -- jQuery object of the main ".content-query" node
		// * config -- the configuration object
		// * done -- the callback to call when loading is complete
		// return -- true, if the loading is asynchronous. If the loading is
		// asynchronous, the content query processing will suspend until the "done"
		// call back is called
		load: null,
		cqRender: function(config) {
		  var me = this;
		  var node = config.queryJNode;
			config.loadDisabled = true;
			me.cqConfig = config;
      me.render(node);
		},
		// Called to create the overall presentation.
		// Parameters:
		// * node -- the node containing the 'data' markup for the grid
		// * params -- object containing grid config parameters
		render: function(node) {
		  var me = this;
			var gA = $.authoring();
			var header = $('>h3,>a', node);
			var behaviorNode;
			$('li', me.cqConfig.queryJNode).each(function() {
			  var item = $(this);
			  var link  = $('<a href="' + item.find('.pageurl').text() + '" />');
			  link.append(item.find('img'));
			  item.empty().append(link);
			});
			
			var pU = $.presentationHelper.uiHandler;
			var storageKeys = pU.getStorageKeys(node);
			if (storageKeys) {
			  pU.add(storageKeys.bucket, storageKeys.key, node);
		  }
		}
	}
};
      
$.uiHandler('image-map', function() {
  var g = global_handler_image_map;
  var handler = g.handler;
  handler.DATA_KEY = g.DATA_KEY;
  return handler;
}());

// ***
// jquery.ui-images.js
// images handler

var global_handler_images = function() {
  var currentImg;
  var HIDDEN_OP = 0;
  var VISIBLE_OP = 0.5;

  function imageSheet(image) {
    var title = image.attr('title');
    if (!title || title == '') {
      title = image.attr('alt');
    }
    if (!title || title == '') {
      title = 'Image';
    }
    image.sheet({
      dialog: true,
      width: 'auto',
      title: title,
      slotClass: 'slate',
      load: function(done, manager) {
        var slot = $(this);
        var imgParts = image.attr('src').split('?');
        var imgSrc = imgParts[0];
        var fullImage = image.clone();
        fullImage.attr('src', imgSrc);
        slot.append(fullImage);
        fullImage.load(function() {
          var width = fullImage.outerWidth() + 30;
          manager.dialog.css('width', width + 'px');
          $.nodePositioner(manager.dialog, image);
        });
      },
      close: function() {
        //settings.close && settings.close();
      },
      open: function(options, manager) {

      }
    });
  }

  function createOverlay(target) {
    var area = $(document.body).div().addClass('image-select has-opener');
    area.append('<a href="#image-select"></a>');
    area.hover(
      function() {
        $(this).animate({ opacity: VISIBLE_OP }, 100);
      },
      function() {
        $(this).animate({ opacity: HIDDEN_OP }, 100);
      }).click(function() {
        var image = $(this).data('image');
        image.click();
        return false;
      });
    var overlay = position(target, area);
    overlay.data('image', target);
    return overlay;

  }

  function position(target, overlay) {
    var topOffset = 0;
    if ($.browser.msie) {
      topOffset = $(document.documentElement).scrollTop();
    }
    var n = box(target);
    return overlay.css({
      position: 'absolute',
      top: (n.top + topOffset) - 10, left: n.left - 10,
      width: n.width + 20, height: n.height + 15,
      opacity: HIDDEN_OP
    });
  }

  function box(n) {
    var r = $.extend(n.box(), n.offset());
    r.bottom = r.top + r.height;
    r.right = r.left + r.width;
    return r;
  };

  var gridConfig = {
    gridParams: {
      caption: 'Resources',
      datatype: 'custom',
      colNames: ['Download', 'Last Updated', 'Details', 'Abstract'],
      colModel: [
        { name: 'download', width: '26%', datatype: 'url' },
        { name: 'updated', width: '13%', datatype: 'datetime', dataformat: 'yyyy-MM-dd', formatter: 'date', formatoptions: { srcformat: "Y-m-d", newformat: "Y-m-d"} },
        { name: 'details', width: '10%', datatype: 'url' },
        { name: 'abstract', width: '50%', datatype: 'multilinetext' }
      ],
      sortable: true,
      height: '100%',
      width: '100%',
      loadcustom: function(gridHelper) {
        var g = global_handler_images;
        g.gridHelper = gridHelper;
        $(g.handler.selectorRoot + g.handler.selectorData, gridHelper.data).each(function() {
          var item = $(this);
          gridHelper.newRow(item.attr('className'));
          $.each(g.gridConfig.gridParams.colModel, function() {
            var c = this;
            gridHelper.addCellWrap($.cqData(item, c.datatype, c.name, c.dataformat), c.name);
          });
          gridHelper.addRow();

        });
        var imageList = gridHelper.dataTable().find('img.image');
        var imageCnt = imageList.length;
        //gridHelper.dataTable().find('img.image').wrap('<div></div>').load(function() {
        var cnt = 0;
        imageList.wrap('<div></div>').load(function(idx) {
          var image = $(this);
          image.click(function() {
            imageSheet($(this));
            return false;
          }).mouseover(function() {
            image.unbind('mouseover');
            createOverlay(image);
          });
          ++cnt;
          if (cnt == imageCnt) {
            $(window).trigger('estradaLoad');
          }
        });
        
      }
    },
    gridId: null,
    navButtons: {},
    customButtons: {},
    topNavigation: true
  };
  var handler = {
    DATA_KEY: null,
    titleCapitalized: 'Images',
    title: 'images',
    className: 'images',
    selectorRoot: '.content-query.images',
    selectorData: ' > ul li',
    selector: function() {
      return this.selectorRoot + this.selectorData;
    },
    gridConfig: {},

    itemClearer: function(config) {
      config.items = [];
    },
    itemLoader: function(config, node) {

    },
    itemCreate: function() {
    },
    // Called when the content query config and handler have been determined
    // by default, takes the items from markup.
    // Parameters:
    // * this -- jQuery object of the main ".content-query" node
    // * config -- the configuration object
    // * done -- the callback to call when loading is complete
    // return -- true, if the loading is asynchronous. If the loading is
    // asynchronous, the content query processing will suspend until the "done"
    // call back is called
    load: function(config, done) {

    },
    // Called to create the overall presentation.
    // Parameters:
    // * config -- the configuration object
    cqRender: function(config) {
      var g = global_handler_images;
      var me = this;
      var gC = global_handler_calendar;
      me.cqConfig = config;
      config.queryJNode.find('>ul').remove();
      var node = config.queryJNode.parent();
      config.queryJNode.addClass(config.handler.className);
      me.view = $.queryFormPhrase(
				{
				  config: config,
				  node: config.queryJNode,
				  categories: !config.categories.isEmpty(),
				  search: !config.search.isEmpty,
				  mode: false,
				  range: !config.range.isEmpty,
				  viewnav: false, //config.layoutHelper.viewnav,
				  extendView: {
				    caption: function(title) {
				      me.gridConfig.gridParams.caption = title;
				    }
				  }
				});

      me.view.gridChange = function(data, done) {
        if (data) {
          g.gridHelper && g.gridHelper.populate(data);
          done();
        }
        else {
          me.gridConfig.grid.change(0);
        }
      }
      me.view.urlChange = function(url, title) {
        config.gridPageMgr.loadUrl(url);
        me.itemClearer(config);
        if (title) {
          me.gridConfig.grid.caption(title);
        }
        me.gridConfig.grid.change(0);
      }
      me.render(node);

    },
    render: function(node) {
      var me = this;
      var g = global_handler_images;
      var gA = $.authoring();
      var gridId = me.cqConfig.key + guid();
      list_jqgrid_globals.gridPageMgr(me.cqConfig);
      var settings = $.extend({
        selector: me.selectorRoot,
        gridConfig: function() {
          g.gridConfig.gridParams = $.extend(true, me.gridConfig.gridParams, me.cqConfig.UiParams);
          return g.gridConfig;
        } (),
        load: function(params) {
          !me.cqConfig.range.isEmpty && me.view.range.update(params.rangeincrement, params.start, params.end);
          me.view.gridParamsReady();
        }
      }, me.cqConfig.gridPageMgr);
      me.gridConfig.gridParams.url = me.cqConfig.postUrl();
      me.gridConfig.grid = me.cqConfig.queryJNode.listjqGrid(gridId, settings);
    }
  };

  var o = {
    DATA_KEY: 'handler_images_data',
    gridConfig: gridConfig,
    handler: handler
  };
  return o;
} ();


$.uiHandler('images', function() {
  var g = global_handler_images;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = $.extend(true, {}, g.gridConfig);
  return handler;
}());


// ***
// jquery.ui-newsletter-issue.js
// Newsletter issue handler

var global_handler_newsletter_issue = {
	DATA_KEY: 'handler_newsletter_issue_data',
	gridConfig: {
	  gridParams: {
	    caption: 'Articles',
      datatype: 'custom',
      loadonce: true,
      colNames:['Title', 'Abstract'],
      colModel:[
        {name:'title', width:300},
        {name:'abstract', width:450}
      ],
      sortable: false,
      height: '100%',
      loadcustom: function(gridHelper) {
        var g = global_handler_newsletter_issue;
        $(gridHelper.handler.selectorData, gridHelper.data).each(function() {
          var item = gridHelper.handler.itemCreate.call($(this));
            gridHelper.newRow($(this).attr('className'));
            gridHelper.addCell(item.title);
            gridHelper.addCell(item.abstract);
            gridHelper.addRow();
		    });
		    gridHelper.data.remove();
        gridHelper.done && gridHelper.done();
      }
    },
    gridId: null,
    navButtons: {},
    customButtons: {}
  },
	handler: {
		titleCapitalized: 'Issue',
		title: 'issue',
		className: 'newsletter',
		selectorData: ' > ul > li',
		itemClearer: null,
		itemLoader: null,
		item: null,
		itemCreate: function() {
			
			var node = $(this);
			// title
			var fieldNode = $("a.title:first", node);
			var title = document.createElement("div");
			fieldNode.clone().prependTo(title);
			// abstract
			fieldNode = $("p.abstract:first", node);
			var abstract = (fieldNode.length > 0) ? fieldNode.html() : "";
			// release date
			var releasedate;
			fieldNode = $("span abbr.releasedate", node);
			if (fieldNode.length == 0) {
					releasedate = new Date();
			}
			else {
				releasedate = $.dates.parse($(fieldNode).attr("title"));
			}

			return {
				title: title,
				releasedate: releasedate,
				abstract: abstract
			};
		},
		// Called when the content query config and handler have been determined
		// by default, takes the items from markup.
		// Parameters:
		// * this -- jQuery object of the main ".content-query" node
		// * config -- the configuration object
		// * done -- the callback to call when loading is complete
		// return -- true, if the loading is asynchronous. If the loading is
		// asynchronous, the content query processing will suspend until the "done"
		// call back is called
		load: null,
		cqRender: function(config) {
		  var me = this;
		  var node = config.queryJNode;
			config.loadDisabled = true;
			me.cqConfig = config;
			me.selectorRoot = config.queryJNode.attr('nodeName').toLowerCase() 
		    + '.' + config.queryJNode.attr('class').replace(' ', '.');
		  me.gridConfig.gridParams.customData = node.find('div.article-list>div.content-query');
			me.gridConfig.gridParams.handler = {
		    selectorRoot: me.selectorRoot,
		    selectorData: me.selectorData,
		    itemCreate: me.itemCreate
		  };
      me.render(node);
		},
		// Called to create the overall presentation.
		// Parameters:
		// * node -- the node containing the 'data' markup for the grid
		// * params -- object containing grid config parameters
		render: function(node) {
		  var me = this;
		  var linkRegex = /link-(\d+)/;
			var gA = $.authoring();
			$.uiHandler('ui-grid').render(node, me.gridConfig);
			gA.getUser(function() {
			  if (gA.user.caps.Author) {
			    $('#' + me.gridConfig.gridId + ' tr').prepend('<td class="itemOrdering">');
			    $('#' + me.gridConfig.gridId).sortable({
			      handle: 'td.itemOrdering',
			      items: 'tr',
			      axis: 'y',
			      scroll: true,
			      forcePlaceholderSize: true,
			      placeholder: 'sort-placeholder',
			      update: function(ev, ui) {
			        var prev = ui.item.prev();
			        var afterLinkId = getLinkId(prev.attr('className'));
			        if (!afterLinkId) afterLinkId = 0;
			        var linkId = getLinkId(ui.item.attr('className'));
			        if (linkId) {
			          var postParams = {
			            after: afterLinkId,
			            linkId: linkId
			          };
			          $.ajah(gA.url.order, postParams, function() {
                });
			        }
			      },
			      start: function(ev, ui) {
			         ui.placeholder.append('<td colspan="3"><div class="marker" /></td>');
			      },
			      change: function(ev, ui) {
			        //console.info(ui.placeholder.html());
			      }
			    });
			  }
			});
			
			function getLinkId(className) {
			  var matched = linkRegex.exec(className);
		    if (matched) {
		      return matched[1];
		    }
			  return null;
			}
		}
	}
};

$.uiHandler('newsletter-issue', function() {
  var g = global_handler_newsletter_issue;
  var handler = g.handler;
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = g.gridConfig;
  return handler;
}());

// ***
// jquery.ui-newsletter-issues.js
// Publication handler

var global_handler_newsletter_issues = {
	DATA_KEY: 'handler_newsletter_issues_data',
	gridConfig: {
	  gridParams: {
	    caption: 'Newsletter Issues',
      datatype: 'custom',
      loadonce: true,
      colNames:['Title', 'Published'],
      colModel:[
        {name:'title', width:300},
        {name:'published', width:200}
      ],
      sortname: 'published',
      sortorder: 'desc',
      height: '100%',
      loadcustom: function(gridHelper) {
        var g = global_handler_newsletter_issues;
        $(gridHelper.handler.selectorData, gridHelper.data).each(function() {
          var item = gridHelper.handler.itemCreate.call(this);
          gridHelper.newRow($(this).attr('className'));
          gridHelper.addCellWrap($(item.title).html(), (item.current ? 'current' : ''));
          gridHelper.addCellWrap($.dates.format(item.published, "yyyy-MM-dd"));
          gridHelper.addRow();
        });
        gridHelper.data.remove();
        gridHelper.done && gridHelper.done();
      }
    },
    gridId: null,
    navButtons: {},
    customButtons: {}
  },
	handler: {
		titleCapitalized: 'Issues',
		title: 'issues',
		className: 'newsletter',
		selectorRoot: '.content-query.issue',
		selectorData: '> li',
		gridConfig: {},
		
		itemClearer: function(config) {
			config.items = [];
		},
		itemLoader: function(config, node) {
		},
		item: function(view, item) {
		},
		itemCreate: function() {
			var node = $(this);
			var id = guid();
			var requiresEdit = false;
			// title
			var fieldNode = $("a.title:first", node);
			var title = document.createElement("div");
			fieldNode.clone().prependTo(title);
      // published
			var published = $.dates.parse($('.published', node).text());
			var isCurrent = $.cqData(node, 'boolean', 'current');
			return {
				title: title,
				published: published,
				current: isCurrent
			};
		},
		// Called when the content query config and handler have been determined
		// by default, takes the items from markup.
		// Parameters:
		// * this -- jQuery object of the main ".content-query" node
		// * config -- the configuration object
		// * done -- the callback to call when loading is complete
		// return -- true, if the loading is asynchronous. If the loading is
		// asynchronous, the content query processing will suspend until the "done"
		// call back is called
		load: function(config, done) {
			if (!config.loadDisabled && config.loadRequired) {
				var me = this;
				config.loadRequired = false;
				$.ajah(config.postUrl(), null,
					function() {
						var list = $(me.selector, this);
						if (list) {
							config.handler.itemLoader(config, list);
						}
						done();
					},
					function() {
						
					}
				);
			}
			else {
				done();
			}
		},
		cqRender: function(config) {
		  var me = this;
			var node = config.queryJNode;
			config.loadDisabled = true;
			me.cqConfig = config;
			me.selectorRoot = config.queryJNode.attr('nodeName').toLowerCase() 
		    + '.' + config.queryJNode.attr('class').replace(' ', '.');
		  me.gridConfig.gridParams.customData = node.find('>ul');
			me.gridConfig.gridParams.handler = {
		    selectorRoot: me.selectorRoot,
		    selectorData: me.selectorData,
		    itemCreate: me.itemCreate
		  };
			me.render(node); 
		},
		// Called to create the overall presentation.
		// Parameters:
		// * node -- the node containing the 'data' markup for the grid
		// * params -- object containing grid config parameters
		render: function(node) {
		  var me = this;
		  $.uiHandler('ui-grid').render(node, me.gridConfig);
		}
	}
};

$.uiHandler('newsletter-issues', function() {
  var g = global_handler_newsletter_issues;
  var handler = g.handler;
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = g.gridConfig;
  return handler;
}());


// ***
// jquery.ui-newsletter.js
// Publication handler

var global_handler_newsletter = {
	DATA_KEY: 'handler_newsletter_data',
	
	 handler: {
		titleCapitalized: 'Issues',
		title: 'issues',
		className: 'newsletter',
		selector: '.content ul:first',
		cqConfig: null,
		itemClearer: function(config) {
		},
		itemLoader: function(config, node) {
		},
		item: function(view, item) {
		},
		itemCreate: function() {

		},
		// Called when the content query config and handler have been determined
		// by default, takes the items from markup.
		// Parameters:
		// * this -- jQuery object of the main ".content-query" node
		// * config -- the configuration object
		// * done -- the callback to call when loading is complete
		// return -- true, if the loading is asynchronous. If the loading is
		// asynchronous, the content query processing will suspend until the "done"
		// call back is called
		load: function(config, done) {
			if (!config.loadDisabled && config.loadRequired) {
				var me = this;
				config.loadRequired = false;
				$.ajah(config.postUrl(), null,
					function() {
						var list = $(me.selector, this);
						if (list) {
							config.handler.itemLoader(config, list);
						}
						done();
					},
					function() {
						
					}
				);
			}
			else {
				done();
			}
		},
		// Called to create the overall presentation.
		// Parameters:
		// * config -- the configuration object
		cqRender: function(config) {
		  var me = this;
		  //var node = config.queryJNode.parent();
		  var node = config.queryJNode;
			me.cqConfig = config;
      me.render(node);
		},
		render: function(node) {
		  var me = this;
		  var g = $.authoring();
		  var main = node.div().attr('id', 'newsletter-main');
		  var toc = node.div().attr('id', 'newsletter-toc');
		  /*
		  toc.append($.div().addClass('l-toc-top').append($.div().addClass('l-toc-top-inner')));
		  toc.append($.div().addClass('l-toc-head').text($('#l-header .header h1').text()));
		  */
		  $('div.previous-issues div.page-url a').each(function() {
		    toc.append($(this).parent().addClass('previous-issues'));
		  });
		  node.div().addClass('float-clear');
		  // format release date
		  $('div.article abbr.releasedate', node).each(function() {
			  var fieldNode = $(this);
				var date = $.dates.parse(fieldNode.attr("title"));
				fieldNode.text($.dates.format(date, "EE, MMM dd, yyyy"));
		  });
		  $('div.article', node).each(function() {
		    var article = $(this);
		    var id = $.guid();
		    article.attr('id', id);
		    article.wrap($.div().attr('id', 'holder-' + id));
		    $('a.title', article).each(function() {
		      var link = $(this);
		      link.after($.div().addClass('main-title').text(link.text()));
		    });
		  });
		  // link behavior
		  $('div.article a.title', node).each(function() {
			  $(this).click(function() {
			    view($(this));
			    return false;
			  });
		  });
		  $('.article-list > .content-query', node).appendTo(toc);
		  if (g.query['a-id']) {
		    $('#newsletter-toc li.' + g.query['a-id'] + ' .article a.title').click();
		  }
		  else {
		    $('#newsletter-toc .article:first a.title').click();
		  }
		  var layoutHelper = me.cqConfig.layoutHelper;
		  layoutHelper.authoring.getUser(function() {
		    if (layoutHelper.authoring.user.isAuthenticated) {
		      layoutHelper.layout.bind("email-newsletter").to(document);
		    }
		  });
		  regBehavior(me.DATA_KEY, me.cqConfig);
		  function view(node) {
		    var article = node.parent();
		    var main = $('#newsletter-main');
		    $('.article', main).each(function() {
		      var a = $(this);
		      var id = a.attr('id');
		      $('#holder-' + id).append(a);
		    });
		    main.append(article);
		  }
		}
	}
};

$.uiHandler('newsletter', function() {
  var g = global_handler_newsletter;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  return handler;
}());

function regBehavior(dataKey, config) {
  estrada.behavior('email-newsletter').is({
    bind: function(node) {
      node = $(node);
      var gA = $.authoring();
      
      var key = dataKey + '.' + 'to';
      var to = $.storage.user(key);
      
      if (!to) {
        to = '';
      }

      var status = $.div().addClass('status');

      var link = $.dom('a').attr('href', '#').text('Email Newsletter');
      var n = $.dom('span').attr('id', 'email-page').append(link);
      var params = {
        title: 'Email Newsletter',
        className: 'email-newsletter',
        custom: function(node) {
          $('.form-body', node).div()
            .append('<input type="hidden" name="subject" value="' + 'EES Newsletter' + '" />')
            .append('<input type="hidden" name="body" value=\'' + emailLayout() + '\' />')
            .append('<label for="to">Email To</label>')
            .append('<input type="text" class="to" id="to" name="to" value="' + to + '" />');
        },
        handler: function(form) {
          var o = {
            html: true,
            to: $('input[name=to]', form).val(),
            subject: $('input[name=subject]', form).val(),
            body: $('input[name=body]', form).val()
          };
          return o;
        }
      };
      link.click(function() {
        link.sheet($.presentationHelper.behaviors.sheet_emaildefault(params));
        return false;
      });
      gA.customPhrase.add($.div().append(n));
      
      function emailLayout() {
        if (config.layoutHelper.emailNewsletterLayout) {
          return config.layoutHelper.emailNewsletterLayout();
        }
        return '';
      }
    }
  });
};


// ***
// jquery.ui-publication.js
// Publication handler

var global_handler_publication = {
	DATA_KEY: 'handler_publication_data',
	gridConfig: {
	  gridParams: {
	    caption: 'Articles',
      datatype: 'custom',
      colNames:['Title', 'Release Date', 'Abstract'],
      colModel:[
        {name:'title', width:'26%'}, //200
        {name:'releasedate', width:'13%', formatter: 'date', formatoptions:{srcformat:"Y-m-d",newformat:"Y-m-d"}},
        {name:'abstract', width:'60%'}
      ],
      sortable: true,
      height: '100%',
      width: '100%',
      loadcustom: function(gridHelper) {
        var g = global_handler_publication;
        g.gridHelper = gridHelper;
        $(g.handler.selectorRoot + g.handler.selectorData, gridHelper.data).each(function() {
          var item = g.handler.itemCreate.call(this);
          gridHelper.newRow($(this).attr('className'));
          gridHelper.addCellWrap($(item.title).html());
          gridHelper.addCellWrap($.dates.format(item.releasedate, "yyyy-MM-dd"));
          gridHelper.addCellWrap(item.abstract);
          gridHelper.addRow();
        });
      }
    },
    gridId: null,
    navButtons: {},
    customButtons: {},
    topNavigation: true
	},
	handler: {
	  DATA_KEY: null,
		titleCapitalized: 'Articles',
		title: 'articles',
		className: 'publication',
		selectorRoot: '.content-query.articles',
		selectorData: ' > ul li',
		selector: function() {
	    return this.selectorRoot + this.selectorData;
		},
		gridConfig: {},
		
		itemClearer: function(config) {
			config.items = [];
		},
		itemLoader: function(config, node) {
			var items = [];
			$('li', node).each(function() {
				var item = $(this);
				items.push(item);
			});
			config.items = items;
		},
		itemCreate: function() {
			var node = $(this);
			var id = guid();
			var requiresEdit = false;
			// title
			var fieldNode = $("a.title:first", node);
			var title = document.createElement("div");
			fieldNode.clone().prependTo(title);
			// abstract
			fieldNode = $(".abstract:first", node);
			var abstract = (fieldNode.length > 0) ? fieldNode.html() : "";
			// release date
			var releasedate = $.dates.parse($('.releasedate', node).text());
			
			return {
				title: title,
				abstract: abstract,
				releasedate: releasedate
			};
		},
		// Called when the content query config and handler have been determined
		// by default, takes the items from markup.
		// Parameters:
		// * this -- jQuery object of the main ".content-query" node
		// * config -- the configuration object
		// * done -- the callback to call when loading is complete
		// return -- true, if the loading is asynchronous. If the loading is
		// asynchronous, the content query processing will suspend until the "done"
		// call back is called
		load: function(config, done) {
			if (!config.loadDisabled && config.loadRequired) {
				var me = this;
				config.loadRequired = false;
				$.ajah(config.postUrl(), null,
					function() {
						var list = $(me.selector, this);
						if (list) {
							config.handler.itemLoader(config, list);
						}
						done();
					},
					function() {
						
					}
				);
			}
			else {
				done();
			}
		},
		// Called to create the overall presentation.
		// Parameters:
		// * config -- the configuration object
		cqRender: function(config) {
		  var g = global_handler_publication;
		  var me = this;
			var gC = global_handler_calendar;
			me.cqConfig = config;
			var node = config.queryJNode.parent();
			//var node = config.queryJNode;
			config.queryJNode.addClass(config.handler.className);
			me.view = $.queryFormPhrase(
				{
					config: config,
					node: config.queryJNode,
					categories: !config.categories.isEmpty(),
					search: !config.search.isEmpty,
					range: !config.range.isEmpty,
					viewnav: config.layoutHelper.viewnav,
					extendView: {
					  caption: function(title) {
					    me.gridConfig.gridParams.caption = title;
					  }
					}
				});
				
			me.view.state.mode = 'list';
			me.view.gridChange = function(data, done) {
			  me.itemClearer(config);
			  if (data) {
			    if (data.start) {
			      // range
			      !config.range.isEmpty && me.gridConfig.grid.change(range);
			    }
			    else {
				    g.gridHelper && g.gridHelper.populate(data);
				    done();
				  }
				}
				else {
				  me.gridConfig.grid.change(0);
				}
			}
			me.view.urlChange = function(url, title) {
			  window.location.assign(url);
			}
			me.render(node);

		},
		render: function(node) {
		  var me = this;
		  var g = $.authoring();
		  var gridId = me.cqConfig.key + guid();
		  list_jqgrid_globals.gridPageMgr(me.cqConfig);
		  var settings = $.extend({
		      selector: me.selectorRoot,
		      gridConfig: me.gridConfig,
		      load: function(params) {
					  !me.cqConfig.range.isEmpty && me.view.range.update(params.rangeincrement, params.start, params.end);
					  me.view.gridParamsReady();
				  }
			  }, me.cqConfig.gridPageMgr); // me.cqConfig.gridPageMgr);
		  me.gridConfig.gridParams.url = me.cqConfig.postUrl();
		  me.gridConfig.grid = me.cqConfig.queryJNode.listjqGrid(gridId, settings);
		}
	}
};


$.uiHandler('articles', function() {
  var g = global_handler_publication;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = $.extend(true, {}, g.gridConfig);
  return handler;
}());

$.uiHandler('articles2', function() {
  var g = global_handler_publication;
  var handler = $.extend(true, {}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = $.extend({}, g.gridConfig);
  handler.gridConfig.gridParams = $.extend({}, handler.gridConfig.gridParams,
    {
      colNames:[''],
      colModel:[
        {name:'title', width:'100%'}
      ],
      loadcustom: function(gridHelper) {
        g.gridHelper = gridHelper;
        $(handler.selectorRoot + handler.selectorData, gridHelper.data).each(function() {
          var item = handler.itemCreate.call(this);
          gridHelper.newRow($(this).attr('className'));
          var holder = $.div();
          holder.div().addClass('title').html($(item.title).html());
          holder.div().addClass('abstract').html(item.abstract);
          var info = holder.div().addClass('info');
          info.dom('span').addClass('label').text('Release Date:');
          info.dom('span').text($.dates.format(item.releasedate, "MM-dd-yyyy hh:mm A"));
          gridHelper.addCellWrap(holder);
          gridHelper.addRow();
        });
      }
    });
  return handler;
}());

// ***
// jquery.ui-quickpoll-mgt.js
// Newsletter issue handler

var global_handler_quickpollmgt = {
  DATA_KEY: 'handler_quickpollmgt_data',
  gridConfig: {
    gridParams: {
      caption: 'Quickpoll',
      datatype: 'customstring',
      loadonce: true,
      colNames: ['Choice'],
      colModel: [
        { name: 'choice', width: '100%' }
      ],
      sortable: false,
      height: '100%',
      loadcustom: function(gridHelper) {
        var g = global_handler_quickpollmgt;
        var dataNode = g.handler.cqConfig.dataNode;
        $('.choices ul:first > li', dataNode).each(function(j) {
          var item = $(this);
          gridHelper.newRow(item.attr('className'));
          // title
          var link = item.find('a.title:first');
          gridHelper.addCellWrap(link.outerHtml());
          gridHelper.addRow();
        });

        dataNode.remove();
        gridHelper.done && gridHelper.done();
      }
    },
    gridId: null,
    navButtons: {},
    customButtons: {}
  },
  handler: {
    titleCapitalized: 'Quickpoll',
    title: 'quickpoll',
    className: 'quickpoll',
    selector: '.content ul:first',

    itemClearer: null,
    itemLoader: null,
    item: null,
    itemCreate: function() {

      var node = $(this);
      // title
      var fieldNode = $("a.title:first", node);
      var title = document.createElement("div");
      fieldNode.clone().prependTo(title);
      // abstract
      fieldNode = $("p.abstract:first", node);
      var abstract = (fieldNode.length > 0) ? fieldNode.html() : "";
      // release date
      var releasedate;
      fieldNode = $("span abbr.releasedate", node);
      if (fieldNode.length == 0) {
        releasedate = new Date();
      }
      else {
        releasedate = $.dates.parse($(fieldNode).attr("title"));
      }

      return {
        title: title,
        releasedate: releasedate,
        abstract: abstract
      };
    },
    // Called when the content query config and handler have been determined
    // by default, takes the items from markup.
    // Parameters:
    // * this -- jQuery object of the main ".content-query" node
    // * config -- the configuration object
    // * done -- the callback to call when loading is complete
    // return -- true, if the loading is asynchronous. If the loading is
    // asynchronous, the content query processing will suspend until the "done"
    // call back is called
    load: null,
    cqRender: function(config) {
      var me = this;
      var node = config.queryJNode.parent();
      config.loadDisabled = true;
      me.cqConfig = config;
      me.render(node);
    },
    // Called to create the overall presentation.
    // Parameters:
    // * node -- the node containing the 'data' markup for the grid
    // * params -- object containing grid config parameters
    render: function(node) {
      var me = this;
      var linkRegex = /link-(\d+)/;
      var gA = $.authoring();
      $.uiHandler('ui-grid').render(node, me.gridConfig);
      gA.getUser(function() {
        if (gA.user.caps.Author) {
          $('#' + me.gridConfig.gridId + ' tr').prepend('<td class="itemOrdering">');
          $('#' + me.gridConfig.gridId).sortable({
            handle: 'td.itemOrdering',
            items: 'tr',
            axis: 'y',
            scroll: true,
            forcePlaceholderSize: true,
            placeholder: 'sort-placeholder',
            update: function(ev, ui) {
              var prev = ui.item.prev();
              var afterLinkId = getLinkId(prev.attr('className'));
              if (!afterLinkId) afterLinkId = 0;
              var linkId = getLinkId(ui.item.attr('className'));
              if (linkId) {
                var postParams = {
                  after: afterLinkId,
                  linkId: linkId
                };
                $.ajah(gA.url.order, postParams, function() {
                });
              }
            },
            start: function(ev, ui) {
              ui.placeholder.append('<td colspan="3"><div class="marker" /></td>');
            },
            change: function(ev, ui) {
              //console.info(ui.placeholder.html());
            }
          });
        }
      });

      function getLinkId(className) {
        var matched = linkRegex.exec(className);
        if (matched) {
          return matched[1];
        }
        return null;
      }
    }
  }
};

$.uiHandler('quickpoll-mgt', function() {
  var g = global_handler_quickpollmgt;
  var handler = g.handler;
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = g.gridConfig;
  return handler;
}());

// ***
// jquery.ui-quickpoll.js
// Publication handler

var global_handler_quickpoll = {
  DATA_KEY: 'handler_quickpoll_data',

  handler: {
    titleCapitalized: 'QuickPoll',
    title: 'quickpoll',
    className: 'quickpoll',
    selector: 'div.quickpoll',
    cqConfig: null,
    // Called to create the overall presentation.
    // Parameters:
    // * config -- the configuration object
    cqRender: function(config) {
      var me = this;
      //var node = config.queryJNode.parent();
      var node = config.queryJNode;
      me.cqConfig = config;
      me.render(node);
    },
    render: function(node) {
      var pageIdRegex = /id-(\d+)/;
      var me = this;
      var g = $.authoring();
      //var idx = 0;
      var qpId = 0;
      var qp = me.cqConfig.dataNode;
      if (!qp) {
        qp = node.is(me.selector) ? node : $(me.selector, node);
      }
      if (qp.length == 0) return;
      var list = qp.find('>ul');
      if (list.length > 0) {
        list.find('>li').each(function() {
          var item = $(this);
          qpId = getPageId(item.attr('class'));
          buildQuickPoll(qpId, item);
        });
      }
      else {
        qpId = g.meta.identifier;
        buildQuickPoll(qpId, qp);
      }
      
      function buildQuickPoll(quickPollId, qpItem) {
        if (quickPollId && (quickPollId > 0)) {
          var multiple = $.cqData3(qpItem, 'boolean', 'div.multiple', null, true);
          var type = multiple ? 'checkbox' : 'radio';
          var submitTitle = $.cqData3(qpItem, 'singlelinetext', 'div.submittitle', null, true);
          if (!submitTitle) {
            submitTitle = "Submit Vote";
          }
          var area = qpItem.find('div.choices:first');
          area.find('li').each(function(idx) {
            var holder = $(this);
            var text = holder.myText();
            var id = 'qpChoice' + idx;
            var name = 'qpChoice';
            holder.empty();
            holder.append('<input type="' + type + '" id="' + id + '" name="' + name + '" value="' + idx + '" />');
            holder.append('<label for="' + id + '">' + text + '</label>');
          });
          var submit;
          $.ajah(g.url.quickpoll + '?page=' + quickPollId, null, function() {
            var result = $('.suppress', this);
            if (result.length > 0) {
              submit = $('<span class="suppress">Vote again in ' + result.text() + '</span>');
            }
            var qpResults = $('.qpResults', this);
            if (qpResults.length > 0) {
              showResults(qpItem, qpResults, true);
            }
            handleSubmit();
          });
          function handleSubmit() {
            if (!submit) {
              submit = $('<input type="button" value="' + submitTitle + '" />');
              submit.click(function() {
                var val = '';
                var cnt = 0;
                $('input:checked', area).each(function() {
                  ++cnt;
                  val += ((cnt > 1) ? ',' : '') + $(this).val();
                });
                var settings = {
                  url: g.url.quickpoll,
                  params: {
                    page: quickPollId,
                    val: val
                  },
                  callback: function(res, spinner) {
                    var result = $('.error', this);
                    if (result.length > 0) {
                      spinner.messageHtml('<h3>Error Occurred</h3>' + result.html());
                    }
                    else {
                      spinner.remove();
                    }
                    var suppress = $('.suppress', this).text();
                    var parent = submit.parent();
                    if (suppress) {
                      submit.remove();
                      parent.append($('<span class="suppress">Vote again in ' + suppress + '</span>'));
                    }
                    var qpResults = $('.qpResults', this);
                    if (qpResults.length > 0) {
                      showResults(qpItem, qpResults, true);
                    }
                  },
                  method: 'post',
                  indicator: true,
                  indicatorNode: area.find('.command'),
                  indicatorMsg: 'Processing vote...',
                  indicatorPos: 'over'
                };
                $.ajah(settings);
              });
            }
            area.div().addClass('command').append(submit);
          }
        }
      }

      function showResults(parent, qpResults, opened) {
        parent.find('table.qpResults').remove();
        var table = parent.tabledom('class="qpResults"');
        table.cell('');
        table.cell('Votes', 'class="votes"');
        table.row();
        var items = [];
        var maxPerc = 0;
        qpResults.find('li').each(function(idx) {
          var li = $(this);
          var perc = parseInt(li.find('div.perc').text());
          if (perc > maxPerc) maxPerc = perc;
          items.push({
            title: li.attr('title'),
            perc: perc,
            votes: li.find('div.votes').text()
          });
        });
        $.each(items, function(idx, result) {
          var percWidth = ((result.perc > 0) ? (100 - (maxPerc - result.perc)) : '0') + '%';
          var perc = result.perc + '%';
          var indicator = $.div();
          indicator.div().addClass('title').text(result.title);
          indicator.div().addClass('indicator qpr' + (idx + 1)).css('width', percWidth).dom('span').text(perc);
          table.cell(indicator, 'class="indicator"');
          table.cell(result.votes, 'class="votes"');
          table.row();
        });
        qpResults.find('div.total').each(function() {
          var node = $(this);
          var cnt = node.text();
          table.cell('Total Votes: ' + cnt, 'colspan="2" class="totals"');
          table.row();
        });
      }
      
      function getPageId(className) {
	      var matched = pageIdRegex.exec(className);
        if (matched) {
          return matched[1];
        }
	      return null;
	    }

      function makeId(name) {
        return multiple ? name + '-' + ++idx : name;
      }
    }
  }
};

$.uiHandler('quickpoll', function() {
  var g = global_handler_quickpoll;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  return handler;
}());



// ***
// jquery.ui-resources.js
// resources handler

var global_handler_resources = {
  DATA_KEY: 'handler_resources_data',
  gridConfig: {
    gridParams: {
      caption: 'Resources',
      datatype: 'custom',
      colNames: ['Download', 'Last Updated', 'Details', 'Abstract'],
      colModel: [
        { name: 'download', width: '26%', datatype: 'url' },
        { name: 'updated', width: '13%', datatype: 'datetime', dataformat: 'yyyy-MM-dd', formatter: 'date', formatoptions: { srcformat: "Y-m-d", newformat: "Y-m-d"} },
        { name: 'details', width: '10%', datatype: 'url' },
        { name: 'abstract', width: '50%', datatype: 'multilinetext' }
      ],
      sortable: true,
      height: '100%',
      width: '100%',
      loadcustom: function(gridHelper) {
        var g = global_handler_resources;
        g.gridHelper = gridHelper;
        $('.content-query #content .folders li', gridHelper.data).each(function() {
          var item = $(this);
          gridHelper.newRow('folder');
          $.each(g.gridConfig.gridParams.colModel, function(idx, colItem) {
            gridHelper.addCellWrap($.cqData(item, colItem.datatype, ((idx == 0) ? 'title' : colItem.name), colItem.dataformat));
          });
          gridHelper.addRow();
        });
        $(g.handler.selectorRoot + g.handler.selectorData, gridHelper.data).each(function() {
          var item = $(this);
          gridHelper.newRow(item.attr('className'));
          $.each(g.gridConfig.gridParams.colModel, function() {
            var c = this;
            gridHelper.addCellWrap($.cqData(item, c.datatype, c.name, c.dataformat));
          });
          gridHelper.addRow();

        });
      }
    },
    gridId: null,
    navButtons: {},
    customButtons: {},
    topNavigation: true
  },
  handler: {
    DATA_KEY: null,
    titleCapitalized: 'Resources',
    title: 'resources',
    className: 'resources',
    selectorRoot: '.content-query.resources',
    selectorData: ' > ul li',
    selector: function() {
      return this.selectorRoot + this.selectorData;
    },
    gridConfig: {},

    itemClearer: function(config) {
      config.items = [];
    },
    itemLoader: function(config, node) {
      var items = [];
      $('li', node).each(function() {
        var item = $(this);
        items.push(item);
      });
      config.items = items;
    },
    itemCreate: function() {
    },
    // Called when the content query config and handler have been determined
    // by default, takes the items from markup.
    // Parameters:
    // * this -- jQuery object of the main ".content-query" node
    // * config -- the configuration object
    // * done -- the callback to call when loading is complete
    // return -- true, if the loading is asynchronous. If the loading is
    // asynchronous, the content query processing will suspend until the "done"
    // call back is called
    load: function(config, done) {
      if (!config.loadDisabled && config.loadRequired) {
        var me = this;
        config.loadRequired = false;
        $.ajah(config.postUrl(), null,
					function() {
					  var list = $(me.selector, this);
					  if (list) {
					    config.handler.itemLoader(config, list);
					  }
					  done();
					},
					function() {

					}
				);
      }
      else {
        done();
      }
    },
    // Called to create the overall presentation.
    // Parameters:
    // * config -- the configuration object
    cqRender: function(config) {
      var g = global_handler_resources;
      var me = this;
      var gC = global_handler_calendar;
      me.cqConfig = config;
      var node = config.queryJNode.parent();
      config.queryJNode.addClass(config.handler.className);
      me.view = $.queryFormPhrase(
				{
				  config: config,
				  node: config.queryJNode,
				  categories: !config.categories.isEmpty(),
				  search: !config.search.isEmpty,
				  mode: false,
				  range: !config.range.isEmpty,
				  viewnav: false, //config.layoutHelper.viewnav,
				  descendants: (config.descendants != null),
				  extendView: {
				    caption: function(title) {
				      me.gridConfig.gridParams.caption = title;
				    }
				  }
				});

      me.view.gridChange = function(data, done) {
        if (data) {
          g.gridHelper && g.gridHelper.populate(data);
          done();
        }
        else {
          me.gridConfig.grid.change(0);
        }
      }
      me.view.urlChange = function(url, title) {
        config.gridPageMgr.loadUrl(url);
        me.itemClearer(config);
        if (title) {
          me.gridConfig.grid.caption(title);
        }
        me.gridConfig.grid.change(0);
      }
      me.render(node);

    },
    render: function(node) {
      var me = this;
      var g = global_handler_resources;
      var gA = $.authoring();
      var gridId = me.cqConfig.key + guid();
      //list_jqgrid_globals.gridPageMgr(me.cqConfig);
      list_jqgrid_globals.gridPageMgr(me.view.config);
      var settings = $.extend({
        selector: me.selectorRoot,
        gridConfig: function() {
          g.gridConfig.gridParams = $.extend(true, me.gridConfig.gridParams, me.cqConfig.UiParams);
          return me.gridConfig;
        } (),
        load: function(params) {
          !me.cqConfig.range.isEmpty && me.view.range.update(params.rangeincrement, params.start, params.end);
          me.view.gridParamsReady();
        }
      }, me.cqConfig.gridPageMgr);
      me.gridConfig.gridParams.url = me.cqConfig.postUrl();
      me.gridConfig.grid = me.cqConfig.queryJNode.listjqGrid(gridId, settings);
    }
  }
};

$.uiHandler('resources', function() {
  var g = global_handler_resources;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  handler.gridConfig = $.extend(true, {}, g.gridConfig);
  return handler;
}());


// ***
// jquery.ui-rotating-images-1.js
// Rotating Images 1 handler

var global_handler_rotating_images_1 = {
	DATA_KEY: 'handler_rotating_images_1_data',
	handler: {
		titleCapitalized: 'Image',
		title: 'image',
		className: 'rotating-image-1',
		selector: '.content ul:first',
		itemClearer: null,
		itemLoader: null,
		item: null,
		itemCreate: function() {
			
			var node = $(this);
			// title
			var fieldNode = $("a.title:first", node);
			var title = document.createElement("div");
			fieldNode.clone().prependTo(title);
			// abstract
			fieldNode = $("p.abstract:first", node);
			var abstract = (fieldNode.length > 0) ? fieldNode.html() : "";
			// release date
			var releasedate;
			fieldNode = $("span abbr.releasedate", node);
			if (fieldNode.length == 0) {
					releasedate = new Date();
			}
			else {
				releasedate = $.dates.parse($(fieldNode).attr("title"));
			}

			return {
				title: title,
				releasedate: releasedate,
				abstract: abstract
			};
		},
		// Called when the content query config and handler have been determined
		// by default, takes the items from markup.
		// Parameters:
		// * this -- jQuery object of the main ".content-query" node
		// * config -- the configuration object
		// * done -- the callback to call when loading is complete
		// return -- true, if the loading is asynchronous. If the loading is
		// asynchronous, the content query processing will suspend until the "done"
		// call back is called
		load: null,
		cqRender: function(config) {
		  var me = this;
		  var node = config.queryJNode;
			config.loadDisabled = true;
			me.cqConfig = config;
      me.render(node);
		},
		// Called to create the overall presentation.
		// Parameters:
		// * node -- the node containing the 'data' markup for the grid
		// * params -- object containing grid config parameters
		render: function(node) {
		  var me = this;
			var gA = $.authoring();
			var header = $('>h3,>a', node);
			var behaviorNode;
			if (header.length > 0) {
			  behaviorNode = node;
			}
			var items = $('li', me.cqConfig.queryJNode);
			//var idx = Math.floor(items.length * Math.random() + 1);
			var idx = Math.floor(items.length * Math.random());
			var current = items.eq(idx);
			var link  = $('<a href="' + current.find('.pageurl').text() + '" />');
			link.append(current.find('img'));
			if (behaviorNode) {
			  $('*:not(' + header.attr('nodeName') + ')', behaviorNode).remove();
			  behaviorNode.append(link);
			}
			else {
			  behaviorNode = link;
			}
			var pU = $.presentationHelper.uiHandler;
			var storageKeys = pU.getStorageKeys(node);
			if (storageKeys) {
			  pU.add(storageKeys.bucket, storageKeys.key, behaviorNode);
		  }
		}
	}
};
      
$.uiHandler('rotating-images-1', function() {
  var g = global_handler_rotating_images_1;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  return handler;
}());

// ***
// jquery.ui-slideshow.js
var global_handler_slideshow = {
	DATA_KEY: 'handler_slideshow_data',
	handler: {
	  DATA_KEY: null,
		titleCapitalized: 'Slideshow',
		title: 'slideshow',
		className: 'slideshow',
		selectorRoot: '.content-query',
		selectorData: ' > ul li',
		itemClearer: null,
		itemLoader: null,
		item: null,
		items: null,
		itemCnt: 0,
		itemCreate: null,
		limit: 0,
		currentIdx: -1,
		intervalId: 0,
		rotateId: 0,
		areas: {},
		// Called when the content query config and handler have been determined
		// by default, takes the items from markup.
		// Parameters:
		// * this -- jQuery object of the main ".content-query" node
		// * config -- the configuration object
		// * done -- the callback to call when loading is complete
		// return -- true, if the loading is asynchronous. If the loading is
		// asynchronous, the content query processing will suspend until the "done"
		// call back is called
		load: function(config, done) {
			if (!config.loadDisabled && config.loadRequired) {
				var me = this;
				config.loadRequired = false;
				$.ajah(config.postUrl(), null,
					function() {
						var list = $(me.selector, this);
						if (list) {
							config.handler.itemLoader(config, list);
						}
						done();
					},
					function() {
						
					}
				);
			}
			else {
				done();
			}
		},
		// Called to create the overall presentation.
		// Parameters:
		// * config -- the configuration object
		cqRender: function(config) {
		  var me = this;
			var node = config.queryJNode;
			config.loadDisabled = true;
			me.cqConfig = config;
      me.render(node);
		},
		render: function(node) {
		  var me = this;
		  var itemTimeoutId;
		  var g = global_handler_slideshow;
		  var pU = $.presentationHelper.uiHandler;
			var storageKeys = pU.getStorageKeys(node);
			//var behaviorNode = node;
			var holder = $.div().addClass('news-reel');
			me.areas.upper = $('<div id="l-upper">');
			var upper = me.areas.upper;
			upper.append('<div id="upper1" class="item">');
      upper.append('<div id="upper2" class="item">');
      upper.append('<div id="upper3" class="item">');
      upper.append('<div id="upper4" class="item">');
		  holder.append(upper);

      me.areas.lower = $('<div id="l-lower">');
      var lower = me.areas.lower;
      lower.append('<div id="lower1" class="item sp-grad"><span class="sp-ctrls indicator"></span><div class="slot"></div></div>');
      lower.append('<div id="lower2" class="item sp-grad"><span class="sp-ctrls indicator"></span><div class="slot"></div></div>');
      lower.append('<div id="lower3" class="item sp-grad"><span class="sp-ctrls indicator"></span><div class="slot"></div></div>');
      lower.append('<div id="lower4" class="item sp-grad"><span class="sp-ctrls indicator"></span><div class="slot"></div></div>');
      lower.append('<div class="clear-float" />');
      holder.append(lower);

      lower.find('.item').hover(
        function() {
          var current = $(this);
          itemTimeoutId = window.setTimeout(function() {
            me.moveTo(current);
            me.stopRotation();
				  }, 300);
          
        },
        function() {
          clearTimeout(itemTimeoutId);
        }
      );
      me.items = [];
			node.find('li').each(function(idx) {
			  var itemNode = $(this);
			  var linkForItem = $('<a href="' + itemNode.find('.pageurl').text() + '" />');
			  var item = {};
			  item.subtitle = linkForItem.clone();
			  item.subtitle.append(itemNode.find('.subtitle'));
			  item.abstract = itemNode.find('.abstract');
			  item.mainImg = linkForItem.clone();
			  item.mainImg.append(itemNode.find('img.primaryimage'));
			  item.secImg = linkForItem.clone();
			  item.secImg.append(itemNode.find('img.secondaryimage'));
			  var link = linkForItem.clone();
			  var sl = itemNode.find('.secondarylink');
			  link.text(sl.text());
			  sl.text('');
			  item.secLink = sl.append(link);
			  me.items.push(item);
			  ++me.itemCnt;
			});
			if (me.itemCnt > 4) {
			  var nav = $('<div id="l-slider" />');
			  info = $('<div class="info" />');
			  navarea = {};
			  navarea.info = info;
			  nav.append(info);
        nav.append($('<div class="pager"><button class="sp-ctrls prev" /><button class="sp-ctrls next" /></div>'));
        nav.append($('<div class="clear-float" />'));
        holder.append(nav);
        navarea.next = nav.find('.next');
        navarea.next.click(function() {
          me.renderSet();
        });
        navarea.prev = nav.find('.prev');
        navarea.prev.click(function() {
          for (var i = 0; i < 8; i++) {
            --me.currentIdx;
            if (me.currentIdx < 0) me.currentIdx = (me.itemCnt-1);
          }
          me.renderSet();
        });
        me.areas.navarea = navarea;
      }   
      me.renderSet();
			node.parent().remove();
			if (storageKeys) {
			  pU.add(storageKeys.bucket, storageKeys.key, holder);
		  }
		},
		setCurrentToNext: function() {
		  var me = this;
		  ++me.currentIdx;
	    if ((me.currentIdx+1) > me.itemCnt) {
	      me.currentIdx = 0;
	    }
		},
		stopRotation: function() {
		  var me = this;
		  if (me.intervalId > 0) {
		    clearInterval(me.intervalId);
		    me.intervalId = 0;
		  }
		},
		moveToNext: function() {
		  var me = this;
		  ++me.rotateId;
		  if (me.rotateId > me.limit) {
		    me.rotateId = 1;
		  }
		  me.moveTo(me.areas.lower.find('#lower' + me.rotateId));
		},
		moveTo: function(current) {
		  var me = this;
		  var idx = current.attr('id').replace('lower', '');
      me.areas.upper.find('.current').removeClass('current');
      me.areas.upper.find('#upper' + idx).addClass('current');
      me.areas.lower.find('.current').removeClass('current');
      current.addClass('current');
		},
		renderSet: function() {
		  var me = this;
		  var start;
		  var infoText;
		  me.limit = (me.itemCnt > 4) ? 4 : me.itemCnt;
		  for (var i = 0; i < me.limit; i++) {
		    var idx = (i+1);
		    me.setCurrentToNext();
		    if (i == 0) {
		      start = me.currentIdx;
		      infoText = (me.currentIdx+1);
		    }
		    else {
		      infoText += ', ' + (me.currentIdx+1);
		    }
		    
		    var item = me.items[me.currentIdx];
        // add to upper area
        var el = me.areas.upper.find('#upper' + idx);
        el.empty();
        el.append(item.mainImg);
        el.append(item.subtitle);
        el.append(item.abstract);
        // add to lower area
        el = me.areas.lower.find('#lower' + idx);
        var slot = el.find('.slot');
        slot.empty();
        slot.append($('<div id="l-img">').append(item.secImg));
        slot.append($('<div id="l-link">').append(item.secLink));
		  }
		  if (me.areas.navarea && me.areas.navarea.info) {
		    var infoText;
		    if (me.currentIdx > start) {
		      infoText = (start+1) + ' - ' + (me.currentIdx+1);
		    }
		    infoText += '  of  ' + me.itemCnt;
		    me.areas.navarea.info.text(infoText);
		  }
		  me.stopRotation();
		  me.rotateId = 0;
		  if (me.itemCnt > 0) {
		    me.moveToNext();
		    if (me.itemCnt > 1) {
          me.intervalId = setInterval(function() {
            me.moveToNext();
            }, 5000);
        }
      }
		}
	}
};

$.uiHandler('slideshow', function() {
  var g = global_handler_slideshow;
  var handler = $.extend({}, g.handler);
  handler.DATA_KEY = g.DATA_KEY;
  return handler;
}());



// ***
// jquery.ui-viewurl.js

plugin('uiViewUrl', function(settings) {
       
  settings = $.extend(
    {
      msg: '',
      title: 'Confirm',
      ok: 'OK',
      cancel: 'CANCEL',
      width: 900,
      height: 300,
      confirm: nop,
      negative: nop
    }, settings || {});

  var slot;
  var close;
  var o = {
    dialog: false,
    resizeable: true,
    SHEET_KEY: 'viewurl',
    title: settings.title,
    width: settings.width,
    height: settings.height,
    volatile: true,
    load: function(end, manager) {
      // save close so that "assign" could use it
      slot = $(this);
      close = manager.close;
      var holder = this.div().addClass('ui-viewurl').addClass('slate');
      holder.html('<iframe src="' + settings.url + '" />');
      manager.command.phrase({
        load: function() {
          if (settings.ok && settings.ok.length) {
            this.button(settings.ok, 'ok', function(done) {
              var button = $(this);
              close();
              settings.confirm();
            }, true);
          }
          if (settings.cancel && settings.cancel.length) {
            this.button(settings.cancel, 'cancel', function(done) {
              close();
              settings.negative();       
            }, true);
          }
        }
      });
      settings.load && settings.load.call(slot, end, manager);
      return false;
    }
  };
  return o;
});

plugin('uiInfo', function(msg) {
  $(this).sheet($.uiConfirm({
    title: 'Alert',
    cancel: '',
    msg: msg
  }));
});
// ***
// jquery.xoxoData.js
plugin('xoxoData', function(item) {
  
  var o = {
    title: item.myText()
  };
  item.find('dt').each(function() {
    var dt = $(this);
    o[dt.text()] = dt.next().text();
  });
  return o;
});



// ***
// jquery.z-authoring.js
// Note: "z" prefix is to ensure that this file is linked last into core.js
// provides authoring UI bootstrap and login handler
// This is not a typical plugin. All of the functionality is
// handled at startup on every load. However, you can use $.authoring to
// get current authoring settings

// Glossary:
// * Bootstrap Criteria -- determine if authoring needs to be loaded
// * Authoring Criteria -- determine user role

// since this script is only processed when HEAD had already loaded, start
// processing meta and link elements.


// TODO(glazkov): see if we could load authoring CSS dynamically
// TODO(glazkov): add keyboard shortcuts: Ctrl+Shift+E to show login window,
//                Esc to hide it, Enter to submit dialog

// workaround for a jQuery bug, where load events are not fired
// if there are no ready events in queue
$(nop);

var estradaCustomDefaults = {
  secureLogin: false
};

var authoring_globals = function() {

  // true if authoring should be engaged
  var engage_authoring;
  
  var adminRegex = /(\/_\.admin\/?|\.admin\/?)/;
  //var regstr = '(' + estradaCustom.sitePath + '(?:(?:/([\\w-]+))+)(\\.\\w+)?)(/)?(?:(\\?)|$)';
  var regstr = '(http://[\\w-\.]+' + estradaCustom.sitePath + '(?:(?:/([\\w-]+))+)(\\.\\w+)?)(/)?(?:(\\?)|$)';
  var setExtensionRegex = new RegExp(regstr);
  var draftRegex = /((\?|&)?draft-preview=true(&?))/;
  var query = function() {
    if (!(window.location.search && (window.location.search.length > 1))) {
      return {};
    }
    var result = {};
    var q = window.location.search.substr(1);
    var pairs = q.split('&');
    for (var i = 0; i < pairs.length; i++) {
      var keyvalue = pairs[i].split('=');
      result[keyvalue[0]] = (keyvalue.length == 2) ? keyvalue[1] : '';
    }
    return result;
  }();
  var meta = function() {
    var g = this;
    var result = {};
    $('meta').each(function() {
      var name = this.name;
      if (name != 'robots') {
        if (name == 'view') {
          var items = this.content.split(',');
          result[name] = {
            name: items[0],
            title: items[1]
          };
        }
        else {
          result[name] = this.content;
        }
      }
    });
    return result;
  }();
  estradaCustom = $.extend({}, estradaCustomDefaults, estradaCustom);
  var authorDisabledForRequest = (query['noauthoring'] != null);
  var _hasDraft = false;
  var secureLogin = false;
  var g = {
    authoringEnabled: function() {
      if (!authorDisabledForRequest) {
        if (/enable-authoring=yes/.test(document.cookie)) {
          return true;
        }
        if (/enable-authoring=secure/.test(document.cookie)) {
          secureLogin = true;
          return true;
        }
      }
      return false;
    }(),
    engageComplete: function() {
      if (g.spinner) {
        g.spinner.remove();
        g.spinner = null;
      }
    },
    // velocity of animation
    animation: 100,
    currentDraft: false,
    // stores all relevant URLs
    // * draft
    // * published
    // * comments
    // * form -- authoring URL
    url: function() {
      // initialize with standard Engine URLs

      var base = ($('script[src$=/scripts/core.js]').attr('src') || '')
          .replace(/scripts\/core.js$/, '');
      var sitePath = estradaCustom.sitePath;

      var result = {
			  host: location.protocol + '//' + location.host + sitePath + '/',
        // base URL for the script to load
        base: base,
        login: (estradaCustom.secureLogin ? ('https://' + location.host) : '') + sitePath + '/engine/login/',
        user: sitePath + '/engine/user/ .results',
        logout: sitePath + '/engine/logout/',
        structure: sitePath + '/engine/structure/',
        comment: sitePath + '/engine/comment/',
        history: sitePath + '/engine/history/',
        workgroup: sitePath + '/engine/workgroup/',
        images: sitePath + '/engine/images/',
        email: sitePath + '/engine/email/',
        order: sitePath + '/engine/order/',
        ordernav: sitePath + '/engine/ordernav/',
        owner: sitePath + '/engine/owner/',
        userinfo: sitePath + '/engine/userinfo/',
        navigation: sitePath + '/engine/navigation/',
        pageinfo: sitePath + '/engine/pageinfo/',
        links: sitePath + '/engine/links/',
        quickpoll: sitePath + '/engine/quickpoll/'
      };

      $('link').each(function() {
          var rel = this.rel;
          if (rel != 'stylesheet' && rel != 'alternate') {
            result[rel] = this.href;
        }
      });
      if (result.draft) {
        _hasDraft = true;
      }
                  
      return result;
    }(),
    authorFormParams: function() {
      if (g.meta.view && (g.meta.view.name != 'default')) {
        return {view: g.meta.view.name};
      }
      return {};
    },
    query: query,
    customPhrase: function() {
      var items = [];
      var customPhraseArea = null;
      var g = {
        add: function(phraseNode) {
          if (customPhraseArea) {
            customPhraseArea.node(phraseNode);
          }
          else {
            items.push(phraseNode);
          }
        },
        foreach: function(delegateFunc) {
          $.each(items, function() {
            delegateFunc(this);
          });
        },
        render: function(phrase) {
          customPhraseArea = phrase;
          $.each(items, function() {
            customPhraseArea.node(this);
          });
        }
      };
      return g;
    }(),
    status: function(msg) {
      $.authoring.shelf.phrase('status', function() {
        this.text(msg);
      });
    },
    hash: function() {
      if (!window.location.hash) {
        return '';
      }
      return window.location.hash.substr(1);
    }(),
    gridTheme: function() {
      return $.presentation().gridTheme;
    }(),
    // stores all meta tags
    // * identifier -- page Id
    // * hidden -- true, if page is hidden
    // * offline -- true, if page is offline
    meta: meta,
    windowsAuth: false,
    // current user information
    user: { login: '', role: 'Anonymous', name: 'Anonymous', 
      isAuthenticated: false, initialized: false
    },
    getUser: function(done) {
      var cnt = 1000;
      userReady();
      function userReady() {
        --cnt;
        if (!g.user.initialized && (cnt > 0)) {
          window.setTimeout(userReady, 100);
        }
        else {
          done(g.user);
        }
      }
    },
    getEditorConfig: function() {
      var config = null;
      config = $.extend({}, estradaCustom.editor['defaultConfig'],
        ((g.user.editor && estradaCustom.editor[g.user.editor]) || {}));
      return config || {};
    },
    setEditable: function(owner, draftStatus) {
      g.isOwner = (g.user.login == owner);
      if (draftStatus && (draftStatus.length > 0)) {
        g.draftVersion = (draftStatus != 'current');
      }
      g.isEditable = !g.draftVersion && (g.user.caps.SuperAdmin || (g.work_view && g.user.caps.Author && g.isOwner) || (g.draft_preview && g.user.caps.Author));
      if (!g.isEditable) {
        $.authoring.shelf.adjustHeight(false);
      }
    },
    hasDraft: _hasDraft,
    isDraft: !!(meta.live),
    isNew: !!(meta['new']),
    isEditable: false,
    isDeleted: !!(meta.deleted && (meta.deleted != 'false')),
    isDraftDeleted: !!(meta.deleted && (meta.deleted == 'draft')),
    isOffline: !!(meta.offline && (meta.offline == 'true')),
    isOwner: false,
    draftVersion: false,
    // true, if the page is a draft preview mode
    draft_preview: draftRegex.test(document.location.search),
    current_view: null,
    // true, if the page should display the "work" tab (either draft page,
    // or draft preview)
    work_view: false,
    admin_view: adminRegex.test(document.location),
    max_z: function() {
      return max_z();
    },
    err: {
      login: 'Invalid login or password.',
      server: 'Server error has occurred.'
    },
    admin: {
      url: null,
      caps: null
    },
    urlNewPage: function(newPageId) {
      return location.href.replace('\/' + g.meta.identifier, '/' + newPageId);
    },
    urlDraftHandler: function(url, isDraft) {
      if (isDraft == null) {
        isDraft = (g.isDraft || g.draft_preview);
      }
      url = url.replace(/#\w*/, '');
      var q = isDraft ? 'draft-preview=true' : '';
      if (isDraft) {
        if (g.draft_preview) {
          return url;
        }
        else {
          //var qSep = ((lHref.indexOf('?') > 0) ? '&' : '?');
          var qSep = ((url.indexOf('?') > 0) ? '&' : '?');
          return url + qSep + q;
        }
      }
      else {
        return url.replace(
         draftRegex, function(match) {
          if (arguments[1] && arguments[1].length > 0) {
            var toReplace;
            if ((arguments[2] && (arguments[2].length > 0)) && (arguments[3] && (arguments[3].length > 0))) {
              toReplace = arguments[1].substr(1);
            }
            else {
              toReplace = arguments[1];
            }
            return arguments[0].replace(toReplace, q);
          }
         });
      }
    },
    urlExtensionHandler: function(url, ext) {
      url = url.replace(/#\w*/, '');
      if ((ext.length > 0) && (ext.substr(0, 1) != '.')) ext = '.' + ext;
      if (setExtensionRegex.test(url)) {
        return url.replace(
         setExtensionRegex, function(match) {
          /*
          console.info('cnt=' + arguments.length);
          for (var i = 0; i < arguments.length; i++) {
            console.info(i + ':' + arguments[i]);
          }
          */
          var replaceRoot = false;
          var root = (arguments[2] == '_');
          var replacePlus = '';
          var replaceWithPlus = ''
          if (ext.length > 0) {
            if (arguments[5] && (arguments[5].length > 0)) {
              replacePlus = arguments[4];
            }
            if (!arguments[3] || (arguments[3].length == 0)) {
              replaceRoot = true;
            }
          }
          else {
            if (root) replaceRoot = true;
            if (!arguments[4] || (arguments[4].length == 0)) {
              if (!root) replaceWithPlus = '/';
            }
            else {
              if (root) replacePlus = arguments[4];
            }
          }
          if (arguments[3] && (arguments[3].length > 0)) {
            var toReplace = (replaceRoot ? '_' : '') + arguments[3] + replacePlus;
            return arguments[0].replace(toReplace, ext + replaceWithPlus);
          }
          else if (arguments[2] &&(arguments[2].length > 0)) {
            var toReplace = arguments[2] + replacePlus;
            /*
            console.info('[' + arguments[0] + '] [' + toReplace + ']');
            return arguments[0].replace(toReplace, arguments[2] + (replaceRoot ? '/_' : '') + ext + replaceWithPlus);
            */
            return arguments[0].replace(toReplace, arguments[2] + ext + replaceWithPlus);
          }
          else {
            return arguments[0].replace(arguments[1], arguments[1] + '_' + ext + '/');
          }
         });
      }
      else {
        if (ext.length > 0) {
          var idx = url.indexOf('?');
          if (idx >= 0) {
            return url.substr(0, idx) + '_' + ext + url.substr(idx);
          }
          else {
            var c = url.substr(url.length-1, 1);
            if (c != '/') {
              url += '/';
            }
            return url + '_' + ext + '/';
          }
        }
        return url;
      }
    }
  };  
  g.windowsAuth = (g.meta['auth-mode'] == 'http');
  if (g.url.form) {
    if (g.meta.view) {
      g.url.formWithContext = buildUrl(g.url.form, {'v-p': g.meta.view.name});
    }
    else {
      g.url.formWithContext = g.url.form;
    }
  }
  g.currentDraft = (g.url.draft != null);
  if (authorDisabledForRequest) {
    $.presentationHelper.authorDisabled();
  }
  if (!g.authoringEnabled) {
    g.user.initialized = true;
    return g;
  }
  if (g.meta.identifier && (g.url.form || g.url.draft)) {

    // determine if this is work view
    if (g.admin_view) {
      g.current_view = "admin";
    }
    else {
      if (g.url.published) {
        g.work_view = true;
        g.draft_preview = false;
      } else {
        g.work_view = g.draft_preview;
      }
      g.current_view = g.work_view ? "work" : "live";
    }
    var lHref = window.location.href;
     // set published URL
    if (!g.url.published) {
      var urlPub = lHref;
      if (g.admin_view) {
        urlPub = g.urlExtensionHandler(urlPub, '');
      }
      g.url.published = g.urlDraftHandler(urlPub, false);
    }
    // set draft URL
    if (!g.url.draft) {
      var urlPub = lHref;
      if (g.admin_view) {
        urlPub = g.urlExtensionHandler(urlPub, '');
      }
      g.url.draft = g.urlDraftHandler(urlPub, true);
    }
    // set admin URL
    g.url.admin || (g.url.admin = g.urlDraftHandler(g.urlExtensionHandler(lHref, '.admin'), false));
    // see if we've been logged in during this session
    g.logged_in = $.storage.session('logged_in') == 'true';
    // Verify Bootstrap Criteria
    // this criteria has one false negative -- if the user logged in on
    // some other page without setting "logged_in" cookie and then
    // viewed this page in "live" mode. This is not a big deal, however --
    // they'll just have to re-login.
    if (g.work_view || g.logged_in) {
      // load authoring assets
      $.getScript(g.url.base + 'scripts/authoring.js', function() {
        // get user role, defer loading login window/handle
        $.ajah(g.url.user, { page: g.meta.identifier }, function() {
          $(this).find('.self .vcard').each(function() {
            var self = $(this);
            $.extend(g.user, {
              name: self.find('.fn').text(),
              login: (self.find('.nickname').text()).toLowerCase(),
              role: self.find('.role').text(),
              isAuthenticated: self.find('.authenticated').text() == 'True',
              editor: self.find('.editor').text(),
              initialized: true
            });
            var caps = {};
            self.find('.capabilities li').each(function() {
              caps[$(this).text()] = true;
            });
            g.user.caps = caps;
          });
          var adminRender = false;
          $(this).find('.admin').each(function() {
            var admin = $(this);
            $.extend(g.admin, {
              url: admin.find('.url').text()
            });
            var caps = {};
            admin.find('.capabilities li').each(function() {
              adminRender = true;
              caps[$(this).text().toLowerCase()] = true;
            });
            g.admin.caps = caps;
          });
          g.admin.render = adminRender;
          if (g.logged_in = $.storage.session('logged_in', g.user.isAuthenticated)) {
            // engage authoring
            engage_authoring = true;
            engage();
          } else {
            loginHandle();
          }
        });
      });
    } else {
      g.user.initialized = true;
      // wait for the page to finish loading
      $(function() {
        loginHandle();
      });
    }
  }
  // otherwise, this is probably not an Estrada page, do not engage
  
  return g;

  function engage() {
    if (!engage_authoring || !$.authoring.engage) {
      return;
    }
    engage_authoring = false;
    authoringNode();
    ($.authoring.engage || nop)(g);    
  }

  // create or get the authoring node
  function authoringNode() {
    if (!g.node) {
      //g.navigator = $.navigator();
      var body = $(document.body);
      //body.prepend(g.navigator.node);
      
      var holder = body.div().css({ position: 'absolute', top: '0', left: '0', width: '100%',
             marginTop: '0', 'z-index': g.max_z() });
      g.node = holder.div().attr('id', 'authoring-ui');
      //holder.div().attr('id', 'navigator-ui');
      
      
    }
    return g.node; 
  }
  /*
  function authoringNode() {
    return g.node || (g.node = $(document.body).div()
      .css({ position: 'absolute', top: '0', left: '0', width: '100%',
             marginTop: '0', 'z-index': g.max_z() }).div().attr('id', 'authoring-ui'));
            
  }
  */

  // render login handle
  function loginHandle() {
    // error timer
    var err_timer;
    var n = authoringNode();
    // element cache
    var els = {};
    
    var open;
    
    $.authoring.login = {
      // attempt login
      attempt: function(button) {
        el('error').slideUp(g.animation);
        var data = { username: $('#e-username').val(),
          password: $('#e-password').val(),
          page: g.meta.identifier
        };
        if (data.username.length && data.password.length) {
          els.button = $(button).attr('disabled', true);
          els.text = $('.text', els.button).hide();
          els.loading = $('.loading', els.button).show();
          $.ajah(g.url.login, data, function(status, location) {
              // reset shelf to open (default)
              $.storage.user('shelf', 'o');
              // redirect to work view
              window.location = location;
            }, function(res) {
              err_timer && window.clearTimeout(err_timer);
              el('error').html(g.err[ res.status == 422 ? 'login' : 'server'])
                .slideDown(g.animation, function() {
                  err_timer = window.setTimeout(function() {
                    err_timer = 0;
                    el('error').slideUp(g.animation);
                  }, 3000);
                });
              els.button = $(button).removeAttr('disabled');
              els.loading.hide();
              els.text.show();
            });
        }
      },
      keycheck: function(evt, obj) {
		    if (evt.keyCode == 13) {
			    if (obj.id == "e-username") {
				    $("input#e-password").focus();
			    } 
			    else if (obj.id == 'e-password') {
				    $.authoring.login.attempt($('button#e-login'));
	      	}
		    }
      },
      // open login dialog
      open: function(n) {
        if (g.meta['auth-mode'] == 'http') {
          // HTTP/Windows authentication
          // attempt authentication
          g.spinner = el('teaser', n).spinner({position: 'after', text: 'Authenticating...'});
          $.ajah(g.url.login, { page: g.meta.identifier },
            function(status, location) {
              // reset shelf to open (default)
              $.storage.user('shelf', 'o');
              // redirect to work view
              window.location = location;
            }, function(status) {
              g.spinner.remove();
              // TODO(dglazkov): Handle HTTP login failure error
            });
          el('teaser', n).hide();
          return;
        } 
        else if (secureLogin) {
          g.spinner = el('teaser', n).spinner({position: 'after', text: 'Accessing Secure Login...'});
          $.ajah({
            url: g.url.login,
            method: 'get',
            followRedirect: true,
            params: { page: g.meta.identifier },
            callback: function(status, location) {
              window.location = location;
            },
            errorCallback: function(status, location) {
              g.spinner.remove();
            },
            indicator: false
          });
        }
        else {
          // forms authentication
          open = true;
          el('teaser', n).hide();
          el('dialog', n).slideDown(g.animation);
        }
      },
      // close login dialog
      close: function(close) {
        el('dialog', n).slideUp(g.animation, function() {
          el('handle', n).show();
        });
        open = false;
      },
      // reveal teaser
      tease: function(n) {
        if (!open) {
          el('handle', n).hide();
          el('teaser', n).slideDown(g.animation);
        }
      },
      // hide teaser
      untease: function(n) {
        if (!open) {
          el('teaser', n).slideUp(g.animation, function() {
            el('handle', n).show();
          });
        }
      }
    };
    // append login HTML (left as one long string to avoid unnecessary string
    // concatenation operations)
    n.append('<div class="login"><div class="handle"><a href="#"><span class="handleText">This is the authoring handle text. Used to workaround IE bug.</span></a></div><div style="display:none;" class="teaser" onmouseout="$.authoring.login.untease(this)" onclick="$.authoring.login.open(this)"></div><div style="display:none;" class="dialog"><form><div class="content"><label for="e-username">Username:</label><input type="text" class="text" id="e-username" name="username" onkeyup="$.authoring.login.keycheck(event,this)"/><label for="e-password">Password:</label><input type="password" class="password" id="e-password" name="password" onkeyup="$.authoring.login.keycheck(event,this)"/><button id="e-login" class="button" type="button" onclick="$.authoring.login.attempt(this)"><span class="text">LOGIN</span><span class="loading" style="display:none;"><b></b></span></button></div><div class="error" style="display:none;"></div></form><div class="close" onclick="$.authoring.login.close(this)">Close</div><div class="sill"></div></div></div>');

    $('.handle a', n).mouseover(
			function(ev) {
				$.authoring.login.tease($(this).parent())
			}
		);
		
    // add document click handler to close the login window when
    // clicked outside
    $(document).click(function(e) {
      if (open) {
        if ($(e.target).parents('#authoring-ui').length == 0) {
          $.authoring.login.close();
        }
      }
    });  
    
    function el(name, node) {
      return (els[name] || (els[name] = node ?
        (node.className == name ?
          $(node) : $(node).parent().find('.' + name).eq(0))
          : $('#authoring-ui').find('.' + name).eq(0)));
    };
  };
  
  // generic function to build URL
  function buildUrl(base, query) {
    return base + (base.indexOf('?') >= 0 ? '&' : '?') + $.param(query);
  };
  
}();

plugin('authoring', function() {
  return authoring_globals;
}, true);

// ***
// ~common.js
// common functions/objects

// register a plugin with jQuery
// * name -- name of the plugin as it would be used, i.e. $(x).name
// * plugin -- the plugin function
// * global -- if true, the plugin will only be registered at jQuery root
//     object, not jQuery.fn
function plugin(name, plugin, global) {
  $[name] = plugin;
  if (!global) {
    $.fn[name] = plugin;
  }
};
window.jPlugin = plugin;

// Utilities
// functions/variables that are frequently used across plugins
  
// empty handler/function
function nop() {};

// empty array
var ear = [];

$.expr[':'].hasText = function(el) {
  return ($.trim($(el).text()).length > 0);
}


// globally uique ID generator
// * value -- a proposed unique value. If specified, generator just returns it.
//   Otherwise, generator returns a newly generated value.
// * returns a unique id string
function guid(value) {
  return value || ('uid-' + guid.count++);
};
plugin('guid', function(value) {
  return guid(value);
});

guid.count = 0;

// always returns the top-most value for z-index
function max_z() {
    return ++max_z.count || (max_z.count = 2008);
};

plugin('htmlDecode', function(value){
	if(value=='&nbsp;' || value=='&#160;') {value = "";}
	return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
});
plugin('htmlEncode', function(value){
  return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
});

plugin('contains', function(target, lookfor) {
  return (target.indexOf(lookfor) >= 0);
});

plugin('replaceClass', function(toReplace, replaceWith) {
	//var node = $(this).get(0);
	$(this).each(function() {
		this.className = this.className.replace(toReplace, replaceWith);
	});
});

plugin('right', function() {
	if (!this[0]) return;
	return $(this).scrollLeft() + $(this).width();
});

plugin('bottom', function() {
	if (!this[0]) return;
	return $(this).scrollTop() + $(this).height();
});

plugin('cols', function(settings) {
	var prefix = "e-";
	var innerClass = prefix+"cols-inner";
	var defaults = {
		holder: {
			id: "cols-"+guid(),
			className: prefix+"cols"
		},
		one: {
			className: prefix+"left"
		},
		two: {
			className: prefix+"right"
		}
	};
	settings = $.extend({}, defaults, settings);
	var holder = $(document.createElement("div"));
	$(holder).attr("id", settings.holder.id);
	holder.div().addClass(innerClass).addClass(settings.one.className);
	holder.div().addClass(innerClass).addClass(settings.two.className);
	holder.div().addClass(prefix+"cols-bottom");
	if (this[0]) {
		$(this).append(holder);
	}
	return holder;
});

plugin('shadow', function(className) {
	if (className) {
		className = ' ' +  className + '-shadow';
	}
	else {
		className = '';
	}
	var node = $('<div class="o-shadow' + className + '"><div class="o-shadow2"></div></div>');
	this.wrap(node);
	return node;
});

plugin('shadowInner', function(className) {
	if (className) {
		className = ' ' +  className + '-shadow';
	}
	else {
		className = '';
	}
	var node = $('<div class="i-shadow' + className + '"><div class="i-shadow2"></div></div>');
	this.wrapInner(node);
	return node;
});

var extWithEndSlashRegex = /.+\.\w+\/$/;
plugin('createUrl', function(baseUrl, queryString, hash) {
  if (extWithEndSlashRegex.test(baseUrl)) {
    baseUrl = baseUrl.substring(0, baseUrl.length-1);
  }
  if (hash) {
    baseUrl += '#' + hash;
  }
	if (baseUrl.indexOf('?') >= 0) {
	  return baseUrl + '&' + queryString;
	}
	else {
	  return baseUrl + '?' + queryString;
	}
});

var nodePositionerDefaults = {
  over: false,
  adjustTop: true,
  adjustLeft: true
};

plugin('nodePositioner', function(node, relatedNode, settings) {
  node = $(node);
  settings = $.extend({}, nodePositionerDefaults, settings || {});
  relatedNode = $(relatedNode);
  var rnPos = relatedNode.offset();
  var topOffset = 0;

  if ($.browser.msie) {
    topOffset = $(document.documentElement).scrollTop();
  }
  var position = (settings.position ? settings.position :
    (settings.over ? 'over' : 'under'));

  var left = rnPos.left;
  var top = rnPos.top + topOffset;
  switch (position) {
    case 'under':
      top += relatedNode.outerHeight();
      break;
    case 'right':
      var p = relatedNode.position();
      node.css({ top: p.top + 'px' });
      left = relatedNode.outerWidth();
      break;
  }
  if (settings.adjustTop || settings.adjustLeft) {
    var nodeWidth = node.outerWidth();
    var nodeHeight = node.outerHeight();
    var w = $(window);
    var windowRight = w.right();
    var windowBottom = w.bottom() + topOffset;
    if (settings.adjustTop) {
      var bottom = top + nodeHeight;
      if (bottom >= windowBottom) {
        node.css({ top: Math.max(0, (windowBottom - nodeHeight)) + 'px' });
      }
      else {
        node.css({ top: top + 'px' });
      }

    }
    if (settings.adjustLeft) {
      var right = left + nodeWidth;
      if (right >= windowRight) {
        node.css({ left: Math.max(0, (windowRight - nodeWidth)) + 'px' });
      }
      else {
        node.css({ left: left + 'px' });
      }
    }
  }
});

plugin('outerHtml', function(node) {
  if (node) {
    return getHtml(node);
  }
  
  var html = '';
  this.each(function() {
    html += getHtml(this);
  });
  return html;
  
  function getHtml(dNode) {
    var n = $(dNode);
    return dNode.outerHTML ? dNode.outerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : function() {
      var parent = n.parent();
      var el = $.dom(parent.attr('tagName'));
      el.append(n.clone(true));
      return el.html();
    }()
  }
});

plugin('eHover', function() {
  this.hover(
	  function() {
	    $(this).addClass('on');
	    return true;
	  },
	  function() {
	    $(this).removeClass('on');
	    return true;
	  }
	);
	return this;
});

plugin('myText', function(text) {
  if (text != null) {
    var textNode = getTextNode(this[0]);
    if (textNode) {
      textNode.nodeValue = text;
    }
    else {
      this[0].appendChild(document.createTextNode(text));
    }
  }
  
  var textNode = getTextNode(this[0]);
  return textNode ? textNode.nodeValue : '';
  
  function getTextNode(node) {
    if (node && node.firstChild) {
      var child = node.firstChild;
	    while(child) {
		    if (child.nodeType == 3) {
			    return child;
		    }
		    child = child.nextSibling;
	    }
	  }
	  return null;
	}
	
});

plugin('propertyHtml', function(node) {
  
  if (node) {
    //node = getNode(node);
    return uniformHtml(node);
  }
  
  var html = '';
  this.each(function() {
    //node = getNode(this);
    html += uniformHtml(this);
  });
  return html;
  //return html.replace(/\r\n/g, '');
  
  function getNode(dNode) {
    var n = $(dNode);
    return dNode.outerHTML ? dNode : function() {
      var parent = n.parent();
      var el = $.dom(parent.attr('tagName'));
      el.append(n.clone(true));
      return el[0];
    }()
  }
});

function uniformHtml($source) {
  if (typeof($source) == 'string') $source = document.getElementById($source);
	var $xhtml = '';
	if ($source.nodeType == 3) {
		var $text_content = $source.nodeValue;
		$text_content = $text_content.replace(/</g,'&lt;');
		$text_content = $text_content.replace(/>/g,'&gt;');
		$xhtml += $text_content;
	}
	else if ($source.nodeType == 8) {
		$xhtml += '<!--'+$source.nodeValue+'-->';
	}
	else {
		$xhtml += '<'+$source.nodeName.toLowerCase();
		var $attributes = $source.attributes;
		for (var $j=0; $j<$attributes.length; $j++) {
			var $attName = $attributes[$j].nodeName.toLowerCase();
			var $attValue = $attributes[$j].nodeValue;
			if ($attName == 'style' && $source.style.cssText) {
				$xhtml += ' style="'+$source.style.cssText.toLowerCase()+'"';
			}
			else if ($attValue && $attName != 'contenteditable') {
				$xhtml += ' '+$attName+'="'+$attValue+'"';
			}
		}
		$xhtml += '>';
		var $children = $source.childNodes;
		for (var $i=0; $i<$children.length; $i++) {
		  $xhtml += uniformHtml($children[$i]);
		  //$xhtml += '>' + uniformHtml($children[$i]);
		}
		$xhtml += '</'+$source.nodeName.toLowerCase()+'>';
	}
	return $xhtml;
}

plugin('isObject', function(obj) {
		return Object.prototype.toString.call(obj) === "[object Object]";
});

plugin('isBoolean', function(obj) {
		return Object.prototype.toString.call(obj) === "[object Boolean]";
});

plugin('isString', function(obj) {
		return typeof obj === 'string';
});

var pidRegex = /id-(\d+)/;
plugin('getPid', function(textVal) {
  var matched = pidRegex.exec(textVal);
  if (matched) {
    return matched[1];
  }
  return null;
});
}

