/*

  Hello Jsutil World.

  Jsutil is basically a port of python/pyutil to Javascsript with some extra
  js-specific functionality thrown in.

  This is just a start -- eventually it would be a full-blown library with a
  decent modular structure and pimp (plex imports), &c.

 */

// -----------------------------------------------------------------------------
// jsutil.core
// -----------------------------------------------------------------------------

var jsutil = {

    __version__: '0.0.1',

    define_namespace: function (namespace) {
/* Check if a given namespace exists, otherwise create it. */

        var obj = window;
        var split_namespace = namespace.split('.');
        var namespace_depth = split_namespace.length - 1;
    
        for (var i=0; i < split_namespace.length + 1; i++) {

            if (!obj[split_namespace[i]])
                obj[split_namespace[i]] = new Object();

            obj = obj[split_namespace[i]];

            if (i == namespace_depth)
                return obj;

        }

    }

}

var printfire = function () {

    /* 1st arg is object. 2nd arg can be xml/css/box/capture/js -- tab name.*/

    if (document.createEvent) {
        write_to_firebug.args = arguments;
        var event = document.createEvent("Events");
        event.initEvent("printfire", false, true);
        dispatchEvent(event);
    }

}

// -----------------------------------------------------------------------------
// jsutil.globals
// -----------------------------------------------------------------------------

var globals = jsutil.define_namespace("jsutil.globals");

globals.debug = true;

globals.XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

globals.dynamic_label_class = "dynamic-label";

globals.proto_edit_mode = true;

globals.transparent_overlay_class = "transparent-overlay";
globals.transparent_inlay_class = "transparent-inlay";

globals.tab_toggle_targets = [];

// -----------------------------------------------------------------------------
// General upgrades to the builtin types that ship with your Javascript
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// Object
// -----------------------------------------------------------------------------

if (Object.update == null) {

    Object.update = function (object, extension) {

	    for (var key in extension) {
		    if (!(key in object))
			    object[key] = extension[key];
		}

        return object;

    }

    Object.extend = function (object, extension) {

        for (var key in extension) {
            // ehm, put this back in soon ...
            // if (!(key in object))
                object[key] = extension[key];
        }

        return object;

    }

}

Object.extend(Object, {

    keys: function (object) {

        var temp = new Array();

        for (var key in object)
            temp[temp.length] = key;

        return temp;

    },

    values: function (object) {

        var temp = new Array();

        for (var key in object)
            temp[temp.length] = object[key];

        return temp;

    },

    items: function (object) {

        var temp = new Array();

        for (var key in object)
            temp[temp.length] = new Array(key, object[key]);

        return temp;

    }

});

// Object.extend(Object.prototype, {

//     extend: function (extension) {
//         return Object.extend.apply(this, [this, extension]);
//     },

//     items: function () {

//         var temp = new Array();

//         for (var key in this)
//             temp[temp.length] = [key, this[key]];

//         return temp;

//     },

// });

// -----------------------------------------------------------------------------
// Function
// -----------------------------------------------------------------------------

// jsolait: builtin apply/call

Object.update(Function.prototype, {

    apply: function (object, args) {

        var temp = [];

        for (var i=0; i < args.length; i++)
            temp[i] = 'args[' + i + ']';

        object.__apply__ = this;

        temp = 'object.__apply__(' + temp.join(',') + ')';
        temp = eval(temp);

        delete object.__apply__;

        return temp;

    },

    call: function (object) {

        var args = [];

        // copy all arguments but the first
        for (var i=1; i < arguments.length; i++)
            args[i-1] = arguments[i];

        return this.apply(object, args);

    }

});

// -----------------------------------------------------------------------------
// Array
// -----------------------------------------------------------------------------

Object.update(Array.prototype, {

    // jsolait: array extensions

    splice: function (index, howMany) {

        var a = this.slice(0, index);
        var e = this.slice(index + howMany, this.length);
        var r = this.slice(index, index + howMany);

        this.length = 0;

        for (var i=0; i < a.length; i++)
            this[this.length] = a[i];

        for (var i=2; i < arguments.length; i++)
            this[this.length] = arguments[i];

        for (var i=0; i < e.length; i++)
            this[this.length] = e[i];

        return r;

    },

    pop: function () {

        var item = this[this.length-1];

        this.length -= 1;
        return item;

    },

    push: function () {

        var length = this.length;

        for (var i=0; i < arguments.length; i++, ++length)
            this[length] = arguments[i];

        return length;

    },

    shift: function () {

        var item = this[0];

        for (var i=1; i < this.length; i++)
            this[i-1] = this[i];

        this.length -= 1;
        return item;

    },

    unshift: function () {

        var temp = []

        for (var i=0; i < arguments.length; i++)
            temp[i] = arguments[i];

        for (var i=0; i < this.length; i++)
            temp[temp.length] = this[i];

        this.length = temp.length;

        for (var i=0; i < temp.length; i++)
            this[i] = temp[i];

        return this.length;

    },

    // open rico

    remove: function (dx) {

      if (isNaN(dx) || dx > this.length)
          return false;
      for (var i=0,n=0; i < this.length; i++)
          if (i != dx)
              this[n++]=this[i];
      this.length-=1;

    },

    remove_item: function (item) {

        for (var i=0 ; i < this.length ; i++) {
            if (this[i] == item) {
                this.remove(i);
                break;
            }
        }

    },

    /* matches value in array: returns bool -- common.js */

    /* common.js Reference Article by Dustin Diaz:
     * http://www.dustindiaz.com/top-ten-javascript/
     *
     */

    has_item: function (item) {

        for (var i=0; i < this.length; i++) {
            if (this[i] === item)
                return true;
        }

        return false;

    }

});

// -----------------------------------------------------------------------------
// Number
// -----------------------------------------------------------------------------

// Wolfgang Dumhs: builtin number extensions

Object.extend(Number.prototype, {

    toFixed: function (d) {

        var n = this;
        d = d || 0;
        var f = Math.pow(10, d);

        n = Math.round (f * n) / f;
        n = (n >= 0) ? n+Math.pow(10, -(d+1)) : n-Math.pow(10, -(d+1));
        n += '';

        return d == 0 ? n.substring(0, n.indexOf('.')) :
               n.substring(0, n.indexOf('.') + d + 1);

    },

    toExponential: function (d) {

        var n = this;
        var e = 0;

        if (n != 0)
            e = Math.floor(Math.log(Math.abs(n)) / Math.LN10);

        n /= Math.pow(10, e);

        if (isFinite(d)) {
            if (Math.abs(n) + 5*Math.pow(10, -(d+1)) >= 10.0){
                n /= 10;
                e += 1;
            }
            n = n.toFixed(d);
        }

        n += 'e';

        if (e >= 0)
            n += '+';

        n += e;
        return n;

    }

});

// -----------------------------------------------------------------------------
// String
// -----------------------------------------------------------------------------

Object.extend(String.prototype, {

    strip: function () {
        return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
    },

    startswith: function (prefix) {
        return (this.indexOf(prefix) === 0);
    },

    endswith: function (suffix) {

        var start = this.length - suffix.length;

        if (start < 0)
            return false;

        return (this.lastIndexOf(suffix, start) == start);

    }

});

// -----------------------------------------------------------------------------
// document
// -----------------------------------------------------------------------------

Object.extend(document, {

    // open rico

    getElementsByTagAndClassName: function (tagName, className) {

        if (tagName == null)
            tagName = '*';

        var children = document.getElementsByTagName(tagName) || document.all;
        var elements = new Array();

        if (className == null)
            return children;

        for (var i=0; i < children.length; i++) {
            var child = children[i];
            var classNames = child.className.split(' ');
            for (var j=0; j < classNames.length; j++) {
                if (classNames[j] == className) {
                    elements.push(child);
                    break;
                }
            }
        }

        return elements;

    },

    /* grab Elements from the DOM by className -- common.js */

    getElementsByClass: function (searchClass, node, tag) {

        var classElements = new Array();

        if (node == null)
            node = document;

        if (tag == null)
            tag = '*';

        var els = node.getElementsByTagName(tag);
        var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");

        for (var i = 0, j = 0; i < els.length; i++) {
            if ( pattern.test(els[i].className) ) {
                classElements[j] = els[i];
                j++;
            }
        }

        return classElements;

    }

});

// -----------------------------------------------------------------------------
// jsutil.dom
// -----------------------------------------------------------------------------

var dom = jsutil.define_namespace("jsutil.dom");

Object.extend(dom, {

    /* common.js */

    insertAfter: function (parent, node, referenceNode) {

        /* insert an element after a particular node */
        parent.insertBefore(node, referenceNode.nextSibling);

    }

});

// -----------------------------------------------------------------------------
// jsutil.sys
// -----------------------------------------------------------------------------

var sys = jsutil.define_namespace("jsutil.sys");

Object.extend(sys, {

    get_sys_path: function () {

        try {

            sys.path = JSUTIL_PATH;
            sys.root = JSUTIL_PATH[0];
            sys.script = null;

        } catch (e) {

            var scripts = document.getElementsByTagName('script');

            for (var i=0; i < scripts.length; i++) {

                var source = scripts[i].src;

                if (source && source.match(/jsutil\.js$/)) {
                    sys.root = source.substring(0, source.indexOf('jsutil.js'));
                    sys.path = [sys.root];
                    sys.script = scripts[i];
                }

            }

            // if (!sys.path)
            //     throw("Couldn't define ``sys.path``.");

        }

    },

    supports_basic_dom: function () {

        /* Return true if the system supports the required DOM functionality. */

        return document.getElementById &&
               (window.addEventListener || window.attachEvent) &&
               null == navigator.appVersion.match(/SomeInvalidBrowser\/\d+$/);

    },

    modules: new Array(),

    import_module: function (module) {

        if (module in sys.modules)
            return sys.modules[module];

        var script_src = sys.root + module + '.js';

        if (sys.supports_basic_dom() && document.documentElement &&
            document.documentElement.namespaceURI == globals.XUL_NS) {

            var script = document.createElement(globals.XUL_NS, 'script');

            script.id = 'jsutil-' + module.replace('/', '-');
            script.type = "application/x-javascript";
            script.src = script_src;

            sys.script.parentNode.appendChild(script);

        } else {

            // appending a ``script`` via DOM doesn't work in Safari =(

            document.write(
              '<script type="text/javascript" src="' + script_src + '"></script>'
              );

        }

    }

});

sys.get_sys_path();

// -----------------------------------------------------------------------------
// jsutil.builtin
// -----------------------------------------------------------------------------

var $P = jsutil.define_namespace("jsutil.builtin");

Object.extend($P, {

    less_than: function (a, b) {

        return a < b;

    },

    // prototype.js

    get: function () {

        var elements = new Array();

        for (var i=0; i < arguments.length; i++) {

            var element = arguments[i];

            if (typeof element == 'string')
                element = document.getElementById(element);

            if (arguments.length == 1)
                return element;

            elements.push(element);
        }

        return elements;

    },

    vars: function () {

        /*

        Having named, positional, optional and default arguments is bloody useful.

        Whilst javascript gives us named and positional, it doesn't give us the
        wonderfulness of python's default and optional parameters.

        Thus the ``vars`` hack ;p

        Until I figure out a slightly prettier syntax, you are stuck with:

          >>> vars.call(this, arguments, 'your', 'argument', 'names', 'here')

        Instead of doing:

          >>> def foo(a, b, c='test')

        you have to do:

          >>> function foo () {
          ...    vars.call(this, arguments, 'a', 'b', {'c':'test'})
          ... }

        Admittedly, not as pretty and obvious, but hey, one step at a time =)

        And, oh, instead of the parameters being available locally, they are
        all on ``this`` =(

        Anyways, this is all very primitive atm -- but, hey Lord Baach, it works!

        */

        var call_args = arguments[0];

        // var call_args = arguments.callee.arguments;

        var args = new Array();
        var kwargs = {};

        for (var i=1; i<arguments.length; i++) {

            var arg = arguments[i];

            if (typeof(arg) == "string") {

                args.push(arg);

            } else {

                for (var ob in arg) {
                    this[ob] = arg[ob];
                    args.push(ob);
                }

            }
      
        }

        var new_call_args = new Array();

        for (var i=0; i<call_args.length; i++) {

            var arg = call_args[i];

            if ($P.get_object_type(arg) == "dict") {
                for (var ob in arg)
                    this[ob] = arg[ob];
                    // delete call_args[i] -- why this no workie?
            } else {
                new_call_args.push(arg);
            }
      
        }

        for (var i=0; i<new_call_args.length; i++)
            this[args[i]] = new_call_args[i];

        // for (var _i in kwargs) eval("var " + _i.toString() + " = kwargs[_i]");
        // ehm, a scope 2nd argument to eval only works in mozilla ;p

    },

    sample_add: function () {

        // work out how to automate this ...

        vars.call(this, arguments, 'a', {'b':2});

        return this.a + this.b;

    },

    import_module: function (name, code) {

        /* Import and return the ``package.module``. */

        return;

    },

    get_globals: function () {

        // when 'this' happens to be 'window' ... but, true global ... ?

        debug.dump_object(this);

    },

    get_object_keys: function (object) {

        var keys = new Array();
        for (var key in object) keys.push(key);
        return keys;

    },

    // t (jan minagawa)

    get_object_type: function (object) {

        // first, we look for constants

        switch (object) {

            case null:
                return "null";

            case window:
                return "window";

            case window.event:
                return "event";

        }

        // then, we try the primitive typeof

        var type = typeof object;

        // fuck me! typeof returned something useful!

        if (type != "object")
            return type;

        // but, most often not =(

        // @/@ bug

        if (type == "function") {

            var function_name = object.toString();

            if ((/^\/.*\/$/).test(function_name))
              return "regexp";

            if ((/^\[object.*\]$/i).test(function_name))
              type = "object";

        }

        // we checkout the constructor

        var constructor = object.constructor;

        if (constructor != null) {
          switch (constructor) {

              case Array:
                  return "array";

              case Date:
                  return "date";

              case RegExp:
                  return "regexp";

              case Object:
                  type = "dict"; // object
                  break;

              case ReferenceError:
                  return "error";

              default:
                  var constructor_name = constructor.toString();
                  var match = constructor_name.match(/\s*function (.*)\(/);
                  if (match != null)
                      return match[1];

          }
        }

        // maybe it's a dom thing?

        var node_type = object.nodeType;

        if (node_type != null ) {
            switch (node_type) {

                case 1:
                    if (object.item == null)
                        return "domelement";
                    break;

                case 3:
                    return "textnode";

            }
        }

        // convert it to a string and see ?

        var object_as_string = object.toString();

        if (object_as_string != null) {

          var match = object_as_string.match(/^\[object (.*)\]$/i);

          if (match != null) {

              match = match[1].toLowerCase();

              switch (match) {

                  case "event":
                      return "event";

                  case "math":
                      return "math";

                  case "error":	
                      return "error";

                  case "mimetypearray":
                      return "mimetypecollection";

                  case "pluginarray":
                      return "plugincollection";

                  case "windowcollection":
                      return "window";

                  case "nodelist":
                  case "htmlcollection":
                  case "elementarray":
                      return "domcollection";

              }

          }

        }

        if (object.moveToBookmark && object.moveToElementText)
            return "textrange";

        if (object.callee != null)
            return "arguments";

        if (object.item != null)
            return "domcollection";

        // as a last resort, we return the naive typeof

        return type;

        // ehm ...

        if (window.event && (window.event.type == object.type))
            return "event";

    },

    is_instance: function (object, type) {

        // behaves like python's ``isinstance``, opposed to the js ``instanceof``
        //   >>> (a_list instanceof Array)

        if ($P.get_object_type(type) == "array") {
            for (var i=0; i<type.length; i++) {
              if ($P.get_object_type(object) == type[i])
                  return true;
            }
            return false;
        }

        return $P.get_object_type(object) == type;

    }

});

// -----------------------------------------------------------------------------
// jsutil.browser
// -----------------------------------------------------------------------------

var browser = jsutil.define_namespace("jsutil.browser");

Object.extend(browser, {

    redirect: function (destination) {

      if (window.location.replace) {
          window.location.replace(destination);
      } else {
          window.location = destination;
      }

    },

    play_gif_animation: function (event) {

        alert(event.target);

    },

    has_flash_support: function () {

        /* Return whether the system claims to support flash. */

        if (navigator.mimeTypes["application/x-shockwave-flash"]) {
            return true;
        } else { 
            return false;
        }

    },

    get_document_body: function () {

        /* A standards-compliance manner of getting ``document.body``. */

        if (document.body) {
            return document.body;
        } else {
            return document.getElementsByTagName("body").item(0);
        }

    },

    get_document_head: function() {

        /* Return the ``<head />`` element. */

        return document.getElementsByTagName("head").item(0);

    },

    create_dom_document: function (namespace, node_name) {

        if (document.implementation && document.implementation.createDocument)

            return document.implementation.createDocument(
              namespace, node_name, null
              );

        // new ActiveXObject("Msxml2.DOMDocument.4.0") ... other versions ?

    },

    /* 'tis better to test for browser capabilities than inspect user agent */

    is_mozilla: function () {

        if (document.implementation && document.implementation.createDocument)
            return true;

    },

    is_internet_explorer: function () {

        try {
            // ehm, MSIE 5.x + ?
            xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
            return true;
        } catch (e) {
            return false;
        }

    },

    // wpbasti (http://www.blogger.com/profile/5400748): msie https iframe solution

    create_iframe: function () {

        var iframe = document.createElement("iframe");

        // hack to overcome shitty MSIE probs in https mode
        // see: http://jszen.blogspot.com/2005/05/secure-iframe-gotcha.html
        iframe.src = "javascript:void()";

        return iframe;

    },

    include_javascript: function(uri) {

        var script = document.createElement('script');

        script.type = "text/javascript";
        script.src = uri;

        browser.get_document_head().appendChild(script);

    },

    // getPageScroll()
    // Returns array with x,y page scroll values.
    // Core code from - quirksmode.org

    get_page_scroll: function () {

        var y_scroll;

        if (self.pageYOffset) {

            y_scroll = self.pageYOffset;

        } else if (
          document.documentElement && document.documentElement.scrollTop
          ) {

            // Explorer 6 Strict
            y_scroll = document.documentElement.scrollTop;

        } else if (document.body) {

            // all other Explorers
            y_scroll = document.body.scrollTop;

        }

        return new Array('', y_scroll);

    },

    // getPageSize()
    // Returns array with page width, height and window width, height
    // Core code from - quirksmode.org
    // Edit for Firefox by pHaez

    get_page_size: function () {

        var xScroll, yScroll;

        if (window.innerHeight && window.scrollMaxY) {	
            xScroll = document.body.scrollWidth;
            yScroll = window.innerHeight + window.scrollMaxY;
        } else if (document.body.scrollHeight > document.body.offsetHeight) {
            // all but Explorer Mac
            xScroll = document.body.scrollWidth;
            yScroll = document.body.scrollHeight;
        } else {
            // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
            xScroll = document.body.offsetWidth;
            yScroll = document.body.offsetHeight;
        }

        var windowWidth, windowHeight;

        if (self.innerHeight) {
            // all except Explorer
            windowWidth = self.innerWidth;
            windowHeight = self.innerHeight;
        } else if (document.documentElement && document.documentElement.clientHeight) {
            // Explorer 6 Strict Mode
            windowWidth = document.documentElement.clientWidth;
            windowHeight = document.documentElement.clientHeight;
        } else if (document.body) {
            // other Explorers
            windowWidth = document.body.clientWidth;
            windowHeight = document.body.clientHeight;
        }

        // for small pages with total height less then height of the viewport
        if (yScroll < windowHeight) {
            pageHeight = windowHeight;
        } else { 
            pageHeight = yScroll;
        }

        // for small pages with total width less then width of the viewport
        if (xScroll < windowWidth) {
            pageWidth = windowWidth;
        } else {
            pageWidth = xScroll;
        }

        arrayPageSize = new Array(pageWidth, pageHeight, windowWidth, windowHeight);
        return arrayPageSize;

    },

    /* common.js -- get, set, and delete cookies */

    get_cookie: function (name) {

        var start = document.cookie.indexOf(name+'=');
        var len = start + name.length + 1;

        if ((!start) && (name != document.cookie.substring(0, name.length)))
            return null;

        if (start == -1)
            return null;

        var end = document.cookie.indexOf(';', len);

        if (end == -1)
            end = document.cookie.length;

        return unescape(document.cookie.substring(len, end));

    },
	
    set_cookie: function (name, value, path, expires, domain, secure) {

        var today = new Date();
        today.setTime(today.getTime());

        if (expires)
            expires = expires * 1000 * 60 * 60 * 24;

        var expires_date = new Date(today.getTime() + (expires));

        document.cookie = name + '=' + escape(value) +
          ((expires) ? ';expires=' + expires_date.toGMTString() : '') + 
          //expires.toGMTString()
          ((path) ? ';path=' + path : '') +
          ((domain) ? ';domain=' + domain : '') +
          ((secure) ? ';secure' : '');

    },
	
    delete_cookie: function (name, path, domain) {

        if (browser.get_cookie(name))
          document.cookie = name + '=' +
			((path) ? ';path=' + path : '') +
			((domain) ? ';domain=' + domain : '') +
			";expires=Thu, 01-Jan-1970 00:00:01 GMT";

    }

});

// -----------------------------------------------------------------------------
// jsutil.datetime
// -----------------------------------------------------------------------------

var datetime = jsutil.define_namespace("jsutil.datetime");

Object.extend(datetime, {

    now: function () {

        return (new Date()).getTime();

    }

});

// -----------------------------------------------------------------------------
// jsutil.debug
// -----------------------------------------------------------------------------

var debug = jsutil.define_namespace("jsutil.debug");

Object.extend(debug, {

    eval: function (code) {

        var eval_output = $P.get("jsutil-eval-output");

        if (!eval_output) {

            var eval_container = document.createElement("div");

            eval_container.id = "jsutil-eval-container";
            eval_container.className = "widget";
            eval_container.innerHTML = "<h3>Jsutil Eval Output</h3>";

            eval_output = document.createTextNode("");
            eval_output.id = "jsutil-eval-output";

            eval_container.appendChild(eval_output);

            browser.get_document_body().appendChild(eval_container);

        }

        eval_output.innerHTML = eval(code);

    },

    setup_eval_output: function () {

        debug.eval("");
        var input_container = document.createElement("div");

        var input = document.createElement("textarea");
        input.cols="60";
        input.rows="20";

        var submit = document.createElement("input");
        submit.type = "submit";
        submit.on_click = debug.eval("2 + 2");

        input_container.appendChild(input);
        input_container.appendChild(submit);

        browser.get_document_body().appendChild(input_container);

    },

    get_debug_output_element: function () {

        var ElementKnoten;

        // if it exists already ... return it

        ElementKnoten = $P.get("jsutil-debug");

        if (ElementKnoten) {
            return ElementKnoten;
        }

        // else, create one

        ElementKnoten = document.createElement("div");

        ElementKnoten.id = "jsutil-debug";
        ElementKnoten.className = "widget";
        ElementKnoten.innerHTML = "<h3>Jsutil Debugger</h3><hr />";

        ElementKnoten.write = function (text) {
            this.innerHTML += text + "<hr />";
        }

        // overload the existing ...
        // debug.write = ElementKnoten.write

        browser.get_document_body().appendChild(ElementKnoten);

        return ElementKnoten;

    },

    write: function (text) {

        // <Debug: >
        if (globals.debug)
            debug.get_debug_output_element().innerHTML += text + "<hr />";

    },

    toggle_debugger: function () {

        if (globals.debug) {
            debug.get_debug_output_element().style.display = 'none';
            globals.debug = false;
        } else {
            debug.get_debug_output_element().style.display = 'show';
            globals.debug = true;
        }

    },

    inspect: function (object) {

        var output = "";

        output += "<h4>Type: " + $P.get_object_type(object) + "</h4>";
        output += "<h4>Value: " + object + "</h4>";

        var attributes = new Array();
        var j = 0;

        // get all objects and their contents

        for (var i in object) {
          attributes[j] = i + ": " + object[i]; // element.getAttribute(i)
          j += 1;
        }

        // sort and modify for display

        attributes.sort();

        for (var i=0; i<j; i++) {
          output += attributes[i] + "<br />";
        }

        return output;

    },

    dump_object: function (object) {

        return debug.write(debug.inspect(object));

    }

});

// -----------------------------------------------------------------------------
// jsutil.exception
// -----------------------------------------------------------------------------

var exception = jsutil.define_namespace("jsutil.exception");

Object.extend(exception, {

    raise: function (type, reason) {

        if (globals.debug) {
            alert("["+type+": "+reason+"]");
        }

    }

});

// throw('hmz');
// exception.raise('IndexError', 'hmz');

// -----------------------------------------------------------------------------
// jsutil.event
// -----------------------------------------------------------------------------

jsutil.define_namespace("jsutil.event");

Object.extend(jsutil.event, {

    /* common.js derivation of scott andrew's */

    add_event: function (obj, event_type, func) {

        /* Attach events to objects, e.g. add_event(window, 'load', somefunc) */

        // da standards way

        if (obj.addEventListener) {

            // false -- bubble up?
            obj.addEventListener(event_type, func, false);
            jsutil.event_cache.add(obj, event_type, func);
            return true;

        // da IE way

        } else if (obj.attachEvent) {

            obj['e'+event_type+func] = func;

            obj[event_type+func] = function () {
                obj['e'+event_type+func](window.event);
            }

            var result = obj.attachEvent('on'+event_type, obj[event_type+func]);
            jsutil.event_cache.add(obj, event_type, func);

            return result;

        } else {

            obj['on'+event_type] = obj['e'+event_type+func];
            return false;

        }

    },

    /* window 'load' attachment */
    add_load_event: function (func) {

        var old_onload = window.onload;

        if (typeof window.onload != 'function') {
            window.onload = func;
        } else {
            window.onload = function() {
                old_onload();
			    func();
            };
        }

    },

    get_event: function (event) {

        if (!event)
            return window.event;

        return event;

    },

    get_event_source: function (event) {

        event = jsutil.event.get_event(event);

        // da standards way

        if (typeof event.target != 'undefined')
            return event.target;

        // da IE way

        if (typeof event.srcElement != 'undefined')
            return event.srcElement;

        return true;

    }

});

// -----------------------------------------------------------------------------
// jsutil.event_cache
// -----------------------------------------------------------------------------

var event_cache = jsutil.define_namespace('jsutil.event_cache');

globals.events_list = new Array();

Object.extend(event_cache, {

    add: function (node, event_name, handler) {
        globals.events_list.push(arguments);
    },

    flush : function () {

        var events_list = globals.events_list;
        var item;

        for (var i=events_list.length-1; i >= 0; i--) {

            item = events_list[i];

            if (item[0].removeEventListener) {
                item[0].removeEventListener(item[1], item[2], item[3]);
            }

            if (item[1].substring(0, 2) != 'on') {
                item[1] = 'on' + item[1];
            }

            if (item[0].detachEvent) {
                item[0].detachEvent(item[1], item[2]);
            }
            
            item[0][item[1]] = null;

        }

	}

});

// -----------------------------------------------------------------------------
// jsutil.io
// -----------------------------------------------------------------------------

var io = jsutil.define_namespace("jsutil.io");

Object.extend(io, {

    focus_on_element: function (element_id) {

      var element = $P.get(element_id);

      if (element)
          element.focus();

    },

    notify: function (message, destination, highlight_area, image_src) {

      if (destination == undefined)
          destination = 'priority-notifications';

      if (highlight_area == undefined)
          highlight_area = 'notifications';

      if (image_src == undefined)
          image_src = '/static/images/icons/info.png';

      destination = $P.get(destination);

      if (!destination)
        return;

      destination.innerHTML = '';
      
      if (image_src) {
          var image = new Image();
          image.src = image_src;
          destination.appendChild(image)
      }

      destination.innerHTML += ' ' + message;

      new Effect.Highlight(highlight_area, {duration: 1.2}); 

    },

    // t (jan minagawa)

    sniff_key: function (event) {

        event = jsutil.event.get_event(event);

        var key_code = io.get_key_code(event);
        var character = String.fromCharCode(key_code);
        var modifier_keys = new Array();

        if (event.shiftKey) modifier_keys.push("shift");
        if (event.altKey) modifier_keys.push("alt");
        if (event.ctrlKey) modifier_keys.push("ctrl");
        if (event.metaKey) modifier_keys.push("meta");

        debug.write("Keypressed:"+character+" ("+modifier_keys.length+") <br />")

        // trying to block keys for firefox 1.0

        if (event.keyCode == 122) // fullscreen ?
            event.stopPropagation();

        return true;

    },

    get_key_code: function (event) {

        if (event.keyCode)
            return event.keyCode;

        if (event.which) 
            return event.which;

        return null;

    },

    setup_key_sniffer: function () {

        document._default_onkeypress = document.onkeypress;
        document.onkeypress = io.sniff_key;

    },

    setup_dynamic_labels: function () {

        var labels = document.getElementsByTagName("label");

        for (var i=0; i < labels.length; i++) {

            if (labels[i].className == globals.dynamic_label_class) {
                var label_for = $P.get(labels[i].htmlFor);
                jsutil.event.add_event(label_for, "focus", io.focus_dynamic_label);
                jsutil.event.add_event(label_for, "blur", io.blur_dynamic_label);
                label_for.default_value = labels[i].firstChild.nodeValue;
                label_for.value = label_for.default_value;
            }
        }

        for (var i=0; i < document.forms.length; i++){
            jsutil.event.add_event(document.forms[i], "submit", io.reset_dynamic_labels);
        }
    },

    focus_dynamic_label: function (event) {

        var element = jsutil.event.get_event_source(event);

        element.select()

        if (element.value == element.default_value) {
            element.value = "";
        }

    },

    blur_dynamic_label: function (event) {

        var element = jsutil.event.get_event_source(event);

        if (element.value == "") {
            element.value = element.default_value;
        }

    },

    reset_dynamic_labels: function (event) {

        var element = jsutil.event.get_event_source(event);
        var labels = element.getElementsByTagName("label");

        for (var i=0; i < labels.length; i++) {

            if (labels[i].className == globals.dynamic_label_class) {
                var label_for = $P.get(labels[i].htmlFor);
                if (label_for.value == label_for.default_value) {
                    label_for.value = "";
                }
            }
        }

    },

    hide_element: function (element) {
        if (element.style) {
            element.style.visibility = "hidden";
            element.style.display = "none";
        }
    },

    show_element: function (element) {
        if (element.style) {
            element.style.visibility = "visible";
            element.style.display = '';
        }
    },

    /* common.js */

    toggle_element_display: function (element) {

        element = $P.get(element);

        if (element.style.display != 'none') {
            element.style.display = 'none';
        } else {
            element.style.display = '';
        }

    },

    /* Simon Willison - http://simon.incutio.com/archive/2003/11/06/easytoggle */

    setup_tab_toggle: function () {

        var link, id, target;
        var first = true;
        var tabs = globals.tab_toggle_targets;

        for (var i=0; (link = document.links[i]); i++) {

            if (/\btabtoggle\b/.exec(link.className)) {

                id = link.href.split('#')[1];
                target = document.getElementById(id);
                tabs.push(target);

                //if (first) {
                //    first = false;
                //} else {
                //    target.style.display = 'none';
                //}

                // link.onclick = io.toggle_tab_handler;

            }

        }

    },

    toggle_tab: function (id, fade_in) {

        if (fade_in == undefined)
          fade_in = false;

        var element;

        for (var i=0; (element = globals.tab_toggle_targets[i]); i++) {
            if (element.id != id) {
                element.style.display = 'none';
            } else {
                if (!fade_in)
                    element.style.display = 'block';
            }
        }

        if (fade_in)
            new Effect.Appear(id);

    },

    toggle_tab_handler: function (event) {

        var source = jsutil.event.get_event_source(event);

        if (source == true)
            return true;

        /* For most browsers, targ would now be a link element; Safari however
           returns a text node so we need to check the node type to make sure */

        if (!source.href)
            source = source.parentNode;

        if (source.nodeType == 3)
            source = source.parentNode;

        var id = source.href.split('#')[1];

        io.toggle_tab(id);

        // document.title = "Viewing" + id;
        // document.location.replace('#'+id);

        return false;

    },

    clear_fade_out: function (event, overlay_id, inlay_id) {

        if (!overlay_id)
            var overlay_id = globals.transparent_overlay_class;

        if (!inlay_id)
            var inlay_id = globals.transparent_inlay_class;

        var overlay = $P.get(overlay_id);
        var inlay = $P.get(inlay_id);

        overlay.style.display = 'none';
        inlay.style.display = 'none';

    },

    fade_out_background: function (overlay_id, inlay_id, o_margin, i_margin) {

        var page_size = browser.get_page_size();
        var page_scroll = browser.get_page_scroll();

        if (!overlay_id)
            var overlay_id = globals.transparent_overlay_class;

        if (!inlay_id)
            var inlay_id = globals.transparent_inlay_class;

        var overlay = $P.get(overlay_id);
        var inlay = $P.get(inlay_id);

        if (!overlay) {

            var body = browser.get_document_body();
            var overlay = document.createElement("div");

            overlay.id = overlay_id;
            overlay.style.display = 'none';
            overlay.style.position = 'absolute';
            overlay.style.top = '0px';
            overlay.style.left = '0px';
            overlay.style.zIndex = '90';
            overlay.style.width = '100%';
            overlay.style.textAlign = 'center';

            body.insertBefore(overlay, body.firstChild);

        }

        if (!inlay) {

            var inlay = document.createElement("div");

            inlay.id = inlay_id;
            inlay.style.display = 'none';
            // inlay.style.position = 'absolute';
            inlay.style.zIndex = '100';	

            overlay.appendChild(inlay);

        }

        if (o_margin) {
            overlay.style.margin = o_margin;
        } else {
            // set height of overlay to take up whole page
            overlay.style.height = (page_size[1] + 'px');
        }

        overlay.style.display = 'block';

        if (i_margin) {
            inlay.style.margin = i_margin;
        } else {
            // var inlay_top = page_scroll[1] + ((page_size[3] - 35 - 00));
            // var inlay_left = ((page_size[0] - 40 - 200) / 2);

            // inlay.style.top = (inlay_top < 0) ? "0px" : inlay_top + "px";
            // inlay.style.left = (inlay_left < 0) ? "0px" : inlay_left + "px";

            inlay.style.marginTop = '200px';
        }

        inlay.style.display = 'block';

    },

    setup_transparent_overlay: function (event, overlay_id, inlay_id) {

        // preload and create loader image
        var imgPreloader = new Image();

        // if loader image found, create link to hide lightbox and create loadingimage
        imgPreloader.onload = function () {

            var objLoadingImageLink = document.createElement("a");
            objLoadingImageLink.setAttribute('href','#');
            objLoadingImageLink.onclick = function () {
                hideLightbox();
                return false;
            }
            objOverlay.appendChild(objLoadingImageLink);

            var objLoadingImage = document.createElement("img");
            objLoadingImage.src = loadingImage;
            objLoadingImage.setAttribute('id','loadingImage');
            objLoadingImage.style.position = 'absolute';
            objLoadingImage.style.zIndex = '150';
            objLoadingImageLink.appendChild(objLoadingImage);

            // clear onLoad, as IE will flip out w/animated gifs
            imgPreloader.onload=function() {
            };
            return false;

        }

        // imgPreloader.src = loadingImage;

    }

});

// -----------------------------------------------------------------------------
// jsutil.mimetype
// -----------------------------------------------------------------------------

var mimetype = jsutil.define_namespace("jsutil.mimetype");

Object.extend(mimetype, {

    get_supported_mimetypes: function () {

        if (navigator.mimeTypes)
            return navigator.mimeTypes;

        if (navigator.plugins)
            return navigator.plugins;

        /* new ? */
        return Array();

    }

});

// -----------------------------------------------------------------------------
// jsutil.functional
// -----------------------------------------------------------------------------

var functional = jsutil.define_namespace("jsutil.functional");

Object.extend(functional, {

    map: function (func, sequence) {

       var output = new Array();

       for (var i = 0; i<sequence.length; i++)
         output[i] = func(sequence[i]);

       return output;

    },

    eliffunc: function () {

        return;

    },

    iterator: function () {

        // this.state = 0
        // this.values = values
        // this.len = len(value)

        // this.next = function () {}

        // this.state += 1
        // return this.values[(this.state - 1) % this.len]

        return;

    },

    partial: function () {

        var func = arguments[0];
        var ori_args = new Array();

        for (var i=1; i < arguments.length; i++) {
            ori_args.push(arguments[i]);
        }

        return function () {

            var new_args = new Array();

            for (var i=0; i < ori_args.length; i++)
                new_args.push(ori_args[i]);

            for (var i=0; i < arguments.length; i++)
                new_args.push(arguments[i]);

            return func.apply(this, new_args);

        }

    }

});

// -----------------------------------------------------------------------------
// jsutil.networking
// -----------------------------------------------------------------------------

var networking = jsutil.define_namespace("jsutil.networking");

globals.http_requests = new Array();

Object.extend(networking, {

    create_http_request: function () {

        // hmz, really should set this constructor up just once in a cache ...

        if (window.XMLHttpRequest)
            return new XMLHttpRequest(); // mozilla and safari

        try {
            return new ActiveXObject("Msxml2.XMLHTTP"); // new MSIE
        } catch (error) {
            try {
                return new ActiveXObject("Msxml2.XMLHTTP.4.0");
            } catch (error) {
                try {
                    return new ActiveXObject("Microsoft.XMLHTTP"); // old MSIE
                } catch (error) {
                    return false;
                }
            }
        }

    },

    abort_http_requests: function () {

        for (var i=0; i < globals.http_requests.length; i++)
            globals.http_requests[i].abort();

    },

    setup_networking: function () {

        jsutil.event.add_event("beforeunload", function () { a = true });
        jsutil.event.add_event("beforeunload", networking.abort_http_requests)

    }

});

networking.supports_networking = networking.create_http_request() && true;

// -----------------------------------------------------------------------------
// jsutil.testing
// -----------------------------------------------------------------------------

var testing = jsutil.define_namespace("jsutil.testing");

testing.run_tests = true;

// -----------------------------------------------------------------------------
// jsutil.time
// -----------------------------------------------------------------------------

//
// pause(numberMillis)
// Pauses code execution for specified time. Uses busy code, not good.
// Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602
//

var time = jsutil.define_namespace("jsutil.time");

Object.extend(time, {

    sleep: function (period) {

        var now = datetime.now();
        var finish_time = now + period;

        while (true) {
            now = datetime.now();
            if (now > finish_time)
                return;
        }

    }

});

// -----------------------------------------------------------------------------
// jsutil.urllib
// -----------------------------------------------------------------------------

var urllib = jsutil.define_namespace("jsutil.urllib");

Object.extend(urllib, {

    quote: function () {

        return;

    },

    // concatenate arguments

    concat: function (kwargs) {

        var output = new Array();

        for (var key in kwargs) {
            output[output.length] = key + '=' + kwargs[key];
        }

        return output.join('&');

    }

});

var xmllib = jsutil.define_namespace("jsutil.xmllib");

Object.extend(xmllib, {

    escape: function (content) {

        content = content.replace(new RegExp('&', 'g'), '&amp;');
        content = content.replace(new RegExp('<', 'g'), '&lt;');
        content = content.replace(new RegExp('>', 'g'), '&gt;');
        // content = content.replace(new RegExp('"', 'g'), '&quot;');

        return content;

    },

    unescape: function (content) {

        // content = content.replace(new RegExp('&quot;', 'g'), '"');
        content = content.replace(new RegExp('&gt;', 'g'), '>');
        content = content.replace(new RegExp('&lt;', 'g'), '<');
        content = content.replace(new RegExp('&amp;', 'g'), '&');

        return content;

    }

});

// -----------------------------------------------------------------------------
// jsutil.widget
// -----------------------------------------------------------------------------

var widget = jsutil.define_namespace("jsutil.widget");

Object.extend(widget, {

    create: function (body, type, label, widget_id, widget_class, value) {

        if (!body) {
            var body = browser.get_document_body();
        } else {
            var body = $P.get(body);
        }

        if (!type)
            var type = 'text_small';

        if (!label)
            var label = 'Text';

        if (!widget_id)
            var widget_id = '';

        if (!widget_class)
            var widget_class = 'widget';

        if (!value)
            var value = '';

        // if (!_default)
        //   var _default = false;

        //var element_label = document.createElement('label');
        //element_label.id = widget_id + '-label';
        //element_label.htmlFor = widget_id;
        //element_label.appendChild(document.createTextNode(label));

        var element = widget[type](widget_id, widget_class, value);

        // body.appendChild(element_label);

        if (type == 'text_wysiwyg') {
          body.appendChild(element);
          //var value_id = element._value_element_id = widget_id + '_value';
          //tinyMCE.addMCEControl(value_id, element, widget_id);
        } else if (type == 'datetime') {

          for (var i=0; i<element.length; i++)
            body.appendChild(element[i]);

          var element = element[0];

        Calendar.setup({
          inputField     :    widget_id,
          ifFormat       :    "%d/%B/%Y",
            // ifFormat       :    "%Y/%m/%d %H:%M",
            // ifFormat       :    "%m/%d/%Y %I:%M %p",
            // ifFormat       :    "%B %e, %Y",
          showsTime      :    true,
          timeFormat     :    "24",
          button         :    widget_id+'-trigger',
          align          :    "Bl",
          singleClick    :    true
        });

        } else if (type == 'text_autocomplete') {

          for (var i=0; i<element.length; i++)
            body.appendChild(element[i]);

          if (!globals.auto_completion_options)
            globals.auto_completion_options = new Array();

          new Autocompleter.Local(
            widget_id, widget_id+'-auto-completions',
            globals.auto_completion_options,
            {tokens: ','}
            )
          var element = element[0];

        } else {
          body.appendChild(element);
        }

        return element;

    },

    multi_checkbox: function(widget_id, widget_class, value_url){
	this.checkbox_elements = document.createElement('p');
	this.widget_id = widget_id;
	this.widget_class = widget_class;
        new Ajax.Request(
     	   value_url,
           Object.update({
           asynchronous: true,
           onComplete: this.get_checkboxes.bind(this)
        }, {method:'get'})
        );
	return this.checkbox_elements;
    },

    integer: function (widget_id, widget_class, value) {

        var element = document.createElement('input');

        element.id = widget_id;
        element.type = 'text';
        element.className = widget_class;
        element.size = 10;
        element.value = value;

        return element;

    },

    //pass in an object "values checked" {name:boolean}
    get_checkboxes: function (transport) {
        values_checked = transport.responseText.stripTags();
	values_checked = eval("("+ values_checked +")");
	var elements = this.checkbox_elements; 
	for (var value in values_checked){         
            var element = document.createElement('input'); //or is that select
	    element.id = this.widget_id;
	    element.type = 'checkbox';
	    element.value = value;
	    var text = document.createTextNode(value);
	    var brek = document.createElement('br');
	    element.checked = values_checked[value];	
	    elements.appendChild(text);
	    elements.appendChild(element);
	    elements.appendChild(brek);
	}			   
	return elements;        
    },

    booleanchoice: function (widget_id, widget_class, value) {

        var element = document.createElement('input');

        element.id = widget_id;
        element.type = 'checkbox';
        element.className = widget_class;

        if (value == 'True') {
          element.value = value;
          element.checked = true;
        } else {
          element.value = '';
          element.checked = false;
        }

        return element;

    },

    text_small: function (widget_id, widget_class, value) {

        var element = document.createElement('input');

        element.id = widget_id;
        element.type = 'text';
        element.className = widget_class;
        element.size = 40;
        element.value = value;

        return element;

    },

    text_autocomplete: function (widget_id, widget_class, value) {

        var element = document.createElement('input');

        element.id = widget_id;
        element.type = 'text';
        element.className = widget_class;
        element.size = 50;
        element.value = value;
        element.autocomplete = 'off';

        var completions = document.createElement('div');
        completions.id = widget_id+'-auto-completions';
        completions.className = 'auto-complete';

        return [element, completions];

    },

    text_medium: function (widget_id, widget_class, value) {

        var element = document.createElement('textarea');

        element.id = widget_id;
        element.className = widget_class;
        element.cols = 60;
        element.rows = 5;
        element.appendChild(document.createTextNode(value));

        return element;

    },

    text_large: function (widget_id, widget_class, value) {

        var element = document.createElement('textarea');
        element.id = widget_id;
        element.className = widget_class;
        element.cols = 80;
        element.rows = 15;
        element.appendChild(document.createTextNode(value));
        return element;

    },

    datetime: function (widget_id, widget_class, value) {

        var element = document.createElement('input');

        element.id = widget_id;
        element.type = 'text';
        element.className = widget_class;
        element.size = 40;
        element.value = value;

        var button = document.createElement('button');
        button.type = 'reset';
        trigger_id = button.id = widget_id + '-trigger';
        button.appendChild(document.createTextNode('...'));

        return [element, button];

    },

    text_password: function (widget_id, widget_class, value) {

        var element = document.createElement('input');

        element.id = widget_id;
        element.type = 'password';
        element.className = widget_class;
        element.value = value;

        return element;

    },

    text_wysiwyg: function (widget_id, widget_class, value) {

        var element =  widget.text_large(widget_id, widget_class, value);

        // element.className = "mceEditor";

        return element;
    }

});

/*

# autocomplete support

! text (small)
text (medium)
! text (large)
text (password)
! text (wysiwyg)

calendar (datetime)
calendar (date)
calendar (time)

number
currency
file
url

boolean

options (string)
options (number)

sequence (comma separated)
sequence (line separated)

metatype

 */

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------

var Effect = jsutil.define_namespace('Effect');

Effect.AccordionSize = Class.create();

Effect.AccordionSize.prototype = {

   initialize: function(e1, e2, start, end, duration, steps, options) {
      this.e1       = $P.get(e1);
      this.e2       = $P.get(e2);
      this.start    = start;
      this.end      = end;
      this.duration = duration;
      this.steps    = steps;
      this.options  = arguments[6] || {};

      this.accordionSize();
   },

   accordionSize: function() {

      if (this.isFinished()) {
         // just in case there are round errors or such...
         this.e1.style.height = this.start + "px";
         this.e2.style.height = this.end + "px";

         if(this.options.complete)
            this.options.complete(this);
         return;
      }

      if (this.timer)
         clearTimeout(this.timer);

      var stepDuration = Math.round(this.duration/this.steps) ;

      var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0;
      this.resizeBy(diff);

      this.duration -= stepDuration;
      this.steps--;

      this.timer = setTimeout(this.accordionSize.bind(this), stepDuration);
   },

   isFinished: function() {
      return this.steps <= 0;
   },

   resizeBy: function(diff) {
      var h1Height = this.e1.offsetHeight;
      var h2Height = this.e2.offsetHeight;
      var intDiff = parseInt(diff);
      if ( diff != 0 ) {
         this.e1.style.height = (h1Height - intDiff) + "px";
         this.e2.style.height = (h2Height + intDiff) + "px";
      }
   }

};

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------

widget.accordion = Class.create();

widget.accordion.prototype = {

   initialize: function(container, options) {
      this.container            = $P.get(container);
      this.lastExpandedTab      = null;
      this.accordionTabs        = new Array();
      this.setOptions(options);
      this._attachBehaviors();
      if (!container)
        return;

      this.container.style.borderBottom = '1px solid ' + this.options.borderColor;
      // validate onloadShowTab
       if (this.options.onLoadShowTab >= this.accordionTabs.length)
        this.options.onLoadShowTab = 0;

      // set the initial visual state...
      for ( var i=0 ; i < this.accordionTabs.length ; i++ )
      {
        if (i != this.options.onLoadShowTab){
         this.accordionTabs[i].collapse();
         this.accordionTabs[i].content.style.display = 'none';
        }
      }
      this.lastExpandedTab = this.accordionTabs[this.options.onLoadShowTab];
      if (this.options.panelHeight == 'auto'){
          var tabToCheck = (this.options.onloadShowTab === 0)? 1 : 0;
          var titleBarSize = parseInt(RicoUtil.getElementsComputedStyle(this.accordionTabs[tabToCheck].titleBar, 'height'));
          if (isNaN(titleBarSize))
            titleBarSize = this.accordionTabs[tabToCheck].titleBar.offsetHeight; 
          var totalTitleBarSize = this.accordionTabs.length * titleBarSize;
          var parentHeight = parseInt(RicoUtil.getElementsComputedStyle(this.container.parentNode, 'height'));
          if (isNaN(parentHeight))
            parentHeight = this.container.parentNode.offsetHeight;
          
          this.options.panelHeight = parentHeight - totalTitleBarSize-2;
      }
      
      this.lastExpandedTab.content.style.height = this.options.panelHeight + "px";
      this.lastExpandedTab.showExpanded();
      this.lastExpandedTab.titleBar.style.fontWeight = this.options.expandedFontWeight;

   },
   //         borderColor         : '#1f669b',

   setOptions: function(options) {
      this.options = {
         expandedBg          : '#63699c',
         hoverBg             : '#63699c',
         collapsedBg         : '#6b79a5',
         expandedTextColor   : '#ffffff',
         expandedFontWeight  : 'bold',
         hoverTextColor      : '#ffffff',
         collapsedTextColor  : '#ced7ef',
         collapsedFontWeight : 'normal',
         hoverTextColor      : '#ffffff',
         // borderColor         : '#644bab',
         borderColor         : '#63699c',
         panelHeight         : 200,
         onHideTab           : null,
         onShowTab           : null,
         onLoadShowTab       : 0
      }
      Object.extend(this.options, options || {});
   },

   showTabByIndex: function( anIndex, animate ) {
      var doAnimate = arguments.length == 1 ? true : animate;
      this.showTab( this.accordionTabs[anIndex], doAnimate );
   },

   showTab: function( accordionTab, animate ) {

      var doAnimate = arguments.length == 1 ? true : animate;

      if ( this.options.onHideTab )
         this.options.onHideTab(this.lastExpandedTab);

      this.lastExpandedTab.showCollapsed(); 
      var accordion = this;
      var lastExpandedTab = this.lastExpandedTab;

      this.lastExpandedTab.content.style.height = (this.options.panelHeight - 1) + 'px';
      accordionTab.content.style.display = '';

      accordionTab.titleBar.style.fontWeight = this.options.expandedFontWeight;

      if ( doAnimate ) {
         new Effect.AccordionSize( this.lastExpandedTab.content,
                                   accordionTab.content,
                                   1,
                                   this.options.panelHeight,
                                   100, 10,
                                   { complete: function() {accordion.showTabDone(lastExpandedTab)} } );
         this.lastExpandedTab = accordionTab;
      }
      else {
         this.lastExpandedTab.content.style.height = "1px";
         accordionTab.content.style.height = this.options.panelHeight + "px";
         this.lastExpandedTab = accordionTab;
         this.showTabDone(lastExpandedTab);
      }
   },

   showTabDone: function(collapsedTab) {
      collapsedTab.content.style.display = 'none';
      this.lastExpandedTab.showExpanded();
      if ( this.options.onShowTab )
         this.options.onShowTab(this.lastExpandedTab);
   },

   _attachBehaviors: function() {
      var panels = this._getDirectChildrenByTag(this.container, 'DIV');
      for ( var i = 0 ; i < panels.length ; i++ ) {

         var tabChildren = this._getDirectChildrenByTag(panels[i],'DIV');
         if ( tabChildren.length != 2 )
            continue; // unexpected

         var tabTitleBar   = tabChildren[0];
         var tabContentBox = tabChildren[1];
         this.accordionTabs.push( new widget.accordion.tab(this,tabTitleBar,tabContentBox) );
      }
   },

   _getDirectChildrenByTag: function(e, tagName) {
      var kids = new Array();
      var allKids = e.childNodes;
      for( var i = 0 ; i < allKids.length ; i++ )
         if ( allKids[i] && allKids[i].tagName && allKids[i].tagName == tagName )
            kids.push(allKids[i]);
      return kids;
   }

};

widget.accordion.tab = Class.create();

widget.accordion.tab.prototype = {

   initialize: function(accordion, titleBar, content) {
      this.accordion = accordion;
      this.titleBar  = titleBar;
      this.content   = content;
      this._attachBehaviors();
   },

   collapse: function() {
      this.showCollapsed();
      this.content.style.height = "1px";
   },

   showCollapsed: function() {
      this.expanded = false;
      this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
      this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
      this.titleBar.style.fontWeight      = this.accordion.options.collapsedFontWeight;
      this.content.style.overflow = "hidden";
   },

   showExpanded: function() {
      this.expanded = true;
      this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
      this.titleBar.style.color           = this.accordion.options.expandedTextColor;
      this.content.style.overflow         = "visible";
   },

   titleBarClicked: function(e) {
      if ( this.accordion.lastExpandedTab == this )
         return;
      this.accordion.showTab(this);
   },

   hover: function(e) {
		this.titleBar.style.backgroundColor = this.accordion.options.hoverBg;
		this.titleBar.style.color           = this.accordion.options.hoverTextColor;
   },

   unhover: function(e) {
      if ( this.expanded ) {
         this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
         this.titleBar.style.color           = this.accordion.options.expandedTextColor;
      }
      else {
         this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
         this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
      }
   },

   _attachBehaviors: function() {
      this.content.style.border = "1px solid " + this.accordion.options.borderColor;
      // this.content.style.borderRightWidth = "0px";
      this.content.style.borderTopWidth    = "0px";
      this.content.style.borderBottomWidth = "0px";
      this.content.style.margin            = "0px";

      this.titleBar.onclick     = this.titleBarClicked.bindAsEventListener(this);
      this.titleBar.onmouseover = this.hover.bindAsEventListener(this);
      this.titleBar.onmouseout  = this.unhover.bindAsEventListener(this);
   }

};

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------



// -----------------------------------------------------------------------------
// zebra
// -----------------------------------------------------------------------------


var Mp3 = {
	playimg: null,
	player: null,
	setup: function() {
		var all = $c('mp3', $tag('ol'), 'li')
		for (var i = 0, o; o = all[i]; i++) { o = $tag('h3',o)
			var img = document.createElement('img')
			img.src = '/static/images/icons/arrow_right.png'; img.title = 'listen'
			img.height = img.width = 12
			img.onclick = Mp3.makeToggle(img, $tag('a', o).href)
			o.insertBefore(img, o.firstChild)
	}},
    play: function (ob_id, uri) {
      var img = document.createElement('img');
      img.src = '/static/images/icons/arrow_right.png';
      img.title = 'listen';
      img.onclick = Mp3.makeToggle(img, uri);
      var o = $P.get(ob_id);
	  o.insertBefore(img, o.firstChild);
    },
	toggle: function(img, url) {
		if (Mp3.playimg == img) Mp3.destroy()
		else {
			if (Mp3.playimg) Mp3.destroy()
			img.src = '/static/images/icons/stop.png'; Mp3.playimg = img;
			Mp3.player = document.createElement('span')
			Mp3.player.innerHTML = '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"' +
			'codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0"' +
			'width="50" height="15" id="player" align="middle">' +
			'<param name="allowScriptAccess" value="sameDomain" />' +
			'<param name="flashVars" value="theLink='+url+'" />' +
			'<param name="movie" value="/static/swf/mp3.swf" /><param name="quality" value="high" />' +
			'<param name="bgcolor" value="#ffffff" />' +
			'<embed src="/static/flash/mp3.swf" flashVars="theLink='+url+'"'+
			'quality="high" bgcolor="#ffffff" width="50" height="15" name="player"' +
			'align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash"' +
			' pluginspage="http://www.macromedia.com/go/getflashplayer" /></object>'
			img.parentNode.insertBefore(Mp3.player, img.nextSibling)
	}},
	destroy: function() {
		Mp3.playimg.src = '/static/img/mp3/play.gif'; Mp3.playimg = null
		Mp3.player.removeChild(Mp3.player.firstChild); Mp3.player.parentNode.removeChild(Mp3.player); Mp3.player = null
	},
	makeToggle: function(img, url) { return function(){ Mp3.toggle(img, url) }}
}

// styling functions
function isA(o,klass){ return new RegExp('\\b'+klass+'\\b').test(o.className) }

function $tags(t,o){ o=o||document; return o.getElementsByTagName(t) }
function $tag(t,o,i) { o=o||document; return o.getElementsByTagName(t)[i||0] }

// get elements by class name
function $c(c,o,t) { o=o||document;
	var children = o.getElementsByTagName(t || '*'), elements = []
	for (var i = 0, child; child = children[i]; i++) if(isA(child,c)) elements.push(child)
	return elements
}

// get mouse pointer position
function pointerX(e) { return e.pageX || (e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)) }
function pointerY(e) { return e.pageY || (e.clientY + (document.documentElement.scrollTop || document.body.scrollTop)) }

// get window size
function windowHeight() { return self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0 }
function windowWidth() { return self.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0 }

// get pixel position of an object
function getY(o){ var y = 0
	if (o.offsetParent) while (o.offsetParent) { y += o.offsetTop; o = o.offsetParent }
	return y
}
function getX(o){ var x = 0
	if (o.offsetParent) while (o.offsetParent) { x += o.offsetLeft; o = o.offsetParent }
	return x
}

  // this function is need to work around 
  // a bug in IE related to element attributes
  function hasClass(obj) {
     var result = false;
     if (obj.getAttributeNode("class") != null) {
         result = obj.getAttributeNode("class").value;
     }
     return result;
  }   

 function stripe(id) {

    // the flag we'll use to keep track of 
    // whether the current row is odd or even
    var even = false;
  
    // if arguments are provided to specify the colours
    // of the even & odd rows, then use the them;
    // otherwise use the following defaults:
    var evenColor = arguments[1] ? arguments[1] : "#fff";
    var oddColor = arguments[2] ? arguments[2] : "#eee";
  
    // obtain a reference to the desired table
    // if no such table exists, abort
    var table = document.getElementById(id);
    if (! table) { return; }
    
    // by definition, tables can have more than one tbody
    // element, so we'll have to get the list of child
    // &lt;tbody&gt;s 
    var tbodies = table.getElementsByTagName("tbody");

    // and iterate through them...
    for (var h = 0; h < tbodies.length; h++) {
    
     // find all the &lt;tr&gt; elements... 
      var trs = tbodies[h].getElementsByTagName("tr");
      
      // ... and iterate through them
      for (var i = 0; i < trs.length; i++) {

	    // avoid rows that have a class attribute
        // or backgroundColor style
	    if (!hasClass(trs[i]) && ! trs[i].style.backgroundColor) {
 
         // get all the cells in this row...
          var tds = trs[i].getElementsByTagName("td");
        
          // and iterate through them...
          for (var j = 0; j < tds.length; j++) {
        
            var mytd = tds[j];

            // avoid cells that have a class attribute
            // or backgroundColor style
	        if (! hasClass(mytd) && ! mytd.style.backgroundColor) {
        
		      mytd.style.backgroundColor = even ? evenColor : oddColor;
              
            }
          }
        }
        // flip from odd to even, or vice-versa
        even =  ! even;
      }
    }
  }

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------

// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
//                    text only at the beginning of strings in the 
//                    autocomplete array. Defaults to true, which will
//                    match text at the beginning of any *word* in the
//                    strings in the autocomplete array. If you want to
//                    search anywhere in the string, additionally set
//                    the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
//                   a partial match (unlike minChars, which defines
//                   how many characters are required to do any match
//                   at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
//                 Defaults to true.
//
// It's possible to pass in a custom function as the 'selector' 
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.

var Autocompleter = jsutil.define_namespace('Autocompleter');

if (Autocompleter.Base == undefined) {
    Autocompleter.Base = function () {
      return new Object();
    }
}

widget.autocompleter = Class.create();
widget.autocompleter.prototype = Object.extend(new Autocompleter.Base(), {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&  
          ret.length < instance.options.choices ; i++) { 

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ? 
            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) { 
              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
                elem.substr(entry.length) + "</li>");
              break;
            } else if (entry.length >= instance.options.partialChars && 
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>");
                break;
              }
            }

            foundPos = instance.options.ignoreCase ? 
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || {});
  }
});

// -----------------------------------------------------------------------------
// in place editor
// -----------------------------------------------------------------------------

// AJAX in-place editor
//
// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor

// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
}

widget.in_place_editor = Class.create();
// widget.in_place_editor = Object();
widget.in_place_editor.defaultHighlightColor = "#ffff99";

widget.in_place_editor.prototype = {

  initialize: function(element, url, options) {

    this.url = url;
    this.element = $P.get(element);
    this.uuid = this.element.parentNode.parentNode.className;
    this.options = Object.extend({
      okText: "ok",
      cancelText: "cancel",
      savingText: "Saving...",
      clickToEditText: "Click here to edit",
      okText: "ok",
      rows: 1,
      onComplete: function(transport, element) {
        new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
      },
      onFailure: function(transport) {
        alert("Error communicating with the server: " + transport.responseText.stripTags());
      },
      callback: function(form) {
        return Form.serialize(form);
      },
      handleLineBreaks: true,
      loadingText: 'Loading...',
      savingClassName: 'inplaceeditor-saving',
      loadingClassName: 'inplaceeditor-loading',
      formClassName: 'inplaceeditor-form',
      highlightcolor: widget.in_place_editor.defaultHighlightColor,
      highlightendcolor: "#FFFFFF",
      externalControl:	null,
      ajaxOptions: {}
    }, options || {});

    if(!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + "-inplaceeditor";
      if ($P.get(this.options.formId)) {
        // there's already a form with that name, don't specify an id
        this.options.formId = null;
      }
    }
    
    if (this.options.externalControl) {
      this.options.externalControl = $P.get(this.options.externalControl);
    }
    
    this.originalBackground = Element.getStyle(this.element, 'background-color');

    if (!this.originalBackground) {
      this.originalBackground = "transparent";
    }
    
    this.element.title = this.options.clickToEditText;

    this.enterListener = this.enterEditMode.bindAsEventListener(this);
    this.exitListener = this.onclickCancel.bindAsEventListener(this);


    this.onclickListener = this.enterEditMode.bindAsEventListener(this);
    this.mouseoverListener = this.enterHover.bindAsEventListener(this);
    this.mouseoutListener = this.leaveHover.bindAsEventListener(this);

    Event.observe(this.element, 'click', this.onclickListener);
    Event.observe(this.element, 'mouseover', this.mouseoverListener);
    Event.observe(this.element, 'mouseout', this.mouseoutListener);

    if (this.options.externalControl) {
      Event.observe(this.options.externalControl, 'click', this.enterListener);
      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
    }
  },

  enterEditMode: function(evt) {
    if (!globals.proto_edit_mode) return;
    if (this.saving) return;
    if (this.editing) return;
    var nodes =  this.element.parentNode.childNodes
    for(x=0; x<nodes.length; x++){
        if (nodes[x].tagName == 'IFRAME')
	   return;
    }

    this.editing = true;
    this.onEnterEditMode();
    //if (this.options.externalControl) {
    //  Element.hide(this.options.externalControl);
    //}
    Element.hide(this.element);
    if (this.options.ui_type == 'file'){
	this.createFileUpload();	   
    }
    else{
       this.createForm();
       // this.element.parentNode.insertBefore(this.form, this.element);
    }	
    Field.scrollFreeActivate(this.editField);
    // stop the event to avoid a page refresh in Safari
    if (evt) {
      Event.stop(evt);
    }
    return false;
  },

  createForm: function() {
    this.form = document.createElement("form");
    this.form.id = this.options.formId;
    Element.addClassName(this.form, this.options.formClassName)
    this.form.onsubmit = this.onSubmit.bind(this);

    this.createEditField();

    if (this.options.textarea) {
      var br = document.createElement("br");
      this.form.appendChild(br);
    }

    okButton = document.createElement("input");
    okButton.type = "submit";
    okButton.value = this.options.okText;
    this.form.appendChild(okButton);

    cancelLink = document.createElement("a");
    cancelLink.href = "#";
    cancelLink.appendChild(document.createTextNode(this.options.cancelText));
    cancelLink.className = "cancel";
    cancelLink.onclick = this.onclickCancel.bind(this);
    this.form.appendChild(cancelLink);
  },


  createFileUpload: function () {
    this.iframe = document.createElement('iframe');
    this.iframe.id = "upload_iframe";
    this.iframe.className = "upload";
    this.iframe.name = "upload"; 
    this.iframe.border = '0px';

    this.iframe.src = '/!system.upload/' + this.uuid + '/'+ this.options.property +'/'+ this.iframe.id;
    this.element.parentNode.insertBefore(this.iframe, this.element);
   
    Element.show(this.element);
    this.editing = false;
    this.onLeaveEditMode();
  },

 
  
  hasHTMLLineBreaks: function(string) {
    if (!this.options.handleLineBreaks) return false;
    return string.match(/<br/i) || string.match(/<p>/i);
  },

  convertHTMLLineBreaks: function(string) {
    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
  },

  createEditField: function() {
    if (this.options.value){
        if (typeof this.options.value == "string")	
            var value =  xmllib.unescape(this.options.value);          
    }   
    this.element.parentNode.insertBefore(this.form, this.element);
	
    if (this.options.get_values)
	var value = this.options.get_values;

    this.editField = widget.create(this.form, this.options.ui_type, this.options.label, this.options.widget_id, this.options.ui_type, value);      	
    
    if (this.options.loadTextURL)
      this.loadExternalText();

    return
  }, 

  getText: function() {
    // alert('getText');
    return this.element.innerHTML;
  },

  loadExternalText: function() {
    Element.addClassName(this.form, this.options.loadingClassName);
    this.editField.disabled = true;
    new Ajax.Request(
      this.options.loadTextURL,
      Object.extend({
        asynchronous: true,
        onComplete: this.onLoadedExternalText.bind(this)
      }, {method:'get'})
    );
  },
  onLoadedExternalText: function(transport) {
    Element.removeClassName(this.form, this.options.loadingClassName);
    this.editField.disabled = false;
    if (this.options.ui_type == 'text_wysiwyg'){
        this.editField.value = transport.responseText;
	var value_id = this.options.widget_id + '_value';
  
        this.tinyMCE = tinyMCE.addMCEControl(this.editField, this.widget_id);     
    }
    else
        this.editField.value = transport.responseText.stripTags();     	
  },
  onclickCancel: function() {
    this.onComplete();
    this.leaveEditMode();
    return false;
  },
  onFailure: function(transport) {
    this.options.onFailure(transport);
    if (this.oldInnerHTML) {
      this.element.innerHTML = this.oldInnerHTML;
      this.oldInnerHTML = null;
    }
    return false;
  },
  onSubmit: function() {
    // onLoading resets these so we need to save them away for the Ajax call
    var form = this.form;
    var method = "get";
    if (this.options.ui_type == 'text_large'|| this.options.ui_type == 'text_wysiwyg') {
        method = "post";
    }

    if (this.options.ui_type == 'multi_checkbox'){
        var checkBoxes = this.editField.childNodes;
	value = new Array();
	for (var i = 0; i < checkBoxes.length; i++) {
  	    var child = checkBoxes[i];
            if (child.tagName == 'INPUT')
                value[value.length] = child.value + ' : ' + child.checked;
	}	
	value.join(',');
    }

    if (this.options.get_value_to_save_from_widget) {
        var value = this.options.get_value_to_save_from_widget();
    }

    if (this.options.ui_type == 'text_wysiwyg') {
        this.tinyMCE.execCommand('mceCleanup', false, null);
        var value =  this.tinyMCE.getDoc().body.innerHTML;       
    } else {
        var value = this.editField.value;
    }
    
     this.onLoading();
     new Ajax.Updater(
           { 
           success: this.element,
            // don't update on failure (this could be an option)
           failure: null
           },
           this.url,
           Object.extend({
        	parameters: this.options.callback(form, value),
       	 	onComplete: this.onComplete.bind(this),
        	onFailure: this.onFailure.bind(this),
        	method: method
      	   })
    	 );
    this.options.value = value;
    

    // stop the event to avoid a page refresh in Safari
    if (arguments.length > 1) {
      Event.stop(arguments[0]);
    }
    return false;
  },
  onLoading: function() {
    this.saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  showSaving: function() {
    this.oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    Element.addClassName(this.element, this.options.savingClassName);
    this.element.style.backgroundColor = this.originalBackground;
    Element.show(this.element);
  },
  removeForm: function() {
    if(this.form) {
      if (this.form.parentNode) Element.remove(this.form);
      this.form = null;
    }
  },
  enterHover: function() {
    if (!globals.proto_edit_mode) return;
    if (this.saving) return;
    this.element.style.backgroundColor = this.options.highlightcolor;
    if (this.effect) {
      this.effect.cancel();
    }
    Element.addClassName(this.element, this.options.hoverClassName)
  },
  leaveHover: function() {
    if (!globals.proto_edit_mode) return;
    if (this.options.backgroundColor) {
      this.element.style.backgroundColor = this.oldBackground;
    }
    Element.removeClassName(this.element, this.options.hoverClassName)
    if (this.saving) return;
    this.effect = new Effect.Highlight(this.element, {
      startcolor: this.options.highlightcolor,
      endcolor: this.options.highlightendcolor,
      restorecolor: this.originalBackground
    });
  },
  leaveEditMode: function() {
    Element.removeClassName(this.element, this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this.originalBackground;
    Element.show(this.element);
    //if (this.options.externalControl) {
    //  Element.show(this.options.externalControl);
    //}
    this.editing = false;
    this.saving = false;
    this.oldInnerHTML = null;
    this.onLeaveEditMode();
  },
  onComplete: function(transport) {
    this.leaveEditMode();
    this.options.onComplete.bind(this)(transport, this.element);
  },
  onEnterEditMode: function() {
     if (this.options.externalControl){
         Event.stopObserving(this.options.externalControl, 'click', this.enterListener);
         //Event.observe(this.options.externalControl, 'click', this.exitListener);
     }
  },
  onLeaveEditMode: function() {
     if (this.options.externalControl){
         //Event.stopObserving(this.options.externalControl, 'click', this.exitListener);
         Event.observe(this.options.externalControl, 'click', this.enterListener);
     }
  },
  dispose: function() {
    if (this.oldInnerHTML) {
      this.element.innerHTML = this.oldInnerHTML;
    }
    this.leaveEditMode();
    Event.stopObserving(this.element, 'click', this.onclickListener);
    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
    if (this.options.externalControl) {
      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
    }
  }
};

// -----------------------------------------------------------------------------
//  User Defined Functions should go here
// -----------------------------------------------------------------------------

function handle_metatype_creation (create_form) {
  window.location = $P.get(create_form).action + '?' + Form.serialize(create_form);
  return;
}

function eval_response(req) {

    object = $P.get('object_properties');
    alert(object);
    alert(req.responseText);
    eval(req.responseText);
    object.appendChild(document.createTextNode(req.responseText));

}

function handle_response_failure(request) {
    io.notify('SOME ERRROR: ' + request.statusText);
}

var proto = {};

proto.cancel_button = function () {
    var cancel_link = document.createElement('a');
    cancel_link.href = '';
    cancel_link.innerHTML = 'Cancel';
    //event.add_event(cancel_link, 'click', proto.cancel_popup);   
    return cancel_link;  
}

proto.handle_delete_object = function (event) {
    io.fade_out_background(null, null, null, '250px 300px 0px 300px');
    var inlay = $P.get(globals.transparent_inlay_class);
    var confirm = $P.get('delete-object');
    var cancel = proto.cancel_button(); 
    inlay.appendChild(confirm);
    confirm.appendChild(cancel); 	
    confirm.style.display = 'block';
}

proto.handle_create_metatype = function (event) {

    io.fade_out_background(null, null, null, '250px 300px 0px 300px');

    var inlay = $P.get(globals.transparent_inlay_class);
    var selektor = $P.get('metatype-selection');
    var cancel = proto.cancel_button();
    inlay.appendChild(selektor);
    selektor.style.display = 'block';
    selektor.appendChild(cancel);
}

proto.handle_submit_search = function (event) {
    var keywords = $F('keywords');
    var metatype = $F('metatype');
    var mode = $F('mode')
    new Ajax.Updater('search_results', '/!basic_search/' + keywords + '/' + metatype + '/' + mode, {evalScripts:true});
}

event_is_cr = function(evt) {
    if (evt.keyCode == 13)
        submit_search(evt);        
}



add_close = function (req) {
    var close = $P.get('close_div');
    jsutil.event.add_event(close, 'click', close_div); 
}

close_div = function(event) {
    search = $P.get('search_result');
    Element.remove(search);
}



//proto.minimise_footer = function(event) {  
  //  $P.get('footer_closed').style.display = 'block';
  //  if (browser.get_cookie('footer_status') == 'open'){
  //      Effect.DropOut('footer_open');  		
        //Effect.Grow('footer_closed');     
  //  } 
  //  else {
  //      $P.get('footer_open').style.display = 'none'; 
  //  }
  //  browser.set_cookie('footer_status', 'closed', '/');
  //  var tab_openthis = $P.get('tab_openthis'); 
  //  jsutil.event.add_event(tab_openthis, 'click', proto.maximise_footer);
//}

//proto.maximise_footer = function(event) { 
  //  $P.get('footer_open').style.display = 'block';
   // if (browser.get_cookie('footer_status') == 'closed'){
     //  Effect.Fade('footer_closed');
       //Effect.Appear('footer_open');  
    //}
    //else {
      // $P.get('footer_closed').style.display = 'none';  
    //}
    //browser.set_cookie('footer_status', 'open', '/');
    //var tab_closethis = $P.get('tab_closethis');
    //jsutil.event.add_event(tab_closethis, 'click', proto.minimise_footer); 
//}


items_search = function (event) {
    proto.toggle_selected_type("Item", event);
    submit_search(event);
}

proto.toggle_selected_type = function (link_id, event) {
    var tabs = $P.get('tabs');
    active = document.getElementsByClassName('tab_active_green')[0];
    var left_corner = $P.get('left_corner');
    var right_corner = $P.get('right_corner');
  
    var replace_active = document.createElement('div');
    replace_active.id = active.id;
    replace_active.className = 'tab_green';
    replace_active.innerHTML = active.innerHTML;
    tabs.insertBefore(replace_active, active);    
    Element.remove(left_corner);
    Element.remove(active);
    Element.remove(right_corner);

    var new_active = document.createElement('div');
    new_active.className = "tab_active_green";
     
    link = $P.get(link_id);
    new_active.id = link_id;
    new_active.innerHTML = link.innerHTML;
   
    tabs.insertBefore(left_corner, link);
    tabs.insertBefore(new_active, link);	
    tabs.insertBefore(right_corner, link.nextSibling);
    Element.remove(link);
   
    globals.search.url = 'autocompletion/' + new_active.id;
 
    jsutil.event.add_event(replace_active, 'click', functional.partial(proto.toggle_selected_type, replace_active.id));

    submit_search(0);             
}

proto.add_field_name = function (type, event) {

    io.fade_out_background(null, null, null, '400px 0px 0px 300px');
    var inlay = $P.get(globals.transparent_inlay_class);

    var real_popup = document.createElement('div');
    real_popup.id = 'real_popup';
    real_popup.appendChild(document.createTextNode("this is the popup"));
		  
    if ($P.get('real_popup')) {
        Element.remove('real_popup');
    }

    inlay.appendChild(real_popup);
    
    var form = document.createElement('form');

    var prompt = document.createElement('input');
    prompt.id = 'field_name';
    prompt.type = 'text';
    prompt.name = 'field_name';
    prompt.defaultValue = 'put text here';
    form.appendChild(prompt);
    real_popup.appendChild(form);

    var cancel = document.createElement('a');
    cancel.id = 'cancel';
    cancel.appendChild(document.createTextNode("cancel"));
    jsutil.event.add_event(
        cancel, 'click', io.clear_fade_out
    );

    real_popup.appendChild(cancel);
		  
    var add = document.createElement('a');
    add.id = 'ok';
    add.appendChild(document.createTextNode("add"));
		  
    jsutil.event.add_event(
        add, 'click', functional.partial(add_field, type)
    );
    real_popup.appendChild(add);
		 		  		 
    event.stopPropagation();
    return false;
 
}

function add_field(type, event) {

    var field_name = $F('field_name');
    var object_uuid = $P.get('object_properties').getAttribute('name');

    new Ajax.Request(
        '/system/add_field?field_name='+field_name + '&type=' + 
        type + '&object_uuid=' + object_uuid, 
        { method : 'get',
		  onComplete : eval_response,
		  onFailure : handle_response_failure
        });
		  
    io.clear_fade_out();
		  
    event.stopPropagation();
    return false;

}

function create_search_notification(text) {

    if (!text) {
        var text = "Searching";
    }

    var container = document.createElement('div');
    container.id = 'search-notification';

    var image = document.createElement('img');
    image.src = "/static/images/spinner.gif";
    image.alt = text;

    container.appendChild(image);
    container.appendChild(document.createTextNode(text));

    return container
}

function popup(sender_id, object_type) {
	  
    var pop = document.createElement('div');
    pop.id = "popup";

    var obtype = document.createElement('img');
    obtype.src = '/static/images/types/' + object_type + '.png';
  
    var submit = document.createElement('a');

    submit.id = 'add_' + object_type;
    // submit.href = "";

    submit.appendChild(
        document.createTextNode('Add ' + object_type + ' Field!')
        );

    submit.className = 'float-right';

    // what's going on here ?
		  
    var field_name = 'test';
		  
    jsutil.event.add_event(
        submit, 'click', functional.partial(proto.add_field_name, object_type)
        );

    pop.appendChild(obtype);
    pop.appendChild(submit);

    if ($P.get('popup')) {
        Element.remove('popup');
    }

    $P.get(sender_id).appendChild(pop);

}



var lists = Class.create();

lists.prototype =  {
    
    listRefresh: function(){
     
        var uuid = this.uuid;
        var property = this.property;
         
        new Ajax.Request("/!change_list_order", {method:'get', parameters:'object='+uuid+'&property='+ property +'&order=' + this.serialize()});
    

        Sortable.create(this.sortable, {onUpdate: this.changeOrder.bind(this), handle:this.handle, tag:this.type, ghosting:false});
   
        
        if (this.accepts != null){  
	    Droppables.add(this.droppable, {accept:this.accepts, onDrop: this.addElement.bind(this)});
        }

        var rows = this.getList();
        
        for(i=0; i<rows.length; i++){
             this.delete_a_row = this.delete_row.bindAsEventListener(this);
             var table_cell = $P.get("delete_" + rows[i] +"_from");
             Event.observe(table_cell, 'click', this.delete_a_row); 
        } 
},
 
   
   changeOrder: function(evt){
       new Ajax.Request("/!change_list_order", {method:'get', parameters:'object='+this.uuid+'&property='+ this.property +'&order=' +  this.serialize()})

},

    
// bind this onclick="lists.delete_row(event, 'item_${uuid2string(val)}')"
    

    initialize: function(sortable, type, handle, accepts){
        this.object = $P.get('object-properties');
        this.uuid = this.object.className;
        this.sortable = sortable;
        this.type = type;
        this.handle = handle;
        this.accepts = accepts;    
        this.sortable_ele = $P.get(sortable);
        this.property = this.sortable_ele.parentNode.parentNode.id;  
    
        if (type == "tr"){
            this.sortable_ele = this.sortable_ele.getElementsByTagName('tbody')[0];
	    this.sortable_ele.id = this.sortable_ele.parentNode.id + "_tbody";
            this.droppable = sortable;
            this.sortable = this.sortable_ele.id;
        }   
        this.listRefresh(); 
         
	
},

    serialize: function(){
        list = this.getList();    
        list.join(',');
        return list
},


    addElement: function(element, event){
        this.element = element;
        this.event = event;
        
        if(this.isInList(element.id) == true) 
            alert("this object is already in the list " + sortable_ele.id); 
        else{ 
            new Ajax.Request("/!render_row/"+ element.id +"/"+ this.accepts, 
                             {onComplete: this.addRow.bind(this)});
            }
},

    addRow: function(transport, event){
        
        var default_element = $P.get('default_' + this.event.parentNode.parentNode.id);
    
        if(this.type == 'tr'){
            new Insertion.Before(default_element, transport.responseText)
            this.listRefresh();            
        }  
        else{  
            new Insertion.Before(default_element, transport.responseText);
            this.listRefresh();
        }
},

    isInList: function(element){
    
        var elements = this.getList();
    
        for(i=0; i<elements.length; i++){
            if(elements[i] == element)
            return true
        }
        return false
},

    getList: function() {
 	sortable_ele = $P.get(this.sortable);
	var list = new Array();
        children = sortable_ele.childNodes;
      
	for (var i=0; i<children.length; i++){
            var item = children.item(i).id;
            if(item != undefined){
                if (item.split('_')[1] != null){
                    item = item.split('_')[1];
                    if (item.length == 36)
                        list[list.length] = item;            
	        }
            }
        }
        return list
},

   delete_row: function(event){
   
        var ele = Event.element(event);
       
        if (ele.parentNode.id.split('_')[0] =="delete"){
	    ele = ele.parentNode;
        }
        Element.remove(ele.parentNode);
        //remember to remove the droppable
        
        this.listRefresh();
}
};

proto.show = function(event, element){
        hidden_input = $P.get(element);
        hidden_input.style.display = "inline";

}

proto.add_type = function(event, input_element){
        var uuid = proto.get_current_uuid(event);
	new Ajax.Updater("object-properties", "/!add_metatype/" + uuid + "/" + input_element, {evalScripts:true});
}

proto.get_current_uuid = function(event){
	var obj_element = $P.get('object-properties');
	return obj_element.className;
}


// -----------------------------------------------------------------------------
// jsutil.init
// -----------------------------------------------------------------------------

/*

  As much as it shames me to admit, but this particular quirk has bit me twice
  now -- basically, your DOM Elements aren't available until the whole document
  has loaded ;p

  So, ehm, wait for the document to load before checking for elements ;p

 */

jsutil.init = function () {

  io.focus_on_element('search-box');

  //globals.search = new Ajax.Autocompleter('search_input', 'search_list', 'autocompletion/search', {afterUpdateElement:submit_search, frequency:0.02, minChars:1});
    
 
  var create_metatype_link = $P.get('create-metatype-link');
  
  var delete_object_link = $P.get('delete-object-link'); 
 
  var submit_search = $P.get('submit_search')

  var create_item = $P.get('create_item');
  if (create_item)
     jsutil.event.add_event(create_item, 'click', proto.show);
  
 
  //if (footer_close){
  //      jsutil.event.add_event(tab_openthis, 'click', functional.partial(maximise_footer, 'footer_closed'));
  //}
  

  if (create_metatype_link)
    jsutil.event.add_event(create_metatype_link, 'click', proto.handle_create_metatype);
  if (delete_object_link)	
    jsutil.event.add_event(delete_object_link, 'click', proto.handle_delete_object);

  if (submit_search)	
    jsutil.event.add_event(submit_search, 'click', proto.handle_submit_search);
	
  return

  debug.write('hello');

  var x = "foo";
  debug.dump_object(x);

  var y = new Array();
  debug.dump_object(y);

  // alert(networking.supports_networking);

  // alert(browser.is_mozilla());
  // alert(browser.is_internet_explorer());

  // io.setup_key_sniffer();

  // alert(sample_add(12, 12));
  // alert(sample_add(1, {'b':5}));
  // alert(sample_add(6));

}

// -----------------------------------------------------------------------------
// 
// -----------------------------------------------------------------------------

if (sys.supports_basic_dom()) {

    // event.add_event(globals.debug, "change", debug.toggle_debugger);
    jsutil.event.add_event(window, 'load', jsutil.init);
    // event.add_event(window, 'load', io.setup_dynamic_labels);
    jsutil.event.add_event(window, 'unload', jsutil.event_cache.flush);
    // event.add_event(window, "load", io.setup_transparent_overlay);
    // event.add_event(window, "load", debug.setup_eval_output);

}

// -----------------------------------------------------------------------------
// un-trap ourselves from framesets
// -----------------------------------------------------------------------------

// if (window.top != window)
//     window.top.location = window.location;

// -----------------------------------------------------------------------------
// kredits
// -----------------------------------------------------------------------------

// individual funktions are attributed -- otherwise tav or tom

// -----------------------------------------------------------------------------
// some basik javaskript info
// -----------------------------------------------------------------------------

// DOM DOM DOM DOM!

// createElement  # nodeType 1 nodeValue null      nodeName "strong"
// createTextNode # nodeType 3 nodeValue "da text" nodeName "#text"

// getElementsByTagName -- can be used on elements to get sub-elements
// getElementById
// getElementsByTagNameNS -- not in MSIE ;p

// getAttribute
// setAttribute # both name *and* value must be str # same as elem.attr = value
// hasAttribute # not in IE5

// element.style.
// element.className

// appendChild
// removeChild
// replaceChild(new, old)
// insertBefore(new, existing)

// old-skool

// document.all | images | forms | links  -- access via "name" not "id"
// document.writeln

// array.length
// array.item(n) --> returns null instead of "IndexError"

// item -- special global object

// autosave -- configurable period

// -----------------------------------------------------------------------------
// deprecated old shit
// -----------------------------------------------------------------------------

function showTocToggler(named_link,display_text,show_string,hide_string,init_status,toggle_div_id) {
    if(document.getElementById) {
        if (init_status == 'show') {
          show_style = 'style="display:none;"'
          hide_style = ''
        } else {
          show_style = ''
          hide_style = 'style="display:none;"'
        }
        document.writeln('<a name="' + named_link + '"></a>' + display_text + ' ' + '<span class="toctoggle">' +
                         '[<a href=\'javascript:toggleDiv("' + toggle_div_id + '","' + named_link + '-showlink","' + named_link + '-hidelink"' +
                         ')\' class="internal" accesskey="t">' +
                         '<span id="' + named_link + '-showlink"' + show_style + '>' + show_string + '</span>' +
                         '<span id="' + named_link + '-hidelink"' + hide_style + '>' + hide_string + '</span>' + 
                         '</a>]</span>');
    }
}

function toggleDiv(div_id, show_id, hide_id) {
    var toc = document.getElementById(div_id);
    var showlink = document.getElementById(show_id);
    var hidelink = document.getElementById(hide_id);

    if (toc.style.display == 'none') {
        show_element(toc);
        hide_element(showlink);
        show_element(hidelink);
    } else {
        hide_element(toc);
        hide_element(hidelink);
        show_element(showlink);
    }
}

function addTocToggler() {

  var toc = document.getElementById('document-toc');

  toc.appendChild(document.createTextNode(' ['));

  var show = document.createElement('a'); 
  var hide = document.createElement('a'); 

  show.id = 'toc-listing-show';
  hide.id = 'toc-listing-hide';

  var href = "javascript:toggleDiv('document-toc-listing', 'toc-listing-show', 'toc-listing-hide')";
   
  show.setAttribute('href', href);
  hide.setAttribute('href', href);

  show.appendChild(document.createTextNode('show')); 
  hide.appendChild(document.createTextNode('hide')); 

  toc.appendChild(show);
  toc.appendChild(hide);

  toc.appendChild(document.createTextNode(']'));

  toggleDiv('document-toc-listing', 'toc-listing-show', 'toc-listing-hide');
  toggleDiv('document-toc-listing', 'toc-listing-show', 'toc-listing-hide');

}

// for (var key in object) {...}

// prompt / confirm

// The !! prefix operator is the smallest way of converting a value to a boolean

// There should be a way of adding members to objects, particularly prototype
// objects, without them being enumerated by the for...in statement.

// dojo # tibet # jsuix # jsolait # jsan # prototype # myghty # mochito

// string.trim()

// Standardizing some of these might of course be a nice thing, but more
// important would be to make that methods can also be added to the Object and
// Array prototype without breaking for/in loops. Like through implementing:

// dontenum Array.prototype.contains

// setInterval(function (t) { animate.call(t); }, 50, this);

// Notice that the first arg is a function reference, not a string. This may not
// be well-known, but in combination with Function.prototype.call (or .apply)
// and extra trailing args to setInterval (or setTimeout), it does the job.

//

// I've mailed Hixie about WHATWG specifying setTimeout and setInterval fully.
// When the first argument is a function object, the trailing arguments after
// the delay (wish I'd got the arg order right, but the function form came after
// the string) must be trailing arguments to the function.

// We already have Object.prototype.propertyIsEnumerable (although that does not
// check for a prototype property, so it gives a different answer from what you
// would get from a for..in loop -- why? I wasn't in ECMA TG1 when this was
// added; if someone who was knows the rationale, please comment). So we could
// have another method to make a property non-enumerable.




// By the way, a small trick to get Ian's import to work (although it only works
// on platforms on which both XmlHTTPRequest and eval.apply/eval.call are
// properly implemented, which excludes IE) is to load the data into a string,
// create an empty object (which will serve as namespace), execute
// eval.call(object, jsstring) and return the object...


// http://www.dotvoid.com/view.php?id=43

// -----------------------------------------------------------------------------

// arguments and certain other variables are read-only. replace() doesn't work
// on these; instead, you need to assign them to normal variables first.

// -----------------------------------------------------------------------------

/*

The confirm dialog box returns a Boolean value, true, if OK is pressed and false
if Cancel is pressed. You can use this in an if statement to perform some action
if you want to. I think the fact that the buttons are labeled 'OK' and 'Cancel'
also makes this awkward to use.

The prompt box returns the value of the text in the field if the OK button is
clicked. It returns null if the cancel button is clicked.

*/
