<commit_before>/*

hashban.js is a plugin to streamline the addition of ajax-style page transitions
to a website.

Example:

    var hashban = Hashban({
        contentWrapSelector: '#mycontent'
    });
    hashban.hijack($('#header nav'));
    hashban.bind();

Options/defaults:

    contentWrapSelector: '#content',
    transitions: [DefaultTransition],
    loader: Hashban.prototype.loader,
    link_order: [],
    link_filter: null,
    loaderTimeout: 300

Transitions:

The transitions option should provide a list of Transition pseudoclasses,
inheriting from Hashban.DefaultTransition and defining the following methods:

    constructor(old_content, data)
    should_use() - returns true if this transition should be used
    transition_out(endfade) - performs "out" transition then calls
                    endfade callback
    transition_in(new_content, contentBody) - performs "in" transition

The Transition objects will be tested in order and the first valid one found
will be used. If none match, the last will be used.

*/

var Hashban = (function($) {

    function DefaultTransition(old_content, data){
        this.old_content = old_content;
        this.data = data;
        this.duration = 500;
    };
    DefaultTransition.prototype = {
        should_use: function() {
            return true;
        },
        transition_out: function(endfade) {
            this.old_content.fadeOut(this.duration, endfade);
        },
        transition_in: function(new_content, contentBody) {
            this.old_content.remove();
            new_content.fadeIn(this.duration);
        }
    };

    function Hashban(user_options) {
        if (!(this instanceof Hashban)) return new Hashban(user_options);
        var that = this;

        this.CACHE = {};
        this.currentXHR = null;
        this.previous_url = window.location.pathname;
        this.options = $.extend({
            contentWrapSelector: '#content',
            transitions: [DefaultTransition],
            content_init: null,
            loader: function(visible) {
                return that.loader(visible);
            },
            link_order: [],
            loaderTimeout: 300,
            uid: 'Hashban.js'
        }, user_options);
    };

    Hashban.LOAD_EVENT = 'hashban-load';
    Hashban.UNLOAD_EVENT = 'hashban-unload';

    Hashban.DefaultTransition = DefaultTransition;

    Hashban.prototype.bind = function(){
        var that = this;

        $(window).bind('popstate', function(e) {
            if (e.originalEvent.state &&
                e.originalEvent.state['handler'] === that.options['uid']) {
                that.loadPage(window.location.pathname, e.originalEvent.state);
                return false;
            }
        });
    };

    Hashban.prototype.hashban = function(links){
        // bind the given links (a jquery collection) to load via ajax

        var that = this;
        links.each(function() {
            var me = $(this),
                url = me.attr('href');

            // TODO uid here? can you bind two Hashbans to one link?

            if (!me.data('hashbanned')) {
                me.click(function() {
                    that.loadPage(url);
                    return false;
                });
                me.data('hashbanned', true);
            }
        });
    };

    Hashban.prototype.filter_links = function(links) {
        var domain = getmatch(window.location.href, /[^\/]+\/\/[^\/]+/, 0);
        return links.filter('a[href]:not([href^="http://"]), ' +
                            'a[href^="' + domain + '"]')
                    .not('[href$=".xml"], [href$=".pdf"], ' +
                         '[href$=".jpg"], [href$=".gif"], ' +
                         '[href$=".png"], [href^="#"], [href^="mailto:"]');
    };
    Hashban.prototype.hijack = function(el, link_filter) {
        // find suitable links within the given element(s), and hijack them
        // to load via ajax

        var domain = getmatch(window.location.href, /[^\/]+\/\/[^\/]+/, 0),
            links = this.filter_links(el.find('a'));

        if (link_filter) {
            links = links.filter(link_filter);
        }

        this.hashban(links);

        return el;
    };

    Hashban.prototype.loadPage = function (url, state, force) {
        // Load the specified url. If state is passed, it is assumed to be the
        // state dictionary from the popstate event, (i.e. back button) and the
        // pageload is handled accordingly. Otherwise, it is assumed the
        // pageLoad was initiated by the user clicking a link.

        var that = this,
            contentWrap = $(this.options.contentWrapSelector),
            html,
            error,
            loaderTimer,
            faded = false,
            completed = false,
            transition_data = {
                direction: get_direction(this.previous_url, url,
                                         this.options.link_order),
                from: this.previous_url,
                to: url,
                state: state
            },
            old_content = contentWrap.children().not(this.options.loader());

        if (this.previous_url !== Hashban.absolute_url(url) || force) {
            // save scroll position in current state; also means the transition
            // will work if the user navigates to the originally loaded page
            // via the back button
            if (!state) {
                window.history.replaceState(that.buildState({
                    scrollTop: $(window).scrollTop()
                }), null, window.location);
            }

            // find appropriate transition (last one is default)
            var transitions = this.options.transitions,
                transition;
            for (var i = 0; i < transitions.length; i++) {
                transition = new transitions[i](old_content, transition_data);
                if (transition.should_use()) {
                    break;
                }
            }

            // Order of events:
            // 1. start loading
            // 2. transition out old content
            // 3. show spinner
            // 4. once load and transition out are both done, transition in

            function fadein() {
                var contentBody = Hashban.getBody(html),
                    bodyClass = contentBody.attr('class'),
                    contentEl = contentBody.find(
                                    that.options.contentWrapSelector),
                    title = getmatch(html, /<title>([\s\S]*?)<\/title>/, 1);

                if (contentEl.length) {
                    if (!state) {
                        // assume a link was clicked if no state present
                        history.pushState(that.buildState(), null, url);
                    }
                    document.title = $('<div>').html(title).text();

                    $('body').attr('class', bodyClass);

                    clearTimeout(loaderTimer);
                    that.options.loader(false);

                    var new_content = contentEl.children(),
                        old_content = contentWrap.children()
                                                 .not(that.options.loader());

                    // trigger unload event here so that it won't effect any
                    // new content
                    // TODO uid here?
                    $(window).trigger(Hashban.UNLOAD_EVENT);

                    new_content.appendTo(contentWrap).hide();

                    //transition_data.contentBody = contentBody;
                    transition.transition_in(new_content, contentBody);

                    if (typeof that.options.content_init === 'function') {
                        that.options.content_init(new_content);
                    }

                    // if the load was triggered by popstate, eg. the back
                    // button and not a clicked link, restore scroll position
                    if (state && state.scrollTop) {
                        $(window).scrollTop(state.scrollTop);
                    }
                    else {
                        // Otherwise, handle hash fragment if present
                        var hash = url.split('#')[1];
                        if (hash && $('#' + hash).length) {
                            $(window).scrollTop($('#' + hash).offset().top);
                        }
                        else {
                            // otherwise scroll to top
                            $(window).scrollTop(0);
                        }
                    }

                    // trigger load event here - assume that new content has
                    // been added in transition_in
                    // TODO uid here?
                    $(window).trigger(Hashban.LOAD_EVENT);

                    // work around jquery's auto overflow switch - see
                    // http://goo.gl/V9UUw
                    contentWrap.css({
                        overflow: 'visible'
                    });

                    that.previous_url = url;
                }
                else {
                    // reload the page to show the error
                    window.location.href = url;
                }
            };

            function endfade() {
                // Called once the transition_out animation is completed,
                // or immediately if there is no transition_out.
                if (!completed) {
                    completed = true;
                    if (html) {
                        fadein();
                    }
                    else {
                        loaderTimer = setTimeout(function() {
                            that.options.loader(true);
                        }, that.options.loaderTimeout);
                        faded = true;
                    }
                }
            };

            if (this.CACHE[url]) {
                html = this.CACHE[url];
            }
            else {
                if (this.currentXHR) {
                    this.currentXHR.abort();
                }
                this.currentXHR = $.ajax(url, {
                    success: function(data) {
                        that.CACHE[url] = data;
                        html = data;
                        if (faded) {
                            fadein();
                        }
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        html = jqXHR.responseText;
                        error = errorThrown;
                        if (faded) {
                            fadein();
                        }
                    }
                });
            }

            if (typeof transition.transition_out === 'function') {
                transition.transition_out(endfade);
            }
            else {
                endfade();
            }
        }
    };

    Hashban.prototype.buildState = function(params) {
        // builds the History state dict, starting with a common base and
        // adding the params obj

        return $.extend({
            handler: this.options['uid']
        }, params || {});
    };

    Hashban.prototype.loader = function(visible) {
        // this function toggles the "loading" state, based on
        // the parameter. If creating a loader element on the
        // page, it should return this element.

        var loading = $('.hashban-loader');
        if (!loading.length) {
            loading = $('<span>').addClass('hashban-loader')
                                 .text('Loading').hide();
            $(this.options.contentWrapSelector).append(loading);
        }
        if (visible === true) {
            loading.show();
        }
        else if (visible === false) {
            loading.hide();
        }

        return loading;
    };

    // PUBLIC INSTANCE METHODS

    Hashban.prototype.bindUntilUnload = function (el, event, callback) {
        // bind event to el, and unbind when navigating away from the page

        // TODO - account for uid here

        el.on(event, callback);
        $(window).one(Hashban.UNLOAD_EVENT, function() {
            el.off(event, callback);
        });
    };

    Hashban.prototype.preload = function (url) {
        // preload a url to avoid transition delay
        var that = this;

        if (!this.CACHE[url]) {
            $.ajax(url, {
                success: function(data) {
                    that.CACHE[url] = data;
                }
            });
        }
    };

    // PUBLIC UTILITIES

    Hashban.getBody = function (html) {
        // get wrapped body element from an html document

        return $('<div' +
                 getmatch(html, /<body([^>]*>[\S\s]*)<\/body>/, 1) +
                 '</div>');
    };

    Hashban.absolute_url = function(url) {
        if (!url.match(/^(?:\/|https?:\/\/|:\/\/)/)) {
            // must be relative
            return window.location.pathname.replace(/\/[^\/]+$/, '/') + url;
        }
        return url;
    };

    // INTERNAL UTILITIES

    function getmatch(str, re, i) {
        // find and return the ith matched pattern in a regex, or
        // return a blank string if not found

        var match = str.match(re);
        if (match) {
            return match[i];
        }
        else {
            return '';
        }
    };

    function get_direction(from, to, link_order) {
        // Returns 1 (forwards) or -1 (backwards) for a given transition.
        // 0 is returned when the direction cannot be inferred.
        // link_order should be a list of the site's top-level link urls,
        // in order (see setup function).

        if (!link_order) {
            link_order = [];
        }

        if (to === from) {
            // this shouldn't really happen
            return 0;
        }
        else if (is_sublink(from, to)) {
            // if to is a child of from, go forward
            return 1;
        }
        else if (is_sublink(to, from)) {
            // if from is a child of to, go backwards
            return -1;
        }
        else {
            // otherwise, investigate the link_order setting to see
            // which way to go:
            var from_i = -1,
                to_i = -1;

            // first look for exact matches
            for (var i = 0; i < link_order.length; i++) {
                if (from_i === -1 && from === link_order[i]) {
                    from_i = i;
                }
                if (to_i === -1 && to === link_order[i]) {
                    to_i = i;
                }
            }
            if (from_i === -1 || to_i === -1) {
                // if no exact match, look for parent urls that match
                for (var i = 0; i < link_order.length; i++) {
                    if (from_i === -1 && is_sublink(link_order[i], from)) {
                        from_i = i;
                    }
                    if (to_i === -1 && is_sublink(link_order[i], to)) {
                        to_i = i;
                    }
                }
            }

            if (from_i === to_i || from_i === -1 || to_i === -1) {
                // can't determine a direction
                return 0;
            }
            else if (to_i < from_i) {
                // return backwards
                return -1;
            }
            else {
                // return forwards
                return 1;
            }
        }
    };

    function is_sublink(link, possible_sublink) {
        // Determine whether possible_sublink is a child of
        // link in the url tree. Returns false if the links
        // are the same.

        // add trailing slashes if they're missing, to ensure
        // that is_sublink(/test', '/test-2') returns false,
        // but is_sublink(/test', '/test/2') returns true.
        if (possible_sublink.slice(-1) !== '/') {
            possible_sublink += '/';
        }
        if (link.slice(-1) !== '/') {
            link += '/';
        }

        if (link !== possible_sublink && possible_sublink.indexOf(link) === 0) {
            return true;
        }
        else {
            return false;
        }
    };

    return Hashban;

})(jQuery);
<commit_msg>prev url fixed<commit_after>/*

hashban.js is a plugin to streamline the addition of ajax-style page transitions
to a website.

Example:

    var hashban = Hashban({
        contentWrapSelector: '#mycontent'
    });
    hashban.hijack($('#header nav'));
    hashban.bind();

Options/defaults:

    contentWrapSelector: '#content',
    transitions: [DefaultTransition],
    loader: Hashban.prototype.loader,
    link_order: [],
    link_filter: null,
    loaderTimeout: 300

Transitions:

The transitions option should provide a list of Transition pseudoclasses,
inheriting from Hashban.DefaultTransition and defining the following methods:

    constructor(old_content, data)
    should_use() - returns true if this transition should be used
    transition_out(endfade) - performs "out" transition then calls
                    endfade callback
    transition_in(new_content, contentBody) - performs "in" transition

The Transition objects will be tested in order and the first valid one found
will be used. If none match, the last will be used.

*/

var Hashban = (function($) {

    function DefaultTransition(old_content, data){
        this.old_content = old_content;
        this.data = data;
        this.duration = 500;
    };
    DefaultTransition.prototype = {
        should_use: function() {
            return true;
        },
        transition_out: function(endfade) {
            this.old_content.fadeOut(this.duration, endfade);
        },
        transition_in: function(new_content, contentBody) {
            this.old_content.remove();
            new_content.fadeIn(this.duration);
        }
    };

    function Hashban(user_options) {
        if (!(this instanceof Hashban)) return new Hashban(user_options);
        var that = this;

        this.CACHE = {};
        this.currentXHR = null;
        this.previous_url = window.location.pathname + window.location.search;
        this.options = $.extend({
            contentWrapSelector: '#content',
            transitions: [DefaultTransition],
            content_init: null,
            loader: function(visible) {
                return that.loader(visible);
            },
            link_order: [],
            loaderTimeout: 300,
            uid: 'Hashban.js'
        }, user_options);
    };

    Hashban.LOAD_EVENT = 'hashban-load';
    Hashban.UNLOAD_EVENT = 'hashban-unload';

    Hashban.DefaultTransition = DefaultTransition;

    Hashban.prototype.bind = function(){
        var that = this;

        $(window).bind('popstate', function(e) {
            if (e.originalEvent.state &&
                e.originalEvent.state['handler'] === that.options['uid']) {
                that.loadPage(window.location.pathname, e.originalEvent.state);
                return false;
            }
        });
    };

    Hashban.prototype.hashban = function(links){
        // bind the given links (a jquery collection) to load via ajax

        var that = this;
        links.each(function() {
            var me = $(this),
                url = me.attr('href');

            // TODO uid here? can you bind two Hashbans to one link?

            if (!me.data('hashbanned')) {
                me.click(function() {
                    that.loadPage(url);
                    return false;
                });
                me.data('hashbanned', true);
            }
        });
    };

    Hashban.prototype.filter_links = function(links) {
        var domain = getmatch(window.location.href, /[^\/]+\/\/[^\/]+/, 0);
        return links.filter('a[href]:not([href^="http://"]), ' +
                            'a[href^="' + domain + '"]')
                    .not('[href$=".xml"], [href$=".pdf"], ' +
                         '[href$=".jpg"], [href$=".gif"], ' +
                         '[href$=".png"], [href^="#"], [href^="mailto:"]');
    };
    Hashban.prototype.hijack = function(el, link_filter) {
        // find suitable links within the given element(s), and hijack them
        // to load via ajax

        var domain = getmatch(window.location.href, /[^\/]+\/\/[^\/]+/, 0),
            links = this.filter_links(el.find('a'));

        if (link_filter) {
            links = links.filter(link_filter);
        }

        this.hashban(links);

        return el;
    };

    Hashban.prototype.loadPage = function (url, state, force) {
        // Load the specified url. If state is passed, it is assumed to be the
        // state dictionary from the popstate event, (i.e. back button) and the
        // pageload is handled accordingly. Otherwise, it is assumed the
        // pageLoad was initiated by the user clicking a link.

        var that = this,
            contentWrap = $(this.options.contentWrapSelector),
            html,
            error,
            loaderTimer,
            faded = false,
            completed = false,
            transition_data = {
                direction: get_direction(this.previous_url, url,
                                         this.options.link_order),
                from: this.previous_url,
                to: url,
                state: state
            },
            old_content = contentWrap.children().not(this.options.loader());

        if (this.previous_url !== Hashban.absolute_url(url) || force) {
            // save scroll position in current state; also means the transition
            // will work if the user navigates to the originally loaded page
            // via the back button
            if (!state) {
                window.history.replaceState(that.buildState({
                    scrollTop: $(window).scrollTop()
                }), null, window.location);
            }

            // find appropriate transition (last one is default)
            var transitions = this.options.transitions,
                transition;
            for (var i = 0; i < transitions.length; i++) {
                transition = new transitions[i](old_content, transition_data);
                if (transition.should_use()) {
                    break;
                }
            }

            // Order of events:
            // 1. start loading
            // 2. transition out old content
            // 3. show spinner
            // 4. once load and transition out are both done, transition in

            function fadein() {
                var contentBody = Hashban.getBody(html),
                    bodyClass = contentBody.attr('class'),
                    contentEl = contentBody.find(
                                    that.options.contentWrapSelector),
                    title = getmatch(html, /<title>([\s\S]*?)<\/title>/, 1);

                if (contentEl.length) {
                    if (!state) {
                        // assume a link was clicked if no state present
                        history.pushState(that.buildState(), null, url);
                    }
                    document.title = $('<div>').html(title).text();

                    $('body').attr('class', bodyClass);

                    clearTimeout(loaderTimer);
                    that.options.loader(false);

                    var new_content = contentEl.children(),
                        old_content = contentWrap.children()
                                                 .not(that.options.loader());

                    // trigger unload event here so that it won't effect any
                    // new content
                    // TODO uid here?
                    $(window).trigger(Hashban.UNLOAD_EVENT);

                    new_content.appendTo(contentWrap).hide();

                    //transition_data.contentBody = contentBody;
                    transition.transition_in(new_content, contentBody);

                    if (typeof that.options.content_init === 'function') {
                        that.options.content_init(new_content);
                    }

                    // if the load was triggered by popstate, eg. the back
                    // button and not a clicked link, restore scroll position
                    if (state && state.scrollTop) {
                        $(window).scrollTop(state.scrollTop);
                    }
                    else {
                        // Otherwise, handle hash fragment if present
                        var hash = url.split('#')[1];
                        if (hash && $('#' + hash).length) {
                            $(window).scrollTop($('#' + hash).offset().top);
                        }
                        else {
                            // otherwise scroll to top
                            $(window).scrollTop(0);
                        }
                    }

                    // trigger load event here - assume that new content has
                    // been added in transition_in
                    // TODO uid here?
                    $(window).trigger(Hashban.LOAD_EVENT);

                    // work around jquery's auto overflow switch - see
                    // http://goo.gl/V9UUw
                    contentWrap.css({
                        overflow: 'visible'
                    });

                    that.previous_url = url;
                }
                else {
                    // reload the page to show the error
                    window.location.href = url;
                }
            };

            function endfade() {
                // Called once the transition_out animation is completed,
                // or immediately if there is no transition_out.
                if (!completed) {
                    completed = true;
                    if (html) {
                        fadein();
                    }
                    else {
                        loaderTimer = setTimeout(function() {
                            that.options.loader(true);
                        }, that.options.loaderTimeout);
                        faded = true;
                    }
                }
            };

            if (this.CACHE[url]) {
                html = this.CACHE[url];
            }
            else {
                if (this.currentXHR) {
                    this.currentXHR.abort();
                }
                this.currentXHR = $.ajax(url, {
                    success: function(data) {
                        that.CACHE[url] = data;
                        html = data;
                        if (faded) {
                            fadein();
                        }
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        html = jqXHR.responseText;
                        error = errorThrown;
                        if (faded) {
                            fadein();
                        }
                    }
                });
            }

            if (typeof transition.transition_out === 'function') {
                transition.transition_out(endfade);
            }
            else {
                endfade();
            }
        }
    };

    Hashban.prototype.buildState = function(params) {
        // builds the History state dict, starting with a common base and
        // adding the params obj

        return $.extend({
            handler: this.options['uid']
        }, params || {});
    };

    Hashban.prototype.loader = function(visible) {
        // this function toggles the "loading" state, based on
        // the parameter. If creating a loader element on the
        // page, it should return this element.

        var loading = $('.hashban-loader');
        if (!loading.length) {
            loading = $('<span>').addClass('hashban-loader')
                                 .text('Loading').hide();
            $(this.options.contentWrapSelector).append(loading);
        }
        if (visible === true) {
            loading.show();
        }
        else if (visible === false) {
            loading.hide();
        }

        return loading;
    };

    // PUBLIC INSTANCE METHODS

    Hashban.prototype.bindUntilUnload = function (el, event, callback) {
        // bind event to el, and unbind when navigating away from the page

        // TODO - account for uid here

        el.on(event, callback);
        $(window).one(Hashban.UNLOAD_EVENT, function() {
            el.off(event, callback);
        });
    };

    Hashban.prototype.preload = function (url) {
        // preload a url to avoid transition delay
        var that = this;

        if (!this.CACHE[url]) {
            $.ajax(url, {
                success: function(data) {
                    that.CACHE[url] = data;
                }
            });
        }
    };

    // PUBLIC UTILITIES

    Hashban.getBody = function (html) {
        // get wrapped body element from an html document

        return $('<div' +
                 getmatch(html, /<body([^>]*>[\S\s]*)<\/body>/, 1) +
                 '</div>');
    };

    Hashban.absolute_url = function(url) {
        if (!url.match(/^(?:\/|https?:\/\/|:\/\/)/)) {
            // must be relative
            return window.location.pathname.replace(/\/[^\/]+$/, '/') + url;
        }
        return url;
    };

    // INTERNAL UTILITIES

    function getmatch(str, re, i) {
        // find and return the ith matched pattern in a regex, or
        // return a blank string if not found

        var match = str.match(re);
        if (match) {
            return match[i];
        }
        else {
            return '';
        }
    };

    function get_direction(from, to, link_order) {
        // Returns 1 (forwards) or -1 (backwards) for a given transition.
        // 0 is returned when the direction cannot be inferred.
        // link_order should be a list of the site's top-level link urls,
        // in order (see setup function).

        if (!link_order) {
            link_order = [];
        }

        if (to === from) {
            // this shouldn't really happen
            return 0;
        }
        else if (is_sublink(from, to)) {
            // if to is a child of from, go forward
            return 1;
        }
        else if (is_sublink(to, from)) {
            // if from is a child of to, go backwards
            return -1;
        }
        else {
            // otherwise, investigate the link_order setting to see
            // which way to go:
            var from_i = -1,
                to_i = -1;

            // first look for exact matches
            for (var i = 0; i < link_order.length; i++) {
                if (from_i === -1 && from === link_order[i]) {
                    from_i = i;
                }
                if (to_i === -1 && to === link_order[i]) {
                    to_i = i;
                }
            }
            if (from_i === -1 || to_i === -1) {
                // if no exact match, look for parent urls that match
                for (var i = 0; i < link_order.length; i++) {
                    if (from_i === -1 && is_sublink(link_order[i], from)) {
                        from_i = i;
                    }
                    if (to_i === -1 && is_sublink(link_order[i], to)) {
                        to_i = i;
                    }
                }
            }

            if (from_i === to_i || from_i === -1 || to_i === -1) {
                // can't determine a direction
                return 0;
            }
            else if (to_i < from_i) {
                // return backwards
                return -1;
            }
            else {
                // return forwards
                return 1;
            }
        }
    };

    function is_sublink(link, possible_sublink) {
        // Determine whether possible_sublink is a child of
        // link in the url tree. Returns false if the links
        // are the same.

        // add trailing slashes if they're missing, to ensure
        // that is_sublink(/test', '/test-2') returns false,
        // but is_sublink(/test', '/test/2') returns true.
        if (possible_sublink.slice(-1) !== '/') {
            possible_sublink += '/';
        }
        if (link.slice(-1) !== '/') {
            link += '/';
        }

        if (link !== possible_sublink && possible_sublink.indexOf(link) === 0) {
            return true;
        }
        else {
            return false;
        }
    };

    return Hashban;

})(jQuery);
<|endoftext|>


<commit_before>(function($){
  var touch = {}, touchTimeout;

  function parentIfText(node){
    return 'tagName' in node ? node : node.parentNode;
  }

  function swipeDirection(x1, x2, y1, y2){
    var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2);
    if (xDelta >= yDelta) {
      return (x1 - x2 > 0 ? 'Left' : 'Right');
    } else {
      return (y1 - y2 > 0 ? 'Down' : 'Up');
    }
  }

  $(document).ready(function(){
    $(document.body).bind('touchstart', function(e){
      var now = Date.now(), delta = now - (touch.last || now);
      touch.target = parentIfText(e.touches[0].target);
      touchTimeout && clearTimeout(touchTimeout);
      touch.x1 = e.touches[0].pageX;
      touch.y1 = e.touches[0].pageY;
      if (delta > 0 && delta <= 250) touch.isDoubleTap = true;
      touch.last = now;
    }).bind('touchmove', function(e){
      touch.x2 = e.touches[0].pageX;
      touch.y2 = e.touches[0].pageY;
    }).bind('touchend', function(e){
      if (touch.isDoubleTap) {
        $(touch.target).trigger('doubleTap');
        touch = {};
      } else if (touch.x2 > 0 || touch.y2 > 0) {
        (Math.abs(touch.x1 - touch.x2) > 30 || Math.abs(touch.y1 - touch.y2) > 30)  &&
          $(touch.target).trigger('swipe') &&
          $(touch.target).trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)));
        touch.x1 = touch.x2 = touch.y1 = touch.y2 = touch.last = 0;
      } else if ('last' in touch) {
        touchTimeout = setTimeout(function(){
          touchTimeout = null;
          $(touch.target).trigger('tap')
          touch = {};
        }, 250);
      }
    }).bind('touchcancel', function(){ touch = {} });
  });

  ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap'].forEach(function(m){
    $.fn[m] = function(callback){ return this.bind(m, callback) }
  });
})(Zepto);
<commit_msg>Corrected direction for vertical swipe.<commit_after>(function($){
  var touch = {}, touchTimeout;

  function parentIfText(node){
    return 'tagName' in node ? node : node.parentNode;
  }

  function swipeDirection(x1, x2, y1, y2){
    var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2);
    if (xDelta >= yDelta) {
      return (x1 - x2 > 0 ? 'Left' : 'Right');
    } else {
      return (y1 - y2 > 0 ? 'Up' : 'Down');
    }
  }

  $(document).ready(function(){
    $(document.body).bind('touchstart', function(e){
      var now = Date.now(), delta = now - (touch.last || now);
      touch.target = parentIfText(e.touches[0].target);
      touchTimeout && clearTimeout(touchTimeout);
      touch.x1 = e.touches[0].pageX;
      touch.y1 = e.touches[0].pageY;
      if (delta > 0 && delta <= 250) touch.isDoubleTap = true;
      touch.last = now;
    }).bind('touchmove', function(e){
      touch.x2 = e.touches[0].pageX;
      touch.y2 = e.touches[0].pageY;
    }).bind('touchend', function(e){
      if (touch.isDoubleTap) {
        $(touch.target).trigger('doubleTap');
        touch = {};
      } else if (touch.x2 > 0 || touch.y2 > 0) {
        (Math.abs(touch.x1 - touch.x2) > 30 || Math.abs(touch.y1 - touch.y2) > 30)  &&
          $(touch.target).trigger('swipe') &&
          $(touch.target).trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)));
        touch.x1 = touch.x2 = touch.y1 = touch.y2 = touch.last = 0;
      } else if ('last' in touch) {
        touchTimeout = setTimeout(function(){
          touchTimeout = null;
          $(touch.target).trigger('tap')
          touch = {};
        }, 250);
      }
    }).bind('touchcancel', function(){ touch = {} });
  });

  ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap'].forEach(function(m){
    $.fn[m] = function(callback){ return this.bind(m, callback) }
  });
})(Zepto);
<|endoftext|>


<commit_before>/**
 * Gets the data attribute. the name must be kebab-case.
 */
export const getDataAttribute = (el, name) => el.getAttribute(`data-vv-${name}`);

/**
 * Determines the input field scope.
 */
export const getScope = (el) => {
  let scope = getDataAttribute(el, 'scope');
  if (! scope && el.form) {
    scope = getDataAttribute(el.form, 'scope');
  }

  return scope;
};

/**
 * Debounces a function.
 */
export const debounce = (callback, wait = 0, immediate) => {
  let timeout;
  if (wait == 0) {
    return callback;
  }
  return (...args) => {
    const later = () => {
      timeout = null;
      if (!immediate) callback(...args);
    };
    const callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) callback(args);
  };
};

/**
 * Emits a warning to the console.
 */
export const warn = (message) => {
  if (! console) {
    return;
  }

    console.warn(`[vee-validate]: ${message}`); // eslint-disable-line
};

/**
 * Checks if the value is an object.
 */
export const isObject = (object) => {
  return object !== null && object && typeof object === 'object' && ! Array.isArray(object);
};

/**
 * Checks if a function is callable.
 */
export const isCallable = (func) => typeof func === 'function';

/**
 * Check if element has the css class on it.
 */
export const hasClass = (el, className) => {
  if (el.classList) {
    return el.classList.contains(className);
  }

  return !!el.className.match(new RegExp(`(\\s|^)${className}(\\s|$)`));
};

/**
 * Adds the provided css className to the element.
 */
export const addClass = (el, className) => {
  if (el.classList) {
    el.classList.add(className);
    return;
  }

  if (!hasClass(el, className)) {
    el.className += ` ${className}`;
  }
};

/**
 * Remove the provided css className from the element.
 */
export const removeClass = (el, className) => {
  if (el.classList) {
    el.classList.remove(className);
    return;
  }

  if (hasClass(el, className)) {
    const reg = new RegExp(`(\\s|^)${className}(\\s|$)`);
    el.className = el.className.replace(reg, ' ');
  }
};

/**
 * Converts an array-like object to array.
 * Simple polyfill for Array.from
 */
export const toArray = (arrayLike) => {
  if (Array.from) {
    return Array.from(arrayLike);
  }

  const array = [];
  const length = arrayLike.length;
  for (let i = 0; i < length; i++) {
    array.push(arrayLike[i]);
  }

  return array;
};

/**
 * Assign polyfill from the mdn.
 */
export const assign = (target, ...others) => {
  if (Object.assign) {
    return Object.assign(target, ...others);
  }

  if (target == null) {
    throw new TypeError('Cannot convert undefined or null to object');
  }

  const to = Object(target);
  others.forEach(arg => {
    // Skip over if undefined or null
    if (arg != null) {
      Object.keys(arg).forEach(key => {
        to[key] = arg[key];
      });
    }
  });

  return to;
};

/**
 * polyfills array.find
 * @param {Array} array
 * @param {Function} predicate
 */
export const find = (array, predicate) => {
  if (array.find) {
    return array.find(predicate);
  }

  let result;
  array.some(item => {
    if (predicate(item)) {
      result = item;
      return true;
    }

    return false;
  });

  return result;
};

/**
 * Gets the rules from a binding value or the element dataset.
 *
 * @param {String} expression The binding expression.
 * @param {Object|String} value The binding value.
 * @param {element} el The element.
 * @returns {String|Object}
 */
export const getRules = (expression, value, el) => {
  if (! expression) {
    return getDataAttribute(el, 'rules');
  }

  if (typeof value === 'string') {
    return value;
  }

  if (~['string', 'object'].indexOf(typeof value.rules)) {
    return value.rules
  }

  return value;
};
<commit_msg>fix #361<commit_after>/**
 * Gets the data attribute. the name must be kebab-case.
 */
export const getDataAttribute = (el, name) => el.getAttribute(`data-vv-${name}`);

/**
 * Determines the input field scope.
 */
export const getScope = (el) => {
  let scope = getDataAttribute(el, 'scope');
  if (! scope && el.form) {
    scope = getDataAttribute(el.form, 'scope');
  }

  return scope;
};

/**
 * Debounces a function.
 */
export const debounce = (callback, wait = 0, immediate) => {
  let timeout;

  return (...args) => {
    const later = () => {
      timeout = null;
      if (!immediate) callback(...args);
    };
    const callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) callback(args);
  };
};

/**
 * Emits a warning to the console.
 */
export const warn = (message) => {
  if (! console) {
    return;
  }

    console.warn(`[vee-validate]: ${message}`); // eslint-disable-line
};

/**
 * Checks if the value is an object.
 */
export const isObject = (object) =>
  object !== null && object && typeof object === 'object' && ! Array.isArray(object);

/**
 * Checks if a function is callable.
 */
export const isCallable = (func) => typeof func === 'function';

/**
 * Check if element has the css class on it.
 */
export const hasClass = (el, className) => {
  if (el.classList) {
    return el.classList.contains(className);
  }

  return !!el.className.match(new RegExp(`(\\s|^)${className}(\\s|$)`));
};

/**
 * Adds the provided css className to the element.
 */
export const addClass = (el, className) => {
  if (el.classList) {
    el.classList.add(className);
    return;
  }

  if (!hasClass(el, className)) {
    el.className += ` ${className}`;
  }
};

/**
 * Remove the provided css className from the element.
 */
export const removeClass = (el, className) => {
  if (el.classList) {
    el.classList.remove(className);
    return;
  }

  if (hasClass(el, className)) {
    const reg = new RegExp(`(\\s|^)${className}(\\s|$)`);
    el.className = el.className.replace(reg, ' ');
  }
};

/**
 * Converts an array-like object to array.
 * Simple polyfill for Array.from
 */
export const toArray = (arrayLike) => {
  if (Array.from) {
    return Array.from(arrayLike);
  }

  const array = [];
  const length = arrayLike.length;
  for (let i = 0; i < length; i++) {
    array.push(arrayLike[i]);
  }

  return array;
};

/**
 * Assign polyfill from the mdn.
 */
export const assign = (target, ...others) => {
  if (Object.assign) {
    return Object.assign(target, ...others);
  }

  if (target == null) {
    throw new TypeError('Cannot convert undefined or null to object');
  }

  const to = Object(target);
  others.forEach(arg => {
    // Skip over if undefined or null
    if (arg != null) {
      Object.keys(arg).forEach(key => {
        to[key] = arg[key];
      });
    }
  });

  return to;
};

/**
 * polyfills array.find
 * @param {Array} array
 * @param {Function} predicate
 */
export const find = (array, predicate) => {
  if (array.find) {
    return array.find(predicate);
  }

  let result;
  array.some(item => {
    if (predicate(item)) {
      result = item;
      return true;
    }

    return false;
  });

  return result;
};

/**
 * Gets the rules from a binding value or the element dataset.
 *
 * @param {String} expression The binding expression.
 * @param {Object|String} value The binding value.
 * @param {element} el The element.
 * @returns {String|Object}
 */
export const getRules = (expression, value, el) => {
  if (! expression) {
    return getDataAttribute(el, 'rules');
  }

  if (typeof value === 'string') {
    return value;
  }

  if (~['string', 'object'].indexOf(typeof value.rules)) {
    return value.rules;
  }

  return value;
};
<|endoftext|>


<commit_before>const childProcess = require('child_process')
const fs = require('fs')
// const _        = require('lodash')
const path = require('path')
// const _ = require('lodash')
const yaml = require('js-yaml')
// const spawnSync   = require('spawn-sync')
// const oneLine     = require('common-tags/lib/oneLine')
// const stripIndent = require('common-tags/lib/stripIndent')
const scrolex = require('scrolex').persistOpts({
  announce             : true,
  addCommandAsComponent: true,
})
const utils = this
const oneLine = require('common-tags/lib/oneLine')
// const pad = require('pad')

if (require.main === module) {
  scrolex.failure(`Please only used this module via require`)
  process.exit(1)
}

module.exports.formatCmd = function formatCmd (cmd, { runtime, cmdName }) {
  // Replace dirs
  cmd = cmd.replace(/\[lanyonDir]/g, runtime.lanyonDir)
  cmd = cmd.replace(/\[contentBuildDir]/g, runtime.contentBuildDir)
  cmd = cmd.replace(/\[projectDir]/g, runtime.projectDir)
  cmd = cmd.replace(/\[cacheDir]/g, runtime.cacheDir)

  // Replace all npms with their first-found full-path executables
  const npmBins = {
    'browser-sync': 'node_modules/browser-sync/bin/browser-sync.js',
    'nodemon'     : 'node_modules/nodemon/bin/nodemon.js',
    'webpack'     : 'node_modules/webpack/bin/webpack.js',
    // 'imagemin'     : 'node_modules/imagemin-cli/cli.js',
  }
  for (const name in npmBins) {
    const tests = [
      `/srv/lanyon/${npmBins[name]}`,
      `${runtime.npmRoot}/${npmBins[name]}`,
      `${runtime.projectDir}/${npmBins[name]}`,
      `${runtime.lanyonDir}/${npmBins[name]}`,
      `${runtime.gitRoot}/${npmBins[name]}`,
    ]

    let found = false
    tests.forEach(test => {
      if (fs.existsSync(test)) {
        npmBins[name] = test
        found = true
        return false // Stop looking on first hit
      }
    })

    if (!found) {
      throw new Error(`Cannot find dependency "${name}" in "${tests.join('", "')}"`)
    }
    const pat = new RegExp(`(\\s|^)\\[${name}\\](\\s|$)`)
    cmd = cmd.replace(pat, `$1node ${npmBins[name]}$2`)

    // let nodeBin = utils.dockerString('node', { extraArgs: extraVolumes, runtime })
    // cmd = cmd.replace(pat, `$1${nodeBin} ${npmBins[name]}$2`)
  }

  // cp -f ${runtime.projectDir}/Gemfile ${runtime.cacheDir}/Gemfile &&
  let jekyllBin = utils.dockerString('jekyll', { runtime })

  // Replace shims
  cmd = cmd.replace(/(\s|^)\[jekyll\](\s|$)/, `$1${jekyllBin}$2`)

  return cmd
}

module.exports.trapCleanup = function trapCleanup ({ runtime, code = 0, signal = '' }) {
  if (runtime.dying === true) {
    return
  }
  runtime.dying = true

  let cleanupCmds = [
    'pkill -f nodemon',
    'pkill -f browser-sync',
  ]

  if (runtime.dockerSync && runtime.dockerSync.enabled === true) {
    cleanupCmds = cleanupCmds.concat([
      `docker-sync stop`,
      // `docker-compose stop`,
    ])
  }

  console.log(`>>> About to exit. code=${code}, signal=${signal}. Cleaning up... `)
  for (let i in cleanupCmds) {
    let c = cleanupCmds[i]
    console.error(`Cleanup command: '${c}' ...`)
    try {
      childProcess.execSync(`${c}`, {
        cwd: `${runtime.cacheDir}`,
      })
    } catch (err) {} // eslint-disable-line
  }

  if (signal === 'SIGINT') {
    console.log(`>>> Process aborted`)
    process.exit(1)
  }
}

module.exports.dockerString = function dockerString (cmd, { runtime }) {
  let volumePaths = utils.volumePaths({ runtime })
  let listVolumes = []
  for (let key in volumePaths) {
    listVolumes.push(`--volume ${key}:${volumePaths[key]}`)
  }

  if (runtime.dockerSync && runtime.dockerSync.enabled === true) {
    // -i
    // --rm
    // ${cmd}
    // --workdir=${runtime.cacheDir}
    // -e "JEKYLL_ENV=${runtime.lanyonEnv}"
    // docker-sync start && docker-compose up && docker-compose exec
    return oneLine`
      docker-compose exec -T
        lanyon-container
      ${cmd}
    `
  }

  return oneLine`
    docker run
      --rm
      -i
      --env "JEKYLL_ENV=${runtime.lanyonEnv}"
      --workdir ${runtime.cacheDir}
      ${listVolumes.join('\n')}
      ${runtime.dockerImage}
    ${cmd}
  `
}

module.exports.volumePaths = function volumePaths ({ runtime }) {
  let volumePaths = {}

  if (runtime.contentBuildDir.indexOf(runtime.projectDir) === -1) {
    volumePaths[runtime.contentBuildDir] = runtime.contentBuildDir
  }
  if (runtime.cacheDir.indexOf(runtime.projectDir) === -1) {
    volumePaths[runtime.cacheDir] = runtime.cacheDir
  }

  volumePaths[runtime.projectDir] = runtime.projectDir

  return volumePaths
}

module.exports.runString = async function runString (cmd, { runtime, cmdName, origCmd, hookName }) {
  const scrolexOpts = {
    cwd                  : runtime.cacheDir,
    fatal                : true,
    mode                 : 'passthru',
    components           : `lanyon>${cmdName}>${origCmd.split('--')[0].trim().replace('[', '').replace(']', '')}`,
    addCommandAsComponent: false,
  }

  await utils.runhooks('pre', hookName, runtime)
  await scrolex.exe(cmd, scrolexOpts)
  await utils.runhooks('post', hookName, runtime)
}

module.exports.runhooks = async (order, cmdName, runtime) => {
  let squashedHooks = utils.gethooks(order, cmdName, runtime)

  scrolex.stick(`Running ${squashedHooks.length} ${order}${cmdName} hooks`)
  if (!squashedHooks.length) {
    return
  }

  return scrolex.exe(squashedHooks.join(' && '), {
    cwd       : runtime.projectDir,
    mode      : 'passthru',
    components: `lanyon>hooks>${order}${cmdName}`,
  })
}

module.exports.gethooks = (order, cmdName, runtime) => {
  let arr = []

  arr = [
    `${order}${cmdName}`,
    `${order}${cmdName}:production`,
    `${order}${cmdName}:development`,
    `${order}${cmdName}:content`,
    `${order}${cmdName}:content:production`,
    `${order}${cmdName}:content:development`,
    `${order}${cmdName}:assets`,
    `${order}${cmdName}:assets:production`,
    `${order}${cmdName}:assets:development`,
  ]

  let squashedHooks = []
  for (let i in arr) {
    let hook = arr[i]
    if (runtime[hook]) {
      const lastPart = hook.split(':').pop()
      let needEnv = 'both'

      if (lastPart === 'production') {
        needEnv = lastPart
      }
      if (lastPart === 'development') {
        needEnv = lastPart
      }

      if (needEnv === 'both' || runtime.lanyonEnv === needEnv) {
        squashedHooks = squashedHooks.concat(runtime[hook])
      }
    }
  }

  return squashedHooks
}

module.exports.upwardDirContaining = (find, cwd, not) => {
  if (!cwd) {
    cwd = process.env.PWD || process.cwd()
  }
  const parts = cwd.split('/')
  while (parts.length) {
    const newParts = parts
    const ppath = `${newParts.join('/')}/${find}`
    if (fs.existsSync(ppath)) {
      if (not === undefined || not !== path.basename(path.dirname(ppath))) {
        return path.dirname(ppath)
      }
    }
    parts.pop()
  }
  return false
}

module.exports.initProject = async ({ assetsBuildDir, gitRoot, cacheDir }) => {
  const scrolexOpts = {
    cwd  : gitRoot,
    fatal: false,
    mode : 'passthru',
  }

  const dirs = [
    assetsBuildDir,
    cacheDir,
  ]

  for (let dir of dirs) {
    if (!fs.existsSync(dir)) {
      let rel = path.relative(gitRoot, dir)
      try {
        await scrolex.exe(`mkdir -p '${rel}' && git ignore '${rel}'`, scrolexOpts)
      } catch (error) {
        console.error(`Could not create dir. rel=${rel} gitRoot=${gitRoot} dir=${dir} err=${err}`)
      }
    }
  }
}

module.exports.fsCopySync = (src, dst, { mode = '644', encoding = 'utf-8' } = {}) => {
  fs.writeFileSync(`${dst}`, fs.readFileSync(`${src}`, 'utf-8'), { mode, encoding })
}

module.exports.writeConfig = (cfg) => {
  if (!fs.existsSync(`${cfg.runtime.cacheDir}/jekyll.lanyon_assets.yml`)) {
    fs.writeFileSync(`${cfg.runtime.cacheDir}/jekyll.lanyon_assets.yml`, '# this file should be overwritten by the Webpack AssetsPlugin', 'utf-8')
  }
  try {
    fs.writeFileSync(`${cfg.runtime.cacheDir}/jekyll.config.yml`, yaml.safeDump(cfg.jekyll), 'utf-8')
  } catch (e) {
    console.error({ jekyll: cfg.jekyll })
    throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/jekyll.config.yml. ${e.message}`)
  }

  if (cfg.runtime.dockerSync && cfg.runtime.dockerSync.enabled === true) {
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-sync.yml`, yaml.safeDump(cfg.dockerSync), 'utf-8')
    } catch (e) {
      console.error({ dockerSync: cfg.dockerSync })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-sync.yml. ${e.message}`)
    }
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-compose.yml`, yaml.safeDump(cfg.dockerCompose), 'utf-8')
    } catch (e) {
      console.error({ dockerCompose: cfg.dockerCompose })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-compose.yml. ${e.message}`)
    }
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-compose-dev.yml`, yaml.safeDump(cfg.dockerComposeDev), 'utf-8')
    } catch (e) {
      console.error({ dockerComposeDev: cfg.dockerComposeDev })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-compose-dev.yml. ${e.message}`)
    }
  }

  fs.writeFileSync(`${cfg.runtime.cacheDir}/nodemon.config.json`, JSON.stringify(cfg.nodemon, null, '  '), 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/full-config-dump.json`, JSON.stringify(cfg, null, '  '), 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/browsersync.config.js`, `module.exports = require("${cfg.runtime.lanyonDir}/src/config.js").browsersync`, 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/webpack.config.js`, `module.exports = require("${cfg.runtime.lanyonDir}/src/config.js").webpack`, 'utf-8')
  fs.writeFileSync(cfg.runtime.recordsPath, JSON.stringify({}, null, '  '), 'utf-8')
}
<commit_msg>Fix typo<commit_after>const childProcess = require('child_process')
const fs = require('fs')
// const _        = require('lodash')
const path = require('path')
// const _ = require('lodash')
const yaml = require('js-yaml')
// const spawnSync   = require('spawn-sync')
// const oneLine     = require('common-tags/lib/oneLine')
// const stripIndent = require('common-tags/lib/stripIndent')
const scrolex = require('scrolex').persistOpts({
  announce             : true,
  addCommandAsComponent: true,
})
const utils = this
const oneLine = require('common-tags/lib/oneLine')
// const pad = require('pad')

if (require.main === module) {
  scrolex.failure(`Please only used this module via require`)
  process.exit(1)
}

module.exports.formatCmd = function formatCmd (cmd, { runtime, cmdName }) {
  // Replace dirs
  cmd = cmd.replace(/\[lanyonDir]/g, runtime.lanyonDir)
  cmd = cmd.replace(/\[contentBuildDir]/g, runtime.contentBuildDir)
  cmd = cmd.replace(/\[projectDir]/g, runtime.projectDir)
  cmd = cmd.replace(/\[cacheDir]/g, runtime.cacheDir)

  // Replace all npms with their first-found full-path executables
  const npmBins = {
    'browser-sync': 'node_modules/browser-sync/bin/browser-sync.js',
    'nodemon'     : 'node_modules/nodemon/bin/nodemon.js',
    'webpack'     : 'node_modules/webpack/bin/webpack.js',
    // 'imagemin'     : 'node_modules/imagemin-cli/cli.js',
  }
  for (const name in npmBins) {
    const tests = [
      `/srv/lanyon/${npmBins[name]}`,
      `${runtime.npmRoot}/${npmBins[name]}`,
      `${runtime.projectDir}/${npmBins[name]}`,
      `${runtime.lanyonDir}/${npmBins[name]}`,
      `${runtime.gitRoot}/${npmBins[name]}`,
    ]

    let found = false
    tests.forEach(test => {
      if (fs.existsSync(test)) {
        npmBins[name] = test
        found = true
        return false // Stop looking on first hit
      }
    })

    if (!found) {
      throw new Error(`Cannot find dependency "${name}" in "${tests.join('", "')}"`)
    }
    const pat = new RegExp(`(\\s|^)\\[${name}\\](\\s|$)`)
    cmd = cmd.replace(pat, `$1node ${npmBins[name]}$2`)

    // let nodeBin = utils.dockerString('node', { extraArgs: extraVolumes, runtime })
    // cmd = cmd.replace(pat, `$1${nodeBin} ${npmBins[name]}$2`)
  }

  // cp -f ${runtime.projectDir}/Gemfile ${runtime.cacheDir}/Gemfile &&
  let jekyllBin = utils.dockerString('jekyll', { runtime })

  // Replace shims
  cmd = cmd.replace(/(\s|^)\[jekyll\](\s|$)/, `$1${jekyllBin}$2`)

  return cmd
}

module.exports.trapCleanup = function trapCleanup ({ runtime, code = 0, signal = '' }) {
  if (runtime.dying === true) {
    return
  }
  runtime.dying = true

  let cleanupCmds = [
    'pkill -f nodemon',
    'pkill -f browser-sync',
  ]

  if (runtime.dockerSync && runtime.dockerSync.enabled === true) {
    cleanupCmds = cleanupCmds.concat([
      `docker-sync stop`,
      // `docker-compose stop`,
    ])
  }

  console.log(`>>> About to exit. code=${code}, signal=${signal}. Cleaning up... `)
  for (let i in cleanupCmds) {
    let c = cleanupCmds[i]
    console.error(`Cleanup command: '${c}' ...`)
    try {
      childProcess.execSync(`${c}`, {
        cwd: `${runtime.cacheDir}`,
      })
    } catch (err) {} // eslint-disable-line
  }

  if (signal === 'SIGINT') {
    console.log(`>>> Process aborted`)
    process.exit(1)
  }
}

module.exports.dockerString = function dockerString (cmd, { runtime }) {
  let volumePaths = utils.volumePaths({ runtime })
  let listVolumes = []
  for (let key in volumePaths) {
    listVolumes.push(`--volume ${key}:${volumePaths[key]}`)
  }

  if (runtime.dockerSync && runtime.dockerSync.enabled === true) {
    // -i
    // --rm
    // ${cmd}
    // --workdir=${runtime.cacheDir}
    // -e "JEKYLL_ENV=${runtime.lanyonEnv}"
    // docker-sync start && docker-compose up && docker-compose exec
    return oneLine`
      docker-compose exec -T
        lanyon-container
      ${cmd}
    `
  }

  return oneLine`
    docker run
      --rm
      -i
      --env "JEKYLL_ENV=${runtime.lanyonEnv}"
      --workdir ${runtime.cacheDir}
      ${listVolumes.join('\n')}
      ${runtime.dockerImage}
    ${cmd}
  `
}

module.exports.volumePaths = function volumePaths ({ runtime }) {
  let volumePaths = {}

  if (runtime.contentBuildDir.indexOf(runtime.projectDir) === -1) {
    volumePaths[runtime.contentBuildDir] = runtime.contentBuildDir
  }
  if (runtime.cacheDir.indexOf(runtime.projectDir) === -1) {
    volumePaths[runtime.cacheDir] = runtime.cacheDir
  }

  volumePaths[runtime.projectDir] = runtime.projectDir

  return volumePaths
}

module.exports.runString = async function runString (cmd, { runtime, cmdName, origCmd, hookName }) {
  const scrolexOpts = {
    cwd                  : runtime.cacheDir,
    fatal                : true,
    mode                 : 'passthru',
    components           : `lanyon>${cmdName}>${origCmd.split('--')[0].trim().replace('[', '').replace(']', '')}`,
    addCommandAsComponent: false,
  }

  await utils.runhooks('pre', hookName, runtime)
  await scrolex.exe(cmd, scrolexOpts)
  await utils.runhooks('post', hookName, runtime)
}

module.exports.runhooks = async (order, cmdName, runtime) => {
  let squashedHooks = utils.gethooks(order, cmdName, runtime)

  scrolex.stick(`Running ${squashedHooks.length} ${order}${cmdName} hooks`)
  if (!squashedHooks.length) {
    return
  }

  return scrolex.exe(squashedHooks.join(' && '), {
    cwd       : runtime.projectDir,
    mode      : 'passthru',
    components: `lanyon>hooks>${order}${cmdName}`,
  })
}

module.exports.gethooks = (order, cmdName, runtime) => {
  let arr = []

  arr = [
    `${order}${cmdName}`,
    `${order}${cmdName}:production`,
    `${order}${cmdName}:development`,
    `${order}${cmdName}:content`,
    `${order}${cmdName}:content:production`,
    `${order}${cmdName}:content:development`,
    `${order}${cmdName}:assets`,
    `${order}${cmdName}:assets:production`,
    `${order}${cmdName}:assets:development`,
  ]

  let squashedHooks = []
  for (let i in arr) {
    let hook = arr[i]
    if (runtime[hook]) {
      const lastPart = hook.split(':').pop()
      let needEnv = 'both'

      if (lastPart === 'production') {
        needEnv = lastPart
      }
      if (lastPart === 'development') {
        needEnv = lastPart
      }

      if (needEnv === 'both' || runtime.lanyonEnv === needEnv) {
        squashedHooks = squashedHooks.concat(runtime[hook])
      }
    }
  }

  return squashedHooks
}

module.exports.upwardDirContaining = (find, cwd, not) => {
  if (!cwd) {
    cwd = process.env.PWD || process.cwd()
  }
  const parts = cwd.split('/')
  while (parts.length) {
    const newParts = parts
    const ppath = `${newParts.join('/')}/${find}`
    if (fs.existsSync(ppath)) {
      if (not === undefined || not !== path.basename(path.dirname(ppath))) {
        return path.dirname(ppath)
      }
    }
    parts.pop()
  }
  return false
}

module.exports.initProject = async ({ assetsBuildDir, gitRoot, cacheDir }) => {
  const scrolexOpts = {
    cwd  : gitRoot,
    fatal: false,
    mode : 'passthru',
  }

  const dirs = [
    assetsBuildDir,
    cacheDir,
  ]

  for (let dir of dirs) {
    if (!fs.existsSync(dir)) {
      let rel = path.relative(gitRoot, dir)
      try {
        await scrolex.exe(`mkdir -p '${rel}' && git ignore '${rel}'`, scrolexOpts)
      } catch (err) {
        console.error(`Could not create dir. rel=${rel} gitRoot=${gitRoot} dir=${dir} err=${err}`)
      }
    }
  }
}

module.exports.fsCopySync = (src, dst, { mode = '644', encoding = 'utf-8' } = {}) => {
  fs.writeFileSync(`${dst}`, fs.readFileSync(`${src}`, 'utf-8'), { mode, encoding })
}

module.exports.writeConfig = (cfg) => {
  if (!fs.existsSync(`${cfg.runtime.cacheDir}/jekyll.lanyon_assets.yml`)) {
    fs.writeFileSync(`${cfg.runtime.cacheDir}/jekyll.lanyon_assets.yml`, '# this file should be overwritten by the Webpack AssetsPlugin', 'utf-8')
  }
  try {
    fs.writeFileSync(`${cfg.runtime.cacheDir}/jekyll.config.yml`, yaml.safeDump(cfg.jekyll), 'utf-8')
  } catch (e) {
    console.error({ jekyll: cfg.jekyll })
    throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/jekyll.config.yml. ${e.message}`)
  }

  if (cfg.runtime.dockerSync && cfg.runtime.dockerSync.enabled === true) {
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-sync.yml`, yaml.safeDump(cfg.dockerSync), 'utf-8')
    } catch (e) {
      console.error({ dockerSync: cfg.dockerSync })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-sync.yml. ${e.message}`)
    }
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-compose.yml`, yaml.safeDump(cfg.dockerCompose), 'utf-8')
    } catch (e) {
      console.error({ dockerCompose: cfg.dockerCompose })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-compose.yml. ${e.message}`)
    }
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-compose-dev.yml`, yaml.safeDump(cfg.dockerComposeDev), 'utf-8')
    } catch (e) {
      console.error({ dockerComposeDev: cfg.dockerComposeDev })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-compose-dev.yml. ${e.message}`)
    }
  }

  fs.writeFileSync(`${cfg.runtime.cacheDir}/nodemon.config.json`, JSON.stringify(cfg.nodemon, null, '  '), 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/full-config-dump.json`, JSON.stringify(cfg, null, '  '), 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/browsersync.config.js`, `module.exports = require("${cfg.runtime.lanyonDir}/src/config.js").browsersync`, 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/webpack.config.js`, `module.exports = require("${cfg.runtime.lanyonDir}/src/config.js").webpack`, 'utf-8')
  fs.writeFileSync(cfg.runtime.recordsPath, JSON.stringify({}, null, '  '), 'utf-8')
}
<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';
import https from 'https';
import child_process from 'child_process';
import { rimrafSync, copydirSync } from 'sander';

const tmpDirName = 'tmp';
const degitConfigName = 'degit.json';

export {degitConfigName};

export class DegitError extends Error {
	constructor(message, opts) {
		super(message);
		Object.assign(this, opts);
	}
}

export function tryRequire(file, opts) {
	try {
		if (opts && opts.clearCache === true) {
			delete require.cache[require.resolve(file)];
		}
		return require(file);
	} catch (err) {
		return null;
	}
}

export function exec(command) {
	return new Promise((fulfil, reject) => {
		child_process.exec(command, (err, stdout, stderr) => {
			if (err) {
				reject(err);
				return;
			}

			fulfil({ stdout, stderr });
		});
	});
}

export function mkdirp(dir) {
	const parent = path.dirname(dir);
	if (parent === dir) return;

	mkdirp(parent);

	try {
		fs.mkdirSync(dir);
	} catch (err) {
		if (err.code !== 'EEXIST') throw err;
	}
}

export function fetch(url, dest) {
	return new Promise((fulfil, reject) => {
		https.get(url, response => {
			const code = response.statusCode;
			if (code >= 400) {
				reject({ code, message: response.statusMessage });
			} else if (code >= 300) {
				fetch(response.headers.location, dest).then(fulfil, reject);
			} else {
				response.pipe(fs.createWriteStream(dest))
					.on('finish', () => fulfil())
					.on('error', reject);
			}
		}).on('error', reject);
	});
}

export function stashFiles(dir, dest) {
	const tmpDir = path.join(dir, tmpDirName);
	console.log('tmpDir', tmpDir)
	rimrafSync(tmpDir);
	mkdirp(tmpDir);
	fs.readdirSync(dest).forEach(file => {
		const filePath = path.join(dest, file);
		const targetPath = path.join(tmpDir, file);
		const isDir = fs.lstatSync(filePath).isDirectory();
		if (isDir) {
			copydirSync(filePath).to(targetPath);
			rimrafSync(filePath);
		} else {
			fs.copyFileSync(filePath, targetPath);
			fs.unlinkSync(filePath);
		}
	});
}

export function unstashFiles(dir, dest) {
	const tmpDir = path.join(dir, tmpDirName);
	fs.readdirSync(tmpDir).forEach(filename => {
		const tmpFile = path.join(tmpDir, filename);
		const targetPath = path.join(dest, filename);
		const isDir = fs.lstatSync(tmpFile).isDirectory();
		if (isDir) {
			copydirSync(tmpFile).to(targetPath);
			rimrafSync(tmpFile);
		} else {
			if (filename !== 'degit.json') {
				fs.copyFileSync(tmpFile, targetPath);
			}
			fs.unlinkSync(tmpFile);
		}
	});
	rimrafSync(tmpDir);
}
<commit_msg>Remove console.log<commit_after>import fs from 'fs';
import path from 'path';
import https from 'https';
import child_process from 'child_process';
import { rimrafSync, copydirSync } from 'sander';

const tmpDirName = 'tmp';
const degitConfigName = 'degit.json';

export {degitConfigName};

export class DegitError extends Error {
	constructor(message, opts) {
		super(message);
		Object.assign(this, opts);
	}
}

export function tryRequire(file, opts) {
	try {
		if (opts && opts.clearCache === true) {
			delete require.cache[require.resolve(file)];
		}
		return require(file);
	} catch (err) {
		return null;
	}
}

export function exec(command) {
	return new Promise((fulfil, reject) => {
		child_process.exec(command, (err, stdout, stderr) => {
			if (err) {
				reject(err);
				return;
			}

			fulfil({ stdout, stderr });
		});
	});
}

export function mkdirp(dir) {
	const parent = path.dirname(dir);
	if (parent === dir) return;

	mkdirp(parent);

	try {
		fs.mkdirSync(dir);
	} catch (err) {
		if (err.code !== 'EEXIST') throw err;
	}
}

export function fetch(url, dest) {
	return new Promise((fulfil, reject) => {
		https.get(url, response => {
			const code = response.statusCode;
			if (code >= 400) {
				reject({ code, message: response.statusMessage });
			} else if (code >= 300) {
				fetch(response.headers.location, dest).then(fulfil, reject);
			} else {
				response.pipe(fs.createWriteStream(dest))
					.on('finish', () => fulfil())
					.on('error', reject);
			}
		}).on('error', reject);
	});
}

export function stashFiles(dir, dest) {
	const tmpDir = path.join(dir, tmpDirName);
	rimrafSync(tmpDir);
	mkdirp(tmpDir);
	fs.readdirSync(dest).forEach(file => {
		const filePath = path.join(dest, file);
		const targetPath = path.join(tmpDir, file);
		const isDir = fs.lstatSync(filePath).isDirectory();
		if (isDir) {
			copydirSync(filePath).to(targetPath);
			rimrafSync(filePath);
		} else {
			fs.copyFileSync(filePath, targetPath);
			fs.unlinkSync(filePath);
		}
	});
}

export function unstashFiles(dir, dest) {
	const tmpDir = path.join(dir, tmpDirName);
	fs.readdirSync(tmpDir).forEach(filename => {
		const tmpFile = path.join(tmpDir, filename);
		const targetPath = path.join(dest, filename);
		const isDir = fs.lstatSync(tmpFile).isDirectory();
		if (isDir) {
			copydirSync(tmpFile).to(targetPath);
			rimrafSync(tmpFile);
		} else {
			if (filename !== 'degit.json') {
				fs.copyFileSync(tmpFile, targetPath);
			}
			fs.unlinkSync(tmpFile);
		}
	});
	rimrafSync(tmpDir);
}
<|endoftext|>


<commit_before>export function trace(clientId: ?string, title: string, value: Object | string) {
  let prefix = '';
  if (window.performance) {
    prefix = '[' + (window.performance.now() / 1000).toFixed(3) + ']';
  }
  if (clientId) {
    prefix = prefix + '[' + clientId + ']';
  }

  if (isEdge()) {
    console.log(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
  else {
    console.info(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
}

function userAgent() {
  return window.navigator.userAgent.toLocaleLowerCase();
}

function isPlanB() {
  if (userAgent().indexOf('chrome') !== -1 || userAgent().indexOf('safari') !== -1) {
    return true;
  } else {
    return false;
  }
}

export function isEdge() {
  return userAgent().indexOf('edge') !== -1;
}

export function createSignalingMessage(role, channelId, accessToken, options) {
  const message = {
    type: 'connect',
    role: role,
    channel_id: channelId,
    access_token: accessToken,
  };
  Object.keys(message).forEach(key => {
    if (message[key] === undefined) {
      message[key] = null;
    }
  });
  // multistream
  if ('multistream' in options && options.multistream === true) {
    message.multistream = true;
    message.plan_b = isPlanB();
  }
  // create audio params
  let audio = true;
  if ('audio' in options && typeof options.audio === 'boolean') {
    audio = options.audio;
  }
  if (audio) {
    if ('audioCodecType' in options) {
      audio = {
        codec_type: options.audioCodecType
      };
    }
  }
  message['audio'] = audio;
  // create video options
  let video = true;
  if ('video' in options) {
    video = options.video;
  }

  if (video) {
    const videoPropertyKeys = ['videoCodecType', 'videoBitRate', 'videoSnapshot'];
    if (Object.keys(options).some(key => { return 0 <= videoPropertyKeys.indexOf(key); })) {
      video = {};
      if ('videoCodecType' in options) {
        video['codec_type'] = options.videoCodecType;
      }
      if ('videoBitRate' in options) {
        video['bit_rate'] = options.videoBitRate;
      }
      if ('videoSnapshot' in options) {
        video['snapshot'] = options.videoSnapshot;
      }
    }
  }
  message['video'] = video;

  return message;
}
<commit_msg>access_token を metadata に修正する<commit_after>export function trace(clientId: ?string, title: string, value: Object | string) {
  let prefix = '';
  if (window.performance) {
    prefix = '[' + (window.performance.now() / 1000).toFixed(3) + ']';
  }
  if (clientId) {
    prefix = prefix + '[' + clientId + ']';
  }

  if (isEdge()) {
    console.log(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
  else {
    console.info(prefix + ' ' + title + '\n', value); // eslint-disable-line
  }
}

function userAgent() {
  return window.navigator.userAgent.toLocaleLowerCase();
}

function isPlanB() {
  if (userAgent().indexOf('chrome') !== -1 || userAgent().indexOf('safari') !== -1) {
    return true;
  } else {
    return false;
  }
}

export function isEdge() {
  return userAgent().indexOf('edge') !== -1;
}

export function createSignalingMessage(role, channelId, metadata, options) {
  const message = {
    type: 'connect',
    role: role,
    channel_id: channelId,
    metadata: metadata,
  };
  Object.keys(message).forEach(key => {
    if (message[key] === undefined) {
      message[key] = null;
    }
  });
  // multistream
  if ('multistream' in options && options.multistream === true) {
    message.multistream = true;
    message.plan_b = isPlanB();
  }
  // create audio params
  let audio = true;
  if ('audio' in options && typeof options.audio === 'boolean') {
    audio = options.audio;
  }
  if (audio) {
    if ('audioCodecType' in options) {
      audio = {
        codec_type: options.audioCodecType
      };
    }
  }
  message['audio'] = audio;
  // create video options
  let video = true;
  if ('video' in options) {
    video = options.video;
  }

  if (video) {
    const videoPropertyKeys = ['videoCodecType', 'videoBitRate', 'videoSnapshot'];
    if (Object.keys(options).some(key => { return 0 <= videoPropertyKeys.indexOf(key); })) {
      video = {};
      if ('videoCodecType' in options) {
        video['codec_type'] = options.videoCodecType;
      }
      if ('videoBitRate' in options) {
        video['bit_rate'] = options.videoBitRate;
      }
      if ('videoSnapshot' in options) {
        video['snapshot'] = options.videoSnapshot;
      }
    }
  }
  message['video'] = video;

  return message;
}
<|endoftext|>


<commit_before>"use strict";

var isEqual = require("fast-deep-equal");
var isBuffer = require("is-buffer");
var isBlob = require("./is_blob");
var toString = Object.prototype.toString;

function find(array, predicate) {
  var length = array.length;
  for (var i = 0; i < length; i++) {
    var value = array[i];
    if (predicate(value)) return value;
  }
}

function isFunction(val) {
  return toString.call(val) === "[object Function]";
}

function isObjectOrArray(val) {
  return val !== null && typeof val === "object";
}

function isStream(val) {
  return isObjectOrArray(val) && isFunction(val.pipe);
}

function isArrayBuffer(val) {
  return toString.call(val) === "[object ArrayBuffer]";
}

function combineUrls(baseURL, url) {
  if (baseURL) {
    return baseURL.replace(/\/+$/, "") + "/" + url.replace(/^\/+/, "");
  }

  return url;
}

function findHandler(
  handlers,
  method,
  url,
  body,
  parameters,
  headers,
  baseURL
) {
  return find(handlers[method.toLowerCase()], function (handler) {
    if (typeof handler[0] === "string") {
      return (
        (isUrlMatching(url, handler[0]) ||
          isUrlMatching(combineUrls(baseURL, url), handler[0])) &&
        isBodyOrParametersMatching(method, body, parameters, handler[1]) &&
        isObjectMatching(headers, handler[2])
      );
    } else if (handler[0] instanceof RegExp) {
      return (
        (handler[0].test(url) || handler[0].test(combineUrls(baseURL, url))) &&
        isBodyOrParametersMatching(method, body, parameters, handler[1]) &&
        isObjectMatching(headers, handler[2])
      );
    }
  });
}

function isUrlMatching(url, required) {
  var noSlashUrl = url[0] === "/" ? url.substr(1) : url;
  var noSlashRequired = required[0] === "/" ? required.substr(1) : required;
  return noSlashUrl === noSlashRequired;
}

function isBodyOrParametersMatching(method, body, parameters, required) {
  var allowedParamsMethods = ["delete", "get", "head", "options"];
  if (allowedParamsMethods.indexOf(method.toLowerCase()) >= 0) {
    var data = required ? required.data : undefined;
    var params = required ? required.params : undefined;
    return isObjectMatching(parameters, params) && isBodyMatching(body, data);
  } else {
    return isBodyMatching(body, required);
  }
}

function isObjectMatching(actual, expected) {
  if (expected === undefined) return true;
  if (typeof expected.asymmetricMatch === "function") {
    return expected.asymmetricMatch(actual);
  }
  return isEqual(actual, expected);
}

function isBodyMatching(body, requiredBody) {
  if (requiredBody === undefined) {
    return true;
  }
  var parsedBody;
  try {
    parsedBody = JSON.parse(body);
  } catch (e) {}

  return isObjectMatching(parsedBody ? parsedBody : body, requiredBody);
}

function purgeIfReplyOnce(mock, handler) {
  Object.keys(mock.handlers).forEach(function (key) {
    var index = mock.handlers[key].indexOf(handler);
    if (index > -1) {
      mock.handlers[key].splice(index, 1);
    }
  });
}

function settle(resolve, reject, response, delay) {
  if (delay > 0) {
    setTimeout(settle, delay, resolve, reject, response);
    return;
  }

  if (
    !response.config.validateStatus ||
    response.config.validateStatus(response.status)
  ) {
    resolve(response);
  } else {
    reject(
      createAxiosError(
        "Request failed with status code " + response.status,
        response.config,
        response
      )
    );
  }
}

function createAxiosError(message, config, response, code) {
  var error = new Error(message);
  error.isAxiosError = true;
  error.config = config;
  if (response !== undefined) {
    error.response = response;
  }
  if (code !== undefined) {
    error.code = code;
  }

  error.toJSON = function toJSON() {
    return {
      // Standard
      message: this.message,
      name: this.name,
      // Microsoft
      description: this.description,
      number: this.number,
      // Mozilla
      fileName: this.fileName,
      lineNumber: this.lineNumber,
      columnNumber: this.columnNumber,
      stack: this.stack,
      // Axios
      config: this.config,
      code: this.code,
    };
  };
  return error;
}

function createCouldNotFindMockError(config) {
  var message =
    "Could not find mock for: \n" +
    JSON.stringify(config, ["method", "url"], 2);
  var error = new Error(message);
  error.isCouldNotFindMockError = true;
  error.url = config.url;
  error.method = config.method;
  return error;
}

module.exports = {
  find: find,
  findHandler: findHandler,
  purgeIfReplyOnce: purgeIfReplyOnce,
  settle: settle,
  isStream: isStream,
  isArrayBuffer: isArrayBuffer,
  isFunction: isFunction,
  isObjectOrArray: isObjectOrArray,
  isBuffer: isBuffer,
  isBlob: isBlob,
  isBodyOrParametersMatching: isBodyOrParametersMatching,
  isEqual: isEqual,
  createAxiosError: createAxiosError,
  createCouldNotFindMockError: createCouldNotFindMockError,
};
<commit_msg>Use AxiosError constructor to create axios errors if available<commit_after>"use strict";

var axios = require("axios");
var isEqual = require("fast-deep-equal");
var isBuffer = require("is-buffer");
var isBlob = require("./is_blob");
var toString = Object.prototype.toString;

function find(array, predicate) {
  var length = array.length;
  for (var i = 0; i < length; i++) {
    var value = array[i];
    if (predicate(value)) return value;
  }
}

function isFunction(val) {
  return toString.call(val) === "[object Function]";
}

function isObjectOrArray(val) {
  return val !== null && typeof val === "object";
}

function isStream(val) {
  return isObjectOrArray(val) && isFunction(val.pipe);
}

function isArrayBuffer(val) {
  return toString.call(val) === "[object ArrayBuffer]";
}

function combineUrls(baseURL, url) {
  if (baseURL) {
    return baseURL.replace(/\/+$/, "") + "/" + url.replace(/^\/+/, "");
  }

  return url;
}

function findHandler(
  handlers,
  method,
  url,
  body,
  parameters,
  headers,
  baseURL
) {
  return find(handlers[method.toLowerCase()], function (handler) {
    if (typeof handler[0] === "string") {
      return (
        (isUrlMatching(url, handler[0]) ||
          isUrlMatching(combineUrls(baseURL, url), handler[0])) &&
        isBodyOrParametersMatching(method, body, parameters, handler[1]) &&
        isObjectMatching(headers, handler[2])
      );
    } else if (handler[0] instanceof RegExp) {
      return (
        (handler[0].test(url) || handler[0].test(combineUrls(baseURL, url))) &&
        isBodyOrParametersMatching(method, body, parameters, handler[1]) &&
        isObjectMatching(headers, handler[2])
      );
    }
  });
}

function isUrlMatching(url, required) {
  var noSlashUrl = url[0] === "/" ? url.substr(1) : url;
  var noSlashRequired = required[0] === "/" ? required.substr(1) : required;
  return noSlashUrl === noSlashRequired;
}

function isBodyOrParametersMatching(method, body, parameters, required) {
  var allowedParamsMethods = ["delete", "get", "head", "options"];
  if (allowedParamsMethods.indexOf(method.toLowerCase()) >= 0) {
    var data = required ? required.data : undefined;
    var params = required ? required.params : undefined;
    return isObjectMatching(parameters, params) && isBodyMatching(body, data);
  } else {
    return isBodyMatching(body, required);
  }
}

function isObjectMatching(actual, expected) {
  if (expected === undefined) return true;
  if (typeof expected.asymmetricMatch === "function") {
    return expected.asymmetricMatch(actual);
  }
  return isEqual(actual, expected);
}

function isBodyMatching(body, requiredBody) {
  if (requiredBody === undefined) {
    return true;
  }
  var parsedBody;
  try {
    parsedBody = JSON.parse(body);
  } catch (e) {}

  return isObjectMatching(parsedBody ? parsedBody : body, requiredBody);
}

function purgeIfReplyOnce(mock, handler) {
  Object.keys(mock.handlers).forEach(function (key) {
    var index = mock.handlers[key].indexOf(handler);
    if (index > -1) {
      mock.handlers[key].splice(index, 1);
    }
  });
}

function settle(resolve, reject, response, delay) {
  if (delay > 0) {
    setTimeout(settle, delay, resolve, reject, response);
    return;
  }

  if (
    !response.config.validateStatus ||
    response.config.validateStatus(response.status)
  ) {
    resolve(response);
  } else {
    reject(
      createAxiosError(
        "Request failed with status code " + response.status,
        response.config,
        response
      )
    );
  }
}

function createAxiosError(message, config, response, code) {
  // axios v0.27.0+ defines AxiosError as constructor
  if (typeof axios.AxiosError === 'function') {
    return new axios.AxiosError(message, code, config, null, response);
  }

  // handling for axios v0.26.1 and below
  var error = new Error(message);
  error.isAxiosError = true;
  error.config = config;
  if (response !== undefined) {
    error.response = response;
  }
  if (code !== undefined) {
    error.code = code;
  }

  error.toJSON = function toJSON() {
    return {
      // Standard
      message: this.message,
      name: this.name,
      // Microsoft
      description: this.description,
      number: this.number,
      // Mozilla
      fileName: this.fileName,
      lineNumber: this.lineNumber,
      columnNumber: this.columnNumber,
      stack: this.stack,
      // Axios
      config: this.config,
      code: this.code,
    };
  };
  return error;
}

function createCouldNotFindMockError(config) {
  var message =
    "Could not find mock for: \n" +
    JSON.stringify(config, ["method", "url"], 2);
  var error = new Error(message);
  error.isCouldNotFindMockError = true;
  error.url = config.url;
  error.method = config.method;
  return error;
}

module.exports = {
  find: find,
  findHandler: findHandler,
  purgeIfReplyOnce: purgeIfReplyOnce,
  settle: settle,
  isStream: isStream,
  isArrayBuffer: isArrayBuffer,
  isFunction: isFunction,
  isObjectOrArray: isObjectOrArray,
  isBuffer: isBuffer,
  isBlob: isBlob,
  isBodyOrParametersMatching: isBodyOrParametersMatching,
  isEqual: isEqual,
  createAxiosError: createAxiosError,
  createCouldNotFindMockError: createCouldNotFindMockError,
};
<|endoftext|>


<commit_before>import { VNodeIterator } from './access';

export function VNode(cx,inode,type,name,value,parent,depth,indexInParent,cache){
	this.cx = cx;
	this.inode = inode;
	this.type = type;
	this.name = name;
	this.value = value;
	this.parent = parent;
	this.depth = depth | 0;
	this.indexInParent = indexInParent;
	this.cache = cache;
}

VNode.prototype.__is_VNode = true;

VNode.prototype.toString = function(){
	return this.cx.stringify(this.inode);
};

VNode.prototype.count = function(){
	if(typeof this.inode == "function") return 0;
	return this.cx.count(this.inode);
};

VNode.prototype.keys = function(){
	var cache = this.cache || this.cx.cached(this.inode, this.type);
	if(cache) return cache.keys();
	return this.cx.keys(this.inode,this.type);
};

VNode.prototype.values = function(){
	return this.cx.values(this.inode,this.type);
};

VNode.prototype.first = function(){
	return this.cx.first(this.inode,this.type);
};

VNode.prototype.last = function(){
	return this.cx.last(this.inode,this.type);
};

VNode.prototype.next = function(node){
	return this.cx.next(this.inode,node,this.type);
};

VNode.prototype.push = function(child){
	this.inode = this.cx.push(this.inode,[child.name,child.inode],this.type);
	return this;
};

VNode.prototype.set = function(key,val){
	this.inode = this.cx.set(this.inode,key,val,this.type);
	return this;
};

VNode.prototype.removeChild = function(child){
	this.inode = this.cx.removeChild(this.inode,child,this.type);
	return this;
};

VNode.prototype.finalize = function(){
	this.inode = this.cx.finalize(this.inode);
	return this;
};

VNode.prototype.attrEntries = function(){
	return this.cx.attrEntries(this.inode);
};

VNode.prototype.attr = function(k,v){
	if(arguments.length == 1) return this.cx.getAttribute(this.inode, k);
	if(arguments.length === 0) {
		this.inode = this.cx.clearAttributes(this.inode);
	} else {
		this.inode = this.cx.setAttribute(this.inode, k, v);
	}
	return this;
};

VNode.prototype.modify = function(node,ref) {
	this.inode = this.cx.modify(this.inode,node,ref,this.type);
	return this;
};

// hitch this on VNode for reuse
VNode.prototype.vnode = function(inode, parent, depth, indexInParent){
	return this.cx.vnode(inode, parent, depth, indexInParent);
};

VNode.prototype.ivalue = function(type, name, value) {
	return this.cx.ivalue(type, name, value);
};

VNode.prototype.emptyINode = function(type, name, attrs, ns){
	return this.cx.emptyINode(type, name, attrs, ns);
};

VNode.prototype.emptyAttrMap = function(init) {
	return this.cx.emptyAttrMap(init);
};

// TODO create iterator that yields a node seq
// position() should overwrite get(), but the check should be name or indexInParent
VNode.prototype[Symbol.iterator] = function(){
	return new VNodeIterator(this.values(),this, this.cx.vnode);
};

VNode.prototype.get = function(idx){
	var val = this.cx.get(this.inode,idx,this.type,this.cache);
	if(!val) return [];
	val = val.constructor == Array ? val : [val];
	return new VNodeIterator(val[Symbol.iterator](), this, this.cx.vnode);
};
<commit_msg>call iterator on values<commit_after>import { VNodeIterator } from './access';

export function VNode(cx,inode,type,name,value,parent,depth,indexInParent,cache){
	this.cx = cx;
	this.inode = inode;
	this.type = type;
	this.name = name;
	this.value = value;
	this.parent = parent;
	this.depth = depth | 0;
	this.indexInParent = indexInParent;
	this.cache = cache;
}

VNode.prototype.__is_VNode = true;

VNode.prototype.toString = function(){
	return this.cx.stringify(this.inode);
};

VNode.prototype.count = function(){
	if(typeof this.inode == "function") return 0;
	return this.cx.count(this.inode);
};

VNode.prototype.keys = function(){
	var cache = this.cache || this.cx.cached(this.inode, this.type);
	if(cache) return cache.keys();
	return this.cx.keys(this.inode,this.type);
};

VNode.prototype.values = function(){
	return this.cx.values(this.inode,this.type);
};

VNode.prototype.first = function(){
	return this.cx.first(this.inode,this.type);
};

VNode.prototype.last = function(){
	return this.cx.last(this.inode,this.type);
};

VNode.prototype.next = function(node){
	return this.cx.next(this.inode,node,this.type);
};

VNode.prototype.push = function(child){
	this.inode = this.cx.push(this.inode,[child.name,child.inode],this.type);
	return this;
};

VNode.prototype.set = function(key,val){
	this.inode = this.cx.set(this.inode,key,val,this.type);
	return this;
};

VNode.prototype.removeChild = function(child){
	this.inode = this.cx.removeChild(this.inode,child,this.type);
	return this;
};

VNode.prototype.finalize = function(){
	this.inode = this.cx.finalize(this.inode);
	return this;
};

VNode.prototype.attrEntries = function(){
	return this.cx.attrEntries(this.inode);
};

VNode.prototype.attr = function(k,v){
	if(arguments.length == 1) return this.cx.getAttribute(this.inode, k);
	if(arguments.length === 0) {
		this.inode = this.cx.clearAttributes(this.inode);
	} else {
		this.inode = this.cx.setAttribute(this.inode, k, v);
	}
	return this;
};

VNode.prototype.modify = function(node,ref) {
	this.inode = this.cx.modify(this.inode,node,ref,this.type);
	return this;
};

// hitch this on VNode for reuse
VNode.prototype.vnode = function(inode, parent, depth, indexInParent){
	return this.cx.vnode(inode, parent, depth, indexInParent);
};

VNode.prototype.ivalue = function(type, name, value) {
	return this.cx.ivalue(type, name, value);
};

VNode.prototype.emptyINode = function(type, name, attrs, ns){
	return this.cx.emptyINode(type, name, attrs, ns);
};

VNode.prototype.emptyAttrMap = function(init) {
	return this.cx.emptyAttrMap(init);
};

// TODO create iterator that yields a node seq
// position() should overwrite get(), but the check should be name or indexInParent
VNode.prototype[Symbol.iterator] = function(){
	return new VNodeIterator(this.values()[Symbol.iterator](),this, this.cx.vnode);
};

VNode.prototype.get = function(idx){
	var val = this.cx.get(this.inode,idx,this.type,this.cache);
	if(!val) return [];
	val = val.constructor == Array ? val : [val];
	return new VNodeIterator(val[Symbol.iterator](), this, this.cx.vnode);
};
<|endoftext|>