<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright (c) 2015 Digi International Inc., All Rights Reserved.
 */

'use strict';

angular.module('XBeeGatewayApp')
    //jshint -W098
    .controller('serialWidgetCtrl', function ($scope, utils, $log, dashboardApi, notificationService) {
        $scope.data_sending = false;
        $scope.last_received_timestamp = undefined;

        $scope.getStreamUpdateHandler = function (newData) {
            // For the serial widget, data *should* come in as an object of the form
            //   {"content": "...", "format": "base64"}
            var _newData = newData.value;
            if (_.isEmpty(_newData)) {
                $log.warn("Serial widget got malformed data", newData);
                return;
            }
            var _timestamp = newData.timestamp;
            newData = _newData;

            // XBee Gateway
            if (angular.isString(newData)) {
                var decoded = utils.base64_decode(newData);

                if (_.isEmpty(decoded)) {
                    // Might not be decoded.
                    newText = newData;
                } else {
                    // Successfully decoded the data.
                    newText = decoded;
                }
                $log.info("Adding text:", newText);

	//CLAUDINE'S CODE STARTS HERE
					//Claudine's Code for Wireless Distress Monitoring Network
					var br = String.fromCharCode(10); //Carriage Return
					// Insert a CR before/after string to make it show on new line
					// on both ends
					var cr = String.fromCharCode(13);
					var zb_data = newText;

					//Search for the Distress Flag information's starting position
					var pos = zb_data.search("DF");
					var df_data = zb_data.slice(pos+2,pos+3);
					var df_str = "Status: ";
					//Determine the status of the worker using the distress flag
					df_data = parseInt(df_data, 10);
					if(df_data < 1){
						df_str = df_str + "OK" + br;
					}else{
						df_str = df_str + "Distressed" + br;
					}

					df_str = br + df_str;

					//Search for the Heart Rate information's starting position
					pos = zb_data.search("HR");
					var hr_data = zb_data.slice(pos+2,pos+5);
					var hr_str = "Heart Rate: ";
					//Just formatting the heart rate value
					hr_data = parseInt(hr_data, 10);
					hr_str = hr_str + hr_data + br;
					hr_str = br + hr_str;

					//Search for the Location information's starting point
					pos = zb_data.search("UTC");
					var pos_loc_end = zb_data.search("t=");
					//Cut the location substring
					var loc_data = zb_data.slice(pos, pos_loc_end-1);
					//LAT substring
					pos = loc_data.search("LAT");
					pos_loc_end = loc_data.search("LON");
					var temp_lat_str = loc_data.slice(pos+3, pos_loc_end-1);
					var lat_str = temp_lat_str.slice(0, 2) + "\xB0"	//50°
								+ temp_lat_str.slice(2,4) + "\'"	//25'
								+ temp_lat_str.slice(5, 7) + "." + temp_lat_str.slice(7, 9) + "\"" //03.10"
								+ temp_lat_str.slice(10, 11) + " ";	//N

					//LON substring
					var temp_lon_str = loc_data.slice(pos_loc_end+3, loc_data.length);
					var lon_str = temp_lon_str.slice(0, 3) + "\xB0" //104°
								+ temp_lon_str.slice(3, 5) + "\'"   //35'
								+ temp_lon_str.slice(6, 8) + "." + temp_lon_str.slice(8, 10) + "\"" //01.80"
								+ temp_lon_str.slice(11, 12); //W

					//Converting GPS string
					var loc_str = "Location: " + lat_str + lon_str;
					loc_str = br + loc_str;
					//Output the worker information to the website
					var worker_info_str = df_str + hr_str + loc_str;
					newText = worker_info_str + cr;

				$scope.displaySerialText(dr_str, true);
				$scope.displaySerialText(hr_str, true);
				$scope.displaySerialText(loc_str, true);
				//$scope.displaySerialText(newText, true);
                $scope.last_received_timestamp = _timestamp;
                return;
            }
            // Or, XBee Wi-Fi
            else if (newData.content !== undefined){
                // Check format to see if we should b64 decode
                var newText;
                if(newData.format === "base64"){
                    newText = utils.base64_decode(newData.content);
                } else {
                    newText = newData.content;
                }
                $scope.displaySerialText(newText, true);
                $scope.last_received_timestamp = _timestamp;
            } else {
                // TODO display some error?
                $log.warn('Serial widget got malformed data', newData);
            }
        }

        $scope.setStreamUpdateHandler = function (newData) {
            // Updates to the set stream could be handled here too, as we use the same for get/set
            return;
        }

        $scope.serialEnterKeypress = function($event){
            if($scope.serialOutText){
               $scope.sendText($scope.serialOutText);
            }
            $event.preventDefault();
        }

        $scope.sendText = function(text) {
            $scope.data_sending = true;

            if ($scope.widget.add_carriage_returns) {
                // Insert a CR before/after string to make it show on new line
                // on both ends
                var cr = String.fromCharCode(13);
                text = "dm=" + cr + text + cr;
            }

            dashboardApi.send_serial($scope.widget.device, $scope.widget.radio, text).then(
                function(result){
                    // On success, show the sent text
                    $scope.displaySerialText(text, false);
                    // Clear the input box for next entry
                    $scope.serialOutText = null;
                    // Reenable input
                    $scope.data_sending = false;
                },
                function(reason){
                    notificationService.error("Error sending text. Please try again.");
                    // Reenable input
                    $scope.data_sending = false;
                });
        }

        $scope.$watch('data_sending', function (sending) {
            if (sending) {
                $scope.widgetState = 1;
            } else {
                $scope.widgetState = 0;
            }
        });
    })
    .directive('serialWidget', function (widgetRegistry, utils, dataStreams, $log) {
        // called after DOM element is compiled
        var linker = function postLink(scope, element) {
            scope.$element = element;
            var type = 'serial';
            var spec = widgetRegistry.get(type);

            // See http://lodash.com/docs#bind
            // (dataUpdate simply calls scope.updateHandler)
            var getCallback = _.bind(scope.getStreamUpdateHandler, scope);
            var setCallback = _.bind(scope.setStreamUpdateHandler, scope);
            utils.postlinkWidget(type, scope, element, spec, getCallback, setCallback);
            // Any more fancy footwork can be done here.

            // Manually listen for serialIn
            var device = scope.widget.device;
            var inputStream = "xbee.serialIn/[" + scope.widget.radio + "]!";
            $log.debug("Listening for serial stream: ", device, inputStream);
            var removeListener = dataStreams.listen(device, inputStream, scope.getStreamUpdateHandler);
            scope.$on('$destroy', function () {
                removeListener();
            });

            // Widget display area
            // Use jquery to find, Angular's jqlite doesn't support selector
            var output_pane = $(element).find(".serial-display");

            scope.displaySerialText = function(text, isInbound){
                var $newText = null;
                if(isInbound){
                    $newText = $('').addClass("serial-in");
                    // Show incomming text inline
                    // Split string to handle any Carriage Returns
                    if(text === "\r"){
                        $newText.append($('
'));
                    } else {
                        var snippets = text.split("\r");
                        _.each(snippets, function (snippet) {
                            // CR at start & end will make cause empty strings
                            if(snippet === ""){
                                $newText.append($('
'));
                            } else {
                                $newText.append($('').text(snippet));
                            }
                        });
                    }
                } else {
                    // Outgoing should be in it's own line
                    $newText = $('
').text(text).addClass("serial-out");
                }
                $(output_pane).append($newText);
                $(output_pane).scrollTop($(output_pane)[0].scrollHeight);
            }
        };

        // AngularJS directive setup
        return {
            templateUrl: "widgets/serialWidget/serialWidget.tpl.html",
            restrict: 'AE',
            link: linker,
            controller: 'serialWidgetCtrl',
            scope: { widget: "=serialWidget", widgetState: "=state" }
        };
    })
    // This function, referred to in AngularJS as a "run block", is called by
    // AngularJS after the injector is created and is used to bootstrap the
    // application. The XBee ZigBee Cloud Kit makes use of the run block
    // to add widget definitions to the widget registry at start-up.
    .run(function(widgetRegistry) {
        // Adding the widget to the widget registry
        var widget_type_key = 'workerInfo';
        var widget_description = 'Worker Information Widget';
        var widget_spec = {
            // Whether or not the widget is built-in or user-created
            // (i.e., whether the code is in /src/app or /src/common)
            builtin: false,
            // widget size: X,Y (columns, rows)
            size: [3, 2],
            // description appearing in 'Widget Type' list when adding new
            // widgets
            description: widget_description,
            directive: "serial-widget",
            // camel-case version of directive
            directive_c: "serialWidget",

            // properties pertaining to widget settings
            /*
            has_input: does the widget's data get updated from Device Cloud?
            sends_output: does the widget send data to the device?
            input_xform: can the user specify a custom transformation to apply
                            to incoming data? (optional)
            options: list of objects defining the settings associated with this
                        widget type
                - options generally follow the Revalidator API
                    (http://github.com/flatiron/revalidator)
            */
            has_input: false,
            sends_output: false,
            options: [
                {key: "add_carriage_returns", label: "Add Carriage Returns",
                 type: "boolean", required: false, 'default': true}
            ]
        };

        // DO NOT CHANGE ANY CODE BELOW HERE.
        widgetRegistry.put(widget_type_key, widget_spec);
    });
<commit_msg>let br = \r<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright (c) 2015 Digi International Inc., All Rights Reserved.
 */

'use strict';

angular.module('XBeeGatewayApp')
    //jshint -W098
    .controller('serialWidgetCtrl', function ($scope, utils, $log, dashboardApi, notificationService) {
        $scope.data_sending = false;
        $scope.last_received_timestamp = undefined;

        $scope.getStreamUpdateHandler = function (newData) {
            // For the serial widget, data *should* come in as an object of the form
            //   {"content": "...", "format": "base64"}
            var _newData = newData.value;
            if (_.isEmpty(_newData)) {
                $log.warn("Serial widget got malformed data", newData);
                return;
            }
            var _timestamp = newData.timestamp;
            newData = _newData;

            // XBee Gateway
            if (angular.isString(newData)) {
                var decoded = utils.base64_decode(newData);

                if (_.isEmpty(decoded)) {
                    // Might not be decoded.
                    newText = newData;
                } else {
                    // Successfully decoded the data.
                    newText = decoded;
                }
                $log.info("Adding text:", newText);

	//CLAUDINE'S CODE STARTS HERE
					//Claudine's Code for Wireless Distress Monitoring Network
					var br = String.fromCharCode(10); //Carriage Return
					// Insert a CR before/after string to make it show on new line
					// on both ends
					var cr = String.fromCharCode(13);
					var zb_data = newText;

					//Search for the Distress Flag information's starting position
						var pos = zb_data.search("DF");
						var df_data = zb_data.slice(pos+2,pos+3);
						var df_str = "Status: ";
						//Determine the status of the worker using the distress flag
						df_data = parseInt(df_data, 10);
						if(df_data < 1){
							df_str = df_str + "OK";
						}else{
							df_str = df_str + "Distressed";
						}

						df_str = cr + df_str + cr;

					//Search for the Heart Rate information's starting position
						pos = zb_data.search("HR");
						var hr_data = zb_data.slice(pos+2,pos+5);
						var hr_str = "Heart Rate: ";
						//Just formatting the heart rate value
						hr_data = parseInt(hr_data, 10);
						hr_str = hr_str + hr_data;
						hr_str = cr + hr_str + cr;

					//Search for the Location information's starting point
						pos = zb_data.search("UTC");
						var pos_loc_end = zb_data.search("t=");
						//Cut the location substring
						var loc_data = zb_data.slice(pos, pos_loc_end-1);
						//LAT substring
						pos = loc_data.search("LAT");
						pos_loc_end = loc_data.search("LON");
						var temp_lat_str = loc_data.slice(pos+3, pos_loc_end-1);
						var lat_str = temp_lat_str.slice(0, 2) + "\xB0"	//50°
									+ temp_lat_str.slice(2,4) + "\'"	//25'
									+ temp_lat_str.slice(5, 7) + "." + temp_lat_str.slice(7, 9) + "\"" //03.10"
									+ temp_lat_str.slice(10, 11) + " ";	//N

						//LON substring
						var temp_lon_str = loc_data.slice(pos_loc_end+3, loc_data.length);
						var lon_str = temp_lon_str.slice(0, 3) + "\xB0" //104°
									+ temp_lon_str.slice(3, 5) + "\'"   //35'
									+ temp_lon_str.slice(6, 8) + "." + temp_lon_str.slice(8, 10) + "\"" //01.80"
									+ temp_lon_str.slice(11, 12); //W

						//Converting GPS string
						var loc_str = "Location: " + lat_str + lon_str;
						loc_str = cr + loc_str + cr;

					//Output the worker information to the website
					var worker_info_str = df_str + hr_str + loc_str;
					//newText = worker_info_str + cr;

				$scope.displaySerialText(df_str, true);
				$scope.displaySerialText(hr_str, true);
				$scope.displaySerialText(loc_str, true);
				//$scope.displaySerialText(newText, true);
                $scope.last_received_timestamp = _timestamp;
                return;
            }
            // Or, XBee Wi-Fi
            else if (newData.content !== undefined){
                // Check format to see if we should b64 decode
                var newText;
                if(newData.format === "base64"){
                    newText = utils.base64_decode(newData.content);
                } else {
                    newText = newData.content;
                }
                $scope.displaySerialText(newText, true);
                $scope.last_received_timestamp = _timestamp;
            } else {
                // TODO display some error?
                $log.warn('Serial widget got malformed data', newData);
            }
        }

        $scope.setStreamUpdateHandler = function (newData) {
            // Updates to the set stream could be handled here too, as we use the same for get/set
            return;
        }

        $scope.serialEnterKeypress = function($event){
            if($scope.serialOutText){
               $scope.sendText($scope.serialOutText);
            }
            $event.preventDefault();
        }

        $scope.sendText = function(text) {
            $scope.data_sending = true;

            if ($scope.widget.add_carriage_returns) {
                // Insert a CR before/after string to make it show on new line
                // on both ends
                var cr = String.fromCharCode(13);
                text = "dm=" + cr + text + cr;
            }

            dashboardApi.send_serial($scope.widget.device, $scope.widget.radio, text).then(
                function(result){
                    // On success, show the sent text
                    $scope.displaySerialText(text, false);
                    // Clear the input box for next entry
                    $scope.serialOutText = null;
                    // Reenable input
                    $scope.data_sending = false;
                },
                function(reason){
                    notificationService.error("Error sending text. Please try again.");
                    // Reenable input
                    $scope.data_sending = false;
                });
        }

        $scope.$watch('data_sending', function (sending) {
            if (sending) {
                $scope.widgetState = 1;
            } else {
                $scope.widgetState = 0;
            }
        });
    })
    .directive('serialWidget', function (widgetRegistry, utils, dataStreams, $log) {
        // called after DOM element is compiled
        var linker = function postLink(scope, element) {
            scope.$element = element;
            var type = 'serial';
            var spec = widgetRegistry.get(type);

            // See http://lodash.com/docs#bind
            // (dataUpdate simply calls scope.updateHandler)
            var getCallback = _.bind(scope.getStreamUpdateHandler, scope);
            var setCallback = _.bind(scope.setStreamUpdateHandler, scope);
            utils.postlinkWidget(type, scope, element, spec, getCallback, setCallback);
            // Any more fancy footwork can be done here.

            // Manually listen for serialIn
            var device = scope.widget.device;
            var inputStream = "xbee.serialIn/[" + scope.widget.radio + "]!";
            $log.debug("Listening for serial stream: ", device, inputStream);
            var removeListener = dataStreams.listen(device, inputStream, scope.getStreamUpdateHandler);
            scope.$on('$destroy', function () {
                removeListener();
            });

            // Widget display area
            // Use jquery to find, Angular's jqlite doesn't support selector
            var output_pane = $(element).find(".serial-display");

            scope.displaySerialText = function(text, isInbound){
                var $newText = null;
                if(isInbound){
                    $newText = $('').addClass("serial-in");
                    // Show incomming text inline
                    // Split string to handle any Carriage Returns
                    if(text === "\r"){
                        $newText.append($('
'));
                    } else {
                        var snippets = text.split("\r");
                        _.each(snippets, function (snippet) {
                            // CR at start & end will make cause empty strings
                            if(snippet === ""){
                                $newText.append($('
'));
                            } else {
                                $newText.append($('').text(snippet));
                            }
                        });
                    }
                } else {
                    // Outgoing should be in it's own line
                    $newText = $('
').text(text).addClass("serial-out");
                }
                $(output_pane).append($newText);
                $(output_pane).scrollTop($(output_pane)[0].scrollHeight);
            }
        };

        // AngularJS directive setup
        return {
            templateUrl: "widgets/serialWidget/serialWidget.tpl.html",
            restrict: 'AE',
            link: linker,
            controller: 'serialWidgetCtrl',
            scope: { widget: "=serialWidget", widgetState: "=state" }
        };
    })
    // This function, referred to in AngularJS as a "run block", is called by
    // AngularJS after the injector is created and is used to bootstrap the
    // application. The XBee ZigBee Cloud Kit makes use of the run block
    // to add widget definitions to the widget registry at start-up.
    .run(function(widgetRegistry) {
        // Adding the widget to the widget registry
        var widget_type_key = 'workerInfo';
        var widget_description = 'Worker Information Widget';
        var widget_spec = {
            // Whether or not the widget is built-in or user-created
            // (i.e., whether the code is in /src/app or /src/common)
            builtin: false,
            // widget size: X,Y (columns, rows)
            size: [3, 2],
            // description appearing in 'Widget Type' list when adding new
            // widgets
            description: widget_description,
            directive: "serial-widget",
            // camel-case version of directive
            directive_c: "serialWidget",

            // properties pertaining to widget settings
            /*
            has_input: does the widget's data get updated from Device Cloud?
            sends_output: does the widget send data to the device?
            input_xform: can the user specify a custom transformation to apply
                            to incoming data? (optional)
            options: list of objects defining the settings associated with this
                        widget type
                - options generally follow the Revalidator API
                    (http://github.com/flatiron/revalidator)
            */
            has_input: false,
            sends_output: false,
            options: [
                {key: "add_carriage_returns", label: "Add Carriage Returns",
                 type: "boolean", required: false, 'default': true}
            ]
        };

        // DO NOT CHANGE ANY CODE BELOW HERE.
        widgetRegistry.put(widget_type_key, widget_spec);
    });
<|endoftext|>


<commit_before>const PRIORITY_EVENT_TITLE_PREFIXES = ['1.', '2.', '3.', '4.', '5.', null];

function dispatchEvent(obj, event) {
    var evt = new Event(
        event,
        {
            target: obj,
            bubbles: true,
        }
    );
    obj.dispatchEvent(evt);
}

function insertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

function insertButtons(referenceNode, onclickAction, where, alternateReferenceNode) {
    var insertTarget = referenceNode;
    var buttonLabels = [
        // buttonLabel, eventTitlePrefix, buttonClassNames.
        ['1',       '1.', ['jfk-button', 'jfk-button-standard']],
        ['2',       '2.', ['jfk-button', 'jfk-button-standard']],
        ['3',       '3.', ['jfk-button', 'jfk-button-standard']],
        ['4',       '4.', ['jfk-button', 'jfk-button-standard']],
        ['5',       '5.', ['jfk-button', 'jfk-button-standard']],
        ['X',       null, ['jfk-button', 'jfk-button-standard']],
        ['DONE',    '✓',  ['jfk-button', 'jfk-button-default' ]],
        ['NOPE',    '✗',  ['jfk-button', 'jfk-button-standard']],
        ['OKAY',    '▣',  ['jfk-button', 'jfk-button-standard']],
        ['AWESOME', 'ツ', ['jfk-button', 'jfk-button-standard']],
    ];
    for (var i in buttonLabels) {
        var buttonLabel = buttonLabels[i][0];
        console.log('buttonLabel:', buttonLabel);

        var eventTitlePrefix = buttonLabels[i][1];
        console.log('eventTitlePrefix:', eventTitlePrefix);

        var buttonClassNames = buttonLabels[i][2];
        console.log('buttonClassNames:', buttonClassNames);

        var button = document.createElement('button');
        button.innerText = buttonLabel;
        button.classList.add(...buttonClassNames);

        (function(myButton, myEventTitlePrefix) {
            myButton.onclick = function() {
                console.log('myButton.onclick');
                onclickAction(myButton, myEventTitlePrefix);
            };
        }(button, eventTitlePrefix));

        console.log('inserting button', button, where, insertTarget);
        if (where === 'after') {
            insertAfter(button, insertTarget);
            insertTarget = button;
        } else if (where === 'inside') {
            if (alternateReferenceNode && !PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
                alternateReferenceNode.appendChild(button);
            } else {
                referenceNode.appendChild(button);
            }
        }
    }
}

function clickButton(clickedData) {
    console.group('clickButton');
    console.log('clickedData:', clickedData);
    console.log('button clicked');

    // Today.
    var date = new Date();
    var monthName = date.toLocaleString('en-us', { 'month': 'short' });
    var todayFormattedDate = monthName + ' ' + date.getDate() + ', ' + date.getFullYear();

    // Event date.
    var startDateInput = document.querySelector('[aria-label="Start date"]');
    var endDateInput = document.querySelector('[aria-label="End date"]');
    var eventDate = startDateInput.value;
    if (endDateInput.value !== startDateInput.value) {
        eventDate += ' - ' + endDateInput.value;
    }
    console.log('eventDate:', eventDate);

    var eventTitle = document.querySelector('[aria-label="Title"]');
    var calendarEventTitle = eventTitle.value;
    console.log('before calendarEventTitle:', calendarEventTitle);

    // Remove leading number (e.g. "1. " in "1. My Calendar Event").
    calendarEventTitle = calendarEventTitle.replace(/^\d+\. /, '');
    // Remove leading ~ character.
    calendarEventTitle = calendarEventTitle.replace(/^~ /, '');

    console.log(' after calendarEventTitle:', calendarEventTitle);

    // "✓ My Event; Dec 31, 2015; event date: Jan 1, 2016"
    // "1. My Event"
    // "My Event"
    var eventTitlePrefix = clickedData['eventTitlePrefix'];
    var newCalendarEventTitle = calendarEventTitle;
    if (eventTitlePrefix !== null) {
        newCalendarEventTitle = eventTitlePrefix + ' ' + calendarEventTitle;
        if (! PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
            newCalendarEventTitle += ';' +
                ' ' + todayFormattedDate + ';' +
                ' event date: ' + eventDate;
        }
    }
    console.log('newCalendarEventTitle:', newCalendarEventTitle);
    eventTitle.value = newCalendarEventTitle;

    dispatchEvent(eventTitle, 'input');

    var saveButton = document.querySelector('[aria-label="Save"]');
    setTimeout(function() {
        saveButton.click();
    }, 500);

    console.groupEnd();
}

var buttonClickedData;
(function(){
    var pathname = window.location.pathname;
    setInterval(function() {
        if (pathname !== window.location.pathname) {
            console.log('pathname changed');
            pathname = window.location.pathname;
            if (pathname.match(/^\/calendar\/r\/eventedit\//)) {
                console.log('on event edit page');

                if (buttonClickedData) {
                    clickButton(buttonClickedData);
                    buttonClickedData = null;
                } else {
                    // Add action buttons to calendar event edit page.
                    console.log('adding buttons');
                    var onclickAction = function(myButton, myEventTitlePrefix) {
                        buttonClickedData = {
                            'button': myButton,
                            'eventTitlePrefix': myEventTitlePrefix,
                        };
                        clickButton(buttonClickedData);
                        buttonClickedData = null;
                    };

                    var saveButton = document.querySelector('[aria-label="Save"]');

                    var buttonWrapper = document.createElement('div');
                    buttonWrapper.style.left = '350px';
                    buttonWrapper.style.position = 'absolute';
                    buttonWrapper.style.top = '65px';
                    buttonWrapper.style.width = '350px';
                    buttonWrapper.style.zIndex = '1';

                    insertAfter(buttonWrapper, saveButton);
                    insertButtons(buttonWrapper, onclickAction, 'inside');
                }
            }
        }
    }, 1000);
})();

document.onclick = function(event) {
    console.group('element clicked');
    console.log('element clicked:', event.target);

    var target = event.target;
    var eventIdFound = false;
    for (var i = 0; i < 5; i++) {
        // Ignore event bubble.
        if (target.getAttribute('id') === 'xDetDlg') {
            break;
        } else if (target.getAttribute('data-eventid')) {
            eventIdFound = true;
            break;
        }
        target = target.parentElement;
    }
    console.log('target:', target);
    if (eventIdFound) {
        var eventId = target.getAttribute('data-eventid');
        console.log('event id found:', eventId);

        function modifyEventBubble(attempt) {
            attempt = attempt || 1;
            if (attempt >= 3) {
                return;
            }
            attempt += 1;

            var eventBubble = document.querySelector('#xDetDlg[data-eventid="' + eventId + '"]');
            console.log('eventBubble:', eventBubble);
            if (!eventBubble) {
                setTimeout(modifyEventBubble, 500, attempt);
                return;
            }

            var editEventButton = eventBubble.querySelector('[data-tooltip="Edit event"]');
            var eventEditable = editEventButton ? true : false;
            if (!eventEditable) {
                return;
            }

            var lastEventBubbleMetaItem = document.querySelector('#xDtlDlgCt > div:last-child');

            var newEventBubbleMetaItem = document.createElement('div');
            newEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(newEventBubbleMetaItem, lastEventBubbleMetaItem);

            var lastNewEventBubbleMetaItem = document.createElement('div');
            lastNewEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(lastNewEventBubbleMetaItem, newEventBubbleMetaItem);

            var onclickAction = function(myButton, myEventTitlePrefix) {
                buttonClickedData = {
                    'button': myButton,
                    'eventTitlePrefix': myEventTitlePrefix,
                };

                var editEventButton = document.querySelector('[aria-label="Edit event"]');
                console.log('editEventButton:', editEventButton);
                editEventButton.click();
            };
            insertButtons(newEventBubbleMetaItem, onclickAction, 'inside', lastNewEventBubbleMetaItem);
        }
        setTimeout(modifyEventBubble, 100);
    }
    console.groupEnd();
};
<commit_msg>Remove "! " from event titles when removing prefixes"<commit_after>const PRIORITY_EVENT_TITLE_PREFIXES = ['1.', '2.', '3.', '4.', '5.', null];

function dispatchEvent(obj, event) {
    var evt = new Event(
        event,
        {
            target: obj,
            bubbles: true,
        }
    );
    obj.dispatchEvent(evt);
}

function insertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

function insertButtons(referenceNode, onclickAction, where, alternateReferenceNode) {
    var insertTarget = referenceNode;
    var buttonLabels = [
        // buttonLabel, eventTitlePrefix, buttonClassNames.
        ['1',       '1.', ['jfk-button', 'jfk-button-standard']],
        ['2',       '2.', ['jfk-button', 'jfk-button-standard']],
        ['3',       '3.', ['jfk-button', 'jfk-button-standard']],
        ['4',       '4.', ['jfk-button', 'jfk-button-standard']],
        ['5',       '5.', ['jfk-button', 'jfk-button-standard']],
        ['X',       null, ['jfk-button', 'jfk-button-standard']],
        ['DONE',    '✓',  ['jfk-button', 'jfk-button-default' ]],
        ['NOPE',    '✗',  ['jfk-button', 'jfk-button-standard']],
        ['OKAY',    '▣',  ['jfk-button', 'jfk-button-standard']],
        ['AWESOME', 'ツ', ['jfk-button', 'jfk-button-standard']],
    ];
    for (var i in buttonLabels) {
        var buttonLabel = buttonLabels[i][0];
        console.log('buttonLabel:', buttonLabel);

        var eventTitlePrefix = buttonLabels[i][1];
        console.log('eventTitlePrefix:', eventTitlePrefix);

        var buttonClassNames = buttonLabels[i][2];
        console.log('buttonClassNames:', buttonClassNames);

        var button = document.createElement('button');
        button.innerText = buttonLabel;
        button.classList.add(...buttonClassNames);

        (function(myButton, myEventTitlePrefix) {
            myButton.onclick = function() {
                console.log('myButton.onclick');
                onclickAction(myButton, myEventTitlePrefix);
            };
        }(button, eventTitlePrefix));

        console.log('inserting button', button, where, insertTarget);
        if (where === 'after') {
            insertAfter(button, insertTarget);
            insertTarget = button;
        } else if (where === 'inside') {
            if (alternateReferenceNode && !PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
                alternateReferenceNode.appendChild(button);
            } else {
                referenceNode.appendChild(button);
            }
        }
    }
}

function clickButton(clickedData) {
    console.group('clickButton');
    console.log('clickedData:', clickedData);
    console.log('button clicked');

    // Today.
    var date = new Date();
    var monthName = date.toLocaleString('en-us', { 'month': 'short' });
    var todayFormattedDate = monthName + ' ' + date.getDate() + ', ' + date.getFullYear();

    // Event date.
    var startDateInput = document.querySelector('[aria-label="Start date"]');
    var endDateInput = document.querySelector('[aria-label="End date"]');
    var eventDate = startDateInput.value;
    if (endDateInput.value !== startDateInput.value) {
        eventDate += ' - ' + endDateInput.value;
    }
    console.log('eventDate:', eventDate);

    var eventTitle = document.querySelector('[aria-label="Title"]');
    var calendarEventTitle = eventTitle.value;
    console.log('before calendarEventTitle:', calendarEventTitle);

    // Remove leading number (e.g. "1. " in "1. My Calendar Event").
    calendarEventTitle = calendarEventTitle.replace(/^\d+\. /, '');
    // Remove leading ! character.
    calendarEventTitle = calendarEventTitle.replace(/^! /, '');
    // Remove leading ~ character.
    calendarEventTitle = calendarEventTitle.replace(/^~ /, '');

    console.log(' after calendarEventTitle:', calendarEventTitle);

    // "✓ My Event; Dec 31, 2015; event date: Jan 1, 2016"
    // "1. My Event"
    // "My Event"
    var eventTitlePrefix = clickedData['eventTitlePrefix'];
    var newCalendarEventTitle = calendarEventTitle;
    if (eventTitlePrefix !== null) {
        newCalendarEventTitle = eventTitlePrefix + ' ' + calendarEventTitle;
        if (! PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
            newCalendarEventTitle += ';' +
                ' ' + todayFormattedDate + ';' +
                ' event date: ' + eventDate;
        }
    }
    console.log('newCalendarEventTitle:', newCalendarEventTitle);
    eventTitle.value = newCalendarEventTitle;

    dispatchEvent(eventTitle, 'input');

    var saveButton = document.querySelector('[aria-label="Save"]');
    setTimeout(function() {
        saveButton.click();
    }, 500);

    console.groupEnd();
}

var buttonClickedData;
(function(){
    var pathname = window.location.pathname;
    setInterval(function() {
        if (pathname !== window.location.pathname) {
            console.log('pathname changed');
            pathname = window.location.pathname;
            if (pathname.match(/^\/calendar\/r\/eventedit\//)) {
                console.log('on event edit page');

                if (buttonClickedData) {
                    clickButton(buttonClickedData);
                    buttonClickedData = null;
                } else {
                    // Add action buttons to calendar event edit page.
                    console.log('adding buttons');
                    var onclickAction = function(myButton, myEventTitlePrefix) {
                        buttonClickedData = {
                            'button': myButton,
                            'eventTitlePrefix': myEventTitlePrefix,
                        };
                        clickButton(buttonClickedData);
                        buttonClickedData = null;
                    };

                    var saveButton = document.querySelector('[aria-label="Save"]');

                    var buttonWrapper = document.createElement('div');
                    buttonWrapper.style.left = '350px';
                    buttonWrapper.style.position = 'absolute';
                    buttonWrapper.style.top = '65px';
                    buttonWrapper.style.width = '350px';
                    buttonWrapper.style.zIndex = '1';

                    insertAfter(buttonWrapper, saveButton);
                    insertButtons(buttonWrapper, onclickAction, 'inside');
                }
            }
        }
    }, 1000);
})();

document.onclick = function(event) {
    console.group('element clicked');
    console.log('element clicked:', event.target);

    var target = event.target;
    var eventIdFound = false;
    for (var i = 0; i < 5; i++) {
        // Ignore event bubble.
        if (target.getAttribute('id') === 'xDetDlg') {
            break;
        } else if (target.getAttribute('data-eventid')) {
            eventIdFound = true;
            break;
        }
        target = target.parentElement;
    }
    console.log('target:', target);
    if (eventIdFound) {
        var eventId = target.getAttribute('data-eventid');
        console.log('event id found:', eventId);

        function modifyEventBubble(attempt) {
            attempt = attempt || 1;
            if (attempt >= 3) {
                return;
            }
            attempt += 1;

            var eventBubble = document.querySelector('#xDetDlg[data-eventid="' + eventId + '"]');
            console.log('eventBubble:', eventBubble);
            if (!eventBubble) {
                setTimeout(modifyEventBubble, 500, attempt);
                return;
            }

            var editEventButton = eventBubble.querySelector('[data-tooltip="Edit event"]');
            var eventEditable = editEventButton ? true : false;
            if (!eventEditable) {
                return;
            }

            var lastEventBubbleMetaItem = document.querySelector('#xDtlDlgCt > div:last-child');

            var newEventBubbleMetaItem = document.createElement('div');
            newEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(newEventBubbleMetaItem, lastEventBubbleMetaItem);

            var lastNewEventBubbleMetaItem = document.createElement('div');
            lastNewEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(lastNewEventBubbleMetaItem, newEventBubbleMetaItem);

            var onclickAction = function(myButton, myEventTitlePrefix) {
                buttonClickedData = {
                    'button': myButton,
                    'eventTitlePrefix': myEventTitlePrefix,
                };

                var editEventButton = document.querySelector('[aria-label="Edit event"]');
                console.log('editEventButton:', editEventButton);
                editEventButton.click();
            };
            insertButtons(newEventBubbleMetaItem, onclickAction, 'inside', lastNewEventBubbleMetaItem);
        }
        setTimeout(modifyEventBubble, 100);
    }
    console.groupEnd();
};
<|endoftext|>


<commit_before>var a='video';var b=document.getElementById('videoPlayer');var c='div#vid-overlay';var d='#vidAnnotation';var e='a#addAnnotation';var f='a.removeAnnotation';var g='a#playPauseButton';var h='progress#progress';var i='div#informationalText';var j='a#volume-down';var k='a#volume-up';var l='a#hidey-showy';var m='a#delete-everything-button';var n='a#toggle-annotations-button';var o='span#play-time';var p=document.getElementById('vid-canvas');var q=p.getContext('2d');var r=400;var s={};var t=false;var u=false;var v=0;var w=[];var x=false;var y=true;function z(a,b,c,d,e,f,g,h){this.textString=a;this.imageUrl=b;this.xPosition=c;this.yPosition=d;this.aWidth=e;this.aHeight=f;this.startTime=g;this.endTime=h;}function A(a){var b=(String(a.startTime))+(String(a.endTime))+(String(a.xPosition))+(String(a.yPosition))+(a.text);var c=CryptoJS.MD5(b);return c.toString();}function B(a){var b=new z(a,null,30,30,30,30,3,5);w.push(b);console.log(w);}function C(a){if(!y)return;var b=A(a);var d='

';var e='div#'+b;$(c).append(d);console.log('attempting to add:'+d);var f=a.aHeight;if(f<20)f=20;var g=a.aWidth;if(g<30)g=30;$(e).css({"width":(g*r/400),"height":(f*r/400),"position":"relative","top":(a.yPosition*r/400),"left":(a.xPosition*r/400)});var h=a.textString;console.log('annotation title: '+h);if(h==null)h="";$(e).text(h);if(a.imageUrl!=null){var i='';$(e).append(i);var j=e+' img';$(j).css({"width":"100%","height":"auto"});}}function D(a){var b=A(a);var c='div#'+b;$(c).remove();}function E(){console.log('update called.');$(o).text(M(v));var a=$.grep(w,function(a){return(v>=a.startTime&&v0){console.log('have new annotations to load');for(var c=0;c0){console.log('have old annotations to remove');for(var c=0;c<e.length;c++){console.log('need to remove something.');D(e[c]);}}console.log(a);}function F(){var a=$.grep(w,function(a){return(v>=a.startTime&&v0){console.log('have new annotations to load');for(var b=0;b<a.length;b++){console.log('need to add something.');var c=a[b];console.log(c);C(c);}}}function G(){var b=$(a).data('easyannotation-file-id');if(localStorage.getItem(b)){var c=JSON.parse(localStorage.getItem(b));if(c==null)w=[];else w=c;}else w=[];$(a).controls=false;w.sort(function(a,b){return a.startTime-b.startTime;});O();R();}function H(){var b=JSON.stringify(w);var c=$(a).data('easyannotation-file-id');localStorage.setItem(c,b);}function I(){window.localStorage.clear();w=[];console.log('Local storage cleared.');}function J(){u=true;console.log('Starting to add annotation...');$(a).trigger('pause');$(i).text("Begin drawing over the video...");}function K(){q.clearRect(0,0,p.width,p.height);u=false;$(i).text("");var a=prompt("Please enter your annotation text:","");var b=prompt("Please enter how long you want the annotation to stay on screen:","");if(b==null||isNaN(b))b="2";var c=s.startX;var d=s.startY;var e=s.w;var f=s.h;if(f<20)f=20;if(e<30)e=30;if(a!=null&&a.length>0){var g=new z(a,null,c,d,e,f,v,(v+parseInt(b)));w.push(g);H();O();}}function L(){if(b.paused||b.ended){b.play();$(g).html('');}else{b.pause();$(g).html('');}}function M(a){var b=Math.floor(a/60);var c=a-(b*60);if(c<10)c="0"+String(c);if(a<10)return "0:0"+String(a);if(a<60)return "0:"+String(a);return String(b)+":"+String(c);}function N(){var a=document.getElementById('progress');var c=Math.floor((100/b.duration)*(b.currentTime));a.value=c;a.innerHTML=c+"%";}function O(){w.sort(function(a,b){return a.startTime-b.startTime;});$("li.vidAnnotationListItem").each(function(a){this.remove();});for(var a=0;a<w.length;a++){var b=w[a];var c='
X
Text annotation

'+M(b.startTime)+' - '+M(b.endTime)+'

'+b.textString+'

';$("ul#vidAnnotationList").append(c);}}function P(a){var b=w[a];D(b);w.splice(a,1);O();H();}function Q(a,b){var c=a.getBoundingClientRect();return{x:b.clientX-c.left,y:b.clientY-c.top};}function R(){p.addEventListener('mousedown',S,false);p.addEventListener('mouseup',T,false);p.addEventListener('mousemove',U,false);}function S(a){if(u){s.startX=Q(p,a).x;s.startY=Q(p,a).y;console.log(s);t=true;}}function T(){if(!u)return;t=false;K();}function U(a){if(t&&u){console.log('pageX: '+a.pageX+' pageY: '+a.pageY);s.w=Q(p,a).x-s.startX;s.h=Q(p,a).y-s.startY;q.clearRect(0,0,p.width,p.height);V();}}function V(){if(u){q.fillStyle="rgba(89, 124, 86, 0.85)";q.fillRect(s.startX,s.startY,s.w,s.h);}}$(document).ready(function(){console.log('ready');G();$(a).bind('play',function(){console.log('started playing.');E();$(g).html('');});$(a).bind('pause',function(){console.log('playback paused.');$(g).html('');});$(a).bind('ended',function(){console.log('finished playing.');v=0;document.getElementById('progress').value=0;$(g).html('');$('div.annotation-on-screen').remove();});$(a).bind('timeupdate',function(){N();var a=Math.floor(this.currentTime);if(a!=v){console.log('seconds changed: '+a);v=a;E();}});$(e).click(function(){J();});$(g).click(function(){L();});$(document).on('click',f,function(){var a=$(this).data('annotation-id');P(a);});$(k).click(function(){if(b.volume==1)return;b.volume+=0.1;});$(j).click(function(){if(b.volume==0)return;b.volume-=0.1;});$(m).click(function(){var a=confirm('This will delete ALL annotations for every video in easyAnnotate. Do you wish to continue?');if(a){I();H();O();$('div.annotation-on-screen').remove();}});$(l).click(function(){if(x){$(a).animate({"width":"400px"},function(){$(d).show();});$(this).text("Hide Annotation List");x=false;r=400;}else{$(a).animate({"width":"600px"});$(d).hide();$(this).text("Show Annotation List");x=true;r=600;}});$(n).click(function(){if(y){y=false;$(this).text("Turn on annotations");$('div.annotation-on-screen').remove();}else{y=true;$(this).text("Turn off annotations");F();}});Mousetrap.bind('space',function(){$(g).trigger('click');});Mousetrap.bind('up',function(){$(k).trigger('click');});Mousetrap.bind('down',function(){$(j).trigger('click');});Mousetrap.bind('n',function(){J();});Mousetrap.bind('h',function(){$(l).trigger('click');});});<commit_msg>Simple build.py run<commit_after>var a='video';var b=document.getElementById('videoPlayer');var c='div#vid-overlay';var d='#vidAnnotation';var e='a#addAnnotation';var f='a.removeAnnotation';var g='a#playPauseButton';var h='progress#progress';var i='div#informationalText';var j='a#volume-down';var k='a#volume-up';var l='a#hidey-showy';var m='a#delete-everything-button';var n='a#toggle-annotations-button';var o='span#play-time';var p=document.getElementById('vid-canvas');var q=p.getContext('2d');var r=400;var s={};var t=false;var u=false;var v=0;var w=[];var x=false;var y=true;function z(a,b,c,d,e,f,g,h){this.textString=a;this.imageUrl=b;this.xPosition=c;this.yPosition=d;this.aWidth=e;this.aHeight=f;this.startTime=g;this.endTime=h;}function A(a){var b=(String(a.startTime))+(String(a.endTime))+(String(a.xPosition))+(String(a.yPosition))+(a.text);var c=CryptoJS.MD5(b);return c.toString();}function B(a){var b=new z(a,null,30,30,30,30,3,5);w.push(b);console.log(w);}function C(a){if(!y)return;var b=A(a);var d='

';var e='div#'+b;$(c).append(d);console.log('attempting to add:'+d);var f=a.aHeight;if(f<20)f=20;var g=a.aWidth;if(g<30)g=30;$(e).css({"width":(g*r/400),"height":(f*r/400),"position":"relative","top":(a.yPosition*r/400),"left":(a.xPosition*r/400)});var h=a.textString;console.log('annotation title: '+h);if(h==null)h="";$(e).text(h);if(a.imageUrl!=null){var i='';$(e).append(i);var j=e+' img';$(j).css({"width":"100%","height":"auto"});}}function D(a){var b=A(a);var c='div#'+b;$(c).remove();}function E(){console.log('update called.');$(o).text(M(v));var a=$.grep(w,function(a){return(v>=a.startTime&&v0){console.log('have new annotations to load');for(var c=0;c0){console.log('have old annotations to remove');for(var c=0;c<e.length;c++){console.log('need to remove something.');D(e[c]);}}console.log(a);}function F(){var a=$.grep(w,function(a){return(v>=a.startTime&&v0){console.log('have new annotations to load');for(var b=0;b<a.length;b++){console.log('need to add something.');var c=a[b];console.log(c);C(c);}}}function G(){var b=$(a).data('easyannotation-file-id');if(localStorage.getItem(b)){var c=JSON.parse(localStorage.getItem(b));if(c==null)w=[];else w=c;}else w=[];$(a).controls=false;w.sort(function(a,b){return a.startTime-b.startTime;});O();R();}function H(){var b=JSON.stringify(w);var c=$(a).data('easyannotation-file-id');localStorage.setItem(c,b);}function I(){window.localStorage.clear();w=[];console.log('Local storage cleared.');}function J(){u=true;console.log('Starting to add annotation...');$(a).trigger('pause');$(i).text("Begin drawing over the video...");}function K(){q.clearRect(0,0,p.width,p.height);u=false;$(i).text("");var a=prompt("Please enter your annotation text:","");var b=prompt("Please enter how long you want the annotation to stay on screen:","");if(b==null||isNaN(b))b="2";if(x==true){var c=(s.startX/1.5);var d=(s.startY/1.5);var e=(s.w/1.5);var f=(s.h/1.5);}else{var c=s.startX;var d=s.startY;var e=s.w;var f=s.h;}if(f<20)f=20;if(e<30)e=30;if(c+e>390)c=390-e;if(d+f>210)d=210-f;if(a!=null&&a.length>0){var g=new z(a,null,c,d,e,f,v,(v+parseInt(b)));w.push(g);H();O();}}function L(){if(b.paused||b.ended){b.play();$(g).html('');}else{b.pause();$(g).html('');}}function M(a){var b=Math.floor(a/60);var c=a-(b*60);if(c<10)c="0"+String(c);if(a<10)return "0:0"+String(a);if(a<60)return "0:"+String(a);return String(b)+":"+String(c);}function N(){var a=document.getElementById('progress');var c=Math.floor((100/b.duration)*(b.currentTime));a.value=c;a.innerHTML=c+"%";}function O(){w.sort(function(a,b){return a.startTime-b.startTime;});$("li.vidAnnotationListItem").each(function(a){this.remove();});for(var a=0;a<w.length;a++){var b=w[a];var c='
X
Text annotation

'+M(b.startTime)+' - '+M(b.endTime)+'

'+b.textString+'

';$("ul#vidAnnotationList").append(c);}}function P(a){var b=w[a];D(b);w.splice(a,1);O();H();}function Q(a,b){var c=a.getBoundingClientRect();return{x:b.clientX-c.left,y:b.clientY-c.top};}function R(){p.addEventListener('mousedown',S,false);p.addEventListener('mouseup',T,false);p.addEventListener('mousemove',U,false);}function S(a){if(u){s.startX=Q(p,a).x;s.startY=Q(p,a).y;console.log(s);t=true;}}function T(){if(!u)return;t=false;K();}function U(a){if(t&&u){console.log('pageX: '+a.pageX+' pageY: '+a.pageY);s.w=Q(p,a).x-s.startX;s.h=Q(p,a).y-s.startY;q.clearRect(0,0,p.width,p.height);V();}}function V(){if(u){q.fillStyle="rgba(89, 124, 86, 0.85)";q.fillRect(s.startX,s.startY,s.w,s.h);}}$(document).ready(function(){console.log('ready');G();$(a).bind('play',function(){console.log('started playing.');E();$(g).html('');});$(a).bind('pause',function(){console.log('playback paused.');$(g).html('');});$(a).bind('ended',function(){console.log('finished playing.');v=0;document.getElementById('progress').value=0;$(g).html('');$('div.annotation-on-screen').remove();});$(a).bind('timeupdate',function(){N();var a=Math.floor(this.currentTime);if(a!=v){console.log('seconds changed: '+a);v=a;E();}});$(e).click(function(){J();});$(g).click(function(){L();});$(document).on('click',f,function(){var a=$(this).data('annotation-id');P(a);});$(k).click(function(){if(b.volume==1)return;b.volume+=0.1;});$(j).click(function(){if(b.volume==0)return;b.volume-=0.1;});$(m).click(function(){var a=confirm('This will delete ALL annotations for every video in easyAnnotate. Do you wish to continue?');if(a){I();H();O();$('div.annotation-on-screen').remove();}});$(l).click(function(){if(x){$(a).animate({"width":"400px"},function(){$(d).show();});$(this).text("Hide Annotation List");x=false;r=400;p.width=400;p.height=220;}else{$(a).animate({"width":"600px"});$(d).hide();$(this).text("Show Annotation List");x=true;r=600;p.width=600;p.height=330;}});$(n).click(function(){if(y){y=false;$(this).text("Turn on annotations");$('div.annotation-on-screen').remove();}else{y=true;$(this).text("Turn off annotations");F();}});Mousetrap.bind('space',function(){$(g).trigger('click');});Mousetrap.bind('up',function(){$(k).trigger('click');});Mousetrap.bind('down',function(){$(j).trigger('click');});Mousetrap.bind('n',function(){J();});Mousetrap.bind('h',function(){$(l).trigger('click');});});<|endoftext|>


<commit_before>/**
 * ClientAppointment
 *
 * @module activities/client-appointment
 *
 * FIXME: Complete jsdocs, description
 * TODO: Quick460 Must complete refactoring
 */

import * as activities from '../index';
import Activity from '../../components/Activity';
import EditClientBookingCardVM from '../../viewmodels/EditClientBookingCardVM';
import UserType from '../../enums/UserType';
import clientAppointments from '../../data/clientAppointments';
import ko from 'knockout';
import shell from '../../app.shell';
import { show as showError } from '../../modals/error';
import style from './style.styl';
import template from './template.html';

const ROUTE_NAME = 'client-appointment';

export default class ClientAppointment extends Activity {

    static get template() { return template; }

    static get style() { return style; }

    constructor($activity, app) {
        super($activity, app);

        this.accessLevel = UserType.loggedUser;
        this.helpLink = '/help/relatedArticles/201983163-making-changes-canceling-appointments';
        this.navBar = Activity.createSubsectionNavBar('', {
            backLink: '/myAppointments',
            helpLink: this.helpLink
        });
        this.title('Your appointments');

        // View properties
        this.list = clientAppointments.list;
        this.currentIndex = ko.observable(-1);
        this.currentItem = new EditClientBookingCardVM(app);
        this.isLoading = ko.observable(false);
        this.returnText = ko.observable('Back');

        this.isEditButtonVisible = ko.pureComputed(function() {
            return (this.canCancel() || this.canEdit()) && !this.isEditMode() && !this.isCancelMode();
        }, this.currentItem);
        this.isCancelEditButtonVisible = ko.pureComputed(function() {
            return (this.canCancel() || this.canEdit()) && (this.isEditMode() || this.isCancelMode());
        }, this.currentItem);
        this.isEmpty = ko.pureComputed(() => this.currentIndex() === -2);

        this.__viewMethods();
    }

    __viewMethods() {
        var updateListIndex = () => {
            if (this.list().length) {
                if (this.currentIndex() === -1) {
                    // Single booking was selected, find in the list
                    var bID = this.currentItem.booking().bookingID();
                    this.list().some((b, i) => {
                        if (b.bookingID() === bID) {
                            this.currentIndex(i);
                            return true;
                        }
                    });
                }
            }
        };

        this.load = (id) => {
            if (id) {
                this.currentIndex(-1);
                this.currentItem.load(id)
                .then(function() {
                    // Load the list in background
                    clientAppointments.sync()
                    .then(updateListIndex);
                })
                .catch(function(err) {
                    showError({
                        title: 'Error loading the appointment',
                        error: err
                    });
                });
            }
            else {
                this.isLoading(true);
                this.currentItem.reset();
                clientAppointments.sync()
                .then(() => {
                    var first = this.list().length ? this.list()[0] : null;
                    if (first) {
                        this.currentIndex(0);
                        this.currentItem.load(first);
                        updateListIndex();
                        // Update URL
                        shell.replaceState(null, null, '/clientAppointment/' + first.bookingID());
                    }
                    else {
                        this.currentIndex(-2);
                        // Update URL
                        shell.replaceState(null, null, '/clientAppointment');
                    }
                    this.isLoading(false);
                })
                .catch((err) => {
                    this.isLoading(false);
                    showError({
                        title: 'Error loading appointments',
                        error: err
                    });
                });
            }
        };

        // Control list movements
        var goToIndex = (i) => {
            var min = 0;
            var max = this.list().length - 1;
            if (this.currentIndex() >= min) {
                var ni = Math.max(min, Math.min(max, i));
                this.currentIndex(ni);
                var b = this.list()[ni];
                this.currentItem.load(b);
                // Update URL
                shell.replaceState(null, null, '/clientAppointment/' + b.bookingID());
            }
        };
        this.goNext = () => {
            goToIndex(this.currentIndex() + 1);
        };
        this.goPrevious = () => {
            goToIndex(this.currentIndex() - 1);
        };
    }

    show(state) {
        super.show(state);

        var params = state.route.segments;
        var id = params[0] |0;
        this.load(id);

        //Get the return nav text
        var returnText = state.route.query.returnText || 'Back';
        this.returnText(decodeURIComponent(returnText));
    }
}

activities.register(ROUTE_NAME, ClientAppointment);
<commit_msg>Fixed rewrite at client-appointment #460<commit_after>/**
 * ClientAppointment
 *
 * @module activities/client-appointment
 *
 * FIXME: Complete jsdocs, description
 * TODO: Quick460 Must complete refactoring
 */

import * as activities from '../index';
import Activity from '../../components/Activity';
import EditClientBookingCardVM from '../../viewmodels/EditClientBookingCardVM';
import UserType from '../../enums/UserType';
import clientAppointments from '../../data/clientAppointments';
import ko from 'knockout';
import shell from '../../app.shell';
import { show as showError } from '../../modals/error';
import style from './style.styl';
import template from './template.html';

const ROUTE_NAME = 'client-appointment';

export default class ClientAppointment extends Activity {

    static get template() { return template; }

    static get style() { return style; }

    constructor($activity, app) {
        super($activity, app);

        this.accessLevel = UserType.loggedUser;
        this.helpLink = '/help/relatedArticles/201983163-making-changes-canceling-appointments';
        this.navBar = Activity.createSubsectionNavBar('', {
            backLink: '/myAppointments',
            helpLink: this.helpLink
        });
        this.title('Your appointments');

        // View properties
        this.list = clientAppointments.list;
        this.currentIndex = ko.observable(-1);
        this.currentItem = new EditClientBookingCardVM(app);
        this.isLoading = ko.observable(false);
        this.returnText = ko.observable('Back');

        this.isEditButtonVisible = ko.pureComputed(function() {
            return (this.canCancel() || this.canEdit()) && !this.isEditMode() && !this.isCancelMode();
        }, this.currentItem);
        this.isCancelEditButtonVisible = ko.pureComputed(function() {
            return (this.canCancel() || this.canEdit()) && (this.isEditMode() || this.isCancelMode());
        }, this.currentItem);
        this.isEmpty = ko.pureComputed(() => this.currentIndex() === -2);

        this.__viewMethods();
    }

    __viewMethods() {
        var updateListIndex = () => {
            if (this.list().length) {
                if (this.currentIndex() === -1) {
                    // Single booking was selected, find in the list
                    var bID = this.currentItem.booking().bookingID();
                    this.list().some((b, i) => {
                        if (b.bookingID() === bID) {
                            this.currentIndex(i);
                            return true;
                        }
                    });
                }
            }
        };

        this.load = (id) => {
            if (id) {
                this.currentIndex(-1);
                this.currentItem.load(id)
                .then(function() {
                    // Load the list in background
                    clientAppointments.sync()
                    .then(updateListIndex);
                })
                .catch(function(err) {
                    showError({
                        title: 'Error loading the appointment',
                        error: err
                    });
                });
            }
            else {
                this.isLoading(true);
                this.currentItem.reset();
                clientAppointments.sync()
                .then(() => {
                    var first = this.list().length ? this.list()[0] : null;
                    if (first) {
                        this.currentIndex(0);
                        this.currentItem.load(first);
                        updateListIndex();
                        // Update URL
                        shell.replaceState(null, null, '/client-appointment/' + first.bookingID());
                    }
                    else {
                        this.currentIndex(-2);
                        // Update URL
                        shell.replaceState(null, null, '/client-appointment');
                    }
                    this.isLoading(false);
                })
                .catch((err) => {
                    this.isLoading(false);
                    showError({
                        title: 'Error loading appointments',
                        error: err
                    });
                });
            }
        };

        // Control list movements
        var goToIndex = (i) => {
            var min = 0;
            var max = this.list().length - 1;
            if (this.currentIndex() >= min) {
                var ni = Math.max(min, Math.min(max, i));
                this.currentIndex(ni);
                var b = this.list()[ni];
                this.currentItem.load(b);
                // Update URL
                shell.replaceState(null, null, '/client-appointment/' + b.bookingID());
            }
        };
        this.goNext = () => {
            goToIndex(this.currentIndex() + 1);
        };
        this.goPrevious = () => {
            goToIndex(this.currentIndex() - 1);
        };
    }

    show(state) {
        super.show(state);

        var params = state.route.segments;
        var id = params[0] |0;
        this.load(id);

        //Get the return nav text
        var returnText = state.route.query.returnText || 'Back';
        this.returnText(decodeURIComponent(returnText));
    }
}

activities.register(ROUTE_NAME, ClientAppointment);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */
define(function (require, exports, module) {
    'use strict';

    require("spec/CodeHint-test");

});<commit_msg>remove unnecessary file<commit_after><|endoftext|>


<commit_before>
/* globals Q */

/***
*
*
*
*/

// current stargateVersion
var stargateVersion = 2;

// logger function
var log = function(msg, obj) {
    if (typeof obj !== 'undefined') {
        console.log("[Stargate] "+msg+" ",obj);
    } else {
        console.log("[Stargate] "+msg);
    }
    return true;
};
var err = function(msg, obj) {
    if (typeof obj !== 'undefined') {
        console.error("[Stargate] "+msg+" ",obj);
    } else {
        console.error("[Stargate] "+msg);
    }
    return false;
};

// device informations   // examples
var runningDevice = {
    available: false,    // true
    cordova: "",         // 4.1.1
    manufacturer: "",    // samsung
    model: "",           // GT-I9505
    platform: "",        // Android
    uuid: "",            // ac7245e38e3dfecb
    version: ""          // 5.0.1
};
var isRunningOnAndroid = function() {
    return runningDevice.platform == "Android";
};
var isRunningOnIos = function() {
    return runningDevice.platform == "iOS";
};
// - not used, enable if needed -
//var isRunningOnCordova = function () {
//    return (typeof window.cordova !== "undefined");
//};
var initDevice = function() {
    if (typeof window.device === 'undefined') {
        return err("Missing cordova device plugin");
    }
    for (var key in runningDevice) {
        if (window.device.hasOwnProperty(key)) {
            runningDevice[key] = window.device[key];
        }
    }
    return true;
};

var getManifest = function() {

    var deferred = Q.defer();

    window.hostedwebapp.getManifest(
        function(manifest){
            deferred.resolve(manifest);
        },
        function(error){
            deferred.reject(new Error(error));
            console.error(error);
        }
    );
    return deferred.promise;
};

var launchUrl = function (url) {
    log("launchUrl: "+url);
    document.location.href = url;
};

var isStargateInitialized = false;
var isStargateOpen = false;
var initializeCallback = null;
var initializeDeferred = null;

var appVersion = '';

/**
 *
 * variables sent by server configuration
 *
 */
var country = '',
    selector = '',
    api_selector = '',
    app_prefix = '',
    hybrid_conf = {};

/**
 *
 * this is got from manifest
 *
 */
var baseUrl;

var updateStatusBar = function() {

    if (typeof window.StatusBar === "undefined") {
        // missing cordova plugin
        return err("[StatusBar] missing cordova plugin");
    }
    if (typeof stargateConf.statusbar === "undefined") {
        return;
    }
    if (typeof stargateConf.statusbar.hideOnUrlPattern !== "undefined" &&
        stargateConf.statusbar.hideOnUrlPattern.constructor === Array) {

        var currentLocation = document.location.href;
        var hide = false;

        for (var i=0; i<stargateConf.statusbar.hideOnUrlPattern.length; i++) {

            var re = new RegExp(stargateConf.statusbar.hideOnUrlPattern[i]);

            if (re.test(currentLocation)) {
                hide = true;
                break;
            }
        }

        if (hide) {
            window.StatusBar.hide();
        }
        else {
            window.StatusBar.show();
        }
    }
};

var onPluginReady = function () {

    // FIXME: this is needed ??
    document.title = stargateConf.title;

    // set back cordova bridge mode to IFRAME_NAV overriding manifold settings
    if (isRunningOnIos() && (typeof window.cordova !== 'undefined') && cordova.require) {
        var exec = cordova.require('cordova/exec');
        exec.setJsToNativeBridgeMode(exec.jsToNativeModes.IFRAME_NAV);
    }

    updateStatusBar();

    if (hasFeature('mfp')) {
        MFP.check();
    }

    if (hasFeature('deltadna')) {
        window.deltadna.startSDK(
            stargateConf.deltadna.environmentKey,
            stargateConf.deltadna.collectApi,
            stargateConf.deltadna.engageApi,

            onDeltaDNAStartedSuccess,
            onDeltaDNAStartedError,

            stargateConf.deltadna.settings
        );
    }

    navigator.splashscreen.hide();
    setBusy(false);

    IAP.initialize();

    document.cookie="hybrid=1; path=/";
    document.cookie="stargateVersion="+stargateVersion+"; path=/";

    if (window.localStorage.getItem('hybrid') !== null) {
        window.localStorage.setItem('hybrid', 1);
    }
    if (window.localStorage.getItem('stargateVersion') !== null) {
        window.localStorage.setItem('stargateVersion', stargateVersion);
    }

    // initialize finished
    isStargateOpen = true;

    //execute callback
    initializeCallback();

    initializeDeferred.resolve("Stargate.initialize() done");
};

var onDeviceReady = function () {
    initDevice();

    Q.all([
        // include here all needed initializazion
        cordova.getAppVersion.getVersionNumber(),
        getManifest()
    ])
    .then(function(results) {

        appVersion = results[0];

		if (typeof results[1] !== 'object') {
			results[1] = JSON.parse(results[1]);
		}

        baseUrl = results[1].start_url;

        stargateConf = results[1].stargateConf;

        onPluginReady();
    })
    .fail(function (error) {
        err("onDeviceReady() error: "+error);
    });
};

var stargateBusy = false;

// - not used, enable if needed -
//var isBusy = function() { return stargateBusy; };

var setBusy = function(value) {
    if (value) {
        stargateBusy = true;
        startLoading();
    }
    else {
        stargateBusy = false;
        stopLoading();
    }
};

var stargateConf = {};

var hasFeature = function(feature) {
    return (typeof stargateConf.features[feature] !== 'undefined' && stargateConf.features[feature]);
};

<commit_msg>call webapp fixes init<commit_after>
/* globals Q */

/***
*
*
*
*/

// current stargateVersion
var stargateVersion = 2;

// logger function
var log = function(msg, obj) {
    if (typeof obj !== 'undefined') {
        console.log("[Stargate] "+msg+" ",obj);
    } else {
        console.log("[Stargate] "+msg);
    }
    return true;
};
var err = function(msg, obj) {
    if (typeof obj !== 'undefined') {
        console.error("[Stargate] "+msg+" ",obj);
    } else {
        console.error("[Stargate] "+msg);
    }
    return false;
};

// device informations   // examples
var runningDevice = {
    available: false,    // true
    cordova: "",         // 4.1.1
    manufacturer: "",    // samsung
    model: "",           // GT-I9505
    platform: "",        // Android
    uuid: "",            // ac7245e38e3dfecb
    version: ""          // 5.0.1
};
var isRunningOnAndroid = function() {
    return runningDevice.platform == "Android";
};
var isRunningOnIos = function() {
    return runningDevice.platform == "iOS";
};
// - not used, enable if needed -
//var isRunningOnCordova = function () {
//    return (typeof window.cordova !== "undefined");
//};
var initDevice = function() {
    if (typeof window.device === 'undefined') {
        return err("Missing cordova device plugin");
    }
    for (var key in runningDevice) {
        if (window.device.hasOwnProperty(key)) {
            runningDevice[key] = window.device[key];
        }
    }
    return true;
};

var getManifest = function() {

    var deferred = Q.defer();

    window.hostedwebapp.getManifest(
        function(manifest){
            deferred.resolve(manifest);
        },
        function(error){
            deferred.reject(new Error(error));
            console.error(error);
        }
    );
    return deferred.promise;
};

var launchUrl = function (url) {
    log("launchUrl: "+url);
    document.location.href = url;
};

var isStargateInitialized = false;
var isStargateOpen = false;
var initializeCallback = null;
var initializeDeferred = null;

var appVersion = '';

/**
 *
 * variables sent by server configuration
 *
 */
var country = '',
    selector = '',
    api_selector = '',
    app_prefix = '',
    hybrid_conf = {};

/**
 *
 * this is got from manifest
 *
 */
var baseUrl;

var updateStatusBar = function() {

    if (typeof window.StatusBar === "undefined") {
        // missing cordova plugin
        return err("[StatusBar] missing cordova plugin");
    }
    if (typeof stargateConf.statusbar === "undefined") {
        return;
    }
    if (typeof stargateConf.statusbar.hideOnUrlPattern !== "undefined" &&
        stargateConf.statusbar.hideOnUrlPattern.constructor === Array) {

        var currentLocation = document.location.href;
        var hide = false;

        for (var i=0; i<stargateConf.statusbar.hideOnUrlPattern.length; i++) {

            var re = new RegExp(stargateConf.statusbar.hideOnUrlPattern[i]);

            if (re.test(currentLocation)) {
                hide = true;
                break;
            }
        }

        if (hide) {
            window.StatusBar.hide();
        }
        else {
            window.StatusBar.show();
        }
    }
};

var onPluginReady = function () {

    // FIXME: this is needed ??
    document.title = stargateConf.title;

    // set back cordova bridge mode to IFRAME_NAV overriding manifold settings
    if (isRunningOnIos() && (typeof window.cordova !== 'undefined') && cordova.require) {
        var exec = cordova.require('cordova/exec');
        exec.setJsToNativeBridgeMode(exec.jsToNativeModes.IFRAME_NAV);
    }

    updateStatusBar();

    if (hasFeature('mfp')) {
        MFP.check();
    }

    if (hasFeature('deltadna')) {
        window.deltadna.startSDK(
            stargateConf.deltadna.environmentKey,
            stargateConf.deltadna.collectApi,
            stargateConf.deltadna.engageApi,

            onDeltaDNAStartedSuccess,
            onDeltaDNAStartedError,

            stargateConf.deltadna.settings
        );
    }

    navigator.splashscreen.hide();
    setBusy(false);

    IAP.initialize();

    document.cookie="hybrid=1; path=/";
    document.cookie="stargateVersion="+stargateVersion+"; path=/";

    if (window.localStorage.getItem('hybrid') !== null) {
        window.localStorage.setItem('hybrid', 1);
    }
    if (window.localStorage.getItem('stargateVersion') !== null) {
        window.localStorage.setItem('stargateVersion', stargateVersion);
    }

    // apply webapp fixes
    webappsFixes.init();

    // initialize finished
    isStargateOpen = true;

    //execute callback
    // FIXME: check callback type is function
    initializeCallback();

    initializeDeferred.resolve("Stargate.initialize() done");
};

var onDeviceReady = function () {
    initDevice();

    Q.all([
        // include here all needed initializazion
        cordova.getAppVersion.getVersionNumber(),
        getManifest()
    ])
    .then(function(results) {

        appVersion = results[0];

		if (typeof results[1] !== 'object') {
			results[1] = JSON.parse(results[1]);
		}

        baseUrl = results[1].start_url;

        stargateConf = results[1].stargateConf;

        onPluginReady();
    })
    .fail(function (error) {
        err("onDeviceReady() error: "+error);
    });
};

var stargateBusy = false;

// - not used, enable if needed -
//var isBusy = function() { return stargateBusy; };

var setBusy = function(value) {
    if (value) {
        stargateBusy = true;
        startLoading();
    }
    else {
        stargateBusy = false;
        stopLoading();
    }
};

var stargateConf = {};

var hasFeature = function(feature) {
    return (typeof stargateConf.features[feature] !== 'undefined' && stargateConf.features[feature]);
};

<|endoftext|>