/*  Prototype JavaScript framework, version 1.5.0
 *  (c) 2005-2007 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 *
 *  Extended with some usefull classes (waitor,MapHandler,Progressbar)
 *  (c) 2005-2007 Tony Chemit
/*--------------------------------------------------------------------------*/

Prototype.profile = true;

var isIE = navigator.appVersion.match(/\bMSIE\b/);

if (!("console" in window) || !("firebug" in console)) {
    var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
            "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
    window.console = {};
    names.each(function(name) {
        window.console[name] = function(msg) {
            if (name == 'error' || name == 'warn') alert(name + " -- " + msg);
        }
    });
}

if (Prototype.profile) {
    console._timeMap = $H();
    console.time2 = function(key) {
        console._timeMap[key] = new Date().getTime();
        //console['time'](key);
    };
    console.timeEnd2 = function(key) {
        var first = this._timeMap[key];
        if (first) {
            console._timeMap.remove(key);            
            //console.debug(key+' : '+time+'ms');
            return new Date().getTime() - first;
        }
        //console['timeEnd'](key);
        return -1;
    };

} else {
    console.time2 = console.timeEnd2 = Prototype.emptyFunction;
}


var DOMUtil = {

    getDimension : function(margin) {
        margin = margin || 0;
        var w = this['innerWidth'] || this['document'].body.offsetWidth;
        var h = this['innerHeight'] || this['document'].body.offsetHeight;
        return [w - margin,h - margin];
    },

    createImage : function(root, src, alt, dimension, basicDimension) {
        //document.cookie='auth=bm9pcmVsdW1pZXJlOmx1bWllcmVub2lyZQ==';
        var requiredDimension = undefined;
        if (dimension) {
            if (basicDimension) {
                //var scale = 0;
                // found optimized scaling
                var fx = parseFloat(dimension[0]) / parseFloat(basicDimension[0]);
                var fy = parseFloat(dimension[1]) / parseFloat(basicDimension[1]);
                requiredDimension = fx < fy ? [dimension[0],parseInt(basicDimension[1] * fx)] : [parseInt(basicDimension[0] * fy),dimension[1]];
            }
            else requiredDimension = dimension;
        }

        var image = requiredDimension ? new this['Image'](requiredDimension[0], requiredDimension[1]) : new this['Image']();
        image.src = src == '' ? 'css/transparent.gif' : src;
        image.alt = alt;
        image.title = alt;
        console.info('createImage0 ' + image.src + '\ndimension (original/found/max,real) : (' + basicDimension + ' / ' + requiredDimension + ' / ' + dimension + '/' + [image.width,image.height] + ')');
        //alert('createImage0 ' + image.src + '::' + alt);
        if (root) root.appendChild(image);
        return image;
    }
};

var Waitor = Class.create();

Object.extend(Waitor.prototype, {
    initialize:function(delay, _methods) {
        this.delay = delay;
        this.condition = _methods.condition.bind(this);
        this.action = _methods.action ? _methods.action.bind(this) : Prototype.emptyFunction;
        this.init = _methods.init ? _methods.init.bind(this) : Prototype.emptyFunction;
        this.callback = _methods.callback.bind(this);
        this.f = function() {
            this.count++;
            if (this.condition()) {
                this.action();
            }
            else {
                this.quit();
            }
        }.bind(this);
    },

    launch:function(condition) {
        if (condition) this.condition = condition.bind(this);
        this.count = 0;
        this.init();
        this.id = setInterval(this.f, this.delay);
        console.time2('waitor' + this.id);
        console.info('launch: ' + this.inspect());
    },
    quit:function() {
        var t1 = console.timeEnd2('waitor' + this.id);
        console.info('quit: ' + this.inspect() + '  total time: ' + t1 + 'ms');
        clearInterval(this.id);
        this.callback();
    },
    inspect:function() {
        return '<#Waitor id:' + this.id + ', delay:' + this.delay + ', count:' + this.count + '>';
    }
});


/*var ImageHighLighter = Class.create();

Object.extend(ImageHighLighter.prototype, {

    initialize:function(win, element, hotSrc, onclick) {
        element = element || $(element);
        element.hotSrc = hotSrc;
        element.normalSrc = element.src;
        //console.info('register a new ImageHighlighter for ' + element.id + '(' + element.normalSrc + ',' + element.hotSrc + ')');
        Element.addClassName(element, 'noborder');
        Element.addClassName(element, 'cursorSelect');
        element.onmouseover = function() {
            if (this.src != this.normalSrc) return;
            with (win) this.src = this.hotSrc;
            this.hotSrc = this.src;
            //console.info('change src form ' + this.src)
        }.bind(element);
        element.onmouseout = function() {
            if (this.src != this.hotSrc) return;
            with (win) this.src = this.normalSrc;
            //console.info('change src form ' + this.src)
        }.bind(element);
        if (onclick) element.onclick = onclick;
    }

});*/

var MapHandler = Class.create();

Object.extend(MapHandler.prototype, {
    currentWidget:undefined,
    currentCoords:undefined,
    inMark : false,
    border:undefined,
    pos:undefined,

    initialize:function(options) {
        this.getContainer = options.getContainer || function() {
            return window;
        };
        if (options.callback) {
            this.getCallback = options.callback;
        } else if (options.linkDispatch) {
            //noinspection JSUnusedLocalSymbols
            this.getCallback = function(id, link) {
                return 'javascript:' + link.dispatch;
            };
        } else {
            this.getCallback = function(id) {
                return 'javascript:window.top.onDisplay("' + id + '")';
            };
        }

        this._borderName = options.borderName || 'border';
        this._mapName = options.mapName || 'mapImage';
        this._cssMark = options.cssMark || 'mark';
        this._cssClean = options.cssClean || 'clean';
        this.load = options.load ? options.load.bind(this) : this.loadMap;
    },

    getPos:function() {
        if (this.pos) return this.pos;
        return this.pos = this.getContainer().getImagePos();
    },

    getCoords:function(widget) {
        var pos = this.getPos();
        var coords = widget.coords.split(',');

        // width
        coords[2] = parseInt(coords[2]) - coords[0];
        // height
        coords[3] = parseInt(coords[3]) - coords[1];
        // left
        coords[0] = parseInt(coords[0]) + pos[0];
        // top
        coords[1] = parseInt(coords[1]) + pos[1];
        return coords;
    },

    loadMap:function(image, links, ratio) {
        //console.info('load map ['+image.src+'] ['+ratio+']');
        this.currentCoords = this.border = this.currentWidget = this.pos = undefined;
        this.inMark = false;

        this._init = false;

        with (this.getContainer()) {

            if (!this.border) {
                this.border = $(this._borderName);
                if (!this.border) {
                    // create border div
                    this.border = document.createElement('div');
                    this.border.id = this._borderName;
                    Element.setStyle(this.border, {position:'absolute'});
                    this.border.className = 'clean';
                    window.document.body.appendChild(this.border);
                }
                if (!isIE) this.border.onclick = function() {
                    this.currentWidget.onclick();
                }.bind(this);
            }
            Element.hide(this.border);

            var map = document.createElement('map');
            map.name = this._mapName;
            map.id = this._mapName;
            var max = 600 * ratio;

            links.each(function(link, index) {

                var node = document.createElement('area');
                node.tabIndex = index + 1;
                node.name = link.name;
                node.title = link.alt || link.name;
                node.alt = link.alt || link.name;
                node.shape = 'rect';
                node.href = this.getCallback(link.id, link);
                var coords = link.coords;
                var coor = '';
                $A(coords.split(',')).each(function(i) {
                    var val = parseInt(parseInt(i) * ratio);
                    if (val > max - 1) val = max - 4;
                    coor += ',' + val;
                });
                if (!isIE) node.onclick = function() {
                    eval(node.href);
                }
                node.coords = coor.substring(1);
                node._mapHandler = this;
                node.onmouseover = this.mark.bindAsEventListener(node);
                node.onfocus = this.focus.bindAsEventListener(node);

                map.appendChild(node);
            }.bind(this));

            document.body.appendChild(map);
        }

        image.useMap = '#' + this._mapName;
        image._mapHandler = this;
        image.onmouseover = this.clean.bindAsEventListener(image);
    },

    markFirst:function() {
        var map;
        with (this.getContainer()) map = $(this._mapName);
        var node = map.childNodes[0];
        if (isIE) node.focus();
        else node.onfocus();
    },

    focus:function(e) {
        this._focus = true;
        //console.info('focus area ' + this.tabIndex);
        this['onmouseover'](e);
    },

    mark:function () {
        try {
            //console.info('mark area ' + this.tabIndex);
            var handler = this['_mapHandler'];
            if (!handler._init) return;
            var _border = handler.border;
            with (handler.getContainer()) {
                if (handler.inMark && this == handler.currentWidget && Element.visible(_border)) return;
                handler.currentWidget = this;
                handler.currentCoords = handler.getCoords(this);
                _border.style.left = handler.currentCoords[0] + 'px';
                _border.style.top = handler.currentCoords[1] + 'px';
                _border.style.width = handler.currentCoords[2] + (isIE ? 4 : 0) + 'px';
                _border.style.height = handler.currentCoords[3] + (isIE ? 4 : 0) + 'px';
                if (!isIE) _border.title = handler.currentWidget.alt;
                Element.show(_border);
                _border.className = handler._cssMark;
                handler.inMark = true;
            }
            if (!this._focus) {
                if (isIE) this.focus(); else this['onfocus']();
            }
            this._focus = false;
        } catch(ex) {
            console.info("ERROR on mark " + $H(ex).inspect());
        }
    },

    clean:function (e) {
        try {
            var handler = this['_mapHandler'];
            if (!handler._init) return;
            e = e || handler.getContainer().event;
            with (handler.getContainer()) {
                var _border = handler.border;
                if (_border && !Element.visible(_border)) return;
                if (handler.inMark && isIE) {
                    handler.inMark = false;
                    return false;
                }
                if (Position.withinIncludingScrolloffsets(this, e.clientX, e.clientY)) return;
                _border.className = handler._cssClean;
                Element.hide(_border);
                handler.inMark = false;
                handler.currentWidget = undefined;
            }
        } catch(ex) {
            console.info("ERROR on clean " + $H(ex).inspect());
        }
    }
});

var FramesHanlder = Class.create();

FramesHanlder.functions = {};

Object.extend(FramesHanlder.prototype, {

    initialize:function(win, mappingFrame, mappingFunction) {
        this.win = win;
        this.mappingFrame = mappingFrame;
        this.mappingFunction = mappingFunction;
        //TODO Should we used bind ? or direct inter-frame call ?
        this.frameUtil = {
            getFrameset : function() {
                return this['document'].getElementsByTagName('frameset')[0];
            }.bind(win),
            getFrame1 : function() {
                //TODO use mappingFrame instead to make it more dynamic
                //noinspection JSUnresolvedVariable
                return this.frames[0];
            }.bind(win),
            getFrame2 : function() {
                return this;
            }.bind(win),
            getContext:function() {
                return this['context'];
            }.bind(win)
        };
        Object.extend(this.win, this.frameUtil);
        //this.frameUtil.AlbumManager = AlbumManager;
        this.frameUtil.console = window.console;
    },

    registerFrame: function(frame, fct) {
        //frame['oncontextmenu']=function(e) {return false;};
        var win = this.win;
        var frameId = this.mappingFrame.indexOf(frame.name);
        if (frameId == undefined) {
            console.error('could not found frame [' + frame.name + '] to register');
            return;
        }
        frame.frameContext = Context.explodeLocation(frame.location);
        frame.AlbumManager = window.AlbumManager;
        //console.info('register frame [' + frame.name + '] (' + fct + ')' + frame.frameContext.inspect());

        //load common code
        Object.extend(frame, this.frameUtil);

        ['createImage','getDimension'].each(function(name) {
            frame[name] = DOMUtil[name].bind(frame);
        });

        //load specific code
        var map = this.mappingFunction[frameId];
        if (map)
            map.each(function(functionName) {
                this.bindNested(win, functionName, frame);
            }.bind(this));
        if (fct) frame[fct]();
    },

    bindNested:function(caller, prop, binder) {
        var fct = FramesHanlder.functions[prop];
        var functionName;
        if (!fct) {
            // try in window lookup
            var tmp = Object.bindNested(caller, prop, binder);
            if (!tmp) {
                console.error("could not found function '" + prop + "' while binding '" + binder + "' from caller '" + caller + "'");
                return;
            }
            functionName = tmp[0];
            fct = tmp[1];
        } else functionName = prop;

        binder[functionName] = fct.bind(binder);
        //console.info("register function from frame '" + binder + "' : " + functionName);
    }
});

Abstract.Model = {
    
    _attrib:$H(),
    _translationMap:undefined,

    decodeAttributes:function(xmlNode, attributeName) {
        attributeName = attributeName || "_";
        var attrib = xmlNode.getAttribute(attributeName);
        this._attrib.clear();
        if (attrib)
            attrib.split('|').inject(this._attrib, function(memo, keyValue) {
                var split = keyValue.split('=');
                memo[split[0]] = split[1];
                return memo;
            });
        return this._attrib;
    },

    _buildTranslationMap:function(xmlNode, translated, tmp) {
        var result = {tag : $H(),attrib:$H()};
        tmp = Object.extend(tmp || $H(), {tagI : $H(),attribI:$H()});
        this.decodeAttributes(xmlNode);

        var decodeDefinition = function(tagName, _attrib, tmp, result) {
            var _tag = _attrib[tagName];
            if (_tag) _tag.split(',').each(function(entry) {
                var _split = entry.split(':');
                tmp[_split[0]] = _split[1];
                result[_split[1]] = _split[0];
            });
        };

        var prepareTranslation0 = function(src, tmp) {
            return src.inject([], function(memo, meta) {
                var val = tmp[meta] || meta;
                if (val) memo.push(val);
                return memo;
            });
        };

        decodeDefinition('_t', this._attrib, tmp.tagI, result.tag);
        decodeDefinition('_a', this._attrib, tmp.attribI, result.attrib);

        // decode zip metas
        this._attrib.each(function(entry) {
            if (entry.key.indexOf('_') == 0) return;
            var meta = entry.key;
            var currentMap = result[meta] = $H();
            var mapping = entry.value;
            var split2;
            mapping.split(',').each(function(value) {
                currentMap[(split2 = value.split(':'))[1]] = split2[0];
            });
            console.debug('meta zip [' + meta + '] :: ' + currentMap.inspect());
        });
        var C = this['C'];
        translated.each(function(listName) {
            C['_' + listName] = prepareTranslation0(C[listName], tmp.attribI);
        }.bind(this));

        return result;
    },

    clearCache:function(newContext) {
        var key;

        for (key in this)
            if (typeof this[key] != 'function' && key[0] == '_') delete this[key];

        if (newContext) {
            var C = this['C'];
            for (key in C) if (typeof C[key] != 'function' && key[0] != '_') newContext[key] = C[key];
        }
        delete this['C'];

    },

/*extractNode: function(node, names, result, translationMap) {
    result = result || $();
    var childs = $A(node.childNodes).inject([], function(memo, child, index) {
        if (names.indexOf(child.tagName) > -1 && child.childNodes.length > 0) memo.push(child);
        return memo;
    });
    return childs.inject(result, function(memo, child) {
        var name = translationMap[child.tagName];
        memo[name] = child.textContent || child.text;
        return memo;
    });
},*/

    extractDefinedAttribute : function(names, result) {
        result = result || $H();
        var node = this._attrib;
        return names.inject(result, function(memo, name) {
            var value = node[name];
            if (value) {
                var key = this._translationMap.attrib[name] || name;
                if (this._translationMap[key]) value = this._translationMap[key][value] || value;
                memo[key] = value;
            }
            return memo;
        }.bind(this));
    },

    extractDimension: function(result) {
        var node = this._attrib;
        result = result || $H();
        var value = node[this._translationMap.dimensionAttribute];
        if (value) {
            var s = value.split(',');
            result.width = s[0];
            result.height = s[1];
        } else {
            result.width = result.height = undefined;
        }
        return result;
    }
};

{
    var dummy = {
        error:undefined,
        warn:undefined,
        info:undefined,
        debug:undefined,
        x0:undefined,
        x1:undefined,
        y0:undefined,
        y1:undefined,
        time:undefined,
        timeEnd:undefined,
        getContext:function() {
        }
    };
}