/* GWE.Utilities, version 0.1
 * (c) 2008 Leif Högberg
 *
 * Misc utilities and helpers used in other GWE classes
 *
 * Gomitech Web Extensions (GWE)
 * GWE is freely distributable under the terms of the MIT license.
 * For details, see the GWE web site: http://www.gomitech.com/projects/gwe/
 */
/* PROTOTYPE EXTENSIONS */
Object.extend = function(destination, source, recursive) {    
    if (!destination) destination = (source instanceof Array) ? [] : {};
    
    for (var property in source)
        destination[property] = (recursive !== true || typeof(source[property]) != "object" || source instanceof Array) ? source[property] : Object.extend(destination[property], source[property], recursive);
    return destination;
};

Object.extend(String.prototype, {
    
    explode: function(delimiter, limit) {

        if (!limit)
        {
            return this.split(delimiter);
        }
        else
        {
            var tmp = this.split(delimiter);
            var partA = tmp.splice(0, limit - 1);
            var partB = tmp.join(delimiter);
            partA.push(partB);
            return partA;
        }

    },
    
    /**
     * Will return the part of the string that is located after needle.
     * If includeNeedle is true the needle will be included in the result.
     * @param String needle
     * @param Bool includeNeedle (false)
     * @return String
     */
    extractFromLast: function(needle, includeNeedle) {
        var pos = this.rpos(needle);
        if (includeNeedle == undefined)
        {
            pos += 1;
        }
        return this.substr(pos);
    },

    extractTo: function(needle, includeNeedle) {
        
        var pos = '';
        
        if (needle instanceof Array) {
            while(n = needle.shift()) {
                pos = this.pos(n);
                if (pos) {
                    needle = n;
                    break;
                }
            }            
        }
        else
        {
            pos = this.pos(needle);
        }
        
        if (includeNeedle) {
            pos += needle.length;
        }
        
        return this.substr(0, pos);        
    },
    
    extractParam: function(param, defaultValue) {
        var v = this.split('/');
        if(param != '')
        {
            for(var i=0; i < v.length; i++)
            {
                if(v[i] == param) { return (v[i+1]); }
            }
        }
        return defaultValue
    },

    replaceParam: function(param, value) {
        if(param != '')
        {
            var v = this.split('/');
            
            for(var i=0; i < v.length; i++)
            {
                if(v[i] == param) { 
                    v[i+1] = value;
                    break;
                }
            }
            return v.join('/');
        }
    },
    
    isNumeric: function() {
        return !isNaN(Number(this));
    },

    pos: function(needle, offset) {
        var i = (this+'').indexOf( needle, offset );
        return i===-1 ? false : i;
    },

    rpos: function(needle, offset) {
        // http://kevin.vanzonneveld.net
        // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // +   bugfixed by: Onno Marsman
        // *     example 1: strrpos('Kevin van Zonneveld', 'e');
        // *     returns 1: 16

        var i = (this+'').lastIndexOf( needle, offset );
        return i >= 0 ? i : false;
    },

    substr: function(f_start, f_length ) {
        
        // http://kevin.vanzonneveld.net
        // +     original by: Martijn Wieringa
        // +     bugfixed by: T.Wild
        // +      tweaked by: Onno Marsman
        // *       example 1: substr('abcdef', 0, -1);
        // *       returns 1: 'abcde'
        // *       example 2: substr(2, 0, -6);
        // *       returns 2: ''

        if(f_start < 0) {
            f_start += this.length;
        }

        if(f_length == undefined) {
            f_length = this.length;
        } else if(f_length < 0){
            f_length += this.length;
        } else {
            f_length += f_start;
        }

        if(f_length < f_start) {
            f_length = f_start;
        }

        return this.substring(f_start, f_length);
    },

    trim: function() {
        return this.replace(/^\s+|\s+$/g, '');  
    },
    
    nl2br: function() {
        return this.replace(/([^>]?)\n/g, '$1<br />\n');
    }
});

//Element extensions...
var extMethods = {
    
    applyStyles: function(element, styles) {
        
        element = $(element);
        if (element && styles) {
            var tmp = {};
            Object.keys(styles).each(function(key){
                tmp[key.camelize()] = styles[key];
            }.bind(this));
            element.setStyle(tmp);
        }
        
        return element;
        
    },

    firstAnscestorOfTag: function(element, tag) {
        parents = $(element).ancestors();

        for (i=0,c=parents.length;i<c;++i)
        {
            if (parents[i].tagName.toLowerCase() == 'tr')
            {
                return parents[i];
            }
        }
        return null;
    },

    getText: function(obj) {
    // return the data of obj if its a text node
        if (obj.nodeType == 3) return obj.nodeValue;
        var txt = new Array(),i=0;
        // loop over the children of obj and recursively pass them back to this function
       while(obj.childNodes[i]) {
            txt[txt.length] = Element.getText(obj.childNodes[i]);
            i++;
        }
        // return the array as a string
        return txt.join("");
    },
    
    truncate: function(element, length, reversed) {
        var children = element.childElements();
        var c = children.length;
        if (length >= c) { return; }
        
        if (reversed)
        {
            for(var i=(c - length - 1); i >= 0; i--)
            {
                children[i].remove();
            }
        }
        else
        {
            for(var i=length; i < c; i++)
            {
                children[i].remove();
            }            
        }
        
    }

}
Element.addMethods(extMethods);

/* GWE.UTILITIES */
if(typeof(GWE) == "undefined")
    GWE = {};
GWE.Utilities = Class.create();
Object.extend(GWE.Utilities,{
    
    loadFile: function(filename, callback) {
        
        if (GWE.Utilities.loadedFiles == undefined) {
            
            GWE.Utilities.loadedFiles = {};
            
            $$('script').each(function(script){
                if (script.src != undefined && script.src != '')
                {
                    GWE.Utilities.loadedFiles[script.src] = true;
                }
            });
        }
        
        // If the file has been loaded already then trigger the callback without loading the file.
        if (GWE.Utilities.loadedFiles[filename] != undefined) {
            callback();
            return;
        }
        
        var script = document.createElement('script');
        script.src = filename;

        // IE uses onreadystatechange, most everything else triggers the onload. Opera triggers on everything it seems.
        script.onload = script.onreadystatechange = function () {
            if ( script.readyState && (script.readyState != 'complete' && script.readyState != 'loaded') ) { return }

            if (GWE.Utilities.loadedFiles[filename] == undefined) {
                GWE.Utilities.loadedFiles[filename] = true;
                callback();
            }
        }
        
        document.getElementsByTagName('head')[0].appendChild(script);

    },
    uniqueCounter: 0,
    uniqueId: function() {
        do { id = 'gwe_unique_' + GWE.Utilities.uniqueCounter++ } while ($(id));
        return id;
    }
    
});

/**
 * Adds Event capabilities to classes that inherit GWE.Event.Base
 * @todo Should we inject GWE.Event.observe into Prototypes Event.observe? This would allow us to write "Event.observe(gweWindow, 'afterOpen', handler);"
 */
GWE.Event = {};
GWE.Event.Break = Class.create({
    initialize: function(value) {
        this.value = value;
    }
});
GWE.Event.Base = Class.create({
    
    initialize: function() {
        this._observers = {};
    },
    
    // protected  
    setupEvent: function(name) {
        if (!this._observers[name]) {
            this._observers[name] = [];
        }
    },
    
    raiseEvent: function(name, args) {
        this.setupEvent(name);
        var results = [];
        if (!args) {args = null}
        
        try {
            var l = this._observers[name].length;
            for(var i=0; i<l;i++) {
                results.push( this._observers[name][i](args) || null );
            }
        } catch(e){
            if(e instanceof GWE.Event.Break)
                results.push(e.value); // This might be a bad idea.. perhaps we should return false here after all.
        }
        
        return results;
    },

    //public

    observe: function(name, handler) {
        this.setupEvent(name);
        this._observers[name].push(handler);
    },

    stopObserving: function(name, handler) {

        if (!name) {
            this._observers = {};
        }

        if (!this._observers[name]) {
            return;
        }

        if (handler) {
            var l = this._observers[name].length;
            for(var i=0; i<l;i++) {
                if (handler == this._observers[name][i]) {
                    this._observers[name] = this._observers[name].without(this._observers[name][i]);
                    break;
                }
            }
        }
        else {
            this._observers[name] = [];
        }
    }

});

/**
 * @todo add option to reuse one iframe instead of creating new ones.
 */
GWE.FileUploader = Class.create(GWE.Event.Base, {
    
    initialize: function($super, formId, options) {
        $super();
        
        this.options = {
            evaluator: function(response) {
                response = response.body.innerHTML.trim();
                var transport = {result: false, text: response};
                if (response == 1) {
                    transport.result = true;
                }
                return transport;
            }
        };
        Object.extend(this.options, options || {});
        
        this.form = $(formId) || false;
        if (!this.form) {
            alert('Invalid form');
            return;
        }
        
        this.form.observe('submit', this.submitForm.bind(this));
        
        if (this.options.evaluator) {
            this.evaluator = this.options.evaluator;
        }
        
    },
    
    isEvaluator: function(mixed) {
        if(typeof( mixed ) == 'object' && !(mixed instanceof Array || mixed == null)) {
            if (typeof(mixed.eval) == 'function') {
                return true;
            }
        }
        return false;
    },
    
    evaluateResponse: function(response) {
        
        var transport = false;
        if (Object.isFunction(this.evaluator))
        {
            transport = this.evaluator(response);
        }
        else if (this.isEvaluator(this.evaluator)) {            
            transport = this.evaluator.eval(response);
        }
        else {
            alert('Error: Invalid response evaluator!');
            return; /*todo throw exception instead*/
        }
        
        if (transport.result) {
            if (typeof(this.options.onSuccess) == 'function') {
                this.options.onSuccess(transport);
            }
        }
        else {
            if (typeof(this.options.onFailure) == 'function') {
                this.options.onFailure(transport);
            }
        }

    },
    
    onFrameLoaded: function() {
        
        var response = '';
        var frame = $(this.frameId);
        if (frame.contentDocument) {
            response = frame.contentDocument;
        } 
        else if (frame.contentWindow) {
            response = frame.contentWindow.document;
        }
        else {
            response = window.frames[this.frameId].document;
        }
        
        if (response.location.href == 'about:blank') {return;}
        
        //frame.stopObserving('load'); //funkar inte i FF
        //frame.remove(); 
        
        this.evaluateResponse(response);
    },
    
    setup: function() {

        this.frameId = GWE.Utilities.uniqueId();
        var frame = new Element('iframe', { id: this.frameId, name: this.frameId, src: 'about:blank' }).hide();        
        
        $(document.body).insert(frame);
        Event.observe(this.frameId, 'load', this.onFrameLoaded.bind(this));
        this.form.setAttribute('target', this.frameId);
        
    },
    
    submitForm: function(e) {
//        Event.stop(e);
        this.setup();
        
    }
    
});

/*
 * @todo GWE.Window.create('messageBox', {position:'centered', modal: true} );
 * @todo load content through ajax (ie. content is an url)
 * @todo load content in to an iframe instead of a div. Can come in handy.
 */
GWE.Window = Class.create(GWE.Event.Base, {
    
    initialize: function($super, id, content, options) {
        $super();
        
        if (!options && !Object.isElement(content) && !Object.isString(content)) {
            options = content;
            content = false;
        }

        this.id = GWE.Window.idPrefix + id;
        GWE.Window.collection[this.id] = this;
        
        this.state = GWE.Window.notReady;
        this.options = {

            containerClassName: false,
            fade: false,
            fadeDuration: 0.75,
            overlayClassName: false,
            overlayOpacity: 0.75,
            overlayStyle: false,
            overlayCloseOnClick: false,
            position: 'center',
            style: false,
            zIndex: 10000
            
        };
        Object.extend(this.options, options || {});
        
        if (this.options.iframe) {
            this.container = new Element('iframe', { id : this.id, src: content, 'style' : 'display:none;' } );
        } else {
            this.container = new Element('div', { id : this.id, 'style' : 'display:none;' } );
            if (content) {
                this.container.insert(content);
            }
        }
        
        if (this.options.containerClassName) {
            this.container.addClassName(this.options.containerClassName);
        }
        if (this.options.style) {
            this.container.applyStyles(this.options.style);
        }
        
    },
    
    activateOverlay: function() {
        
        var overlay = new Element('div', { 'id' : this.id+'Overlay', 'style' : 'display:none' });
        this.overlay = overlay;
        
        if (this.options.overlayClassName) {
            overlay.addClassName(this.options.overlayClassName);
        }
        
        this.viewPort.insert(overlay);
        
        if (this.viewPort == document.body) {
            overlay.setStyle( { position: 'fixed', top:0, left: 0, height: '100%', width: '100%', zIndex: this.options.zIndex } );
        }
        else {
            overlay.setStyle( { position: 'absolute', zIndex: this.options.zIndex } );
            overlay.clonePosition(this.viewPort);
            Event.observe(window, 'resize', function() {
                overlay.clonePosition(this.viewPort);
            }.bind(this));
        }

        if (this.options.overlayCloseOnClick) {
            this.overlay.observe('click', function(e){GWE.Window.close(this.id)}.bind(this) );
        }
                
        if (this.options.fade) {
            overlay.appear({
                queue: {
                    position: 'front',
                    scope: 'GWE.Window'
                },
                to: this.options.overlayOpacity,
                duration: this.options.fadeDuration
            });
        }
        else {
            overlay.show();
        }
  
    },
    
    afterClose: function() {
        this.state = GWE.Window.states.closed;
        if (GWE.Window.collection[this.id]) {
            delete GWE.Window.collection[this.id];
        }
        this.raiseEvent('afterClose');
    },
    
    afterOpen: function() {
        this.state = GWE.Window.states.opened;
        this.raiseEvent('afterOpen');
    },
        
    close: function() {
        
        if (this.options.fade) {
            this.container.fade({
                queue: {
                    position: 'front',
                    scope: 'GWE.Window'
                },
                from: 1,
                to: 0,
                duration: this.options.fadeDuration,
                afterFinish: function() {
                    
                    this.container.remove();
                    if (this.overlay) {
                        this.closeOverlay();
                    }
                    this.afterClose();
                    
                }.bind(this)
            });
        }
        else {
            this.container.remove();
            if (this.overlay) {
                this.closeOverlay();
            }
            this.afterClose();
        }
    },
    
    closeOverlay: function() {
        
        if (this.overlay) {
            if (this.options.fade) {
                this.overlay.fade({
                    queue: {
                        position: 'end',
                        scope: 'GWE.Window'
                    },
                    from: this.options.overlayOpacity,
                    to: 0,
                    duration: this.options.fadeDuration
                });
            }
            else {
                this.overlay.remove();
            }
        }
                
    },

    getElement: function() {
        return this.container;
    },
    
    getState: function() {
        return this.state;
    },
    
    insert: function(mixed){
        this.container.insert(mixed);
        return this;
    },

    open: function() {

        if (this.parent && this.parent.getState() != GWE.Window.states.opened) {
            this.foobar = function() {
                this.parent.stopObserving('afterOpen', this.foobar);
                this.open();
            }.bind(this);
            this.parent.observe('afterOpen', this.foobar);
            return;
        }
        
        if (!this.viewPort) {
            this.viewPort = $(document.body);
        }

        if (this.options.modal) {
            this.activateOverlay();
        }
        
        this.viewPort.insert(this.container);
        this.positionContainer();

        if (this.options.fade) {
            this.isFading = true;
            this.container.appear({
                afterFinish: this.afterOpen.bind(this),
                queue: {
                    position: 'end',
                    scope: 'GWE.Window'
                },
                to: 1,
                duration: this.options.fadeDuration
            });
        }
        else {
          this.container.show();
          this.afterOpen();
        }
        
    },

    positionContainer: function() {
        
        if (this.options.position == 'center')
        {
            var renderAsFixed = (this.options.fixed && !this.parent ? true : (this.options.modal && !this.parent ? true : false) );
            if (!this.container.getStyle('position') == 'absolute') {
                this.container.absolutize();
            }

            if (this.options.height || this.options.width) {
                var tmp = {};
                if (this.options.height) { tmp.height = this.options.height; }
                if (this.options.width)  { tmp.width  = this.options.width;  }
                
                this.container.setStyle(tmp);
            }
            containerDimensions = this.container.getDimensions();

            var viewport_dimensions = null;
            if (this.viewPort == document.body) {
                
                viewport_dimensions = document.viewport.getDimensions();
                var pos = { x: 0, y: 0 };
                if (!renderAsFixed) {
                    pos.x = window.pageXOffset
                                || document.documentElement.scrollLeft
                                || document.body.scrollLeft
                                || 0;
                                
                    pos.y = window.pageYOffset
                                || document.documentElement.scrollTop
                                || document.body.scrollTop
                                || 0;
                }
                
                if (viewport_dimensions.height < containerDimensions.height || viewport_dimensions.width < containerDimensions.width) {
                    renderAsFixed = false;
                }

                var offset_top =  (pos.y  + ((viewport_dimensions.height > containerDimensions.height) ? Math.floor((viewport_dimensions.height - containerDimensions.height) / 2) : 0));
                var offset_left = (pos.x  + ((viewport_dimensions.width > containerDimensions.width)   ? Math.floor((viewport_dimensions.width  - containerDimensions.width)  / 2) : 0));
                
            }
            else {

                viewport_dimensions = this.viewPort.getDimensions();
                var offset_top = Math.floor((viewport_dimensions.height - containerDimensions.height) / 2);
                var offset_left = Math.floor((viewport_dimensions.width - containerDimensions.width) / 2);
                
                if (offset_top < 0 || offset_left < 0) {
                    var docDimensions = document.viewport.getDimensions();
                    if (containerDimensions.width > docDimensions.width) {
                        offset_left = -parseFloat(this.viewPort.style.left  || 0);
                    }
                    if (containerDimensions.height > docDimensions.height) {
                        offset_top = -parseFloat(this.viewPort.style.top  || 0);
                    }
                }
                
            }
            
            var top = (offset_top ? offset_top : 0);
            var left = (offset_left ? offset_left : 0);

            this.container.setStyle({
                'position' : ( (renderAsFixed) ? 'fixed' : 'absolute'),
                'zIndex' : this.options.zIndex+1,
                top: top + 'px',
                left: left + 'px'
            });
            
            if (renderAsFixed) {
                Event.observe(window, 'resize', this.positionContainer.bindAsEventListener(this));
            }
            
        }
    },

    setParent: function(parent) {
        if (parent instanceof GWE.Window) {
            this.parent = parent;
            this.viewPort = parent.getElement();            
        }
        else {
            this.setViewPort(parent);
        }
    },

    setViewPort: function(element) {
        this.viewPort = element;
    }

});
Object.extend(GWE.Window,{
    
    collection: {},
    idPrefix: 'gwe_window_',
    states: {
        closed: 0,
        notReady: 1,
        ready: 2,
        opened: 3
    },
    
    close: function(id) {
        if ((id+'').pos(GWE.Window.idPrefix) === false) {
            id = GWE.Window.idPrefix + id;
        }

        if (GWE.Window.collection[id])
        {
            GWE.Window.collection[id].close();
        }
    }
    
});
