String.prototype.lTrim = function() { return (this.replace(/\s*((\S+\s*)*)/, "$1")); }
String.prototype.rtrim = function() { return this.replace(/((\s*\S+)*)\s*/, "$1"); }
String.prototype.trim = function() { return this.replace(/\s*(\S+)s*/, "$1"); }
String.prototype.removeWhiteSpace = function() { return this.replace(" ","$1"); }
String.prototype.count = function(chr) { 
    var count = 0;
    for ( var i = 0; i < this.length; i++ ) {
        if (this.charAt(i) == chr) count++;
    }

    return count;
}
var email_share = null;
var email_subscribe = null;

var actionHash = null;

function listen(element, name, observer){
    if(element.addEventListener){
        element.addEventListener(name, observer, false);
    }else if(element.attachEvent){
        element.attachEvent('on' + name, observer);
    }
}
/*
 * This function removes the element by it's id
 */
function rElement(element_id) {

    var element = document.getElementById(element_id);
    element.parentNode.removeChild(element);

}
/*
 * Templating system which snags the HTML code from the page upon window.load events (specified by developer) and returns a copy within memory removing the 
 * copy in HTML.
 */
var Template = Class.create();
Template.prototype = {
    /* Obscenely simple templating engine. */

    initialize: function(element_id){
        try{
            this._data = $(element_id).innerHTML;
        }catch(e){
            //alert(element_id + " not found!");
        }

        this._data = this._data.replace(/&lt;/g, "<");
        this._data = this._data.replace(/&gt;/g, ">");
        this._data = this._data.replace(/%7B/g, "{");
        this._data = this._data.replace(/%7D/g, "}");
    },

    render: function(self, vars){
        var rdiv = document.createElement('div');
        var r = this._data;

        var bevals = r.match(/<\([\s\S]*?\)>/g);
        if(bevals){
            for(var _idx=0; _idx<bevals.length;_idx++){
                var e = bevals[_idx];
                eval(e.substring(2, e.length-2));
                r = r.replace(/<\([\s\S]*?\)>/, "");
            }
        }

        var evals = r.match(/#{[\s\S]*?}/g);
        if(evals){
            for(_idx=0; _idx<evals.length; _idx++){
                var e = evals[_idx];
                e = eval(decodeURI(e.substring(2, e.length-1)));
                r = r.replace(/#{[\s\S]*?}/, decodeURI(e));
            }
        }

        rdiv.innerHTML = r;
        return rdiv;
    }
}
// snag the templates upon window load
listen(window,"load", function () {
    email_share = (new Template("container-mail-share"));
    rElement("container-mail-share");


    email_subscribe = (new Template("container-mail-subscribe"));
    rElement("container-mail-subscribe");
});

function pop_email_share(e) {
    var ret = null;
    actionHash = e;
    if (email_share != null) {

        ret = email_share.render(this, e.root.options)
        ret.setAttribute("class", "container-mail-form");
        ret.setAttribute("id", "container-mail-share");
	ret.rootElement = e.root;

        var aCancel = document.createElement("a");
	aCancel.setAttribute("style", "float: left; display: block; background-image: none; padding-top: 2px; padding-left: 0px;  height: 15px;");
        //aCancel.setAttribute("href", "#");
        aCancel.name = "mail_share";
        aCancel.appendChild(document.createTextNode("Cancel"));
        aCancel.rootElement = e.root;
        r = ret.getChildById("container-mail-share-submit");
	r.appendChild(aCancel);

        listen(aCancel, "click", do_something);                                                                                // preform a 'default' action
    }

    return ret;
}


/*
 * This function will show a popup window requesting email information for shairing and subscriptions of titles. 
 *  ...I need to find a better way of GENERATING this gd script...
 */
function pop_email_subscribe(e) {
    var ret = null;
    actionHash = e;

    if (email_subscribe != null) {

        ret = email_subscribe.render(this, e.root.options)
        ret.setAttribute("class", "container-mail-form");
        ret.setAttribute("id", "container-mail-subscribe");
	ret.rootElement = e.root;

        var aCancel = document.createElement("a");
	aCancel.setAttribute("style", "float: left; display: block; background-image: none; padding-top: 2px; padding-left: 0px;  height: 15px;");
        //aCancel.setAttribute("href", "#");
        aCancel.name = "mail_subscribe";
        aCancel.appendChild(document.createTextNode("Cancel"));
        aCancel.rootElement = e.root;
        ret.getChildById("container-mail-subscribe-submit").appendChild(aCancel);

        listen(aCancel, "click", do_something);                                                                                // preform a 'default' action
    }

    return ret;
}

function cancel_email() {

}




/*

    I hacked the last bit together...i should goback and rework the whole execution deal...ugg...

    do_something only supports one level of menus by differenciating tags DIV and LI

    these tabs do not yet support active upon initalization

    implement ["css"] hash entry within items during css generation

    FIXED - At the moment, unfortunetly, this script is limited to a maximum of 9 topic titles.  i.e. Share, Subscribe, etc, not items under each topic.
    - migrated to elements

*/

/* default url if none is specified */
var DEFAULT_URL = location.href;

/* default_title if none is specified */
var DEFAULT_TITLE = document.title; 

/* default target if none is specified */
//var DEFAULT_TARGET = "_blank";
var DEFAULT_TARGET = false;

ShareStack = [];

/* The tabs.  css: defines the default root css class to be applied to the topmost or even itimized entries.  As menues are navigated this script automatically applies *-closed and *-opened
class styles.
*/
var tabs = [
    { title: "Bookmark", active: false, enabled: true, css: "class: container-tabs;",
        items: [
            { title: "Email", action: pop_email_share, instances: 1, global_instances: 1, target: "self", enabled: true,
                css: "class: container-c-mail"},
            { title: "Delicious", action: "http://del.icio.us/post?url={url}&title={title}", target: DEFAULT_TARGET, enabled: true,
                css: "class: container-c-delicious"},
            { title: "myYahoo", action: "http://myweb2.search.yahoo.com/myresults/bookmarklet?u={url}&t={title}", target: DEFAULT_TARGET, enabled: true,
                css: "class: container-c-myyahoo"},
            { title: "Google", action: "http://www.google.com/bookmarks/mark?op=edit&output=popup&bkmk={url}&title={title}", target: DEFAULT_TARGET, enabled: true,
                css: "class: container-c-google"},
            { title: "Magnolia", action: "http://ma.gnolia.com/bookmarklet/add?url={url}&title={title}", target: false, enabled: true,
                css: "class: container-c-magnolia"}
        ]
    },
    { title: "Subscribe", active: false, enabled: true, css: "class: container-tabs;",
        items: [
            { title: "Mailing List", action: pop_email_subscribe, instances: 1, global_instances: 1, target: "self", enabled: true, css: "class: container-c-mail;" },
            { title: "Blog RSS", action: "http://www.ideum.com/blog/feed/", target: DEFAULT_TARGET, enabled: true, css: "class: container-c-blogrss;" },
            { title: "Flickr RSS", action: false, target: DEFAULT_TARGET, enabled: false, css: "class: container-c-flickrrss;" },
            { title: "iTunes", action: false, target: DEFAULT_TARGET, enabled: false, css: "class: container-c-itunes;" }
        ]
    }
]

listen(window,"load", function () {
    if(navigator.appVersion.toLowerCase().indexOf('msie') == -1){
        for(var i = 0; i < ShareStack.length; i++) {
            document.getElementById("container-" + i).appendChild(ShareStack[i].render());
        }
    }

});

Object.prototype.update = function(n){
    for(k in n){
        if(this[k])
            this["_" + k] = this[k];
        this[k] = n[k];
    }
}

var count = 0;
var ShareWidget = Class.create();
ShareWidget.prototype.update({
    initialize: function(options){
        //console.log(options);
	this.url = (options.page_location != null && options.page_location != "") ? options.page_location : DEFAULT_URL ;
        this.title = (options.page_title != null && options.page_title != "") ? options.page_title : DEFAULT_TITLE ;
        this.options = options;
        this.id = count;                                                                                                // sorta pointless ATM...
        // an array of functions that have been executed as a means of better tracking their elements...this is an array which holds arrays
        this.executed = new Array();

        var container = document.createElement("div");
        this.container = container;
    },

    // called upon initalization from the render function outside this object which in turn is called from html
    render: function(){
        var ret = "";

        // create each tab if it is enabled
        for(var i = 0; i < tabs.length; i++) {
            if(tabs[i].enabled == true) {
                var el = Element.extend(document.createElement("div"));
                this.container.appendChild(el);                                                                    // append element to the <span> container
                el.setAttribute("class", "container-tabs inactive");
                el.appendChild(document.createTextNode(tabs[i].title));
                el.tabIndex = i;                                                                                   // used to determine later on which item this corresponds
                //el.parentElement = this;                                                                           // establish the parent as this element
                el.rootElement = this;                                                                             // for redundancy with the <li> and <ul> elements
                applyCSS(el, tabs[i].css, "closed");
                //element.applyCSS(tabs[i].css, "closed");
                listen(el, "click", do_something);                                                                 // establish an event listener
            }
        }

        // create a div clear between the tab options and the content div
        var element = document.createElement("div");
        element.setAttribute("class", "clear");
        this.container.appendChild(element);

        // create the content div
        var element = document.createElement("div");
        element.setAttribute("class","container-c");
        this.container.appendChild(element);
        this.content = element;

        this.activeTab = null;

        return this.container;
    },


    /** these functions below enable single instance executions per function globaly... **/


    /*
     * checks if function f has already been executed
     *      returns: true or false dependant upon the limit of instances the function may be concurrently open
     */
    canExecute: function(f) {

        //if (this.executed[f]) return true;
        var count = 0; var instancesMax = 0;
        for (var i = 0; i < this.executed.length; i++) {
            if ( f == this.executed[i]["fname"] ) {
                instancesMax = this.executed[i]["instances"];
                count++;
            }
        }

        if ( count < instancesMax || count == 0) return true;

        return false;
    },

    /*
     * checks if a function has been executed
     *      returns: the index i within this.executed[i] at the FIRST located function
     */
    hasExecuted: function(f) {
        for (var i = 0; i < this.executed.length; i++) {
            if ( f == this.executed[i]["fname"] ) return true;
        }

        return false;
    },

    // adds an executed function to the array
    //      f - the function executed
    //      e - the elements written from execution
    //      i - the items entry in tabs
    addExecuted: function(f, e, i) {
        if (this.canExecute(f)) {
            this.executed.push({fname: f, elements: e, instances: i});
        }
    },

    /*
     * removes the element related to the executed function f
     * THIS IS A HACK ATM...IT RELIES ON ALL EXECUSTIONS LIMITED TO ONE INSTANCE
     */
    removeExecuted: function(f) {
//        if((var i = hasExecuted(f)) != undefined)
        for (var i = 0; i < this.executed.length; i++) {
            if ( f == this.executed[i]["fname"] ) {
                var e = this.executed[i]["elements"];

                for (var j = 0; j < e.length; j++) {
                    e[j].parentElement.removeChild(e[j]);
                }

                this.executed = new Array();
            }
        }
    },

    /*
     * executes function f
     *      e - the items hash table passed from do_action
     */
    execute: function(e) {
        var f = e["item"]["action"];

        // verify the function may be executed
        if (!this.canExecute(f)) {
            // default action if max local instances == 1 is to close the opened function
            if ( e["item"]["instances"] == 1 ) this.removeExecuted(f);
            return;
        }

//        var elements = new Array();
        var elements = [];

        var content = this.content;
        var divClear = document.createElement("div");
        divClear.setAttribute("class", "clear");
        divClear.parentElement = content;

        var result = f(e);
        result.parentElement = content;

        elements.push(divClear);
        elements.push(result);

        content.appendChild(divClear);
        content.appendChild(result);
        this.addExecuted(f, elements, e["item"]["instances"]);

        // if there is a global instance limit for this function, determine if any other objects have also executed this function and remove their instances
        if ( e["item"]["global_instances"] > 0 ) {
            // loop for all objects
            for ( var i = 0 ; i < ShareStack.length; i++) {
                if (i != this.id) {
                    if (ShareStack[i].hasExecuted(f)) {
                        ShareStack[i].removeExecuted(f);
                    }
                }
            }
        }
    }

});



function render(options){
    var t = new ShareWidget(options);
    ShareStack.push(t);

    //write the searchable container for content addition
    document.write("<div id=\"container-" + count + "\" class=\"container-x\"></div>");                                 // MUST continue to use this as a means of initiation
    count++ ;
}

/**
 * This function generates appropiate css entries by applying a predefined class to an element.
 *  css - is drawn from the tabs array as the default class to apply
 *  specific - may be specified to apply an array of secific entries from the given default root class name.
 *      e.g. css = container-tabs; specific = down; => we are applying the difference between the default and newly specified class, container-tabs-down
 *
*/
function applyCSS(element, css, specific) {
    css.trim();
    css.removeWhiteSpace();

    // apply the difference FINISH THIS THING APPROPIATLY!!!!  at the moment it just applies both
    var gen_cls = /*{ }*/ "";
    if (css !="" && specific != undefined) {                                          // these are generic validators...not effictive against ""

        def_cls = (css.split(":"))[1].replace(";", "");
        gen_cls = def_cls + "-" + specific;
        element.setAttribute("class", def_cls + " " + gen_cls);

    // just apply the default
    } else if (css != "" && specific == undefined) {
        element.setAttribute("class", css.split(":")[1].replace(";", ""));
    }

    return element;
}


/*
 * This function parses the appropiate mechanisms per action preformed.  This is the main listener hook.
 *      - e is the action element, in this case a MouseEvent
 */
function do_something(e) {

    var source = e.target ? e.target : e.srcElement; //determine the target
    var rootElement = source.rootElement;                                                                               // the root element which returns a reference to our object
    
    // this hash is passed along to virtually all action events
    var actionHash = {source: source, tabsource: rootElement.activeTab, root: rootElement};

    // if the user selected a container-tab...
    if ( source.tagName == "DIV") {
        do_expand(rootElement, source);

    // act upon a menu selection
    } else if (source.tagName == "LI") {
        var item = find_item({title: source.childNodes[0].nodeValue}, actionHash /*{title: source.childNodes[0].nodeValue}*/);                                                  // find and return the hash associated with the <li>
        if (item != null) {
            actionHash["item"] = item;
            do_action(actionHash);                                                                                      // preform an action
        }

    // default action: clear last child in content .... THIS IS ONE HELL OF A HACK FOR CLEARING THE MAIL FORM OUT...
    } else if(source.name == "mail_subscribe") {
        source.rootElement.removeExecuted(pop_email_subscribe);

    } else if(source.name == "mail_share") {
        source.rootElement.removeExecuted(pop_email_share);

    } else {
        //alert("'" + source.tagName + "'");
    }

}

/*
 * This function expands upon the root menu showing all sub-menu items
 *      - e is 
 */
function do_expand(rootElement, source) {
    var content = rootElement.content;
    var tabIndex = source.tabIndex;
    var cTab = tabs[tabIndex];

    //remove the old child elements
    while ( content.hasChildNodes() ) {
        clear_content(content);
        rootElement.executed = new Array();
        //content.removeChild(content.firstChild);
    }

    // if the tab is not enabled then exit
    if(cTab.enabled != true) return;

    // if the tab was active then exit
    if(source == rootElement.activeTab) {
        applyCSS(source, cTab.css, "closed");
        //source.applyCSS(cTab.css, "closed");
        rootElement.activeTab = null;
        return;
    }

    // set the tab as active and all others not     THERE WILL BE A MAJOR BUG HERE IF TABS ARE NOT ENABLED...
    for (var i = 0; i < tabs.length; i++) {
        var child = rootElement.container.childNodes[i];
        if (child.tagName == "DIV") {
            rootElement.container.childNodes[i].active = false;
            applyCSS(rootElement.container.childNodes[i], tabs[i].css, "closed");
            //rootElement.container.childNodes[i].applyCSS(tabs[i].css, "closed");
        }
    }
    applyCSS(source, cTab.css, "opened");
    //source.applyCSS(cTab.css, "opened");
    rootElement.activeTab = source;


    // add the new child elements
    var container = document.createElement("ul");                                                                       // create a <ul> container
    container.rootElement = rootElement;
    container.parentElement = content;
    for (var i = 0; i < cTab.items.length; i++) {                                                                       // iterate for all items within tab[index]
        // create the element according to the item's enabled property
        if(cTab.items[i].enabled == true) {
            var element = document.createElement("li");                                                                 // create an <li> element per item
            applyCSS(element, cTab.items[i].css );
            //element.applyCSS(cTab.items[i].css);
            element.appendChild(document.createTextNode(cTab.items[i].title));                                          // create and append a TextNode to the <li> element
            element.rootElement = rootElement;                                                                          // the root element is the topmost div
            element.parentElement = container;                                                                          // the parent element is the <ul> container
            element.parentTab = cTab.title;
            container.appendChild(element);                                                                             // append the <li> element to the <ul> container

            listen(element, "click", do_something);
        }
    }

    content.appendChild(container);
}

/*
 * This function preforms an action wether it be a url to open or a function to call.
 *      - e is passed as a hash of {action: "someAction", tabsource: theSource, root: theRoot}
 */
function do_action(e) {
    //switch on types: string and function
    var item = e["item"];
    switch(typeof item["action"]) {
        // if URL
        case "string":
            var url = item["action"].replace("{url}", e["root"].url);                                                   // replace {url} and {title} with the appropiate URL and title
            url = url.replace("{title}", e["root"].title);

            if(!item.target){
                location.href = url;
            }else{
                var target = (item["target"] != null && item["target"] != "") ? item["target"] : DEFAULT_TARGET;
                var winRef = window.open(url, "", target);                                                                   // open the url in a window
            }

            winRef.focus();                                                                                             // set winRef to have focus

            break;

        // if function
        case "function":
            e["root"].execute(e);
        break;

        // default
        default: 
            //alert("is default");
            break;
    }
}


//removes the children nodes under content
function clear_content(content) {
    var children = content.childNodes;

    for (var i = 0; i < children.length; i++) {
        content.removeChild(children[i]);
    }
}


/*
 * This function will search through tabs[] and return an entire item entry.
 *      - may search according to title, icon, action, target
 *      - useage: find_item( {title: "someTitle", icon: "someIcon", action: "someAction", target: "someTarget"}, e as passed from do_something() ...I think...);
 *      - havent' tested with multiple search items yet...
 */

var clean = {};

function find_item(e, v) {
    var ret = null;

    //search the current active tab index
    for ( var i = 0; i < tabs[v.tabsource.tabIndex].items.length; i++) {
        for ( var ki in e ) {
            // iterate for each key in items
            for ( var kj in tabs[v.tabsource.tabIndex].items[i] ) {
                if(ki == kj && (clean[kj] == undefined)){
                //if ( ki == kj && (ki != "clone" && ki != "update" && ki != "toJSONString") ) {
                    if ( e[ki] != tabs[v.tabsource.tabIndex].items[i][kj] ) {
                        ret = null;
                        break;
                    } else {
                        //alert(ki + " value: " + e[ki] + "\n" + kj + " value: " + tabs[v.tabsource.tabIndex].items[i][kj]);
                        ret = tabs[v.tabsource.tabIndex].items[i];
                    }
                }

                if ( ret != null ) return ret;
            }
        }
    }
}


