/**
 * WebRiders Framework
 *
 * @version 0.9
 * @author Brizgunov Rostislav
 * @license MIT or GPL
 */

var WebRiders = WR = {};

/**
 * WebRiders Framework - Utility functions
 *
 * @version 0.9
 * @author Bryzgunov Rostislav (aka kottenator)
 * @license MIT or GPL
 *
 * Dependencies:
 *     jquery-1.4.0.js
 *     webriders.core-0.9.js
 */

WebRiders.util = {
    /**
     * Create namespace or return existent.
     *
     * @param {String} name - namespace name. E.g. 'WR.CustomSite'
     *
     * @return {Object} - namespace object. May be not empty
     */
    namespace: function(name) {
        name = name.split('.');
        var c = window, n = '';
        while(n = name.shift()) {
            if (typeof c[n] == 'undefined')
                c[n] = {};
            c = c[n];
        };
        return c;
    },

    /**
     * Alias for the 'namespace' method
     */
    ns: function() { return this.namespace.apply(this, arguments); },

    /**
     * Load script dependencies
     *
     * @param {String/Array} scripts - path:
     *     "my/script.js"
     *     "my/script1.js my/script2.js"
     *     "my/script1.js, my/script2.js  my/script3.js ,    my/script4.js"
     *     ["my/script1.js", "my/script2.js", "my/script3.js"]
     * @param {Object} config
     *
     * @config {Function} callback. Not required
     * @config {*} context - callback context (scope) object. Not required
     * @config {Boolean} cache - true to leave URL unchanged. false to add '?_=12442432' to it. Default: false
     * @config {Boolean} async - true to load scripts asynchronous. Default: false
     */
    require: function(scripts, config) {
        scripts = scripts || '';
        config = config || {};

        if (typeof scripts == "string")
            scripts = scripts.replace(/^\s+|\s+$/g, '').replace(/\s*,\s*|\s+/g, ',').split(',');

        if (!$.isArray(scripts))
            return false;

        var loaded = this.require.loaded = this.require.loaded || {};

        for (var i = 0, l = scripts.length; i < l; i++) {
            var script = scripts[i] || '';
            if (script.search(/^https?:\/\//) !== 0) {
                var loc = window.location, path = '';
                if (script.indexOf('/') === 0)
                    path = window.location.protocol + '//' + window.location.host;
                else
                    path = (config.path || this.require.defaultPath || '');
                script = path + script;
            }

            if (!loaded[script]) {
                var success = function(data, textStatus) {
                    loaded[script] = true;
                    if (typeof config.callback == "function")
                        config.callback.apply(this, arguments);
                };

                $.ajax({
                    url: script,
                    dataType: 'script',
                    success: success,
                    context: config.context,
                    cache: !!config.cache,
                    async: !!config.async
                });
            }
        }

        return true;
    },

    /**
     * Get shuffled subset from the list
     *
     * @param {Array} list
     * @param {Number} cnt - how many elements should we get? Default: all
     */
    randomSample: function(list, cnt) {
        cnt = cnt || list.length;
        list = $.extend([], list);
        var new_list = [];
        while (cnt) {
            cnt--;
            new_list.push(list.splice(Math.round(Math.random() * (list.length - 1)), 1)[0]);
        }
        return new_list;
    }
}

/**
 * Simple string template
 * @param {String} source
 * @constructor
 */
WR.util.Template = function(source) {
    this.source = source;
}

WR.util.Template.prototype = {
    source: '',

    apply: function(config) {
        config = config || {};
        return this.source.replace(
            /{{\s*(.*?)\s*}}/g,
            function (a, b) {
                var r = config[b];
                if (typeof r == "undefined")
                    r = "";
                return r;
            }
        );
    }
}

/**
 * WebRiders Framework - Data-related stuff:
 *     set, array, object, other data-structures and related methods
 *
 * @version 0.9
 * @author Bryzgunov Rostislav (aka kottenator)
 * @license MIT or GPL
 *
 * Dependencies:
 *     jquery-1.4.0.js
 *     webriders.core-0.9.js
 */

 WR.data = {}

/**
 * Collection
 * Something between Array and Object :)
 * @param {Array} items
 * @constructor
 */
WR.data.Collection = function(items) {
    this.items = [];

    if (items instanceof WR.data.Collection)
        items = items.toArray();

    if ($.isArray(items))
        this.proxyFn('push', items);
}

WR.data.Collection.prototype = {
    // @private
    items: null,

    getLength: function() {
        return this.items.length;
    },

    proxyFn: function(fnName, args) {
        return this.items[fnName].apply(this.items, args);
    },

    /**
     * Proxy for [].push(new1, [new2, new3, ...])
     * Adds one ore more elements to the end of the array
     * @return {Number} new array length
     */
    push: function() {
        return this.proxyFn('push', arguments);
    },

    /**
     * Proxy for [].pop()
     * Removes the last element of the array
     * @return removed element
     */
    pop: function() {
        return this.proxyFn('pop', arguments);
    },

    /**
     * Proxy for [].shift()
     * Removes the first element of the array
     * @return removed element
     */
    shift: function() {
        return this.proxyFn('shift', arguments);
    },

    /**
     * Proxy for [].unshift(new1, [new2, new3, ...])
     * Adds one ore more elements to the beginning of the array
     * @return {Number} new array length
     */
    unshift: function() {
        return this.proxyFn('unshift', arguments);
    },

    /**
     * Proxy for [].slice(fromPos, [toPos])
     * @return {WebRiders.data.Collection} sliced sub-collection (from fromPos index till toPos index)
     */
    slice: function() {
        this.proxyFn('slice', arguments);
        return this;
    },

    /**
     * Proxy for [].splice(fromPos, [howMany, [new1, [new2, new3, ...]]])
     * Removes howMany elements from fromPos index and inserts there one or more new elements
     * @return {WebRiders.data.Collection} spliced sub-collection
     */
    splice: function() {
        this.proxyFn('splice', arguments);
        return this;
    },

    /**
     * Get item by id
     * @param id
     * @return found element or undefined
     */
    getById: function(id) {
        return this.get({ 'id': id })[0];
    },

    /**
     * Get item by something
     *
     * @param {String/Number/Function/Object} by:
     *     0, 1, 2, 'asd', 'qwe' - returns array item by index (or smth)
     *     { year: 2009, sum: 15 } - returns array items, that has EXACTLY the same params (year === 2009, sum === 15)
     *     { x: null, y: undefined } - true, when item.x === null and item.x in not defined
     *     { z: '*' } - spacial case. Equal to any (you can't use '*' as just '*', sorry).
     *     function(o) { return o.year === 2009 && sum === 15; } - filter by function
     *
     * @return {Array} found elements (may be empty but still Array)
     */
    get: function(by) {
        // Filter by params set
        if (typeof by == "object") {
            var ret = [];

            function _cmp(o1, o2) {
                for (var k in o1) {
                    if (o1.hasOwnProperty(k)) {
                        if (o1[k] === '*' && o2.hasOwnProperty(k))
                            continue;

                        if (o1[k] !== o2[k])
                            return false;
                    }

                }
                return true;
            }

            for (var i = 0, it = this.items, l = it.length; i < l; i++)
                if (_cmp(by, it[i]))
                    ret.push(it[i]);

            return ret;
        }
        // Filter by function
        else if (typeof by == "function") {
            var ret = [];

            for (var i = 0, it = this.items, l = it.length; i < l; i++)
                if (by.call(it[i], it[i]))
                    ret.push(it[i]);

            return ret;
        }
        // Get by index
        else {
            return this.items[by];
        }
    },

    /**
     * Items iterator
     * @param {Function/String} fn - function or method name
     */
    each: function(fn) {
        if (typeof fn == "function")
            return $.each(this.items, fn);
        else if (typeof fn == "string")
            return $.each(this.items, function() {
                this[fn]();
            });
    },

    toArray: function() {
        return this.items;
    },

    toString: function() {
        return this.items.toString();
    }
}

/**
 * Alias for WR.data.Collection
 */
WR.data.C = WR.data.Collection;

/**
 * WebRiders Framework - UI functions
 *
 * @version 0.9
 * @author Bryzgunov Rostislav (aka kottenator)
 * @license MIT or GPL
 *
 * Dependencies:
 *     jquery-1.4.0.js
 *     webriders.core-0.9.js
 */

WR.ui = {}

/**
 * Screen overlay
 */
WR.ui.overlay = {
	options: {
		css: {
			zIndex: 1000,
			background: '#000',
			opacity: .5
		},
		showMethod: "fadeIn",
		hideMethod: "fadeOut"
	},

	overlay: null,
	container: "body",

	methods: {
		fadeIn: function(overlay) {
			overlay.fadeIn(500);
		},

		show: function(overlay) {
			overlay.show();
		},

		fadeOut: function(overlay) {
			overlay.fadeOut(300);
		},

		hide: function(overlay) {
			overlay.hide();
		}
	},

	init: function() {
		if (!window.jQuery)
			return;

		$('head').append(
			'<style type="text/css">' +
				'.overlay { width: 100%; min-height: 100%; position: fixed; top: 0; left: 0; display: none; }' +
				'* html .overlay { height: 100%; position: absolute; }' +
			'</style>'
		);

		this.overlay = $('<div class="overlay"></div>');
		this.overlay.appendTo(this.container);

		this.updateStyles();
	},

	updateStyles: function(newStyles) {
		$.extend(this.options.css, newStyles);
		this.overlay.css(this.options.css);
	},

	show: function() {
		if (!window.jQuery)
			return;

		if (!this.overlay)
			this.init();

		this.beforeShow();

		this.methods[this.options.showMethod](this.overlay);
	},

	beforeShow: function() {
		this.overlay.height($(document.body).height());
	},

	hide: function() {
		if (!window.jQuery)
			return;

		if (!this.overlay)
			this.init();

		this.methods[this.options.hideMethod](this.overlay);
	}
}

