<commit_before>'use strict';

/**
 * TasksController
 * controller for the view of tasks
 */
tasksModule.controller('TasksController',
    ['$scope', '$rootScope', '$state', '$stateParams', '$timeout', '$filter', '$http', 'fileSystemService', 'tasksService', '$cookies', '$interval',
        function ($scope, $rootScope, $state, $stateParams, $timeout, $filter, $http, fileSystemService, tasksService, $cookies, $interval) {

            console.log("tasks!");

            $scope.myMessages = [];
            $scope.validTasks = {"no": "...not loaded..."};
            $scope.availableContainers = undefined;

            $scope.newTaskDefinition = {
                "type": "",
                "container": "",
                "tenant": $cookies.get('MCM-TENANT'),
                "token": $cookies.get('XSRF-TOKEN')
            };
            $scope.credentials = {
                "tenant": $cookies.get('MCM-TENANT'),
                "token": $cookies.get('XSRF-TOKEN')
            };

            /**
             *
             * Get the list of valid tasks
             *
             * */
            tasksService.getValidTasks()
                .then(function (response) {
                        $scope.validTasks = response.data;
                        if (!response.data) {
                            $rootScope.$broadcast('FlashMessage', {
                                "type": "danger",
                                "text": "unable to retrieve task list..."
                            });
                        }
                    },
                    function errorCallback(response) {
                        console.log(JSON.stringify(response));
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "danger",
                            "text": "Error: "
                            + response.data
                        });
                    });

            /**
             *
             * Get the list of container from swift
             *
             * */

            fileSystemService.getContainers("", "", 10000)
                .then(function (response) {
                    $scope.availableContainers = response.containers;
                    //console.log($scope.availableContainers);
                })
                .catch(function (response) {
                    if (401 == response.status) {
                        $state.go('loginState', {noAuth: true});
                        return;
                    }
                    $rootScope.$broadcast('FlashMessage', {
                        "type": "warning",
                        "text": response.data
                    });
                });

            /**
             *
             * send a new message
             *
             * */

            $scope.sendMessage = function () {
                tasksService.postMessage($scope.newTaskDefinition)
                    .then(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "success",
                            "text": "message sent"
                        });
                        //console.log($scope.availableContainers);
                    })
                    .catch(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "warning",
                            "text": response.data
                        });
                    })
            };

            /**
             *
             * receive the messages
             *
             * */

            $scope.receive_from_beginning = function () {
                tasksService.retrieveMessages($scope.credentials, true)
                    .then(function (response) {
                        $scope.myMessages = $scope.myMessages.concat(response.data);
                    })
                    .catch(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "warning",
                            "text": response.data
                        });
                    })
            };

            var receive = function () {
                tasksService.retrieveMessages($scope.credentials, false)
                    .then(function (response) {
                        $scope.myMessages = $scope.myMessages.concat(response.data);
                        //console.log(response.data);
                        $interval(function () {
                            receive();
                        }, 2000, 1);
                    })
                    .catch(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "warning",
                            "text": response.data
                        });
                    })
            };
            receive();

            /**
             *
             * clear the existing messages
             *
             * */
            $scope.clear_all_messages = function () {
                $scope.myMessages = [];
            };

            /**
             *
             * helper for html styling
             * */
            $scope.ui_color_for_msg = function (msg) {
                try {
                    return '#' + msg.correlation.substring(0,6);
                }
                 catch(err)
                     {
                    return "white";
                }

            };

            /**
             *
             * helper for html styling
             * */
            $scope.ui_icon_for_msg = function (msg) {
                try {
                    if (msg.type in $scope.validTasks) {
                        return "flight_takeoff";
                    } else if (msg.type.startsWith("response")) {
                        return "flight_landing";
                    } else{
                        return "code";
                    }
                }
                 catch(err)
                     {
                    return "code";
                }

            };

        }]);
<commit_msg>wrong color...<commit_after>'use strict';

/**
 * TasksController
 * controller for the view of tasks
 */
tasksModule.controller('TasksController',
    ['$scope', '$rootScope', '$state', '$stateParams', '$timeout', '$filter', '$http', 'fileSystemService', 'tasksService', '$cookies', '$interval',
        function ($scope, $rootScope, $state, $stateParams, $timeout, $filter, $http, fileSystemService, tasksService, $cookies, $interval) {

            console.log("tasks!");

            $scope.myMessages = [];
            $scope.validTasks = {"no": "...not loaded..."};
            $scope.availableContainers = undefined;

            $scope.newTaskDefinition = {
                "type": "",
                "container": "",
                "tenant": $cookies.get('MCM-TENANT'),
                "token": $cookies.get('XSRF-TOKEN')
            };
            $scope.credentials = {
                "tenant": $cookies.get('MCM-TENANT'),
                "token": $cookies.get('XSRF-TOKEN')
            };

            /**
             *
             * Get the list of valid tasks
             *
             * */
            tasksService.getValidTasks()
                .then(function (response) {
                        $scope.validTasks = response.data;
                        if (!response.data) {
                            $rootScope.$broadcast('FlashMessage', {
                                "type": "danger",
                                "text": "unable to retrieve task list..."
                            });
                        }
                    },
                    function errorCallback(response) {
                        console.log(JSON.stringify(response));
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "danger",
                            "text": "Error: "
                            + response.data
                        });
                    });

            /**
             *
             * Get the list of container from swift
             *
             * */

            fileSystemService.getContainers("", "", 10000)
                .then(function (response) {
                    $scope.availableContainers = response.containers;
                    //console.log($scope.availableContainers);
                })
                .catch(function (response) {
                    if (401 == response.status) {
                        $state.go('loginState', {noAuth: true});
                        return;
                    }
                    $rootScope.$broadcast('FlashMessage', {
                        "type": "warning",
                        "text": response.data
                    });
                });

            /**
             *
             * send a new message
             *
             * */

            $scope.sendMessage = function () {
                tasksService.postMessage($scope.newTaskDefinition)
                    .then(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "success",
                            "text": "message sent"
                        });
                        //console.log($scope.availableContainers);
                    })
                    .catch(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "warning",
                            "text": response.data
                        });
                    })
            };

            /**
             *
             * receive the messages
             *
             * */

            $scope.receive_from_beginning = function () {
                tasksService.retrieveMessages($scope.credentials, true)
                    .then(function (response) {
                        $scope.myMessages = $scope.myMessages.concat(response.data);
                    })
                    .catch(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "warning",
                            "text": response.data
                        });
                    })
            };

            var receive = function () {
                tasksService.retrieveMessages($scope.credentials, false)
                    .then(function (response) {
                        $scope.myMessages = $scope.myMessages.concat(response.data);
                        //console.log(response.data);
                        $interval(function () {
                            receive();
                        }, 2000, 1);
                    })
                    .catch(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "warning",
                            "text": response.data
                        });
                    })
            };
            receive();

            /**
             *
             * clear the existing messages
             *
             * */
            $scope.clear_all_messages = function () {
                $scope.myMessages = [];
            };

            /**
             *
             * helper for html styling
             * */
            $scope.ui_color_for_msg = function (msg) {
                try {
                    return '#' + msg.correlation.substring(0,6);
                }
                 catch(err)
                     {
                    return "black";
                }

            };

            /**
             *
             * helper for html styling
             * */
            $scope.ui_icon_for_msg = function (msg) {
                try {
                    if (msg.type in $scope.validTasks) {
                        return "flight_takeoff";
                    } else if (msg.type.startsWith("response")) {
                        return "flight_landing";
                    } else{
                        return "code";
                    }
                }
                 catch(err)
                     {
                    return "code";
                }

            };

        }]);
<|endoftext|>


<commit_before>'use strict';

/**
 * TasksController
 * controller for the view of tasks
 */
tasksModule.controller('TasksController',
    ['$scope', '$rootScope', '$state', '$stateParams', '$timeout', '$filter', '$http', 'fileSystemService', 'tasksService', '$cookies', '$interval',
        function ($scope, $rootScope, $state, $stateParams, $timeout, $filter, $http, fileSystemService, tasksService, $cookies, $interval) {

            console.log("tasks!");

            $scope.myMessages = [];
            $scope.myKeys = [];
            $scope.validTasks = {"no": "...not loaded..."};
            $scope.availableContainers = undefined;

            $scope.newTaskDefinition = {
                "type": $stateParams.task,
                "container": $stateParams.container,
                "tenant": $cookies.get('MCM-TENANT'),
                "token": $cookies.get('XSRF-TOKEN')
            };
            $scope.credentials = {
                "tenant": $cookies.get('MCM-TENANT'),
                "token": $cookies.get('XSRF-TOKEN')
            };

            /**
             *
             * Get the list of valid tasks
             *
             * */
            tasksService.getValidTasks()
                .then(function (response) {
                    $scope.validTasks = response.data;
                });

            /**
             *
             * Get the list of container from swift
             *
             * */

            fileSystemService.getContainers("", "", 10000)
                .then(function (response) {
                    $scope.availableContainers = response.containers;
                });

            /**
             *
             * send a new message
             *
             * */

            $scope.sendMessage = function () {
                tasksService.postMessage($scope.newTaskDefinition)
                    .then(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "success",
                            "text": "message sent"
                        });
                        //console.log($scope.availableContainers);
                    })
            };
            /**
            helper function to add response data to messages
            */
            var addMsgs= function (resp) {
                var idx=$scope.myKeys.indexOf(resp.correlation);
                if(idx==-1)
                {
                    $scope.myKeys.unshift(resp.correlation);
                    $scope.myMessages.unshift([resp]);
                }
                else{
                    $scope.myMessages[idx].push(resp);
                }
		     };
            /**
             *
             * receive the messages
             *
             * */
            $scope.receive_from_beginning = function () {
                tasksService.retrieveMessages($scope.credentials, true)
                    .then(function (response) {
                        for (var i = 0; i < response.data.length; i++) {
                            addMsgs(response.data[i]);
                        }
                    })
            };

            var receive = function () {
                tasksService.retrieveMessages($scope.credentials, false)
                    .then(function (response) {
                        if (response.data) {
                            for (var i = 0; i < response.data.length; i++) {
                               addMsgs(response.data[i]);
                            }
                            $interval(function () {
                                receive();
                            }, 2000, 1);
                        }
                    })
            };
            receive();

            /**
             *
             * clear the existing messages
             *
             * */
            $scope.clear_all_messages = function () {
                $scope.myMessages = {};
            };

            /**
             *
             * helper for html styling
             * */
            $scope.ui_color_for_msg = function (msg) {

                try {
                    return '#' + msg.correlation.substring(0, 6);
                }
                catch (err) {
                    return "black";
                }

            };

            /**
             *
             * helper for html styling
             * */
            $scope.ui_icon_for_msg = function (msg) {
                try {
                    if (msg.type in $scope.validTasks) {
                        return "call_made";
                    } else if (msg.type.startsWith("error")) {
                        return "error";
                    } else if (msg.type.startsWith("processing")) {
                        return "settings";
                    } else if (msg.type.startsWith("pong")) {
                        return "call_missed_outgoing";
                    } else if (msg.type.startsWith("response")) {
                        return "call_received";
                    } else if (msg.type.startsWith("success")) {
                        return "done";
                    } else {
                        return "code";
                    }
                }
                catch (err) {
                    return "code";
                }

            };

            $scope.txt_for_msg = function (msg) {
                try {
                    if (msg.type in $scope.validTasks) {
                        return "tenant:"+msg.tenant+" > worker:"+msg.worker;
                    } else if (msg.type.startsWith("processing")||msg.type.startsWith("pong")) {
                        return "worker:"+msg.worker;
                    } else if (msg.type.startsWith("success")) {
                        return msg.message.substring(msg.message.lastIndexOf("finished:")+"finished:".length) || msg.substring(msg.lastIndexOf("finished:")+"finished:".length);
                    } else {
                        return msg;
                    }
                }
                catch (err) {
                    return msg;
                }

            };

        }]);
<commit_msg>fixed bug when clearing tasks list and loading new tasks<commit_after>'use strict';

/**
 * TasksController
 * controller for the view of tasks
 */
tasksModule.controller('TasksController',
    ['$scope', '$rootScope', '$state', '$stateParams', '$timeout', '$filter', '$http', 'fileSystemService', 'tasksService', '$cookies', '$interval',
        function ($scope, $rootScope, $state, $stateParams, $timeout, $filter, $http, fileSystemService, tasksService, $cookies, $interval) {

            console.log("tasks!");

            $scope.myMessages = [];
            $scope.myKeys = [];
            $scope.validTasks = {"no": "...not loaded..."};
            $scope.availableContainers = undefined;

            $scope.newTaskDefinition = {
                "type": $stateParams.task,
                "container": $stateParams.container,
                "tenant": $cookies.get('MCM-TENANT'),
                "token": $cookies.get('XSRF-TOKEN')
            };
            $scope.credentials = {
                "tenant": $cookies.get('MCM-TENANT'),
                "token": $cookies.get('XSRF-TOKEN')
            };

            /**
             *
             * Get the list of valid tasks
             *
             * */
            tasksService.getValidTasks()
                .then(function (response) {
                    $scope.validTasks = response.data;
                });

            /**
             *
             * Get the list of container from swift
             *
             * */

            fileSystemService.getContainers("", "", 10000)
                .then(function (response) {
                    $scope.availableContainers = response.containers;
                });

            /**
             *
             * send a new message
             *
             * */

            $scope.sendMessage = function () {
                tasksService.postMessage($scope.newTaskDefinition)
                    .then(function (response) {
                        $rootScope.$broadcast('FlashMessage', {
                            "type": "success",
                            "text": "message sent"
                        });
                        //console.log($scope.availableContainers);
                    })
            };
            /**
            helper function to add response data to messages
            */
            var addMsgs= function (resp) {
                var idx=$scope.myKeys.indexOf(resp.correlation);
                if(idx==-1)
                {
                    $scope.myKeys.unshift(resp.correlation);
                    $scope.myMessages.unshift([resp]);
                }
                else{
                    $scope.myMessages[idx].push(resp);
                }
		     };
            /**
             *
             * receive the messages
             *
             * */
            $scope.receive_from_beginning = function () {
                tasksService.retrieveMessages($scope.credentials, true)
                    .then(function (response) {
                        $scope.clear_all_messages();
                        for (var i = 0; i < response.data.length; i++) {
                            addMsgs(response.data[i]);
                        }
                    })
            };

            var receive = function () {
                tasksService.retrieveMessages($scope.credentials, false)
                    .then(function (response) {
                        if (response.data) {
                            for (var i = 0; i < response.data.length; i++) {
                               addMsgs(response.data[i]);
                            }
                            $interval(function () {
                                receive();
                            }, 2000, 1);
                        }
                    })
            };
            receive();

            /**
             *
             * clear the existing messages
             *
             * */
            $scope.clear_all_messages = function () {
                $scope.myMessages = [];
                $scope.myKeys = [];
            };

            /**
             *
             * helper for html styling
             * */
            $scope.ui_color_for_msg = function (msg) {

                try {
                    return '#' + msg.correlation.substring(0, 6);
                }
                catch (err) {
                    return "black";
                }

            };

            /**
             *
             * helper for html styling
             * */
            $scope.ui_icon_for_msg = function (msg) {
                try {
                    if (msg.type in $scope.validTasks) {
                        return "call_made";
                    } else if (msg.type.startsWith("error")) {
                        return "error";
                    } else if (msg.type.startsWith("processing")) {
                        return "settings";
                    } else if (msg.type.startsWith("pong")) {
                        return "call_missed_outgoing";
                    } else if (msg.type.startsWith("response")) {
                        return "call_received";
                    } else if (msg.type.startsWith("success")) {
                        return "done";
                    } else {
                        return "code";
                    }
                }
                catch (err) {
                    return "code";
                }

            };

            $scope.txt_for_msg = function (msg) {
                try {
                    if (msg.type in $scope.validTasks) {
                        return "tenant:"+msg.tenant+" > worker:"+msg.worker;
                    } else if (msg.type.startsWith("processing")||msg.type.startsWith("pong")) {
                        return "worker:"+msg.worker;
                    } else if (msg.type.startsWith("success")) {
                        return msg.message.substring(msg.message.lastIndexOf("finished:")+"finished:".length) || msg.substring(msg.lastIndexOf("finished:")+"finished:".length);
                    } else {
                        return msg;
                    }
                }
                catch (err) {
                    return msg;
                }

            };

        }]);
<|endoftext|>


<commit_before>/***********************************************************************
 * Header Directive
 * Author: Brenton Klik
 *
 * Prerequisites:
 *  - AngularJS
 *  - styleSheetFactory (https://github.com/bklik/styleSheetFactory)
 *
 * Description:
 * Creates a header control with responsive sub navigation.
/**********************************************************************/
angular.module('headerDirective', ['styleSheetFactory'])

.directive('header', ['$timeout', 'styleSheetFactory', function($timeout, styleSheetFactory) {
    return {
        scope: {
            api: '=',
        },
        restrict: 'E',
        link: function($scope, $element, $attrs) {
            // The document's stylesheet.
            var styleSheet = styleSheetFactory.getStyleSheet();

            // The prefix used by the browser for non-standard properties.
            var prefix = styleSheetFactory.getPrefix();

            // Add this directive's styles to the document's stylesheet.
            styleSheetFactory.addCSSRule(styleSheet, 'header',
                'background-color: black;' +
                'color: white;' +
                'display: block;' +
                'overflow: hidden;' +
                'position: fixed;' +
                'top: 0;' +
                'right: 0;' +
                'left: 0;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header.animate',
                'box-shadow: 0 2px 8px rgba(0, 0, 0, .25);' +
                'transition: top ease 250ms;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav',
                'background-color: grey;' +
                'color: white;' +
                'display: block;' +
                'height: 48px;' +
                'overflow: hidden;' +
                'position: relative;' +
                '-'+prefix+'-transition: all ease 250ms;' +
                'transition: all ease 250ms;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav:after',
                'content: \'\\25be\';' +
                'display: none;' +
                'line-height: 48px;' +
                'text-align: center;' +
                '-'+prefix+'-transition: all ease 250ms;' +
                'transition: all ease 250ms;' +
                'position: absolute;' +
                'top: 0;' +
                'right: 0;' +
                'height: 48px;' +
                'width: 48px;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav.show:after',
                '-'+prefix+'-transform: rotate(180deg);' +
                'transform: rotate(180deg);'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav button',
                'border-bottom: 0px solid white;' +
                'cursor: pointer;' +
                'display: block;' +
                'float: left;' +
                'height: 48px;' +
                'overflow: hidden;' +
                'padding: 0 16px;' +
                'text-overflow: ellipsis;' +
                '-'+prefix+'-transition: border-bottom-width ease 250ms;' +
                'transition: border-bottom-width ease 250ms;' +
                'white-space: nowrap;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav button.selected',
                'border-bottom-width: 4px;' +
                'max-height: 48px !important;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav.show button',
                'max-height: 48px !important;'
            ,1);

            /***********************************************************************
             * Function: scrollHander
             *
             * Description:
             * If the user is scrolling up, allow the header to animate.
             *
             * If the distance from the top if <= to the height of the main header
             * stop animating the header.
             *
             * If the user is scrolling up, faster than the tolerance, show the
             * sub navigation.
             *
             * If the user is scrolling down, faster than the tolerance, hide
             * the header.
            /**********************************************************************/
            var headerHeight = null;
            var navHeight = null;
            var oldScroll = 0;
            var scrolltolerance = 7;
            var scrollUp = false;

            var scrollHander = function(event) {
                var top = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
                var height = document.body.scrollHeight;

                scrollUp = (top < oldScroll) ? true : false;

                if(!scrollUp) {
                    $element.addClass('animate');
                }

                if(top <= 56) {
                    $element.removeClass('animate');
                    $element.attr('style', 'top: -'+top+'px;');
                } else if(top <= 200 || oldScroll - top > scrolltolerance) {
                    $element.attr('style', 'top: -'+(headerHeight - navHeight)+'px;');
                } else if(!scrollUp && top - oldScroll > scrolltolerance) {
                    $element.attr('style', 'top: -'+headerHeight+'px;');
                }

                oldScroll = top;
            };

            /***********************************************************************
             * Function: tabHandler
             *
             * Description:
             * When a button is clicked in the sub navigation, remove all selected
             * styles from the buttons.
             *
             * Crawl up the DOM from the target, until you reach the button (incase
             * there are other elements in the button).
             *
             * If the sub navigation meets the responsive criteria, show the all the
             * sub navigation buttons. Otherwise, cause a selection, and hide the
             * sub navigation (if applicable).
            /**********************************************************************/
            var nav = null;
            var responsiveMQ = null;

            var tabHandler = function(event) {
                $element.find('button').removeClass('selected');

                var parent = event.target;
                while(parent.nodeName != 'BUTTON' || typeof parent.nodeName == 'undefined') {
                    parent = parent.parentNode;
                }

                if(responsiveMQ.matches && !nav.hasClass('show')) {
                    nav.addClass('show');
                } else {
                    nav.removeClass('show');
                    window.scrollTo(0, 0);
                }

                parent.classList.add('selected');
            };

            var resizeHandler = function(event) {
                nav.removeClass('show');
            }

            /***********************************************************************
             * Function: configureResponsiveNav
             *
             * Description:
             * Detect the width of all the buttons in sub navigation.
             *
             * Add a rule for a responsive sub navigation menu.
             *
             * Add a Javascript media query of the same responsive rule.
            /**********************************************************************/
            var configureResponsiveNav = function() {
                var scrollbarOffset = 17;
                var navWidth = 0;
                var buttons = $element.find('button');

                for(var i=0; i<buttons.length; i++) {
                    navWidth += buttons[i].clientWidth;
                }

                styleSheetFactory.addCSSRule(styleSheet, '@media screen and (max-width: '+(navWidth + scrollbarOffset)+'px)',
                    'header nav:after {' +
                        'display: block !important;' +
                    '}' +
                    'header nav button {' +
                        'border-bottom-width: 0 !important;' +
                        'float: auto;' +
                        'max-height: 0;' +
                        'text-align: left;' +
                        'width: 100%;' +
                    '}'
                ,1);

                styleSheetFactory.addCSSRule(styleSheet, 'header nav.show',
                    'height: '+(48 * buttons.length)+'px;'
                ,1);

                responsiveMQ = window.matchMedia('(max-width: '+(navWidth + scrollbarOffset)+'px)');
            };

            /***********************************************************************
             * Function: init
             *
             * Description:
             * Initialize the component with it has been fully drawn to the DOM.
            /**********************************************************************/
            var init = function() {
                // Configure responsive nav classes
                nav = $element.find('nav');
                configureResponsiveNav();

                // Calculate heights
                headerHeight = $element[0].clientHeight;
                navHeight = nav[0].clientHeight;

                // Add events
                $element.find('button').bind('click', tabHandler);
                window.addEventListener('scroll', scrollHander, true);
                window.addEventListener('resize', resizeHandler, true);
                scrollHander(null);
            };
            $timeout(init, 0);
        }
    }
}]);
<commit_msg>Allowed the responsive nav to be dismissed by clicking outside the nav area.<commit_after>/***********************************************************************
 * Header Directive
 * Author: Brenton Klik
 *
 * Prerequisites:
 *  - AngularJS
 *  - styleSheetFactory (https://github.com/bklik/styleSheetFactory)
 *
 * Description:
 * Creates a header control with responsive sub navigation.
/**********************************************************************/
angular.module('headerDirective', ['styleSheetFactory'])

.directive('header', ['$timeout', 'styleSheetFactory', function($timeout, styleSheetFactory) {
    return {
        scope: {
            api: '=',
        },
        restrict: 'E',
        link: function($scope, $element, $attrs) {
            // The document's stylesheet.
            var styleSheet = styleSheetFactory.getStyleSheet();

            // The prefix used by the browser for non-standard properties.
            var prefix = styleSheetFactory.getPrefix();

            // Add this directive's styles to the document's stylesheet.
            styleSheetFactory.addCSSRule(styleSheet, 'header',
                'background-color: black;' +
                'color: white;' +
                'display: block;' +
                'overflow: hidden;' +
                'position: fixed;' +
                'top: 0;' +
                'right: 0;' +
                'left: 0;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header.animate',
                'box-shadow: 0 2px 8px rgba(0, 0, 0, .25);' +
                'transition: top ease 250ms;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav',
                'background-color: grey;' +
                'color: white;' +
                'display: block;' +
                'height: 48px;' +
                'overflow: hidden;' +
                'position: relative;' +
                '-'+prefix+'-transition: all ease 250ms;' +
                'transition: all ease 250ms;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav:after',
                'content: \'\\25be\';' +
                'display: none;' +
                'line-height: 48px;' +
                'text-align: center;' +
                '-'+prefix+'-transition: all ease 250ms;' +
                'transition: all ease 250ms;' +
                'position: absolute;' +
                'top: 0;' +
                'right: 0;' +
                'height: 48px;' +
                'width: 48px;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav.show:after',
                '-'+prefix+'-transform: rotate(180deg);' +
                'transform: rotate(180deg);'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav button',
                'border-bottom: 0px solid white;' +
                'cursor: pointer;' +
                'display: block;' +
                'float: left;' +
                'height: 48px;' +
                'overflow: hidden;' +
                'padding: 0 16px;' +
                'text-overflow: ellipsis;' +
                '-'+prefix+'-transition: border-bottom-width ease 250ms;' +
                'transition: border-bottom-width ease 250ms;' +
                'white-space: nowrap;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav button.selected',
                'border-bottom-width: 4px;' +
                'max-height: 48px !important;'
            ,1);

            styleSheetFactory.addCSSRule(styleSheet, 'header nav.show button',
                'max-height: 48px !important;'
            ,1);

            /***********************************************************************
             * Function: scrollHander
             *
             * Description:
             * If the user is scrolling up, allow the header to animate.
             *
             * If the distance from the top if <= to the height of the main header
             * stop animating the header.
             *
             * If the user is scrolling up, faster than the tolerance, show the
             * sub navigation.
             *
             * If the user is scrolling down, faster than the tolerance, hide
             * the header.
            /**********************************************************************/
            var headerHeight = null;
            var navHeight = null;
            var oldScroll = 0;
            var scrolltolerance = 7;
            var scrollUp = false;

            var scrollHander = function(event) {
                var top = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
                var height = document.body.scrollHeight;

                scrollUp = (top < oldScroll) ? true : false;

                if(!scrollUp) {
                    $element.addClass('animate');
                }

                if(!nav.hasClass('show')) {
                    if(top <= 56) {
                        $element.removeClass('animate');
                        $element.attr('style', 'top: -'+top+'px;');
                    } else if(top <= 200 || oldScroll - top > scrolltolerance) {
                        $element.attr('style', 'top: -'+(headerHeight - navHeight)+'px;');
                    } else if(!scrollUp && top - oldScroll > scrolltolerance) {
                        $element.attr('style', 'top: -'+headerHeight+'px;');
                    }
                }

                oldScroll = top;
            };

            /***********************************************************************
             * Function: tabHandler
             *
             * Description:
             * When a button is clicked in the sub navigation, remove all selected
             * styles from the buttons.
             *
             * Crawl up the DOM from the target, until you reach the button (incase
             * there are other elements in the button).
             *
             * If the sub navigation meets the responsive criteria, show the all the
             * sub navigation buttons. Otherwise, cause a selection, and hide the
             * sub navigation (if applicable).
            /**********************************************************************/
            var nav = null;
            var responsiveMQ = null;

            var tabHandler = function(event) {
                $element.find('button').removeClass('selected');

                var parent = event.target;
                while(parent.nodeName != 'BUTTON' || typeof parent.nodeName == 'undefined') {
                    parent = parent.parentNode;
                }

                if(responsiveMQ.matches && !nav.hasClass('show')) {
                    nav.addClass('show');
                    $timeout(function() {
                        window.addEventListener('click', closeSelect, false);
                    },100);
                } else {
                    window.scrollTo(0, 0);
                }

                parent.classList.add('selected');
            };

            // Allows the responsive nav to be dismissed
            var closeSelect = function() {
                if(nav.hasClass('show')) {
                    nav.removeClass('show');
                }
                window.removeEventListener('click', closeSelect, false);
            }

            var resizeHandler = function(event) {
                nav.removeClass('show');
                window.removeEventListener('click', closeSelect, false);
            }

            /***********************************************************************
             * Function: configureResponsiveNav
             *
             * Description:
             * Detect the width of all the buttons in sub navigation.
             *
             * Add a rule for a responsive sub navigation menu.
             *
             * Add a Javascript media query of the same responsive rule.
            /**********************************************************************/
            var configureResponsiveNav = function() {
                var scrollbarOffset = 17;
                var navWidth = 0;
                var buttons = $element.find('button');

                for(var i=0; i<buttons.length; i++) {
                    navWidth += buttons[i].clientWidth;
                }

                styleSheetFactory.addCSSRule(styleSheet, '@media screen and (max-width: '+(navWidth + scrollbarOffset)+'px)',
                    'header nav:after {' +
                        'display: block !important;' +
                    '}' +
                    'header nav button {' +
                        'border-bottom-width: 0 !important;' +
                        'float: auto;' +
                        'max-height: 0;' +
                        'text-align: left;' +
                        'width: 100%;' +
                    '}' +
                    'header nav button.selected {' +
                        'background-color: rgba(0,0,0,.1);' +
                    '}'
                ,1);

                styleSheetFactory.addCSSRule(styleSheet, 'header nav.show',
                    'height: '+(48 * buttons.length)+'px;'
                ,1);

                responsiveMQ = window.matchMedia('(max-width: '+(navWidth + scrollbarOffset)+'px)');
            };

            /***********************************************************************
             * Function: init
             *
             * Description:
             * Initialize the component with it has been fully drawn to the DOM.
            /**********************************************************************/
            var init = function() {
                // Configure responsive nav classes
                nav = $element.find('nav');
                configureResponsiveNav();

                // Calculate heights
                headerHeight = $element[0].clientHeight;
                navHeight = nav[0].clientHeight;

                // Add events
                $element.find('button').bind('click', tabHandler);
                window.addEventListener('scroll', scrollHander, true);
                window.addEventListener('resize', resizeHandler, true);
                scrollHander(null);
            };
            $timeout(init, 0);
        }
    }
}]);
<|endoftext|>


<commit_before>// ==========================================================================
//                            DG.Attribute
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('models/model_store');
sc_require('models/base_model');

/** @class
 *
 * Describes the named constituent data values of a case. An attribute may be
 * 'nominal' or 'numeric', meaning the data value will either be interpreted as
 * string valued or numeric. An attribute may be a formula, meaning that the
 * attribute's corresponding data value would be computed.
 * An attribute may be editable or not. Some properties of attributes affect
 * how the corresponding values are displayed. For example, 'precision' affects
 * the formatting of numeric attributes, and 'colormap' affects the color's used
 * when the attribute is displayed in a graph legend.
 *
 * @extends SC.BaseModel
 */
DG.Attribute = DG.BaseModel.extend(
  /** @scope DG.Attribute.prototype */ {

    /**
     * The name of the attribute
     * @property {String}
     */
    name: '',

    /**
     * The formula for the attribute
     * @property {String}
     */
    formula: '',

    /**
     * The map of attribute and category color values, for the attribute.
     * For special color keys and values, see:
     *   DG.ColorUtilities.getAttributeColorFromColorMap(),
     *   DG.ColorUtilities.getCategoryColorFromColorMap(),
     *   DG.ColorUtilities.simpleColorNames()
     * @property {Object}
     */
    colormap: null,

    /**
     * A description of/comment for the attribute
     * @property {String}
     */
    description: '',

    /**
     * Type of the attribute: 'numeric' or 'nominal'
     */
    type: null,

    /**
     * A relational link back to the parent collection.
     * @property {DG.CollectionRecord}
     */
    collection: null,

    /**
     * Number of decimal places to use for display.
     * Applicable only to numeric values.
     * @property {Number}
     */
    precision: 2,

      /**
       * Currently the only significance of this string property is that it
       * shows in parentheses next to attribute names in case table column
       * headers and graph axis labels.
       * @property{String}
       */
      unit: null,

    /**
     * True if the attribute is user-editable, false otherwise.
     * By default user-created attributes are editable as well as
     * any game-created attributes marked as such.
     * @property {Boolean}
     */
    editable: false,

    /**
     * True if the attribute is renameable, false otherwise.
     * @property {Boolean}
     */
    renameable: true,

    /**
     * True if the attribute is user-deletable, false otherwise.
     *
     * @property {Boolean}
     */
    deleteable: true,

    /**
     @private
     The DG.Formula for evaluation.
     @property   {DG.Formula}
     */
    _dgFormula: null,

    /**
     Initialization function.
     */
    init: function() {
      sc_super();

      // Handle formula passed in as property at creation
      var formulaSource = this.get('formula');
      if( !SC.empty( formulaSource) && !this._dgFormula) {
        this.createDGFormula();
        this._dgFormula.set('source', formulaSource);
      }
      if (typeof this.collection === 'number') {
        this.collection = DG.store.find(DG.Attribute, this.collection);
      }
      this.colormap = {};
    },

    verify: function () {
      if (SC.empty(this.collection)) {
        DG.logWarn('Unattached attribute: ' + this.id);
      }
      if (typeof this.collection === 'number') {
        DG.logWarn('Unresolved reference to collection id, ' + this.collection +
          ', in attribute: ' + this.id);
      }
      if (!(this.collection.recordType
        && this.collection.recordType() === DG.Collection) ) {
        DG.logWarn('Unexpected collection RecordType: '
          + this.collection.recordTypeString());
      }
    },

    /**
     Destruction function.
     */
    destroy: function() {
      var attrID = this.get('id');

      if( !SC.none( attrID)) {
        delete DG.Attribute.idMap[ attrID];
      }

      if (this.collection) {
        this.collection.attrs.removeObject(this);
      }

      if( this._dgFormula)
        this.destroyDGFormula();
      sc_super();
    },

    /**
     Utility function for creating the DG.Formula when necessary
     and hooking up the necessary observers.
     */
    createDGFormula: function() {
      this._dgFormula = DG.Formula.create({
        context: DG.CollectionFormulaContext.
          create({ collection: this.get('collection') })
      });
      this._dgFormula.addObserver('dependentChange', this, 'dependentDidChange');
    },

    /**
     Utility function for destroying the DG.Formula when necessary
     and cleanup up the necessary observers.
     */
    destroyDGFormula: function() {
      this._dgFormula.removeObserver('dependentChange', this, 'dependentDidChange');
      this._dgFormula.destroy();
      this._dgFormula = null;
    },

    /**
     * {Computed} True if the attribute has a non-empty formula.
     * @property {Boolean}
     */
    hasFormula: function() {
      return !SC.empty( this.get('formula'));
    }.property('formula').cacheable(),

    /**
     * Evaluates the attribute's formula in the context of the specified namespace object.
     * @param {Object}    Namespace object with property:value pairs for evaluation context
     * @return {Number | ?} Result of evaluation
     */
    evalFormula: function( iCase) {
      var tFormula = this._dgFormula,
        tReturnValue = NaN;
      try {
        // Client is responsible for passing _case_ and _id_
        tReturnValue = tFormula.evaluate({ _case_: iCase, _id_: iCase && iCase.get('id') });
      }
      catch(e) {
        // Return error objects as attribute values.
        tReturnValue = e;
      }

      return tReturnValue;
    },

    /**
     Observer function called when the formula changes.
     This function creates, updates, or destroys the corresponding
     DG.Formula as needed and sets up/tears down the necessary
     observers whenever the formula changes.
     */
    formulaDidChange: function() {
      var tSource = this.get('formula');

      // Non-empty formula string -- we need a DG.Formula
      if( !SC.empty( tSource)) {
        // Create the DG.Formula if we don't have one yet
        if( !this._dgFormula)
          this.createDGFormula();
        // Update the DG.Formula with the new source
        this._dgFormula.set('source', tSource);
      }

      // empty formula string -- no need for a DG.Formula
      else {
        if( this._dgFormula)
          this.destroyDGFormula();
      }
    }.observes('formula'),

    /**
     Observer function called when an attribute formula notifies
     that one of its dependents has changed.
     */
    dependentDidChange: function( iNotifier, iKey) {
      this.notifyPropertyChange( iKey);
    },

    /**
     Override to update our attribute map to include attributes loaded
     from documents.
     */
    didLoadRecord: function() {
      var id = this.get('id');
      DG.Attribute.idMap[ id] = this;
    },
    toArchive: function () {
      return {
        name: this.name,
        type: this.type,
        defaultMin: this.defaultMin,
        defaultMax: this.defaultMax,
        description: this.description,
        colormap: this.colormap || undefined,
        blockDisplayOfEmptyCategories: this.blockDisplayOfEmptyCategories || undefined,
        editable: this.editable,
        formula: this.hasFormula()? this.formula: undefined,
        guid: this.id,
        precision: this.precision,
        unit: this.unit
      };
    }
  });

/**
 Global map from attribute ID to attribute.
 More efficient than calling DG.store.find(DG.Attribute, attrID);
 */
DG.Attribute.idMap = {};

/**
 Retrieves an attribute by its ID using our internal map.
 @param  {Number}  iAttrID -- the ID of the attribute to be retrieved
 */
DG.Attribute.getAttributeByID = function( iAttrID) {
  return DG.Attribute.idMap[ iAttrID];
};

/**
 * Creates a new attribute with the specified properties.
 * @param {Object}  iProperties List of properties to apply to the newly-created DG.Attribute.
 * @return {DG.Attribute}   The newly-created DG.Attribute
 */
DG.Attribute.createAttribute = function( iProperties) {
  var collection = DG.store.resolve(iProperties.collection),
    newAttribute;

  iProperties.collection = collection;
  newAttribute = DG.Attribute.create(iProperties || {});
  if( iProperties.colormap) {
    newAttribute.colormap = SC.clone( iProperties.colormap);
  }

  DG.store.commitRecords();
  DG.Attribute.idMap[ newAttribute.get('id')] = newAttribute;
  return newAttribute;
};

/**
 * Destroys the specified DG.Attribute.
 * @param {DG.Attribute}  The DG.Attribute to destroy
 */
DG.Attribute.destroyAttribute = function( iAttribute) {
  iAttribute.destroy();
};
<commit_msg>[Fixes #111563512] Initialize blockDisplayOfEmptyCategories to true. As a consequence, by default deleting all the cases in a given category will cause that category in legends or plots to disappear.<commit_after>// ==========================================================================
//                            DG.Attribute
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('models/model_store');
sc_require('models/base_model');

/** @class
 *
 * Describes the named constituent data values of a case. An attribute may be
 * 'nominal' or 'numeric', meaning the data value will either be interpreted as
 * string valued or numeric. An attribute may be a formula, meaning that the
 * attribute's corresponding data value would be computed.
 * An attribute may be editable or not. Some properties of attributes affect
 * how the corresponding values are displayed. For example, 'precision' affects
 * the formatting of numeric attributes, and 'colormap' affects the color's used
 * when the attribute is displayed in a graph legend.
 *
 * @extends SC.BaseModel
 */
DG.Attribute = DG.BaseModel.extend(
  /** @scope DG.Attribute.prototype */ {

    /**
     * The name of the attribute
     * @property {String}
     */
    name: '',

    /**
     * The formula for the attribute
     * @property {String}
     */
    formula: '',

    /**
     * The map of attribute and category color values, for the attribute.
     * For special color keys and values, see:
     *   DG.ColorUtilities.getAttributeColorFromColorMap(),
     *   DG.ColorUtilities.getCategoryColorFromColorMap(),
     *   DG.ColorUtilities.simpleColorNames()
     * @property {Object}
     */
    colormap: null,

    /**
     * If false, empty categories will be displayed in categorical plots and legends
     * @property{Boolean}
     */
    blockDisplayOfEmptyCategories: true,

    /**
     * A description of/comment for the attribute
     * @property {String}
     */
    description: '',

    /**
     * Type of the attribute: 'numeric' or 'nominal'
     */
    type: null,

    /**
     * A relational link back to the parent collection.
     * @property {DG.CollectionRecord}
     */
    collection: null,

    /**
     * Number of decimal places to use for display.
     * Applicable only to numeric values.
     * @property {Number}
     */
    precision: 2,

      /**
       * Currently the only significance of this string property is that it
       * shows in parentheses next to attribute names in case table column
       * headers and graph axis labels.
       * @property{String}
       */
      unit: null,

    /**
     * True if the attribute is user-editable, false otherwise.
     * By default user-created attributes are editable as well as
     * any game-created attributes marked as such.
     * @property {Boolean}
     */
    editable: false,

    /**
     * True if the attribute is renameable, false otherwise.
     * @property {Boolean}
     */
    renameable: true,

    /**
     * True if the attribute is user-deletable, false otherwise.
     *
     * @property {Boolean}
     */
    deleteable: true,

    /**
     @private
     The DG.Formula for evaluation.
     @property   {DG.Formula}
     */
    _dgFormula: null,

    /**
     Initialization function.
     */
    init: function() {
      sc_super();

      // Handle formula passed in as property at creation
      var formulaSource = this.get('formula');
      if( !SC.empty( formulaSource) && !this._dgFormula) {
        this.createDGFormula();
        this._dgFormula.set('source', formulaSource);
      }
      if (typeof this.collection === 'number') {
        this.collection = DG.store.find(DG.Attribute, this.collection);
      }
      this.colormap = {};
    },

    verify: function () {
      if (SC.empty(this.collection)) {
        DG.logWarn('Unattached attribute: ' + this.id);
      }
      if (typeof this.collection === 'number') {
        DG.logWarn('Unresolved reference to collection id, ' + this.collection +
          ', in attribute: ' + this.id);
      }
      if (!(this.collection.recordType
        && this.collection.recordType() === DG.Collection) ) {
        DG.logWarn('Unexpected collection RecordType: '
          + this.collection.recordTypeString());
      }
    },

    /**
     Destruction function.
     */
    destroy: function() {
      var attrID = this.get('id');

      if( !SC.none( attrID)) {
        delete DG.Attribute.idMap[ attrID];
      }

      if (this.collection) {
        this.collection.attrs.removeObject(this);
      }

      if( this._dgFormula)
        this.destroyDGFormula();
      sc_super();
    },

    /**
     Utility function for creating the DG.Formula when necessary
     and hooking up the necessary observers.
     */
    createDGFormula: function() {
      this._dgFormula = DG.Formula.create({
        context: DG.CollectionFormulaContext.
          create({ collection: this.get('collection') })
      });
      this._dgFormula.addObserver('dependentChange', this, 'dependentDidChange');
    },

    /**
     Utility function for destroying the DG.Formula when necessary
     and cleanup up the necessary observers.
     */
    destroyDGFormula: function() {
      this._dgFormula.removeObserver('dependentChange', this, 'dependentDidChange');
      this._dgFormula.destroy();
      this._dgFormula = null;
    },

    /**
     * {Computed} True if the attribute has a non-empty formula.
     * @property {Boolean}
     */
    hasFormula: function() {
      return !SC.empty( this.get('formula'));
    }.property('formula').cacheable(),

    /**
     * Evaluates the attribute's formula in the context of the specified namespace object.
     * @param {Object}    Namespace object with property:value pairs for evaluation context
     * @return {Number | ?} Result of evaluation
     */
    evalFormula: function( iCase) {
      var tFormula = this._dgFormula,
        tReturnValue = NaN;
      try {
        // Client is responsible for passing _case_ and _id_
        tReturnValue = tFormula.evaluate({ _case_: iCase, _id_: iCase && iCase.get('id') });
      }
      catch(e) {
        // Return error objects as attribute values.
        tReturnValue = e;
      }

      return tReturnValue;
    },

    /**
     Observer function called when the formula changes.
     This function creates, updates, or destroys the corresponding
     DG.Formula as needed and sets up/tears down the necessary
     observers whenever the formula changes.
     */
    formulaDidChange: function() {
      var tSource = this.get('formula');

      // Non-empty formula string -- we need a DG.Formula
      if( !SC.empty( tSource)) {
        // Create the DG.Formula if we don't have one yet
        if( !this._dgFormula)
          this.createDGFormula();
        // Update the DG.Formula with the new source
        this._dgFormula.set('source', tSource);
      }

      // empty formula string -- no need for a DG.Formula
      else {
        if( this._dgFormula)
          this.destroyDGFormula();
      }
    }.observes('formula'),

    /**
     Observer function called when an attribute formula notifies
     that one of its dependents has changed.
     */
    dependentDidChange: function( iNotifier, iKey) {
      this.notifyPropertyChange( iKey);
    },

    /**
     Override to update our attribute map to include attributes loaded
     from documents.
     */
    didLoadRecord: function() {
      var id = this.get('id');
      DG.Attribute.idMap[ id] = this;
    },
    toArchive: function () {
      return {
        name: this.name,
        type: this.type,
        defaultMin: this.defaultMin,
        defaultMax: this.defaultMax,
        description: this.description,
        colormap: this.colormap || undefined,
        blockDisplayOfEmptyCategories: this.blockDisplayOfEmptyCategories || undefined,
        editable: this.editable,
        formula: this.hasFormula()? this.formula: undefined,
        guid: this.id,
        precision: this.precision,
        unit: this.unit
      };
    }
  });

/**
 Global map from attribute ID to attribute.
 More efficient than calling DG.store.find(DG.Attribute, attrID);
 */
DG.Attribute.idMap = {};

/**
 Retrieves an attribute by its ID using our internal map.
 @param  {Number}  iAttrID -- the ID of the attribute to be retrieved
 */
DG.Attribute.getAttributeByID = function( iAttrID) {
  return DG.Attribute.idMap[ iAttrID];
};

/**
 * Creates a new attribute with the specified properties.
 * @param {Object}  iProperties List of properties to apply to the newly-created DG.Attribute.
 * @return {DG.Attribute}   The newly-created DG.Attribute
 */
DG.Attribute.createAttribute = function( iProperties) {
  var collection = DG.store.resolve(iProperties.collection),
    newAttribute;

  iProperties.collection = collection;
  newAttribute = DG.Attribute.create(iProperties || {});
  if( iProperties.colormap) {
    newAttribute.colormap = SC.clone( iProperties.colormap);
  }

  DG.store.commitRecords();
  DG.Attribute.idMap[ newAttribute.get('id')] = newAttribute;
  return newAttribute;
};

/**
 * Destroys the specified DG.Attribute.
 * @param {DG.Attribute}  The DG.Attribute to destroy
 */
DG.Attribute.destroyAttribute = function( iAttribute) {
  iAttribute.destroy();
};
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import pureRender from 'pure-render-decorator'

import '../../css/multiple-choice-details.less'

function noAnswer(answer) {
  return answer.answer === null
}

function answerIncludeChoice(answer, choice) {
  return !noAnswer(answer) && answer.answer.find(a => a.choice === choice.choice)
}

function getChoicesStats(choices, answers) {
  let stats = {}
  const answersFlat = answers.reduce((res, answer) => res.concat(answer.answer), [])
  choices.forEach((choice) => {
    const filterFunc = choice.noAnswer ? noAnswer : ans => answerIncludeChoice(ans, choice)
    const count = answers.filter(filterFunc).length
    stats[choice.choice] = {
      count,
      percent: (count / answersFlat.length * 100).toFixed(1)
    }
  })
  return stats
}

@pureRender
export default class MultipleChoiceDetails extends Component {
  get choices() {
    const choices = this.props.question.get('choices').toJS()
    // Add fake, no-answer choice.
    choices.push({choice: 'No response', noAnswer: true})
    return choices
  }

  get answers() {
    return this.props.question.get('answers').toJS()
  }

  render() {
    const stats = getChoicesStats(this.choices, this.answers)
    return (

          {this.choices.map((choiceDesc, idx) => {
            const { choice, isCorrect } = choiceDesc
            return <ChoiceRow key={idx} idx={idx} choice={choice} isCorrect={isCorrect}
                              percent={stats[choice].percent} count={stats[choice].count}/>
          })}

    )
  }
}

const ChoiceRow = ({idx, choice, isCorrect, percent, count}) => (

    {idx}. {choice}

    {percent}%
    {count}

)
<commit_msg>Fix: If no answers given, percentage for responses showing as 'NaN%.'<commit_after>import React, { Component } from 'react'
import pureRender from 'pure-render-decorator'

import '../../css/multiple-choice-details.less'

function noAnswer(answer) {
  return answer.answer === null
}

function answerIncludeChoice(answer, choice) {
  return !noAnswer(answer) && answer.answer.find(a => a.choice === choice.choice)
}

function getChoicesStats(choices, answers) {
  let stats = {}
  const answersFlat = answers.reduce((res, answer) => res.concat(answer.answer), [])
  const totalAnswers = answersFlat.length
  choices.forEach((choice) => {
    const filterFunc = choice.noAnswer ? noAnswer : ans => answerIncludeChoice(ans, choice)
    const count = answers.filter(filterFunc).length
    // avoid division by zero:
    const percent = totalAnswers === 0 ? 0 : count / totalAnswers * 100
    stats[choice.choice] = {
      count,
      percent: (percent).toFixed(1)
    }
  })
  return stats
}

@pureRender
export default class MultipleChoiceDetails extends Component {
  get choices() {
    const choices = this.props.question.get('choices').toJS()
    // Add fake, no-answer choice.
    choices.push({choice: 'No response', noAnswer: true})
    return choices
  }

  get answers() {
    return this.props.question.get('answers').toJS()
  }

  render() {
    const stats = getChoicesStats(this.choices, this.answers)
    return (

          {this.choices.map((choiceDesc, idx) => {
            const { choice, isCorrect } = choiceDesc
            return <ChoiceRow key={idx} idx={idx} choice={choice} isCorrect={isCorrect}
                              percent={stats[choice].percent} count={stats[choice].count}/>
          })}

    )
  }
}

const ChoiceRow = ({idx, choice, isCorrect, percent, count}) => (

    {idx}. {choice}

    {percent}%
    {count}

)
<|endoftext|>


<commit_before>//viewmodel for the assess page
function pageViewModel(gvm) {
    gvm.app  = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectRapportName")}, gvm);
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n. __("AssessTitle2");}, gvm);
    gvm.userId = -1;

    // Page specific i18n bindings
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AssessTitle2");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectRapportName");}, gvm);
    gvm.homeManual = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("HomeRapportManual");}, gvm);
    gvm.foundProjects = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("FoundProjects");}, gvm);
    gvm.selectCourse = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("SelectCourse");}, gvm);

    // Pagination i18n bindings
    gvm.addBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AddBtn")}, gvm);

    // Table i18n bindings
    gvm.werkficheID = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("werkficheID");}, gvm);
    gvm.werkficheName = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("werkficheName");}, gvm);
    gvm.werkficheDate = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("werkficheDate");}, gvm);
    gvm.werkficheAction = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("werkficheAction");}, gvm);

    gvm.availableCourses = ko.observableArray([]);
    gvm.availableStudentlists = ko.observableArray([]);
    gvm.availableStudents = ko.observableArray([]);

    gvm.currentCourseRapportId = null;
    gvm.currentStudentlistId = null;
    gvm.currentStudentId = null;

    gvm.updateDropdowns = function() {
        $.getJSON('api/lastdropdownrapporten/' + gvm.userId, function(data) {
            if(!$.isEmptyObject(data)) {
                $.each(data, function(i, item) {
                    $(".btn-courseRapport span:first").text(item.course);
                    $(".btn-studentlist span:first").text(item.studentlist);
                    $('.btn-student span:first').text(item.student);
                    gvm.currentCourseRapportId = item.courseid;
                    gvm.currentStudentlistId = item.studentlistid;
                    gvm.currentStudentId = item.studentid;
                    gvm.updateCourseRapport();
                    loadTablePage(item.courseid, 1);
                });
            } else {
                $(".btn-courseRapport span:first").text("Course");
                $(".btn-studentlist span:first").text("Studentlist");
                $('.btn-student span:first').text("Student");
                gvm.updateCourseRapport();
            }
        });
    }

    //Houdt bij wat geselecteerd wordt
    //Wordt opgeroepen bij iedere wijziging (niet ingevulde velden = NULL)
    gvm.saveLastSelectedDropdowns = function() {
        data = {};
        data["course"] = $(".btn-courseRapport span:first").text();
        data["courseid"] = gvm.currentCourseRapportId;
        data["student"] = $(".btn-student span:first").text();
        data["studentid"] = gvm.currentStudentId;
        data["user"] = gvm.userId;
        console.log(data);
        $.ajax({
            type: "POST",
            url: "/api/savedropdownsRapport",
            data: data,
            success: function() {
                console.log("success");
            }
        })
    }

    gvm.updateCourseRapport = function() {
        $.getJSON('/api/coursedrop', function(data) {
            gvm.availableCourses.removeAll();
            $.each(data, function(i, item) {
                //  Put item in list
                gvm.availableCourses.push(item);

                // Add listener to listitem
                $("#coursebtn-" + item.id).click(function(){
                    gvm.currentCourseRapportId = item.id;
                    gvm.currentStudentlistId = null;
                    gvm.currentStudentId = null;
                    gvm.updateStudentlists(item.id, gvm.userId);
                    $(".btn-courseRapport span:first").text($(this).text());
                    $(".btn-studentlist span:first").text("Studentlist");
                    $('.btn-student span:first').text("Student");
                    //gvm.saveLastSelectedDropdowns();
                });
            });
        });
    };

    /*
     * Update the student data
     */
    gvm.updateStudentlists = function(cid, uid) {
        //get all studentlists
        $.getJSON('api/studentlistdrop/' + cid + '/' + uid, function(data) {
            gvm.availableStudentlists.removeAll();
            $.each(data, function(i,item) {
                // Put item in list
                gvm.availableStudentlists.push(item);

                // Add listener to listitem
                $("#studentlistbtn-" + item.id).click(function(){
                    gvm.currentStudentlistId = item.id;
                    gvm.currentStudentId = null;
                    gvm.updateStudents(item.id);
                    $(".btn-studentlist span:first").text($(this).text());
                    $('.btn-student span:first').text("Student");
                    //gvm.saveLastSelectedDropdowns();
                });
            })
        })
    }

    /*
     * Update the student data
     */
    gvm.updateStudents = function(id) {
        //get all students who follow that course
        $.getJSON('api/studentdrop/' + id, function(data) {
            gvm.availableStudents.removeAll();
            $.each(data, function(i,item) {
                // Put item in list
                gvm.availableStudents.push(item);

                // Add listener to listitem
                $("#studentbtn-" + item.id).click(function(){
                    gvm.currentStudentId = item.id;
                    $(".btn-student span:first").text($(this).text());
                    //gvm.saveLastSelectedDropdowns();
                });
            })
        })
    }

    // The table data observable array
    gvm.tabledata = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, name, lastname, mail, score) {
        // Push data
        var tblOject = {tstudid: id, tname: name, tlname: lastname, tmail: mail, tscore: score};
        gvm.tabledata.push(tblOject);
    }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }
}

function loadTablePage(pagenr)
{
    $.getJSON('/api/studentscourse/page/' + pagenr, function(data){

        /* Clear current table page */
        viewModel.clearTable();

        // Load table data
        $.each(data.data, function(i, item) {
            viewModel.addTableData(item.id,  item.firstname, item.lastname, item.mail);
        });

        /* Let previous en next buttons work */
        if(data.prev == "none"){
            $('#pager-prev-btn').addClass('disabled');
        } else {
            $('#pager-prev-btn').removeClass('disabled');
            $('#pager-prev-btn a').click(function(){
                loadTablePage(data.prev);
            });
        }

        if(data.next == "none"){
            $('#pager-next-btn').addClass('disabled');
        } else {
            $('#pager-next-btn').removeClass('disabled');
            $('#pager-next-btn a').click(function(){
                loadTablePage(data.next);
            });
        }

        // Number of pager buttons
        var numItems = $('.pager-nr-btn').length;

        /* Calculate for the pager buttons */
        var lowPage = Math.floor(pagenr/numItems) + 1;

        $('.pager-nr-btn').each(function() {
            /* calculate current page number */
            var thispagenr = lowPage++;

            /* Add the page number */
            $(this).html('' + thispagenr + '');

            /* Add active class to current page */
            if(thispagenr == pagenr) {
                $(this).addClass('active');
            } else {
                $(this).removeClass('active');
            }

            /* Disable inactive classes and bind handlers to active classes */
            if(thispagenr > data.pagecount) {
                $(this).addClass('disabled');
            } else {
                /* Add click listener for button */
                $(this).click(function() {
                    loadTablePage(thispagenr);
                });
            }
        });
    });

}

function initPage() {
    $.getJSON('/api/currentuser', function(data) {
        viewModel.userId = data.id;
        viewModel.updateDropdowns();
    });
}<commit_msg>assesspagina reworken, met extra studentlist dropdown<commit_after>//viewmodel for the assess page
function pageViewModel(gvm) {
    gvm.app  = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectRapportName")}, gvm);
    gvm.title = ko.computed(function(){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n. __("AssessTitle2");}, gvm);
    gvm.userId = -1;

    // Page specific i18n bindings
    gvm.pageHeader = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AssessTitle2");}, gvm);
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectRapportName");}, gvm);
    gvm.homeManual = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("HomeRapportManual");}, gvm);
    gvm.foundProjects = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("FoundProjects");}, gvm);
    gvm.selectCourse = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("SelectCourse");}, gvm);

    // Pagination i18n bindings
    gvm.addBtn = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("AddBtn")}, gvm);

    // Table i18n bindings
    gvm.werkficheID = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("werkficheID");}, gvm);
    gvm.werkficheName = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("werkficheName");}, gvm);
    gvm.werkficheDate = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("werkficheDate");}, gvm);
    gvm.werkficheAction = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("werkficheAction");}, gvm);

    gvm.availableCourses = ko.observableArray([]);
    gvm.availableStudentlists = ko.observableArray([]);
    gvm.availableStudents = ko.observableArray([]);

    gvm.currentCourseId = null;
    gvm.currentStudentlistId = null;
    gvm.currentStudentId = null;

    gvm.updateDropdowns = function() {
        $.getJSON('api/lastdropdownrapporten/' + gvm.userId, function(data) {
            if(!$.isEmptyObject(data)) {
                $.each(data, function(i, item) {
                    $(".btn-courseRapport span:first").text(item.course);
                    $(".btn-studentlist span:first").text(item.studentlist);
                    $('.btn-student span:first').text(item.student);
                    gvm.currentCourseId = item.courseid;
                    gvm.currentStudentlistId = item.studentlistid;
                    gvm.currentStudentId = item.studentid;
                    gvm.updateCourseRapport();
                    loadTablePage(item.courseid, 1);
                });
            } else {
                gvm.updateCourseRapport();
            }
        });
    }

    //Houdt bij wat geselecteerd wordt
    //Wordt opgeroepen bij iedere wijziging (niet ingevulde velden = NULL)
    gvm.saveLastSelectedDropdowns = function() {
        data = {};
        data["course"] = $(".btn-courseRapport span:first").text();
        data["courseid"] = gvm.currentCourseId;
        data["student"] = $(".btn-student span:first").text();
        data["studentid"] = gvm.currentStudentId;
        data["user"] = gvm.userId;
        console.log(data);
        $.ajax({
            type: "POST",
            url: "/api/savedropdownsRapport",
            data: data,
            success: function() {
                console.log("success");
            }
        })
    }

    gvm.updateCourseRapport = function() {
        $.getJSON('/api/coursedrop', function(data) {
            gvm.availableCourses.removeAll();
            $.each(data, function(i, item) {
                //  Put item in list
                gvm.availableCourses.push(item);

                // Add listener to listitem
                $("#coursebtn-" + item.id).click(function(){
                    gvm.currentCourseId = item.id;
                    gvm.currentStudentlistId = null;
                    gvm.currentStudentId = null;
                    gvm.updateStudentlists(item.id, gvm.userId);
                    $(".btn-courseRapport span:first").text($(this).text());
                    $(".btn-studentlist span:first").text("Studentlist");
                    $('.btn-student span:first').text("Student");
                    //gvm.saveLastSelectedDropdowns();
                });
            });
        });
    };

    /*
     * Update the student data
     */
    gvm.updateStudentlists = function(cid, uid) {
        //get all studentlists
        $.getJSON('api/studentlistdrop/' + cid + '/' + uid, function(data) {
            gvm.availableStudentlists.removeAll();
            $.each(data, function(i,item) {
                // Put item in list
                gvm.availableStudentlists.push(item);

                // Add listener to listitem
                $("#studentlistbtn-" + item.id).click(function(){
                    gvm.currentStudentlistId = item.id;
                    gvm.currentStudentId = null;
                    gvm.updateStudents(item.id);
                    $(".btn-studentlist span:first").text($(this).text());
                    $('.btn-student span:first').text("Student");
                    //gvm.saveLastSelectedDropdowns();
                });
            })
        })
    }

    /*
     * Update the student data
     */
    gvm.updateStudents = function(id) {
        //get all students who follow that course
        $.getJSON('api/studentdrop/' + id, function(data) {
            gvm.availableStudents.removeAll();
            $.each(data, function(i,item) {
                // Put item in list
                gvm.availableStudents.push(item);

                // Add listener to listitem
                $("#studentbtn-" + item.id).click(function(){
                    gvm.currentStudentId = item.id;
                    $(".btn-student span:first").text($(this).text());
                    //gvm.saveLastSelectedDropdowns();
                });
            })
        })
    }

    // The table data observable array
    gvm.tabledata = ko.observableArray([]);

    // Add data to the table
    gvm.addTableData = function(id, name, lastname, mail, score) {
        // Push data
        var tblOject = {tstudid: id, tname: name, tlname: lastname, tmail: mail, tscore: score};
        gvm.tabledata.push(tblOject);
    }

    gvm.clearTable = function() {
        gvm.tabledata.removeAll();
    }
}

function loadTablePage(pagenr)
{
    $.getJSON('/api/studentscourse/page/' + pagenr, function(data){

        /* Clear current table page */
        viewModel.clearTable();

        // Load table data
        $.each(data.data, function(i, item) {
            viewModel.addTableData(item.id,  item.firstname, item.lastname, item.mail);
        });

        /* Let previous en next buttons work */
        if(data.prev == "none"){
            $('#pager-prev-btn').addClass('disabled');
        } else {
            $('#pager-prev-btn').removeClass('disabled');
            $('#pager-prev-btn a').click(function(){
                loadTablePage(data.prev);
            });
        }

        if(data.next == "none"){
            $('#pager-next-btn').addClass('disabled');
        } else {
            $('#pager-next-btn').removeClass('disabled');
            $('#pager-next-btn a').click(function(){
                loadTablePage(data.next);
            });
        }

        // Number of pager buttons
        var numItems = $('.pager-nr-btn').length;

        /* Calculate for the pager buttons */
        var lowPage = Math.floor(pagenr/numItems) + 1;

        $('.pager-nr-btn').each(function() {
            /* calculate current page number */
            var thispagenr = lowPage++;

            /* Add the page number */
            $(this).html('' + thispagenr + '');

            /* Add active class to current page */
            if(thispagenr == pagenr) {
                $(this).addClass('active');
            } else {
                $(this).removeClass('active');
            }

            /* Disable inactive classes and bind handlers to active classes */
            if(thispagenr > data.pagecount) {
                $(this).addClass('disabled');
            } else {
                /* Add click listener for button */
                $(this).click(function() {
                    loadTablePage(thispagenr);
                });
            }
        });
    });

}

function initPage() {
    $.getJSON('/api/currentuser', function(data) {
        viewModel.userId = data.id;
        viewModel.updateDropdowns();
    });
}<|endoftext|>


<commit_before>/* @flow */

import escapeRegExp from 'lodash/escapeRegExp';

const findWithRegex = (regex, contentBlock, callback) => {
  const contentBlockText = contentBlock.getText();

  // exclude entities, when matching
  contentBlock.findEntityRanges(
    (character) => !character.getEntity(),
    (nonEntityStart, nonEntityEnd) => {
      const text = contentBlockText.slice(nonEntityStart, nonEntityEnd);
      let matchArr;
      let start;
      let prevLastIndex = regex.lastIndex;

      // Go through all matches in the text and return the indices to the callback
      // Force update regex's lastIndex to avoid Infinite loop
      while ((matchArr = regex.exec(text)) !== null) { // eslint-disable-line
        if (regex.lastIndex === prevLastIndex) {
          regex.lastIndex += 1;
        }
        prevLastIndex = regex.lastIndex;
        start = nonEntityStart + matchArr.index;
        callback(start, start + matchArr[0].length);
      }
    }
  );
};

export default (trigger: string, supportWhiteSpace: boolean, regExp: string) => { //eslint-disable-line
  const MENTION_REGEX = supportWhiteSpace ?
    new RegExp(`${escapeRegExp(trigger)}(${regExp}|\\s){0,}`, 'g') :
    new RegExp(`(\\s|^)${escapeRegExp(trigger)}${regExp}`, 'g');

  return (contentBlock: Object, callback: Function) => {
    findWithRegex(MENTION_REGEX, contentBlock, callback);
  };
};
<commit_msg>lint(mention-plugin): fix lint error<commit_after>/* @flow */

import escapeRegExp from 'lodash/escapeRegExp';

const findWithRegex = (regex, contentBlock, callback) => {
  const contentBlockText = contentBlock.getText();

  // exclude entities, when matching
  contentBlock.findEntityRanges(
    (character) => !character.getEntity(),
    (nonEntityStart, nonEntityEnd) => {
      const text = contentBlockText.slice(nonEntityStart, nonEntityEnd);
      let matchArr;
      let start;
      let prevLastIndex = regex.lastIndex;

      // Go through all matches in the text and return the indices to the callback
      // Force update regex's lastIndex to avoid Infinite loop
      while ((matchArr = regex.exec(text)) !== null) { // eslint-disable-line
        if (regex.lastIndex === prevLastIndex) {
          regex.lastIndex += 1; // eslint-disable-line no-param-reassign
        }
        prevLastIndex = regex.lastIndex;
        start = nonEntityStart + matchArr.index;
        callback(start, start + matchArr[0].length);
      }
    }
  );
};

export default (trigger: string, supportWhiteSpace: boolean, regExp: string) => { //eslint-disable-line
  const MENTION_REGEX = supportWhiteSpace ?
    new RegExp(`${escapeRegExp(trigger)}(${regExp}|\\s){0,}`, 'g') :
    new RegExp(`(\\s|^)${escapeRegExp(trigger)}${regExp}`, 'g');

  return (contentBlock: Object, callback: Function) => {
    findWithRegex(MENTION_REGEX, contentBlock, callback);
  };
};
<|endoftext|>


<commit_before>var SteamCrypto = require('steam-crypto');
var Socket = require('net').Socket;

module.exports = TCPConnection;

const MAGIC = 'VT01';

require('util').inherits(TCPConnection, Socket);

/**
 * @augments Socket
 * @constructor
 */
function TCPConnection() {
	Socket.call(this);
	this.on('readable', this._readPacket.bind(this));
}

/**
 * Send data over the connection.
 * @param {Buffer} data
 */
TCPConnection.prototype.send = function(data) {
	// encrypt
	if (this.sessionKey) {
		data = SteamCrypto.symmetricEncrypt(data, this.sessionKey);
	}

	var buffer = new Buffer(4 + 4 + data.length);
	buffer.writeUInt32LE(data.length, 0);
	buffer.write(MAGIC, 4);
	data.copy(buffer, 8);
	this.write(buffer);
};

TCPConnection.prototype._readPacket = function() {
	if (!this._packetLen) {
		var header = this.read(8);
		if (!header) {
			return;
		}
		this._packetLen = header.readUInt32LE(0);
	}

	var packet = this.read(this._packetLen);

	if (!packet) {
		this.emit('debug', 'incomplete packet');
		return;
	}

	delete this._packetLen;

	// decrypt
	if (this.sessionKey) {
		packet = SteamCrypto.symmetricDecrypt(packet, this.sessionKey);
	}

	this.emit('packet', packet);

	// keep reading until there's nothing left
	this._readPacket();
};
<commit_msg>Check magic on incoming data<commit_after>var SteamCrypto = require('steam-crypto');
var Socket = require('net').Socket;

module.exports = TCPConnection;

const MAGIC = 'VT01';

require('util').inherits(TCPConnection, Socket);

/**
 * @augments Socket
 * @constructor
 */
function TCPConnection() {
	Socket.call(this);
	this.on('readable', this._readPacket.bind(this));
}

/**
 * Send data over the connection.
 * @param {Buffer} data
 */
TCPConnection.prototype.send = function(data) {
	// encrypt
	if (this.sessionKey) {
		data = SteamCrypto.symmetricEncrypt(data, this.sessionKey);
	}

	var buffer = new Buffer(4 + 4 + data.length);
	buffer.writeUInt32LE(data.length, 0);
	buffer.write(MAGIC, 4);
	data.copy(buffer, 8);
	this.write(buffer);
};

TCPConnection.prototype._readPacket = function() {
	if (!this._packetLen) {
		var header = this.read(8);
		if (!header) {
			return;
		}
		this._packetLen = header.readUInt32LE(0);
		if (header.slice(4).toString('ascii') != MAGIC) {
			this.emit('error', 'Bad magic');
			this.destroy();
			return;
		}
	}

	var packet = this.read(this._packetLen);

	if (!packet) {
		this.emit('debug', 'incomplete packet');
		return;
	}

	delete this._packetLen;

	// decrypt
	if (this.sessionKey) {
		packet = SteamCrypto.symmetricDecrypt(packet, this.sessionKey);
	}

	this.emit('packet', packet);

	// keep reading until there's nothing left
	this._readPacket();
};
<|endoftext|>


<commit_before>/**
 * @file 用户移动鼠标时小窗口自动躲避鼠标，防止遮挡用户操作
 *
 * @author liyinan
 * @version 1.0
 * @date 2017-07-11
 */

import React, {Component} from 'react';
import {connect} from 'react-redux';
import _ from 'underscore';
import Checkbox from 'material-ui/Checkbox';
import {
    mouseNear,
    enableAntiBlock,
    MOUSE_NEAR_TRUE,
    MOUSE_NEAR_FALSE
} from '../actions/index';
// import './floatwindow.less';

function createNegative() {
    let val = null;
    return function (predict, fn) {
        if (typeof  predict === 'function') {
            predict = predict();
        }
        if (val !== predict) {
            val = predict;
            fn();
        }
    }
}

class FloatWindow extends Component {

    constructor() {
        super();
        this.mousemovePtr = _.throttle(this.onmousemove.bind(this), 300);
        this.keyupPtr = _.throttle(this.onkeyup.bind(this), 300);
        this.negative = createNegative();
    }
    componentDidMount() {
        // 绑定鼠标移动事件
        document.body.addEventListener('mousemove', this.mousemovePtr, false);
        document.body.addEventListener('keyup', this.keyupPtr, false);
    }

    componentWillUnmount() {
        // 卸载鼠标移动事件
        document.body.removeEventListener('mousemove', this.mousemovePtr, false);
        document.body.removeEventListener('keyup', this.keyupPtr, false);
    }

    isNear(x, y) {
        let screenWidth = window.innerWidth;
        let screenHeight = window.innerHeight;
        let floatWindowWidth = 200;
        let floatWindowHeight = 200;

        if (
            screenWidth - x < floatWindowWidth
            && y < floatWindowHeight
        ) {
            return true;
        }
        else {
            return false;
        }
    }

    onkeyup(e) {
        // b的keyCode是66
        if (e.keyCode === 66 && e.ctrlKey) {
            let currentValue = this.props.floatWindow.enableAntiBlock;
            this.props.dispatch(enableAntiBlock(!currentValue));
        }
    }

    onmousemove(e) {
        let currentValue = this.props.floatWindow.enableAntiBlock;
        if (!currentValue) {
            return;
        }
        let param = '';
        let _this = this;
        if (this.isNear(e.pageX, e.pageY)) {
            param = MOUSE_NEAR_TRUE;
        }
        else {
            param = MOUSE_NEAR_FALSE;
        }
        // 当结果跟上次不同的时候才执行回调
        this.negative(param, function () {
            _this.props.dispatch(mouseNear(param));
        });
    }

    render() {
        let status = this.props.floatWindow.status;
        let antiBlockStatus = this.props.floatWindow.enableAntiBlock;
        let style = {};
        if (status === MOUSE_NEAR_TRUE) {
            style.display = 'none';
        }
        else {
            style.display = '';
        }
        return

            {this.props.children}

<Checkbox checked={antiBlockStatus}/>ctrl + b 启用防遮挡功能

;
    }

}

export default connect(s => s)(FloatWindow);
<commit_msg>修复页面滚动导致自动躲鼠标功能失效的问题<commit_after>/**
 * @file 用户移动鼠标时小窗口自动躲避鼠标，防止遮挡用户操作
 *
 * @author liyinan
 * @version 1.0
 * @date 2017-07-11
 */

import React, {Component} from 'react';
import {connect} from 'react-redux';
import _ from 'underscore';
import Checkbox from 'material-ui/Checkbox';
import {
    mouseNear,
    enableAntiBlock,
    MOUSE_NEAR_TRUE,
    MOUSE_NEAR_FALSE
} from '../actions/index';
// import './floatwindow.less';

function createNegative() {
    let val = null;
    return function (predict, fn) {
        if (typeof  predict === 'function') {
            predict = predict();
        }
        if (val !== predict) {
            val = predict;
            fn();
        }
    }
}

class FloatWindow extends Component {

    constructor() {
        super();
        this.mousemovePtr = _.throttle(this.onmousemove.bind(this), 300);
        this.keyupPtr = _.throttle(this.onkeyup.bind(this), 300);
        this.negative = createNegative();
    }
    componentDidMount() {
        // 绑定鼠标移动事件
        document.body.addEventListener('mousemove', this.mousemovePtr, false);
        document.body.addEventListener('keyup', this.keyupPtr, false);
    }

    componentWillUnmount() {
        // 卸载鼠标移动事件
        document.body.removeEventListener('mousemove', this.mousemovePtr, false);
        document.body.removeEventListener('keyup', this.keyupPtr, false);
    }

    isNear(x, y) {
        let screenWidth = window.innerWidth;
        let screenHeight = window.innerHeight;
        let floatWindowWidth = 200;
        let floatWindowHeight = 200;

        if (
            screenWidth - x < floatWindowWidth
            && y < floatWindowHeight
        ) {
            return true;
        }
        else {
            return false;
        }
    }

    onkeyup(e) {
        // b的keyCode是66
        if (e.keyCode === 66 && e.ctrlKey) {
            let currentValue = this.props.floatWindow.enableAntiBlock;
            this.props.dispatch(enableAntiBlock(!currentValue));
        }
    }

    onmousemove(e) {
        let currentValue = this.props.floatWindow.enableAntiBlock;
        if (!currentValue) {
            return;
        }
        let param = '';
        let _this = this;
        if (this.isNear(e.clientX, e.clientY)) {
            param = MOUSE_NEAR_TRUE;
        }
        else {
            param = MOUSE_NEAR_FALSE;
        }
        // 当结果跟上次不同的时候才执行回调
        this.negative(param, function () {
            _this.props.dispatch(mouseNear(param));
        });
    }

    render() {
        let status = this.props.floatWindow.status;
        let antiBlockStatus = this.props.floatWindow.enableAntiBlock;
        let style = {};
        if (status === MOUSE_NEAR_TRUE) {
            style.display = 'none';
        }
        else {
            style.display = '';
        }
        return

            {this.props.children}

<Checkbox checked={antiBlockStatus}/>ctrl + b 启用防遮挡功能

;
    }

}

export default connect(s => s)(FloatWindow);
<|endoftext|>


<commit_before>/*jshint browser: true */
/*jshint unused: false */
/*global Backbone, $, window, setTimeout, Joi, _ */
/*global templateEngine*/

(function () {
  "use strict";

  window.ProgressView = Backbone.View.extend({

    template: templateEngine.createTemplate("progressBase.ejs"),

    el: "#progressPlaceholder",

    el2: "#progressPlaceholderIcon",

    toShow: false,

    action: function(){},

    events: {
      "click .progress-action button": "performAction"
    },

    performAction: function() {
      this.action();
    },

    initialize: function() {
    },

    showWithDelay: function(delay, msg, action, button) {
    var self = this;
      self.toShow = true;

      setTimeout(function(delay) {
        if (self.toShow === true) {
          self.show(msg, action, button);
        }
      }, delay);
    },

    show: function(msg, action, button) {
      $(this.el).html(this.template.render({}));
      $(".progress-text").text(msg);
      $(".progress-action").html(button);

      this.action = action;

      $(this.el).show();
      //$(this.el2).html('');
    },

    hide: function() {
      $(this.el).hide();
      this.action = function(){};
      //$(this.el2).html('');
    }

  });
}());
<commit_msg>added remove prog view button<commit_after>/*jshint browser: true */
/*jshint unused: false */
/*global Backbone, $, window, setTimeout, Joi, _ */
/*global templateEngine*/

(function () {
  "use strict";

  window.ProgressView = Backbone.View.extend({

    template: templateEngine.createTemplate("progressBase.ejs"),

    el: "#progressPlaceholder",

    el2: "#progressPlaceholderIcon",

    toShow: false,

    action: function(){},

    events: {
      "click .progress-action button": "performAction",
    },

    performAction: function() {
      //this.action();
      window.progressView.hide();
    },

    initialize: function() {
    },

    showWithDelay: function(delay, msg, action, button) {
    var self = this;
      self.toShow = true;

      setTimeout(function(delay) {
        if (self.toShow === true) {
          self.show(msg, action, button);
        }
      }, delay);
    },

    show: function(msg, action, button) {
      $(this.el).html(this.template.render({}));
      $(".progress-text").text(msg);
      $(".progress-action").html('<button class="button-danger">Cancel</button>');
      this.action = this.hide();
      //$(".progress-action").html(button);
      //this.action = action;

      $(this.el).show();
      //$(this.el2).html('');
    },

    hide: function() {
      $(this.el).hide();
      this.action = function(){};
      //$(this.el2).html('');
    }

  });
}());
<|endoftext|>