The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * LICENSE AND COPYRIGHT
 *
 * Copyright (C) 2012 Eric Wolf
 * This module is free software; it can be used under the terms of the artistic license
 *
 * Version 0.203
 */

/*
  Upon script load, find the port that the script came from, if any.
 */

var scripts = document.getElementsByTagName('script');
var index = scripts.length - 1;
var myScriptUrl = scripts[index].src;
var ma = myScriptUrl.match( /^((https?:\/\/)?[^\/]+(:(\d+))?)\// );
var yote_scr_url = ma && ma.length > 1 ? ma[ 1 ] : '';

// the whole yote object.
if( typeof $.yote === 'object' ) {
    var eh = $.yote;
}

$.yote = {
    url:yote_scr_url,
    has_updated :false,
    guest_token:0,
    token:0,
    port:null,
    err:null,
    objs:{},
    apps:{},
    debug:false,
    app:null,
    root:null,
    need_reinit:false,

    _ids:0,
    _next_id:function() {
        return '__yidx_'+this._ids++;
    }, //_next_id
    _pag_list_cache : {},
    _pag_hash_cache : {},


    init:function( appname, token ) {
        token = token ? token : $.cookie('yoken');
	    $.yote.token = token || 0;
        var ret;
	    this.message( {
	        async:false,
	        cmd:'fetch_initial',
	        data:{ t:token,a:appname },
	        passhandler:function( initial_data ) {	    
		        if( typeof initial_data === 'object' && initial_data.get(  'root' ) && initial_data.get(  'app' ) ) {
		            var yote_root = initial_data.get(  'root' ); 
                    yote_root._app_id = yote_root.id;
		            $.yote.yote_root = yote_root;
		            $.yote.objs[ yote_root.id ] = yote_root;

		            var app = initial_data.get( 'app' ) || yote_root;
		            app._app_id = app.id;
		            $.yote._app_id = app.id;
		            $.yote.default_app = app;
		            $.yote.default_appname = appname;
		            $.yote.objs[ app.id ] = app;

		            $.yote.login_obj   = initial_data.get(  'login' );
		            $.yote.acct_obj    = initial_data.get(  'account' );
                    $.yote.acct_obj._app_id = app.id;

		            $.yote.guest_token = initial_data.get(  'guest_token' );

		            ret = app;
		        }
		        else {
		            console.log( "ERROR in init for app '" + appname + "' Load did not work" );
		        }
	        },
	        failhandler:function( err ) {
		        console.log( "ERROR in init for app '" + appname + "' : " + err );
	        }
	    } );
        return ret;
    }, //init

    reinit:function( token ) {
	    if( ! this.default_app || this.need_reinit ) {
	        this.init( this.default_appname, token );
	        this.need_reinit = false;
	        return true;
	    }
	    return false;
    }, //reinit

    fetch_default_app:function() {
	    return this.default_app || this.fetch_root();
    },

    fetch_account:function() {
	    if( this.default_app ) {
	        if( ! this.acct_obj ) {
		        this.acct_obj = this.default_app.account();
	        }
	        return this.acct_obj;
	    }
	    return undefined;
    },

    fetch_app:function(appname,passhandler,failhandler) {
	    var yote_root = this.fetch_default_app();
	    if( typeof yote_root === 'object' ) {
	        var ret = yote_root.fetch_app_by_class( appname );
	        ret._app_id = ret.id;
	        return ret;
	    } else if( typeof failhanlder === 'function' ) {
	        failhandler('lost connection to yote server');
	    } else {
	        _error('lost connection to yote server');
	    }
    }, //fetch_app

    fetch_root:function() {
	    var r = $.yote.yote_root;
	    if( ! r ) {
	        r = this.message( {
		        async:false,
		        cmd:'fetch_root'
	        } );
	        $.yote.yote_root = r;
	    }
	    return r;
    }, //fetch_root

    // return not only root but login if applicable
    // returns root, app, login, account
    fetch_initial:function( token, appname ) {
	    if( r && typeof r === 'object' && r.length() > 2 ) {
	        return [ r.get(0), r.get(1), r.get(2), r.get(3) ];
	    }
    }, //fetch_initial

    get_by_id:function( id ) {
	    return $.yote.objs[id+''] || $.yote.fetch_default_app().fetch(id).get(0);
    },

    has_root_permissions:function() {
	    return this.is_logged_in() && 1*this.get_login().get_is_root();
    },

    get_login:function() {
	    return this.login_obj;
    }, //get_login

    is_logged_in:function() {
	    return typeof this.login_obj === 'object';
    }, //is_logged_in

    login:function( handle, password, passhandler, failhandler ) {
	    var root = this.fetch_root();
	    if( typeof root === 'object' ) {
	        root.login( { h:handle, p:password },
			            function(res) {
			                $.yote.token = res.get( 't' ) || 0;
			                $.yote.login_obj = res.get( 'l' );
			                $.cookie( 'yoken', $.yote.token, { path : '/' } );
			                if( typeof passhandler === 'function' ) {
				                passhandler(res);
			                }
			            },
			            failhandler );
	        return $.yote.login_obj;
	    } else if( typeof failhanlder === 'function' ) {
	        failhandler('lost connection to yote server');
	    } else {
	        _error('lost connection to yote server');
	    }
    }, //login

    logout:function() {
	    $.yote.fetch_root().logout();
	    $.yote.login_obj = undefined;
	    $.yote.acct_obj = undefined;
	    $.yote.default_app = undefined;
	    $.yote.token = 0;
	    $.yote._dump_cache();
	    $.cookie( 'yoken', '', { path : '/' } );
    }, //logout

    /* general functions */
    message:function( params ) {
        var root   = this;
        var data   = root._translate_data( params.data || {} );
        var async  = params.async == true ? 1 : 0;
        var url    = params.url;
        var app_id = params.app_id || '';
        var cmd    = params.cmd;
        var obj_id = params.obj_id || ''; //id to act on

	    root.upload_count = 0;

	    if( ! app_id ) app_id = $.yote._app_id || 0;
	    if( ! obj_id ) obj_id = 0;

        var url = $.yote.url + '/_/' + app_id + '/' + obj_id + '/' + cmd;

	    var uploads = root._functions_in( data );
	    if( uploads.length > 0 ) {
	        return root.upload_message( params, uploads );
	    }
        if( async == 0 ) {
            root._disable();
        }
	    var encoded_data = $.base64.encode( JSON.stringify( { d : data } ) );
        var get_data = $.yote.token + "/" + $.yote.guest_token;
	    var resp;

        if( $.yote.debug == true ) {
	        console.log("\noutgoing : " + cmd + '  : ' + url + '/' + get_data + '-------------------------' );
	        console.log( data );
	    }

	    $.ajax( {
	        async:async,
	        cache: false,
	        contentType: "application/json; charset=utf-8",
	        data : encoded_data,
	        dataFilter:function(a,b) {
		        if( $.yote.debug == true ) {
                    console.log( 'raw incoming ' );
                    var len = 160;
                    for( var i=0; i<a.length; i+=len ) {
                        console.log( a.substring( i, i+len ) );
                    }
                    // print out eadch substring on a line
                }
		        return a;
	        },

	        error:function(a,b,c) { 
                root._error(a); 
            },
	        success:function( data ) {
		        if( $.yote.debug == true ) {
                    console.log( ['incoming ', data ] );
                }
                if( typeof data !== 'undefined' ) {
		            resp = ''; //for returning synchronous

		            if( typeof data.err === 'undefined' ) {
			            //dirty objects that may need a refresh
                        $.yote.has_updated = false;
			            if( typeof data.d === 'object' ) {
                            $.yote.has_updated = true;                            
			                for( var oid in data.d ) {
				                if( root._is_in_cache( oid ) ) {
				                    
				                    var cached = root.objs[ oid + '' ];
				                    for( fld in cached._d ) {
					                    //take off old getters/setters
					                    delete cached['get_'+fld];
				                    }
				                    cached._d = data.d[ oid ];

				                    for( fld in cached._d ) {
					                    //add new getters/setters
					                    cached['get_'+fld] = (function(fl) { return function() { return this.get(fl) } } )(fld);
				                    }
				                }
			                } //each dirty
			            } //if dirty

			            if( typeof data.r === 'object' ) {
			                resp = root._create_obj( data.r, app_id );
		                    if( typeof params.passhandler === 'function' ) {
				                params.passhandler( resp );
			                }
			            } else if( typeof data.r === 'undefined' ) {
		                    if( typeof params.passhandler === 'function' ) {
				                params.passhandler();
			                }
			            } else {
			                resp = data.r.substring( 1 );
		                    if( typeof params.passhandler === 'function' ) {
				                params.passhandler( resp );
			                }
		                }
		            } else if( typeof params.failhandler === 'function' ) {
			            console.log( data.err );
		                params.failhandler(data.err);
                    } //error case. no handler defined
                } else {
                    console.log( "Success reported but no response data received" );
                }
	        },
	        type:'POST',
	        url:url + '/' + get_data
	    } );
        if( ! async ) {
            root._reenable();
            return resp;
        }
    }, //message

    /* the upload function takes a selector returns a function that sets the name of the selector to a particular value,
       which corresponds to the parameter name in the inputs.
       For example some_yote_obj->do_something( { a : 'a data', file_up = upload( '#myfileuploader' ) } )
    */
    upload:function( selector_id ) {
	    var uctxt = 'u' + this.upload_count++;
	    $( selector_id ).attr( 'name', uctxt );
	    return (function(uct, sel_id) {
	        return function( return_selector_id ) { //if given no arguments, just returns the name given to the file input control
		        if( return_selector_id ) return sel_id;
		        return uctxt;
	        };
	    } )( uctxt, selector_id );
    }, //upload

    /* Should have a upload_multiple. This would pass the files as filename -> data pairs, and include a filenames list */

    /*
      This is called automatically by message if there is an upload involved. It is not meant to be invoked directly.
    */
    upload_message:function( params, uploads ) {


	    // for multiple, upload the files in order, then get the filehelper objs as callbacks and then make the call

        var root   = this;
        var data   = root._translate_data( params.data || {}, true );
        var url    = params.url;
        var app_id = params.app_id || '';
        var cmd    = params.cmd;
        var obj_id = params.obj_id || ''; //id to act on

        var url = $.yote.url + '/_u/' + app_id + '/' + obj_id + '/' + cmd;

	    root.iframe_count++;
	    var iframe_name = 'yote_upload_' + root.iframe_count;
	    var form_id = 'yote_upload_form_' + root.iframe_count;
	    var iframe = $( '<iframe id="' + iframe_name + '" name="' + iframe_name + '" style="position;absolute;top:-9999px;display:none" /> ').appendTo( 'body' );
	    var form = '<form id="' + form_id + '" target="' + iframe_name + '" method="post" enctype="multipart/form-data" />';
	    var upload_selector_ids = uploads.map( function( x ) { return x(true) } );
	    var cb_list = [];
	    $( upload_selector_ids.join(',') ).each(
	        function( idx, domEl ) {
		        $( this ).prop( 'disabled', false );
		        cb_list.push(  $( 'input:checkbox', this ) );
	        }
	    );
	    if( $.yote.debug == true ) {
	        console.log("\noutgoing " + url + '-------------------------' );
	        console.log( data );
	    }

	    var form_sel = $( upload_selector_ids.join(',') ).wrapAll( form ).parent('form').attr('action',url);
	    $( '#' + form_id ).append( '<input type=hidden name=d value="' + $.base64.encode(JSON.stringify( {d:data} ) ) + '">');
	    $( '#' + form_id ).append( '<input type=hidden name=t value="' + $.yote.token + '">');
	    $( '#' + form_id ).append( '<input type=hidden name=gt value="' + $.yote.guest_token + '">');

	    for( var i=0; i<cb_list.length; i++ ) {
	        cb_list[ i ].removeAttr('checked');
	        cb_list[ i ].attr('checked', true);
	    }
	    var resp;

	    var xx = form_sel.submit(function() {
	        iframe.load(function() {
		        var contents = $(this).contents().get(0).body.innerHTML;
		        while( contents.match( /^\s*</ ) ) {
		            contents = contents.replace( /^\s*<\/?[^\>]*>/, '' );
		            contents = contents.replace( /<\/?[^\>]*>\s*$/, '' );
		        }
		        $( '#' + iframe_name ).remove();
		        try {
		            resp = JSON.parse( contents );
		            if( $.yote.debug == true ) {
			            console.log([ 'incoming ', resp ] );
		            }

                    if( typeof resp !== 'undefined' ) {
			            if( typeof resp.err === 'undefined' ) {
			                //dirty objects that may need a refresh
			                if( typeof resp.d === 'object' ) {
				                for( var oid in resp.d ) {
				                    if( root._is_in_cache( oid ) ) {
					                    var cached = root.objs[ oid + '' ];
					                    for( fld in cached._d ) {
					                        //take off old getters/setters
					                        delete cached['get_'+fld];
					                    }
					                    cached._d = resp.d[ oid ];
					                    for( fld in cached._d ) {
					                        //add new getters/setters
					                        cached['get_'+fld] = (function(fl) { return function() { return this.get(fl) } } )(fld);
					                    }
				                    }
				                }
			                }
		                    if( typeof params.passhandler === 'function' ) {
				                if( typeof resp.r === 'object' ) {
				                    params.passhandler( root._create_obj( ret.r, this._app_id ) );
				                } else if( typeof resp.r === 'undefined' ) {
				                    params.passhandler();
				                } else {
				                    params.passhandler( resp.r.substring( 1 ) );
				                }
		                    }
			            } else if( typeof params.failhandler === 'function' ) {
		                    params.failhandler(resp.err);
			            } //error case. no handler defined
                    } else {
			            console.log( "Success reported but no response data received" );
                    }
		        } catch(err) {
		            root._error(err);
		        }
	        } )
	    } ).submit();
    }, //upload_message


    _cache_size:function() { //used for unit tests
        var i = 0;
        for( v in this.objs ) {
            ++i;
        }
        return i;
    },

    _wrap_list:function( obj, field, key ) {
        return $.yote._data_wrapper( obj, field, key );
    }, //wrap_list

    _wrap_hash:function( obj, field, key ) {
        return $.yote._data_wrapper( obj, field, key, true );
    }, //wrap_hash

    _data_wrapper:function( obj, field, key, is_hash ) {
        var node = is_hash ? $.yote._pag_hash_cache[ key ] : $.yote._pag_list_cache[ key ];
        if( ! key || (! node && ( (! obj && ! field ) ) ) ) {
            if( is_hash ) 
                throw new Exception( 'wrap hash called without ' + ( key ? 'hash' : 'key' ) );
            else
                throw new Exception( 'wrap list called without ' + ( key ? 'list' : 'key' ) );
        }

        if( ! node ) {
            var full_size = obj.count( { name : field } );
            var server_paginate = field.match( /^_/ ) || full_size > 300;

            var start = 0;
            node = {
                _server_paginate : server_paginate,
                _start : start,
                _data_size : full_size,
                _page_size  : 0,
                _filter_function     : undefined,
                _sort_function       : undefined,
                _transform_function  : undefined,
                set_filter : function( filter_fun ) {
                    this._filter_function = filter_fun;
                },
                set_sort : function( sort_fun ) {
                    this._sort_function = sort_fun;
                },
                set_transform : function( trans_fun ) {
                    this._transform_function = trans_fun;
                },
                back:function(){
                    this._start -= this._page_size;
                    if( this._start < 0 ) {
                        this._start = 0;                    
                    }
                },
                can_rewind:function(){
                    return this._start > 0;
                },
                can_fast_forward:function(){
                    return (this._start + this._page_size) < this._data_size;
                },
                forwards:function(){
                    this._start += this._page_size;
                    if( this._start >= this._data_size ) {
                        this._start = this._data_size - 1;
                    }
                },
                first:function(){
                    this._start = 0;
                },
                last:function(){
                    this._start = this._data_size - this._page_size;
                    if( this._start < 0 ) {
                        this._start = 0;
                    }
                },
                set_size : function( newsize ) {
                    this._page_size = Number(newsize);
                },
                to_list : function() {
                    var ret;
                    if( this._server_paginate ) {
                        ret = this._obj.paginate( { name : this._field } ).to_list();
                        //TODO : make this paginate for the filters rather than grabbing all
                    } else {
                        var o = this._obj.get( this._field );
                        ret = o ? o.to_list() : [];
                    }
                    if( typeof this._filter_function !== 'undefined' ) {
                        ret = this._arry.map( this._filter_function );
                    }
                    if( typeof this._sort_function !== 'undefined' ) {
                        ret = ret.sort( this._sort_function );
                    }
                    if( typeof this._start !== 'undefined' || typeof this._page_size !== 'undefined' ) {
                        if( typeof this._page_size !== 'undefined' ) 
                            ret = ret.slice( this._start, this._start + this._page_size );
                        else
                            ret = ret.slice( this._start );
                    }
                    return ret;
                },
                keys : function() {
                    var _hash;
                    if( this._server_paginate ) {
                        _hash = this._obj.paginate( { name : this._field, return_hash : 1 } ).to_hash();
                    } else {
                        var o = this._obj.get( this._field )
                        _hash = o ? o.to_hash() : {};
                    }
                    var ret = Object.keys( _hash );
                    if( typeof this._filter_function !== 'undefined' ) {
                        var new_ret = [];
                        for( var i=0; i<ret.length; i++ ) {
                            var k = ret[ i ];
                                if( this._filter_function( k, _hash[ k ] ) )
                                    new_ret.push( k );
                        }
                        ret = new_ret;
                    } 
                    ret = ret.sort( this._sort_function );
                    if( typeof this._start !== 'undefined' || typeof this._page_size !== 'undefined' ) {
                        if( typeof this._page_size !== 'undefined' ) 
                            ret = ret.slice( this._start, this._start + this._page_size );
                        else
                            ret = ret.slice( this._start );
                    }
                    return ret;
                },
                to_hash : function() {
                    var h;
                    if( this._server_paginate ) {
                        h = this._obj.paginate( { name : this._field, return_hash : 1 } ).to_hash();
                    } else {
                        var o = this._obj.get( this._field );
                        h = o ? o.to_hash() : {};
                    }
                    var r = {};
                    var k = this.keys();
                    for( var i=0; i<k.length; i++ ) {
                        r[ k[i] ] = h[ k[i] ];
                    }
                    return r;
                }
            };
            if( is_hash ) {
                $.yote._pag_hash_cache[ key ] = node;
            } else {
                $.yote._pag_list_cache[ key ] = node;
            }
        } else if( node.server_paginate ) {
            node._data_size = obj.count( { name : field } );
        } else {
            node._data_size = is_hash ? Object.count( node._obj.get( node._field ).to_hash() ) : node._obj.get( node._field ).to_list().length;
        }
        if( obj && field ) {
            node._obj = obj;
            node._field = field;
        }
        return node;
    }, //data_wrapper
        

    // TODO : use prototype for the _create_obj
    _create_obj:function(data,app_id) { //creates the javascript proxy object for the perl object.
	    var root = this;
	    if( data.id != null && typeof data.id !== 'undefined' && root._is_in_cache( data.id ) ) {
	        return root.objs[ data.id + '' ];
	    }
	    var retty = (function(x,ai) {
	        var o = {
		        _app_id:ai,
                _dirty:false,
		        _d:{},
		        id:x.id+'',
		        class:x.c,
                _staged:{},
		        length:function() {
		            var cnt = 0;
		            for( key in this._d ) {
			            ++cnt;
		            }
		            return cnt;
		        },
		        equals:function(oth) {
		            return typeof oth === 'object' && oth.id && oth.id == this.id;
		        },
		        keys:function() {
		            return Object.keys( this._d );
		        },
		        values:function() {
		            var thing = this;
		            return this.keys().map(function(a) { return thing.get(a); } );
		        },
		        sort:function(sortfun) {
		            var res = this.values().sort( sortfun );
		            return res;
		        }
	        };
	        if( o.class == 'HASH' ) {
		        o.to_hash = function() {
		            var hash = {};
		            for( var key in this._d ) {
			            hash[ key ] = this.get( key );
		            }
		            return hash;
		        };
	        }
	        else if( o.class == 'ARRAY' ) {
		        o.to_list = function() {
		            var list = [];
		            for( var i=0; i < this.length(); i++ ) {
			            list[i] = this.get(i);
		            }
		            return list;
		        };
	        }
	        else {
		        if( typeof x.m === 'object' && x.m !== null ) { // set methods
		            for( m in x.m ) {
			            o[x.m[m]] = (function(key,thobj) {
			                return function( params, passhandler, failhandler, use_async ) {
				                return root.message( {
				                    async: use_async ? true : false,
				                    app_id:this._app_id,
				                    cmd:key,
				                    data:params,
				                    failhandler:failhandler,
                                    obj_id:this.id,
				                    passhandler:passhandler
				                } ); //sending message
			                } } )(x.m[m],x);
		            } //each method
		        } // if methods were included in the return value of the call
	        } // if object

	        o.get = function( key ) {
		        var val = this._staged[key] || this._d[key];
		        if( typeof val === 'undefined' ) return undefined;
		        if( typeof val === 'object' ) return val;
		        if( typeof val === 'function' ) return val;

		        if( val.substring(0,1) != 'v' ) {
		            var obj = root.objs[val+''];
		            if( ! obj ) {
			            var ret = $.yote.fetch_default_app().fetch(val);
			            if( ! ret ) return undefined; //this can happen if an authorized user logs out
			            obj = ret.get(0);
		            }
		            obj._app_id = this._app_id;
                    return obj;
		        }
		        var ret = val.substring(1);
                return typeof ret * 1 !== 'NaN' ? ret : ret * 1;
	        };
	        
	        o.is = function( othero ) {
		        var k = this.id;
		        var ok = othero ? othero.id : undefined;
		        return k !== 'undefined' && k == ok;
	        }

	        o._get_id = function( key ) {
		        // returns the id ( if any of the item specified by the key )
		        var val = this._d[key];
		        return val && val.substring(0,1) != 'v' ? val : undefined;
	        },

	        o.set = function( key, val, failh, passh ) {
		        this._stage( key, val );
		        this._send_update( undefined, failh, passh );
		        delete this._staged[ key ];
                if( ! this[ 'set_' + key ] ) 
                    this[ 'set_' + key ] = (function(k) { return function(val,fh,ph) { return this.set(k,val,fh,ph) } } )(key);
		        return val;
	        };

	        // get fields
	        if( typeof x.d === 'object' && x.d !== null ) {
		        for( fld in x.d ) {
		            var val = x.d[fld];
		            if( typeof val === 'object' && val != null ) {
			            o._d[fld] = (function(xx) { return root._create_obj( xx, app_id ); })(val);
		            }
		            else {
			            o._d[fld] = (function(xx) { return xx; })(val);
		            }
		            o['get_'+fld] = (function(fl) { return function() { return this.get(fl) } } )(fld);
		            o['set_'+fld] = (function(fl) { return function(val,fh,ph) { return this.set(fl,val,fh,ph) } } )(fld);
		        }
	        }

            // stages functions for updates
            o._stage = function( key, val ) {
                if( this._staged[key] !== root._translate_data( val ) ) {
                    this._staged[key] = root._translate_data( val );
                    this._dirty = true;
                }
            }

            // resets staged info
            o._reset = function( field ) {
		        if( field ) {
		            delete this._staged[ field ];
		        } else {
                    this._staged = {};
		        }
            }

            o._is_dirty = function(field) {
                return typeof field === 'undefined' ? this._dirty : this._staged[field] !== this._d[field] ;
            }

            // sends data structure as an update, or uses staged values if no data
            o._send_update = function(data,failhandler,passhandler) {
                var to_send = {};
                if( this.c === 'Array' ) {
                    to_send = Array();
                }
                if( typeof data === 'undefined' ) { //sending from staged
                    for( var key in this._staged ) {
                        if( this.c === 'Array' ) {
                            to_send.push( root._untranslate_data(this._staged[key]) );
                        } else {
                            to_send[key] = root._untranslate_data(this._staged[key]);
                        }
                    }
                } else {
                    for( var key in data ) {
                        if( this.c === 'Array' ) {
                            to_send.push( data[key] );
                        } else {
                            to_send[key] = data[key];
                        }
                    }
                }
                var needs = 0;
                for( var key in to_send ) {
                    needs = 1;
                }
                if( needs == 0 ) { return; }
                root.message( { //for send update
                    app_id:$.yote._app_id,
                    async:false,
                    data:to_send,
                    cmd:'update',
                    failhandler:function() {
                        if( typeof failhandler === 'function' ) {
                            failhandler();
                        }
                    },
                    obj_id:this.id,
                    passhandler:(function(td) {
                        return function() {
                            o._staged = {};
                            if( typeof passhandler === 'function' ) {
                                passhandler();
                            }
                        }
                    } )(to_send),
                } );
            }; //_send_update

	        if( o.id && o.id.substring(0,1) != 'v' ) {
		        root.objs[o.id+''] = o;
	        }
	        return o;
        } )(data,app_id);
	    return retty;
    }, //_create_obj

    _disable:function() {
	    if( $( 'body' ).css("cursor") !== "wait" ) {
            $("*").css("cursor", "wait");
            this.enabled = $(':enabled');
	        $.each( this.enabled, function(idx,val) { val.disabled = true; } );
	    }
    }, //_disable

    _dump_cache:function() {
        this.objs = {};
	    this.apps = {};
	    this.yote_root   = undefined;
	    this.default_app = undefined;
        this._app_id = undefined;
    },

    // generic server type error
    _error:function(msg) {
        console.log( "a server side error has occurred" );
        console.log( msg );
    },

    _functions_in:function( thing ) {
	    var to_ret, res;
	    if( typeof thing === 'function' ) return [thing];
	    if( typeof thing === 'object' || typeof thing === 'array' ) {
	        to_ret = [];
	        for( x in thing ) {
		        res = this._functions_in( thing[ x ] );
		        for( y in res ) {
		            to_ret.push( res[ y ] );
		        }
	        }
	        return to_ret;
	    }
	    return [];
    }, //_functions_in

    _is_in_cache:function(id) {
        return typeof this.objs[id+''] === 'object' && this.objs[id+''] != null;
    },

    _reenable:function() {
        $("*").css("cursor", "auto");
        $.each( this.enabled, function(idx,val) { val.disabled = false } );
    }, //_reenable

    _translate_data:function(data,run_functions) {
        if( typeof data === 'undefined' || data == null ) {
            return undefined;
        }
        if( typeof data === 'object' ) {
            if( data.id  && typeof data._d !== 'undefined' && data.id.substring(0,1) != 'v' ) {
                return data.id;
            }
            // this case is for paramers being sent thru message
            // that will not get ids.
            var ret;
	        if (data instanceof Array) {
		        ret = [];
	        } else {
		        ret = Object();
	        }
            for( var key in data ) {
                ret[key] = this._translate_data( data[key], run_functions );
            }
            return ret;
        }
	    if( typeof data === 'function' ) {
	        if( run_functions )
		        return data();
	        return data;
	    }
        return 'v' + data;
    }, //_translate_data

    _untranslate_data:function(data) {
	    if( typeof data === 'function' ) {
	        return data;
	    }
        if( data.substring(0,1) == 'v' ) {
            return data.substring(1);
        }
        if( this._is_in_cache(data) ) {
            return this.objs[data+''];
        }
        console.log( "Don't know how to translate " + data);
    }, //_untranslate_data

    upload_count: 0,
    iframe_count: 0

}; //$.yote

if( eh ) {
    for( var key in eh ) {
        $.yote[ key ] = eh[ key ];
    }
}

// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
    Array.prototype.map = function(callback, thisArg) {

	    var T, A, k;

	    if (this == null) {
	        throw new TypeError(" this is null or not defined");
	    }

	    // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
	    var O = Object(this);

	    // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
	    // 3. Let len be ToUint32(lenValue).
	    var len = O.length >>> 0;

	    // 4. If IsCallable(callback) is false, throw a TypeError exception.
	    // See: http://es5.github.com/#x9.11
	    if ({}.toString.call(callback) != "[object Function]") {
	        throw new TypeError(callback + " is not a function");
	    }

	    // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
	    if (thisArg) {
	        T = thisArg;
	    }

	    // 6. Let A be a new array created as if by the expression new Array(len) where Array is
	    // the standard built-in constructor with that name and len is the value of len.
	    A = new Array(len);

	    // 7. Let k be 0
	    k = 0;

	    // 8. Repeat, while k < len
	    while(k < len) {

	        var kValue, mappedValue;

	        // a. Let Pk be ToString(k).
	        //   This is implicit for LHS operands of the in operator
	        // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
	        //   This step can be combined with c
	        // c. If kPresent is true, then
	        if (k in O) {

		        // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
		        kValue = O[ k ];

		        // ii. Let mappedValue be the result of calling the Call internal method of callback
		        // with T as the this value and argument list containing kValue, k, and O.
		        mappedValue = callback.call(T, kValue, k, O);

		        // iii. Call the DefineOwnProperty internal method of A with arguments
		        // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
		        // and false.

		        // In browsers that support Object.defineProperty, use the following:
		        // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });

		        // For best browser support, use the following:
		        A[ k ] = mappedValue;
	        }
	        // d. Increase k by 1.
	        k++;
	    }

	    // 9. return A
	    return A;
    };
} //map definition

if( ! Object.size ) {
    Object.size = function(obj) {
	    var size = 0, key;
	    for (key in obj) {
            if (obj.hasOwnProperty(key)) size++;
	    }
	    return size;
    };
}
if( ! Object.keys ) {
    Object.keys = function( t ) {
    	var k = []
	    for( var key in t ) {
	        k.push( key );
	    }
	    return k;
    }
}
if( ! Object.clone ) {
    // shallow clone
    Object.clone = function( h ) {
        var clone = {};
        for( var key in h ) {
	        clone[ key ] = h[ key ];
        }
        return clone;
    }
}