/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
(function($){
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
  // The base Class implementation (does nothing)
  this.Class = function(){};
  
  // Create a new Class that inherits from this class
  Class.extend = function(prop, extend) {
    var _super = this.prototype;
    var extendObjects = extend || true;
    
    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;
    
    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" && 
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;
            
            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];
            
            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);        
            this._super = tmp;
            
            return ret;
          };
        })(name, prop[name]) :
        // Added support for extending objects in inheritance chain - Glen Somerville
        (typeof prop[name] == 'object' && extendObjects === true) ? $.extend(true, {}, prototype[name], prop[name]) : prop[name];
    }
    
    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }
    
    // Populate our constructed prototype object
    Class.prototype = prototype;
    
    // Enforce the constructor to be what we expect
    Class.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;
    
    return Class;
  };
})(jQuery);

// Holder for Fores namespace
var frs = function(){};

(function($){
        
    var frs = this,
        ua = navigator.userAgent.toLowerCase();

    frs.isIE8 = $.browser.msie && $.browser.version < 9;
    frs.isIE7 = $.browser.msie && $.browser.version < 8;
    frs.isIE6 = $.browser.msie && $.browser.version < 7;

    frs.isMobile = (/iphone|ipad|ipod|android|blackberry|mini|windows\sce|palm/i.test(ua));
    frs.isiOS5 = (/iphone os 5_0/i.test(ua));

    frs.utils = {
        isMethodSupported: function(el, m) {
            var t = typeof el[m], re = new RegExp('^function|object$', 'i');
            return !!((re.test(t) && el[m]) || t == 'unknown');
        },
        elementSupportsAttribute: function(el, attr) {
            var test = document.createElement(el);
            return (attr in test);
        },
        getLocation: function() {
            return window.location.toString().split('vap')[0] + 'vap/';
        },
        // A function to hide select elements on underlying forms in IE6
        dealWithIlk: function(action) {
            if (frs.isIE6) $('fieldset select', frs.app.form.$el)[action]();
        }
    };
    
    frs.Collection = Class.extend({
        init: function(collection, node, options) {
            if (!collection || !node) throw new Error('Collection expects parameters collection and node.');
            var self = this;
            this.options = $.extend(true, {}, this.options, options);
            this.length = 0;
            this.items = {};
            this.selector = collection.selector;
            this.collection = this.options.container ? collection.children() : collection;
            
            $.each(this.collection, function(key, item) {
                self._push(new node($(item), self.options.nodeOptions || null));
            });
        },
        options: {
            // A flag used to determine whether to make the collection
            // out of the passed in jQuery element instances, or the
            // children of the instances.
            // * container == true ==> children of instance
            // * container == false ==> the actual instances
            container: false
        },
        _push: function(node) {
            var index = node.el.id || this.length;
            this.items[index] = node;
            this.items[index].index = index;
            this.length++;
            return this.items[index];
        },
        _refreshCollection: function() {
            this.collection = this.options.container ? $(this.selector).children() : $(this.selector);
        },
        add: function(node) {
            if (arguments.length < 1) throw new Error('Collection.add expects argument node, which should of type frs.Node.');
            var item = this._push(node).$el;
            item.appendTo(this.options.container ? $(this.selector) : item.parent());
            this._refreshCollection();
        },
        del: function(id, refresh) {
            var id = arguments.length ? id : this.collection[this.collection.length-1].id || this.collection.length-1,
                r = refresh || true;

            this.items[id].$el.remove();
            delete this.items[id];
            this.length--;
            if (r) this._refreshCollection();
        },
        empty: function(refresh) {
            var r = refresh || true;
            for (var key in this.items) {
                this.del(key, false);
            }
            if (r) this._refreshCollection();
        }
    });
    
    frs.Node = Class.extend({
        init: function(el, options, events) {
            if (!el) throw new Error('Node expected parameter el, none specified.');
            this.el = el[0];
            this.$el = el;
            this.options = $.extend(true, {}, this.options, options);
            this.events = $.extend(true, {}, this.events, events);

            // Bind events
            for (var e in this.events) {
                this.$el.bind(e, $.proxy(this.events[e], this));
            }
        }
    });

    // Tabs
    frs.Tabs =  Class.extend({
        init: function(tabs, node, content, options) {
            this.options = $.extend(true, {}, this.options, options);
            this.content = content;
            this.tabs = new frs.Collection(tabs, node, { nodeOptions: { tabs: this }});
        },
        options: {
            type: 'GET',
            dataType: 'html'
        }
    });
    frs.Tab = frs.Node.extend({
        init: function(el, options) {
            this.options = $.extend(true, {}, this.options, options);
            this._super(el, options);

            var self = this;

            $.ajaxSetup({
                cache: false,
                timeout: 60000
            });
        },
        events: {
            click: function(e) {
                this.handleClick(e);
            }
        },
        handleClick: function(e) {
            var self = this;
            e.preventDefault();
            $.ajax({
                type: this.options.tabs.options.type,
                dataType: this.options.tabs.options.dataType,
                url: this.$el.attr('data-src'),
                beforeSend: function(xhr, settings) { self.beforeSend(self, xhr, settings); },
                success: function(data) { self.success(self, data); },
                error: function(xhr, status) { self.error(self, xhr, status); }
            });
        },
        beforeSend: function(self, xhr, settings) {
            self.options.tabs.content.css('height', self.options.tabs.content.height());
            self.options.tabs.content.html(frs.loader);
        },
        success: function(self, data) {
            self.options.tabs.content.html((self.options.tabs.options.dataType == 'json') ? data.html : data);
            self.options.tabs.content.css('height', 'auto');
            self.options.tabs.tabs.collection.removeClass('active');
            self.$el.addClass('active');

            if (frs.isIE7) {
                var prependSelectors = [
                    '.country-name',
                    '#footer-links a',
                    'a.button.lms',
                    '#lms a.button.lms',
                    '.promotion h3'
                ];
                var appendSelectors = ['button.next span', 'a.button.next', '#lms a.button.lms', '#search .flight.compact #group-info'];
                $(prependSelectors.join(','), self.options.tabs.content).prepend('<b />');
                $(appendSelectors.join(','), self.options.tabs.content).append('<b />');
            }
        },
        error: function(self, xhr, status) {
            self.options.tabs.content.html('Haku ei onnistunut.');
            self.options.tabs.content.css('height', 'auto');
        }
    });

    // Overlay
    frs.Overlay = frs.Node.extend({
        init: function(el, options) {
            this.options = $.extend(true, {}, this.options, options);
            this._super(el, options, this.events);
            this.trigger = this.options.trigger || null;
        },
        events: {
            mouseenter: function() { this.cancel(); },
            mouseleave: function() { this.hide(); },
            onShow: function() { this.$el.show(); },
            onHide: function() { this.$el.hide(); }
        },
        show: function() {
            this.cancel();
            this.$el.trigger('onShow');
        },
        hide: function() {
            this.cancel();
            this.t = window.setTimeout($.proxy(function() {
                this.$el.trigger('onHide');
            }, this), 500);
        },
        cancel: function() {
            if (typeof this.t== "number") {
                window.clearTimeout(this.t);
                delete this.t;
            }
        }
    });

    // Fancybox
    frs.FancyBox = Class.extend({
        init: function(options) {
            this.options = $.extend(this.options, options);
            $.fancybox(this.options);
        },
        options: {
            transitionIn: 'elastic',
            transitionOut: 'elastic',
            overlayShow: true,
            overlayColor: '#000',
            overlayOpacity: 0.25,
            opacity: true
        }
    });
    
}).call(frs, jQuery);

/**
 * Behaves just like the python range() built-in function.
 * Arguments:   [start,] stop[, step]
 *
 * @start   Number  start value
 * @stop    Number  stop value (excluded from result)
 * @step    Number  skip values by this step size
 *
 * Number.range() -> error: needs more arguments
 * Number.range(4) -> [0, 1, 2, 3]
 * Number.range(0) -> []
 * Number.range(0, 4) -> [0, 1, 2, 3]
 * Number.range(0, 4, 1) -> [0, 1, 2, 3]
 * Number.range(0, 4, -1) -> []
 * Number.range(4, 0, -1) -> [4, 3, 2, 1]
 * Number.range(0, 4, 5) -> [0]
 * Number.range(5, 0, 5) -> []
 * Number.range(5, 4, 1) -> []
 * Number.range(0, 1, 0) -> error: step cannot be zero
 * Number.range(0.2, 4.0) -> [0, 1, 2, 3]
 */
Number.range = function() {
    var start, end, step;
    var array = [];

    switch(arguments.length) {
        case 0:
            throw new Error('range() expected at least 1 argument, got 0 - must be specified as [start,] stop[, step]');
            return array;
        case 1:
            start = 0;
            end = Math.floor(arguments[0]) - 1;
            step = 1;
            break;
        case 2:
        case 3:
        default:
            start = Math.floor(arguments[0]);
            end = Math.floor(arguments[1]) - 1;
            var s = arguments[2];
            if (typeof s === 'undefined'){
                s = 1;
            }
            step = Math.floor(s) || (function(){ throw new Error('range() step argument must not be zero'); })();
            break;
    }
    
    if (step > 0) {
        for (var i = start; i <= end; i += step) {
            array.push(i);
        }
    } else if (step < 0) {
        step = -step;
        if (start > end) {
            for (var i = start; i > end + 1; i -= step) {
                array.push(i);
            }
        }
    }
    return array;
};

Object.size = function(obj) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) size++;
    }
    return size;
};

Object.equals = function(x) {

    for (p in this) {
        if(typeof(x[p]) == 'undefined') { return false; }
    }

    for (p in this) {
        if (this[p]) {
            switch(typeof(this[p])) {
                case 'object':
                    if (!this[p].equals(x[p])) { return false; }
                    break;
                case 'function':
                    if (typeof(x[p])=='undefined' || (p != 'equals' && this[p].toString() != x[p].toString())) { return false; }
                    break;
                default:
                    if (this[p] != x[p]) { return false; }
            }
        } else {
            if (x[p]) {
                return false;
            }
        }
    }

    for (p in x) {
        if (typeof(this[p])=='undefined') { return false; }
    }

    return true;
};
