
function dump(o) {
    for (var p in o)
        debug(p +"="+ o[p]);
}

function dumpToString(o) {
    var s = "";
    for (var p in o)
        s += p +"="+ o[p] +",";
    return s;
}

function debug(s) {
    var div = document.createElement("div");
    div.innerHTML = "DEBUG: "+ s;
    document.body.appendChild(div);
}

function contains(arr, e) {
    for (var i=0; i<arr.length; i++) {
        if (arr[i] == e)
            return true;
    }
    return false;
}

function display(node, showNode) {
    if (showNode)
        $(node).show();
    else
        $(node).hide();
}

function disableOnclick(node) {
    node.disabledOnclick = node.onclick;
    node.onclick = null;
}

function enableOnclick(node) {
    if (node.disabledOnclick) {
        node.onclick = node.disabledOnclick;
        node.disabledOnclick = null;
    }
}

function disable() {
    var field;
    for (var i=0; i<arguments.length; i++) {
        field = getFieldIfString(arguments[i]);
        if(!field)
          return;
        field.disabled = true;
        field.addClassName('disabled')
    }
}

function enable() {
    var field;
    for (var i=0; i<arguments.length; i++) {
        field = getFieldIfString(arguments[i]);
        if(!field)
          return;
        field.disabled = false;
        field.removeClassName('disabled')
    }
}

function setDisabled(node, disabled) {
    if (disabled)
        disable(node);
    else
        enable(node);
}

function $get(comp) {
    var field = $(comp);
    if (field && field.customType == 'S7ImageChooser')
        return field.s7Object;
    if (field && field.customType == 'lookup')
        return field.lookupValue;
    if (field && field.customType == 'datechooser')
        return field.getValue();
    return dwr.util.getValue(comp);
}

function $set(comp, value) {
    var field = $(comp);
    if (field && field.customType == 'S7ImageChooser') {
        field.s7Object = value;
        field.update();
    }
    else if (field && field.customType == "lookup") {
        field.lookupValue = value;
        field.update();
    }
    else {
        dwr.util.setValue(comp, value);
        
        if (dwr.util._isHTMLElement(field, "select"))
            Form.Dynamic.drawSelectOptions(field);
    }
}

function $field(idname) {
    // Look first by name.
    var nodes = document.getElementsByName(idname);
    if (nodes.length > 0)
        return nodes[0];
    // Now look by id.
    return $(idname);
}

// Convenience method. Returns the first element in the given array that has an id property the same as the given id.
function getElement(arr, id, idName) {
    if (!idName)
        idName = "id";

    for (var i=0; i<arr.length; i++) {
        if (arr[i][idName] == id)
            return arr[i];
    }
    return null;
}

function setElement(arr, id, newValue, idName) {
    if (!idName)
        idName = "id";

    for (var i=0; i<arr.length; i++) {
        if (arr[i][idName] == id) {
            arr[i] = newValue;
            break;
        }
    }
}
 
function updateElement(arr, idKey, idValue, propKey, propValue, dobreak) {
    var found = false;
    for (var i=0; i<arr.length; i++) {
        if (arr[i][idKey] == idValue) {
            arr[i][propKey] = propValue;
            found = true;
            if (dobreak)
                return;
        }
    }
    if (!found)
        debug("Warning: updateElement did not find an element to update");
}

function removeElement(arr, id, idName) {
    if (!idName)
        idName = "id";

    for (var i=arr.length-1; i>=0; i--) {
        if (arr[i][idName] == id)
            arr.splice(i, 1);
    }
}

function showMessage(node, msg) {
    node = getNodeIfString(node);
    if (msg) {
        node.show();
        node.innerHTML = msg;
    }
    else
        node.hide();
}
  
function getNodeIfString(node) {
    if (typeof(node) == "string")
        return $(node);
    return node;
}

function getFieldIfString(field) {
    if (typeof(field) == "string")
        return $field(field);
    return field;
}

function escapeQuotes(str) {
    if (!str)
        return "";
    return str.replace(/\'/g,"\\'");
}

function escapeDQuotes(str) {
    if (!str)
        return "";
    return str.replace(/\"/g,"\\\"");
}

function encodeQuotes(str) {
    if (!str)
        return "";
    return str.replace(/\'/g,"%27").replace(/\"/g,"%22");
}

function encodeHtml(str) {
    if (!str)
        return "";
    return str.replace(/</g,"&lt;");
}

function appendNewElement(/*string*/type, /*node*/parent) {
    var node = document.createElement(type);
    parent.appendChild(node);
    return node;
}

function hideContextualMessages(parent) {
  //$$('.ctxmsg').invoke('hide');
  $$('.select-box-error').invoke('removeClassName','select-box-error');
  $$('.input-box-error').invoke('removeClassName','input-box-error');
  $$('.textarea-box-error').invoke('removeClassName','textarea-box-error');
  $$('.check-box-error').invoke('removeClassName','check-box-error');
}

function createContextualMessageNode(field, fieldId) {
}

function cleanUpMessages(messageNode) {
    messageNode = $(typeof(messageNode)=="undefined" ? "errorMessage" : messageNode);
    
    var messagesNode;
    // Delete the last batch of message
    if(messagesNode = messageNode.down('.messages'))
      messagesNode.replace('<div class="messages"></div>');
    else
      messageNode.insert({bottom:'<div class="messages"></div>'});
    return messageNode.down('.messages');
}

function showStatusMessage(message, messageNode) {
    var $messageNode = $(messageNode);

    if(!$messageNode) {
      debug('sero.js - showStatusMessage - messageNode not found - message: ' + message);
      return;
    }

    hideContextualMessages();
    
    cleanUpMessages($messageNode);

    $messageNode.down('h3').replace('<h3>'+message+'</h3>');

    // Show the message box
    $messageNode.show();
}

function showDwrMessages(/*DwrResponse.messages*/messages, /*tbody*/messageNode) {
    var $messageNode = $(typeof(messageNode)=="undefined" ? "errorMessage" : messageNode);

    hideContextualMessages();

    var $messagesNode = cleanUpMessages($messageNode);

    // Hide the message box
    $messageNode.hide();

    var $m, $errorMessage;
    var $messages = new Array();
    for (var i = 0; i < messages.length; i++) {
        $m = messages[i];
        if ($m.contextKey) {
            $errorMessage = $m.contextualMessage;

            var encKey = $m.contextKey.gsub(/\./,'_').gsub(/\[/,'_LB_').gsub(/\]/,'_RB_');
            var $currentContext = $(encKey);
            if ($currentContext == null) {
                $currentContext = $('model_'+ encKey);
                if ($currentContext == null) {
                    alert("Could not find field for contextual message: model_" + $m.contextKey.gsub(/\./,'_'));
                    continue;
                }
            }
            // Add .error CSS Class to context item to show red box
            $currentContext.up('div').addClassName($currentContext.boxName + '-error');
            // Add the <LABEL> value from the context item to the error message
            $errorMessage = $errorMessage.sub('${label}', $currentContext.previous('label').innerHTML.sub('*',''));
        }
        else
            $errorMessage = $m.genericMessage;
        $messages[$messages.length] = $errorMessage;
    }
    
    if ($messages.length > 0) {
        if (!$messageNode) {
            alert("message node not defined");
            return;
        }

        $messages.each(function(m) {
            $messagesNode.insert({bottom:'<div class="message">' + m + '</div>'});
        });
        
        // This is not good. Sometimes the message is custom to the page.
        //$messageNode.down('h3').replace('<h3>please check the following:</h3>');

        $messageNode.show();
    }
}

function hideDwrMessages(/*tbody*/messageNode) {
    var $messageNode = $(typeof(messageNode)=="undefined" ? "errorMessage" : messageNode);
    // Hide the message box
    $messageNode.hide();
}

function endsWith(str, ending) {
    return str.endsWith(ending);
}

var sero = {};

function addOnLoad(func) {
    Event.observe(document, 'dom:loaded', func);
//    Event.observe(window, 'load', func);
}

function setInputFields(obj, /*optional*/fieldPrefix) {
    if (!fieldPrefix)
        fieldPrefix = "";
    
    var field, p;
    for (p in obj) {
        field = $field(fieldPrefix + p);
        if (field != null) {
            $set(fieldPrefix + p, obj[p]);
            // Run the field 'out' event on each field that has data
            // This will clear the label and show the field value
            Form.Dynamic.out(field);
        }
    }
}

function getInputFields(obj, /*optional*/fieldPrefix) {
    if (!fieldPrefix)
        fieldPrefix = "";

    var field, p;
    for (p in obj) {
        field = $field(fieldPrefix + p);
        if (field != null)
            obj[p] = $get(fieldPrefix + p);
    }
}

/**
 * name: (string) the name of the node in which content appears. Also, the prefix for specific nodes, including:
 *   Table - where typically all of the stuff below lives. It can start out not displayed, and will be revealed upon search.
 *   Pagination - where pagination should go
 *   Empty - the node to show if the list is empty
 *   List - the node in which to display the non-empty list
 * list: (array) the array of content to be displayed
 * cellFunctions: the array of functions that render the rows, or the function that returns such an array of functions
 * options:
 *   onRowClick: (function) what to do if a row gets clicked
 *   rowClass: (string) styling class name for a row
 *   itemsPerPage: (int) just what it says. defaults to 10.
 *   paginationRadius: (int) the number of page links to have on either side of the current page. defaults to 5.
 *   cellCreator: (function) create TDs and set attributes on them. Passed on to DWR. (optional)
 */
function SearchResultPagination(name, list, cellFunctions, options) {
    var self = this;
    this.name = name;
    this.list = list;
    this._page = null; // array of current page - cache semantics
    this.cellFunctions = cellFunctions;
    this.itemsPerPage = 10;
    this.paginationRadius = 5;
    this.currentPage = 0;
    this.errorDiv = null;
    this.cellCreator = null;

    if (options) {
        this._onRowClick = options.onRowClick || null;
        this._rowClass = options.rowClass || null;
        if (options.itemsPerPage)
            this.itemsPerPage = options.itemsPerPage;
        if (options.paginationRadius)
            this.paginationRadius = options.paginationRadius;
        if (options.cellCreator)
            this.cellCreator = options.cellCreator;
    }

    this.isEmpty = function() {
        return !this.list || this.list.length == 0;
    };
    
    this.getPage = function() {
        if (!this._page) {
            var start = Math.min(this.currentPage * this.itemsPerPage, this.list.length);
            this._page = this.list.slice(start, Math.min(start+this.itemsPerPage, this.list.length));
        }
        return this._page;
    };

    this.updatePagination = function(node) {
        if (this.isEmpty()) {
            node.hide();
            return;
        }
        
        if (!node.gotoPage)
            node.gotoPage = function(pageNum) { self.gotoPage(pageNum); };
        
        var gotoStart = " onclick='$(\""+ name +"Pagination\").gotoPage(";
        var gotoEnd = "); return false;'";
        var content = "";
        
        if (this.currentPage > 0)
            content += "<a href='#'"+ gotoStart + (this.currentPage - 1) + gotoEnd +">Prev</a>";
        
        var pagesStart = this.currentPage - this.paginationRadius;
        if (pagesStart < 0)
            pagesStart = 0;
        var pagesEnd = this.currentPage + this.paginationRadius;
        if (pagesEnd > this.numberOfPages)
            pagesEnd = this.numberOfPages;
        
        if (pagesStart > 0)
            content += " ... ";
        
        for (var i=pagesStart; i<pagesEnd; i++) {
            if (i == this.currentPage)
                content += " <b>"+ (i+1) +"</b> ";
            else
                content += " <a href='#'"+ gotoStart + i + gotoEnd +">"+ (i+1) +"</a>";
        }
        
        if (pagesEnd < this.numberOfPages)
            content += " ... ";
        
        if (this.currentPage < this.numberOfPages - 1)
            content += " <a href='#'"+ gotoStart + (this.currentPage + 1) + gotoEnd +">Next</a>";
        
        content += " (total of "+ this.list.length +" items)";
        
        showMessage(node, content);
    };
    
    this.gotoPage = function(pageNum) {
        this._page = null;
        this.currentPage = pageNum;
        this.renderList();
    };
    
    this.refreshPage = function() {
        this.gotoPage(this.currentPage);
    };
    
    this.renderList = function() {
        if ($(this.name +"Table"))
            $(this.name +"Table").show();
        
        this.errorDiv = $(this.name +"Empty") || $("errorMessage");
        
        // delete the rows
        dwr.util.removeAllRows(this.name +"List");
        
        if (this.isEmpty()) {
            // show the error div
            this.errorDiv.show();
            
            // CAN'T DO THIS BECAUSE IT CLOSES THE LOOKUP LAYER, not to mention hides the error div we just showed (and it doesn't look ugly)
            //// hide the results table - we don't want to see the headers all by themselves - looks ugly
            //if ($(name))
            //    $(name).hide();
        }
        else {
            // hide the error div if it's still up
            this.errorDiv.hide();
            // show the results table
            var page = this.getPage();
            var funcs = this.cellFunctions;
            if (typeof funcs == "function")
                funcs = funcs();
            dwr.util.addRows(this.name +"List", page, funcs, {cellCreator: this.cellCreator });
            
            // do stuff to new rows
            $(this.name+'List').childElements().each(function(e,i) {
                e.value = page[i];  // stuff the list data into the element
                this._onRowClick && Event.observe(e,'click',this._onRowClick);
                this._rowClass && e.addClassName(this._rowClass);
            }, this);
        }
        
        this.updatePagination($(this.name +"Pagination"));
    };
    
    // Clear any old pagination function.
    $(name +"Pagination").gotoPage = null;
    
    if (this.isEmpty())
        this.numberOfPages = 0;
    else
        this.numberOfPages = parseInt((this.list.length - 1) / this.itemsPerPage) + 1;
    this.renderList();
}

/**
 * EA content management. Uses a list of comma-delimited ids to sort the given list.
 */
function sortList(/*Array*/list, /*field name*/idProperty, /*comma-delimited id string*/orderString) {
    if(typeof(orderString)!="string")
      return;
    var ids = orderString.split(",");
    var i, j, item;
    var listIndex = 0;
    for (i=0; i<ids.length; i++) {
        for (j=listIndex; j<list.length; j++) {
            if (list[j][idProperty] == ids[i]) {
                item = list[j];
                list.splice(j, 1);
                list.splice(listIndex, 0, item);
                listIndex++;
            }
        }
    }
}

/**
 * EA content management. Returns a string of comma-delimited ids representing the order of given array.
 */
function getSortOrder(/*Array*/list, /*field name*/idProperty) {
    var first = true;
    var result = "";
    for (var i=0; i<list.length; i++) {
        if (first)
            first = false;
        else
            result += ",";
        result += list[i][idProperty];
    }
    return result;
}

/**
 * EA content management. Converts a comma-delimited list to an array.
 */
function stringToArray(str) {
    return str.split(",");
}

/**
 * EA content management. Converts an array to a comma-delimited list.
 */
function arrayToString(arr, prop) {
    if (prop) {
        return ""+ arr.collect(function(item) {
            return item[prop];
        });
    }
    return ""+ arr;
}

function show(e) {
 	$(e).show();
}

/**
 * Returns a new array of items from the first array that are also in the second array.
 */
function intersection(arr1, idProperty1, arr2, idProperty2) {
    var result = new Array();
    
    if (!arr1 || arr1.length == 0 || !arr2 || arr2.length == 0)
        return result;
    
    var e1, e2;
    for (var i=0; i<arr1.length; i++) {
        if (idProperty1)
            e1 = arr1[i][idProperty1];
        else
            e1 = arr1[i];
        
        for (var j=0; j<arr1.length; j++) {
            if (idProperty2)
                e2 = arr2[j][idProperty2];
            else
                e2 = arr2[j];
            
            if (e1 == e2)
                result.push(arr1[i]);
        }
    }
    
    return result;
}

function radioClicked(cb) {
    var cbs = document.getElementsByName(cb.name);
    for (var i=0; i<cbs.length; i++)
        $set(cbs[i], cbs[i] == cb);
}

function getRadioValue(name) {
    var cbs = document.getElementsByName(name);
    for (var i=0; i<cbs.length; i++) {
        if ($get(cbs[i]))
            return cbs[i].value;
    }
    return null;
}

function pad(str, char, len) {
    if (!str)
        str = "";
    else
        str = new String(str);
    while (str.length < len)
        str = char + str;
    return str.substring(str.length - len);
}
