# Source: index.js

    //==========================================================
    //
    //       ___           ___           ___           ___           ___           ___           ___           ___           ___
    //      /\__\         /\  \         /\__\         /\  \         /\__\         /\  \         /\  \         /\__\         /\  \
    //     /:/  /        /::\  \       /::|  |       /::\  \       /:/ _/_       /::\  \       /::\  \       /:/  /        /::\  \
    //    /:/__/        /:/\:\  \     /:|:|  |      /:/\:\  \     /:/ /\__\     /:/\:\  \     /:/\:\  \     /:/__/        /:/\ \  \
    //   /::\  \ ___   /:/  \:\  \   /:/|:|__|__   /::\~\:\  \   /:/ /:/ _/_   /:/  \:\  \   /::\~\:\  \   /::\__\____   _\:\~\ \  \
    //  /:/\:\  /\__\ /:/__/ \:\__\ /:/ |::::\__\ /:/\:\ \:\__\ /:/_/:/ /\__\ /:/__/ \:\__\ /:/\:\ \:\__\ /:/\:::::\__\ /\ \:\ \ \__\
    //  \/__\:\/:/  / \:\  \ /:/  / \/__/~~/:/  / \:\~\:\ \/__/ \:\/:/ /:/  / \:\  \ /:/  / \/_|::\/:/  / \/_|:|~~|~    \:\ \:\ \/__/
    //       \::/  /   \:\  /:/  /        /:/  /   \:\ \:\__\    \::/_/:/  /   \:\  /:/  /     |:|::/  /     |:|  |      \:\ \:\__\
    //       /:/  /     \:\/:/  /        /:/  /     \:\ \/__/     \:\/:/  /     \:\/:/  /      |:|\/__/      |:|  |       \:\/:/  /
    //      /:/  /       \::/  /        /:/  /       \:\__\        \::/  /       \::/  /       |:|  |        |:|  |        \::/  /
    //      \/__/         \/__/         \/__/         \/__/         \/__/         \/__/         \|__|         \|__|         \/__/
    //
    //
    //
    // @ HOMEWORKS FRAMEWORK
    // @ All Rights Reserved IGAWorks Inc.
    //
    //==========================================================
    //
    // @ UPDATE  2017-01-13
    // @ AUTHOR  Kenneth
    //
    //=========================================================

    window.HOMEWORKS_VERSION = '2.0.9.4';
    var VERSION = '@@VERSION';
    if (VERSION.replace(/@/g, '') !== 'VERSION') {
        window.HOMEWORKS_VERSION = VERSION;
    }

    var _promiseVariables  = {}; // Promise standard global variables.
    var _superVariables   = {}; // Plugin option standard global variables.

    /*=================================================
     *= NOTE - String formatter.
     *= DATE - 2016-01-19
     *================================================*/
    String.prototype.getFormat = function (options) {
        var string = this;
        for (var idx in options) {
            var value = options[idx];
            idx = idx.replace(/\\/gi, '\\\\')
                     .replace(/-/gi, '\\-');
            var regexp = new RegExp("{" + idx + "}", "gi");
            string = string.replace(regexp, value);
        }
        return string.toString();
    };

    /*=================================================
     *= NOTE - Component biding feature.
     *= DATE - 2016-01-19
     *================================================*/
    Function.prototype.hook = function (name, args) {
        var context = this;

        try {
            jQuery(document).ready(function() {
                var format = '[data-{data-name}]';

                jQuery(format.getFormat({
                    'data-name': name
                })).each(function () {
                    var element = $(this);
                    var target = element.data('pen');
                    var plugin = element.data(name);

                    if (plugin === false)
                        return true;

                    if (typeof context === 'function') {
                        if (typeof target === 'undefined') {
                            context.call(element, null, plugin, args);
                        } else {
                            context.call(element, $(target), plugin, args);
                        }
                    }
                });
            });
        } catch (e) {
            console.trace(e.stack);
        }
    };

    /*=================================================
     *= NOTE - HOMEWORKS Component define region.
     *= DATE - 2016-01-19
     *================================================*/

    /**
     * @constructor
     * @description HOMEWORKS COMPONENT Store constructor, It is separated by each COMPOENTS.
     * @see Refer an example document {@link https://kennethanceyer.gitbooks.io/homeworks-framework-wiki/content/DEVELOPMENT/ComponentData.html|here}.
     * @author Kenneth <kenneth@igaworks.com>
     * @param {ComponentMethod} context - HOMEWORKS ComponentMethod Context.
     * @param {string} id -  HOMEWORKS Component Unique ID
     * @returns {ComponentData}
     */
    function ComponentData(context, id) {
        /*=================================================
         *= NOTE - HOMEWORKS Compoent storagy variables region.
         *= DATE - 2016-01-19
         *================================================*/
        if (typeof _superVariables[id] === 'undefined') {
            _superVariables[id] = {};
        }

        /**
         * @member
         * @description ComponentData's store variable, This is the heart of ComponentData.
         * @property {ComponentData} $self - Reference of ComponentData (self).
         * @property {ComponentMethod} $super - Reference of ComponentMethod.
         * @property {ComponentHelper} $helper - Reference of ComponentHelper.
         * @property {Boolean} _init - If component already done initialize process, It will be true (This is prevent a duplication of initializing).
         * @property {Boolean} _anim - The logical variable of Component animating.
         * @property {Boolean} _bind - The logical variable of Component binding.
         * @property {Object} anim - Animation configuration object.
         * @property {String} framework - Framework full name.
         * @property {String} prefix - Framework short name for using set class name prefix of Component.
         * @property {String} id - An unique id of Component, It will be automatically setted in the process of ComponentMethod.
         * @property {Array<jQuery>} element - Quick reference of jQuery objects that refered frequently.
         * @property {Object} global - Global properties each of Components.
         */
        this.store = {
            $self: this,
            $super: context,
            $helper: new ComponentHelper(context, this.store),
            _init: false,
            _anim: false,
            _bind: false,
            _debug: true,
            anim: {
                time: 300,
                effect: 'swing'
            },
            framework: 'homeworks',
            prefix: 'works',
            id: id,
            element: {
                $window: $(window),
                $document: $(document)
            },
            global: _superVariables[id] || {}
        };
    }

    /**
     * @constructor
     * @description HOMEWORKS Helper constructor, This is helping to treat DOM Elements or JS operators.
     * @see Refer an example document {@link https://kennethanceyer.gitbooks.io/homeworks-framework-wiki/content/DEVELOPMENT/ComponentHelper.html|here}.
     * @author Kenneth <kenneth@igaworks.com>
     * @param {ComponentMethod} context - HOMEWORKS ComponentMethod Context.
     * @param {ComponentData.store} data - HOMEWORKS ComponentData store for get unique id.
     * @returns {ComponentHelper}
     */
    function ComponentHelper(context, data) {
        /*=================================================
         *= NOTE - HOMEWORKS Component shared feature.
         *= DATE - 2016-01-19
         *================================================*/

        /**
         * @function
         * @description Promiss is helping to make a schedule, It is similar like setTimeout.
         * @param {String} name - Promiss name for control.
         * @param {Function} callback - The callback when promiss is done.
         * @param {Number} time - A millisecond time for promiss.
         * @param {Function} invoke - The callback when invoke request be received.
         * @returns {Number} setTimeout number.
         */
        this.promise = function (name, callback, time, invoke) {
            var self = this;
            if (typeof name === 'function' && typeof callback === 'number') {
                time = callback;
                callback = name;
                name = context.id;
            }

            if (typeof invoke !== 'undefined' && invoke === true) {
                this.invoke(name);
            }

            _promiseVariables[context.framework + '.' + name] = setTimeout(function () {
                try {
                    delete _promiseVariables[context.framework + '.' + name];
                } catch (e) {
                    self.log(e.stack);
                }

                if (typeof callback === 'function') {
                    callback();
                }
            }, time);
            return _promiseVariables;
        };

        /**
         * @function
         * @description This function will invoke promiss request that you created before.
         * @param {String} name - Promiss name for invoke a request.
         * @param {Function} callback - The callback when promiss is done.
         * @param {Number} time - A millisecond time for promiss.
         * @param {Function} invoke - The callback when invoke request be received.
         * @returns {Boolean}
         */
        this.invoke = function (name) {
            if (typeof name === 'undefined') {
                name = context.id;
            }
            if (typeof _promiseVariables[_this.framework + '.' + name] !== 'undefined') {
                try {
                    clearTimeout(_promiseVariables[_this.framework + '.' + name]);
                    delete _promiseVariables[_this.framework + '.' + name];
                } catch (e) {
                    self.log(e.stack);
                }
            }
            return true;
        };

        /**
         * @function
         * @description The logger of HOMEWORKS Frameworks, Use this instead console.log.
         * @param {String} message - String error message or Exception object.
         * @param {String} code - Error code or number.
         * @returns {undefined}
         */
        this.log = function (message, code) {
            if (context._debug === true) {
                var template = message;

                if (typeof message !== 'undefined' && typeof message.stack !== 'undefined') {
                    console.error(message.stack);
                } else {
                    if (typeof code !== 'undefined' && code !== null) {
                        template = '[' + code + '] ' + template;
                    }

                    console.error(template);
                }
            }
        };

        /**
         * @function
         * @description This function find template that you declared on ComponentMethod's templates array, and than replace it using a key what you gave.
         * @param {String} key - The template key.
         * @param {Object} map - The template object.
         * @returns {String}
         */
        this.parseTemplate = function (key, map) {
            var data = context.template[key];
            if (typeof data === 'undefined') {
                this.log("'" + key + "' 이름의 템플릿이 확인되지 않습니다.");
                return false;
            }
            return data.getFormat(map);
        };

        /**
         * @function
         * @description You can get unique id for set each of component by using this function.
         * @returns {String}
         */
        this.getIdentifier = function () {
            var id = data.id.replace(/,?\s/g, '-');
            var id_arr = id.split('');
            id = [id_arr[0].toUpperCase()].concat(id_arr.splice(1, id.length)).join('');
            return data.framework + id;
        };

        /**
         * @function
         * @description It will attach an event to your element, Use this function instead jQuery.bind.
         * @param {jQuery} element - jQuery object for attach event.
         * @param {String} type - Event type name.
         * @param {Function} callback - The callback function which fired when event triggered.
         * @param {Boolean} initialize - If this parameter is true, The event is triggered once automatically.
         * @returns {undefined}
         */
        this.bind = function (element, type, callback, initialize) {
            try {
                var forms = type.toString().split(' ');
                for (var name in forms) {
                    forms[name] = forms[name] + '.' + this.getIdentifier();
                }

                forms = forms.join(' ');

                element.bind(forms, function (event, value) {
                    if (typeof value === 'object') {
                        $.extend(event, value);
                    }

                    if (typeof callback === 'function') {
                        callback.apply(this, Array.prototype.slice.call(arguments));
                    }
                });

                if (typeof initialize !== 'undefined' && initialize === true) {
                    this.triggerHandler(element, type);
                }
            } catch (exception) {
                this.log(exception);
            }
        };

        /**
         * @function
         * @description Deattach event from the jQuery object, Use this function instead jQuery.unbind.
         * @param {jQuery} element - jQuery object for deattach event.
         * @param {String} type - Event type name.
         * @returns {undefined}
         */
        this.unbind = function (element, type) {
            element.unbind(type + '.' + this.getIdentifier());
        };

        /**
         * @function
         * @description This function will trigger an event, Use this function instead jQuery.trigger.
         * @param {jQuery} element - jQuery object for trigger event.
         * @param {String} type - Event type name.
         * @param {Any} value - Extra values for sending the binder function.
         * @returns {undefined}
         */
        this.trigger = function (element, type, value) {
            var forms = (type.toString().split(' '))[0];
            element.trigger(value === true ? forms : (forms + '.' + this.getIdentifier()), value);
        };

        /**
         * @function
         * @description This function will trigger an event "only logically", Use this function instead jQuery.triggerHandler.
         * @param {jQuery} element - jQuery object for trigger event logically.
         * @param {String} type - Event type name.
         * @param {Any} value - Extra values for sending the binder function.
         * @returns {undefined}
         */
        this.triggerHandler = function (element, type, value) {
            var forms = (type.toString().split(' '))[0];
            element.triggerHandler(value === true? forms : (forms + '.' + this.getIdentifier()), value);
        };
    }

    /**
     * @constructor
     * @description HOMEWORKS COMPONENT Create manager, This is core constructor for making HOMEWORKS COMPONENT.
     * @see Refer an example document {@link https://kennethanceyer.gitbooks.io/homeworks-framework-wiki/content/DEVELOPMENT/ComponentMethod.html|here}.
     * @author Kenneth <kenneth@igaworks.com>
     * @param {string} name - COMPONENT Name.
     * @params {Object} settings - COMPONENT Settings.
     * @returns {ComponentMethod}
     */
    function ComponentMethod(name, settings) {
        /*=================================================
         *= NOTE - HOMEWORKS Component settings region.
         *= DATE - 2016-01-19
         *================================================*/
        var context = this;

        var _componentVariables = (new ComponentData(this, name)).store;
        var _globalVariables = _componentVariables.global;

        if (typeof settings.options !== 'undefined' && settings.options !== null) {
            $.extend(_superVariables[name], settings.options);
        }

        /**
         * @member {Function}
         * @description The constructor of Component, This function will be called when Component mapping be started.
         */
        this.init = settings.init;

        /**
         * @member {Object<Function>}
         * @description An object wrapped functions, This object is declared Component methods.
         */
        this.method = {
            init: this.init
        };

        /**
         * @member {Object<String>}
         * @description An object wrapped templates, This object is declared templates which use in Component.
         */
        this.template = {
        };

        /**
         * @member {Object<Any>}
         * @description Options decleared by caller, You can default option in ComponentMethod.
         */
        this.options = {
        };

        $.extend(this.method, settings.method);
        $.extend(this.template, settings.template);
        $.extend(this.options, settings.options);

        /*=================================================
         *= NOTE - HOMEWORKS ROUTE START
         *= DATE - 2017-01-10
         *================================================*/

        /**
         * @function
         * @description This function is core of ComponentMethod, This is provider to collecting Component by call patterns.
         * @param {string} id - Component unique id for give to internel of Component.
         * @returns {jQuery}
         */
        this.route = function (id) {
            var self = this;
            var args = [];
            if (arguments.length > 1) {
                $.map(Array.prototype.slice.call(arguments, 1), function (e, i) {
                    args.push(e);
                });
            }

            var ElementBinder = function () {
                var _localVariables = this.data;
                if (typeof this === 'object') {
                    if (typeof _localVariables === 'undefined') {
                        _localVariables = {
                            '_id': id,
                            '_init': false,
                            '_prototype': {},
                            '_options': $.extend({}, context.options)
                        };
                        this.data = _localVariables;
                        $.extend(_localVariables._prototype, context.method);
                    }
                }

                var componentContext = $.extend(this, {
                    local: _localVariables,
                }, _componentVariables);

                if (args.length === 0 || typeof args[0] === 'object') {
                    // Function(obj) or Function() pattern.
                    if (typeof args[0] === 'object') {
                        // Function(obj) pattern.
                        $.extend(_localVariables._options, args[0]);
                    }

                    if (_localVariables._init === false) {
                        _localVariables._init = true;
                        context.init.apply(componentContext, [$(this)].concat(Array.prototype.slice.call(args)));
                    }
                } else if (typeof args[0] === 'string') {
                    // Function(Method Name) pattern.
                    try {
                        if (_localVariables._init === false) {
                            _localVariables._init = true;
                            if (typeof context.method.init !== 'undefined') {
                                context.method.init.apply(componentContext, [$(this)].concat(Array.prototype.slice.call(args, 1)));
                            } else {
                                context.init.apply(componentContext, [$(this)].concat(Array.prototype.slice.call(args, 1)));
                            }
                        }
                        return context.method[args[0]].apply(componentContext, [$(this)].concat(Array.prototype.slice.call(args, 1)));
                    } catch (e) {
                        _componentVariables.$helper.log(e.stack);
                    }
                } else {
                    _componentVariables.$helper.log('Compnent has been got invalid parameters.');
                }
            };

            if (args.length > 0 && self === window) {
                // Global basic function type - Function()
                var _localVariables = this.data;
                if (typeof this === 'object') {
                    if (typeof _localVariables === 'undefined') {
                        _localVariables = {
                            '_id': id,
                            '_init': false,
                            '_prototype': {},
                            '_options': $.extend({}, context.options)
                        };
                        this.data = _localVariables;
                        $.extend(_localVariables._prototype, context.method);
                    }
                }
                var componentContext = $.extend(window, {
                    local: _localVariables,
                }, _componentVariables);
                context.method.init.apply(componentContext, Array.prototype.slice.call(args));
            } else {
                // By element channing method type - Elem.method()
                if (typeof self !== 'undefined') {
                    return self.each(ElementBinder);
                }
            }
        };
        /*=================================================
         *= NOTE - HOMEWORKS ROUTE END
         *= DATE - 2017-01-10
         *================================================*/

        //============================================================================

        if (_componentVariables._bind === false) {
            _componentVariables._bind = true;
            name = name.split(',');
            for (var idx in name) {
                var id = $.trim(name[idx]);

                /* jshint ignore:start */
                /* @DATE 2017. 01. 09 */
                /* @USER Kenneth */
                /* @NOTE 런타임 매개변수 독립 사용을 위한 IIFE 설정. */
                !(function () {
                    var _id = id;
                    var bindFunc = function () {
                        return context.route.apply(this, [_id].concat(Array.prototype.slice.call(arguments)));
                    };
                    $.fn[_id] = bindFunc;
                    window[_id] = bindFunc;
                } ());
                /* jshint ignore:end */

                /* jshint ignore:start */
                /* @DATE 2016. 02. 22 */
                /* @USER Kenneth */
                /* @NOTE 함수 동적반영을 위한 jshint Escape 처리. */
                for (var key in this.method) {
                    if (typeof $.fn[key] === 'undefined') {
                        !(function () {
                            var method = key;
                            $.fn[method] = function () {
                                var _localVariables;
                                var element = this[0];
                                 if (typeof element === 'object') {
                                    _localVariables = element.data;
                                    if (typeof _localVariables === 'undefined') {
                                        _localVariables = {
                                            '_id': key,
                                            '_init': false,
                                            '_prototype': {},
                                            '_options': {}
                                        };
                                        element.data = _localVariables;
                                        $.extend(_localVariables._prototype, context.method);
                                    }

                                    var componentContext = $.extend(context, {
                                        local: _localVariables,
                                    }, _componentVariables);
                                }

                                if (_localVariables._init === false) {
                                    _localVariables._init = true;
                                    if (typeof context.method.init !== 'undefined') {
                                        context.method.init.apply(componentContext, [$(this)].concat(Array.prototype.slice.call(arg, 1)));
                                    } else {
                                        context.init.apply(componentContext, [$(this)].concat(Array.prototype.slice.call(arg, 1)));
                                    }
                                }

                                return context.method[method].apply(componentContext, [this].concat(Array.prototype.slice.call(arguments)));
                            };
                        }());
                    }
                }
                /* jshint ignore:end */
            }
        }
    }

    (function($) {
        /*******************************
         * NOTE - Additional jquery functions
         * DATE - 2017-01-09
         *******************************/
        if(typeof $.fn.scrollParent === 'undefined') {
            $.fn.scrollParent = function() {
                var overflowRegex = /(auto|scroll)/;
                var position = this.css('position');
                var excludeStaticParent = (position === 'absolute');
                var scrollParent = this.parents().filter(function() {
                    var $parent = $(this);
                    if (excludeStaticParent === true && $parent.css('position') === 'static') {
                        return false;
                    }
                    var overflowState = $parent.css(['overflow', 'overflowX', 'overflowY']);
                    return (overflowRegex).test(overflowState.overflow + overflowState.overflowX + overflowState.overflowY);
                }).eq(0);

                return position === 'fixed' || !scrollParent.length ? $( this[ 0 ].ownerDocument || document ) : scrollParent;
            };
        }
    }(jQuery));