/*
 * e107 website system
 *
 * Copyright (C) 2001-2008 e107 Inc (e107.org)
 * Released under the terms and conditions of the
 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
 *
 * e107 Javascript API
 *
 * $Source: /cvs_backup/e107_0.8/e107_files/jslib/e107.js.php,v $
 * $Revision: 1.31 $
 * $Date: 2009-04-23 10:16:35 $
 * $Author: secretr $
 *
*/

var e107API = {
	Version: '1.0.1',
	ServerVersion: '0.8.1'
}

/*
 * Old stuff
 * FIXME ASAP
 */
var nowLocal = new Date();		/* time at very beginning of js execution */
var localTime = Math.floor(nowLocal.getTime()/1000);	/* time, in ms -- recorded at top of jscript */
/**
 * NOTE: if serverDelta is needed for js functions, you must pull it from
 * the cookie (as calculated during a previous page load!)
 * The value calculated in SyncWithServerTime is not known until after the
 * entire page has been processed.
 */
function SyncWithServerTime(serverTime)
{
	if (serverTime)
	{
	  	/* update time difference cookie */
		var serverDelta=Math.floor(localTime-serverTime);
	  	document.cookie = 'e107_tdOffset='+serverDelta+'; path=/';
	  	document.cookie = 'e107_tdSetTime='+(localTime-serverDelta)+'; path=/'; /* server time when set */
	}

	var tzCookie = 'e107_tzOffset=';
//	if (document.cookie.indexOf(tzCookie) < 0) {
		/* set if not already set */
		var timezoneOffset = nowLocal.getTimezoneOffset(); /* client-to-GMT in minutes */
		document.cookie = tzCookie + timezoneOffset+'; path=/';
//	}
}

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

/**
 * Prototype Xtensions
 * @author    Simon Martins
 * @copyright (c) 2008 Netatoo SARL <http://www.netatoo.fr>
 * @license   MIT License <http://www.prototypextensions.com/#main=license>
 *
 * @desc Retrieve the browser version
 */
(function() {
    var nav       = navigator;
    var userAgent = ua = navigator.userAgent;
    var v         = nav.appVersion;
    var version   = parseFloat(v);

    e107API.Browser = {
        IE      : (Prototype.Browser.IE)    ? parseFloat(v.split("MSIE ")[1]) || 0 : 0,
        Firefox : (Prototype.Browser.Gecko) ? parseFloat(ua.split("Firefox/")[1]) || 0 : 0,
        Camino  : (Prototype.Browser.Gecko) ? parseFloat(ua.split("Camino/")[1]) || 0 : 0,
        Flock   : (Prototype.Browser.Gecko) ? parseFloat(ua.split("Flock/")[1]) || 0 : 0,
        Opera   : (Prototype.Browser.Opera) ? version : 0,
        AIR     : (ua.indexOf("AdobeAIR") >= 0) ? 1 : 0,
        Mozilla : (Prototype.Browser.Gecko || !this.Khtml) ? version : 0,
        Khtml   : (v.indexOf("Konqueror") >= 0 && this.safari) ? version : 0,
        Safari  : (function() {
            var safari = Math.max(v.indexOf("WebKit"), v.indexOf("Safari"), 0);
            return (safari) ? (
                parseFloat(v.split("Version/")[1]) || ( ( parseFloat(v.substr(safari+7)) >= 419.3 ) ? 3 : 2 ) || 2
            ) : 0;
        })()
    };
})();

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

/**
 * Main registry object
 */
var e107Registry = {
    
    //System Path
    Path: {
        e_IMAGE:    '<?php echo e_IMAGE_ABS; ?>',
        SITEURL:    '<?php echo SITEURL; ?>',
        SITEURLBASE:    '<?php echo SITEURLBASE; ?>',
        e_HTTP:    '<?php echo e_HTTP; ?>',
        e_IMAGE_PACK:    '<?php echo e_IMAGE_ABS; ?>',
        e_PLUGIN:   '<?php echo e_PLUGIN_ABS; ?>',
        e_FILE:     '<?php echo e_FILE_ABS; ?>',
        e_ADMIN: 	'<?php echo defsettrue(ADMIN) ? e_ADMIN_ABS : ""; ?>',
        e_THEME:    '<?php echo e_THEME_ABS; ?>',
        THEME:      '<?php echo THEME_ABS; ?>'
    },
    
    //Language Constants
    Lan: {},

    //Global Templates
    Template: {
    	Core: {
    		//e107Helper#duplicateHTML method
	        duplicateHTML:	'<div><div class="clear"><!-- --></div>' +
                           		'#{duplicateBody}' +
                           		'<a href="#" id="#{removeId}"><img src="#{e_IMAGE_PACK}admin_images/delete_16.png" class="icon action" style="vertical-align: middle" /></a>' +
                           	'</div>'
    	},
    	
    	//e107Helper#LoadingStatus class
    	CoreLoading:   {
    		template: 		'<div id="loading-mask">' +
								'<p id="loading-mask-loader" class="loader">' +
									'<img src="#{e_IMAGE_PACK}generic/loading_32.gif" alt="#{JSLAN_CORE_LOADING_ALT}" />' +
									'<br /> <span class="loading-text">#{JSLAN_CORE_LOADING_TEXT}</span>' +
								'</p>' +
							'</div>'
    	}
    },
    
    //Cache
    Cache: new Hash,
    
    //Cached vars
    CachedVars: new Hash,
    
    //Global Preferences
    Pref: {
    	Core: {
    		zIndex: 5 //base system z-index
    	}
    }
}

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

/**
 * Global helpers - server side clonings
 */
var isset = function(varname) {
    return !Object.isUndefined(varname);
}

var varset = function(varname) {
    if(Object.isUndefined(varname)) {
        return (Object.isUndefined(arguments[1]) ? null : arguments[1]);
    }
    return varname;
}

var varsettrue = function(varname) {
    if(Object.isUndefined(varname) || !varname) {
        return (Object.isUndefined(arguments[1]) ? null : arguments[1]);
    }
    return varname;
}

var cachevars = function(varname, data) {
    e107Registry.CachedVars.set(data)
}

var getcachedvars = function(varname, destroy) {
	if(destroy) 
		return clearcachedvars(varname);
    return e107Registry.CachedVars.get(varname);
}

var clearcachedvars = function(varname) {
    return e107Registry.CachedVars.unset(varname);
}

var echo = Prototype.emptyFunction, print_a = Prototype.emptyFunction, var_dump = Prototype.emptyFunction;

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


/**
 * e107 custom events
 */
var e107Event = {
		
    fire: function(eventName, memo, element) { 
    	if ((!element || element == document) && !document.createEvent)
    	{
    		element = $(document.documentElement); 
    	}
    	else 
    		element = $(element) || document; 
    	memo = memo || {}; 
    	return element.fire('e107:' + eventName, memo);
    },	
    
    observe: function(eventName, handler, element) {
    	element = $(element) || document; 
    	element.observe('e107:' + eventName, handler);
    	return this;
    },	
    
    stopObserving: function(eventName, handler, element) {
    	element = $(element) || document; 
    	element.stopObserving('e107:' + eventName, handler);
    	return this;
    },
    
    //Server side - e107_event aliases
    trigger: function(eventName, memo, element) {
    	this.fire(eventName, memo, element);
    },
    
    register: function(eventName, handler, element) {
    	this.observe(eventName, handler, element);
    },
    
    unregister:  function(eventName, handler, element) {
    	this.stopObserving(eventName, handler, element);
    }
}



/**
 * EventManager
 * Prototype Xtensions http://www.prototypextensions.com
 * 
 * @desc Create custom events on your own class
 */
var e107EventManager = Class.create({
        
    /**
     * Initialize
     *
     * @desc Set scope and events hash
     */
    initialize: function(scope) {
        this.scope  = scope;
        this.events = new Hash();
    },
    
    /**
     * addListener
     *
     * @desc Add event observer
     */
    addObserver: function(name) {
        return this.events.set(name, new Hash());
    },
    
    /**
     * observe
     *
     * @desc Add a callback for listener 'name'
     */
    observe: function(name, callback) {
        var observers = this.events.get(name);
        
        if(!observers) observers = this.addObserver(name);
        
        if(!Object.isFunction(callback)) {
            //throw('e107EventManager.observe : callback must be an js function');
            //surpess error 
            return this;
        }
        
        var i = this.events.get(name).keys().length;
        observers.set(i, callback.bind(this.scope));
        
        return this;
    },
    
    /**
     * stopObserving (class improvements)
     *
     * @desc Remove callback for listener 'name'
     */
    stopObserving: function(name, callback) {
        var observers = this.events.get(name);
        
        if(!observers) return this;
        observers.each( function(pair) {
        	if(pair.value == callback) {
        		observers.unset(pair.key);
        		$break;
        	}
        });
        return this;
    },
    
    /**
     * notify
     *
     * @desc Launch all callbacks for listener 'name'
     */
    notify: function(name) {
        var observers = this.events.get(name);
        //console.log('notifying ' + name);
        if(observers) { 
            var args = $A(arguments).slice(1); 
            //Fix - preserve order
            observers.keys().sort().each( function(key) {
            	var callback = observers.get(key);
                if(Object.isFunction(callback)) { 
                    callback.apply(this.scope, args);
                }
            });
        }
        
        return this;
    }
    
});

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


/**
 * Base e107 Object - interacts with the registry object
 */
var e107Base = {
    
    setPath: function(path_object) {
        e107Registry.Path = Object.extend( this.getPathVars(), path_object || {});
        return this;
    },
    
    addPath: function(path_var, path) {
    	//don't allow overwrite
        if(!e107Registry.Path[path_var]) e107Registry.Path[path_var] = path; 
        return this;
    },

    getPathVars: function() { 
        return e107Registry.Path;
    },

    getPath: function(path_name) {
        return varset(e107Registry.Path[path_name]);
    },

    _addLan: function(lan_name, lan_value) {
        e107Registry.Lan[lan_name] = lan_value;
        return this;
    },
    
    _getLan: function(lan_name) {
        return varsettrue(e107Registry.Lan[lan_name], lan_name);
    },
    
    setLan: function(lan_object) {
    	if(!arguments[1]) {
	        Object.keys(lan_object).each(function(key) {
	            this.addLan(key, lan_object[key]);
	        }, this);
	        return this
    	}
        Object.extend(e107Registry.Lan, (lan_object || {}));
        return this;
    },
    
    addLan: function(lan_name, lan_value) {
        this._addLan(this.toLanName(lan_name), lan_value);
        return this;
    },
    
    setModLan: function(mod, lan_object) {
    	Object.keys(lan_object).each( function(key) {
    		this.addModLan(mod, key, lan_object[key]);
    	}, this);
    	return this;
    },
    
    addModLan: function(mod, lan_name, lan_value) {
        return this._addLan(this.toModLanName(mod, lan_name), lan_value);
    },

    getLan: function(lan_name) {
        return this._getLan(this.toLanName(lan_name));
    },
    
    getModLan: function(mod, lan_name) {
    	return this._getLan(this.toModLanName(mod, lan_name));
    },

    getLanVars: function() {
        return e107Registry.Lan;
    },
    
    getModLanVars: function(mod) {
    	return this.getLanFilter(this.toModLanName(mod));
    },
    
    //Example e107.getLanRange('lan1 lan2 ...'); -- { LAN1: 'lan value1', LAN2: 'lan value2', ... }
    getLanRange: function(lan_keys) {
        var ret = {};
        $w(lan_keys).each( function(key) {
            this[key.toUpperCase()] = e107.getLan(key);
        }, ret);
        return ret;
    },
    
    //Example e107.getLanFilter('lan_myplug'); -- { LAN_MYPLUG_1: 'lan value1', LAN_MYPLUG_2: 'lan value2', ... }
    getLanFilter: function(filter) {
        var ret = {};
        filter = filter.toUpperCase();
        $H(e107Registry.Lan).keys().each( function(key) {
            if(key.startsWith(filter)) {
                this[key] = e107Registry.Lan[key];
            }
        }, ret);
        
        return ret;
    },
    
    setTemplate: function(mod, tmpl_object) {
        mod = this.toModName(mod);
        if(!varset(e107Registry.Template[mod])) {
            e107Registry.Template[mod] = {};
        }
        Object.extend(e107Registry.Template[mod], (tmpl_object || {}));
        
        return this;
    },
    
    addTemplate: function(mod, name, tmpl_string) {
        mod = this.toModName(mod);
        if(!varset(e107Registry.Template[mod])) {
            e107Registry.Template[mod] = {};
        }
        e107Registry.Template[mod][name] = tmpl_string;
        
        return this;
    },
    
    getTemplates: function(mod) {
        return varsettrue(e107Registry.Template[this.toModName(mod)], {});
    },

    getTemplate: function(mod, name) {
        mod = this.toModName(mod);
        
        if(varset(e107Registry.Template[mod])) {
            return varsettrue(e107Registry.Template[mod][name], '');
        }
        
        return '';
    },

    setPrefs: function(mod, pref_object) { 
        mod = this.toModName(mod);
        if(!varset(e107Registry.Pref[mod])) {
            e107Registry.Pref[mod] = {};
        }
        Object.extend(e107Registry.Pref[mod], (pref_object || {}));
        
        return this;
    },
    
    addPref: function(mod, pref_name, pref_value) {
        mod = this.toModName(mod);
        if(!varset(e107Registry.Pref[mod])) {
            e107Registry.Pref[mod] = {};
        }
        e107Registry.Pref[mod][pref_name] = pref_value;
        
        return this;
    },
    
    getPrefs: function(mod) {
        return varsettrue(e107Registry.Pref[this.toModName(mod)], {});
    },

    getPref: function(mod, pref_name, def) {
        mod = this.toModName(mod);
        if(varset(e107Registry.Pref[mod])) {
            return varsettrue(e107Registry.Pref[mod][pref_name], varset(def, null));
        }
        return varset(def, null);
    },
    
    setCache: function(cache_str, cache_item) {
    	this.clearCache(cache_str); 
        e107Registry.Cache['cache-' + cache_str] = cache_item;
        return this;
    },
    
    getCache: function(cache_str, def) {  
        return varset(e107Registry.Cache['cache-' + cache_str], def);
    },
    
    clearCache: function(cache_str, nodestroy) {
    	var cached = this.getCache(cache_str);
    	if(!nodestroy && cached && Object.isFunction(cached['destroy'])) cached.destroy();
    	e107Registry.Cache['cache-' + cache_str] = null;
    	delete e107Registry.Cache['cache-' + cache_str];
    	return this;
    },

    parseTemplate: function(mod, name, data) {
        var cacheStr = mod + '_' + name; 
        var cached = this.getCache(cacheStr);
        if(null === cached) {
            var tmp = this.getTemplate(mod, name);
            cached = new Template(tmp);
            this.setCache(cacheStr, cached);
        }
        
        if(varsettrue(arguments[3])) {
            data = this.getParseData(Object.clone(data || {}));
        }

        try{
           return cached.evaluate(data || {});
        } catch(e) {
            return '';
        }
    },
    
    getParseData: function (data) {
        Object.extend(data || {}, 
          Object.extend(this.getLanVars(), this.getPathVars())
        );
        
        return data;
    },
    
    parseLan: function(str) { 
        return String(str).interpolate(this.getLanVars());
    },
    
    parsePath: function(str) {
        return String(str).interpolate(this.getPathVars());
    },
    
    toModName: function(mod, raw) {
    	return raw ? mod.dasherize() : mod.dasherize().camelize().ucfirst();
    },
    
    toLanName: function(lan) {
    	return 'JSLAN_' + lan.underscore().toUpperCase();
    },
    
    toModLanName: function(raw_mod, lan) {
    	return this.toLanName(raw_mod + '_' + varset(lan, ''));
    }
};

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

/**
 * String Extensions
 * 
 * Methods used later in the core + e107base shorthands
 */
Object.extend(String.prototype, {
	
	//php like
    ucfirst: function() {
        return this.charAt(0).toUpperCase() + this.substring(1);
    },
    
	//Create element from string - Prototype UI
	createElement: function() {
	    var wrapper = new Element('div'); wrapper.innerHTML = this;
	    return wrapper.down();
	},
	
	parseToElement: function(data) {
		return this.parseTemplate(data).createElement();
	},
	
	parseTemplate: function(data) {
		return this.interpolate(e107Base.getParseData(data || {}));
	},

	parsePath: function() {
		return e107Base.parsePath(this);
	},

	parseLan: function() {
		return e107Base.parseLan(this);
	},
	
    addLan: function(lan_name) {
    	if(lan_name)
        	e107Base.addLan(lan_name, this);
        return e107Base.toLanName(lan_name);
    },
    
    addModLan: function(mod, lan_name) {
    	if(mod && lan_name)
        	e107Base.addModLan(mod, lan_name, this);
        return e107Base.toModLanName(mod, lan_name);
    },
    
    getLan: function() {
        return e107Base.getLan(this);
    },
    
    getModLan: function(mod) {
    	if(mod)
    		return e107Base.getModLan(mod, this);
    	return this;
    }
});

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

/**
 * e107WidgetAbstract Class
 */
var e107WidgetAbstract = Class.create(e107Base);
var e107WidgetAbstract = Class.create(e107WidgetAbstract, {
    
    initMod: function(modId, options, inherit) {

        this.mod = e107Base.toModName(modId, true);
        if(!this.mod) {
            throw 'Illegal Mod ID';
        }

		var methods = 'setTemplate addTemplate getTemplate parseTemplate setPrefs addPref getPref getPrefs getLan getLanVars addLan setLan';
		var that = this;
		
		//Some magic
		$w(methods).each(function(method){
			var mod_method = method.gsub(/^(set|get|add|parse)(.*)$/, function(match){
				return match[1] + 'Mod' + match[2];
			});
			var parent_method = !e107Base[mod_method] ? method : mod_method;
			this[mod_method] = e107Base[parent_method].bind(this, this.mod);
		}.bind(that));
		
		Object.extend(that, {
			getModName: function(raw) {
				return raw ? this.mod : e107Base.toModName(this.mod);
			},
		    
		    parseModLan: function(str) {
		        return String(str).interpolate(e107Base.getModLan(this.mod));
		    },
		    
		    setModCache: function(cache_str, cache_item) {
		    	e107Base.setCache(this.getModName(true) + varsettrue(cache_str, ''), cache_item);
		    	return this;
		    },
		    
		    getModCache: function(cache_str) {
		    	return e107Base.getCache(this.getModName(true) + varsettrue(cache_str, ''));
		    },
		    
		    clearModCache: function(cache_str) {
		    	e107Base.clearCache(this.getModName(true) + varsettrue(cache_str, ''));
		    	return this;
		    }
		});
		
        //Merge option object (recursive) 
        this.setOptions(options, inherit);
        
        return this;
    },
    

    setOptions: function(options, inherit) {
        this.options = {};

        var c = this.constructor;
        
        if (c.superclass && inherit) {
            var chain = [], klass = c;
            
            while (klass = klass.superclass) 
                chain.push(klass);
             
            chain = chain.reverse();
            for (var i = 0, len = chain.length; i < len; i++) {
                if(!chain[i].getModPrefs) chain[i].getModPrefs = Prototype.emptyFunction;
                //global options if available
                Object.extend(this.options, chain[i].getModPrefs() || {});
            }
        }

        //global options if available
        if(!this.getModPrefs) { this.getModPrefs = Prototype.emptyFunction; }
        
        Object.extend(this.options, this.getModPrefs() || {});
        return Object.extend(this.options, options || {});
    }
    
});

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

/**
 * Core - everything's widget!
 */
var e107Core = Class.create(e107WidgetAbstract, {
    initialize: function() {
        this.initMod('core');
    },

    /**
     * e107:loaded Event observer
     */
    runOnLoad: function(handler, element, reload) {
    	e107Event.register('loaded', handler, element || document);
    	if(reload) 
    		this.runOnReload(handler, element);
    	
    	return this;
    },
    
    /**
     * Ajax after update Event observer
     */
    runOnReload: function(handler, element) {
    	e107Event.register('ajax_update_after', handler, element || document);
    	return this;
    }

});

//e107Core instance 
var e107 = new e107Core();

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

/*
 * Widgets namespace
 * @descr should contain only high-level classes
 */
 var e107Widgets = {};

/**
 * Utils namespace
 * @descr contains low-level classes and non-widget high-level classes/objects
 */
var e107Utils = {}

/**
 * Helper namespace
 * @descr includes all old e107 functions + some new helper methods/classes
 */
var e107Helper = {
    fxToggle: function(el, fx) {
    	var opt = Object.extend( { effect: 'blind' , options: {duration: 0.5} }, fx || {});
        Effect.toggle(el, opt.effect, opt.options);
    }
}

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

/*
 * Element extension
 */
Element.addMethods( {
	fxToggle: function(element, options) {
	    e107Helper.fxToggle(element, options);
	}
});

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

/**
 * Backward compatibility
 */
Object.extend(e107Helper, {
	
	toggle: function(el) {
		var eltoggle; 
		/**
		 * (SecretR) Notice
		 * 
		 * Logic mismatch!
		 * Passed element/string should be always the target element (which will be toggled)
		 *  OR
		 * anchor: <a href="#some-id"> where 'some-id' is the id of the target element
		 * This method will be rewritten after the core is cleaned up. After this point
		 * the target element will be auto-hidden (no need of class="e-hideme")
		 */
		
        if(false === Object.isString(el) || (  
        	($(el) && $(el).nodeName.toLowerCase() == 'a' && $(el).readAttribute('href'))
        		|| 
        	($(el) && $(el).readAttribute('type') && $(el).readAttribute('type').toLowerCase() == 'input') /* deprecated */
        )) { 
        	eltoggle = (function(el) {
	    		return Try.these(
	    		    function() { var ret = $(el.readAttribute('href').substr(1));  if(ret) { return ret; } throw 'Error';}, //This will be the only valid case in the near future
                    function() { var ret = el.next('.e-expandme'); if(ret) { return ret; } throw 'Error';},// maybe this too?
                    function() { var ret = el.next('div'); if(ret) { return ret; } throw 'Error'; }, //backward compatibality - DEPRECATED
                    function() { return null; } //break
	    		) || false;
        	})($(el));
        } else {
            var eltoggle = $(el);
        }
		
        if(!eltoggle) return false;
        
		var fx = varset(arguments[1], null);
		
		if(false !== fx)
		    this.fxToggle(eltoggle, fx || {});
		else 
		    $(eltoggle).toggle();
		    
		return true;
	},
	
    /**
     * added as Element method below
     * No toggle effects!
     */
    downToggle: function(element, selector) {
    	$(element).select(varsettrue(selector, '.e-expandme')).invoke('toggle');
    	return element;
    },
	
	/**
	 * Event listener - e107:loaded|e107:ajax_update_after
	 * @see e107Core#addOnLoad
	 */
    toggleObserver: function(event) {
    	var element = event.memo['element'] ? $(event.memo.element) : $$('body')[0];
        Element.select(element, '.e-expandit').invoke('observe', 'click', function(e) {
            var element = e.findElement('a');
            if(!element) element = e.element();
            if(this.toggle(element, {})) e.stop();
        }.bindAsEventListener(e107Helper));
    },
    
    /**
     * Add fx scroll on click event
     * on all <a href='#something" class="scroll-to"> elements
     */
    scrollToObserver: function(event) {
    	var element = event.memo['element'] ? $(event.memo.element) : $$('body')[0];
		Element.select(element, 'a[href^=#].scroll-to:not([href=#])').invoke('observe', 'click', function(e) {
			new Effect.ScrollTo(e.element().hash.substr(1));
			e.stop();
		});
    },
    
    /**
     * added as Element method below
     */
    downHide: function(element, selector) {
    	$(element).select(varsettrue(selector, '.e-hideme')).invoke('hide');
    	return element;
    },
    
    /**
     * added as Element method below
     */
    downShow: function(element, selector) {
    	$(element).select(varsettrue(selector, '.e-hideme')).invoke('show');
    	return element;
    },
    
    //event listener
    autoHide: function(event) {
    	var hideunder = event.memo['element'] ? $(event.memo.element) : $$('body')[0];
        if(hideunder) hideunder.downHide();
    },
    
    /**
     * added as Element method below
     * autocomplete="off" - all major browsers except Opera(?!)
     */
    noHistory: function(element) {
        $(element).writeAttribute('autocomplete', 'off');
        return element;
    },

    /**
     * added as Element method below
     */
    downNoHistory: function(element, selector) {
    	$(element).select(varsettrue(selector, 'input.e-nohistory')).invoke('noHistory');
    	return element;
    },
    
    //event listener
    autoNoHistory: function(event) {
    	var down = event.memo['element'] ? $(event.memo.element) : $$('body')[0];
        if(down) down.downNoHistory();
    },
    
    /**
     * added as Element method below
     */
	externalLink: function (element) {
	    $(element).writeAttribute('target', '_blank');
	    return element;
	}, 
	
    /**
     * added as Element method below
     */
    downExternalLinks: function(element) {
    	$(element).select('a[rel~=external]').invoke('externalLink');
    	return element;
    },
    
    //event listener
	autoExternalLinks: function (event) {
		//event.element() works for IE now!
		//TODO - remove memo.element references
		//event.memo['element'] ? $(event.memo.element) : $$('body')[0];
		var down = event.element() != document ? event.element() : $$('body')[0]; 
	    if(down) down.downExternalLinks();
	}, 

	urlJump: function(url) {
	    top.window.location = url;
	},
	
	//TODO Widget - e107Window#confirm;
    confirm: function(thetext) {
    	return confirm(thetext);
    },
    
    autoConfirm: function(event) {
    	
    },
	
	imagePreload: function(ejs_path, ejs_imageString) {
	    var ejs_imageArray = ejs_imageString.split(',');
	    for(var ejs_loadall = 0, len = ejs_imageArray.length; ejs_loadall < len; ejs_loadall++){
	        var ejs_LoadedImage = new Image();
	        ejs_LoadedImage.src=ejs_path + ejs_imageArray[ejs_loadall];
	    }
	},
	
	toggleChecked: function(form, state, selector, byId) {
		form = $(form); if(!form) { return; } 
		if(byId) selector = 'id^=' + selector; 
		$A(form.select('input[type=checkbox][' + selector + ']')).each(function(element) { if(!element.disabled) element.checked=state });
	},
	
	//This will be replaced later with upload_ui.php own JS method
	//and moved to a separate class
    __dupCounter: 1,
    __dupTmpTemplate: '',
	//FIXME
	duplicateHTML: function(copy, paste, baseid) {
        if(!$(copy) || !$(paste)) { return; }
        this.__dupCounter++;
        var source = $($(copy).cloneNode(true)), newentry, newid, containerId, clearB; 
        
        source.writeAttribute('id', source.readAttribute('id') + this.__dupCounter); 
        newid = (baseid || 'duplicated') + '-' + this.__dupCounter;

        var tmpl = this.getDuplicateTemplate();
        if(tmpl) {
        	var sourceInnerHTML = source.innerHTML;
        	source = source.update(tmpl.parseToElement({
                duplicateBody: sourceInnerHTML,
                removeId: 'remove-' + newid,
                baseId: baseid || '',
                newId: newid,
                counter: this.__dupCounter
            })).down().hide();
        	clearB = $(source.select('#remove-' + newid)[0]);
        } else {
        	//see clear, clearL and clearR CSS definitions
        	clearB = new Element('input', { 'class': 'button', 'value': 'x', 'type': 'button', 'id': 'remove-' + newid }); //backward compat. - subject of removal
        	source.insert({
        		top: new Element('div', {'class': 'clear'}),  
        		bottom: clearB
        	}).hide();
        }
        if(baseid) {
            source.innerHTML = source.innerHTML.replace(new RegExp(baseid, 'g'), newid);
        }
        var containerId = source.identify(); 
        $(paste).insert(source);
        //Again - the EVIL IE6
        if(!clearB) { clearB = $('remove-' + newid); }
        
        clearB.observe('click', function(e) {
        	e.stop();
        	var el = e.element().up('#'+containerId);
	        el.fxToggle({
	            effect: 'appear',
	            options: { 
	            	duration: 0.4, 
	                afterFinish: function(o) { o.element.remove(); } 
	            }
	        });
        }.bind(this));

        source.fxToggle({
        	effect: 'appear',
        	options: { duration: 0.5 }
        });
	},
	
    getDuplicateTemplate: function() {
    	if(this.__dupTmpTemplate) {
    		var tmpl = this.__dupTmpTemplate;
    		this.__dupTmpTemplate = '';
    		return tmpl;
    	}
    	return e107.getModTemplate('duplicateHTML');
    },
    
    setDuplicateTemplate: function(tmpl) {
        return this.__dupTmpTemplate = tmpl;
    },
    
	previewImage: function(src_val, img_path, not_found) {
	   $(src_val + '_prev').src = $(src_val).value ? img_path + $(src_val).value : not_found;
	    return;
	},
	
	insertText: function(str, tagid, display) {
	    $(tagid).value = str.escapeHTML();
	    if($(display)) {
	        $(display).fxToggle();
	    }
	},
	
	appendText: function(str, tagid, display) {
	    $(tagid).focus().value += str.escapeHTML();
	    if($(display)) {
	        $(display).fxToggle();
	    }
	},
	
	//by Lokesh Dhakar - http://www.lokeshdhakar.com
    getPageSize: function() {
	        
	     var xScroll, yScroll;
		
		if (window.innerHeight && window.scrollMaxY) {	
			xScroll = window.innerWidth + window.scrollMaxX;
			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
			if(document.documentElement.clientWidth){
				windowWidth = document.documentElement.clientWidth; 
			} else {
				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 = xScroll;		
		} else {
			pageWidth = windowWidth;
		}

		return [pageWidth,pageHeight];
	}
});


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

/*
 * Element extensions
 */
Element.addMethods( {
	downNoHistory: e107Helper.downNoHistory,
	downHide: e107Helper.downHide,
	downShow: e107Helper.downShow,
	downToggle: e107Helper.downToggle,
	downExternalLinks: e107Helper.downExternalLinks
});

Element.addMethods('INPUT', {
	noHistory: e107Helper.noHistory
});

Element.addMethods('A', {
	externalLink: e107Helper.externalLink
});

Element.addMethods('FORM', {
	toggleChecked: e107Helper.toggleChecked
});

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

/**
 * e107BB helper
 */
e107Helper.BB = {
	
	__selectedInputArea: null,
	
	store: function(textAr){
	    this.__selectedInputArea = $(textAr);
	},
	
	/**
	 * New improved version - fixed scroll to top behaviour when inserting BBcodes
	 * @TODO - improve it further
	 */
	insert: function(text, emote) {
	    if (!this.__selectedInputArea) { 
	    	return; //[SecretR] TODO - alert the user 
	    }
	    var eField = this.__selectedInputArea, tags = this.parse(text, emote); 
        if(this.insertIE(eField, text, tags)) return;
	    
	    var scrollPos = eField.scrollTop, sel = (eField.value).substring(eField.selectionStart, eField.selectionEnd);
	    if (eField.selectionEnd <= 2 && typeof(eField.textLength) != 'undefined') {
	        eField.selectionEnd = eField.textLength;
	    }

	    var newStart = eField.selectionStart + tags.start.length + sel.length + tags.end.length;
	    eField.value = (eField.value).substring(0, eField.selectionStart) + tags.start + sel + tags.end + (eField.value).substring(eField.selectionEnd, eField.textLength);
	
	    eField.focus(); eField.selectionStart = newStart; eField.selectionEnd = newStart; eField.scrollTop = scrollPos;
	    return;
	
	},
	
	insertIE: function(area, text, tags) {
        // IE fix  
        if (!document.selection) return false;
        var eSelection = document.selection.createRange().text;
        area.focus();
        if (eSelection) {
            document.selection.createRange().text = tags.start + eSelection + tags.end;
        } else {
            document.selection.createRange().text = tags.start + tags.end;
        }
        eSelection = ''; area.blur(); area.focus();
        return true;
	},
	
	parse: function(text, isEmote) {
		var tOpen = text, tClose = '';    
        if (isEmote != true) {  // Split if its a paired bbcode
            var tmp = text.split('][', 2);
            tOpen = varset(tmp[1]) ? tmp[0] + ']' : text;
            tClose = varset(tmp[1]) ? '[' + tmp[1] : '';
        }
        return { start: tOpen, end: tClose };
	},
	
	//TODO VERY BAD - make it right ASAP! 
	help_old: function(help, tagid, nohtml){
		if(nohtml) { help = help.escapeHTML(); }
		if($(tagid)) { $(tagid).value = help; }
		else if($('helpb')) { 
			$('helpb').value = help;
		}
	},
	
	//FIXME - The new BB help system 
	help: function(help, tagid, nohtml){
		if(nohtml) { help = help.escapeHTML(); }
		if(!$(tagid)) return;
		if(help) {
			var wrapper = new Element('div', {'style': 'position: relative'}).update(help);
			$(tagid).update(wrapper).fxToggle();
		} else {
			$(tagid).update('').fxToggle();
		}
	}
};

//Iframe Shim - from PrototypeUI
e107Utils.IframeShim = Class.create({
	initialize: function() {
		this.element = new Element('iframe',{
			style: 'position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);display:none;',
			src: 'javascript:void(0);',
			frameborder: 0 
		});
		$(document.body).insert(this.element);
	},
	hide: function() {
		this.element.hide();
		return this;
	},
	show: function() {
		this.element.show();
		return this;
	},
	positionUnder: function(element) {
		var element = $(element);
		var offset = element.cumulativeOffset();
		var dimensions = element.getDimensions();
		
		this.element.setStyle({
			left: offset[0] + 'px',
			top: offset[1] + 'px',
			width: dimensions.width + 'px',
			height: dimensions.height + 'px',
			zIndex: element.getStyle('zIndex') - 1
		}).show();

		return this;
	},
	setBounds: function(bounds) {
		for(prop in bounds)
			bounds[prop] += 'px';
		this.element.setStyle(bounds);
		return this;
	},
	destroy: function() {
		if(this.element)
			this.element.remove();
		return this;
	}
});

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

/**
 * Show Page/Element loading status (during AJAX call)
 * 
 * @class e107Utils.LoadingStatus
 * @widget: core-loading
 * @version 1.0
 * @author SecretR
 * @extends e107WidgetAbstract
 * @template: 'template'
 * @cache_string: 'instance-loading-status'
 */


('Loading')				   .addModLan('core-loading', 'alt');
('Loading, please wait...').addModLan('core-loading', 'text');

/**
 * Global Prefs
 */
e107Base.setPrefs('core-loading', {
	opacity: 0.8
	//TODO - more to come!
});

e107Utils.LoadingStatus = Class.create(e107WidgetAbstract, {

	initialize: function(dest_element, options) {
		this.initMod('core-loading', options);
		this.cacheStr = 'instance-loading-status';
		
		this.loading_mask_loader = false;
		this.loading_mask = $('loading-mask');
		this.iframeShim = this.getModCache(this.cacheStr + '-iframe');
		this.destElement = ($(dest_element) || $$('body')[0]);

		this.re_center = this.recenter.bindAsEventListener(this);

		this.create();
	    if(this.options.show_auto) 
	    	this.show();
	},
	
	startObserving: function() {
		Event.observe(window,"resize", this.re_center);
    	if(e107API.Browser.IE && e107API.Browser.IE <= 7)
    		Event.observe(window,"scroll", this.re_center);
    	return this;
	},
	
	stopObserving:  function() {
		Event.stopObserving(window, "resize", this.re_center);
    	if(e107API.Browser.IE && e107API.Browser.IE <= 7)
    		Event.stopObserving(window, "scroll", this.re_center);
    	return this;
	},
	
	set_destination: function(dest_element) {
		this.destElement = $(dest_element) || $$('body')[0];
		return this;
	},
	
	create: function() {
		if(!this.loading_mask) {
			var objBody = $$('body')[0];
			this.loading_mask = this.getModTemplate('template').parseToElement().hide();

			objBody.insert({
				bottom: this.loading_mask
			});
		}
		this.loading_mask.setStyle( { 'opacity': this.options.opacity, zIndex: 9000 } );
		this.loading_mask_loader = this.loading_mask.down('#loading-mask-loader');
		this.loading_mask_loader.setStyle( { /*'position': 'fixed', */zIndex: 9100 } );
		//Create iframeShim if required
		this.createShim();
		return this;
 	},

	show: function () { 
		if(this.loading_mask.visible()) return;
		this.startObserving();
		this.center();
		this.loading_mask.show();
		return this;
	},
	
	hide: function () {
		this.loading_mask.hide();
		this.stopObserving().positionShim(true);
		return this;
	},
	
	center: function() {
		//Evil IE6
		if(!this.iecenter()) { 
			Element.clonePosition(this.loading_mask, this.destElement);
			this.fixBody().positionShim(false);  
		}
		return this;
		
	},
	
	recenter: function() {
		if(!this.iecenter()) {
			Element.clonePosition(this.loading_mask, this.destElement);
			this.fixBody().positionShim(false);
		}
		return this;
	},
	
	iecenter: function() { 
		//TODO - actually ie7 should work without this - investigate
		if(e107API.Browser.IE && e107API.Browser.IE <= 7) {
			//The 'freezing' problem solved (opacity = 1 ?!)
			this.loading_mask.show();
			var offset = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
			var destdim = document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
			
			if(!this.lmh) this.lmh = this.loading_mask_loader.getHeight();
			var eldim = this.lmh;
			var toph = parseInt(destdim/2 - eldim/2 + offset );
			this.loading_mask.setStyle({top: 0, left: 0, 'opacity': 1}); 
			this.fixBody(true);
			this.loading_mask_loader.setStyle( { 
				'position': 'absolute', 
				'top': toph + 'px',
				'opacity': 1
			});

			this.positionShim(false);
			return true;
		}
		return false;
	},

	fixBody: function(force) {
		if(force || this.destElement.nodeName.toLowerCase() == 'body') {
			var ps = e107Helper.getPageSize();
			this.loading_mask.setStyle({ 'width': parseInt(ps[0]) + 'px', 'height': parseInt(ps[1]) + 'px' });	
		}
		return this;
	},
	
	createShim: function() {
		if(e107API.Browser.IE && e107API.Browser.IE <= 7 && !this.iframeShim) {
			this.iframeShim = new e107Utils.IframeShim().hide();
			this.setModCache(this.cacheStr +'-iframe', this.iframeShim);
		}
		
		return this;
	},
	
	positionShim: function(hide) {
		if(!e107API.Browser.IE || e107API.Browser.IE > 6) return this;
		if(hide) {
			this.iframeShim.hide(); return this;
		}
		this.iframeShim.positionUnder(this.loading_mask).show();
		return this;
	}
});

/**
 * Register page loading core events
 */
e107Event.register('ajax_loading_start', function(event) {
	var loadingObj = e107.getModCache('ajax-loader');
	if(!loadingObj) {
		loadingObj = new e107Utils.LoadingStatus(false, { show_auto: false });
		e107.setModCache('ajax-loader', loadingObj);
	}
	loadingObj.set_destination(event.memo.overlayPage).show();
});

e107Event.register('ajax_loading_end', function(event) {
	var loadingObj = e107.getModCache('ajax-loader');
	if(loadingObj) {
		window.setTimeout( function(){ loadingObj.hide() }, 200);
	}
});

/**
 * e107Utils.LoadingElement
 * based on Protoload by Andreas Kalsch
 */
e107Base.setPrefs('core-loading-element', {
	overlayDelay: 50,
	opacity: 0.8,
	zIndex: 10,
	className: 'element-loading-mask',
	backgroundImage: '#{e_IMAGE}generic/loading_32.gif'
});

e107Utils.LoadingElement = {
	startLoading: function(element, options) {
		if(!options) options = {};
		Object.extend(options, e107Base.getPrefs('core-loading-element') || {});
		element = $(element);
			
		var zindex = parseInt(e107.getModPref('zIndex')) + parseInt(options.zIndex);
		var cacheStr = 'core-loading-element-' + $(element).identify();
		element._waiting = true;
		//can't use element._eloading for storing objects because of IE6 memory leak
		var _eloading = e107Base.getCache(cacheStr);
		
		if (!_eloading) {
			_eloading = new Element('div', { 'class': options.className }).setStyle({
				position: 'absolute',
				opacity: options.opacity,
				zIndex: zindex
				//backgroundImage: 'url(' + options.backgroundImage.parsePath() + ')'
			});
			
			$$('body')[0].insert({ bottom: _eloading }); 
			var imgcheck = _eloading.getStyle('background-image');
			//console.log(options.backgroundImage.parsePath());
			if(!imgcheck || imgcheck == 'none') //only if not specified by CSS
				_eloading.setStyle( {backgroundImage: 'url(' + options.backgroundImage.parsePath() + ')'});
			e107Base.setCache(cacheStr, _eloading);
		}
		window.setTimeout(( function() {
			if (this._waiting) {
				Element.clonePosition(_eloading, this);
				_eloading.show();
			}
		}).bind(element), options.overlayDelay);
		
	},
	
	stopLoading: function(element) {
		if (element._waiting) {
			element._waiting = false;
			var cacheStr = 'core-loading-element-' + $(element).identify(), _eloading = e107Base.getCache(cacheStr);
			if($(_eloading)) $(_eloading).hide();//remove it or not?
			//e107Base.clearCache(cacheStr);
		}
	}
};

Element.addMethods(e107Utils.LoadingElement);

/**
 * Register element loading core events
 */
e107Event.register('ajax_loading_element_start', function(event) {
	var element = $(event.memo.overlayElement);
	if(element) element.startLoading();
});

e107Event.register('ajax_loading_element_end', function(event) { 
	var element = $(event.memo.overlayElement);
	if(element)  window.setTimeout( function(){ element.stopLoading() }.bind(element), 50);
});

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

// ###### START DEPRECATED - subject of removal!!! ######

//@see e107Helper#toggle, e107Helper#autoToggle
var expandit = function(curobj, hide) {
	e107Helper.toggle(curobj, {});
	
    if(hide) { //don't use it - will be removed
        hide.replace(/[\s]?,[\s]?/, ' ').strip();
        $w(hide).each(function(h) {
            if(Object.isElement($(h))) { $(h).hide(); }
        });
    }
}

//Use Prototype JS instead: $(id).update(txt);
var setInner = function(id, txt) {
    $(id).update(txt);
}

//@see e107Helper#confirm TODO @see e107ModalConfirm#confirm
var jsconfirm = function(thetext){
        return e107Helper.confirm(thetext);
}

//Use Prototype JS instead e.g.: $(tagid).value = str; $(display).hide();
var insertext = function(str, tagid, display) {
    e107Helper.insertText(str, tagid, display);
}

//Use Prototype JS instead e.g.: $(tagid).focus().value += str; $(display).hide();
var appendtext = function(str, tagid, display) {
    e107Helper.appendText(str, tagid, display);
}

//TODO - e107Window class, e107Helper#openWindow proxy
var open_window = function(url, wth, hgt) {
    if('full' == wth){
        pwindow = window.open(url);
    } else {
    	mywidth = varset(wth, 600);
    	myheight = varset(wth, 400);
        pwindow = window.open(url,'Name', 'top=100,left=100,resizable=yes,width='+mywidth+',height='+myheight+',scrollbars=yes,menubar=yes')
    }
    pwindow.focus();
}

//TODO Window class
var closeWindow = function(form){
    if((window.opener!=null)&&(!window.opener.closed)){
        window.opener.location.reload();
    }
    if(window.opener!=null) {
        window.close();
    }else{setWinType(form);form.whatAction.value="Close";form.submit();}
}


//@see e107Helper#urljump
var urljump = function(url) {
    e107Helper.urlJump(url);
}

//@see e107Helper#imagePreload
var ejs_preload = function(ejs_path, ejs_imageString){
    e107Helper.imagePreload(ejs_path, ejs_imageString)
}

//Use Prototype JS e.g.: $(cntfield).value = $(field).value.length;
var textCounter = function(field,cntfield) {
    cntfield.value = field.value.length;
}

//Not used anymore - seek & remove 
/*
function openwindow() {
    opener = window.open("htmlarea/index.php", "popup","top=50,left=100,resizable=no,width=670,height=520,scrollbars=no,menubar=no");
    opener.focus();
}
*/

//@see e107Helper#toggleChecked
var setCheckboxes = function(the_form, do_check, the_cb) { //backward compatibility
    e107Helper.toggleChecked(the_form, do_check, 'name^=' + the_cb.gsub(/[\[\]]/, ''), false);
}

//@see e107Helper.BB#storeCaret
var storeCaret = function(textAr) {
	e107Helper.BB.store(textAr); return;
}

//@see e107Helper.BB#insert
var addtext = function(text, emote) {
    e107Helper.BB.insert(text, emote); return;
}

//@see e107Helper.BB#help
var help = function(help,tagid) {
    e107Helper.BB.help_old(help, tagid, true);
}

//Use Prototype JS e.g.: $(object).addClassName(over); $(object).removeClassName(over);
var eover = function(object, over) {
    $(object).writeAttribute('class', over);
}

//@see e107Helper#duplicateHTML
var duplicateHTML = function(copy, paste, baseid) {
    e107Helper.duplicateHTML(copy,paste,baseid);
}

var preview_image = function(src_val,img_path, not_found) {
    e107Helper.previewImage(src_val, img_path, not_found)
}

var externalLinks = function () {
    //already event listener
};
// ###### END DEPRECATED ######

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

/**
 * e107History
 *
 * Prototype Xtensions http://www.prototypextensions.com/
 */
var e107History = {
    __altered: false,
    __currentHash: null,
    __previousHash: null,
    __iframe: false,
    __title: false,
    
    /**
     * init()
     * @desc Initialize the hash. Call this method in first
     */
    init: function() {
        var inst  = this;
        var hash  = location.hash.substring(1);
        this.hash = $H(hash.toQueryParams());
        this.__currentHash  = hash;
        this.__previousHash = hash;

        this.__title = document.title;
        
        if(e107API.Browser.IE && e107API.Browser.IE < 8) {
            document.observe('dom:loaded', function(e) {
                if(!$('e107-px-historyframe')) {
                    e107History.__iframe = new Element('iframe', {
                        name   : 'e107-px-historyframe',
                        id     : 'e107-px-historyframe',
                        src    : '',
                        width  : '0',
                        height : '0',
                        style  : {
                            visibility: 'hidden'
                        }
                    });
                    
                    document.body.appendChild(e107History.__iframe);
                    
                    e107History.setHashOnIframe(inst.hash.toQueryString());
                }
            });
        }
    },
    
    /**
     * set( string name, string value )
     *
     * @desc Set new value value for parameter name
     */
    set: function(name, value) {
        this.__previousHash = this.hash.toQueryString();
        this.hash.set(name, value);
        this.apply();
    },
    
    /**
     * get( string $name )
     *
     * @desc Get value parameter $name
     */
    get: function(name) {
        return this.hash.get(name);
    },
    
    /**
     * unset( string $name )
     *
     * @desc Unset parameter $name
     */
    unset: function(name) {
        this.hash.unset(name);
        this.apply();
    },
    
    /**
     * update()
     *
     * @desc Updates this.hash with the current hash
     */
    update: function() {
        this.__previousHash = this.hash.toQueryString();
        var hash = window.location.hash.substring(1);

        // If IE, look in the iframe if the hash is updated
        if(e107API.Browser.IE && e107API.Browser.IE < 8 && this.__iframe) {
            var hashInFrame = this.getHashOnIframe();
            
            if(hashInFrame != hash) {
                hash = hashInFrame;
            }
        }

        this.hash = $H(hash.toQueryParams());
        this.__currentHash = hash;
    },
    
    /**
     * apply()
     *
     * @desc Apply this.hash to location.hash
     */
    apply: function() {
        var newHash = this.hash.toQueryString();

        // set new hash
        window.location.hash = newHash;
        
        // If IE, apply new hash to frame for history    
        if(e107API.Browser.IE && e107API.Browser.IE < 8 && this.__iframe) {
            if(this.__currentHash != newHash) 
            {
                this.setHashOnIframe(newHash);   
            }
            else if(newHash != this.getHashOnIframe()) 
            {
                this.setHashOnIframe(newHash);    
            }
        }
    },

    /**
     * isAltered()
     *
     * @desc Return true if current hash is different of previous hash.
     * this.__altered allows to force the dispatch.
     */
    isAltered: function() {
        if(this.__altered) {
            return true;
        }
        this.__altered = false;

        return (e107History.__currentHash != e107History.__previousHash);
    },
    
    /**
     * setHashOnIframe()
     *
     * @use  For IE compatibility
     * @desc Set hash value on iframe
     */
    setHashOnIframe: function(hash) {
        try {
            var doc = e107History.__iframe.contentWindow.document;
            doc.open();
            doc.write('<html><body id="history">' + hash + '</body></html>');
            doc.close();
        } catch(e) {}
    },
    
    /**
     * getHashOnIframe()
     *
     * @use  For IE compatibility
     * @desc Get hash value on iframe
     */
    getHashOnIframe: function() {
        var doc = this.__iframe.contentWindow.document;
        if (doc && doc.body.id == 'history') {
            return doc.body.innerText;
        } else {
            return this.hash.toQueryString();
        }
    },
    
    /**
     * setTitle()
     *
     * @desc Set a new title for window
     */
    setTitle: function(title) {
        if(document.title) {
            document.title = title;
        }
    },
    
    /**
     * getTitle()
     *
     * @desc Return current window title
     */
    getTitle: function() {
        return this.__title;
    }
};

e107History.init();
 
/**
 * History.Registry
 * Prototype Xtensions http://www.prototypextensions.com/
 *
 * @desc Used to register a callback for a parameter
 */
e107History.Registry = 
{
    /**
     * @desc Hash
     */
    hash : new Hash(),
    
    /**
     * set( string $config )
     *
     * @desc Set new value historyId for parameter config
     */
    set: function(config) {
    
        if(typeof(config) != 'object') {
            throw('e107History.Registry.set : config must be an javascript object');
        } 
        
        // id
        if(!config.id || !Object.isString(config.id)) {
            throw('e107History.Registry.set : config.id must be an string');
        } 
        
        // onChange
        if(!config.onStateChange || !Object.isFunction(config.onStateChange)) {
            throw('e107History.Registry.set : config.onStateChange '
                + 'must be an javascript callback function');
        }
    
        // defaultValue
        if(!config.defaultValue || !Object.isString(config.defaultValue)) {
            config.defaultValue = '';
        }
                
        this.hash.set(config.id, config);
    },
    
    /**
     * flat version of set method 
     *
     * @desc Register callback function for historyId
     */
    register: function(historyId, callback, defval) {
        var config = {
        	id: historyId,
        	onStateChange: callback,
        	defaultValue: defval
        };
        this.set(config);
    },
    
    /**
     * get( string $id )
     *
     * @desc Get value parameter $id
     */
    get: function(id) {
        return this.hash.get(id);
    },
    
    /**
     * unset( string $id )
     *
     * @desc Unset parameter $id
     */
    unset: function(id) {
        this.hash.unset(id);
    }
}

/**
 * History.Observer
 * Prototype Xtensions http://www.prototypextensions.com/
 * 
 * @desc Used to perform actions defined in the registry, 
 * according to the hash of the url.
 */
e107History.Observer = {

    /**
     * @desc Interval delay in seconds
     */
    delay : 0.4,
    
    /**
     * @desc Interval timer instance
     */
    interval : null,
    
    /**
     * @desc If interval is started : true, else false
     */
    started : false,
    
    /**
     * start()
     *
     * @desc Start a interval timer
     */
    start: function() {
        if(this.started) return;
        this.interval = new PeriodicalExecuter(e107History.Observer.dispatch, this.delay);
        this.started = true;
    },
    
    /**
     * stop()
     *
     * @desc Stop the interval timer
     */
    stop: function() {
        if(!this.started) return;
        this.interval.stop();
        this.started = false;
    },
    
    /**
     * dispatch()
     *
     * @desc This method is called each time interval, 
     * the dispatch of the registry is implemented only if 
     * the hash has been amended (optimisiation)
     */
    dispatch: function() {
        // Update the hash
        e107History.update();

        // Dispatch only if location.hash has been altered
        if(e107History.isAltered()) {
        	var oldstate = String(e107History.__previousHash).toQueryParams();
        	//FIXME - possible bugs/performance issues here - investigate further
            e107History.hash.each(function(pair)  {
                var registry = e107History.Registry.get(pair.key);
                //Bugfix - notify callbacks only when required
                if(registry && (e107History.__altered === pair.key || oldstate[pair.key] !== pair.value)) {
                   registry.onStateChange.bind(e107History)( pair.value );
                }
            });
        }
    }
};

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

/*
 * AJAX related 
 */
var e107Ajax = {};

/**
 * Ajax.History
 * Prototype Xtensions http://www.prototypextensions.com/
 * 
 * @desc Provides core methods to easily manage browsing history 
 * with Ajax.History.Request / Updater.
 */
e107Ajax.History = {

    /**
     * @desc Allowed Ajax.History prefix (for validation)
     */
    types : ['Request', 'Updater'],
    
    cacheString: 'ajax-history-',
    
    /**
     * observe( string type, string id, string url, object options )
     *
     * @desc This method helps manage the browsing history
     */
    observe: function(type, id, url, options) {
    
        var getter         = e107.getModCache(this.cacheString + id);
        var currentVersion = 0;
        var output         = false;
        
        // Type validation   
        if(this.types.indexOf(type) == -1) {
            throw('e107Ajax.History.observer: type ' + type + ' is invalid !');
        }      

        // Registry management
        if(!getter) {
            currentVersion = (options.history.state) ? options.history.state : 0;  
            var hash = new Hash();
            hash.set(currentVersion, options);
            e107.setModCache(this.cacheString + id, hash);
            //console.log(id,  e107.getModCache(this.cacheString + id));
        } else {
            currentVersion = (options.history.state) 
                ? options.history.state : this.getCurrentVersion(id);
            getter.set(currentVersion, options);
        }
            
        // add handler on registry
        this.addCallback(type, id);

        return currentVersion;
    },
    
    /**
     * addCallback( string type, string id )
     *
     * @desc This method adds a state for request on History.Registry
     */
    addCallback: function(type, id) {
        
        e107History.Observer.start();
        // Set history altered state to true : force dispatch
        e107History.__altered = id;
        
        // Return void if registry is already set
        if(!Object.isUndefined(e107History.Registry.get(id))) return; 

        // Add this id to history registry
        var cacheS = this.cacheString + id;
        e107History.Registry.set({ 
            id: id,
            onStateChange: function(state) { 
                var options = e107.getModCache(cacheS).get(state.toString()); 
                var request = null;

                if(Object.isUndefined(options)) return;
				                	
                if(options.history.cache == true && options.history.__request) { 
                    new Ajax.Cache(options.history.__request);
                } else {
                	
                	//make a request
                    if(type == 'Request') {
                        request = new Ajax.Request(options.history.__url, options);
                    } else if(type == 'Updater') {
                        request = new Ajax.Updater(options.container, options.history.__url, options);
                    }
                    options.history.__request = request; 
                }
                
                e107History.__altered = false;
                
                if (Object.isFunction(options.history.onStateChange)) {
                    options.history.onStateChange(state);
                }
            } 
        }); 
    },
    
    /**
     * getCurrentVersion( string id )
     *
     * @desc This method returns the current state in history 
     * (if the state is not defined)
     */
    getCurrentVersion: function(id) {
        var getter = e107.getModCache(this.cacheString + id);        
        return Object.isUndefined(getter) ? 0 : getter.keys().length;
    }
};

e107Ajax.ObjectMap = {
    id              : null,    // set custom history value for this instance
    state           : false,   // set custom state value for this instance
    cache           : false,   // enable/disable history cache 
    onStateChange   : null,    // handler called on history change
    __url           : null,
    __request       : null
};

/**
 * Ajax.Cache
 * Prototype Xtensions http://www.prototypextensions.com/
 * 
 * @desc Ajax.Cache can "simulate" an Ajax request from an 
 * Ajax.Request/Updater made beforehand. 
 */
Ajax.Cache = Class.create(Ajax.Base, {
    _complete: false,
    initialize: function($super, request) {
        $super(request.options);
        request._complete = false;
        this.transport = request.transport;
        this.request(request.url);
        return this;
    },
    
    request: function(url) {
        this.url = url;
        this.method = this.options.method;
        var params = Object.clone(this.options.parameters);
        
        try {
            var response = new Ajax.Response(this);

            if (this.options.onCreate) this.options.onCreate(response);
            Ajax.Responders.dispatch('onCreate', this, response);
                        
            if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
            
            this.onStateChange();
        }
        catch (e) {
            this.dispatchException(e);
        }
    }
});

Object.extend(Ajax.Cache.prototype, {
    respondToReadyState : Ajax.Request.prototype.respondToReadyState,
    onStateChange       : Ajax.Request.prototype.onStateChange,
    success             : Ajax.Request.prototype.getStatus,
    getStatus           : Ajax.Request.prototype.getStatus,
    isSameOrigin        : Ajax.Request.prototype.isSameOrigin,
    getHeader           : Ajax.Request.prototype.getHeader,
    evalResponse        : Ajax.Request.prototype.evalResponse,
    dispatchException   : Ajax.Request.prototype.dispatchException
});

/**
 * Ajax.Request Extended
 * Prototype Xtensions http://www.prototypextensions.com/
 * 
 * @desc Just a small change: now Ajax.Request return self scope.
 * It is required by Ajax.Cache
 */
Ajax.Request = Class.create(Ajax.Request, {
    initialize: function($super, url, options) {
        $super(url, options);
        return this;
    }
});

Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

/**
 * Ajax.Updater Extended
 * Prototype Xtensions http://www.prototypextensions.com/
 * 
 * @desc Just a small change: now Ajax.Updater return self scope
 * It is required by Ajax.Cache
 */
Ajax.Updater = Class.create(Ajax.Updater, {
    initialize: function($super, container, url, options) {
        $super(container, url, options);
        return this;
    } 
});



//Register Ajax Responder
(function() {

		var e_responder = {
				onCreate: function(request) {
					if(request.options['updateElement']) {
						request.options.element = request.options.updateElement;
						e107Event.trigger('ajax_update_before', request.options, request.options.updateElement);
					}
					if(request.options['overlayPage']){
						e107Event.trigger('ajax_loading_start', request.options, request.options.overlayPage);
					} else if(request.options['overlayElement']) { 
						e107Event.trigger('ajax_loading_element_start', request.options, request.options.overlayElement);
					}
				},
				
				onComplete: function(request) { 
					/*Ajax.activeRequestCount == 0 && */
					if(request.options['overlayPage']) {
						e107Event.trigger('ajax_loading_end', request.options, request.options.overlayPage);						
					} else if(request.options['overlayElement']) { 
						e107Event.trigger('ajax_loading_element_end', request.options, request.options.overlayElement);
					}
					
					if(request.options['updateElement']) { 
						request.options.element = request.options.updateElement;
						e107Event.trigger('ajax_update_after', request.options, request.options.updateElement); 
					}
				},
				
				onException: function(request, e) {
					//TODO handle exceptions
					alert('e107Ajax Exception: ' + e);
				}
		}
		
		Ajax.Responders.register(e_responder);	
})();

/**
 * e107AjaxAbstract
 */
var e107AjaxAbstract = Class.create ({
	_processResponse: function(transport) {
		if(null !== transport.responseXML) { 
			this._handleXMLResponse(transport.responseXML);
		} else if(null !== transport.responseJSON) {
			this._handleJSONResponse(transport.responseJSON);
		} else {
			this._handleTextResponse(transport.responseText);
		}
		
	},

	_handleXMLResponse: function (response) {
		var xfields = $A(response.getElementsByTagName('e107response')[0].childNodes);
		var parsed = {};  
		xfields.each( function(el) { 
			if (el.nodeType == 1 && el.nodeName == 'e107action' && el.getAttribute('name') && el.childNodes) {
				
				var action = el.getAttribute('name'), items = el.childNodes;
				if(!varsettrue(parsed[action])) {
					parsed[action] = {};
				}
				
				for(var i=0, len=items.length; i<len; i++) {
					var field = items[i];
					
					if(field.nodeType!=1)
						continue;
					
					if(field.getAttribute('name')) {
						var type = field.getAttribute('type'), //not used yet
							name = field.getAttribute('name'), 
							eldata = field.firstChild
							val = eldata ? eldata.data : '';
						if(parsed[action][name] && Object.isArray(parsed[action][name]))
							parsed[action][name].push(val);
						else if(parsed[action][name] && Object.isString(parsed[action][name]))
							parsed[action][name] = [parsed[action][name], val];
						else
							parsed[action][name]= val;
					}
					
				}
			}
		}.bind(this)); 
		this._handleResponse(parsed);
	},
	
	_handleJSONResponse: function (response) {
		this._handleResponse(response);
	},
	
	_handleTextResponse: function (response) {
		this._handleResponse({ 'auto': response} );
	},
	
	_handleResponse: function(parsed) {
		
		Object.keys(parsed).each(function(method) {
			try{
				this['_processResponse' + ('-' + method).camelize()](parsed[method]);
			} catch(e) {
				//
			}
		}.bind(this));

	},
	
	_processResponseAuto: function(response) {
		//find by keys as IDs & update
		Object.keys(response).each(function(key) {
			this._updateElement(key, response[key]);
		}.bind(this));
	},
	
	/**
	 * Reset checked property of form elements by selector name attribute (checkbox, radio)
	 */
	_processResponseResetChecked: function(response) {
		Object.keys(response).each(function(key) { 
			var checked = parseInt(response[key]) ? true : false;
			$$('input[name^=' + key + ']').each( function(felement) {
				var itype = String(felement.type); 
				if(itype && 'checkbox radio'.include(itype.toLowerCase()))
					felement.checked = checked;
			});
		}.bind(this));
	},
	
	/**
	 * Invoke methods/set properties on element or element collections by id
	 * 
	 * Examples:
	 * {'show': 'id1,id2,id3'} -> show elements with id id1,id2 and id3
	 * {'writeAttribute,rel,external': 'id1,id2,id3'} -> invoke writeAttribute('rel', 'external') on elements with id id1,id2 and id3
	 * {'disabled,true': 'button-el,other-button-el'} -> set disabled property of elements with id button-el,other-button-el to true
	 * 
	 */
	_processResponseElementInvokeById: function(response) {
		//response.key is comma separated list representing method -> args to be invoked on every element
		Object.keys(response).each(function(key) {
			var tmp = $A(key.split(',')),
				method = tmp[0],
				args = tmp.slice(1);

			//search for boolean type
			$A(args).each( function(arg, i) {
				switch(arg) {
					case 'false': args[i] = false; break;
					case 'true': args[i] = true; break;
					case 'null': args[i] = null; break;
				}
			});
			//response.value is comma separated element id list
			$A(response[key].split(',')).each( function(el) {
				el = el ? $(el.strip()) : null;
				if(!el) return;

				if(Object.isFunction(el[method]))
					el[method].apply(el, args);
				else if(typeof el[method] !== 'undefined') {
					//XXX - should we allow adding values to undefined yet properties? At this time not allowed
					el[method] = varset(args[0], null);
				}
			});
		});
	},

	/**
	 * Update element by type
	 */
	_updateElement: function(el, data) {
		el = $(el); if(!el) return; 
		var type = el.nodeName.toLowerCase(), itype = el.type; 
        if(type == 'input' || type == 'textarea') {
        	if(itype) itype = itype.toLowerCase();
        	switch (itype) {
        		case 'checkbox':
        		case 'radio': 
        			el.checked = (el.value == data);
        			break;
        		default: 
        			el.value = data.unescapeHTML(); //browsers doesn't unescape entities on JS update, why?!
        			break;
        	}
            
        } else if(type == 'select') {
            if(el.options) {
                var opt = $A(el.options).find( function(op, ind) {
                    return op.value == data; 
                });
                if(opt)
                	el.selectedIndex = opt.index;
            }
        } else if(type == 'img') {
        	el.writeAttribute('src', data).show(); //show if hidden
        }else if(el.nodeType == 1) {
        	el.update(data);
        }
	}
});

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

/**
 * e107Ajax.Request
 * Prototype Xtensions http://www.prototypextensions.com/
 * 
 * @desc @desc e107Ajax.Update wrapper, used to execute an Ajax.Request by integrating 
 * the management of browsing history
 */
e107Ajax.Request = Class.create({
    initialize: function(url, options) {
    
        this.options = {};
        Object.extend(this.options, options || {});
        if(!this.options['parameters'])
        	this.options['parameters'] = { 'ajax_used': 1 }
        else if(!this.options.parameters['ajax_used'])
        	this.options['parameters']['ajax_used'] = 1;
         
        // only if required
        if(this.options.history) {
            var tmpOpt = Object.clone(e107Ajax.ObjectMap);
            Object.extend(tmpOpt, this.options.history);  
            this.options.history = tmpOpt;
            this.options.history.__url = url;
            
            // History id
            if(Object.isUndefined(options.history.id))
                throw('e107Ajax.Request error : you must define historyId');
                
            var id = this.options.history.id;
            
            // Enable history observer
            var version = e107Ajax.History.observe('Request', id, url, this.options);
            
            // Set current version value for container
            e107History.set(id, version);
            
        } else {
            return new Ajax.Request(url, this.options);
        }
    }
});

/**
 * e107Ajax.Updater
 * 
 * @desc e107Ajax.Updater wrapper, used to execute an Ajax.Updater by integrating 
 * the management of browsing history
 */
e107Ajax.Updater = Class.create({
    initialize: function(container, url, options) {
        
        this.options = {};
        
        Object.extend(this.options, options || {});
        if(!this.options['parameters'])
        	this.options['parameters'] = { 'ajax_used': 1 }
        else if(!this.options.parameters['ajax_used'])
        	this.options['parameters']['ajax_used'] = 1;
		
		//required for ajax_update event trigger
		this.options.updateElement = container;
		
        // only if required
        if(this.options.history) {
            var tmpOpt = Object.clone(e107Ajax.ObjectMap);
            Object.extend(tmpOpt, this.options.history);  
            this.options.history = tmpOpt;
            this.options.history.__url = url;
                
            // History id
            if(Object.isUndefined(options.history.id)) {
                var id = (Object.isString(container)) ? container : container.identify();
                this.options.history.id = id;
            } else {
                var id = this.options.history.id;
            }
            // Add container to this.options
            this.options.container = container;

            // Enable history observer
            var version = e107Ajax.History.observe('Updater', id, url, this.options);
            
            // Set current version value for container
            e107History.set(id, version);
            
        } else { 
            return new Ajax.Updater(container, url, this.options);
        }
    }
});

Object.extend(e107Ajax, {
	
	/**
	 * Ajax Submit Form method
	 * 
	 * @descr e107 analog to Prototpye native Form.request method
	 */
	submitForm: function(form, container, options, handler) {
		var parm = $(form).serialize(true),
			opt = Object.clone(options || {}),
			url = !handler ? $(form).readAttribute('action') : String(handler).parsePath();
			
		if(!opt.parameters) opt.parameters = {};
		Object.extend(opt.parameters, parm || {});
		opt.method = 'post';
		
		if ($(form).hasAttribute('method') && !opt.method)
		      opt.method = $(form).method;
		
		if(container)
			return new e107Ajax.Updater(container, url, opt);
		
		return new e107Ajax.Request(url, opt);
	},
	
	/**
	 * Ajax Submit Form method and auto-replace SC method
	 */
	submitFormSC: function(form, sc, scfile, container) {
		var handler = ('#{e_FILE}e_ajax.php'), parm = { 'ajax_sc': sc, 'ajax_scfile': scfile };
		return this.submitForm(form, varsettrue(container, sc), { parameters: parm, overlayElement: varsettrue(container, sc) }, handler);
	},
	
	toggleUpdate: function(toggle, container, url, cacheid, options) {
		container = $(container);
		toggle = $(toggle);
		opt = Object.clone(options || {});
		opt.method = 'post';
		
		if(!toggle) return;
		
		if(!toggle.visible())
		{
			
			if(cacheid && $(cacheid)) return toggle.fxToggle();
			
			opt.onComplete = function() { toggle.fxToggle() };
			if(url.startsWith('sc:')) 
			{
				return e107Ajax.scUpdate(url.substring(3), container, opt);
			} 
			return new e107Ajax.Updater(container, url, opt);
		}
		
		return toggle.fxToggle();
	},
	
	scUpdate: function(sc, container, options) {
		var handler = ('#{e_FILE}e_ajax.php').parsePath(), parm = { 'ajax_sc': sc };
		opt = Object.clone(options || {});
		opt.method = 'post';
		if(!opt.parameters) opt.parameters = {};
		Object.extend(opt.parameters, parm || {});
		return new e107Ajax.Updater(container, handler, opt);
	}
});

/**
 * e107Ajax.fillForm
 * 
 * @desc 
 */
e107Ajax.fillForm = Class.create(e107AjaxAbstract, {
	
	initialize: function(form, overlay_dest, options) {
		//TODO - options
		this.options = Object.extend({
			start: true
		}, options || {});
		
		this.form = $(form);
		if(!this.form) return;
		
		if(this.options['start'])
			this.start(overlay_dest);
	},
	
	start: function(overlay_dest) {		
		e107Event.trigger("ajax_fillForm_start", {form: this.form});
		var destEl = $(overlay_dest) || false; 
		var C = this;
		
		//Ajax history is NOT supported (and shouldn't be)
		var options = {
			overlayPage: destEl,
			
			history: false,
			
			onSuccess: function(transport) {
				try {
					this._processResponse(transport);
				} catch(e) {
					var err_obj = { message: 'Callback Error!', extended: e, code: -1 }
					e107Event.trigger("ajax_fillForm_error", {form: this.form, error: err_obj});
				}
			}.bind(C),
			
			onFailure: function(transport) { 
				//We don't use transport.statusText only because of Safari!!!
				var err = transport.getHeader('e107ErrorMessage') || ''; 
				//TODO - move error messages to the ajax responder object, convert it to an 'error' object (message, extended, code)
				//Add Ajax option e.g. printErrors (true|false)
				var err_obj = { message: err, extended: transport.responseText, code: transport.status }
				e107Event.trigger("ajax_fillForm_error", {form: this.form, error: err_obj });
			}.bind(C)
		}
		Object.extend(options, this.options.request || {}); //update - allow passing request options
		
		this.form.submitForm(null, options, this.options.handler);
	},
	
	_processResponseFillForm: function(response) {
		if(!response || !this.form) return; 
		var C = this, left_response = Object.clone(response); 
		this.form.getElements().each(function(el) { 
			var elid = el.identify(), elname = el.readAttribute('name'), data, elnameid = String(elname).gsub(/[\[\]\_]/, '-');

			if(isset(response[elname])) {
				data = response[elname]; 
				if(left_response[elname]) delete left_response[elname];
			} else if(isset(response[elnameid])) {
				data = response[elnameid]; 
				if(left_response[elnameid]) delete left_response[elnameid];
			} else if(isset(response[elid])) {
				data = response[elid]; 
				if(left_response[elid]) delete left_response[elid];
			} else {
				return;
			}
            this._updateElement(el, data);
		}.bind(C));
		
		if(left_response) { //update non-form elements (by id)
			Object.keys(left_response).each( function(el) {
				this._updateElement(el, left_response[el]);
			}.bind(C));
		}

		e107Event.trigger("ajax_fillForm_success", {form: this.form});
	}

});

Element.addMethods('FORM', {
	
	submitForm: e107Ajax.submitForm.bind(e107Ajax),
	
	submitFormSC: e107Ajax.submitFormSC.bind(e107Ajax),
	
	fillForm: function(form, overlay_element, options) {
		new e107Ajax.fillForm(form, overlay_element, options);
	}
});

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

//DEPRECATED!!! Use e107Ajax.submitFormSC() || form.submitFormSC() instead
function replaceSC(sc, form, container, scfile) {
		$(form).submitFormSC(sc, scfile, container);
}

//DEPRECATED!!! Use e107Ajax.submitForm() || form.submitForm() instead
function sendInfo(handler, container, form) {
	$(form).submitForm(container, null, handler);
}

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

/*
 * Core Auto-load
 */
$w('autoExternalLinks autoNoHistory autoHide toggleObserver scrollToObserver').each( function(f) {
	e107.runOnLoad(e107Helper[f], null, true); 
});
