/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
(function () {
    var initializing = false;

    // The base JQClass implementation (does nothing)
    window.JQClass = function () {
    };

    // Collection of derived classes
    JQClass.classes = {};

    // Create a new JQClass that inherits from this class
    JQClass.extend = function extender(prop) {
        var base = this.prototype;

        // 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 base[name] == 'function' ?
                    (function (name, fn) {
                        return function () {
                            var __super = this._super;

                            // Add a new ._super() method that is the same method
                            // but on the super-class
                            this._super = function (args) {
                                return base[name].apply(this, args || []);
                            };

                            var ret = fn.apply(this, arguments);

                            // The method only need to be bound temporarily, so we
                            // remove it when we're done executing
                            this._super = __super;

                            return ret;
                        };
                    })(name, prop[name]) :
                    prop[name];
        }

        // The dummy class constructor
        function JQClass() {
            // All construction is actually done in the init method
            if (!initializing && this._init) {
                this._init.apply(this, arguments);
            }
        }

        // Populate our constructed prototype object
        JQClass.prototype = prototype;

        // Enforce the constructor to be what we expect
        JQClass.prototype.constructor = JQClass;

        // And make this class extendable
        JQClass.extend = extender;

        return JQClass;
    };
})();

(function ($) { // Ensure $, encapsulate

    /** Abstract base class for collection plugins v1.0.1.
     Written by Keith Wood (kbwood{at}iinet.com.au) December 2013.
     Licensed under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license.
     @module $.JQPlugin
     @abstract */
    JQClass.classes.JQPlugin = JQClass.extend({
        /** Name to identify this plugin.
         @example name: 'tabs' */
        name: 'plugin',
        /** Default options for instances of this plugin (default: {}).
         @example defaultOptions: {
         selectedClass: 'selected',
         triggers: 'click'
         } */
        defaultOptions: {},
        /** Options dependent on the locale.
         Indexed by language and (optional) country code, with '' denoting the default language (English/US).
         @example regionalOptions: {
         '': {
         greeting: 'Hi'
         }
         } */
        regionalOptions: {},
        /** Names of getter methods - those that can't be chained (default: []).
         @example _getters: ['activeTab'] */
        _getters: [],
        /** Retrieve a marker class for affected elements.
         @private
         @return {string} The marker class. */
        _getMarker: function () {
            return 'is-' + this.name;
        },
        /** Initialise the plugin.
         Create the jQuery bridge - plugin name <code>xyz</code>
         produces <code>$.xyz</code> and <code>$.fn.xyz</code>. */
        _init: function () {
            // Apply default localisations
            $.extend(this.defaultOptions, (this.regionalOptions && this.regionalOptions['']) || {});
            // Camel-case the name
            var jqName = camelCase(this.name);
            // Expose jQuery singleton manager
            $[jqName] = this;
            // Expose jQuery collection plugin
            $.fn[jqName] = function (options) {
                var otherArgs = Array.prototype.slice.call(arguments, 1);
                if ($[jqName]._isNotChained(options, otherArgs)) {
                    return $[jqName][options].apply($[jqName], [this[0]].concat(otherArgs));
                }
                return this.each(function () {
                    if (typeof options === 'string') {
                        if (options[0] === '_' || !$[jqName][options]) {
                            throw 'Unknown method: ' + options;
                        }
                        $[jqName][options].apply($[jqName], [this].concat(otherArgs));
                    }
                    else {
                        $[jqName]._attach(this, options);
                    }
                });
            };
        },
        /** Set default values for all subsequent instances.
         @param options {object} The new default options.
         @example $.plugin.setDefauls({name: value}) */
        setDefaults: function (options) {
            $.extend(this.defaultOptions, options || {});
        },
        /** Determine whether a method is a getter and doesn't permit chaining.
         @private
         @param name {string} The method name.
         @param otherArgs {any[]} Any other arguments for the method.
         @return {boolean} True if this method is a getter, false otherwise. */
        _isNotChained: function (name, otherArgs) {
            if (name === 'option' && (otherArgs.length === 0 ||
                    (otherArgs.length === 1 && typeof otherArgs[0] === 'string'))) {
                return true;
            }
            return $.inArray(name, this._getters) > -1;
        },
        /** Initialise an element. Called internally only.
         Adds an instance object as data named for the plugin.
         @param elem {Element} The element to enhance.
         @param options {object} Overriding settings. */
        _attach: function (elem, options) {
            elem = $(elem);
            if (elem.hasClass(this._getMarker())) {
                return;
            }
            elem.addClass(this._getMarker());
            options = $.extend({}, this.defaultOptions, this._getMetadata(elem), options || {});
            var inst = $.extend({name: this.name, elem: elem, options: options},
            this._instSettings(elem, options));
            elem.data(this.name, inst); // Save instance against element
            this._postAttach(elem, inst);
            this.option(elem, options);
        },
        /** Retrieve additional instance settings.
         Override this in a sub-class to provide extra settings.
         @param elem {jQuery} The current jQuery element.
         @param options {object} The instance options.
         @return {object} Any extra instance values.
         @example _instSettings: function(elem, options) {
         return {nav: elem.find(options.navSelector)};
         } */
        _instSettings: function (elem, options) {
            return {};
        },
        /** Plugin specific post initialisation.
         Override this in a sub-class to perform extra activities.
         @param elem {jQuery} The current jQuery element.
         @param inst {object} The instance settings.
         @example _postAttach: function(elem, inst) {
         elem.on('click.' + this.name, function() {
         ...
         });
         } */
        _postAttach: function (elem, inst) {
        },
        /** Retrieve metadata configuration from the element.
         Metadata is specified as an attribute:
         <code>data-&lt;plugin name>="&lt;setting name>: '&lt;value>', ..."</code>.
         Dates should be specified as strings in this format: 'new Date(y, m-1, d)'.
         @private
         @param elem {jQuery} The source element.
         @return {object} The inline configuration or {}. */
        _getMetadata: function (elem) {
            try {
                var data = elem.data(this.name.toLowerCase()) || '';
                data = data.replace(/'/g, '"');
                data = data.replace(/([a-zA-Z0-9]+):/g, function (match, group, i) {
                    var count = data.substring(0, i).match(/"/g); // Handle embedded ':'
                    return (!count || count.length % 2 === 0 ? '"' + group + '":' : group + ':');
                });
                data = $.parseJSON('{' + data + '}');
                for (var name in data) { // Convert dates
                    var value = data[name];
                    if (typeof value === 'string' && value.match(/^new Date\((.*)\)$/)) {
                        data[name] = eval(value);
                    }
                }
                return data;
            }
            catch (e) {
                return {};
            }
        },
        /** Retrieve the instance data for element.
         @param elem {Element} The source element.
         @return {object} The instance data or {}. */
        _getInst: function (elem) {
            return $(elem).data(this.name) || {};
        },
        /** Retrieve or reconfigure the settings for a plugin.
         @param elem {Element} The source element.
         @param name {object|string} The collection of new option values or the name of a single option.
         @param [value] {any} The value for a single named option.
         @return {any|object} If retrieving a single value or all options.
         @example $(selector).plugin('option', 'name', value)
         $(selector).plugin('option', {name: value, ...})
         var value = $(selector).plugin('option', 'name')
         var options = $(selector).plugin('option') */
        option: function (elem, name, value) {
            elem = $(elem);
            var inst = elem.data(this.name);
            if (!name || (typeof name === 'string' && value == null)) {
                var options = (inst || {}).options;
                return (options && name ? options[name] : options);
            }
            if (!elem.hasClass(this._getMarker())) {
                return;
            }
            var options = name || {};
            if (typeof name === 'string') {
                options = {};
                options[name] = value;
            }
            this._optionsChanged(elem, inst, options);
            $.extend(inst.options, options);
        },
        /** Plugin specific options processing.
         Old value available in <code>inst.options[name]</code>, new value in <code>options[name]</code>.
         Override this in a sub-class to perform extra activities.
         @param elem {jQuery} The current jQuery element.
         @param inst {object} The instance settings.
         @param options {object} The new options.
         @example _optionsChanged: function(elem, inst, options) {
         if (options.name != inst.options.name) {
         elem.removeClass(inst.options.name).addClass(options.name);
         }
         } */
        _optionsChanged: function (elem, inst, options) {
        },
        /** Remove all trace of the plugin.
         Override <code>_preDestroy</code> for plugin-specific processing.
         @param elem {Element} The source element.
         @example $(selector).plugin('destroy') */
        destroy: function (elem) {
            elem = $(elem);
            if (!elem.hasClass(this._getMarker())) {
                return;
            }
            this._preDestroy(elem, this._getInst(elem));
            elem.removeData(this.name).removeClass(this._getMarker());
        },
        /** Plugin specific pre destruction.
         Override this in a sub-class to perform extra activities and undo everything that was
         done in the <code>_postAttach</code> or <code>_optionsChanged</code> functions.
         @param elem {jQuery} The current jQuery element.
         @param inst {object} The instance settings.
         @example _preDestroy: function(elem, inst) {
         elem.off('.' + this.name);
         } */
        _preDestroy: function (elem, inst) {
        }
    });

    /** Convert names from hyphenated to camel-case.
     @private
     @param value {string} The original hyphenated name.
     @return {string} The camel-case version. */
    function camelCase(name) {
        return name.replace(/-([a-z])/g, function (match, group) {
            return group.toUpperCase();
        });
    }

    /** Expose the plugin base.
     @namespace "$.JQPlugin" */
    $.JQPlugin = {
        /** Create a new collection plugin.
         @memberof "$.JQPlugin"
         @param [superClass='JQPlugin'] {string} The name of the parent class to inherit from.
         @param overrides {object} The property/function overrides for the new class.
         @example $.JQPlugin.createPlugin({
         name: 'tabs',
         defaultOptions: {selectedClass: 'selected'},
         _initSettings: function(elem, options) { return {...}; },
         _postAttach: function(elem, inst) { ... }
         }); */
        createPlugin: function (superClass, overrides) {
            if (typeof superClass === 'object') {
                overrides = superClass;
                superClass = 'JQPlugin';
            }
            superClass = camelCase(superClass);
            var className = camelCase(overrides.name);
            JQClass.classes[className] = JQClass.classes[superClass].extend(overrides);
            new JQClass.classes[className]();
        }
    };

})(jQuery);