<commit_before>// Global state
var $upNext = null;
var $w;
var $h;
var $slides;
var $arrows;
var $nextArrow;
var $startCardButton;
var $controlBtn;
var $thisPlayerProgress;
var $playedBar;
var $subtitleWrapper;
var $subtitles;
var $slideTitle;
var $ambientPlayer;
var $narrativePlayer;
var $share;
var $shareModal;
var $progressIndicator
var $currentProgress;
var $support;
var $supportBtn;
var $question;
var $careStory;
var $careStoryBtns;
var $email;
var $emailBtn;
var isTouch = Modernizr.touch;
var mobileSuffix;
var aspectWidth = 16;
var aspectHeight = 9;
var optimalWidth;
var optimalHeight;
var w;
var h;
var completion = 0;
var arrowTest;
var progressTest;
var conclusionTest;
var firstRightArrowClicked = false;
// var hammer;
var NO_AUDIO = (window.location.search.indexOf('noaudio') >= 0);
var visibilityProperty = null;

var resize = function() {
    $w = $(window).width();
    $h = $(window).height();

    $slides.width($w);

    optimalWidth = ($h * aspectWidth) / aspectHeight;
    optimalHeight = ($w * aspectHeight) / aspectWidth;

    w = $w;
    h = optimalHeight;

    if (optimalWidth > $w) {
        w = optimalWidth;
        h = $h;
    }

};

var setUpFullPage = function() {
    var anchors = ['_'];
    for (var i = 0; i < COPY.content.length; i++) {
        anchors.push(COPY.content[i][0]);
    }
    $.fn.fullpage({
        anchors: (!APP_CONFIG.DEPLOYMENT_TARGET) ? anchors : false,
        autoScrolling: false,
        keyboardScrolling: false,
        verticalCentered: false,
        fixedElements: '.primary-navigation, #share-modal, .share, .progress-indicator',
        resize: false,
        css3: true,
        loopHorizontal: false,
        afterRender: onPageLoad,
        afterSlideLoad: lazyLoad,
        onSlideLeave: onSlideLeave
    });
};

var onPageLoad = function() {
    setSlidesForLazyLoading(0);
    $('.section').css({
      'opacity': 1,
      'visibility': 'visible',
    });
    showNavigation();
};

// after a new slide loads
var lazyLoad = function(anchorLink, index, slideAnchor, slideIndex) {
    setSlidesForLazyLoading(slideIndex);
    showNavigation();
    AUDIO.checkForAudio(slideAnchor);
    animateProgress(slideIndex);

    if (slideIndex === 0) {
        $share.hide();
    } else {
        $share.show();
    }

    if (slideIndex === $slides.length - 1) {
        buildConclusionSlide();
    }

    // Completion tracking
    how_far = (slideIndex + 1) / ($slides.length - 1);

    if (how_far >= completion + 0.25) {
        completion = how_far - (how_far % 0.25);

        if (completion === 0.25) {
            ANALYTICS.completeTwentyFivePercent(progressTest);
        }
        else if (completion === 0.5) {
            ANALYTICS.completeFiftyPercent(progressTest);
        }
        else if (completion === 0.75) {
            ANALYTICS.completeSeventyFivePercent(progressTest);
        }
        else if (completion === 1) {
            ANALYTICS.completeOneHundredPercent(progressTest);
        }
    }
};

var setSlidesForLazyLoading = function(slideIndex) {
    /*
    * Sets up a list of slides based on your position in the deck.
    * Lazy-loads images in future slides because of reasons.
    */
    var slides = [
        $slides.eq(slideIndex),
        $slides.eq(slideIndex + 1),
        $slides.eq(slideIndex + 2),
        $slides.eq(slideIndex + 3),
        $slides.eq(slideIndex + 4)
    ];

    // Mobile suffix should be blank by default.
    mobileSuffix = '';
    /*
    if ($w < 769) {
        mobileSuffix = '-sq';
    }*/

    for (var i = 0; i < slides.length; i++) {
        loadImages(slides[i]);
    };

}

var loadImages = function($slide) {
    /*
    * Sets the background image on a div for our fancy slides.
    */
    if ($slide.data('bgimage')) {
        var image_filename = $slide.data('bgimage').split('.')[0];
        var image_extension = '.' + $slide.data('bgimage').split('.')[1];
        var image_path = 'assets/' + image_filename + mobileSuffix + image_extension;

        if ($slide.css('background-image') === 'none') {
            $slide.css('background-image', 'url(' + image_path + ')');
        }
    }

    var $images = $slide.find('img.lazy-load');
    if ($images.length > 0) {
        for (var i = 0; i < $images.length; i++) {
            var image = $images.eq(i).data('src');
            $images.eq(i).attr('src', 'assets/' + image);
        }
    }
};

var showNavigation = function() {
    /*
    * Nav doesn't exist by default.
    * This function loads it up.
    */

    if ($slides.first().hasClass('active')) {
        /*
        * Don't show arrows on titlecard
        */
        $arrows.hide();
    }

    else if ($slides.last().hasClass('active')) {
        /*
        * Last card gets no next arrow but does have the nav.
        */
        if (!$arrows.hasClass('active')) {
            showArrows();
        }

        $nextArrow.removeClass('active');
        $nextArrow.hide();
    } else if ($slides.eq(1).hasClass('active')) {
        showArrows();

        switch (arrowTest) {
            case 'bright-arrow':
                $nextArrow.addClass('titlecard-nav');
                break;
            case 'bouncy-arrow':
                $nextArrow.addClass('shake animated titlecard-nav');
                break;
            default:
                break;
        }

        $nextArrow.on('click', onFirstRightArrowClick);
    } else {
        /*
        * All of the other cards? Arrows and navs.
        */
        if ($arrows.filter('active').length != $arrows.length) {
            showArrows();
        }
        $nextArrow.removeClass('shake animated titlecard-nav');

        $nextArrow.off('click', onFirstRightArrowClick);
    }

    if (progressTest === 'progress-bar') {
        $progressIndicator.show();
    }
}

var showArrows = function() {
    /*
    * Show the arrows.
    */
    $arrows.addClass('active');
    $arrows.show();
};

var determineTest = function(possibleTests) {
    var test = possibleTests[getRandomInt(0, possibleTests.length)]
    return test
}

var getRandomInt = function(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}

var setCustomVars = function() {
    ANALYTICS.setCustomVar(40, 'progress-test', progressTest);
}

var buildConclusionSlide = function() {
    ANALYTICS.trackEvent('tests-run', conclusionTest);

    if (conclusionTest === 'no-question') {
        $support.show();
    } else {
        $question.text(COPY['post_metadata'][conclusionTest]);
        $careStory.show();
    }
}

var onCareStoryBtnClick = function(e) {
    e.preventDefault();

    var $this = $(this);

    $careStory.hide();

    if ($this.hasClass('yes')) {
        ANALYTICS.trackEvent('like-story', 'yes');
        $support.show();
    } else {
        ANALYTICS.trackEvent('like-story', 'no');
        $email.show();
    }
}

var onSupportBtnClick = function(e) {
    e.preventDefault();

    var $this = $(this);
    var link = $this.attr('href');

    ANALYTICS.trackEvent('support-btn-click', conclusionTest);

    window.top.location = link
    return true;
}

var onEmailBtnClick = function() {
    ANALYTICS.trackEvent('email-btn-click');
}

var animateProgress = function(index) {
    var totalSlides = $slides.length;
    var percentage = (index + 1) / totalSlides;
    $currentProgress.css('width', percentage * 100 + '%');

    if (index === 0) {
        $progressIndicator.width(0);
    } else {
        $progressIndicator.width('100%');
    }
}

var onSlideLeave = function(anchorLink, index, slideIndex, direction) {
    /*
    * Called when leaving a slide.
    */
    ANALYTICS.exitSlide(slideIndex.toString());
}

var onFirstRightArrowClick = function() {
    if (firstRightArrowClicked === false) {
        ANALYTICS.firstRightArrowClick(arrowTest);
        firstRightArrowClicked = true;
    }
}

var onStartCardButtonClick = function() {
    $.fn.fullpage.moveSlideRight();
    if (isTouch) {
        AUDIO.fakeAmbientPlayer();
        AUDIO.fakeNarrativePlayer();
    }
}

var onDocumentKeyDown = function(e) {
    if (e.which === 37 || e.which === 39) {
        ANALYTICS.useKeyboardNavigation();
        if (e.which === 37) {
            $.fn.fullpage.moveSlideLeft();
        } else if (e.which === 39) {
            $.fn.fullpage.moveSlideRight();
        }
    }
    // jquery.fullpage handles actual scrolling
    return true;
}

var onSlideClick = function(e) {
    if (isTouch) {
        if ($slides.first().hasClass('active')) {
            AUDIO.fakeAmbientPlayer();
            AUDIO.fakeNarrativePlayer();
        }
        $.fn.fullpage.moveSlideRight();
    }
    return true;
}

var onNextPostClick = function(e) {
    e.preventDefault();

    ANALYTICS.trackEvent('next-post');
    window.top.location = NEXT_POST_URL;
    return true;
}

var fakeMobileHover = function() {
    $(this).css({
        'background-color': '#fff',
        'color': '#000',
        'opacity': .9
    });
}

var rmFakeMobileHover = function() {
    $(this).css({
        'background-color': 'rgba(0, 0, 0, 0.2)',
        'color': '#fff',
        'opacity': .3
    });
}

/*
 * Text copied to clipboard.
 */
var onClippyCopy = function(e) {
    alert('Copied to your clipboard!');

    ANALYTICS.copySummary();
}

var onControlBtnClick = function(e) {
    e.preventDefault();
    AUDIO.toggleNarrativeAudio();
    ANALYTICS.trackEvent('pause-button');

    e.stopPropagation();
}

var onVisibilityChange = function() {
    AUDIO.toggleAllAudio();
}

var getHiddenProperty = function() {
    var prefixes = ['webkit','moz','ms','o'];

    // if 'hidden' is natively supported just return it
    if ('hidden' in document) return 'hidden';

    // otherwise loop over all the known prefixes until we find one
    for (var i = 0; i < prefixes.length; i++){
        if ((prefixes[i] + 'Hidden') in document)
            return prefixes[i] + 'Hidden';
    }

    // otherwise it's not supported
    return null;
}

var isHidden = function() {
    var prop = getHiddenProperty();
    if (!prop) return false;

    return document[prop];
}

/*
 * Share modal opened.
 */
var onShareModalShown = function(e) {
    ANALYTICS.openShareDiscuss();
}

/*
 * Share modal closed.
 */
var onShareModalHidden = function(e) {
    ANALYTICS.closeShareDiscuss();
}

$(document).ready(function() {
    $w = $(window).width();
    $h = $(window).height();

    $slides = $('.slide');
    $navButton = $('.primary-navigation-btn');
    $startCardButton = $('.btn-go');
    $arrows = $('.controlArrow');
    $nextArrow = $arrows.filter('.next');
    $upNext = $('.up-next');
    $controlBtn = $('.control-btn');
    $narrativePlayer = $('#narrative-player');
    $ambientPlayer = $('#ambient-player');
    $share = $('.share');
    $shareModal = $('#share-modal')
    $progressIndicator = $('.progress-indicator');
    $currentProgress = $('.current-progress');
    $support = $('.support')
    $supportBtn = $('.support-btn');
    $careStory = $('.care-story');
    $question = $('.question');
    $careStoryBtns = $('.care-story-btn');
    $email = $('.email');
    $emailBtn = $('.email-btn');

    arrowTest = determineTest(['faded-arrow', 'bright-arrow', 'bouncy-arrow']);
    progressTest = determineTest(['progress-bar', 'no-progress-bar']);
    conclusionTest = determineTest(['no-question', 'question_a', 'question_b', 'question_c', 'question_d']);

    $shareModal.on('shown.bs.modal', onShareModalShown);
    $shareModal.on('hidden.bs.modal', onShareModalHidden);
    $startCardButton.on('click', onStartCardButtonClick);
    $slides.on('click', onSlideClick);
    $upNext.on('click', onNextPostClick);
    $controlBtn.on('click', onControlBtnClick);
    $arrows.on('touchstart', fakeMobileHover);
    $arrows.on('touchend', rmFakeMobileHover);
    $careStoryBtns.on('click', onCareStoryBtnClick);
    $supportBtn.on('click', onSupportBtnClick);
    $emailBtn.on('click', onEmailBtnClick);
    $(document).keydown(onDocumentKeyDown);

    AUDIO.setUpNarrativePlayer();
    AUDIO.setUpAmbientPlayer();
    setUpFullPage();
    resize();
    setCustomVars();

    // Redraw slides if the window resizes
    window.addEventListener("deviceorientation", resize, true);
    $(window).resize(resize);

    // listen for page visibility changes
    visibilityProperty = getHiddenProperty();
    if (visibilityProperty) {
        var evtname = visibilityProperty.replace(/[H|h]idden/,'') + 'visibilitychange';
        document.addEventListener(evtname, onVisibilityChange);
    }
});<commit_msg>fix the fake mobile hover so it matches the stylesheet<commit_after>// Global state
var $upNext = null;
var $w;
var $h;
var $slides;
var $arrows;
var $nextArrow;
var $startCardButton;
var $controlBtn;
var $thisPlayerProgress;
var $playedBar;
var $subtitleWrapper;
var $subtitles;
var $slideTitle;
var $ambientPlayer;
var $narrativePlayer;
var $share;
var $shareModal;
var $progressIndicator
var $currentProgress;
var $support;
var $supportBtn;
var $question;
var $careStory;
var $careStoryBtns;
var $email;
var $emailBtn;
var isTouch = Modernizr.touch;
var mobileSuffix;
var aspectWidth = 16;
var aspectHeight = 9;
var optimalWidth;
var optimalHeight;
var w;
var h;
var completion = 0;
var arrowTest;
var progressTest;
var conclusionTest;
var firstRightArrowClicked = false;
// var hammer;
var NO_AUDIO = (window.location.search.indexOf('noaudio') >= 0);
var visibilityProperty = null;

var resize = function() {
    $w = $(window).width();
    $h = $(window).height();

    $slides.width($w);

    optimalWidth = ($h * aspectWidth) / aspectHeight;
    optimalHeight = ($w * aspectHeight) / aspectWidth;

    w = $w;
    h = optimalHeight;

    if (optimalWidth > $w) {
        w = optimalWidth;
        h = $h;
    }

};

var setUpFullPage = function() {
    var anchors = ['_'];
    for (var i = 0; i < COPY.content.length; i++) {
        anchors.push(COPY.content[i][0]);
    }
    $.fn.fullpage({
        anchors: (!APP_CONFIG.DEPLOYMENT_TARGET) ? anchors : false,
        autoScrolling: false,
        keyboardScrolling: false,
        verticalCentered: false,
        fixedElements: '.primary-navigation, #share-modal, .share, .progress-indicator',
        resize: false,
        css3: true,
        loopHorizontal: false,
        afterRender: onPageLoad,
        afterSlideLoad: lazyLoad,
        onSlideLeave: onSlideLeave
    });
};

var onPageLoad = function() {
    setSlidesForLazyLoading(0);
    $('.section').css({
      'opacity': 1,
      'visibility': 'visible',
    });
    showNavigation();
};

// after a new slide loads
var lazyLoad = function(anchorLink, index, slideAnchor, slideIndex) {
    setSlidesForLazyLoading(slideIndex);
    showNavigation();
    AUDIO.checkForAudio(slideAnchor);
    animateProgress(slideIndex);

    if (slideIndex === 0) {
        $share.hide();
    } else {
        $share.show();
    }

    if (slideIndex === $slides.length - 1) {
        buildConclusionSlide();
    }

    // Completion tracking
    how_far = (slideIndex + 1) / ($slides.length - 1);

    if (how_far >= completion + 0.25) {
        completion = how_far - (how_far % 0.25);

        if (completion === 0.25) {
            ANALYTICS.completeTwentyFivePercent(progressTest);
        }
        else if (completion === 0.5) {
            ANALYTICS.completeFiftyPercent(progressTest);
        }
        else if (completion === 0.75) {
            ANALYTICS.completeSeventyFivePercent(progressTest);
        }
        else if (completion === 1) {
            ANALYTICS.completeOneHundredPercent(progressTest);
        }
    }
};

var setSlidesForLazyLoading = function(slideIndex) {
    /*
    * Sets up a list of slides based on your position in the deck.
    * Lazy-loads images in future slides because of reasons.
    */
    var slides = [
        $slides.eq(slideIndex),
        $slides.eq(slideIndex + 1),
        $slides.eq(slideIndex + 2),
        $slides.eq(slideIndex + 3),
        $slides.eq(slideIndex + 4)
    ];

    // Mobile suffix should be blank by default.
    mobileSuffix = '';
    /*
    if ($w < 769) {
        mobileSuffix = '-sq';
    }*/

    for (var i = 0; i < slides.length; i++) {
        loadImages(slides[i]);
    };

}

var loadImages = function($slide) {
    /*
    * Sets the background image on a div for our fancy slides.
    */
    if ($slide.data('bgimage')) {
        var image_filename = $slide.data('bgimage').split('.')[0];
        var image_extension = '.' + $slide.data('bgimage').split('.')[1];
        var image_path = 'assets/' + image_filename + mobileSuffix + image_extension;

        if ($slide.css('background-image') === 'none') {
            $slide.css('background-image', 'url(' + image_path + ')');
        }
    }

    var $images = $slide.find('img.lazy-load');
    if ($images.length > 0) {
        for (var i = 0; i < $images.length; i++) {
            var image = $images.eq(i).data('src');
            $images.eq(i).attr('src', 'assets/' + image);
        }
    }
};

var showNavigation = function() {
    /*
    * Nav doesn't exist by default.
    * This function loads it up.
    */

    if ($slides.first().hasClass('active')) {
        /*
        * Don't show arrows on titlecard
        */
        $arrows.hide();
    }

    else if ($slides.last().hasClass('active')) {
        /*
        * Last card gets no next arrow but does have the nav.
        */
        if (!$arrows.hasClass('active')) {
            showArrows();
        }

        $nextArrow.removeClass('active');
        $nextArrow.hide();
    } else if ($slides.eq(1).hasClass('active')) {
        showArrows();

        switch (arrowTest) {
            case 'bright-arrow':
                $nextArrow.addClass('titlecard-nav');
                break;
            case 'bouncy-arrow':
                $nextArrow.addClass('shake animated titlecard-nav');
                break;
            default:
                break;
        }

        $nextArrow.on('click', onFirstRightArrowClick);
    } else {
        /*
        * All of the other cards? Arrows and navs.
        */
        if ($arrows.filter('active').length != $arrows.length) {
            showArrows();
        }
        $nextArrow.removeClass('shake animated titlecard-nav');

        $nextArrow.off('click', onFirstRightArrowClick);
    }

    if (progressTest === 'progress-bar') {
        $progressIndicator.show();
    }
}

var showArrows = function() {
    /*
    * Show the arrows.
    */
    $arrows.addClass('active');
    $arrows.show();
};

var determineTest = function(possibleTests) {
    var test = possibleTests[getRandomInt(0, possibleTests.length)]
    return test
}

var getRandomInt = function(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}

var setCustomVars = function() {
    ANALYTICS.setCustomVar(40, 'progress-test', progressTest);
}

var buildConclusionSlide = function() {
    ANALYTICS.trackEvent('tests-run', conclusionTest);

    if (conclusionTest === 'no-question') {
        $support.show();
    } else {
        $question.text(COPY['post_metadata'][conclusionTest]);
        $careStory.show();
    }
}

var onCareStoryBtnClick = function(e) {
    e.preventDefault();

    var $this = $(this);

    $careStory.hide();

    if ($this.hasClass('yes')) {
        ANALYTICS.trackEvent('like-story', 'yes');
        $support.show();
    } else {
        ANALYTICS.trackEvent('like-story', 'no');
        $email.show();
    }
}

var onSupportBtnClick = function(e) {
    e.preventDefault();

    var $this = $(this);
    var link = $this.attr('href');

    ANALYTICS.trackEvent('support-btn-click', conclusionTest);

    window.top.location = link
    return true;
}

var onEmailBtnClick = function() {
    ANALYTICS.trackEvent('email-btn-click');
}

var animateProgress = function(index) {
    var totalSlides = $slides.length;
    var percentage = (index + 1) / totalSlides;
    $currentProgress.css('width', percentage * 100 + '%');

    if (index === 0) {
        $progressIndicator.width(0);
    } else {
        $progressIndicator.width('100%');
    }
}

var onSlideLeave = function(anchorLink, index, slideIndex, direction) {
    /*
    * Called when leaving a slide.
    */
    ANALYTICS.exitSlide(slideIndex.toString());
}

var onFirstRightArrowClick = function() {
    if (firstRightArrowClicked === false) {
        ANALYTICS.firstRightArrowClick(arrowTest);
        firstRightArrowClicked = true;
    }
}

var onStartCardButtonClick = function() {
    $.fn.fullpage.moveSlideRight();
    if (isTouch) {
        AUDIO.fakeAmbientPlayer();
        AUDIO.fakeNarrativePlayer();
    }
}

var onDocumentKeyDown = function(e) {
    if (e.which === 37 || e.which === 39) {
        ANALYTICS.useKeyboardNavigation();
        if (e.which === 37) {
            $.fn.fullpage.moveSlideLeft();
        } else if (e.which === 39) {
            $.fn.fullpage.moveSlideRight();
        }
    }
    // jquery.fullpage handles actual scrolling
    return true;
}

var onSlideClick = function(e) {
    if (isTouch) {
        if ($slides.first().hasClass('active')) {
            AUDIO.fakeAmbientPlayer();
            AUDIO.fakeNarrativePlayer();
        }
        $.fn.fullpage.moveSlideRight();
    }
    return true;
}

var fakeMobileHover = function() {
    $(this).css({
        'background-color': '#fff',
        'color': '#000',
        'opacity': .9
    });
}

var rmFakeMobileHover = function() {
    $(this).css({
        'background-color': 'rgba(0, 0, 0, 0.5)',
        'color': '#fff',
        'opacity': .5
    });
}

/*
 * Text copied to clipboard.
 */
var onClippyCopy = function(e) {
    alert('Copied to your clipboard!');

    ANALYTICS.copySummary();
}

var onControlBtnClick = function(e) {
    e.preventDefault();
    AUDIO.toggleNarrativeAudio();
    ANALYTICS.trackEvent('pause-button');

    e.stopPropagation();
}

var onVisibilityChange = function() {
    AUDIO.toggleAllAudio();
}

var getHiddenProperty = function() {
    var prefixes = ['webkit','moz','ms','o'];

    // if 'hidden' is natively supported just return it
    if ('hidden' in document) return 'hidden';

    // otherwise loop over all the known prefixes until we find one
    for (var i = 0; i < prefixes.length; i++){
        if ((prefixes[i] + 'Hidden') in document)
            return prefixes[i] + 'Hidden';
    }

    // otherwise it's not supported
    return null;
}

var isHidden = function() {
    var prop = getHiddenProperty();
    if (!prop) return false;

    return document[prop];
}

/*
 * Share modal opened.
 */
var onShareModalShown = function(e) {
    ANALYTICS.openShareDiscuss();
}

/*
 * Share modal closed.
 */
var onShareModalHidden = function(e) {
    ANALYTICS.closeShareDiscuss();
}

$(document).ready(function() {
    $w = $(window).width();
    $h = $(window).height();

    $slides = $('.slide');
    $navButton = $('.primary-navigation-btn');
    $startCardButton = $('.btn-go');
    $arrows = $('.controlArrow');
    $nextArrow = $arrows.filter('.next');
    $upNext = $('.up-next');
    $controlBtn = $('.control-btn');
    $narrativePlayer = $('#narrative-player');
    $ambientPlayer = $('#ambient-player');
    $share = $('.share');
    $shareModal = $('#share-modal')
    $progressIndicator = $('.progress-indicator');
    $currentProgress = $('.current-progress');
    $support = $('.support')
    $supportBtn = $('.support-btn');
    $careStory = $('.care-story');
    $question = $('.question');
    $careStoryBtns = $('.care-story-btn');
    $email = $('.email');
    $emailBtn = $('.email-btn');

    arrowTest = determineTest(['faded-arrow', 'bright-arrow', 'bouncy-arrow']);
    progressTest = determineTest(['progress-bar', 'no-progress-bar']);
    conclusionTest = determineTest(['no-question', 'question_a', 'question_b', 'question_c', 'question_d']);

    $shareModal.on('shown.bs.modal', onShareModalShown);
    $shareModal.on('hidden.bs.modal', onShareModalHidden);
    $startCardButton.on('click', onStartCardButtonClick);
    $slides.on('click', onSlideClick);
    $upNext.on('click', onNextPostClick);
    $controlBtn.on('click', onControlBtnClick);
    $arrows.on('touchstart', fakeMobileHover);
    $arrows.on('touchend', rmFakeMobileHover);
    $careStoryBtns.on('click', onCareStoryBtnClick);
    $supportBtn.on('click', onSupportBtnClick);
    $emailBtn.on('click', onEmailBtnClick);
    $(document).keydown(onDocumentKeyDown);

    AUDIO.setUpNarrativePlayer();
    AUDIO.setUpAmbientPlayer();
    setUpFullPage();
    resize();
    setCustomVars();

    // Redraw slides if the window resizes
    window.addEventListener("deviceorientation", resize, true);
    $(window).resize(resize);

    // listen for page visibility changes
    visibilityProperty = getHiddenProperty();
    if (visibilityProperty) {
        var evtname = visibilityProperty.replace(/[H|h]idden/,'') + 'visibilitychange';
        document.addEventListener(evtname, onVisibilityChange);
    }
});<|endoftext|>


<commit_before>'use strict';

var ciApp = soajsApp.components;
ciApp.controller('ciAppCtrl', ['$scope', '$timeout', '$modal', '$cookies', 'ngDataApi', 'injectFiles', function ($scope, $timeout, $modal, $cookies, ngDataApi, injectFiles) {
	$scope.$parent.isUserLoggedIn();

	$scope.access = {};
	constructModulePermissions($scope, $scope.access, ciAppConfig.permissions);

	$scope.ciData = {};

	$scope.images = {
		travis: "./themes/" + themeToUse + "/img/travis_logo.png",
		drone: "./themes/" + themeToUse + "/img/drone_logo.png",
		jenkins: "./themes/" + themeToUse + "/img/jenkins_logo.png",
		teamcity: "./themes/" + themeToUse + "/img/teamcity_logo.png"
	};

	$scope.unsupported = ['jenkins', 'teamcity'];

	$scope.listAccounts = function () {
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			'method': 'get',
			'routeName': '/dashboard/ci'
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			} else {
				$scope.accounts = [];
				var processed = [];
				response.forEach(function(oneEntry){
					if(oneEntry.owner && oneEntry.gitProvider && processed.indexOf(oneEntry.owner + oneEntry.gitProvider) === -1){
						var oneAccount = {
							owner: oneEntry.owner,
							hide: true,
							icon: 'plus',
							providers: [],
							gitProvider: oneEntry.gitProvider
						};

						response.forEach(function(oneEntryAgain){
							if(oneEntryAgain.owner === oneAccount.owner && $scope.images[oneEntryAgain.provider]){
								oneEntryAgain.icon = $scope.images[oneEntryAgain.provider];
								oneEntryAgain.locked = ($scope.unsupported.indexOf(oneEntryAgain.provider) !== -1);
								oneAccount.providers.push(oneEntryAgain);
							}
						});

						$scope.accounts.push(oneAccount);
						processed.push(oneEntry.owner + oneEntry.gitProvider);
					}
				});

				$scope.accounts.forEach(function(oneAccount){
					var logos = angular.copy($scope.images);
					oneAccount.providers.forEach(function(oneProvider){
						if(logos[oneProvider.provider]){
							delete logos[oneProvider.provider];
						}
					});
					for(var logo in logos){
						var nEntry = {
							provider: logo,
							icon: logos[logo]
						};
						nEntry.locked = ($scope.unsupported.indexOf(logo) !== -1);

						oneAccount.providers.push(nEntry);
					}
				});

				$scope.accounts[0].hide = false;
				$scope.accounts[0].icon = 'minus';
			}
		});
	};

	$scope.deactivateAccount = function(provider){
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			'method': 'put',
			'routeName': '/dashboard/ci/provider',
			'data':{
				'owner': provider.owner,
				'provider': provider.provider
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			} else {
				$scope.displayAlert('success', "Provider has been deactivated");
				$scope.listAccounts();
			}
		});
	};

	$scope.activateAccount = function(owner, provider){
		var formConfig;

		switch(provider.provider){
			case 'travis':
				formConfig = angular.copy(ciAppConfig.form.f1.travis);
				formConfig.entries[0].value = 'api.travis-ci.org';
				break;
			case 'drone':
				formConfig = angular.copy(ciAppConfig.form.f1.drone);
				formConfig.entries[0].value = '';
				break;
		}

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'continuousIntegration',
			label: 'Integrating with ' + provider.provider,
			actions: [
				{
					type: 'submit',
					label: "Submit",
					btn: 'success',
					action: function (formData) {
						var data = {
							"domain": formData.domain,
							"gitToken": formData.gitToken,
							"owner": owner,
							"provider": provider.provider
						};
						var regex = /^[^:]+(?=:\/\/)/;
						if (regex.test(formData.domain) && formData.domain.match(regex)[0] !== 'http' || formData.domain.match(regex)[0] !== 'https') {
							$scope.form.displayAlert('danger', "Invalid domain name provided!");

						}
						else {
							overlayLoading.show();
							getSendDataFromServer($scope, ngDataApi, {
								method: 'post',
								routeName: '/dashboard/ci/provider',
								data: data
							}, function (error, response) {
								overlayLoading.hide();
								if (error) {
									$scope.form.displayAlert('danger', error.message);
								}
								else {
									$scope.displayAlert('success', 'Provider has been integrated');
									$scope.form.formData = {};
									if ($scope.modalInstance) {
										$scope.modalInstance.close();
									}
									$scope.listAccounts();
								}
							});
						}
					}
				},
				{
					type: "reset",
					label: "Cancel",
					btn: "danger",
					action: function(){
						$scope.form.formData = {};
						$scope.modalInstance.close();
					}
				}
			]
		};
		buildFormWithModal($scope, $modal, options);
	};

	$scope.updateAccount = function(provider){
		var formConfig;

		switch(provider.provider){
			case 'travis':
				formConfig = angular.copy(ciAppConfig.form.f1.travis);
				formConfig.entries[0].value = 'api.travis-ci.org';
				break;
			case 'drone':
				formConfig = angular.copy(ciAppConfig.form.f1.drone);
				formConfig.entries[0].value = '';
				break;
		}

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'continuousIntegration',
			label: 'Updating Integrating with ' + provider.provider,
			data: provider,
			actions: [
				{
					type: 'submit',
					label: "Submit",
					btn: 'success',
					action: function (formData) {
						var data = {
							"id": provider._id,
							"domain": formData.domain,
							"gitToken": formData.gitToken,
							"owner": provider.owner,
							"provider": provider.provider
						};
						var regex = /^[^:]+(?=:\/\/)/;
						if (regex.test(formData.domain) && formData.domain.match(regex)[0] !== 'http' || formData.domain.match(regex)[0] !== 'https') {
							$scope.form.displayAlert('danger', "Invalid domain name provided!");

						}
					    else {
							overlayLoading.show();
							getSendDataFromServer($scope, ngDataApi, {
								method: 'post',
								routeName: '/dashboard/ci/provider',
								data: data
							}, function (error, response) {
								overlayLoading.hide();
								if (error) {
									$scope.form.displayAlert('danger', error.message);
								}
								else {
									$scope.displayAlert('success', 'Provider integration has been updated');
									$scope.form.formData = {};
									if ($scope.modalInstance) {
										$scope.modalInstance.close();
									}
									$scope.listAccounts();

								}
							});
						}

					}
				},
				{
					type: "reset",
					label: "Cancel",
					btn: "danger",
					action: function(){
						$scope.form.formData = {};
						$scope.modalInstance.close();
					}
				}
			]
		};
		buildFormWithModal($scope, $modal, options);
	};

	$scope.showHide = function (account) {
		if (!account.hide) {
			jQuery('#a_' + account.owner + " .body .inner").slideUp();
			account.icon = 'plus';
			account.hide = true;
		}
		else {
			jQuery('#a_' + account.owner + " .body .inner").slideDown();
			account.icon = 'minus';
			account.hide = false;
		}
	};

	$scope.listUniqueProviders = function(){
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			'method': 'get',
			'routeName': '/dashboard/ci/providers'
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			} else {
				$scope.providers = angular.copy(response);
				delete $scope.providers.soajsauth;
			}
		});
	};

	$scope.addRecipe = function (provider, type) {
		var formConfig = angular.copy(ciAppConfig.form.f2);

		if (type === 'blank') {
			formConfig.entries.splice(0, 1);
		}
		else{
			$scope.providers[provider].forEach(function (oneRecipe) {
				var label = oneRecipe.name;
				formConfig.entries[0].value.push({ l: label, v: oneRecipe });
			});

			formConfig.entries[0].onAction = function (id, data, form) {
				var recipeTemplate = JSON.parse(data);
				delete recipeTemplate._id;
				delete recipeTemplate.locked;
				form.formData.name = recipeTemplate.name;
				form.formData.recipe = recipeTemplate.recipe;
			};
		}

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'addRecipe',
			label: 'Add New Recipe',
			actions: [
				{
					type: 'submit',
					label: 'Submit',
					btn: 'primary',
					action: function (formData) {
						overlayLoading.show();
						getSendDataFromServer($scope, ngDataApi, {
							method: 'post',
							routeName: '/dashboard/ci/recipe',
							data: {
								provider: provider,
								name: formData.name,
								recipe: formData.recipe
							}
						}, function (error, response) {
							overlayLoading.hide();
							if (error) {
								$scope.form.displayAlert('danger', error.message);
							}
							else {
								$scope.displayAlert('success', 'Recipe added successfully');
								$scope.modalInstance.close();
								$scope.form.formData = {};
								$scope.listUniqueProviders();
							}
						});
					}
				},
				{
					type: 'reset',
					label: 'Cancel',
					btn: 'danger',
					action: function () {
						$scope.modalInstance.dismiss('cancel');
						$scope.form.formData = {};
					}
				}
			]
		};

		buildFormWithModal($scope, $modal, options);
	};

	$scope.viewRecipe = function (recipe) {
		var formConfig = angular.copy(ciAppConfig.form.f2);
		formConfig.entries.splice(0,2);
		formConfig.entries[0].value = recipe.recipe;

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'viewRecipe',
			label: 'Viewing ' + recipe.name,
			actions: [
				{
					type: 'reset',
					label: 'Close',
					btn: 'primary',
					action: function () {
						$scope.modalInstance.dismiss('cancel');
						$scope.form.formData = {};
					}
				}
			]
		};

		buildFormWithModal($scope, $modal, options);
	};

	$scope.updateRecipe = function (recipe) {
		var formConfig = angular.copy(ciAppConfig.form.f2);
		formConfig.entries.splice(0, 1);

		formConfig.entries[0].readonly = true;
		formConfig.entries[0].disabled = true;

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'editRecipe',
			label: 'Edit Recipe',
			data: recipe,
			actions: [
				{
					type: 'submit',
					label: 'Submit',
					btn: 'primary',
					action: function (formData) {
						overlayLoading.show();
						getSendDataFromServer($scope, ngDataApi, {
							method: 'put',
							routeName: '/dashboard/ci/recipe',
							params: {
								id: recipe._id
							},
							data: {
								provider: recipe.provider,
								name: formData.name,
								recipe: formData.recipe
							}
						}, function (error, response) {
							overlayLoading.hide();
							if (error) {
								$scope.form.displayAlert('danger', error.message);
							}
							else {
								$scope.displayAlert('success', 'Recipe updated successfully');
								$scope.modalInstance.close();
								$scope.form.formData = {};
								$scope.listUniqueProviders();
							}
						});
					}
				},
				{
					type: 'reset',
					label: 'Cancel',
					btn: 'danger',
					action: function () {
						$scope.modalInstance.dismiss('cancel');
						$scope.form.formData = {};
					}
				}
			]
		};

		buildFormWithModal($scope, $modal, options);
	};

	$scope.deleteRecipe = function (recipe) {
		var params = {
			id: recipe._id
		};

		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'delete',
			routeName: '/dashboard/ci/recipe',
			params: params
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.displayAlert('success', 'Recipe deleted successfully');
				$scope.listUniqueProviders();
			}
		});
	};

	$scope.filterData = function (provider, query) {
		if(!$scope.originalRecipes){
			$scope.originalRecipes = angular.copy($scope.providers[provider]);
		}

		if (query && query !== "") {
			query = query.toLowerCase();
			var filtered = [];
			for (var i = 0; i < $scope.providers[provider].length; i++) {
				if ($scope.providers[provider][i].name.toLowerCase().indexOf(query) !== -1) {
					filtered.push($scope.providers[provider][i]);
				}
			}
			$scope.providers[provider] = filtered;
		} else {
			$scope.providers[provider] = $scope.originalRecipes;
			delete $scope.originalRecipes;
		}
	};

	injectFiles.injectCss("modules/dashboard/ci/ci.css");

	//start here
	if ($scope.access.get) {
		$scope.listAccounts();
	}

}]);
<commit_msg>minor fix<commit_after>'use strict';

var ciApp = soajsApp.components;
ciApp.controller('ciAppCtrl', ['$scope', '$timeout', '$modal', '$cookies', 'ngDataApi', 'injectFiles', function ($scope, $timeout, $modal, $cookies, ngDataApi, injectFiles) {
	$scope.$parent.isUserLoggedIn();

	$scope.access = {};
	constructModulePermissions($scope, $scope.access, ciAppConfig.permissions);

	$scope.ciData = {};

	$scope.images = {
		travis: "./themes/" + themeToUse + "/img/travis_logo.png",
		drone: "./themes/" + themeToUse + "/img/drone_logo.png",
		jenkins: "./themes/" + themeToUse + "/img/jenkins_logo.png",
		teamcity: "./themes/" + themeToUse + "/img/teamcity_logo.png"
	};

	$scope.unsupported = ['jenkins', 'teamcity'];

	$scope.listAccounts = function () {
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			'method': 'get',
			'routeName': '/dashboard/ci'
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			} else {
				$scope.accounts = [];
				var processed = [];
				response.forEach(function(oneEntry){
					if(oneEntry.owner && oneEntry.gitProvider && processed.indexOf(oneEntry.owner + oneEntry.gitProvider) === -1){
						var oneAccount = {
							owner: oneEntry.owner,
							hide: true,
							icon: 'plus',
							providers: [],
							gitProvider: oneEntry.gitProvider
						};

						response.forEach(function(oneEntryAgain){
							if(oneEntryAgain.owner === oneAccount.owner && $scope.images[oneEntryAgain.provider]){
								oneEntryAgain.icon = $scope.images[oneEntryAgain.provider];
								oneEntryAgain.locked = ($scope.unsupported.indexOf(oneEntryAgain.provider) !== -1);
								oneAccount.providers.push(oneEntryAgain);
							}
						});

						$scope.accounts.push(oneAccount);
						processed.push(oneEntry.owner + oneEntry.gitProvider);
					}
				});

				$scope.accounts.forEach(function(oneAccount){
					var logos = angular.copy($scope.images);
					oneAccount.providers.forEach(function(oneProvider){
						if(logos[oneProvider.provider]){
							delete logos[oneProvider.provider];
						}
					});
					for(var logo in logos){
						var nEntry = {
							provider: logo,
							icon: logos[logo]
						};
						nEntry.locked = ($scope.unsupported.indexOf(logo) !== -1);

						oneAccount.providers.push(nEntry);
					}
				});

				$scope.accounts[0].hide = false;
				$scope.accounts[0].icon = 'minus';
			}
		});
	};

	$scope.deactivateAccount = function(provider){
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			'method': 'put',
			'routeName': '/dashboard/ci/provider',
			'data':{
				'owner': provider.owner,
				'provider': provider.provider
			}
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			} else {
				$scope.displayAlert('success', "Provider has been deactivated");
				$scope.listAccounts();
			}
		});
	};

	$scope.activateAccount = function(owner, provider){
		var formConfig;

		switch(provider.provider){
			case 'travis':
				formConfig = angular.copy(ciAppConfig.form.f1.travis);
				formConfig.entries[0].value = 'api.travis-ci.org';
				break;
			case 'drone':
				formConfig = angular.copy(ciAppConfig.form.f1.drone);
				formConfig.entries[0].value = '';
				break;
		}

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'continuousIntegration',
			label: 'Integrating with ' + provider.provider,
			actions: [
				{
					type: 'submit',
					label: "Submit",
					btn: 'success',
					action: function (formData) {
						var data = {
							"domain": formData.domain,
							"gitToken": formData.gitToken,
							"owner": owner,
							"provider": provider.provider
						};
						//test if protocol is supplied
						var regex = /^[^:]+(?=:\/\/)/;
						if (regex.test(formData.domain) && formData.domain.match(regex)[0] !== 'http' && formData.domain.match(regex)[0] !== 'https') {
							$scope.form.displayAlert('danger', "Invalid domain name provided!");
						}
						else {
							overlayLoading.show();
							getSendDataFromServer($scope, ngDataApi, {
								method: 'post',
								routeName: '/dashboard/ci/provider',
								data: data
							}, function (error, response) {
								overlayLoading.hide();
								if (error) {
									$scope.form.displayAlert('danger', error.message);
								}
								else {
									$scope.displayAlert('success', 'Provider has been integrated');
									$scope.form.formData = {};
									if ($scope.modalInstance) {
										$scope.modalInstance.close();
									}
									$scope.listAccounts();
								}
							});
						}
					}
				},
				{
					type: "reset",
					label: "Cancel",
					btn: "danger",
					action: function(){
						$scope.form.formData = {};
						$scope.modalInstance.close();
					}
				}
			]
		};
		buildFormWithModal($scope, $modal, options);
	};

	$scope.updateAccount = function(provider){
		var formConfig;

		switch(provider.provider){
			case 'travis':
				formConfig = angular.copy(ciAppConfig.form.f1.travis);
				formConfig.entries[0].value = 'api.travis-ci.org';
				break;
			case 'drone':
				formConfig = angular.copy(ciAppConfig.form.f1.drone);
				formConfig.entries[0].value = '';
				break;
		}

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'continuousIntegration',
			label: 'Updating Integrating with ' + provider.provider,
			data: provider,
			actions: [
				{
					type: 'submit',
					label: "Submit",
					btn: 'success',
					action: function (formData) {
						var data = {
							"id": provider._id,
							"domain": formData.domain,
							"gitToken": formData.gitToken,
							"owner": provider.owner,
							"provider": provider.provider
						};
						//test if protocol is supplied
						var regex = /^[^:]+(?=:\/\/)/;
						if (regex.test(formData.domain) && formData.domain.match(regex)[0] !== 'http' && formData.domain.match(regex)[0] !== 'https') {
							$scope.form.displayAlert('danger', "Invalid domain name provided!");
						}
					    else {
							overlayLoading.show();
							getSendDataFromServer($scope, ngDataApi, {
								method: 'post',
								routeName: '/dashboard/ci/provider',
								data: data
							}, function (error, response) {
								overlayLoading.hide();
								if (error) {
									$scope.form.displayAlert('danger', error.message);
								}
								else {
									$scope.displayAlert('success', 'Provider integration has been updated');
									$scope.form.formData = {};
									if ($scope.modalInstance) {
										$scope.modalInstance.close();
									}
									$scope.listAccounts();

								}
							});
						}

					}
				},
				{
					type: "reset",
					label: "Cancel",
					btn: "danger",
					action: function(){
						$scope.form.formData = {};
						$scope.modalInstance.close();
					}
				}
			]
		};
		buildFormWithModal($scope, $modal, options);
	};

	$scope.showHide = function (account) {
		if (!account.hide) {
			jQuery('#a_' + account.owner + " .body .inner").slideUp();
			account.icon = 'plus';
			account.hide = true;
		}
		else {
			jQuery('#a_' + account.owner + " .body .inner").slideDown();
			account.icon = 'minus';
			account.hide = false;
		}
	};

	$scope.listUniqueProviders = function(){
		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			'method': 'get',
			'routeName': '/dashboard/ci/providers'
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			} else {
				$scope.providers = angular.copy(response);
				delete $scope.providers.soajsauth;
			}
		});
	};

	$scope.addRecipe = function (provider, type) {
		var formConfig = angular.copy(ciAppConfig.form.f2);

		if (type === 'blank') {
			formConfig.entries.splice(0, 1);
		}
		else{
			$scope.providers[provider].forEach(function (oneRecipe) {
				var label = oneRecipe.name;
				formConfig.entries[0].value.push({ l: label, v: oneRecipe });
			});

			formConfig.entries[0].onAction = function (id, data, form) {
				var recipeTemplate = JSON.parse(data);
				delete recipeTemplate._id;
				delete recipeTemplate.locked;
				form.formData.name = recipeTemplate.name;
				form.formData.recipe = recipeTemplate.recipe;
			};
		}

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'addRecipe',
			label: 'Add New Recipe',
			actions: [
				{
					type: 'submit',
					label: 'Submit',
					btn: 'primary',
					action: function (formData) {
						overlayLoading.show();
						getSendDataFromServer($scope, ngDataApi, {
							method: 'post',
							routeName: '/dashboard/ci/recipe',
							data: {
								provider: provider,
								name: formData.name,
								recipe: formData.recipe
							}
						}, function (error, response) {
							overlayLoading.hide();
							if (error) {
								$scope.form.displayAlert('danger', error.message);
							}
							else {
								$scope.displayAlert('success', 'Recipe added successfully');
								$scope.modalInstance.close();
								$scope.form.formData = {};
								$scope.listUniqueProviders();
							}
						});
					}
				},
				{
					type: 'reset',
					label: 'Cancel',
					btn: 'danger',
					action: function () {
						$scope.modalInstance.dismiss('cancel');
						$scope.form.formData = {};
					}
				}
			]
		};

		buildFormWithModal($scope, $modal, options);
	};

	$scope.viewRecipe = function (recipe) {
		var formConfig = angular.copy(ciAppConfig.form.f2);
		formConfig.entries.splice(0,2);
		formConfig.entries[0].value = recipe.recipe;

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'viewRecipe',
			label: 'Viewing ' + recipe.name,
			actions: [
				{
					type: 'reset',
					label: 'Close',
					btn: 'primary',
					action: function () {
						$scope.modalInstance.dismiss('cancel');
						$scope.form.formData = {};
					}
				}
			]
		};

		buildFormWithModal($scope, $modal, options);
	};

	$scope.updateRecipe = function (recipe) {
		var formConfig = angular.copy(ciAppConfig.form.f2);
		formConfig.entries.splice(0, 1);

		formConfig.entries[0].readonly = true;
		formConfig.entries[0].disabled = true;

		var options = {
			timeout: $timeout,
			form: formConfig,
			name: 'editRecipe',
			label: 'Edit Recipe',
			data: recipe,
			actions: [
				{
					type: 'submit',
					label: 'Submit',
					btn: 'primary',
					action: function (formData) {
						overlayLoading.show();
						getSendDataFromServer($scope, ngDataApi, {
							method: 'put',
							routeName: '/dashboard/ci/recipe',
							params: {
								id: recipe._id
							},
							data: {
								provider: recipe.provider,
								name: formData.name,
								recipe: formData.recipe
							}
						}, function (error, response) {
							overlayLoading.hide();
							if (error) {
								$scope.form.displayAlert('danger', error.message);
							}
							else {
								$scope.displayAlert('success', 'Recipe updated successfully');
								$scope.modalInstance.close();
								$scope.form.formData = {};
								$scope.listUniqueProviders();
							}
						});
					}
				},
				{
					type: 'reset',
					label: 'Cancel',
					btn: 'danger',
					action: function () {
						$scope.modalInstance.dismiss('cancel');
						$scope.form.formData = {};
					}
				}
			]
		};

		buildFormWithModal($scope, $modal, options);
	};

	$scope.deleteRecipe = function (recipe) {
		var params = {
			id: recipe._id
		};

		overlayLoading.show();
		getSendDataFromServer($scope, ngDataApi, {
			method: 'delete',
			routeName: '/dashboard/ci/recipe',
			params: params
		}, function (error, response) {
			overlayLoading.hide();
			if (error) {
				$scope.displayAlert('danger', error.message);
			}
			else {
				$scope.displayAlert('success', 'Recipe deleted successfully');
				$scope.listUniqueProviders();
			}
		});
	};

	$scope.filterData = function (provider, query) {
		if(!$scope.originalRecipes){
			$scope.originalRecipes = angular.copy($scope.providers[provider]);
		}

		if (query && query !== "") {
			query = query.toLowerCase();
			var filtered = [];
			for (var i = 0; i < $scope.providers[provider].length; i++) {
				if ($scope.providers[provider][i].name.toLowerCase().indexOf(query) !== -1) {
					filtered.push($scope.providers[provider][i]);
				}
			}
			$scope.providers[provider] = filtered;
		} else {
			$scope.providers[provider] = $scope.originalRecipes;
			delete $scope.originalRecipes;
		}
	};

	injectFiles.injectCss("modules/dashboard/ci/ci.css");

	//start here
	if ($scope.access.get) {
		$scope.listAccounts();
	}

}]);
<|endoftext|>


<commit_before>import addChannelInfo from "./ChannelBuilder";
import {expect} from "chai";
import large_data from "./last_96h.js"

const data = [
  {
    start_time: 0,
    stop_time: 10,
    group: "t1",
    expected_channel: 0,
    expected_offset: 0
  },
  {
    start_time: 5,
    stop_time: 15,
    group: "t1",
    expected_channel: 1,
    expected_offset: 1
  },
  {
    start_time: 10,
    stop_time: 16,
    group: "t1",
    expected_channel: 0,
    expected_offset: 0
  },
  {
    start_time: 0,
    stop_time: 15,
    group: "t1",
    expected_channel: 2,
    expected_offset: 2
  },
  {
    start_time: 0,
    stop_time: 10,
    group: "t2",
    expected_channel: 0,
    expected_offset: 3
  },
  {
    start_time: 5,
    stop_time: 16,
    group: "t2",
    expected_channel: 1,
    expected_offset: 4
  },
  {
    start_time: 11,
    stop_time: 15,
    group: "t2",
    expected_channel: 0,
    expected_offset: 3
  },
  {
    start_time: 0,
    stop_time: 15,
    group: "t3",
    expected_channel: 0,
    expected_offset: 5
  }
];

it("enriches data with channel info", () => {
  addChannelInfo(data);
  for (var d of data) {
    // console.log(d);
    expect(d).to.contain.all.keys("channel", "offset");
  }
});

it("enriches data with channel info", () => {
  addChannelInfo(data);
  for (var d of data) {
    expect(d.offset).equal(d.expected_offset);
    expect(d.channel).equal(d.expected_channel);
  }
});

it("enriches real data", () => {
  // console.log(large_data);
  addChannelInfo(large_data);
  console.log(large_data[large_data.length-1])
})<commit_msg>Fix missing large_data test<commit_after>import addChannelInfo from "./ChannelBuilder";
import { expect } from "chai";

const data = [
  {
    start_time: 0,
    stop_time: 10,
    group: "t1",
    expected_channel: 0,
    expected_offset: 0
  },
  {
    start_time: 5,
    stop_time: 15,
    group: "t1",
    expected_channel: 1,
    expected_offset: 1
  },
  {
    start_time: 10,
    stop_time: 16,
    group: "t1",
    expected_channel: 0,
    expected_offset: 0
  },
  {
    start_time: 0,
    stop_time: 15,
    group: "t1",
    expected_channel: 2,
    expected_offset: 2
  },
  {
    start_time: 0,
    stop_time: 10,
    group: "t2",
    expected_channel: 0,
    expected_offset: 3
  },
  {
    start_time: 5,
    stop_time: 16,
    group: "t2",
    expected_channel: 1,
    expected_offset: 4
  },
  {
    start_time: 11,
    stop_time: 15,
    group: "t2",
    expected_channel: 0,
    expected_offset: 3
  },
  {
    start_time: 0,
    stop_time: 15,
    group: "t3",
    expected_channel: 0,
    expected_offset: 5
  }
];

it("enriches data with channel info", () => {
  addChannelInfo(data);
  for (var d of data) {
    // console.log(d);
    expect(d).to.contain.all.keys("channel", "offset");
  }
});

it("enriches data with channel info", () => {
  addChannelInfo(data);
  for (var d of data) {
    expect(d.offset).equal(d.expected_offset);
    expect(d.channel).equal(d.expected_channel);
  }
});
<|endoftext|>


<commit_before>angular.module('cosmic.controllers', []);
angular.module('cosmic.directives', []);
angular.module('cosmic.services', []);

angular.module('cosmic', ['ionic', 'ngCordova', 'cosmic.controllers', 'cosmic.services','cosmic.directives'])

.run(function($ionicPlatform,$cordovaStatusbar,$localstorage,$animate,$state) {
	$animate.enabled(false);
	$ionicPlatform.ready(function() {
		if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
			cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
		}
		if (window.StatusBar) {
			StatusBar.styleLightContent();
		}
		if ($localstorage.get('showStatusBar','true') === 'false'){
			$cordovaStatusbar.hide();
		}
		ionic.Platform.setGrade('c'); // remove advanced css feature
		$ionicPlatform.registerBackButtonAction(function (event) {
			if ($state.current.name==='tab.playlists') {
				backAsHome.trigger(function(){
					console.log("Success: back as home");
				}, function(){
					console.log("Error: back as home");
				});
			} else {
				navigator.app.backHistory();
			}
		}, 100);
	});
})

.config(function($stateProvider, $urlRouterProvider) {
	$stateProvider
	.state('tab', {
		url: "/tab",
		abstract: true,
		controller: 'TabsCtrl',
		templateUrl: "templates/tabs.html",
	})

	.state('tab.playlists', {
		url: '/playlists',
		views: {
			'tab-playlists': {
				templateUrl: 'templates/playlists.html',
				controller: 'PlaylistsCtrl'
			}
		}
	})

	.state('tab.playlist-items', {
		url: '/playlists/user/:playlistId/:playlistName',
		views: {
			'tab-playlists': {
				templateUrl: 'templates/playlist-items.html',
				controller: 'PlaylistItemsCtrl'
			}
		}
	})

	.state('tab.playlist-special', {
		url: '/playlists/special/:playlistId/:playlistName',
		views: {
			'tab-playlists': {
				templateUrl: 'templates/playlist-items.html',
				controller: 'PlaylistSpecialCtrl'
			}
		}
	})

	.state('tab.artists', {
		url: '/artists',
		views: {
			'tab-artists': {
				templateUrl: 'templates/artists.html',
				controller: 'ArtistsCtrl'
			}
		}
	})

	.state('tab.titles', {
		url: '/artists/:artistId',
		views: {
			'tab-artists': {
				templateUrl: 'templates/titles.html',
				controller: 'TitlesCtrl'
			}
		}
	})

	.state('tab.settings', {
		url: '/settings',
		views: {
			'tab-settings': {
				templateUrl: 'templates/settings.html',
				controller: 'SettingsCtrl'
			}
		}
	})

	.state('tab.manage-directories', {
		url: '/settings/manage-directories',
		views: {
			'tab-settings': {
				templateUrl: 'templates/manage-directories.html',
				controller: 'ManageDirectoriesCtrl'
			}
		}
	})

	.state('tab.search', {
		url: '/search',
		views: {
			'tab-search': {
				templateUrl: 'templates/search.html',
				controller: 'SearchCtrl'
			}
		}
	})

	.state('player', {
		url: '/player',
		templateUrl: 'templates/player.html',
		controller: 'PlayerCtrl'
	})

	.state('browse', {
    url: '/browse',
    templateUrl: 'templates/browse.html',
    controller: 'browseCtrl'
  })

.state('searchCloud', {
    url: '/searchcloud',
    templateUrl: 'templates/searchC.html',
    controller: 'searchCloudCtrl'
  })

.state('share', {
    url: '/share',
    templateUrl: 'templates/share.html',
    controller: 'shareCtrl'
  });

	// if none of the above states are matched, use this as the fallback
	$urlRouterProvider.otherwise('/tab/playlists');

})
.config(['$ionicConfigProvider', function($ionicConfigProvider) {
	//$ionicConfigProvider.scrolling.jsScrolling(false); // native scrolling
	$ionicConfigProvider.tabs.position('bottom'); // other values: top
	$ionicConfigProvider.tabs.style('standard');
	$ionicConfigProvider.spinner.icon('ios');
	$ionicConfigProvider.views.transition('ios');
	$ionicConfigProvider.views.swipeBackEnabled(true);
	$ionicConfigProvider.views.swipeBackHitWidth(60);
	ionic.Platform.isFullScreen = true;
}]);

function bootstrapAngular(){
	console.log('Bootstrap Angular App');
	var domElement = document.querySelector('body');
	angular.bootstrap(domElement, ['cosmic']);
}

if (document.URL.indexOf( 'http://' ) === -1 && document.URL.indexOf( 'https://' ) === -1) {
	console.log("URL: Running in Cordova/PhoneGap");
	document.addEventListener("deviceready", bootstrapAngular, false);
} else {
	console.log("URL: Running in browser");
	document.addEventListener("DOMContentLoaded", bootstrapAngular, false);
}
<commit_msg>Update app.js<commit_after>angular.module('cosmic.controllers', []);
angular.module('cosmic.directives', []);
angular.module('cosmic.services', []);

angular.module('cosmic', ['ionic', 'ngCordova', 'cosmic.controllers', 'cosmic.services','cosmic.directives'])

.run(function($ionicPlatform,$cordovaStatusbar,$localstorage,$animate,$state) {
	$animate.enabled(false);
	$ionicPlatform.ready(function() {
		if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
			cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
		}
		if (window.StatusBar) {
			StatusBar.styleLightContent();
		}
		if ($localstorage.get('showStatusBar','true') === 'false'){
			$cordovaStatusbar.hide();
		}
		ionic.Platform.setGrade('c'); // remove advanced css feature
		$ionicPlatform.registerBackButtonAction(function (event) {
			if ($state.current.name==='tab.playlists') {
				backAsHome.trigger(function(){
					console.log("Success: back as home");
				}, function(){
					console.log("Error: back as home");
				});
			} else {
				navigator.app.backHistory();
			}
		}, 100);
	});
})

.config(function($stateProvider, $urlRouterProvider) {
	$stateProvider
	.state('tab', {
		url: "/tab",
		abstract: true,
		templateUrl: "templates/tabs.html",
	})

	.state('tab.playlists', {
		url: '/playlists',
		views: {
			'tab-playlists': {
				templateUrl: 'templates/playlists.html',
				controller: 'PlaylistsCtrl'
			}
		}
	})

	.state('tab.playlist-items', {
		url: '/playlists/user/:playlistId/:playlistName',
		views: {
			'tab-playlists': {
				templateUrl: 'templates/playlist-items.html',
				controller: 'PlaylistItemsCtrl'
			}
		}
	})

	.state('tab.playlist-special', {
		url: '/playlists/special/:playlistId/:playlistName',
		views: {
			'tab-playlists': {
				templateUrl: 'templates/playlist-items.html',
				controller: 'PlaylistSpecialCtrl'
			}
		}
	})

	.state('tab.artists', {
		url: '/artists',
		views: {
			'tab-artists': {
				templateUrl: 'templates/artists.html',
				controller: 'ArtistsCtrl'
			}
		}
	})

	.state('tab.titles', {
		url: '/artists/:artistId',
		views: {
			'tab-artists': {
				templateUrl: 'templates/titles.html',
				controller: 'TitlesCtrl'
			}
		}
	})

	.state('tab.settings', {
		url: '/settings',
		views: {
			'tab-settings': {
				templateUrl: 'templates/settings.html',
				controller: 'SettingsCtrl'
			}
		}
	})

	.state('tab.manage-directories', {
		url: '/settings/manage-directories',
		views: {
			'tab-settings': {
				templateUrl: 'templates/manage-directories.html',
				controller: 'ManageDirectoriesCtrl'
			}
		}
	})

	.state('tab.search', {
		url: '/search',
		views: {
			'tab-search': {
				templateUrl: 'templates/search.html',
				controller: 'SearchCtrl'
			}
		}
	})

	.state('player', {
		url: '/player',
		templateUrl: 'templates/player.html',
		controller: 'PlayerCtrl'
	})

	.state('browse', {
    url: '/browse',
    templateUrl: 'templates/browse.html',
    controller: 'browseCtrl'
  })

.state('searchCloud', {
    url: '/searchcloud',
    templateUrl: 'templates/searchC.html',
    controller: 'searchCloudCtrl'
  })

.state('share', {
    url: '/share',
    templateUrl: 'templates/share.html',
    controller: 'shareCtrl'
  });

	// if none of the above states are matched, use this as the fallback
	$urlRouterProvider.otherwise('/tab/playlists');

})
.config(['$ionicConfigProvider', function($ionicConfigProvider) {
	//$ionicConfigProvider.scrolling.jsScrolling(false); // native scrolling
	$ionicConfigProvider.tabs.position('bottom'); // other values: top
	$ionicConfigProvider.tabs.style('standard');
	$ionicConfigProvider.spinner.icon('ios');
	$ionicConfigProvider.views.transition('ios');
	$ionicConfigProvider.views.swipeBackEnabled(true);
	$ionicConfigProvider.views.swipeBackHitWidth(60);
	ionic.Platform.isFullScreen = true;
}]);

function bootstrapAngular(){
	console.log('Bootstrap Angular App');
	var domElement = document.querySelector('body');
	angular.bootstrap(domElement, ['cosmic']);
}

if (document.URL.indexOf( 'http://' ) === -1 && document.URL.indexOf( 'https://' ) === -1) {
	console.log("URL: Running in Cordova/PhoneGap");
	document.addEventListener("deviceready", bootstrapAngular, false);
} else {
	console.log("URL: Running in browser");
	document.addEventListener("DOMContentLoaded", bootstrapAngular, false);
}
<|endoftext|>


<commit_before>/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */

/*************************************************************
 *
 *  MathJax/extensions/TeX/newcommand.js
 *
 *  Implements the \newcommand, \newenvironment and \def
 *  macros, and is loaded automatically when needed.
 *
 *  ---------------------------------------------------------------------
 *
 *  Copyright (c) 2009-2015 The MathJax Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

MathJax.Extension["TeX/newcommand"] = {
  version: "2.6.0"
};

MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {

  var TEX = MathJax.InputJax.TeX;
  var TEXDEF = TEX.Definitions;

  TEXDEF.Add({
    macros: {
      newcommand:       'NewCommand',
      renewcommand:     'NewCommand',
      newenvironment:   'NewEnvironment',
      renewenvironment: 'NewEnvironment',
      def:              'MacroDef',
      let:              'Let'
    }
  },null,true);

  TEX.Parse.Augment({

    /*
     *  Implement \newcommand{\name}[n][default]{...}
     */
    NewCommand: function (name) {
      var CS = this.GetArgument(name), cs = this.trimSpaces(CS),
          n  = this.GetBrackets(name),
          opt = this.GetBrackets(name),
          def = this.GetArgument(name);
      if (cs.charAt(0) === "\\") {cs = cs.substr(1)}
      if (cs === "" && CS.substr(CS.length-1,1) === " ") {cs += " "}
      if (!cs.match(/^(.|[a-z]+)$/i)) {
        TEX.Error(["IllegalControlSequenceName",
                   "Illegal control sequence name for %1",name]);
      }
      if (n) {
        n = this.trimSpaces(n);
        if (!n.match(/^[0-9]+$/)) {
          TEX.Error(["IllegalParamNumber",
                     "Illegal number of parameters specified in %1",name]);
        }
      }
      this.setDef(cs,['Macro',def,n,opt]);
    },

    /*
     *  Implement \newenvironment{name}[n][default]{begincmd}{endcmd}
     */
    NewEnvironment: function (name) {
      var env  = this.trimSpaces(this.GetArgument(name)),
          n    = this.GetBrackets(name),
          opt  = this.GetBrackets(name),
          bdef = this.GetArgument(name),
          edef = this.GetArgument(name);
      if (n) {
        n = this.trimSpaces(n);
        if (!n.match(/^[0-9]+$/)) {
          TEX.Error(["IllegalParamNumber",
                     "Illegal number of parameters specified in %1",name]);
        }
      }
      this.setEnv(env,['BeginEnv',[null,'EndEnv'],bdef,edef,n,opt]);
    },

    /*
     *  Implement \def command
     */
    MacroDef: function (name) {
      var cs     = this.GetCSname(name),
          params = this.GetTemplate(name,"\\"+cs),
          def    = this.GetArgument(name);
      if (!(params instanceof Array)) {this.setDef(cs,['Macro',def,params])}
        else {this.setDef(cs,['MacroWithTemplate',def].concat(params))}
    },

    /*
     *  Implements the \let command
     */
    Let: function (name) {
      var cs = this.GetCSname(name), macro;
      var c = this.GetNext(); if (c === "=") {this.i++; c = this.GetNext()}
      //
      //  All \let commands create entries in the macros array, but we
      //  have to look in the various mathchar and delimiter arrays if
      //  the source isn't a macro already, and attach the data to a
      //  macro with the proper routine to process it.
      //
      //  A command of the form \let\cs=char produces a macro equivalent
      //  to \def\cs{char}, which is as close as MathJax can get for this.
      //  So \let\bgroup={ is possible, but doesn't work as it does in TeX.
      //
      if (c === "\\") {
        name = this.GetCSname(name);
        macro = this.csFindMacro(name);
        if (!macro) {
          if (TEXDEF.mathchar0mi[name])            {macro = ["csMathchar0mi",TEXDEF.mathchar0mi[name]]}  else
          if (TEXDEF.mathchar0mo[name])            {macro = ["csMathchar0mo",TEXDEF.mathchar0mo[name]]}  else
          if (TEXDEF.mathchar7[name])              {macro = ["csMathchar7",TEXDEF.mathchar7[name]]}      else
          if (TEXDEF.delimiter["\\"+name] != null) {macro = ["csDelimiter",TEXDEF.delimiter["\\"+name]]} else
          return;
        }
      } else {macro = ["Macro",c]; this.i++}
      this.setDef(cs,macro);
    },

    /*
     *  Routines to set the macro and environment definitions
     *  (overridden by begingroup to make localized versions)
     */
    setDef: function (name,value) {value.isUser = true; TEXDEF.macros[name] = value},
    setEnv: function (name,value) {value.isUser = true; TEXDEF.environment[name] = value},

    /*
     *  Get a CS name or give an error
     */
    GetCSname: function (cmd) {
      var c = this.GetNext();
      if (c !== "\\") {
        TEX.Error(["MissingCS",
                   "%1 must be followed by a control sequence", cmd])
      }
      var cs = this.GetArgument(cmd), CS = this.trimSpaces(cs);
      if (CS == "\\" && cs.substr(cs.length-1,1) === " ") {CS += " "}
      return CS.substr(1);
    },

    /*
     *  Get a \def parameter template
     */
    GetTemplate: function (cmd,cs) {
      var c, params = [], n = 0;
      c = this.GetNext(); var i = this.i;
      while (this.i < this.string.length) {
        c = this.GetNext();
        if (c === '#') {
          if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
          c = this.string.charAt(++this.i);
          if (!c.match(/^[1-9]$/)) {
            TEX.Error(["CantUseHash2",
                       "Illegal use of # in template for %1",cs]);
          }
          if (parseInt(c) != ++n) {
            TEX.Error(["SequentialParam",
                       "Parameters for %1 must be numbered sequentially",cs]);
          }
          i = this.i+1;
        } else if (c === '{') {
          if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
          if (params.length > 0) {return [n,params]} else {return n}
        }
        this.i++;
      }
      TEX.Error(["MissingReplacementString",
                 "Missing replacement string for definition of %1",cmd]);
    },

    /*
     *  Process a macro with a parameter template
     */
    MacroWithTemplate: function (name,text,n,params) {
      if (n) {
        var args = []; this.GetNext();
        if (params[0] && !this.MatchParam(params[0])) {
          TEX.Error(["MismatchUseDef",
                     "Use of %1 doesn't match its definition",name]);
        }
        for (var i = 0; i < n; i++) {args.push(this.GetParameter(name,params[i+1]))}
        text = this.SubstituteArgs(args,text);
      }
      this.string = this.AddArgs(text,this.string.slice(this.i));
      this.i = 0;
      if (++this.macroCount > TEX.config.MAXMACROS) {
        TEX.Error(["MaxMacroSub1",
                   "MathJax maximum macro substitution count exceeded; " +
                   "is there a recursive macro call?"]);
      }
    },

    /*
     *  Process a user-defined environment
     */
    BeginEnv: function (begin,bdef,edef,n,def) {
      if (n) {
        var args = [];
        if (def != null) {
          var optional = this.GetBrackets("\\begin{"+name+"}");
          args.push(optional == null ? def : optional);
        }
        for (var i = args.length; i < n; i++) {args.push(this.GetArgument("\\begin{"+name+"}"))}
        bdef = this.SubstituteArgs(args,bdef);
        edef = this.SubstituteArgs([],edef); // no args, but get errors for #n in edef
      }
      this.string = this.AddArgs(bdef,this.string.slice(this.i)); this.i = 0;
      return begin;
    },
    EndEnv: function (begin,bdef,edef,n) {
      var end = "\\end{\\end\\"+begin.name+"}"; // special version of \end for after edef
      this.string = this.AddArgs(edef,end+this.string.slice(this.i)); this.i = 0;
      return null;
    },

    /*
     *  Find a single parameter delimited by a trailing template
     */
    GetParameter: function (name,param) {
      if (param == null) {return this.GetArgument(name)}
      var i = this.i, j = 0, hasBraces = 0;
      while (this.i < this.string.length) {
        if (this.string.charAt(this.i) === '{') {
          if (this.i === i) {hasBraces = 1}
          this.GetArgument(name); j = this.i - i;
        } else if (this.MatchParam(param)) {
          if (hasBraces) {i++; j -= 2}
          return this.string.substr(i,j);
        } else {
          this.i++; j++; hasBraces = 0;
        }
      }
      TEX.Error(["RunawayArgument","Runaway argument for %1?",name]);
    },

    /*
     *  Check if a template is at the current location.
     *  (The match must be exact, with no spacing differences.  TeX is
     *   a little more forgiving than this about spaces after macro names)
     */
    MatchParam: function (param) {
      if (this.string.substr(this.i,param.length) !== param) {return 0}
      this.i += param.length;
      return 1;
    }

  });

  TEX.Environment = function (name) {
    TEXDEF.environment[name] = ['BeginEnv',[null,'EndEnv']].concat([].slice.call(arguments,1));
    TEXDEF.environment[name].isUser = true;
  }

  MathJax.Hub.Startup.signal.Post("TeX newcommand Ready");

});

MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/newcommand.js");
<commit_msg>Treat control sequences as a unit when matching a macro template.  Resolves both issues for #1568.<commit_after>/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */

/*************************************************************
 *
 *  MathJax/extensions/TeX/newcommand.js
 *
 *  Implements the \newcommand, \newenvironment and \def
 *  macros, and is loaded automatically when needed.
 *
 *  ---------------------------------------------------------------------
 *
 *  Copyright (c) 2009-2015 The MathJax Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

MathJax.Extension["TeX/newcommand"] = {
  version: "2.6.0"
};

MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {

  var TEX = MathJax.InputJax.TeX;
  var TEXDEF = TEX.Definitions;

  TEXDEF.Add({
    macros: {
      newcommand:       'NewCommand',
      renewcommand:     'NewCommand',
      newenvironment:   'NewEnvironment',
      renewenvironment: 'NewEnvironment',
      def:              'MacroDef',
      let:              'Let'
    }
  },null,true);

  TEX.Parse.Augment({

    /*
     *  Implement \newcommand{\name}[n][default]{...}
     */
    NewCommand: function (name) {
      var CS = this.GetArgument(name), cs = this.trimSpaces(CS),
          n  = this.GetBrackets(name),
          opt = this.GetBrackets(name),
          def = this.GetArgument(name);
      if (cs.charAt(0) === "\\") {cs = cs.substr(1)}
      if (cs === "" && CS.substr(CS.length-1,1) === " ") {cs += " "}
      if (!cs.match(/^(.|[a-z]+)$/i)) {
        TEX.Error(["IllegalControlSequenceName",
                   "Illegal control sequence name for %1",name]);
      }
      if (n) {
        n = this.trimSpaces(n);
        if (!n.match(/^[0-9]+$/)) {
          TEX.Error(["IllegalParamNumber",
                     "Illegal number of parameters specified in %1",name]);
        }
      }
      this.setDef(cs,['Macro',def,n,opt]);
    },

    /*
     *  Implement \newenvironment{name}[n][default]{begincmd}{endcmd}
     */
    NewEnvironment: function (name) {
      var env  = this.trimSpaces(this.GetArgument(name)),
          n    = this.GetBrackets(name),
          opt  = this.GetBrackets(name),
          bdef = this.GetArgument(name),
          edef = this.GetArgument(name);
      if (n) {
        n = this.trimSpaces(n);
        if (!n.match(/^[0-9]+$/)) {
          TEX.Error(["IllegalParamNumber",
                     "Illegal number of parameters specified in %1",name]);
        }
      }
      this.setEnv(env,['BeginEnv',[null,'EndEnv'],bdef,edef,n,opt]);
    },

    /*
     *  Implement \def command
     */
    MacroDef: function (name) {
      var cs     = this.GetCSname(name),
          params = this.GetTemplate(name,"\\"+cs),
          def    = this.GetArgument(name);
      if (!(params instanceof Array)) {this.setDef(cs,['Macro',def,params])}
        else {this.setDef(cs,['MacroWithTemplate',def].concat(params))}
    },

    /*
     *  Implements the \let command
     */
    Let: function (name) {
      var cs = this.GetCSname(name), macro;
      var c = this.GetNext(); if (c === "=") {this.i++; c = this.GetNext()}
      //
      //  All \let commands create entries in the macros array, but we
      //  have to look in the various mathchar and delimiter arrays if
      //  the source isn't a macro already, and attach the data to a
      //  macro with the proper routine to process it.
      //
      //  A command of the form \let\cs=char produces a macro equivalent
      //  to \def\cs{char}, which is as close as MathJax can get for this.
      //  So \let\bgroup={ is possible, but doesn't work as it does in TeX.
      //
      if (c === "\\") {
        name = this.GetCSname(name);
        macro = this.csFindMacro(name);
        if (!macro) {
          if (TEXDEF.mathchar0mi[name])            {macro = ["csMathchar0mi",TEXDEF.mathchar0mi[name]]}  else
          if (TEXDEF.mathchar0mo[name])            {macro = ["csMathchar0mo",TEXDEF.mathchar0mo[name]]}  else
          if (TEXDEF.mathchar7[name])              {macro = ["csMathchar7",TEXDEF.mathchar7[name]]}      else
          if (TEXDEF.delimiter["\\"+name] != null) {macro = ["csDelimiter",TEXDEF.delimiter["\\"+name]]} else
          return;
        }
      } else {macro = ["Macro",c]; this.i++}
      this.setDef(cs,macro);
    },

    /*
     *  Routines to set the macro and environment definitions
     *  (overridden by begingroup to make localized versions)
     */
    setDef: function (name,value) {value.isUser = true; TEXDEF.macros[name] = value},
    setEnv: function (name,value) {value.isUser = true; TEXDEF.environment[name] = value},

    /*
     *  Get a CS name or give an error
     */
    GetCSname: function (cmd) {
      var c = this.GetNext();
      if (c !== "\\") {
        TEX.Error(["MissingCS",
                   "%1 must be followed by a control sequence", cmd])
      }
      var cs = this.GetArgument(cmd), CS = this.trimSpaces(cs);
      if (CS == "\\" && cs.substr(cs.length-1,1) === " ") {CS += " "}
      return CS.substr(1);
    },

    /*
     *  Get a \def parameter template
     */
    GetTemplate: function (cmd,cs) {
      var c, params = [], n = 0;
      c = this.GetNext(); var i = this.i;
      while (this.i < this.string.length) {
        c = this.GetNext();
        if (c === '#') {
          if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
          c = this.string.charAt(++this.i);
          if (!c.match(/^[1-9]$/)) {
            TEX.Error(["CantUseHash2",
                       "Illegal use of # in template for %1",cs]);
          }
          if (parseInt(c) != ++n) {
            TEX.Error(["SequentialParam",
                       "Parameters for %1 must be numbered sequentially",cs]);
          }
          i = this.i+1;
        } else if (c === '{') {
          if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
          if (params.length > 0) {return [n,params]} else {return n}
        }
        this.i++;
      }
      TEX.Error(["MissingReplacementString",
                 "Missing replacement string for definition of %1",cmd]);
    },

    /*
     *  Process a macro with a parameter template
     */
    MacroWithTemplate: function (name,text,n,params) {
      if (n) {
        var args = []; this.GetNext();
        if (params[0] && !this.MatchParam(params[0])) {
          TEX.Error(["MismatchUseDef",
                     "Use of %1 doesn't match its definition",name]);
        }
        for (var i = 0; i < n; i++) {args.push(this.GetParameter(name,params[i+1]))}
        text = this.SubstituteArgs(args,text);
      }
      this.string = this.AddArgs(text,this.string.slice(this.i));
      this.i = 0;
      if (++this.macroCount > TEX.config.MAXMACROS) {
        TEX.Error(["MaxMacroSub1",
                   "MathJax maximum macro substitution count exceeded; " +
                   "is there a recursive macro call?"]);
      }
    },

    /*
     *  Process a user-defined environment
     */
    BeginEnv: function (begin,bdef,edef,n,def) {
      if (n) {
        var args = [];
        if (def != null) {
          var optional = this.GetBrackets("\\begin{"+name+"}");
          args.push(optional == null ? def : optional);
        }
        for (var i = args.length; i < n; i++) {args.push(this.GetArgument("\\begin{"+name+"}"))}
        bdef = this.SubstituteArgs(args,bdef);
        edef = this.SubstituteArgs([],edef); // no args, but get errors for #n in edef
      }
      this.string = this.AddArgs(bdef,this.string.slice(this.i)); this.i = 0;
      return begin;
    },
    EndEnv: function (begin,bdef,edef,n) {
      var end = "\\end{\\end\\"+begin.name+"}"; // special version of \end for after edef
      this.string = this.AddArgs(edef,end+this.string.slice(this.i)); this.i = 0;
      return null;
    },

    /*
     *  Find a single parameter delimited by a trailing template
     */
    GetParameter: function (name,param) {
      if (param == null) {return this.GetArgument(name)}
      var i = this.i, j = 0, hasBraces = 0;
      while (this.i < this.string.length) {
        var c = this.string.charAt(this.i);
        if (c === '{') {
          if (this.i === i) {hasBraces = 1}
          this.GetArgument(name); j = this.i - i;
        } else if (this.MatchParam(param)) {
          if (hasBraces) {i++; j -= 2}
          return this.string.substr(i,j);
	} else if (c === "\\") {
	  this.i++; j++; hasBraces = 0;
	  var match = this.string.substr(this.i).match(/[a-z]+|./i);
	  if (match) {this.i += match[0].length; j = this.i - i}
        } else {
          this.i++; j++; hasBraces = 0;
        }
      }
      TEX.Error(["RunawayArgument","Runaway argument for %1?",name]);
    },

    /*
     *  Check if a template is at the current location.
     *  (The match must be exact, with no spacing differences.  TeX is
     *   a little more forgiving than this about spaces after macro names)
     */
    MatchParam: function (param) {
      if (this.string.substr(this.i,param.length) !== param) {return 0}
      if (param.match(/\\[a-z]+/i) &&
          this.string.charAt(this.i+param.length).match(/[a-z]/i)) {return 0}
      this.i += param.length;
      return 1;
    }

  });

  TEX.Environment = function (name) {
    TEXDEF.environment[name] = ['BeginEnv',[null,'EndEnv']].concat([].slice.call(arguments,1));
    TEXDEF.environment[name].isUser = true;
  }

  MathJax.Hub.Startup.signal.Post("TeX newcommand Ready");

});

MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/newcommand.js");
<|endoftext|>


<commit_before>/**
 * A simple progressbar with synchronous calls to tick()
 * (i.e. no setTimeout/setInterval)
 */

var ProgressBar = require('../');

var len = 10000000; // Adjust to your machine's speed
var bar = new ProgressBar('[:bar]', len);

for (var i = 0; i <= len; i++) {
  bar.tick();
}<commit_msg>Set renderThrottle in synch example to 100 ms<commit_after>/**
 * A simple progressbar with synchronous calls to tick()
 * (i.e. no setTimeout/setInterval)
 */

var ProgressBar = require('../');

var len = 10000000; // Adjust to your machine's speed
var bar = new ProgressBar('[:bar]', {total: len, renderThrottle: 100});

for (var i = 0; i <= len; i++) {
  bar.tick();
}<|endoftext|>


<commit_before>"use strict";

angular.module('wuw', ['ionic', 'wuw.controllers', 'wuw.services', 'wuw.directives', 'wuw.filters', 'angular.filter', 'pascalprecht.translate', 'wuw.czErrorMessage'])

.run(function($ionicPlatform, $state, $rootScope, Settings, Users) {

    var apiUrl = "http://dmz26.rz.hft-stuttgart.de:4342/api/v0";
    var versionNumber = "0.4.3";

    // predefined settings
    Settings.setSetting('version', versionNumber);
    Settings.setSetting('apiUrl', apiUrl);
    Settings.setSetting('cacheDeadline', 300);
    Settings.setSetting('cacheLectures', 300);
    Settings.setSetting('cacheMensa', 86400);

    $ionicPlatform.ready(function() {

        // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
        // for form inputs)
        if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
            cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
        }
        if (window.StatusBar) {
            // org.apache.cordova.statusbar required
            StatusBar.styleDefault();
        }

        if (window.cordova && typeof Settings.getSetting('uuid') === "undefined") {
            Settings.setSetting('uuid', device.uuid);
        }

        // ping home
        Users.ping();
    });
})

.config(function($stateProvider, $urlRouterProvider, $translateProvider, $ionicConfigProvider) {

    // enable native scrolling on Android
    if (ionic.Platform.isAndroid()) {
        $ionicConfigProvider.scrolling.jsScrolling(false);
    }

    // Ionic uses AngularUI Router which uses the concept of states
    // Learn more here: https://github.com/angular-ui/ui-router
    // Set up the various states which the app can be in.
    // Each state's controller can be found in controllers.js

    var otherwise = "/tab/home";
    if (typeof window.localStorage.wuw_selectedGroups === "undefined") {
        otherwise = "/setup";
    }

    $stateProvider
    // setup an abstract state for the tabs directive
    .state('tab', {
        url: "/tab",
        abstract: true,
        templateUrl: "templates/tabs.html",
        controller: function($scope, Settings) {
            $scope.lecturesUrl = Settings.getSetting("lecturesView") || "lecturesWeekly";
        }
    })

    // Each tab has its own nav history stack:
    .state('tab.home', {
        url: '/home',
        views: {
            'tab-home': {
            templateUrl: 'templates/tab-home.html',
            controller: 'HomeCtrl'
        }
      }
    })

    .state('tab.deadlines', {
        url: '/deadlines',
        views: {
            'tab-deadlines': {
            templateUrl: 'templates/tab-deadlines.html',
            controller: 'DeadlinesCtrl'
        }
      }
    })
    .state('tab.deadline-detail', {
        url: '/deadlines/:deadlineId',
        views: {
            'tab-deadlines': {
            templateUrl: 'templates/tab-deadline-detail.html',
            controller: 'DeadlinesDetailCtrl'
        }
      }
    })
    .state('tab.deadline-create', {
        url: '/createDeadline',
        views: {
            'tab-deadlines': {
            templateUrl: 'templates/tab-create-deadline.html',
            controller: 'DeadlinesCreateCtrl'
        }
      }
    })

    .state('tab.lecturesList', {
        url: '/lecturesList',
        views: {
            'tab-lectures': {
            templateUrl: 'templates/tab-lectures-list.html',
            controller: 'LecturesListCtrl'
        }
      }
    })

    .state('tab.lecturesWeekly', {
        url: '/lecturesWeekly',
        views: {
            'tab-lectures': {
            templateUrl: 'templates/tab-lectures-weekly.html',
            controller: 'LecturesWeeklyCtrl'
        }
      }
    })

    .state('tab.mensa', {
        url: '/mensa',
        views: {
            'tab-mensa': {
            templateUrl: 'templates/tab-mensa.html',
            controller: 'MensaCtrl'
        }
      }
    })

    .state('tab.settings', {
        url: '/settings',
        views: {
            'tab-settings': {
            templateUrl: 'templates/tab-settings.html',
            controller: 'SettingsCtrl'
        }
      }
    })

    .state('tab.news', {
        url: '/news',
        views: {
            'tab-news': {
            templateUrl: 'templates/news.html',
            controller: 'NewsCtrl'
        }
      }
    })

    .state('tab.events', {
        url: '/events',
        views: {
            'tab-events': {
            templateUrl: 'templates/events.html',
            controller: 'EventsCtrl'
        }
      }
    })

    .state('setup', {
        url: '/setup',
        templateUrl: 'templates/setup.html',
        controller: 'SetupCtrl'
    });

    // if none of the above states are matched, use this as the fallback
    $urlRouterProvider.otherwise(otherwise);

    // load translation
    $translateProvider.useStaticFilesLoader({
        prefix: 'lang/locale-',
        suffix: '.json'
    });
    $translateProvider.preferredLanguage('de_DE');
    $translateProvider.fallbackLanguage('en_US');
});
<commit_msg>tabs bottom on android<commit_after>"use strict";

angular.module('wuw', ['ionic', 'wuw.controllers', 'wuw.services', 'wuw.directives', 'wuw.filters', 'angular.filter', 'pascalprecht.translate', 'wuw.czErrorMessage'])

.run(function($ionicPlatform, $state, $rootScope, Settings, Users) {

    var apiUrl = "http://dmz26.rz.hft-stuttgart.de:4342/api/v0";
    var versionNumber = "0.4.3";

    // predefined settings
    Settings.setSetting('version', versionNumber);
    Settings.setSetting('apiUrl', apiUrl);
    Settings.setSetting('cacheDeadline', 300);
    Settings.setSetting('cacheLectures', 300);
    Settings.setSetting('cacheMensa', 86400);

    $ionicPlatform.ready(function() {

        // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
        // for form inputs)
        if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
            cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
        }
        if (window.StatusBar) {
            // org.apache.cordova.statusbar required
            StatusBar.styleDefault();
        }

        if (window.cordova && typeof Settings.getSetting('uuid') === "undefined") {
            Settings.setSetting('uuid', device.uuid);
        }

        // ping home
        Users.ping();
    });
})

.config(function($stateProvider, $urlRouterProvider, $translateProvider, $ionicConfigProvider) {

    // enable native scrolling on Android
    if (ionic.Platform.isAndroid()) {
        $ionicConfigProvider.scrolling.jsScrolling(false);
    }

    // tabs bottom
    $ionicConfigProvider.tabs.position('bottom');

    // Ionic uses AngularUI Router which uses the concept of states
    // Learn more here: https://github.com/angular-ui/ui-router
    // Set up the various states which the app can be in.
    // Each state's controller can be found in controllers.js

    var otherwise = "/tab/home";
    if (typeof window.localStorage.wuw_selectedGroups === "undefined") {
        otherwise = "/setup";
    }

    $stateProvider
    // setup an abstract state for the tabs directive
    .state('tab', {
        url: "/tab",
        abstract: true,
        templateUrl: "templates/tabs.html",
        controller: function($scope, Settings) {
            $scope.lecturesUrl = Settings.getSetting("lecturesView") || "lecturesWeekly";
        }
    })

    // Each tab has its own nav history stack:
    .state('tab.home', {
        url: '/home',
        views: {
            'tab-home': {
            templateUrl: 'templates/tab-home.html',
            controller: 'HomeCtrl'
        }
      }
    })

    .state('tab.deadlines', {
        url: '/deadlines',
        views: {
            'tab-deadlines': {
            templateUrl: 'templates/tab-deadlines.html',
            controller: 'DeadlinesCtrl'
        }
      }
    })
    .state('tab.deadline-detail', {
        url: '/deadlines/:deadlineId',
        views: {
            'tab-deadlines': {
            templateUrl: 'templates/tab-deadline-detail.html',
            controller: 'DeadlinesDetailCtrl'
        }
      }
    })
    .state('tab.deadline-create', {
        url: '/createDeadline',
        views: {
            'tab-deadlines': {
            templateUrl: 'templates/tab-create-deadline.html',
            controller: 'DeadlinesCreateCtrl'
        }
      }
    })

    .state('tab.lecturesList', {
        url: '/lecturesList',
        views: {
            'tab-lectures': {
            templateUrl: 'templates/tab-lectures-list.html',
            controller: 'LecturesListCtrl'
        }
      }
    })

    .state('tab.lecturesWeekly', {
        url: '/lecturesWeekly',
        views: {
            'tab-lectures': {
            templateUrl: 'templates/tab-lectures-weekly.html',
            controller: 'LecturesWeeklyCtrl'
        }
      }
    })

    .state('tab.mensa', {
        url: '/mensa',
        views: {
            'tab-mensa': {
            templateUrl: 'templates/tab-mensa.html',
            controller: 'MensaCtrl'
        }
      }
    })

    .state('tab.settings', {
        url: '/settings',
        views: {
            'tab-settings': {
            templateUrl: 'templates/tab-settings.html',
            controller: 'SettingsCtrl'
        }
      }
    })

    .state('tab.news', {
        url: '/news',
        views: {
            'tab-news': {
            templateUrl: 'templates/news.html',
            controller: 'NewsCtrl'
        }
      }
    })

    .state('tab.events', {
        url: '/events',
        views: {
            'tab-events': {
            templateUrl: 'templates/events.html',
            controller: 'EventsCtrl'
        }
      }
    })

    .state('setup', {
        url: '/setup',
        templateUrl: 'templates/setup.html',
        controller: 'SetupCtrl'
    });

    // if none of the above states are matched, use this as the fallback
    $urlRouterProvider.otherwise(otherwise);

    // load translation
    $translateProvider.useStaticFilesLoader({
        prefix: 'lang/locale-',
        suffix: '.json'
    });
    $translateProvider.preferredLanguage('de_DE');
    $translateProvider.fallbackLanguage('en_US');
});
<|endoftext|>