<commit_before>/*!
	Copyright 2010 Mal Curtis
*/

// Support for UMD: https://github.com/umdjs/umd/blob/master/jqueryPluginCommonjs.js
// This allows for tools such as Browserify to compose the components together into a single HTTP request.
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS
        module.exports = factory(require('jquery'));
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {
    if (!$.fn.on) {
        // Patch jQuery 1.4.2 - 1.7 with an on function (that uses delegate).
        $.fn.on = function (events, selector, data, handler) {
            return this.delegate(selector, events, data, handler);
        };
    }

    // Public General Plugin methods $.DirtyForms
    $.extend({
        DirtyForms: {
            message: 'You\'ve made changes on this page which aren\'t saved. If you leave you will lose these changes.',
            title: 'Are you sure you want to do that?',
            dirtyClass: 'dirty',
            listeningClass: 'dirtylisten',
            ignoreClass: 'ignoredirty',
            choiceContinue: false,
            helpers: [],
            dialog: {
                selector: '#facebox .content',
                fire: function (message, title) {
                    var content = '
' + title + '

' + message + '

ContinueStop';
                    $.facebox(content);
                },
                bind: function () {
                    var close = function (decision) {
                        return function (e) {
                            if (e.type !== 'keydown' || (e.type === 'keydown' && e.keyCode === 27)) {
                                $(document).trigger('close.facebox');
                                decision(e);
                            }
                        };
                    };
                    $(document).bind('keydown.facebox', close(settings.decidingCancel));
                    $('#facebox .cancel, #facebox .close, #facebox_overlay').click(close(settings.decidingCancel));
                    $('#facebox .continue').click(close(settings.decidingContinue));
                },
                stash: function () {
                    var fb = $('#facebox');
                    return ($.trim(fb.html()) === '' || fb.css('display') != 'block') ?
					   false :
					   $('#facebox .content').clone(true);
                },
                refire: function (content, ev) {
                    $.facebox(content);
                }
            },
            /*<log>*/
            debug: false,

            dirtylog: function (msg) {
                dirtylog(msg);
            },
            /*</log>*/

            isDirty: function () {
                return $(':dirtylistening').dirtyForms('isDirty');
            },

            // DEPRECATED: Duplicate functionality.
            // Use $('html').addClass($.DirtyForms.ignoreClass); instead.
            disable: function () {
                $('html').addClass(settings.ignoreClass);
            },

            ignoreParentDocs: function () {
                settings.watchParentDocs = false;
            },

            choiceCommit: function (ev) {
                if (settings.deciding) {
                    $(document).trigger('choicecommit.dirtyforms');
                    if (settings.choiceContinue) {
                        settings.decidingContinue(ev);
                    } else {
                        settings.decidingCancel(ev);
                    }
                    $(document).trigger('choicecommitAfter.dirtyforms');
                }
            },

            isDeciding: function () {
                return settings.deciding;
            },

            decidingContinue: function (ev) {
                clearUnload(); // fix for chrome/safari
                ev.preventDefault();
                settings.dialogStash = false;
                $(document).trigger('decidingcontinued.dirtyforms');
                refire(settings.decidingEvent);
                settings.deciding = settings.currentForm = settings.decidingEvent = false;
            },

            decidingCancel: function (ev) {
                ev.preventDefault();
                $(document).trigger('decidingcancelled.dirtyforms');
                if (settings.dialog !== false && settings.dialogStash !== false && typeof settings.dialog.refire === 'function') {
                    dirtylog('Refiring the dialog with stashed content');
                    settings.dialog.refire(settings.dialogStash.html(), ev);
                }
                $(document).trigger('decidingcancelledAfter.dirtyforms');
                settings.deciding = settings.currentForm = settings.decidingEvent = settings.dialogStash = false;
            }
        }
    });

    // Create a custom selector $('form:dirty')
    $.extend($.expr[":"], {
        dirtylistening: function (a) {
            return $(a).hasClass(settings.listeningClass);
        },
        dirty: function (a) {
            return $(a).hasClass(settings.dirtyClass);
        }
    });

    // Public Element methods ( $('form').dirtyForms('methodName', args) )
    var methods = {
        init: function () {
            dirtylog('Adding forms to watch');
            bindExit();

            // exclude all HTML 4 except text and password, but include HTML 5 except search
            var inputSelector = "textarea,input:not([type='checkbox'],[type='radio'],[type='button']," +
        		"[type='image'],[type='submit'],[type='reset'],[type='file'],[type='search'])";

            // Initialize settings with the currently focused element (HTML 5 autofocus)
            var $focused = $(document.activeElement);
            if ($focused.is(inputSelector)) {
                settings.focused.element = $focused;
                settings.focused.value = $focused.val();
            }

            return this.each(function (e) {
                var $form = $(this);
                if (!$form.is('form')) return;

                dirtylog('Adding form ' + $form.attr('id') + ' to forms to watch');
                $form.addClass(settings.listeningClass)
                    .on('focus change', inputSelector, onFocus)
                    .on('change', "input[type='checkbox'],input[type='radio'],select", onSelectionChange)
                    .on('click', "input[type='reset']", onReset);
            });
        },
        // Returns true if any of the supplied elements are dirty
        isDirty: function () {
            var isDirty = false,
                node = this;
            if (focusedIsDirty()) {
                isDirty = true;
                return true;
            }
            this.each(function (e) {
                if ($(this).hasClass(settings.dirtyClass)) {
                    isDirty = true;
                    return true;
                }
            });
            $.each(settings.helpers, function (key, obj) {
                if ("isDirty" in obj) {
                    if (obj.isDirty(node)) {
                        isDirty = true;
                        return true;
                    }
                }
                // For backward compatibility, we call isNodeDirty (deprecated)
                if ("isNodeDirty" in obj) {
                    if (obj.isNodeDirty(node)) {
                        isDirty = true;
                        return true;
                    }
                }
            });

            dirtylog('isDirty returned ' + isDirty);
            return isDirty;
        },
        // Marks the element(s) that match the selector dirty
        setDirty: function () {
            dirtylog('setDirty called');
            return this.each(function (e) {
                $(this).addClass(settings.dirtyClass).parents('form').addClass(settings.dirtyClass);
            });
        },
        // "Cleans" this dirty form by essentially forgetting that it is dirty
        setClean: function () {
            dirtylog('setClean called');
            settings.focused = { element: false, value: false };

            return this.each(function (e) {
                var node = this, $node = $(this);

                // remove the current dirty class
                $node.removeClass(settings.dirtyClass);

                if ($node.is('form')) {
                    // remove all dirty classes from children
                    $node.find(':dirty').removeClass(settings.dirtyClass);
                } else {
                    // if this is last dirty child, set form clean
                    var $form = $node.parents('form');
                    if ($form.find(':dirty').length === 0) {
                        $form.removeClass(settings.dirtyClass);
                    }
                }

                // Clean helpers
                $.each(settings.helpers, function (key, obj) {
                    if ("setClean" in obj) {
                        obj.setClean(node);
                    }
                });
            });
        }

        // ADD NEW METHODS HERE
    };

    $.fn.dirtyForms = function (method) {
        // Method calling logic
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.dirtyForms');
        }
    };

    // Deprecated Methods for Backward Compatibility
    // DO NOT ADD MORE METHODS LIKE THESE, ADD METHODS WHERE INDICATED ABOVE
    $.fn.setDirty = function () {
        return this.dirtyForms('setDirty');
    };
    $.fn.isDirty = function () {
        return this.dirtyForms('isDirty');
    };
    $.fn.cleanDirty = function () {
        return this.dirtyForms('setClean');
    };

    // Private Properties and Methods
    var settings = $.DirtyForms = $.extend({
        watchParentDocs: true,
        exitBound: false,
        formStash: false,
        dialogStash: false,
        deciding: false,
        decidingEvent: false,
        currentForm: false,
        /*<log>*/
        hasFirebug: "console" in window && "firebug" in window.console,
        hasConsoleLog: "console" in window && "log" in window.console,
        /*</log>*/
        focused: { "element": false, "value": false }
    }, $.DirtyForms);

    var onReset = function () {
        $(this).parents('form').dirtyForms('setClean');
        if (settings.onFormCheck) {
            settings.onFormCheck();
        }
    };

    var onSelectionChange = function () {
        if (isIgnored($(this))) return;
        $(this).dirtyForms('setDirty');
        if (settings.onFormCheck) {
            settings.onFormCheck();
        }
    };

    var onFocus = function () {
        var $this = $(this);
        if (focusedIsDirty() && !isIgnored($this)) {
            settings.focused.element.dirtyForms('setDirty');
            if (settings.onFormCheck) {
                settings.onFormCheck();
            }
        }
        settings.focused.element = $this;
        settings.focused.value = $this.val();
    };

    var focusedIsDirty = function () {
        // Check, whether the value of focused element has changed
        return settings.focused.element &&
			(settings.focused.element.val() !== settings.focused.value);
    };

    /*<log>*/
    var dirtylog = function (msg) {
        if (!settings.debug) return;
        msg = "[DirtyForms] " + msg;
        if (settings.hasFirebug) {
            console.log(msg);
        } else if (settings.hasConsoleLog) {
            window.console.log(msg);
        } else {
            alert(msg);
        }
    };
    /*</log>*/

    var bindExit = function () {
        if (settings.exitBound) return;
        var inIframe = (top !== self);

        $(document).on('click', 'a[href]', aBindFn)
                   .on('submit', 'form', formBindFn);
        $(window).bind('beforeunload', beforeunloadBindFn);
        if (settings.watchParentDocs && inIframe) {
            $(top.document).on('click', 'a[href]', aBindFn)
                           .on('submit', 'form', formBindFn);
            $(top.window).bind('beforeunload', beforeunloadBindFn);
        }

        settings.exitBound = true;
    };

    var getIgnoreAnchorSelector = function () {
        var result = '';
        $.each(settings.helpers, function (key, obj) {
            if ("ignoreAnchorSelector" in obj) {
                if (result.length > 0) { result += ','; }
                result += obj.ignoreAnchorSelector;
            }
        });
        return result;
    };

    var aBindFn = function (ev) {
        if (!$(this).is(getIgnoreAnchorSelector()) && !isDifferentTarget($(this))) {
            bindFn(ev);
        }
    };

    var formBindFn = function (ev) {
        settings.currentForm = this;
        bindFn(ev);
    };

    var beforeunloadBindFn = function (ev) {
        var result = bindFn(ev);

        if (result && settings.doubleunloadfix !== true) {
            dirtylog('Before unload will be called, resetting');
            settings.deciding = false;
        }

        settings.doubleunloadfix = true;
        setTimeout(function () { settings.doubleunloadfix = false; }, 200);

        // Bug Fix: Only return the result if it is a string,
        // otherwise don't return anything.
        if (typeof (result) == 'string') {
            ev = ev || window.event;

            // For IE and Firefox prior to version 4
            if (ev) {
                ev.returnValue = result;
            }

            // For Safari
            return result;
        }
    };

    var bindFn = function (ev) {
        var $element = $(ev.target), eventType = ev.type;
        dirtylog('Entering: Leaving Event fired, type: ' + eventType + ', element: ' + ev.target + ', class: ' + $element.attr('class') + ' and id: ' + ev.target.id);

        // Important: Do this check before calling clearUnload()
        if (ev.isDefaultPrevented()) {
            dirtylog('Leaving: Event has been stopped elsewhere');
            return false;
        }

        if (eventType == 'beforeunload' && settings.doubleunloadfix) {
            dirtylog('Skip this unload, Firefox bug triggers the unload event multiple times');
            settings.doubleunloadfix = false;
            return false;
        }

        if (isIgnored($element)) {
            dirtylog('Leaving: Element has ignore class or a descendant of an ignored element');
            clearUnload();
            return false;
        }

        if (settings.deciding) {
            dirtylog('Leaving: Already in the deciding process');
            return false;
        }

        if (!settings.isDirty()) {
            dirtylog('Leaving: Not dirty');
            clearUnload();
            return false;
        }

        if (eventType == 'submit' && $element.dirtyForms('isDirty')) {
            dirtylog('Leaving: Form submitted is a dirty form');
            clearUnload();
            return true;
        }

        // Callback for page access in current state
        $(document).trigger('defer.dirtyforms');

        if (eventType == 'beforeunload') {
            dirtylog('Returning to beforeunload browser handler with: ' + settings.message);
            return settings.message;
        }
        if (!settings.dialog) return;

        // Using the GUI dialog...
        settings.deciding = true;
        settings.decidingEvent = ev;
        dirtylog('Setting deciding active');

        if (typeof settings.dialog.stash === 'function') {
            dirtylog('Saving dialog content');
            settings.dialogStash = settings.dialog.stash();
            dirtylog(settings.dialogStash);
        }

        ev.preventDefault();
        ev.stopImmediatePropagation();

        if (typeof settings.dialog.selector === 'string' && $element.is('form') && $element.parents(settings.dialog.selector).length > 0) {
            dirtylog('Stashing form');
            settings.formStash = $element.clone(true).hide();
        } else {
            settings.formStash = false;
        }

        dirtylog('Deferring to the dialog');
        settings.dialog.fire(settings.message, settings.title);
        if (typeof settings.dialog.bind === 'function')
            settings.dialog.bind();
    };

    var isDifferentTarget = function ($element) {
        var aTarget = $element.attr('target');
        return typeof aTarget === 'string' ? aTarget.toLowerCase() === '_blank' : false;
    };

    var isIgnored = function ($element) {
        return $element.closest('.' + settings.ignoreClass).length > 0;
    };

    var clearUnload = function () {
        // I'd like to just be able to unbind this but there seems
        // to be a bug in jQuery which doesn't unbind onbeforeunload
        dirtylog('Clearing the beforeunload event');
        $(window).unbind('beforeunload', beforeunloadBindFn);
        window.onbeforeunload = null;
        $(document).trigger('beforeunload.dirtyforms');
    };

    var refire = function (e) {
        $(document).trigger('beforeRefire.dirtyforms')
                   .trigger('beforeunload.dirtyforms');
        if (e.type === 'click') {
            dirtylog("Refiring click event");
            var event = new $.Event('click');
            $(e.target).trigger(event);
            if (!event.isDefaultPrevented()) {
                var href = $(e.target).closest('[href]').attr('href');
                dirtylog('Sending location to ' + href);
                location.href = href;
                return;
            }
        } else {
            dirtylog("Refiring " + e.type + " event on " + e.target);
            var target;
            if (settings.formStash) {
                dirtylog('Appending stashed form to body');
                target = settings.formStash;
                $('body').append(target);
            }
            else {
                target = $(e.target).closest('form');
            }
            target.trigger(e.type);
        }
    };

}));
<commit_msg>Use ECMAScript 5's strict mode<commit_after>/*!
	Copyright 2010 Mal Curtis
*/

// Support for UMD: https://github.com/umdjs/umd/blob/master/jqueryPluginCommonjs.js
// This allows for tools such as Browserify to compose the components together into a single HTTP request.
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS
        module.exports = factory(require('jquery'));
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {
    // Use ECMAScript 5's strict mode
    "use strict";

    if (!$.fn.on) {
        // Patch jQuery 1.4.2 - 1.7 with an on function (that uses delegate).
        $.fn.on = function (events, selector, data, handler) {
            return this.delegate(selector, events, data, handler);
        };
    }

    // Public General Plugin methods $.DirtyForms
    $.extend({
        DirtyForms: {
            message: 'You\'ve made changes on this page which aren\'t saved. If you leave you will lose these changes.',
            title: 'Are you sure you want to do that?',
            dirtyClass: 'dirty',
            listeningClass: 'dirtylisten',
            ignoreClass: 'ignoredirty',
            choiceContinue: false,
            helpers: [],
            dialog: {
                selector: '#facebox .content',
                fire: function (message, title) {
                    var content = '
' + title + '

' + message + '

ContinueStop';
                    $.facebox(content);
                },
                bind: function () {
                    var close = function (decision) {
                        return function (e) {
                            if (e.type !== 'keydown' || (e.type === 'keydown' && e.keyCode === 27)) {
                                $(document).trigger('close.facebox');
                                decision(e);
                            }
                        };
                    };
                    $(document).bind('keydown.facebox', close(settings.decidingCancel));
                    $('#facebox .cancel, #facebox .close, #facebox_overlay').click(close(settings.decidingCancel));
                    $('#facebox .continue').click(close(settings.decidingContinue));
                },
                stash: function () {
                    var fb = $('#facebox');
                    return ($.trim(fb.html()) === '' || fb.css('display') != 'block') ?
					   false :
					   $('#facebox .content').clone(true);
                },
                refire: function (content, ev) {
                    $.facebox(content);
                }
            },
            /*<log>*/
            debug: false,

            dirtylog: function (msg) {
                dirtylog(msg);
            },
            /*</log>*/

            isDirty: function () {
                return $(':dirtylistening').dirtyForms('isDirty');
            },

            // DEPRECATED: Duplicate functionality.
            // Use $('html').addClass($.DirtyForms.ignoreClass); instead.
            disable: function () {
                $('html').addClass(settings.ignoreClass);
            },

            ignoreParentDocs: function () {
                settings.watchParentDocs = false;
            },

            choiceCommit: function (ev) {
                if (settings.deciding) {
                    $(document).trigger('choicecommit.dirtyforms');
                    if (settings.choiceContinue) {
                        settings.decidingContinue(ev);
                    } else {
                        settings.decidingCancel(ev);
                    }
                    $(document).trigger('choicecommitAfter.dirtyforms');
                }
            },

            isDeciding: function () {
                return settings.deciding;
            },

            decidingContinue: function (ev) {
                clearUnload(); // fix for chrome/safari
                ev.preventDefault();
                settings.dialogStash = false;
                $(document).trigger('decidingcontinued.dirtyforms');
                refire(settings.decidingEvent);
                settings.deciding = settings.currentForm = settings.decidingEvent = false;
            },

            decidingCancel: function (ev) {
                ev.preventDefault();
                $(document).trigger('decidingcancelled.dirtyforms');
                if (settings.dialog !== false && settings.dialogStash !== false && typeof settings.dialog.refire === 'function') {
                    dirtylog('Refiring the dialog with stashed content');
                    settings.dialog.refire(settings.dialogStash.html(), ev);
                }
                $(document).trigger('decidingcancelledAfter.dirtyforms');
                settings.deciding = settings.currentForm = settings.decidingEvent = settings.dialogStash = false;
            }
        }
    });

    // Create a custom selector $('form:dirty')
    $.extend($.expr[":"], {
        dirtylistening: function (a) {
            return $(a).hasClass(settings.listeningClass);
        },
        dirty: function (a) {
            return $(a).hasClass(settings.dirtyClass);
        }
    });

    // Public Element methods ( $('form').dirtyForms('methodName', args) )
    var methods = {
        init: function () {
            dirtylog('Adding forms to watch');
            bindExit();

            // exclude all HTML 4 except text and password, but include HTML 5 except search
            var inputSelector = "textarea,input:not([type='checkbox'],[type='radio'],[type='button']," +
        		"[type='image'],[type='submit'],[type='reset'],[type='file'],[type='search'])";

            // Initialize settings with the currently focused element (HTML 5 autofocus)
            var $focused = $(document.activeElement);
            if ($focused.is(inputSelector)) {
                settings.focused.element = $focused;
                settings.focused.value = $focused.val();
            }

            return this.each(function (e) {
                var $form = $(this);
                if (!$form.is('form')) return;

                dirtylog('Adding form ' + $form.attr('id') + ' to forms to watch');
                $form.addClass(settings.listeningClass)
                    .on('focus change', inputSelector, onFocus)
                    .on('change', "input[type='checkbox'],input[type='radio'],select", onSelectionChange)
                    .on('click', "input[type='reset']", onReset);
            });
        },
        // Returns true if any of the supplied elements are dirty
        isDirty: function () {
            var isDirty = false,
                node = this;
            if (focusedIsDirty()) {
                isDirty = true;
                return true;
            }
            this.each(function (e) {
                if ($(this).hasClass(settings.dirtyClass)) {
                    isDirty = true;
                    return true;
                }
            });
            $.each(settings.helpers, function (key, obj) {
                if ("isDirty" in obj) {
                    if (obj.isDirty(node)) {
                        isDirty = true;
                        return true;
                    }
                }
                // For backward compatibility, we call isNodeDirty (deprecated)
                if ("isNodeDirty" in obj) {
                    if (obj.isNodeDirty(node)) {
                        isDirty = true;
                        return true;
                    }
                }
            });

            dirtylog('isDirty returned ' + isDirty);
            return isDirty;
        },
        // Marks the element(s) that match the selector dirty
        setDirty: function () {
            dirtylog('setDirty called');
            return this.each(function (e) {
                $(this).addClass(settings.dirtyClass).parents('form').addClass(settings.dirtyClass);
            });
        },
        // "Cleans" this dirty form by essentially forgetting that it is dirty
        setClean: function () {
            dirtylog('setClean called');
            settings.focused = { element: false, value: false };

            return this.each(function (e) {
                var node = this, $node = $(this);

                // remove the current dirty class
                $node.removeClass(settings.dirtyClass);

                if ($node.is('form')) {
                    // remove all dirty classes from children
                    $node.find(':dirty').removeClass(settings.dirtyClass);
                } else {
                    // if this is last dirty child, set form clean
                    var $form = $node.parents('form');
                    if ($form.find(':dirty').length === 0) {
                        $form.removeClass(settings.dirtyClass);
                    }
                }

                // Clean helpers
                $.each(settings.helpers, function (key, obj) {
                    if ("setClean" in obj) {
                        obj.setClean(node);
                    }
                });
            });
        }

        // ADD NEW METHODS HERE
    };

    $.fn.dirtyForms = function (method) {
        // Method calling logic
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.dirtyForms');
        }
    };

    // Deprecated Methods for Backward Compatibility
    // DO NOT ADD MORE METHODS LIKE THESE, ADD METHODS WHERE INDICATED ABOVE
    $.fn.setDirty = function () {
        return this.dirtyForms('setDirty');
    };
    $.fn.isDirty = function () {
        return this.dirtyForms('isDirty');
    };
    $.fn.cleanDirty = function () {
        return this.dirtyForms('setClean');
    };

    // Private Properties and Methods
    var settings = $.DirtyForms = $.extend({
        watchParentDocs: true,
        exitBound: false,
        formStash: false,
        dialogStash: false,
        deciding: false,
        decidingEvent: false,
        currentForm: false,
        /*<log>*/
        hasFirebug: "console" in window && "firebug" in window.console,
        hasConsoleLog: "console" in window && "log" in window.console,
        /*</log>*/
        focused: { "element": false, "value": false }
    }, $.DirtyForms);

    var onReset = function () {
        $(this).parents('form').dirtyForms('setClean');
        if (settings.onFormCheck) {
            settings.onFormCheck();
        }
    };

    var onSelectionChange = function () {
        if (isIgnored($(this))) return;
        $(this).dirtyForms('setDirty');
        if (settings.onFormCheck) {
            settings.onFormCheck();
        }
    };

    var onFocus = function () {
        var $this = $(this);
        if (focusedIsDirty() && !isIgnored($this)) {
            settings.focused.element.dirtyForms('setDirty');
            if (settings.onFormCheck) {
                settings.onFormCheck();
            }
        }
        settings.focused.element = $this;
        settings.focused.value = $this.val();
    };

    var focusedIsDirty = function () {
        // Check, whether the value of focused element has changed
        return settings.focused.element &&
			(settings.focused.element.val() !== settings.focused.value);
    };

    /*<log>*/
    var dirtylog = function (msg) {
        if (!settings.debug) return;
        msg = "[DirtyForms] " + msg;
        if (settings.hasFirebug) {
            console.log(msg);
        } else if (settings.hasConsoleLog) {
            window.console.log(msg);
        } else {
            alert(msg);
        }
    };
    /*</log>*/

    var bindExit = function () {
        if (settings.exitBound) return;
        var inIframe = (top !== self);

        $(document).on('click', 'a[href]', aBindFn)
                   .on('submit', 'form', formBindFn);
        $(window).bind('beforeunload', beforeunloadBindFn);
        if (settings.watchParentDocs && inIframe) {
            $(top.document).on('click', 'a[href]', aBindFn)
                           .on('submit', 'form', formBindFn);
            $(top.window).bind('beforeunload', beforeunloadBindFn);
        }

        settings.exitBound = true;
    };

    var getIgnoreAnchorSelector = function () {
        var result = '';
        $.each(settings.helpers, function (key, obj) {
            if ("ignoreAnchorSelector" in obj) {
                if (result.length > 0) { result += ','; }
                result += obj.ignoreAnchorSelector;
            }
        });
        return result;
    };

    var aBindFn = function (ev) {
        if (!$(this).is(getIgnoreAnchorSelector()) && !isDifferentTarget($(this))) {
            bindFn(ev);
        }
    };

    var formBindFn = function (ev) {
        settings.currentForm = this;
        bindFn(ev);
    };

    var beforeunloadBindFn = function (ev) {
        var result = bindFn(ev);

        if (result && settings.doubleunloadfix !== true) {
            dirtylog('Before unload will be called, resetting');
            settings.deciding = false;
        }

        settings.doubleunloadfix = true;
        setTimeout(function () { settings.doubleunloadfix = false; }, 200);

        // Bug Fix: Only return the result if it is a string,
        // otherwise don't return anything.
        if (typeof (result) == 'string') {
            ev = ev || window.event;

            // For IE and Firefox prior to version 4
            if (ev) {
                ev.returnValue = result;
            }

            // For Safari
            return result;
        }
    };

    var bindFn = function (ev) {
        var $element = $(ev.target), eventType = ev.type;
        dirtylog('Entering: Leaving Event fired, type: ' + eventType + ', element: ' + ev.target + ', class: ' + $element.attr('class') + ' and id: ' + ev.target.id);

        // Important: Do this check before calling clearUnload()
        if (ev.isDefaultPrevented()) {
            dirtylog('Leaving: Event has been stopped elsewhere');
            return false;
        }

        if (eventType == 'beforeunload' && settings.doubleunloadfix) {
            dirtylog('Skip this unload, Firefox bug triggers the unload event multiple times');
            settings.doubleunloadfix = false;
            return false;
        }

        if (isIgnored($element)) {
            dirtylog('Leaving: Element has ignore class or a descendant of an ignored element');
            clearUnload();
            return false;
        }

        if (settings.deciding) {
            dirtylog('Leaving: Already in the deciding process');
            return false;
        }

        if (!settings.isDirty()) {
            dirtylog('Leaving: Not dirty');
            clearUnload();
            return false;
        }

        if (eventType == 'submit' && $element.dirtyForms('isDirty')) {
            dirtylog('Leaving: Form submitted is a dirty form');
            clearUnload();
            return true;
        }

        // Callback for page access in current state
        $(document).trigger('defer.dirtyforms');

        if (eventType == 'beforeunload') {
            dirtylog('Returning to beforeunload browser handler with: ' + settings.message);
            return settings.message;
        }
        if (!settings.dialog) return;

        // Using the GUI dialog...
        settings.deciding = true;
        settings.decidingEvent = ev;
        dirtylog('Setting deciding active');

        if (typeof settings.dialog.stash === 'function') {
            dirtylog('Saving dialog content');
            settings.dialogStash = settings.dialog.stash();
            dirtylog(settings.dialogStash);
        }

        ev.preventDefault();
        ev.stopImmediatePropagation();

        if (typeof settings.dialog.selector === 'string' && $element.is('form') && $element.parents(settings.dialog.selector).length > 0) {
            dirtylog('Stashing form');
            settings.formStash = $element.clone(true).hide();
        } else {
            settings.formStash = false;
        }

        dirtylog('Deferring to the dialog');
        settings.dialog.fire(settings.message, settings.title);
        if (typeof settings.dialog.bind === 'function')
            settings.dialog.bind();
    };

    var isDifferentTarget = function ($element) {
        var aTarget = $element.attr('target');
        return typeof aTarget === 'string' ? aTarget.toLowerCase() === '_blank' : false;
    };

    var isIgnored = function ($element) {
        return $element.closest('.' + settings.ignoreClass).length > 0;
    };

    var clearUnload = function () {
        // I'd like to just be able to unbind this but there seems
        // to be a bug in jQuery which doesn't unbind onbeforeunload
        dirtylog('Clearing the beforeunload event');
        $(window).unbind('beforeunload', beforeunloadBindFn);
        window.onbeforeunload = null;
        $(document).trigger('beforeunload.dirtyforms');
    };

    var refire = function (e) {
        $(document).trigger('beforeRefire.dirtyforms')
                   .trigger('beforeunload.dirtyforms');
        if (e.type === 'click') {
            dirtylog("Refiring click event");
            var event = new $.Event('click');
            $(e.target).trigger(event);
            if (!event.isDefaultPrevented()) {
                var href = $(e.target).closest('[href]').attr('href');
                dirtylog('Sending location to ' + href);
                location.href = href;
                return;
            }
        } else {
            dirtylog("Refiring " + e.type + " event on " + e.target);
            var target;
            if (settings.formStash) {
                dirtylog('Appending stashed form to body');
                target = settings.formStash;
                $('body').append(target);
            }
            else {
                target = $(e.target).closest('form');
            }
            target.trigger(e.type);
        }
    };

}));
<|endoftext|>


<commit_before>let request = require('request-promise')
const log = require('./logger')

let singleton = null

module.exports = function (options = {}) {
  if (singleton) return singleton

  const defaultOptions = {
    debug: false,
    json: true,
    cheerio: false,
    headers: {
      // a lot of web service do not want to be called by robots and then check the user agent to
      // be sure they are called by a browser. This user agent works most of the time.
      'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:36.0) ' +
                    'Gecko/20100101 Firefox/36.0'
    },
    followAllRedirects: true
  }

  options = Object.assign(defaultOptions, options)

  const requestOptions = {}
  if (options.debug) require('request-debug')(request)

  requestOptions.json = options.json
  requestOptions.jar = options.jar
  requestOptions.headers = options.headers
  requestOptions.followAllRedirects = options.followAllRedirects

  if (options.cheerio) requestOptions.transform = (body) => require('cheerio').load(body)

  request = request.defaults(requestOptions)

  log('debug', requestOptions, 'Getting a new request instance with the following options')

  return request
}
<commit_msg>Added strictSSL option<commit_after>let request = require('request-promise')
const log = require('./logger')

let singleton = null

module.exports = function (options = {}) {
  if (singleton) return singleton

  const defaultOptions = {
    debug: false,
    json: true,
    cheerio: false,
    strictSSL: false,
    headers: {
      // a lot of web service do not want to be called by robots and then check the user agent to
      // be sure they are called by a browser. This user agent works most of the time.
      'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:36.0) ' +
                    'Gecko/20100101 Firefox/36.0'
    },
    followAllRedirects: true
  }

  options = Object.assign(defaultOptions, options)

  if (options.debug) require('request-debug')(request)

  const requestOptions = {}

  requestOptions.json = options.json
  requestOptions.jar = options.jar
  requestOptions.headers = options.headers
  requestOptions.followAllRedirects = options.followAllRedirects
  requestOptions.strictSSL = options.strictSSL

  if (options.cheerio) requestOptions.transform = (body) => require('cheerio').load(body)

  request = request.defaults(requestOptions)

  log('debug', requestOptions, 'Getting a new request instance with the following options')

  return request
}
<|endoftext|>


<commit_before>// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @externs_url https://raw.githubusercontent.com/google/closure-compiler/master/contrib/externs/jquery-1.9.js
// ==/ClosureCompiler==
/** @preserve    Library by Daniel Cohen Gindi (danielgindi@gmail.com)
    MIT License!
 */
(function($){

    function touchById(touches, touchId) {
        for (var i = 0; i < touches.length; i++) {
            if (touches[i].identifier === touchId) return touches[i];
        }
        return null;
    }

    /** @expose */
    $.fn.fakebutton = function (tabIndex) {
        if (tabIndex !== false) {
            if (tabIndex === true) {
                tabIndex = 0;
            }
            this.each(function(){
                if (this.tabIndex === -1) {
                    this.tabIndex = tabIndex;
                }
            });
        }

        this.unfakebutton();

        this.on('keydown.fakebutton', function (event) {
            var $this = $(this);
            if (event.target === this && (event.which === 13 || event.which === 32)) {
                event.preventDefault();
                $this
                    .addClass('active') // Add active class
                    .on('keyup.fakebutton', function (event) { // Hook event for keyup
                        if (event.target === this && (event.which === 13 || event.which === 32)) {
                            event.preventDefault();
                            $this
                                .removeClass('active') // Remove active class
                                .off('keyup.fakebutton') // Unhook keypress
                                .click(); // Trigger click
                        }
                    });
            }
        }).on('touchstart.fakebutton', function (startEvent) {
            var $this = $(this),
                didScroll = false,
                scrollHandler = function () {
                    didScroll = true;
                },
                touchId = startEvent.originalEvent.changedTouches[0].identifier,
                active = true,
                ghostBuster = 0; // Some Android browsers to not support preventDefault(), we need to do catch the ghost events

            var onCancel = function (event) {
                $this
                    .removeClass('active') // Remove active class
                    .off('touchmove.fakebutton') // Unhook touchmove
                    .off('touchend.fakebutton') // Unhook touchend
                    .off('touchcancel.fakebutton'); // Unhook touchcancel

                if (event.type === 'touchcancel') {
                    $this.off('mousedown.fakebutton');
                }

                trackedScrolling.off('scroll.fakebutton', scrollHandler) // Stop tracking scroll;
            };

            var trackedScrolling = $this.parents().add(window);
            trackedScrolling.on('scroll.fakebutton', scrollHandler); // Start tracking scroll to prevent "click" after scroll

            $this
                .addClass('active') // Add active class
                .on('touchmove.fakebutton', function (event) { // Start tracking touch movement to see if we are still on top

                    var touch = touchById(event.originalEvent.changedTouches, touchId);
                    if (!touch) return;

                    var element = document.elementFromPoint(touch.pageX, touch.pageY);
                    active = element && (this === element || $.contains(this, element));
                    $this.toggleClass('active', !!active);

                }).on('touchend.fakebutton', function (event) { // Hook event for keyup

                    var touch = touchById(event.originalEvent.changedTouches, touchId);
                    if (!touch) return;

                    onCancel(event);

                    if (active && !didScroll && !event.isDefaultPrevented() && document.contains(this)) {
                        var fakeEvent = $.Event('click');
                        $.each(['target', 'clientX', 'clientY', 'offsetX', 'offsetY', 'screenX', 'screenY', 'pageX', 'pageY'],
                            function () {
                                fakeEvent[this] = startEvent[this];
                                fakeEvent[this] = touch[this];
                            });
                        event.preventDefault();
                        setTimeout(function () {
                            $this.trigger(fakeEvent);
                        }, 0);
                    }

                }).on('touchcancel.fakebutton', onCancel)
                .on('mousedown.fakebutton', function (event) {
                    if ((+new Date - ghostBuster) <= 500) { // Bust the ghost mouse events on old Android browsers
                        event.preventDefault();
                        event.stopPropagation();
                    }
                    $this.off('mousedown.fakebutton');
                });
        });
        return this;
    };

    /** @expose */
    $.fn.unfakebutton = function () {
        $(this).off('.fakebutton');
        return this;
    };

})(jQuery);
<commit_msg>Support amd/cjs<commit_after>// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @externs_url https://raw.githubusercontent.com/google/closure-compiler/master/contrib/externs/jquery-1.9.js
// ==/ClosureCompiler==
/** @preserve    Library by Daniel Cohen Gindi (danielgindi@gmail.com)
    MIT License!
 */
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define('jquery.fakebutton', ['jquery'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('jquery'));
    } else {
        /*root.fakebutton = */factory(root.jQuery);
    }
}(this, function ($) {
    'use strict';

    function touchById(touches, touchId) {
        for (var i = 0; i < touches.length; i++) {
            if (touches[i].identifier === touchId) return touches[i];
        }
        return null;
    }

    /** @expose */
    $.fn.fakebutton = function (tabIndex) {
        if (tabIndex !== false) {
            if (tabIndex === true) {
                tabIndex = 0;
            }
            this.each(function(){
                if (this.tabIndex === -1) {
                    this.tabIndex = tabIndex;
                }
            });
        }

        this.unfakebutton();

        this.on('keydown.fakebutton', function (event) {
            var $this = $(this);
            if (event.target === this && (event.which === 13 || event.which === 32)) {
                event.preventDefault();
                $this
                    .addClass('active') // Add active class
                    .on('keyup.fakebutton', function (event) { // Hook event for keyup
                        if (event.target === this && (event.which === 13 || event.which === 32)) {
                            event.preventDefault();
                            $this
                                .removeClass('active') // Remove active class
                                .off('keyup.fakebutton') // Unhook keypress
                                .click(); // Trigger click
                        }
                    });
            }
        }).on('touchstart.fakebutton', function (startEvent) {
            var $this = $(this),
                didScroll = false,
                scrollHandler = function () {
                    didScroll = true;
                },
                touchId = startEvent.originalEvent.changedTouches[0].identifier,
                active = true,
                ghostBuster = 0; // Some Android browsers to not support preventDefault(), we need to do catch the ghost events

            var onCancel = function (event) {
                $this
                    .removeClass('active') // Remove active class
                    .off('touchmove.fakebutton') // Unhook touchmove
                    .off('touchend.fakebutton') // Unhook touchend
                    .off('touchcancel.fakebutton'); // Unhook touchcancel

                if (event.type === 'touchcancel') {
                    $this.off('mousedown.fakebutton');
                }

                trackedScrolling.off('scroll.fakebutton', scrollHandler) // Stop tracking scroll;
            };

            var trackedScrolling = $this.parents().add(window);
            trackedScrolling.on('scroll.fakebutton', scrollHandler); // Start tracking scroll to prevent "click" after scroll

            $this
                .addClass('active') // Add active class
                .on('touchmove.fakebutton', function (event) { // Start tracking touch movement to see if we are still on top

                    var touch = touchById(event.originalEvent.changedTouches, touchId);
                    if (!touch) return;

                    var element = document.elementFromPoint(touch.pageX, touch.pageY);
                    active = element && (this === element || $.contains(this, element));
                    $this.toggleClass('active', !!active);

                }).on('touchend.fakebutton', function (event) { // Hook event for keyup

                    var touch = touchById(event.originalEvent.changedTouches, touchId);
                    if (!touch) return;

                    onCancel(event);

                    if (active && !didScroll && !event.isDefaultPrevented() && document.contains(this)) {
                        var fakeEvent = $.Event('click');
                        $.each(['target', 'clientX', 'clientY', 'offsetX', 'offsetY', 'screenX', 'screenY', 'pageX', 'pageY'],
                            function () {
                                fakeEvent[this] = startEvent[this];
                                fakeEvent[this] = touch[this];
                            });
                        event.preventDefault();
                        setTimeout(function () {
                            $this.trigger(fakeEvent);
                        }, 0);
                    }

                }).on('touchcancel.fakebutton', onCancel)
                .on('mousedown.fakebutton', function (event) {
                    if ((+new Date - ghostBuster) <= 500) { // Bust the ghost mouse events on old Android browsers
                        event.preventDefault();
                        event.stopPropagation();
                    }
                    $this.off('mousedown.fakebutton');
                });
        });
        return this;
    };

    /** @expose */
    $.fn.unfakebutton = function () {
        $(this).off('.fakebutton');
        return this;
    };

}));
<|endoftext|>


<commit_before>/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
 * Licensed under the MIT License (LICENSE.txt).
 *
 * Version: 3.1.5-pre
 *
 * Requires: jQuery 1.2.2+
 */

(function (factory) {
    if ( typeof define === 'function' && define.amd ) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS style for Browserify
        module.exports = factory;
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

    var toFix  = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
        toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
                    ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
        slice  = Array.prototype.slice,
        lowestDelta;

    if ( $.event.fixHooks ) {
        for ( var i = toFix.length; i; ) {
            $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
        }
    }

    $.event.special.mousewheel = {
        setup: function() {
            if ( this.addEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.addEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = handler;
            }
        },

        teardown: function() {
            if ( this.removeEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.removeEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = null;
            }
        }
    };

    $.fn.extend({
        mousewheel: function(fn) {
            return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
        },

        unmousewheel: function(fn) {
            return this.unbind('mousewheel', fn);
        }
    });

    function handler(event) {
        var orgEvent   = event || window.event,
            args       = slice.call(arguments, 1),
            delta      = 0,
            deltaX     = 0,
            deltaY     = 0,
            absDelta   = 0;
        event = $.event.fix(orgEvent);
        event.type = 'mousewheel';

        // Old school scrollwheel delta
        if ( 'detail'      in orgEvent ) { deltaY = orgEvent.detail * -1;      }
        if ( 'wheelDelta'  in orgEvent ) { deltaY = orgEvent.wheelDelta;       }
        if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY;      }
        if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }

        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
        if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
            deltaY = 0;
            deltaX = delta * -1;
        }

        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
        delta = deltaY === 0 ? deltaX : deltaY;

        // New school wheel delta (wheel event)
        if ( 'deltaY' in orgEvent ) {
          deltaY = orgEvent.deltaY * -1;
          delta  = deltaY;
        }
        if ( 'deltaX' in orgEvent ) {
          deltaX = orgEvent.deltaX;
          delta  = detlaX * -1;
        }

        // No change actually happened, no reason to go any further
        if ( deltaY === 0 && deltaX === 0 ) { return; }

        // Store lowest absolute delta to normalize the delta values
        absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
        if ( !lowestDelta || absDelta < lowestDelta ) {
          lowestDelta = absDelta;
        }

        // Get a whole, normalized value for the deltas
        delta  = Math[ delta  >= 1 ? 'floor' : 'ceil' ](delta  / lowestDelta);
        deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
        deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);

        // Add event and delta to the front of the arguments
        args.unshift(event, delta, deltaX, deltaY);

        return ($.event.dispatch || $.event.handle).apply(this, args);
    }

}));
<commit_msg>Attempt to better handle multiple devices that give a different lowestDelta value<commit_after>/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
 * Licensed under the MIT License (LICENSE.txt).
 *
 * Version: 3.1.5-pre
 *
 * Requires: jQuery 1.2.2+
 */

(function (factory) {
    if ( typeof define === 'function' && define.amd ) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS style for Browserify
        module.exports = factory;
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

    var toFix  = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
        toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
                    ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
        slice  = Array.prototype.slice,
        nullLowestDeltaTimeout, lowestDelta;

    if ( $.event.fixHooks ) {
        for ( var i = toFix.length; i; ) {
            $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
        }
    }

    $.event.special.mousewheel = {
        setup: function() {
            if ( this.addEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.addEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = handler;
            }
        },

        teardown: function() {
            if ( this.removeEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.removeEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = null;
            }
        }
    };

    $.fn.extend({
        mousewheel: function(fn) {
            return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
        },

        unmousewheel: function(fn) {
            return this.unbind('mousewheel', fn);
        }
    });

    function handler(event) {
        var orgEvent   = event || window.event,
            args       = slice.call(arguments, 1),
            delta      = 0,
            deltaX     = 0,
            deltaY     = 0,
            absDelta   = 0;
        event = $.event.fix(orgEvent);
        event.type = 'mousewheel';

        // Old school scrollwheel delta
        if ( 'detail'      in orgEvent ) { deltaY = orgEvent.detail * -1;      }
        if ( 'wheelDelta'  in orgEvent ) { deltaY = orgEvent.wheelDelta;       }
        if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY;      }
        if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }

        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
        if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
            deltaY = 0;
            deltaX = delta * -1;
        }

        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
        delta = deltaY === 0 ? deltaX : deltaY;

        // New school wheel delta (wheel event)
        if ( 'deltaY' in orgEvent ) {
          deltaY = orgEvent.deltaY * -1;
          delta  = deltaY;
        }
        if ( 'deltaX' in orgEvent ) {
          deltaX = orgEvent.deltaX;
          delta  = detlaX * -1;
        }

        // No change actually happened, no reason to go any further
        if ( deltaY === 0 && deltaX === 0 ) { return; }

        // Store lowest absolute delta to normalize the delta values
        absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
        if ( !lowestDelta || absDelta < lowestDelta ) {
          lowestDelta = absDelta;
        }

        // Get a whole, normalized value for the deltas
        delta  = Math[ delta  >= 1 ? 'floor' : 'ceil' ](delta  / lowestDelta);
        deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
        deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);

        // Add event and delta to the front of the arguments
        args.unshift(event, delta, deltaX, deltaY);

        // Clearout lowestDelta after sometime to better
        // handle multiple device types that give different
        // a different lowestDelta
        // Ex: trackpad = 3 and mouse wheel = 120
        if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
        nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);

        return ($.event.dispatch || $.event.handle).apply(this, args);
    }

    function nullLowestDelta() {
      lowestDelta = null;
    }

}));
<|endoftext|>


<commit_before>/*
 *
 * jQuery noreferrer
 *
 * ---
 * @version 0.0.0
 * @author falsandtru https://github.com/falsandtru/
 * @license MIT http://opensource.org/licenses/mit-license.php
 * @copyright 2014, falsandtru
 *
 */
(function(jQuery, window, document, undefined) {
  jQuery.fn.noreferrer = jQuery.noreferrer = function(re) {
    var selector, scope, eventName = 'click.noreferrer';
    re = re instanceof RegExp ? re : /^([\w-]+:)?\/\//i;

    if (this instanceof jQuery && this.is('a')) {
      this.unbind(eventName).bind(eventName, handler);
    }
    else {
      selector = selector || 'a';
      scope = this instanceof jQuery && this[0] ? this : jQuery(document);
      scope.undelegate(selector, eventName).delegate(selector, eventName, handler);
    }

    function handler(event) {
      var $context = jQuery(event.target), url = $context.attr('href'), userAgent = window.navigator.userAgent.toLowerCase();
      var space = '　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　'
                + '　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　;'

      if (!re.test(url) && !$context.is('[rel~="noreferrer"]') || /^data:/i.test(url) || event.isDefaultPrevented()) {return ;}

      // noreferrer support browser
      if (/webkit/i.test(userAgent)) {
        $context[0].rel += !$context.is('[rel~="noreferrer"]') ? ' noreferrer' : '';
      }
      // IE
      else if (~userAgent.indexOf('msie') || ~userAgent.indexOf('trident') && ~userAgent.indexOf('rv:')) {
        if ($context.attr('target') === '_blank'){
          var subwin = window.open('', '', 'location=yes, menubar=yes, toolbar=yes, status=yes, resizable=yes, scrollbars=yes,');
          subwin.document.open();
          subwin.document.write('<meta http-equiv="refresh" content="0;url=' + url + '">');
          subwin.document.close();
        }
        else{
          window.location.hash += '+noreferrer';
          document.open();
          document.write('<meta http-equiv="refresh" content="0;url=' + url + '">');
          document.close();
        }
        event.preventDefault();
      }
      // Firefox
      else {
        if (!url.match(/data:text\/html;charset=utf-8/)) {
          var html = '<script>document.write(\'<meta http-equiv="refresh" content="0;url=' + url + '">\');</script>';
          $context.attr('href', 'data:noreferrer' + space + 'text/html;charset=utf-8,' + encodeURIComponent(html));
        }
      }
    }
  };
})(jQuery, window, window.document, void 0);
<commit_msg>Version 0.0.1<commit_after>/*
 *
 * jQuery noreferrer
 *
 * ---
 * @version 0.0.1
 * @author falsandtru https://github.com/falsandtru/
 * @license MIT http://opensource.org/licenses/mit-license.php
 * @copyright 2014, falsandtru
 *
 */
(function(jQuery, window, document, undefined) {
  jQuery.fn.noreferrer = jQuery.noreferrer = function(re) {
    var selector, scope, eventName = 'click.noreferrer';
    re = re instanceof RegExp ? re : /^([\w-]+:)?\/\//i;

    if (this instanceof jQuery && this.is('a')) {
      this.unbind(eventName).bind(eventName, handler);
    }
    else {
      selector = selector || 'a';
      scope = this instanceof jQuery && this[0] ? this : jQuery(document);
      scope.undelegate(selector, eventName).delegate(selector, eventName, handler);
    }

    function handler(event) {
      var $context = jQuery(event.target), url = $context.attr('href'), userAgent = window.navigator.userAgent.toLowerCase();
      var space = '　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　'
                + '　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　;'

      if (!re.test(url) && !$context.is('[rel~="noreferrer"]') || /^data:/i.test(url) || event.isDefaultPrevented()) {return ;}

      // noreferrer support browser
      if (/webkit/i.test(userAgent)) {
        $context[0].rel += !$context.is('[rel~="noreferrer"]') ? ' noreferrer' : '';
      }
      // IE
      else if (~userAgent.indexOf('msie') || ~userAgent.indexOf('trident') && ~userAgent.indexOf('rv:')) {
        if ($context.attr('target') === '_blank'){
          var subwin = window.open('', '', 'location=yes, menubar=yes, toolbar=yes, status=yes, resizable=yes, scrollbars=yes,');
          subwin.document.open();
          subwin.document.write('<meta http-equiv="refresh" content="0;url=' + url + '">');
          subwin.document.close();
        }
        else{
          window.location.hash += '+noreferrer';
          document.open();
          document.write('<meta http-equiv="refresh" content="0;url=' + url + '">');
          document.close();
        }
        event.preventDefault();
      }
      // Firefox
      else {
        if (!url.match(/data:text\/html;charset=utf-8/)) {
          var html = '<script>document.write(\'<meta http-equiv="refresh" content="0;url=' + url + '">\');</script>';
          $context.attr('href', 'data:noreferrer' + space + 'text/html;charset=utf-8,' + encodeURIComponent(html));
        }
      }
    }
  };
})(jQuery, window, window.document, void 0);
<|endoftext|>


<commit_before>/*jslint browser: true, nomen: true, todo: true */
(function ($, window, document) {
    'use strict';

    var P, Parallaxin, px, pct;

    px = function (n) { return {value: n, px: true}; };
    pct = function (n) { return {value: n, pct: true}; };

    P = Parallaxin = function () {};
    Parallaxin.instances = [];

    Parallaxin.prototype = {
        optionAttributePrefix: 'data-parallaxin-',

        defaultOptions: {
            // The container element. Defaults to the parent of the selected element.
            container: null,

            // Should the element move vertically?
            vertical: true,

            // Should the element move horizontally?
            horizontal: false,

            // Does the container change sizes?
            responsive: false,

            // Should the element use fixed positioning? The default value
            // depends on whether the element is styles as "position: fixed"
            // at the time the plugin is initialized, so normally you shouldn't
            // need to set this option; just set "position: fixed" in your
            // stylesheet.
            //
            // Using fixed positioning means the element is positioned relative
            // to the document instead of its parent and can result in a much
            // smoother parallax effect.
            fixed: null,

            // Should the element be hidden when its container leaves the
            // viewport? This helps to eliminate z-index issues when using fixed
            // positioning and multiple parallaxin elements.
            hideOnExit: true,

            // Contrain the range of motion within the container. Can be a
            // string or array, specified in the order "top", "right", "bottom",
            // "left." If less than four values are provided, they will be
            // expanded like with CSS. For example, the following are all
            // equivalent: `0`, `'0'`, `'0 0'`, `'0 0 0 0'` `[0]`, `[0, 0]`,
            // `[0, 0, 0, 0]`. Percentage values are also allowed.
            insets: 0
        },

        initialize: function ($el, options) {
            var isFixed;

            this.$el = $el;
            this.options = $.extend({}, this.defaultOptions, this.getHtmlOptions(), options);
            if (this.options.container) {
                this.$container = $(this.options.container);
            } else {
                this.$container = this.$el.parent();
            }
            this._insets = this.parseInsets(this.options.insets);

            isFixed = $el.css('position') === 'fixed';
            if (this.options.fixed === null || this.options.fixed === undefined) {
                this.options.fixed = isFixed;
            }
            if (this.options.fixed && !isFixed) {
                this.$el.css('position', 'fixed');
            }

            if (!P.$win) {
                P.$win = $(window);
            }
            if (!P._hasScrollHandler) {
                P._hasScrollHandler = true;
                P.$win.on('scroll', P.onScroll);
            }
            if (!P._hasResizeHandler && this.options.responsive) {
                P._hasResizeHandler = true;
                P.$win.on('resize', P.onWindowResize);
            }

            P.instances.push(this);
            this.update(false);
            return this;
        },

        destroy: function () {
            var index = P.instances.indexOf(this);
            P.instances.splice(index, 1);
            return this;
        },

        // Update the position of the element.
        update: function (r) {
            var
                shouldHide,
                recalculate = r !== false,
                recalculateContainerSize = recalculate,
                recalculateSize = recalculate,
                // TODO: Performance could be improved if there could be an option for setting this to false and a way to do it manually. (Gotta figure out a good name for the option.)
                recalculateContainerPosition = true,
                bounds = this._bounds,
                size = this._size,
                containerSize = this._containerSize,
                containerPosition = this._containerPosition,
                scrollPosition = P.getScrollPosition(),
                windowSize = P.getWindowSize(),
                css = {};

            if (recalculateContainerSize || !containerSize) {
                containerSize = this._containerSize = this.calculateElSize(this.$container);
                bounds = this._bounds = this.calculateBounds(this._insets, containerSize);
            }

            if (recalculateSize || !size) {
                size = this._size = this.calculateElSize(this.$el, this._isHidden);
            }

            if (recalculateContainerPosition || !containerPosition) {
                // Unlike sizes, jQUery calculates both vertical and horizontal
                // positions with one measurement, so we can't do them
                // separately.
                containerPosition = this._containerPosition = this.$container.offset();
            }

            if (this.options.horizontal) {
                css.left = this.updatePosition(bounds.L, bounds.R,
                    containerPosition.left, containerSize.width, size.width,
                    scrollPosition.left, windowSize.width);
            }
            if (this.options.vertical) {
                css.top = this.updatePosition(bounds.T, bounds.B,
                    containerPosition.top, containerSize.height, size.height,
                    scrollPosition.top, windowSize.height);
            }

            if (this.options.hideOnExit) {
                shouldHide = (!this.options.horizontal || css.left === undefined) &&
                    (!this.options.vertical || css.top === undefined);
                if (shouldHide !== !!this._isHidden) {
                    this.$el[shouldHide ? 'hide' : 'show']();
                    this._isHidden = shouldHide;
                }
            }

            if (css.left !== undefined || css.top !== undefined) {
                this.$el.css(css);
            }
        },

        updatePosition: function (boundsMin, boundsMax, containerPosition, containerSize, elSize, scrollPosition, windowSize) {
            // When the container is at the top of the viewport (min scrollTop),
            // $el should be aligned to its top. When the container is at the
            // bottom of the viewport (max scrollTop), $el should be aligned to
            // the bottom.
            var min, max, pct, targetMin, targetMax, pos;

            if (containerPosition + containerSize < scrollPosition) {
                // Container is above viewport.
                return undefined;
            }
            if (containerPosition > scrollPosition + windowSize) {
                // Container is below viewport.
                return undefined;
            }

            if (elSize <= windowSize) {
                max = containerPosition;
                min = containerPosition + containerSize - windowSize;
                targetMin = boundsMax - elSize;
                targetMax = boundsMin;
            } else {
                min = containerPosition - windowSize;
                max = containerPosition + containerSize;
                targetMin = boundsMin - elSize;
                targetMax = boundsMax;
            }

            pct = (scrollPosition - min) / (max - min);
            pos = targetMin + pct * (targetMax - targetMin);

            if (this.options.fixed) {
                // Make the positioin relative to the document instead of the
                // container.
                pos += containerPosition - scrollPosition;
            }

            return pos;
        },

        calculateElSize: function ($el, isHidden) {
            var size = {};
            if (isHidden) {
                $el.show();
            }
            if (this.options.horizontal) {
                size.width = $el.outerWidth();
            }
            if (this.options.vertical) {
                size.height = $el.outerHeight();
            }
            if (isHidden) {
                $el.hide();
            }
            return size;
        },

        calculateBound: function (inset, size, fromOpposite) {
            if (inset.pct) {
                return this.calculateBound(px(inset.value * size), size, fromOpposite);
            }
            if (fromOpposite) {
                return size - inset.value;
            }
            return inset.value;
        },

        // Convert parsed insets into actual pixel positions based on the size
        // of the container.
        calculateBounds: function (insets, containerSize) {
            var bounds = {};
            if (this.options.horizontal) {
                bounds.R = this.calculateBound(insets.R, containerSize.width);
                bounds.L = this.calculateBound(insets.L, containerSize.width, true);
            }
            if (this.options.vertical) {
                bounds.T = this.calculateBound(insets.T, containerSize.height);
                bounds.B = this.calculateBound(insets.B, containerSize.height, true);
            }
            return bounds;
        },

        parseInset: function (val) {
            switch ($.type(val)) {
            case 'number':
                return px(val);
            case 'string':
                if (val.indexOf('%') > -1) {
                    return pct(parseFloat(val) / 100);
                }
                return px(parseInt(val, 10));
            }
        },

        // Parse inset options into numbers and units corresponding to each
        // side.
        parseInsets: function (val) {
            var parsed, a, v, h, t, r, b, l;

            switch ($.type(val)) {
            case 'number':
                return {T: px(val), R: px(val), B: px(val), L: px(val)};
            case 'array':
                switch (val.length) {
                case 1:
                    a = this.parseInset(val[0]);
                    return {T: a, R: a, B: a, L: a};
                case 2:
                    v = this.parseInset(val[0]);
                    h = this.parseInset(val[1]);
                    return {T: v, R: h, B: v, L: h};
                case 4:
                    t = this.parseInset(val[0]);
                    r = this.parseInset(val[1]);
                    b = this.parseInset(val[2]);
                    l = this.parseInset(val[4]);
                    return {T: t, R: r, B: b, L: l};
                }
                break;
            case 'string':
                return this.parseInsets(val.replace(/^\s+|\s+$/, '').split(/\s+/));
            }
        },

        getHtmlOptions: function () {
            var self = this,
                opts = {};
            $.each(this.$el[0].attributes, function (i, attr) {
                if (attr.name.indexOf(self.optionAttributePrefix) !== -1) {
                    opts[attr.name.substr(self.optionAttributePrefix.length)] = attr.value;
                }
            });
            return opts;
        }
    };

    Parallaxin.getScrollPosition = function (recalculate) {
        if (recalculate || !P._scrollPosition) {
            P._scrollPosition = {
                top: P.$win.scrollTop(),
                left: P.$win.scrollLeft()
            };
        }
        return P._scrollPosition;
    };

    Parallaxin.getWindowSize = function (recalculate) {
        if (recalculate || !P._windowSize) {
            P._windowSize = {
                width: P.$win.width(),
                height: P.$win.height()
            };
        }
        return P._windowSize;
    };

    Parallaxin.onScroll = function () {
        var i, instance,
            oldPos = P._scrollPosition,
            pos = P.getScrollPosition(true),
            topChanged = !oldPos || oldPos.top !== pos.top,
            leftChanged = !oldPos || oldPos.top !== pos.top;
        for (i = P.instances.length - 1; i >= 0; i -= 1) {
            instance = P.instances[i];
            if ((topChanged && instance.options.vertical) || (leftChanged && instance.options.horizontal)) {
                instance.update(false);
            }
        }
    };

    Parallaxin.onWindowResize = function () {
        var i, instance;
        P.getWindowSize(true);
        for (i = P.instances.length - 1; i >= 0; i -= 1) {
            instance = P.instances[i];
            if (instance.options.responsive) {
                instance.update();
            }
        }
    };

    $.extend($.fn, {
        parallaxin: function (optionsOrMethod) {
            var returnValue = this,
                args = arguments;
            this.each(function (i, el) {
                var $el = $(el),
                    plugin = $el.data('parallaxin'),
                    method = typeof optionsOrMethod === 'string' ? optionsOrMethod : null,
                    options = method === null ? optionsOrMethod || {} : {};

                if (!plugin) {
                    if (method) {
                        $.error('You can\'t call the parallaxin method "' + method
                                + '" without first initializing the plugin by calling '
                                + 'parallaxin() on the jQuery object.');
                    } else {
                        plugin = new Parallaxin().initialize($el, options);
                        $el.data('parallaxin', plugin);
                    }
                } else if (method) {
                    if (typeof plugin[method] !== 'function') {
                        $.error('Method "' + method + '" does not exist on jQuery.parallaxin');
                    } else {
                        // NOTE: If you call a method that returns a value, you will only get the result from the last item in the collection.
                        returnValue = plugin[method].apply(plugin, Array.prototype.slice.call(args, 1));
                    }
                }
            });
            return returnValue;
        }
    });
}(this.jQuery, window, document));
<commit_msg>Support alternate scrolling elements<commit_after>/*jslint browser: true, nomen: true, todo: true */
(function ($, window, document) {
    'use strict';

    var P, Parallaxin, px, pct;

    px = function (n) { return {value: n, px: true}; };
    pct = function (n) { return {value: n, pct: true}; };

    P = Parallaxin = function () {};
    Parallaxin.instances = [];

    Parallaxin.prototype = {
        optionAttributePrefix: 'data-parallaxin-',

        defaultOptions: {
            // The container element. Defaults to the parent of the selected element.
            container: null,

            // Should the element move vertically?
            vertical: true,

            // Should the element move horizontally?
            horizontal: false,

            // Does the container change sizes?
            responsive: false,

            // The element whose scrolling should change the position.
            scrollingElement: 'window',

            // Should the element use fixed positioning? The default value
            // depends on whether the element is styles as "position: fixed"
            // at the time the plugin is initialized, so normally you shouldn't
            // need to set this option; just set "position: fixed" in your
            // stylesheet.
            //
            // Using fixed positioning means the element is positioned relative
            // to the document instead of its parent and can result in a much
            // smoother parallax effect.
            fixed: null,

            // Should the element be hidden when its container leaves the
            // viewport? This helps to eliminate z-index issues when using fixed
            // positioning and multiple parallaxin elements.
            hideOnExit: true,

            // Contrain the range of motion within the container. Can be a
            // string or array, specified in the order "top", "right", "bottom",
            // "left." If less than four values are provided, they will be
            // expanded like with CSS. For example, the following are all
            // equivalent: `0`, `'0'`, `'0 0'`, `'0 0 0 0'` `[0]`, `[0, 0]`,
            // `[0, 0, 0, 0]`. Percentage values are also allowed.
            insets: 0
        },

        initialize: function ($el, options) {
            var isFixed;

            this.$el = $el;
            this.options = $.extend({}, this.defaultOptions, this.getHtmlOptions(), options);
            if (this.options.container) {
                this.$container = $(this.options.container);
            } else {
                this.$container = this.$el.parent();
            }
            this._insets = this.parseInsets(this.options.insets);

            isFixed = $el.css('position') === 'fixed';
            if (this.options.fixed === null || this.options.fixed === undefined) {
                this.options.fixed = isFixed;
            }
            if (this.options.fixed && !isFixed) {
                this.$el.css('position', 'fixed');
            }

            if (!P.$win) {
                P.$win = $(window);
            }

            // Add the listener to the scrolling element.
            this.$scrollingEl = this.options.scrollingElement ? $(this.options.scrollingElement) : P.$win;
            this._onScroll = $.proxy(this.onScroll, this);
            this.$scrollingEl.on('scroll', this._onScroll);

            if (!P._hasResizeHandler && this.options.responsive) {
                P._hasResizeHandler = true;
                P.$win.on('resize', P.onWindowResize);
            }

            P.instances.push(this);
            this.update(false);
            return this;
        },

        destroy: function () {
            var index = P.instances.indexOf(this);
            P.instances.splice(index, 1);
            this.$scrollingEl.off('scroll', this._onScroll);
            return this;
        },

        // Update the position of the element.
        update: function (r) {
            var
                a, b, c,
                shouldHide,
                recalculate = r !== false,
                recalculateContainerSize = recalculate,
                recalculateSize = recalculate,
                // TODO: Performance could be improved if there could be an option for setting this to false and a way to do it manually. (Gotta figure out a good name for the option.)
                recalculateContainerPosition = true,
                bounds = this._bounds,
                size = this._size,
                containerSize = this._containerSize,
                containerPosition = this._containerPosition,
                scrollPosition = this.getScrollPosition(),
                windowSize = P.getWindowSize(),
                css = {};

            if (recalculateContainerSize || !containerSize) {
                containerSize = this._containerSize = this.calculateElSize(this.$container);
                bounds = this._bounds = this.calculateBounds(this._insets, containerSize);
            }

            if (recalculateSize || !size) {
                size = this._size = this.calculateElSize(this.$el, this._isHidden);
            }

            if (recalculateContainerPosition || !containerPosition) {
                // Unlike sizes, jQuery calculates both vertical and horizontal
                // positions with one measurement, so we can't do them
                // separately.

                // Determine the position of the element relative to the top of
                // the scrolling element's contents. We need to make sure we
                // account for both the normal case (scrolling the document) and
                // for scrolling of arbitrary (overflown) elements.
                a = this.$container.offset();
                b = {left: this.$scrollingEl.scrollLeft(), top: this.$scrollingEl.scrollTop()};
                c = {left: P.$win.scrollLeft(), top: P.$win.scrollTop()};
                containerPosition = this._containerPosition = {
                  left: a.left + b.left - c.left,
                  top: a.top + b.top - c.top
                }
            }

            if (this.options.horizontal) {
                css.left = this.updatePosition(bounds.L, bounds.R,
                    containerPosition.left, containerSize.width, size.width,
                    scrollPosition.left, windowSize.width);
            }
            if (this.options.vertical) {
                css.top = this.updatePosition(bounds.T, bounds.B,
                    containerPosition.top, containerSize.height, size.height,
                    scrollPosition.top, windowSize.height);
            }

            if (this.options.hideOnExit) {
                shouldHide = (!this.options.horizontal || css.left === undefined) &&
                    (!this.options.vertical || css.top === undefined);
                if (shouldHide !== !!this._isHidden) {
                    this.$el[shouldHide ? 'hide' : 'show']();
                    this._isHidden = shouldHide;
                }
            }
            if (css.left !== undefined || css.top !== undefined) {
                this.$el.css(css);
            }
        },

        updatePosition: function (boundsMin, boundsMax, containerPosition, containerSize, elSize, scrollPosition, windowSize) {
            // When the container is at the top of the viewport (min scrollTop),
            // $el should be aligned to its top. When the container is at the
            // bottom of the viewport (max scrollTop), $el should be aligned to
            // the bottom.
            var min, max, pct, targetMin, targetMax, pos;

            if (containerPosition + containerSize < scrollPosition) {
                // Container is above viewport.
                return undefined;
            }
            if (containerPosition > scrollPosition + windowSize) {
                // Container is below viewport.
                return undefined;
            }

            if (elSize <= windowSize) {
                max = containerPosition;
                min = containerPosition + containerSize - windowSize;
                targetMin = boundsMax - elSize;
                targetMax = boundsMin;
            } else {
                min = containerPosition - windowSize;
                max = containerPosition + containerSize;
                targetMin = boundsMin - elSize;
                targetMax = boundsMax;
            }

            pct = (scrollPosition - min) / (max - min);
            pos = targetMin + pct * (targetMax - targetMin);

            if (this.options.fixed) {
                // Make the positioin relative to the document instead of the
                // container.
                pos += containerPosition - scrollPosition;
            }

            return pos;
        },

        calculateElSize: function ($el, isHidden) {
            var size = {};
            if (isHidden) {
                $el.show();
            }
            if (this.options.horizontal) {
                size.width = $el.outerWidth();
            }
            if (this.options.vertical) {
                size.height = $el.outerHeight();
            }
            if (isHidden) {
                $el.hide();
            }
            return size;
        },

        calculateBound: function (inset, size, fromOpposite) {
            if (inset.pct) {
                return this.calculateBound(px(inset.value * size), size, fromOpposite);
            }
            if (fromOpposite) {
                return size - inset.value;
            }
            return inset.value;
        },

        // Convert parsed insets into actual pixel positions based on the size
        // of the container.
        calculateBounds: function (insets, containerSize) {
            var bounds = {};
            if (this.options.horizontal) {
                bounds.R = this.calculateBound(insets.R, containerSize.width);
                bounds.L = this.calculateBound(insets.L, containerSize.width, true);
            }
            if (this.options.vertical) {
                bounds.T = this.calculateBound(insets.T, containerSize.height);
                bounds.B = this.calculateBound(insets.B, containerSize.height, true);
            }
            return bounds;
        },

        parseInset: function (val) {
            switch ($.type(val)) {
            case 'number':
                return px(val);
            case 'string':
                if (val.indexOf('%') > -1) {
                    return pct(parseFloat(val) / 100);
                }
                return px(parseInt(val, 10));
            }
        },

        // Parse inset options into numbers and units corresponding to each
        // side.
        parseInsets: function (val) {
            var parsed, a, v, h, t, r, b, l;

            switch ($.type(val)) {
            case 'number':
                return {T: px(val), R: px(val), B: px(val), L: px(val)};
            case 'array':
                switch (val.length) {
                case 1:
                    a = this.parseInset(val[0]);
                    return {T: a, R: a, B: a, L: a};
                case 2:
                    v = this.parseInset(val[0]);
                    h = this.parseInset(val[1]);
                    return {T: v, R: h, B: v, L: h};
                case 4:
                    t = this.parseInset(val[0]);
                    r = this.parseInset(val[1]);
                    b = this.parseInset(val[2]);
                    l = this.parseInset(val[4]);
                    return {T: t, R: r, B: b, L: l};
                }
                break;
            case 'string':
                return this.parseInsets(val.replace(/^\s+|\s+$/, '').split(/\s+/));
            }
        },

        getHtmlOptions: function () {
            var self = this,
                opts = {};
            $.each(this.$el[0].attributes, function (i, attr) {
                if (attr.name.indexOf(self.optionAttributePrefix) !== -1) {
                    opts[attr.name.substr(self.optionAttributePrefix.length)] = attr.value;
                }
            });
            return opts;
        },

        onScroll: function () {
            // TODO: We really want to not get the position multiple times for the same element.
            var
                oldPos = this._scrollPosition,
                pos = this.getScrollPosition(true),
                topChanged = !oldPos || oldPos.top !== pos.top,
                leftChanged = !oldPos || oldPos.top !== pos.top;

            if ((topChanged && this.options.vertical) || (leftChanged && this.options.horizontal)) {
                this.update(false);
            }
        },

        getScrollPosition: function (recalculate) {
            if (recalculate || !this._scrollPosition) {
                this._scrollPosition = {
                    top: this.$scrollingEl.scrollTop(),
                    left: this.$scrollingEl.scrollLeft()
                };
            }
            return this._scrollPosition;
        }
    };

    Parallaxin.getWindowSize = function (recalculate) {
        if (recalculate || !P._windowSize) {
            P._windowSize = {
                width: P.$win.width(),
                height: P.$win.height()
            };
        }
        return P._windowSize;
    };

    Parallaxin.onWindowResize = function () {
        var i, instance;
        P.getWindowSize(true);
        for (i = P.instances.length - 1; i >= 0; i -= 1) {
            instance = P.instances[i];
            if (instance.options.responsive) {
                instance.update();
            }
        }
    };

    $.extend($.fn, {
        parallaxin: function (optionsOrMethod) {
            var returnValue = this,
                args = arguments;
            this.each(function (i, el) {
                var $el = $(el),
                    plugin = $el.data('parallaxin'),
                    method = typeof optionsOrMethod === 'string' ? optionsOrMethod : null,
                    options = method === null ? optionsOrMethod || {} : {};

                if (!plugin) {
                    if (method) {
                        $.error('You can\'t call the parallaxin method "' + method
                                + '" without first initializing the plugin by calling '
                                + 'parallaxin() on the jQuery object.');
                    } else {
                        plugin = new Parallaxin().initialize($el, options);
                        $el.data('parallaxin', plugin);
                    }
                } else if (method) {
                    if (typeof plugin[method] !== 'function') {
                        $.error('Method "' + method + '" does not exist on jQuery.parallaxin');
                    } else {
                        // NOTE: If you call a method that returns a value, you will only get the result from the last item in the collection.
                        returnValue = plugin[method].apply(plugin, Array.prototype.slice.call(args, 1));
                    }
                }
            });
            return returnValue;
        }
    });
}(this.jQuery, window, document));
<|endoftext|>