<commit_before>/**
    Navbar extension of the App,
    adds the elements to manage a view model
    for the NavBar and automatic changes
    under some model changes like user login/logout
**/
'use strict';

var ko = require('knockout'),
    $ = require('jquery'),
    NavBar = require('./viewmodels/NavBar'),
    NavAction = require('./viewmodels/NavAction');
var user = require('./data/userProfile').data;

// Components in use by the AppNav template
require('./kocomponents/smart-nav-bar-action');

exports.extend = function (app) {

    // REVIEW: still needed? Maybe the per activity navBar means
    // this is not needed. Some previous logic was already removed
    // because was useless.
    //
    // Adjust the navbar setup depending on current user,
    // since different things are need for logged-in/out.
    function adjustUserBar() {
        //jshint maxcomplexity:8

        if (user.isAnonymous()) {
            var prev = app.navBar().leftAction();
            if (prev !== NavAction.menuIn) {
                app.navBar().leftAction(NavAction.menuIn);
                app.navBar().prevLeftAction = prev;
            }
            var prevRight = app.navBar().rightAction();
            if (prevRight === NavAction.menuNewItem) {
                app.navBar().rightAction(null);
                app.navBar().prevRightAction = prevRight;
            }
        }
        else {
            if (app.navBar().prevLeftAction) {
                app.navBar().leftAction(app.navBar().prevLeftAction);
                app.navBar().prevLeftAction = null;
            }
            // If we are in a 'section' activity, that is with the menuNewItem in the rightAction,
            // we need to change it for just-client profiles:
            if (!user.isServiceProfessional()) {
                // Only if is menuNewItem, otherwise we do NOT restore preRightAction (thats why
                // the nested 'if' rather than 'professional AND menuNewItem').
                if (app.navBar().rightAction() === NavAction.menuNewItem) {
                    // Replace default right-menu for professionals by
                    // a client link to go marketplace
                    app.navBar().preRightAction = app.navBar().rightAction();
                    app.navBar().rightAction(NavAction.goMarketplace);
                }
            }
            else if (app.navBar().preRightAction) {
                // Recovering previous right action, like after change it to a just-client option
                // and then entering with service-professional profile
                app.navBar().rightAction(app.navBar().preRightAction);
                app.navBar().preRightAction = null;
            }
        }
    }

    app.navBar = ko.observable(null);

    var refreshNav = function refreshNav() {
        // Trigger event to force a component update
        $('.AppNav').trigger('contentChange');
    };
    var autoRefreshNav = function autoRefreshNav(action) {
        if (action) {
            action.text.subscribe(refreshNav);
            action.isTitle.subscribe(refreshNav);
            action.icon.subscribe(refreshNav);
            action.isMenu.subscribe(refreshNav);
        }
    };

    /**
        Update the nav model using the Activity defaults
        // TODO Look a way to replace all this complex navbar logic, maybe per
        // activity nabvars, more model helpers and global state helpers.
    **/
    app.updateAppNav = function updateAppNav(activity, state) {

        // if the activity has its own
        if ('navBar' in activity) {
            if (activity.navBar === null) {
                // Activity requires no menu, create a hidden NavBar instance
                app.navBar(new NavBar({
                    hidden: true
                }));
            }
            else {
                // Use specializied activity bar data
                app.navBar(activity.navBar);
            }
        }
        else {
            // Use default one
            app.navBar(new NavBar());
        }

        if (!app.applyNavbarMustReturn(state)) {
            // Changes depending on non-logged user
            adjustUserBar();
        }

        refreshNav();
        autoRefreshNav(app.navBar().leftAction());
        autoRefreshNav(app.navBar().rightAction());
    };

    /**
     * It describes the values of a return-to request
     * @typedef {Object} ReturnRequestInfo
     * @property {string} [link] Local URL where the request must return to
     * @property {bool} isGoBack It's true in case must return with a 'go back'
     * call; it discards the link value since it will just go to previous step,
     * and the link value is required if this is false.
     * @property {string} [label] Text to display in the 'return' link
     */

    /**
     * Looks for parameters in the navigation state requesting to
     * use a specific setting for the current activity 'back' button.
     * It returns an object with values properly processed describing
     * the desired place to go back, or null if no parameters are present.
     * @returns {ReturnRequestInfo} Null if nothing is requested.
     */
    app.getReturnRequestInfo = function(state) {
        var q = state && state.route && state.route.query;

        if (q.mustReturn) {
            var data = {
                link: null,
                label: null,
                isGoBack: false
            };

            // Link: auto goBack or explicit link
            var returnLink = decodeURIComponent(q.mustReturn);
            if (returnLink === '1' || returnLink === 'true') {
                data.isGoBack = true;
            }
            else {
                data.link = returnLink;
            }
            // A text can be provided
            data.label = decodeURIComponent(q.returnText || '');
            return data;
        }
        return null;
    };

    /**
     * Detects parameters requesting a specific 'go back' behavior
     * and updates the navbar to reflect that, or left untouched if
     * no parameters present.
     * Parameter that triggers the logic: state.route.query.mustReturn
     * Optional parameter: state.route.query.returnText
     * @returns {bool} True if parameters present and changes to navbar
     * were applied, false if untouched.
     */
    app.applyNavbarMustReturn = function(state) {
        var info = app.getReturnRequestInfo(state);
        if (info) {
            if (info.isGoBack) {
                // Left action forced to be a go-back
                app.navBar().leftAction(NavAction.goBack.model.clone({
                    text: info.label,
                    isShell: true,
                    isTitle: true
                }));
            }
            else {
                // Left action force to return to the given URL
                app.navBar().leftAction(NavAction.goBack.model.clone({
                    link: info.link,
                    text: info.label,
                    isShell: false,
                    isTitle: true
                }));
            }
            return true;
        }
        return false;
    };

    /**
        Update the app menu to highlight the
        given link name
    **/
    app.updateMenu = function updateMenu(/*name*/) {

        var $menu = $('.App-menus .navbar-collapse');

        /* DONE WITH KNOCKOUT BINDING RIGHT NOW
        // Remove any active
        $menu
        .find('li')
        .removeClass('active');
        // Add active
        $menu
        .find('.go-' + name)
        .closest('li')
        .addClass('active');
        */

        // Hide menu
        $menu
        .filter(':visible')
        .collapse('hide');
    };

    // Set model for the AppNav
    app.navBarBinding = {
        navBar: app.navBar,
        // Both: are later filled with a call to the model once loaded and ready
        photoUrl: ko.observable('about:blank'),
        userName: ko.observable('Me'),
        isServiceProfessional: ko.observable(false),
        isClient: ko.observable(false),
        isApp: ko.observable(!!window.cordova),
        isInOnboarding: ko.observable(false),
        isAtCurrentOnboardingStep: ko.observable(false),
        active: ko.observable('')
    };

    var onboarding = require('./data/onboarding');
    ko.computed(function() {
        app.navBarBinding.isInOnboarding(onboarding.inProgress());
        app.navBarBinding.isAtCurrentOnboardingStep(onboarding.isAtCurrentStep());
    });

    app.navBarBinding.continueOnboarding = function() {
        onboarding.goCurrentStep();
    };

    app.shell.on(app.shell.events.itemReady, function() {
        app.navBarBinding.active(app.shell.currentRoute.name);
    });

    app.navBarBinding.isAnonymous = ko.pureComputed(function() {
        return !this.isServiceProfessional() && !this.isClient();
    }, app.navBarBinding);

    app.navBarBinding.cssIfActive = function(activityName) {
        return ko.computed(function() {
            return activityName === app.navBarBinding.active() ? 'active' : '';
        });
    };

    app.setupNavBarBinding = function setupNavBarBinding() {
        app.navBarBinding.isApp(!!window.cordova);
        ko.applyBindings(app.navBarBinding, $('.AppNav').get(0));
    };

    /**
        Performs the 'back' task from the navbar link, if any.
        That is, trigger the left action.
        Fallback to shell goBack
    **/
    app.performsNavBarBack = function performsNavBarBack(options) {
        var nav = this.navBar(),
            left = nav && nav.leftAction(),
            $btn = $('body > .AppNav .SmartNavBar-edge.left > a.SmartNavBar-btn');

        // There is an action, trigger like a click so all the handlers
        // attached on spare places do their work:
        if (left && !left.isMenu()) {
            var event = $.Event('click');
            event.options = options || {};
            $btn.trigger(event);
        }
        else if (this.shell) {
            this.shell.goBack();
        }
    };

    /**
        It shows an unobtrusive notification on the navbar place, that
        hides after a short timeout
    **/
    var lastNotificationTimer = null;
    app.showNavBarNotification = function showNavBarNotification(settings) {
        var msg = settings && settings.message || 'Hello World!',
            duration = settings && settings.duration || 2000,
            transitionDuration = settings && settings.transitionDuration || 400,
            $el = $('.AppNav .SmartNavBar-notification');

        $el.text(msg);
        $el.fadeIn(transitionDuration)
        .queue(function() {

            // Manual hide on clicking
            $el
            .off('click.manualHide')
            .on('click.manualHide', function() {
                $el.fadeOut(transitionDuration);
            });

            // Auto hide after timeout
            clearTimeout(lastNotificationTimer);
            lastNotificationTimer = setTimeout(function() {
                $el.fadeOut(transitionDuration);
            }, duration);

            $(this).dequeue();
        });
    };
};
<commit_msg>Fixed bug with successSave 'auto go back' not working, after accessibility change in the AppNav #652<commit_after>/**
    Navbar extension of the App,
    adds the elements to manage a view model
    for the NavBar and automatic changes
    under some model changes like user login/logout
**/
'use strict';

var ko = require('knockout'),
    $ = require('jquery'),
    NavBar = require('./viewmodels/NavBar'),
    NavAction = require('./viewmodels/NavAction');
var user = require('./data/userProfile').data;

// Components in use by the AppNav template
require('./kocomponents/smart-nav-bar-action');

exports.extend = function (app) {

    // REVIEW: still needed? Maybe the per activity navBar means
    // this is not needed. Some previous logic was already removed
    // because was useless.
    //
    // Adjust the navbar setup depending on current user,
    // since different things are need for logged-in/out.
    function adjustUserBar() {
        //jshint maxcomplexity:8

        if (user.isAnonymous()) {
            var prev = app.navBar().leftAction();
            if (prev !== NavAction.menuIn) {
                app.navBar().leftAction(NavAction.menuIn);
                app.navBar().prevLeftAction = prev;
            }
            var prevRight = app.navBar().rightAction();
            if (prevRight === NavAction.menuNewItem) {
                app.navBar().rightAction(null);
                app.navBar().prevRightAction = prevRight;
            }
        }
        else {
            if (app.navBar().prevLeftAction) {
                app.navBar().leftAction(app.navBar().prevLeftAction);
                app.navBar().prevLeftAction = null;
            }
            // If we are in a 'section' activity, that is with the menuNewItem in the rightAction,
            // we need to change it for just-client profiles:
            if (!user.isServiceProfessional()) {
                // Only if is menuNewItem, otherwise we do NOT restore preRightAction (thats why
                // the nested 'if' rather than 'professional AND menuNewItem').
                if (app.navBar().rightAction() === NavAction.menuNewItem) {
                    // Replace default right-menu for professionals by
                    // a client link to go marketplace
                    app.navBar().preRightAction = app.navBar().rightAction();
                    app.navBar().rightAction(NavAction.goMarketplace);
                }
            }
            else if (app.navBar().preRightAction) {
                // Recovering previous right action, like after change it to a just-client option
                // and then entering with service-professional profile
                app.navBar().rightAction(app.navBar().preRightAction);
                app.navBar().preRightAction = null;
            }
        }
    }

    app.navBar = ko.observable(null);

    var refreshNav = function refreshNav() {
        // Trigger event to force a component update
        $('.AppNav').trigger('contentChange');
    };
    var autoRefreshNav = function autoRefreshNav(action) {
        if (action) {
            action.text.subscribe(refreshNav);
            action.isTitle.subscribe(refreshNav);
            action.icon.subscribe(refreshNav);
            action.isMenu.subscribe(refreshNav);
        }
    };

    /**
        Update the nav model using the Activity defaults
        // TODO Look a way to replace all this complex navbar logic, maybe per
        // activity nabvars, more model helpers and global state helpers.
    **/
    app.updateAppNav = function updateAppNav(activity, state) {

        // if the activity has its own
        if ('navBar' in activity) {
            if (activity.navBar === null) {
                // Activity requires no menu, create a hidden NavBar instance
                app.navBar(new NavBar({
                    hidden: true
                }));
            }
            else {
                // Use specializied activity bar data
                app.navBar(activity.navBar);
            }
        }
        else {
            // Use default one
            app.navBar(new NavBar());
        }

        if (!app.applyNavbarMustReturn(state)) {
            // Changes depending on non-logged user
            adjustUserBar();
        }

        refreshNav();
        autoRefreshNav(app.navBar().leftAction());
        autoRefreshNav(app.navBar().rightAction());
    };

    /**
     * It describes the values of a return-to request
     * @typedef {Object} ReturnRequestInfo
     * @property {string} [link] Local URL where the request must return to
     * @property {bool} isGoBack It's true in case must return with a 'go back'
     * call; it discards the link value since it will just go to previous step,
     * and the link value is required if this is false.
     * @property {string} [label] Text to display in the 'return' link
     */

    /**
     * Looks for parameters in the navigation state requesting to
     * use a specific setting for the current activity 'back' button.
     * It returns an object with values properly processed describing
     * the desired place to go back, or null if no parameters are present.
     * @returns {ReturnRequestInfo} Null if nothing is requested.
     */
    app.getReturnRequestInfo = function(state) {
        var q = state && state.route && state.route.query;

        if (q.mustReturn) {
            var data = {
                link: null,
                label: null,
                isGoBack: false
            };

            // Link: auto goBack or explicit link
            var returnLink = decodeURIComponent(q.mustReturn);
            if (returnLink === '1' || returnLink === 'true') {
                data.isGoBack = true;
            }
            else {
                data.link = returnLink;
            }
            // A text can be provided
            data.label = decodeURIComponent(q.returnText || '');
            return data;
        }
        return null;
    };

    /**
     * Detects parameters requesting a specific 'go back' behavior
     * and updates the navbar to reflect that, or left untouched if
     * no parameters present.
     * Parameter that triggers the logic: state.route.query.mustReturn
     * Optional parameter: state.route.query.returnText
     * @returns {bool} True if parameters present and changes to navbar
     * were applied, false if untouched.
     */
    app.applyNavbarMustReturn = function(state) {
        var info = app.getReturnRequestInfo(state);
        if (info) {
            if (info.isGoBack) {
                // Left action forced to be a go-back
                app.navBar().leftAction(NavAction.goBack.model.clone({
                    text: info.label,
                    isShell: true,
                    isTitle: true
                }));
            }
            else {
                // Left action force to return to the given URL
                app.navBar().leftAction(NavAction.goBack.model.clone({
                    link: info.link,
                    text: info.label,
                    isShell: false,
                    isTitle: true
                }));
            }
            return true;
        }
        return false;
    };

    /**
        Update the app menu to highlight the
        given link name
    **/
    app.updateMenu = function updateMenu(/*name*/) {

        var $menu = $('.App-menus .navbar-collapse');

        /* DONE WITH KNOCKOUT BINDING RIGHT NOW
        // Remove any active
        $menu
        .find('li')
        .removeClass('active');
        // Add active
        $menu
        .find('.go-' + name)
        .closest('li')
        .addClass('active');
        */

        // Hide menu
        $menu
        .filter(':visible')
        .collapse('hide');
    };

    // Set model for the AppNav
    app.navBarBinding = {
        navBar: app.navBar,
        // Both: are later filled with a call to the model once loaded and ready
        photoUrl: ko.observable('about:blank'),
        userName: ko.observable('Me'),
        isServiceProfessional: ko.observable(false),
        isClient: ko.observable(false),
        isApp: ko.observable(!!window.cordova),
        isInOnboarding: ko.observable(false),
        isAtCurrentOnboardingStep: ko.observable(false),
        active: ko.observable('')
    };

    var onboarding = require('./data/onboarding');
    ko.computed(function() {
        app.navBarBinding.isInOnboarding(onboarding.inProgress());
        app.navBarBinding.isAtCurrentOnboardingStep(onboarding.isAtCurrentStep());
    });

    app.navBarBinding.continueOnboarding = function() {
        onboarding.goCurrentStep();
    };

    app.shell.on(app.shell.events.itemReady, function() {
        app.navBarBinding.active(app.shell.currentRoute.name);
    });

    app.navBarBinding.isAnonymous = ko.pureComputed(function() {
        return !this.isServiceProfessional() && !this.isClient();
    }, app.navBarBinding);

    app.navBarBinding.cssIfActive = function(activityName) {
        return ko.computed(function() {
            return activityName === app.navBarBinding.active() ? 'active' : '';
        });
    };

    app.setupNavBarBinding = function setupNavBarBinding() {
        app.navBarBinding.isApp(!!window.cordova);
        ko.applyBindings(app.navBarBinding, $('.AppNav').get(0));
    };

    /**
        Performs the 'back' task from the navbar link, if any.
        That is, trigger the left action.
        Fallback to shell goBack
    **/
    app.performsNavBarBack = function performsNavBarBack(options) {
        var nav = this.navBar(),
            left = nav && nav.leftAction(),
            $btn = $('.AppNav .SmartNavBar-edge.left > a.SmartNavBar-btn');

        // There is an action, trigger like a click so all the handlers
        // attached on spare places do their work:
        if (left && !left.isMenu()) {
            var event = $.Event('click');
            event.options = options || {};
            $btn.trigger(event);
        }
        else if (this.shell) {
            this.shell.goBack();
        }
    };

    /**
        It shows an unobtrusive notification on the navbar place, that
        hides after a short timeout
    **/
    var lastNotificationTimer = null;
    app.showNavBarNotification = function showNavBarNotification(settings) {
        var msg = settings && settings.message || 'Hello World!',
            duration = settings && settings.duration || 2000,
            transitionDuration = settings && settings.transitionDuration || 400,
            $el = $('.AppNav .SmartNavBar-notification');

        $el.text(msg);
        $el.fadeIn(transitionDuration)
        .queue(function() {

            // Manual hide on clicking
            $el
            .off('click.manualHide')
            .on('click.manualHide', function() {
                $el.fadeOut(transitionDuration);
            });

            // Auto hide after timeout
            clearTimeout(lastNotificationTimer);
            lastNotificationTimer = setTimeout(function() {
                $el.fadeOut(transitionDuration);
            }, duration);

            $(this).dequeue();
        });
    };
};
<|endoftext|>


<commit_before>/// <reference path="../App.js" />

(function () {
    "use strict";

    // The initialize function must be run each time a new page is loaded
    Office.initialize = function (reason) {
        $(document).ready(function () {
            app.initialize();
            var picUrl;

            // If getSelectedDataAsync method is supported by the host application,
            // reads data from current document selection and displays a preview of the picture
            if (Office.context.document.getSelectedDataAsync) {
                $('#searhPictures').click(function () {
                    Office.context.document.getSelectedDataAsync(Office.CoercionType.Text,
                        function (result) {
                            if (result.status === Office.AsyncResultStatus.Succeeded) {
                                picUrl = result.value.trim();
                                app.showNotification('Selected:', picUrl);
                                window.location.assign("http://104.236.173.186/search/" + picUrl);
                            } else {
                                app.showNotification('Error:', result.error.message);
                            }
                        }
                    );
                });
            }

            // If setSelectedDataAsync method is supported by the host application,
            // insertImageLink is hooked up to call the method
            if (Office.context.document.setSelectedDataAsync) {
                $('img').click(function() {
                    var imgHtml = "<img " + "src='" + $(this).prop("src") + "' img/>";
                    setHTMLImage(imgHtml);
                });
            }
        });
    };

    // invalidate Picture Url
    function validatePic(picUrl) {
        if (picUrl.length == 0) {
            app.showNotification("Please select a text from the document to search awesome pictures.");
            return false;
        }

        var picextension = picUrl.substring(picUrl.lastIndexOf("."), picUrl.length);
        picextension = picextension.toLowerCase();
        if ((picextension != ".jpg") && (picextension != ".gif") && (picextension != ".jpeg") && (picextension != ".png") && (picextension != ".bmp")) {
            app.showNotification("Image type must be one of gif,jpeg,jpg,png");

            // Clear Picture Url
            picUrl = "";
            document.selection.clear();
            return false;
        }

        return true;
    }

    // Preview Picture to make sure that url is valid
    function preViewPic(picUrl) {
        $('#insertImageLinkThumbnail').prop("src", picUrl);
        $('#insertImageLink').prop("src", picUrl);
    }

    // Insert image
    function setHTMLImage(imgHTML) {
        Office.context.document.setSelectedDataAsync(
            imgHTML,
            { coercionType: "html" },
            function (asyncResult) {
                if (asyncResult.status == "failed") {
                    writeError('Error: ' + asyncResult.error.message);
                }
            });
    }
})();
<commit_msg>update text<commit_after>/// <reference path="../App.js" />

(function () {
    "use strict";

    // The initialize function must be run each time a new page is loaded
    Office.initialize = function (reason) {
        $(document).ready(function () {
            app.initialize();
            var picUrl;

            // If getSelectedDataAsync method is supported by the host application,
            // reads data from current document selection and displays a preview of the picture
            if (Office.context.document.getSelectedDataAsync) {
                $('#searhPictures').click(function () {
                    Office.context.document.getSelectedDataAsync(Office.CoercionType.Text,
                        function (result) {
                            if (result.status === Office.AsyncResultStatus.Succeeded) {
                                picUrl = result.value.trim();
                                app.showNotification('Searching ' + picUrl + '.', picUrl);
                                window.location.assign("http://104.236.173.186/search/" + picUrl);
                            } else {
                                app.showNotification('Error:', result.error.message);
                            }
                        }
                    );
                });
            }

            // If setSelectedDataAsync method is supported by the host application,
            // insertImageLink is hooked up to call the method
            if (Office.context.document.setSelectedDataAsync) {
                $('img').click(function() {
                    var imgHtml = "<img " + "src='" + $(this).prop("src") + "' img/>";
                    setHTMLImage(imgHtml);
                });
            }
        });
    };

    // invalidate Picture Url
    function validatePic(picUrl) {
        if (picUrl.length == 0) {
            app.showNotification("Please select a text from the document to search awesome pictures.");
            return false;
        }

        var picextension = picUrl.substring(picUrl.lastIndexOf("."), picUrl.length);
        picextension = picextension.toLowerCase();
        if ((picextension != ".jpg") && (picextension != ".gif") && (picextension != ".jpeg") && (picextension != ".png") && (picextension != ".bmp")) {
            app.showNotification("Image type must be one of gif,jpeg,jpg,png");

            // Clear Picture Url
            picUrl = "";
            document.selection.clear();
            return false;
        }

        return true;
    }

    // Preview Picture to make sure that url is valid
    function preViewPic(picUrl) {
        $('#insertImageLinkThumbnail').prop("src", picUrl);
        $('#insertImageLink').prop("src", picUrl);
    }

    // Insert image
    function setHTMLImage(imgHTML) {
        Office.context.document.setSelectedDataAsync(
            imgHTML,
            { coercionType: "html" },
            function (asyncResult) {
                if (asyncResult.status == "failed") {
                    writeError('Error: ' + asyncResult.error.message);
                }
            });
    }
})();
<|endoftext|>


<commit_before>/*
Capturing and modifying positional data for display on the phone.

Some refs -
https://developer.mozilla.org/en-US/docs/Web/API/Detecting_device_orientation
http://stackoverflow.com/questions/18112729/calculate-compass-heading-from-deviceorientation-event-api
http://w3c.github.io/deviceorientation/spec-source-orientation.html#introduction
*/

// DECLARATIONS AND INSTANTIATING DEVICE
// some declared variables in a function as we can later reset
// ============================================================
var onoff = 0;
var stamp, elapse, elapseMin = 50;
var tiltStamp = 0, remTime, elapseMax = 4000;
var tallyS = 5, tallyE = 11;
var ball = document.querySelector('.ball');
var garden = document.querySelector('.garden');
var maxX = garden.clientWidth  - ball.clientWidth;
var maxY = garden.clientHeight - ball.clientHeight;

motionVars();
// setShaker();
getDeviceData();

// MAIN DEVICE FUNCTIONS
// ============================================================
function getDeviceData() {
    // call motion
    if (window.DeviceMotionEvent) {
        window.addEventListener('devicemotion', MotionHandler, false);
    } else {
        document.getElementById("dmEvent").innerHTML = "Motion not supported."
    }
    // call orientation
    if (window.DeviceOrientationEvent) {
        window.addEventListener('deviceorientation', OrientationHandler, false);
    } else {
        document.getElementById("dmEvent2").innerHTML = "Orientation not supported."
    }
}

function OrientationHandler(eventData) {
    /*    Grab and modify rotation data
    alpha is the compass direction (rotation around z axis (out of face);
    varies 0 to 360;
    beta is the front-to-back tilt in degrees, where front is positive
    (so rotation around short x axis at base of phone);
    varies -180 to +180;
    gamma is rotation around the long y axis on LHS of phone;
    ie left-to-right tilt in degrees, where right is positive;
    varies -90 to +90 (so doesn't know if upside down);
    */
    dir_a = round(eventData.alpha);
    dir_b = round(eventData.beta);
    dir_g = round(eventData.gamma);
    northFace = compassHeading(
        dir_a, dir_b, dir_g
    );
    document.getElementById('raws').innerHTML = [
        dir_a, dir_b, dir_g
    ]; // temp fudge
    document.getElementById('compass').innerHTML = northFace;
}

function MotionHandler(eventData) {
    /* Grab acceleration results */
    interval = eventData.interval;
    acc_x = round(eventData.acceleration.x);
    acc_y = round(eventData.acceleration.y);
    acc_z = round(eventData.acceleration.z);
    document.getElementById('accs').innerHTML = [
        acc_x, acc_y, acc_z
    ]; // temp fudge
    document.getElementById('ival').innerHTML = [
        interval
    ]; // temp fudge
}

// CONVERTING ALPHA
// ============================================================
function compassHeading(alpha, beta, gamma) {
    /* This function collapses the beta and gamma into alpha;
    To explain: where alpha is rotation about the z axis (coming
    out of the face of the phone, our new compassHeading asks
    how northerly is the plane of the phone (as it extends
    out of the back) as this is how our user operates the phone.

    The exception to the rule is where the phone is totally flat;
    then the top the phone acts as north.
     */
    if (Math.abs(beta) < 5) {
        beta = 0;
    }
    if (Math.abs(gamma) < 5) {
        gamma = 0;
    }
    if (Math.abs(beta) != 0 || Math.abs(gamma) != 0) {
        // degrees to radians
        var alphaRad = alpha * (Math.PI / 180);
        var betaRad = beta * (Math.PI / 180);
        var gammaRad = gamma * (Math.PI / 180);
        // sines and cosines
        var sA = Math.sin(alphaRad);
        var sB = Math.sin(betaRad);
        var sG = Math.sin(gammaRad);
        var cA = Math.cos(alphaRad);
        var cB = Math.cos(betaRad);
        var cG = Math.cos(gammaRad);
        // Calculate A, B, G rotations
        var rA = - cA * sG - sA * sB * cG;
        var rB = - sA * sG + cA * sB * cG;
        var rG = - cB * cG;
        // Compass heading
        var compassHeading = Math.atan(rA / rB);
        // Convert compass heading to use whole unit circle
        if(rB < 0) {
            compassHeading += Math.PI;
        } else if(rA < 0) {
            compassHeading += 2 * Math.PI;
        }
        return Math.round(compassHeading * (180 / Math.PI)); // Compass Heading (in degrees)
    } else {
        revAlpha = 360 - alpha;
        return Math.round(revAlpha);
    }
}

// CONVENIENCES
// ============================================================
function round(val) {
    var amt = 100;
    return Math.round(val * amt) /  amt;
}

function motionVars() {
    // cumulative capture of velocity, disp;
    // co-ordinates is just disp restated for the graph;
    // set up like this allows us to reset these vars.
    prevTilt = 0;
    tallyTilt = 0;
    stamp = Date.now();
    tiltStamp = 0;
    prettyButtons('off', elapseMax);

    acc = {  // TEMP STORE
        x: [0],
        y: [0],
        z: [0]
    };
    rot = {  // TEMP STORE
        theta: [0],
        beta: [0],
        gamma: [0]
    };
    /*
    vel = {
        x: [0],
        y: [0],
        z: [0]
    };
    disp = {
        x: [0],
        y: [0],
        z: [0]
    };
    cords = {
        x: [0],
        y: [0]
    };
    bucketAcc = {  // TEMP STORE
        x: [0],
        y: [0],
        z: [0]
    };
    cords2 = [{  // TEMP STORE
        x: 0,
        y: 0,
        // color: 'rgba(100, 100, 100, .5)'
    }];
    */
}

function toggler() {
    onoff = 1 - onoff;
    if (onoff == 1) {
        $('#booler').attr('class', 'btn btn-success');
        $('#booler').text('RecorderReady');
        $('#remTime').attr('class', 'label label-warning');
    } else {
        $('#booler').attr('class', 'btn btn-danger');
        $('#booler').text('RecorderOff');
        $('#remTime').attr('class', 'label label-default');
    }
}

function prettyButtons(power, rem) {
    rem = round(rem / 1000);
    if (power == 'on') {
        $('#beep6').get(0).play();
        $('#remTime').attr('class', 'label label-success');
        $('#remTime').text('Remaining: ' + rem + ' secs');
        $('#captButton').show(1000).hide(1000);
    } else {
        $('#remTime').attr('class', 'label label-warning');
        $('#remTime').text('ShakeAndSign');
        $('#captButton').hide();
    }
}

// ==============================================
// MAIN OPERATIVE FUNCTIONS
// ==============================================
function runShaker(tilt) {
    // When user does a -+ tilt, we increase our tally;
    // prevTilt holds the prevailing (and then previous result);
    // Tally has a start and a stop level;
    if (tilt < 0) {
        if (prevTilt >= 0) {
            tallyTilt += 1;
        }
        prevTilt = -1;
    } else {
        if (prevTilt < 0) {
            tallyTilt += 1;
        }
        prevTilt = 1;
    }
    remTime = elapseMax - ((tiltStamp == 0) ? 0 : Date.now() - tiltStamp);
    // document.getElementById('remTime').innerHTML = 'Stopwatch: ' + round(remTime / 1000) + ' secs';
    // Pass data and reset our vars
    if (tallyTilt >= tallyE || remTime <= 0) {
        passMotionData();
        motionVars();
    }
}

function passMotionData() {
    // sends it back for database handling
    var strData = {
        'strAcx': JSON.stringify(acc['x']), //.slice(0, 5)),
        'strAcy': JSON.stringify(acc['y']),
        'strTheta': JSON.stringify(rot['theta']),
        'strBeta': JSON.stringify(rot['beta']),
        'strGamma': JSON.stringify(rot['gamma'])
    }
    $.getJSON('./_balldata', strData, function(data) {
        $("#result").text(data.ballData);
    });
};

function handleOrientation(event) {
    // var x = event.beta;  // In degree in the range [-180,180]
    // var y = event.gamma; // In degree in the range [-90,90]
    // document.getElementById('beta').innerHTML = "x : " + x + "\n";
    // document.getElementById('gamma').innerHTML = "y : " + y + "\n";
    /*
    // Because we don't want to have the device upside down
    // We constrain the x value to the range [-90,90]
    if (x >  90) { x =  90};
    if (x < -90) { x = -90};

    // To make computation easier we shift the range of
    // x and y to [0,180]
    x += 90;
    y += 90;
    */
    // 10 is half the size of the ball
    // It center the positioning point to the center of the ball
    ball.style.left  = (maxX * (acc_x / 10 + 0.5) ) + "px";
    ball.style.top = (maxY* (-acc_y / 10 + 0.5) ) + "px";

    if (onoff == 1) {
        // record snapshot if we get signal
        runShaker(dir_g);
        // otherwise keep capturing data
        elapse = Date.now() - stamp;
        if (tallyTilt >= tallyS && elapse >= elapseMin) {
            prettyButtons('on', remTime);
            acc['x'].push(acc_x);
            acc['y'].push(acc_y);
            rot['theta'].push(northFace);
            rot['beta'].push(dir_b);
            rot['gamma'].push(dir_g);
            // start tilt clock
            if (tiltStamp == 0 ) {
                tiltStamp = Date.now();
            }
            // timestamp, display
            stamp = Date.now();
            /*
            document.getElementById('acx').innerHTML = acc['x'].length;
            document.getElementById('acy').innerHTML = acc['y'];
            document.getElementById('theta').innerHTML = rot['theta'];
            document.getElementById('beta').innerHTML = rot['beta'];
            document.getElementById('gamma').innerHTML = rot['gamma'];
            */
        }
    }
}

// AND RUN
// ============================================================
window.addEventListener('deviceorientation', handleOrientation);
<commit_msg>Add tiltclock<commit_after>/*
Capturing and modifying positional data for display on the phone.

Some refs -
https://developer.mozilla.org/en-US/docs/Web/API/Detecting_device_orientation
http://stackoverflow.com/questions/18112729/calculate-compass-heading-from-deviceorientation-event-api
http://w3c.github.io/deviceorientation/spec-source-orientation.html#introduction
*/

// DECLARATIONS AND INSTANTIATING DEVICE
// some declared variables in a function as we can later reset
// ============================================================
var onoff = 0;
var stamp, elapse, elapseMin = 50;
var tiltStamp = 0, remTime, elapseMax = 4000;
var tallyS = 5, tallyM = 3, tallyE = 11;
var ball = document.querySelector('.ball');
var garden = document.querySelector('.garden');
var maxX = garden.clientWidth  - ball.clientWidth;
var maxY = garden.clientHeight - ball.clientHeight;

motionVars();
// setShaker();
getDeviceData();

// MAIN DEVICE FUNCTIONS
// ============================================================
function getDeviceData() {
    // call motion
    if (window.DeviceMotionEvent) {
        window.addEventListener('devicemotion', MotionHandler, false);
    } else {
        document.getElementById("dmEvent").innerHTML = "Motion not supported."
    }
    // call orientation
    if (window.DeviceOrientationEvent) {
        window.addEventListener('deviceorientation', OrientationHandler, false);
    } else {
        document.getElementById("dmEvent2").innerHTML = "Orientation not supported."
    }
}

function OrientationHandler(eventData) {
    /*    Grab and modify rotation data
    alpha is the compass direction (rotation around z axis (out of face);
    varies 0 to 360;
    beta is the front-to-back tilt in degrees, where front is positive
    (so rotation around short x axis at base of phone);
    varies -180 to +180;
    gamma is rotation around the long y axis on LHS of phone;
    ie left-to-right tilt in degrees, where right is positive;
    varies -90 to +90 (so doesn't know if upside down);
    */
    dir_a = round(eventData.alpha);
    dir_b = round(eventData.beta);
    dir_g = round(eventData.gamma);
    northFace = compassHeading(
        dir_a, dir_b, dir_g
    );
    document.getElementById('raws').innerHTML = [
        dir_a, dir_b, dir_g
    ]; // temp fudge
    document.getElementById('compass').innerHTML = northFace;
}

function MotionHandler(eventData) {
    /* Grab acceleration results */
    interval = eventData.interval;
    acc_x = round(eventData.acceleration.x);
    acc_y = round(eventData.acceleration.y);
    acc_z = round(eventData.acceleration.z);
    document.getElementById('accs').innerHTML = [
        acc_x, acc_y, acc_z
    ]; // temp fudge
    document.getElementById('ival').innerHTML = [
        interval
    ]; // temp fudge
}

// CONVERTING ALPHA
// ============================================================
function compassHeading(alpha, beta, gamma) {
    /* This function collapses the beta and gamma into alpha;
    To explain: where alpha is rotation about the z axis (coming
    out of the face of the phone, our new compassHeading asks
    how northerly is the plane of the phone (as it extends
    out of the back) as this is how our user operates the phone.

    The exception to the rule is where the phone is totally flat;
    then the top the phone acts as north.
     */
    if (Math.abs(beta) < 5) {
        beta = 0;
    }
    if (Math.abs(gamma) < 5) {
        gamma = 0;
    }
    if (Math.abs(beta) != 0 || Math.abs(gamma) != 0) {
        // degrees to radians
        var alphaRad = alpha * (Math.PI / 180);
        var betaRad = beta * (Math.PI / 180);
        var gammaRad = gamma * (Math.PI / 180);
        // sines and cosines
        var sA = Math.sin(alphaRad);
        var sB = Math.sin(betaRad);
        var sG = Math.sin(gammaRad);
        var cA = Math.cos(alphaRad);
        var cB = Math.cos(betaRad);
        var cG = Math.cos(gammaRad);
        // Calculate A, B, G rotations
        var rA = - cA * sG - sA * sB * cG;
        var rB = - sA * sG + cA * sB * cG;
        var rG = - cB * cG;
        // Compass heading
        var compassHeading = Math.atan(rA / rB);
        // Convert compass heading to use whole unit circle
        if(rB < 0) {
            compassHeading += Math.PI;
        } else if(rA < 0) {
            compassHeading += 2 * Math.PI;
        }
        return Math.round(compassHeading * (180 / Math.PI)); // Compass Heading (in degrees)
    } else {
        revAlpha = 360 - alpha;
        return Math.round(revAlpha);
    }
}

// CONVENIENCES
// ============================================================
function round(val) {
    var amt = 100;
    return Math.round(val * amt) /  amt;
}

function motionVars() {
    // cumulative capture of velocity, disp;
    // co-ordinates is just disp restated for the graph;
    // set up like this allows us to reset these vars.
    prevTilt = 0;
    tallyTilt = 0;
    stamp = Date.now();
    tiltStamp = 0;
    prettyButtons('off', elapseMax);

    acc = {  // TEMP STORE
        x: [0],
        y: [0],
        z: [0]
    };
    rot = {  // TEMP STORE
        theta: [0],
        beta: [0],
        gamma: [0]
    };
    /*
    vel = {
        x: [0],
        y: [0],
        z: [0]
    };
    disp = {
        x: [0],
        y: [0],
        z: [0]
    };
    cords = {
        x: [0],
        y: [0]
    };
    bucketAcc = {  // TEMP STORE
        x: [0],
        y: [0],
        z: [0]
    };
    cords2 = [{  // TEMP STORE
        x: 0,
        y: 0,
        // color: 'rgba(100, 100, 100, .5)'
    }];
    */
}

function toggler() {
    onoff = 1 - onoff;
    if (onoff == 1) {
        $('#booler').attr('class', 'btn btn-success');
        $('#booler').text('RecorderReady');
        $('#remTime').attr('class', 'label label-danger');
    } else {
        $('#booler').attr('class', 'btn btn-danger');
        $('#booler').text('RecorderOff');
        $('#remTime').attr('class', 'label label-default');
        $('#beep6').get(0).play();
    }
}

function prettyButtons(power, rem) {
    rem = round(rem / 1000);
    if (power == 'on') {
        $('#beep6').get(0).play();
        $('#remTime').attr('class', 'label label-success');
        $('#remTime').text('Remaining: ' + rem + ' secs');
        $('#captButton').show(1000).hide(1000);
    } else {
        $('#remTime').attr('class', 'label label-danger');
        $('#remTime').text('ShakeAndSign');
        $('#captButton').hide();
    }
}

// ==============================================
// MAIN OPERATIVE FUNCTIONS
// ==============================================
function runShaker(tilt) {
    // When user does a -+ tilt, we increase our tally;
    // prevTilt holds the prevailing (and then previous result);
    // Tally has a start and a stop level;
    if (tilt < 0) {
        if (prevTilt >= 0) {
            tallyTilt += 1;
        }
        prevTilt = -1;
    } else {
        if (prevTilt < 0) {
            tallyTilt += 1;
        }
        prevTilt = 1;
    }
    remTime = elapseMax - ((tiltStamp == 0) ? 0 : Date.now() - tiltStamp);
    // document.getElementById('remTime').innerHTML = 'Stopwatch: ' + round(remTime / 1000) + ' secs';
    // Pass data and reset our vars
    if (tallyTilt >= tallyE || remTime <= 0) {
        passMotionData();
        motionVars();
    }
}

function passMotionData() {
    // sends it back for database handling
    var strData = {
        'strAcx': JSON.stringify(acc['x']), //.slice(0, 5)),
        'strAcy': JSON.stringify(acc['y']),
        'strTheta': JSON.stringify(rot['theta']),
        'strBeta': JSON.stringify(rot['beta']),
        'strGamma': JSON.stringify(rot['gamma'])
    }
    $.getJSON('./_balldata', strData, function(data) {
        $("#result").text(data.ballData);
    });
};

function handleOrientation(event) {
    // var x = event.beta;  // In degree in the range [-180,180]
    // var y = event.gamma; // In degree in the range [-90,90]
    // document.getElementById('beta').innerHTML = "x : " + x + "\n";
    // document.getElementById('gamma').innerHTML = "y : " + y + "\n";
    /*
    // Because we don't want to have the device upside down
    // We constrain the x value to the range [-90,90]
    if (x >  90) { x =  90};
    if (x < -90) { x = -90};

    // To make computation easier we shift the range of
    // x and y to [0,180]
    x += 90;
    y += 90;
    */
    // 10 is half the size of the ball
    // It center the positioning point to the center of the ball
    ball.style.left  = (maxX * (acc_x / 10 + 0.5) ) + "px";
    ball.style.top = (maxY* (-acc_y / 10 + 0.5) ) + "px";

    if (onoff == 1) {
        // record snapshot if we get signal
        runShaker(dir_g);
        // otherwise keep capturing data
        elapse = Date.now() - stamp;
        // Primed, go amber
        if (tallyTilt >= tallyM ) {
            $('#remTime').attr('class', 'label label-warning');
        }
        if (tallyTilt >= tallyS && elapse >= elapseMin) {
            prettyButtons('on', remTime);
            acc['x'].push(acc_x);
            acc['y'].push(acc_y);
            rot['theta'].push(northFace);
            rot['beta'].push(dir_b);
            rot['gamma'].push(dir_g);
            // start tilt clock
            if (tiltStamp == 0 ) {
                tiltStamp = Date.now();
            }
            // timestamp, display
            stamp = Date.now();
            /*
            document.getElementById('acx').innerHTML = acc['x'].length;
            document.getElementById('acy').innerHTML = acc['y'];
            document.getElementById('theta').innerHTML = rot['theta'];
            document.getElementById('beta').innerHTML = rot['beta'];
            document.getElementById('gamma').innerHTML = rot['gamma'];
            */
        }
    }
}

// AND RUN
// ============================================================
window.addEventListener('deviceorientation', handleOrientation);
<|endoftext|>


<commit_before>import { AsyncStorage } from 'react-native';
import { createStore, applyMiddleware } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';

import rootReducer from '../reducers';

const loggerMiddleware = createLogger();

export default function configureStore(initialState, onComplete: ?() => void) {
	const store = createStore(
		rootReducer,
		applyMiddleware(
			thunkMiddleware, // lets us dispatch() functions
			loggerMiddleware // neat middleware that logs actions
		),
		autoRehydrate()
	);

	if (module.hot) {
		// Enable Webpack hot module replacement for reducers
		module.hot.accept('../reducers', () => {
			const nextRootReducer = require('../reducers');

			store.replaceReducer(nextRootReducer);
		});
	}

	persistStore(store, { storage: AsyncStorage }, onComplete);
	return store;
}
<commit_msg>Whitelist only cards<commit_after>import { AsyncStorage } from 'react-native';
import { createStore, applyMiddleware } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';

import rootReducer from '../reducers';

const loggerMiddleware = createLogger();

export default function configureStore(initialState, onComplete: ?() => void) {
	const store = createStore(
		rootReducer,
		applyMiddleware(
			thunkMiddleware, // lets us dispatch() functions
			loggerMiddleware // neat middleware that logs actions
		),
		autoRehydrate()
	);

	if (module.hot) {
		// Enable Webpack hot module replacement for reducers
		module.hot.accept('../reducers', () => {
			const nextRootReducer = require('../reducers');

			store.replaceReducer(nextRootReducer);
		});
	}

	persistStore(store, { storage: AsyncStorage, whitelist: [ 'cards' ] }, onComplete);
	return store;
}
<|endoftext|>


<commit_before>'use strict';

var $ = require('jquery');
var Backbone = require('backbone');
Backbone.$ = $;
var _ = require('underscore');
var UserUtils = require('../../utils/userUtils');

var EditProfileView = Backbone.View.extend({
    el: '#full-width-container',
    events: {
        'click button.btn-profile-save': 'handleProfileSubmit'
    },
    initialize: function() {
        this.template = _.template($('#edit-profile-template').html());
    },

    render: function() {
        this.$el.html(this.template(this.model.toJSON()));

        //set the select values if needed
        this.setSelectValue(this.model.get('ageRange'), 'select[name="age-range"] option');
        this.setSelectValue(this.model.get('gender'), 'select[name="gender"] option');
    },

    handleProfileSubmit: function(e) {
        e.preventDefault();

        var isNew = this.model.isNew();

        //set the values into the model
        this.model.set('name', this.$('input[name="name"]').val());
        this.model.set('ageRange', this.$('select[name="age-range"]').val());
        this.model.set('gender', this.$('select[name="gender"]').val());

        //save the model
        this.model.save(null, {
            success: function(model) {

                if(isNew) {
                    //add the new model to the user's profile collection
                    var user = UserUtils.getCurrentUser();
                    user.get('profiles').add(model);
                }

                //navigate to the management page
                Backbone.history.navigate('pmanage', {trigger: true});
            }
        });
    },

    setSelectValue: function(value, optionSelector) {
        if(!_.isEmpty(value)) {
            this.$(optionSelector).filter(function() {
                return $(this).val() == value;
            }).prop('selected', true);
        }
    }
});

module.exports = EditProfileView;<commit_msg>now saving list items in profiles<commit_after>'use strict';

var $ = require('jquery');
var Backbone = require('backbone');
Backbone.$ = $;
var _ = require('underscore');
var UserUtils = require('../../utils/userUtils');

var EditProfileView = Backbone.View.extend({
    el: '#full-width-container',
    events: {
        'click button.btn-profile-save': 'handleProfileSubmit'
    },
    initialize: function() {
        this.template = _.template($('#edit-profile-template').html());
    },

    render: function() {
        this.$el.html(this.template(this.model.toJSON()));

        //set the select values if needed
        this.setSelectValue(this.model.get('ageRange'), 'select[name="age-range"] option');
        this.setSelectValue(this.model.get('gender'), 'select[name="gender"] option');
    },

    handleProfileSubmit: function(e) {
        e.preventDefault();

        var isNew = this.model.isNew();

        //set the values into the model
        this.model.set('name', this.$('input[name="name"]').val());
        this.model.set('ageRange', this.$('select[name="age-range"]').val());
        this.model.set('gender', this.$('select[name="gender"]').val());
        this.model.set('medications', this.$('input[name="meds"]').val().split(/\s*,\s*/));
        this.model.set('conditions', this.$('input[name="conditions"]').val().split(/\s*,\s*/));
        this.model.set('allergies', this.$('input[name="allergies"]').val().split(/\s*,\s*/));

        //save the model
        this.model.save(null, {
            success: function(model) {

                if(isNew) {
                    //add the new model to the user's profile collection
                    var user = UserUtils.getCurrentUser();
                    user.get('profiles').add(model);
                }

                //navigate to the management page
                Backbone.history.navigate('pmanage', {trigger: true});
            }
        });
    },

    setSelectValue: function(value, optionSelector) {
        if(!_.isEmpty(value)) {
            this.$(optionSelector).filter(function() {
                return $(this).val() == value;
            }).prop('selected', true);
        }
    }
});

module.exports = EditProfileView;<|endoftext|>


<commit_before>/**
 * jqPlot
 * Pure JavaScript plotting plugin using jQuery
 *
 * Version: @VERSION
 *
 * Copyright (c) 2009-2011 Chris Leonello
 * jqPlot is currently available for use in all personal or commercial projects
 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
 * choose the license that best suits your project and use it accordingly.
 *
 * Although not required, the author would appreciate an email letting him
 * know of any substantial use of jqPlot.  You can reach the author at:
 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
 *
 * If you are feeling kind and generous, consider supporting the project by
 * making a donation at: http://www.jqplot.com/donate.php .
 *
 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
 *
 *     version 2007.04.27
 *     author Ash Searle
 *     http://hexmen.com/blog/2007/03/printf-sprintf/
 *     http://hexmen.com/js/sprintf.js
 *     The author (Ash Searle) has placed this code in the public domain:
 *     "This code is unrestricted: you are free to use it however you like."
 *
 * jsDate library by Chris Leonello:
 *
 * Copyright (c) 2010-2011 Chris Leonello
 *
 * jsDate is currently available for use in all personal or commercial projects
 * under both the MIT and GPL version 2.0 licenses. This means that you can
 * choose the license that best suits your project and use it accordingly.
 *
 * jsDate borrows many concepts and ideas from the Date Instance
 * Methods by Ken Snyder along with some parts of Ken's actual code.
 * Ken has generously given permission to adapt his code and release
 * under the MIT and GPL V2 licenses.
 *
 * Ken's origianl Date Instance Methods and copyright notice:
 *
 * Ken Snyder (ken d snyder at gmail dot com)
 * 2008-09-10
 * version 2.0.2 (http://kendsnyder.com/sandbox/date/)
 * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
 *
 * jqplotToImage function based on Larry Siden's export-jqplot-to-png.js.
 * Larry has generously given permission to adapt his code for inclusion
 * into jqPlot.
 *
 * Larry's original code can be found here:
 *
 * https://github.com/lsiden/export-jqplot-to-png
 *
 *
 */

(function($) {

    $.fn.jqplotChildText = function() {
        return $(this).contents().filter(function() {
            return this.nodeType == 3;  // Node.TEXT_NODE not defined in I7
        }).text();
    };

    // Returns font style as abbreviation for "font" property.
    $.fn.jqplotGetComputedFontStyle = function() {
        var css = window.getComputedStyle ?  window.getComputedStyle(this[0], "") : this[0].currentStyle;
        var attrs = css['font-style'] ? ['font-style', 'font-weight', 'font-size', 'font-family'] : ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'];
        var style = [];

        for (var i=0 ; i < attrs.length; ++i) {
            var attr = String(css[attrs[i]]);

            if (attr && attr != 'normal') {
                style.push(attr);
            }
        }
        return style.join(' ');
    };

    /**
     * Namespace: $.fn
     * jQuery namespace to attach functions to jQuery elements.
     *
     */

    $.fn.jqplotToImageCanvas = function(options) {

        options = options || {};
        var x_offset = (options.x_offset == null) ? 0 : options.x_offset;
        var y_offset = (options.y_offset == null) ? 0 : options.y_offset;
        var backgroundColor = (options.backgroundColor == null) ? 'rgb(255,255,255)' : options.backgroundColor;

        if ($(this).width() == 0 || $(this).height() == 0) {
            return null;
        }

        // excanvas and hence IE < 9 do not support toDataURL and cannot export images.
        if ($.jqplot.use_excanvas) {
            return null;
        }

        var newCanvas = document.createElement("canvas");
        var h = $(this).outerHeight(true);
        var w = $(this).outerWidth(true);
        var offs = $(this).offset();
        var plotleft = offs.left;
        var plottop = offs.top;
        var transx = 0, transy = 0;

        // have to check if any elements are hanging outside of plot area before rendering,
        // since changing width of canvas will erase canvas.

        var clses = ['jqplot-table-legend', 'jqplot-xaxis-tick', 'jqplot-x2axis-tick', 'jqplot-yaxis-tick', 'jqplot-y2axis-tick', 'jqplot-y3axis-tick',
        'jqplot-y4axis-tick', 'jqplot-y5axis-tick', 'jqplot-y6axis-tick', 'jqplot-y7axis-tick', 'jqplot-y8axis-tick', 'jqplot-y9axis-tick',
        'jqplot-xaxis-label', 'jqplot-x2axis-label', 'jqplot-yaxis-label', 'jqplot-y2axis-label', 'jqplot-y3axis-label', 'jqplot-y4axis-label',
        'jqplot-y5axis-label', 'jqplot-y6axis-label', 'jqplot-y7axis-label', 'jqplot-y8axis-label', 'jqplot-y9axis-label' ];

        var temptop, templeft, tempbottom, tempright;

        for (var i in clses) {
            $(this).find('.'+clses[i]).each(function() {
                temptop = $(this).offset().top - plottop;
                templeft = $(this).offset().left - plotleft;
                tempright = templeft + $(this).outerWidth(true) + transx;
                tempbottom = temptop + $(this).outerHeight(true) + transy;
                if (templeft < -transx) {
                    w = w - transx - templeft;
                    transx = -templeft;
                }
                if (temptop < -transy) {
                    h = h - transy - temptop;
                    transy = - temptop;
                }
                if (tempright > w) {
                    w = tempright;
                }
                if (tempbottom > h) {
                    h =  tempbottom;
                }
            });
        }

        newCanvas.width = w + Number(x_offset);
        newCanvas.height = h + Number(y_offset);

        var newContext = newCanvas.getContext("2d");

        newContext.save();
        newContext.fillStyle = backgroundColor;
        newContext.fillRect(0,0, newCanvas.width, newCanvas.height);
        newContext.restore();

        newContext.translate(transx, transy);
        newContext.textAlign = 'left';
        newContext.textBaseline = 'top';

        function getLineheight(el) {
            var lineheight = parseInt($(el).css('line-height'), 10);

            if (isNaN(lineheight)) {
                lineheight = parseInt($(el).css('font-size'), 10) * 1.2;
            }
            return lineheight;
        }

        function writeWrappedText (el, context, text, left, top, canvasWidth) {
            var lineheight = getLineheight(el);
            var tagwidth = $(el).innerWidth();
            var tagheight = $(el).innerHeight();
            var words = text.split(/\s+/);
            var wl = words.length;
            var w = '';
            var breaks = [];
            var temptop = top;
            var templeft = left;

            for (var i=0; i<wl; i++) {
                w += words[i];
                if (context.measureText(w).width > tagwidth) {
                    breaks.push(i);
                    w = '';
                }
            }
            if (breaks.length === 0) {
                // center text if necessary
                if ($(el).css('textAlign') === 'center') {
                    templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
                }
                context.fillText(text, templeft, top);
            }
            else {
                w = words.slice(0, breaks[0]).join(' ');
                // center text if necessary
                if ($(el).css('textAlign') === 'center') {
                    templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
                }
                context.fillText(w, templeft, temptop);
                temptop += lineheight;
                for (var i=1, l=breaks.length; i<l; i++) {
                    w = words.slice(breaks[i-1], breaks[i]).join(' ');
                    // center text if necessary
                    if ($(el).css('textAlign') === 'center') {
                        templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
                    }
                    context.fillText(w, templeft, temptop);
                    temptop += lineheight;
                }
                w = words.slice(breaks[i-1], words.length).join(' ');
                // center text if necessary
                if ($(el).css('textAlign') === 'center') {
                    templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
                }
                context.fillText(w, templeft, temptop);
            }

        }

        function _jqpToImage(el, x_offset, y_offset) {
            var tagname = el.tagName.toLowerCase();
            var p = $(el).position();
            var css = window.getComputedStyle ?  window.getComputedStyle(el, "") : el.currentStyle; // for IE < 9
            var left = x_offset + p.left + parseInt(css.marginLeft, 10) + parseInt(css.borderLeftWidth, 10) + parseInt(css.paddingLeft, 10);
            var top = y_offset + p.top + parseInt(css.marginTop, 10) + parseInt(css.borderTopWidth, 10)+ parseInt(css.paddingTop, 10);
            var w = newCanvas.width;
            // var left = x_offset + p.left + $(el).css('marginLeft') + $(el).css('borderLeftWidth')

            // somehow in here, for divs within divs, the width of the inner div should be used instead of the canvas.

            if ((tagname == 'div' || tagname == 'span') && !$(el).hasClass('jqplot-highlighter-tooltip')) {
                $(el).children().each(function() {
                    _jqpToImage(this, left, top);
                });
                var text = $(el).jqplotChildText();

                if (text) {
                    newContext.font = $(el).jqplotGetComputedFontStyle();
                    newContext.fillStyle = $(el).css('color');

                    writeWrappedText(el, newContext, text, left, top, w);
                }
            }

            // handle the standard table legend

            else if (tagname === 'table' && $(el).hasClass('jqplot-table-legend')) {
                newContext.strokeStyle = $(el).css('border-top-color');
                newContext.fillStyle = $(el).css('background-color');
                newContext.fillRect(left, top, $(el).innerWidth(), $(el).innerHeight());
                if (parseInt($(el).css('border-top-width'), 10) > 0) {
                    newContext.strokeRect(left, top, $(el).innerWidth(), $(el).innerHeight());
                }

                // find all the swatches
                $(el).find('div.jqplot-table-legend-swatch-outline').each(function() {
                    // get the first div and stroke it
                    var elem = $(this);
                    newContext.strokeStyle = elem.css('border-top-color');
                    var l = left + elem.position().left;
                    var t = top + elem.position().top;
                    newContext.strokeRect(l, t, elem.innerWidth(), elem.innerHeight());

                    // now fill the swatch

                    l += parseInt(elem.css('padding-left'), 10);
                    t += parseInt(elem.css('padding-top'), 10);
                    var h = elem.innerHeight() - 2 * parseInt(elem.css('padding-top'), 10);
                    var w = elem.innerWidth() - 2 * parseInt(elem.css('padding-left'), 10);

                    var swatch = elem.children('div.jqplot-table-legend-swatch');
                    newContext.fillStyle = swatch.css('background-color');
                    newContext.fillRect(l, t, w, h);
                });

                // now add text

                $(el).find('td.jqplot-table-legend-label').each(function(){
                    var elem = $(this);
                    var l = left + elem.position().left;
                    var t = top + elem.position().top + parseInt(elem.css('padding-top'), 10);
                    newContext.font = elem.jqplotGetComputedFontStyle();
                    newContext.fillStyle = elem.css('color');
                    newContext.fillText(elem.text(), l, t);
                });

                var elem = null;
            }

            else if (tagname == 'canvas') {
                newContext.drawImage(el, left, top);
            }
        }
        $(this).children().each(function() {
            _jqpToImage(this, x_offset, y_offset);
        });
        return newCanvas;
    };

    // return the raw image data string.
    // Should work on canvas supporting browsers.
    $.fn.jqplotToImageStr = function(options) {
        var imgCanvas = $(this).jqplotToImageCanvas(options);
        if (imgCanvas) {
            return imgCanvas.toDataURL("image/png");
        }
        else {
            return null;
        }
    };

    // return a DOM <img> element and return it.
    // Should work on canvas supporting browsers.
    $.fn.jqplotToImageElem = function(options) {
        var elem = document.createElement("img");
        var str = $(this).jqplotToImageStr(options);
        elem.src = str;
        return elem;
    };

    // return a string for an <img> element and return it.
    // Should work on canvas supporting browsers.
    $.fn.jqplotToImageElemStr = function(options) {
        var str = '<img src='+$(this).jqplotToImageStr(options)+' />';
        return str;
    };

    // Not gauranteed to work, even on canvas supporting browsers due to
    // limitations with location.href and browser support.
    $.fn.jqplotSaveImage = function() {
        var imgData = $(this).jqplotToImageStr({});
        if (imgData) {
            window.location.href = imgData.replace("image/png", "image/octet-stream");
        }

    };

    // Not gauranteed to work, even on canvas supporting browsers due to
    // limitations with window.open and arbitrary data.
    $.fn.jqplotViewImage = function() {
        var imgStr = $(this).jqplotToImageElemStr({});
        var imgData = $(this).jqplotToImageStr({});
        if (imgStr) {
            var w = window.open('');
            w.document.open("image/png");
            w.document.write(imgStr);
            w.document.close();
            w = null;
        }
    };

})(jQuery);<commit_msg>Fixes issue #528<commit_after>/**
 * jqPlot
 * Pure JavaScript plotting plugin using jQuery
 *
 * Version: @VERSION
 *
 * Copyright (c) 2009-2011 Chris Leonello
 * jqPlot is currently available for use in all personal or commercial projects
 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
 * choose the license that best suits your project and use it accordingly.
 *
 * Although not required, the author would appreciate an email letting him
 * know of any substantial use of jqPlot.  You can reach the author at:
 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
 *
 * If you are feeling kind and generous, consider supporting the project by
 * making a donation at: http://www.jqplot.com/donate.php .
 *
 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
 *
 *     version 2007.04.27
 *     author Ash Searle
 *     http://hexmen.com/blog/2007/03/printf-sprintf/
 *     http://hexmen.com/js/sprintf.js
 *     The author (Ash Searle) has placed this code in the public domain:
 *     "This code is unrestricted: you are free to use it however you like."
 *
 * jsDate library by Chris Leonello:
 *
 * Copyright (c) 2010-2011 Chris Leonello
 *
 * jsDate is currently available for use in all personal or commercial projects
 * under both the MIT and GPL version 2.0 licenses. This means that you can
 * choose the license that best suits your project and use it accordingly.
 *
 * jsDate borrows many concepts and ideas from the Date Instance
 * Methods by Ken Snyder along with some parts of Ken's actual code.
 * Ken has generously given permission to adapt his code and release
 * under the MIT and GPL V2 licenses.
 *
 * Ken's origianl Date Instance Methods and copyright notice:
 *
 * Ken Snyder (ken d snyder at gmail dot com)
 * 2008-09-10
 * version 2.0.2 (http://kendsnyder.com/sandbox/date/)
 * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
 *
 * jqplotToImage function based on Larry Siden's export-jqplot-to-png.js.
 * Larry has generously given permission to adapt his code for inclusion
 * into jqPlot.
 *
 * Larry's original code can be found here:
 *
 * https://github.com/lsiden/export-jqplot-to-png
 *
 *
 */

(function($) {

    $.fn.jqplotChildText = function() {
        return $(this).contents().filter(function() {
            return this.nodeType == 3;  // Node.TEXT_NODE not defined in I7
        }).text();
    };

    // Returns font style as abbreviation for "font" property.
    $.fn.jqplotGetComputedFontStyle = function() {
        var css = window.getComputedStyle ?  window.getComputedStyle(this[0], "") : this[0].currentStyle;
        var attrs = css['font-style'] ? ['font-style', 'font-weight', 'font-size', 'font-family'] : ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily'];
        var style = [];

        for (var i=0 ; i < attrs.length; ++i) {
            var attr = String(css[attrs[i]]);

            if (attr && attr != 'normal') {
                style.push(attr);
            }
        }
        return style.join(' ');
    };

    /**
     * Namespace: $.fn
     * jQuery namespace to attach functions to jQuery elements.
     *
     */

    $.fn.jqplotToImageCanvas = function(options) {

        options = options || {};
        var x_offset = (options.x_offset == null) ? 0 : options.x_offset;
        var y_offset = (options.y_offset == null) ? 0 : options.y_offset;
        var backgroundColor = (options.backgroundColor == null) ? 'rgb(255,255,255)' : options.backgroundColor;

        if ($(this).width() == 0 || $(this).height() == 0) {
            return null;
        }

        // excanvas and hence IE < 9 do not support toDataURL and cannot export images.
        if ($.jqplot.use_excanvas) {
            return null;
        }

        var newCanvas = document.createElement("canvas");
        var h = $(this).outerHeight(true);
        var w = $(this).outerWidth(true);
        var offs = $(this).offset();
        var plotleft = offs.left;
        var plottop = offs.top;
        var transx = 0, transy = 0;

        // have to check if any elements are hanging outside of plot area before rendering,
        // since changing width of canvas will erase canvas.

        var clses = ['jqplot-table-legend', 'jqplot-xaxis-tick', 'jqplot-x2axis-tick', 'jqplot-yaxis-tick', 'jqplot-y2axis-tick', 'jqplot-y3axis-tick',
        'jqplot-y4axis-tick', 'jqplot-y5axis-tick', 'jqplot-y6axis-tick', 'jqplot-y7axis-tick', 'jqplot-y8axis-tick', 'jqplot-y9axis-tick',
        'jqplot-xaxis-label', 'jqplot-x2axis-label', 'jqplot-yaxis-label', 'jqplot-y2axis-label', 'jqplot-y3axis-label', 'jqplot-y4axis-label',
        'jqplot-y5axis-label', 'jqplot-y6axis-label', 'jqplot-y7axis-label', 'jqplot-y8axis-label', 'jqplot-y9axis-label' ];

        var temptop, templeft, tempbottom, tempright;

        for (var i in clses) {
            $(this).find('.'+clses[i]).each(function() {
                temptop = $(this).offset().top - plottop;
                templeft = $(this).offset().left - plotleft;
                tempright = templeft + $(this).outerWidth(true) + transx;
                tempbottom = temptop + $(this).outerHeight(true) + transy;
                if (templeft < -transx) {
                    w = w - transx - templeft;
                    transx = -templeft;
                }
                if (temptop < -transy) {
                    h = h - transy - temptop;
                    transy = - temptop;
                }
                if (tempright > w) {
                    w = tempright;
                }
                if (tempbottom > h) {
                    h =  tempbottom;
                }
            });
        }

        newCanvas.width = w + Number(x_offset);
        newCanvas.height = h + Number(y_offset);

        var newContext = newCanvas.getContext("2d");

        newContext.save();
        newContext.fillStyle = backgroundColor;
        newContext.fillRect(0,0, newCanvas.width, newCanvas.height);
        newContext.restore();

        newContext.translate(transx, transy);
        newContext.textAlign = 'left';
        newContext.textBaseline = 'top';

        function getLineheight(el) {
            var lineheight = parseInt($(el).css('line-height'), 10);

            if (isNaN(lineheight)) {
                lineheight = parseInt($(el).css('font-size'), 10) * 1.2;
            }
            return lineheight;
        }

        function writeWrappedText (el, context, text, left, top, canvasWidth) {
            var lineheight = getLineheight(el);
            var tagwidth = $(el).innerWidth();
            var tagheight = $(el).innerHeight();
            var words = text.split(/\s+/);
            var wl = words.length;
            var w = '';
            var breaks = [];
            var temptop = top;
            var templeft = left;

            for (var i=0; i<wl; i++) {
                w += words[i];
                if (context.measureText(w).width > tagwidth) {
                    breaks.push(i);
                    w = '';
                }
            }
            if (breaks.length === 0) {
                // center text if necessary
                if ($(el).css('textAlign') === 'center') {
                    templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
                }
                context.fillText(text, templeft, top);
            }
            else {
                w = words.slice(0, breaks[0]).join(' ');
                // center text if necessary
                if ($(el).css('textAlign') === 'center') {
                    templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
                }
                context.fillText(w, templeft, temptop);
                temptop += lineheight;
                for (var i=1, l=breaks.length; i<l; i++) {
                    w = words.slice(breaks[i-1], breaks[i]).join(' ');
                    // center text if necessary
                    if ($(el).css('textAlign') === 'center') {
                        templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
                    }
                    context.fillText(w, templeft, temptop);
                    temptop += lineheight;
                }
                w = words.slice(breaks[i-1], words.length).join(' ');
                // center text if necessary
                if ($(el).css('textAlign') === 'center') {
                    templeft = left + (canvasWidth - context.measureText(w).width)/2  - transx;
                }
                context.fillText(w, templeft, temptop);
            }

        }

        function _jqpToImage(el, x_offset, y_offset) {
            var tagname = el.tagName.toLowerCase();
            var p = $(el).position();
            var css = window.getComputedStyle ?  window.getComputedStyle(el, "") : el.currentStyle; // for IE < 9
            var left = x_offset + p.left + parseInt(css.marginLeft, 10) + parseInt(css.borderLeftWidth, 10) + parseInt(css.paddingLeft, 10);
            var top = y_offset + p.top + parseInt(css.marginTop, 10) + parseInt(css.borderTopWidth, 10)+ parseInt(css.paddingTop, 10);
            var w = newCanvas.width;
            // var left = x_offset + p.left + $(el).css('marginLeft') + $(el).css('borderLeftWidth')

            // somehow in here, for divs within divs, the width of the inner div should be used instead of the canvas.

            if ((tagname == 'div' || tagname == 'span') && !$(el).hasClass('jqplot-highlighter-tooltip')) {
                $(el).children().each(function() {
                    _jqpToImage(this, left, top);
                });
                var text = $(el).jqplotChildText();

                if (text) {
                    newContext.font = $(el).jqplotGetComputedFontStyle();
                    newContext.fillStyle = $(el).css('color');

                    writeWrappedText(el, newContext, text, left, top, w);
                }
            }

            // handle the standard table legend

            else if (tagname === 'table' && $(el).hasClass('jqplot-table-legend')) {
                newContext.strokeStyle = $(el).css('border-top-color');
                newContext.fillStyle = $(el).css('background-color');
                newContext.fillRect(left, top, $(el).innerWidth(), $(el).innerHeight());
                if (parseInt($(el).css('border-top-width'), 10) > 0) {
                    newContext.strokeRect(left, top, $(el).innerWidth(), $(el).innerHeight());
                }

                // find all the swatches
                $(el).find('div.jqplot-table-legend-swatch-outline').each(function() {
                    // get the first div and stroke it
                    var elem = $(this);
                    newContext.strokeStyle = elem.css('border-top-color');
                    var l = left + elem.position().left;
                    var t = top + elem.position().top;
                    newContext.strokeRect(l, t, elem.innerWidth(), elem.innerHeight());

                    // now fill the swatch

                    l += parseInt(elem.css('padding-left'), 10);
                    t += parseInt(elem.css('padding-top'), 10);
                    var h = elem.innerHeight() - 2 * parseInt(elem.css('padding-top'), 10);
                    var w = elem.innerWidth() - 2 * parseInt(elem.css('padding-left'), 10);

                    var swatch = elem.children('div.jqplot-table-legend-swatch');
                    newContext.fillStyle = swatch.css('background-color');
                    newContext.fillRect(l, t, w, h);
                });

                // now add text

                $(el).find('td.jqplot-table-legend-label').each(function(){
                    var elem = $(this);
                    var l = left + elem.position().left;
                    var t = top + elem.position().top + parseInt(elem.css('padding-top'), 10);
                    newContext.font = elem.jqplotGetComputedFontStyle();
                    newContext.fillStyle = elem.css('color');
                    writeWrappedText(elem, newContext, elem.text(), l, t, w);
                });

                var elem = null;
            }

            else if (tagname == 'canvas') {
                newContext.drawImage(el, left, top);
            }
        }
        $(this).children().each(function() {
            _jqpToImage(this, x_offset, y_offset);
        });
        return newCanvas;
    };

    // return the raw image data string.
    // Should work on canvas supporting browsers.
    $.fn.jqplotToImageStr = function(options) {
        var imgCanvas = $(this).jqplotToImageCanvas(options);
        if (imgCanvas) {
            return imgCanvas.toDataURL("image/png");
        }
        else {
            return null;
        }
    };

    // return a DOM <img> element and return it.
    // Should work on canvas supporting browsers.
    $.fn.jqplotToImageElem = function(options) {
        var elem = document.createElement("img");
        var str = $(this).jqplotToImageStr(options);
        elem.src = str;
        return elem;
    };

    // return a string for an <img> element and return it.
    // Should work on canvas supporting browsers.
    $.fn.jqplotToImageElemStr = function(options) {
        var str = '<img src='+$(this).jqplotToImageStr(options)+' />';
        return str;
    };

    // Not gauranteed to work, even on canvas supporting browsers due to
    // limitations with location.href and browser support.
    $.fn.jqplotSaveImage = function() {
        var imgData = $(this).jqplotToImageStr({});
        if (imgData) {
            window.location.href = imgData.replace("image/png", "image/octet-stream");
        }

    };

    // Not gauranteed to work, even on canvas supporting browsers due to
    // limitations with window.open and arbitrary data.
    $.fn.jqplotViewImage = function() {
        var imgStr = $(this).jqplotToImageElemStr({});
        var imgData = $(this).jqplotToImageStr({});
        if (imgStr) {
            var w = window.open('');
            w.document.open("image/png");
            w.document.write(imgStr);
            w.document.close();
            w = null;
        }
    };

})(jQuery);
<|endoftext|>


<commit_before>jest.dontMock('../MesosStateStore');

const MesosStateUtil = require('../../utils/MesosStateUtil');
const Pod = require('../../../../plugins/services/src/js/structs/Pod');
const Framework = require('../../../../plugins/services/src/js/structs/Framework');
const MesosStateStore = require('../MesosStateStore');
const Application = require('../../../../plugins/services/src/js/structs/Application');
const Task = require('../../../../plugins/services/src/js/structs/Task');

const MESOS_STATE_WITH_HISTORY = require('../../utils/__tests__/fixtures/MesosStateWithHistory');

describe('MesosStateStore', function () {

  describe('#getTaskFromServiceName', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = function () {
        return {
          frameworks: [{
            name: 'marathon',
            tasks: [1, 2, 3]
          }]
        };
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return tasks of service with name that matches', function () {
      var result = MesosStateStore.getTasksFromServiceName('marathon');
      expect(result).toEqual([1, 2, 3]);
    });

    it('should null if no service matches', function () {
      var result = MesosStateStore.getTasksFromServiceName('nonExistent');
      expect(result).toEqual([]);
    });
  });

  describe('#getTasksByService', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = function () {
        return {
          frameworks: [
            {
              name: 'marathon',
              tasks: [
                {name: 'spark', id: 'spark.1'},
                {name: 'alpha', id: 'alpha.1'},
                {name: 'alpha', id: 'alpha.2'}
              ],
              completed_tasks: [
                {name: 'alpha', id: 'alpha.3'}
              ]
            },
            {
              name: 'spark',
              tasks: [
                {name: '1'},
                {name: '2'}
              ],
              completed_tasks: [
                {name: '3'}
              ]
            }
          ]
        };
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return matching framework tasks including scheduler tasks',
      function () {
        var tasks = MesosStateStore.getTasksByService(
          new Framework({id: '/spark', labels: {DCOS_PACKAGE_FRAMEWORK_NAME: 'spark'}})
        );
        expect(tasks).toEqual([
          {name: 'spark', id: 'spark.1'},
          {name: '1'},
          {name: '2'},
          {name: '3'}
        ]);
      }
    );

    it('should return matching application tasks', function () {
      var tasks = MesosStateStore.getTasksByService(
        new Application({id: '/alpha'})
      );
      expect(tasks).toEqual([
        {name: 'alpha', id: 'alpha.1'},
        {name: 'alpha', id: 'alpha.2'},
        {name: 'alpha', id: 'alpha.3'}
      ]);
    });

    it('should empty task list if no service matches', function () {
      var tasks = MesosStateStore.getTasksByService(
        new Application({id: '/non-existent'})
      );
      expect(tasks).toEqual([]);
    });
  });

  describe('#getNodeFromID', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = function () {
        return {
          slaves: [{
            id: 'amazon-thing',
            fakeProp: 'fake'
          }]
        };
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return the node with the correct ID', function () {
      var result = MesosStateStore.getNodeFromID('amazon-thing');
      expect(result.fakeProp).toEqual('fake');
    });

    it('should return null if node not found', function () {
      var result = MesosStateStore.getNodeFromID('nonExistentNode');
      expect(result).toEqual(undefined);
    });
  });

  describe('#getTaskFromTaskID', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      const data = {
        frameworks: [{
          tasks: [{id: 1}],
          completed_tasks: [{id: 2}]
        }]
      };
      MesosStateStore.processStateSuccess(data);
      const taskCache = MesosStateStore.indexTasksByID(data);
      MesosStateStore.get = function (id) {
        if (id === 'taskCache') {
          return taskCache;
        }
        return data;
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return null from an unknown task ID', function () {
      var result = MesosStateStore.getTaskFromTaskID('not-a-task-id');
      expect(result).toBeNull();
    });

    it('should return an instance of Task', function () {
      var result = MesosStateStore.getTaskFromTaskID(1);
      expect(result instanceof Task).toBeTruthy();
    });

    it('should find a currently running task', function () {
      var result = MesosStateStore.getTaskFromTaskID(1);
      expect(result.get()).toEqual({id: 1});
    });

    it('should find a completed task', function () {
      var result = MesosStateStore.getTaskFromTaskID(2);
      expect(result.get()).toEqual({id: 2});
    });
  });

  describe('#getSchedulerTasks', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return scheduler tasks', function () {
      MesosStateStore.get = function () {
        return {
          frameworks: [{
            name: 'marathon',
            tasks: [
              {
                id: 'foo',
                labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'foo'}]
              },
              {
                id: 'bar'
              },
              {
                id: 'baz',
                labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'baz'}]
              }
            ]
          }]
        };
      };

      const schedulerTasks = MesosStateStore.getSchedulerTasks();
      expect(schedulerTasks).toEqual([
        {
          id: 'foo',
          labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'foo'}]
        },
        {
          id: 'baz',
          labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'baz'}]
        }
      ]);
    });

    it('should not return plain tasks', function () {
      MesosStateStore.get = function () {
        return {
          frameworks: [{
            name: 'marathon',
            tasks: [
              {
                id: 'foo'
              }
            ]
          }]
        };
      };

      const schedulerTasks = MesosStateStore.getSchedulerTasks();
      expect(schedulerTasks).toEqual([]);
    });

  });

  describe('#getSchedulerTaskFromServiceName', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = function () {
        return {
          frameworks: [{
            name: 'marathon',
            tasks: [{
              id: 1,
              labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'foo'}]
            }]
          }]
        };
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should find a currently running task', function () {
      var result = MesosStateStore.getSchedulerTaskFromServiceName('foo');
      expect(result.id).toEqual(1);
    });

    it('shouldn\'t find a task', function () {
      var result = MesosStateStore.getSchedulerTaskFromServiceName('bar');
      expect(result).toEqual(undefined);
    });
  });

  describe('#getPodHistoricalInstances', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = () => {
        return MESOS_STATE_WITH_HISTORY;
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should pass-through to MesosStateUtil.getPodHistoricalInstances', function () {
      var pod = new Pod({id: '/pod-p0'});
      var result = MesosStateStore.getPodHistoricalInstances(pod);
      var expected = MesosStateUtil.getPodHistoricalInstances(MESOS_STATE_WITH_HISTORY, pod);
      expect(result).toEqual(expected);
    });
  });
});
<commit_msg>Improve Mesos state store scheduler tests<commit_after>jest.dontMock('../MesosStateStore');

const MesosStateUtil = require('../../utils/MesosStateUtil');
const Pod = require('../../../../plugins/services/src/js/structs/Pod');
const Framework = require('../../../../plugins/services/src/js/structs/Framework');
const MesosStateStore = require('../MesosStateStore');
const Application = require('../../../../plugins/services/src/js/structs/Application');
const Task = require('../../../../plugins/services/src/js/structs/Task');

const MESOS_STATE_WITH_HISTORY = require('../../utils/__tests__/fixtures/MesosStateWithHistory');

describe('MesosStateStore', function () {

  describe('#getTaskFromServiceName', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = function () {
        return {
          frameworks: [{
            name: 'marathon',
            tasks: [1, 2, 3]
          }]
        };
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return tasks of service with name that matches', function () {
      var result = MesosStateStore.getTasksFromServiceName('marathon');
      expect(result).toEqual([1, 2, 3]);
    });

    it('should null if no service matches', function () {
      var result = MesosStateStore.getTasksFromServiceName('nonExistent');
      expect(result).toEqual([]);
    });
  });

  describe('#getTasksByService', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = function () {
        return {
          frameworks: [
            {
              name: 'marathon',
              tasks: [
                {name: 'spark', id: 'spark.1'},
                {name: 'alpha', id: 'alpha.1'},
                {name: 'alpha', id: 'alpha.2'}
              ],
              completed_tasks: [
                {name: 'alpha', id: 'alpha.3'}
              ]
            },
            {
              name: 'spark',
              tasks: [
                {name: '1'},
                {name: '2'}
              ],
              completed_tasks: [
                {name: '3'}
              ]
            }
          ]
        };
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return matching framework tasks including scheduler tasks',
      function () {
        var tasks = MesosStateStore.getTasksByService(
          new Framework({id: '/spark', labels: {DCOS_PACKAGE_FRAMEWORK_NAME: 'spark'}})
        );
        expect(tasks).toEqual([
          {name: 'spark', id: 'spark.1'},
          {name: '1'},
          {name: '2'},
          {name: '3'}
        ]);
      }
    );

    it('should return matching application tasks', function () {
      var tasks = MesosStateStore.getTasksByService(
        new Application({id: '/alpha'})
      );
      expect(tasks).toEqual([
        {name: 'alpha', id: 'alpha.1'},
        {name: 'alpha', id: 'alpha.2'},
        {name: 'alpha', id: 'alpha.3'}
      ]);
    });

    it('should empty task list if no service matches', function () {
      var tasks = MesosStateStore.getTasksByService(
        new Application({id: '/non-existent'})
      );
      expect(tasks).toEqual([]);
    });
  });

  describe('#getNodeFromID', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = function () {
        return {
          slaves: [{
            id: 'amazon-thing',
            fakeProp: 'fake'
          }]
        };
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return the node with the correct ID', function () {
      var result = MesosStateStore.getNodeFromID('amazon-thing');
      expect(result.fakeProp).toEqual('fake');
    });

    it('should return null if node not found', function () {
      var result = MesosStateStore.getNodeFromID('nonExistentNode');
      expect(result).toEqual(undefined);
    });
  });

  describe('#getTaskFromTaskID', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      const data = {
        frameworks: [{
          tasks: [{id: 1}],
          completed_tasks: [{id: 2}]
        }]
      };
      MesosStateStore.processStateSuccess(data);
      const taskCache = MesosStateStore.indexTasksByID(data);
      MesosStateStore.get = function (id) {
        if (id === 'taskCache') {
          return taskCache;
        }
        return data;
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return null from an unknown task ID', function () {
      var result = MesosStateStore.getTaskFromTaskID('not-a-task-id');
      expect(result).toBeNull();
    });

    it('should return an instance of Task', function () {
      var result = MesosStateStore.getTaskFromTaskID(1);
      expect(result instanceof Task).toBeTruthy();
    });

    it('should find a currently running task', function () {
      var result = MesosStateStore.getTaskFromTaskID(1);
      expect(result.get()).toEqual({id: 1});
    });

    it('should find a completed task', function () {
      var result = MesosStateStore.getTaskFromTaskID(2);
      expect(result.get()).toEqual({id: 2});
    });
  });

  describe('#getSchedulerTasks', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return scheduler tasks', function () {
      MesosStateStore.get = function () {
        return {
          frameworks: [{
            name: 'marathon',
            tasks: [
              {
                id: 'foo',
                labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'foo'}]
              },
              {
                id: 'bar'
              },
              {
                id: 'baz',
                labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'baz'}]
              }
            ]
          }]
        };
      };

      const schedulerTasks = MesosStateStore.getSchedulerTasks();
      expect(schedulerTasks).toEqual([
        {
          id: 'foo',
          labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'foo'}]
        },
        {
          id: 'baz',
          labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'baz'}]
        }
      ]);
    });

    it('should not return plain tasks', function () {
      MesosStateStore.get = function () {
        return {
          frameworks: [{
            name: 'marathon',
            tasks: [
              {
                id: 'foo'
              }
            ]
          }]
        };
      };

      const schedulerTasks = MesosStateStore.getSchedulerTasks();
      expect(schedulerTasks).toEqual([]);
    });

  });

  describe('#getSchedulerTaskFromServiceName', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = function () {
        return {
          frameworks: [{
            name: 'marathon',
            tasks: [
              {
                id: 'foo',
                labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'foo'}]
              },
              {
                id: 'bar'
              }
            ]
          }]
        };
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should return the matching scheduler task', function () {
      const schedulerTask =
        MesosStateStore.getSchedulerTaskFromServiceName('foo');

      expect(schedulerTask).toEqual({
        id: 'foo',
        labels: [{key: 'DCOS_PACKAGE_FRAMEWORK_NAME', value: 'foo'}]
      });
    });

    it('should return undefined if no matching scheduler task was found',
      function () {
        const schedulerTask =
          MesosStateStore.getSchedulerTaskFromServiceName('bar');

        expect(schedulerTask).toEqual(undefined);
      }
    );
  });

  describe('#getPodHistoricalInstances', function () {
    beforeEach(function () {
      this.get = MesosStateStore.get;
      MesosStateStore.get = () => {
        return MESOS_STATE_WITH_HISTORY;
      };
    });

    afterEach(function () {
      MesosStateStore.get = this.get;
    });

    it('should pass-through to MesosStateUtil.getPodHistoricalInstances', function () {
      var pod = new Pod({id: '/pod-p0'});
      var result = MesosStateStore.getPodHistoricalInstances(pod);
      var expected = MesosStateUtil.getPodHistoricalInstances(MESOS_STATE_WITH_HISTORY, pod);
      expect(result).toEqual(expected);
    });
  });
});
<|endoftext|>