From 893e24ff9862a563cf0eb8348fc8858eeb38784f Mon Sep 17 00:00:00 2001 From: Dhrubomoy Das Gupta Date: Sun, 28 May 2017 20:21:52 -0400 Subject: [PATCH 01/11] [CLOCK] Issue #1273 : Allow clock to set timezone with autocomplete dropdown option. --- bower.json | 3 +- openmct.js | 1 + .../general/res/sass/controls/_controls.scss | 33 +++++ platform/features/clock/bundle.js | 11 +- .../clock/src/controllers/ClockController.js | 38 ++++-- .../test/controllers/ClockControllerSpec.js | 34 +++-- platform/forms/bundle.js | 15 ++ .../res/templates/controls/autocomplete.html | 43 ++++++ .../src/controllers/AutocompleteController.js | 128 ++++++++++++++++++ .../controllers/AutocompleteControllerSpec.js | 63 +++++++++ test-main.js | 1 + 11 files changed, 340 insertions(+), 30 deletions(-) create mode 100644 platform/forms/res/templates/controls/autocomplete.html create mode 100644 platform/forms/src/controllers/AutocompleteController.js create mode 100644 platform/forms/test/controllers/AutocompleteControllerSpec.js diff --git a/bower.json b/bower.json index ed3dbaf899..369dcdc81b 100644 --- a/bower.json +++ b/bower.json @@ -22,6 +22,7 @@ "eventemitter3": "^1.2.0", "lodash": "3.10.1", "almond": "~0.3.2", - "html2canvas": "^0.4.1" + "html2canvas": "^0.4.1", + "moment-timezone": "^0.5.13" } } diff --git a/openmct.js b/openmct.js index 4c578b9b3f..8f116ea1a6 100644 --- a/openmct.js +++ b/openmct.js @@ -32,6 +32,7 @@ requirejs.config({ "html2canvas": "bower_components/html2canvas/build/html2canvas.min", "moment": "bower_components/moment/moment", "moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format", + "moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data", "saveAs": "bower_components/FileSaver.js/FileSaver.min", "screenfull": "bower_components/screenfull/dist/screenfull.min", "text": "bower_components/text/text", diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss index f286f362e9..a826466ac4 100644 --- a/platform/commonUI/general/res/sass/controls/_controls.scss +++ b/platform/commonUI/general/res/sass/controls/_controls.scss @@ -297,6 +297,39 @@ textarea.lg { position: relative; height: 300px; } } } +/******************************************************** AUTOCOMPLETE */ +.autocomplete { + input { + width: 226px; + padding: 5px 0px 5px 7px; + } + .icon-arrow-down { + position: absolute; + top: 8px; + left: 210px; + font-size: 10px; + } + .autocompleteOptions { + border: 1px solid $colorFormLines; + border-radius: 5px; + width: 224px; + max-height: 170px; + overflow-y: auto; + overflow-x: hidden; + li { + border: 1px solid $colorFormLines; + padding: 8px 0px 8px 5px; + .optionText { + cursor: pointer; + } + } + .optionPreSelected { + background-color: $colorInspectorSectionHeaderBg; + color: $colorInspectorSectionHeaderFg; + } + } +} + /******************************************************** OBJECT-HEADER */ .object-header { font-size: 1em; diff --git a/platform/features/clock/bundle.js b/platform/features/clock/bundle.js index c7b941c1d9..99eddef550 100644 --- a/platform/features/clock/bundle.js +++ b/platform/features/clock/bundle.js @@ -21,6 +21,7 @@ *****************************************************************************/ define([ + "moment-timezone", "./src/indicators/ClockIndicator", "./src/services/TickerService", "./src/controllers/ClockController", @@ -32,6 +33,7 @@ define([ "text!./res/templates/timer.html", 'legacyRegistry' ], function ( + MomentTimezone, ClockIndicator, TickerService, ClockController, @@ -200,13 +202,20 @@ define([ "cssClass": "l-inline" } ] + }, + { + "key": "timezone", + "name": "Timezone", + "control": "autocomplete", + "options": MomentTimezone.tz.names() } ], "model": { "clockFormat": [ "YYYY/MM/DD hh:mm:ss", "clock12" - ] + ], + "timezone": "UTC" } }, { diff --git a/platform/features/clock/src/controllers/ClockController.js b/platform/features/clock/src/controllers/ClockController.js index 9103fd5327..945be23f87 100644 --- a/platform/features/clock/src/controllers/ClockController.js +++ b/platform/features/clock/src/controllers/ClockController.js @@ -20,9 +20,14 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define( - ['moment'], - function (moment) { +define([ + 'moment', + 'moment-timezone' + ], + function ( + moment, + momentTimezone + ) { /** * Controller for views of a Clock domain object. @@ -37,10 +42,13 @@ define( var lastTimestamp, unlisten, timeFormat, + zoneName, self = this; function update() { - var m = moment.utc(lastTimestamp); + var m = zoneName ? + moment.utc(lastTimestamp).tz(zoneName) : moment.utc(lastTimestamp); + self.zoneAbbr = zoneName ? m.zoneAbbr() : "UTC"; self.textValue = timeFormat && m.format(timeFormat); self.ampmValue = m.format("A"); // Just the AM or PM part } @@ -50,21 +58,23 @@ define( update(); } - function updateFormat(clockFormat) { + function updateModel(model) { var baseFormat; + if (model !== undefined) { + baseFormat = model.clockFormat[0]; - if (clockFormat !== undefined) { - baseFormat = clockFormat[0]; - - self.use24 = clockFormat[1] === 'clock24'; + self.use24 = model.clockFormat[1] === 'clock24'; timeFormat = self.use24 ? baseFormat.replace('hh', "HH") : baseFormat; - - update(); + // If wrong timezone is provided, the UTC will be used + zoneName = momentTimezone.tz.names().includes(model.timezone) ? + model.timezone : "UTC"; } + update(); } - // Pull in the clock format from the domain object model - $scope.$watch('model.clockFormat', updateFormat); + + // Pull in the model (clockFormat and timezone) from the domain object model + $scope.$watch('model', updateModel); // Listen for clock ticks ... and stop listening on destroy unlisten = tickerService.listen(tick); @@ -76,7 +86,7 @@ define( * @returns {string} */ ClockController.prototype.zone = function () { - return "UTC"; + return this.zoneAbbr; }; /** diff --git a/platform/features/clock/test/controllers/ClockControllerSpec.js b/platform/features/clock/test/controllers/ClockControllerSpec.js index e48cc5c3ae..8774333d14 100644 --- a/platform/features/clock/test/controllers/ClockControllerSpec.js +++ b/platform/features/clock/test/controllers/ClockControllerSpec.js @@ -1,5 +1,5 @@ /***************************************************************************** - * Open MCT, Copyright (c) 2009-2016, United States Government + * Open MCT, Copyright (c) 2009-2017, United States Government * as represented by the Administrator of the National Aeronautics and Space * Administration. All rights reserved. * @@ -43,9 +43,9 @@ define( controller = new ClockController(mockScope, mockTicker); }); - it("watches for clock format from the domain object model", function () { + it("watches for model (clockFormat and timezone) from the domain object model", function () { expect(mockScope.$watch).toHaveBeenCalledWith( - "model.clockFormat", + "model", jasmine.any(Function) ); }); @@ -67,29 +67,35 @@ define( it("formats using the format string from the model", function () { mockTicker.listen.mostRecentCall.args[0](TEST_TIMESTAMP); - mockScope.$watch.mostRecentCall.args[1]([ - "YYYY-DDD hh:mm:ss", - "clock24" - ]); + mockScope.$watch.mostRecentCall.args[1]({ + "clockFormat": [ + "YYYY-DDD hh:mm:ss", + "clock24" + ], + "timezone": "Canada/Eastern" + }); - expect(controller.zone()).toEqual("UTC"); - expect(controller.text()).toEqual("2015-154 17:56:14"); + expect(controller.zone()).toEqual("EDT"); + expect(controller.text()).toEqual("2015-154 13:56:14"); expect(controller.ampm()).toEqual(""); }); it("formats 12-hour time", function () { mockTicker.listen.mostRecentCall.args[0](TEST_TIMESTAMP); - mockScope.$watch.mostRecentCall.args[1]([ - "YYYY-DDD hh:mm:ss", - "clock12" - ]); + mockScope.$watch.mostRecentCall.args[1]({ + "clockFormat": [ + "YYYY-DDD hh:mm:ss", + "clock12" + ], + "timezone": "" + }); expect(controller.zone()).toEqual("UTC"); expect(controller.text()).toEqual("2015-154 05:56:14"); expect(controller.ampm()).toEqual("PM"); }); - it("does not throw exceptions when clockFormat is undefined", function () { + it("does not throw exceptions when model is undefined", function () { mockTicker.listen.mostRecentCall.args[0](TEST_TIMESTAMP); expect(function () { mockScope.$watch.mostRecentCall.args[1](undefined); diff --git a/platform/forms/bundle.js b/platform/forms/bundle.js index 8bcc1ea7a1..c723962571 100644 --- a/platform/forms/bundle.js +++ b/platform/forms/bundle.js @@ -24,10 +24,12 @@ define([ "./src/MCTForm", "./src/MCTToolbar", "./src/MCTControl", + "./src/controllers/AutocompleteController", "./src/controllers/DateTimeController", "./src/controllers/CompositeController", "./src/controllers/ColorController", "./src/controllers/DialogButtonController", + "text!./res/templates/controls/autocomplete.html", "text!./res/templates/controls/checkbox.html", "text!./res/templates/controls/datetime.html", "text!./res/templates/controls/select.html", @@ -44,10 +46,12 @@ define([ MCTForm, MCTToolbar, MCTControl, + AutocompleteController, DateTimeController, CompositeController, ColorController, DialogButtonController, + autocompleteTemplate, checkboxTemplate, datetimeTemplate, selectTemplate, @@ -85,6 +89,10 @@ define([ } ], "controls": [ + { + "key": "autocomplete", + "template": autocompleteTemplate + }, { "key": "checkbox", "template": checkboxTemplate @@ -131,6 +139,13 @@ define([ } ], "controllers": [ + { + "key": "AutocompleteController", + "implementation": AutocompleteController, + "depends": [ + "$scope" + ] + }, { "key": "DateTimeController", "implementation": DateTimeController, diff --git a/platform/forms/res/templates/controls/autocomplete.html b/platform/forms/res/templates/controls/autocomplete.html new file mode 100644 index 0000000000..14022916a8 --- /dev/null +++ b/platform/forms/res/templates/controls/autocomplete.html @@ -0,0 +1,43 @@ + + +
+ + +
+
    +
  • + {{opt.name}} +
  • +
+
+
\ No newline at end of file diff --git a/platform/forms/src/controllers/AutocompleteController.js b/platform/forms/src/controllers/AutocompleteController.js new file mode 100644 index 0000000000..d7fd7baa8d --- /dev/null +++ b/platform/forms/src/controllers/AutocompleteController.js @@ -0,0 +1,128 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2017, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is 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. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +define( + [], + function () { + + /** + * Controller for the `autocomplete` form control. + * + * @memberof platform/forms + * @constructor + */ + function AutocompleteController($scope) { + + var key = { + down: 40, + up: 38, + enter: 13 + } + + if($scope.options[0].name) { + // If "options" include name, value pair + $scope.optionNames = $scope.options.map(function(opt) { + return opt.name; + }) + } else { + // If options is only an array of string. + $scope.optionNames = $scope.options; + } + + function decrementOptionIndex() { + if($scope.optionIndex === 0) { + $scope.optionIndex = $scope.filteredOptions.length; + } + $scope.optionIndex--; + } + + function incrementOptionIndex() { + if($scope.optionIndex === $scope.filteredOptions.length-1) { + $scope.optionIndex = -1; + } + $scope.optionIndex++; + } + + function fillInputWithString(string) { + $scope.hideOptions = true; + // Hard coded!! + $scope.ngModel[4] = string; + } + + function fillInputWithIndexedOption() { + $scope.ngModel[4] = $scope.filteredOptions[$scope.optionIndex].name; + } + + $scope.keyDown = function($event) { + if($scope.filteredOptions) { + var keyCode = $event.keyCode; + switch(keyCode) { + case key.down: + incrementOptionIndex(); + fillInputWithIndexedOption(); + break; + case key.up: + $event.preventDefault(); // Prevents cursor jumping back and forth + decrementOptionIndex(); + fillInputWithIndexedOption(); + break; + case key.enter: + if($scope.filteredOptions[$scope.optionIndex]) { + fillInputWithString($scope.filteredOptions[$scope.optionIndex].name); + } + } + } + } + + $scope.filterOptions = function(string) { + $scope.hideOptions = false; + $scope.filteredOptions = $scope.optionNames.filter(function(option) { + return option.toLowerCase().indexOf(string.toLowerCase()) >= 0; + }).map(function(option, index) { + return { + optionId: index, + name: option + } + }); + } + + $scope.inputClicked = function($event) { + var target = $event.target; + target.select(); + $scope.hideOptions = false; + $scope.filterOptions(target.value); + $scope.optionIndex = 0; + } + + $scope.fillInput = function(string) { + fillInputWithString(string); + } + + $scope.optionMouseover = function(optionId) { + $scope.optionIndex = optionId; + } + } + + return AutocompleteController; + + } +); \ No newline at end of file diff --git a/platform/forms/test/controllers/AutocompleteControllerSpec.js b/platform/forms/test/controllers/AutocompleteControllerSpec.js new file mode 100644 index 0000000000..7de87a2b6d --- /dev/null +++ b/platform/forms/test/controllers/AutocompleteControllerSpec.js @@ -0,0 +1,63 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2017, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT is 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. + * + * Open MCT includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ + +define( + ["../../src/controllers/AutocompleteController"], + function (AutocompleteController) { + + describe("The autocomplete controller", function () { + var mockScope, + controller; + + beforeEach(function () { + mockScope = jasmine.createSpyObj("$scope", ["$watch"]); + mockScope.options = ['Asia/Dhaka', 'UTC', 'Toronto', 'Asia/Shanghai', 'Hotel California']; + mockScope.ngModel = [null, null, null, null, null]; + controller = new AutocompleteController(mockScope); + }); + + it("makes optionNames array equal to options if options is an array of string", function () { + expect(mockScope.optionNames).toEqual(mockScope.options); + }); + + it("filters options by returning array containing optionId and name", function () { + mockScope.filterOptions('Asia'); + var filteredOptions = [ { optionId : 0, name : 'Asia/Dhaka' }, + { optionId : 1, name : 'Asia/Shanghai' } ]; + expect(mockScope.filteredOptions).toEqual(filteredOptions); + }); + + it("fills input with given string", function () { + var str = "UTC"; + mockScope.fillInput(str); + expect(mockScope.hideOptions).toEqual(true); + expect(mockScope.ngModel[4]).toEqual(str); + }); + + it("sets a new optionIndex on mouse hover", function () { + mockScope.optionMouseover(1); + expect(mockScope.optionIndex).toEqual(1); + }); + + }); + } +); diff --git a/test-main.js b/test-main.js index 901022ca66..d108b82631 100644 --- a/test-main.js +++ b/test-main.js @@ -58,6 +58,7 @@ requirejs.config({ "html2canvas": "bower_components/html2canvas/build/html2canvas.min", "moment": "bower_components/moment/moment", "moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format", + "moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data", "saveAs": "bower_components/FileSaver.js/FileSaver.min", "screenfull": "bower_components/screenfull/dist/screenfull.min", "text": "bower_components/text/text", From 7f1439726222e62f9b2003b23d4328e5004d0ae6 Mon Sep 17 00:00:00 2001 From: dhrubomoy Date: Tue, 30 May 2017 16:20:12 -0400 Subject: [PATCH 02/11] [CLOCK] Issue #1273 : Use '$ scope.field' to avoid hard-coding. --- platform/forms/src/controllers/AutocompleteController.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/platform/forms/src/controllers/AutocompleteController.js b/platform/forms/src/controllers/AutocompleteController.js index d7fd7baa8d..85fb23ded1 100644 --- a/platform/forms/src/controllers/AutocompleteController.js +++ b/platform/forms/src/controllers/AutocompleteController.js @@ -64,12 +64,11 @@ define( function fillInputWithString(string) { $scope.hideOptions = true; - // Hard coded!! - $scope.ngModel[4] = string; + $scope.ngModel[$scope.field] = string; } function fillInputWithIndexedOption() { - $scope.ngModel[4] = $scope.filteredOptions[$scope.optionIndex].name; + $scope.ngModel[$scope.field] = $scope.filteredOptions[$scope.optionIndex].name; } $scope.keyDown = function($event) { From 7cdb8db775a7dcd100a8c5417cf248c03afae669 Mon Sep 17 00:00:00 2001 From: Dhrubomoy Das Gupta Date: Tue, 30 May 2017 23:14:05 -0400 Subject: [PATCH 03/11] [Autocomplete] Hide options if clicked elsewhere. --- platform/forms/bundle.js | 16 ++++++++++++++++ .../res/templates/controls/autocomplete.html | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/platform/forms/bundle.js b/platform/forms/bundle.js index c723962571..7eafc46441 100644 --- a/platform/forms/bundle.js +++ b/platform/forms/bundle.js @@ -24,6 +24,7 @@ define([ "./src/MCTForm", "./src/MCTToolbar", "./src/MCTControl", + "../commonUI/general/src/directives/MCTClickElsewhere", "./src/controllers/AutocompleteController", "./src/controllers/DateTimeController", "./src/controllers/CompositeController", @@ -46,6 +47,7 @@ define([ MCTForm, MCTToolbar, MCTControl, + MCTClickElsewhere, AutocompleteController, DateTimeController, CompositeController, @@ -86,6 +88,13 @@ define([ "templateLinker", "controls[]" ] + }, + { + "key": "mctClickElsewhere", + "implementation": MCTClickElsewhere, + "depends": [ + "$document" + ] } ], "controls": [ @@ -168,6 +177,13 @@ define([ "$scope", "dialogService" ] + }, + { + "key": "mctClickElsewhere", + "implementation": MCTClickElsewhere, + "depends": [ + "$document" + ] } ] } diff --git a/platform/forms/res/templates/controls/autocomplete.html b/platform/forms/res/templates/controls/autocomplete.html index 14022916a8..b0e4c5db3b 100644 --- a/platform/forms/res/templates/controls/autocomplete.html +++ b/platform/forms/res/templates/controls/autocomplete.html @@ -30,7 +30,8 @@
+ ng-hide="hideOptions" + mct-click-elsewhere="hideOptions = true">
  • Date: Sun, 4 Jun 2017 17:49:31 -0400 Subject: [PATCH 04/11] [Autocomplete] Check if indexed filteredOptions is defined --- platform/forms/src/controllers/AutocompleteController.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/forms/src/controllers/AutocompleteController.js b/platform/forms/src/controllers/AutocompleteController.js index 85fb23ded1..48423db899 100644 --- a/platform/forms/src/controllers/AutocompleteController.js +++ b/platform/forms/src/controllers/AutocompleteController.js @@ -68,7 +68,9 @@ define( } function fillInputWithIndexedOption() { - $scope.ngModel[$scope.field] = $scope.filteredOptions[$scope.optionIndex].name; + if($scope.filteredOptions[$scope.optionIndex]) { + $scope.ngModel[$scope.field] = $scope.filteredOptions[$scope.optionIndex].name; + } } $scope.keyDown = function($event) { From 3870266131ad792ec292038b72d5cac03d3f8a1d Mon Sep 17 00:00:00 2001 From: Dhrubomoy Das Gupta Date: Tue, 6 Jun 2017 18:55:16 -0400 Subject: [PATCH 05/11] [Autocomplete] Clicking the arrow will display the entire list of timezones This change is based on the following code review: - Display the entire list of timezones, regardless of what's currently entered. - As soon as the user typed (or deleted chars in the input) then that filtration would take over the list display --- .../general/res/sass/controls/_controls.scss | 1 + platform/forms/bundle.js | 3 ++- .../res/templates/controls/autocomplete.html | 8 +++--- .../src/controllers/AutocompleteController.js | 26 +++++++++++++------ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss index a826466ac4..b54201e16c 100644 --- a/platform/commonUI/general/res/sass/controls/_controls.scss +++ b/platform/commonUI/general/res/sass/controls/_controls.scss @@ -308,6 +308,7 @@ textarea.lg { position: relative; height: 300px; } top: 8px; left: 210px; font-size: 10px; + cursor: pointer; } .autocompleteOptions { border: 1px solid $colorFormLines; diff --git a/platform/forms/bundle.js b/platform/forms/bundle.js index 7eafc46441..fcd52c0b3c 100644 --- a/platform/forms/bundle.js +++ b/platform/forms/bundle.js @@ -152,7 +152,8 @@ define([ "key": "AutocompleteController", "implementation": AutocompleteController, "depends": [ - "$scope" + "$scope", + "$element" ] }, { diff --git a/platform/forms/res/templates/controls/autocomplete.html b/platform/forms/res/templates/controls/autocomplete.html index b0e4c5db3b..e64085fa4a 100644 --- a/platform/forms/res/templates/controls/autocomplete.html +++ b/platform/forms/res/templates/controls/autocomplete.html @@ -21,13 +21,15 @@ -->
    - + - +
    Date: Wed, 7 Jun 2017 23:01:14 -0400 Subject: [PATCH 06/11] [Autocomplete] Minor refactoring --- .../clock/src/controllers/ClockController.js | 4 ++-- .../res/templates/controls/autocomplete.html | 4 ++-- .../src/controllers/AutocompleteController.js | 21 +++++++++---------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/platform/features/clock/src/controllers/ClockController.js b/platform/features/clock/src/controllers/ClockController.js index 945be23f87..02b32cfc4f 100644 --- a/platform/features/clock/src/controllers/ClockController.js +++ b/platform/features/clock/src/controllers/ClockController.js @@ -48,7 +48,7 @@ define([ function update() { var m = zoneName ? moment.utc(lastTimestamp).tz(zoneName) : moment.utc(lastTimestamp); - self.zoneAbbr = zoneName ? m.zoneAbbr() : "UTC"; + self.zoneAbbr = m.zoneAbbr(); self.textValue = timeFormat && m.format(timeFormat); self.ampmValue = m.format("A"); // Just the AM or PM part } @@ -69,8 +69,8 @@ define([ // If wrong timezone is provided, the UTC will be used zoneName = momentTimezone.tz.names().includes(model.timezone) ? model.timezone : "UTC"; + update(); } - update(); } // Pull in the model (clockFormat and timezone) from the domain object model diff --git a/platform/forms/res/templates/controls/autocomplete.html b/platform/forms/res/templates/controls/autocomplete.html index e64085fa4a..9649088848 100644 --- a/platform/forms/res/templates/controls/autocomplete.html +++ b/platform/forms/res/templates/controls/autocomplete.html @@ -26,12 +26,12 @@ type="text" ng-model="ngModel[field]" ng-change="filterOptions(ngModel[field])" - ng-click="inputClicked($event)" + ng-click="inputClicked()" ng-keydown="keyDown($event)"/>
      diff --git a/platform/forms/src/controllers/AutocompleteController.js b/platform/forms/src/controllers/AutocompleteController.js index fa26788e9d..efba33b742 100644 --- a/platform/forms/src/controllers/AutocompleteController.js +++ b/platform/forms/src/controllers/AutocompleteController.js @@ -33,10 +33,11 @@ define( function AutocompleteController($scope, $element) { var key = { - down: 40, - up: 38, - enter: 13 - } + down: 40, + up: 38, + enter: 13 + }, + autocompleteInputElement = $element[0].getElementsByClassName('autocompleteInput')[0]; if($scope.options[0].name) { // If "options" include name, value pair @@ -53,6 +54,7 @@ define( $scope.optionIndex = $scope.filteredOptions.length; } $scope.optionIndex--; + fillInputWithIndexedOption(); } function incrementOptionIndex() { @@ -60,6 +62,7 @@ define( $scope.optionIndex = -1; } $scope.optionIndex++; + fillInputWithIndexedOption(); } function fillInputWithString(string) { @@ -85,12 +88,10 @@ define( switch(keyCode) { case key.down: incrementOptionIndex(); - fillInputWithIndexedOption(); break; case key.up: $event.preventDefault(); // Prevents cursor jumping back and forth decrementOptionIndex(); - fillInputWithIndexedOption(); break; case key.enter: if($scope.filteredOptions[$scope.optionIndex]) { @@ -112,14 +113,12 @@ define( }); } - $scope.inputClicked = function($event) { - var target = $event.target; - target.select(); - showOptions(target.value); + $scope.inputClicked = function() { + autocompleteInputElement.select(); + showOptions(autocompleteInputElement.value); } $scope.arrowClicked = function() { - var autocompleteInputElement = $element[0].getElementsByClassName('autocompleteInput')[0]; autocompleteInputElement.select(); showOptions(''); } From 17a067752f64b4c50085d4e86b171b3e4e9654df Mon Sep 17 00:00:00 2001 From: dhrubomoy Date: Wed, 21 Jun 2017 14:31:55 -0400 Subject: [PATCH 07/11] [Forms] Remove redundant mctClickElsewhere --- platform/forms/bundle.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/platform/forms/bundle.js b/platform/forms/bundle.js index fcd52c0b3c..7e654d7077 100644 --- a/platform/forms/bundle.js +++ b/platform/forms/bundle.js @@ -24,7 +24,6 @@ define([ "./src/MCTForm", "./src/MCTToolbar", "./src/MCTControl", - "../commonUI/general/src/directives/MCTClickElsewhere", "./src/controllers/AutocompleteController", "./src/controllers/DateTimeController", "./src/controllers/CompositeController", @@ -47,7 +46,6 @@ define([ MCTForm, MCTToolbar, MCTControl, - MCTClickElsewhere, AutocompleteController, DateTimeController, CompositeController, @@ -88,13 +86,6 @@ define([ "templateLinker", "controls[]" ] - }, - { - "key": "mctClickElsewhere", - "implementation": MCTClickElsewhere, - "depends": [ - "$document" - ] } ], "controls": [ @@ -178,13 +169,6 @@ define([ "$scope", "dialogService" ] - }, - { - "key": "mctClickElsewhere", - "implementation": MCTClickElsewhere, - "depends": [ - "$document" - ] } ] } From f20c8b7d9933b43a89b1e181eacbe2f14e9440f6 Mon Sep 17 00:00:00 2001 From: dhrubomoy Date: Wed, 21 Jun 2017 15:29:28 -0400 Subject: [PATCH 08/11] Fix code style and add missing semicolons --- .../clock/src/controllers/ClockController.js | 2 +- .../src/controllers/AutocompleteController.js | 64 +++++++++---------- .../controllers/AutocompleteControllerSpec.js | 8 +-- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/platform/features/clock/src/controllers/ClockController.js b/platform/features/clock/src/controllers/ClockController.js index 02b32cfc4f..3c7dc7b216 100644 --- a/platform/features/clock/src/controllers/ClockController.js +++ b/platform/features/clock/src/controllers/ClockController.js @@ -67,7 +67,7 @@ define([ timeFormat = self.use24 ? baseFormat.replace('hh', "HH") : baseFormat; // If wrong timezone is provided, the UTC will be used - zoneName = momentTimezone.tz.names().includes(model.timezone) ? + zoneName = momentTimezone.tz.names().includes(model.timezone) ? model.timezone : "UTC"; update(); } diff --git a/platform/forms/src/controllers/AutocompleteController.js b/platform/forms/src/controllers/AutocompleteController.js index efba33b742..46423ba728 100644 --- a/platform/forms/src/controllers/AutocompleteController.js +++ b/platform/forms/src/controllers/AutocompleteController.js @@ -39,18 +39,24 @@ define( }, autocompleteInputElement = $element[0].getElementsByClassName('autocompleteInput')[0]; - if($scope.options[0].name) { + if ($scope.options[0].name) { // If "options" include name, value pair - $scope.optionNames = $scope.options.map(function(opt) { + $scope.optionNames = $scope.options.map(function (opt) { return opt.name; - }) + }); } else { // If options is only an array of string. $scope.optionNames = $scope.options; } + function fillInputWithIndexedOption() { + if ($scope.filteredOptions[$scope.optionIndex]) { + $scope.ngModel[$scope.field] = $scope.filteredOptions[$scope.optionIndex].name; + } + } + function decrementOptionIndex() { - if($scope.optionIndex === 0) { + if ($scope.optionIndex === 0) { $scope.optionIndex = $scope.filteredOptions.length; } $scope.optionIndex--; @@ -58,7 +64,7 @@ define( } function incrementOptionIndex() { - if($scope.optionIndex === $scope.filteredOptions.length-1) { + if ($scope.optionIndex === $scope.filteredOptions.length - 1) { $scope.optionIndex = -1; } $scope.optionIndex++; @@ -70,22 +76,16 @@ define( $scope.ngModel[$scope.field] = string; } - function fillInputWithIndexedOption() { - if($scope.filteredOptions[$scope.optionIndex]) { - $scope.ngModel[$scope.field] = $scope.filteredOptions[$scope.optionIndex].name; - } - } - function showOptions(string) { $scope.hideOptions = false; $scope.filterOptions(string); $scope.optionIndex = 0; } - $scope.keyDown = function($event) { - if($scope.filteredOptions) { + $scope.keyDown = function ($event) { + if ($scope.filteredOptions) { var keyCode = $event.keyCode; - switch(keyCode) { + switch (keyCode) { case key.down: incrementOptionIndex(); break; @@ -94,45 +94,45 @@ define( decrementOptionIndex(); break; case key.enter: - if($scope.filteredOptions[$scope.optionIndex]) { + if ($scope.filteredOptions[$scope.optionIndex]) { fillInputWithString($scope.filteredOptions[$scope.optionIndex].name); } } } - } + }; - $scope.filterOptions = function(string) { + $scope.filterOptions = function (string) { $scope.hideOptions = false; - $scope.filteredOptions = $scope.optionNames.filter(function(option) { + $scope.filteredOptions = $scope.optionNames.filter(function (option) { return option.toLowerCase().indexOf(string.toLowerCase()) >= 0; - }).map(function(option, index) { + }).map(function (option, index) { return { optionId: index, name: option - } + }; }); - } + }; - $scope.inputClicked = function() { + $scope.inputClicked = function () { autocompleteInputElement.select(); showOptions(autocompleteInputElement.value); - } + }; - $scope.arrowClicked = function() { + $scope.arrowClicked = function () { autocompleteInputElement.select(); showOptions(''); - } - - $scope.fillInput = function(string) { - fillInputWithString(string); - } + }; - $scope.optionMouseover = function(optionId) { + $scope.fillInput = function (string) { + fillInputWithString(string); + }; + + $scope.optionMouseover = function (optionId) { $scope.optionIndex = optionId; - } + }; } return AutocompleteController; } -); \ No newline at end of file +); diff --git a/platform/forms/test/controllers/AutocompleteControllerSpec.js b/platform/forms/test/controllers/AutocompleteControllerSpec.js index 7de87a2b6d..4c884fe116 100644 --- a/platform/forms/test/controllers/AutocompleteControllerSpec.js +++ b/platform/forms/test/controllers/AutocompleteControllerSpec.js @@ -41,18 +41,18 @@ define( it("filters options by returning array containing optionId and name", function () { mockScope.filterOptions('Asia'); - var filteredOptions = [ { optionId : 0, name : 'Asia/Dhaka' }, - { optionId : 1, name : 'Asia/Shanghai' } ]; + var filteredOptions = [{ optionId : 0, name : 'Asia/Dhaka' }, + { optionId : 1, name : 'Asia/Shanghai' }]; expect(mockScope.filteredOptions).toEqual(filteredOptions); }); - + it("fills input with given string", function () { var str = "UTC"; mockScope.fillInput(str); expect(mockScope.hideOptions).toEqual(true); expect(mockScope.ngModel[4]).toEqual(str); }); - + it("sets a new optionIndex on mouse hover", function () { mockScope.optionMouseover(1); expect(mockScope.optionIndex).toEqual(1); From 504b2e1ecf6e55426744e820f7cd2b456e002cbb Mon Sep 17 00:00:00 2001 From: Dhrubomoy Das Gupta Date: Wed, 21 Jun 2017 18:14:14 -0400 Subject: [PATCH 09/11] [Autocomplete] Update test --- .../controllers/AutocompleteControllerSpec.js | 82 ++++++++++--------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/platform/forms/test/controllers/AutocompleteControllerSpec.js b/platform/forms/test/controllers/AutocompleteControllerSpec.js index 4c884fe116..4edff63083 100644 --- a/platform/forms/test/controllers/AutocompleteControllerSpec.js +++ b/platform/forms/test/controllers/AutocompleteControllerSpec.js @@ -20,44 +20,50 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -define( - ["../../src/controllers/AutocompleteController"], - function (AutocompleteController) { +define([ + "../../src/controllers/AutocompleteController", + "angular" +], function ( + AutocompleteController, + angular +) { - describe("The autocomplete controller", function () { - var mockScope, - controller; - - beforeEach(function () { - mockScope = jasmine.createSpyObj("$scope", ["$watch"]); - mockScope.options = ['Asia/Dhaka', 'UTC', 'Toronto', 'Asia/Shanghai', 'Hotel California']; - mockScope.ngModel = [null, null, null, null, null]; - controller = new AutocompleteController(mockScope); - }); - - it("makes optionNames array equal to options if options is an array of string", function () { - expect(mockScope.optionNames).toEqual(mockScope.options); - }); - - it("filters options by returning array containing optionId and name", function () { - mockScope.filterOptions('Asia'); - var filteredOptions = [{ optionId : 0, name : 'Asia/Dhaka' }, - { optionId : 1, name : 'Asia/Shanghai' }]; - expect(mockScope.filteredOptions).toEqual(filteredOptions); - }); - - it("fills input with given string", function () { - var str = "UTC"; - mockScope.fillInput(str); - expect(mockScope.hideOptions).toEqual(true); - expect(mockScope.ngModel[4]).toEqual(str); - }); - - it("sets a new optionIndex on mouse hover", function () { - mockScope.optionMouseover(1); - expect(mockScope.optionIndex).toEqual(1); - }); + describe("The autocomplete controller", function () { + var mockScope, + mockElement, + controller; + beforeEach(function () { + mockScope = jasmine.createSpyObj("$scope", ["$watch"]); + mockScope.options = ['Asia/Dhaka', 'UTC', 'Toronto', 'Asia/Shanghai', 'Hotel California']; + mockScope.ngModel = [null, null, null, null, null]; + mockScope.field = 4; + mockElement = angular.element("
      "); + controller = new AutocompleteController(mockScope, mockElement); }); - } -); + + it("makes optionNames array equal to options if options is an array of string", function () { + expect(mockScope.optionNames).toEqual(mockScope.options); + }); + + it("filters options by returning array containing optionId and name", function () { + mockScope.filterOptions('Asia'); + var filteredOptions = [{ optionId : 0, name : 'Asia/Dhaka' }, + { optionId : 1, name : 'Asia/Shanghai' }]; + expect(mockScope.filteredOptions).toEqual(filteredOptions); + }); + + it("fills input with given string", function () { + var str = "UTC"; + mockScope.fillInput(str); + expect(mockScope.hideOptions).toEqual(true); + expect(mockScope.ngModel[mockScope.field]).toEqual(str); + }); + + it("sets a new optionIndex on mouse hover", function () { + mockScope.optionMouseover(1); + expect(mockScope.optionIndex).toEqual(1); + }); + + }); +}); From 307320b3ffd47c2bf5f5741f61cd56c5f9204d13 Mon Sep 17 00:00:00 2001 From: Dhrubomoy Das Gupta Date: Wed, 21 Jun 2017 22:35:32 -0400 Subject: [PATCH 10/11] [Autocomplete] Show warning icon if invalid option was typed --- platform/commonUI/general/res/sass/controls/_controls.scss | 7 +++++++ platform/forms/res/templates/controls/autocomplete.html | 4 ++++ platform/forms/src/controllers/AutocompleteController.js | 1 + .../forms/test/controllers/AutocompleteControllerSpec.js | 5 +++++ 4 files changed, 17 insertions(+) diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss index b54201e16c..dd2cca893d 100644 --- a/platform/commonUI/general/res/sass/controls/_controls.scss +++ b/platform/commonUI/general/res/sass/controls/_controls.scss @@ -329,6 +329,13 @@ textarea.lg { position: relative; height: 300px; } color: $colorInspectorSectionHeaderFg; } } + .autocompleteWarning { + color: $colorFormInvalid; + position: absolute; + font-size: 11px; + left: 235px; + bottom: 7px; + } } /******************************************************** OBJECT-HEADER */ diff --git a/platform/forms/res/templates/controls/autocomplete.html b/platform/forms/res/templates/controls/autocomplete.html index 9649088848..3f022af148 100644 --- a/platform/forms/res/templates/controls/autocomplete.html +++ b/platform/forms/res/templates/controls/autocomplete.html @@ -26,6 +26,7 @@ type="text" ng-model="ngModel[field]" ng-change="filterOptions(ngModel[field])" + ng-init="filterOptions(ngModel[field])" ng-click="inputClicked()" ng-keydown="keyDown($event)"/>
    + +
    \ No newline at end of file diff --git a/platform/forms/src/controllers/AutocompleteController.js b/platform/forms/src/controllers/AutocompleteController.js index 46423ba728..1d13ca1337 100644 --- a/platform/forms/src/controllers/AutocompleteController.js +++ b/platform/forms/src/controllers/AutocompleteController.js @@ -111,6 +111,7 @@ define( name: option }; }); + $scope.invalidOption = $scope.filteredOptions.length === 0; }; $scope.inputClicked = function () { diff --git a/platform/forms/test/controllers/AutocompleteControllerSpec.js b/platform/forms/test/controllers/AutocompleteControllerSpec.js index 4edff63083..f284157d70 100644 --- a/platform/forms/test/controllers/AutocompleteControllerSpec.js +++ b/platform/forms/test/controllers/AutocompleteControllerSpec.js @@ -53,6 +53,11 @@ define([ expect(mockScope.filteredOptions).toEqual(filteredOptions); }); + it("checks if invalid option was typed", function () { + mockScope.filterOptions('openmct'); + expect(mockScope.invalidOption).toEqual(true); + }); + it("fills input with given string", function () { var str = "UTC"; mockScope.fillInput(str); From 7b690d0785d6524ba2eef2dc3fee0888c2ab6149 Mon Sep 17 00:00:00 2001 From: Dhrubomoy Das Gupta Date: Sun, 25 Jun 2017 14:58:28 -0400 Subject: [PATCH 11/11] Revert "[Autocomplete] Show warning icon if invalid option was typed" This reverts commit 307320b3ffd47c2bf5f5741f61cd56c5f9204d13. --- platform/commonUI/general/res/sass/controls/_controls.scss | 7 ------- platform/forms/res/templates/controls/autocomplete.html | 4 ---- platform/forms/src/controllers/AutocompleteController.js | 1 - .../forms/test/controllers/AutocompleteControllerSpec.js | 5 ----- 4 files changed, 17 deletions(-) diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss index dd2cca893d..b54201e16c 100644 --- a/platform/commonUI/general/res/sass/controls/_controls.scss +++ b/platform/commonUI/general/res/sass/controls/_controls.scss @@ -329,13 +329,6 @@ textarea.lg { position: relative; height: 300px; } color: $colorInspectorSectionHeaderFg; } } - .autocompleteWarning { - color: $colorFormInvalid; - position: absolute; - font-size: 11px; - left: 235px; - bottom: 7px; - } } /******************************************************** OBJECT-HEADER */ diff --git a/platform/forms/res/templates/controls/autocomplete.html b/platform/forms/res/templates/controls/autocomplete.html index 3f022af148..9649088848 100644 --- a/platform/forms/res/templates/controls/autocomplete.html +++ b/platform/forms/res/templates/controls/autocomplete.html @@ -26,7 +26,6 @@ type="text" ng-model="ngModel[field]" ng-change="filterOptions(ngModel[field])" - ng-init="filterOptions(ngModel[field])" ng-click="inputClicked()" ng-keydown="keyDown($event)"/>
- - \ No newline at end of file diff --git a/platform/forms/src/controllers/AutocompleteController.js b/platform/forms/src/controllers/AutocompleteController.js index 1d13ca1337..46423ba728 100644 --- a/platform/forms/src/controllers/AutocompleteController.js +++ b/platform/forms/src/controllers/AutocompleteController.js @@ -111,7 +111,6 @@ define( name: option }; }); - $scope.invalidOption = $scope.filteredOptions.length === 0; }; $scope.inputClicked = function () { diff --git a/platform/forms/test/controllers/AutocompleteControllerSpec.js b/platform/forms/test/controllers/AutocompleteControllerSpec.js index f284157d70..4edff63083 100644 --- a/platform/forms/test/controllers/AutocompleteControllerSpec.js +++ b/platform/forms/test/controllers/AutocompleteControllerSpec.js @@ -53,11 +53,6 @@ define([ expect(mockScope.filteredOptions).toEqual(filteredOptions); }); - it("checks if invalid option was typed", function () { - mockScope.filterOptions('openmct'); - expect(mockScope.invalidOption).toEqual(true); - }); - it("fills input with given string", function () { var str = "UTC"; mockScope.fillInput(str);