


if (!window.Acat){
  Acat = {};
}


if (!Acat.jq){
  Acat.jq = {};
}


if (!Acat.jq.shared){
  Acat.jq.shared = {};
}



Acat.jq.base = function (){

  var 
    blank = {blank:{}}, // fool SourceCookifier into correctly parsing functions
    aa = Acat.array,
    ac = Acat.color,
    ad = Acat.date,
    am = Acat.math,
    as = Acat.string,
    validating = false,
    autocompleteMax = 5,
    logStringArr = [],
    forms = [],
    jq = Acat.jq,
    base,
    shared = Acat.jq.shared;
    
  shared.logStringArr = logStringArr;
  shared.forms = forms;
  shared.locationSearchPreparse = {
    '/': ' ',
    '\\-': ' ',
    ',': ' ',
    '\\.': ' ',
    '\\(': ' ',
    '\\)': ' ',
    '\\[': ' ',
    '\\]': ' ',
    '&': ' ',
    ' and ': ' ',
    ' of ': ' ',
    '^the ': ' ',
    '^a ': ' ',
    "'": ' ',
    "\\'": ' ',
    '"': ' ',
    '  ': ' '
  };
  shared.locationSearchIgnored = [
    '/',
    '\\-',
    ',',
    '\\.',
    // '\\(',
    // '\\)',
    // '\\[',
    // '\\]',
    '\''
    // '"'
  ];
  
  
 



    
  

  function formObject(config){

    // copy config properties to this object instance
    for (var i in config){
      this[i] = config[i];
    }
  
    // set defaults
    this.invalid = [];
    this.blurred = [];
    this.lookups = [];
    this.buttons = [];
    this.toggles = [];
    this.textareas = [];
    this.dropdowns = [];
    this.datepickers = [];
    this.selects = [];
    this.ajaxes = {};
    this.requiredCount = 0;
    this.submitText = config.submitText || 'Submit';
    this.submitInvalidText = config.submitInvalidText || 'Form not complete';
    this.injectTarget = config.injectTarget || 'jq-form-inject';
    this.addedInjectTarget = config.addedInjectTarget || 'jq-formstatic-inject';

    // make sure all validation regexes are defined
    if (!config.dontValidate){
      this.validation = config.validation || {};
      this.validation = {
        standard: this.validation.standard || '^.{0,}$',
        required: this.validation.required || '^.{1,}$',
        fields: this.validation.fields || {}
      };
    }

  }

  
  
  
  function formBuild(formDef, injectTarget, onVisible, dontFocus){
    if (formDef.fields){
      var
        fieldIndex,
        contentArray = [],
        i;

      injectTarget = $(['#', injectTarget].join(''));

      // beginning of form
      contentArray.push('<div class="flatContainer">');
      
      if (formDef.fields.length){
        if (formDef.noFormElement){
          contentArray.push('<div');
        }
        else {
          contentArray.push('<form method="post" name="', formDef.id, '"');
        }
         
        contentArray.push(' id="', formDef.id, '" class="flat', (formDef.noGap ? ' noGap' : ''), (formDef.category || formDef.title ? ' widthAuto' : ''), '">');
      }

      // only use title and body frames if desired
      if (!formDef.noFrame){
      
        if (formDef.category || formDef.title){
          contentArray.push('<div class="title title-blue"><p>', (formDef.category ? ['<span class="small">', formDef.category, '</span><br>'].join('') : ''), '<b>', formDef.title, '</b></p>', (formDef.headerNav ? ['<div class="nav">', formDef.headerNav, '</div>'].join('') : ''), '</div>');
          contentArray.push('<div id="', formDef.id, 'Body" class="body bodyWithTitle">');
        }
        else {
          contentArray.push('<div id="', formDef.id, 'Body" class="body">');
        }
      }
      
      if (formDef.fields.length){

        formDef.lookups = [];
        formDef.autoClones = [];
        formDef.checkables = [];
        formDef.expandables = [];
        formDef.blurCallbacks = [];

        if (formDef.messagePreamble){
          contentArray.push('<div id="', formDef.prefix, 'messagePreamble" class="callout calloutWide clear">', formDef.messagePreamble, '</div>');
        }


        
        // generate form based on fields array
        for (var i = 0; i < formDef.fields.length; i++){
          contentArray.push(formCreateField(formDef, i));
        }


        
        if (formDef.messageInvalid){
          contentArray.push('<div class="clearfix"></div><div id="', formDef.prefix, 'messageInvalid" class="callout calloutWide calloutStatic" style="display: none">', formDef.messageInvalid, '</div>');
        }



        // end of form
        contentArray.push('<div class="clearfix"></div>');

        if (formDef.isStatic || formDef.submitType == 'standard'){
          contentArray.push('<div class="row button-submit-container">');
        }
          
        if (formDef.submitType == 'standard') {
          contentArray.push(
            '<input type="submit" id="',
            formDef.prefix,
            'buttonPost" class="button-submit" ',
            (
              formDef.dontValidate ? 
                ['value="', formDef.submitText, '"'].join('')
                :
                ['value="', formDef.submitInvalidText, '"'].join('')
            ),
            ' />',
            (
              formDef.dontValidate ?
              ''
              :
              ['<input type="hidden" id="', formDef.prefix, 'valid" name="valid" value="false" />'].join('')
            ),
            '<div id="',
            formDef.prefix,
            'buttonPostReplace" style="display: none"><img src="/common/assets/images/loading_16x16.gif" align="absmiddle"/>&nbsp;',
            formDef.submitInProgressText,
            '</div>'
          );
            
        }
        else if (!formDef.submitType){
          if (formDef.staticLink){
            contentArray.push('<a href="', formDef.staticLink, '">Click here to continue</a>');
          }
          else {
            contentArray.push('<a href="home">Return to home page</a>');
          }
        }
          
        if (formDef.isStatic || formDef.submitType == 'standard'){
          contentArray.push('</div>');
        }

      } // end if formDef.fields.length
      
      
      
      if (formDef.addedInjectTarget && formDef.addedInjectTarget != 'none'){
        contentArray.push('</div><div id="', formDef.addedInjectTarget, '" class="body', /*(formDef.noGapTop && formDef.noGapTop === true ? '' : ' gapTop'),*/ ' hidden"></div>');
      }

      if (formDef.fields.length){
        if (formDef.noFormElement){
          contentArray.push('</div>');
        }
        else {
          contentArray.push('</form>');
        }
      }
      
      // insert form into document
      injectTarget.html(contentArray.join(''));
      
      if (onVisible){
        onVisible();
      }
      
      // attach autocomplete to applicable fields
      for (i in formDef.lookups){
        if (formDef.lookups.hasOwnProperty(i)){
          
          var
            lookupElement = $(['#', i].join('')),
            currentVal = lookupElement.val(),
            lookupForm = lookupElement.parents('form');
            fieldDef = aa.findObjectByKey(formDef.fields, i.replace(formDef.prefix, ''), 'name')[0];
          
          
          lookupElement.addClass('iconrightgap');
          
          if (currentVal){
            if (fieldDef.prefillInvalid){  // add ok icon if current location is valid
              lookupElement.removeClass('icon-ok').addClass('ac_noresults icon-error');
            }
            else {
              lookupElement.removeClass('ac_noresults icon-error').addClass('icon-ok');
            }
          }
            
          // setup autocomplete functionality
          lookupElement.autocomplete(shared.locations, {
              delay: 50,
              field: i,
              matchContains: true,
              scroll: true,
              scrollHeight: 400,
              mustMatch: false,
              max: base.autocompleteMax,
              inputInvalidClass: 'ac_noresults icon-error',
              inputValidClass: 'icon-ok',
              loadingClass: 'icon-loading',
              persistInput: fieldDef.lookupPersistInput ? true : false,  // allow input to remain unchanged if defined in form
              preparse: shared.locationSearchPreparse,
              minChars: fieldDef.lookupRestrict ? 3 : 2,
              trim: fieldDef.lookupRestrict ? true : false,
              highlight: base.autocompleteHighlight,
              formatItem: base.autocompleteFormatItem,
              formatMatch: base.autocompleteFormatMatch,
              formatResult: base.autocompleteFormatResult
            })
            
            // callback when item selected
            .result(base.autocompleteResult)
            
            .keydown(base.autocompleteKeydown);
            
          // update each dropdown arrow icon's position relative to each input element
          lookupElement.after('<div class="ac_loading hidden" />');
          
        }
      }
      
      
      
      
      // setup custom button click handlers
      for (i in formDef.buttons){
        this.formSetupButton(formDef, formDef.buttons[i]);
      }
      
      
      
      for (i in formDef.autoClones){
        shared.formSetupAutoClone(formDef, formDef.autoClones[i]);
      }
      
      
      for (i in formDef.checkables){
        formSetupCheckbox(formDef, formDef.checkables[i]);
      }
      
      for (i in formDef.expandables){
        shared.formSetupExpand(formDef, formDef.expandables[i]);
      }
      
      
      
      for (i in formDef.blurCallbacks){
        $(['#', formDef.prefix, formDef.blurCallbacks[i].name, formDef.blurCallbacks[i].unique].join(''))
          .blur((function(thisFieldDef){
            return function(){
              thisFieldDef.blurCallback(thisFieldDef);
            };
          })(formDef.blurCallbacks[i]));
      }
      
      
      
      
      // iterate through dropdowns, adding special formatting
      for (i = 0; i < formDef.dropdowns.length; i++){
        formSetupDropdown(formDef, formDef.dropdowns[i]);
      }
      
      
      
      // create datepickers
      for (i = 0; i < formDef.datepickers.length; i++){
        formSetupDatepicker(formDef, formDef.datepickers[i].fieldDef);
      }
      
      
      
      // add custom callback (if defined) for each select control
      for (i = 0; i < formDef.selects.length; i++){
        var fieldDef = aa.findObjectByKey(formDef.fields, formDef.selects[i].id.replace(formDef.prefix, ''), 'name');
        
        if (fieldDef && fieldDef.length){
          fieldDef = fieldDef[0];
        
          if (fieldDef.selectCallback && typeof fieldDef.selectCallback == 'function'){
            $(['#', formDef.selects[i].id].join('')).change(fieldDef.selectCallback);
          }
        }
      }
      

      
      // setup form for validation
      if (!formDef.dontValidate){
        // copy to new array to iterate through, since source array will be modified during this process
        var invalids = $.map(formDef.invalid, function(i){
          return i;
        });
        
        // validate initially after form creation (in case default values passed in)
        for (i in invalids){
          if (invalids.hasOwnProperty(i)){
            var element = $(['#', invalids[i]].join(''));
            if (element.length){
              formValidateItem(element, (formDef.invalidatePrefills ? false : true));
            }
          }
        }
      }
      
      
      // don't intercept submits in IE6 or below
      // some older versions of IE6 seem to hang indefinitely at this step...
      if(!($.browser.msie && $.browser.version <= 6)){
        // setup submit handler
        $(['#', formDef.id].join('')).submit(function(){
          if (shared.inputFocused){
            $(['#', shared.inputFocused].join('')).blur();
          }
          $(['#', formDef.prefix, 'buttonPostReplace'].join('')).show(); //css('display', 'block');
          $(['#', formDef.prefix, 'buttonPost'].join('')).hide(); //css('display', 'none');
          
          return true;
        });
      }
      
      
      $(['#', formDef.id, ' input.inputText, #', formDef.id, ' textarea.inputTextarea'].join('')).keyup(function(){
        if (!validating){
          formValidateItem($(this));
        }
      });

      
      // delay textarea autogrow to end of javascript message queue
      // ensures textareas with content will grow on initial display
      setTimeout(function(){
        // iterate through textareas, adding special behavior (autogrow)
        for (i = 0; i < formDef.textareas.length; i++){
          $(['#', formDef.textareas[i]].join(''))
            .growfield({
              offset: 2,
              animate: false
            });
        }    
      
      }, 0);
      
      formSetupEventHandlers(formDef);
      
      setupTooltips($(['#', formDef.id].join('')));

      // focus applicable form field
      if (!dontFocus){
        if (formDef.invalidatePrefills && formDef.invalid.length > 0){
          // focus first invalid field
          forceFocus($(['#', formDef.invalid[0]].join('')));
          formValidateItem($(['#', formDef.invalid[0]].join('')));
        }
        else {
          var doFocus = true;
          if (formDef.focusCondition !== undefined){
            if (typeof formDef.focusCondition == 'function'){
              if (!formDef.focusCondition()){
                doFocus = false;
              }
            }
            else if (!formDef.focusCondition){
              doFocus = false;
            }
          }
          
          if (doFocus){
            // if focus not defined, try to set focus to first text input field in form
            i = 0;
            if (!formDef.focus && formDef.fields){
              while (formDef.fields[i]){
                if (!formDef.fields[i].type){ // field is a text input - all other fields have a type defined
                  formDef.focus = formDef.fields[i].name;
                  break;
                }
                
                i++;
              }
            }
            
            // only try to focus if a valid focus is set
            if (formDef.focus){
              // focus passed element
              forceFocus($(['#', formDef.prefix, formDef.focus].join('')));
            }
          }
        }
      }
      
    }
    
  }
  
  
  
  
  
  function formSetupButton(formDef, fieldDef){
    var button;

    if (typeof fieldDef == 'object'){
      if (!fieldDef.id){
        fblog(['formSetupButton(): button ', fieldDef.name, ' (', fieldDef.unique, ') doesn\'t exist'].join(''));
        return;
      }
      button = $(['#', fieldDef.id].join(''));
      
      button.click(function(buttonDef){
        return function(e){
          var element = $(this);

          if (element.hasClass('buttonDisabled')){
            return;
          }
          
          $(e.currentTarget).children('.body').addClass('bodyClick');
          
          var possibleActionFunction;
          
          // try converting string to function name
          if (typeof buttonDef.action == 'string'){
            
            try {
              possibleActionFunction = eval(['(', buttonDef.action, ')'].join(''));
              if (typeof possibleActionFunction == 'function'){
                buttonDef.action = possibleActionFunction;
              }
            }
            catch (exception){
            }
          }
          
          
          if (typeof buttonDef.action == 'function'){
            
            var targetId, target;
            
            if (formDef){
              targetId = [formDef.prefix, buttonDef.target].join('');
              target = $(['#', targetId].join(''));
            }

            buttonDef.action({
              element: element,
              formDef: formDef,
              fieldDef: buttonDef,
              target: target,
              event: e
            });
          }
          
          // prevent this control from taking focus
          if (e.preventDefault) { 
            e.preventDefault();
          }
          
        };
      }(fieldDef));
      
      button.mouseup(function(){
        return function(e){
          if (mouseWhich(e) == 'left') {
            $(this).children('.body').removeClass('bodyClick');
          }
          
        };
      }());

      if (fieldDef.update){
        button.data('update', fieldDef.update);
      }

    }


    if (!button){
      return;
    }
    
    
    if (button.length && fieldDef.attach && !fieldDef.isClone){
      var parent = button.parent();
      
      parent.addClass('buttonWrapFix');
      // parent.css('padding-top', '2.01em');
      
      // adjust position of buttons without titles after rendering
      setTimeout(function(){
        var parentOffset = parent.offset().top;
        parent.addClass('buttonWrapFix');
        
        var compareTo = parent.prevUntil('div.clearfix').first();
        var compareToOffset = compareTo.offset().top;
        
        if (parentOffset != compareToOffset){
          parent.css({
            'margin-top': '.5em',
            'padding-top': ''
          });
        }
      }, 0);


      
      
    }
  }
  
  
  
  
  
  
  function formSetupDropdown(formDef, dropdownDef, fieldDef){
  
    if (!fieldDef){
      fieldDef = dropdownDef.fieldDef || aa.findObjectByKey(formDef.fields, dropdownDef.id.replace(formDef.prefix, ''), 'name')[0];
    }
    
    var inputClass = ['inputText'];
    var optionsClass = ['inputText'];
    var readOnly = false;
    
    if (fieldDef.required){
      inputClass.push('required');
      optionsClass.push('required');
    }
    
    if (fieldDef.help){
      inputClass.push('help');
      optionsClass.push('help');
    }
    
    if (fieldDef.width){
      inputClass.push(['width-', fieldDef['width']].join(''));
      optionsClass.push(['width-', (fieldDef.dropdownOptionsWidth || fieldDef.width)].join(''));
    }
    
    if (
      fieldDef.editPermissions
      &&
      fieldDef.editPermissions.length
      &&
      !aa.existsIn(fieldDef.editPermissions, shared.permissions)
    ){
      readOnly = true;
    }
    
    if (
      fieldDef.displayPermissions
      &&
      fieldDef.displayPermissions.length
      &&
      !aa.existsIn(fieldDef.displayPermissions, shared.permissions)
    ){
      inputClass.push('hidden');
      optionsClass.push('hidden');
    }
    
    if (
      fieldDef.enableCondition
      &&
      typeof fieldDef.enableCondition == 'function'
      &&
      !fieldDef.enableCondition(formDef, fieldDef)
    ){
      readOnly = true;
    }
    
    // MultiSelect plugin
    if (!shared.multiselect){
      shared.multiselect = {};
    }
    
    if (!shared.multiselectOptions){
      shared.multiselectOptions = [];
    }
    
    // shared.multiselect.options = fieldDef.multiSelectOptions = dropdownDef.config = $(['#', dropdownDef.id].join('')).multiSelect({
    shared.multiselectOptions[dropdownDef.id] = fieldDef.multiSelectOptions = dropdownDef.config = $(['#', dropdownDef.id].join('')).multiSelect({
      selectAll: false,
      defaultValue: fieldDef.defaultValue,
      noneSelected: '- not applicable -',
      noneSelectedClass: 'multiSelectEmpty',
      oneOrMoreSelected: '*',
      inputClass: inputClass.join(' '),
      optionsClass: optionsClass.join(' '),
      readOnly: fieldDef.readOnly || readOnly,
      readOnlyClass: 'disabled',
      selectExclusive: fieldDef.selectExclusive,
      selectExclusiveAll: fieldDef.selectExclusiveAll,
      separator: fieldDef.separator
    }, fieldDef.selectCallback);
  }
  
  
  
  
  
  
  
  function formSetupDatepicker(formDef, fieldDef){
    $(['#', formDef.prefix, fieldDef.name].join('')).click(function(e){
      var sourceEl = $(this);
      var targetEl = sourceEl.siblings('.datepickerTarget');
      var monthView = (fieldDef.dateType == 'month');
      
      var
        dateTarget = new Date.today();
      
      var pickerEl = $('.datepick-inline', targetEl);
      if (!pickerEl.length){
        targetEl.datepick({
          //defaultDate: ad.dpExactDate('2010-01-01'),
          //yearRange: '2008:2018',
          minDate: ad.dpExactDate(shared.systemStart), //'2008-07-01'),
          maxDate: '+2Y',
          hideIfNoPrevNext: true,
          changeMonth: false,
          changeYear: false,
          showOtherMonths: true,
          selectOtherMonths: true,
          numberOfMonths: 1,
          monthSelect: monthView,
          //showStatus: true,
          currentText: 'Current',
          prevText: '< Prev',
          nextText: 'Next >',
          highlightWeek: true,
          onSelect: function (value, date) {
            fblog(['datepick() onSelect: ', value].join(''));
            datepickerHide();
            
            fblog(['datepicker selected ', ad.dateString(date)].join(''));
            
            // fill in selected date in input field (if applicable)
            if (fieldDef.dateFill == true){
              if (monthView){
                sourceEl.attr('value', ad.dateFriendlyStringMonthYear(date));
              }
              else {
                sourceEl.attr('value', ad.dateFriendlyShortString(date));
              }
            }
            
            if (fieldDef.selectCallback && typeof fieldDef.selectCallback == 'function'){
              fieldDef.selectCallback(date);
            }
            
          }
        });
        targetEl.datepick('setDate', dateTarget);
        pickerEl = $('.datepick-inline', targetEl);
      }
      else {
        if (pickerEl.is(':visible')){
          pickerEl.hide();
        }
        else {
          var currDate = targetEl.datepick('getDate');
          if (Date.compare(currDate, dateTarget) != 0){
            targetEl.datepick('setDate', dateTarget);
          }
          targetEl.datepick('option', {monthSelect: monthView});
          pickerEl.show();
        }
      }
    });              
  }
  
  
  
  
  
  
  function datepickerHide(){
    $('#date-picker div.datepick-inline').hide();
  }
  
  
  
  
  
  
  
  
  
  
  
  function formSetupCheckbox(formDef, fieldDef){
    var input = $(['#', formDef.prefix, fieldDef.name, fieldDef.unique].join(''));
    var checkbox = input.nextAll('div.inputCheckboxControl');
    
    checkbox.click(function(){
      // don't process clicks for readonly controls
      if (input.hasClass('disabled') || input.attr('readonly') == 'readonly'){
        return;
      }
    
      if (checkbox.hasClass('inputCheckboxControlChecked')){
        checkbox.removeClass('inputCheckboxControlChecked').addClass('inputCheckboxControlUnchecked');
      }
      else {
        checkbox.addClass('inputCheckboxControlChecked').removeClass('inputCheckboxControlUnchecked');
      }
    });
    
  }
  

  
  

  
  
  
  
  
  function formSetupEventHandlers(formDef, container){
    if (!container) {
      container = $('#col1');
    }
  
    // set up input hover handlers
    if (formDef){
      inputSetupHover($("input.inputText, textarea.inputTextarea", container).parents('div.item'), formDef);
    }
    
    // set up button hover handlers
    $("div.buttonFramedContainer > div.frame", container).hover(  
      function () {
        $(this).addClass('frameHover').children('.body').addClass('bodyHover');
      },
      function () {
        $(this).removeClass('frameHover').children('.body').removeClass('bodyHover bodyClick');
      }
    );

    // set up inline button hover handlers
    $("div.button:not(.buttonFramedContainer), span.button:not(.buttonFramedContainer)", container).hover(  
      function () {
        $(this).addClass('buttonYellow');
      },
      function () {
        $(this).removeClass('buttonYellow');
      }
    );

  }
  
  
  
  function formClearEventHandlers(container){
    if (!container) {
      container = $('#col1');
    }
  
    $("input.inputText, textarea.inputTextarea", container).parents('div.item').unbind('mouseenter mouseleave');
    $("div.buttonFramedContainer > div.frame", container).unbind('mouseenter mouseleave');
    $("div.button:not(.buttonFramedContainer), span.button:not(.buttonFramedContainer)", container).unbind('mouseenter mouseleave');
  }
  
   
  
  
  
  
  
  
  
  function formCreateField(formDef, index){
    var field;

    if (Number(index) == index){
      field = formDef.fields[index];
    }
    else {
      field = index;
    }
    
    var fieldName = field.name; // || index;
    var fieldId = [formDef.prefix, fieldName, field.unique || ''].join('');
    var contentArr = [];
    var i;
    var cssArr = [];
    
    // only parse if this appears to be a valid field definition
    if (field) {
    
      // add autocomplete fields to lookups array
      if (field.lookup){
        formDef.lookups[fieldId] = field.lookup;
      }
      
      if (field.autoClone){
        formDef.autoClones.push(field);
      }
      
      if (field.checkable){
        formDef.checkables.push(field);
      }
      
      if (field.expandable){
        formDef.expandables.push(field);
      }
      
      if (field.blurCallback && typeof field.blurCallback == 'function'){
        formDef.blurCallbacks.push(field);
      }
      
      // if this field requests an ajax call after being shown, add its config object to formDef's list
      if (field.ajaxOnShow){
        formDef.ajaxes[fieldId] = field.ajaxOnShow;
      }
      
      // add required fields to invalid array before validation
      if (!formDef.dontValidate && field.required){
        aa.pushUnique(formDef.invalid, fieldId);
        if (field.confirm){
          aa.pushUnique(formDef.invalid, [fieldId, '_confirm'].join(''));
        }
      }
      
      // if displayCondition defined, test it and apply hidden class if false
      if (field.displayCondition !== undefined){
        if (
          (
            typeof field.displayCondition == 'function'
            &&
            !field.displayCondition()
          )
          ||
          !field.displayCondition
        ){
          cssArr.push('hidden');
        }
      }
      
      if (field.checkable && jq.schedule && jq.schedule.editNotInternal()){
        cssArr.push('checkable');
      }
      if (field.expandable){
        cssArr.push('expandable');
      }
      if (field.cssClass){
        cssArr.push(field.cssClass);
      }
      
      
      switch (field.type){
      
        case 'raw':
          contentArr.push(field.content);
          break;
          
      
        case 'splitleftStart':
          contentArr.push('<div class="floatLeft">');
          break;
        case 'splitrightStart':
          contentArr.push('</div><div class="floatRight">');
          break;
        case 'splitEnd':
          contentArr.push('</div>');
          break;
          
          
        // end of form line, start next field on new line
        case 'eol':
          if (formDef.inSplit){
            contentArr.push('</div>');
            formDef.inSplit = false;
          }
          
          // if 'type' is the only field set, use a plain separator
          contentArr.push('<div class="clearfix"></div>');
          if (field['divider']){
            contentArr.push('</div><div class="body gapTop">');
          }
          break;
          

        // static text content
        case 'static':
          // otherwise parse additional content
          if (field['description']){
            contentArr.push('<div class="clearfix"></div>');
            contentArr.push(field['description']);
          }
          
          if (formDef.data && formDef.data[field.name]){
            contentArr.push('<div class="item ', cssArr.join(' '), '">');
            if (field.label){
              contentArr.push('<label id="');
              contentArr.push(fieldId);
              contentArr.push('_label" for="');
              contentArr.push(fieldId);
              contentArr.push('"');
              
              if (field['labelInline'] && field['labelInline'] == true){
                contentArr.push(' style="display: inline; margin-right: .25em;"');
              }
              else {
                contentArr.push(' style="padding-left: 0"');
              }
              
              contentArr.push('>');
              contentArr.push(field['label'] ? [field['label'], ':'].join('') : '');
              contentArr.push('</label>');
            }
            contentArr.push('<div class="large bold" style="line-height: 1.25em">', formDef.data[field.name], '</div></div>');
          }
          
          if (field['help']){
            contentArr.push('<div class="callout calloutWide calloutStatic ');
            contentArr.push(cssArr.join(' '));
            contentArr.push('">');
            contentArr.push(field['help']);
            contentArr.push('</div>');
          }
          break;

          
        case 'expandBar':
          contentArr.push(
            '<div class="taskItemExpandContainer" style="display: none">',
              '<div style="position: relative; background-color: #e6a7a7; padding: .25em .25em .25em .5em">',
                '<div style="color: #F7E8E8" class="bold caps">', field.text, '</div>',
              '</div>',
            '</div>',
            '<div class="clearfix" />'
          );
          break;
          
          
        // button
        case 'button':
          if (!field.readOnly || field.upload){
            contentArr.push(buttonCreate(formDef, field));
          }
          break;

          
          
        // all input fields, including those with no type specified
        default:
      
          if (field['description']){
            contentArr.push('<div class="clearfix"></div>');
            contentArr.push(field['description']);
          }
          
          // otherwise display a normal text input
          else {
          
            // outer item container
            if (field.inline){
              contentArr.push('<span style="margin-left: .25em;" class="');
            }
            else {
              contentArr.push('<div class="item ');
            }
            
            if (
              field['hideUntilLookup']
              ||
              (
                field['applicableIf']
                &&
                (function (){
                  return function(){
                    for (var i in field['applicableIf']){
                      var iField = aa.findObjectByKey(formDef.fields, i, 'name')[0];
                      switch (typeof iField.prefillVal){
                      
                        case 'string':
                          if ($.trim(iField.prefillVal.toLowerCase()) != $.trim(field['applicableIf'][i].toLowerCase())){
                            return true;
                          }
                          break;
                          
                        case 'number':
                          if (iField.prefillVal != field['applicableIf'][i]){
                            return true;
                          }
                          break;
                          
                        case 'object':
                          if (iField.type == 'dropdown'){
                            if (field['applicableIf'][i] instanceof Array){ // setting is an array
                              if (!aa.objectCompare(field['applicableIf'][i], iField.prefillVal)){
                                return true;
                              }
                            }
                            else { // setting is a single value
                              if (iField.prefillVal.length != 1 || iField.prefillVal[0] != field['applicableIf'][i]){
                                return true;
                              }
                            }
                          }
                        
                          else if (!aa.existsIn(iField.prefillVal, field['applicableIf'][i])){
                            return true;
                          }
                          break;
                      }
                    }
                    return false;
                  };
                }())
              )
            ){
              cssArr.push('hidden');
            }
              
            contentArr.push(cssArr.join(' '), '">');
            
            if ($.trim(field.label) || field.helpIcon){
            
              if (!(field['labelInline'] && field['labelInline'] == true)){
                contentArr.push('<table class="');
                if (field['width'] == 'full'){
                  contentArr.push('labelwidth-full');
                }
                else if (!field['width']){
                  contentArr.push('');
                }
                contentArr.push('" cellspacing="0"><tr><td class="inputLabelLeft');
                contentArr.push(!field['required'] && !formDef['dontDifferentiateRequired'] ? ' inputLabelOptional' : '');
                contentArr.push('">');
              }
              
              contentArr.push('<label id="');
              contentArr.push(fieldId);
              contentArr.push('_label" for="');
              contentArr.push(fieldId);
              contentArr.push('"');
              
              if (field['labelInline'] && field['labelInline'] == true){
                contentArr.push(' style="display: inline; margin-right: .25em; white-space: nowrap"');
              }
              
              if (field.helpIcon){
                contentArr.push(' class="icon-help iconright" title="', field.helpIcon, '"');
              }
              
              contentArr.push('>');
              
              contentArr.push(field['label'] ? [field['label'], ':'].join('') : '');
              contentArr.push('</label>');
              
              if (!(field['labelInline'] && field['labelInline'] == true)){
                contentArr.push('</td>');
                
                if (field['lookupFilter']){
                  contentArr.push('<td class="inputLabelRight"><span class="smallCaps">Search by:</span>');
                  for (i in field['lookupFilter']){
                    contentArr.push(buttonCreate(formDef, {
                      target: i,
                      action: 'filterToggle',
                      text: field['lookupFilter'][i],
                      buttonStyle: 'inline toggle'
                    }));
                  }
                  contentArr.push('</td>');
                }
                
                contentArr.push('</tr></table>');
              }
            }
            
           
            // determine which type of input this is (if not specified, use standard input control)
            switch (field['type']){
            
              case 'readonly':
                contentArr.push('<div class="readOnly">');
                contentArr.push(field['content']);
                contentArr.push('</div>');
                break;
                
                
              case 'select':
                formDef.selects.push({id: fieldId});
                
                contentArr.push(
                  '<select name="',
                  fieldName,
                  '" id="',
                  fieldId,
                  '"');
                  
                if (field.unique){
                  contentArr.push(' unique="', field.unique, '"');
                }
                
                contentArr.push('>');
                
                for (i = 0; i < field.values.length; i++){
                  contentArr.push('<option value="', field.values[i].id, '" ', (field.values[i].selected ? ' selected="selected"' : ''), '>', field.values[i].name, '</option>');
                }
                
                contentArr.push(
                  '</select>'
                );
                break;
                
            
              case 'dropdown':
                formDef.dropdowns.push({id: fieldId, fieldDef: field});
              
              
                // create SELECT element for MultiSelect plugin
                contentArr.push('<select multiple="multiple" name="');
                contentArr.push(fieldName);
                contentArr.push('" id="');
                contentArr.push(fieldId);
                contentArr.push('" class="hidden inputSelect');
                contentArr.push(field.required ? ' required' : '');
                contentArr.push(field.help ? ' help' : '');
                contentArr.push(field.width ? [' width-', field.width].join('') : '');
                contentArr.push('"');
                contentArr.push(field.regex ? [' regex="', field.regex, '"'].join('') : '');
                
                if (field.unique){
                  contentArr.push(' unique="', field.unique, '"');
                }
                
                contentArr.push('>');
                             
                if (!field.values){
                  fblog('formCreateField() error: field.values not specified for dropdown ' + fieldName);
                }
                
                if (typeof field.values == 'function'){
                  field.values = field.values();
                }
                
                outer: for (i = 0; i < field.values.length; i++){
                  contentArr.push('<option value="');
                  contentArr.push(field.values[i].id);
                  contentArr.push('"');
                  
                  if (field.prefillVal == undefined && formDef.data && formDef.data[field.name]){
                    if (field.values[i].id == formDef.data[field.name]){
                      contentArr.push(' selected="selected"');
                    }
                  }
                  else {
                    // add default selection if applicable
                    switch (typeof field.prefillVal){
                      case 'string':
                        if ($.trim(field.prefillVal.toLowerCase()) == $.trim(field.values[i].name.toLowerCase())){
                          contentArr.push(' selected="selected"');
                          //break outer;
                        }
                        break;
                        
                      case 'number':
                        if (field.prefillVal == field.values[i].id){
                          contentArr.push(' selected="selected"');
                          //break outer;
                        }
                        break;
                        
                      case 'function':
                        if (field.prefillVal() == field.values[i].id){
                          contentArr.push(' selected="selected"');
                        }
                        break;
                        
                      case 'object': // array
                        for (var j in field.prefillVal){
                          if (field.prefillVal[j] == field.values[i].id){
                            contentArr.push(' selected="selected"');
                            break;
                          }
                        }
                        break;
                    }
                  }
                  
                  if (field.values[i].description){
                    contentArr.push(' description="', field.values[i].description, '"');
                  }
                  contentArr.push('>');
                  contentArr.push(field.values[i].name);
                  contentArr.push('</option>');
                }
                
                contentArr.push('</select>');
                break;
            
              case 'datepicker':
                formDef.datepickers.push({id: fieldId, fieldDef: field});

                contentArr.push(
                  '<div id="date-picker" class="datepickerTarget" />', // hidden placeholder div for datepicker control
                  
                  '<input type="text"',
                  
                  ' name="',
                  fieldName,
                  '" id="',
                  fieldId,
                  '"',

                  ' class="inputText multiSelect',
                  (field['required'] ? ' required' : ''),
                  ((field.readOnly || field.prefillDisabled) ? ' disabled' : ''),
                  (field['help'] ? ' help' : ''),
                  (field['width'] ? [' width-', field['width']].join('') : ''),
                  '"',
                  
                  ' size="10" maxlength="200"',

                  (field.unique ? [' unique="', field.unique, '"'].join('') : ''),
                
                  ((field.readOnly || field.prefillDisabled) ? ' readonly="readonly"' : ''),

                  ' value="'
                );
                  
                if (field.get && typeof field.get == 'function'){
                  contentArr.push(field.get(formDef.data));
                }
                else if (field.prefillVal !== undefined){
                  var prefillDate = field.prefillVal;
                  if (!(prefillDate instanceof Date)){
                    prefillDate = Date.parse(prefillDate);
                  }
                  if (field.dateType == 'month'){
                    contentArr.push(ad.dateFriendlyStringMonthYear(prefillDate));
                  }
                  else {
                    contentArr.push(ad.dateFriendlyString(prefillDate));
                  }
                }
                else {
                  contentArr.push('');
                }
                contentArr.push('" />');
                
                break;
            
              case 'textarea':
                formDef.textareas.push(fieldId);
            
                contentArr.push('<textarea name="');
                contentArr.push(fieldName);
                contentArr.push('"');
                contentArr.push(' id="');
                contentArr.push(fieldId);
                contentArr.push('" class="inputTextarea');
                contentArr.push(field['required'] ? ' required' : '');
                contentArr.push(field['help'] ? ' help' : '');
                contentArr.push((field['prefillDisabled'] || field['readOnly']) ? ' disabled' : '');
                contentArr.push(field['width'] ? [' width-', field['width']].join('') : '');
                contentArr.push('"');
                contentArr.push(field['regex'] ? [' regex="', field['regex'], '"'].join('') : '');

                if (field.readOnly || field.prefillDisabled){
                  contentArr.push(' readonly="readonly"');
                }
                
                if (field.unique){
                  contentArr.push(' unique="', field.unique, '"');
                }
                
                contentArr.push('>');

                if (field.prefillVal !== undefined){
                  contentArr.push(field.prefillVal);
                }
                else if (formDef.data && formDef.data[field.name]){
                  contentArr.push(formDef.data[field.name]);
                }
                else {
                  contentArr.push('');
                }
                contentArr.push('</textarea>');
                break;
            
              default:
                contentArr.push('<input type="');
                contentArr.push(field['mask'] ? 'password' : 'text');
                contentArr.push('"');
                
                contentArr.push(' name="');
                contentArr.push(fieldName);
                contentArr.push('" id="');
                contentArr.push(fieldId);
                contentArr.push('" class="inputText');
                contentArr.push(field['capitalize'] === true ? ' titleCase' : '');
                contentArr.push(field['capitalize'] == 'sentence' ? ' sentenceCase' : '');
                contentArr.push(field['capitalize'] == 'upper' ? ' upperCase' : '');
                contentArr.push(field['required'] ? ' required' : '');
                contentArr.push((field['prefillDisabled'] || field['readOnly']) ? ' disabled' : '');
                contentArr.push(field['help'] ? ' help' : '');
                contentArr.push(field['width'] ? [' width-', field['width']].join('') : '');
                contentArr.push('"');
                contentArr.push(field['lookup'] ? [' lookup="', field['lookup'], '"'].join('') : '');
                contentArr.push(field['lookupFill'] ? [' lookupFill="', field['lookupFill'], '"'].join('') : '');
                contentArr.push(field['confirm'] ? [' confirm="', fieldId, '"'].join('') : '');
                contentArr.push(field['regex'] ? [' regex="', field['regex'], '"'].join('') : '');
                contentArr.push(' size="10" maxlength="200"');

                if (field.unique){
                  contentArr.push(' unique="', field.unique, '"');
                }
                
                if (field.readOnly || field.prefillDisabled){
                  contentArr.push(' readonly="readonly"');
                }
                
                if (field.title){
                  contentArr.push(' title="', field.title, '"');
                }
                
                contentArr.push(' value="');
                if (field.get && typeof field.get == 'function'){
                  contentArr.push(field.get(formDef.data));
                }
                else if (field.prefillVal !== undefined){
                  contentArr.push(as.htmlEncode(field.prefillVal));
                }
                else if (formDef.data && formDef.data[field.name]){
                  contentArr.push(formDef.data[field.name]);
                }
                else {
                  contentArr.push('');
                }
                contentArr.push('" />');
                
                contentArr.push(
                  '<div class="inputExpandControl icon-expandable"',
                    (field.expandableHint ? ['title="', field.expandableHint, '"'].join('') : ''),
                    '>&nbsp;</div>'
                );
                
                if (field.checkable){
                  contentArr.push(
                    '<div class="inputCheckboxControl icon-checkable',
                    (field.checkableValue ? ' inputCheckboxControlChecked' : ' inputCheckboxControlUnchecked'),
                    '" title="', field.checkableHint, '">&nbsp;</div>'
                  );
                }
                break;
            }
            
            
            // display attached help information
            if (field['help'] && !field.readOnly){
              contentArr.push('<div class="callout calloutAttached');
              contentArr.push(field['width'] ? [' calloutWidth', as.toTitleCase(field['width'])].join('') : '');
              contentArr.push('"');
              contentArr.push('" helpFor="');
              contentArr.push(fieldId);
              contentArr.push('">');
              contentArr.push(field['help']);
              contentArr.push('</div>');
            }
            
            
            // add hidden field for extra value (usually id or index) associated with lookup field
            if (field['lookup']){
              contentArr.push('<input type="hidden" id="');
              contentArr.push(fieldId);
              contentArr.push('_lookupVal" name="');
              contentArr.push(fieldName);
              contentArr.push('_lookupVal" value="');
              contentArr.push(field['lookupValPrefill']);
              contentArr.push('" />');
            }

            
            // close input div
            if (field.inline){
              contentArr.push('</span>');
            }
            else {
              contentArr.push('</div>');
            }
            

            // display second confirm input if needed
            if (field['confirm']){
              contentArr.push('<div class="item');
              contentArr.push(field['hideUntilLookup'] ? ' hidden' : '');
              contentArr.push('">');
              contentArr.push('<table class="');
              contentArr.push(field['width'] == 'full' ? 'Wide' : '');
              contentArr.push('" cellspacing="0"><tr><td class="inputLabelLeft"><label id="');
              contentArr.push(fieldId);
              contentArr.push('_confirm_label" for="');
              contentArr.push(fieldId);
              contentArr.push('_confirm">');
              contentArr.push(field['label']);
              contentArr.push(' again to confirm:</label></td></tr></table>');
              
              contentArr.push('<input type="');
              contentArr.push(field['mask'] ? 'password' : 'text');
              contentArr.push('" name="');
              contentArr.push(fieldName);
              contentArr.push('_confirm" id="');
              contentArr.push(fieldId);
              contentArr.push('_confirm" class="inputText');
              contentArr.push(field['required'] ? ' required' : '');
              contentArr.push(field['help'] ? ' help' : '');
              contentArr.push('"');
              contentArr.push(field['lookup'] ? [' lookup="', field['lookup'], '"'].join('') : '');
              contentArr.push(field['lookupFill'] ? [' lookupFill="', field['lookupFill'], '"'].join('') : '');
              contentArr.push(field['confirm'] ? [' confirm="', fieldId, '"'].join('') : '');
              contentArr.push(field['regex'] ? [' regex="', field['regex'], '"'].join('') : '');
              contentArr.push(' size="10" maxlength="200" value="');
              contentArr.push(field['prefillVal'] ? as.htmlEncode(field.prefillVal) : '');
              contentArr.push('" />');
              
              if (field['helpConfirm']){
                contentArr.push('<div class="callout calloutAttached" helpFor="');
                contentArr.push(fieldId);
                contentArr.push('_confirm">');
                contentArr.push(field['helpConfirm']);
                contentArr.push('</div>');
              }
              contentArr.push('</div>');
            }
          }
          
          break;
          
          // end of default case for outer field[type] switch
        
      }
    }
    
    return contentArr.join('');
      
  }
  
  
  
  
  function buttonCreate(formDef, fieldDef){
    var buttonId, cssArr, hidden = false, disabled = false;
    var contentArray = [];
    var iconArray = [];
    var buttonText = 'button';
    var testResult;
    
    var action;
    
    if (typeof fieldDef.action == 'function'){
      action = null;
    }
    else {
      action = fieldDef.action;
    }
    
    if (!formDef){
      formDef = {buttons: []};
    }

    if (fieldDef.textCondition){
      if (typeof fieldDef.textCondition.test == 'function'){
        testResult = fieldDef.textCondition.test();
      }
      else {
        testResult = fieldDef.textCondition.test;
      }
        
      if (testResult){
        buttonText = typeof fieldDef.textCondition.whenTrue == 'function' ? fieldDef.textCondition.whenTrue() : fieldDef.textCondition.whenTrue;
      }
      else {
        buttonText = typeof fieldDef.textCondition.whenFalse == 'function' ? fieldDef.textCondition.whenFalse() : fieldDef.textCondition.whenFalse;
      }
    }
    else if (fieldDef.text) {
      buttonText = fieldDef.text;
    }

    
    if (fieldDef.name){
      buttonId = [formDef.prefix, fieldDef.name, fieldDef.unique].join('');
    }
    else {
      buttonId = [formDef.prefix, 'button_', (fieldDef.target ? ([fieldDef.target, '_'].join('')) : ''), (action ? action : '')].join('');
    }
  
  
    if (fieldDef.name){
      formDef.buttons[[fieldDef.name, fieldDef.unique].join('')] = fieldDef;
    }
    else {
      formDef.buttons.push(buttonId);
    }
    
    
    cssArr = (fieldDef.cssClass ? [fieldDef.cssClass] : []);
    
    // if displayCondition defined, test it and apply hidden class if false
    if (fieldDef.displayCondition !== undefined){
      if (
        (
          typeof fieldDef.displayCondition == 'function'
          &&
          !fieldDef.displayCondition()
        )
        ||
        !fieldDef.displayCondition
      ){
        hidden = true;
      }
    }
    
    if (as.wordIn(fieldDef.buttonStyle, 'inline')){
      if (as.wordIn(fieldDef.buttonStyle, 'toggle')){
        cssArr.push('buttonToggle');
      }
    
      contentArray.push('<span class="button buttonInline', as.wordIn(fieldDef.buttonStyle, 'left-align') ? ' buttonInlineLeft ' : ' ', cssArr.join(' '), (hidden ? ' hidden' : ''), '" id="', buttonId, '"', (action ? [' action="', action, '"'].join('') : ''), '>', iconArray.join(''), buttonText, '</span>');    
    }
    
    else if (as.wordIn(fieldDef.buttonStyle, 'tab')){
      contentArray.push('<span class="button buttonInline buttonTab ', (as.wordIn(fieldDef.buttonStyle, 'active') ? 'buttonTabActive ' : ''), cssArr.join(' '), '" id="', buttonId, (fieldDef.target ? ['" target="', fieldDef.target].join('') : ''), '"', (action ? [' action="', action, '"'].join('') : ''), '>', iconArray.join(''), buttonText, '</span>');    
    }
    
    else if (as.wordIn(fieldDef.buttonStyle, 'small')){
      contentArray.push('<div class="item', (as.wordIn(fieldDef.buttonStyle, 'floatRight') ? ' floatRight' : ''), (hidden ? ' hidden' : ''), '"><span class="button buttonInline', as.wordIn(fieldDef.buttonStyle, 'left-align') ? ' buttonInlineLeft ' : ' ', cssArr.join(' '), '" id="', buttonId, '"', (action ? [' action="', action, '"'].join('') : ''), '>', iconArray.join(''), buttonText, '</span></div>');    
    }
    
    else if (as.wordIn(fieldDef.buttonStyle, 'big')){
      contentArray.push('<div class="item', (hidden ? ' hidden' : ''), '"><div class="buttonFramedContainer', (as.wordIn(fieldDef.buttonStyle, 'alignForm') ? ' buttonFramedContainerAdjacent' : ''), '"><div class="frame" id="', buttonId, '" target="', fieldDef.target, '"', (action ? [' action="', action, '"'].join('') : ''), '><div class="body', (as.wordIn(fieldDef.buttonStyle, 'big') ? ' big' : ''), ' ', cssArr.join(''), '">', iconArray.join(''), buttonText, '</div></div></div></div>');
    }

    else {
      contentArray.push('<div class="item', (as.wordIn(fieldDef.buttonStyle, 'floatRight') ? ' floatRight' : ''), (hidden ? ' hidden' : ''), ' ', cssArr.join(''), '"><div class="button', (disabled ? ' buttonDisabled': ''), '" id="', buttonId, '" name="', fieldDef.name, '" unique="', fieldDef.unique, '" target="', fieldDef.target, '"', (action ? [' action="', action, '"'].join('') : ''), '>', iconArray.join(''), buttonText, '</div></div>');
    }
    
    fieldDef.id = buttonId;
    
    return contentArray.join('');
  }

  
  function buttonUpdate(config){
    var buttonText, result;
    
    if (config.fieldDef.textCondition){
      if (typeof config.fieldDef.textCondition.test == 'function'){
        result = config.fieldDef.textCondition.test();
      }
      else {
        result = config.fieldDef.textCondition.test;
      }
      
      if (result){
        buttonText = config.fieldDef.textCondition.whenTrue;
      }
      else {
        buttonText = config.fieldDef.textCondition.whenFalse;
      }
    }
    else if (config.fieldDef.text){
      buttonText = config.fieldDef.text;
    }
    
    config.element.text(buttonText);
  }
  
  
  
  
  
  
  function buttonAddHoverHandler(buttons, offset){
    var hoverClass = ['icon-hover', offset, '-hover'].join('');
    var activeClass = ['icon-hover', offset, '-active'].join('');
    
    buttons.unbind('mouseenter mouseleave click mousedown mouseup');
    
    buttons
      .hover(
        function(){
          $(this).addClass(hoverClass);
        },
        function(){
          $(this).removeClass([hoverClass, ' ', activeClass].join(''));
        }
      )
      .mousedown(
        function(){
          $(this).addClass(activeClass);
        }
      )
      .mouseup(
        function(){
          $(this).removeClass(activeClass);
        }
      );
  }
  
  





  
  function formGetTextFieldNames(formDef){
    var i, fields = formDef.fields, names = [];
    
    for (i = 0; i < fields.length; i++){
      if (fields[i].name && (!fields[i].type || fields[i].type == 'textarea')){
        names.push(fields[i].name);
      }
    }
    
    return names;
  }
  
  
  
  
  
  function formGetValues(formDef, fieldList){
    var i, values = {}, fieldDef, requiredMissing = [];
    
    for (i = 0; i < fieldList.length; i++){
      values[fieldList[i]] = $.trim($(['#', formDef.prefix, fieldList[i]].join('')).val());
      
      fieldDef = aa.findObjectByKey(formDef.fields, fieldList[i], 'name');
      if (fieldDef.length){
        fieldDef = fieldDef[0];
        if (fieldDef.required && !values[fieldList[i]]){
          requiredMissing.push(fieldList[i]);
        }
      }
    }
    
    return {values: values, missing: requiredMissing};
  }
  
  
  
  
  function formDataCapitalize(formDef, fields){
    var i, thisField;
    
    for (i in fields){
      thisField = aa.findObjectByKey(formDef.fields, i, 'name');
      
      if (thisField.length){
        thisField = thisField[0];
        
        if (thisField.capitalize){
          if (thisField.capitalize == 'sentence'){
            fields[i] = as.toSentenceCase(fields[i]);
          }
          if (thisField.capitalize == 'upper'){
            fields[i] = fields[i].toUpperCase();
          }
          else {
            fields[i] = as.toTitleCase(fields[i]);
          }
        }
      }
    }
  }
  







  
  
  // check input value against regex if present
  // check input value against values from other elements with same 'confirm' attribute
  function formValidateItem(element, invalidUnmarked){
    if (element && element.length){
      validating = true;
      
      var parent = element.parents('item');
      var formEl = element.parents('form');
      var formId = formEl.attr('id');
      var formDef = shared.forms[formId];
      var id = element.attr('id');

      // fblog('formValidateItem(' + id + ')');

      if (!formDef){
        fblog(['no formDef for ', id, ' (parent = ', formId, ')'].join(''));
      }
      
      var fieldDef = aa.findObjectByKey(formDef.fields, id.replace(formDef.prefix, '').replace('_confirm', ''), 'name')[0]; //formDef.fields[id.replace(formDef.prefix, '')];
      if (!fieldDef){
        fblog(['formValidateItem(): no fieldDef for ', id, ' (parent = ', formId, ')'].join(''));
        return;
      }
      
      var validationField = id.replace(formDef.prefix, '').replace('_confirm', '');
      
      var regex = formDef.validation.fields[validationField] ? formDef.validation.fields[validationField] :
          (element.hasClass('required') ? formDef.validation.required : formDef.validation.standard);
          
      switch (element.attr('titleCase')){
        case 'true':
          break;
          
        case 'sentence':
          break;
      }
      

      var valid = true;
      var val = element.val(); //legalVal;
      var confirm = element.attr('confirm');
      
      if (!(val == val.match(new RegExp(regex)))){
        aa.pushUnique(formDef.invalid, id);
        if (!invalidUnmarked){
          parent.addClass('itemInvalid'); //css("border-color", "#f99");
        }
        valid = false;
      }
      
      // check if other confirm elements match this
      if (confirm){
        var confirmSame = $(['input[confirm=', confirm, ']'].join(''), formEl); 
        var mismatched = confirmSame
          .filter(function(){
            return $(this).val() != val;
          });
          
        if (mismatched.length > 0) {
          if (!invalidUnmarked){
            confirmSame.each(function(){
              var thisEl = $(this);
              var thisId = thisEl.attr('id');
              if (thisId != shared.inputFocused){
                thisEl.parents('div.item').addClass('itemInvalid'); //css("border-color", "#f99");
              }
            });
          }
            
          confirmSame.each(function(){
            aa.pushUnique(formDef.invalid, $(this).attr('id'));
            aa.pushUnique(formDef.blurred, $(this).attr('id'));
          });
          
          valid = false;
        }
      }


      // call callback function for additional validation
      if (!confirm && $.isFunction(fieldDef.validationCallback)){
        if (!fieldDef.validationCallback(formDef, val)){
          parent.addClass('itemInvalid');
          valid = false;
        }
      }
      

      if (valid !== false){
        var el = $((confirm ? ['input[confirm=', element.attr('confirm'), ']'].join('') : element), formEl);
        var elsToChange = el.parents('div.item'); //$('input.inputText, textarea.inputTextarea, td.inputLabelLeft', el.parent());
        elsToChange.removeClass('itemInvalid'); //.addClass('valid'); //css("border-color", "#bbb");
          
        el.each(function(){
          aa.removeByValue(formDef.invalid, $(this).attr('id'));
        });

        // call callback function after validation successful
        if (!confirm && $.isFunction(fieldDef.whenValidCallback)){
        }
        
      }

      var result = formValidate(formDef);
      
      validating = false;
      
      return result;
    }
    else {
      fblog('formValidateItem(): undefined element passed');
    }
  }

  
  
  // check valid state of all applicable form items
  function formValidate(formDef, validateAll){
    if (validateAll){
      $(['#', formDef.id, ' input.inputText, #', formDef.id, ' textarea.inputTextarea'].join('')).each(function(index, domEl){
        formValidateItem($(this));
        aa.pushUnique(formDef.blurred, this.id);
      });
      
    }
    
    if (!formDef.invalid.length){ //(invalid.length == 0){
      $(['#', formDef.prefix, 'buttonPost'].join('')).val(formDef.submitText);
      $(['#', formDef.prefix, 'messageInvalid'].join('')).hide(); //slideUp(100); //css('display', 'none');
      return true;
    }
    else {
      $(['#', formDef.prefix, 'buttonPost'].join('')).val(formDef.submitInvalidText); //.attr('disabled', 'disabled');
      $(['#', formDef.prefix, 'messageInvalid'].join('')).show(); //slideDown(100); //css('display', 'block');
    }
    return false;
  }
  
    
  
  



  function inputSetupHover(el, formDef){
    el.hover(
      function () {
        var
          thisEl = $(this),
          thisElInput = thisEl.contents('input.inputText, textarea.inputTextarea');
          
        thisEl.removeClass('itemInvalid');
        thisElInput.addClass('focused');
        if (thisElInput.hasClass('help')){
          $(['div[helpFor=', thisElInput.attr('id'), ']'].join(''), thisEl).addClass('calloutFocused');
        }
      },
      function () {
        
        var
          thisElFormId = formDef.id,
          allInputs = $(['#', thisElFormId, ' input.inputText, #', thisElFormId, ' textarea.inputTextarea'].join('')); //thisEl.parents('form').attr('id');

        // dehover all text inputs on this form
        allInputs.each(function(){
          var
            thisElInput = $(this),
            thisElInputId = thisElInput.attr('id'),
            thisElFormId = formDef.id;
            
          // only remove hover indication if element is not already focused
          if (thisElInputId != shared.inputFocused){
          
            if (thisElInput.hasClass('help')){
              $(['div[helpFor=', thisElInputId, ']'].join(''), thisElInput.parent()).removeClass('calloutFocused');
            }
            thisElInput.removeClass('focused');
            if (aa.existsIn(shared.forms[thisElFormId].blurred, thisElInputId) && aa.existsIn(shared.forms[thisElFormId].invalid, thisElInputId)){
              thisElInput.parent().addClass('itemInvalid');
            }
          }
        });
        
      }
    );
  }


  
  
  
  
  
  
  function setupTooltips(container, isEl){
    var els;
    
    if (!isEl){
      els = $('[title]', container);
    }
    else {
      els = container.filter('[title]');
    }
    
    els.each(function(){
      var el = $(this);
      el.attr('tooltip', el.attr('title'));
      el.removeAttr('title');
    });
    
    els.tooltip({
      delay: 100,
      bodyHandler: function(){
        var el = $(this);
        return ['<p>', el.attr('tooltip').replace(/ -- /g, '</p><p>'), '</p>'].join('');
      }
    });

  }
  
  

  
  
  
  
  function forceFocus(target){
    if (target && target.length){
      var element = target;
      
      // element passed as a string, so get matching jquery object
      if (typeof target == 'string'){
        element = $(['#', target].join(''));
      }

      // passed element is not a jquery object,
      // so assume it's a formDef
      else if (!target.jquery){
        element = $(['#', target.prefix, target.focus].join(''));
      }
      
      if (element.length){
      
        shared.inputFocused = element.get(0).id;
        
        // delay focus trigger slightly to work correctly in IE 8
        setTimeout(function(){
          element.trigger('focus').addClass('focused');
        }, 50);
        
        if (element.hasClass('help')){
          $(['div[helpFor=', element.attr('id'), ']'].join(''), element.parent()).addClass('calloutFocused');
        }
      }
    }
  }
  

  
  
  
  // for each instance of each element of terms (array) in str, insert prepend before and append after
  // e.g., used for search match highlighting
  function formatString(str, terms, prepend, append, ignored){
    if (!str){
      return '';
    }

    var regex = new RegExp(
      [
        '(', terms.join('|'), ')'
      ].join(''),
      'gi'
    );
    
    str = str.replace(regex, [prepend, '$1', append].join(''));
    
    return str;
  }






  
  
  
  
  function autocompleteKeydown(e){
    // prevent form submission by not passing enter key to window
    if (e.which == 13){
      return false;
    }
  }  
  
  
  
  function autocompleteLoading(input, isLoading){
    if (isLoading){
      input.addClass('iconrightgap icon-loading');
    }
    else {
      input.removeClass('icon-loading');
    }
    
    return true;
    
  }
  
  
  // custom search term highlighting
  function autocompleteHighlight(value, term){
    return value;
  }


  // format single result item for display
  function autocompleteFormatItem(data, i, max, value, query) {
    var stringArray = [];
    var terms = query.split(' ');
    
    value = data.location_name;

    var searchIgnoredString = shared.locationSearchIgnored.join('');
    stringArray.push('<p class="main">', formatString(value, terms, '<span class="ac_resultsMatch">', '</span>', searchIgnoredString));
    
    if (data.unique_identifier && data.unique_identifier != 'null'){
      stringArray.push(' (', formatString(data.unique_identifier, terms, '<span class="ac_resultsMatch">', '</span>', searchIgnoredString), ')');
    }
    
    if (data.city && data.state){
      stringArray.push(' <span class="details nowrap">', formatString(data.city, terms, '<span class="ac_resultsMatch">', '</span>', searchIgnoredString), ', ', formatString(data.state, terms, '<span class="ac_resultsMatch">', '</span>', searchIgnoredString), '</span>');
    }

    stringArray.push('</p>');
    
    if (data.aliases && data.aliases != 'null'){
      stringArray.push('<p class="detailsHanging"><span class="small">AKA: </span>', formatString(data.aliases, terms, '<span class="ac_resultsMatch">', '</span>', searchIgnoredString), '</p>');
    }
    
    return stringArray.join('');
  }

  // determine which parts of this item's data to attempt to match against
  // creates a space delimited string made up of item elements  
  function autocompleteFormatMatch(row, i, max) {
    var matchArray = [];
    var match;

    matchArray.push(row.value);
    
    if (row.unique_identifier && row.unique_identifier != 'null'){
      matchArray.push(' ', row.unique_identifier);
    }
    if (row.aliases && row.aliases != 'null'){
      matchArray.push(' ', row.aliases);
    }
    matchArray.push(' ', row.city);

    match = matchArray.join('');
    return match;
  }
  
  
  function autocompleteFormatResult(row) {
    var resultArray = [row.value];
    
    return resultArray.join('');
  }
  
  
  function autocompleteResult(event, data, formatted){
    var subject = $(event.target).attr('lookup');
    var element = $(['#', event.target.id].join(''));
    var lookup = element.attr('lookup');
    var formObj = element.parents('form');
    var formDef = shared.forms[formObj.attr('id')];
    
    var addedInjectTargetElement = $(['#', formDef.addedInjectTarget].join(''));

    // put lookupVal in hidden field attached to this lookup
    $(['#', event.target.id, '_lookupVal'].join('')).val(data[aa.findObjectByKey(formDef.fields, event.target.id.replace(formDef.prefix, ''), 'name')[0].lookupVal]);                  
    base.formValidateItem(element);


    
    switch (subject){
    
    
    
      case 'company':
        //base.formValidateItem(element);

        // grey out lookupFill fields while ajaxing
        $(["input[lookupFill=", lookup, "]"].join(''), formObj).fadeTo("fast", 0.33).val('Loading...');
        $.post([shared.urlRoot, '/publicSSGet/details'].join(''),
          {
            subject: lookup,
            value: data.location_id
          },
          function(data){
            // data = eval(['(', data, ')'].join(''));
            if (!shared.lastJSON){
              shared.lastJSON = {};
            }
            shared.lastJSON.generic = data;
            var formObj = element.parents('form');
            for (var field in data.result){
              //fblog('#' + shared.forms[formDef.attr('id')].prefix + field + ' = ' + data[field]);
              base.formValidateItem($(['#', shared.forms[formObj.attr('id')].prefix, field].join('')).val(data.result[field] || ''));  // need to access calling form field to determine which sibling fields to update
              // flag this field as having been blurred at least once
              aa.pushUnique(shared.forms[formObj.attr('id')].blurred, [shared.forms[formObj.attr('id')].prefix, field].join(''));
            }
            $(["input[lookupFill=", lookup, "]"].join(''), formObj).fadeTo("fast", 1);
          }
        );
        break;
        
        
    }
  }  
    
  
  
  
  
  
  
    
    
  function timerGetName(args){
    var timerName;
    
    if (args.callee){
      var t1 = args.callee.toString();
      var pos = aa.indexOf(t1, ')');
      if (pos != -1){
        timerName = t1.substr(0, pos + 1).replace('function ', '').replace('function', '').replace(/\(.*\)/, '()');
      }
    }
    else if (String(args) === args) {
      timerName = args;
    }
    
    return timerName;
  }

  
  
  
  // start a stopwatch
  function timerStart(args){
    var timerName = timerGetName(args);
    
    if (timerName){
      ad.stopwatchStart(timerName);
    }
  }
  
  
  
  
  // end a stopwatch and output elapsed time
  function timerEnd(args, note){
    var timerName = timerGetName(args);
    
    if (timerName){
      fblog([timerName, note ? note : '', ': ',  (ad.stopwatchEnd(timerName) / 1000), 's'].join(''));
    }
  }

    
    
    
  // only reliable in IE when passing in MOUSEDOWN or MOUSEUP event (not CLICK event)
  // taken from http://unixpapa.com/js/mouse.html
  function mouseWhich(e){
    var button = false;
    
    /* IE case */
    if (e.which == null) {
      button = (e.button < 2) ? 'left' :
        ((e.button == 4) ? 'middle' : 'right');
    }
    
    /* All others */
    else {
      // button = (e.which < 2) ? 'left' :
        // ((e.which == 2) ? 'middle' : 'right');
      
      button = [e.which, 'left', 'middle', 'right', e.which][am.limit(e.which, 0, 4)]; // event.which for a mouse button should be 1, 2, or 3... outside that range return actual number
    }
    
    return button;
  }
  
  
  
    
    
    
    
    
  function fblog(str, raw){
    if (raw){
      str = as.htmlEncode(str);
    }
    else if (str.replace){
      str = str.replace(/\n/g, '<br />');
    }
    else {
      str = '[Object]';
    }
    
    var time = ad.timeMSString();
    
    logStringArr.push({time: time, text: str});

    // if (!deploy){
    if (shared.logEnabled){
      
      pageLogRefresh();
      
      Acat.fire.log([time, ': ', str].join(''));
    }
    
    return str;
  }




  
  // log object state
  // if forceLog == true:
  //     obj should be a string naming the obj, which will be eval'ed to traverse the actual object
  //     if passing a local object (or one whose scope is inaccessible to the Acat.jq namespace), pass in a reference to the actual object as scopedObj
  function fbdir(obj, forceLog, scopedObj){
    
    // if (!deploy){
    if (shared.logEnabled){
      var time = ad.timeMSString();
      Acat.fire.log([time, ': ', (String(obj) === obj ? obj : '<unnamed>'), ' (Object) = ...'].join(''));
      Acat.fire.dir(scopedObj || eval(obj));
      
      if (forceLog){
        var objName = obj;
        obj = scopedObj || eval(obj);
        logStringArr.push({time: time, text: Acat.array.var_dump(obj, 'html', 2, null, objName)});
        pageLogRefresh();
      }
    }    
  }

  
  
  
  function pageLogRefresh(){
    // var regex = /^([^(\s)]+)\((.*?)\)(.*?:)/;
    // var regex = /^([^\s]+\: )((([^(\s)]+)((\()([^\(\)\:]*)(\)))?(.*:))|(.+))(.*)/;
    // var regex = /^([^\s]+\: )((([^\s]+)((\()([^\(\)\:]*)(\)))?(.*:))|(.+))(.*)/;
    // var regex = /^([\d\:\.]*)\s*([\w\.\s\/]+)(\([^\)]*\)[\s]*)?([\:\=])?(.*)/; //((([^\s]+)((\()([^\(\)\:]*)(\)))?(.*:))|(.+))(.*)/;
    var regex = /^([\d:.-]*)\s*([\w\.\s\/]+)(\([^\)]*\)[\s]*)?([\:\=])?(.*)/; //((([^\s]+)((\()([^\(\)\:]*)(\)))?(.*:))|(.+))(.*)/;
    // var regexError = /[^\s]+\: ---/;
    var regexError = /[^\s]+\: ---|--- ERROR:/;
    var i;
    var outputLine;
    var outputArr = ['<table id="log" cellspacing="0" cellpadding="0">'];

    for (i = 0; i < logStringArr.length; i++){
      if (logStringArr[i].text.match(regexError)){
        outputLine = ['<span class="highlightWarning">', logStringArr[i].text, '</span>'].join('');
      }
      else {
        outputLine = logStringArr[i].text.replace(regex, '<span class="lighter">$1</span> <span class="logFuncName">$2</span><span class="light">$3</span><span class="light">$4</span>$5');
      }
      outputArr.push('<tr><td class="lineNum">', i+1, '</td><td>', logStringArr[i].time, '</td><td>', outputLine, '</td></tr>');
    }
    
    outputArr.push('</table>');
    
    document.getElementById('logContainer').innerHTML = outputArr.join('');
    
    // keep log in view, useful when modal popups prevent seeing end of log
    // $.scrollTo($('#log tr:last'), 0, {offset:-100});
  }
  
    
    
    
  function initLog(){
    if (!shared.logEnabled){
      if (!$('#viewport_log').length){
        $('#viewport_bottom').after('<div id="viewport_log" class="dontprint"><div class="widthCap"><a name="log"></a><div id="logtitle">Application Log (scroll up to return to main document)</div><div id="logsubtitle">To submit this information, use your mouse to click and drag over the text below to highlight it, then right-click and copy the highlighted text (or press Ctrl-C on your keyboard) to copy the text to your clipboard.  You may then paste the text into an email or other document.</div><div id="logContainer" /></div>');
      }
      
      // add links for viewing and copying log
      $('#footerContent').append('<span class="dontprint">&nbsp; &#0183; &nbsp;<a id="link-showlog">Hide Log</a></span>');
      
      $('#link-showlog').click(function(){
        var logViewportEl = $('#viewport_log');
        var thisEl = $(this);
        
        if (logViewportEl.css('display') == 'none'){
          logViewportEl.css('display', 'block');
          $(window).scrollTo(logViewportEl);
          thisEl.html('Hide Log');
          
        }
        else {
          logViewportEl.css('display', 'none');
          thisEl.html('Show Log');
        }
      });
      
      shared.logEnabled = true;
    }

  }


  
  
  function init(params){
    var i;
    
    shared.deploy = (params.deploy !== false ? true : false);
    // insert log output div
    if (!shared.deploy) { 
      initLog();
    }
    
    // log user-agent string for debugging
    fblog(navigator.userAgent);
    fblog('base.init()');
    
    base = Acat.jq.base;
    
    if (shared.initStarted){
      return;
    }
    
    params.target = document.location.hash.substring(1);

    params.vars = params.vars || {};
    for (i in params.vars){
      shared[i] = params.vars[i];
    }
    
    shared.deploy = (params.deploy !== false ? true : false);
    shared.failover = (params.failover !== true ? false : true);
    
    shared.revision = params.revision;
    shared.revisionRaw = params.revisionRaw;
    
    shared.urlRoot = params.urlRoot;
    
    // // insert log output div
    // if (!shared.deploy) { 
      // initLog();
    // }
    
    // // log user-agent string for debugging
    // fblog(navigator.userAgent);
    // fblog('base.init()');
    
    // intercept all ajax requests, appending revision number to data
    $(document).unbind('ajaxSend');
    $(document).bind('ajaxSend', function(event, XMLHttpRequest, ajaxOptions){
      if (ajaxOptions.data){
        ajaxOptions.data = [ajaxOptions.data, '&revision=', shared.revisionRaw].join('');
      }
      else {
        ajaxOptions.data = ['revision=', shared.revisionRaw].join('');
      }
      // XMLHttpRequest.abort();
    });

    
    $.ajaxSetup({
      type: 'post',
      
      // if data isn't defined in a call, set this to a urlencoded space to enable all data-sending procedures in jQuery.ajax
      // otherwise, extra data added in ajaxSend event will be ignored
      data: '+'
    });
    

    // if nothing else to do, return
    if (!params.loadInterface){
      return;
    }


    shared.activeInterface = params.loadInterface;
    
    
    // add passed form data to forms collection
    if (params.formDef){
      var formDef = new formObject(params.formDef);
      
      // add this form to collection
      shared.forms[formDef.id] = formDef;
      // generate form
      formBuild(shared.forms[formDef.id], shared.forms[formDef.id].injectTarget, null, (params.loadInterface == 'clients' && params.query && params.query[0]));

      
      // set up focus/blur handlers
      $.listen(
        'focus',
        'input.inputText, textarea.inputTextarea',
        
        function (e) {
          var thisEl = $(this);
          var thisElParent = thisEl.parents('div.item');
          shared.inputFocused = thisEl.get(0).id;
          //fblog('focused ' + shared.inputFocused);
          thisElParent.removeClass('itemInvalid')
            .contents('input.inputText, textarea.inputTextarea').addClass('focused')
            .filter('input.inputText').select();
          if (thisEl.hasClass('help')){
            $(['div[helpFor=', this.id, ']'].join(''), thisElParent).addClass('calloutFocused');
          }
        }
      );      
      
      $.listen(
        'blur',
        'input.inputText, textarea.inputTextarea',
        
        function (e) {
      //$.intercept('blur', {'input.inputText, textarea.inputTextarea': function(e){
        var thisEl = $(this);
        var thisElId = thisEl.attr('id');
        var thisElParent = thisEl.parents('div.item');
        var formDef = shared.forms[thisEl.parents('form').attr('id')];
        var inputs = $('input.inputText, textarea.inputTextarea, td.inputLabelLeft', thisElParent);
        //fblog('blurred ' + thisElId);
        shared.inputFocused = '';
        // validate contents of this element
        if (!formDef.dontValidate){
          formValidateItem(thisEl);
        }
        
        // flag this field as having been blurred at least once
        aa.pushUnique(formDef.blurred, thisElId);
        
        // always remove focused on blur
        inputs.removeClass('focused');
        if (aa.existsIn(formDef.invalid, thisElId)){
          thisElParent.addClass('itemInvalid');
        }
        if (thisEl.hasClass('help')){
          $(['div[helpFor=', this.id, ']'].join(''), thisElParent).removeClass('calloutFocused');
        }
      });


    }

    
    
    switch(shared.activeInterface){
        
      case "photos-index":
        var galleryElement = $('#galleryContainer');
        
        var galleryItem = $('div.galleryItem');
        
        galleryItem.each(
          function(){
            $(this).click(
              function(e){
                window.location.href = ['photos/', $(this).attr('link')].join('');
              }
            );
          }
        );
        
        // galleryElement.hover(
          // function(e){
            // var el = $(e.target);
            
            // if (!el.is('div.galleryItem')){
              // el = el.parents('div.galleryItem');
            // }
            
            // el.addClass('galleryItemHover');
          // },
          // function(e){
            // var el = $(e.target);
            
            // if (!el.is('div.galleryItem')){
              // el = el.parents('div.galleryItem');
            // }
            
            // el.removeClass('galleryItemHover');
          // }
        // );
        break;


        
      case "photos-gallery":
        var galleryElement = $('#galleryContainer');
      
        $('#galleryContainer a').lightBox({
          imageLoading:      ['/common-', shared.revisionRaw, '/jquery-lightbox-0.5/images/lightbox-ico-loading.gif'].join(''),    // (string) Path and the name of the loading icon
          imageBtnPrev:      ['/common-', shared.revisionRaw, '/jquery-lightbox-0.5/images/lightbox-btn-prev.gif'].join(''),      // (string) Path and the name of the prev button image
          imageBtnNext:      ['/common-', shared.revisionRaw, '/jquery-lightbox-0.5/images/lightbox-btn-next.gif'].join(''),      // (string) Path and the name of the next button image
          imageBtnClose:    ['/common-', shared.revisionRaw, '/jquery-lightbox-0.5/images/lightbox-btn-close.gif'].join(''),    // (string) Path and the name of the close btn
          imageBlank:        ['/common-', shared.revisionRaw, '/jquery-lightbox-0.5/images/lightbox-blank.gif'].join('')    // (string) Path and the name of a blank image (one pixel)
        });
        
        // $('.galleryContainer galleryRow').each(function(){
          // $(this).find('a').lightBox();
        // });
        break;
    }
        
    
  }
  
  
  
  
  if (!Acat.jq.init){
    Acat.jq.init = init;
  }
  
  return {
    buttonAddHoverHandler: buttonAddHoverHandler,
    buttonCreate: buttonCreate,
    buttonUpdate: buttonUpdate,
    forceFocus: forceFocus,
    formObject: formObject,
    formBuild: formBuild,
    formClearEventHandlers: formClearEventHandlers,
    formCreateField: formCreateField,
    formDataCapitalize: formDataCapitalize,
    formGetTextFieldNames: formGetTextFieldNames,
    formGetValues: formGetValues,
    formSetupCheckbox: formSetupCheckbox,
    formSetupDropdown: formSetupDropdown,
    formSetupEventHandlers: formSetupEventHandlers,
    formValidate: formValidate,
    formValidateItem: formValidateItem,
    inputSetupHover: inputSetupHover,
    setupTooltips: setupTooltips,
    
    datepickerHide: datepickerHide,
  
    autocompleteFormatItem: autocompleteFormatItem,
    autocompleteFormatMatch: autocompleteFormatMatch,
    autocompleteFormatResult: autocompleteFormatResult,
    autocompleteHighlight: autocompleteHighlight,
    autocompleteLoading: autocompleteLoading,
    autocompleteResult: autocompleteResult,
    autocompleteMax: autocompleteMax,
    
    formatString: formatString,
  
    init: init,
    
    fbdir: fbdir,
    fblog: fblog,
    initLog: initLog,
    pageLogRefresh: pageLogRefresh,
    
    mouseWhich: mouseWhich,
    
    timerEnd: timerEnd,
    timerGetName: timerGetName,
    timerStart: timerStart
  };
}();


