<commit_before>/*===============================================================================
************   Swipeout Actions (Swipe to delete)   ************
===============================================================================*/
app.openedSwipeOutEl = undefined;
app.allowSwipeOut = true;
app.initSwipeOutList = function () {
    var isTouched, isMoved, isScrolling, touchesStart = {}, touchStartTime, touchesDiff, swipeOutEl, swipeOutContent, swipeOutActions, swipeOutActionsWidth, translate, opened;
    $(document).on(app.touchEvents.start, function (e) {
        if (app.openedSwipeOutEl) {
            var target = $(e.target);
            if (!(
                app.openedSwipeOutEl.is(target[0]) ||
                target.parents('.swipeout').is(app.openedSwipeOutEl) ||
                target.hasClass('modal-in') ||
                target.parents('.modal-in').length > 0 ||
                target.hasClass('modal-overlay')
                )) {
                app.closeSwipeOutList(app.openedSwipeOutEl);
            }
        }
    });
    $(document).on(app.touchEvents.start, '.list-block li.swipeout', function (e) {
        if (!app.allowSwipeOut) return;
        isMoved = false;
        isTouched = true;
        isScrolling = undefined;
        touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
        touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
        touchStartTime = (new Date()).getTime();

    });
    $(document).on(app.touchEvents.move, '.list-block li.swipeout', function (e) {
        if (!isTouched) return;

        var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
        var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
        if (typeof isScrolling === 'undefined') {
            isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x));
        }
        if (isScrolling) {
            isTouched = false;
            return;
        }

        if (!isMoved) {
            swipeOutEl = $(this);
            swipeOutContent = swipeOutEl.find('.swipeout-content');
            swipeOutActions = swipeOutEl.find('.swipeout-actions-inner');
            swipeOutActionsWidth = swipeOutActions.width();
            opened = swipeOutEl.hasClass('opened');
            swipeOutEl.removeClass('transitioning');
        }
        isMoved = true;

        e.preventDefault();
        touchesDiff = pageX - touchesStart.x;
        translate = touchesDiff  - (opened ? swipeOutActionsWidth : 0);
        if (translate > 0) translate = 0;
        if (translate < -swipeOutActionsWidth) {
            translate = -swipeOutActionsWidth - Math.pow(-translate - swipeOutActionsWidth, 0.8);
        }

        swipeOutContent.transform('translate3d(' + translate + 'px,0,0)');

    });
    $(document).on(app.touchEvents.end, '.list-block li.swipeout', function (e) {
        if (!isTouched || !isMoved) {
            isTouched = false;
            isMoved = false;
            return;
        }
        isTouched = false;
        isMoved = false;
        var timeDiff = (new Date()).getTime() - touchStartTime;
        if (!(translate === 0 || translate === -swipeOutActionsWidth)) app.allowSwipeOut = false;

        var action;
        if (opened) {
            if (
                timeDiff < 300 && translate > -(swipeOutActionsWidth - 10) ||
                timeDiff >= 300 && translate > -swipeOutActionsWidth / 2
            ) {
                action = 'close';
            }
            else {
                action = 'open';
            }
        }
        else {
            if (
                timeDiff < 300 && translate < -10 ||
                timeDiff >= 300 && translate < -swipeOutActionsWidth / 2
            ) {
                action = 'open';
            }
            else {
                action = 'close';
            }
        }
        if (action === 'open') {
            app.openedSwipeOutEl = swipeOutEl;
            swipeOutEl.trigger('open');
            swipeOutEl.addClass('opened transitioning');
            swipeOutContent.transform('translate3d(' + -swipeOutActionsWidth + 'px,0,0)');
        }
        else {
            swipeOutEl.trigger('close');
            app.openedSwipeOutEl = undefined;
            swipeOutEl.addClass('transitioning').removeClass('opened');
            swipeOutContent.transform('translate3d(' + 0 + 'px,0,0)');
        }
        swipeOutContent.transitionEnd(function () {
            app.allowSwipeOut = true;
            swipeOutEl.trigger(action === 'open' ? 'opened' : 'closed');
        });
    });
};
app.openSwipeOutList = function (el) {
    el = $(el);
    if (!el.hasClass('swipeout')) return;
    if (el.length === 0) return;
    if (el.length > 1) el = $(el[0]);
    el.trigger('open').addClass('transitioning opened');
    var swipeOutActions = el.find('.swipeout-actions-inner');
    el.find('.swipeout-content').transform('translate3d(-' + swipeOutActions.width() + 'px,0,0)').transitionEnd(function () {
        el.trigger('opened');
    });
    app.openedSwipeOutEl = el;
};
app.closeSwipeOutList = function (el) {
    el = $(el);
    if (el.length === 0) return;
    app.allowSwipeOut = false;
    el.trigger('close');
    el.removeClass('opened')
        .addClass('transitioning')
    .find('.swipeout-content')
        .transform('translate3d(' + 0 + 'px,0,0)')
        .transitionEnd(function () {
            el.trigger('closed');
            app.allowSwipeOut = true;
        });

    if (app.openedSwipeOutEl[0] === el[0]) app.openedSwipeOutEl = undefined;
};
app.deleteSwipeOutList = function (el) {
    el = $(el);
    if (el.length === 0) return;
    if (el.length > 1) el = $(el[0]);
    app.openedSwipeOutEl = undefined;
    el.trigger('delete');
    el.css({height: el.outerHeight() + 'px'});
    var clientLeft = el[0].clientLeft;
    el.css({height: 0 + 'px'}).addClass('deleting transitioning').transitionEnd(function () {
        el.trigger('deleted');
        el.remove();
    });
    el.find('.swipeout-content').transform('translate3d(-100%,0,0)');
};<commit_msg>scoped class name for opened swipeout<commit_after>/*===============================================================================
************   Swipeout Actions (Swipe to delete)   ************
===============================================================================*/
app.openedSwipeOutEl = undefined;
app.allowSwipeOut = true;
app.initSwipeOutList = function () {
    var isTouched, isMoved, isScrolling, touchesStart = {}, touchStartTime, touchesDiff, swipeOutEl, swipeOutContent, swipeOutActions, swipeOutActionsWidth, translate, opened;
    $(document).on(app.touchEvents.start, function (e) {
        if (app.openedSwipeOutEl) {
            var target = $(e.target);
            if (!(
                app.openedSwipeOutEl.is(target[0]) ||
                target.parents('.swipeout').is(app.openedSwipeOutEl) ||
                target.hasClass('modal-in') ||
                target.parents('.modal-in').length > 0 ||
                target.hasClass('modal-overlay')
                )) {
                app.closeSwipeOutList(app.openedSwipeOutEl);
            }
        }
    });
    $(document).on(app.touchEvents.start, '.list-block li.swipeout', function (e) {
        if (!app.allowSwipeOut) return;
        isMoved = false;
        isTouched = true;
        isScrolling = undefined;
        touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
        touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
        touchStartTime = (new Date()).getTime();

    });
    $(document).on(app.touchEvents.move, '.list-block li.swipeout', function (e) {
        if (!isTouched) return;

        var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
        var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
        if (typeof isScrolling === 'undefined') {
            isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x));
        }
        if (isScrolling) {
            isTouched = false;
            return;
        }

        if (!isMoved) {
            swipeOutEl = $(this);
            swipeOutContent = swipeOutEl.find('.swipeout-content');
            swipeOutActions = swipeOutEl.find('.swipeout-actions-inner');
            swipeOutActionsWidth = swipeOutActions.width();
            opened = swipeOutEl.hasClass('swipeout-opened');
            swipeOutEl.removeClass('transitioning');
        }
        isMoved = true;

        e.preventDefault();
        touchesDiff = pageX - touchesStart.x;
        translate = touchesDiff  - (opened ? swipeOutActionsWidth : 0);
        if (translate > 0) translate = 0;
        if (translate < -swipeOutActionsWidth) {
            translate = -swipeOutActionsWidth - Math.pow(-translate - swipeOutActionsWidth, 0.8);
        }

        swipeOutContent.transform('translate3d(' + translate + 'px,0,0)');

    });
    $(document).on(app.touchEvents.end, '.list-block li.swipeout', function (e) {
        if (!isTouched || !isMoved) {
            isTouched = false;
            isMoved = false;
            return;
        }
        isTouched = false;
        isMoved = false;
        var timeDiff = (new Date()).getTime() - touchStartTime;
        if (!(translate === 0 || translate === -swipeOutActionsWidth)) app.allowSwipeOut = false;

        var action;
        if (opened) {
            if (
                timeDiff < 300 && translate > -(swipeOutActionsWidth - 10) ||
                timeDiff >= 300 && translate > -swipeOutActionsWidth / 2
            ) {
                action = 'close';
            }
            else {
                action = 'open';
            }
        }
        else {
            if (
                timeDiff < 300 && translate < -10 ||
                timeDiff >= 300 && translate < -swipeOutActionsWidth / 2
            ) {
                action = 'open';
            }
            else {
                action = 'close';
            }
        }
        if (action === 'open') {
            app.openedSwipeOutEl = swipeOutEl;
            swipeOutEl.trigger('open');
            swipeOutEl.addClass('swipeout-opened transitioning');
            swipeOutContent.transform('translate3d(' + -swipeOutActionsWidth + 'px,0,0)');
        }
        else {
            swipeOutEl.trigger('close');
            app.openedSwipeOutEl = undefined;
            swipeOutEl.addClass('transitioning').removeClass('swipeout-opened');
            swipeOutContent.transform('translate3d(' + 0 + 'px,0,0)');
        }
        swipeOutContent.transitionEnd(function () {
            app.allowSwipeOut = true;
            swipeOutEl.trigger(action === 'open' ? 'opened' : 'closed');
        });
    });
};
app.openSwipeOutList = function (el) {
    el = $(el);
    if (!el.hasClass('swipeout')) return;
    if (el.length === 0) return;
    if (el.length > 1) el = $(el[0]);
    el.trigger('open').addClass('transitioning swipeout-opened');
    var swipeOutActions = el.find('.swipeout-actions-inner');
    el.find('.swipeout-content').transform('translate3d(-' + swipeOutActions.width() + 'px,0,0)').transitionEnd(function () {
        el.trigger('opened');
    });
    app.openedSwipeOutEl = el;
};
app.closeSwipeOutList = function (el) {
    el = $(el);
    if (el.length === 0) return;
    app.allowSwipeOut = false;
    el.trigger('close');
    el.removeClass('swipeout-opened')
        .addClass('transitioning')
    .find('.swipeout-content')
        .transform('translate3d(' + 0 + 'px,0,0)')
        .transitionEnd(function () {
            el.trigger('closed');
            app.allowSwipeOut = true;
        });

    if (app.openedSwipeOutEl[0] === el[0]) app.openedSwipeOutEl = undefined;
};
app.deleteSwipeOutList = function (el) {
    el = $(el);
    if (el.length === 0) return;
    if (el.length > 1) el = $(el[0]);
    app.openedSwipeOutEl = undefined;
    el.trigger('delete');
    el.css({height: el.outerHeight() + 'px'});
    var clientLeft = el[0].clientLeft;
    el.css({height: 0 + 'px'}).addClass('deleting transitioning').transitionEnd(function () {
        el.trigger('deleted');
        el.remove();
    });
    el.find('.swipeout-content').transform('translate3d(-100%,0,0)');
};<|endoftext|>


<commit_before>/*===============================================================================
************   Swipeout Actions (Swipe to delete)   ************
===============================================================================*/
app.swipeoutOpenedEl = undefined;
app.allowSwipeout = true;
app.initSwipeout = function (swipeoutEl) {
    var isTouched, isMoved, isScrolling, touchesStart = {}, touchStartTime, touchesDiff, swipeOutEl, swipeOutContent, actionsRight, actionsLeft, actionsLeftWidth, actionsRightWidth, translate, opened, openedActions, buttonsLeft, buttonsRight, direction, overswipeLeftButton, overswipeRightButton, overswipeLeft, overswipeRight;
    $(document).on(app.touchEvents.start, function (e) {
        if (app.swipeoutOpenedEl) {
            var target = $(e.target);
            if (!(
                app.swipeoutOpenedEl.is(target[0]) ||
                target.parents('.swipeout').is(app.swipeoutOpenedEl) ||
                target.hasClass('modal-in') ||
                target.parents('.modal-in').length > 0 ||
                target.hasClass('modal-overlay')
                )) {
                app.swipeoutClose(app.swipeoutOpenedEl);
            }
        }
    });

    function handleTouchStart(e) {
        if (!app.allowSwipeout) return;
        isMoved = false;
        isTouched = true;
        isScrolling = undefined;
        touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
        touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
        touchStartTime = (new Date()).getTime();
    }
    function handleTouchMove(e) {
        if (!isTouched) return;
        var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
        var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
        if (typeof isScrolling === 'undefined') {
            isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x));
        }
        if (isScrolling) {
            isTouched = false;
            return;
        }

        if (!isMoved) {
            if ($('.list-block.sortable-opened').length > 0) return;
            /*jshint validthis:true */
            swipeOutEl = $(this);
            swipeOutContent = swipeOutEl.find('.swipeout-content');
            actionsRight = swipeOutEl.find('.swipeout-actions-right');
            actionsLeft = swipeOutEl.find('.swipeout-actions-left');
            actionsLeftWidth = actionsRightWidth = buttonsLeft = buttonsRight = overswipeRightButton = overswipeLeftButton = null;
            if (actionsLeft.length > 0) {
                actionsLeftWidth = actionsLeft.width();
                buttonsLeft = actionsLeft.children('a');
                overswipeLeftButton = actionsLeft.find('.swipeout-overswipe');
            }
            if (actionsRight.length > 0) {
                actionsRightWidth = actionsRight.width();
                buttonsRight = actionsRight.children('a');
                overswipeRightButton = actionsRight.find('.swipeout-overswipe');
            }
            opened = swipeOutEl.hasClass('swipeout-opened');
            if (opened) {
                openedActions = swipeOutEl.find('.swipeout-actions-left.swipeout-actions-opened').length > 0 ? 'left' : 'right';
            }
            swipeOutEl.removeClass('transitioning');
            if (!app.params.swipeoutNoFollow) {
                swipeOutEl.find('.swipeout-actions-opened').removeClass('swipeout-actions-opened');
                swipeOutEl.removeClass('swipeout-opened');
            }
        }
        isMoved = true;
        e.preventDefault();

        touchesDiff = pageX - touchesStart.x;
        translate = touchesDiff;

        if (opened) {
            if (openedActions === 'right') translate = translate - actionsRightWidth;
            else translate = translate + actionsLeftWidth;
        }

        if (translate > 0 && actionsLeft.length === 0 || translate < 0 && actionsRight.length === 0) {
            if (!opened) {
                isTouched = isMoved = false;
                return;
            }
            translate = 0;
        }

        if (translate < 0) direction = 'to-left';
        else if (translate > 0) direction = 'to-right';
        else {
            if (direction) direction = direction;
            else direction = 'to-left';
        }

        var i, buttonOffset, progress;

        e.f7PreventPanelSwipe = true;
        if (app.params.swipeoutNoFollow) {
            if (opened) {
                if (openedActions === 'right' && touchesDiff > 0) {
                    app.swipeoutClose(swipeOutEl);
                }
                if (openedActions === 'left' && touchesDiff < 0) {
                    app.swipeoutClose(swipeOutEl);
                }
            }
            else {
                if (touchesDiff < 0 && actionsRight.length > 0) {
                    app.swipeoutOpen(swipeOutEl, 'right');
                }
                if (touchesDiff > 0 && actionsLeft.length > 0) {
                    app.swipeoutOpen(swipeOutEl, 'left');
                }
            }
            isTouched = false;
            isMoved = false;
            return;
        }
        overswipeLeft = false;
        overswipeRight = false;
        var $button;
        if (actionsRight.length > 0) {
            // Show right actions
            progress = translate / actionsRightWidth;
            if (translate < -actionsRightWidth) {
                translate = -actionsRightWidth - Math.pow(-translate - actionsRightWidth, 0.8);
                if (overswipeRightButton.length > 0) {
                    overswipeRight = true;
                }

            }
            for (i = 0; i < buttonsRight.length; i++) {
                if (typeof buttonsRight[i]._buttonOffset === 'undefined') {
                    buttonsRight[i]._buttonOffset = buttonsRight[i].offsetLeft;
                }
                buttonOffset = buttonsRight[i]._buttonOffset;
                $button = $(buttonsRight[i]);
                if (overswipeRightButton.length > 0 && $button.hasClass('swipeout-overswipe')) {
                    $button.css({left: (overswipeRight ? -buttonOffset : 0) + 'px'});
                }
                $button.transform('translate3d(' + (translate - buttonOffset * (1 + Math.max(progress, -1))) + 'px,0,0)');
            }
        }
        if (actionsLeft.length > 0) {
            // Show left actions
            progress = translate / actionsLeftWidth;
            if (translate > actionsLeftWidth) {
                translate = actionsLeftWidth + Math.pow(translate - actionsLeftWidth, 0.8);
                if (overswipeLeftButton.length > 0) {
                    overswipeLeft = true;
                }
            }
            for (i = 0; i < buttonsLeft.length; i++) {
                if (typeof buttonsLeft[i]._buttonOffset === 'undefined') {
                    buttonsLeft[i]._buttonOffset = actionsLeftWidth - buttonsLeft[i].offsetLeft - buttonsLeft[i].offsetWidth;
                }
                buttonOffset = buttonsLeft[i]._buttonOffset;
                $button = $(buttonsLeft[i]);
                if (overswipeLeftButton.length > 0 && $button.hasClass('swipeout-overswipe')) {
                    $button.css({left: (overswipeLeft ? buttonOffset : 0) + 'px'});
                }
                $button.css('z-index', buttonsLeft.length - i).transform('translate3d(' + (translate + buttonOffset * (1 - Math.min(progress, 1))) + 'px,0,0)');
            }
        }
        swipeOutContent.transform('translate3d(' + translate + 'px,0,0)');
    }
    function handleTouchEnd(e) {
        if (!isTouched || !isMoved) {
            isTouched = false;
            isMoved = false;
            return;
        }
        isTouched = false;
        isMoved = false;
        var timeDiff = (new Date()).getTime() - touchStartTime;
        var action, actionsWidth, actions, buttons, i;

        actions = direction === 'to-left' ? actionsRight : actionsLeft;
        actionsWidth = direction === 'to-left' ? actionsRightWidth : actionsLeftWidth;

        if (
            timeDiff < 300 && (touchesDiff < -10 && direction === 'to-left' || touchesDiff > 10 && direction === 'to-right') ||
            timeDiff >= 300 && Math.abs(translate) > actionsWidth / 2
        ) {
            action = 'open';
        }
        else {
            action = 'close';
        }
        if (timeDiff < 300) {
            if (Math.abs(translate) === 0) action = 'close';
            if (Math.abs(translate) === actionsWidth) action = 'open';
        }

        if (action === 'open') {
            app.swipeoutOpenedEl = swipeOutEl;
            swipeOutEl.trigger('open');
            swipeOutEl.addClass('swipeout-opened transitioning');
            var newTranslate = direction === 'to-left' ? -actionsWidth : actionsWidth;
            swipeOutContent.transform('translate3d(' + newTranslate + 'px,0,0)');
            actions.addClass('swipeout-actions-opened');
            buttons = direction === 'to-left' ? buttonsRight : buttonsLeft;
            if (buttons) {
                for (i = 0; i < buttons.length; i++) {
                    $(buttons[i]).transform('translate3d(' + newTranslate + 'px,0,0)');
                }
            }
            if (overswipeRight) {
                actionsRight.find('.swipeout-overswipe')[0].click();
            }
            if (overswipeLeft) {
                actionsLeft.find('.swipeout-overswipe')[0].click();
            }
        }
        else {
            swipeOutEl.trigger('close');
            app.swipeoutOpenedEl = undefined;
            swipeOutEl.addClass('transitioning').removeClass('swipeout-opened');
            swipeOutContent.transform('');
            actions.removeClass('swipeout-actions-opened');
        }

        var buttonOffset;
        if (buttonsLeft && buttonsLeft.length > 0 && buttonsLeft !== buttons) {
            for (i = 0; i < buttonsLeft.length; i++) {
                buttonOffset = buttonsLeft[i]._buttonOffset;
                if (typeof buttonOffset === 'undefined') {
                    buttonsLeft[i]._buttonOffset = actionsLeftWidth - buttonsLeft[i].offsetLeft - buttonsLeft[i].offsetWidth;
                }
                $(buttonsLeft[i]).transform('translate3d(' + (buttonOffset) + 'px,0,0)');
            }
        }
        if (buttonsRight && buttonsRight.length > 0 && buttonsRight !== buttons) {
            for (i = 0; i < buttonsRight.length; i++) {
                buttonOffset = buttonsRight[i]._buttonOffset;
                if (typeof buttonOffset === 'undefined') {
                    buttonsRight[i]._buttonOffset = buttonsRight[i].offsetLeft;
                }
                $(buttonsRight[i]).transform('translate3d(' + (-buttonOffset) + 'px,0,0)');
            }
        }
        swipeOutContent.transitionEnd(function (e) {
            if (opened && action === 'open' || closed && action === 'close') return;
            swipeOutEl.trigger(action === 'open' ? 'opened' : 'closed');
            if (opened && action === 'close') {
                if (actionsRight.length > 0) {
                    buttonsRight.transform('');
                }
                if (actionsLeft.length > 0) {
                    buttonsLeft.transform('');
                }
            }
        });
    }
    if (swipeoutEl) {
        $(swipeoutEl).on(app.touchEvents.start, handleTouchStart);
        $(swipeoutEl).on(app.touchEvents.move, handleTouchMove);
        $(swipeoutEl).on(app.touchEvents.end, handleTouchEnd);
    }
    else {
        $(document).on(app.touchEvents.start, '.list-block li.swipeout', handleTouchStart);
        $(document).on(app.touchEvents.move, '.list-block li.swipeout', handleTouchMove);
        $(document).on(app.touchEvents.end, '.list-block li.swipeout', handleTouchEnd);
    }

};
app.swipeoutOpen = function (el, dir) {
    el = $(el);

    if (el.length === 0) return;
    if (el.length > 1) el = $(el[0]);
    if (!el.hasClass('swipeout') || el.hasClass('swipeout-opened')) return;
    if (!dir) {
        if (el.find('.swipeout-actions-right').length > 0) dir = 'right';
        else dir = 'left';
    }
    var swipeOutActions = el.find('.swipeout-actions-' + dir);
    if (swipeOutActions.length === 0) return;
    el.trigger('open').addClass('swipeout-opened').removeClass('transitioning');
    swipeOutActions.addClass('swipeout-actions-opened');
    var buttons = swipeOutActions.children('a');
    var swipeOutActionsWidth = swipeOutActions.width();
    var translate = dir === 'right' ? -swipeOutActionsWidth : swipeOutActionsWidth;
    var i;
    if (buttons.length > 1) {
        for (i = 0; i < buttons.length; i++) {
            if (dir === 'right') {
                $(buttons[i]).transform('translate3d(' + (- buttons[i].offsetLeft) + 'px,0,0)');
            }
            else {
                $(buttons[i]).css('z-index', buttons.length - i).transform('translate3d(' + (swipeOutActionsWidth - buttons[i].offsetWidth - buttons[i].offsetLeft) + 'px,0,0)');
            }
        }
        var clientLeft = buttons[1].clientLeft;
    }

    el.addClass('transitioning');
    for (i = 0; i < buttons.length; i++) {
        $(buttons[i]).transform('translate3d(' + (translate) + 'px,0,0');
    }
    el.find('.swipeout-content').transform('translate3d(' + translate + 'px,0,0)').transitionEnd(function () {
        el.trigger('opened');
    });
    app.swipeoutOpenedEl = el;
};
app.swipeoutClose = function (el) {
    el = $(el);
    if (el.length === 0) return;
    if (!el.hasClass('swipeout-opened')) return;
    var dir = el.find('.swipeout-actions-opened').hasClass('swipeout-actions-right') ? 'right' : 'left';
    var swipeOutActions = el.find('.swipeout-actions-opened').removeClass('swipeout-actions-opened');
    var buttons = swipeOutActions.children('a');
    var swipeOutActionsWidth = swipeOutActions.width();
    app.allowSwipeout = false;
    el.trigger('close');
    el.removeClass('swipeout-opened').addClass('transitioning');

    el.find('.swipeout-content').transform('translate3d(' + 0 + 'px,0,0)').transitionEnd(function () {
        el.trigger('closed');
        app.allowSwipeout = true;
    });
    for (var i = 0; i < buttons.length; i++) {
        if (dir === 'right') {
            $(buttons[i]).transform('translate3d(' + (-buttons[i].offsetLeft) + 'px,0,0)');
        }
        else {
            $(buttons[i]).transform('translate3d(' + (swipeOutActionsWidth - buttons[i].offsetWidth - buttons[i].offsetLeft) + 'px,0,0)');
        }
        $(buttons[i]).css({left:0 + 'px'});
    }
    if (app.swipeoutOpenedEl && app.swipeoutOpenedEl[0] === el[0]) app.swipeoutOpenedEl = undefined;
};
app.swipeoutDelete = function (el) {
    el = $(el);
    if (el.length === 0) return;
    if (el.length > 1) el = $(el[0]);
    app.swipeoutOpenedEl = undefined;
    el.trigger('delete');
    el.css({height: el.outerHeight() + 'px'});
    var clientLeft = el[0].clientLeft;
    el.css({height: 0 + 'px'}).addClass('deleting transitioning').transitionEnd(function () {
        el.trigger('deleted');
        el.remove();
    });
    var translate = '-100%';
    el.find('.swipeout-content').transform('translate3d(' + translate + ',0,0)');
};
<commit_msg>Unset transform on buttons<commit_after>/*===============================================================================
************   Swipeout Actions (Swipe to delete)   ************
===============================================================================*/
app.swipeoutOpenedEl = undefined;
app.allowSwipeout = true;
app.initSwipeout = function (swipeoutEl) {
    var isTouched, isMoved, isScrolling, touchesStart = {}, touchStartTime, touchesDiff, swipeOutEl, swipeOutContent, actionsRight, actionsLeft, actionsLeftWidth, actionsRightWidth, translate, opened, openedActions, buttonsLeft, buttonsRight, direction, overswipeLeftButton, overswipeRightButton, overswipeLeft, overswipeRight;
    $(document).on(app.touchEvents.start, function (e) {
        if (app.swipeoutOpenedEl) {
            var target = $(e.target);
            if (!(
                app.swipeoutOpenedEl.is(target[0]) ||
                target.parents('.swipeout').is(app.swipeoutOpenedEl) ||
                target.hasClass('modal-in') ||
                target.parents('.modal-in').length > 0 ||
                target.hasClass('modal-overlay')
                )) {
                app.swipeoutClose(app.swipeoutOpenedEl);
            }
        }
    });

    function handleTouchStart(e) {
        if (!app.allowSwipeout) return;
        isMoved = false;
        isTouched = true;
        isScrolling = undefined;
        touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
        touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
        touchStartTime = (new Date()).getTime();
    }
    function handleTouchMove(e) {
        if (!isTouched) return;
        var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
        var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
        if (typeof isScrolling === 'undefined') {
            isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x));
        }
        if (isScrolling) {
            isTouched = false;
            return;
        }

        if (!isMoved) {
            if ($('.list-block.sortable-opened').length > 0) return;
            /*jshint validthis:true */
            swipeOutEl = $(this);
            swipeOutContent = swipeOutEl.find('.swipeout-content');
            actionsRight = swipeOutEl.find('.swipeout-actions-right');
            actionsLeft = swipeOutEl.find('.swipeout-actions-left');
            actionsLeftWidth = actionsRightWidth = buttonsLeft = buttonsRight = overswipeRightButton = overswipeLeftButton = null;
            if (actionsLeft.length > 0) {
                actionsLeftWidth = actionsLeft.width();
                buttonsLeft = actionsLeft.children('a');
                overswipeLeftButton = actionsLeft.find('.swipeout-overswipe');
            }
            if (actionsRight.length > 0) {
                actionsRightWidth = actionsRight.width();
                buttonsRight = actionsRight.children('a');
                overswipeRightButton = actionsRight.find('.swipeout-overswipe');
            }
            opened = swipeOutEl.hasClass('swipeout-opened');
            if (opened) {
                openedActions = swipeOutEl.find('.swipeout-actions-left.swipeout-actions-opened').length > 0 ? 'left' : 'right';
            }
            swipeOutEl.removeClass('transitioning');
            if (!app.params.swipeoutNoFollow) {
                swipeOutEl.find('.swipeout-actions-opened').removeClass('swipeout-actions-opened');
                swipeOutEl.removeClass('swipeout-opened');
            }
        }
        isMoved = true;
        e.preventDefault();

        touchesDiff = pageX - touchesStart.x;
        translate = touchesDiff;

        if (opened) {
            if (openedActions === 'right') translate = translate - actionsRightWidth;
            else translate = translate + actionsLeftWidth;
        }

        if (translate > 0 && actionsLeft.length === 0 || translate < 0 && actionsRight.length === 0) {
            if (!opened) {
                isTouched = isMoved = false;
                return;
            }
            translate = 0;
        }

        if (translate < 0) direction = 'to-left';
        else if (translate > 0) direction = 'to-right';
        else {
            if (direction) direction = direction;
            else direction = 'to-left';
        }

        var i, buttonOffset, progress;

        e.f7PreventPanelSwipe = true;
        if (app.params.swipeoutNoFollow) {
            if (opened) {
                if (openedActions === 'right' && touchesDiff > 0) {
                    app.swipeoutClose(swipeOutEl);
                }
                if (openedActions === 'left' && touchesDiff < 0) {
                    app.swipeoutClose(swipeOutEl);
                }
            }
            else {
                if (touchesDiff < 0 && actionsRight.length > 0) {
                    app.swipeoutOpen(swipeOutEl, 'right');
                }
                if (touchesDiff > 0 && actionsLeft.length > 0) {
                    app.swipeoutOpen(swipeOutEl, 'left');
                }
            }
            isTouched = false;
            isMoved = false;
            return;
        }
        overswipeLeft = false;
        overswipeRight = false;
        var $button;
        if (actionsRight.length > 0) {
            // Show right actions
            progress = translate / actionsRightWidth;
            if (translate < -actionsRightWidth) {
                translate = -actionsRightWidth - Math.pow(-translate - actionsRightWidth, 0.8);
                if (overswipeRightButton.length > 0) {
                    overswipeRight = true;
                }

            }
            for (i = 0; i < buttonsRight.length; i++) {
                if (typeof buttonsRight[i]._buttonOffset === 'undefined') {
                    buttonsRight[i]._buttonOffset = buttonsRight[i].offsetLeft;
                }
                buttonOffset = buttonsRight[i]._buttonOffset;
                $button = $(buttonsRight[i]);
                if (overswipeRightButton.length > 0 && $button.hasClass('swipeout-overswipe')) {
                    $button.css({left: (overswipeRight ? -buttonOffset : 0) + 'px'});
                }
                $button.transform('translate3d(' + (translate - buttonOffset * (1 + Math.max(progress, -1))) + 'px,0,0)');
            }
        }
        if (actionsLeft.length > 0) {
            // Show left actions
            progress = translate / actionsLeftWidth;
            if (translate > actionsLeftWidth) {
                translate = actionsLeftWidth + Math.pow(translate - actionsLeftWidth, 0.8);
                if (overswipeLeftButton.length > 0) {
                    overswipeLeft = true;
                }
            }
            for (i = 0; i < buttonsLeft.length; i++) {
                if (typeof buttonsLeft[i]._buttonOffset === 'undefined') {
                    buttonsLeft[i]._buttonOffset = actionsLeftWidth - buttonsLeft[i].offsetLeft - buttonsLeft[i].offsetWidth;
                }
                buttonOffset = buttonsLeft[i]._buttonOffset;
                $button = $(buttonsLeft[i]);
                if (overswipeLeftButton.length > 0 && $button.hasClass('swipeout-overswipe')) {
                    $button.css({left: (overswipeLeft ? buttonOffset : 0) + 'px'});
                }
                $button.css('z-index', buttonsLeft.length - i).transform('translate3d(' + (translate + buttonOffset * (1 - Math.min(progress, 1))) + 'px,0,0)');
            }
        }
        swipeOutContent.transform('translate3d(' + translate + 'px,0,0)');
    }
    function handleTouchEnd(e) {
        if (!isTouched || !isMoved) {
            isTouched = false;
            isMoved = false;
            return;
        }
        isTouched = false;
        isMoved = false;
        var timeDiff = (new Date()).getTime() - touchStartTime;
        var action, actionsWidth, actions, buttons, i;

        actions = direction === 'to-left' ? actionsRight : actionsLeft;
        actionsWidth = direction === 'to-left' ? actionsRightWidth : actionsLeftWidth;

        if (
            timeDiff < 300 && (touchesDiff < -10 && direction === 'to-left' || touchesDiff > 10 && direction === 'to-right') ||
            timeDiff >= 300 && Math.abs(translate) > actionsWidth / 2
        ) {
            action = 'open';
        }
        else {
            action = 'close';
        }
        if (timeDiff < 300) {
            if (Math.abs(translate) === 0) action = 'close';
            if (Math.abs(translate) === actionsWidth) action = 'open';
        }

        if (action === 'open') {
            app.swipeoutOpenedEl = swipeOutEl;
            swipeOutEl.trigger('open');
            swipeOutEl.addClass('swipeout-opened transitioning');
            var newTranslate = direction === 'to-left' ? -actionsWidth : actionsWidth;
            swipeOutContent.transform('translate3d(' + newTranslate + 'px,0,0)');
            actions.addClass('swipeout-actions-opened');
            buttons = direction === 'to-left' ? buttonsRight : buttonsLeft;
            if (buttons) {
                for (i = 0; i < buttons.length; i++) {
                    $(buttons[i]).transform('translate3d(' + newTranslate + 'px,0,0)');
                }
            }
            if (overswipeRight) {
                actionsRight.find('.swipeout-overswipe')[0].click();
            }
            if (overswipeLeft) {
                actionsLeft.find('.swipeout-overswipe')[0].click();
            }
        }
        else {
            swipeOutEl.trigger('close');
            app.swipeoutOpenedEl = undefined;
            swipeOutEl.addClass('transitioning').removeClass('swipeout-opened');
            swipeOutContent.transform('');
            actions.removeClass('swipeout-actions-opened');
        }

        var buttonOffset;
        if (buttonsLeft && buttonsLeft.length > 0 && buttonsLeft !== buttons) {
            for (i = 0; i < buttonsLeft.length; i++) {
                buttonOffset = buttonsLeft[i]._buttonOffset;
                if (typeof buttonOffset === 'undefined') {
                    buttonsLeft[i]._buttonOffset = actionsLeftWidth - buttonsLeft[i].offsetLeft - buttonsLeft[i].offsetWidth;
                }
                $(buttonsLeft[i]).transform('translate3d(' + (buttonOffset) + 'px,0,0)');
            }
        }
        if (buttonsRight && buttonsRight.length > 0 && buttonsRight !== buttons) {
            for (i = 0; i < buttonsRight.length; i++) {
                buttonOffset = buttonsRight[i]._buttonOffset;
                if (typeof buttonOffset === 'undefined') {
                    buttonsRight[i]._buttonOffset = buttonsRight[i].offsetLeft;
                }
                $(buttonsRight[i]).transform('translate3d(' + (-buttonOffset) + 'px,0,0)');
            }
        }
        swipeOutContent.transitionEnd(function (e) {
            if (opened && action === 'open' || closed && action === 'close') return;
            swipeOutEl.trigger(action === 'open' ? 'opened' : 'closed');
            if (opened && action === 'close') {
                if (actionsRight.length > 0) {
                    buttonsRight.transform('');
                }
                if (actionsLeft.length > 0) {
                    buttonsLeft.transform('');
                }
            }
        });
    }
    if (swipeoutEl) {
        $(swipeoutEl).on(app.touchEvents.start, handleTouchStart);
        $(swipeoutEl).on(app.touchEvents.move, handleTouchMove);
        $(swipeoutEl).on(app.touchEvents.end, handleTouchEnd);
    }
    else {
        $(document).on(app.touchEvents.start, '.list-block li.swipeout', handleTouchStart);
        $(document).on(app.touchEvents.move, '.list-block li.swipeout', handleTouchMove);
        $(document).on(app.touchEvents.end, '.list-block li.swipeout', handleTouchEnd);
    }

};
app.swipeoutOpen = function (el, dir) {
    el = $(el);

    if (el.length === 0) return;
    if (el.length > 1) el = $(el[0]);
    if (!el.hasClass('swipeout') || el.hasClass('swipeout-opened')) return;
    if (!dir) {
        if (el.find('.swipeout-actions-right').length > 0) dir = 'right';
        else dir = 'left';
    }
    var swipeOutActions = el.find('.swipeout-actions-' + dir);
    if (swipeOutActions.length === 0) return;
    el.trigger('open').addClass('swipeout-opened').removeClass('transitioning');
    swipeOutActions.addClass('swipeout-actions-opened');
    var buttons = swipeOutActions.children('a');
    var swipeOutActionsWidth = swipeOutActions.width();
    var translate = dir === 'right' ? -swipeOutActionsWidth : swipeOutActionsWidth;
    var i;
    if (buttons.length > 1) {
        for (i = 0; i < buttons.length; i++) {
            if (dir === 'right') {
                $(buttons[i]).transform('translate3d(' + (- buttons[i].offsetLeft) + 'px,0,0)');
            }
            else {
                $(buttons[i]).css('z-index', buttons.length - i).transform('translate3d(' + (swipeOutActionsWidth - buttons[i].offsetWidth - buttons[i].offsetLeft) + 'px,0,0)');
            }
        }
        var clientLeft = buttons[1].clientLeft;
    }

    el.addClass('transitioning');
    for (i = 0; i < buttons.length; i++) {
        $(buttons[i]).transform('translate3d(' + (translate) + 'px,0,0');
    }
    el.find('.swipeout-content').transform('translate3d(' + translate + 'px,0,0)').transitionEnd(function () {
        el.trigger('opened');
    });
    app.swipeoutOpenedEl = el;
};
app.swipeoutClose = function (el) {
    el = $(el);
    if (el.length === 0) return;
    if (!el.hasClass('swipeout-opened')) return;
    var dir = el.find('.swipeout-actions-opened').hasClass('swipeout-actions-right') ? 'right' : 'left';
    var swipeOutActions = el.find('.swipeout-actions-opened').removeClass('swipeout-actions-opened');
    var buttons = swipeOutActions.children('a');
    var swipeOutActionsWidth = swipeOutActions.width();
    app.allowSwipeout = false;
    el.trigger('close');
    el.removeClass('swipeout-opened').addClass('transitioning');

    el.find('.swipeout-content').transform('translate3d(' + 0 + 'px,0,0)').transitionEnd(function () {
        el.trigger('closed');
        buttons.transform('');
        app.allowSwipeout = true;
    });
    for (var i = 0; i < buttons.length; i++) {
        if (dir === 'right') {
            $(buttons[i]).transform('translate3d(' + (-buttons[i].offsetLeft) + 'px,0,0)');
        }
        else {
            $(buttons[i]).transform('translate3d(' + (swipeOutActionsWidth - buttons[i].offsetWidth - buttons[i].offsetLeft) + 'px,0,0)');
        }
        $(buttons[i]).css({left:0 + 'px'});
    }
    if (app.swipeoutOpenedEl && app.swipeoutOpenedEl[0] === el[0]) app.swipeoutOpenedEl = undefined;
};
app.swipeoutDelete = function (el) {
    el = $(el);
    if (el.length === 0) return;
    if (el.length > 1) el = $(el[0]);
    app.swipeoutOpenedEl = undefined;
    el.trigger('delete');
    el.css({height: el.outerHeight() + 'px'});
    var clientLeft = el[0].clientLeft;
    el.css({height: 0 + 'px'}).addClass('deleting transitioning').transitionEnd(function () {
        el.trigger('deleted');
        el.remove();
    });
    var translate = '-100%';
    el.find('.swipeout-content').transform('translate3d(' + translate + ',0,0)');
};
<|endoftext|>


<commit_before>/**
 * @fileOverview
 * Copyright (c) 2012 Adam Ranfelt
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
 * OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Scene Module Definition
 * @author Adam Ranfelt <adamRenny@gmail.com>
 * @version 1.3.1
 */
define([
    'layer/Stage',
    'layer/Input',
    'layer/HitEvent',
    'layer/Events'
], function(
    Stage,
    Input,
    HitEvent,
    Events
) {
    'use strict';

    /**
     * Render Layer
     * Renders the layer called during an iteration on the Stage object
     *
     * @param {Layer} layer Layer to render with
     * @since 1.1
     */
    var _renderLayer = function(layer) {
        layer.render();
    };

    /**
     * Scene Constructor
     *
     * Effectively acts as Facade of the interactive engine
     * Scene to coordinate all components of a display
     * Facade pattern to encapsulate the stage, layers, and hit detection
     *
     * @name Scene
     * @class Scene container object for layered canvas
     * @constructor
     *
     * @param {HTMLElement} container Container object where the canvases are contained
     * @param {number} width Base unscaled width
     * @param {number} height Base unscaled height
     * @since 1.0
     */
    var Scene = function(container, width, height) {
        if (container && width && height) {
            this.init(container, width, height);
        }
    };

    /**
     * Initializes the Scene with the container
     * Sets up a stage and all necessary layers
     *
     * @param {HTMLElement} container Container object where the canvases are contained
     * @param {number} width Base unscaled width
     * @param {number} height Base unscaled height
     * @returns {Scene}
     * @since 1.0
     */
    Scene.prototype.init = function(container, width, height) {
        /**
         * Stage for the scene to be contained within
         *
         * @default null
         * @name Scene#stage
         * @type {Stage}
         * @since 1.0
         */
        this.stage = null;

        /**
         * Container object for the scene to be contained within
         * Container may have canvas elements within it
         * Container will be used as the viewport for the stage
         *
         * @name Scene#container
         * @type {HTMLElement}
         * @since 1.3.1
         */
        this.container = container;

        this.setupStage(container, width, height);

        /**
         * Scene Input controller to listen from the container
         *
         * @name Scene#input
         * @type {Input}
         * @since 1.0
         */
        this.input = new Input(container);

        /**
         * Currently active input target
         * Used to determine which element is currently active and hovered within the scene
         *
         * @name Scene#activeTarget
         * @type {Renderable}
         * @since 1.1
         */
        this.activeTarget = null;

        /**
         * Last used mouse object
         * Should be considered immutable and opaque
         *
         * @private
         * @name Scene#activeMouse
         * @type {Mouse}
         * @since 1.1
         */
        this.activeMouse = null;

        this.setupHandlers();

        var namespace = this.input.getNamespace();

        Events.on(Input.MOUSE_MOVE + namespace, this.onMoveHandler);
        Events.on(Input.MOUSE_UP + namespace, this.onUpHandler);
        Events.on(Input.MOUSE_DOWN + namespace, this.onDownHandler);
        Events.on(Input.CLICK + namespace, this.onClickHandler);
    };

    /**
     * Create new stage for the scene to be contained within
     *
     * @param {HTMLElement} container Container object where the canvases are contained
     * @param {number} width Base unscaled width
     * @param {number} height Base unscaled height
     * @returns {Scene}
     * @since 1.3
     */
    Scene.prototype.setupStage = function(container, width, height) {
        this.stage = new Stage(container, width, height);

        return this;
    };

    /**
     * Binds all handler functions to local bound handler functions
     *
     * @returns {Scene}
     * @since 1.0
     */
    Scene.prototype.setupHandlers = function() {
        this.onMoveHandler = this.onMove.bind(this);
        this.onUpHandler = this.onUp.bind(this);
        this.onDownHandler = this.onDown.bind(this);
        this.onClickHandler = this.onClick.bind(this);

        return this;
    };

    /**
     * Adds a child to the layer at index 0 if no target is defined
     * Otherwise the target will be parsed by number or string to pipe to the proper method
     *
     * @param {Renderable} child Child to add
     * @param {string|number|null} target Optional target element key to add the child to
     * @returns {Scene}
     * @since 1.1
     */
    Scene.prototype.addChild = function(child, target) {
        if (target === undefined) {
            return this.addChildToLayerByIndex(child, 0);
        } else if (typeof target === 'number') {
            return this.addChildToLayerByIndex(child, target);
        } else if (typeof target === 'string') {
            return this.addChildToLayerByName(child, name);
        }

        return this;
    };

    /**
     * Removes a child from the layer at index 0 if no target is defined
     * Otherwise, the target will be parsed by number or string to pipe to the proper method
     *
     * @param {Renderable} child Child to remove
     * @param {string|number|null} target Optional target element key to add the child to
     * @returns {Scene}
     * @since 1.1
     */
    Scene.prototype.removeChild = function(child, target) {
        if (target === undefined) {
            return this.removeChildFromLayerByIndex(child, 0);
        } else if (typeof target === 'number') {
            return this.removeChildFromLayerByIndex(child, target);
        } else if (typeof target === 'string') {
            return this.removeChildFromLayerByName(child, name);
        }

        return this;
    };

    /**
     * Adds a child to the layer at the index specified
     *
     * @param {Renderable} child Child to add
     * @param {number} layerIndex Index of the layer to add to
     * @returns {Scene}
     * @since 1.2
     */
    Scene.prototype.addChildToLayerByIndex = function(child, layerIndex) {
        var layer = this.stage.getLayerByIndex(layerIndex);
        layer.root.addChild(child);

        return this;
    };

    /**
     * Removes a child to the layer from the index specified
     *
     * @param {Renderable} child Child to remove
     * @param {number} layerIndex Index of the layer to add to
     * @returns {Scene}
     * @since 1.2
     */
    Scene.prototype.removeChildFromLayerByIndex = function(child, layerIndex) {
        var layer = this.stage.getLayerByIndex(layerIndex);
        layer.root.removeChild(child);

        return this;
    };

    /**
     * Adds a child to the layer at the name specified
     *
     * @param {Renderable} child Child to add
     * @param {number} layerName Name of the layer to add to
     * @returns {Scene}
     * @since 1.3
     */
    Scene.prototype.addChildToLayerByName = function(child, layerName) {
        var layer = this.stage.getLayerByName(layerName);
        layer.root.addChild(child);

        return this;
    };

    /**
     * Removes a child to the layer from the name specified
     *
     * @param {Renderable} child Child to remove
     * @param {string} layerName Name of the layer to add to
     * @returns {Scene}
     * @since 1.3
     */
    Scene.prototype.removeChildFromLayerByName = function(child, layerName) {
        var layer = this.stage.getLayerByName(layerName);
        layer.root.removeChild(child);

        return this;
    };

    /**
     * Generates a stack of renderables
     * If it no target exists, returns an empty stack
     *
     * Generates a hit stack array object from a renderable source and point
     * The stack is in ordered by the origin at 0 to the front-most hit area at the end
     * TODO: Resolve issues with stack order - currently adding items to the stack in a reverse order
     * TODO: Update hit stack to only gather methods that have the proper hit callback methods implemented
     * TODO: Support touch events
     * TODO: Support custom hit event callbacks
     *
     * @param {number} x X Position
     * @param {number} y Y Position
     * @returns {Renderable[]}
     * @since 1.1
     */
    Scene.prototype.getHitStack = function(x, y) {
        var hitStack = [];
        var target = null;
        // var stackCache = [];
        this.stage.forEachLayer(function(layer) {
            target = layer.root.getChildHitTarget(x, y);

            // Collect the targets in the order they are encountered
            while (target !== null) {
                hitStack.push(target);
                target = target.getChildHitTarget(x, y);
            }
        });

        return hitStack;
    };

    /**
     * Gathers the bottommost hit target to use
     * If it no target exists, returns null
     *
     * Target is gathered from all the layers
     *
     * @param {number} x X Position
     * @param {number} y Y Position
     * @returns {Renderable[]|null}
     * @since 1.1
     */
    Scene.prototype.getHitTarget = function(x, y) {
        var targetList = [];
        var target = null;

        this.stage.forEachLayer(function(layer) {
            target = layer.root.getHitTarget(x, y);

            if (target) {
                targetList.push(target);
            }
        });

        target = targetList.shift();
        return target || null;
    };

    /**
     * Updates the active target based on the passed positioning
     * Active target updates include out/over logic to update which element is currently hovered
     *
     * @param {Renderable[]} hitStack Stack of hit targets
     * @param {number} x X Hit position
     * @param {number} y Y Hit position
     * @returns {Scene}
     * @since 1.1
     */
    Scene.prototype.updateActiveTarget = function(hitStack, x, y) {
        var hitStack = this.getHitStack(x, y);

        var topHitTarget = null;
        if (hitStack.length) {
            topHitTarget = hitStack[hitStack.length - 1 || 0];
        }

        if (this.activeTarget !== topHitTarget) {

            if (this.activeTarget !== null) {
                this.onOut(this.activeTarget, x, y);
            }

            if (topHitTarget !== null) {
                this.onOver(topHitTarget, x, y);
            }

            this.activeTarget = topHitTarget;
        }

        return this;
    };

    /**
     * Gets the Stage
     *
     * @returns {Stage}
     * @since 1.1
     */
    Scene.prototype.getStage = function() {
        return this.stage;
    };

    /**
     * onMove Handler
     * Creates a HitEvent when a mousemove event is triggered
     *
     * @param {string} type Event type
     * @param {Mouse} mouse Mouse state
     * @since 1.0
     */
    Scene.prototype.onMove = function(type, mouse) {
        var hitStack = this.getHitStack(mouse.x, mouse.y);
        this.updateActiveTarget(hitStack, mouse.x, mouse.y);

        this.activeMouse = mouse;

        var event = new HitEvent(HitEvent.MOUSE_MOVE, mouse.x, mouse.y, hitStack, true);
    };

    /**
     * onUp Handler
     * Creates a HitEvent when a mouseup event is triggered
     *
     * @param {string} type Event type
     * @param {Mouse} mouse Mouse state
     * @since 1.0
     */
    Scene.prototype.onUp = function(type, mouse) {
        var hitStack = this.getHitStack(mouse.x, mouse.y);
        var event = new HitEvent(HitEvent.MOUSE_UP, mouse.x, mouse.y, hitStack, true);
    };

    /**
     * onDown Handler
     * Creates a HitEvent when a mousedown event is triggered
     *
     * @param {string} type Event type
     * @param {Mouse} mouse Mouse state
     * @since 1.0
     */
    Scene.prototype.onDown = function(type, mouse) {
        var hitStack = this.getHitStack(mouse.x, mouse.y);
        var event = new HitEvent(HitEvent.MOUSE_DOWN, mouse.x, mouse.y, hitStack, true);
    };

    /**
     * onClick Handler
     * Creates a HitEvent when a click event is triggered
     *
     * @param {string} type Event type
     * @param {Mouse} mouse Mouse state
     * @since 1.0
     */
    Scene.prototype.onClick = function(type, mouse) {
        var hitStack = this.getHitStack(mouse.x, mouse.y);
        var event = new HitEvent(HitEvent.CLICK, mouse.x, mouse.y, hitStack, true);
    };

    /**
     * onOver Handler
     * Creates a HitEvent when a mouseover event is triggered
     *
     * @param {Renderable} target Mouse state
     * @param {number} x X mouse position
     * @param {number} y Y mouse position
     * @since 1.1
     */
    Scene.prototype.onOver = function(target, x, y) {
        var event = new HitEvent(HitEvent.MOUSE_OVER, x, y, target, false);
    };

    /**
     * onOut Handler
     * Creates a HitEvent when a mouseout event is triggered
     *
     * @param {Renderable} target Mouse state
     * @param {number} x X mouse position
     * @param {number} y Y mouse position
     * @since 1.1
     */
    Scene.prototype.onOut = function(target, x, y) {
        var event = new HitEvent(HitEvent.MOUSE_OUT, x, y, target, false);
    };

    /**
     * Updates the scene input elements
     * Tells the active target to update in case something has moved
     * TODO: Update the hit stack logic to remove so much computational complexity
     *
     * @since 1.1
     */
    Scene.prototype.update = function() {
        var activeMouse = this.activeMouse;
        if (activeMouse === null) {
            return;
        }

        var hitStack = this.getHitStack(activeMouse.x, activeMouse.y);
        this.updateActiveTarget(hitStack, activeMouse.x, activeMouse.y);
    };

    /**
     * Renders all the children in the stage's layer stack
     *
     * @since 1.0
     */
    Scene.prototype.render = function() {
        this.stage.forEachLayer(_renderLayer);
    };

    return Scene;
});<commit_msg>Add hitStack validation before creating new HitEvent. #20<commit_after>/**
 * @fileOverview
 * Copyright (c) 2012 Adam Ranfelt
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
 * OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Scene Module Definition
 * @author Adam Ranfelt <adamRenny@gmail.com>
 * @version 1.3.1
 */
define([
    'layer/Stage',
    'layer/Input',
    'layer/HitEvent',
    'layer/Events'
], function(
    Stage,
    Input,
    HitEvent,
    Events
) {
    'use strict';

    /**
     * Render Layer
     * Renders the layer called during an iteration on the Stage object
     *
     * @param {Layer} layer Layer to render with
     * @since 1.1
     */
    var _renderLayer = function(layer) {
        layer.render();
    };

    /**
     * Scene Constructor
     *
     * Effectively acts as Facade of the interactive engine
     * Scene to coordinate all components of a display
     * Facade pattern to encapsulate the stage, layers, and hit detection
     *
     * @name Scene
     * @class Scene container object for layered canvas
     * @constructor
     *
     * @param {HTMLElement} container Container object where the canvases are contained
     * @param {number} width Base unscaled width
     * @param {number} height Base unscaled height
     * @since 1.0
     */
    var Scene = function(container, width, height) {
        if (container && width && height) {
            this.init(container, width, height);
        }
    };

    /**
     * Initializes the Scene with the container
     * Sets up a stage and all necessary layers
     *
     * @param {HTMLElement} container Container object where the canvases are contained
     * @param {number} width Base unscaled width
     * @param {number} height Base unscaled height
     * @returns {Scene}
     * @since 1.0
     */
    Scene.prototype.init = function(container, width, height) {
        /**
         * Stage for the scene to be contained within
         *
         * @default null
         * @name Scene#stage
         * @type {Stage}
         * @since 1.0
         */
        this.stage = null;

        /**
         * Container object for the scene to be contained within
         * Container may have canvas elements within it
         * Container will be used as the viewport for the stage
         *
         * @name Scene#container
         * @type {HTMLElement}
         * @since 1.3.1
         */
        this.container = container;

        this.setupStage(container, width, height);

        /**
         * Scene Input controller to listen from the container
         *
         * @name Scene#input
         * @type {Input}
         * @since 1.0
         */
        this.input = new Input(container);

        /**
         * Currently active input target
         * Used to determine which element is currently active and hovered within the scene
         *
         * @name Scene#activeTarget
         * @type {Renderable}
         * @since 1.1
         */
        this.activeTarget = null;

        /**
         * Last used mouse object
         * Should be considered immutable and opaque
         *
         * @private
         * @name Scene#activeMouse
         * @type {Mouse}
         * @since 1.1
         */
        this.activeMouse = null;

        this.setupHandlers();

        var namespace = this.input.getNamespace();

        Events.on(Input.MOUSE_MOVE + namespace, this.onMoveHandler);
        Events.on(Input.MOUSE_UP + namespace, this.onUpHandler);
        Events.on(Input.MOUSE_DOWN + namespace, this.onDownHandler);
        Events.on(Input.CLICK + namespace, this.onClickHandler);
    };

    /**
     * Create new stage for the scene to be contained within
     *
     * @param {HTMLElement} container Container object where the canvases are contained
     * @param {number} width Base unscaled width
     * @param {number} height Base unscaled height
     * @returns {Scene}
     * @since 1.3
     */
    Scene.prototype.setupStage = function(container, width, height) {
        this.stage = new Stage(container, width, height);

        return this;
    };

    /**
     * Binds all handler functions to local bound handler functions
     *
     * @returns {Scene}
     * @since 1.0
     */
    Scene.prototype.setupHandlers = function() {
        this.onMoveHandler = this.onMove.bind(this);
        this.onUpHandler = this.onUp.bind(this);
        this.onDownHandler = this.onDown.bind(this);
        this.onClickHandler = this.onClick.bind(this);

        return this;
    };

    /**
     * Adds a child to the layer at index 0 if no target is defined
     * Otherwise the target will be parsed by number or string to pipe to the proper method
     *
     * @param {Renderable} child Child to add
     * @param {string|number|null} target Optional target element key to add the child to
     * @returns {Scene}
     * @since 1.1
     */
    Scene.prototype.addChild = function(child, target) {
        if (target === undefined) {
            return this.addChildToLayerByIndex(child, 0);
        } else if (typeof target === 'number') {
            return this.addChildToLayerByIndex(child, target);
        } else if (typeof target === 'string') {
            return this.addChildToLayerByName(child, name);
        }

        return this;
    };

    /**
     * Removes a child from the layer at index 0 if no target is defined
     * Otherwise, the target will be parsed by number or string to pipe to the proper method
     *
     * @param {Renderable} child Child to remove
     * @param {string|number|null} target Optional target element key to add the child to
     * @returns {Scene}
     * @since 1.1
     */
    Scene.prototype.removeChild = function(child, target) {
        if (target === undefined) {
            return this.removeChildFromLayerByIndex(child, 0);
        } else if (typeof target === 'number') {
            return this.removeChildFromLayerByIndex(child, target);
        } else if (typeof target === 'string') {
            return this.removeChildFromLayerByName(child, name);
        }

        return this;
    };

    /**
     * Adds a child to the layer at the index specified
     *
     * @param {Renderable} child Child to add
     * @param {number} layerIndex Index of the layer to add to
     * @returns {Scene}
     * @since 1.2
     */
    Scene.prototype.addChildToLayerByIndex = function(child, layerIndex) {
        var layer = this.stage.getLayerByIndex(layerIndex);
        layer.root.addChild(child);

        return this;
    };

    /**
     * Removes a child to the layer from the index specified
     *
     * @param {Renderable} child Child to remove
     * @param {number} layerIndex Index of the layer to add to
     * @returns {Scene}
     * @since 1.2
     */
    Scene.prototype.removeChildFromLayerByIndex = function(child, layerIndex) {
        var layer = this.stage.getLayerByIndex(layerIndex);
        layer.root.removeChild(child);

        return this;
    };

    /**
     * Adds a child to the layer at the name specified
     *
     * @param {Renderable} child Child to add
     * @param {number} layerName Name of the layer to add to
     * @returns {Scene}
     * @since 1.3
     */
    Scene.prototype.addChildToLayerByName = function(child, layerName) {
        var layer = this.stage.getLayerByName(layerName);
        layer.root.addChild(child);

        return this;
    };

    /**
     * Removes a child to the layer from the name specified
     *
     * @param {Renderable} child Child to remove
     * @param {string} layerName Name of the layer to add to
     * @returns {Scene}
     * @since 1.3
     */
    Scene.prototype.removeChildFromLayerByName = function(child, layerName) {
        var layer = this.stage.getLayerByName(layerName);
        layer.root.removeChild(child);

        return this;
    };

    /**
     * Generates a stack of renderables
     * If it no target exists, returns an empty stack
     *
     * Generates a hit stack array object from a renderable source and point
     * The stack is in ordered by the origin at 0 to the front-most hit area at the end
     * TODO: Resolve issues with stack order - currently adding items to the stack in a reverse order
     * TODO: Update hit stack to only gather methods that have the proper hit callback methods implemented
     * TODO: Support touch events
     * TODO: Support custom hit event callbacks
     *
     * @param {number} x X Position
     * @param {number} y Y Position
     * @returns {Renderable[]}
     * @since 1.1
     */
    Scene.prototype.getHitStack = function(x, y) {
        var hitStack = [];
        var target = null;
        // var stackCache = [];
        this.stage.forEachLayer(function(layer) {
            target = layer.root.getChildHitTarget(x, y);

            // Collect the targets in the order they are encountered
            while (target !== null) {
                hitStack.push(target);
                target = target.getChildHitTarget(x, y);
            }
        });

        return hitStack;
    };

    /**
     * Gathers the bottommost hit target to use
     * If it no target exists, returns null
     *
     * Target is gathered from all the layers
     *
     * @param {number} x X Position
     * @param {number} y Y Position
     * @returns {Renderable[]|null}
     * @since 1.1
     */
    Scene.prototype.getHitTarget = function(x, y) {
        var targetList = [];
        var target = null;

        this.stage.forEachLayer(function(layer) {
            target = layer.root.getHitTarget(x, y);

            if (target) {
                targetList.push(target);
            }
        });

        target = targetList.shift();
        return target || null;
    };

    /**
     * Updates the active target based on the passed positioning
     * Active target updates include out/over logic to update which element is currently hovered
     *
     * @param {Renderable[]} hitStack Stack of hit targets
     * @param {number} x X Hit position
     * @param {number} y Y Hit position
     * @returns {Scene}
     * @since 1.1
     */
    Scene.prototype.updateActiveTarget = function(hitStack, x, y) {
        var topHitTarget = null;
        if (hitStack.length) {
            topHitTarget = hitStack[hitStack.length - 1 || 0];
        }

        if (this.activeTarget !== topHitTarget) {

            if (this.activeTarget !== null) {
                this.onOut(this.activeTarget, x, y);
            }

            if (topHitTarget !== null) {
                this.onOver(topHitTarget, x, y);
            }

            this.activeTarget = topHitTarget;
        }

        return this;
    };

    /**
     * Gets the Stage
     *
     * @returns {Stage}
     * @since 1.1
     */
    Scene.prototype.getStage = function() {
        return this.stage;
    };

    /**
     * onMove Handler
     * Creates a HitEvent when a mousemove event is triggered
     *
     * @param {string} type Event type
     * @param {Mouse} mouse Mouse state
     * @since 1.0
     */
    Scene.prototype.onMove = function(type, mouse) {
        var hitStack = this.getHitStack(mouse.x, mouse.y);
        this.updateActiveTarget(hitStack, mouse.x, mouse.y);

        this.activeMouse = mouse;

        if (hitStack.length > 0) {
            var event = new HitEvent(HitEvent.MOUSE_MOVE, mouse.x, mouse.y, hitStack, true);
        }
    };

    /**
     * onUp Handler
     * Creates a HitEvent when a mouseup event is triggered
     *
     * @param {string} type Event type
     * @param {Mouse} mouse Mouse state
     * @since 1.0
     */
    Scene.prototype.onUp = function(type, mouse) {
        var hitStack = this.getHitStack(mouse.x, mouse.y);
        if (hitStack.length > 0) {
            var event = new HitEvent(HitEvent.MOUSE_UP, mouse.x, mouse.y, hitStack, true);
        }
    };

    /**
     * onDown Handler
     * Creates a HitEvent when a mousedown event is triggered
     *
     * @param {string} type Event type
     * @param {Mouse} mouse Mouse state
     * @since 1.0
     */
    Scene.prototype.onDown = function(type, mouse) {
        var hitStack = this.getHitStack(mouse.x, mouse.y);
        if (hitStack.length > 0) {
            var event = new HitEvent(HitEvent.MOUSE_DOWN, mouse.x, mouse.y, hitStack, true);
        }
    };

    /**
     * onClick Handler
     * Creates a HitEvent when a click event is triggered
     *
     * @param {string} type Event type
     * @param {Mouse} mouse Mouse state
     * @since 1.0
     */
    Scene.prototype.onClick = function(type, mouse) {
        var hitStack = this.getHitStack(mouse.x, mouse.y);
        if (hitStack.length > 0) {
            var event = new HitEvent(HitEvent.CLICK, mouse.x, mouse.y, hitStack, true);
        }
    };

    /**
     * onOver Handler
     * Creates a HitEvent when a mouseover event is triggered
     *
     * @param {Renderable} target Mouse state
     * @param {number} x X mouse position
     * @param {number} y Y mouse position
     * @since 1.1
     */
    Scene.prototype.onOver = function(target, x, y) {
        var event = new HitEvent(HitEvent.MOUSE_OVER, x, y, target, false);
    };

    /**
     * onOut Handler
     * Creates a HitEvent when a mouseout event is triggered
     *
     * @param {Renderable} target Mouse state
     * @param {number} x X mouse position
     * @param {number} y Y mouse position
     * @since 1.1
     */
    Scene.prototype.onOut = function(target, x, y) {
        var event = new HitEvent(HitEvent.MOUSE_OUT, x, y, target, false);
    };

    /**
     * Updates the scene input elements
     * Tells the active target to update in case something has moved
     * TODO: Update the hit stack logic to remove so much computational complexity
     *
     * @since 1.1
     */
    Scene.prototype.update = function() {
        var activeMouse = this.activeMouse;
        if (activeMouse === null) {
            return;
        }

        var hitStack = this.getHitStack(activeMouse.x, activeMouse.y);
        this.updateActiveTarget(hitStack, activeMouse.x, activeMouse.y);
    };

    /**
     * Renders all the children in the stage's layer stack
     *
     * @since 1.0
     */
    Scene.prototype.render = function() {
        this.stage.forEachLayer(_renderLayer);
    };

    return Scene;
});<|endoftext|>


<commit_before>'use strict';

module.exports = class Context {
  constructor () {
    this.sdk = {
      init: require('../init'),
      config: require('../config'),
      api: require('../api'),
      connection: require('../connection'),
      channels: require('../channels'),
      utilities: require('./'), // Deprecate for lib
      lib: require('./'),
      runtime: require('../runtime')
    };
  }

  clear () {
    // Remove all listeners and clean up.
    console.info('(STUB) Cleaning up context.');
  }

};
<commit_msg>WIP context.<commit_after>'use strict';

module.exports = class Context {
  constructor () {
    const Runtime = require('../runtime');
    this.sdk = {
      config: require('../config'),
      api: require('../api'),
      connection: require('../connection'),
      channels: require('../channels'),
      utilities: require('./index'), // Deprecate for lib
      lib: require('./index'),
      runtime: new Runtime()
    };
  }

  clear () {
    // Remove all listeners and clean up.
    console.info('(STUB) Cleaning up context.');
  }

};
<|endoftext|>


<commit_before>import username from 'username';
/*
 * Copyright (c) 2015 TechnologyAdvice
 */
import _ from 'lodash';

const parsers = {
  /**
   * Parses host environment variables
   * @param {String} str The string to parse
   * @returns {String}
   */
  parseHostEnvVars: (str) => {
    // Matches any ${VAR} format vars
    const matcher = (i, match) =>  process.env.hasOwnProperty(match) ? process.env[match] : null;
    // Replace matches on ${VAR}
    return str.toString().replace(/\$\{([^}]+)\}/g, matcher);
  },
  /**
   * Process environment variables
   * @param {Array} env Array of environment variables
   * @returns {Array} env var flags and args
   */
  parseEnvVars: (env) => {
    let envs = [];
    env.map((e) => { envs = envs.concat([ '-e', parsers.parseHostEnvVars(e) ]); });
    return envs;
  },

  /**
   * Process any ports to expose
   * @param {Array} expose Array of ports to expose
   * @returns {Array} port expose flags and args
   */
  parseExpose: (expose) => {
    let ports = [];
    expose.map((p) => { ports = ports.concat([ '-p', p ]); });
    return ports;
  },

  /**
   * Scans the manifest for any exposed ports with `forward` set to `true` at the same level.
   * @param {Object} manifest A parsed devlab manifest
   * @returns {Array<string>} An array of ports exposed on the host machine, in string form.
   */
  parseForwardedPorts: (manifest) => {
    let ports = [];
    let serviceBlocks = [manifest];
    if (manifest.services) {
      serviceBlocks = serviceBlocks.concat(_.values(manifest.services));
    }
    serviceBlocks
      .filter((elem) => {
        return elem.forward && elem.expose && elem.expose.length;
      })
      .map((elem) => elem.expose)
      .forEach((elem) => {
        elem.forEach((expose) => {
          const portMatch = expose.match(/^(\d+):/);
          if (portMatch && portMatch[1]) {
            ports.push(portMatch[1]);
          }
        });
      });
    return ports;
  },

  /**
   * Process any volumes to map
   * @param {Array} volumes Array of volumes to map
   * @returns {Array} volume map flags and args
   */
  parseVolumes: (volumes) => {
    let vols = [];
    volumes.map((v) => { vols = vols.concat([ '-v', parsers.parseHostEnvVars(v) ]); });
    return vols;
  },
  /**
   * Parses the service container name
   * @param {String} name
   * @returns {String}
   */
  parseSvcObjName: (name) => {
    let user = username.sync() || 'unknown';
    return `devlab_${name}_${user}`.toLowerCase().replace(/[^A-Z0-9]/ig, '_');
  },
  /**
   * Parses the service object and ensures all required props set
   * @param {Object} svc The service object from the manifest
   * @returns {Object}
   */
  parseSvcObj: (svc) => {
    const image = Object.keys(svc)[0];
    const name = parsers.parseSvcObjName(svc[image].name || image);
    const alias = svc[image].name || image;
    const env = svc[image].env || false;
    const expose = svc[image].expose || false;
    const persist = svc[image].persist === false ? false : true;
    // Return svc object
    return { image, name, alias, env, expose, persist };
  },
  /**
   * Strips line breaks and splits with semicolons
   * @param {String} task The task to parse
   * @returns {String}
   */
  parseTask: (task) => {
    if (task && task.indexOf('\n') >= 0) {
      let tmp = task.split('\n');
      return tmp.join('; ');
    }
    return task;
  },
  /**
   * Parses aliases to other tasks
   * @param {Object} manifest The config manifest
   * @param {String} task The task
   * @returns {String}
   */
  parseAliases: (manifest, task) => {
    const matchAliases = (i, match) => {
      if (manifest.tasks[match]) {
        return manifest.tasks[match] + ';';
      }
      return '.' + match;
    };
    return parsers.parseTask(task.toString().replace(/\.(\S+)\b/g, matchAliases));
  }
};

export default parsers;
<commit_msg>Tunnels: Make forwarding the default<commit_after>import username from 'username';
/*
 * Copyright (c) 2015 TechnologyAdvice
 */
import _ from 'lodash';

const parsers = {
  /**
   * Parses host environment variables
   * @param {String} str The string to parse
   * @returns {String}
   */
  parseHostEnvVars: (str) => {
    // Matches any ${VAR} format vars
    const matcher = (i, match) =>  process.env.hasOwnProperty(match) ? process.env[match] : null;
    // Replace matches on ${VAR}
    return str.toString().replace(/\$\{([^}]+)\}/g, matcher);
  },
  /**
   * Process environment variables
   * @param {Array} env Array of environment variables
   * @returns {Array} env var flags and args
   */
  parseEnvVars: (env) => {
    let envs = [];
    env.map((e) => { envs = envs.concat([ '-e', parsers.parseHostEnvVars(e) ]); });
    return envs;
  },

  /**
   * Process any ports to expose
   * @param {Array} expose Array of ports to expose
   * @returns {Array} port expose flags and args
   */
  parseExpose: (expose) => {
    let ports = [];
    expose.map((p) => { ports = ports.concat([ '-p', p ]); });
    return ports;
  },

  /**
   * Scans the manifest for any exposed ports with `forward` not set to `false` at the same level.
   * @param {Object} manifest A parsed devlab manifest
   * @returns {Array<string>} An array of ports exposed on the host machine, in string form.
   */
  parseForwardedPorts: (manifest) => {
    let ports = [];
    let serviceBlocks = [manifest];
    if (manifest.services) {
      serviceBlocks = serviceBlocks.concat(_.values(manifest.services));
    }
    serviceBlocks
      .filter((elem) => {
        return elem.forward !== false && elem.expose && elem.expose.length;
      })
      .map((elem) => elem.expose)
      .forEach((elem) => {
        elem.forEach((expose) => {
          const portMatch = expose.match(/^(\d+):/);
          if (portMatch && portMatch[1]) {
            ports.push(portMatch[1]);
          }
        });
      });
    return ports;
  },

  /**
   * Process any volumes to map
   * @param {Array} volumes Array of volumes to map
   * @returns {Array} volume map flags and args
   */
  parseVolumes: (volumes) => {
    let vols = [];
    volumes.map((v) => { vols = vols.concat([ '-v', parsers.parseHostEnvVars(v) ]); });
    return vols;
  },
  /**
   * Parses the service container name
   * @param {String} name
   * @returns {String}
   */
  parseSvcObjName: (name) => {
    let user = username.sync() || 'unknown';
    return `devlab_${name}_${user}`.toLowerCase().replace(/[^A-Z0-9]/ig, '_');
  },
  /**
   * Parses the service object and ensures all required props set
   * @param {Object} svc The service object from the manifest
   * @returns {Object}
   */
  parseSvcObj: (svc) => {
    const image = Object.keys(svc)[0];
    const name = parsers.parseSvcObjName(svc[image].name || image);
    const alias = svc[image].name || image;
    const env = svc[image].env || false;
    const expose = svc[image].expose || false;
    const persist = svc[image].persist === false ? false : true;
    // Return svc object
    return { image, name, alias, env, expose, persist };
  },
  /**
   * Strips line breaks and splits with semicolons
   * @param {String} task The task to parse
   * @returns {String}
   */
  parseTask: (task) => {
    if (task && task.indexOf('\n') >= 0) {
      let tmp = task.split('\n');
      return tmp.join('; ');
    }
    return task;
  },
  /**
   * Parses aliases to other tasks
   * @param {Object} manifest The config manifest
   * @param {String} task The task
   * @returns {String}
   */
  parseAliases: (manifest, task) => {
    const matchAliases = (i, match) => {
      if (manifest.tasks[match]) {
        return manifest.tasks[match] + ';';
      }
      return '.' + match;
    };
    return parsers.parseTask(task.toString().replace(/\.(\S+)\b/g, matchAliases));
  }
};

export default parsers;
<|endoftext|>


<commit_before>import makeStlStream from 'usco-stl-parser'
import { concatStream } from 'usco-stl-parser'
import fetchStream from 'fetch-readablestream'
// import XhrStream from 'xhr-stream'
// require("web-streams-polyfill/dist/polyfill.min.js")
// import 'whatwg-fetch'
// import { ReadableStream } from "web-streams-polyfill"

const Duplex = require('stream').Duplex
const Readable = require('stream').Readable

import create from '@most/create'

function supportsXhrResponseType (type) {
  try {
    var tmpXhr = new XMLHttpRequest()
    tmpXhr.responseType = type
    return tmpXhr.responseType === type
  } catch (e) { /* IE throws on setting responseType to an unsupported value */ }
  return false
}

function supportsStreaming () {
  let fetchSupport = (typeof Response !== 'undefined' && Response.prototype.hasOwnProperty('body'))
  let mozChunkSupport = (supportsXhrResponseType('moz-chunked-arraybuffer'))
  return fetchSupport || mozChunkSupport
}

export default function loadTest (uri) {
  //console.log(`loading model from: ${uri}`)
  const stlStream = makeStlStream({useWorker: true})

  const debugHelper = new Duplex({
    read() {
      console.log('reading')
    },
    write(chunk, encoding, next) {
      console.log('chunk in foo', chunk)
      next(null, Buffer(chunk))
    }
  })

  class HttpSourceStream extends Readable {
    constructor (uri) {
      super()

      let reader
      let self = this
      const finish = () => self.emit('finish')
      const end = () => self.emit('end')
      const push = (data) => self.push(data)
      const emitError = (error) => {
        self.emit('error', error)}

      const process = (data) => {
        const { value, done } = data
        // console.log('SOURCE chunk', data , value, done)
        if (done) {
          // finish()
          end()
        } else {
          push(Buffer(value))
          return reader.read().then(process).catch(e => emitError(e))
        }
      }

      function streams () {
        fetchStream(uri)
          .then(function (response) {
            reader = response.body.getReader()
            reader.read().then(process)
          }).catch(function (e) {
          emitError(e)
        })
      }

      function noStreams () {
        function onLoad (e) {
          let value = e.target.response
          push(Buffer(value))
          setTimeout(end, 0.0001) // don't call end directly !
        }

        function onError (e) {
          emitError(e)
        }

        const xhr = new XMLHttpRequest()
        xhr.responseType = 'arraybuffer'
        xhr.addEventListener('load', onLoad)
        xhr.addEventListener('error', onError)
        xhr.open('GET', uri, true)
        xhr.send()
      }

      if (supportsStreaming()) {
        streams()
      } else {
        noStreams()
      }
    // if (!this.push(chunk)){
    // }
    }
    _read (size) {
      // console.log('_read')
      // signal the source that it can start again
    }
  }
  // return HttpSourceStream(uri).pipe(makeStlStream({useWorker: true}))
  return create((add, end, error) => {
    function streamErrorHandler (_error) {
      // console.log('error in load stream')
      error(_error)
    }

    new HttpSourceStream(uri)
      .on('error', streamErrorHandler)
      .pipe(makeStlStream({useWorker: true}))
      .on('error', streamErrorHandler)
      .pipe(concatStream(data => add(data)))
      .on('error', streamErrorHandler)
  // FIXME: TODO: add error handling here
  })
}
<commit_msg>fix(loader): attempt at fixing ios issues with setTimeout + RAF: - attempt at clean solution (stream _read etc): failing so far - attempt at RAF based settimout : worked (reliably), then did not ??<commit_after>import makeStlStream from 'usco-stl-parser'
import { concatStream } from 'usco-stl-parser'
import fetchStream from 'fetch-readablestream'
// import XhrStream from 'xhr-stream'
// require("web-streams-polyfill/dist/polyfill.min.js")
// import 'whatwg-fetch'
// import { ReadableStream } from "web-streams-polyfill"

//needed workaround for setTimeout issues on ios should be removed once things are solved cleanly (?no more polyfills)
function requestTimeout (fn, delay) {
  var start = new Date().getTime(),
    handle = { }

  function loop () {
    var current = new Date().getTime(),
      delta = current - start

    delta >= delay ? fn.call() : handle.value = requestAnimationFrame(loop)
  }

  handle.value = requestAnimationFrame(loop)
  return handle
}

const Duplex = require('stream').Duplex
const Readable = require('stream').Readable

import create from '@most/create'

function supportsXhrResponseType (type) {
  try {
    var tmpXhr = new XMLHttpRequest()
    tmpXhr.responseType = type
    return tmpXhr.responseType === type
  } catch (e) { /* IE throws on setting responseType to an unsupported value */ }
  return false
}

function supportsStreaming () {
  let fetchSupport = (typeof Response !== 'undefined' && Response.prototype.hasOwnProperty('body'))
  let mozChunkSupport = (supportsXhrResponseType('moz-chunked-arraybuffer'))
  return fetchSupport || mozChunkSupport
}

export default function loadTest (uri) {
  // console.log(`loading model from: ${uri}`)
  const stlStream = makeStlStream({useWorker: true})

  const debugHelper = new Duplex({
    read() {
      console.log('reading')
    },
    write(chunk, encoding, next) {
      console.log('chunk in foo', chunk)
      next(null, Buffer(chunk))
    }
  })

  class HttpSourceStream extends Readable {
    constructor (uri) {
      super()

      let reader
      let self = this
      const finish = () => self.emit('finish')
      const end = () => self.emit('end')
      const push = (data) => self.push(data)
      const emitError = (error) => {
        self.emit('error', error)}

      const process = (data) => {
        const { value, done } = data
        // console.log('SOURCE chunk', data , value, done)
        if (done) {
          end()
        } else {
          push(Buffer(value))
          return reader.read().then(process).catch(e => emitError(e))
        }
      }

      function streams () {
        fetchStream(uri)
          .then(function (response) {
            reader = response.body.getReader()
            reader.read().then(process)
          }).catch(function (e) {
          emitError(e)
        })
      }

      this.readyData
      this.lenToSend = undefined

      function noStreams () {
        function onLoad (e) {
          console.log('onload', e.target.response)
          let value = Buffer(e.target.response)
          self.lenToSend = value.byteLength
          self.readyData = value

          push(value)
          requestTimeout(end, 5)
          // const pushRes = push(value)
          /*while(push(value) !== false){
            //readyData
            console.log('here')
          }*/
          // console.log(pushRes)
          // if push() returns false, then stop reading from source
          // if (!this.push(chunk)){
          // }
          // push(value)
          // end()
          // alert(pushRes)

        // setTimeout(end, 1,true) // don't call end directly !
        }

        function onError (e) {
          emitError(e)
        }

        const xhr = new XMLHttpRequest()
        xhr.responseType = 'arraybuffer'
        xhr.addEventListener('load', onLoad.bind(this))
        xhr.addEventListener('error', onError)
        xhr.open('GET', uri, true)
        xhr.send()
      }

      if (supportsStreaming()) {
        this.streamMode = true
        streams()
      } else {
        this.streamMode = false
        noStreams()
      }
    }
    _read (size) {
      /*console.log('_read', size, this.lenToSend, this)
      // this.push(this.readyData)
      // setTimeout(this.end, 1,true) // don't call end directly !
      let ready = true
      if (this.streamMode) {
        return
      }
      while(ready) {
        if (size && this.lenToSend !== undefined && this.lenToSend > 0) {
          size = Math.min(size, this.readyData.length)
          const workChunk = this.readyData
          const chunk = workChunk.slice(0, size)
          ready = this.push(chunk)
          this.lenToSend -= size // this.lenToSend//size
          this.readyData = workChunk.slice(size)
          console.log('sent', chunk)
        } else {
          ready = false
        }
        console.log('here')
        if (this.lenToSend !== undefined && this.lenToSend <= 0) {
          console.log('done')
          ready = false
          // this.emit('end')
          setTimeout(this.end, 1, true) // don't call end directly !
        }
      }*/
    // signal the source that it can start again
    }
  }
  // return HttpSourceStream(uri).pipe(makeStlStream({useWorker: true}))
  return create((add, end, error) => {
    function streamErrorHandler (_error) {
      error(_error)
    }

    new HttpSourceStream(uri)
      .on('error', streamErrorHandler)
      .pipe(makeStlStream({useWorker: true}))
      .on('error', streamErrorHandler)
      .pipe(concatStream(data => add(data)))
      .on('error', streamErrorHandler)
  })
}
<|endoftext|>