From c932e953bc1a580b672a0596d8b437e995db3a48 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 14 Sep 2015 16:45:38 -0700 Subject: [PATCH 001/488] [Plugins] Bring over timeline, clock plugins WTD-1239 --- bundles.json | 2 + platform/features/clock/bundle.json | 173 +++++++ .../clock/lib/moment-duration-format.js | 482 ++++++++++++++++++ .../features/clock/res/templates/clock.html | 13 + .../features/clock/res/templates/timer.html | 21 + .../src/actions/AbstractStartTimerAction.js | 41 ++ .../clock/src/actions/RestartTimerAction.js | 33 ++ .../clock/src/actions/StartTimerAction.js | 34 ++ .../clock/src/controllers/ClockController.js | 79 +++ .../src/controllers/RefreshingController.js | 29 ++ .../clock/src/controllers/TimerController.js | 146 ++++++ .../clock/src/controllers/TimerFormatter.js | 60 +++ .../clock/src/indicators/ClockIndicator.js | 38 ++ .../clock/src/services/TickerService.js | 68 +++ .../actions/AbstractStartTimerActionSpec.js | 66 +++ .../test/actions/RestartTimerActionSpec.js | 76 +++ .../test/actions/StartTimerActionSpec.js | 76 +++ .../test/controllers/ClockControllerSpec.js | 83 +++ .../controllers/RefreshingControllerSpec.js | 63 +++ .../test/controllers/TimerControllerSpec.js | 178 +++++++ .../test/controllers/TimerFormatterSpec.js | 96 ++++ .../test/indicators/ClockIndicatorSpec.js | 40 ++ .../clock/test/services/TickerServiceSpec.js | 43 ++ platform/features/clock/test/suite.json | 11 + platform/features/timeline/README.md | 70 +++ platform/features/timeline/bundle.json | 372 ++++++++++++++ .../res/templates/activity-gantt.html | 18 + .../res/templates/controls/datetime.html | 62 +++ .../timeline/res/templates/legend-item.html | 13 + .../res/templates/resource-graph-labels.html | 16 + .../res/templates/resource-graphs.html | 13 + .../templates/tabular-swimlane-cols-data.html | 16 + .../templates/tabular-swimlane-cols-tree.html | 36 ++ .../timeline/res/templates/ticks.html | 18 + .../timeline/res/templates/timeline.html | 197 +++++++ .../timeline/res/templates/values.html | 6 + .../timeline/src/TimelineConstants.js | 11 + .../timeline/src/TimelineFormatter.js | 57 +++ .../src/capabilities/ActivityTimespan.js | 100 ++++ .../ActivityTimespanCapability.js | 42 ++ .../src/capabilities/ActivityUtilization.js | 31 ++ .../src/capabilities/CostCapability.js | 56 ++ .../src/capabilities/CumulativeGraph.js | 134 +++++ .../src/capabilities/GraphCapability.js | 78 +++ .../src/capabilities/ResourceGraph.js | 128 +++++ .../src/capabilities/TimelineTimespan.js | 105 ++++ .../TimelineTimespanCapability.js | 68 +++ .../src/capabilities/TimelineUtilization.js | 31 ++ .../src/capabilities/UtilizationCapability.js | 198 +++++++ .../ActivityModeValuesController.js | 41 ++ .../src/controllers/TimelineController.js | 128 +++++ .../controllers/TimelineGanttController.js | 67 +++ .../controllers/TimelineGraphController.js | 76 +++ .../controllers/TimelineTableController.js | 32 ++ .../src/controllers/TimelineTickController.js | 97 ++++ .../src/controllers/TimelineZoomController.js | 109 ++++ .../src/controllers/WARPDateTimeController.js | 72 +++ .../drag/TimelineDragHandleFactory.js | 55 ++ .../controllers/drag/TimelineDragHandler.js | 237 +++++++++ .../controllers/drag/TimelineDragPopulator.js | 76 +++ .../src/controllers/drag/TimelineEndHandle.js | 77 +++ .../controllers/drag/TimelineMoveHandle.js | 115 +++++ .../controllers/drag/TimelineSnapHandler.js | 85 +++ .../controllers/drag/TimelineStartHandle.js | 77 +++ .../src/controllers/graph/TimelineGraph.js | 172 +++++++ .../graph/TimelineGraphPopulator.js | 136 +++++ .../graph/TimelineGraphRenderer.js | 62 +++ .../swimlane/TimelineColorAssigner.js | 101 ++++ .../src/controllers/swimlane/TimelineProxy.js | 58 +++ .../controllers/swimlane/TimelineSwimlane.js | 156 ++++++ .../swimlane/TimelineSwimlaneDecorator.js | 93 ++++ .../swimlane/TimelineSwimlaneDropHandler.js | 186 +++++++ .../swimlane/TimelineSwimlanePopulator.js | 164 ++++++ .../src/directives/SwimlaneDragConstants.js | 20 + .../src/directives/WARPSwimlaneDrag.js | 47 ++ .../src/directives/WARPSwimlaneDrop.js | 106 ++++ .../timeline/src/services/ObjectLoader.js | 114 +++++ .../timeline/test/TimelineConstantsSpec.js | 14 + .../timeline/test/TimelineFormatterSpec.js | 41 ++ .../ActivityTimespanCapabilitySpec.js | 71 +++ .../test/capabilities/ActivityTimespanSpec.js | 80 +++ .../capabilities/ActivityUtilizationSpec.js | 20 + .../test/capabilities/CostCapabilitySpec.js | 60 +++ .../test/capabilities/CumulativeGraphSpec.js | 67 +++ .../test/capabilities/GraphCapabilitySpec.js | 98 ++++ .../test/capabilities/ResourceGraphSpec.js | 56 ++ .../TimelineTimespanCapabilitySpec.js | 115 +++++ .../test/capabilities/TimelineTimespanSpec.js | 91 ++++ .../capabilities/TimelineUtilizationSpec.js | 20 + .../capabilities/UtilizationCapabilitySpec.js | 195 +++++++ .../ActivityModeValuesControllerSpec.js | 32 ++ .../controllers/TimelineControllerSpec.js | 229 +++++++++ .../TimelineGanttControllerSpec.js | 80 +++ .../TimelineGraphControllerSpec.js | 68 +++ .../TimelineTableControllerSpec.js | 31 ++ .../controllers/TimelineTickControllerSpec.js | 67 +++ .../controllers/TimelineZoomControllerSpec.js | 80 +++ .../controllers/WARPDateTimeControllerSpec.js | 57 +++ .../drag/TimelineDragHandleFactorySpec.js | 66 +++ .../drag/TimelineDragHandlerSpec.js | 209 ++++++++ .../drag/TimelineDragPopulatorSpec.js | 53 ++ .../controllers/drag/TimelineEndHandleSpec.js | 96 ++++ .../drag/TimelineMoveHandleSpec.js | 163 ++++++ .../drag/TimelineSnapHandlerSpec.js | 60 +++ .../drag/TimelineStartHandleSpec.js | 95 ++++ .../graph/TimelineGraphPopulatorSpec.js | 132 +++++ .../graph/TimelineGraphRendererSpec.js | 56 ++ .../controllers/graph/TimelineGraphSpec.js | 151 ++++++ .../swimlane/TimelineColorAssignerSpec.js | 65 +++ .../controllers/swimlane/TimelineProxySpec.js | 87 ++++ .../swimlane/TimelineSwimlaneDecoratorSpec.js | 160 ++++++ .../TimelineSwimlaneDropHandlerSpec.js | 173 +++++++ .../swimlane/TimelineSwimlanePopulatorSpec.js | 135 +++++ .../swimlane/TimelineSwimlaneSpec.js | 202 ++++++++ .../directives/SwimlaneDragConstantsSpec.js | 15 + .../test/directives/WARPSwimlaneDragSpec.js | 76 +++ .../test/directives/WARPSwimlaneDropSpec.js | 147 ++++++ .../test/services/ObjectLoaderSpec.js | 136 +++++ platform/features/timeline/test/suite.json | 50 ++ 119 files changed, 10485 insertions(+) create mode 100644 platform/features/clock/bundle.json create mode 100644 platform/features/clock/lib/moment-duration-format.js create mode 100644 platform/features/clock/res/templates/clock.html create mode 100644 platform/features/clock/res/templates/timer.html create mode 100644 platform/features/clock/src/actions/AbstractStartTimerAction.js create mode 100644 platform/features/clock/src/actions/RestartTimerAction.js create mode 100644 platform/features/clock/src/actions/StartTimerAction.js create mode 100644 platform/features/clock/src/controllers/ClockController.js create mode 100644 platform/features/clock/src/controllers/RefreshingController.js create mode 100644 platform/features/clock/src/controllers/TimerController.js create mode 100644 platform/features/clock/src/controllers/TimerFormatter.js create mode 100644 platform/features/clock/src/indicators/ClockIndicator.js create mode 100644 platform/features/clock/src/services/TickerService.js create mode 100644 platform/features/clock/test/actions/AbstractStartTimerActionSpec.js create mode 100644 platform/features/clock/test/actions/RestartTimerActionSpec.js create mode 100644 platform/features/clock/test/actions/StartTimerActionSpec.js create mode 100644 platform/features/clock/test/controllers/ClockControllerSpec.js create mode 100644 platform/features/clock/test/controllers/RefreshingControllerSpec.js create mode 100644 platform/features/clock/test/controllers/TimerControllerSpec.js create mode 100644 platform/features/clock/test/controllers/TimerFormatterSpec.js create mode 100644 platform/features/clock/test/indicators/ClockIndicatorSpec.js create mode 100644 platform/features/clock/test/services/TickerServiceSpec.js create mode 100644 platform/features/clock/test/suite.json create mode 100644 platform/features/timeline/README.md create mode 100644 platform/features/timeline/bundle.json create mode 100644 platform/features/timeline/res/templates/activity-gantt.html create mode 100644 platform/features/timeline/res/templates/controls/datetime.html create mode 100644 platform/features/timeline/res/templates/legend-item.html create mode 100644 platform/features/timeline/res/templates/resource-graph-labels.html create mode 100644 platform/features/timeline/res/templates/resource-graphs.html create mode 100644 platform/features/timeline/res/templates/tabular-swimlane-cols-data.html create mode 100644 platform/features/timeline/res/templates/tabular-swimlane-cols-tree.html create mode 100644 platform/features/timeline/res/templates/ticks.html create mode 100644 platform/features/timeline/res/templates/timeline.html create mode 100644 platform/features/timeline/res/templates/values.html create mode 100644 platform/features/timeline/src/TimelineConstants.js create mode 100644 platform/features/timeline/src/TimelineFormatter.js create mode 100644 platform/features/timeline/src/capabilities/ActivityTimespan.js create mode 100644 platform/features/timeline/src/capabilities/ActivityTimespanCapability.js create mode 100644 platform/features/timeline/src/capabilities/ActivityUtilization.js create mode 100644 platform/features/timeline/src/capabilities/CostCapability.js create mode 100644 platform/features/timeline/src/capabilities/CumulativeGraph.js create mode 100644 platform/features/timeline/src/capabilities/GraphCapability.js create mode 100644 platform/features/timeline/src/capabilities/ResourceGraph.js create mode 100644 platform/features/timeline/src/capabilities/TimelineTimespan.js create mode 100644 platform/features/timeline/src/capabilities/TimelineTimespanCapability.js create mode 100644 platform/features/timeline/src/capabilities/TimelineUtilization.js create mode 100644 platform/features/timeline/src/capabilities/UtilizationCapability.js create mode 100644 platform/features/timeline/src/controllers/ActivityModeValuesController.js create mode 100644 platform/features/timeline/src/controllers/TimelineController.js create mode 100644 platform/features/timeline/src/controllers/TimelineGanttController.js create mode 100644 platform/features/timeline/src/controllers/TimelineGraphController.js create mode 100644 platform/features/timeline/src/controllers/TimelineTableController.js create mode 100644 platform/features/timeline/src/controllers/TimelineTickController.js create mode 100644 platform/features/timeline/src/controllers/TimelineZoomController.js create mode 100644 platform/features/timeline/src/controllers/WARPDateTimeController.js create mode 100644 platform/features/timeline/src/controllers/drag/TimelineDragHandleFactory.js create mode 100644 platform/features/timeline/src/controllers/drag/TimelineDragHandler.js create mode 100644 platform/features/timeline/src/controllers/drag/TimelineDragPopulator.js create mode 100644 platform/features/timeline/src/controllers/drag/TimelineEndHandle.js create mode 100644 platform/features/timeline/src/controllers/drag/TimelineMoveHandle.js create mode 100644 platform/features/timeline/src/controllers/drag/TimelineSnapHandler.js create mode 100644 platform/features/timeline/src/controllers/drag/TimelineStartHandle.js create mode 100644 platform/features/timeline/src/controllers/graph/TimelineGraph.js create mode 100644 platform/features/timeline/src/controllers/graph/TimelineGraphPopulator.js create mode 100644 platform/features/timeline/src/controllers/graph/TimelineGraphRenderer.js create mode 100644 platform/features/timeline/src/controllers/swimlane/TimelineColorAssigner.js create mode 100644 platform/features/timeline/src/controllers/swimlane/TimelineProxy.js create mode 100644 platform/features/timeline/src/controllers/swimlane/TimelineSwimlane.js create mode 100644 platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDecorator.js create mode 100644 platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDropHandler.js create mode 100644 platform/features/timeline/src/controllers/swimlane/TimelineSwimlanePopulator.js create mode 100644 platform/features/timeline/src/directives/SwimlaneDragConstants.js create mode 100644 platform/features/timeline/src/directives/WARPSwimlaneDrag.js create mode 100644 platform/features/timeline/src/directives/WARPSwimlaneDrop.js create mode 100644 platform/features/timeline/src/services/ObjectLoader.js create mode 100644 platform/features/timeline/test/TimelineConstantsSpec.js create mode 100644 platform/features/timeline/test/TimelineFormatterSpec.js create mode 100644 platform/features/timeline/test/capabilities/ActivityTimespanCapabilitySpec.js create mode 100644 platform/features/timeline/test/capabilities/ActivityTimespanSpec.js create mode 100644 platform/features/timeline/test/capabilities/ActivityUtilizationSpec.js create mode 100644 platform/features/timeline/test/capabilities/CostCapabilitySpec.js create mode 100644 platform/features/timeline/test/capabilities/CumulativeGraphSpec.js create mode 100644 platform/features/timeline/test/capabilities/GraphCapabilitySpec.js create mode 100644 platform/features/timeline/test/capabilities/ResourceGraphSpec.js create mode 100644 platform/features/timeline/test/capabilities/TimelineTimespanCapabilitySpec.js create mode 100644 platform/features/timeline/test/capabilities/TimelineTimespanSpec.js create mode 100644 platform/features/timeline/test/capabilities/TimelineUtilizationSpec.js create mode 100644 platform/features/timeline/test/capabilities/UtilizationCapabilitySpec.js create mode 100644 platform/features/timeline/test/controllers/ActivityModeValuesControllerSpec.js create mode 100644 platform/features/timeline/test/controllers/TimelineControllerSpec.js create mode 100644 platform/features/timeline/test/controllers/TimelineGanttControllerSpec.js create mode 100644 platform/features/timeline/test/controllers/TimelineGraphControllerSpec.js create mode 100644 platform/features/timeline/test/controllers/TimelineTableControllerSpec.js create mode 100644 platform/features/timeline/test/controllers/TimelineTickControllerSpec.js create mode 100644 platform/features/timeline/test/controllers/TimelineZoomControllerSpec.js create mode 100644 platform/features/timeline/test/controllers/WARPDateTimeControllerSpec.js create mode 100644 platform/features/timeline/test/controllers/drag/TimelineDragHandleFactorySpec.js create mode 100644 platform/features/timeline/test/controllers/drag/TimelineDragHandlerSpec.js create mode 100644 platform/features/timeline/test/controllers/drag/TimelineDragPopulatorSpec.js create mode 100644 platform/features/timeline/test/controllers/drag/TimelineEndHandleSpec.js create mode 100644 platform/features/timeline/test/controllers/drag/TimelineMoveHandleSpec.js create mode 100644 platform/features/timeline/test/controllers/drag/TimelineSnapHandlerSpec.js create mode 100644 platform/features/timeline/test/controllers/drag/TimelineStartHandleSpec.js create mode 100644 platform/features/timeline/test/controllers/graph/TimelineGraphPopulatorSpec.js create mode 100644 platform/features/timeline/test/controllers/graph/TimelineGraphRendererSpec.js create mode 100644 platform/features/timeline/test/controllers/graph/TimelineGraphSpec.js create mode 100644 platform/features/timeline/test/controllers/swimlane/TimelineColorAssignerSpec.js create mode 100644 platform/features/timeline/test/controllers/swimlane/TimelineProxySpec.js create mode 100644 platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js create mode 100644 platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDropHandlerSpec.js create mode 100644 platform/features/timeline/test/controllers/swimlane/TimelineSwimlanePopulatorSpec.js create mode 100644 platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneSpec.js create mode 100644 platform/features/timeline/test/directives/SwimlaneDragConstantsSpec.js create mode 100644 platform/features/timeline/test/directives/WARPSwimlaneDragSpec.js create mode 100644 platform/features/timeline/test/directives/WARPSwimlaneDropSpec.js create mode 100644 platform/features/timeline/test/services/ObjectLoaderSpec.js create mode 100644 platform/features/timeline/test/suite.json diff --git a/bundles.json b/bundles.json index 35d6f11728..f32f21abf5 100644 --- a/bundles.json +++ b/bundles.json @@ -11,11 +11,13 @@ "platform/containment", "platform/execution", "platform/telemetry", + "platform/features/clock", "platform/features/imagery", "platform/features/layout", "platform/features/pages", "platform/features/plot", "platform/features/scrolling", + "platform/features/timeline", "platform/features/events", "platform/forms", "platform/identity", diff --git a/platform/features/clock/bundle.json b/platform/features/clock/bundle.json new file mode 100644 index 0000000000..ee40b028b9 --- /dev/null +++ b/platform/features/clock/bundle.json @@ -0,0 +1,173 @@ +{ + "name": "WARP Clocks/Timers", + "descriptions": "Domain objects for displaying current & relative times.", + "configuration": { + "paths": { + "moment-duration-format": "moment-duration-format" + } + }, + "extensions": { + "constants": [ + { + "key": "CLOCK_INDICATOR_FORMAT", + "value": "YYYY/MM/DD HH:mm:ss" + } + + ], + "indicators": [ + { + "implementation": "indicators/ClockIndicator.js", + "depends": [ "warp.tickerService", "CLOCK_INDICATOR_FORMAT" ], + "priority": "preferred" + } + ], + "services": [ + { + "key": "warp.tickerService", + "implementation": "services/TickerService.js", + "depends": [ "$timeout", "now" ] + } + ], + "controllers": [ + { + "key": "ClockController", + "implementation": "controllers/ClockController.js", + "depends": [ "$scope", "warp.tickerService" ] + }, + { + "key": "TimerController", + "implementation": "controllers/TimerController.js", + "depends": [ "$scope", "$window", "now" ] + }, + { + "key": "RefreshingController", + "implementation": "controllers/RefreshingController.js", + "depends": [ "$scope", "warp.tickerService" ] + } + ], + "views": [ + { + "key": "warp.clock", + "type": "warp.clock", + "templateUrl": "templates/clock.html" + }, + { + "key": "warp.timer", + "type": "warp.timer", + "templateUrl": "templates/timer.html" + } + ], + "actions": [ + { + "key": "warp.timer.start", + "implementation": "actions/StartTimerAction.js", + "depends": ["now"], + "category": "contextual", + "name": "Start", + "glyph": "\u00EF", + "priority": "preferred" + }, + { + "key": "warp.timer.restart", + "implementation": "actions/RestartTimerAction.js", + "depends": ["now"], + "category": "contextual", + "name": "Restart at 0", + "glyph": "r", + "priority": "preferred" + } + ], + "types": [ + { + "key": "warp.clock", + "name": "Clock", + "glyph": "C", + "features": [ "creation" ], + "properties": [ + { + "key": "clockFormat", + "name": "Display Format", + "control": "composite", + "items": [ + { + "control": "select", + "options": [ + { + "value": "YYYY/MM/DD hh:mm:ss", + "name": "YYYY/MM/DD hh:mm:ss" + }, + { + "value": "YYYY/DDD hh:mm:ss", + "name": "YYYY/DDD hh:mm:ss" + }, + { + "value": "hh:mm:ss", + "name": "hh:mm:ss" + } + ] + }, + { + "control": "select", + "options": [ + { + "value": "clock12", + "name": "12hr" + }, + { + "value": "clock24", + "name": "24hr" + } + ] + } + ] + } + ], + "model": { + "clockFormat": [ "YYYY/MM/DD hh:mm:ss", "clock12" ] + } + }, + { + "key": "warp.timer", + "name": "Timer", + "glyph": "\u00F5", + "features": [ "creation" ], + "properties": [ + { + "key": "timestamp", + "control": "datetime", + "name": "Target" + }, + { + "key": "timerFormat", + "control": "select", + "options": [ + { + "value": "long", + "name": "DDD hh:mm:ss" + }, + { + "value": "short", + "name": "hh:mm:ss" + } + ] + } + ], + "model": { + "timerFormat": "DDD hh:mm:ss" + } + } + ], + "licenses": [ + { + "name": "moment-duration-format", + "version": "1.3.0", + "author": "John Madhavan-Reese", + "description": "Duration parsing/formatting", + "website": "https://github.com/jsmreese/moment-duration-format", + "copyright": "Copyright 2014 John Madhavan-Reese", + "license": "license-mit", + "link": "https://github.com/jsmreese/moment-duration-format/blob/master/LICENSE" + } + ] + } +} diff --git a/platform/features/clock/lib/moment-duration-format.js b/platform/features/clock/lib/moment-duration-format.js new file mode 100644 index 0000000000..9e85421003 --- /dev/null +++ b/platform/features/clock/lib/moment-duration-format.js @@ -0,0 +1,482 @@ +/*! Moment Duration Format v1.3.0 + * https://github.com/jsmreese/moment-duration-format + * Date: 2014-07-15 + * + * Duration format plugin function for the Moment.js library + * http://momentjs.com/ + * + * Copyright 2014 John Madhavan-Reese + * Released under the MIT license + */ + +(function (root, undefined) { + + // repeatZero(qty) + // returns "0" repeated qty times + function repeatZero(qty) { + var result = ""; + + // exit early + // if qty is 0 or a negative number + // or doesn't coerce to an integer + qty = parseInt(qty, 10); + if (!qty || qty < 1) { return result; } + + while (qty) { + result += "0"; + qty -= 1; + } + + return result; + } + + // padZero(str, len [, isRight]) + // pads a string with zeros up to a specified length + // will not pad a string if its length is aready + // greater than or equal to the specified length + // default output pads with zeros on the left + // set isRight to `true` to pad with zeros on the right + function padZero(str, len, isRight) { + if (str == null) { str = ""; } + str = "" + str; + + return (isRight ? str : "") + repeatZero(len - str.length) + (isRight ? "" : str); + } + + // isArray + function isArray(array) { + return Object.prototype.toString.call(array) === "[object Array]"; + } + + // isObject + function isObject(obj) { + return Object.prototype.toString.call(obj) === "[object Object]"; + } + + // findLast + function findLast(array, callback) { + var index = array.length; + + while (index -= 1) { + if (callback(array[index])) { return array[index]; } + } + } + + // find + function find(array, callback) { + var index = 0, + max = array.length, + match; + + if (typeof callback !== "function") { + match = callback; + callback = function (item) { + return item === match; + }; + } + + while (index < max) { + if (callback(array[index])) { return array[index]; } + index += 1; + } + } + + // each + function each(array, callback) { + var index = 0, + max = array.length; + + if (!array || !max) { return; } + + while (index < max) { + if (callback(array[index], index) === false) { return; } + index += 1; + } + } + + // map + function map(array, callback) { + var index = 0, + max = array.length, + ret = []; + + if (!array || !max) { return ret; } + + while (index < max) { + ret[index] = callback(array[index], index); + index += 1; + } + + return ret; + } + + // pluck + function pluck(array, prop) { + return map(array, function (item) { + return item[prop]; + }); + } + + // compact + function compact(array) { + var ret = []; + + each(array, function (item) { + if (item) { ret.push(item); } + }); + + return ret; + } + + // unique + function unique(array) { + var ret = []; + + each(array, function (_a) { + if (!find(ret, _a)) { ret.push(_a); } + }); + + return ret; + } + + // intersection + function intersection(a, b) { + var ret = []; + + each(a, function (_a) { + each(b, function (_b) { + if (_a === _b) { ret.push(_a); } + }); + }); + + return unique(ret); + } + + // rest + function rest(array, callback) { + var ret = []; + + each(array, function (item, index) { + if (!callback(item)) { + ret = array.slice(index); + return false; + } + }); + + return ret; + } + + // initial + function initial(array, callback) { + var reversed = array.slice().reverse(); + + return rest(reversed, callback).reverse(); + } + + // extend + function extend(a, b) { + for (var key in b) { + if (b.hasOwnProperty(key)) { a[key] = b[key]; } + } + + return a; + } + + // define internal moment reference + var moment; + + if (typeof require === "function") { + try { moment = require('moment'); } + catch (e) {} + } + + if (!moment && root.moment) { + moment = root.moment; + } + + if (!moment) { + throw "Moment Duration Format cannot find Moment.js"; + } + + // moment.duration.format([template] [, precision] [, settings]) + moment.duration.fn.format = function () { + + var tokenizer, tokens, types, typeMap, momentTypes, foundFirst, trimIndex, + args = [].slice.call(arguments), + settings = extend({}, this.format.defaults), + // keep a shadow copy of this moment for calculating remainders + remainder = moment.duration(this); + + // add a reference to this duration object to the settings for use + // in a template function + settings.duration = this; + + // parse arguments + each(args, function (arg) { + if (typeof arg === "string" || typeof arg === "function") { + settings.template = arg; + return; + } + + if (typeof arg === "number") { + settings.precision = arg; + return; + } + + if (isObject(arg)) { + extend(settings, arg); + } + }); + + // types + types = settings.types = (isArray(settings.types) ? settings.types : settings.types.split(" ")); + + // template + if (typeof settings.template === "function") { + settings.template = settings.template.apply(settings); + } + + // tokenizer regexp + tokenizer = new RegExp(map(types, function (type) { + return settings[type].source; + }).join("|"), "g"); + + // token type map function + typeMap = function (token) { + return find(types, function (type) { + return settings[type].test(token); + }); + }; + + // tokens array + tokens = map(settings.template.match(tokenizer), function (token, index) { + var type = typeMap(token), + length = token.length; + + return { + index: index, + length: length, + + // replace escaped tokens with the non-escaped token text + token: (type === "escape" ? token.replace(settings.escape, "$1") : token), + + // ignore type on non-moment tokens + type: ((type === "escape" || type === "general") ? null : type) + + // calculate base value for all moment tokens + //baseValue: ((type === "escape" || type === "general") ? null : this.as(type)) + }; + }, this); + + // unique moment token types in the template (in order of descending magnitude) + momentTypes = intersection(types, unique(compact(pluck(tokens, "type")))); + + // exit early if there are no momentTypes + if (!momentTypes.length) { + return pluck(tokens, "token").join(""); + } + + // calculate values for each token type in the template + each(momentTypes, function (momentType, index) { + var value, wholeValue, decimalValue, isLeast, isMost; + + // calculate integer and decimal value portions + value = remainder.as(momentType); + wholeValue = (value > 0 ? Math.floor(value) : Math.ceil(value)); + decimalValue = value - wholeValue; + + // is this the least-significant moment token found? + isLeast = ((index + 1) === momentTypes.length); + + // is this the most-significant moment token found? + isMost = (!index); + + // update tokens array + // using this algorithm to not assume anything about + // the order or frequency of any tokens + each(tokens, function (token) { + if (token.type === momentType) { + extend(token, { + value: value, + wholeValue: wholeValue, + decimalValue: decimalValue, + isLeast: isLeast, + isMost: isMost + }); + + if (isMost) { + // note the length of the most-significant moment token: + // if it is greater than one and forceLength is not set, default forceLength to `true` + if (settings.forceLength == null && token.length > 1) { + settings.forceLength = true; + } + + // rationale is this: + // if the template is "h:mm:ss" and the moment value is 5 minutes, the user-friendly output is "5:00", not "05:00" + // shouldn't pad the `minutes` token even though it has length of two + // if the template is "hh:mm:ss", the user clearly wanted everything padded so we should output "05:00" + // if the user wanted the full padded output, they can set `{ trim: false }` to get "00:05:00" + } + } + }); + + // update remainder + remainder.subtract(wholeValue, momentType); + }); + + // trim tokens array + if (settings.trim) { + tokens = (settings.trim === "left" ? rest : initial)(tokens, function (token) { + // return `true` if: + // the token is not the least moment token (don't trim the least moment token) + // the token is a moment token that does not have a value (don't trim moment tokens that have a whole value) + return !(token.isLeast || (token.type != null && token.wholeValue)); + }); + } + + + // build output + + // the first moment token can have special handling + foundFirst = false; + + // run the map in reverse order if trimming from the right + if (settings.trim === "right") { + tokens.reverse(); + } + + tokens = map(tokens, function (token) { + var val, + decVal; + + if (!token.type) { + // if it is not a moment token, use the token as its own value + return token.token; + } + + // apply negative precision formatting to the least-significant moment token + if (token.isLeast && (settings.precision < 0)) { + val = (Math.floor(token.wholeValue * Math.pow(10, settings.precision)) * Math.pow(10, -settings.precision)).toString(); + } else { + val = token.wholeValue.toString(); + } + + // remove negative sign from the beginning + val = val.replace(/^\-/, ""); + + // apply token length formatting + // special handling for the first moment token that is not the most significant in a trimmed template + if (token.length > 1 && (foundFirst || token.isMost || settings.forceLength)) { + val = padZero(val, token.length); + } + + // add decimal value if precision > 0 + if (token.isLeast && (settings.precision > 0)) { + decVal = token.decimalValue.toString().replace(/^\-/, "").split(/\.|e\-/); + switch (decVal.length) { + case 1: + val += "." + padZero(decVal[0], settings.precision, true).slice(0, settings.precision); + break; + + case 2: + val += "." + padZero(decVal[1], settings.precision, true).slice(0, settings.precision); + break; + + case 3: + val += "." + padZero(repeatZero((+decVal[2]) - 1) + (decVal[0] || "0") + decVal[1], settings.precision, true).slice(0, settings.precision); + break; + + default: + throw "Moment Duration Format: unable to parse token decimal value."; + } + } + + // add a negative sign if the value is negative and token is most significant + if (token.isMost && token.value < 0) { + val = "-" + val; + } + + foundFirst = true; + + return val; + }); + + // undo the reverse if trimming from the right + if (settings.trim === "right") { + tokens.reverse(); + } + + return tokens.join(""); + }; + + moment.duration.fn.format.defaults = { + // token definitions + escape: /\[(.+?)\]/, + years: /[Yy]+/, + months: /M+/, + weeks: /[Ww]+/, + days: /[Dd]+/, + hours: /[Hh]+/, + minutes: /m+/, + seconds: /s+/, + milliseconds: /S+/, + general: /.+?/, + + // token type names + // in order of descending magnitude + // can be a space-separated token name list or an array of token names + types: "escape years months weeks days hours minutes seconds milliseconds general", + + // format options + + // trim + // "left" - template tokens are trimmed from the left until the first moment token that has a value >= 1 + // "right" - template tokens are trimmed from the right until the first moment token that has a value >= 1 + // (the final moment token is not trimmed, regardless of value) + // `false` - template tokens are not trimmed + trim: "left", + + // precision + // number of decimal digits to include after (to the right of) the decimal point (positive integer) + // or the number of digits to truncate to 0 before (to the left of) the decimal point (negative integer) + precision: 0, + + // force first moment token with a value to render at full length even when template is trimmed and first moment token has length of 1 + forceLength: null, + + // template used to format duration + // may be a function or a string + // template functions are executed with the `this` binding of the settings object + // so that template strings may be dynamically generated based on the duration object + // (accessible via `this.duration`) + // or any of the other settings + template: function () { + var types = this.types, + dur = this.duration, + lastType = findLast(types, function (type) { + return dur._data[type]; + }); + + // default template strings for each duration dimension type + switch (lastType) { + case "seconds": + return "h:mm:ss"; + case "minutes": + return "d[d] h:mm"; + case "hours": + return "d[d] h[h]"; + case "days": + return "M[m] d[d]"; + case "weeks": + return "y[y] w[w]"; + case "months": + return "y[y] M[m]"; + case "years": + return "y[y]"; + default: + return "y[y] M[m] d[d] h:mm:ss"; + } + } + }; + +})(this); diff --git a/platform/features/clock/res/templates/clock.html b/platform/features/clock/res/templates/clock.html new file mode 100644 index 0000000000..69c690e69d --- /dev/null +++ b/platform/features/clock/res/templates/clock.html @@ -0,0 +1,13 @@ +
+
+ + {{clock.zone()}} + + + {{clock.text()}} + + + {{clock.ampm()}} + +
+
\ No newline at end of file diff --git a/platform/features/clock/res/templates/timer.html b/platform/features/clock/res/templates/timer.html new file mode 100644 index 0000000000..36bfe3f972 --- /dev/null +++ b/platform/features/clock/res/templates/timer.html @@ -0,0 +1,21 @@ +
+
+ + {{timer.buttonGlyph()}} + + + {{timer.sign()}} + {{timer.text() || "--:--:--"}} + + + + +
+
\ No newline at end of file diff --git a/platform/features/clock/src/actions/AbstractStartTimerAction.js b/platform/features/clock/src/actions/AbstractStartTimerAction.js new file mode 100644 index 0000000000..bf520ff6c7 --- /dev/null +++ b/platform/features/clock/src/actions/AbstractStartTimerAction.js @@ -0,0 +1,41 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Implements the "Start" and "Restart" action for timers. + * + * Sets the reference timestamp in a timer to the current + * time, such that it begins counting up. + * + * Both "Start" and "Restart" share this implementation, but + * control their visibility with different `appliesTo` behavior. + * + * @implements Action + */ + function AbstractStartTimerAction(now, context) { + var domainObject = context.domainObject; + + function doPersist() { + var persistence = domainObject.getCapability('persistence'); + return persistence && persistence.persist(); + } + + function setTimestamp(model) { + model.timestamp = now(); + } + + return { + perform: function () { + return domainObject.useCapability('mutation', setTimestamp) + .then(doPersist); + } + }; + } + + return AbstractStartTimerAction; + } +); diff --git a/platform/features/clock/src/actions/RestartTimerAction.js b/platform/features/clock/src/actions/RestartTimerAction.js new file mode 100644 index 0000000000..42723887c5 --- /dev/null +++ b/platform/features/clock/src/actions/RestartTimerAction.js @@ -0,0 +1,33 @@ +/*global define*/ + +define( + ['./AbstractStartTimerAction'], + function (AbstractStartTimerAction) { + "use strict"; + + /** + * Implements the "Restart at 0" action. + * + * Behaves the same as (and delegates functionality to) + * the "Start" action. + * @implements Action + */ + function RestartTimerAction(now, context) { + return new AbstractStartTimerAction(now, context); + } + + RestartTimerAction.appliesTo = function (context) { + var model = + (context.domainObject && context.domainObject.getModel()) + || {}; + + // We show this variant for timers which already have + // a target time. + return model.type === 'warp.timer' && + model.timestamp !== undefined; + }; + + return RestartTimerAction; + + } +); \ No newline at end of file diff --git a/platform/features/clock/src/actions/StartTimerAction.js b/platform/features/clock/src/actions/StartTimerAction.js new file mode 100644 index 0000000000..39f604d784 --- /dev/null +++ b/platform/features/clock/src/actions/StartTimerAction.js @@ -0,0 +1,34 @@ +/*global define*/ + +define( + ['./AbstractStartTimerAction'], + function (AbstractStartTimerAction) { + "use strict"; + + /** + * Implements the "Start" action for timers. + * + * Sets the reference timestamp in a timer to the current + * time, such that it begins counting up. + * + * @implements Action + */ + function StartTimerAction(now, context) { + return new AbstractStartTimerAction(now, context); + } + + StartTimerAction.appliesTo = function (context) { + var model = + (context.domainObject && context.domainObject.getModel()) + || {}; + + // We show this variant for timers which do not yet have + // a target time. + return model.type === 'warp.timer' && + model.timestamp === undefined; + }; + + return StartTimerAction; + + } +); \ No newline at end of file diff --git a/platform/features/clock/src/controllers/ClockController.js b/platform/features/clock/src/controllers/ClockController.js new file mode 100644 index 0000000000..5bfc99a652 --- /dev/null +++ b/platform/features/clock/src/controllers/ClockController.js @@ -0,0 +1,79 @@ +/*global define*/ + +define( + ['moment'], + function (moment) { + "use strict"; + + /** + * Controller for views of a Clock domain object. + * + * @constructor + */ + function ClockController($scope, tickerService) { + var text, + ampm, + use24, + lastTimestamp, + unlisten, + timeFormat; + + function update() { + var m = moment.utc(lastTimestamp); + text = timeFormat && m.format(timeFormat); + ampm = m.format("A"); // Just the AM or PM part + } + + function tick(timestamp) { + lastTimestamp = timestamp; + update(); + } + + function updateFormat(clockFormat) { + var baseFormat; + + if (clockFormat !== undefined) { + baseFormat = clockFormat[0]; + + use24 = clockFormat[1] === 'clock24'; + timeFormat = use24 ? + baseFormat.replace('hh', "HH") : baseFormat; + + update(); + } + } + // Pull in the clock format from the domain object model + $scope.$watch('model.clockFormat', updateFormat); + + // Listen for clock ticks ... and stop listening on destroy + unlisten = tickerService.listen(tick); + $scope.$on('$destroy', unlisten); + + return { + /** + * Get the clock's time zone, as displayable text. + * @returns {string} + */ + zone: function () { + return "UTC"; + }, + /** + * Get the current time, as displayable text. + * @returns {string} + */ + text: function () { + return text; + }, + /** + * Get the text to display to qualify a time as AM or PM. + * @returns {string} + */ + ampm: function () { + return use24 ? '' : ampm; + } + }; + } + + return ClockController; + } +); diff --git a/platform/features/clock/src/controllers/RefreshingController.js b/platform/features/clock/src/controllers/RefreshingController.js new file mode 100644 index 0000000000..49af341308 --- /dev/null +++ b/platform/features/clock/src/controllers/RefreshingController.js @@ -0,0 +1,29 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Continually refreshes the represented domain object. + * + * This is a short-term workaround to assure Timer views stay + * up-to-date; should be replaced by a global auto-refresh. + */ + function RefreshingController($scope, tickerService) { + var unlisten; + + function triggerRefresh() { + var persistence = $scope.domainObject && + $scope.domainObject.getCapability('persistence'); + return persistence && persistence.refresh(); + } + + unlisten = tickerService.listen(triggerRefresh); + $scope.$on('$destroy', unlisten); + } + + return RefreshingController; + } +); diff --git a/platform/features/clock/src/controllers/TimerController.js b/platform/features/clock/src/controllers/TimerController.js new file mode 100644 index 0000000000..9538f914dd --- /dev/null +++ b/platform/features/clock/src/controllers/TimerController.js @@ -0,0 +1,146 @@ +/*global define*/ + +define( + ['./TimerFormatter'], + function (TimerFormatter) { + "use strict"; + + var FORMATTER = new TimerFormatter(); + + + /** + * Controller for views of a Timer domain object. + * + * @constructor + */ + function TimerController($scope, $window, now) { + var timerObject, + relevantAction, + sign = '', + text = '', + formatter, + active = true, + relativeTimestamp, + lastTimestamp; + + function update() { + var timeDelta = lastTimestamp - relativeTimestamp; + + if (formatter && !isNaN(timeDelta)) { + text = formatter(timeDelta); + sign = timeDelta < 0 ? "-" : timeDelta >= 1000 ? "+" : ""; + } else { + text = ""; + sign = ""; + } + } + + function updateFormat(key) { + formatter = FORMATTER[key] || FORMATTER.long; + } + + function updateTimestamp(timestamp) { + relativeTimestamp = timestamp; + } + + function updateObject(domainObject) { + var model = domainObject.getModel(), + timestamp = model.timestamp, + formatKey = model.timerFormat, + actionCapability = domainObject.getCapability('action'), + actionKey = (timestamp === undefined) ? + 'warp.timer.start' : 'warp.timer.restart'; + + updateFormat(formatKey); + updateTimestamp(timestamp); + + relevantAction = actionCapability && + actionCapability.getActions(actionKey)[0]; + + update(); + } + + function handleObjectChange(domainObject) { + if (domainObject) { + updateObject(domainObject); + } + } + + function handleModification() { + handleObjectChange($scope.domainObject); + } + + function tick() { + var lastSign = sign, lastText = text; + lastTimestamp = now(); + update(); + // We're running in an animation frame, not in a digest cycle. + // We need to trigger a digest cycle if our displayable data + // changes. + if (lastSign !== sign || lastText !== text) { + $scope.$apply(); + } + if (active) { + $window.requestAnimationFrame(tick); + } + } + + $window.requestAnimationFrame(tick); + + // Pull in the timer format from the domain object model + $scope.$watch('domainObject', handleObjectChange); + $scope.$watch('model.modified', handleModification); + + // When the scope is destroyed, stop requesting anim. frames + $scope.$on('$destroy', function () { + active = false; + }); + + return { + /** + * Get the glyph to display for the start/restart button. + * @returns {string} glyph to display + */ + buttonGlyph: function () { + return relevantAction ? + relevantAction.getMetadata().glyph : ""; + }, + /** + * Get the text to show for the start/restart button + * (e.g. in a tooltip) + * @returns {string} name of the action + */ + buttonText: function () { + return relevantAction ? + relevantAction.getMetadata().name : ""; + }, + /** + * Perform the action associated with the start/restart button. + */ + clickButton: function () { + if (relevantAction) { + relevantAction.perform(); + updateObject($scope.domainObject); + } + }, + /** + * Get the sign (+ or -) of the current timer value, as + * displayable text. + * @returns {string} sign of the current timer value + */ + sign: function () { + return sign; + }, + /** + * Get the text to display for the current timer value. + * @returns {string} current timer value + */ + text: function () { + return text; + } + }; + } + + return TimerController; + } +); diff --git a/platform/features/clock/src/controllers/TimerFormatter.js b/platform/features/clock/src/controllers/TimerFormatter.js new file mode 100644 index 0000000000..5b4db6f7cf --- /dev/null +++ b/platform/features/clock/src/controllers/TimerFormatter.js @@ -0,0 +1,60 @@ +/*global define*/ + +define( + ['moment', 'moment-duration-format'], + function (moment) { + "use strict"; + + var SHORT_FORMAT = "HH:mm:ss", + LONG_FORMAT = "d[D] HH:mm:ss"; + + /** + * Provides formatting functions for Timers. + * + * Display formats for timers are a little different from what + * moment.js provides, so we have custom logic here. This specifically + * supports `TimerController`. + * + * @constructor + */ + function TimerFormatter() { + + // Round this timestamp down to the second boundary + // (e.g. 1124ms goes down to 1000ms, -2400ms goes down to -3000ms) + function toWholeSeconds(duration) { + return Math.abs(Math.floor(duration / 1000) * 1000); + } + + // Short-form format, e.g. 02:22:11 + function short(duration) { + return moment.duration(toWholeSeconds(duration), 'ms') + .format(SHORT_FORMAT, { trim: false }); + } + + // Long-form format, e.g. 3d 02:22:11 + function long(duration) { + return moment.duration(toWholeSeconds(duration), 'ms') + .format(LONG_FORMAT, { trim: false }); + } + + return { + /** + * Format a duration for display, using the short form. + * (e.g. 03:33:11) + * @param {number} duration the duration, in milliseconds + * @param {boolean} sign true if positive + */ + short: short, + /** + * Format a duration for display, using the long form. + * (e.g. 0d 03:33:11) + * @param {number} duration the duration, in milliseconds + * @param {boolean} sign true if positive + */ + long: long + }; + } + + return TimerFormatter; + } +); diff --git a/platform/features/clock/src/indicators/ClockIndicator.js b/platform/features/clock/src/indicators/ClockIndicator.js new file mode 100644 index 0000000000..55f43fc0f7 --- /dev/null +++ b/platform/features/clock/src/indicators/ClockIndicator.js @@ -0,0 +1,38 @@ +/*global define*/ + +define( + ['moment'], + function (moment) { + "use strict"; + + /** + * Indicator that displays the current UTC time in the status area. + * @implements Indicator + */ + function ClockIndicator(tickerService, CLOCK_INDICATOR_FORMAT) { + var text = ""; + + tickerService.listen(function (timestamp) { + text = moment.utc(timestamp).format(CLOCK_INDICATOR_FORMAT) + " UTC"; + }); + + return { + getGlyph: function () { + return "C"; + }, + getGlyphClass: function () { + return ""; + }, + getText: function () { + return text; + }, + getDescription: function () { + return ""; + } + }; + + } + + return ClockIndicator; + } +); diff --git a/platform/features/clock/src/services/TickerService.js b/platform/features/clock/src/services/TickerService.js new file mode 100644 index 0000000000..0304b57c2a --- /dev/null +++ b/platform/features/clock/src/services/TickerService.js @@ -0,0 +1,68 @@ +/*global define*/ + +define( + ['moment'], + function (moment) { + "use strict"; + + /** + * Calls functions every second, as close to the actual second + * tick as is feasible. + * @constructor + * @param $timeout Angular's $timeout + * @param {Function} now function to provide the current time in ms + */ + function TickerService($timeout, now) { + var callbacks = [], + last = now() - 1000; + + function tick() { + var timestamp = now(), + millis = timestamp % 1000; + + // Only update callbacks if a second has actually passed. + if (timestamp >= last + 1000) { + callbacks.forEach(function (callback) { + callback(timestamp); + }); + last = timestamp - millis; + } + + // Try to update at exactly the next second + $timeout(tick, 1000 - millis, true); + } + + tick(); + + return { + /** + * Listen for clock ticks. The provided callback will + * be invoked with the current timestamp (in milliseconds + * since Jan 1 1970) at regular intervals, as near to the + * second boundary as possible. + * + * @method listen + * @name TickerService#listen + * @param {Function} callback callback to invoke + * @returns {Function} a function to unregister this listener + */ + listen: function (callback) { + callbacks.push(callback); + + // Provide immediate feedback + callback(last); + + // Provide a deregistration function + return function () { + callbacks = callbacks.filter(function (cb) { + return cb !== callback; + }); + }; + } + }; + + } + + return TickerService; + } +); diff --git a/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js b/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js new file mode 100644 index 0000000000..8714392a07 --- /dev/null +++ b/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js @@ -0,0 +1,66 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/actions/AbstractStartTimerAction"], + function (AbstractStartTimerAction) { + "use strict"; + + describe("A timer's start/restart action", function () { + var mockNow, + mockDomainObject, + mockPersistence, + testModel, + action; + + function asPromise(value) { + return (value || {}).then ? value : { + then: function (callback) { + return asPromise(callback(value)); + } + }; + } + + beforeEach(function () { + mockNow = jasmine.createSpy('now'); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getCapability', 'useCapability' ] + ); + mockPersistence = jasmine.createSpyObj( + 'persistence', + ['persist'] + ); + + mockDomainObject.getCapability.andCallFake(function (c) { + return (c === 'persistence') && mockPersistence; + }); + mockDomainObject.useCapability.andCallFake(function (c, v) { + if (c === 'mutation') { + testModel = v(testModel) || testModel; + return asPromise(true); + } + }); + + testModel = {}; + + action = new AbstractStartTimerAction(mockNow, { + domainObject: mockDomainObject + }); + }); + + it("updates the model with a timestamp and persists", function () { + mockNow.andReturn(12000); + action.perform(); + expect(testModel.timestamp).toEqual(12000); + expect(mockPersistence.persist).toHaveBeenCalled(); + }); + + it("does not truncate milliseconds", function () { + mockNow.andReturn(42321); + action.perform(); + expect(testModel.timestamp).toEqual(42321); + expect(mockPersistence.persist).toHaveBeenCalled(); + }); + }); + } +); diff --git a/platform/features/clock/test/actions/RestartTimerActionSpec.js b/platform/features/clock/test/actions/RestartTimerActionSpec.js new file mode 100644 index 0000000000..23df5f3142 --- /dev/null +++ b/platform/features/clock/test/actions/RestartTimerActionSpec.js @@ -0,0 +1,76 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/actions/RestartTimerAction"], + function (RestartTimerAction) { + "use strict"; + + describe("A timer's restart action", function () { + var mockNow, + mockDomainObject, + mockPersistence, + testModel, + testContext, + action; + + function asPromise(value) { + return (value || {}).then ? value : { + then: function (callback) { + return asPromise(callback(value)); + } + }; + } + + beforeEach(function () { + mockNow = jasmine.createSpy('now'); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getCapability', 'useCapability', 'getModel' ] + ); + mockPersistence = jasmine.createSpyObj( + 'persistence', + ['persist'] + ); + + mockDomainObject.getCapability.andCallFake(function (c) { + return (c === 'persistence') && mockPersistence; + }); + mockDomainObject.useCapability.andCallFake(function (c, v) { + if (c === 'mutation') { + testModel = v(testModel) || testModel; + return asPromise(true); + } + }); + mockDomainObject.getModel.andCallFake(function () { + return testModel; + }); + + testModel = {}; + testContext = { domainObject: mockDomainObject }; + + action = new RestartTimerAction(mockNow, testContext); + }); + + it("updates the model with a timestamp and persists", function () { + mockNow.andReturn(12000); + action.perform(); + expect(testModel.timestamp).toEqual(12000); + expect(mockPersistence.persist).toHaveBeenCalled(); + }); + + it("applies only to timers with a target time", function () { + testModel.type = 'warp.timer'; + testModel.timestamp = 12000; + expect(RestartTimerAction.appliesTo(testContext)).toBeTruthy(); + + testModel.type = 'warp.timer'; + testModel.timestamp = undefined; + expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy(); + + testModel.type = 'warp.clock'; + testModel.timestamp = 12000; + expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy(); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/clock/test/actions/StartTimerActionSpec.js b/platform/features/clock/test/actions/StartTimerActionSpec.js new file mode 100644 index 0000000000..10f60595d5 --- /dev/null +++ b/platform/features/clock/test/actions/StartTimerActionSpec.js @@ -0,0 +1,76 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/actions/StartTimerAction"], + function (StartTimerAction) { + "use strict"; + + describe("A timer's start action", function () { + var mockNow, + mockDomainObject, + mockPersistence, + testModel, + testContext, + action; + + function asPromise(value) { + return (value || {}).then ? value : { + then: function (callback) { + return asPromise(callback(value)); + } + }; + } + + beforeEach(function () { + mockNow = jasmine.createSpy('now'); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getCapability', 'useCapability', 'getModel' ] + ); + mockPersistence = jasmine.createSpyObj( + 'persistence', + ['persist'] + ); + + mockDomainObject.getCapability.andCallFake(function (c) { + return (c === 'persistence') && mockPersistence; + }); + mockDomainObject.useCapability.andCallFake(function (c, v) { + if (c === 'mutation') { + testModel = v(testModel) || testModel; + return asPromise(true); + } + }); + mockDomainObject.getModel.andCallFake(function () { + return testModel; + }); + + testModel = {}; + testContext = { domainObject: mockDomainObject }; + + action = new StartTimerAction(mockNow, testContext); + }); + + it("updates the model with a timestamp and persists", function () { + mockNow.andReturn(12000); + action.perform(); + expect(testModel.timestamp).toEqual(12000); + expect(mockPersistence.persist).toHaveBeenCalled(); + }); + + it("applies only to timers without a target time", function () { + testModel.type = 'warp.timer'; + testModel.timestamp = 12000; + expect(StartTimerAction.appliesTo(testContext)).toBeFalsy(); + + testModel.type = 'warp.timer'; + testModel.timestamp = undefined; + expect(StartTimerAction.appliesTo(testContext)).toBeTruthy(); + + testModel.type = 'warp.clock'; + testModel.timestamp = 12000; + expect(StartTimerAction.appliesTo(testContext)).toBeFalsy(); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/clock/test/controllers/ClockControllerSpec.js b/platform/features/clock/test/controllers/ClockControllerSpec.js new file mode 100644 index 0000000000..640b180a40 --- /dev/null +++ b/platform/features/clock/test/controllers/ClockControllerSpec.js @@ -0,0 +1,83 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/controllers/ClockController"], + function (ClockController) { + "use strict"; + + // Wed, 03 Jun 2015 17:56:14 GMT + var TEST_TIMESTAMP = 1433354174000; + + describe("A clock view's controller", function () { + var mockScope, + mockTicker, + mockUnticker, + mockDomainObject, + controller; + + beforeEach(function () { + mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']); + mockTicker = jasmine.createSpyObj('ticker', ['listen']); + mockUnticker = jasmine.createSpy('unticker'); + + mockTicker.listen.andReturn(mockUnticker); + + controller = new ClockController(mockScope, mockTicker); + }); + + it("watches for clock format from the domain object model", function () { + expect(mockScope.$watch).toHaveBeenCalledWith( + "model.clockFormat", + jasmine.any(Function) + ); + }); + + it("subscribes to clock ticks", function () { + expect(mockTicker.listen) + .toHaveBeenCalledWith(jasmine.any(Function)); + }); + + it("unsubscribes to ticks when destroyed", function () { + // Make sure $destroy is being listened for... + expect(mockScope.$on.mostRecentCall.args[0]).toEqual('$destroy'); + expect(mockUnticker).not.toHaveBeenCalled(); + + // ...and makes sure that its listener unsubscribes from ticker + mockScope.$on.mostRecentCall.args[1](); + expect(mockUnticker).toHaveBeenCalled(); + }); + + 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" + ]); + + expect(controller.zone()).toEqual("UTC"); + expect(controller.text()).toEqual("2015-154 17: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" + ]); + + 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 () { + mockTicker.listen.mostRecentCall.args[0](TEST_TIMESTAMP); + expect(function () { + mockScope.$watch.mostRecentCall.args[1](undefined); + }).not.toThrow(); + }); + + }); + } +); diff --git a/platform/features/clock/test/controllers/RefreshingControllerSpec.js b/platform/features/clock/test/controllers/RefreshingControllerSpec.js new file mode 100644 index 0000000000..bf1cbceeb9 --- /dev/null +++ b/platform/features/clock/test/controllers/RefreshingControllerSpec.js @@ -0,0 +1,63 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/controllers/RefreshingController"], + function (RefreshingController) { + "use strict"; + + + + describe("The refreshing controller", function () { + var mockScope, + mockTicker, + mockUnticker, + controller; + + beforeEach(function () { + mockScope = jasmine.createSpyObj('$scope', ['$on']); + mockTicker = jasmine.createSpyObj('ticker', ['listen']); + mockUnticker = jasmine.createSpy('unticker'); + + mockTicker.listen.andReturn(mockUnticker); + + controller = new RefreshingController(mockScope, mockTicker); + }); + + it("refreshes the represented object on every tick", function () { + var mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getCapability' ] + ), + mockPersistence = jasmine.createSpyObj( + 'persistence', + [ 'persist', 'refresh' ] + ); + + mockDomainObject.getCapability.andCallFake(function (c) { + return (c === 'persistence') && mockPersistence; + }); + + mockScope.domainObject = mockDomainObject; + + mockTicker.listen.mostRecentCall.args[0](12321); + expect(mockPersistence.refresh).toHaveBeenCalled(); + expect(mockPersistence.persist).not.toHaveBeenCalled(); + }); + + it("subscribes to clock ticks", function () { + expect(mockTicker.listen) + .toHaveBeenCalledWith(jasmine.any(Function)); + }); + + it("unsubscribes to ticks when destroyed", function () { + // Make sure $destroy is being listened for... + expect(mockScope.$on.mostRecentCall.args[0]).toEqual('$destroy'); + expect(mockUnticker).not.toHaveBeenCalled(); + + // ...and makes sure that its listener unsubscribes from ticker + mockScope.$on.mostRecentCall.args[1](); + expect(mockUnticker).toHaveBeenCalled(); + }); + }); + } +); diff --git a/platform/features/clock/test/controllers/TimerControllerSpec.js b/platform/features/clock/test/controllers/TimerControllerSpec.js new file mode 100644 index 0000000000..9f80e21d9d --- /dev/null +++ b/platform/features/clock/test/controllers/TimerControllerSpec.js @@ -0,0 +1,178 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/controllers/TimerController"], + function (TimerController) { + "use strict"; + + // Wed, 03 Jun 2015 17:56:14 GMT + var TEST_TIMESTAMP = 1433354174000; + + describe("A timer view's controller", function () { + var mockScope, + mockWindow, + mockNow, + mockDomainObject, + mockActionCapability, + mockStart, + mockRestart, + testModel, + controller; + + function invokeWatch(expr, value) { + mockScope.$watch.calls.forEach(function (call) { + if (call.args[0] === expr) { + call.args[1](value); + } + }); + } + + beforeEach(function () { + mockScope = jasmine.createSpyObj( + '$scope', + ['$watch', '$on', '$apply'] + ); + mockWindow = jasmine.createSpyObj( + '$window', + ['requestAnimationFrame'] + ); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getCapability', 'useCapability', 'getModel' ] + ); + mockActionCapability = jasmine.createSpyObj( + 'action', + ['getActions'] + ); + mockStart = jasmine.createSpyObj( + 'start', + ['getMetadata', 'perform'] + ); + mockRestart = jasmine.createSpyObj( + 'restart', + ['getMetadata', 'perform'] + ); + mockNow = jasmine.createSpy('now'); + + mockDomainObject.getCapability.andCallFake(function (c) { + return (c === 'action') && mockActionCapability; + }); + mockDomainObject.getModel.andCallFake(function () { + return testModel; + }); + mockActionCapability.getActions.andCallFake(function (k) { + return [{ + 'warp.timer.start': mockStart, + 'warp.timer.restart': mockRestart + }[k]]; + }); + mockStart.getMetadata.andReturn({ glyph: "S", name: "Start" }); + mockRestart.getMetadata.andReturn({ glyph: "R", name: "Restart" }); + mockScope.domainObject = mockDomainObject; + + testModel = {}; + + controller = new TimerController(mockScope, mockWindow, mockNow); + }); + + it("watches for the domain object in view", function () { + expect(mockScope.$watch).toHaveBeenCalledWith( + "domainObject", + jasmine.any(Function) + ); + }); + + it("watches for domain object modifications", function () { + expect(mockScope.$watch).toHaveBeenCalledWith( + "model.modified", + jasmine.any(Function) + ); + }); + + it("updates on a timer", function () { + expect(mockWindow.requestAnimationFrame) + .toHaveBeenCalledWith(jasmine.any(Function)); + }); + + it("displays nothing when there is no target", function () { + // Notify that domain object is available via scope + invokeWatch('domainObject', mockDomainObject); + mockNow.andReturn(TEST_TIMESTAMP); + mockWindow.requestAnimationFrame.mostRecentCall.args[0](); + expect(controller.sign()).toEqual(""); + expect(controller.text()).toEqual(""); + }); + + it("formats time to display relative to target", function () { + testModel.timestamp = TEST_TIMESTAMP; + testModel.timerFormat = 'long'; + // Notify that domain object is available via scope + invokeWatch('domainObject', mockDomainObject); + + mockNow.andReturn(TEST_TIMESTAMP + 121000); + mockWindow.requestAnimationFrame.mostRecentCall.args[0](); + expect(controller.sign()).toEqual("+"); + expect(controller.text()).toEqual("0D 00:02:01"); + + mockNow.andReturn(TEST_TIMESTAMP - 121000); + mockWindow.requestAnimationFrame.mostRecentCall.args[0](); + expect(controller.sign()).toEqual("-"); + expect(controller.text()).toEqual("0D 00:02:01"); + + mockNow.andReturn(TEST_TIMESTAMP); + mockWindow.requestAnimationFrame.mostRecentCall.args[0](); + expect(controller.sign()).toEqual(""); + expect(controller.text()).toEqual("0D 00:00:00"); + }); + + it("shows glyph & name for the applicable start/restart action", function () { + invokeWatch('domainObject', mockDomainObject); + expect(controller.buttonGlyph()).toEqual("S"); + expect(controller.buttonText()).toEqual("Start"); + + testModel.timestamp = 12321; + invokeWatch('model.modified', 1); + expect(controller.buttonGlyph()).toEqual("R"); + expect(controller.buttonText()).toEqual("Restart"); + }); + + it("performs correct start/restart action on click", function () { + invokeWatch('domainObject', mockDomainObject); + expect(mockStart.perform).not.toHaveBeenCalled(); + controller.clickButton(); + expect(mockStart.perform).toHaveBeenCalled(); + + testModel.timestamp = 12321; + invokeWatch('model.modified', 1); + expect(mockRestart.perform).not.toHaveBeenCalled(); + controller.clickButton(); + expect(mockRestart.perform).toHaveBeenCalled(); + }); + + it("stops requesting animation frames when destroyed", function () { + var initialCount = mockWindow.requestAnimationFrame.calls.length; + + // First, check that normally new frames keep getting requested + mockWindow.requestAnimationFrame.mostRecentCall.args[0](); + expect(mockWindow.requestAnimationFrame.calls.length) + .toEqual(initialCount + 1); + mockWindow.requestAnimationFrame.mostRecentCall.args[0](); + expect(mockWindow.requestAnimationFrame.calls.length) + .toEqual(initialCount + 2); + + // Now, verify that it stops after $destroy + expect(mockScope.$on.mostRecentCall.args[0]) + .toEqual('$destroy'); + mockScope.$on.mostRecentCall.args[1](); + + // Frames should no longer get requested + mockWindow.requestAnimationFrame.mostRecentCall.args[0](); + expect(mockWindow.requestAnimationFrame.calls.length) + .toEqual(initialCount + 2); + mockWindow.requestAnimationFrame.mostRecentCall.args[0](); + expect(mockWindow.requestAnimationFrame.calls.length) + .toEqual(initialCount + 2); + }); + }); + } +); diff --git a/platform/features/clock/test/controllers/TimerFormatterSpec.js b/platform/features/clock/test/controllers/TimerFormatterSpec.js new file mode 100644 index 0000000000..f0f4d954c3 --- /dev/null +++ b/platform/features/clock/test/controllers/TimerFormatterSpec.js @@ -0,0 +1,96 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/controllers/TimerFormatter"], + function (TimerFormatter) { + "use strict"; + + var MS_IN_SEC = 1000, + MS_IN_MIN = MS_IN_SEC * 60, + MS_IN_HR = MS_IN_MIN * 60, + MS_IN_DAY = MS_IN_HR * 24; + + describe("The timer value formatter", function () { + var formatter = new TimerFormatter(); + + function sum(a, b) { + return a + b; + } + + function toDuration(days, hours, mins, secs) { + return [ + days * MS_IN_DAY, + hours * MS_IN_HR, + mins * MS_IN_MIN, + secs * MS_IN_SEC + ].reduce(sum, 0); + } + + function twoDigits(n) { + return n < 10 ? ('0' + n) : n; + } + + it("formats short-form values (no days)", function () { + expect(formatter.short(toDuration(0, 123, 2, 3) + 123)) + .toEqual("123:02:03"); + }); + + it("formats negative short-form values (no days)", function () { + expect(formatter.short(-toDuration(0, 123, 2, 3) + 123)) + .toEqual("123:02:03"); + }); + + it("formats long-form values (with days)", function () { + expect(formatter.long(toDuration(0, 123, 2, 3) + 123)) + .toEqual("5D 03:02:03"); + }); + + it("formats negative long-form values (no days)", function () { + expect(formatter.long(-toDuration(0, 123, 2, 3) + 123)) + .toEqual("5D 03:02:03"); + }); + + it("rounds seconds down for positive durations", function () { + expect(formatter.short(MS_IN_SEC + 600)) + .toEqual("00:00:01"); + }); + + it("rounds seconds up for negative durations", function () { + expect(formatter.short(-MS_IN_SEC - 600)) + .toEqual("00:00:02"); + }); + + it("short-formats correctly around negative time borders", function () { + expect(formatter.short(-1)).toEqual("00:00:01"); + expect(formatter.short(-1000)).toEqual("00:00:01"); + expect(formatter.short(-1001)).toEqual("00:00:02"); + expect(formatter.short(-2000)).toEqual("00:00:02"); + expect(formatter.short(-59001)).toEqual("00:01:00"); + expect(formatter.short(-60000)).toEqual("00:01:00"); + + expect(formatter.short(-MS_IN_HR + 999)).toEqual("01:00:00"); + expect(formatter.short(-MS_IN_HR)).toEqual("01:00:00"); + }); + + it("differentiates between values around zero", function () { + // These are more than 1000 ms apart so should not appear + // as the same second + expect(formatter.short(-999)) + .not.toEqual(formatter.short(999)); + }); + + it("handles negative days", function () { + expect(formatter.long(-10 * MS_IN_DAY)) + .toEqual("10D 00:00:00"); + expect(formatter.long(-10 * MS_IN_DAY + 100)) + .toEqual("10D 00:00:00"); + expect(formatter.long(-10 * MS_IN_DAY + 999)) + .toEqual("10D 00:00:00"); + + expect(formatter.short(-10 * MS_IN_DAY + 100)) + .toEqual("240:00:00"); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/clock/test/indicators/ClockIndicatorSpec.js b/platform/features/clock/test/indicators/ClockIndicatorSpec.js new file mode 100644 index 0000000000..30c8fe3e26 --- /dev/null +++ b/platform/features/clock/test/indicators/ClockIndicatorSpec.js @@ -0,0 +1,40 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/indicators/ClockIndicator"], + function (ClockIndicator) { + "use strict"; + + // Wed, 03 Jun 2015 17:56:14 GMT + var TEST_TIMESTAMP = 1433354174000, + TEST_FORMAT = "YYYY-DDD HH:mm:ss"; + + describe("The clock indicator", function () { + var mockTicker, + mockUnticker, + indicator; + + beforeEach(function () { + mockTicker = jasmine.createSpyObj('ticker', ['listen']); + mockUnticker = jasmine.createSpy('unticker'); + + mockTicker.listen.andReturn(mockUnticker); + + indicator = new ClockIndicator(mockTicker, TEST_FORMAT); + }); + + it("displays the current time", function () { + mockTicker.listen.mostRecentCall.args[0](TEST_TIMESTAMP); + expect(indicator.getText()).toEqual("2015-154 17:56:14 UTC"); + }); + + it("implements the Indicator interface", function () { + expect(indicator.getGlyph()).toEqual(jasmine.any(String)); + expect(indicator.getGlyphClass()).toEqual(jasmine.any(String)); + expect(indicator.getText()).toEqual(jasmine.any(String)); + expect(indicator.getDescription()).toEqual(jasmine.any(String)); + }); + + }); + } +); diff --git a/platform/features/clock/test/services/TickerServiceSpec.js b/platform/features/clock/test/services/TickerServiceSpec.js new file mode 100644 index 0000000000..d4456ee756 --- /dev/null +++ b/platform/features/clock/test/services/TickerServiceSpec.js @@ -0,0 +1,43 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/services/TickerService"], + function (TickerService) { + "use strict"; + + var TEST_TIMESTAMP = 1433354174000; + + describe("The ticker service", function () { + var mockTimeout, + mockNow, + mockCallback, + tickerService; + + beforeEach(function () { + mockTimeout = jasmine.createSpy('$timeout'); + mockNow = jasmine.createSpy('now'); + mockCallback = jasmine.createSpy('callback'); + + mockNow.andReturn(TEST_TIMESTAMP); + + tickerService = new TickerService(mockTimeout, mockNow); + }); + + it("notifies listeners of clock ticks", function () { + tickerService.listen(mockCallback); + mockNow.andReturn(TEST_TIMESTAMP + 12321); + mockTimeout.mostRecentCall.args[0](); + expect(mockCallback) + .toHaveBeenCalledWith(TEST_TIMESTAMP + 12321); + }); + + it("allows listeners to unregister", function () { + tickerService.listen(mockCallback)(); // Unregister immediately + mockNow.andReturn(TEST_TIMESTAMP + 12321); + mockTimeout.mostRecentCall.args[0](); + expect(mockCallback).not + .toHaveBeenCalledWith(TEST_TIMESTAMP + 12321); + }); + }); + } +); diff --git a/platform/features/clock/test/suite.json b/platform/features/clock/test/suite.json new file mode 100644 index 0000000000..be10ff57f8 --- /dev/null +++ b/platform/features/clock/test/suite.json @@ -0,0 +1,11 @@ +[ + "actions/AbstractStartTimerAction", + "actions/RestartTimerAction", + "actions/StartTimerAction", + "controllers/ClockController", + "controllers/RefreshingController", + "controllers/TimerController", + "controllers/TimerFormatter", + "indicators/ClockIndicator", + "services/TickerService" +] diff --git a/platform/features/timeline/README.md b/platform/features/timeline/README.md new file mode 100644 index 0000000000..5dd9561c2c --- /dev/null +++ b/platform/features/timeline/README.md @@ -0,0 +1,70 @@ +This bundle provides the Timeline domain object type, as well +as other associated domain object types and relevant views. + +# Implementation notes + +## Model Properties + +The properties below record properties relevant to using and +understanding timelines based on their JSON representation. +Additional common properties, such as `modified` +or `persisted` timestamps, may also be present. + +### Timeline Model + +A timeline's model looks like: + +``` +{ + "type": "warp.timeline", + "start": { + "timestamp": (milliseconds since epoch), + "epoch": (currently, always "SET") + }, + "capacity": (optional; battery capacity in watt-hours) + "composition": (array of identifiers for contained objects) +} +``` + +The identifiers in a timeline's `composition` field should refer to +other Timeline objects, or to Activity objects. + +### Activity Model + +An activity's model looks like: + +``` +{ + "type": "warp.activity", + "start": { + "timestamp": (milliseconds since epoch), + "epoch": (currently, always "SET") + }, + "duration": { + "timestamp": (duration of this activity, in milliseconds) + "epoch": "SET" (this is ignored) + }, + "relationships": { + "modes": (array of applicable Activity Mode ids) + }, + "link": (optional; URL linking to associated external resource) + "composition": (array of identifiers for contained objects) +} +``` + +The identifiers in a timeline's `composition` field should only refer to +other Activity objects. + +### Activity Mode Model + +An activity mode's model looks like: + +``` +{ + "type": "warp.mode", + "resources": { + "comms": (communications utilization, in Kbps) + "power": (power utilization, in watts) + } +} +``` diff --git a/platform/features/timeline/bundle.json b/platform/features/timeline/bundle.json new file mode 100644 index 0000000000..1132ed1840 --- /dev/null +++ b/platform/features/timeline/bundle.json @@ -0,0 +1,372 @@ +{ + "name": "WARP Timeline", + "description": "Resources, templates, CSS, and code for Timelines.", + "resources": "res", + "extensions": { + "constants": [ + { + "key": "TIMELINE_MINIMUM_DURATION", + "description": "The minimum duration to display in a timeline view (one hour.)", + "value": 3600000 + }, + { + "key": "TIMELINE_MAXIMUM_OFFSCREEN", + "description": "Maximum amount, in pixels, of a Gantt bar which may go off screen.", + "value": 1000 + }, + { + "key": "TIMELINE_ZOOM_CONFIGURATION", + "description": "Describes major tick sizes in milliseconds, and width in pixels.", + "value": { + "levels": [ + 1000, + 2000, + 5000, + + 10000, + 20000, + 30000, + 60000, + + 120000, + 300000, + 600000, + + 1200000, + 1800000, + 3600000, + 7200000, + + 14400000, + 28800000, + 43200000, + 86400000 + ], + "width": 200 + } + } + ], + "types": [ + { + "key": "warp.timeline", + "name": "Timeline", + "glyph": "S", + "description": "A container for arranging Timelines and Activities in time.", + "features": [ "creation" ], + "contains": [ "warp.timeline", "warp.activity" ], + "properties": [ + { + "name": "Start date/time", + "control": "warp.datetime", + "required": true, + "property": [ "start" ], + "options": [ "SET" ] + }, + { + "name": "Battery capacity (Watt-hours)", + "control": "textfield", + "required": false, + "conversion": "number", + "property": [ "capacity" ], + "pattern": "^-?\\d+(\\.\\d*)?$" + } + ], + "model": { "composition": [] } + }, + { + "key": "warp.activity", + "name": "Activity", + "glyph": "a", + "features": [ "creation" ], + "contains": [ "warp.activity" ], + "description": "An action that takes place in time. You can define a start time and duration. Activities can be nested within other Activities, or within Timelines.", + "properties": [ + { + "name": "Start date/time", + "control": "warp.datetime", + "required": true, + "property": [ "start" ], + "options": [ "SET" ] + }, + { + "name": "Duration", + "control": "warp.duration", + "required": true, + "property": [ "duration" ] + } + ], + "model": { "composition": [], "relationships": { "modes": [] } } + }, + { + "key": "warp.mode", + "name": "Activity Mode", + "glyph": "A", + "features": [ "creation" ], + "description": "Define resource utilizations over time, then apply to an Activity.", + "model": { "resources": { "comms": 0, "power": 0 } }, + "properties": [ + { + "name": "Comms (Kbps)", + "control": "textfield", + "conversion": "number", + "pattern": "^-?\\d+(\\.\\d*)?$", + "property": [ "resources", "comms" ] + }, + { + "name": "Power (watts)", + "control": "textfield", + "conversion": "number", + "pattern": "^-?\\d+(\\.\\d*)?$", + "property": [ "resources", "power" ] + } + ] + } + ], + "views": [ + { + "key": "warp.values", + "name": "Values", + "glyph": "A", + "templateUrl": "templates/values.html", + "type": "warp.mode", + "uses": [ "cost" ], + "editable": false + }, + { + "key": "warp.timeline", + "name": "Timeline", + "glyph": "S", + "type": "warp.timeline", + "description": "A timeline view of Timelines and Activities.", + "templateUrl": "templates/timeline.html", + "toolbar": { + "sections": [ + { + "items": [ + { + "method": "add", + "glyph": "+", + "control": "menu-button", + "text": "Add", + "options": [ + { + "name": "Timeline", + "glyph": "S", + "key": "warp.timeline" + }, + { + "name": "Activity", + "glyph": "a", + "key": "warp.activity" + } + ] + } + ] + }, + { + "items": [ + { + "glyph": "\u00E9", + "description": "Graph resource utilization", + "control": "button", + "method": "toggleGraph" + }, + { + "glyph": "A", + "control": "dialog-button", + "description": "Apply Activity Modes...", + "title": "Apply Activity Modes", + "dialog": { + "control": "selector", + "name": "Modes", + "type": "warp.mode" + }, + "property": "modes" + }, + { + "glyph": "\u00E8", + "description": "Edit Activity Link", + "title": "Activity Link", + "control": "dialog-button", + "dialog": { + "control": "textfield", + "name": "Link", + "pattern": "^(ftp|https?)\\:\\/\\/\\w+(\\.\\w+)*(\\:\\d+)?(\\/\\S*)*$" + }, + "property": "link" + }, + { + "glyph": "\u0047", + "description": "Edit Properties...", + "control": "button", + "method": "properties" + } + ] + }, + { + "items": [ + { + "method": "remove", + "description": "Remove item", + "control": "button", + "glyph": "Z" + } + ] + } + ] + } + } + ], + "representations": [ + { + "key": "warp.gantt", + "templateUrl": "templates/activity-gantt.html", + "uses": [ "timespan", "type" ] + } + ], + "templates": [ + { + "key": "timeline-tabular-swimlane-cols-tree", + "priority": "mandatory", + "templateUrl": "templates/tabular-swimlane-cols-tree.html" + }, + { + "key": "timeline-tabular-swimlane-cols-data", + "priority": "mandatory", + "templateUrl": "templates/tabular-swimlane-cols-data.html" + }, + { + "key": "timeline-resource-graphs", + "priority": "mandatory", + "templateUrl": "templates/resource-graphs.html" + }, + { + "key": "timeline-resource-graph-labels", + "priority": "mandatory", + "templateUrl": "templates/resource-graph-labels.html" + }, + { + "key": "timeline-legend-item", + "priority": "mandatory", + "templateUrl": "templates/legend-item.html" + }, + { + "key": "timeline-ticks", + "priority": "mandatory", + "templateUrl": "templates/ticks.html" + } + ], + "controls": [ + { + "key": "warp.datetime", + "templateUrl": "templates/controls/datetime.html" + }, + { + "key": "warp.duration", + "templateUrl": "templates/controls/datetime.html" + } + ], + "controllers": [ + { + "key": "TimelineController", + "implementation": "controllers/TimelineController.js", + "depends": [ "$scope", "$q", "warp.objectLoader", "TIMELINE_MINIMUM_DURATION" ] + }, + { + "key": "TimelineGraphController", + "implementation": "controllers/TimelineGraphController.js", + "depends": [ "$scope", "warp.resources[]" ] + }, + { + "key": "WARPDateTimeController", + "implementation": "controllers/WARPDateTimeController.js", + "depends": [ "$scope" ] + }, + { + "key": "TimelineZoomController", + "implementation": "controllers/TimelineZoomController.js", + "depends": [ "$scope", "TIMELINE_ZOOM_CONFIGURATION" ] + }, + { + "key": "TimelineTickController", + "implementation": "controllers/TimelineTickController.js" + }, + { + "key": "TimelineTableController", + "implementation": "controllers/TimelineTableController.js" + }, + { + "key": "TimelineGanttController", + "implementation": "controllers/TimelineGanttController.js", + "depends": [ "TIMELINE_MAXIMUM_OFFSCREEN" ] + }, + { + "key": "ActivityModeValuesController", + "implementation": "controllers/ActivityModeValuesController.js", + "depends": [ "warp.resources[]" ] + } + ], + "capabilities": [ + { + "key": "timespan", + "implementation": "capabilities/ActivityTimespanCapability.js", + "depends": [ "$q" ] + }, + { + "key": "timespan", + "implementation": "capabilities/TimelineTimespanCapability.js", + "depends": [ "$q" ] + }, + { + "key": "utilization", + "implementation": "capabilities/UtilizationCapability.js", + "depends": [ "$q" ] + }, + { + "key": "graph", + "implementation": "capabilities/GraphCapability.js", + "depends": [ "$q" ] + }, + { + "key": "cost", + "implementation": "capabilities/CostCapability.js" + } + ], + "directives": [ + { + "key": "warpSwimlaneDrop", + "implementation": "directives/WARPSwimlaneDrop.js", + "depends": [ "dndService" ] + }, + { + "key": "warpSwimlaneDrag", + "implementation": "directives/WARPSwimlaneDrag.js", + "depends": [ "dndService" ] + } + ], + "services": [ + { + "key": "warp.objectLoader", + "implementation": "services/ObjectLoader.js", + "depends": [ "$q" ] + } + ], + "warp.resources": [ + { + "key": "power", + "name": "Power", + "units": "watts" + }, + { + "key": "comms", + "name": "Comms", + "units": "Kbps" + }, + { + "key": "battery", + "name": "Battery State-of-Charge", + "units": "%" + } + ] + } +} diff --git a/platform/features/timeline/res/templates/activity-gantt.html b/platform/features/timeline/res/templates/activity-gantt.html new file mode 100644 index 0000000000..3edc77e567 --- /dev/null +++ b/platform/features/timeline/res/templates/activity-gantt.html @@ -0,0 +1,18 @@ +
+ +
+ + {{type.getGlyph()}} + + + {{model.name}} + +
+ +
diff --git a/platform/features/timeline/res/templates/controls/datetime.html b/platform/features/timeline/res/templates/controls/datetime.html new file mode 100644 index 0000000000..65386ed3ef --- /dev/null +++ b/platform/features/timeline/res/templates/controls/datetime.html @@ -0,0 +1,62 @@ +
+ +
+ Days + Hours + Minutes + Seconds + Time System +
+ + +
+ + + + + + + + + + + + + + SET + +
+
+ + +
\ No newline at end of file diff --git a/platform/features/timeline/res/templates/legend-item.html b/platform/features/timeline/res/templates/legend-item.html new file mode 100644 index 0000000000..9908e701ba --- /dev/null +++ b/platform/features/timeline/res/templates/legend-item.html @@ -0,0 +1,13 @@ + + + + + + {{ngModel.path}} + {{ngModel.domainObject.getModel().name}} + + \ No newline at end of file diff --git a/platform/features/timeline/res/templates/resource-graph-labels.html b/platform/features/timeline/res/templates/resource-graph-labels.html new file mode 100644 index 0000000000..918643f691 --- /dev/null +++ b/platform/features/timeline/res/templates/resource-graph-labels.html @@ -0,0 +1,16 @@ +
+ {{parameters.title}} +
+
+
+
+ {{parameters.high}} +
+
+ {{parameters.middle}} +
+
+ {{parameters.low}} +
+
+
\ No newline at end of file diff --git a/platform/features/timeline/res/templates/resource-graphs.html b/platform/features/timeline/res/templates/resource-graphs.html new file mode 100644 index 0000000000..317d4976ac --- /dev/null +++ b/platform/features/timeline/res/templates/resource-graphs.html @@ -0,0 +1,13 @@ + +
+
+ +
+
+ + +
+
+
\ No newline at end of file diff --git a/platform/features/timeline/res/templates/tabular-swimlane-cols-data.html b/platform/features/timeline/res/templates/tabular-swimlane-cols-data.html new file mode 100644 index 0000000000..63c618974f --- /dev/null +++ b/platform/features/timeline/res/templates/tabular-swimlane-cols-data.html @@ -0,0 +1,16 @@ +
+
+ {{tabularVal.niceTime(ngModel.timespan().getStart())}} + {{tabularVal.niceTime(ngModel.timespan().getEnd())}} + {{tabularVal.niceTime(ngModel.timespan().getDuration())}} + +
+
\ No newline at end of file diff --git a/platform/features/timeline/res/templates/tabular-swimlane-cols-tree.html b/platform/features/timeline/res/templates/tabular-swimlane-cols-tree.html new file mode 100644 index 0000000000..de071ed4f8 --- /dev/null +++ b/platform/features/timeline/res/templates/tabular-swimlane-cols-tree.html @@ -0,0 +1,36 @@ +
+
+ + + é + + + + + è + + + + + + +
+
\ No newline at end of file diff --git a/platform/features/timeline/res/templates/ticks.html b/platform/features/timeline/res/templates/ticks.html new file mode 100644 index 0000000000..6726c831b8 --- /dev/null +++ b/platform/features/timeline/res/templates/ticks.html @@ -0,0 +1,18 @@ + diff --git a/platform/features/timeline/res/templates/timeline.html b/platform/features/timeline/res/templates/timeline.html new file mode 100644 index 0000000000..f0e29c577f --- /dev/null +++ b/platform/features/timeline/res/templates/timeline.html @@ -0,0 +1,197 @@ +
+ + + + + +
+ +
+ + +
+ + +
+ +
+ + +
+
+ +
+ + +
+
+
+
+ + + + + +
+
{{ngModel.title}}Resource Graph Legend
+
+ + +
+
+
+ + + + + + + + + + + +
+ + +
+ + +
+ + +
+
+
+ + + + + + + + + +
+
+
+ + +
+ + + + + +
+ +
+ + +
+ + +
+ +
+ +
+
+
+
+
+
+
+
+
diff --git a/platform/features/timeline/res/templates/values.html b/platform/features/timeline/res/templates/values.html new file mode 100644 index 0000000000..01dc980444 --- /dev/null +++ b/platform/features/timeline/res/templates/values.html @@ -0,0 +1,6 @@ +
    +
  • + {{controller.metadata(key).name}} + {{value}} {{controller.metadata(key).units}} +
  • +
\ No newline at end of file diff --git a/platform/features/timeline/src/TimelineConstants.js b/platform/features/timeline/src/TimelineConstants.js new file mode 100644 index 0000000000..c68f9c2240 --- /dev/null +++ b/platform/features/timeline/src/TimelineConstants.js @@ -0,0 +1,11 @@ +/*global define*/ + +/** + * Defines constant values for use in timeline view. + */ +define({ + // Pixel width of start/end handles + HANDLE_WIDTH: 32, + // Pixel tolerance for snapping behavior + SNAP_WIDTH: 16 +}); \ No newline at end of file diff --git a/platform/features/timeline/src/TimelineFormatter.js b/platform/features/timeline/src/TimelineFormatter.js new file mode 100644 index 0000000000..ccbf63b8ca --- /dev/null +++ b/platform/features/timeline/src/TimelineFormatter.js @@ -0,0 +1,57 @@ +/*global define*/ + +define( + [], + function () { + 'use strict'; + + // Conversion factors from time units to milliseconds + var SECONDS = 1000, + MINUTES = SECONDS * 60, + HOURS = MINUTES * 60, + DAYS = HOURS * 24; + + /** + * Formatters for durations shown in a timeline view. + * @constructor + */ + function TimelineFormatter() { + + // Format a numeric value to a string with some number of digits + function formatValue(value, digits) { + var v = value.toString(10); + // Pad with zeroes + while (v.length < digits) { + v = "0" + v; + } + return v; + } + + // Format duration to string + function formatDuration(duration) { + var days = Math.floor(duration / DAYS), + hours = Math.floor(duration / HOURS) % 24, + minutes = Math.floor(duration / MINUTES) % 60, + seconds = Math.floor(duration / SECONDS) % 60, + millis = Math.floor(duration) % 1000; + + return formatValue(days, 3) + " " + + formatValue(hours, 2) + ":" + + formatValue(minutes, 2) + ":" + + formatValue(seconds, 2) + "." + + formatValue(millis, 3); + } + + return { + /** + * Format the provided duration. + * @param {number} duration duration, in milliseconds + * @returns {string} displayable representation of duration + */ + format: formatDuration + }; + } + + return TimelineFormatter; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/ActivityTimespan.js b/platform/features/timeline/src/capabilities/ActivityTimespan.js new file mode 100644 index 0000000000..69b0039b2d --- /dev/null +++ b/platform/features/timeline/src/capabilities/ActivityTimespan.js @@ -0,0 +1,100 @@ +/*global define*/ + +define( + [], + function () { + 'use strict'; + + /** + * Describes the time span of an activity object. + * @param model the activity's object model + */ + function ActivityTimespan(model, mutation) { + // Get the start time for this timeline + function getStart() { + return model.start.timestamp; + } + + // Get the end time for this timeline + function getEnd() { + return model.start.timestamp + model.duration.timestamp; + } + + // Get the duration of this timeline + function getDuration() { + return model.duration.timestamp; + } + + // Get the epoch used by this timeline + function getEpoch() { + return model.start.epoch; // Surface elapsed time + } + + // Set the start time associated with this object + function setStart(value) { + var end = getEnd(); + mutation.mutate(function (model) { + model.start.timestamp = Math.max(value, 0); + // Update duration to keep end time + model.duration.timestamp = Math.max(end - value, 0); + }, model.modified); + } + + // Set the duration associated with this object + function setDuration(value) { + mutation.mutate(function (model) { + model.duration.timestamp = Math.max(value, 0); + }, model.modified); + } + + // Set the end time associated with this object + function setEnd(value) { + var start = getStart(); + mutation.mutate(function (model) { + model.duration.timestamp = Math.max(value - start, 0); + }, model.modified); + } + + return { + /** + * Get the start time, in milliseconds relative to the epoch. + * @returns {number} the start time + */ + getStart: getStart, + /** + * Get the duration, in milliseconds. + * @returns {number} the duration + */ + getDuration: getDuration, + /** + * Get the end time, in milliseconds relative to the epoch. + * @returns {number} the end time + */ + getEnd: getEnd, + /** + * Set the start time, in milliseconds relative to the epoch. + * @param {number} the new value + */ + setStart: setStart, + /** + * Set the duration, in milliseconds. + * @param {number} the new value + */ + setDuration: setDuration, + /** + * Set the end time, in milliseconds relative to the epoch. + * @param {number} the new value + */ + setEnd: setEnd, + /** + * Get a string identifying the reference epoch used for + * start and end times. + * @returns {string} the epoch + */ + getEpoch: getEpoch + }; + } + + return ActivityTimespan; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/ActivityTimespanCapability.js b/platform/features/timeline/src/capabilities/ActivityTimespanCapability.js new file mode 100644 index 0000000000..f4927a10dd --- /dev/null +++ b/platform/features/timeline/src/capabilities/ActivityTimespanCapability.js @@ -0,0 +1,42 @@ +/*global define*/ + +define( + ['./ActivityTimespan'], + function (ActivityTimespan) { + 'use strict'; + + /** + * Implements the `warp.timespan` capability for Activity objects. + * + * @constructor + * @param $q Angular's $q, for promise-handling + * @param {DomainObject} domainObject the Activity + */ + function ActivityTimespanCapability($q, domainObject) { + // Promise time span + function promiseTimeSpan() { + return $q.when(new ActivityTimespan( + domainObject.getModel(), + domainObject.getCapability('mutation') + )); + } + + return { + /** + * Get the time span (start, end, duration) of this activity. + * @returns {Promise.} the time span of + * this activity + */ + invoke: promiseTimeSpan + }; + } + + // Only applies to timeline objects + ActivityTimespanCapability.appliesTo = function (model) { + return model && (model.type === 'warp.activity'); + }; + + return ActivityTimespanCapability; + + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/ActivityUtilization.js b/platform/features/timeline/src/capabilities/ActivityUtilization.js new file mode 100644 index 0000000000..bf77cb1ff1 --- /dev/null +++ b/platform/features/timeline/src/capabilities/ActivityUtilization.js @@ -0,0 +1,31 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Provides data to populate resource graphs associated + * with activities in a timeline view. + * This is a placeholder until WTD-918. + * @constructor + */ + function ActivityUtilization() { + return { + getPointCount: function () { + return 0; + }, + getDomainValue: function (index) { + return 0; + }, + getRangeValue: function (index) { + return 0; + } + }; + } + + return ActivityUtilization; + } + +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/CostCapability.js b/platform/features/timeline/src/capabilities/CostCapability.js new file mode 100644 index 0000000000..56d2dafaa7 --- /dev/null +++ b/platform/features/timeline/src/capabilities/CostCapability.js @@ -0,0 +1,56 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Exposes costs associated with a subsystem mode. + * @constructor + */ + function CostCapability(domainObject) { + var model = domainObject.getModel(); + + return { + /** + * Get a list of resource types which have associated + * costs for this object. Returned values are machine-readable + * keys, and should be paired with external metadata for + * presentation (see category of extension `warp.resources`). + * @returns {string[]} resource types + */ + resources: function () { + return Object.keys(model.resources || {}).sort(); + }, + /** + * Get the cost associated with a resource of an identified + * type (typically, one of the types reported from a + * `resources` call.) + * @param {string} key the resource type + * @returns {number} the associated cost + */ + cost: function (key) { + return (model.resources || {})[key] || 0; + }, + /** + * Get an object containing key-value pairs describing + * resource utilization as described by this object. + * Keys are resource types; values are levels of associated + * resource utilization. + * @returns {object} resource utilizations + */ + invoke: function () { + return model.resources || {}; + } + }; + } + + // Only applies to subsystem modes. + CostCapability.appliesTo = function (model) { + return (model || {}).type === 'warp.mode'; + }; + + return CostCapability; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/CumulativeGraph.js b/platform/features/timeline/src/capabilities/CumulativeGraph.js new file mode 100644 index 0000000000..b6694e3834 --- /dev/null +++ b/platform/features/timeline/src/capabilities/CumulativeGraph.js @@ -0,0 +1,134 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Provide points for a cumulative resource summary graph, using + * a provided instantaneous resource summary graph. + * + * @param {ResourceGraph} graph the resource graph + * @param {number} minimum the minimum allowable level + * @param {number} maximum the maximum allowable level + * @param {number} initial the initial state of the resource + * @param {number} rate the rate at which one unit of instantaneous + * utilization changes the available level in one unit + * of domain values (that is, per millisecond) + * @constructor + */ + function CumulativeGraph(graph, minimum, maximum, initial, rate) { + var values; + + // Calculate the domain value at which a line starting at + // (domain, range) and proceeding with the specified slope + // will have the specified range value. + function intercept(domain, range, slope, value) { + // value = slope * (intercept - domain) + range + // value - range = slope * ... + // intercept - domain = (value - range) / slope + // intercept = domain + (value - range) / slope + return domain + (value - range) / slope; + } + + // Initialize the data values + function initializeValues() { + var values = [], + slope = 0, + previous = 0, + i; + + // Add a point (or points, if needed) reaching to the provided + // domain and/or range value + function addPoint(domain, range) { + var previous = values[values.length - 1], + delta = domain - previous.domain, // time delta + change = delta * slope * rate, // change + next = previous.range + change; + + // Crop to minimum boundary... + if (next < minimum) { + values.push({ + domain: intercept( + previous.domain, + previous.range, + slope * rate, + minimum + ), + range: minimum + }); + next = minimum; + } + + // ...and maximum boundary + if (next > maximum) { + values.push({ + domain: intercept( + previous.domain, + previous.range, + slope * rate, + maximum + ), + range: maximum + }); + next = maximum; + } + + // Add the new data value + if (delta > 0) { + values.push({ domain: domain, range: next }); + } + + slope = range; + } + + values.push({ domain: 0, range: initial }); + + for (i = 0; i < graph.getPointCount(); i += 1) { + addPoint(graph.getDomainValue(i), graph.getRangeValue(i)); + } + + return values; + } + + function convertToPercent(point) { + point.range = 100 * + (point.range - minimum) / (maximum - minimum); + } + + // Calculate cumulative values... + values = initializeValues(); + + // ...and convert to percentages. + values.forEach(convertToPercent); + + return { + /** + * Get the total number of points in this graph. + * @returns {number} the total number of points + */ + getPointCount: function () { + return values.length; + }, + /** + * Get the domain value (timestamp) for a point in this graph. + * @returns {number} the domain value + */ + getDomainValue: function (index) { + return values[index].domain; + }, + /** + * Get the range value (utilization level) for a point in + * this graph. + * @returns {number} the range value + */ + getRangeValue: function (index) { + return values[index].range; + } + }; + } + + return CumulativeGraph; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/GraphCapability.js b/platform/features/timeline/src/capabilities/GraphCapability.js new file mode 100644 index 0000000000..fb59272678 --- /dev/null +++ b/platform/features/timeline/src/capabilities/GraphCapability.js @@ -0,0 +1,78 @@ +/*global define*/ + +define( + ['./ResourceGraph', './CumulativeGraph'], + function (ResourceGraph, CumulativeGraph) { + 'use strict'; + + /** + * Implements the `graph` capability for Timeline and + * Activity objects. + * + * @constructor + * @param {DomainObject} domainObject the Timeline or Activity + */ + function GraphCapability($q, domainObject) { + + + // Build graphs for this group of utilizations + function buildGraphs(utilizations) { + var utilizationMap = {}, + result = {}; + + // Bucket utilizations by type + utilizations.forEach(function (u) { + var k = u.key; + utilizationMap[k] = utilizationMap[k] || []; + utilizationMap[k].push(u); + }); + + // ...then convert to graphs + Object.keys(utilizationMap).forEach(function (k) { + result[k] = new ResourceGraph(utilizationMap[k]); + }); + + // Add battery state of charge + if (domainObject.getModel().type === 'warp.timeline' && + result.power && + domainObject.getModel().capacity > 0) { + + result.battery = new CumulativeGraph( + result.power, + 0, + domainObject.getModel().capacity, // Watts + domainObject.getModel().capacity, + 1 / 3600000 // millis-to-hour (since units are watt-hours) + ); + } + + return result; + } + + return { + /** + * Get resource graphs associated with this object. + * This is given as a promise for key-value pairs, + * where keys are resource types and values are graph + * objects. + * @returns {Promise} a promise for resource graphs + */ + invoke: function () { + return $q.when( + domainObject.useCapability('utilization') || [] + ).then(buildGraphs); + } + }; + } + + // Only applies to timeline objects + GraphCapability.appliesTo = function (model) { + return model && + ((model.type === 'warp.timeline') || + (model.type === 'warp.activity')); + }; + + return GraphCapability; + + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/ResourceGraph.js b/platform/features/timeline/src/capabilities/ResourceGraph.js new file mode 100644 index 0000000000..7fd135ebbf --- /dev/null +++ b/platform/features/timeline/src/capabilities/ResourceGraph.js @@ -0,0 +1,128 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + // Utility function to copy an array, sorted by a specific field + function sort(array, field) { + return array.slice().sort(function (a, b) { + return a[field] - b[field]; + }); + } + + /** + * Provides data to populate resource graphs associated + * with timelines and activities. + * @param {Array} utilizations resource utilizations + * @constructor + */ + function ResourceGraph(utilizations) { + // Overview of algorithm here: + // * Goal: Have a list of time/value pairs which represents + // points along a stepped chart of resource utilization. + // Each change (stepping up or down) should have two points, + // at the bottom and top of the step respectively. + // * Step 1: Prepare two lists of utilizations sorted by start + // and end times. The "starts" will become step-ups, the + // "ends" will become step-downs. + // * Step 2: Initialize empty arrays for results, and a variable + // for the current utilization level. + // * Step 3: While there are still start or end times to add... + // * Step 3a: Determine whether the next change should be a + // step-up (start) or step-down (end) based on which of the + // next start/end times comes next (note that starts and ends + // are both sorted, so we look at the head of the array.) + // * Step 3b: Pull the next start or end (per previous decision) + // and convert it to a time-delta pair, negating if it's an + // end time (to step down or "un-step") + // * Step 3c: Add a point at the new time and the current + // running total (first point in the step, before the change) + // then increment the running total and add a new point + // (second point in the step, after the change) + // * Step 4: Filter out unnecessary points (if two activities + // run up against each other, there will be a zero-duration + // spike if we don't filter out the extra points from their + // start/end times.) + // + var starts = sort(utilizations, "start"), + ends = sort(utilizations, "end"), + values = [], + running = 0; + + // If there are sequences of points with the same timestamp, + // allow only the first and last. + function filterPoint(value, index, values) { + // Allow the first or last point as a base case; aside from + // that, allow only points that have different timestamps + // from their predecessor or successor. + return (index === 0) || (index === values.length - 1) || + (value.domain !== values[index - 1].domain) || + (value.domain !== values[index + 1].domain); + } + + // Add a step up or down (Step 3c above) + function addDelta(time, delta) { + values.push({ domain: time, range: running }); + running += delta; + values.push({ domain: time, range: running }); + } + + // Add a start time (Step 3b above) + function addStart() { + var next = starts.shift(); + addDelta(next.start, next.value); + } + + // Add an end time (Step 3b above) + function addEnd() { + var next = ends.shift(); + addDelta(next.end, -next.value); + } + + // Decide whether next step should correspond to a start or + // an end. (Step 3c above) + function pickStart() { + return ends.length < 1 || + (starts.length > 0 && starts[0].start <= ends[0].end); + } + + // Build up start/end arrays (step 3 above) + while (starts.length > 0 || ends.length > 0) { + (pickStart() ? addStart : addEnd)(); + } + + // Filter out excess points + values = values.filter(filterPoint); + + return { + /** + * Get the total number of points in this graph. + * @returns {number} the total number of points + */ + getPointCount: function () { + return values.length; + }, + /** + * Get the domain value (timestamp) for a point in this graph. + * @returns {number} the domain value + */ + getDomainValue: function (index) { + return values[index].domain; + }, + /** + * Get the range value (utilization level) for a point in + * this graph. + * @returns {number} the range value + */ + getRangeValue: function (index) { + return values[index].range; + } + }; + } + + return ResourceGraph; + } + +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/TimelineTimespan.js b/platform/features/timeline/src/capabilities/TimelineTimespan.js new file mode 100644 index 0000000000..9cb427f678 --- /dev/null +++ b/platform/features/timeline/src/capabilities/TimelineTimespan.js @@ -0,0 +1,105 @@ +/*global define*/ + +define( + [], + function () { + 'use strict'; + + /** + * Describes the time span of a timeline object. + * @param model the timeline's object model + * @param {Timespan[]} time spans of contained activities + */ + function TimelineTimespan(model, mutation, timespans) { + // Get the start time for this timeline + function getStart() { + return model.start.timestamp; + } + + // Get the end time for another time span + function getTimespanEnd(timespan) { + return timespan.getEnd(); + } + + // Wrapper for Math.max; used for max-finding of end time + function max(a, b) { + return Math.max(a, b); + } + + // Get the end time for this timeline + function getEnd() { + return timespans.map(getTimespanEnd).reduce(max, getStart()); + } + + // Get the duration of this timeline + function getDuration() { + return getEnd() - getStart(); + } + + // Set the start time associated with this object + function setStart(value) { + mutation.mutate(function (model) { + model.start.timestamp = Math.max(value, 0); + }, model.modified); + } + + // Set the duration associated with this object + function setDuration(value) { + // No-op; duration is implicit + } + + // Set the end time associated with this object + function setEnd(value) { + // No-op; end time is implicit + } + + // Get the epoch used by this timeline + function getEpoch() { + return model.start.epoch; + } + + return { + /** + * Get the start time, in milliseconds relative to the epoch. + * @returns {number} the start time + */ + getStart: getStart, + /** + * Get the duration, in milliseconds. + * @returns {number} the duration + */ + getDuration: getDuration, + /** + * Get the end time, in milliseconds relative to the epoch. + * @returns {number} the end time + */ + getEnd: getEnd, + /** + * Set the start time, in milliseconds relative to the epoch. + * @param {number} the new value + */ + setStart: setStart, + /** + * Set the duration, in milliseconds. Timeline durations are + * implicit, so this is actually a no-op + * @param {number} the new value + */ + setDuration: setDuration, + /** + * Set the end time, in milliseconds. Timeline end times are + * implicit, so this is actually a no-op. + * @param {number} the new value + */ + setEnd: setEnd, + /** + * Get a string identifying the reference epoch used for + * start and end times. + * @returns {string} the epoch + */ + getEpoch: getEpoch + }; + } + + return TimelineTimespan; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/TimelineTimespanCapability.js b/platform/features/timeline/src/capabilities/TimelineTimespanCapability.js new file mode 100644 index 0000000000..30ca28b873 --- /dev/null +++ b/platform/features/timeline/src/capabilities/TimelineTimespanCapability.js @@ -0,0 +1,68 @@ +/*global define*/ + +define( + ['./TimelineTimespan'], + function (TimelineTimespan) { + 'use strict'; + + /** + * Implements the `timespan` capability for Timeline objects. + * + * @constructor + * @param $q Angular's $q, for promise-handling + * @param {DomainObject} domainObject the Timeline + */ + function TimelineTimespanCapability($q, domainObject) { + // Check if a capability is defin + + // Look up a child object's time span + function lookupTimeSpan(childObject) { + return childObject.useCapability('timespan'); + } + + // Check if a child object exposes a time span + function hasTimeSpan(childObject) { + return childObject.hasCapability('timespan'); + } + + // Instantiate a time span bounding other time spans + function giveTimeSpan(timespans) { + return new TimelineTimespan( + domainObject.getModel(), + domainObject.getCapability('mutation'), + timespans + ); + } + + // Build a time span object that fits all children + function buildTimeSpan(childObjects) { + return $q.all( + childObjects.filter(hasTimeSpan).map(lookupTimeSpan) + ).then(giveTimeSpan); + } + + // Promise + function promiseTimeSpan() { + return domainObject.useCapability('composition') + .then(buildTimeSpan); + } + + return { + /** + * Get the time span (start, end, duration) of this timeline. + * @returns {Promise.} the time span of + * this timeline + */ + invoke: promiseTimeSpan + }; + } + + // Only applies to timeline objects + TimelineTimespanCapability.appliesTo = function (model) { + return model && (model.type === 'warp.timeline'); + }; + + return TimelineTimespanCapability; + + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/TimelineUtilization.js b/platform/features/timeline/src/capabilities/TimelineUtilization.js new file mode 100644 index 0000000000..d6a3e649ba --- /dev/null +++ b/platform/features/timeline/src/capabilities/TimelineUtilization.js @@ -0,0 +1,31 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Provides data to populate resource graphs associated + * with timelines in a timeline view. + * This is a placeholder until WTD-918. + * @constructor + */ + function TimelineUtilization() { + return { + getPointCount: function () { + return 1000; + }, + getDomainValue: function (index) { + return 60000 * index; + }, + getRangeValue: function (index) { + return Math.sin(index) * (index % 10); + } + }; + } + + return TimelineUtilization; + } + +); \ No newline at end of file diff --git a/platform/features/timeline/src/capabilities/UtilizationCapability.js b/platform/features/timeline/src/capabilities/UtilizationCapability.js new file mode 100644 index 0000000000..4add7de939 --- /dev/null +++ b/platform/features/timeline/src/capabilities/UtilizationCapability.js @@ -0,0 +1,198 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Provide the resource utilization over time for a timeline + * or activity object. A utilization is presented as an object + * with four properties: + * * `key`: The resource being utilized. + * * `value`: The numeric utilization of that resource. + * * `start`: The start time of the resource's utilization. + * * `end`: The duration of this resource's utilization. + * * `epoch`: The epoch to which `start` is relative. + * @constructor + */ + function UtilizationCapability($q, domainObject) { + + // Utility function for array reduction + function concatenate(a, b) { + return (a || []).concat(b || []); + } + + // Check whether an element in an array looks unique (for below) + function unique(element, index, array) { + return (index === 0) || (array[index - 1] !== element); + } + + // Utility function to ensure sorted array is all unique + function uniquify(array) { + return array.filter(unique); + } + + // Utility function for sorting strings arrays + function sort(array) { + return array.sort(); + } + + // Combine into one big array + function flatten(arrayOfArrays) { + return arrayOfArrays.reduce(concatenate, []); + } + + // Promise the objects contained by this timeline/activity + function promiseComposition() { + return $q.when(domainObject.useCapability('composition') || []); + } + + // Promise all subsystem modes associated with this object + function promiseModes() { + var relationship = domainObject.getCapability('relationship'), + modes = relationship && relationship.getRelatedObjects('modes'); + return $q.when(modes || []); + } + + // Promise the utilization which results directly from this object + function promiseInternalUtilization() { + var utilizations = {}; + + // Record the cost of a given activity mode + function addUtilization(mode) { + var cost = mode.getCapability('cost'); + if (cost) { + cost.resources().forEach(function (k) { + utilizations[k] = utilizations[k] || 0; + utilizations[k] += cost.cost(k); + }); + } + } + + // Record costs for these modes + function addUtilizations(modes) { + modes.forEach(addUtilization); + } + + // Look up start/end times for this object + function lookupTimespan() { + return domainObject.useCapability('timespan'); + } + + // Provide the result + function giveResult(timespan) { + // Convert to utilization objects + return Object.keys(utilizations).sort().map(function (k) { + return { + key: k, + value: utilizations[k], + start: timespan.getStart(), + end: timespan.getEnd(), + epoch: timespan.getEpoch() + }; + }); + } + + return promiseModes() + .then(addUtilizations) + .then(lookupTimespan) + .then(giveResult); + } + + // Look up a specific object's resource utilization + function lookupUtilization(domainObject) { + return domainObject.useCapability('utilization'); + } + + // Look up a specific object's resource utilization keys + function lookupUtilizationResources(domainObject) { + var utilization = domainObject.getCapability('utilization'); + return utilization && utilization.resources(); + } + + // Promise a consolidated list of resource utilizations + function mapUtilization(objects) { + return $q.all(objects.map(lookupUtilization)) + .then(flatten); + } + + // Promise a consolidated list of resource utilization keys + function mapUtilizationResources(objects) { + return $q.all(objects.map(lookupUtilizationResources)) + .then(flatten); + } + + // Promise utilization associated with contained objects + function promiseExternalUtilization() { + // Get the composition, then consolidate their utilizations + return promiseComposition().then(mapUtilization); + } + + // Get resource keys for this mode + function getModeKeys(mode) { + var cost = mode.getCapability('cost'); + return cost ? cost.resources() : []; + } + + // Map the above (for use in below) + function mapModeKeys(modes) { + return modes.map(getModeKeys); + } + + // Promise identifiers for resources associated with modes + function promiseInternalKeys() { + return promiseModes().then(mapModeKeys).then(flatten); + } + + // Promise identifiers for resources associated with modes + function promiseExternalKeys() { + return promiseComposition().then(mapUtilizationResources); + } + + // Promise identifiers for resources used + function promiseResourceKeys() { + return $q.all([ + promiseInternalKeys(), + promiseExternalKeys() + ]).then(flatten).then(sort).then(uniquify); + } + + // Promise all utilization + function promiseAllUtilization() { + // Concatenate internal utilization (from activity modes) + // with external utilization (from subactivities) + return $q.all([ + promiseInternalUtilization(), + promiseExternalUtilization() + ]).then(flatten); + } + + return { + /** + * Get the keys for resources associated with this object. + * @returns {Promise.} a promise for resource identifiers + */ + resources: promiseResourceKeys, + /** + * Get the resource utilization associated with this + * object. Results are not sorted. This requires looking + * at contained objects, which in turn must happen + * asynchronously, so this returns a promise. + * @returns {Promise.} a promise for all resource + * utilizations + */ + invoke: promiseAllUtilization + }; + } + + // Only applies to timelines and activities + UtilizationCapability.appliesTo = function (model) { + return model && + ((model.type === 'warp.timeline') || + (model.type === 'warp.activity')); + }; + + return UtilizationCapability; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/ActivityModeValuesController.js b/platform/features/timeline/src/controllers/ActivityModeValuesController.js new file mode 100644 index 0000000000..be6d461d09 --- /dev/null +++ b/platform/features/timeline/src/controllers/ActivityModeValuesController.js @@ -0,0 +1,41 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Controller which support the Values view of Activity Modes. + * @constructor + * @param {Array} resources definitions for extensions of + * category `warp.resources` + */ + function ActivityModeValuesController(resources) { + var metadata = {}; + + // Store metadata for a specific resource type + function storeMetadata(resource) { + var key = (resource || {}).key; + if (key) { + metadata[key] = resource; + } + } + + // Populate the lookup table to resource metadata + resources.forEach(storeMetadata); + + return { + /** + * Look up metadata associated with the specified + * resource type. + */ + metadata: function (key) { + return metadata[key]; + } + }; + } + + return ActivityModeValuesController; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/TimelineController.js b/platform/features/timeline/src/controllers/TimelineController.js new file mode 100644 index 0000000000..0ce156888f --- /dev/null +++ b/platform/features/timeline/src/controllers/TimelineController.js @@ -0,0 +1,128 @@ +/*global define*/ + +define( + [ + './swimlane/TimelineSwimlanePopulator', + './graph/TimelineGraphPopulator', + './drag/TimelineDragPopulator' + ], + function ( + TimelineSwimlanePopulator, + TimelineGraphPopulator, + TimelineDragPopulator + ) { + 'use strict'; + + /** + * Controller for the Timeline view. + * @constructor + */ + function TimelineController($scope, $q, objectLoader, MINIMUM_DURATION) { + var swimlanePopulator = new TimelineSwimlanePopulator( + objectLoader, + $scope.configuration || {}, + $scope.selection + ), + graphPopulator = new TimelineGraphPopulator($q), + dragPopulator = new TimelineDragPopulator(objectLoader); + + // Hash together all modification times. A sum is sufficient here, + // since modified timestamps should be non-decreasing. + function modificationSum() { + var sum = 0; + swimlanePopulator.get().forEach(function (swimlane) { + sum += swimlane.domainObject.getModel().modified || 0; + }); + return sum; + } + + // Reduce graph states to a watch-able number. A bitmask is + // sufficient here, since only ~30 graphed elements make sense + // (due to limits on recognizably unique line colors) + function graphMask() { + var mask = 0, bit = 1; + swimlanePopulator.get().forEach(function (swimlane) { + mask += swimlane.graph() ? 0 : bit; + bit *= 2; + }); + return mask; + } + + // Repopulate based on detected modification to in-view objects + function repopulateSwimlanes() { + swimlanePopulator.populate($scope.domainObject); + dragPopulator.populate($scope.domainObject); + graphPopulator.populate(swimlanePopulator.get()); + } + + // Repopulate graphs based on modification to swimlane graph state + function repopulateGraphs() { + graphPopulator.populate(swimlanePopulator.get()); + } + + // Get pixel width for right pane, using zoom controller + function width(zoomController) { + var start = swimlanePopulator.start(), + end = swimlanePopulator.end(); + return zoomController.toPixels(zoomController.duration( + Math.max(end - start, MINIMUM_DURATION) + )); + } + + // Refresh resource graphs + function refresh() { + if (graphPopulator) { + graphPopulator.get().forEach(function (graph) { + graph.refresh(); + }); + } + } + + // Recalculate swimlane state on changes + $scope.$watch("domainObject", swimlanePopulator.populate); + + // Also recalculate whenever anything in view is modified + $scope.$watch(modificationSum, repopulateSwimlanes); + + // Carry over changes in swimlane set to changes in graphs + $scope.$watch(graphMask, repopulateGraphs); + + // Convey current selection to drag handle populator + $scope.$watch("selection.get()", dragPopulator.select); + + // Provide initial scroll bar state, container for pane positions + $scope.scroll = { x: 0, y: 0 }; + $scope.panes = {}; + + // Expose active set of swimlanes + return { + /** + * Get the width, in pixels, of the timeline area + * @returns {number} width, in pixels + */ + width: width, + /** + * Get the swimlanes which should currently be displayed. + * @returns {TimelineSwimlane[]} the swimlanes + */ + swimlanes: swimlanePopulator.get, + /** + * Get the resource graphs which should currently be displayed. + * @returns {TimelineGraph[]} the graphs + */ + graphs: graphPopulator.get, + /** + * Get drag handles for the current selection. + * @returns {TimelineDragHandle[]} the drag handles + */ + handles: dragPopulator.get, + /** + * Refresh resource graphs (during drag.) + */ + refresh: refresh + }; + } + + return TimelineController; + } +); diff --git a/platform/features/timeline/src/controllers/TimelineGanttController.js b/platform/features/timeline/src/controllers/TimelineGanttController.js new file mode 100644 index 0000000000..0a48ed6087 --- /dev/null +++ b/platform/features/timeline/src/controllers/TimelineGanttController.js @@ -0,0 +1,67 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Control for Gantt bars in a timeline view. + * Primarily reesponsible for supporting the positioning of Gantt + * bars; particularly, this ensures that the left and right edges + * never go to far off screen, because in some environments this + * will effect rendering performance without visible results. + * @constructor + * @param {number} MAXIMUM_OFFSCREEN the maximum number of pixels + * allowed to go off-screen (to either the left or the right) + */ + function TimelineGanttController(MAXIMUM_OFFSCREEN) { + // Pixel position for the CSS left property + function left(timespan, scroll, toPixels) { + return Math.max( + toPixels(timespan.getStart()), + scroll.x - MAXIMUM_OFFSCREEN + ); + } + + // Pixel value for the CSS width property + function width(timespan, scroll, toPixels) { + var x = left(timespan, scroll, toPixels), + right = Math.min( + toPixels(timespan.getEnd()), + scroll.x + scroll.width + MAXIMUM_OFFSCREEN + ); + return right - x; + } + + return { + /** + * Get the pixel position for the `left` style property + * of a Gantt bar for the specified timespan. + * @param {Timespan} timespan the timespan to be represented + * @param scroll an object containing an `x` and `width` + * property, representing the scroll position and + * visible width, respectively. + * @param {Function} toPixels a function to convert + * a timestamp to a pixel position + * @returns {number} the pixel position of the left edge + */ + left: left, + /** + * Get the pixel value for the `width` style property + * of a Gantt bar for the specified timespan. + * @param {Timespan} timespan the timespan to be represented + * @param scroll an object containing an `x` and `width` + * property, representing the scroll position and + * visible width, respectively. + * @param {Function} toPixels a function to convert + * a timestamp to a pixel position + * @returns {number} the pixel width of this Gantt bar + */ + width: width + }; + } + + return TimelineGanttController; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/TimelineGraphController.js b/platform/features/timeline/src/controllers/TimelineGraphController.js new file mode 100644 index 0000000000..5fa870002d --- /dev/null +++ b/platform/features/timeline/src/controllers/TimelineGraphController.js @@ -0,0 +1,76 @@ +/*global define*/ +define( + [], + function () { + 'use strict'; + + /** + * Controller for the graph area of a timeline view. + * The set of graphs to show is provided by the timeline + * controller and communicated into the template via "parameters" + * in scope. + * @constructor + */ + function TimelineGraphController($scope, resources) { + var resourceMap = {}, + labelCache = {}; + + // Add an element to the resource map + function addToResourceMap(resource) { + var key = resource.key; + if (key && !resourceMap[key]) { + resourceMap[key] = resource; + } + } + + // Update the display bounds for all graphs to match + // scroll and/or width. + function updateGraphs(parameters) { + (parameters.graphs || []).forEach(function (graph) { + graph.setBounds(parameters.origin, parameters.duration); + }); + } + + // Add all resources to map for simpler lookup + resources.forEach(addToResourceMap); + + // Update graphs as parameters change + $scope.$watchCollection("parameters", updateGraphs); + + return { + /** + * Get a label object (suitable to pass into the + * `timeline-resource-graph-labels` template) for + * the specified graph. + * @param {TimelineGraph} the graph to label + * @returns {object} an object containing labels + */ + label: function (graph) { + var key = graph.key, + resource = resourceMap[key] || {}, + name = resource.name || "", + units = resource.units, + min = graph.minimum() || 0, + max = graph.maximum() || 0, + label = labelCache[key] || {}; + + // Cache the label (this is passed into a template, + // so avoid excessive digest cycles) + labelCache[key] = label; + + // Include units in title + label.title = name + (units ? (" (" + units + ")") : ""); + + // Provide low, middle, high data values + label.low = min.toFixed(3); + label.middle = ((min + max) / 2).toFixed(3); + label.high = max.toFixed(3); + + return label; + } + }; + } + + return TimelineGraphController; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/TimelineTableController.js b/platform/features/timeline/src/controllers/TimelineTableController.js new file mode 100644 index 0000000000..d38d6e6bdd --- /dev/null +++ b/platform/features/timeline/src/controllers/TimelineTableController.js @@ -0,0 +1,32 @@ +/*global define*/ + +define( + ["../TimelineFormatter"], + function (TimelineFormatter) { + "use strict"; + + var FORMATTER = new TimelineFormatter(); + + /** + * Provides tabular data for the Timeline's tabular view area. + */ + function TimelineTableController() { + + function getNiceTime(millis) { + return FORMATTER.format(millis); + } + + return { + /** + * Return human-readable time in the expected format, + * currently SET. + * @param {number} millis duration, in millisecond + * @return {string} human-readable duration + */ + niceTime: getNiceTime + }; + } + + return TimelineTableController; + } +); diff --git a/platform/features/timeline/src/controllers/TimelineTickController.js b/platform/features/timeline/src/controllers/TimelineTickController.js new file mode 100644 index 0000000000..131ad7722c --- /dev/null +++ b/platform/features/timeline/src/controllers/TimelineTickController.js @@ -0,0 +1,97 @@ +/*global define*/ + +define( + ["../TimelineFormatter"], + function (TimelineFormatter) { + "use strict"; + + var FORMATTER = new TimelineFormatter(); + + /** + * Provides labels for the tick mark area of a timeline view. + * Since the tick mark regin is potentially extremeley large, + * only the subset of ticks which will actually be shown in + * view are provided. + * @constructor + */ + function TimelineTickController() { + var labels = [], + lastFirst, + lastStep, + lastCount, + lastStartMillis, + lastEndMillis; + + // Actually recalculate the labels from scratch + function calculateLabels(first, count, step, toMillis) { + var result = [], + current; + + // Create enough labels to fill the visible area + while (result.length < count) { + current = first + step * result.length; + result.push({ + // Horizontal pixel position of this label + left: current, + // Text to display in this label + text: FORMATTER.format(toMillis(current)) + }); + } + + return result; + } + + // Get tick labels for this pixel span (recalculating if needed) + function getLabels(start, width, step, toMillis) { + // Calculate parameters for labels (first pixel position, last + // pixel position.) These are checked to detect changes. + var first = Math.floor(start / step) * step, + last = Math.ceil((start + width) / step) * step, + count = ((last - first) / step) + 1, + startMillis = toMillis(first), + endMillis = toMillis(last), + changed = (lastFirst !== first) || + (lastCount !== count) || + (lastStep !== step) || + (lastStartMillis !== startMillis) || + (lastEndMillis !== endMillis); + + // This will be used in a template, so only recalculate on + // change. + if (changed) { + labels = calculateLabels(first, count, step, toMillis); + // Cache to avoid recomputing later + lastFirst = first; + lastCount = count; + lastStep = step; + lastStartMillis = startMillis; + lastEndMillis = endMillis; + } + + return labels; + } + + + return { + /** + * Get labels for use in the visible region of a timeline's + * tick mark area. This will return the same array instance + * (without recalculating its contents) if called with the + * same parameters (and same apparent zoom state, as determined + * via `toMillis`), so it is safe to use in a template. + * + * @param {number} start left-most pixel position in view + * @param {number} width pixel width in view + * @param {number} step size, in pixels, of each major tick + * @param {Function} toMillis function to convert from pixel + * positions to milliseconds + * @returns {Array} an array of tick mark labels, suitable + * for use in the `timeline-ticks` template + */ + labels: getLabels + }; + } + + return TimelineTickController; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/TimelineZoomController.js b/platform/features/timeline/src/controllers/TimelineZoomController.js new file mode 100644 index 0000000000..6d6a72f516 --- /dev/null +++ b/platform/features/timeline/src/controllers/TimelineZoomController.js @@ -0,0 +1,109 @@ +/*global define*/ +define( + ['../TimelineFormatter'], + function (TimelineFormatter) { + "use strict"; + + + var FORMATTER = new TimelineFormatter(); + + /** + * Controls the pan-zoom state of a timeline view. + * @constructor + */ + function TimelineZoomController($scope, ZOOM_CONFIGURATION) { + // Prefer to start with the middle index + var zoomLevels = ZOOM_CONFIGURATION.levels || [ 1000 ], + zoomIndex = Math.floor(zoomLevels.length / 2), + tickWidth = ZOOM_CONFIGURATION.width || 200, + duration = 86400000; // Default duration in view + + // Round a duration to a larger value, to ensure space for editing + function roundDuration(value) { + // Ensure there's always an extra day or so + var sz = zoomLevels[zoomLevels.length - 1]; + value *= 1.25; // Add 25% padding to start + return Math.ceil(value / sz) * sz; + } + + // Get/set zoom level + function setZoomLevel(level) { + if (!isNaN(level)) { + // Modify zoom level, keeping it in range + zoomIndex = Math.min( + Math.max(level, 0), + zoomLevels.length - 1 + ); + } + } + + // Persist current zoom level + function storeZoom() { + var isEditMode = $scope.commit && + $scope.domainObject && + $scope.domainObject.hasCapability('editor'); + if (isEditMode) { + $scope.configuration = $scope.configuration || {}; + $scope.configuration.zoomLevel = zoomIndex; + $scope.commit(); + } + } + + $scope.$watch("configuration.zoomLevel", setZoomLevel); + + return { + /** + * Increase or decrease the current zoom level by a given + * number of steps. Positive steps zoom in, negative steps + * zoom out. + * If called with no arguments, this returns the current + * zoom level, expressed as the number of milliseconds + * associated with a given tick mark. + * @param {number} steps how many steps to zoom in + * @returns {number} current zoom level (as the size of a + * major tick mark, in pixels) + */ + zoom: function (amount) { + // Update the zoom level if called with an argument + if (arguments.length > 0 && !isNaN(amount)) { + setZoomLevel(zoomIndex + amount); + storeZoom(zoomIndex); + } + return zoomLevels[zoomIndex]; + }, + /** + * Get the width, in pixels, of a specific time duration at + * the current zoom level. + * @returns {number} the number of pixels + */ + toPixels: function (millis) { + return tickWidth * millis / zoomLevels[zoomIndex]; + }, + /** + * Get the time duration, in milliseconds, occupied by the + * width (specified in pixels) at the current zoom level. + * @returns {number} the number of pixels + */ + toMillis: function (pixels) { + return (pixels / tickWidth) * zoomLevels[zoomIndex]; + }, + /** + * Get or set the current displayed duration. If used as a + * setter, this will typically be rounded up to ensure extra + * space is available at the right. + * @returns {number} duration, in milliseconds + */ + duration: function (value) { + var prior = duration; + if (arguments.length > 0) { + duration = roundDuration(value); + } + return duration; + } + }; + } + + return TimelineZoomController; + + } +); diff --git a/platform/features/timeline/src/controllers/WARPDateTimeController.js b/platform/features/timeline/src/controllers/WARPDateTimeController.js new file mode 100644 index 0000000000..35a660551a --- /dev/null +++ b/platform/features/timeline/src/controllers/WARPDateTimeController.js @@ -0,0 +1,72 @@ +/*global define,moment*/ + +define( + [], + function () { + "use strict"; + + /** + * Controller for the `datetime` form control. + * This is a composite control; it includes multiple + * input fields but outputs a single timestamp (in + * milliseconds since start of 1970) to the ngModel. + * + * @constructor + */ + function DateTimeController($scope) { + + // Update the data model + function updateModel(datetime) { + var days = parseInt(datetime.days, 10) || 0, + hour = parseInt(datetime.hours, 10) || 0, + min = parseInt(datetime.minutes, 10) || 0, + sec = parseInt(datetime.seconds, 10) || 0, + epoch = "SET", // Only permit SET, for now + timestamp; + + // Build up timestamp + timestamp = days * 24; + timestamp = (hour + timestamp) * 60; + timestamp = (min + timestamp) * 60; + timestamp = (sec + timestamp) * 1000; + + // Set in the model + $scope.ngModel[$scope.field] = { + timestamp: timestamp, + epoch: epoch + }; + } + + // Update the displayed state + function updateForm(modelState) { + var timestamp = (modelState || {}).timestamp || 0, + datetime = $scope.datetime; + + timestamp = Math.floor(timestamp / 1000); + datetime.seconds = timestamp % 60; + timestamp = Math.floor(timestamp / 60); + datetime.minutes = timestamp % 60; + timestamp = Math.floor(timestamp / 60); + datetime.hours = timestamp % 24; + timestamp = Math.floor(timestamp / 24); + datetime.days = timestamp; + } + + // Retrieve state from field, for watch + function getModelState() { + return $scope.ngModel[$scope.field]; + } + + // Update value whenever any field changes. + $scope.$watchCollection("datetime", updateModel); + $scope.$watchCollection(getModelState, updateForm); + + // Initialize the scope + $scope.datetime = {}; + updateForm(getModelState()); + } + + return DateTimeController; + + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/drag/TimelineDragHandleFactory.js b/platform/features/timeline/src/controllers/drag/TimelineDragHandleFactory.js new file mode 100644 index 0000000000..7f36e303db --- /dev/null +++ b/platform/features/timeline/src/controllers/drag/TimelineDragHandleFactory.js @@ -0,0 +1,55 @@ +/*global define*/ + +define( + ['./TimelineStartHandle', './TimelineEndHandle', './TimelineMoveHandle'], + function (TimelineStartHandle, TimelineEndHandle, TimelineMoveHandle) { + "use strict"; + + + var DEFAULT_HANDLES = [ + TimelineStartHandle, + TimelineMoveHandle, + TimelineEndHandle + ], + TIMELINE_HANDLES = [ + TimelineStartHandle, + TimelineMoveHandle + ]; + + /** + * Create a factory for drag handles for timelines/activities + * in a timeline view. + * @constructor + */ + function TimelineDragHandleFactory(dragHandler, snapHandler) { + return { + /** + * Create drag handles for this domain object. + * @param {DomainObject} domainObject the object to be + * manipulated by these gestures + * @returns {Array} array of drag handles + */ + handles: function (domainObject) { + var type = domainObject.getCapability('type'), + id = domainObject.getId(); + + // Instantiate a handle + function instantiate(Handle) { + return new Handle( + id, + dragHandler, + snapHandler + ); + } + + // Instantiate smaller set of handles for timelines + return (type && type.instanceOf('warp.timeline') ? + TIMELINE_HANDLES : DEFAULT_HANDLES) + .map(instantiate); + } + }; + } + + return TimelineDragHandleFactory; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js b/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js new file mode 100644 index 0000000000..b1e5011280 --- /dev/null +++ b/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js @@ -0,0 +1,237 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Handles business logic (mutation of objects, retrieval of start/end + * times) associated with drag gestures to manipulate start/end times + * of activities and timelines in a Timeline view. + * @constructor + * @param {DomainObject} domainObject the object being viewed + * @param {ObjectLoader} objectLoader service to assist in loading + * subtrees + */ + function TimelineDragHandler(domainObject, objectLoader) { + var timespans = {}, + persists = {}, + mutations = {}, + compositions = {}, + dirty = {}; + + // "Cast" a domainObject to an id, if necessary + function toId(value) { + return (typeof value !== 'string' && value.getId) ? + value.getId() : value; + } + + // Get the timespan associated with this domain object + function populateCapabilityMaps(domainObject) { + var id = domainObject.getId(), + timespanPromise = domainObject.useCapability('timespan'); + if (timespanPromise) { + timespanPromise.then(function (timespan) { + // Cache that timespan + timespans[id] = timespan; + // And its mutation capability + mutations[id] = domainObject.getCapability('mutation'); + // Also cache the persistence capability for later + persists[id] = domainObject.getCapability('persistence'); + // And the composition, for bulk moves + compositions[id] = domainObject.getModel().composition || []; + }); + } + } + + // Populate the id->timespan map + function populateTimespans(subgraph) { + populateCapabilityMaps(subgraph.domainObject); + subgraph.composition.forEach(populateTimespans); + } + + // Persist changes for objects by id (when dragging ends) + function doPersist(id) { + var persistence = persists[id], + mutation = mutations[id]; + if (mutation) { + // Mutate just to update the timestamp (since we + // explicitly don't do this during the drag to + // avoid firing a ton of refreshes.) + mutation.mutate(function () {}); + } + if (persistence) { + // Persist the changes + persistence.persist(); + } + } + + // Use the object loader to get objects which have timespans + objectLoader.load(domainObject, 'timespan').then(populateTimespans); + + return { + /** + * Get a list of identifiers for domain objects which have + * timespans that are managed here. + * @returns {string[]} ids for all objects which have managed + * timespans here + */ + ids: function () { + return Object.keys(timespans).sort(); + }, + /** + * Persist any changes to timespans that have been made through + * this handler. + */ + persist: function () { + // Persist every dirty object... + Object.keys(dirty).forEach(doPersist); + // Clear out the dirty list + dirty = {}; + }, + /** + * Get the start time for a specific domain object. The domain + * object may be specified by its identifier, or passed as a + * domain object instance. If a second, numeric argument is + * passed, this functions as a setter. + * @returns {number} the start time + * @param {string|DomainObject} id the domain object to modify + * @param {number} [value] the new value + */ + start: function (id, value) { + // Convert to domain object id, look up timespan + var timespan = timespans[toId(id)]; + // Use as setter if argument is present + if ((typeof value === 'number') && timespan) { + // Set the start (ensuring that it's non-negative, + // and not after the end time.) + timespan.setStart( + Math.min(Math.max(value, 0), timespan.getEnd()) + ); + // Mark as dirty for subsequent persistence + dirty[toId(id)] = true; + } + // Return value from the timespan + return timespan && timespan.getStart(); + }, + /** + * Get the end time for a specific domain object. The domain + * object may be specified by its identifier, or passed as a + * domain object instance. If a second, numeric argument is + * passed, this functions as a setter. + * @returns {number} the end time + * @param {string|DomainObject} id the domain object to modify + * @param {number} [value] the new value + */ + end: function (id, value) { + // Convert to domain object id, look up timespan + var timespan = timespans[toId(id)]; + // Use as setter if argument is present + if ((typeof value === 'number') && timespan) { + // Set the end (ensuring it doesn't preceed start) + timespan.setEnd( + Math.max(value, timespan.getStart()) + ); + // Mark as dirty for subsequent persistence + dirty[toId(id)] = true; + } + // Return value from the timespan + return timespan && timespan.getEnd(); + }, + /** + * Get the duration for a specific domain object. The domain + * object may be specified by its identifier, or passed as a + * domain object instance. If a second, numeric argument is + * passed, this functions as a setter. + * @returns {number} the duration + * @param {string|DomainObject} id the domain object to modify + * @param {number} [value] the new value + */ + duration: function (id, value) { + // Convert to domain object id, look up timespan + var timespan = timespans[toId(id)]; + // Use as setter if argument is present + if ((typeof value === 'number') && timespan) { + // Set duration (ensure that it's non-negative) + timespan.setDuration( + Math.max(value, 0) + ); + // Mark as dirty for subsequent persistence + dirty[toId(id)] = true; + } + // Return value from the timespan + return timespan && timespan.getDuration(); + }, + /** + * Move the start and end of this domain object by the + * specified delta. Contained objects will move as well. + * @param {string|DomainObject} id the domain object to modify + * @param {number} delta the amount by which to change + */ + move: function (id, delta) { + // Overview of algorithm used here: + // - Build up list of ids to actually move + // - Find the minimum start time + // - Change delta so it cannot move minimum past 0 + // - Update start, then end time + var ids = {}, + queue = [toId(id)], + minStart; + + // Update start & end, in that order + function updateStartEnd(id) { + var timespan = timespans[id], start, end; + if (timespan) { + // Get start/end so we don't get fooled by our + // own adjustments + start = timespan.getStart(); + end = timespan.getEnd(); + // Update start, then end + timespan.setStart(start + delta); + timespan.setEnd(end + delta); + // Mark as dirty for subsequent persistence + dirty[toId(id)] = true; + } + } + + // Build up set of ids + while (queue.length > 0) { + // Get the next id to consider + id = queue.shift(); + // If we haven't already considered this... + if (!ids[id]) { + // Add it to the set + ids[id] = true; + // And queue up its composition + queue = queue.concat(compositions[id] || []); + } + } + + // Find the minimum start time + minStart = Object.keys(ids).map(function (id) { + // Get the start time; default to +Inf if not + // found, since this will not survive a min + // test if any real timespans are present + return timespans[id] ? + timespans[id].getStart() : + Number.POSITIVE_INFINITY; + }).reduce(function (a, b) { + // Reduce with a minimum test + return Math.min(a, b); + }, Number.POSITIVE_INFINITY); + + // Ensure delta doesn't exceed bounds + delta = Math.max(delta, -minStart); + + // Update start/end times + if (delta !== 0) { + Object.keys(ids).forEach(updateStartEnd); + } + } + }; + } + + return TimelineDragHandler; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/drag/TimelineDragPopulator.js b/platform/features/timeline/src/controllers/drag/TimelineDragPopulator.js new file mode 100644 index 0000000000..65f68a88ed --- /dev/null +++ b/platform/features/timeline/src/controllers/drag/TimelineDragPopulator.js @@ -0,0 +1,76 @@ +/*global define*/ + +define( + ['./TimelineDragHandler', './TimelineSnapHandler', './TimelineDragHandleFactory'], + function (TimelineDragHandler, TimelineSnapHandler, TimelineDragHandleFactory) { + "use strict"; + + /** + * Provides drag handles for the active selection in a timeline view. + * @constructor + */ + function TimelineDragPopulator(objectLoader) { + var handles = [], + factory, + selectedObject; + + // Refresh active set of drag handles + function refreshHandles() { + handles = (factory && selectedObject) ? + factory.handles(selectedObject) : + []; + } + + // Create a new factory for handles, based on root object in view + function populateForObject(domainObject) { + var dragHandler = domainObject && new TimelineDragHandler( + domainObject, + objectLoader + ); + + // Reinstantiate the factory + factory = dragHandler && new TimelineDragHandleFactory( + dragHandler, + new TimelineSnapHandler(dragHandler) + ); + + // If there's a selected object, restore the handles + refreshHandles(); + } + + // Change the current selection + function select(swimlane) { + // Cache selection to restore handles if other changes occur + selectedObject = swimlane && swimlane.domainObject; + + // Provide handles for this selection, if it's defined + refreshHandles(); + } + + return { + /** + * Get the currently-applicable set of drag handles. + * @returns {Array} drag handles + */ + get: function () { + return handles; + }, + /** + * Set the root object in view. Drag interactions consider + * the full graph for snapping behavior, so this is needed. + * @param {DomainObject} domainObject the timeline object + * being viewed + */ + populate: populateForObject, + /** + * Update selection state. Passing undefined means there + * is no selection. + * @param {TimelineSwimlane} swimlane the selected swimlane + */ + select: select + }; + } + + return TimelineDragPopulator; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/drag/TimelineEndHandle.js b/platform/features/timeline/src/controllers/drag/TimelineEndHandle.js new file mode 100644 index 0000000000..2d013a8139 --- /dev/null +++ b/platform/features/timeline/src/controllers/drag/TimelineEndHandle.js @@ -0,0 +1,77 @@ +/*global define*/ + +define( + ['../../TimelineConstants'], + function (Constants) { + "use strict"; + + /** + * Handle for changing the end time of a timeline or + * activity in the Timeline view. + * @constructor + * @param {string} id identifier of the domain object + * @param {TimelineDragHandler} dragHandler the handler which + * will update object state + * @param {TimelineSnapHandler} snapHandler the handler which + * provides candidate snap-to locations. + */ + function TimelineEndHandle(id, dragHandler, snapHandler) { + var initialEnd; + + // Get the snap-to location for a timestamp + function snap(timestamp, zoom) { + return snapHandler.snap( + timestamp, + zoom.toMillis(Constants.SNAP_WIDTH), + id + ); + } + + return { + /** + * Start dragging this handle. + */ + begin: function () { + // Cache the initial state + initialEnd = dragHandler.end(id); + }, + /** + * Drag this handle. + * @param {number} delta pixel delta from start + * @param {TimelineZoomController} zoom provider of zoom state + */ + drag: function (delta, zoom) { + if (initialEnd !== undefined) { + // Update the state + dragHandler.end( + id, + snap(initialEnd + zoom.toMillis(delta), zoom) + ); + } + }, + /** + * Finish dragging this handle. + */ + finish: function () { + // Clear initial state + initialEnd = undefined; + // Persist changes + dragHandler.persist(); + }, + /** + * Get a style object (suitable for passing into `ng-style`) + * for this handle. + * @param {TimelineZoomController} zoom provider of zoom state + */ + style: function (zoom) { + return { + left: zoom.toPixels(dragHandler.end(id)) - Constants.HANDLE_WIDTH + 'px', + width: Constants.HANDLE_WIDTH + 'px' + }; + } + }; + } + + return TimelineEndHandle; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/drag/TimelineMoveHandle.js b/platform/features/timeline/src/controllers/drag/TimelineMoveHandle.js new file mode 100644 index 0000000000..a5fbe8f0d5 --- /dev/null +++ b/platform/features/timeline/src/controllers/drag/TimelineMoveHandle.js @@ -0,0 +1,115 @@ +/*global define*/ + +define( + ['../../TimelineConstants'], + function (Constants) { + "use strict"; + + /** + * Handle for moving (by drag) a timeline or + * activity in the Timeline view. + * @constructor + * @param {string} id identifier of the domain object + * @param {TimelineDragHandler} dragHandler the handler which + * will update object state + * @param {TimelineSnapHandler} snapHandler the handler which + * provides candidate snap-to locations. + */ + function TimelineMoveHandle(id, dragHandler, snapHandler) { + var initialStart, + initialEnd; + + // Get the snap-to location for a timestamp + function snap(timestamp, zoom) { + return snapHandler.snap( + timestamp, + zoom.toMillis(Constants.SNAP_WIDTH), + id + ); + } + + // Convert a pixel delta to a millisecond delta that will align + // with some useful snap location + function snapDelta(delta, zoom) { + var timeDelta = zoom.toMillis(delta), + desiredStart = initialStart + timeDelta, + desiredEnd = initialEnd + timeDelta, + snappedStart = snap(desiredStart, zoom), + snappedEnd = snap(desiredEnd, zoom), + diffStart = Math.abs(snappedStart - desiredStart), + diffEnd = Math.abs(snappedEnd - desiredEnd), + chooseEnd = false; + + // First, check for case where both changed... + if ((diffStart > 0) && (diffEnd > 0)) { + // ...and choose the smallest change that snaps. + chooseEnd = diffEnd < diffStart; + } else { + // ...otherwise, snap toward the end if it changed. + chooseEnd = diffEnd > 0; + } + // Start is chosen if diffEnd didn't snap, or nothing snapped + + // Our delta is relative to our initial state, but + // dragHandler.move is relative to current state, so whichever + // end we're snapping to, we need to compute a delta + // relative to the current state to get the desired result. + return chooseEnd ? + (snappedEnd - dragHandler.end(id)) : + (snappedStart - dragHandler.start(id)); + } + + return { + /** + * Start dragging this handle. + */ + begin: function () { + // Cache the initial state + initialStart = dragHandler.start(id); + initialEnd = dragHandler.end(id); + }, + /** + * Drag this handle. + * @param {number} delta pixel delta from start + * @param {TimelineZoomController} zoom provider of zoom state + */ + drag: function (delta, zoom) { + if (initialStart !== undefined && initialEnd !== undefined) { + if (delta !== 0) { + dragHandler.move(id, snapDelta(delta, zoom)); + } + } + }, + /** + * Finish dragging this handle. + */ + finish: function () { + // Clear initial state + initialStart = undefined; + initialEnd = undefined; + // Persist changes + dragHandler.persist(); + }, + /** + * Get a style object (suitable for passing into `ng-style`) + * for this handle. + * @param {TimelineZoomController} zoom provider of zoom state + */ + + style: function (zoom) { + return { + left: zoom.toPixels(dragHandler.start(id)) + + Constants.HANDLE_WIDTH + + 'px', + width: zoom.toPixels(dragHandler.duration(id)) - + Constants.HANDLE_WIDTH * 2 + + 'px' + //cursor: initialStart === undefined ? 'grab' : 'grabbing' + }; + } + }; + } + + return TimelineMoveHandle; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/drag/TimelineSnapHandler.js b/platform/features/timeline/src/controllers/drag/TimelineSnapHandler.js new file mode 100644 index 0000000000..b56bed2fad --- /dev/null +++ b/platform/features/timeline/src/controllers/drag/TimelineSnapHandler.js @@ -0,0 +1,85 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Snaps timestamps to match other timestamps within a + * certain tolerance, to support the snap-to-start-and-end + * behavior of drag interactions in a timeline view. + * @constructor + * @param {TimelineDragHandler} dragHandler the handler + * for drag interactions, which maintains start/end + * information for timelines in this view. + */ + function TimelineSnapHandler(dragHandler) { + // Snap to other end points + function snap(timestamp, tolerance, exclude) { + var result = timestamp, + closest = tolerance, + ids, + candidates; + + // Filter an id for inclustion + function include(id) { return id !== exclude; } + + // Evaluate a candidate timestamp as a snap-to location + function evaluate(candidate) { + var difference = Math.abs(candidate - timestamp); + // Is this closer than anything else we've found? + if (difference < closest) { + // ...then this is our new result + result = candidate; + // Track how close it was, for subsequent comparison. + closest = difference; + } + } + + // Look up start time; for mapping below + function getStart(id) { + return dragHandler.start(id); + } + + // Look up end time; for mapping below + function getEnd(id) { + return dragHandler.end(id); + } + + // Get list of candidate ids + ids = dragHandler.ids().filter(include); + + // Get candidate timestamps + candidates = ids.map(getStart).concat(ids.map(getEnd)); + + // ...and find the best one + candidates.forEach(evaluate); + + // Closest candidate (or original timestamp) is our result + // now, so return it. + return result; + } + + return { + /** + * Get a timestamp location that is near this + * timestamp (or simply return the provided + * timestamp if none are near enough, according + * to the specified tolerance.) + * Start/end times associated with the domain object + * with the specified identifier will be excluded + * from consideration (to avoid an undesired snap-to-self + * behavior.) + * @param {number} timestamp the timestamp to snap + * @param {number} tolerance the difference within which + * to snap + * @param {string} id the identifier to exclude + */ + snap: snap + }; + } + + return TimelineSnapHandler; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/drag/TimelineStartHandle.js b/platform/features/timeline/src/controllers/drag/TimelineStartHandle.js new file mode 100644 index 0000000000..efaba1a8e1 --- /dev/null +++ b/platform/features/timeline/src/controllers/drag/TimelineStartHandle.js @@ -0,0 +1,77 @@ +/*global define*/ + +define( + ['../../TimelineConstants'], + function (Constants) { + "use strict"; + + /** + * Handle for changing the start time of a timeline or + * activity in the Timeline view. + * @constructor + * @param {string} id identifier of the domain object + * @param {TimelineDragHandler} dragHandler the handler which + * will update object state + * @param {TimelineSnapHandler} snapHandler the handler which + * provides candidate snap-to locations. + */ + function TimelineStartHandle(id, dragHandler, snapHandler) { + var initialStart; + + // Get the snap-to location for a timestamp + function snap(timestamp, zoom) { + return snapHandler.snap( + timestamp, + zoom.toMillis(Constants.SNAP_WIDTH), + id + ); + } + + return { + /** + * Start dragging this handle. + */ + begin: function () { + // Cache the initial state + initialStart = dragHandler.start(id); + }, + /** + * Drag this handle. + * @param {number} delta pixel delta from start + * @param {TimelineZoomController} zoom provider of zoom state + */ + drag: function (delta, zoom) { + if (initialStart !== undefined) { + // Update the state + dragHandler.start( + id, + snap(initialStart + zoom.toMillis(delta), zoom) + ); + } + }, + /** + * Finish dragging this handle. + */ + finish: function () { + // Clear initial state + initialStart = undefined; + // Persist changes + dragHandler.persist(); + }, + /** + * Get a style object (suitable for passing into `ng-style`) + * for this handle. + * @param {TimelineZoomController} zoom provider of zoom state + */ + style: function (zoom) { + return { + left: zoom.toPixels(dragHandler.start(id)) + 'px', + width: Constants.HANDLE_WIDTH + 'px' + }; + } + }; + } + + return TimelineStartHandle; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/graph/TimelineGraph.js b/platform/features/timeline/src/controllers/graph/TimelineGraph.js new file mode 100644 index 0000000000..84ae3a2e74 --- /dev/null +++ b/platform/features/timeline/src/controllers/graph/TimelineGraph.js @@ -0,0 +1,172 @@ +/*global define*/ + +define( + [], + function () { + 'use strict'; + + /** + * Provides data to populate a graph in a timeline view. + * @constructor + * @param {string} key the resource's identifying key + * @param {Object.} domainObjects and object + * containing key-value pairs where keys are colors, and + * values are DomainObject instances to be drawn in that + * color + * @param {TimelineGraphRenderer} renderer a renderer which + * can be used to prepare Float32Array instances + */ + function TimelineGraph(key, domainObjects, renderer) { + var drawingObject = { origin: [0, 0], dimensions: [0, 0], modified: 0}, + // lines for the drawing object, by swimlane index + lines = [], + // min/max seen for a given swimlane, by swimlane index + extrema = [], + // current minimum + min = 0, + // current maximum + max = 0, + // current displayed time span + duration = 1000, + // line colors to display + colors = Object.keys(domainObjects); + + // Get minimum value, ensure there's some room + function minimum() { + return (min >= max) ? (max - 1) : min; + } + + // Get maximum value, ensure there's some room + function maximum() { + return (min >= max) ? (min + 1) : max; + } + + // Update minimum and maximum values + function updateMinMax() { + // Find the minimum among plot lines + min = extrema.map(function (ex) { + return ex.min; + }).reduce(function (a, b) { + return Math.min(a, b); + }, Number.POSITIVE_INFINITY); + + // Do the same for the maximum + max = extrema.map(function (ex) { + return ex.max; + }).reduce(function (a, b) { + return Math.max(a, b); + }, Number.NEGATIVE_INFINITY); + + // Ensure the infinities don't survive + min = min === Number.POSITIVE_INFINITY ? max : min; + min = min === Number.NEGATIVE_INFINITY ? 0 : min; + max = max === Number.NEGATIVE_INFINITY ? min : max; + } + + // Change contents of the drawing object (to trigger redraw) + function updateDrawingObject() { + // Update drawing object to include non-empty lines + drawingObject.lines = lines.filter(function (line) { + return line.points > 1; + }); + + // Update drawing bounds to fit data + drawingObject.origin[1] = minimum(); + drawingObject.dimensions[1] = maximum() - minimum(); + } + + // Update a specific line, by index + function updateLine(graph, index) { + var buffer = renderer.render(graph), + line = lines[index], + ex = extrema[index], + i; + + // Track minimum/maximum; note we skip x values + for (i = 1; i < buffer.length; i += 2) { + ex.min = Math.min(buffer[i], ex.min); + ex.max = Math.max(buffer[i], ex.max); + } + + // Update line in drawing object + line.buffer = buffer; + line.points = graph.getPointCount(); + line.color = renderer.decode(colors[index]); + + // Update the graph's total min/max + if (line.points > 0) { + updateMinMax(); + } + + // Update the drawing object (used to draw the graph) + updateDrawingObject(); + } + + // Request initialization for a line's contents + function populateLine(color, index) { + var domainObject = domainObjects[color], + graphPromise = domainObject.useCapability('graph'); + + if (graphPromise) { + graphPromise.then(function (g) { + if (g[key]) { + updateLine(g[key], index); + } + }); + } + } + + // Create empty lines + lines = colors.map(function () { + // Sentinel value to exclude these lines + return { points: 0 }; + }); + + // Specify initial min/max state per-line + extrema = colors.map(function () { + return { + min: Number.POSITIVE_INFINITY, + max: Number.NEGATIVE_INFINITY + }; + }); + + // Start creating lines for all swimlanes + colors.forEach(populateLine); + + return { + /** + * Get the minimum resource value that appears in this graph. + * @returns {number} the minimum value + */ + minimum: minimum, + /** + * Get the maximum resource value that appears in this graph. + * @returns {number} the maximum value + */ + maximum: maximum, + /** + * Set the displayed origin and duration, in milliseconds. + * @param {number} [value] value to set, if setting + */ + setBounds: function (offset, duration) { + // We don't update in-place, because we need the change + // to trigger a watch in mct-chart. + drawingObject.origin = [ offset, drawingObject.origin[1] ]; + drawingObject.dimensions = [ duration, drawingObject.dimensions[1] ]; + }, + /** + * Redraw lines in this graph. + */ + refresh: function () { + colors.forEach(populateLine); + }, + // Expose key, drawing object directly for use in templates + key: key, + drawingObject: drawingObject + }; + + } + + return TimelineGraph; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/graph/TimelineGraphPopulator.js b/platform/features/timeline/src/controllers/graph/TimelineGraphPopulator.js new file mode 100644 index 0000000000..b3037bd404 --- /dev/null +++ b/platform/features/timeline/src/controllers/graph/TimelineGraphPopulator.js @@ -0,0 +1,136 @@ +/*global define*/ +define( + ['./TimelineGraph', './TimelineGraphRenderer'], + function (TimelineGraph, TimelineGraphRenderer) { + 'use strict'; + + /** + * Responsible for determining which resource graphs + * to display (based on capabilities exposed by included + * domain objects) and allocating data to those different + * graphs. + * @constructor + */ + function TimelineGraphPopulator($q) { + var graphs = [], + cachedAssignments = {}, + renderer = new TimelineGraphRenderer(); + + // Compare two domain objects + function idsMatch(objA, objB) { + return (objA && objA.getId && objA.getId()) === + (objB && objB.getId && objB.getId()); + } + + // Compare two object sets for equality, to detect + // when graph updates are truly needed. + function deepEquals(objA, objB) { + var keysA, keysB; + + // Check if all keys in both objects match + function keysMatch(keys) { + return keys.map(function (k) { + return deepEquals(objA[k], objB[k]); + }).reduce(function (a, b) { + return a && b; + }, true); + } + + // First, check if they're matching domain objects + if (typeof (objA && objA.getId) === 'function') { + return idsMatch(objA, objB); + } + + // Otherwise, assume key-value pairs + keysA = Object.keys(objA || {}).sort(); + keysB = Object.keys(objB || {}).sort(); + + return (keysA.length === keysB.length) && keysMatch(keysA); + } + + // Populate the graphs for these swimlanes + function populate(swimlanes) { + // Somewhere to store resource assignments + // (as key -> swimlane[]) + var assignments = {}; + + // Look up resources for a domain object + function lookupResources(swimlane) { + var graphs = swimlane.domainObject.useCapability('graph'); + function getKeys(obj) { + return Object.keys(obj); + } + return $q.when(graphs ? (graphs.then(getKeys)) : []); + } + + // Add all graph assignments appropriate for this swimlane + function buildAssignments(swimlane) { + // Assign this swimlane to graphs for its resource keys + return lookupResources(swimlane).then(function (resources) { + resources.forEach(function (key) { + assignments[key] = assignments[key] || {}; + assignments[key][swimlane.color()] = + swimlane.domainObject; + }); + }); + } + + // Make a graph for this resource (after assigning) + function makeGraph(key) { + return new TimelineGraph( + key, + assignments[key], + renderer + ); + } + + // Used to filter down to swimlanes which need graphs + function needsGraph(swimlane) { + // Only show swimlanes with graphs & resources to graph + return swimlane.graph() && + swimlane.domainObject.hasCapability('graph'); + } + + // Create graphs according to assignments that have been built + function createGraphs() { + // Only refresh graphs if our assignments actually changed + if (!deepEquals(cachedAssignments, assignments)) { + // Make new graphs + graphs = Object.keys(assignments).sort().map(makeGraph); + // Save resource->color->object assignments + cachedAssignments = assignments; + } else { + // Just refresh the existing graphs + graphs.forEach(function (graph) { + graph.refresh(); + }); + } + } + + // Build up list of assignments, then create graphs + $q.all(swimlanes.filter(needsGraph).map(buildAssignments)) + .then(createGraphs); + } + + return { + /** + * Populate (or re-populate) the list of available resource + * graphs, based on the provided list of swimlanes (and their + * current state.) + * @param {TimelineSwimlane[]} swimlanes the swimlanes to use + */ + populate: populate, + /** + * Get the current list of displayable resource graphs. + * @returns {TimelineGraph[]} the resource graphs + */ + get: function () { + return graphs; + } + }; + } + + return TimelineGraphPopulator; + + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/graph/TimelineGraphRenderer.js b/platform/features/timeline/src/controllers/graph/TimelineGraphRenderer.js new file mode 100644 index 0000000000..d19e81ceaa --- /dev/null +++ b/platform/features/timeline/src/controllers/graph/TimelineGraphRenderer.js @@ -0,0 +1,62 @@ +/*global define,Float32Array*/ + +define( + [], + function () { + 'use strict'; + + /** + * Responsible for preparing data for display by + * `mct-chart` in a timeline's resource graph. + * @constructor + */ + function TimelineGraphRenderer() { + return { + /** + * Render a resource utilization to a Float32Array, + * to be passed to WebGL for display. + * @param {ResourceGraph} graph the resource utilization + * @returns {Float32Array} the rendered buffer + */ + render: function (graph) { + var count = graph.getPointCount(), + buffer = new Float32Array(count * 2), + i; + + // Populate the buffer + for (i = 0; i < count; i += 1) { + buffer[i * 2] = graph.getDomainValue(i); + buffer[i * 2 + 1] = graph.getRangeValue(i); + } + + return buffer; + }, + /** + * Convert an HTML color (in #-prefixed 6-digit hexadecimal) + * to an array of floating point values in a range of 0.0-1.0. + * An alpha element is included to facilitate display in an + * `mct-chart` (which uses WebGL.) + * @param {string} the color + * @returns {number[]} the same color, in floating-point format + */ + decode: function (color) { + // Check for bad input, default to black if needed + color = /^#[A-Fa-f0-9]{6}$/.test(color) ? color : "#000000"; + + // Pull out R, G, B hex values + return [ + color.substring(1, 3), + color.substring(3, 5), + color.substring(5, 7) + ].map(function (c) { + // Hex -> number + return parseInt(c, 16) / 255; + }).concat([1]); // Add the alpha channel + } + }; + } + + return TimelineGraphRenderer; + + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/swimlane/TimelineColorAssigner.js b/platform/features/timeline/src/controllers/swimlane/TimelineColorAssigner.js new file mode 100644 index 0000000000..ceb629e2e5 --- /dev/null +++ b/platform/features/timeline/src/controllers/swimlane/TimelineColorAssigner.js @@ -0,0 +1,101 @@ +/*global define*/ +define( + [], + function () { + "use strict"; + + var COLOR_OPTIONS = [ + "#20b2aa", + "#9acd32", + "#ff8c00", + "#d2b48c", + "#40e0d0", + "#4169ff", + "#ffd700", + "#6a5acd", + "#ee82ee", + "#cc9966", + "#99cccc", + "#66cc33", + "#ffcc00", + "#ff6633", + "#cc66ff", + "#ff0066", + "#ffff00", + "#800080", + "#00868b", + "#008a00", + "#ff0000", + "#0000ff", + "#f5deb3", + "#bc8f8f", + "#4682b4", + "#ffafaf", + "#43cd80", + "#cdc1c5", + "#a0522d", + "#6495ed" + ], + // Fall back to black, as "no more colors available" + FALLBACK_COLOR = "#000000"; + + /** + * Responsible for choosing unique colors for the resource + * graph listing of a timeline view. Supports TimelineController. + * @constructor + * @param colors an object to store color configuration into; + * typically, this should be a property from the view's + * configuration, but TimelineSwimlane manages this. + */ + function TimelineColorAssigner(colors) { + // Find an unused color + function freeColor() { + // Set of used colors + var set = {}, found; + + // Build up a set of used colors + Object.keys(colors).forEach(function (id) { + set[colors[id]] = true; + }); + + // Find an unused color + COLOR_OPTIONS.forEach(function (c) { + found = (!set[c] && !found) ? c : found; + }); + + // Provide the color + return found || FALLBACK_COLOR; + } + + return { + /** + * Get the current color assignment. + * @param {string} id the id to which the color is assigned + */ + get: function (id) { + return colors[id]; + }, + /** + * Assign a new color to this id. If no color is specified, + * an unused color will be chosen. + * @param {string} id the id to which the color is assigned + * @param {string} [color] the new color to assign + */ + assign: function (id, color) { + colors[id] = typeof color === 'string' ? color : freeColor(); + }, + /** + * Release the color assignment for this id. That id will + * no longer have a color associated with it, and its color + * will be free to use in subsequent calls. + * @param {string} id the id whose color should be released + */ + release: function (id) { + delete colors[id]; + } + }; + } + + return TimelineColorAssigner; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/swimlane/TimelineProxy.js b/platform/features/timeline/src/controllers/swimlane/TimelineProxy.js new file mode 100644 index 0000000000..055af7335f --- /dev/null +++ b/platform/features/timeline/src/controllers/swimlane/TimelineProxy.js @@ -0,0 +1,58 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Selection proxy for the Timeline view. Implements + * behavior associated with the Add button in the + * timeline's toolbar. + * @constructor + */ + function TimelineProxy(domainObject, selection) { + var actionMap = {}; + + // Populate available Create actions for this domain object + function populateActionMap(domainObject) { + var actionCapability = domainObject.getCapability('action'), + actions = actionCapability ? + actionCapability.getActions('create') : []; + actions.forEach(function (action) { + actionMap[action.getMetadata().type] = action; + }); + } + + // Populate available actions based on current selection + // (defaulting to object-in-view if there is none.) + function populateForSelection() { + var swimlane = selection && selection.get(), + selectedObject = swimlane && swimlane.domainObject; + populateActionMap(selectedObject || domainObject); + } + + populateActionMap(domainObject); + + return { + /** + * Add a domain object of the specified type. + * @param {string} type the type of domain object to add + */ + add: function (type) { + // Update list of create actions; this needs to reflect + // the current selection so that Save in defaults + // appropriately. + populateForSelection(); + + // Create an object of that type + if (actionMap[type]) { + return actionMap[type].perform(); + } + } + }; + } + + return TimelineProxy; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/swimlane/TimelineSwimlane.js b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlane.js new file mode 100644 index 0000000000..045987a2d8 --- /dev/null +++ b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlane.js @@ -0,0 +1,156 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Describes a swimlane in a timeline view. This will be + * used directly from timeline view. + * + * Only general properties of swimlanes are included here. + * Since swimlanes are also directly selected and exposed to the + * toolbar, the TimelineSwimlaneDecorator should also be used + * to add additional properties to specific swimlanes. + * + * @constructor + * @param {DomainObject} domainObject the represented object + * @param {TimelineColorAssigner} assigner color assignment handler + * @param configuration the view's configuration object + * @param {TimelineSwimlane} parent the parent swim lane (if any) + */ + function TimelineSwimlane(domainObject, assigner, configuration, parent, index) { + var id = domainObject.getId(), + highlight = false, // Drop highlight (middle) + highlightBottom = false, // Drop highlight (lower) + idPath = (parent ? parent.idPath : []).concat([domainObject.getId()]), + depth = parent ? (parent.depth + 1) : 0, + timespan, + path = (!parent || !parent.parent) ? "" : parent.path + + //(parent.path.length > 0 ? " / " : "") + + parent.domainObject.getModel().name + + " > "; + + // Look up timespan for this object + domainObject.useCapability('timespan').then(function (t) { + timespan = t; + }); + + return { + /** + * Check if this swimlane is currently visible. (That is, + * check to see if its parents are expanded.) + * @returns {boolean} true if it is visible + */ + visible: function () { + return !parent || (parent.expanded && parent.visible()); + }, + /** + * Show the Edit Properties dialog. + */ + properties: function () { + return domainObject.getCapability("action").perform("properties"); + }, + /** + * Toggle inclusion of this swimlane's represented object in + * the resource graph area. + */ + toggleGraph: function () { + configuration.graph = configuration.graph || {}; + configuration.graph[id] = !configuration.graph[id]; + // Assign or release legend color + assigner[configuration.graph[id] ? 'assign' : 'release'](id); + }, + /** + * Get (or set, if an argument is provided) the flag which + * determines if the object in this swimlane is included in + * the set of active resource graphs. + * @param {boolean} [value] the state to set (if setting) + * @returns {boolean} true if included; otherwise false + */ + graph: function (value) { + // Set if an argument was provided + if (arguments.length > 0) { + configuration.graph = configuration.graph || {}; + configuration.graph[id] = !!value; + // Assign or release the legend color + assigner[value ? 'assign' : 'release'](id); + } + // Provide the current state + return (configuration.graph || {})[id]; + }, + /** + * Get (or set, if an argument is provided) the color + * associated with this swimlane when its contents are + * included in the set of active resource graphs. + * @param {string} [value] the color to set (if setting) + * @returns {string} the color for resource graphing + */ + color: function (value) { + // Set if an argument was provided + if (arguments.length > 0) { + // Defer to the color assigner + assigner.assign(id, value); + } + // Provide the current value + return assigner.get(id); + }, + /** + * Get (or set, if an argument is provided) the drag + * highlight state for this swimlane. True means the body + * of the swimlane should be highlighted for drop into. + */ + highlight: function (value) { + // Set if an argument was provided + if (arguments.length > 0) { + highlight = value; + } + // Provide current value + return highlight; + }, + /** + * Get (or set, if an argument is provided) the drag + * highlight state for this swimlane. True means the body + * of the swimlane should be highlighted for drop after. + */ + highlightBottom: function (value) { + // Set if an argument was provided + if (arguments.length > 0) { + highlightBottom = value; + } + // Provide current value + return highlightBottom; + }, + /** + * Check if a swimlane exceeds the bounds of its parent. + * @returns {boolean} true if there is a bounds violation + */ + exceeded: function () { + var parentTimespan = parent && parent.timespan(); + return timespan && parentTimespan && + (timespan.getStart() < parentTimespan.getStart() || + timespan.getEnd() > parentTimespan.getEnd()); + }, + /** + * Get the timespan associated with this swimlane + */ + timespan: function () { + return timespan; + }, + // Expose domain object, expansion state, indentation depth + domainObject: domainObject, + expanded: true, + depth: depth, + path: path, + id: id, + idPath: idPath, + parent: parent, + index: index, + children: [] // Populated by populator + }; + } + + return TimelineSwimlane; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDecorator.js b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDecorator.js new file mode 100644 index 0000000000..28fabbb94b --- /dev/null +++ b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDecorator.js @@ -0,0 +1,93 @@ +/*global define*/ + +define( + ['./TimelineSwimlaneDropHandler'], + function (TimelineSwimlaneDropHandler) { + "use strict"; + + var ACTIVITY_RELATIONSHIP = "modes"; + + /** + * Adds optional methods to TimelineSwimlanes, in order + * to conditionally make available options in the toolbar. + * @constructor + */ + function TimelineSwimlaneDecorator(swimlane, selection) { + var domainObject = swimlane && swimlane.domainObject, + model = (domainObject && domainObject.getModel()) || {}, + mutator = domainObject && domainObject.getCapability('mutation'), + persister = domainObject && domainObject.getCapability('persistence'), + type = domainObject && domainObject.getCapability('type'), + dropHandler = new TimelineSwimlaneDropHandler(swimlane); + + // Activity Modes dialog + function modes(value) { + // Can be used as a setter... + if (arguments.length > 0 && Array.isArray(value)) { + // Update the relationships + mutator.mutate(function (model) { + model.relationships = model.relationships || {}; + model.relationships[ACTIVITY_RELATIONSHIP] = value; + }).then(persister.persist); + } + // ...otherwise, use as a getter + return (model.relationships || {})[ACTIVITY_RELATIONSHIP] || []; + } + + // Activity Link dialog + function link(value) { + // Can be used as a setter... + if (arguments.length > 0 && (typeof value === 'string') && + value !== model.link) { + // Update the link + mutator.mutate(function (model) { + model.link = value; + }).then(persister.persist); + } + return model.link; + } + + // Fire the Remove action + function remove() { + return domainObject.getCapability("action").perform("remove"); + } + + // Select the current swimlane + function select() { + selection.select(swimlane); + } + + // Check if the swimlane is selected + function selected() { + return selection.get() === swimlane; + } + + // Activities should have the Activity Modes and Activity Link dialog + if (type && type.instanceOf("warp.activity") && mutator && persister) { + swimlane.modes = modes; + swimlane.link = link; + } + + // Everything but the top-level object should have Remove + if (swimlane.parent) { + swimlane.remove = remove; + } + + // We're in edit mode, if a selection is available + if (selection) { + // Add shorthands to select, and check for selection + swimlane.select = select; + swimlane.selected = selected; + } + + // Expose drop handlers (which needed a reference to the swimlane) + swimlane.allowDropIn = dropHandler.allowDropIn; + swimlane.allowDropAfter = dropHandler.allowDropAfter; + swimlane.drop = dropHandler.drop; + + return swimlane; + } + + return TimelineSwimlaneDecorator; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDropHandler.js b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDropHandler.js new file mode 100644 index 0000000000..0c45531572 --- /dev/null +++ b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDropHandler.js @@ -0,0 +1,186 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Handles drop (from drag-and-drop) initiated changes to a swimlane. + * @constructor + */ + function TimelineSwimlaneDropHandler(swimlane) { + // Utility function; like $q.when, but synchronous (to reduce + // performance impact when wrapping synchronous values) + function asPromise(value) { + return (value && value.then) ? value : { + then: function (callback) { + return asPromise(callback(value)); + } + }; + } + + // Check if we are in edit mode + function inEditMode() { + return swimlane.domainObject.hasCapability("editor"); + } + + // Boolean and (for reduce below) + function or(a, b) { + return a || b; + } + + // Check if pathA entirely contains pathB + function pathContains(swimlane, id) { + // Check if id at a specific index matches (for map below) + function matches(pathId) { + return pathId === id; + } + + // Path A contains Path B if it is longer, and all of + // B's ids match the ids in A. + return swimlane.idPath.map(matches).reduce(or, false); + } + + // Check if a swimlane contains a child with the specified id + function contains(swimlane, id) { + // Check if a child swimlane has a matching domain object id + function matches(child) { + return child.domainObject.getId() === id; + } + + // Find any one child id that matches this id + return swimlane.children.map(matches).reduce(or, false); + } + + // Remove a domain object from its current location + function remove(domainObject) { + return domainObject && + domainObject.getCapability('action').perform('remove'); + } + + // Initiate mutation of a domain object + function doMutate(domainObject, mutator) { + return asPromise( + domainObject.useCapability("mutation", mutator) + ).then(function () { + // Persist the results of mutation + var persistence = domainObject.getCapability("persistence"); + if (persistence) { + // Persist the changes + persistence.persist(); + } + }); + } + + // Check if this swimlane is in a state where a drop-after will + // act as a drop-into-at-first position (expanded and non-empty) + function expandedForDropInto() { + return swimlane.expanded && swimlane.children.length > 0; + } + + // Check if the swimlane is ready to accept a drop-into + // (instead of drop-after) + function isDropInto() { + return swimlane.highlight() || expandedForDropInto(); + } + + // Choose an index for insertion in a domain object's composition + function chooseTargetIndex(id, offset, composition) { + return Math.max( + Math.min( + (composition || []).indexOf(id) + offset, + (composition || []).length + ), + 0 + ); + } + + // Insert an id into target's composition + function insert(id, target, indexOffset) { + var myId = swimlane.domainObject.getId(); + return doMutate(target, function (model) { + model.composition.splice( + chooseTargetIndex(myId, indexOffset, model.composition), + 0, + id + ); + }); + } + + // Check if a compose action is allowed for the object in this + // swimlane (we handle the link differently to set the index, + // but check for the existence of the action to invole the + // relevant policies.) + function allowsCompose(swimlane, domainObject) { + var actionCapability = + swimlane.domainObject.getCapability('action'); + return actionCapability && actionCapability.getActions({ + key: 'compose', + selectedObject: domainObject + }).length > 0; + } + + return { + /** + * Check if a drop-into should be allowed for this swimlane, + * for the provided domain object identifier. + * @param {string} id identifier for the domain object to be + * dropped + * @returns {boolean} true if this should be allowed + */ + allowDropIn: function (id, domainObject) { + return inEditMode() && + !pathContains(swimlane, id) && + !contains(swimlane, id) && + allowsCompose(swimlane, domainObject); + }, + /** + * Check if a drop-after should be allowed for this swimlane, + * for the provided domain object identifier. + * @param {string} id identifier for the domain object to be + * dropped + * @returns {boolean} true if this should be allowed + */ + allowDropAfter: function (id, domainObject) { + var target = expandedForDropInto() ? + swimlane : swimlane.parent; + return inEditMode() && + target && + !pathContains(target, id) && + allowsCompose(target, domainObject); + }, + /** + * Drop the provided domain object into a timeline. This is + * provided as a mandatory id, and an optional domain object + * instance; if the latter is provided, it will be removed + * from its parent before being added. (This is specifically + * to support moving Activity objects around within a Timeline.) + * @param {string} id the identifier for the domain object + * @param {DomainObject} [domainObject] the object itself + */ + drop: function (id, domainObject) { + // Get the desired drop object, and destination index + var dropInto = isDropInto(), + dropTarget = dropInto ? + swimlane.domainObject : + swimlane.parent.domainObject, + dropIndexOffset = (!dropInto) ? 1 : + (swimlane.expanded && swimlane.highlightBottom()) ? + Number.NEGATIVE_INFINITY : + Number.POSITIVE_INFINITY; + + if (swimlane.highlight() || swimlane.highlightBottom()) { + // Remove the domain object from its original location... + return asPromise(remove(domainObject)).then(function () { + // ...then insert it at its new location. + insert(id, dropTarget, dropIndexOffset); + }); + } + } + }; + } + + return TimelineSwimlaneDropHandler; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/controllers/swimlane/TimelineSwimlanePopulator.js b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlanePopulator.js new file mode 100644 index 0000000000..24c72582d7 --- /dev/null +++ b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlanePopulator.js @@ -0,0 +1,164 @@ +/*global define*/ + +define( + [ + './TimelineSwimlane', + './TimelineSwimlaneDecorator', + './TimelineColorAssigner', + './TimelineProxy' + ], + function ( + TimelineSwimlane, + TimelineSwimlaneDecorator, + TimelineColorAssigner, + TimelineProxy + ) { + 'use strict'; + + /** + * Populates and maintains a list of swimlanes for a given + * timeline view. + * @constructor + */ + function TimelineSwimlanePopulator(objectLoader, configuration, selection) { + var swimlanes = [], + start = Number.POSITIVE_INFINITY, + end = Number.NEGATIVE_INFINITY, + colors = (configuration.colors || {}), + assigner = new TimelineColorAssigner(colors); + + // Track extremes of start/end times + function trackStartEnd(timespan) { + if (timespan) { + start = Math.min(start, timespan.getStart()); + end = Math.max(end, timespan.getEnd()); + } + } + + // Add domain object (and its subgraph) in as swimlanes + function populateSwimlanes(subgraph, parent, index) { + var domainObject = subgraph.domainObject, + swimlane; + + // For the recursive step + function populate(childSubgraph, index) { + populateSwimlanes(childSubgraph, swimlane, index); + } + + // Make sure we have a valid object instance... + if (domainObject) { + // Create the new swimlane + swimlane = new TimelineSwimlaneDecorator(new TimelineSwimlane( + domainObject, + assigner, + configuration, + parent, + index || 0 + ), selection); + // Track start & end times of this domain object + domainObject.useCapability('timespan').then(trackStartEnd); + // Add it to our list + swimlanes.push(swimlane); + // Fill in parent's children + ((parent || {}).children || []).push(swimlane); + // Add in children + subgraph.composition.forEach(populate); + } + } + + // Restore a selection + function reselect(path, candidates, depth) { + // Next ID on the path + var next = path[depth || 0]; + + // Ensure a default + depth = depth || 0; + + // Search through this layer of candidates to see + // if they might contain our selection (based on id path) + candidates.forEach(function (swimlane) { + // Check if we're on the right path... + if (swimlane.id === next) { + // Do we still have ids to check? + if (depth < path.length - 1) { + // Yes, so recursively explore that path + reselect(path, swimlane.children, depth + 1); + } else { + // Nope, we found the object to select + selection.select(swimlane); + } + } + }); + } + + // Handle population of swimlanes + function recalculateSwimlanes(domainObject) { + function populate(subgraph) { + // Cache current selection state during refresh + var selected = selection && selection.get(), + selectedIdPath = selected && selected.idPath; + + // Clear existing swimlanes + swimlanes = []; + + // Build new set of swimlanes + populateSwimlanes(subgraph); + + // Restore selection, if there was one + if (selectedIdPath && swimlanes.length > 0) { + reselect(selectedIdPath, [swimlanes[0]]); + } + } + + // Repopulate swimlanes for this object + if (!domainObject) { + populate({}); + } else { + objectLoader.load(domainObject, 'timespan').then(populate); + } + + // Set the selection proxy as well (for the Add button) + if (selection) { + selection.proxy( + domainObject && new TimelineProxy(domainObject, selection) + ); + } + } + + // Ensure colors are exposed in configuration + configuration.colors = colors; + + return { + /** + * Update list of swimlanes to match those reachable from this + * object. + * @param {DomainObject} the timeline being viewed + */ + populate: recalculateSwimlanes, + /** + * Get a list of swimlanes for this timeline view. + * @returns {TimelineSwimlane[]} current swimlanes + */ + get: function () { + return swimlanes; + }, + /** + * Get the first timestamp in the set of swimlanes. + * @returns {number} first timestamp + */ + start: function () { + return start; + }, + /** + * Get the last timestamp in the set of swimlanes. + * @returns {number} first timestamp + */ + end: function () { + return end; + } + }; + } + + return TimelineSwimlanePopulator; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/directives/SwimlaneDragConstants.js b/platform/features/timeline/src/directives/SwimlaneDragConstants.js new file mode 100644 index 0000000000..d88b466dd6 --- /dev/null +++ b/platform/features/timeline/src/directives/SwimlaneDragConstants.js @@ -0,0 +1,20 @@ +/*global define*/ + +define({ + /** + * The string identifier for the data type used for drag-and-drop + * composition of domain objects. (e.g. in event.dataTransfer.setData + * calls.) + */ + MCT_DRAG_TYPE: 'mct-domain-object-id', + /** + * The string identifier for the data type used for drag-and-drop + * composition of domain objects, by object instance (passed through + * the dndService) + */ + MCT_EXTENDED_DRAG_TYPE: 'mct-domain-object', + /** + * String identifier for swimlanes being dragged. + */ + WARP_SWIMLANE_DRAG_TYPE: 'warp-swimlane' +}); \ No newline at end of file diff --git a/platform/features/timeline/src/directives/WARPSwimlaneDrag.js b/platform/features/timeline/src/directives/WARPSwimlaneDrag.js new file mode 100644 index 0000000000..83a4e41bb6 --- /dev/null +++ b/platform/features/timeline/src/directives/WARPSwimlaneDrag.js @@ -0,0 +1,47 @@ +/*global define*/ + +define( + ['./SwimlaneDragConstants'], + function (SwimlaneDragConstants) { + "use strict"; + + /** + * Defines the `warp-swimlane-drag` directive. When a drag is initiated + * form an element with this attribute, the swimlane being dragged + * (identified by the value of this attribute, as an Angular expression) + * will be exported to the `dndService` as part of the active drag-drop + * state. + * @param {DndService} dndService drag-and-drop service + */ + function WARPSwimlaneDrag(dndService) { + function link(scope, element, attrs) { + // Look up the swimlane from the provided expression + function swimlane() { + return scope.$eval(attrs.warpSwimlaneDrag); + } + // When drag starts, publish via dndService + element.on('dragstart', function () { + dndService.setData( + SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE, + swimlane() + ); + }); + // When drag ends, clear via dndService + element.on('dragend', function () { + dndService.removeData( + SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE + ); + }); + } + + return { + // Applies to attributes + restrict: "A", + // Link using above function + link: link + }; + } + + return WARPSwimlaneDrag; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/directives/WARPSwimlaneDrop.js b/platform/features/timeline/src/directives/WARPSwimlaneDrop.js new file mode 100644 index 0000000000..916fb82bd7 --- /dev/null +++ b/platform/features/timeline/src/directives/WARPSwimlaneDrop.js @@ -0,0 +1,106 @@ +/*global define*/ + +define( + ['./SwimlaneDragConstants'], + function (SwimlaneDragConstants) { + "use strict"; + + /** + * Defines the `warp-swimlane-drop` directive. When a drop occurs + * on an element with this attribute, the swimlane targeted by the drop + * (identified by the value of this attribute, as an Angular expression) + * will receive the dropped domain object (at which point it can handle + * the drop, typically by inserting/reordering.) + * @param {DndService} dndService drag-and-drop service + */ + function WARPSwimlaneDrop(dndService) { + + // Handle dragover events + function dragOver(e, element, swimlane) { + var event = (e || {}).originalEvent || e, + height = element[0].offsetHeight, + rect = element[0].getBoundingClientRect(), + offset = event.pageY - rect.top, + dataTransfer = event.dataTransfer, + id = dndService.getData( + SwimlaneDragConstants.MCT_DRAG_TYPE + ), + draggedObject = dndService.getData( + SwimlaneDragConstants.MCT_EXTENDED_DRAG_TYPE + ); + + if (id) { + // TODO: Vary this based on modifier keys + event.dataTransfer.dropEffect = 'move'; + + // Set the swimlane's drop highlight state; top 75% is + // for drop-into, bottom 25% is for drop-after. + swimlane.highlight( + offset < (height * 0.75) && + swimlane.allowDropIn(id, draggedObject) + ); + swimlane.highlightBottom( + offset >= (height * 0.75) && + swimlane.allowDropAfter(id, draggedObject) + ); + + // Indicate that we will accept the drag + if (swimlane.highlight() || swimlane.highlightBottom()) { + event.preventDefault(); // Required in Chrome? + return false; + } + } + } + + // Handle drop events + function drop(e, element, swimlane) { + var event = (e || {}).originalEvent || e, + dataTransfer = event.dataTransfer, + id = dataTransfer.getData( + SwimlaneDragConstants.MCT_DRAG_TYPE + ), + draggedSwimlane = dndService.getData( + SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE + ); + + if (id) { + // Delegate the drop to the swimlane itself + swimlane.drop(id, (draggedSwimlane || {}).domainObject); + } + + // Clear the swimlane highlights + swimlane.highlight(false); + swimlane.highlightBottom(false); + } + + function link(scope, element, attrs) { + // Lookup swimlane by evaluating this attribute + function swimlane() { + return scope.$eval(attrs.warpSwimlaneDrop); + } + // Handle dragover + element.on('dragover', function (e) { + dragOver(e, element, swimlane()); + }); + // Handle drops + element.on('drop', function (e) { + drop(e, element, swimlane()); + }); + // Clear highlights when drag leaves this swimlane + element.on('dragleave', function () { + swimlane().highlight(false); + swimlane().highlightBottom(false); + }); + } + + return { + // Applies to attributes + restrict: "A", + // Link using above function + link: link + }; + } + + return WARPSwimlaneDrop; + } +); \ No newline at end of file diff --git a/platform/features/timeline/src/services/ObjectLoader.js b/platform/features/timeline/src/services/ObjectLoader.js new file mode 100644 index 0000000000..38596557bf --- /dev/null +++ b/platform/features/timeline/src/services/ObjectLoader.js @@ -0,0 +1,114 @@ +/*global define*/ + +define( + [], + function () { + 'use strict'; + + /** + * The ObjectLoader is a utility service for loading subgraphs + * of the composition hierarchy, starting at a provided object, + * and optionally filtering out objects which fail to meet certain + * criteria. + * @constructor + */ + function ObjectLoader($q) { + + // Build up an object containing id->object pairs + // for the subset of the graph that is relevant. + function loadSubGraph(domainObject, criterion) { + var result = { domainObject: domainObject, composition: [] }, + visiting = {}, + filter; + + // Check object existence (for criterion-less filtering) + function exists(domainObject) { + return !!domainObject; + } + + // Check for capability matching criterion + function hasCapability(domainObject) { + return domainObject && domainObject.hasCapability(criterion); + } + + // For the recursive step... + function loadSubGraphFor(childObject) { + return loadSubGraph(childObject, filter); + } + + // Store loaded subgraphs into the result + function storeSubgraphs(subgraphs) { + result.composition = subgraphs; + } + + // Avoid infinite recursion + function notVisiting(domainObject) { + return !visiting[domainObject.getId()]; + } + + // Put the composition of this domain object into the result + function mapIntoResult(composition) { + return $q.all( + composition.filter(filter).filter(notVisiting) + .map(loadSubGraphFor) + ).then(storeSubgraphs); + } + + // Used to give the final result after promise chaining + function giveResult() { + // Stop suppressing recursive visitation + visiting[domainObject.getId()] = true; + // And return the expecting result value + return result; + } + + // Load composition for + function loadComposition() { + // First, record that we're looking at this domain + // object to detect cycles and avoid an infinite loop + visiting[domainObject.getId()] = true; + // Look up the composition, store it to the graph structure + return domainObject.useCapability('composition') + .then(mapIntoResult) + .then(giveResult); + } + + // Choose the filter function to use + filter = typeof criterion === 'function' ? criterion : + (typeof criterion === 'string' ? hasCapability : + exists); + + // Load child hierarchy, then provide the flat list + return domainObject.hasCapability('composition') ? + loadComposition() : $q.when(result); + } + + return { + /** + * Load domain objects contained in the subgraph of the + * composition hierarchy which starts at the specified + * domain object, optionally pruning out objects (and their + * subgraphs) which match a certain criterion. + * The result is given as a promise for an object containing + * key-value pairs, where keys are domain object identifiers + * and values are domain objects in the subgraph. + * The criterion may be omitted (in which case no pruning is + * done) or specified as a string, in which case it will be + * treated as the name of a required capability, or specified + * as a function, which should return a truthy/falsy value + * when called with a domain object to indicate whether or + * not it should be included in the result set. + * + * @param {DomainObject} domainObject the domain object to + * start from + * @param {string|Function} [criterion] the criterion used + * to prune domain objects + * @returns {Promise} a promise for loaded domain objects + */ + load: loadSubGraph + }; + } + + return ObjectLoader; + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/TimelineConstantsSpec.js b/platform/features/timeline/test/TimelineConstantsSpec.js new file mode 100644 index 0000000000..303bb6b2d7 --- /dev/null +++ b/platform/features/timeline/test/TimelineConstantsSpec.js @@ -0,0 +1,14 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../src/TimelineConstants'], + function (TimelineConstants) { + "use strict"; + describe("The set of Timeline constants", function () { + it("specifies a handle width", function () { + expect(TimelineConstants.HANDLE_WIDTH) + .toEqual(jasmine.any(Number)); + }); + }); + } +); diff --git a/platform/features/timeline/test/TimelineFormatterSpec.js b/platform/features/timeline/test/TimelineFormatterSpec.js new file mode 100644 index 0000000000..01ca2f7204 --- /dev/null +++ b/platform/features/timeline/test/TimelineFormatterSpec.js @@ -0,0 +1,41 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../src/TimelineFormatter'], + function (TimelineFormatter) { + 'use strict'; + + var SECOND = 1000, + MINUTE = SECOND * 60, + HOUR = MINUTE * 60, + DAY = HOUR * 24; + + describe("The timeline formatter", function () { + var formatter; + + beforeEach(function () { + formatter = new TimelineFormatter(); + }); + + it("formats durations with seconds", function () { + expect(formatter.format(SECOND)).toEqual("000 00:00:01.000"); + }); + + it("formats durations with milliseconds", function () { + expect(formatter.format(SECOND + 42)).toEqual("000 00:00:01.042"); + }); + + it("formats durations with days", function () { + expect(formatter.format(3 * DAY + SECOND)).toEqual("003 00:00:01.000"); + }); + + it("formats durations with hours", function () { + expect(formatter.format(DAY + HOUR * 11 + SECOND)).toEqual("001 11:00:01.000"); + }); + + it("formats durations with minutes", function () { + expect(formatter.format(HOUR + MINUTE * 21)).toEqual("000 01:21:00.000"); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/ActivityTimespanCapabilitySpec.js b/platform/features/timeline/test/capabilities/ActivityTimespanCapabilitySpec.js new file mode 100644 index 0000000000..9728fd2181 --- /dev/null +++ b/platform/features/timeline/test/capabilities/ActivityTimespanCapabilitySpec.js @@ -0,0 +1,71 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/ActivityTimespanCapability'], + function (ActivityTimespanCapability) { + 'use strict'; + + describe("An Activity's timespan capability", function () { + var mockQ, + mockDomainObject, + capability; + + function asPromise(v) { + return (v || {}).then ? v : { + then: function (callback) { + return asPromise(callback(v)); + } + }; + } + + beforeEach(function () { + mockQ = jasmine.createSpyObj('$q', ['when']); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getModel', 'getCapability' ] + ); + + mockQ.when.andCallFake(asPromise); + mockDomainObject.getModel.andReturn({ + start: { + timestamp: 42000, + epoch: "TEST" + }, + duration: { + timestamp: 12321 + } + }); + + capability = new ActivityTimespanCapability( + mockQ, + mockDomainObject + ); + }); + + it("applies only to activity objects", function () { + expect(ActivityTimespanCapability.appliesTo({ + type: 'warp.activity' + })).toBeTruthy(); + expect(ActivityTimespanCapability.appliesTo({ + type: 'folder' + })).toBeFalsy(); + }); + + it("provides timespan based on model", function () { + var mockCallback = jasmine.createSpy('callback'); + capability.invoke().then(mockCallback); + // We verify other methods in ActivityTimespanSpec, + // so just make sure we got something that looks right. + expect(mockCallback).toHaveBeenCalledWith({ + getStart: jasmine.any(Function), + getEnd: jasmine.any(Function), + getDuration: jasmine.any(Function), + setStart: jasmine.any(Function), + setEnd: jasmine.any(Function), + setDuration: jasmine.any(Function), + getEpoch: jasmine.any(Function) + }); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/ActivityTimespanSpec.js b/platform/features/timeline/test/capabilities/ActivityTimespanSpec.js new file mode 100644 index 0000000000..da256ed12f --- /dev/null +++ b/platform/features/timeline/test/capabilities/ActivityTimespanSpec.js @@ -0,0 +1,80 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/ActivityTimespan'], + function (ActivityTimespan) { + 'use strict'; + + describe("An Activity's timespan", function () { + var testModel, + mutatorModel, + mockMutation, + timespan; + + beforeEach(function () { + testModel = { + start: { + timestamp: 42000, + epoch: "TEST" + }, + duration: { + timestamp: 12321 + } + }; + // Provide a cloned model for mutation purposes + // It is important to distinguish mutation made to + // the model provided via the mutation capability from + // changes made to the model directly (the latter is + // not intended usage.) + mutatorModel = JSON.parse(JSON.stringify(testModel)); + mockMutation = jasmine.createSpyObj("mutation", ["mutate"]); + mockMutation.mutate.andCallFake(function (mutator) { + mutator(mutatorModel); + }); + timespan = new ActivityTimespan(testModel, mockMutation); + }); + + it("provides a start time", function () { + expect(timespan.getStart()).toEqual(42000); + }); + + it("provides an end time", function () { + expect(timespan.getEnd()).toEqual(54321); + }); + + it("provides duration", function () { + expect(timespan.getDuration()).toEqual(12321); + }); + + it("provides an epoch", function () { + expect(timespan.getEpoch()).toEqual("TEST"); + }); + + it("sets start time using mutation capability", function () { + timespan.setStart(52000); + expect(mutatorModel.start.timestamp).toEqual(52000); + // Should have also changed duration to preserve end + expect(mutatorModel.duration.timestamp).toEqual(2321); + // Original model should still be the same + expect(testModel.start.timestamp).toEqual(42000); + }); + + it("sets end time using mutation capability", function () { + timespan.setEnd(44000); + // Should have also changed duration to preserve end + expect(mutatorModel.duration.timestamp).toEqual(2000); + // Original model should still be the same + expect(testModel.duration.timestamp).toEqual(12321); + }); + + it("sets duration using mutation capability", function () { + timespan.setDuration(8000); + // Should have also changed duration to preserve end + expect(mutatorModel.duration.timestamp).toEqual(8000); + // Original model should still be the same + expect(testModel.duration.timestamp).toEqual(12321); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/ActivityUtilizationSpec.js b/platform/features/timeline/test/capabilities/ActivityUtilizationSpec.js new file mode 100644 index 0000000000..6450f08491 --- /dev/null +++ b/platform/features/timeline/test/capabilities/ActivityUtilizationSpec.js @@ -0,0 +1,20 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/ActivityUtilization'], + function (ActivityUtilization) { + 'use strict'; + + describe("An Activity's resource utilization", function () { + + // Placeholder; WTD-918 will implement + it("has the expected interface", function () { + var utilization = new ActivityUtilization(); + expect(utilization.getPointCount()).toEqual(jasmine.any(Number)); + expect(utilization.getDomainValue()).toEqual(jasmine.any(Number)); + expect(utilization.getRangeValue()).toEqual(jasmine.any(Number)); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/CostCapabilitySpec.js b/platform/features/timeline/test/capabilities/CostCapabilitySpec.js new file mode 100644 index 0000000000..1d9ddbc0e7 --- /dev/null +++ b/platform/features/timeline/test/capabilities/CostCapabilitySpec.js @@ -0,0 +1,60 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/CostCapability'], + function (CostCapability) { + 'use strict'; + + describe("A subsystem mode's cost capability", function () { + var testModel, + capability; + + beforeEach(function () { + var mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getModel', 'getId' ] + ); + + testModel = { + resources: { + abc: -1, + power: 12321, + comms: 42 + } + }; + + mockDomainObject.getModel.andReturn(testModel); + + capability = new CostCapability(mockDomainObject); + }); + + it("provides a list of resource types", function () { + expect(capability.resources()) + .toEqual(['abc', 'comms', 'power']); + }); + + it("provides resource costs", function () { + expect(capability.cost('abc')).toEqual(-1); + expect(capability.cost('power')).toEqual(12321); + expect(capability.cost('comms')).toEqual(42); + }); + + it("provides all resources in a group", function () { + expect(capability.invoke()).toEqual(testModel.resources); + }); + + it("applies to subsystem modes", function () { + expect(CostCapability.appliesTo({ + type: "warp.mode" + })).toBeTruthy(); + expect(CostCapability.appliesTo({ + type: "warp.activity" + })).toBeFalsy(); + expect(CostCapability.appliesTo({ + type: "warp.other" + })).toBeFalsy(); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/CumulativeGraphSpec.js b/platform/features/timeline/test/capabilities/CumulativeGraphSpec.js new file mode 100644 index 0000000000..dd9a797ac9 --- /dev/null +++ b/platform/features/timeline/test/capabilities/CumulativeGraphSpec.js @@ -0,0 +1,67 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/CumulativeGraph'], + function (CumulativeGraph) { + 'use strict'; + + describe("A cumulative resource graph", function () { + var mockGraph, + points, + graph; + + beforeEach(function () { + points = [ 0, 10, -10, -100, 20, 100, 0 ]; + + mockGraph = jasmine.createSpyObj( + 'graph', + [ 'getPointCount', 'getDomainValue', 'getRangeValue' ] + ); + + mockGraph.getPointCount.andReturn(points.length * 2); + mockGraph.getDomainValue.andCallFake(function (i) { + return Math.floor(i / 2) * 100 + 25; + }); + mockGraph.getRangeValue.andCallFake(function (i) { + return points[Math.floor(i / 2) + i % 2]; + }); + + graph = new CumulativeGraph( + mockGraph, + 1000, + 2000, + 1500, + 1 / 10 + ); + }); + + it("accumulates its wrapped instantaneous graph", function () { + // Note that range values are percentages + expect(graph.getDomainValue(0)).toEqual(0); + expect(graph.getRangeValue(0)).toEqual(50); // initial state + expect(graph.getDomainValue(1)).toEqual(25); + expect(graph.getRangeValue(1)).toEqual(50); // initial state + expect(graph.getDomainValue(2)).toEqual(125); + expect(graph.getRangeValue(2)).toEqual(60); // +10 + expect(graph.getDomainValue(3)).toEqual(225); + expect(graph.getRangeValue(3)).toEqual(50); // -10 + expect(graph.getDomainValue(4)).toEqual(275); + expect(graph.getRangeValue(4)).toEqual(0); // -100 (hit bottom) + expect(graph.getDomainValue(5)).toEqual(325); + expect(graph.getRangeValue(5)).toEqual(0); // still at 0... + expect(graph.getDomainValue(6)).toEqual(425); + expect(graph.getRangeValue(6)).toEqual(20); // +20 + expect(graph.getDomainValue(7)).toEqual(505); + expect(graph.getRangeValue(7)).toEqual(100); // +100 + expect(graph.getDomainValue(8)).toEqual(525); + expect(graph.getRangeValue(8)).toEqual(100); // still full + expect(graph.getDomainValue(9)).toEqual(625); + expect(graph.getRangeValue(9)).toEqual(100); // still full + expect(graph.getPointCount()).toEqual(10); + }); + + + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/GraphCapabilitySpec.js b/platform/features/timeline/test/capabilities/GraphCapabilitySpec.js new file mode 100644 index 0000000000..0ec3c7f58c --- /dev/null +++ b/platform/features/timeline/test/capabilities/GraphCapabilitySpec.js @@ -0,0 +1,98 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/GraphCapability'], + function (GraphCapability) { + 'use strict'; + + describe("A Timeline's graph capability", function () { + var mockQ, + mockDomainObject, + testModel, + capability; + + function asPromise(v) { + return (v || {}).then ? v : { + then: function (cb) { + return asPromise(cb(v)); + } + }; + } + + beforeEach(function () { + mockQ = jasmine.createSpyObj('$q', ['when']); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getId', 'getModel', 'useCapability' ] + ); + + testModel = { + type: "warp.activity", + resources: { + abc: 100, + xyz: 42 + } + }; + + mockQ.when.andCallFake(asPromise); + mockDomainObject.getModel.andReturn(testModel); + + capability = new GraphCapability( + mockQ, + mockDomainObject + ); + }); + + it("is applicable to timelines", function () { + expect(GraphCapability.appliesTo({ + type: "warp.timeline" + })).toBeTruthy(); + }); + + it("is applicable to activities", function () { + expect(GraphCapability.appliesTo(testModel)) + .toBeTruthy(); + }); + + it("is not applicable to other objects", function () { + expect(GraphCapability.appliesTo({ + type: "something" + })).toBeFalsy(); + }); + + it("provides one graph per resource type", function () { + var mockCallback = jasmine.createSpy('callback'); + + mockDomainObject.useCapability.andReturn(asPromise([ + { key: "abc", start: 0, end: 15 }, + { key: "abc", start: 0, end: 15 }, + { key: "def", start: 4, end: 15 }, + { key: "xyz", start: 0, end: 20 } + ])); + + capability.invoke().then(mockCallback); + + expect(mockCallback).toHaveBeenCalledWith({ + abc: jasmine.any(Object), + def: jasmine.any(Object), + xyz: jasmine.any(Object) + }); + }); + + it("provides a battery graph for timelines with capacity", function () { + var mockCallback = jasmine.createSpy('callback'); + testModel.capacity = 1000; + testModel.type = "warp.timeline"; + mockDomainObject.useCapability.andReturn(asPromise([ + { key: "power", start: 0, end: 15 } + ])); + capability.invoke().then(mockCallback); + expect(mockCallback).toHaveBeenCalledWith({ + power: jasmine.any(Object), + battery: jasmine.any(Object) + }); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/ResourceGraphSpec.js b/platform/features/timeline/test/capabilities/ResourceGraphSpec.js new file mode 100644 index 0000000000..c6a47c8c55 --- /dev/null +++ b/platform/features/timeline/test/capabilities/ResourceGraphSpec.js @@ -0,0 +1,56 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/ResourceGraph'], + function (ResourceGraph) { + 'use strict'; + + describe("A resource graph capability", function () { + + // Placeholder; WTD-918 will implement + it("has zero points for zero utilization changes", function () { + var graph = new ResourceGraph([]); + expect(graph.getPointCount()).toEqual(0); + }); + + it("creates steps based on resource utilizations", function () { + var graph = new ResourceGraph([ + { start: 5, end: 100, value: 42 }, + { start: 50, end: 120, value: -22 }, + { start: 15, end: 40, value: 30 }, + { start: 150, end: 180, value: -10 } + ]); + + expect(graph.getPointCount()).toEqual(16); + + // Should get two values at every time stamp, for step-like appearance + [ 5, 15, 40, 50, 100, 120, 150, 180].forEach(function (v, i) { + expect(graph.getDomainValue(i * 2)).toEqual(v); + expect(graph.getDomainValue(i * 2 + 1)).toEqual(v); + }); + + // Should also repeat values at subsequent indexes, but offset differently, + // for horizontal spans between steps + [ 0, 42, 72, 42, 20, -22, 0, -10].forEach(function (v, i) { + expect(graph.getRangeValue(i * 2)).toEqual(v); + // Offset backwards; wrap around end of the series + expect(graph.getRangeValue((16 + i * 2 - 1) % 16)).toEqual(v); + }); + }); + + it("filters out zero-duration spikes", function () { + var graph = new ResourceGraph([ + { start: 5, end: 100, value: 42 }, + { start: 100, end: 120, value: -22 }, + { start: 100, end: 180, value: 30 }, + { start: 130, end: 180, value: -10 } + ]); + + // There are only 5 unique timestamps there, so there should + // be 5 steps, for 10 total points + expect(graph.getPointCount()).toEqual(10); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/TimelineTimespanCapabilitySpec.js b/platform/features/timeline/test/capabilities/TimelineTimespanCapabilitySpec.js new file mode 100644 index 0000000000..2208ec0002 --- /dev/null +++ b/platform/features/timeline/test/capabilities/TimelineTimespanCapabilitySpec.js @@ -0,0 +1,115 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/TimelineTimespanCapability'], + function (TimelineTimespanCapability) { + 'use strict'; + + describe("A Timeline's timespan capability", function () { + var mockQ, + mockDomainObject, + mockChildA, + mockChildB, + mockTimespanA, + mockTimespanB, + capability; + + function asPromise(v) { + return (v || {}).then ? v : { + then: function (callback) { + return asPromise(callback(v)); + } + }; + } + + beforeEach(function () { + mockQ = jasmine.createSpyObj('$q', ['when', 'all']); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getModel', 'getCapability', 'useCapability' ] + ); + mockChildA = jasmine.createSpyObj( + 'childA', + [ 'getModel', 'useCapability', 'hasCapability' ] + ); + mockChildB = jasmine.createSpyObj( + 'childA', + [ 'getModel', 'useCapability', 'hasCapability' ] + ); + mockTimespanA = jasmine.createSpyObj( + 'timespanA', + [ 'getEnd' ] + ); + mockTimespanB = jasmine.createSpyObj( + 'timespanB', + [ 'getEnd' ] + ); + + mockQ.when.andCallFake(asPromise); + mockQ.all.andCallFake(function (values) { + var result = []; + function addResult(v) { result.push(v); } + function promiseResult(v) { asPromise(v).then(addResult); } + values.forEach(promiseResult); + return asPromise(result); + }); + mockDomainObject.getModel.andReturn({ + start: { + timestamp: 42000, + epoch: "TEST" + }, + duration: { + timestamp: 12321 + } + }); + mockDomainObject.useCapability.andCallFake(function (c) { + if (c === 'composition') { + return asPromise([ mockChildA, mockChildB ]); + } + }); + mockChildA.hasCapability.andReturn(true); + mockChildB.hasCapability.andReturn(true); + mockChildA.useCapability.andCallFake(function (c) { + return c === 'timespan' && mockTimespanA; + }); + mockChildB.useCapability.andCallFake(function (c) { + return c === 'timespan' && mockTimespanB; + }); + + capability = new TimelineTimespanCapability( + mockQ, + mockDomainObject + ); + }); + + it("applies only to timeline objects", function () { + expect(TimelineTimespanCapability.appliesTo({ + type: 'warp.timeline' + })).toBeTruthy(); + expect(TimelineTimespanCapability.appliesTo({ + type: 'folder' + })).toBeFalsy(); + }); + + it("provides timespan based on model", function () { + var mockCallback = jasmine.createSpy('callback'); + capability.invoke().then(mockCallback); + // We verify other methods in ActivityTimespanSpec, + // so just make sure we got something that looks right. + expect(mockCallback).toHaveBeenCalledWith({ + getStart: jasmine.any(Function), + getEnd: jasmine.any(Function), + getDuration: jasmine.any(Function), + setStart: jasmine.any(Function), + setEnd: jasmine.any(Function), + setDuration: jasmine.any(Function), + getEpoch: jasmine.any(Function) + }); + // Finally, verify that getEnd recurses + mockCallback.mostRecentCall.args[0].getEnd(); + expect(mockTimespanA.getEnd).toHaveBeenCalled(); + expect(mockTimespanB.getEnd).toHaveBeenCalled(); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/TimelineTimespanSpec.js b/platform/features/timeline/test/capabilities/TimelineTimespanSpec.js new file mode 100644 index 0000000000..1216762fba --- /dev/null +++ b/platform/features/timeline/test/capabilities/TimelineTimespanSpec.js @@ -0,0 +1,91 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/TimelineTimespan'], + function (TimelineTimespan) { + 'use strict'; + + describe("A Timeline's timespan", function () { + var testModel, + mockTimespans, + mockMutation, + mutationModel, + timespan; + + function makeMockTimespan(end) { + var mockTimespan = jasmine.createSpyObj( + 'timespan-' + end, + ['getEnd'] + ); + mockTimespan.getEnd.andReturn(end); + return mockTimespan; + } + + beforeEach(function () { + testModel = { + start: { + timestamp: 42000, + epoch: "TEST" + } + }; + + mutationModel = JSON.parse(JSON.stringify(testModel)); + + mockMutation = jasmine.createSpyObj("mutation", ["mutate"]); + mockTimespans = [ 44000, 65000, 1100 ].map(makeMockTimespan); + + mockMutation.mutate.andCallFake(function (mutator) { + mutator(mutationModel); + }); + + timespan = new TimelineTimespan( + testModel, + mockMutation, + mockTimespans + ); + }); + + it("provides a start time", function () { + expect(timespan.getStart()).toEqual(42000); + }); + + it("provides an end time", function () { + expect(timespan.getEnd()).toEqual(65000); + }); + + it("provides duration", function () { + expect(timespan.getDuration()).toEqual(65000 - 42000); + }); + + it("provides an epoch", function () { + expect(timespan.getEpoch()).toEqual("TEST"); + }); + + + it("sets start time using mutation capability", function () { + timespan.setStart(52000); + expect(mutationModel.start.timestamp).toEqual(52000); + // Original model should still be the same + expect(testModel.start.timestamp).toEqual(42000); + }); + + it("makes no changes with setEnd", function () { + // Copy initial state to verify that it doesn't change + var initialModel = JSON.parse(JSON.stringify(testModel)); + timespan.setEnd(123454321); + // Neither model should have changed + expect(testModel).toEqual(initialModel); + expect(mutationModel).toEqual(initialModel); + }); + + it("makes no changes with setDuration", function () { + // Copy initial state to verify that it doesn't change + var initialModel = JSON.parse(JSON.stringify(testModel)); + timespan.setDuration(123454321); + // Neither model should have changed + expect(testModel).toEqual(initialModel); + expect(mutationModel).toEqual(initialModel); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/TimelineUtilizationSpec.js b/platform/features/timeline/test/capabilities/TimelineUtilizationSpec.js new file mode 100644 index 0000000000..82ff0468eb --- /dev/null +++ b/platform/features/timeline/test/capabilities/TimelineUtilizationSpec.js @@ -0,0 +1,20 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/TimelineUtilization'], + function (TimelineUtilization) { + 'use strict'; + + describe("A Timeline's resource utilization", function () { + + // Placeholder; WTD-918 will implement + it("has the expected interface", function () { + var utilization = new TimelineUtilization(); + expect(utilization.getPointCount()).toEqual(jasmine.any(Number)); + expect(utilization.getDomainValue()).toEqual(jasmine.any(Number)); + expect(utilization.getRangeValue()).toEqual(jasmine.any(Number)); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/capabilities/UtilizationCapabilitySpec.js b/platform/features/timeline/test/capabilities/UtilizationCapabilitySpec.js new file mode 100644 index 0000000000..e9693d89db --- /dev/null +++ b/platform/features/timeline/test/capabilities/UtilizationCapabilitySpec.js @@ -0,0 +1,195 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/capabilities/UtilizationCapability'], + function (UtilizationCapability) { + 'use strict'; + + describe("A Timeline's utilization capability", function () { + var mockQ, + mockDomainObject, + testModel, + testCapabilities, + mockRelationship, + mockComposition, + mockCallback, + capability; + + function asPromise(v) { + return (v || {}).then ? v : { + then: function (callback) { + return asPromise(callback(v)); + }, + testValue: v + }; + } + + function allPromises(promises) { + return asPromise(promises.map(function (p) { + return (p || {}).then ? p.testValue : p; + })); + } + + // Utility function for making domain objects with utilization + // and/or cost capabilities + function fakeDomainObject(resources, start, end, costs) { + return { + getCapability: function (c) { + return ((c === 'utilization') && { + // Utilization capability + resources: function () { + return asPromise(resources); + }, + invoke: function () { + return asPromise(resources.map(function (k) { + return { key: k, start: start, end: end }; + })); + } + }) || ((c === 'cost') && { + // Cost capability + resources: function () { + return Object.keys(costs).sort(); + }, + cost: function (c) { + return costs[c]; + } + }); + }, + useCapability: function (c) { + return this.getCapability(c).invoke(); + } + }; + } + + beforeEach(function () { + mockQ = jasmine.createSpyObj('$q', ['when', 'all']); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getId', 'getModel', 'getCapability', 'useCapability' ] + ); + mockRelationship = jasmine.createSpyObj( + 'relationship', + [ 'getRelatedObjects' ] + ); + mockComposition = jasmine.createSpyObj( + 'composition', + [ 'invoke' ] + ); + mockCallback = jasmine.createSpy('callback'); + + testModel = { + type: "warp.activity", + resources: { + abc: 100, + xyz: 42 + } + }; + testCapabilities = { + composition: mockComposition, + relationship: mockRelationship + }; + + mockQ.when.andCallFake(asPromise); + mockQ.all.andCallFake(allPromises); + mockDomainObject.getModel.andReturn(testModel); + mockDomainObject.getCapability.andCallFake(function (c) { + return testCapabilities[c]; + }); + mockDomainObject.useCapability.andCallFake(function (c) { + return testCapabilities[c] && testCapabilities[c].invoke(); + }); + + capability = new UtilizationCapability( + mockQ, + mockDomainObject + ); + }); + + it("is applicable to timelines", function () { + expect(UtilizationCapability.appliesTo({ + type: "warp.timeline" + })).toBeTruthy(); + }); + + it("is applicable to activities", function () { + expect(UtilizationCapability.appliesTo(testModel)) + .toBeTruthy(); + }); + + it("is not applicable to other objects", function () { + expect(UtilizationCapability.appliesTo({ + type: "something" + })).toBeFalsy(); + }); + + it("accumulates resources from composition", function () { + mockComposition.invoke.andReturn(asPromise([ + fakeDomainObject(['abc', 'def']), + fakeDomainObject(['def', 'xyz']), + fakeDomainObject(['abc', 'xyz']) + ])); + + capability.resources().then(mockCallback); + + expect(mockCallback) + .toHaveBeenCalledWith(['abc', 'def', 'xyz']); + }); + + it("accumulates utilizations from composition", function () { + mockComposition.invoke.andReturn(asPromise([ + fakeDomainObject(['abc', 'def'], 10, 100), + fakeDomainObject(['def', 'xyz'], 50, 90) + ])); + + capability.invoke().then(mockCallback); + + expect(mockCallback).toHaveBeenCalledWith([ + { key: 'abc', start: 10, end: 100 }, + { key: 'def', start: 10, end: 100 }, + { key: 'def', start: 50, end: 90 }, + { key: 'xyz', start: 50, end: 90 } + ]); + }); + + it("provides intrinsic utilization from related objects", function () { + var mockTimespan = jasmine.createSpyObj( + 'timespan', + ['getStart', 'getEnd', 'getEpoch'] + ), + mockTimespanCapability = jasmine.createSpyObj( + 'timespanCapability', + ['invoke'] + ); + mockComposition.invoke.andReturn(asPromise([])); + mockRelationship.getRelatedObjects.andReturn(asPromise([ + fakeDomainObject([], 0, 0, { abc: 5, xyz: 15 }) + ])); + + testCapabilities.timespan = mockTimespanCapability; + mockTimespanCapability.invoke.andReturn(asPromise(mockTimespan)); + mockTimespan.getStart.andReturn(42); + mockTimespan.getEnd.andReturn(12321); + mockTimespan.getEpoch.andReturn("TEST"); + + capability.invoke().then(mockCallback); + + expect(mockCallback).toHaveBeenCalledWith([ + { key: 'abc', start: 42, end: 12321, value: 5, epoch: "TEST" }, + { key: 'xyz', start: 42, end: 12321, value: 15, epoch: "TEST" } + ]); + }); + + it("provides resource keys from related objects", function () { + mockComposition.invoke.andReturn(asPromise([])); + mockRelationship.getRelatedObjects.andReturn(asPromise([ + fakeDomainObject([], 0, 0, { abc: 5, xyz: 15 }) + ])); + + capability.resources().then(mockCallback); + + expect(mockCallback).toHaveBeenCalledWith(['abc', 'xyz']); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/ActivityModeValuesControllerSpec.js b/platform/features/timeline/test/controllers/ActivityModeValuesControllerSpec.js new file mode 100644 index 0000000000..9c11262deb --- /dev/null +++ b/platform/features/timeline/test/controllers/ActivityModeValuesControllerSpec.js @@ -0,0 +1,32 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/controllers/ActivityModeValuesController'], + function (ActivityModeValuesController) { + 'use strict'; + + describe("An Activity Mode's Values view controller", function () { + var testResources, + controller; + + beforeEach(function () { + testResources = [ + { key: 'abc', name: "Some name" }, + { key: 'def', name: "Test type", units: "Test units" }, + { key: 'xyz', name: "Something else" } + ]; + controller = new ActivityModeValuesController(testResources); + }); + + it("exposes resource metadata by key", function () { + expect(controller.metadata('abc')).toEqual(testResources[0]); + expect(controller.metadata('def')).toEqual(testResources[1]); + expect(controller.metadata('xyz')).toEqual(testResources[2]); + }); + + it("exposes no metadata for unknown keys", function () { + expect(controller.metadata('???')).toBeUndefined(); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/TimelineControllerSpec.js b/platform/features/timeline/test/controllers/TimelineControllerSpec.js new file mode 100644 index 0000000000..e1cb518535 --- /dev/null +++ b/platform/features/timeline/test/controllers/TimelineControllerSpec.js @@ -0,0 +1,229 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/controllers/TimelineController'], + function (TimelineController) { + 'use strict'; + + var DOMAIN_OBJECT_METHODS = [ + 'getModel', + 'getId', + 'useCapability', + 'hasCapability', + 'getCapability' + ]; + + describe("The timeline controller", function () { + var mockScope, + mockQ, + mockLoader, + mockDomainObject, + mockSpan, + testModels, + testConfiguration, + controller; + + function asPromise(v) { + return (v || {}).then ? v : { + then: function (callback) { + return asPromise(callback(v)); + }, + testValue: v + }; + } + + function allPromises(promises) { + return asPromise(promises.map(function (p) { + return (p || {}).then ? p.testValue : p; + })); + } + + function subgraph(domainObject, objects) { + function lookupSubgraph(id) { + return subgraph(objects[id], objects); + } + return { + domainObject: domainObject, + composition: (domainObject.getModel().composition || []) + .map(lookupSubgraph) + }; + } + + + beforeEach(function () { + var mockA, mockB, mockUtilization, mockPromise, mockGraph, testCapabilities; + + function getCapability(c) { + return testCapabilities[c]; + } + + function useCapability(c) { + return c === 'timespan' ? asPromise(mockSpan) : + c === 'graph' ? asPromise({ abc: mockGraph, xyz: mockGraph }) : + undefined; + } + + testModels = { + a: { modified: 40, composition: ['b'] }, + b: { modified: 2 } + }; + + testConfiguration = {}; + + mockQ = jasmine.createSpyObj('$q', ['when', 'all']); + mockA = jasmine.createSpyObj('a', DOMAIN_OBJECT_METHODS); + mockB = jasmine.createSpyObj('b', DOMAIN_OBJECT_METHODS); + mockSpan = jasmine.createSpyObj('span', ['getStart', 'getEnd']); + mockUtilization = jasmine.createSpyObj('utilization', ['resources', 'utilization']); + mockGraph = jasmine.createSpyObj('graph', ['getPointCount']); + mockPromise = jasmine.createSpyObj('promise', ['then']); + + mockScope = jasmine.createSpyObj( + "$scope", + [ '$watch', '$on' ] + ); + mockLoader = jasmine.createSpyObj('objectLoader', ['load']); + mockDomainObject = mockA; + + mockScope.domainObject = mockDomainObject; + mockScope.configuration = testConfiguration; + mockQ.when.andCallFake(asPromise); + mockQ.all.andCallFake(allPromises); + mockA.getId.andReturn('a'); + mockA.getModel.andReturn(testModels.a); + mockB.getId.andReturn('b'); + mockB.getModel.andReturn(testModels.b); + mockA.useCapability.andCallFake(useCapability); + mockB.useCapability.andCallFake(useCapability); + mockA.hasCapability.andReturn(true); + mockB.hasCapability.andReturn(true); + mockA.getCapability.andCallFake(getCapability); + mockB.getCapability.andCallFake(getCapability); + mockSpan.getStart.andReturn(42); + mockSpan.getEnd.andReturn(12321); + mockUtilization.resources.andReturn(['abc', 'xyz']); + mockUtilization.utilization.andReturn(mockPromise); + mockLoader.load.andCallFake(function () { + return asPromise(subgraph(mockA, { + a: mockA, + b: mockB + })); + }); + + testCapabilities = { + "utilization": mockUtilization + }; + + controller = new TimelineController(mockScope, mockQ, mockLoader, 0); + }); + + it("exposes scroll state tracker in scope", function () { + expect(mockScope.scroll.x).toEqual(0); + expect(mockScope.scroll.y).toEqual(0); + }); + + it("repopulates when modifications are made", function () { + var fnWatchCall, + strWatchCall; + + // Find the $watch that was given a function + mockScope.$watch.calls.forEach(function (call) { + if (typeof call.args[0] === 'function') { + // white-box: we know the first call is + // the one we're looking for + fnWatchCall = fnWatchCall || call; + } else if (typeof call.args[0] === 'string') { + strWatchCall = strWatchCall || call; + } + }); + + // Make sure string watch was for domainObject + expect(strWatchCall.args[0]).toEqual('domainObject'); + // Initially populate + strWatchCall.args[1](mockDomainObject); + + // There should be to swimlanes + expect(controller.swimlanes().length).toEqual(2); + + // Watch should be for sum of modified flags... + expect(fnWatchCall.args[0]()).toEqual(42); + + // Remove the child, then fire the watch + testModels.a.composition = []; + fnWatchCall.args[1](); + + // Swimlanes should have updated + expect(controller.swimlanes().length).toEqual(1); + }); + + it("repopulates graphs when graph choices change", function () { + var tmp; + + // Note that this test is brittle; it relies upon the + // order of $watch calls in TimelineController. + + // Initially populate + mockScope.$watch.calls[0].args[1](mockDomainObject); + + // Verify precondition - no graphs + expect(controller.graphs().length).toEqual(0); + + // Execute the watch function for graph state + tmp = mockScope.$watch.calls[2].args[0](); + + // Change graph state + testConfiguration.graph = { a: true, b: true }; + + // Verify that this would have triggered a watch + expect(mockScope.$watch.calls[2].args[0]()) + .not.toEqual(tmp); + + // Run the function the watch would have triggered + mockScope.$watch.calls[2].args[1](); + + // Should have some graphs now + expect(controller.graphs().length).toEqual(2); + + }); + + it("reports full scrollable width using zoom controller", function () { + var mockZoom = jasmine.createSpyObj('zoom', ['toPixels', 'duration']); + mockZoom.toPixels.andReturn(54321); + mockZoom.duration.andReturn(12345); + + // Initially populate + mockScope.$watch.calls[0].args[1](mockDomainObject); + + expect(controller.width(mockZoom)).toEqual(54321); + // Verify interactions; we took zoom's duration for our start/end, + // and converted it to pixels. + // First, check that we used the start/end (from above) + expect(mockZoom.duration).toHaveBeenCalledWith(12321 - 42); + // Next, verify that the result was passed to toPixels + expect(mockZoom.toPixels).toHaveBeenCalledWith(12345); + }); + + it("provides drag handles", function () { + // TimelineDragPopulator et al are tested for these, + // so just verify that handles are indeed exposed. + expect(controller.handles()).toEqual(jasmine.any(Array)); + }); + + it("refreshes graphs on request", function () { + var mockGraph = jasmine.createSpyObj('graph', ['refresh']); + + // Sneak a mock graph into the graph populator... + // This is whiteboxy and will have to change if + // GraphPopulator changes + controller.graphs().push(mockGraph); + + // Refresh + controller.refresh(); + + // Should have refreshed the graph + expect(mockGraph.refresh).toHaveBeenCalled(); + }); + }); + + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/TimelineGanttControllerSpec.js b/platform/features/timeline/test/controllers/TimelineGanttControllerSpec.js new file mode 100644 index 0000000000..b38417d1ed --- /dev/null +++ b/platform/features/timeline/test/controllers/TimelineGanttControllerSpec.js @@ -0,0 +1,80 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/controllers/TimelineGanttController'], + function (TimelineGanttController) { + "use strict"; + + var TEST_MAX_OFFSCREEN = 50; + + describe("The timeline Gantt bar controller", function () { + var mockTimespan, + testScroll, + mockToPixels, + controller; + + // Shorthands for passing these arguments to the controller + function width() { + return controller.width( + mockTimespan, + testScroll, + mockToPixels + ); + } + function left() { + return controller.left( + mockTimespan, + testScroll, + mockToPixels + ); + } + + + beforeEach(function () { + mockTimespan = jasmine.createSpyObj( + 'timespan', + ['getStart', 'getEnd', 'getDuration'] + ); + testScroll = { x: 0, width: 2000 }; + mockToPixels = jasmine.createSpy('toPixels'); + + mockTimespan.getStart.andReturn(100); + mockTimespan.getDuration.andReturn(50); + mockTimespan.getEnd.andReturn(150); + + mockToPixels.andCallFake(function (t) { return t * 10; }); + + controller = new TimelineGanttController(TEST_MAX_OFFSCREEN); + }); + + it("positions start and end points correctly on-screen", function () { + // Test's initial conditions are nominal, so should have + // the same return value as mockToPixels + expect(left()).toEqual(1000); + expect(width()).toEqual(500); + }); + + it("prevents excessive off screen values to the left", function () { + testScroll.x = 1200; + expect(left()).toEqual(1150); + expect(width()).toEqual(350); // ...such that right edge is 1500 + }); + + it("prevents excessive off screen values to the right", function () { + testScroll.width = 1200; + expect(left()).toEqual(1000); + expect(width()).toEqual(250); // ...such that right edge is 1250 + }); + + it("prevents excessive off screen values on both edges", function () { + testScroll.x = 1100; + testScroll.width = 200; // Visible right edge is now 1300 + expect(left()).toEqual(1050); + expect(width()).toEqual(300); // ...such that right edge is 1350 + }); + + + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/TimelineGraphControllerSpec.js b/platform/features/timeline/test/controllers/TimelineGraphControllerSpec.js new file mode 100644 index 0000000000..edadead54d --- /dev/null +++ b/platform/features/timeline/test/controllers/TimelineGraphControllerSpec.js @@ -0,0 +1,68 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/controllers/TimelineGraphController'], + function (TimelineGraphController) { + 'use strict'; + + describe("The Timeline graph controller", function () { + var mockScope, + testResources, + controller; + + beforeEach(function () { + mockScope = jasmine.createSpyObj( + '$scope', + [ '$watchCollection' ] + ); + testResources = [ + { key: 'abc', name: "Some name" }, + { key: 'def', name: "Test type", units: "Test units" }, + { key: 'xyz', name: "Something else" } + ]; + controller = new TimelineGraphController( + mockScope, + testResources + ); + }); + + it("watches for parameter changes", function () { + expect(mockScope.$watchCollection).toHaveBeenCalledWith( + 'parameters', + jasmine.any(Function) + ); + }); + + it("updates graphs when parameters change", function () { + var mockGraphA = jasmine.createSpyObj('graph-a', ['setBounds']), + mockGraphB = jasmine.createSpyObj('graph-b', ['setBounds']); + + // Supply new parameters + mockScope.$watchCollection.mostRecentCall.args[1]({ + graphs: [ mockGraphA, mockGraphB ], + origin: 9, + duration: 144 + }); + + // Graphs should have both been updated + expect(mockGraphA.setBounds).toHaveBeenCalledWith(9, 144); + expect(mockGraphB.setBounds).toHaveBeenCalledWith(9, 144); + }); + + it("provides labels for graphs", function () { + var mockGraph = jasmine.createSpyObj('graph', ['minimum', 'maximum']); + + mockGraph.minimum.andReturn(12.3412121); + mockGraph.maximum.andReturn(88.7555555); + mockGraph.key = "def"; + + expect(controller.label(mockGraph)).toEqual({ + title: "Test type (Test units)", + low: "12.341", + middle: "50.548", + high: "88.756" + }); + }); + }); + } +); diff --git a/platform/features/timeline/test/controllers/TimelineTableControllerSpec.js b/platform/features/timeline/test/controllers/TimelineTableControllerSpec.js new file mode 100644 index 0000000000..fff1cfeab2 --- /dev/null +++ b/platform/features/timeline/test/controllers/TimelineTableControllerSpec.js @@ -0,0 +1,31 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + [ + '../../src/controllers/TimelineTableController', + '../../src/TimelineFormatter' + ], + function (TimelineTableController, TimelineFormatter) { + "use strict"; + + describe("The timeline table controller", function () { + var formatter, controller; + + beforeEach(function () { + controller = new TimelineTableController(); + formatter = new TimelineFormatter(); + }); + + // This controller's job is just to expose the formatter + // in scope, so simply verify that the two agree. + it("formats durations", function () { + [ 0, 100, 4123, 93600, 748801230012].forEach(function (n) { + expect(controller.niceTime(n)) + .toEqual(formatter.format(n)); + }); + }); + + + }); + } +); diff --git a/platform/features/timeline/test/controllers/TimelineTickControllerSpec.js b/platform/features/timeline/test/controllers/TimelineTickControllerSpec.js new file mode 100644 index 0000000000..b51fe674f8 --- /dev/null +++ b/platform/features/timeline/test/controllers/TimelineTickControllerSpec.js @@ -0,0 +1,67 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/controllers/TimelineTickController', '../../src/TimelineFormatter'], + function (TimelineTickController, TimelineFormatter) { + 'use strict'; + + var BILLION = 1000000000, + FORMATTER = new TimelineFormatter(); + + describe("The timeline tick controller", function () { + var mockToMillis, + controller; + + function expectedTick(pixelValue) { + return { + left: pixelValue, + text: FORMATTER.format(pixelValue * 2 + BILLION) + }; + } + + beforeEach(function () { + mockToMillis = jasmine.createSpy('toMillis'); + mockToMillis.andCallFake(function (v) { + return v * 2 + BILLION; + }); + controller = new TimelineTickController(); + }); + + it("exposes tick marks within a requested pixel span", function () { + // Simple case + expect(controller.labels(8000, 300, 100, mockToMillis)) + .toEqual([8000, 8100, 8200, 8300].map(expectedTick)); + + // Slightly more complicated case + expect(controller.labels(7480, 4500, 1000, mockToMillis)) + .toEqual([7000, 8000, 9000, 10000, 11000, 12000].map(expectedTick)); + }); + + it("does not rebuild arrays for same inputs", function () { + var firstValue = controller.labels(800, 300, 100, mockToMillis); + + expect(controller.labels(800, 300, 100, mockToMillis)) + .toEqual(firstValue); + + expect(controller.labels(800, 300, 100, mockToMillis)) + .toBe(firstValue); + }); + + it("does rebuild arrays when zoom changes", function () { + var firstValue = controller.labels(800, 300, 100, mockToMillis); + + mockToMillis.andCallFake(function (v) { + return BILLION * 2 + v; + }); + + expect(controller.labels(800, 300, 100, mockToMillis)) + .not.toEqual(firstValue); + + expect(controller.labels(800, 300, 100, mockToMillis)) + .not.toBe(firstValue); + }); + + }); + + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/TimelineZoomControllerSpec.js b/platform/features/timeline/test/controllers/TimelineZoomControllerSpec.js new file mode 100644 index 0000000000..1849c9d9c2 --- /dev/null +++ b/platform/features/timeline/test/controllers/TimelineZoomControllerSpec.js @@ -0,0 +1,80 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + + +define( + ['../../src/controllers/TimelineZoomController'], + function (TimelineZoomController) { + 'use strict'; + + describe("The timeline zoom state controller", function () { + var testConfiguration, + mockScope, + controller; + + beforeEach(function () { + testConfiguration = { + levels: [ + 1000, + 2000, + 3500 + ], + width: 12321 + }; + mockScope = jasmine.createSpyObj("$scope", ['$watch']); + mockScope.commit = jasmine.createSpy('commit'); + controller = new TimelineZoomController( + mockScope, + testConfiguration + ); + }); + + it("starts off at a middle zoom level", function () { + expect(controller.zoom()).toEqual(2000); + }); + + it("allows duration to be changed", function () { + var initial = controller.duration(); + controller.duration(initial * 3.33); + expect(controller.duration() > initial).toBeTruthy(); + }); + + it("handles time-to-pixel conversions", function () { + var zoomLevel = controller.zoom(); + expect(controller.toPixels(zoomLevel)).toEqual(12321); + expect(controller.toPixels(zoomLevel * 2)).toEqual(24642); + }); + + it("handles pixel-to-time conversions", function () { + var zoomLevel = controller.zoom(); + expect(controller.toMillis(12321)).toEqual(zoomLevel); + expect(controller.toMillis(24642)).toEqual(zoomLevel * 2); + }); + + it("allows zoom to be changed", function () { + controller.zoom(1); + expect(controller.zoom()).toEqual(3500); + }); + + it("does not normally persist zoom changes", function () { + controller.zoom(1); + expect(mockScope.commit).not.toHaveBeenCalled(); + }); + + it("persists zoom changes in Edit mode", function () { + mockScope.domainObject = jasmine.createSpyObj( + 'domainObject', + ['hasCapability'] + ); + mockScope.domainObject.hasCapability.andCallFake(function (c) { + return c === 'editor'; + }); + controller.zoom(1); + expect(mockScope.commit).toHaveBeenCalled(); + expect(mockScope.configuration.zoomLevel) + .toEqual(jasmine.any(Number)); + }); + + }); + + } +); diff --git a/platform/features/timeline/test/controllers/WARPDateTimeControllerSpec.js b/platform/features/timeline/test/controllers/WARPDateTimeControllerSpec.js new file mode 100644 index 0000000000..fadd6b5bf0 --- /dev/null +++ b/platform/features/timeline/test/controllers/WARPDateTimeControllerSpec.js @@ -0,0 +1,57 @@ + +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ["../../src/controllers/WARPDateTimeController"], + function (WARPDateTimeController) { + "use strict"; + + describe("The date-time controller for timeline creation", function () { + var mockScope, + controller; + + beforeEach(function () { + mockScope = jasmine.createSpyObj('$scope', ['$watchCollection']); + mockScope.field = 'testField'; + mockScope.ngModel = { testField: { timestamp: 0, epoch: "SET" } }; + controller = new WARPDateTimeController(mockScope); + }); + + + // Verify two-way binding support + it("updates model on changes to entry fields", function () { + // Make sure we're looking at the right watch + expect(mockScope.$watchCollection.calls[0].args[0]) + .toEqual("datetime"); + mockScope.$watchCollection.calls[0].args[1]({ + days: 4, + hours: 12, + minutes: 30, + seconds: 11 + }); + expect(mockScope.ngModel.testField.timestamp).toEqual( + ((((((4 * 24) + 12) * 60) + 30) * 60) + 11) * 1000 + ); + }); + + it("updates form when model changes", function () { + // Make sure we're looking at the right watch + expect(mockScope.$watchCollection.calls[1].args[0]) + .toEqual(jasmine.any(Function)); + // ...and that it's really looking at the field in ngModel + expect(mockScope.$watchCollection.calls[1].args[0]()) + .toBe(mockScope.ngModel.testField); + mockScope.$watchCollection.calls[1].args[1]({ + timestamp: ((((((4 * 24) + 12) * 60) + 30) * 60) + 11) * 1000 + }); + expect(mockScope.datetime).toEqual({ + days: 4, + hours: 12, + minutes: 30, + seconds: 11 + }); + }); + + }); + } +); diff --git a/platform/features/timeline/test/controllers/drag/TimelineDragHandleFactorySpec.js b/platform/features/timeline/test/controllers/drag/TimelineDragHandleFactorySpec.js new file mode 100644 index 0000000000..fc577ba782 --- /dev/null +++ b/platform/features/timeline/test/controllers/drag/TimelineDragHandleFactorySpec.js @@ -0,0 +1,66 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/drag/TimelineDragHandleFactory'], + function (TimelineDragHandleFactory) { + 'use strict'; + + describe("A Timeline drag handle factory", function () { + var mockDragHandler, + mockSnapHandler, + mockDomainObject, + mockType, + testType, + factory; + + beforeEach(function () { + mockDragHandler = jasmine.createSpyObj( + 'dragHandler', + [ 'start' ] + ); + mockSnapHandler = jasmine.createSpyObj( + 'snapHandler', + [ 'snap' ] + ); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getCapability', 'getId' ] + ); + mockType = jasmine.createSpyObj( + 'type', + [ 'instanceOf' ] + ); + + mockDomainObject.getId.andReturn('test-id'); + mockDomainObject.getCapability.andReturn(mockType); + mockType.instanceOf.andCallFake(function (t) { + return t === testType; + }); + + factory = new TimelineDragHandleFactory( + mockDragHandler, + mockSnapHandler + ); + }); + + it("inspects an object's type capability", function () { + factory.handles(mockDomainObject); + expect(mockDomainObject.getCapability) + .toHaveBeenCalledWith('type'); + }); + + it("provides three handles for activities", function () { + testType = "warp.activity"; + expect(factory.handles(mockDomainObject).length) + .toEqual(3); + }); + + it("provides two handles for timelines", function () { + testType = "warp.timeline"; + expect(factory.handles(mockDomainObject).length) + .toEqual(2); + }); + + }); + } +); diff --git a/platform/features/timeline/test/controllers/drag/TimelineDragHandlerSpec.js b/platform/features/timeline/test/controllers/drag/TimelineDragHandlerSpec.js new file mode 100644 index 0000000000..9cd4c86a53 --- /dev/null +++ b/platform/features/timeline/test/controllers/drag/TimelineDragHandlerSpec.js @@ -0,0 +1,209 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/drag/TimelineDragHandler'], + function (TimelineDragHandler) { + 'use strict'; + + describe("A Timeline drag handler", function () { + var mockLoader, + mockSelection, + testConfiguration, + mockDomainObject, + mockDomainObjects, + mockTimespans, + mockMutations, + mockPersists, + mockCallback, + handler; + + function asPromise(value) { + return (value || {}).then ? value : { + then: function (callback) { + return asPromise(callback(value)); + } + }; + } + + function subgraph(domainObject, objects) { + function lookupSubgraph(id) { + return subgraph(objects[id], objects); + } + return { + domainObject: domainObject, + composition: (domainObject.getModel().composition || []) + .map(lookupSubgraph) + }; + } + + function makeMockDomainObject(id, composition) { + var mockDomainObject = jasmine.createSpyObj( + 'domainObject-' + id, + ['getId', 'getModel', 'getCapability', 'useCapability'] + ); + + mockDomainObject.getId.andReturn(id); + mockDomainObject.getModel.andReturn({ composition: composition }); + mockDomainObject.useCapability.andReturn(asPromise(mockTimespans[id])); + mockDomainObject.getCapability.andCallFake(function (c) { + return { + persistence: mockPersists[id], + mutation: mockMutations[id] + }[c]; + }); + + return mockDomainObject; + } + + beforeEach(function () { + mockTimespans = {}; + mockPersists = {}; + mockMutations = {}; + ['a', 'b', 'c', 'd', 'e', 'f'].forEach(function (id, index) { + mockTimespans[id] = jasmine.createSpyObj( + 'timespan-' + id, + [ 'getStart', 'getEnd', 'getDuration', 'setStart', 'setEnd', 'setDuration' ] + ); + mockPersists[id] = jasmine.createSpyObj( + 'persistence-' + id, + [ 'persist' ] + ); + mockMutations[id] = jasmine.createSpyObj( + 'mutation-' + id, + [ 'mutate' ] + ); + mockTimespans[id].getStart.andReturn(index * 1000); + mockTimespans[id].getDuration.andReturn(4000 + index); + mockTimespans[id].getEnd.andReturn(4000 + index + index * 1000); + }); + + mockLoader = jasmine.createSpyObj('objectLoader', ['load']); + mockDomainObject = makeMockDomainObject('a', ['b', 'c']); + mockDomainObjects = { + a: mockDomainObject, + b: makeMockDomainObject('b', ['d']), + c: makeMockDomainObject('c', ['e', 'f']), + d: makeMockDomainObject('d', []), + e: makeMockDomainObject('e', []), + f: makeMockDomainObject('f', []) + }; + mockSelection = jasmine.createSpyObj('selection', ['get', 'select']); + mockCallback = jasmine.createSpy('callback'); + + testConfiguration = {}; + + mockLoader.load.andReturn(asPromise( + subgraph(mockDomainObject, mockDomainObjects) + )); + + handler = new TimelineDragHandler( + mockDomainObject, + mockLoader + ); + }); + + it("uses the loader to find subgraph", function () { + expect(mockLoader.load).toHaveBeenCalledWith( + mockDomainObject, + 'timespan' + ); + }); + + it("reports available object identifiers", function () { + expect(handler.ids()) + .toEqual(Object.keys(mockDomainObjects).sort()); + }); + + it("exposes start/end/duration from timespan capabilities", function () { + expect(handler.start('a')).toEqual(0); + expect(handler.start('b')).toEqual(1000); + expect(handler.start('c')).toEqual(2000); + expect(handler.duration('a')).toEqual(4000); + expect(handler.duration('b')).toEqual(4001); + expect(handler.duration('c')).toEqual(4002); + expect(handler.end('a')).toEqual(4000); + expect(handler.end('b')).toEqual(5001); + expect(handler.end('c')).toEqual(6002); + }); + + it("accepts objects instead of identifiers for start/end/duration calls", function () { + Object.keys(mockDomainObjects).forEach(function (id) { + expect(handler.start(mockDomainObjects[id])).toEqual(handler.start(id)); + expect(handler.duration(mockDomainObjects[id])).toEqual(handler.duration(id)); + expect(handler.end(mockDomainObjects[id])).toEqual(handler.end(id)); + }); + }); + + it("mutates objects", function () { + handler.start('a', 123); + expect(mockTimespans.a.setStart).toHaveBeenCalledWith(123); + handler.duration('b', 42); + expect(mockTimespans.b.setDuration).toHaveBeenCalledWith(42); + handler.end('c', 12321); + expect(mockTimespans.c.setEnd).toHaveBeenCalledWith(12321); + }); + + it("disallows negative starts, durations", function () { + handler.start('a', -100); + handler.duration('b', -1000); + expect(mockTimespans.a.setStart).toHaveBeenCalledWith(0); + expect(mockTimespans.b.setDuration).toHaveBeenCalledWith(0); + }); + + it("disallows starts greater than ends violations", function () { + handler.start('a', 5000); + handler.end('b', 500); + expect(mockTimespans.a.setStart).toHaveBeenCalledWith(4000); // end time + expect(mockTimespans.b.setEnd).toHaveBeenCalledWith(1000); // start time + }); + + it("moves objects in groups", function () { + handler.move('b', 42); + expect(mockTimespans.b.setStart).toHaveBeenCalledWith(1042); + expect(mockTimespans.b.setEnd).toHaveBeenCalledWith(5043); + expect(mockTimespans.d.setStart).toHaveBeenCalledWith(3042); + expect(mockTimespans.d.setEnd).toHaveBeenCalledWith(7045); + // Verify no other interactions + ['a', 'c', 'e', 'f'].forEach(function (id) { + expect(mockTimespans[id].setStart).not.toHaveBeenCalled(); + expect(mockTimespans[id].setEnd).not.toHaveBeenCalled(); + }); + }); + + it("moves whole subtrees", function () { + handler.move('a', 12321); + // We verify the math in the previous test, so just verify + // that the whole tree is effected here. + Object.keys(mockTimespans).forEach(function (id) { + expect(mockTimespans[id].setStart).toHaveBeenCalled(); + }); + }); + + it("prevents bulk moves past 0", function () { + // Have a start later; new lowest start is b, at 1000 + mockTimespans.a.getStart.andReturn(10000); + handler.move('a', -10000); + // Verify that move was stopped at 0, for b, even though + // move was initiated at a + expect(mockTimespans.a.setStart).toHaveBeenCalledWith(9000); + expect(mockTimespans.b.setStart).toHaveBeenCalledWith(0); + expect(mockTimespans.c.setStart).toHaveBeenCalledWith(1000); + }); + + it("persists mutated objects", function () { + handler.start('a', 20); + handler.end('b', 50); + handler.duration('c', 30); + handler.persist(); + expect(mockPersists.a.persist).toHaveBeenCalled(); + expect(mockPersists.b.persist).toHaveBeenCalled(); + expect(mockPersists.c.persist).toHaveBeenCalled(); + expect(mockPersists.d.persist).not.toHaveBeenCalled(); + expect(mockPersists.e.persist).not.toHaveBeenCalled(); + expect(mockPersists.f.persist).not.toHaveBeenCalled(); + }); + + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/drag/TimelineDragPopulatorSpec.js b/platform/features/timeline/test/controllers/drag/TimelineDragPopulatorSpec.js new file mode 100644 index 0000000000..feb1ff10f7 --- /dev/null +++ b/platform/features/timeline/test/controllers/drag/TimelineDragPopulatorSpec.js @@ -0,0 +1,53 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + + +define( + ['../../../src/controllers/drag/TimelineDragPopulator'], + function (TimelineDragPopulator) { + "use strict"; + + describe("The timeline drag populator", function () { + var mockObjectLoader, + mockPromise, + mockSwimlane, + mockDomainObject, + populator; + + beforeEach(function () { + mockObjectLoader = jasmine.createSpyObj("objectLoader", ["load"]); + mockPromise = jasmine.createSpyObj("promise", ["then"]); + mockSwimlane = jasmine.createSpyObj("swimlane", ["color"]); + mockDomainObject = jasmine.createSpyObj( + "domainObject", + ["getCapability", "getId"] + ); + + mockSwimlane.domainObject = mockDomainObject; + mockObjectLoader.load.andReturn(mockPromise); + + populator = new TimelineDragPopulator(mockObjectLoader); + }); + + it("loads timespans for the represented object's subgraph", function () { + populator.populate(mockDomainObject); + expect(mockObjectLoader.load).toHaveBeenCalledWith( + mockDomainObject, + 'timespan' + ); + }); + + it("updates handles for selections", function () { + // Ensure we have a represented object context + populator.populate(mockDomainObject); + // Initially, no selection and no handles + expect(populator.get()).toEqual([]); + // Select the swimlane + populator.select(mockSwimlane); + // We should have handles now + expect(populator.get().length).toEqual(3); + }); + + }); + + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/drag/TimelineEndHandleSpec.js b/platform/features/timeline/test/controllers/drag/TimelineEndHandleSpec.js new file mode 100644 index 0000000000..6841b9fa1f --- /dev/null +++ b/platform/features/timeline/test/controllers/drag/TimelineEndHandleSpec.js @@ -0,0 +1,96 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/drag/TimelineEndHandle', '../../../src/TimelineConstants'], + function (TimelineEndHandle, TimelineConstants) { + 'use strict'; + + describe("A Timeline end drag handle", function () { + var mockDragHandler, + mockSnapHandler, + mockZoomController, + handle; + + beforeEach(function () { + mockDragHandler = jasmine.createSpyObj( + 'dragHandler', + [ 'end', 'persist' ] + ); + mockSnapHandler = jasmine.createSpyObj( + 'snapHandler', + [ 'snap' ] + ); + mockZoomController = jasmine.createSpyObj( + 'zoom', + [ 'toMillis', 'toPixels' ] + ); + + mockDragHandler.end.andReturn(12321); + + // Echo back the value from snapper for most tests + mockSnapHandler.snap.andCallFake(function (ts) { + return ts; + }); + + // Double pixels to get millis, for test purposes + mockZoomController.toMillis.andCallFake(function (px) { + return px * 2; + }); + + mockZoomController.toPixels.andCallFake(function (ms) { + return ms / 2; + }); + + handle = new TimelineEndHandle( + 'test-id', + mockDragHandler, + mockSnapHandler + ); + }); + + it("provides a style for templates", function () { + var w = TimelineConstants.HANDLE_WIDTH; + expect(handle.style(mockZoomController)).toEqual({ + // Left should be adjusted by zoom controller + left: (12321 / 2) - w + 'px', + // Width should match the defined constant + width: w + 'px' + }); + }); + + it("forwards drags to the drag handler", function () { + handle.begin(); + handle.drag(100, mockZoomController); + // Should have been interpreted as a +200 ms change + expect(mockDragHandler.end).toHaveBeenCalledWith( + "test-id", + 12521 + ); + }); + + it("snaps drags to other end points", function () { + mockSnapHandler.snap.andReturn(42); + handle.begin(); + handle.drag(-10, mockZoomController); + // Should have used snap-to timestamp + expect(mockDragHandler.end).toHaveBeenCalledWith( + "test-id", + 42 + ); + }); + + it("persists when a move is complete", function () { + // Simulate normal drag cycle + handle.begin(); + handle.drag(100, mockZoomController); + // Should not have persisted yet + expect(mockDragHandler.persist).not.toHaveBeenCalled(); + // Finish the drag + handle.finish(); + // Now it should have persisted + expect(mockDragHandler.persist).toHaveBeenCalled(); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/drag/TimelineMoveHandleSpec.js b/platform/features/timeline/test/controllers/drag/TimelineMoveHandleSpec.js new file mode 100644 index 0000000000..df1a398fc4 --- /dev/null +++ b/platform/features/timeline/test/controllers/drag/TimelineMoveHandleSpec.js @@ -0,0 +1,163 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/drag/TimelineMoveHandle', '../../../src/TimelineConstants'], + function (TimelineMoveHandle, TimelineConstants) { + 'use strict'; + + describe("A Timeline move drag handle", function () { + var mockDragHandler, + mockSnapHandler, + mockZoomController, + handle; + + beforeEach(function () { + mockDragHandler = jasmine.createSpyObj( + 'dragHandler', + [ 'start', 'duration', 'end', 'move', 'persist' ] + ); + mockSnapHandler = jasmine.createSpyObj( + 'snapHandler', + [ 'snap' ] + ); + mockZoomController = jasmine.createSpyObj( + 'zoom', + [ 'toMillis', 'toPixels' ] + ); + + mockDragHandler.start.andReturn(12321); + mockDragHandler.duration.andReturn(4200); + mockDragHandler.end.andReturn(12321 + 4200); + + // Echo back the value from snapper for most tests + mockSnapHandler.snap.andCallFake(function (ts) { + return ts; + }); + + // Double pixels to get millis, for test purposes + mockZoomController.toMillis.andCallFake(function (px) { + return px * 2; + }); + + mockZoomController.toPixels.andCallFake(function (ms) { + return ms / 2; + }); + + handle = new TimelineMoveHandle( + 'test-id', + mockDragHandler, + mockSnapHandler + ); + }); + + it("provides a style for templates", function () { + var w = TimelineConstants.HANDLE_WIDTH; + expect(handle.style(mockZoomController)).toEqual({ + // Left should be adjusted by zoom controller + left: (12321 / 2) + w + 'px', + // Width should be duration minus end points + width: 2100 - (w * 2) + 'px' + }); + }); + + it("forwards drags to the drag handler", function () { + handle.begin(); + handle.drag(100, mockZoomController); + // Should have been interpreted as a +200 ms change + expect(mockDragHandler.move).toHaveBeenCalledWith( + "test-id", + 200 + ); + }); + + it("tracks drags incrementally", function () { + handle.begin(); + + handle.drag(100, mockZoomController); + // Should have been interpreted as a +200 ms change... + expect(mockDragHandler.move).toHaveBeenCalledWith( + "test-id", + 200 + ); + + // Reflect the change from the drag handler + mockDragHandler.start.andReturn(12521); + mockDragHandler.end.andReturn(12521 + 4200); + + // ....followed by a +100 ms change. + handle.drag(150, mockZoomController); + expect(mockDragHandler.move).toHaveBeenCalledWith( + "test-id", + 100 + ); + }); + + it("snaps drags to other end points", function () { + mockSnapHandler.snap.andCallFake(function (ts) { + return ts + 10; + }); + handle.begin(); + handle.drag(100, mockZoomController); + // Should have used snap-to timestamp, which was 10 + // ms greater than the provided one + expect(mockDragHandler.move).toHaveBeenCalledWith( + "test-id", + 210 + ); + }); + + it("considers snaps for both endpoints", function () { + handle.begin(); + expect(mockSnapHandler.snap).not.toHaveBeenCalled(); + handle.drag(100, mockZoomController); + expect(mockSnapHandler.snap.calls.length).toEqual(2); + }); + + it("chooses the closest snap-to location", function () { + // Use a toggle to give snapped timestamps that are + // different distances away from the original. + // The move handle needs to choose the closest snap-to, + // regardless of whether it is the start/end (which + // will vary based on the initial state of this toggle.) + var toggle = false; + mockSnapHandler.snap.andCallFake(function (ts) { + toggle = !toggle; + return ts + (toggle ? -5 : 10); + }); + handle.begin(); + handle.drag(100, mockZoomController); + expect(mockDragHandler.move).toHaveBeenCalledWith( + "test-id", + 195 // Chose the -5 + ); + + // Reflect the change from the drag handler + mockDragHandler.start.andReturn(12521 - 5); + mockDragHandler.end.andReturn(12521 + 4200 - 5); + + toggle = true; // Change going-in state + handle.drag(300, mockZoomController); + // Note that the -5 offset is shown in the current state, + // so snapping to the -5 implies that the full 400ms will + // be moved (again, relative to dragHandler's reported state) + expect(mockDragHandler.move).toHaveBeenCalledWith( + "test-id", + 400 // Still chose the -5 + ); + }); + + it("persists when a move is complete", function () { + // Simulate normal drag cycle + handle.begin(); + handle.drag(100, mockZoomController); + // Should not have persisted yet + expect(mockDragHandler.persist).not.toHaveBeenCalled(); + // Finish the drag + handle.finish(); + // Now it should have persisted + expect(mockDragHandler.persist).toHaveBeenCalled(); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/drag/TimelineSnapHandlerSpec.js b/platform/features/timeline/test/controllers/drag/TimelineSnapHandlerSpec.js new file mode 100644 index 0000000000..73560c53d7 --- /dev/null +++ b/platform/features/timeline/test/controllers/drag/TimelineSnapHandlerSpec.js @@ -0,0 +1,60 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/drag/TimelineSnapHandler'], + function (TimelineSnapHandler) { + 'use strict'; + + describe("A Timeline snap handler", function () { + var mockDragHandler, + handler; + + beforeEach(function () { + var starts = { a: 1000, b: 2000, c: 2500, d: 2600 }, + ends = { a: 2050, b: 3000, c: 2700, d: 10000 }; + + mockDragHandler = jasmine.createSpyObj( + 'dragHandler', + [ 'start', 'end', 'ids' ] + ); + + mockDragHandler.ids.andReturn(['a', 'b', 'c', 'd']); + mockDragHandler.start.andCallFake(function (id) { + return starts[id]; + }); + mockDragHandler.end.andCallFake(function (id) { + return ends[id]; + }); + + handler = new TimelineSnapHandler(mockDragHandler); + }); + + it("provides a preferred snap location within tolerance", function () { + expect(handler.snap(2511, 15, 'a')).toEqual(2500); // c's start + expect(handler.snap(2488, 15, 'a')).toEqual(2500); // c's start + expect(handler.snap(10, 1000, 'b')).toEqual(1000); // a's start + expect(handler.snap(2711, 20, 'd')).toEqual(2700); // c's end + }); + + it("excludes provided id from snapping", function () { + // Don't want objects to snap to themselves, so we need + // this exclusion. + expect(handler.snap(2010, 50, 'b')).toEqual(2050); // a's end + // Verify that b's start would have been used had the + // id not been provided + expect(handler.snap(2010, 50, 'd')).toEqual(2000); + }); + + it("snaps to the closest point, when multiple match", function () { + // 2600 and 2700 (plus others) are both in range here + expect(handler.snap(2651, 1000, 'a')).toEqual(2700); + }); + + it("does not snap if no points are within tolerance", function () { + // Closest are 1000 and 2000, which are well outside of tolerance + expect(handler.snap(1503, 100, 'd')).toEqual(1503); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/drag/TimelineStartHandleSpec.js b/platform/features/timeline/test/controllers/drag/TimelineStartHandleSpec.js new file mode 100644 index 0000000000..6c752678b3 --- /dev/null +++ b/platform/features/timeline/test/controllers/drag/TimelineStartHandleSpec.js @@ -0,0 +1,95 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/drag/TimelineStartHandle', '../../../src/TimelineConstants'], + function (TimelineStartHandle, TimelineConstants) { + 'use strict'; + + describe("A Timeline start drag handle", function () { + var mockDragHandler, + mockSnapHandler, + mockZoomController, + handle; + + beforeEach(function () { + mockDragHandler = jasmine.createSpyObj( + 'dragHandler', + [ 'start', 'persist' ] + ); + mockSnapHandler = jasmine.createSpyObj( + 'snapHandler', + [ 'snap' ] + ); + mockZoomController = jasmine.createSpyObj( + 'zoom', + [ 'toMillis', 'toPixels' ] + ); + + mockDragHandler.start.andReturn(12321); + + // Echo back the value from snapper for most tests + mockSnapHandler.snap.andCallFake(function (ts) { + return ts; + }); + + // Double pixels to get millis, for test purposes + mockZoomController.toMillis.andCallFake(function (px) { + return px * 2; + }); + + mockZoomController.toPixels.andCallFake(function (ms) { + return ms / 2; + }); + + handle = new TimelineStartHandle( + 'test-id', + mockDragHandler, + mockSnapHandler + ); + }); + + it("provides a style for templates", function () { + expect(handle.style(mockZoomController)).toEqual({ + // Left should be adjusted by zoom controller + left: (12321 / 2) + 'px', + // Width should match the defined constant + width: TimelineConstants.HANDLE_WIDTH + 'px' + }); + }); + + it("forwards drags to the drag handler", function () { + handle.begin(); + handle.drag(100, mockZoomController); + // Should have been interpreted as a +200 ms change + expect(mockDragHandler.start).toHaveBeenCalledWith( + "test-id", + 12521 + ); + }); + + it("snaps drags to other end points", function () { + mockSnapHandler.snap.andReturn(42); + handle.begin(); + handle.drag(-10, mockZoomController); + // Should have used snap-to timestamp + expect(mockDragHandler.start).toHaveBeenCalledWith( + "test-id", + 42 + ); + }); + + it("persists when a move is complete", function () { + // Simulate normal drag cycle + handle.begin(); + handle.drag(100, mockZoomController); + // Should not have persisted yet + expect(mockDragHandler.persist).not.toHaveBeenCalled(); + // Finish the drag + handle.finish(); + // Now it should have persisted + expect(mockDragHandler.persist).toHaveBeenCalled(); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/graph/TimelineGraphPopulatorSpec.js b/platform/features/timeline/test/controllers/graph/TimelineGraphPopulatorSpec.js new file mode 100644 index 0000000000..3ecd93a66f --- /dev/null +++ b/platform/features/timeline/test/controllers/graph/TimelineGraphPopulatorSpec.js @@ -0,0 +1,132 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/graph/TimelineGraphPopulator'], + function (TimelineGraphPopulator) { + 'use strict'; + + describe("A Timeline's resource graph populator", function () { + var mockSwimlanes, + mockQ, + testResources, + populator; + + function asPromise(v) { + return (v || {}).then ? v : { + then: function (callback) { + return asPromise(callback(v)); + }, + testValue: v + }; + } + + function allPromises(promises) { + return asPromise(promises.map(function (p) { + return (p || {}).then ? p.testValue : p; + })); + } + + + beforeEach(function () { + testResources = { + a: [ 'xyz', 'abc' ], + b: [ 'xyz' ], + c: [ 'xyz', 'abc', 'def', 'ghi' ] + }; + + mockQ = jasmine.createSpyObj('$q', ['when', 'all']); + + mockSwimlanes = ['a', 'b', 'c'].map(function (k) { + var mockSwimlane = jasmine.createSpyObj( + 'swimlane-' + k, + [ 'graph', 'color' ] + ), + mockGraph = jasmine.createSpyObj( + 'graph-' + k, + [ 'getPointCount', 'getDomainValue', 'getRangeValue' ] + ); + mockSwimlane.graph.andReturn(true); + mockSwimlane.domainObject = jasmine.createSpyObj( + 'domainObject-' + k, + [ 'getCapability', 'hasCapability', 'useCapability', 'getId' ] + ); + mockSwimlane.color.andReturn('#' + k); + // Provide just enough information about graphs to support + // determination of which graphs to show + mockSwimlane.domainObject.useCapability.andCallFake(function () { + var obj = {}; + testResources[k].forEach(function (r) { + obj[r] = mockGraph; + }); + return asPromise(obj); + }); + mockSwimlane.domainObject.hasCapability + .andReturn(true); + mockSwimlane.domainObject.getId + .andReturn(k); + mockGraph.getPointCount.andReturn(0); + + return mockSwimlane; + }); + + mockQ.when.andCallFake(asPromise); + mockQ.all.andCallFake(allPromises); + + populator = new TimelineGraphPopulator(mockQ); + }); + + it("provides no graphs by default", function () { + expect(populator.get()).toEqual([]); + }); + + it("provides one graph per resource type", function () { + populator.populate(mockSwimlanes); + // There are 4 unique resource types shared here... + expect(populator.get().length).toEqual(4); + }); + + it("does not include graphs based on swimlane configuration", function () { + mockSwimlanes[2].graph.andReturn(false); + populator.populate(mockSwimlanes); + // Only two unique swimlanes in the other two + expect(populator.get().length).toEqual(2); + // Verify interactions as well + expect(mockSwimlanes[0].domainObject.useCapability) + .toHaveBeenCalledWith('graph'); + expect(mockSwimlanes[1].domainObject.useCapability) + .toHaveBeenCalledWith('graph'); + expect(mockSwimlanes[2].domainObject.useCapability) + .not.toHaveBeenCalled(); + }); + + it("does not recreate graphs when swimlanes don't change", function () { + var initialValue; + // Get an initial set of graphs + populator.populate(mockSwimlanes); + initialValue = populator.get(); + // Repopulate with same data; should get same graphs + populator.populate(mockSwimlanes); + expect(populator.get()).toBe(initialValue); + // Something changed... + mockSwimlanes.pop(); + populator.populate(mockSwimlanes); + // Now we should get different graphs + expect(populator.get()).not.toBe(initialValue); + }); + + // Regression test for WTD-1155 + it("does recreate graphs when inclusions change", function () { + var initialValue; + // Get an initial set of graphs + populator.populate(mockSwimlanes); + initialValue = populator.get(); + // Change resource graph inclusion... + mockSwimlanes[1].graph.andReturn(false); + populator.populate(mockSwimlanes); + // Now we should get different graphs + expect(populator.get()).not.toBe(initialValue); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/graph/TimelineGraphRendererSpec.js b/platform/features/timeline/test/controllers/graph/TimelineGraphRendererSpec.js new file mode 100644 index 0000000000..303ee0f539 --- /dev/null +++ b/platform/features/timeline/test/controllers/graph/TimelineGraphRendererSpec.js @@ -0,0 +1,56 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/graph/TimelineGraphRenderer'], + function (TimelineGraphRenderer) { + 'use strict'; + + describe("A Timeline's graph renderer", function () { + var renderer; + + beforeEach(function () { + renderer = new TimelineGraphRenderer(); + }); + + it("converts utilizations to buffers", function () { + var utilization = { + getPointCount: function () { + return 10; + }, + getDomainValue: function (i) { + return i * 10; + }, + getRangeValue: function (i) { + return Math.sin(i); + } + }, + buffer = renderer.render(utilization), + i; + + // Should be flat list of alternating x/y, + // so 20 elements + expect(buffer.length).toEqual(20); + + // Verify contents + for (i = 0; i < 10; i += 1) { + // Check for 6 decimal digits of precision, roughly + // what we expect after conversion to 32-bit float + expect(buffer[i * 2]).toBeCloseTo(i * 10, 6); + expect(buffer[i * 2 + 1]).toBeCloseTo(Math.sin(i), 6); + } + }); + + it("decodes color strings", function () { + // Note that decoded color should have alpha channel as well + expect(renderer.decode('#FFFFFF')) + .toEqual([1, 1, 1, 1]); + expect(renderer.decode('#000000')) + .toEqual([0, 0, 0, 1]); + expect(renderer.decode('#FF8000')) + .toEqual([1, 0.5019607843137255, 0, 1]); + }); + + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/graph/TimelineGraphSpec.js b/platform/features/timeline/test/controllers/graph/TimelineGraphSpec.js new file mode 100644 index 0000000000..2b577b823f --- /dev/null +++ b/platform/features/timeline/test/controllers/graph/TimelineGraphSpec.js @@ -0,0 +1,151 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/graph/TimelineGraph'], + function (TimelineGraph) { + 'use strict'; + + describe("A Timeline's resource graph", function () { + var mockDomainObjects, + mockRenderer, + testColors, + mockGraphs, + graph; + + function asPromise(v) { + return (v || {}).then ? v : { + then: function (callback) { + return asPromise(callback(v)); + } + }; + } + + + beforeEach(function () { + testColors = { + a: [ 0, 1, 0 ], + b: [ 1, 0, 1 ], + c: [ 1, 0, 0 ] + }; + + mockGraphs = []; + mockDomainObjects = {}; + + ['a', 'b', 'c'].forEach(function (k, i) { + var mockGraph = jasmine.createSpyObj( + 'utilization-' + k, + [ 'getPointCount', 'getDomainValue', 'getRangeValue' ] + ); + mockDomainObjects[k] = jasmine.createSpyObj( + 'domainObject-' + k, + [ 'getCapability', 'useCapability' ] + ); + mockDomainObjects[k].useCapability.andReturn(asPromise({ + testResource: mockGraph + })); + mockGraph.getPointCount.andReturn(i + 2); + mockGraph.testField = k; + mockGraph.testIndex = i; + + // Store to allow changes later + mockGraphs.push(mockGraph); + }); + + mockRenderer = jasmine.createSpyObj( + 'renderer', + [ 'render', 'decode' ] + ); + + mockRenderer.render.andCallFake(function (utilization) { + var result = []; + while (result.length < (utilization.testIndex + 2) * 2) { + result.push(Math.floor(result.length / 2)); + // Alternate +/- to give something to test to + result.push( + ((result.length % 4 > 1) ? -1 : 1) * + (10 * utilization.testIndex) + ); + } + return result; + }); + + mockRenderer.decode.andCallFake(function (color) { + return testColors[color]; + }); + + graph = new TimelineGraph( + 'testResource', + mockDomainObjects, + mockRenderer + ); + }); + + it("exposes minimum/maximum", function () { + expect(graph.minimum()).toEqual(-20); + expect(graph.maximum()).toEqual(20); + }); + + it("exposes resource key", function () { + expect(graph.key).toEqual('testResource'); + }); + + it("exposes a rendered drawing object", function () { + // Much of the work here is done by the renderer, + // so just check that it got used and assigned + expect(graph.drawingObject.lines).toEqual([ + { + color: testColors.a, + buffer: [0, 0, 1, 0], + points: 2 + }, + { + color: testColors.b, + buffer: [0, 10, 1, -10, 2, 10], + points: 3 + }, + { + color: testColors.c, + buffer: [0, 20, 1, -20, 2, 20, 3, -20], + points: 4 + } + ]); + }); + + it("allows its bounds to be specified", function () { + graph.setBounds(42, 12321); + expect(graph.drawingObject.origin[0]).toEqual(42); + expect(graph.drawingObject.dimensions[0]).toEqual(12321); + }); + + it("provides a minimum/maximum even with no data", function () { + mockGraphs.forEach(function (mockGraph) { + mockGraph.getPointCount.andReturn(0); + }); + + // Create a graph of these utilizations + graph = new TimelineGraph( + 'testResource', + mockDomainObjects, + mockRenderer + ); + + // Verify that we get some usable defaults + expect(graph.minimum()).toEqual(jasmine.any(Number)); + expect(graph.maximum()).toEqual(jasmine.any(Number)); + expect(graph.maximum() > graph.minimum()).toBeTruthy(); + }); + + it("refreshes lines upon request", function () { + // Mock renderer looks at testIndex, so change it here... + mockGraphs[0].testIndex = 3; + // Should trigger a new render + graph.refresh(); + // Values correspond to the new index now + expect(graph.drawingObject.lines[0].buffer).toEqual( + [0, 30, 1, -30, 2, 30, 3, -30, 4, 30] + ); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineColorAssignerSpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineColorAssignerSpec.js new file mode 100644 index 0000000000..f1433bc025 --- /dev/null +++ b/platform/features/timeline/test/controllers/swimlane/TimelineColorAssignerSpec.js @@ -0,0 +1,65 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/swimlane/TimelineColorAssigner'], + function (TimelineColorAssigner) { + 'use strict'; + + describe("The Timeline legend color assigner", function () { + var testConfiguration, + assigner; + + beforeEach(function () { + testConfiguration = {}; + assigner = new TimelineColorAssigner(testConfiguration); + }); + + it("assigns colors by identifier", function () { + expect(assigner.get('xyz')).toBeUndefined(); + assigner.assign('xyz'); + expect(assigner.get('xyz')).toEqual(jasmine.any(String)); + }); + + it("releases colors", function () { + assigner.assign('xyz'); + assigner.release('xyz'); + expect(assigner.get('xyz')).toBeUndefined(); + }); + + it("provides at least 30 unique colors", function () { + var colors = {}, i, ids = []; + + // Add item to set + function set(c) { colors[c] = true; } + + // Generate ids + for (i = 0; i < 30; i += 1) { ids.push("id" + i); } + + // Assign colors to each id, then retrieve colors, + // storing into the set + ids.forEach(assigner.assign); + ids.map(assigner.get).map(set); + + // Should now be 30 elements in that set + expect(Object.keys(colors).length).toEqual(30); + }); + + it("populates the configuration with colors", function () { + assigner.assign('xyz'); + expect(testConfiguration.xyz).toBeDefined(); + }); + + it("preserves other colors when releases occur", function () { + var c; + assigner.assign('xyz'); + c = assigner.get('xyz'); + // Assign/release a different color + assigner.assign('abc'); + assigner.release('abc'); + // Our original assignment should be preserved + expect(assigner.get('xyz')).toEqual(c); + }); + + }); + } +); diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineProxySpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineProxySpec.js new file mode 100644 index 0000000000..25b807070f --- /dev/null +++ b/platform/features/timeline/test/controllers/swimlane/TimelineProxySpec.js @@ -0,0 +1,87 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/swimlane/TimelineProxy'], + function (TimelineProxy) { + 'use strict'; + + describe("The Timeline's selection proxy", function () { + var mockDomainObject, + mockSelection, + mockActionCapability, + mockActions, + proxy; + + beforeEach(function () { + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + ['getCapability'] + ); + mockSelection = jasmine.createSpyObj( + 'selection', + [ 'get' ] + ); + mockActionCapability = jasmine.createSpyObj( + 'action', + [ 'getActions' ] + ); + mockActions = ['a', 'b', 'c'].map(function (type) { + var mockAction = jasmine.createSpyObj( + 'action-' + type, + [ 'perform', 'getMetadata' ] + ); + mockAction.getMetadata.andReturn({ type: type }); + return mockAction; + }); + + mockDomainObject.getCapability.andReturn(mockActionCapability); + mockActionCapability.getActions.andReturn(mockActions); + + proxy = new TimelineProxy(mockDomainObject, mockSelection); + }); + + it("triggers a create action on add", function () { + // Should trigger b's create action + proxy.add('b'); + expect(mockActions[1].perform).toHaveBeenCalled(); + + // Also check that other actions weren't invoked + expect(mockActions[0].perform).not.toHaveBeenCalled(); + expect(mockActions[2].perform).not.toHaveBeenCalled(); + + // Verify that interactions were for correct keys + expect(mockDomainObject.getCapability) + .toHaveBeenCalledWith('action'); + expect(mockActionCapability.getActions) + .toHaveBeenCalledWith('create'); + }); + + it("invokes the action on the selection, if any", function () { + var mockOtherObject = jasmine.createSpyObj( + 'other', + ['getCapability'] + ), + mockOtherAction = jasmine.createSpyObj( + 'actionCapability', + ['getActions'] + ), + mockAction = jasmine.createSpyObj( + 'action', + ['perform', 'getMetadata'] + ); + + // Set up mocks + mockSelection.get.andReturn({ domainObject: mockOtherObject }); + mockOtherObject.getCapability.andReturn(mockOtherAction); + mockOtherAction.getActions.andReturn([mockAction]); + mockAction.getMetadata.andReturn({ type: 'z' }); + + // Invoke add method; should create with selected object + proxy.add('z'); + expect(mockAction.perform).toHaveBeenCalled(); + }); + + + }); + } +); diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js new file mode 100644 index 0000000000..ba4b72dca2 --- /dev/null +++ b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js @@ -0,0 +1,160 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/swimlane/TimelineSwimlaneDecorator'], + function (TimelineSwimlaneDecorator) { + 'use strict'; + + describe("A Timeline swimlane decorator", function () { + var mockSwimlane, + mockSelection, + mockCapabilities, + testModel, + mockPromise, + decorator; + + beforeEach(function () { + mockSwimlane = {}; + mockCapabilities = {}; + testModel = {}; + + mockSelection = jasmine.createSpyObj('selection', ['select', 'get']); + + mockSwimlane.domainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getCapability', 'getModel' ] + ); + + mockCapabilities.mutation = jasmine.createSpyObj( + 'mutation', + ['mutate'] + ); + mockCapabilities.persistence = jasmine.createSpyObj( + 'persistence', + ['persist'] + ); + mockCapabilities.type = jasmine.createSpyObj( + 'type', + ['instanceOf'] + ); + mockPromise = jasmine.createSpyObj('promise', ['then']); + + mockSwimlane.domainObject.getCapability.andCallFake(function (c) { + return mockCapabilities[c]; + }); + mockSwimlane.domainObject.getModel.andReturn(testModel); + + mockCapabilities.type.instanceOf.andReturn(true); + mockCapabilities.mutation.mutate.andReturn(mockPromise); + + decorator = new TimelineSwimlaneDecorator( + mockSwimlane, + mockSelection + ); + }); + + it("returns the same object instance", function () { + // Decoration should occur in-place + expect(decorator).toBe(mockSwimlane); + }); + + it("adds a 'modes' getter-setter to activities", function () { + expect(mockSwimlane.modes).toEqual(jasmine.any(Function)); + expect(mockCapabilities.type.instanceOf) + .toHaveBeenCalledWith('warp.activity'); + }); + + it("adds a 'link' getter-setter to activities", function () { + expect(mockSwimlane.link).toEqual(jasmine.any(Function)); + expect(mockCapabilities.type.instanceOf) + .toHaveBeenCalledWith('warp.activity'); + }); + + it("gets modes from the domain object model", function () { + testModel.relationships = { modes: ['a', 'b', 'c'] }; + expect(decorator.modes()).toEqual(['a', 'b', 'c']); + testModel.relationships = { modes: ['x', 'y', 'z'] }; + expect(decorator.modes()).toEqual(['x', 'y', 'z']); + // Verify that it worked as a getter + expect(mockCapabilities.mutation.mutate) + .not.toHaveBeenCalled(); + }); + + it("gets links from the domain object model", function () { + testModel.link = "http://www.nasa.gov"; + expect(decorator.link()).toEqual("http://www.nasa.gov"); + // Verify that it worked as a getter + expect(mockCapabilities.mutation.mutate) + .not.toHaveBeenCalled(); + }); + + it("mutates modes when used as a setter", function () { + decorator.modes(['abc', 'xyz']); + expect(mockCapabilities.mutation.mutate) + .toHaveBeenCalledWith(jasmine.any(Function)); + mockCapabilities.mutation.mutate.mostRecentCall.args[0](testModel); + expect(testModel.relationships.modes).toEqual(['abc', 'xyz']); + + // Verify that persistence is called when promise resolves + expect(mockCapabilities.persistence.persist).not.toHaveBeenCalled(); + mockPromise.then.mostRecentCall.args[0](); + expect(mockCapabilities.persistence.persist).toHaveBeenCalled(); + }); + + it("mutates modes when used as a setter", function () { + decorator.link("http://www.noaa.gov"); + expect(mockCapabilities.mutation.mutate) + .toHaveBeenCalledWith(jasmine.any(Function)); + mockCapabilities.mutation.mutate.mostRecentCall.args[0](testModel); + expect(testModel.link).toEqual("http://www.noaa.gov"); + + // Verify that persistence is called when promise resolves + expect(mockCapabilities.persistence.persist).not.toHaveBeenCalled(); + mockPromise.then.mostRecentCall.args[0](); + expect(mockCapabilities.persistence.persist).toHaveBeenCalled(); + }); + + it("does not provide a 'remove' method with no parent", function () { + expect(decorator.remove).not.toEqual(jasmine.any(Function)); + }); + + it("fires the 'remove' action when remove is called", function () { + var mockChild = jasmine.createSpyObj( + 'childObject', + [ 'getCapability', 'getModel' ] + ), + mockAction = jasmine.createSpyObj( + 'action', + [ 'perform' ] + ); + + mockChild.getCapability.andCallFake(function (c) { + return c === 'action' ? mockAction : undefined; + }); + + // Create a child swimlane; it should have a remove action + new TimelineSwimlaneDecorator({ + domainObject: mockChild, + parent: decorator, + depth: 1 + }).remove(); + + expect(mockChild.getCapability).toHaveBeenCalledWith('action'); + expect(mockAction.perform).toHaveBeenCalledWith('remove'); + }); + + it("allows the swimlane to be selected", function () { + decorator.select(); + expect(mockSelection.select).toHaveBeenCalledWith(decorator); + }); + + it("allows checking for swimlane selection state", function () { + expect(decorator.selected()).toBeFalsy(); + mockSelection.get.andReturn(decorator); + expect(decorator.selected()).toBeTruthy(); + }); + + }); + + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDropHandlerSpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDropHandlerSpec.js new file mode 100644 index 0000000000..1019379326 --- /dev/null +++ b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDropHandlerSpec.js @@ -0,0 +1,173 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/swimlane/TimelineSwimlaneDropHandler'], + function (TimelineSwimlaneDropHandler) { + "use strict"; + + describe("A timeline's swimlane drop handler", function () { + var mockSwimlane, + mockOtherObject, + mockActionCapability, + mockPersistence, + handler; + + beforeEach(function () { + mockSwimlane = jasmine.createSpyObj( + "swimlane", + [ "highlight", "highlightBottom" ] + ); + // domainObject, idPath, children, expanded + mockSwimlane.domainObject = jasmine.createSpyObj( + "domainObject", + [ "getId", "getCapability", "useCapability", "hasCapability" ] + ); + mockSwimlane.idPath = [ 'a', 'b' ]; + mockSwimlane.children = [ {} ]; + mockSwimlane.expanded = true; + + mockSwimlane.parent = {}; + mockSwimlane.parent.idPath = ['a']; + mockSwimlane.parent.domainObject = jasmine.createSpyObj( + "domainObject", + [ "getId", "getCapability", "useCapability", "hasCapability" ] + ); + mockSwimlane.parent.children = [ mockSwimlane ]; + + mockSwimlane.children[0].domainObject = jasmine.createSpyObj( + "domainObject", + [ "getId", "getCapability", "useCapability", "hasCapability" ] + ); + + + mockOtherObject = jasmine.createSpyObj( + "domainObject", + [ "getId", "getCapability", "useCapability", "hasCapability" ] + ); + mockActionCapability = jasmine.createSpyObj("action", ["perform", "getActions"]); + mockPersistence = jasmine.createSpyObj("persistence", ["persist"]); + + mockActionCapability.getActions.andReturn([{}]); + mockSwimlane.parent.domainObject.getId.andReturn('a'); + mockSwimlane.domainObject.getId.andReturn('b'); + mockSwimlane.children[0].domainObject.getId.andReturn('c'); + mockOtherObject.getId.andReturn('d'); + + mockSwimlane.domainObject.getCapability.andCallFake(function (c) { + return { + action: mockActionCapability, + persistence: mockPersistence + }[c]; + }); + mockOtherObject.getCapability.andReturn(mockActionCapability); + + mockSwimlane.domainObject.hasCapability.andReturn(true); + + handler = new TimelineSwimlaneDropHandler(mockSwimlane); + }); + + it("disallows drop outside of edit mode", function () { + // Verify precondition + expect(handler.allowDropIn('d')).toBeTruthy(); + expect(handler.allowDropAfter('d')).toBeTruthy(); + // Act as if we're not in edit mode + mockSwimlane.domainObject.hasCapability.andReturn(false); + // Now, they should be disallowed + expect(handler.allowDropIn('d')).toBeFalsy(); + expect(handler.allowDropAfter('d')).toBeFalsy(); + + // Verify that editor capability was really checked for + expect(mockSwimlane.domainObject.hasCapability) + .toHaveBeenCalledWith('editor'); + }); + + it("disallows dropping of parents", function () { + expect(handler.allowDropIn('a')).toBeFalsy(); + expect(handler.allowDropAfter('a')).toBeFalsy(); + }); + + it("does not drop when no highlight state is present", function () { + // If there's no hover highlight, there's no drop allowed + handler.drop('d', mockOtherObject); + expect(mockOtherObject.getCapability) + .not.toHaveBeenCalled(); + expect(mockSwimlane.domainObject.useCapability) + .not.toHaveBeenCalled(); + expect(mockSwimlane.parent.domainObject.useCapability) + .not.toHaveBeenCalled(); + }); + + it("inserts into when highlighted", function () { + var testModel = { composition: [ 'c' ] }; + mockSwimlane.highlight.andReturn(true); + handler.drop('d'); + // Should have mutated + expect(mockSwimlane.domainObject.useCapability) + .toHaveBeenCalledWith("mutation", jasmine.any(Function)); + // Run the mutator + mockSwimlane.domainObject.useCapability.mostRecentCall + .args[1](testModel); + expect(testModel.composition).toEqual(['c', 'd']); + // Finally, should also have persisted + expect(mockPersistence.persist).toHaveBeenCalled(); + }); + + it("removes objects before insertion, if provided", function () { + var testModel = { composition: [ 'c' ] }; + mockSwimlane.highlight.andReturn(true); + handler.drop('d', mockOtherObject); + // Should have invoked a remove action + expect(mockActionCapability.perform) + .toHaveBeenCalledWith('remove'); + // Verify that mutator still ran as expected + mockSwimlane.domainObject.useCapability.mostRecentCall + .args[1](testModel); + expect(testModel.composition).toEqual(['c', 'd']); + }); + + it("inserts after as a peer when highlighted at the bottom", function () { + var testModel = { composition: [ 'x', 'b', 'y' ] }; + mockSwimlane.highlightBottom.andReturn(true); + mockSwimlane.expanded = false; + handler.drop('d'); + // Should have mutated + expect(mockSwimlane.parent.domainObject.useCapability) + .toHaveBeenCalledWith("mutation", jasmine.any(Function)); + // Run the mutator + mockSwimlane.parent.domainObject.useCapability.mostRecentCall + .args[1](testModel); + expect(testModel.composition).toEqual([ 'x', 'b', 'd', 'y']); + }); + + it("inserts into when highlighted at the bottom and expanded", function () { + var testModel = { composition: [ 'c' ] }; + mockSwimlane.highlightBottom.andReturn(true); + mockSwimlane.expanded = true; + handler.drop('d'); + // Should have mutated + expect(mockSwimlane.domainObject.useCapability) + .toHaveBeenCalledWith("mutation", jasmine.any(Function)); + // Run the mutator + mockSwimlane.domainObject.useCapability.mostRecentCall + .args[1](testModel); + expect(testModel.composition).toEqual([ 'd', 'c' ]); + }); + + it("inserts after as a peer when highlighted at the bottom and childless", function () { + var testModel = { composition: [ 'x', 'b', 'y' ] }; + mockSwimlane.highlightBottom.andReturn(true); + mockSwimlane.expanded = true; + mockSwimlane.children = []; + handler.drop('d'); + // Should have mutated + expect(mockSwimlane.parent.domainObject.useCapability) + .toHaveBeenCalledWith("mutation", jasmine.any(Function)); + // Run the mutator + mockSwimlane.parent.domainObject.useCapability.mostRecentCall + .args[1](testModel); + expect(testModel.composition).toEqual([ 'x', 'b', 'd', 'y']); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlanePopulatorSpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlanePopulatorSpec.js new file mode 100644 index 0000000000..6d5c370de6 --- /dev/null +++ b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlanePopulatorSpec.js @@ -0,0 +1,135 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/swimlane/TimelineSwimlanePopulator'], + function (TimelineSwimlanePopulator) { + 'use strict'; + + describe("A Timeline swimlane populator", function () { + var mockLoader, + mockSelection, + testConfiguration, + mockDomainObject, + mockDomainObjects, + mockCallback, + populator; + + function asPromise(value) { + return (value || {}).then ? value : { + then: function (callback) { + return asPromise(callback(value)); + } + }; + } + + function makeMockDomainObject(id, composition) { + var mockDomainObject = jasmine.createSpyObj( + 'domainObject-' + id, + ['getId', 'getModel', 'getCapability', 'useCapability'] + ); + + mockDomainObject.getId.andReturn(id); + mockDomainObject.getModel.andReturn({ composition: composition }); + mockDomainObject.useCapability.andReturn(asPromise(false)); + + return mockDomainObject; + } + + function subgraph(domainObject, objects) { + function lookupSubgraph(id) { + return subgraph(objects[id], objects); + } + return { + domainObject: domainObject, + composition: domainObject.getModel().composition + .map(lookupSubgraph) + }; + } + + beforeEach(function () { + mockLoader = jasmine.createSpyObj('objectLoader', ['load']); + mockDomainObject = makeMockDomainObject('a', ['b', 'c']); + mockDomainObjects = { + a: mockDomainObject, + b: makeMockDomainObject('b', ['d']), + c: makeMockDomainObject('c', ['e', 'f']), + d: makeMockDomainObject('d', []), + e: makeMockDomainObject('e', []), + f: makeMockDomainObject('f', []) + }; + mockSelection = jasmine.createSpyObj( + 'selection', + ['get', 'select', 'proxy'] + ); + mockCallback = jasmine.createSpy('callback'); + + testConfiguration = {}; + + mockLoader.load.andReturn(asPromise(subgraph( + mockDomainObject, + mockDomainObjects + ))); + + populator = new TimelineSwimlanePopulator( + mockLoader, + testConfiguration, + mockSelection + ); + }); + + it("uses the loader to find subgraph", function () { + populator.populate(mockDomainObject); + expect(mockLoader.load).toHaveBeenCalledWith( + mockDomainObject, + 'timespan' + ); + }); + + it("provides a list of swimlanes", function () { + populator.populate(mockDomainObject); + // Ensure swimlane order matches depth-first search + expect(populator.get().map(function (swimlane) { + return swimlane.domainObject; + })).toEqual([ + mockDomainObjects.a, + mockDomainObjects.b, + mockDomainObjects.d, + mockDomainObjects.c, + mockDomainObjects.e, + mockDomainObjects.f + ]); + }); + + it("clears swimlanes if no object is provided", function () { + populator.populate(); + expect(populator.get()).toEqual([]); + }); + + it("preserves selection state when possible", function () { + // Repopulate swimlanes + populator.populate(mockDomainObject); + + // Act as if something is already selected + mockSelection.get.andReturn(populator.get()[1]); + + // Verify precondition + expect(mockSelection.select).not.toHaveBeenCalled(); + + // Repopulate swimlanes + populator.populate(mockDomainObject); + + // Selection should have been preserved + expect(mockSelection.select).toHaveBeenCalled(); + expect(mockSelection.select.mostRecentCall.args[0].domainObject) + .toEqual(mockDomainObjects.b); + }); + + it("exposes a selection proxy for the timeline", function () { + populator.populate(mockDomainObject); + expect(mockSelection.proxy).toHaveBeenCalled(); + }); + + + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneSpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneSpec.js new file mode 100644 index 0000000000..2351786973 --- /dev/null +++ b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneSpec.js @@ -0,0 +1,202 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../../src/controllers/swimlane/TimelineSwimlane'], + function (TimelineSwimlane) { + 'use strict'; + + describe("A Timeline swimlane", function () { + var parent, + child, + mockParentObject, + mockChildObject, + mockAssigner, + mockActionCapability, + mockParentTimespan, + mockChildTimespan, + testConfiguration; + + function asPromise(v) { + return { then: function (cb) { cb(v); } }; + } + + beforeEach(function () { + mockParentObject = jasmine.createSpyObj( + 'parent', + ['getId', 'getCapability', 'useCapability', 'getModel'] + ); + mockChildObject = jasmine.createSpyObj( + 'child', + ['getId', 'getCapability', 'useCapability', 'getModel'] + ); + mockAssigner = jasmine.createSpyObj( + 'assigner', + ['get', 'assign', 'release'] + ); + mockParentTimespan = jasmine.createSpyObj( + 'parentTimespan', + ['getStart', 'getEnd'] + ); + mockChildTimespan = jasmine.createSpyObj( + 'childTimespan', + ['getStart', 'getEnd'] + ); + mockActionCapability = jasmine.createSpyObj('action', ['perform']); + + mockParentObject.getId.andReturn('test-parent'); + mockParentObject.getCapability.andReturn(mockActionCapability); + mockParentObject.useCapability.andReturn(asPromise(mockParentTimespan)); + mockParentObject.getModel.andReturn({ name: "Test Parent" }); + mockChildObject.getModel.andReturn({ name: "Test Child" }); + mockChildObject.useCapability.andReturn(asPromise(mockChildTimespan)); + + testConfiguration = { graph: {} }; + + parent = new TimelineSwimlane( + mockParentObject, + mockAssigner, + testConfiguration + ); + child = new TimelineSwimlane( + mockChildObject, + mockAssigner, + testConfiguration, + parent + ); + }); + + it("exposes its domain object", function () { + expect(parent.domainObject).toEqual(mockParentObject); + expect(child.domainObject).toEqual(mockChildObject); + }); + + it("exposes its depth", function () { + expect(parent.depth).toEqual(0); + expect(child.depth).toEqual(1); + expect(new TimelineSwimlane(mockParentObject, {}, {}, child).depth) + .toEqual(2); + }); + + it("exposes its path as readable text", function () { + var grandchild = new TimelineSwimlane(mockParentObject, {}, {}, child), + ggc = new TimelineSwimlane(mockParentObject, {}, {}, grandchild); + + expect(parent.path).toEqual(""); + expect(child.path).toEqual(""); + expect(grandchild.path).toEqual("Test Child > "); + expect(ggc.path).toEqual("Test Child > Test Parent > "); + }); + + it("starts off expanded", function () { + expect(parent.expanded).toBeTruthy(); + expect(child.expanded).toBeTruthy(); + }); + + it("determines visibility based on parent expansion", function () { + parent.expanded = false; + expect(child.visible()).toBeFalsy(); + parent.expanded = true; + expect(child.visible()).toBeTruthy(); + }); + + it("is visible when it is the root of the timeline subgraph", function () { + expect(parent.visible()).toBeTruthy(); + }); + + it("fires the Edit Properties action on request", function () { + parent.properties(); + expect(mockParentObject.getCapability).toHaveBeenCalledWith('action'); + expect(mockActionCapability.perform).toHaveBeenCalledWith('properties'); + }); + + it("allows resource graph inclusion to be toggled", function () { + expect(testConfiguration.graph['test-parent']).toBeFalsy(); + parent.toggleGraph(); + expect(testConfiguration.graph['test-parent']).toBeTruthy(); + parent.toggleGraph(); + expect(testConfiguration.graph['test-parent']).toBeFalsy(); + }); + + it("provides a getter-setter for graph inclusion", function () { + expect(testConfiguration.graph['test-parent']).toBeFalsy(); + expect(parent.graph(true)).toBeTruthy(); + expect(parent.graph()).toBeTruthy(); + expect(testConfiguration.graph['test-parent']).toBeTruthy(); + expect(parent.graph(false)).toBeFalsy(); + expect(parent.graph()).toBeFalsy(); + expect(testConfiguration.graph['test-parent']).toBeFalsy(); + }); + + it("gets colors from the provided assigner", function () { + mockAssigner.get.andReturn("#ABCABC"); + expect(parent.color()).toEqual("#ABCABC"); + // Verify that id was passed, and no other interactions + expect(mockAssigner.get).toHaveBeenCalledWith('test-parent'); + expect(mockAssigner.assign).not.toHaveBeenCalled(); + expect(mockAssigner.release).not.toHaveBeenCalled(); + }); + + it("allows colors to be set", function () { + parent.color("#F0000D"); + expect(mockAssigner.assign).toHaveBeenCalledWith( + 'test-parent', + "#F0000D" + ); + }); + + it("assigns colors when resource graph state is toggled", function () { + expect(mockAssigner.assign).not.toHaveBeenCalled(); + parent.toggleGraph(); + expect(mockAssigner.assign).toHaveBeenCalledWith('test-parent'); + expect(mockAssigner.release).not.toHaveBeenCalled(); + parent.toggleGraph(); + expect(mockAssigner.release).toHaveBeenCalledWith('test-parent'); + }); + + it("assigns colors when resource graph state is set", function () { + expect(mockAssigner.assign).not.toHaveBeenCalled(); + parent.graph(true); + expect(mockAssigner.assign).toHaveBeenCalledWith('test-parent'); + expect(mockAssigner.release).not.toHaveBeenCalled(); + parent.graph(false); + expect(mockAssigner.release).toHaveBeenCalledWith('test-parent'); + }); + + it("provides getter-setters for drag-drop highlights", function () { + expect(parent.highlight()).toBeFalsy(); + parent.highlight(true); + expect(parent.highlight()).toBeTruthy(); + + expect(parent.highlightBottom()).toBeFalsy(); + parent.highlightBottom(true); + expect(parent.highlightBottom()).toBeTruthy(); + }); + + it("detects start/end violations", function () { + mockParentTimespan.getStart.andReturn(42); + mockParentTimespan.getEnd.andReturn(12321); + + // First, start with a valid timespan + mockChildTimespan.getStart.andReturn(84); + mockChildTimespan.getEnd.andReturn(100); + expect(child.exceeded()).toBeFalsy(); + + // Start time violation + mockChildTimespan.getStart.andReturn(21); + expect(child.exceeded()).toBeTruthy(); + + // Now both in violation + mockChildTimespan.getEnd.andReturn(20000); + expect(child.exceeded()).toBeTruthy(); + + // And just the end + mockChildTimespan.getStart.andReturn(100); + expect(child.exceeded()).toBeTruthy(); + + // Now back to everything's-just-fine + mockChildTimespan.getEnd.andReturn(10000); + expect(child.exceeded()).toBeFalsy(); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/directives/SwimlaneDragConstantsSpec.js b/platform/features/timeline/test/directives/SwimlaneDragConstantsSpec.js new file mode 100644 index 0000000000..b9fe7caf50 --- /dev/null +++ b/platform/features/timeline/test/directives/SwimlaneDragConstantsSpec.js @@ -0,0 +1,15 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/directives/SwimlaneDragConstants'], + function (SwimlaneDragConstants) { + "use strict"; + + describe("Timeline swimlane drag constants", function () { + it("define a custom type for swimlane drag-drop", function () { + expect(SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE) + .toEqual(jasmine.any(String)); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/directives/WARPSwimlaneDragSpec.js b/platform/features/timeline/test/directives/WARPSwimlaneDragSpec.js new file mode 100644 index 0000000000..360fff4b69 --- /dev/null +++ b/platform/features/timeline/test/directives/WARPSwimlaneDragSpec.js @@ -0,0 +1,76 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/directives/WARPSwimlaneDrag', '../../src/directives/SwimlaneDragConstants'], + function (WARPSwimlaneDrag, SwimlaneDragConstants) { + "use strict"; + + describe("The warp-swimlane-drag directive", function () { + var mockDndService, + mockScope, + mockElement, + testAttrs, + handlers, + directive; + + beforeEach(function () { + var scopeExprs = { + someTestExpr: "some swimlane" + }; + + handlers = {}; + + mockDndService = jasmine.createSpyObj( + 'dndService', + ['setData', 'getData', 'removeData'] + ); + mockScope = jasmine.createSpyObj('$scope', ['$eval']); + mockElement = jasmine.createSpyObj('element', ['on']); + testAttrs = { warpSwimlaneDrag: "someTestExpr" }; + + // Simulate evaluation of expressions in scope + mockScope.$eval.andCallFake(function (expr) { + return scopeExprs[expr]; + }); + + directive = new WARPSwimlaneDrag(mockDndService); + + // Run the link function, then capture the event handlers + // for testing. + directive.link(mockScope, mockElement, testAttrs); + + mockElement.on.calls.forEach(function (call) { + handlers[call.args[0]] = call.args[1]; + }); + + }); + + it("is available as an attribute", function () { + expect(directive.restrict).toEqual("A"); + }); + + it("exposes the swimlane when dragging starts", function () { + // Verify precondition + expect(mockDndService.setData).not.toHaveBeenCalled(); + // Start a drag + handlers.dragstart(); + // Should have exposed the swimlane + expect(mockDndService.setData).toHaveBeenCalledWith( + SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE, + "some swimlane" + ); + }); + + it("clears the swimlane when dragging ends", function () { + // Verify precondition + expect(mockDndService.removeData).not.toHaveBeenCalled(); + // Start a drag + handlers.dragend(); + // Should have exposed the swimlane + expect(mockDndService.removeData).toHaveBeenCalledWith( + SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE + ); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/directives/WARPSwimlaneDropSpec.js b/platform/features/timeline/test/directives/WARPSwimlaneDropSpec.js new file mode 100644 index 0000000000..f1f0858503 --- /dev/null +++ b/platform/features/timeline/test/directives/WARPSwimlaneDropSpec.js @@ -0,0 +1,147 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/directives/WARPSwimlaneDrop'], + function (WARPSwimlaneDrop) { + "use strict"; + + var TEST_HEIGHT = 100, + TEST_TOP = 600; + + describe("The warp-swimlane-drop directive", function () { + var mockDndService, + mockScope, + mockElement, + testAttrs, + mockSwimlane, + mockRealElement, + testEvent, + handlers, + directive; + + function getterSetter(value) { + return function (newValue) { + return (value = (arguments.length > 0) ? newValue : value); + }; + } + + beforeEach(function () { + var scopeExprs = {}; + + handlers = {}; + + mockDndService = jasmine.createSpyObj( + 'dndService', + ['setData', 'getData', 'removeData'] + ); + mockScope = jasmine.createSpyObj('$scope', ['$eval']); + mockElement = jasmine.createSpyObj('element', ['on']); + testAttrs = { warpSwimlaneDrop: "mockSwimlane" }; + mockSwimlane = jasmine.createSpyObj( + "swimlane", + [ "allowDropIn", "allowDropAfter", "drop", "highlight", "highlightBottom" ] + ); + mockElement[0] = jasmine.createSpyObj( + "realElement", + [ "getBoundingClientRect" ] + ); + mockElement[0].offsetHeight = TEST_HEIGHT; + mockElement[0].getBoundingClientRect.andReturn({ top: TEST_TOP }); + + // Simulate evaluation of expressions in scope + scopeExprs.mockSwimlane = mockSwimlane; + mockScope.$eval.andCallFake(function (expr) { + return scopeExprs[expr]; + }); + + + mockSwimlane.allowDropIn.andReturn(true); + mockSwimlane.allowDropAfter.andReturn(true); + // Simulate getter-setter behavior + mockSwimlane.highlight.andCallFake(getterSetter(false)); + mockSwimlane.highlightBottom.andCallFake(getterSetter(false)); + + + + testEvent = { + pageY: TEST_TOP + TEST_HEIGHT / 10, + dataTransfer: { getData: jasmine.createSpy() }, + preventDefault: jasmine.createSpy() + }; + + testEvent.dataTransfer.getData.andReturn('abc'); + mockDndService.getData.andReturn({ domainObject: 'someDomainObject' }); + + directive = new WARPSwimlaneDrop(mockDndService); + + // Run the link function, then capture the event handlers + // for testing. + directive.link(mockScope, mockElement, testAttrs); + + mockElement.on.calls.forEach(function (call) { + handlers[call.args[0]] = call.args[1]; + }); + + }); + + it("is available as an attribute", function () { + expect(directive.restrict).toEqual("A"); + }); + + it("updates highlights on drag over", function () { + // Near the top + testEvent.pageY = TEST_TOP + TEST_HEIGHT / 10; + + handlers.dragover(testEvent); + + expect(mockSwimlane.highlight).toHaveBeenCalledWith(true); + expect(mockSwimlane.highlightBottom).toHaveBeenCalledWith(false); + }); + + it("updates bottom highlights on drag over", function () { + // Near the bottom + testEvent.pageY = TEST_TOP + TEST_HEIGHT - TEST_HEIGHT / 10; + + handlers.dragover(testEvent); + + expect(mockSwimlane.highlight).toHaveBeenCalledWith(false); + expect(mockSwimlane.highlightBottom).toHaveBeenCalledWith(true); + }); + + it("respects swimlane's allowDropIn response", function () { + // Near the top + testEvent.pageY = TEST_TOP + TEST_HEIGHT / 10; + + mockSwimlane.allowDropIn.andReturn(false); + + handlers.dragover(testEvent); + + expect(mockSwimlane.highlight).toHaveBeenCalledWith(false); + expect(mockSwimlane.highlightBottom).toHaveBeenCalledWith(false); + }); + + it("respects swimlane's allowDropAfter response", function () { + // Near the top + testEvent.pageY = TEST_TOP + TEST_HEIGHT - TEST_HEIGHT / 10; + + mockSwimlane.allowDropAfter.andReturn(false); + + handlers.dragover(testEvent); + + expect(mockSwimlane.highlight).toHaveBeenCalledWith(false); + expect(mockSwimlane.highlightBottom).toHaveBeenCalledWith(false); + }); + + it("notifies swimlane on drop", function () { + handlers.drop(testEvent); + expect(mockSwimlane.drop).toHaveBeenCalledWith('abc', 'someDomainObject'); + }); + + it("clears highlights when drag leaves", function () { + handlers.dragleave(); + expect(mockSwimlane.highlight).toHaveBeenCalledWith(false); + expect(mockSwimlane.highlightBottom).toHaveBeenCalledWith(false); + }); + }); + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/services/ObjectLoaderSpec.js b/platform/features/timeline/test/services/ObjectLoaderSpec.js new file mode 100644 index 0000000000..77b5d13c36 --- /dev/null +++ b/platform/features/timeline/test/services/ObjectLoaderSpec.js @@ -0,0 +1,136 @@ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ + +define( + ['../../src/services/ObjectLoader'], + function (ObjectLoader) { + "use strict"; + + describe("The domain object loader", function () { + var mockQ, + mockCallback, + mockDomainObjects, + testCompositions, + objectLoader; + + function asPromise(value) { + return (value || {}).then ? value : { + then: function (callback) { + return asPromise(callback(value)); + } + }; + } + + function lookupObject(id) { + return mockDomainObjects[id]; + } + + function fullSubgraph(id) { + return { + domainObject: mockDomainObjects[id], + composition: (testCompositions[id] || []) + .map(fullSubgraph) + }; + } + + function addDomainObject(id, children, capabilities) { + var mockDomainObject = jasmine.createSpyObj( + 'object-' + id, + [ 'useCapability', 'hasCapability', 'getId' ] + ); + + mockDomainObject.getId.andReturn(id); + mockDomainObject.useCapability.andCallFake(function (c) { + return c === 'composition' ? + asPromise(children.map(lookupObject)) : + undefined; + }); + mockDomainObject.hasCapability.andCallFake(function (c) { + return (capabilities.indexOf(c) !== -1) || (c === 'composition'); + }); + mockDomainObjects[id] = mockDomainObject; + + testCompositions[id] = children; + } + + beforeEach(function () { + mockQ = jasmine.createSpyObj('$q', [ 'when', 'all' ]); + mockCallback = jasmine.createSpy('callback'); + mockDomainObjects = {}; + testCompositions = {}; + + // Provide subset of q's actual behavior which we + // expect object loader to really need + mockQ.when.andCallFake(asPromise); + mockQ.all.andCallFake(function (values) { + var result = []; + function addResult(v) { result.push(v); } + function promiseResult(v) { asPromise(v).then(addResult); } + values.forEach(promiseResult); + return asPromise(result); + }); + + // Populate some mock domain objects + addDomainObject('a', ['b', 'c', 'd'], ['test']); + addDomainObject('b', ['c', 'd', 'ba'], []); + addDomainObject('c', ['ca'], ['test']); + addDomainObject('d', [], ['test']); + addDomainObject('ba', [], ['test']); + addDomainObject('ca', [], ['test']); + + objectLoader = new ObjectLoader(mockQ); + }); + + + + it("loads sub-graphs of composition hierarchy", function () { + objectLoader.load(mockDomainObjects.a).then(mockCallback); + // Should have loaded full graph + expect(mockCallback).toHaveBeenCalledWith(fullSubgraph('a')); + }); + + it("filters based on capabilities, if requested", function () { + objectLoader.load(mockDomainObjects.a, 'test') + .then(mockCallback); + // Should have pruned 'b' + expect(mockCallback).toHaveBeenCalledWith({ + domainObject: mockDomainObjects.a, + composition: [ + fullSubgraph('c'), + fullSubgraph('d') + ] + }); + }); + + it("filters with a function, if requested", function () { + function shortName(domainObject) { + return domainObject.getId().length === 1; + } + objectLoader.load(mockDomainObjects.a, shortName) + .then(mockCallback); + // Should have pruned 'ba' and 'ca' + expect(mockCallback).toHaveBeenCalledWith({ + domainObject: mockDomainObjects.a, + composition: [ + { + domainObject: mockDomainObjects.b, + composition: [ + { + domainObject: mockDomainObjects.c, + composition: [] + }, + fullSubgraph('d') + ] + }, + { + domainObject: mockDomainObjects.c, + composition: [] + }, + fullSubgraph('d') + ] + }); + }); + + }); + + } +); \ No newline at end of file diff --git a/platform/features/timeline/test/suite.json b/platform/features/timeline/test/suite.json new file mode 100644 index 0000000000..f04528a61c --- /dev/null +++ b/platform/features/timeline/test/suite.json @@ -0,0 +1,50 @@ +[ + "TimelineConstants", + "TimelineFormatter", + + "capabilities/ActivityTimespan", + "capabilities/ActivityTimespanCapability", + "capabilities/ActivityUtilization", + "capabilities/CostCapability", + "capabilities/GraphCapability", + "capabilities/CumulativeGraph", + "capabilities/ResourceGraph", + "capabilities/TimelineTimespan", + "capabilities/TimelineTimespanCapability", + "capabilities/TimelineUtilization", + "capabilities/UtilizationCapability", + + "controllers/ActivityModeValuesController", + "controllers/TimelineController", + "controllers/TimelineGanttController", + "controllers/TimelineGraphController", + "controllers/TimelineTableController", + "controllers/TimelineTickController", + "controllers/TimelineZoomController", + "controllers/WARPDateTimeController", + + "controllers/drag/TimelineDragHandler", + "controllers/drag/TimelineDragHandleFactory", + "controllers/drag/TimelineDragPopulator", + "controllers/drag/TimelineSnapHandler", + "controllers/drag/TimelineStartHandle", + "controllers/drag/TimelineMoveHandle", + "controllers/drag/TimelineEndHandle", + + "controllers/graph/TimelineGraph", + "controllers/graph/TimelineGraphPopulator", + "controllers/graph/TimelineGraphRenderer", + + "controllers/swimlane/TimelineColorAssigner", + "controllers/swimlane/TimelineProxy", + "controllers/swimlane/TimelineSwimlane", + "controllers/swimlane/TimelineSwimlaneDecorator", + "controllers/swimlane/TimelineSwimlaneDropHandler", + "controllers/swimlane/TimelineSwimlanePopulator", + + "directives/SwimlaneDragConstants", + "directives/WARPSwimlaneDrag", + "directives/WARPSwimlaneDrop", + + "services/ObjectLoader" +] From 96f72b376573ea29c23a188beb8ddb81150c0709 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 14 Sep 2015 16:52:56 -0700 Subject: [PATCH 002/488] [Clocks] Add license, change keys Un-namespace keys in clock plugin, and add licensing info for moment-duration-format. WTD-1239 --- LICENSES.md | 35 +++++++++++++++++++ platform/features/clock/bundle.json | 26 +++++++------- .../clock/src/actions/RestartTimerAction.js | 4 +-- .../clock/src/actions/StartTimerAction.js | 4 +-- .../clock/src/controllers/TimerController.js | 2 +- .../test/actions/RestartTimerActionSpec.js | 8 ++--- .../test/controllers/TimerControllerSpec.js | 4 +-- 7 files changed, 59 insertions(+), 24 deletions(-) diff --git a/LICENSES.md b/LICENSES.md index 0224a1ae4d..f7b6c98b11 100644 --- a/LICENSES.md +++ b/LICENSES.md @@ -345,6 +345,41 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI --- +### moment-duration-format + +#### Info + +* Link: https://github.com/jsmreese/moment-duration-format + +* Version: 1.3.0 + +* Authors: John Madhavan-Reese + +* Description: Duration parsing/formatting + +#### License + +Copyright 2014 John Madhavan-Reese + +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. + +--- + ### Json.NET #### Info diff --git a/platform/features/clock/bundle.json b/platform/features/clock/bundle.json index ee40b028b9..0d1ac5611a 100644 --- a/platform/features/clock/bundle.json +++ b/platform/features/clock/bundle.json @@ -1,5 +1,5 @@ { - "name": "WARP Clocks/Timers", + "name": "Clocks/Timers", "descriptions": "Domain objects for displaying current & relative times.", "configuration": { "paths": { @@ -17,13 +17,13 @@ "indicators": [ { "implementation": "indicators/ClockIndicator.js", - "depends": [ "warp.tickerService", "CLOCK_INDICATOR_FORMAT" ], + "depends": [ "tickerService", "CLOCK_INDICATOR_FORMAT" ], "priority": "preferred" } ], "services": [ { - "key": "warp.tickerService", + "key": "tickerService", "implementation": "services/TickerService.js", "depends": [ "$timeout", "now" ] } @@ -32,7 +32,7 @@ { "key": "ClockController", "implementation": "controllers/ClockController.js", - "depends": [ "$scope", "warp.tickerService" ] + "depends": [ "$scope", "tickerService" ] }, { "key": "TimerController", @@ -42,24 +42,24 @@ { "key": "RefreshingController", "implementation": "controllers/RefreshingController.js", - "depends": [ "$scope", "warp.tickerService" ] + "depends": [ "$scope", "tickerService" ] } ], "views": [ { - "key": "warp.clock", - "type": "warp.clock", + "key": "clock", + "type": "clock", "templateUrl": "templates/clock.html" }, { - "key": "warp.timer", - "type": "warp.timer", + "key": "timer", + "type": "timer", "templateUrl": "templates/timer.html" } ], "actions": [ { - "key": "warp.timer.start", + "key": "timer.start", "implementation": "actions/StartTimerAction.js", "depends": ["now"], "category": "contextual", @@ -68,7 +68,7 @@ "priority": "preferred" }, { - "key": "warp.timer.restart", + "key": "timer.restart", "implementation": "actions/RestartTimerAction.js", "depends": ["now"], "category": "contextual", @@ -79,7 +79,7 @@ ], "types": [ { - "key": "warp.clock", + "key": "clock", "name": "Clock", "glyph": "C", "features": [ "creation" ], @@ -127,7 +127,7 @@ } }, { - "key": "warp.timer", + "key": "timer", "name": "Timer", "glyph": "\u00F5", "features": [ "creation" ], diff --git a/platform/features/clock/src/actions/RestartTimerAction.js b/platform/features/clock/src/actions/RestartTimerAction.js index 42723887c5..9a89b877cb 100644 --- a/platform/features/clock/src/actions/RestartTimerAction.js +++ b/platform/features/clock/src/actions/RestartTimerAction.js @@ -23,11 +23,11 @@ define( // We show this variant for timers which already have // a target time. - return model.type === 'warp.timer' && + return model.type === 'timer' && model.timestamp !== undefined; }; return RestartTimerAction; } -); \ No newline at end of file +); diff --git a/platform/features/clock/src/actions/StartTimerAction.js b/platform/features/clock/src/actions/StartTimerAction.js index 39f604d784..bb0ad4b9d3 100644 --- a/platform/features/clock/src/actions/StartTimerAction.js +++ b/platform/features/clock/src/actions/StartTimerAction.js @@ -24,11 +24,11 @@ define( // We show this variant for timers which do not yet have // a target time. - return model.type === 'warp.timer' && + return model.type === 'timer' && model.timestamp === undefined; }; return StartTimerAction; } -); \ No newline at end of file +); diff --git a/platform/features/clock/src/controllers/TimerController.js b/platform/features/clock/src/controllers/TimerController.js index 9538f914dd..0c4e021d84 100644 --- a/platform/features/clock/src/controllers/TimerController.js +++ b/platform/features/clock/src/controllers/TimerController.js @@ -49,7 +49,7 @@ define( formatKey = model.timerFormat, actionCapability = domainObject.getCapability('action'), actionKey = (timestamp === undefined) ? - 'warp.timer.start' : 'warp.timer.restart'; + 'timer.start' : 'timer.restart'; updateFormat(formatKey); updateTimestamp(timestamp); diff --git a/platform/features/clock/test/actions/RestartTimerActionSpec.js b/platform/features/clock/test/actions/RestartTimerActionSpec.js index 23df5f3142..e96cd7f8cc 100644 --- a/platform/features/clock/test/actions/RestartTimerActionSpec.js +++ b/platform/features/clock/test/actions/RestartTimerActionSpec.js @@ -59,18 +59,18 @@ define( }); it("applies only to timers with a target time", function () { - testModel.type = 'warp.timer'; + testModel.type = 'timer'; testModel.timestamp = 12000; expect(RestartTimerAction.appliesTo(testContext)).toBeTruthy(); - testModel.type = 'warp.timer'; + testModel.type = 'timer'; testModel.timestamp = undefined; expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy(); - testModel.type = 'warp.clock'; + testModel.type = 'clock'; testModel.timestamp = 12000; expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy(); }); }); } -); \ No newline at end of file +); diff --git a/platform/features/clock/test/controllers/TimerControllerSpec.js b/platform/features/clock/test/controllers/TimerControllerSpec.js index 9f80e21d9d..8d081570a2 100644 --- a/platform/features/clock/test/controllers/TimerControllerSpec.js +++ b/platform/features/clock/test/controllers/TimerControllerSpec.js @@ -62,8 +62,8 @@ define( }); mockActionCapability.getActions.andCallFake(function (k) { return [{ - 'warp.timer.start': mockStart, - 'warp.timer.restart': mockRestart + 'timer.start': mockStart, + 'timer.restart': mockRestart }[k]]; }); mockStart.getMetadata.andReturn({ glyph: "S", name: "Start" }); From 8ce8080253f446734a8e1b028920811fff66201f Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 15 Sep 2015 08:34:15 -0700 Subject: [PATCH 003/488] [Timelines] Begin removing namespacing Begin de-namespacing timelines/activities for inclusion in open source. WTD-1239 --- platform/features/timeline/bundle.json | 50 +++++++++---------- .../src/directives/SwimlaneDragConstants.js | 4 +- .../src/directives/WARPSwimlaneDrag.js | 8 +-- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/platform/features/timeline/bundle.json b/platform/features/timeline/bundle.json index 1132ed1840..6cf2f9be8d 100644 --- a/platform/features/timeline/bundle.json +++ b/platform/features/timeline/bundle.json @@ -48,16 +48,16 @@ ], "types": [ { - "key": "warp.timeline", + "key": "timeline", "name": "Timeline", "glyph": "S", "description": "A container for arranging Timelines and Activities in time.", "features": [ "creation" ], - "contains": [ "warp.timeline", "warp.activity" ], + "contains": [ "timeline", "activity" ], "properties": [ { "name": "Start date/time", - "control": "warp.datetime", + "control": "datetime", "required": true, "property": [ "start" ], "options": [ "SET" ] @@ -74,23 +74,23 @@ "model": { "composition": [] } }, { - "key": "warp.activity", + "key": "activity", "name": "Activity", "glyph": "a", "features": [ "creation" ], - "contains": [ "warp.activity" ], + "contains": [ "activity" ], "description": "An action that takes place in time. You can define a start time and duration. Activities can be nested within other Activities, or within Timelines.", "properties": [ { "name": "Start date/time", - "control": "warp.datetime", + "control": "datetime", "required": true, "property": [ "start" ], "options": [ "SET" ] }, { "name": "Duration", - "control": "warp.duration", + "control": "duration", "required": true, "property": [ "duration" ] } @@ -98,7 +98,7 @@ "model": { "composition": [], "relationships": { "modes": [] } } }, { - "key": "warp.mode", + "key": "mode", "name": "Activity Mode", "glyph": "A", "features": [ "creation" ], @@ -124,19 +124,19 @@ ], "views": [ { - "key": "warp.values", + "key": "values", "name": "Values", "glyph": "A", "templateUrl": "templates/values.html", - "type": "warp.mode", + "type": "mode", "uses": [ "cost" ], "editable": false }, { - "key": "warp.timeline", + "key": "timeline", "name": "Timeline", "glyph": "S", - "type": "warp.timeline", + "type": "timeline", "description": "A timeline view of Timelines and Activities.", "templateUrl": "templates/timeline.html", "toolbar": { @@ -152,12 +152,12 @@ { "name": "Timeline", "glyph": "S", - "key": "warp.timeline" + "key": "timeline" }, { "name": "Activity", "glyph": "a", - "key": "warp.activity" + "key": "activity" } ] } @@ -179,7 +179,7 @@ "dialog": { "control": "selector", "name": "Modes", - "type": "warp.mode" + "type": "mode" }, "property": "modes" }, @@ -219,7 +219,7 @@ ], "representations": [ { - "key": "warp.gantt", + "key": "gantt", "templateUrl": "templates/activity-gantt.html", "uses": [ "timespan", "type" ] } @@ -258,11 +258,11 @@ ], "controls": [ { - "key": "warp.datetime", + "key": "datetime", "templateUrl": "templates/controls/datetime.html" }, { - "key": "warp.duration", + "key": "duration", "templateUrl": "templates/controls/datetime.html" } ], @@ -270,12 +270,12 @@ { "key": "TimelineController", "implementation": "controllers/TimelineController.js", - "depends": [ "$scope", "$q", "warp.objectLoader", "TIMELINE_MINIMUM_DURATION" ] + "depends": [ "$scope", "$q", "objectLoader", "TIMELINE_MINIMUM_DURATION" ] }, { "key": "TimelineGraphController", "implementation": "controllers/TimelineGraphController.js", - "depends": [ "$scope", "warp.resources[]" ] + "depends": [ "$scope", "resources[]" ] }, { "key": "WARPDateTimeController", @@ -303,7 +303,7 @@ { "key": "ActivityModeValuesController", "implementation": "controllers/ActivityModeValuesController.js", - "depends": [ "warp.resources[]" ] + "depends": [ "resources[]" ] } ], "capabilities": [ @@ -334,24 +334,24 @@ ], "directives": [ { - "key": "warpSwimlaneDrop", + "key": "mctSwimlaneDrop", "implementation": "directives/WARPSwimlaneDrop.js", "depends": [ "dndService" ] }, { - "key": "warpSwimlaneDrag", + "key": "mctSwimlaneDrag", "implementation": "directives/WARPSwimlaneDrag.js", "depends": [ "dndService" ] } ], "services": [ { - "key": "warp.objectLoader", + "key": "objectLoader", "implementation": "services/ObjectLoader.js", "depends": [ "$q" ] } ], - "warp.resources": [ + "resources": [ { "key": "power", "name": "Power", diff --git a/platform/features/timeline/src/directives/SwimlaneDragConstants.js b/platform/features/timeline/src/directives/SwimlaneDragConstants.js index d88b466dd6..8adcffc4ae 100644 --- a/platform/features/timeline/src/directives/SwimlaneDragConstants.js +++ b/platform/features/timeline/src/directives/SwimlaneDragConstants.js @@ -16,5 +16,5 @@ define({ /** * String identifier for swimlanes being dragged. */ - WARP_SWIMLANE_DRAG_TYPE: 'warp-swimlane' -}); \ No newline at end of file + TIMELINE_SWIMLANE_DRAG_TYPE: 'timeline-swimlane' +}); diff --git a/platform/features/timeline/src/directives/WARPSwimlaneDrag.js b/platform/features/timeline/src/directives/WARPSwimlaneDrag.js index 83a4e41bb6..cd0aed252e 100644 --- a/platform/features/timeline/src/directives/WARPSwimlaneDrag.js +++ b/platform/features/timeline/src/directives/WARPSwimlaneDrag.js @@ -17,19 +17,19 @@ define( function link(scope, element, attrs) { // Look up the swimlane from the provided expression function swimlane() { - return scope.$eval(attrs.warpSwimlaneDrag); + return scope.$eval(attrs.mctSwimlaneDrag); } // When drag starts, publish via dndService element.on('dragstart', function () { dndService.setData( - SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE, + SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE, swimlane() ); }); // When drag ends, clear via dndService element.on('dragend', function () { dndService.removeData( - SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE + SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE ); }); } @@ -44,4 +44,4 @@ define( return WARPSwimlaneDrag; } -); \ No newline at end of file +); From 73e959f95a8257ca6eb4569f4b5b3311543d5269 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Thu, 24 Sep 2015 09:44:24 -0700 Subject: [PATCH 004/488] Incremental commit of developer's guide --- docs/src/guide/index.md | 2288 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 2286 insertions(+), 2 deletions(-) diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index c575439d48..5c30ddb8e9 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -1,3 +1,2287 @@ -# Developer Guide +# Open MCT Web Developer Guide +Victor Woeltjen + +[victor.woeltjen@nasa.gov](mailto:victor.woeltjen@nasa.gov) + +September 23, 2015 +Document Version 1.1 + +Date | Version | Summary of Changes | Author +------------------- | --------- | ----------------------- | --------------- +April 29, 2015 | 0 | Initial Draft | Victor Woeltjen +May 12, 2015 | 0.1 | | Victor Woeltjen +June 4, 2015 | 1.0 | Name Changes | Victor Woeltjen +September 23, 2015 | 1.1 | Conversion to MarkDown | Andrew Henry + +# Contents +1. [Introduction](#Introduction) + 1. [What is Open MCT Web?](#What-is-Open-MCT-Web-) + 2. [Client-Server Relationship](#Client-Server-Relationship) + +# Introduction +The purpose of this guide is to familiarize software developers with the Open +MCT Web platform. + +## What is Open MCT Web? +Open MCT Web is a platform for building user interface and display tools, +developed at the NASA Ames Research Center in collaboration with teams at the +Jet Propulsion Laboratory. It is written in HTML5, CSS3, and JavaScript, using +[AngularJS](h​ttp://www.angularjs.org) as a framework. Its intended use is to +create single­page web applications which integrate data and behavior from a +variety of sources and domains. + +Open MCT Web has been developed to support the remote operation of space +vehicles, so some of its features are specific to that task; however, it is +flexible enough to be adapted to a variety of other application domains where a +display tool oriented toward browsing, composing, and visualizing would be +useful. + +Open MCT Web provides: + +* A common user interface paradigm which can be applied to a variety of domains +and tasks. Open MCT Web is more than a widget toolkit - it provides a standard +tree­on­the­left, view­on­the­right browsing environment which you customize by +adding new browsable object types, visualizations, and back­end adapters. +* A plugin framework and an extensible API for introducing new application +features of a variety of types. +* A set of general-purpose object types and visualizations, as well as some +visualizations and infrastructure specific to telemetry display. + +## Client-Server Relationship +Open MCT Web is client software - it runs entirely in the user’s web browser. As +such, it is largely “server agnostic”; any web server capable of serving files +from paths is capable of providing Open MCT Web. + +While Open MCT Web can be configured to run as a standalone client, this is +rarely very useful. Instead, it is intended to be used as a display and +interaction layer for information obtained from a variety of back­end services. +Doing so requires authoring or utilizing adapter plugins which allow Open MCT +Web to interact with these services. + +Typically, the pattern here is to provide a known interface that Open MCT Web +can utilize, and implement it such that it interacts with whatever back­end +provides the relevant information. Examples of back­ends that can be utilized in +this fashion include databases for the persistence of user­created objects, or +sources of telemetry data. + +See the [Architecture Guide](../architecture/index.md#Overview) for more details +on the client-server relationship. + +## Developing with Open MCT Web +Building applications with Open MCT Web typically means authoring and utilizing +a set of plugins which provide application­specific details about how Open MCT +Web should behave. + +### Technologies + +Open MCT Web sources are written in JavaScript, with a number of configuration +files written in JSON. Displayable components are written in HTML5 and CSS3. +Open MCT Web is built using [AngularJS](h​ttp://www.angularjs.org) ​from Google. A +good understanding of Angular is recommended for developers working with Open +MCT Web. + +### Forking +Open MCT Web does not currently have a single stand­alone artifact that can be +used as a library. Instead, the recommended approach for creating a new +application is to start by forking/branching Open MCT Web, and then adding new +features from there. Put another way, Open MCT Web’s source structure is built +to serve as a template for specific applications. + +Forking in this manner should not require that you edit Open MCT Web’s sources. +The preferred approach is to create a new directory (peer to ​`index.html`)​for +the new application, then add new bundles (as described in the Framework +chapter) within that directory. + +To initially clone the Open MCT Web repository: +`git clone ­b open­master` + +To create a fork to begin working on a new application using Open MCT Web: + + cd + git checkout open­master + git checkout ­b + +As a convention used internally, applications built using Open MCT Web have +master branch names with an identifying prefix. For instance, if building an +application called “Foo”, the last statement above would look like: + + git checkout ­b foo­master + +This convention is not enforced or understood by Open MCT Web in any way; it is +mentioned here as a more general recommendation. + +# Overview + +Open MCT Web is implemented as a framework component which manages a set of +other components. These components, called “bundles”, act as containers to group +sets of related functionality; individual units of functionality are expressed +within these bundles as "extensions." + +Extensions declare dependencies on other extensions (either individually or +categorically), and the framework provides actual extension instances at +run­time to satisfy these declared dependency. This dependency injection +approach allows software components which have been authored separately (e.g. as +plugins) but to collaborate at run­time. + +Open MCT Web’s framework layer is implemented on top of AngularJS’s [dependency +injection mechanism](https://docs.angularjs.org/guide/di)​ and is modelled after +[OSGi](hhttp://www.osgi.org/)​ and its [Declarative Services component model] +(h​ttp://wiki.osgi.org/wiki/Declarative_Services)​. In particular, this is where +the term "bundle" comes from. + +## Framework Overview + +The framework’s role in the application is to manage connections between +bundles. All application­specific behavior is provided by individual bundles, or +as the result of their collaboration. + +ADD LINK TO DIAGRAM HERE + +### Tiers +While all bundles in a running Open MCT Web instance are effectively peers, it +is useful to think of them as a tiered architecture, where each tier adds more +specificity to the application. + +ADD LINK TO DIAGRAM HERE + +* __Framework__ : This tier is responsible for wiring together the set of +configured components (called bundles) together to instantiate the running +application. It is responsible for mediating between AngularJS (in particular, +its dependency injection mechanism) and RequireJS (to load scripts at run­time.) +It additionally interprets bundle definitions (see explanation below, as well as +further detail in the Framework chapter.) At this tier, we are at our most +general: We know only that we are a plugin­based application.
 +* __Platform__: Components in the Platform tier describe both the general user +interface and corresponding developer­facing interfaces of Open MCT Web. This +tier provides the general infrastructure for applications. It is less general +than the framework tier, insofar as this tier introduces a specific user +interface paradigm, but it is still non-specific as to what useful features +will be provided. Although they can be removed or replaced easily, bundles +provided by the Platform tier generally should not be thought of as optional.
 +* __Application__: The application tier consists of components which utilize the +infrastructure provided by the Platform to provide functionality which will (or +could) be useful to specific applications built using Open MCT Web. These +include adapters to specific persistence back­ends (such as ElasticSearch or +CouchDB) as well as bundles which describe more user­facing features (such as +Plot views for visualizing time series data, or Layout objects for +display­building.) Bundles from this tier can be added or removed without +compromising basic application functionality, with the caveat that at least one +persistence adapter needs to be present. +* __Plugins__: Conceptually, this tier is not so different from the application +tier; it consists of bundles describing new features, back­end adapters, that +are specific to the application being built on Open MCT Web. It is described as +a separate tier here because it has one important distinction from the +application tier: It consists of bundles that are not included with the platform +(either authored anew for the specific application, or obtained from elsewhere.) + +Note that bundles in any tier can go off and consult back­end services. In +practice, this responsibility is handled at the Application and/or Plugin tiers; +Open MCT Web is built to be server­agnostic, so any back­end is considered an +application­specific detail. + +## Platform Overview + +The "tiered" architecture described in the preceding text describes a way of +thinking of and categorizing software components of a Open MCT Web application, +as well as the framework layer’s role in mediating between these components. +Once the framework layer has wired these software components together, however, +the application’s logical architecture emerges. + +### Logical Architecture +INSERT DIAGRAM HERE + +* __Templates__​: HTML templates written in Angular’s template syntax; see  +the [Angular documentation on templates](https://docs.angularjs.org/guide/templates)​.  +These describe the page as actually seen by the user. Conceptually, stylesheets  +(controlling the look­and­feel of the rendered templates) belong in this  +grouping as well.  +* __Presentation__: ​Responsible for providing information to be displayed in  +templates, and managing interactions with the information model. Provides the  +logic and behavior of the user interface itself.  +* __Information model__: ​Provides a common (within Open MCT Web) set of interfaces  +for dealing with “things” ­ domain objects ­ within the system. User­facing  +concerns in a Open MCT Web application are expressed as domain objects; examples  +include folders (used to organize other domain objects), layouts (used to build  +displays), or telemetry points (used as handles for streams of remote  +measurements.) These domain objects expose a common set of interfaces to allow  +reusable user interfaces to be built in the presentation and template tiers; the  +specifics of these behaviors are then mapped to interactions with underlying  +services.  +* __Services__: ​A set of interfaces for dealing with back­end services. +* __Back­end__​: External to the Open MCT Web client; the underlying persistence  +stores, telemetry streams, and so forth which the Open MCT Web client is being  +used to interact with. + +### Web Services + +As mentioned in the Introduction, Open MCT Web is a platform single­page  +applications which runs entirely in the browser. Most applications will want to  +additionally interact with server­side resources, to (for example) read  +telemetry data or store user­created objects. This interaction is handled by  +individual bundles using APIs which are supported in browser (such as  +`XMLHttpRequest`​, typically wrapped by Angular’s '`$http​`.) + +INSERT DIAGRAM HERE + +This architectural approach ensures a loose coupling between applications built  +using Open MCT Web and the backends which support them.  +  +### Glossary +  +Certain terms are used throughout Open MCT Web with consistent meanings or  +conventions. Other developer documentation, particularly in­line documentation,  +may presume an understanding of these terms. + +* __bundle__​: A bundle is a removable, reusable grouping of software elements.  +The application is composed of bundles. Plug­ins are bundles. +* __capability__​: A JavaScript object which exposes dynamic behavior or  +non­persistent state associated with a domain object. +* __category__​: A machine­readable identifier for a group that something may  +belong to. +* __composition​__: In the context of a domain object, this refers to the set of +other domain objects that compose or are contained by that object. A domain  +object's composition is the set of domain objects that should appear immediately + beneath it in a tree hierarchy. A domain object's composition is described in  +its model as an array of identifiers; its composition capability provides a  +means to retrieve the actual domain object instances associated with these  +identifiers asynchronously.  +* __description__​: When used as an object property, this refers to the human­ +readable description of a thing; usually a single sentence or short paragraph.  +(Most often used in the context of extensions, domain object models, or other  +similar application­specific objects.)  +* __domain object​__: A meaningful object to the user; a distinct thing in the  +work support by Open MCT Web. Anything that appears in the left­hand tree is a  +domain object.  +* __extension​__: An extension is a unit of functionality exposed to the platform  +in a declarative fashion by a bundle. The term “extension category” is used to  +distinguish types of extensions from specific extension instances.  +* __id__​: A string which uniquely identifies a domain object.  +* __key__​: When used as an object property, this refers to the machine­readable  +identifier for a specific thing in a set of things. (Most often used in the  +context of extensions or other similar application­specific object sets.) This  +term is chosen to avoid attaching ambiguous meanings to “id”.  +* __model__​: The persistent state associated with a domain object. A domain object's  +model is a JavaScript object which can be converted to JSON without losing  +information (that is, it contains no methods.)  +* __name__​: When used as an object property, this refers to the human­readable name  +for a thing. (Most often used in the context of extensions, domain object  +models, or other similar application­specific objects.)  +* __navigation__​: Refers to the current state of the application with respect to the  +user's expressed interest in a specific domain object; e.g. when a user clicks  +on a domain object in the tree, they are ​navigating​ to it, and it is thereafter  +considered the ​navigated object (until the user makes another such choice.) This  +term is used to distinguish navigation from selection, which occurs in an  +editing context.  +* __space__​: A machine­readable name used to identify a persistence store.  +Interactions with persistence with generally involve a space parameter in some  +form, to distinguish multiple persistence stores from one another (for cases  +where there are multiple valid persistence locations available.)  +* __source__​: A machine­readable name used to identify a source of telemetry data.  +Similar to "space", this allows multiple telemetry sources to operate  +side­by­side without conflicting.  + +# Framework +   +Open MCT Web is built on the [AngularJS framework](​http://www.angularjs.org​). A  +good understanding of that framework is recommended.  + +Open MCT Web adds an extra layer on top of AngularJS to (a) generalize its  +dependency injection mechanism slightly, particularly to handle many­to­one  +relationships; and (b) handle script loading. Combined, these features become a  +plugin mechanism.  +   +This framework layer operates on two key concepts: + +* __Bundle:__ ​A bundle is a collection of related functionality that can be added to  +the application as a group. More concretely, a bundle is a directory containing  +a JSON file declaring its contents, as well as JavaScript sources, HTML  +templates, and other resources used to support that functionality. (The term  +bundle is borrowed from [OSGi](http://www.osgi.org/)​ ­ which has also inspired  +many of the concepts used in the framework layer. A familiarity with OSGi,  +particularly Declarative Services, may be useful when working with Open MCT  +Web.) +* __Extension:__ ​An extension is an individual unit of functionality. Extensions are  +collected together in bundles, and may interact with other extensions.  + +The framework layer, loaded and initiated from ​`index.html`​, is the main point of  +entry for an application built on Open MCT Web. It is responsible for wiring  +together the application at run time (much of this responsibility is actually  +delegated to Angular); at a high­level, the framework does this by proceeding  +through four stages: + +1. __Loading definitions:__​ JSON declarations are loaded for all bundles which will  +constitute the application, and wrapped in a useful API for subsequent stages.  +2. __Resolving extensions:__​ Any scripts which provide implementations for  +extensions exposed by bundles are loaded, using Require.  +3. __Registering extensions__​ Resolved extensions are registered with Angular, such  +that they can be used by the application at run­time. This stage includes both  +registration of Angular built­ins (directives, controllers, routes, constants,  +and services) as well as registration of non­Angular extensions.  +4. __Bootstrapping__​ The Angular application is bootstrapped; at that point,  +Angular takes over and populates the body of the page using the extensions that  +have been registered.  + +UP TO HERE + +## Bundles + +The basic configurable unit of Open MCT Web is the bundle. This term has been used a  +bit already; now we’ll get to a more formal definition.  + +A bundle is a directory which contains:  +  + ● A bundle definition; a file named ​bundle.json​.  + ● Subdirectories for sources, resources, and tests.  + ● Optionally, a ​README.md​ Markdown file describing its contents (this is not used by  + Open MCT Web in any way, but it’s a helpful convention to follow.)  +  + The bundle definition is the main point of entry for the bundle. The framework looks at  +this to determine which components need to be loaded and how they interact.  + A plugin in Open MCT Web is a bundle. The platform itself is also decomposed into  +bundles, each of which provides some category of functionality. The difference between a  +“bundle” and a “plugin” is purely a matter of the intended use; a plugin is just a bundle that is  +meant to be easily added or removed. When developing, it is typically more useful to think in  +terms of bundles.  +  +   +Configuring Active Bundles +  + To decide ​which​ bundles should be loaded, the framework loads a file named  +bundles.json​ (peer to the index.html file which serves the application) to determine which  +bundles should be loaded. This file should contain a single JSON array of strings, where each is  +the path to a bundle. These paths should not include ​bundle.json​ (this is implicit) or a trailing  +slash.  + For instance, if bundles.json contained:  +   + [  + "example/builtins",  + "example/extensions"  + ]  +   + ...then the Open MCT Web framework would look for bundle definitions at  +example/builtins/bundle.json​ and ​example/extensions/bundle.json​, relative  +to the path of ​index.html​. No other bundles would be loaded.  +  + + + 14  +Bundle Definition +  + A bundle definition (the ​bundle.json​ file located within a bundle) contains a  +description of the bundle itself, as well as the information exposed by the bundle.  +  + This definition is expressed as a single JSON object with the following properties (all of  +which are optional, falling back to reasonable defaults):  +  + ● key​: A machine­readable name for the bundle. (Currently used only in logging.)  + ● name​: A human­readable name for the bundle. (Also only used in logging.)  + ● sources​: Names a directory in which source scripts (which will implement extensions)  + are located. Defaults to “src”  + ● resources​: Names a directory in which resource files (such as HTML templates,  + images, CS files, and other non­JavaScript files needed by this bundle) are located.  + Defaults to “res”   + ● libraries​: Names a directory in which third­party libraries are located. Defaults to “lib”  + ● configuration​: A bundle’s configuration object, which should be formatted as would  + be passed to require.config (see RequireJS documentation at  + http://requirejs.org/docs/api.html​); note that only paths and shim have been tested.  + ● extensions​: An object containing key­value pairs, where keys are extension  + categories, and values are extension definitions. See the section on Extensions for more  + information.   +  + For example, the bundle definition for ​example/policy​ looks like:  +  +{  +    "name": "Example Policy",  +    "description": "Provides an example of using policies.",  +    "sources": "src",  +    "extensions": {  +        "policies": [  +            {  +                "implementation": "ExamplePolicy.js",  +                "category": "action"  +            }  +        ]  +    }  +}  +  + + + 15  +Bundle Directory Structure +  + In addition to the directories defined in the bundle definition, a bundle will typically  +contain other directories not used at run­time. Additionally, some useful development scripts  +(such as the command line build and the test suite) expect this directory structure to be in use,  +and may ignore options chosen by b​ undle.json​. It is recommended that the directory  +structure described below be used for new bundles.  +  + ● src​: Contains JavaScript sources for this bundle. May contain additional subdirectories  + to organize these sources; typically, these subdirectories are named to correspond to the  + extension categories they contain and/or support, but this is only a convention.  + ● res​: Contains other files needed by this bundle, such as HTML templates. May contain  + additional subdirectories to organize these sources.  + ● lib​: Contains JavaScript sources from third­party libraries. These are separated from  + bundle sources in order to ignore them during code style checking from the command  + line build.  + ● test​: Contains JavaScript sources implementing Jasmine (http://jasmine.github.io/)  + tests, as well as a file named ​suite.json​ describing which files to test. Should have  + the same folder structure as the src directory; see the section on automated testing for  + more information.  +  + For example, the directory structure for bundle ​platform/commonUI/about ​looks  +like:  +   +  +  + + + 16  +Extensions +  + While bundles provide groupings of related behaviors, the individual units of behavior  +are called extensions.  + Extensions belong to categories; an extension category is the machine­readable  +identifier used to identify groups of extensions. In the ​extensions​ property of a bundle  +definition, the keys are extension categories and the values are arrays of extension definitions.  +  +General Extensions +  + Extensions are intended as a general­purpose mechanism for adding new types of  +functionality to Open MCT Web.  + An extension category is registered with Angular under the name of the extension, plus a  +suffix of two square brackets; so, an Angular service (or, generally, any other extension) can  +access the full set of registered extensions, from all bundles, by including this string (e.g.  +types[]​ to get all type definitions) in a dependency declaration.  + As a convention, extension categories are given single­word, plural nouns for names  +within Open MCT Web (e.g. ​types​.) This convention is not enforced by the platform in any  +way. For extension categories introduced by external plugins, it is recommended to prefix the  +extension category with a vendor identifier (or similar) followed by a dot, to avoid collisions.  +  +Extension Definitions +  + The properties used in extension definitions are typically unique to each category of  +extension; a few properties have standard interpretations by the platform.  +  + ● implementation​: Identifies a JavaScript source file (in the sources folder) which  + implements this extension. This JavaScript file is expected to contain an AMD module  + (see ​http://requirejs.org/docs/whyamd.html#amd​) which gives as its result a single  + constructor function.  + ● depends​: An array of dependencies needed by this extension; these will be passed on  + to Angular’s dependency injector, ​https://docs.angularjs.org/guide/di​. By default, this is  + treated as an empty array. Note that ​depends​ does not make sense without  + implementation​ (since these dependencies will be passed to the implementation  + when it is instantiated.)  + ● priority​: A number or string indicating the priority order (see below) of this extension  + instance. Before an extension category is registered with AngularJS, the extensions of  + this category from all bundles will be concatenated into a single array, and then sorted  + by priority.  +  + 17  + Extensions do not need to have an implementation. If no implementation is provided,  +consumers of the extension category will receive the extension definition as a plain JavaScript  +object. Otherwise, they will receive the partialized (see below) constructor for that  +implementation, which will additionally have all properties from the extension definition attached.  + +Partial Construction +  + In general, extensions are intended to be implemented as constructor functions, which  +will be used elsewhere to instantiate new objects of that type. However, the Angular­supported  +method for dependency injection is (effectively) constructor­style injection; so, both declared  +dependencies and run­time arguments are competing for space in a constructor’s arguments.  + To resolve this, the Open MCT Web framework registers extension instances in a  +partially constructed​ form. That is, the constructor exposed by the extension’s implementation is  +effectively decomposed into two calls; the first takes the dependencies, and returns the  +constructor in its second form, which takes the remaining arguments.  + This means that, when writing implementations, the constructor function should be  +written to include all declared dependencies, followed by all run­time arguments. When using  +extensions, only the run­time arguments need to be provided.  +  +Priority +  + Within each extension category, registration occurs in priority order. An extension's  +priority may be specified as a ​priority​ property in its extension definition; this may be a  +number, or a symbolic string. Extensions are registered in reverse order (highest­priority first),  +and symbolic strings are mapped to the numeric values as follows:  +  + ● fallback​: Negative infinity. Used for extensions that are not intended for use (that is,  + they are meant to be overridden) but are present as an option of last resort.  + ● default​: ­100. Used for extensions that are expected to be overridden, but need a  + useful default.  + ● none​: 0. Also used if no priority is specified, or if an unknown or malformed priority is  + specified.  + ● optional​: 100. Used for extensions that are meant to be used, but may be overridden.  + ● preferred​: 1000. Used for extensions that are specifically intended to be used, but still  + may be overridden in principle.  + ● mandatory​: Positive infinity. Used when an extension should definitely not be  + overridden.  +  + These symbolic names are chosen to support usage where many extensions may satisfy  +a given need, but only one may be used; in this case, as a convention it should be the  +lowest­ordered (highest­priority) extensions available. In other cases, a full set (or multi­element  + 18  +subset) of extensions may be desired, with a specific ordering; in these cases, it is preferable to  +specify priority numerically when declaring extensions, and to understand that extensions will be  +sorted according to these conventions when using them.  +   +Angular Built-ins +  + Several entities supported Angular are expressed and managed as extensions in Open  +MCT Web. Specifically, these extension categories are ​directives​, ​controllers​,  +services​, ​constants​, ​runs​, and ​routes​.  +  +Directives +  + New directives (see ​https://docs.angularjs.org/guide/directive​) may be registered as  +extensions of the ​directives​ category. Implementations of directives in this category should  +take only dependencies as arguments, and should return a directive definition object.   + The directive’s name should be provided as a ​key​ property of its extension definition, in  +camel­case format.  +  +Controllers +  + New controllers (see ​https://docs.angularjs.org/guide/controller​) may be registered as  +extensions of the ​controllers​ category. The implementation is registered directly as the  +controller; its only constructor arguments are its declared dependencies.  + The directive’s identifier should be provided as a ​key​ property of its extension definition.  +   +  +Services +  + New services (see ​https://docs.angularjs.org/guide/services​) may be registered as  +extensions of the ​services​ category. The implementation is registered via a service call  +(​https://docs.angularjs.org/api/auto/service/$provide#service​), so it will be instantiated with the  +new​ operator.  +  +  + +Constants +  + Constant values may be registered as extensions of the ​constants​ category; see  +https://docs.angularjs.org/api/ng/type/angular.Module#constant​. These extensions have no  + 19  +implementation; instead, they should contain a property ​key​, which is the name under which the  +constant will be registered, and a property ​value​, which is the constant value that will be  +registered.  +  +  +Runs +  + In some cases, you want to register code to run as soon as the application starts; these  +can be registered as extensions of the ​runs​ category; see  +https://docs.angularjs.org/api/ng/type/angular.Module#run​. Implementations registered in this  +category will be invoked (with their declared dependencies) when the Open MCT Web  +application first starts. (Note that, in this case, the implementation is better thought of as just a  +function, as opposed to a constructor function.)  +  +  +Routes +  + Extensions of category ​routes​ will be registered with Angular’s route provider,  +https://docs.angularjs.org/api/ngRoute/provider/$routeProvider​. Extensions of this category have  +no implementations, and need only two properties in their definition:  +  + ● when​: The value that will be passed as the path argument to ​$routeProvider.when​;  + specifically, the string that will appear in the trailing part of the URL corresponding to this  + route. This property may be omitted, in which case this extension instance will be treated  + as the default route.  + ● templateUrl​: A path to the template to render for this route. Specified as a path  + relative to the bundle’s resource directory (​res​ by default.)  +  +  + + + 20  +Composite Services +  + A special category of extensions recognized by the framework are ​components​; these  +are parts of services intended to be fit together in a common pattern.  +  +   +  + Components all implement the same interface, which is the interface expected for  +services of the type that they create. Components fall into three types:  +  + ● provider​: Provides an actual implementation of the service in question.  + ● aggregator​: Makes many implementations of the service in question appear as one.  + ● decorator​: Modifies the inputs or outputs of another implementation of the service.  +  + When the framework layer encounters components, it assembles them into single  +service instances that can be referred to elsewhere as single dependencies. All providers are  +instantiated, and passed to the first available aggregator; decorators are then layered on in  +priority order to create the final form of the service.  + A component should include the following properties in its extension definition:  +  + ● provides​: The symbolic identifier for the service that will be composed. The  + fully­composed service will be registered with Angular under this name.  + ● type​: One of ​provider​, ​aggregator​, or ​decorator​ (as above)  +  + In addition to any declared dependencies, aggregators and decorators both receive one  +more argument (immediately following declared dependencies) that is provided by the  +framework. For an aggregator, this will be an array of all providers of the same service (that is,  +with matching ​provides​ properties); for a decorator, this will be whichever provider, decorator,  +or aggregator is next in the sequence of decorators.  + Services exposed by the Open MCT Web platform are often declared as composite  +services, as this form is open for a variety of common modifications.  + + 21  +Core API +  + Most of Open MCT Web’s relevant API is provided and/or mediated by the framework;  +that is, much of developing for Open MCT Web is a matter of adding extensions which access  +other parts of the platform by means of dependency injection.  + The core bundle (​platform/core​) introduces a few additional object types meant to  +be passed along by other services.  +  +Domain Objects +  + Domain objects are the most fundamental component of Open MCT Web’s information  +model. A domain object is some distinct thing relevant to a user’s work flow, such as a telemetry  +channel, display, or similar. Open MCT Web is a tool for viewing, browsing, manipulating, and  +otherwise interacting with a graph of domain objects.  + A domain object should be conceived of as the union of the following:  +   + ● Identifier: A machine­readable string that uniquely identifies the domain object within this  + application instance.  + ● Model: The persistent state of the domain object. A domain object’s model is a  + JavaScript object that can be losslessly converted to JSON.  + ● Capabilities: Dynamic behavior associated with the domain object. Capabilities are  + JavaScript objects which provide additional methods for interacting with the domain  + objects which expose those capabilities. Not all domain objects expose all capabilities.  +  + At run­time, a domain object has the following interface:  +  + ● getId()​: Get the identifier for this domain object.  + ● getModel()​: Get the plain state associated with this domain object. This will return a  + JavaScript object that can be losslessly converted to JSON. Note that the model  + returned here can be modified directly but should not be; instead, use the ​mutation  + capability.  + ● getCapability(key)​: Get the specified capability associated with this domain object.  + This will return a JavaScript object whose interface is specific to the type of capability  + being requested. If the requested capability is not exposed by this domain object, this  + will return ​undefined​.  + 22  + ● hasCapability(key)​: Shorthand for checking if a domain object exposes the  + requested capability.  + ● useCapability(key, arguments…)​: Shorthand for  + getCapability(key).invoke(arguments)​, with additional checking between  + calls. If the provided capability has no invoke method, the return value here functions as  + getCapability​, including returning ​undefined​ if the capability is not exposed.  +  +Actions +  + An ​Action​ is behavior that can be performed upon/using a ​DomainObject​. An Action  +has the following interface:  +  + ● perform()​: Do this action. For example, if one had an instance of a ​RemoveAction​,  + invoking its ​perform​ method would cause the domain object which exposed it to be  + removed from its container.  + ● getMetadata()​: Get metadata associated with this action. Returns an object  + containing:  + ○ name​: Human­readable name.  + ○ description​: Human­readable summary of this action.  + ○ glyph​: Single character to be displayed in Open MCT Web’s icon font set.  + ○ context​: The context in which this action is being performed (see below)  +  + Action instances are typically obtained via a domain object’s ​action​ capability.  +  +Action Contexts +  + An action context is a JavaScript object with the following properties:  +  + ● domainObject​: The domain object being acted upon.  + ● selectedObject​: Optional; the selection at the time of action (e.g. the dragged object  + in a drag­and­drop operation.)  + + + + 23  +Telemetry +  + Telemetry series data in Open MCT Web is represented by a common interface, and  +packaged in a consistent manner to facilitate passing telemetry updates around multiple  +visualizations.  +  +Telemetry Requests +  + A telemetry request is a JavaScript object containing the following properties:  +  + ● source​: A machine­readable identifier for the source of this telemetry. This is useful  + when multiple distinct data sources are in use side­by­side.  + ● key​: A machine­readable identifier for a unique series of telemetry within that source.  + ● Note: This API is still under development; additional properties, such as start and end  + time, should be present in future versions of Open MCT Web.  +  + Additional properties may be included in telemetry requests which have specific  +interpretations for specific sources.  +  +Telemetry Responses +  + When returned from the ​telemetryService​ (see Services section), telemetry series  +data will be packaged in a ​source ­> key ­> TelemetrySeries​ fashion. That is,  +telemetry is passed in an object containing key­value pairs. Keys identify telemetry sources;  +values are objects containing additional key­value pairs. In this object, keys identify individual  +telemetry series (and match they ​key​ property from corresponding requests) and values are  +TelemetrySeries​ objects (see below.)  +  + + + 24  +Telemetry Series +  + A telemetry series is a specific sequence of data, typically associated with a specific  +instrument. Telemetry is modeled as an ordered sequence of domain and range values, where  +domain values must be non­decreasing but range values do not. (Typically, domain values are  +interpreted as UTC timestamps in milliseconds relative to the UNIX epoch.) A series must have  +at least one domain and one range, and may have more than one.  + Telemetry series data in Open MCT Web is expressed via the following  +TelemetrySeries​ interface:  +  + ● getPointCount()​: Returns the number of unique points/samples in this series.  + ● getDomainValue(index, [domain]):​ Get the domain value at the specified  + index​. If a second ​domain​ argument is provided, this is taken as a string identifier  + indicating which domain option (of, presumably, multiple) should be returned.  + ● getRangeValue(index, [range]):​ Get the domain value at the specified ​index​.  + If a second ​range​ argument is provided, this is taken as a string identifier indicating  + which range option (of, presumably, multiple) should be returned.  +  +Telemetry Metadata +  + Domain objects which have associated telemetry also expose metadata about that  +telemetry; this is retrievable via the ​getMetadata()​ of the telemetry capability. This will return  +a single JavaScript object containing the following properties:  +  + ● source​: The machine­readable identifier for the source of telemetry data for this object.  + ● key​: The machine­readable identifier for the individual telemetry series.  + ● domains​: An array of supported domains (see ​TelemetrySeries​ above.) Each  + domain should be expressed as an object which includes:  + ○ key​: Machine­readable identifier for this domain, as will be passed into a  + getDomainValue(index, domain)​ call.  + ○ name​: Human­readable name for this domain.  + ● ranges​: An array of supported ranges; same format as ​domains​.  +  + Note that this metadata is also used as the prototype for telemetry requests made using  +this capability.  +  +  +Types +  + A domain object’s type is represented as a ​Type​ object, which has the following  +interface:  + 25  +  + ● getKey()​: Get the machine­readable identifier for this type.  + ● getName()​: Get the human­readable name for this type.  + ● getDescription()​: Get a human­readable summary of this type.  + ● getGlyph()​: Get the single character to be rendered as an icon for this type in Open  + MCT Web’s custom font set.  + ● getInitialModel()​: Get a domain object model that represents the initial state  + (before user specification of properties) for domain objects of this type.  + ● getDefinition()​: Get the extension definition for this type, as a JavaScript object.  + ● instanceOf(type)​: Check if this type is (or inherits from) a specified ​type​. This type  + can be either a string, in which case it is taken to be that type’s ​key​, or it may be a ​Type  + instance.  + ● hasFeature(feature)​: Returns a boolean value indicating whether or not this type  + supports the specified ​feature​, which is a symbolic string.  + ● getProperties()​: Get all properties associated with this type, expressed as an array  + of ​TypeProperty​ instances.  +  +Type Features +  + Features of a domain object type are expressed as symbolic string identifiers. They are  +defined in practice by usage; currently, the Open MCT Web platform only uses the ​creation  +feature to determine which domain object types should appear in the Create menu.  +  +Type Properties +  + Types declare the user­editable properties of their domain object instances in order to  +allow the forms which appear in the Create and Edit Properties dialogs to be generated by the  +platform. A ​TypeProperty​ has the following interface:  +  + ● getValue(model)​: Get the current value for this property, as it appears in the  + provided domain object ​model​.  + ● setValue(model, value)​: Set a new ​value​ for this property in the provided  + domain object ​model​.  + ● getDefinition()​: Get the raw definition for this property as a JavaScript object (as it  + was declared in this type’s extension definition.)  +Extension Categories +  + The information in this section is focused on registering new extensions of specific types;  +it does not contain a catalog of the extension instances of these categories provided by the  +platform. Relevant summaries there are provided in subsequent sections.  + 26  +  +Actions +  + An action is a thing that can be done to or using a domain object, typically as initiated by  +the user.  +  + An action’s implementation:  + ● Should take a single ​context​ argument in its constructor. (See Action Contexts, under  + Core API.)  + ● Should provide a method ​perform​, which causes the behavior associated with the  + action to occur.  + ● May provide a method ​getMetadata​, which provides metadata associated with the  + action. If omitted, one will be provided by the platform which includes metadata from the  + action’s extension definition.  + ● May provide a static method ​appliesTo(context)​ (that is, a function available as a  + property of the implementation’s constructor itself), which will be used by the platform to  + filter out actions from contexts in which they are inherently inapplicable.  +  + An action’s bundle definition (and/or ​getMetadata()​ return value) may include:  + ● category​: A string or dearray of strings identifying which category or categories an  + action falls into; used to determine when an action is displayed. Categories supported by  + the platform include:  + ○ contextual​: Actions in a context menu.  + ○ view­control​: Actions triggered by buttons in the top­right of Browse view.  + ● key​: A machine­readable identifier for this action.  + ● name​: A human­readable name for this action (e.g. to show in a menu)  + ● description​: A human­readable summary of the behavior of this action.  + ● glyph​: A single character which will be rendered in Open MCT Web’s custom font set  + as an icon for this action.  +  +  + + + 27  +Capabilities +  + Capabilities are exposed by domain objects (e.g. via the g​ etCapability​ method) but  +most commonly originate as extensions of this category.  +  + Extension definitions for capabilities should include both an implementation, and a  +property named ​key​ whose value should be a string used as a machine­readable identifier for  +that capability, e.g. when passed as the argument to a domain object’s ​getCapability(key)  +call.   +  + A capability’s implementation should have methods specific to that capability; that is,  +there is no common format for capability implementations, aside from support for ​invoke​ via  +the ​useCapability​ shorthand.  + A capability’s implementation will take a single argument (in addition to any declared  +dependencies), which is the domain object that will expose that capability.  + A capability’s implementation may also expose a static method ​appliesTo(model)  +which should return a boolean value, and will be used by the platform to filter down capabilities  +to those which should be exposed by specific domain objects, based on their domain object  +models.  +  +Controls +  + Controls provide options for the ​mct­control​ directive.  +  + Four standard control types are included in the forms bundle:  +   + ● textfield​: An area to enter plain text.  + ● select​: A drop­down list of options.  + ● checkbox​: A box which may be checked/unchecked.  + ● color​: A color picker.  + ● button​: A button.  + ● datetime​: An input for UTC date/time entry; gives result as a UNIX timestamp, in  + milliseconds since start of 1970, UTC.  +  + New controls may be added as extensions of the controls category. Extensions of this  +category have two properties:  +  + ● key​: The symbolic name for this control (matched against the control field in rows of the  + form structure).  + ● templateUrl​: The URL to the control's Angular template, relative to the resources  + directory of the bundle which exposes the extension.  + 28  +  +Within the template for a control, the following variables will be included in scope:  +  + ● ngModel​: The model where form input will be stored. Notably we also need to look at  + field​ (see below) to determine which field in the model should be modified.  + ● ngRequired​: True if input is required.  + ● ngPattern​: The pattern to match against (for text entry.)  + ● options​: The options for this control, as passed from the ​options​ property of an  + individual row definition.  + ● field​: Name of the field in ​ngModel​ which will hold the value for this control.  +  +Gestures +  + A gesture is a user action which can be taken upon a representation of a domain object.  +Examples of gestures included in the platform are:  +   + ● drag​: For representations that can be used to initiate drag­and­drop composition.  + ● drop​: For representations that can be drop targets for drag­and­drop composition.  + ● menu​: For representations that can be used to pop up a context menu.  +  + Gesture definitions have a property ​key​ which is used as a machine­readable identifier  +for the gesture (e.g. ​drag​, ​drop​, ​menu​ above.)  +  + A gesture’s implementation is instantiated once per representation that uses the gesture.  +This class will receive the jqLite­wrapped ​mct­representation​ element and the domain  +object being represented as arguments, and should do any necessary "wiring" (e.g. listening for  +events) during its constructor call. The gesture’s implementation may also expose an optional  +destroy()​ method which will be called when the gesture should be removed, to avoid  +memory leaks by way of unremoved listeners.  +  +Indicators +  + An indicator is an element that should appear in the status area at the bottom of a  +running Open MCT Web client instance.  +  + + + 29  +Standard Indicators +  + Indicators which wish to appear in the common form of an icon­text pair should provide  +implementations with the following methods:  +  + ● getText()​: Provides the human­readable text that will be displayed for this indicator.  + ● getGlyph()​: Provides a single­character string that will be displayed as an icon in  + Open MCT Web’s custom font set.  + ● getDescription()​: Provides a human­readable summary of the current state of this  + indicator; will be displayed in a tooltip on hover.  + ● getClass()​: Get a CSS class that will be applied to this indicator.  + ● getTextClass()​: Get a CSS class that will be applied to this indicator’s text portion.  + ● getGlyphClass()​: Get a CSS class that will be applied to this indicator’s icon portion.  + ● configure()​: If present, a configuration icon will appear to the right of this indicator,  + and clicking it will invoke this method.  +  + Note that all methods are optional, and are called directly from an Angular template, so  +they should be appropriate to run during digest cycles.  +  +Custom Indicators +  + Indicators which wish to have an arbitrary appearance (instead of following the icon­text  +convention commonly used) may specify a ​template​ property in their extension definition. The  +value of this property will be used as the ​key​ for an ​mct­include​ directive (so should refer to  +an extension of category ​templates​.) This template will be rendered to the status area.  +Indicators of this variety do not need to provide an implementation.  +  +  +Licenses +  + The extension category ​licenses​ can be used to add entries into the “Licensing  +information” page, reachable from Open MCT Web’s About dialog.  + Licenses may have the following properties, all of which are strings:  +  + ● name​: Human­readable name of the licensed component. (e.g. “AngularJS”.)  + ● version​: Human­readable version of the licensed component. (e.g. “1.2.26”.)  + ● description​: Human­readable summary of the component.  + ● author​: Name or names of entities to which authorship should be attributed.  + ● copyright​: Copyright text to display for this component.  + ● link​: URL to full license text.  +  + 30  +  +Policies +  + Policies are used to handle decisions made using Open MCT Web’s ​policyService​;  +examples of these decisions are determining the applicability of certain actions, or checking  +whether or not a domain object of one type can contain a domain object of a different type. See  +the section on the Policies for an overview of Open MCT Web’s policy model.  + A policy’s extension definition should include:  +  + ● category​: The machine­readable identifier for the type of policy decision being  + supported here. For a list of categories supported by the platform, see the section on  + Policies. Plugins may introduce and utilize additional policy categories not in that list.  + ● message​: Optional; a human­readable message describing the policy, intended for  + display in situations where this specific policy has disallowed something.  +  + A policy’s implementation should include a single method, ​allow(candidate,  +context)​. The specific types used for ​candidate​ and ​context​ vary by policy category; in  +general, what is being asked is “is this candidate allowed in this context?” This method should  +return a boolean value.  + Open MCT Web’s policy model requires consensus; a policy decision is allowed when  +and only when all policies choose to allow it. As such, policies should generally be written to  +reject a certain case, and allow (by returning true) anything else.  +  +Representations +  + A representation is an Angular template used to display a domain object. The  +representations​ extension category is used to add options for the ​mct­representation  +directive.  +  + A representation definition should include the following properties:  +   + ● key​: The machine­readable name which identifies the representation.  + ● templateUrl​: The path to the representation's Angular template. This path is relative  + to the bundle's resources directory.  + ● uses​: Optional; an array of capability names. Indicates that this representation intends  + to use those capabilities of a domain object (via a ​useCapability​ call), and expects to  + find the latest results of that ​useCapability​ call in the scope of the presented  + template (under the same name as the capability itself.) Note that, if ​useCapability  + returns a promise, this will be resolved before being placed in the representation’s  + scope.  + 31  + ● gestures​: An array of keys identifying gestures (see the ​gestures​ extension  + category) which should be available upon this representation. Examples of gestures  + include ​drag​ (for representations that should act as draggable sources for drag­drop  + operations) and ​menu​ (for representations which should show a domain­object­specific  + context menu on right­click.)  +  +Representation Scope +  + While ​representations​ do not have implementations, per se, they do refer to  +Angular templates which need to interact with information (e.g. the domain object being  +represented) provided by the platform. This information is passed in through the template’s  +scope, such that simple representations may be created by providing only templates. (More  +complex representations will need controllers which are referenced from templates. See  +https://docs.angularjs.org/guide/controller​ for more information on controllers in Angular.)  +  + A representation’s scope will contain:  +  + ● domainObject​: The represented domain object.  + ● model​: The domain object’s model.  + ● configuration​: An object containing configuration information for this representation  + (an empty object if there is no saved configuration.) The contents of this object are  + managed entirely by the view/representation which receives it.  + ● representation​: An empty object, useful as a “scratch pad” for representation state.  + ● ngModel​: An object passed through the ​ng­model​ attribute of the  + mct­representation​, if any.  + ● parameters​: An object passed through the ​parameters​ attribute of the  + mct­representation​, if any.  + ● Any capabilities requested by the ​uses​ property of the representation definition.  +  +Representers +  + The ​representers​ extension category is used to add additional behavior to the  +mct­representation​ directive. This extension category is intended primarily for use internal  +to the platform.  + Unlike represent​ations​, which describe specific ways to represent domain objects,  +represent​ers ​are used to modify or augment the process of representing domain objects in  +general. For example, support for the ​gestures​ extension category is added by a representer.  + A representer needs only provide an implementation. When an ​mct­representation  +is linked (see ​https://docs.angularjs.org/guide/directive​) or when the domain object being  +represented changes, a new representer of each declared type is instantiated. The constructor  +arguments for a representer are the same as the arguments to the link function in an Angular  + 32  +directive: ​scope​, the Angular scope for this representation; ​element​, the jqLite­wrapped  +mct­representation​ element, and ​attrs​, a set of key­value pairs of that element’s  +attributes. Representers may wish to populate the scope, attach event listeners to the element,  +etc.  + This implementation must provide a single method, ​destroy()​, which will be invoked  +when the representer is no longer needed.  +  +Roots +  + The extension category ​roots​ is used to provide root­level domain object models.  +Root­level domain objects appear at the top­level of the tree hierarchy. For example, the “My  +Items” folder is added as an extension of this category.  + Extensions of this category should have the following properties:  +  + ● id​: The machine­readable identifier for the domain object being exposed.  + ● model​: The model, as a JSON object, for the domain object being exposed.  +  +Stylesheets +  + The ​stylesheets​ extension category is used to add CSS files to style the application.  +Extension definitions for this category should include one property:  +   + ● stylesheetUrl​: Path and filename, including extension, for the stylesheet to include.  + This path is relative to the bundle’s resources folder (by default, ​res​)  +  + To control the order of CSS files, use ​priority​ (see the section on Extension  +Definitions above.)  +   +  + + + 33  +Templates +  + The ​templates​ extension category is used to expose Angular templates under  +symbolic identifiers. These can then be utilized using the ​mct­include​ directive, which  +behaves similarly to ​ng­include​, except that it uses these symbolic identifiers instead of  +paths.  + A template’s extension definition should include the following properties:  +   + ● key​: The machine­readable name which identifies this template, matched against the  + value given to the key attribute of the mct­include directive.  + ● templateUrl​: The path to the relevant Angular template. This path is relative to the  + bundle's resources directory.  +  + Note that, when multiple templates are present with the same ​key​, the one with the  +highest priority will be used from mct­include. This behavior can be used to override templates  +exposed by the platform (to change the logo which appears in the bottom right, for instance.)  +  + Templates do not have implementations.  +  +Types +  + The ​types​ extension category describes types of domain objects which may appear  +within Open MCT Web.  + A type’s extension definition should have the following properties:  +  + ● key​: The machine­readable identifier for this domain object type. Will be stored to and  + matched against the ​type​ property of domain object models.  + ● name​: The human­readable name for this domain object type.  + ● description​: A human­readable summary of this domain object type.  + ● glyph​: A single character to be rendered as an icon in Open MCT Web’s custom font  + set.  + ● model​: A domain object model, used as the initial state for created domain objects of  + this type (before any properties are specified.)  + ● features​: Optional; an array of strings describing features of this domain object type.  + Currently, only ​creation​ is recognized by the platform; this is used to determine that  + this type should appear in the Create menu. More generally, this is used to support the  + hasFeature(...)​ method of the ​type​ capability.  + ● properties​: An array describing individual properties of this domain object (as should  + appear in the Create or the Edit Properties dialog.) Each property is described by an  + object containing the following properties:  + 34  + ○ control​: The key of the control (see mct­control and the controls extension  + category) to use for editing this property.  + ○ property​: A string which will be used as the name of the property in the domain  + object’s model that the value for this property should be stored under. If this value  + should be stored in an object nested within the domain object model, then  + property should be specified as an array of strings identifying these nested  + objects and, finally, the property itself.  + ○ ...other properties as appropriate for a control of this type (each property’s  + definition will also be passed in as the structure for its control.) See  + documentation of ​mct­form​ for more detail on these properties.  +  + Types do not have implementations.  +  +Versions +  + The ​versions​ extension category is used to introduce line items in Open MCT Web’s  +About dialog. These should have the following properties:  +  + ● name​: The name of this line item, as should appear in the left­hand side of the list of  + version information in the About dialog.  + ● value​: The value which should appear to the right of the name in the About dialog.  +  + To control the ordering of line items within the About dialog, use ​priority​. (See  +section on Extension Definitions above.)  +   + This extension category does not have implementations.  +  +Views +  + The ​views​ extension category is used to determine which options appear to the user as  +available views of domain objects of specific types. A view’s extension definition has the same  +properties as a representation (and views can be utilized via ​mct­representation​);  +additionally:  +  + ● name​: The human­readable name for this view type.  + ● description​: A human­readable summary of this view type.  + ● glyph​: A single character to be rendered as an icon in Open MCT Web’s custom font  + set.  + ● type​: Optional; if present, this representation is only applicable for domain object’s of  + this type.  + 35  + ● needs​: Optional array of strings; if present, this representation is only applicable for  + domain objects which have the capabilities identified by these strings.  + ● delegation​: Optional boolean, intended to be used in conjunction with ​needs​;  if  + present, allow required capabilities to be satisfied by means of capability delegation.  + (See the ​delegation​ capability, in the Capabilities section.)  + ● toolbar​: Optional; a definition for the toolbar which may appear in a toolbar when  + using this view in Edit mode. This should be specified as a structure for ​mct­toolbar​,  + with additional properties available for each item in that toolbar:  + ○ property​: A property name. This will refer to a property in the view’s current  + selection; that property on the selected object will be modifiable as the  + ng­model​ of the displayed control in the toolbar. If the value of the property is a  + function, it will be used as a getter­setter (called with no arguments to use as a  + getter, called with a value to use as a setter.)  + ○ method​: A method to invoke (again, on the selected object) from the toolbar  + control. Useful particularly for buttons (which don’t edit a single property,  + necessarily.)  +  +View Scope +  + Views do not have implementations, but do get the same properties in scope that are  +provided for ​representations​.  +  + When a view is in Edit mode, this scope will additionally contain:  +  + ● commit()​: A function which can be invoked to mark any changes to the view’s  + configuration​ as ready to persist.  + ● selection​: An object representing the current selection state.  +  +Selection State +  + A view’s selection state is, conceptually, a set of JavaScript objects. The presence of  +methods/properties on these objects determine which toolbar controls are visible, and what  +state they manage and/or behavior they invoke.  + This set may contain up to two different objects: The ​view proxy​, which is used to make  +changes to the view as a whole, and the ​selected object​, which is used to represent some state  +within the view. (Future versions of Open MCT Web may support multiple selected objects.)  +  +   +    + 36  + The ​selection​ object made available during Edit mode has the following methods:  +  + ● proxy([object])​: Get (or set, if called with an argument) the current view proxy.   + ● select(object)​: Make this object the selected object.  + ● deselect()​: Clear the currently selected object.  + ● get()​: Get the currently selected object. Returns ​undefined​ if there is no currently  + selected object.  + ● selected(object)​: Check if the JavaScript object is currently in the selection set.  + Returns ​true​ if the object is either the currently selected object, or the current view  + proxy.  + ● all()​: Get an array of all objects in the selection state. Will include either or both of the  + view proxy and selected object.  +  + + + 37  +Directives +  + Open MCT Web defines several Angular directives that are intended for use both  +internally within the platform, and by plugins.  +  +Before Unload +  + The ​mct­before­unload​ directive is used to listen for (and prompt for user  +confirmation) of navigation changes in the browser. This includes reloading, following links out  +of Open MCT Web, or changing routes. It is used to hook into both ​onbeforeunload​ event  +handling as well as route changes from within Angular.  + This directive is useable as an attribute. Its value should be an Angular expression.  +When an action that would trigger an unload and/or route change occurs, this Angular  +expression is evaluated. Its result should be a message to display to the user to confirm their  +navigation change; if this expression evaluates to a falsy value, no message will be displayed.  +  +Chart +  + The ​mct­chart​ directive is used to support drawing of simple charts. It is present to  +support the Plot view, and its functionality is limited to the functionality that is relevant for that  +view.  + This directive is used at the element level and takes one attribute, ​draw​, which is an  +Angular expression which will should evaluate to a drawing object. This drawing object should  +contain the following properties:  + ● dimensions​: The size, in logical coordinates, of the chart area. A two­element  + array or numbers.  + ● origin​: The position, in logical coordinates, of the lower­left corner of the chart  + area. A two­element array or numbers.  + ● lines​: An array of lines (e.g. as a plot line) to draw, where each line is  + expressed as an object containing:  + ○ buffer​: A Float32Array containing points in the line, in logical  + coordinates, in sequential x,y pairs.  + ○ color​: The color of the line, as a four­element RGBA array, where each  + element is a number in the range of 0.0­1.0.  + ○ points​: The number of points in the line.  + ● boxes​: An array of rectangles to draw in the chart area. Each is an object  + containing:  + ○ start​: The first corner of the rectangle, as a two­element array of  + numbers, in logical coordinates.  + 38  + ○ end​: The opposite corner of the rectangle, as a two­element array of  + numbers, in logical coordinates.  + ○ color​: The color of the line, as a four­element RGBA array, where each  + element is a number in the range of 0.0­1.0.  +  + While ​mct­chart​ is intended to support plots specifically, it does perform some useful  +management of canvas objects (e.g. choosing between WebGL and Canvas 2D APIs for  +drawing based on browser support) so its usage is recommended when its supported drawing  +primitives are sufficient for other charting tasks.  +  +Container +  + The ​mct­container​ is similar to the ​mct­include​ directive insofar as it allows  +templates to be referenced by symbolic keys instead of by URL. Unlike ​mct­include​, it  +supports transclusion.  + Unlike ​mct­include​, ​mct­container​ accepts a ​key​ as a plain string attribute,  +instead of as an Angular expression.  +   +Control +  + The ​mct­control​ directive is used to display user input elements. Several controls are  +included with the platform to wrap default input types. This directive is primarily intended for  +internal use by the ​mct­form​ and ​mct­toolbar​ directives.  + When using ​mct­control​, the attributes ​ng­model​, ​ng­disabled​, ​ng­required​,  +and ​ng­pattern​ may also be used. These have the usual meaning (as they would for an input  +element) except for ​ng­model​; when used, it will actually be ​ngModel[field]​ (see below)  +that is two­way bound by this control. This allows ​mct­control​ elements to more easily  +delegate to other ​mct­control​ instances, and also facilitates usage for generated forms.  + This directive supports the following additional attributes, all specified as Angular  +expressions:  +  + ● key​: A machine­readable identifier for the specific type of control to display.  + ● options​: A set of options to display in this control.  + ● structure​: In practice, contains the definition object which describes this form row or  + toolbar item. Used to pass additional control­specific parameters.  + ● field​: The field in the ​ngModel​ under which to read/store the property associated with  + this control.  +  +Drag +  + 39  + The ​mct­drag​ directive is used to support drag­based gestures on HTML elements.  +Note that this is not “drag” in the “drag­and­drop” sense, but “drag” in the more general “mouse  +down, mouse move, mouse up” sense.  + This takes the form of three attributes:  +  + ● mct­drag​: An Angular expression to evaluate during drag movement.  + ● mct­drag­down​: An Angular expression to evaluate when the drag starts.  + ● mct­drag­up​: An Angular expression to evaluate when the drag ends.  +  + In each case, a variable ​delta​ will be provided to the expression; this is a two­element  +array or the horizontal and vertical pixel offset of the current mouse position relative to the  +mouse position where dragging began.  +   +Form +  + The ​mct­form​ directive is used to generate forms using a declarative structure, and to  +gather back user input. It is applicable at the element level and supports the following attributes:  +  + ● ng­model​: The object which should contain the full form input. Individual fields in this  + model are bound to individual controls; the names used for these fields are provided in  + the form structure (see below).  + ● structure​: The structure of the form; e.g. sections, rows, their names, and so forth.  + The value of this attribute should be an Angular expression.  + ● name​: The name in the containing scope under which to publish form "meta­state", e.g.  + $valid​, ​$dirty​, etc. This is as the behavior of ​ng­form​. Passed as plain text in the  + attribute.  +  + + + 40  +Form Structure +  + Forms in Open MCT Web have a common structure to permit consistent display. A form  +is broken down into sections, which will be displayed in groups; each section is broken down  +into rows, each of which provides a control for a single property. Input from this form is two­way  +bound to the object passed via ​ng­model​.  + A form’s structure is represented by a JavaScript object in the following form:  +{  +    "name": ... title to display for the form, as a string ...,  +    "sections": [  +        {  +            "name": ... title to display for the section ...,  +            "rows": [  +                {  +                    "name": ... title to display for this row ...,  +                    "control": ... symbolic key for the control ...,  +                    "key": ... field name in ng­model ...  +                    "pattern": ... optional, reg exp to match against ...  +                    "required": ... optional boolean ...  +                    "options": [  +                        "name": ... name to display (e.g. in a select) ...,  +                        "value": ... value to store in the model ...  +                    ]  +                },  +                ... and other rows ...  +            ]  +        },  +        ... and other sections ...  +    ]  +}  +  +Note that ​pattern​ may be specified as a string, to simplify storing for structures as JSON  +when necessary. The string should be given in a form appropriate to pass to a ​RegExp  +constructor.  +  + + + 41  +Form Controls +  + A few standard control types are included in the ​platform/forms​ bundle:  +  + ● textfield​: An area to enter plain text.  + ● select​: A drop­down list of options.  + ● checkbox​: A box which may be checked/unchecked.  + ● color​: A color picker.  + ● button​: A button.  + ● datetime​: An input for UTC date/time entry; gives result as a UNIX timestamp, in  + milliseconds since start of 1970, UTC.  +  +Include +  + The ​mct­include​ directive is similar to ​ng­include​, except that it takes a symbolic  +identifier for a template instead of a URL. Additionally, templates included via ​mct­include  +will have an isolated scope.  + The directive should be used at the element level and supports the following attributes,  +all of which are specified as Angular expressions:  +  + ● key​: Machine­readable identifier for the template (of extension category ​templates​) to  + be displayed.  + ● ng­model​: Optional; will be passed into the template’s scope as ​ngModel​. Intended  + usage is for two­way bound user input.  + ● parameters​: Optional; will be passed into the template’s scope as ​parameters​.  + Intended usage is for template­specific display parameters.  +  + + + 42  +Representation +  + The ​mct­representation​ directive is used to include templates which specifically  +represent domain objects. Usage is similar to ​mct­include​.  + The directive should be used at the element level and supports the following attributes,  +all of which are specified as Angular expressions:  +  + ● key​: Machine­readable identifier for the representation (of extension category  + representations​ or ​views​) to be displayed.  + ● mct­object​: The domain object being represented.  + ● ng­model​: Optional; will be passed into the template’s scope as ​ngModel​. Intended  + usage is for two­way bound user input.  + ● parameters​: Optional; will be passed into the template’s scope as ​parameters​.  + Intended usage is for template­specific display parameters.  +  +Resize +  + The ​mct­resize​ directive is used to monitor the size of an HTML element. It is  +specified as an attribute whose value is an Angular expression that will be evaluated when the  +size of the HTML element changes. This expression will be provided a single variable, ​bounds​,  +which is an object containing two properties, ​width​ and ​height​, describing the size in pixels  +of the element.  + When using this directive, an attribute ​mct­resize­interval​ may optionally be  +provided. Its value is an Angular expression describing the number of milliseconds to wait  +before next checking the size of the HTML element; this expression is evaluated when the  +directive is linked and reevaluated whenever the size is checked.  +  +Scroll +  + The ​mct­scroll­x​ and ​mct­scroll­y​ directives are used to both monitor and  +control the horizontal and vertical scroll bar state of an element, respectively. They are intended  +to be used as attributes whose values are assignable Angular expressions which two­way bind  +to the scroll bar state.  +  + + + 43  +Toolbar +  + The ​mct­toolbar​ directive is used to generate toolbars using a declarative structure,  +and to gather back user input. It is applicable at the element level and supports the following  +attributes:  +  + ● ng­model​: The object which should contain the full toolbar input. Individual fields in this  + model are bound to individual controls; the names used for these fields are provided in  + the form structure (see below).  + ● structure​: The structure of the toolbar; e.g. sections, rows, their names, and so forth.  + The value of this attribute should be an Angular expression.  + ● name​: The name in the containing scope under which to publish form "meta­state", e.g.  + $valid​, ​$dirty​, etc. This is as the behavior of ​ng­form​. Passed as plain text in the  + attribute.  +  + Toolbars support the same ​control​ options as forms.   +  +Toolbar Structure +  + A toolbar’s structure is defined similarly to forms, except instead of ​rows​ there are  +items​.  +  +{  +    "name": ... title to display for the form, as a string ...,  +    "sections": [  +        {  +            "name": ... title to display for the section ...,  +            "items": [  +                {  +                    "name": ... title to display for this row ...,  +                    "control": ... symbolic key for the control ...,  +                    "key": ... field name in ng­model ...  +                    "pattern": ... optional, reg exp to match against ...  +                    "required": ... optional boolean ...  +                    "options": [  +                        "name": ... name to display (e.g. in a select) ...,  +                        "value": ... value to store in the model ...  +                    ],  +                    "disabled": ... true if control should be disabled ...  +                    "size": ... size of the control (for textfields) ...  +                    "click": ... function to invoke (for buttons) ...  +                    "glyph": ... glyph to display (for buttons) ...  +                    "text": ... text within control (for buttons) ...  + 44  +                },  +                ... and other rows ...  +            ]  +        },  +        ... and other sections ...  +    ]  +}  +  +Services +  + The Open MCT Web platform provides a variety of services which can be retrieved and  +utilized via dependency injection. These services fall into two categories:  +  + ● Composite Services are defined by a set of ​components​ extensions; plugins may  + introduce additional components with matching interfaces to extend or augment the  + functionality of the composed service. (See the Framework section on Composite  + Services.)  + ● Other services which are defined as standalone service objects; these can be utilized by  + plugins but are not intended to be modified or augmented.  +  +Composite Services +  + This section describes the composite services exposed by Open MCT Web, specifically  +focusing on their interface and contract.  +   + In many cases, the platform will include a provider for a service which consumes a  +specific extension category; for instance, the ​actionService​ depends on ​actions[]​ and  +will expose available actions based on the rules defined for that extension category.   + In these cases, it will usually be simpler to add a new extension of a given category (e.g.  +of category ​actions​) even when the same behavior could be introduced by a service  +component (e.g. an extension of category ​components​ where ​provides​ is ​actionService​,  +and ​type​ is  ​provider​.)   + Occasionally, the extension category does not provide enough expressive power to  +achieve a desired result. For instance, the Create menu is populated with ​create​ actions,  +where one such action exists for each creatable type. Since the framework does not provide a  +declarative means to introduce a new action per type declaratively, the platform implements this  +explicitly in an ​actionService​ component of type ​provider​. Plugins may use a similar  +approach when the normal extension mechanism is insufficient to achieve a desired result.  +  +Action Service +  + 45  + The ​actionService​ provides ​Action​ instances which are applicable in specific  +contexts. See Core API for additional notes on the interface for actions.  + The ​actionService​ has the following interface:  +   + ● getActions(context)​: Returns an array of ​Action​ objects which are applicable in  + the specified action context.    +  +  +Capability Service +  + The ​capabilityService​ provides constructors for capabilities which will be exposed  +for a given domain object.  + The ​capabilityService​ has the following interface:  +  + ● getCapabilities(model)​: Returns a an object containing key­value pairs,  + representing capabilities which should be exposed by the domain object with this model.  + Keys in this object are the capability keys (as used in a ​getCapability(...)​ call)  + and values are either:  + ○ Functions, in which case they will be used as constructors, which will receive the  + domain object instance to which the capability applies as their sole argument.  + The resulting object will be provided as the result of a domain object’s  + getCapability(...)​call. Note that these instances are cached by each  + object, but may be recreated when an object is mutated.  + ○ Other objects, which will be used directly as the result of a domain object’s  + getCapability(...)​ call.  +  +  +Dialog Service +  + The ​dialogService​ provides a means for requesting user input via a modal dialog. It  +has the following interface:  +  + ● getUserInput(formStructure, formState)​: Prompt the user to fill out a form.  + The first argument describes the form’s structure (as will be passed to ​mct­form​) while  + the second argument contains the initial state of that form. This returns a ​Promise​ for  + the state of the form after the user has filled it in; this promise will be rejected if the user  + cancels input.  + ● getUserChoice(dialogStructure)​: Prompt the user to make a single choice from  + a set of options, which (in the platform implementation) will be expressed as buttons in  + the displayed dialog. Returns a ​Promise​ for the user’s choice, which will be rejected if  + the user cancels input.  + 46  +  +Dialog Structure +  + The object passed as the ​dialogStructure​ to ​getUserChoice​ should have the  +following properties:  +  + ● title​: The title to display at the top of the dialog.  + ● hint​: Short message to display below the title.  + ● template​: Identifying ​key​ (as will be passed to ​mct­include​) for the template which  + will be used to populate the inner area of the dialog.  + ● model​: Model to pass in the ​ng­model​ attribute of ​mct­include​.  + ● parameters​: Parameters to pass in the ​parameters​ attribute of ​mct­include​.  + ● options​: An array of options describing each button at the bottom. Each option may  + have the following properties:  + ○ name​: Human­readable name to display in the button.  + ○ key​: Machine­readable key, to pass as the result of the resolved promise when  + clicked.  + ○ description​: Description to show in tooltip on hover.  +  +  +Domain Object Service +  + The ​objectService​ provides domain object instances. It has the following interface:  +  + ● getObjects(ids)​: For the provided array of domain object identifiers, returns a  + Promise​ for an object containing key­value pairs, where keys are domain object  + identifiers and values are corresponding ​DomainObject​ instances. Note that the result  + may contain a superset or subset of the objects requested.  +   +  +Gesture Service +  + The ​gestureService​ is used to attach gestures (see extension category ​gestures​)  +to representations. It has the following interface:  +  + ● attachGestures(element, domainObject, keys)​: Attach gestures specified  + by the provided gesture ​keys​ (an array of strings) to this jqLite­wrapped HTML  + element​, which represents the specified ​domainObject​. Returns an object with a  + single method ​destroy()​, to be invoked when it is time to detach these gestures.  +  +  + 47  +Model Service +  + The ​modelService​ provides domain object models. It has the following interface:  +  + ● getModels(ids)​: For the provided array of domain object identifiers, returns a  + Promise​ for an object containing key­value pairs, where keys are domain object  + identifiers and values are corresponding domain object models. Note that the result may  + contain a superset or subset of the models requested.  +   +  +Persistence Service +  + The ​persistenceService​ provides the ability to load/store JavaScript objects  +(presumably serializing/deserializing to JSON in the process.) This is used primarily to store  +domain object models. It has the following interface:  +  + ● listSpaces()​: Returns a ​Promise​ for an array of strings identifying the different  + persistence spaces this service supports. Spaces are intended to be used to distinguish  + between different underlying persistence stores, to allow these to live side by side.  + ● listObjects()​: Returns a Promise for an array of strings identifying all documents  + stored in this persistence service.  + ● createObject(space, key, value)​: Create a new document in the specified  + persistence ​space​, identified by the specified ​key​, the contents of which shall match  + the specified ​value​. Returns a promise that will be rejected if creation fails.  + ● readObject(space, key)​: Read an existing document in the specified persistence  + space​, identified by the specified ​key​. Returns a promise for the specified document;  + this promise will resolve to ​undefined​ if the document does not exist.  + ● updateObject(space, key, value)​: Update an existing document in the  + specified persistence ​space​, identified by the specified ​key​, such that its contents  + match the specified ​value​. Returns a promise that will be rejected if the update fails.  + ● deleteObject(space, key)​: Delete an existing document from the specified  + persistence ​space​, identified by the specified ​key​. Returns a promise which will be  + rejected if deletion fails.  +  +  + + + 48  +Policy Service +  + The ​policyService​ may be used to determine whether or not certain behaviors are  +allowed within the application. It has the following interface:  +  + ● allow(category, candidate, context, [callback])​: Check if this decision  + should be allowed. Returns a boolean. Its arguments are interpreted as:  + ○ category​: A string identifying which kind of decision is being made. See the  + section on Policies for categories supported by the platform; plugins may define  + and utilize policies of additional categories, as well.  + ○ candidate​: An object representing the thing which shall or shall not be allowed.  + Usually, this will be an instance of an extension of the category defined above.  + This does need to be the case; additional policies which are not specific to any  + extension may also be defined and consulted using unique category identifiers. In  + this case, the type of the object delivered for the candidate may be unique to the  + policy type.  + ○ context​: An object representing the context in which the decision is occurring.  + Its contents are specific to each policy category.  + ○ callback​: Optional; a function to call if the policy decision is rejected. This  + function will be called with the message string (which may be undefined) of  + whichever individual policy caused the operation to fail.  +  +  +Telemetry Service +  + The ​telemetryService​ is used to acquire telemetry data. See the section on  +Telemetry in Core API for more information on how both the arguments and responses of this  +service are structured.  + When acquiring telemetry for display, it is recommended that the ​telemetryHandler  +service be used instead of this service. The ​telemetryHandler​ has additional support for  +subscribing to and requesting telemetry data associated with domain objects or groups of  +domain objects. See the Other Services section for more information.  + The ​telemetryService​ has the following interface:  +  + ● requestTelemetry(requests)​: Issue a request for telemetry, matching the  + specified telemetry ​requests​. Returns a ​Promise​ for a telemetry response object.   + ● subscribe(callback, requests)​: Subscribe to real­time updates for telemetry,  + matching the specified ​requests​. The specified ​callback​ will be invoked with  + telemetry response objects as they become available. This method returns a function  + which can be invoked to terminate the subscription.  +  + 49  +Type Service +  + The ​typeService​ exposes domain object types. It has the following interface:  +  + ● listTypes()​: Returns all domain object types supported in the application, as an  + array of ​Type​ instances.  + ● getType(key)​: Returns the ​Type​ instance identified by the provided key, or  + undefined​ if no such type exists.  +  +View Service +  + The ​viewService​ exposes definitions for views of domain objects. It has the following  +interface:  +  + ● getViews(domainObject):​ Get an array of extension definitions of category ​views  + which are valid and applicable to the specified ​domainObject​.  +  +Other Services +  +Drag and Drop +  + The ​dndService​ provides information about the content of an active drag­and­drop  +gesture within the application. It is intended to complement the ​DataTransfer​ API of HTML5  +drag­and­drop, by providing access to non­serialized JavaScript objects being dragged, as well  +as by permitting inspection during drag (which is normally prohibited by browsers for security  +reasons.)  + The ​dndService​ has the following methods:  +  + ● setData(key, value)​: Set drag data associated with a given type, specified by the  + key​ argument.  + ● getData(key)​: Get drag data associated with a given type, specified by the ​key  + argument.  + ● removeData(key)​: Clear drag data associated with a given type, specified by the ​key  + argument.  +  + + + 50  +Navigation +  + The ​navigationService​ provides information about the current navigation state of  +the application; that is, which object is the user currently viewing? This service merely tracks this  +state and notifies listeners; it does not take immediate action when navigation changes,  +although its listeners might.  + The ​navigationService​ has the following methods:  +  + ● getNavigation()​: Get the current navigation state. Returns a ​DomainObject​.  + ● setNavigation(domainObject)​: Set the current navigation state. Returns a  + DomainObject​.  + ● addListener(callback)​: Listen for changes in navigation state. The provided  + callback​ should be a ​Function​ which takes a single ​DomainObject​ as an  + argument.  + ● removeListener(callback)​: Stop listening for changes in navigation state. The  + provided ​callback​ should be a ​Function​ which has previously been passed to  + addListener​.  +  +Now +  + The service ​now​ is a function which acts as a simple wrapper for ​Date.now()​. It is  +present mainly so that this functionality may be more easily mocked in tests for scripts which  +use the current time.  +  +Telemetry Formatter +  + The ​telemetryFormatter​ is a utility for formatting domain and range values read  +from a telemetry series.  + The ​telemetryFormatter​ has the following methods:  +  + ● formatDomainValue(value)​: Format the provided domain value (which will be  + assumed to be a timestamp) for display; returns a string.  + ● formatRangeValue(value)​: Format the provided range value (a number) for  + display; returns a string.  +  + + + 51  +Telemetry Handler +  + The ​telemetryHandler​ is a utility for retrieving telemetry data associated with  +domain objects; it is particularly useful for dealing with cases where the ​telemetry​ capability  +is delegated to contained objects (as occurs in Telemetry Panels.)  + The ​telemetryHandler​ has the following methods:  +  + ● handle(domainObject, callback, [lossless])​: Subscribe to and issue  + future requests for telemetry associated with the provided ​domainObject​, invoking the  + provided ​callback​ function when streaming data becomes available. Returns a  + TelemetryHandle​ (see below.)  +  +Telemetry Handle +  + A ​TelemetryHandle​ has the following methods:  +  + ● getTelemetryObjects()​: Get the domain objects (as a ​DomainObject[]​) that  + have a ​telemetry​ capability and are being handled here. Note that these are looked  + up asynchronously, so this method may return an empty array if the initial lookup is not  + yet completed.  + ● promiseTelemetryObjects()​: As ​getTelemetryObjects()​, but returns a  + Promise​ that will be fulfilled when the lookup is complete.  + ● unsubscribe()​: Unsubscribe to streaming telemetry updates associated with this  + handle.  + ● getDomainValue(domainObject)​: Get the most recent domain value received via a  + streaming update for the specified ​domainObject​.  + ● getRangeValue(domainObject)​: Get the most recent range value received via a  + streaming update for the specified ​domainObject​.  + ● getMetadata()​: Get metadata (as reported by the ​getMetadata()​ method of a  + telemetry​ capability) associated with telemetry­providing domain objects. Returns an  + array, which is in the same order as ​getTelemetryObjects()​.  + ● request(request, callback)​: Issue a new ​request​ for historical telemetry data.  + The provided ​callback​ will be invoked when new data becomes available, which may  + occur multiple times (e.g. if there are multiple domain objects.) It will be invoked with the  + DomainObject​ for which a new series is available, and the ​TelemetrySeries​ itself,  + in that order.  + ● getSeries(domainObject)​: Get the latest ​TelemetrySeries​ (as resulted from a  + previous ​request(...)​ call) available for this domain object.  +  + 52  +Models +  + Domain object models in Open MCT Web are JavaScript objects describing the  +persistent state of the domain objects they describe. Their contents include a mix of commonly  +understood metadata attributes; attributes which are recognized by and/or determine the  +applicability of specific extensions; and properties specific to given types.  +  +General Metadata +  + Some properties of domain object models have a ubiquitous meaning through Open  +MCT Web and can be utilized directly:  +  + ● name​: The human­readable name of the domain object.  +  +Extension-specific Properties +  + Other properties of domain object models have specific meaning imposed by other  +extensions within the Open MCT Web platform.  +  +Capability-specific Properties +  + Some properties either trigger the presence/absence of certain capabilities, or are  +managed by specific capabilities:  +  + ● composition​: An array of domain object identifiers that represents the contents of this  + domain object (e.g. as will appear in the tree hierarchy.) Understood by the  + composition​ capability; the presence or absence of this property determines the  + presence or absence of that capability.  + ● modified​: The timestamp (in milliseconds since the UNIX epoch) of the last  + modification made to this domain object. Managed by the ​mutation​ capability.  + ● persisted​: The timestamp (in milliseconds since the UNIX epoch) of the last time  + when changes to this domain object were persisted. Managed by the ​persistence  + capability.  + ● relationships​: An object containing key­value pairs, where keys are symbolic  + identifiers for relationship types, and values are arrays of domain object identifiers. Used  + by the ​relationship​ capability; the presence or absence of this property determines  + the presence or absence of that capability.  + ● telemetry​: An object which serves as a template for telemetry requests associated  + with this domain object (e.g. specifying ​source​ and ​key​; see Telemetry Requests  + 53  + under Core API.) Used by the ​telemetry​ capability; the presence or absence of this  + property determines the presence or absence of that capability.  + ● type​: A string identifying the type of this domain object. Used by the ​type​ capability.  +  +View Configurations +  + Persistent configurations for specific views of domain objects are stored in the domain  +object model under the property ​configurations​. This is an object containing key­value  +pairs, where keys identify the view, and values are objects containing view­specific (and  +view­managed) configuration properties.  +  +Modifying Models +  + When interacting with a domain object’s model, it is possible to make modifications to it  +directly. ​Don’t! ​These changes may not be properly detected by the platform, meaning that  +other representations of the domain object may not be updated, changes may not be saved at  +the expected times, and generally, that unexpected behavior may occur.  + Instead, use the ​mutation​ capability.  +  + + + 54  +Capabilities +  + Dynamic behavior associated with a domain object is expressed as capabilities. A  +capability is a JavaScript object with an interface that is specific to the type of capability in use.  + Often, there is a relationship between capabilities and services. For instance, there is an  +action​ capability and an ​actionService​, and there is a ​telemetry​ capability as well as a  +telemetryService​. Typically, the pattern here is that the capability will utilize the service ​for  +the specific domain object​.   + When interacting with domain objects, it is generally preferable to use a capability  +instead of a service when the option is available. Capability interfaces are typically easier to use  +and/or more powerful in these situations. Additionally, this usage provides a more robust  +substitutability mechanism; for instance, one could configure a plugin such that it provided a  +totally new implementation of a given capability which might not invoke the underlying service,  +while user code which interacts with capabilities remains indifferent to this detail.  +  +Action +   + The ​action​ capability is present for all domain objects. It allows applicable ​Action  +instances to be retrieved and performed for specific domain objects.  + For example:  + domainObject.getCapability("action").perform("navigate");  + ...will initiate a navigate action upon the domain object, if an action with key "navigate" is  +defined.  +   + This capability has the following interface:  +   + ● getActions(context)​: Get the actions that are applicable in the specified action  + context​; the capability will fill in the ​domainObject​ field of this context if necessary. If  + context​ is specified as a string, they will instead be used as the ​key​ of the action  + context. Returns an array of ​Action​ instances.  + ● perform(context)​: Perform an action. This will find and perform the first matching  + action available for the specified action ​context​, filling in the ​domainObject​ field as  + necessary. If ​context​ is specified as a string, they will instead be used as the ​key​ of  + the action context. Returns a ​Promise​ for the result of the action that was performed, or  + undefined if no matching action was found.  +  + 55  +Composition +  + The ​composition​ capability provides access to domain objects that are contained by  +this domain object. While the ​composition​ property of a domain object’s model describes  +these contents (by their identifiers), the ​composition​ capability provides a means to load the  +corresponding ​DomainObject​ instances in the same order. The absence of this property in the  +model will result in the absence of this capability in the domain object.  + This capability has the following interface:  +  + ● invoke()​: Returns a ​Promise​ for an array of ​DomainObject​ instances.  + +Delegation +   + The ​delegation​ capability is used to communicate the intent of a domain object to  +delegate responsibilities, which would normally handled by other capabilities, to the domain  +objects in its composition.  + This capability has the following interface:  +  + ● getDelegates(key)​: Returns a ​Promise​ for an array of ​DomainObject​ instances,  + to which this domain object wishes to delegate the capability with the specified ​key​.  + ● invoke(key)​: Alias of ​getDelegates(key)​.  + ● doesDelegate(key)​: Returns ​true​ if the domain object does delegate the capability  + with the specified ​key​.   +  + The platform implementation of the ​delegation​ capability inspects the domain object’s  +type definition for a property ​delegates​, whose value is an array of strings describing which  +capabilities domain objects of that type wish to delegate. If this property is not present, the  +delegation​ capability will not be present in domain objects of that type.   +  +  + + + 56  +Editor +  + The ​editor​ capability is meant primarily for internal use by Edit mode, and helps to  +manage the behavior associated with exiting Edit mode via Save or Cancel. Its interface is not  +intended for general use. However, ​domainObject.hasCapability(‘editor’)​ is a  +useful way of determining whether or not we are looking at an object in Edit mode.  +   +  +Mutation +  + The ​mutation​ capability provides a means by which the contents of a domain object’s  +model can be modified. This capability is provided by the platform for all domain objects, and  +has the following interface:  +  + ● mutate(mutator, [timestamp])​: Modify the domain object’s model using the  + specified ​mutator​ function. After changes are made, the ​modified​ property of the  + model will be updated with the specified ​timestamp​, if one was provided, or with the  + current system time.  + ● invoke(...)​: Alias of ​mutate​.  +  + Changes to domain object models should only be made via the ​mutation​ capability;  +other platform behavior is likely to break (either by exhibiting undesired behavior, or failing to  +exhibit desired behavior) if models are modified by other means.  +  +Mutator Function +  + The ​mutator​ argument above is a function which will receive a cloned copy of the  +domain object’s model as a single argument. It may return:  +  + ● A ​Promise​, in which case the resolved value of the promise will be used to determine  + which of the following forms is used.  + ● Boolean ​false​, in which case the mutation is cancelled.  + ● A JavaScript object, in which case this object will be used as the new model for this  + domain object.  + 57  + ● No value (or, equivalently, ​undefined​), in which case the cloned copy (including any  + changes made in place by the mutator function) will be used as the new domain object  + model.  +  +Persistence +  + The ​persistence​ capability provides a mean for interacting with the underlying  +persistence service which stores this domain object’s model. It has the following interface:  +  + ● persist()​: Store the local version of this domain object, including any changes, to the  + persistence store. Returns a ​Promise​ for a boolean value, which will be true when the  + object was successfully persisted.  + ● refresh()​: Replace this domain object’s model with the most recent version from  + persistence. Returns a ​Promise​ which will resolve when the change has completed.  + ● getSpace()​: Return the string which identifies the persistence space which stores this  + domain object.  +  +Relationship +  + The ​relationship​ capability provides a means for accessing other domain objects  +with which this domain object has some typed relationship. It has the following interface:  +  + ● listRelationships()​: List all types of relationships exposed by this object. Returns  + an array of strings identifying the types of relationships.  + ● getRelatedObjects(relationship)​: Get all domain objects to which this domain  + object has the specified type of ​relationship​, which is a string identifier (as above.)  + Returns a ​Promise​ for an array of ​DomainObject​ instances.  +  + The platform implementation of the ​relationship​ capability is present for domain  +objects which has a ​relationships​ property in their model, whose value is an object  +containing key­value pairs, where keys are strings identifying relationship types, and values are  +arrays of domain object identifiers.  +  + 58  +Telemetry +  + The ​telemetry​ capability provides a means for accessing telemetry data associated  +with a domain object. It has the following interface:  +  + ● requestData([request])​: Request telemetry data for this specific domain object,  + using telemetry request parameters from the specified ​request​ if provided. This  + capability will fill in telemetry request properties as­needed for this domain object.  + Returns a ​Promise​ for a ​TelemetrySeries​.  + ● subscribe(callback, [request])​:  Subscribe to telemetry data updates for this  + specific domain object, using telemetry request parameters from the specified ​request  + if provided. This capability will fill in telemetry request properties as­needed for this  + domain object. The specified ​callback​ will be invoked with ​TelemetrySeries  + instances as they arrive. Returns a function which can be invoked to terminate the  + subscription, or ​undefined​ if no subscription could be obtained.  + ● getMetadata()​: Get metadata associated with this domain object’s telemetry.  +   + The platform implementation of the ​telemetry​ capability is present for domain objects  +which has a ​telemetry​ property in their model and/or type definition; this object will serve as a  +template for telemetry requests made using this object, and will also be returned by  +getMetadata() ​above.  +  +Type +   + The ​type​ capability exposes information about the domain object’s type. It has the  +same interface as ​Type​; see Core API.  +   +View +  + The ​view​ capability exposes views which are applicable to a given domain object. It has  +the following interface:  +   + ● invoke()​: Returns an array of extension definitions for views which are applicable for  + this domain object.  + 59  +Actions +  + Actions are reusable processes/behaviors performed by users within the system,  +typically upon domain objects.  +Action Categories +  + The platform understands the following action categories (specifiable as the ​category  +parameter of an action’s extension definition.)  +  + ● contextual​: Appears in context menus.  + ● view­control​: Appears in top­right area of view (as buttons) in Browse mode  +  +Platform Actions +  + The platform defines certain actions which can be utilized by way of a domain object’s  +action​ capability. Unless otherwise specified, these act upon (and modify) the object  +described by the ​domainObject​ property of the action’s context.  +  + ● cancel​: Cancel the current editing action (invoked from Edit mode.)  + ● compose​: Place an object in another object’s composition. The object to be added  + should be provided as the ​selectedObject​ of the action context.  + ● edit​: Start editing an object (enter Edit mode.)  + ● fullscreen​: Enter full screen mode.  + ● navigate​: Make this object the focus of navigation (e.g. highlight it within the tree,  + display a view of it to the right.)  + ● properties​: Show the “Edit Properties” dialog.  + ● remove​: Remove this domain object from its parent’s composition. (The parent, in this  + case, is whichever other domain object exposed this object by way of its ​composition  + capability.)  + ● save​: Save changes (invoked from Edit mode.)  + ● window​: Open this object in a new window.  +  + + + 60  +Policies +  + Policies are consulted to determine when certain behavior in Open MCT Web is allowed.  +Policy questions are assigned to certain categories, which broadly describe the type of decision  +being made; within each category, policies have a candidate (the thing which may or may not be  +allowed) and, optionally, a context (describing, generally, the context in which the decision is  +occurring.)   + The types of objects passed for “candidate” and “context” vary by category; these types  +are documented below.  +Policy Categories +  + The platform understands the following policy categories (specifiable as the ​category  +parameter of an policy’s extension definition.)  +  + ● action​: Determines whether or not a given action is allowable. The candidate  + argument here is an ​Action​; the context is its action context object.  + ● composition​: Determines whether or not domain objects of a given type are allowed  + to contain domain objects of another type. The candidate argument here is the  + container’s ​Type​; the context argument is the ​Type​ of the object to be contained.  + ● view​: Determines whether or not a view is applicable for a domain object. The  + candidate argument is the view’s extension definition; the context argument is the  + DomainObject​ to be viewed.  +  +  +  + + + 61  +Build, Test, Deploy +  + Open MCT Web is designed to support a broad variety of build and deployment options.  +The sources can be deployed in the same directory structure used during development. A few  +utilities are included to support development processes.  +  +Command-line Build +  + Open MCT Web includes a script for building via command line using Maven 3.0.4  +(​https://maven.apache.org/​).  +   + Invoking ​mvn clean install​ will:  +  + ● Check code style using JSLint. The build will fail if JSLint raises any warnings.  + ● Run the test suite (see below.) The build will fail if any tests fail.  + ● Populate version info (e.g. commit hash, build time.)  + ● Produce a web archive (​.war​) artifact in the ​target​ directory.  +  + The produced artifact contains a subset of the repository’s own folder hierarchy, omitting  +tests and example bundles.   + Note that an internet connection is required to run this build, in order to download build  +dependencies.  +  +Test Suite +  + Open MCT Web uses Jasmine (​http://jasmine.github.io/​) for automated testing. The file  +test.html​, included at the top level of the source repository, can be run from the browser to  +perform tests for all active bundles, as defined in ​bundle.json​.  + To define tests for a bundle:  +   + ● Include a directory named ​test​ within that bundle.  + ● In the ​test​ directory, include a file named ​suite.json​. This will identify which scripts  + will be tested.  + ● The file ​suite.json​ must contain a JSON array of strings, where each string is the  + name of a script to be tested. These names should include any directory paths to the  + script after (but not including) the ​src​ folder, and should not include the file’s ​.js  + extension. (Note that while Open MCT Web’s framework allows a different name to be  + chosen for the ​src​ directory, the test runner does not: This directory must be named  + src​ for the test runner to find it.)  + 62  + ● For each script to be tested, a corresponding test script should be located in the bundle’s  + test​ directory. This should include the suffix ​Spec​ at the end of the filename (but  + before the ​.js​ extension.) This test script should be an AMD module which uses the  + Jasmine API to declare its test behavior. It should declare an AMD dependency on the  + script to be tested, using a relative path.  +   + For example, if writing tests for a bundle at ​example/foo​ with two scripts:  + ● example/foo/src/controllers/FooController.js  + ● example/foo/src/directives/FooDirective.js  +  + First, these scripts should be identified in ​example/foo/test/suite.json​, e.g. with  +contents:  + [ "controllers/FooController", "directives/FooDirective" ]  +  + Then, scripts which describe these tests should be written. For example, test  +example/foo/test/controllers/FooControllerSpec.js​ could look like:  +  +/*global define,Promise,describe,it,expect,beforeEach*/  +  +define(  +    ["../../src/controllers/FooController"],  +    function (FooController) {  +        "use strict";  +  +        describe("The foo controller", function () {  +            it("does something", function () {  +                var controller = new FooController();  +                expect(controller.foo()).toEqual("foo");  +            });  +        });  +    }  +);  +  +Code Coverage +  + In addition to running tests, the test runner will also capture code coverage information  +using Blanket.JS (​http://blanketjs.org/​) and display this at the bottom of the screen. Currently,  +only statement coverage is displayed.  + + 63  +Deployment +  + Open MCT Web is built to be flexible in terms of the deployment strategies it supports. In  +order to run in the browser, Open MCT Web needs:  +  + 1. HTTP access to sources/resources for the framework, platform, and all active bundles.  + 2. Access to any external services utilized by active bundles. (This means that external  + services need to support HTTP or some other web­accessible interface, like  + WebSockets.)  +  + Any HTTP server capable of serving flat files is sufficient for the first point. The  +command­line build also packages Open MCT Web into a ​.war​ file for easier deployment on  +containers such as Apache Tomcat.  + The second point may be less flexible, as it depends upon the specific services to be  +utilized by Open MCT Web. Because of this, it is often the set of external services (and the  +manner in which they are exposed) that determine how to deploy Open MCT Web.  +  + One important constraint to consider in this context is the browser’s same origin policy. If  +external services are not on the same apparent host and port as the client (from the perspective  +of the browser) then access may be disallowed. There are two workarounds if this occurs:  + ● Make the external service appear to be on the same host/port, either by actually  + deploying it there, or by proxying requests to it.  + ● Enable CORS (cross­origin resource sharing) on the external service. This is only  + possible if the external service can be configured to support CORS. Care should be  + exercised if choosing this option to ensure that the chosen configuration does not create  + a security vulnerability.  +  + Examples of deployment strategies (and the conditions under which they make the most  +sense) include:  +  + ● If the external services that Open MCT Web will utilize are all running on Apache Tomcat  + (​https://tomcat.apache.org/​), then it makes sense to run Open MCT Web from the same  + Tomcat instance as a separate web application. The ​.war​ artifact produced by the  + command line build facilitates this deployment option. (See  + https://tomcat.apache.org/tomcat­8.0­doc/deployer­howto.html ​ for general information on  + deploying in Tomcat.)  + ● If a variety of external services will be running from a variety of hosts/ports, then it may  + make sense to use a web server that supports proxying, such as the Apache HTTP  + Server (​http://httpd.apache.org/​). In this configuration, the HTTP server would be  + configured to proxy (or reverse proxy) requests at specific paths to the various external  + services, while providing Open MCT Web as flat files from a different path.  + 64  + ● If a single server component is being developed to handle all server­side needs of an  + Open MCT Web instance, it can make sense to serve Open MCT Web (as flat files) from  + the same component using an embedded HTTP server such as Nancy  + (​http://nancyfx.org/​).  + ● If no external services are needed (or if the “external services” will just be generating flat  + files to read) it makes sense to utilize a lightweight flat file HTTP server such as Lighttpd  + (​http://www.lighttpd.net/​). In this configuration, Open MCT Web sources/resources would  + be placed at one path, while the files generated by the external service are placed at  + another path.  + ● If all external services support CORS, it may make sense to have an HTTP server that is  + solely responsible for making Open MCT Web sources/resources available, and to have  + Open MCT Web contact these external services directly. Again, lightweight HTTP  + servers such as Lighttpd (​http://www.lighttpd.net/​) are useful in this circumstance. The  + downside of this option is that additional configuration effort is required, both to enable  + CORS on the external services, and to ensure that Open MCT Web can correctly locate  + these services.  +  + Another important consideration is authentication. By design, Open MCT Web does not  +handle user authentication. Instead, this should typically be treated as a deployment­time  +concern, where authentication is handled by the HTTP server which provides Open MCT Web,  +or an external access management system.  +  +Configuration +  + In most of the deployment options above, some level of configuration is likely to be  +needed or desirable to make sure that bundles can reach the external services they need to  +reach. Most commonly this means providing the path or URL to an external service.  + Configurable parameters within Open MCT Web are specified via constants (literally, as  +extensions of the ​constants​ category) and accessed via dependency injection by the scripts  +which need them. Reasonable defaults for these constants are provided in the bundle where  +they are used. Plugins are encouraged to follow the same pattern.  + Constants may be specified in any bundle; if multiple constants are specified with the  +same ​key​, the highest­priority one will be used. This allows default values to be overridden by  +specifying constants with higher priority.  +   + This permits at least three configuration approaches:  +  + ● Modify the constants defined in their original bundles when deploying. This is generally  + undesirable due to the amount of manual work required and potential for error, but is  + viable if there are a small number of constants to change.  + ● Add a separate configuration bundle which overrides the values of these constants. This  + is particularly appropriate when multiple configurations (e.g. development, test,  + 65  + production) need to be managed easily; these can be swapped quickly by changing the  + set of active bundles in ​bundles.json​.  + ● Deploy Open MCT Web and its external services in such a fashion that the default paths  + to reach external services are all correct.  +  +Configuration Constants +  + The following configuration constants are recognized by Open MCT Web bundles:  +  + ● CouchDB adapter, ​platform/persistence/coucb  + ○ COUCHDB_PATH​: URL or path to the CouchDB database to be used for domain  + object persistence. Should not include a trailing slash.  + ● ElasticSearch adapter, ​platform/persistence/elastic  + ○ ELASTIC_ROOT​: URL or path to the ElasticSearch instance to be used for  + domain object persistence. Should not include a trailing slash.  + ○ ELASTIC_PATH​: Path relative to the ElasticSearch instance where domain  +  object models should be persisted. Should take the form ​/​.  + 66  + + -This is a placeholder for the developer guide. From 2f4cf44229c4d345c3edb77c658de6512b39abf9 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Thu, 24 Sep 2015 13:17:46 -0700 Subject: [PATCH 005/488] Added additional pages to developer's guide MD version --- docs/src/guide/index.md | 1468 ++++++++++++++++++++------------------- 1 file changed, 742 insertions(+), 726 deletions(-) diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 5c30ddb8e9..25b18575b8 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -260,24 +260,24 @@ distinguish types of extensions from specific extension instances.  identifier for a specific thing in a set of things. (Most often used in the  context of extensions or other similar application­specific object sets.) This  term is chosen to avoid attaching ambiguous meanings to “id”.  -* __model__​: The persistent state associated with a domain object. A domain object's  -model is a JavaScript object which can be converted to JSON without losing  -information (that is, it contains no methods.)  -* __name__​: When used as an object property, this refers to the human­readable name  -for a thing. (Most often used in the context of extensions, domain object  +* __model__​: The persistent state associated with a domain object. A domain  +object's model is a JavaScript object which can be converted to JSON without  +losing information (that is, it contains no methods.)  +* __name__​: When used as an object property, this refers to the human­readable  +name for a thing. (Most often used in the context of extensions, domain object  models, or other similar application­specific objects.)  -* __navigation__​: Refers to the current state of the application with respect to the  -user's expressed interest in a specific domain object; e.g. when a user clicks  -on a domain object in the tree, they are ​navigating​ to it, and it is thereafter  -considered the ​navigated object (until the user makes another such choice.) This  -term is used to distinguish navigation from selection, which occurs in an  -editing context.  +* __navigation__​: Refers to the current state of the application with respect to  +the user's expressed interest in a specific domain object; e.g. when a user  +clicks on a domain object in the tree, they are ​navigating​ to it, and it is  +thereafter considered the ​navigated object (until the user makes another such  +choice.) This term is used to distinguish navigation from selection, which  +occurs in an editing context.  * __space__​: A machine­readable name used to identify a persistence store.  Interactions with persistence with generally involve a space parameter in some  form, to distinguish multiple persistence stores from one another (for cases  where there are multiple valid persistence locations available.)  -* __source__​: A machine­readable name used to identify a source of telemetry data.  -Similar to "space", this allows multiple telemetry sources to operate  +* __source__​: A machine­readable name used to identify a source of telemetry  +data. Similar to "space", this allows multiple telemetry sources to operate  side­by­side without conflicting.  # Framework @@ -292,555 +292,571 @@ plugin mechanism.    This framework layer operates on two key concepts: -* __Bundle:__ ​A bundle is a collection of related functionality that can be added to  -the application as a group. More concretely, a bundle is a directory containing  -a JSON file declaring its contents, as well as JavaScript sources, HTML  -templates, and other resources used to support that functionality. (The term  -bundle is borrowed from [OSGi](http://www.osgi.org/)​ ­ which has also inspired  -many of the concepts used in the framework layer. A familiarity with OSGi,  -particularly Declarative Services, may be useful when working with Open MCT  -Web.) -* __Extension:__ ​An extension is an individual unit of functionality. Extensions are  -collected together in bundles, and may interact with other extensions.  +* __Bundle:__ ​A bundle is a collection of related functionality that can be  +added to the application as a group. More concretely, a bundle is a directory  +containing a JSON file declaring its contents, as well as JavaScript sources,  +HTML templates, and other resources used to support that functionality. (The  +term bundle is borrowed from [OSGi](http://www.osgi.org/)​ ­ which has also  +inspired many of the concepts used in the framework layer. A familiarity with  +OSGi, particularly Declarative Services, may be useful when working with Open  +MCT Web.) +* __Extension:__ ​An extension is an individual unit of functionality. Extensions  +are collected together in bundles, and may interact with other extensions.  -The framework layer, loaded and initiated from ​`index.html`​, is the main point of  -entry for an application built on Open MCT Web. It is responsible for wiring  +The framework layer, loaded and initiated from ​`index.html`​, is the main point  +of entry for an application built on Open MCT Web. It is responsible for wiring  together the application at run time (much of this responsibility is actually  delegated to Angular); at a high­level, the framework does this by proceeding  through four stages: -1. __Loading definitions:__​ JSON declarations are loaded for all bundles which will  -constitute the application, and wrapped in a useful API for subsequent stages.  +1. __Loading definitions:__​ JSON declarations are loaded for all bundles which  +will constitute the application, and wrapped in a useful API for subsequent  +stages.  2. __Resolving extensions:__​ Any scripts which provide implementations for  extensions exposed by bundles are loaded, using Require.  -3. __Registering extensions__​ Resolved extensions are registered with Angular, such  -that they can be used by the application at run­time. This stage includes both  -registration of Angular built­ins (directives, controllers, routes, constants,  -and services) as well as registration of non­Angular extensions.  +3. __Registering extensions__​ Resolved extensions are registered with Angular,  +such that they can be used by the application at run­time. This stage includes  +both registration of Angular built­ins (directives, controllers, routes,  +constants, and services) as well as registration of non­Angular extensions.  4. __Bootstrapping__​ The Angular application is bootstrapped; at that point,  Angular takes over and populates the body of the page using the extensions that  have been registered.  -UP TO HERE - ## Bundles -The basic configurable unit of Open MCT Web is the bundle. This term has been used a  -bit already; now we’ll get to a more formal definition.  +The basic configurable unit of Open MCT Web is the bundle. This term has been  +used a bit already; now we’ll get to a more formal definition.  -A bundle is a directory which contains:  -  - ● A bundle definition; a file named ​bundle.json​.  - ● Subdirectories for sources, resources, and tests.  - ● Optionally, a ​README.md​ Markdown file describing its contents (this is not used by  - Open MCT Web in any way, but it’s a helpful convention to follow.)  -  - The bundle definition is the main point of entry for the bundle. The framework looks at  -this to determine which components need to be loaded and how they interact.  - A plugin in Open MCT Web is a bundle. The platform itself is also decomposed into  -bundles, each of which provides some category of functionality. The difference between a  -“bundle” and a “plugin” is purely a matter of the intended use; a plugin is just a bundle that is  -meant to be easily added or removed. When developing, it is typically more useful to think in  -terms of bundles.  -  -   -Configuring Active Bundles -  - To decide ​which​ bundles should be loaded, the framework loads a file named  -bundles.json​ (peer to the index.html file which serves the application) to determine which  -bundles should be loaded. This file should contain a single JSON array of strings, where each is  -the path to a bundle. These paths should not include ​bundle.json​ (this is implicit) or a trailing  -slash.  - For instance, if bundles.json contained:  -   - [  - "example/builtins",  - "example/extensions"  - ]  -   - ...then the Open MCT Web framework would look for bundle definitions at  -example/builtins/bundle.json​ and ​example/extensions/bundle.json​, relative  -to the path of ​index.html​. No other bundles would be loaded.  -  +A bundle is a directory which contains: - - 14  -Bundle Definition +* A bundle definition; a file named `​bundle.json​`. +* Subdirectories for sources, resources, and tests.  +* Optionally, a ​`README.md`​ Markdown file describing its contents (this is not  +used by Open MCT Web in any way, but it’s a helpful convention to follow.) + +The bundle definition is the main point of entry for the bundle. The framework  +looks at this to determine which components need to be loaded and how they  +interact. + +A plugin in Open MCT Web is a bundle. The platform itself is also decomposed  +into bundles, each of which provides some category of functionality. The  +difference between a _bundle_ and a _plugin_ is purely a matter of the intended  +use; a plugin is just a bundle that is meant to be easily added or removed. When  +developing, it is typically more useful to think in terms of bundles.  +   +### Configuring Active Bundles   - A bundle definition (the ​bundle.json​ file located within a bundle) contains a  -description of the bundle itself, as well as the information exposed by the bundle.  +To decide ​which​ bundles should be loaded, the framework loads a file named  +`bundles.json`​ (peer to the `index.html` file which serves the application) to  +determine which bundles should be loaded. This file should contain a single JSON  +array of strings, where each is the path to a bundle. These paths should not  +include ​bundle.json​ (this is implicit) or a trailing slash.  + +For instance, if `bundles.json` contained:  + + [  + "example/builtins",  + "example/extensions"  + ]  +   +...then the Open MCT Web framework would look for bundle definitions at  +`example/builtins/bundle.json`​ and `​example/extensions/bundle.json`​, relative  +to the path of `​index.html`​. No other bundles would be loaded.   + +### Bundle Definition   - This definition is expressed as a single JSON object with the following properties (all of  -which are optional, falling back to reasonable defaults):  +A bundle definition (the ​`bundle.json`​ file located within a bundle) contains a  +description of the bundle itself, as well as the information exposed by the  +bundle.    - ● key​: A machine­readable name for the bundle. (Currently used only in logging.)  - ● name​: A human­readable name for the bundle. (Also only used in logging.)  - ● sources​: Names a directory in which source scripts (which will implement extensions)  - are located. Defaults to “src”  - ● resources​: Names a directory in which resource files (such as HTML templates,  - images, CS files, and other non­JavaScript files needed by this bundle) are located.  - Defaults to “res”   - ● libraries​: Names a directory in which third­party libraries are located. Defaults to “lib”  - ● configuration​: A bundle’s configuration object, which should be formatted as would  - be passed to require.config (see RequireJS documentation at  - http://requirejs.org/docs/api.html​); note that only paths and shim have been tested.  - ● extensions​: An object containing key­value pairs, where keys are extension  - categories, and values are extension definitions. See the section on Extensions for more  - information.   +This definition is expressed as a single JSON object with the following  +properties (all of which are optional, falling back to reasonable defaults): + +* `key​`: A machine­readable name for the bundle. (Currently used only in  +logging.)  +* `name​`: A human­readable name for the bundle. (Also only used in logging.)  +* `sources​`: Names a directory in which source scripts (which will implement  +extensions) are located. Defaults to “src”  +* `resources​`: Names a directory in which resource files (such as HTML templates,  +images, CS files, and other non­JavaScript files needed by this bundle) are  +located. Defaults to “res”   +* `libraries`​: Names a directory in which third­party libraries are located.  +Defaults to “lib”  +* `configuration`​: A bundle’s configuration object, which should be formatted as  +would be passed to require.config (see [RequireJS documentation](http://requirejs.org/docs/api.html​) );  +note that only paths and shim have been tested.  +* `extensions`​: An object containing key­value pairs, where keys are extension  +categories, and values are extension definitions. See the section on Extensions  +for more information.   + +For example, the bundle definition for ​example/policy​ looks like:   + + { + "name": "Example Policy",  + "description": "Provides an example of using policies.",  + "sources": "src",  + "extensions": {  + "policies": [  + {  + "implementation": "ExamplePolicy.js",  + "category": "action"  + } + ]  + }  + } + +### Bundle Directory Structure   - For example, the bundle definition for ​example/policy​ looks like:  +In addition to the directories defined in the bundle definition, a bundle will  +typically contain other directories not used at run­time. Additionally, some  +useful development scripts (such as the command line build and the test suite)  +expect this directory structure to be in use, and may ignore options chosen by  +`b​undle.json`​. It is recommended that the directory structure described below be  +used for new bundles. + +* `src`​: Contains JavaScript sources for this bundle. May contain additional  +subdirectories to organize these sources; typically, these subdirectories are  +named to correspond to the extension categories they contain and/or support, but  +this is only a convention.  +* `res`​: Contains other files needed by this bundle, such as HTML templates. May  +contain additional subdirectories to organize these sources.  +* `lib`​: Contains JavaScript sources from third­party libraries. These are  +separated from bundle sources in order to ignore them during code style checking  +from the command line build. +* `test`​: Contains JavaScript sources implementing [Jasmine](http://jasmine.github.io/)  +tests, as well as a file named `​suite.json`​ describing which files to test.  +Should have the same folder structure as the `src` directory; see the section on  +automated testing for more information.    -{  -    "name": "Example Policy",  -    "description": "Provides an example of using policies.",  -    "sources": "src",  -    "extensions": {  -        "policies": [  -            {  -                "implementation": "ExamplePolicy.js",  -                "category": "action"  -            }  -        ]  -    }  -}  -  - - - 15  -Bundle Directory Structure -  - In addition to the directories defined in the bundle definition, a bundle will typically  -contain other directories not used at run­time. Additionally, some useful development scripts  -(such as the command line build and the test suite) expect this directory structure to be in use,  -and may ignore options chosen by b​ undle.json​. It is recommended that the directory  -structure described below be used for new bundles.  -  - ● src​: Contains JavaScript sources for this bundle. May contain additional subdirectories  - to organize these sources; typically, these subdirectories are named to correspond to the  - extension categories they contain and/or support, but this is only a convention.  - ● res​: Contains other files needed by this bundle, such as HTML templates. May contain  - additional subdirectories to organize these sources.  - ● lib​: Contains JavaScript sources from third­party libraries. These are separated from  - bundle sources in order to ignore them during code style checking from the command  - line build.  - ● test​: Contains JavaScript sources implementing Jasmine (http://jasmine.github.io/)  - tests, as well as a file named ​suite.json​ describing which files to test. Should have  - the same folder structure as the src directory; see the section on automated testing for  - more information.  -  - For example, the directory structure for bundle ​platform/commonUI/about ​looks  +For example, the directory structure for bundle ​`platform/commonUI/about` ​looks  like:  -   -  -  - - 16  -Extensions +INSERT DIAGRAM HERE + +## Extensions + +While bundles provide groupings of related behaviors, the individual units of  +behavior are called extensions.  + +Extensions belong to categories; an extension category is the machine­readable  +identifier used to identify groups of extensions. In the ​`extensions`​ property  +of a bundle definition, the keys are extension categories and the values are  +arrays of extension definitions.    - While bundles provide groupings of related behaviors, the individual units of behavior  -are called extensions.  - Extensions belong to categories; an extension category is the machine­readable  -identifier used to identify groups of extensions. In the ​extensions​ property of a bundle  -definition, the keys are extension categories and the values are arrays of extension definitions.  -  -General Extensions -  - Extensions are intended as a general­purpose mechanism for adding new types of  +### General Extensions + +Extensions are intended as a general­purpose mechanism for adding new types of  functionality to Open MCT Web.  - An extension category is registered with Angular under the name of the extension, plus a  -suffix of two square brackets; so, an Angular service (or, generally, any other extension) can  -access the full set of registered extensions, from all bundles, by including this string (e.g.  -types[]​ to get all type definitions) in a dependency declaration.  - As a convention, extension categories are given single­word, plural nouns for names  -within Open MCT Web (e.g. ​types​.) This convention is not enforced by the platform in any  -way. For extension categories introduced by external plugins, it is recommended to prefix the  -extension category with a vendor identifier (or similar) followed by a dot, to avoid collisions.  -  -Extension Definitions -  - The properties used in extension definitions are typically unique to each category of  -extension; a few properties have standard interpretations by the platform.  -  - ● implementation​: Identifies a JavaScript source file (in the sources folder) which  - implements this extension. This JavaScript file is expected to contain an AMD module  - (see ​http://requirejs.org/docs/whyamd.html#amd​) which gives as its result a single  - constructor function.  - ● depends​: An array of dependencies needed by this extension; these will be passed on  - to Angular’s dependency injector, ​https://docs.angularjs.org/guide/di​. By default, this is  - treated as an empty array. Note that ​depends​ does not make sense without  - implementation​ (since these dependencies will be passed to the implementation  - when it is instantiated.)  - ● priority​: A number or string indicating the priority order (see below) of this extension  - instance. Before an extension category is registered with AngularJS, the extensions of  - this category from all bundles will be concatenated into a single array, and then sorted  - by priority.  -  - 17  - Extensions do not need to have an implementation. If no implementation is provided,  -consumers of the extension category will receive the extension definition as a plain JavaScript  -object. Otherwise, they will receive the partialized (see below) constructor for that  -implementation, which will additionally have all properties from the extension definition attached.  -Partial Construction +An extension category is registered with Angular under the name of the  +extension, plus a suffix of two square brackets; so, an Angular service (or,  +generally, any other extension) can access the full set of registered  +extensions, from all bundles, by including this string (e.g. `types[]`​ to get  +all type definitions) in a dependency declaration.  + +As a convention, extension categories are given single­word, plural nouns for  +names within Open MCT Web (e.g. ​`types`​.) This convention is not enforced by the  +platform in any way. For extension categories introduced by external plugins, it  +is recommended to prefix the extension category with a vendor identifier (or  +similar) followed by a dot, to avoid collisions.    - In general, extensions are intended to be implemented as constructor functions, which  -will be used elsewhere to instantiate new objects of that type. However, the Angular­supported  -method for dependency injection is (effectively) constructor­style injection; so, both declared  -dependencies and run­time arguments are competing for space in a constructor’s arguments.  - To resolve this, the Open MCT Web framework registers extension instances in a  -partially constructed​ form. That is, the constructor exposed by the extension’s implementation is  -effectively decomposed into two calls; the first takes the dependencies, and returns the  -constructor in its second form, which takes the remaining arguments.  - This means that, when writing implementations, the constructor function should be  -written to include all declared dependencies, followed by all run­time arguments. When using  -extensions, only the run­time arguments need to be provided.  +### Extension Definitions + +The properties used in extension definitions are typically unique to each  +category of extension; a few properties have standard interpretations by the  +platform.    -Priority +* `implementation`​: Identifies a JavaScript source file (in the sources  +folder) which implements this extension. This JavaScript file is expected to  +contain an AMD module (see ​http://requirejs.org/docs/whyamd.html#amd​) which  +gives as its result a single constructor function.  +* `depends`​: An array of dependencies needed by this extension; these will be  +passed on to Angular’s [dependency injector](https://docs.angularjs.org/guide/di​)​.  +By default, this is treated as an empty array. Note that ​depends​ does not make  +sense without `implementation`​ (since these dependencies will be passed to the  +implementation when it is instantiated.)  +* `priority`​: A number or string indicating the priority order (see below) of  +this extension instance. Before an extension category is registered with  +AngularJS, the extensions of this category from all bundles will be concatenated  +into a single array, and then sorted by priority.  + +Extensions do not need to have an implementation. If no implementation is  +provided, consumers of the extension category will receive the extension  +definition as a plain JavaScript object. Otherwise, they will receive the  +partialized (see below) constructor for that implementation, which will  +additionally have all properties from the extension definition attached.  + +#### Partial Construction + +In general, extensions are intended to be implemented as constructor functions,  +which will be used elsewhere to instantiate new objects of that type. However,  +the Angular­supported method for dependency injection is (effectively)  +constructor­style injection; so, both declared dependencies and run­time  +arguments are competing for space in a constructor’s arguments.  + +To resolve this, the Open MCT Web framework registers extension instances in a  +partially constructed​ form. That is, the constructor exposed by the extension’s  +implementation is effectively decomposed into two calls; the first takes the  +dependencies, and returns the constructor in its second form, which takes the  +remaining arguments.  + +This means that, when writing implementations, the constructor function should  +be written to include all declared dependencies, followed by all run­time  +arguments. When using extensions, only the run­time arguments need to be  +provided.    - Within each extension category, registration occurs in priority order. An extension's  -priority may be specified as a ​priority​ property in its extension definition; this may be a  -number, or a symbolic string. Extensions are registered in reverse order (highest­priority first),  -and symbolic strings are mapped to the numeric values as follows:  +#### Priority + +Within each extension category, registration occurs in priority order. An  +extension's priority may be specified as a ​`priority`​ property in its extension  +definition; this may be a number, or a symbolic string. Extensions are  +registered in reverse order (highest­priority first), and symbolic strings are  +mapped to the numeric values as follows:    - ● fallback​: Negative infinity. Used for extensions that are not intended for use (that is,  - they are meant to be overridden) but are present as an option of last resort.  - ● default​: ­100. Used for extensions that are expected to be overridden, but need a  - useful default.  - ● none​: 0. Also used if no priority is specified, or if an unknown or malformed priority is  - specified.  - ● optional​: 100. Used for extensions that are meant to be used, but may be overridden.  - ● preferred​: 1000. Used for extensions that are specifically intended to be used, but still  - may be overridden in principle.  - ● mandatory​: Positive infinity. Used when an extension should definitely not be  - overridden.  +* `fallback`​: Negative infinity. Used for extensions that are not intended for  +use (that is, they are meant to be overridden) but are present as an option of  +last resort.  +* `default​`: ­100. Used for extensions that are expected to be overridden, but  +need a useful default.  +* `none`​: 0. Also used if no priority is specified, or if an unknown or  +malformed priority is specified.  +* `optional`​: 100. Used for extensions that are meant to be used, but may be  +overridden.  +* `preferred​`: 1000. Used for extensions that are specifically intended to be  +used, but still may be overridden in principle.  +* `mandatory`​: Positive infinity. Used when an extension should definitely not  +be overridden.    - These symbolic names are chosen to support usage where many extensions may satisfy  -a given need, but only one may be used; in this case, as a convention it should be the  -lowest­ordered (highest­priority) extensions available. In other cases, a full set (or multi­element  - 18  -subset) of extensions may be desired, with a specific ordering; in these cases, it is preferable to  -specify priority numerically when declaring extensions, and to understand that extensions will be  +These symbolic names are chosen to support usage where many extensions may  +satisfy a given need, but only one may be used; in this case, as a convention it  +should be the lowest­ordered (highest­priority) extensions available. In other  +cases, a full set (or multi­element subset) of extensions may be desired, with a  +specific ordering; in these cases, it is preferable to specify priority  +numerically when declaring extensions, and to understand that extensions will be  sorted according to these conventions when using them.    -Angular Built-ins +### Angular Built-ins + +Several entities supported Angular are expressed and managed as extensions in  +Open MCT Web. Specifically, these extension categories are _directives​_,  +_​controllers​_, _services​_, _​constants​_, _​runs​_, and _​routes​_.    - Several entities supported Angular are expressed and managed as extensions in Open  -MCT Web. Specifically, these extension categories are ​directives​, ​controllers​,  -services​, ​constants​, ​runs​, and ​routes​.  +#### Angular Directives + +New [directives](​https://docs.angularjs.org/guide/directive​) may be  +registered as extensions of the ​directives​ category. Implementations of  +directives in this category should take only dependencies as arguments, and  +should return a directive definition object.  + +The directive’s name should be provided as a ​key​ property of its extension  +definition, in camel­case format.    -Directives -  - New directives (see ​https://docs.angularjs.org/guide/directive​) may be registered as  -extensions of the ​directives​ category. Implementations of directives in this category should  -take only dependencies as arguments, and should return a directive definition object.   - The directive’s name should be provided as a ​key​ property of its extension definition, in  -camel­case format.  -  -Controllers -  - New controllers (see ​https://docs.angularjs.org/guide/controller​) may be registered as  -extensions of the ​controllers​ category. The implementation is registered directly as the  -controller; its only constructor arguments are its declared dependencies.  - The directive’s identifier should be provided as a ​key​ property of its extension definition.  +#### Angular Controllers + +New [controllers](​https://docs.angularjs.org/guide/controller​) may be registered  +as extensions of the ​controllers​ category. The implementation is registered  +directly as the controller; its only constructor arguments are its declared  +dependencies.  + +The directive’s identifier should be provided as a ​key​ property of its extension  +definition.      -Services -  - New services (see ​https://docs.angularjs.org/guide/services​) may be registered as  -extensions of the ​services​ category. The implementation is registered via a service call  -(​https://docs.angularjs.org/api/auto/service/$provide#service​), so it will be instantiated with the  -new​ operator.  -  -  +#### Angular Services -Constants -  - Constant values may be registered as extensions of the ​constants​ category; see  -https://docs.angularjs.org/api/ng/type/angular.Module#constant​. These extensions have no  - 19  -implementation; instead, they should contain a property ​key​, which is the name under which the  -constant will be registered, and a property ​value​, which is the constant value that will be  -registered.  -  -  -Runs -  - In some cases, you want to register code to run as soon as the application starts; these  -can be registered as extensions of the ​runs​ category; see  -https://docs.angularjs.org/api/ng/type/angular.Module#run​. Implementations registered in this  -category will be invoked (with their declared dependencies) when the Open MCT Web  -application first starts. (Note that, in this case, the implementation is better thought of as just a  -function, as opposed to a constructor function.)  -  -  -Routes -  - Extensions of category ​routes​ will be registered with Angular’s route provider,  -https://docs.angularjs.org/api/ngRoute/provider/$routeProvider​. Extensions of this category have  -no implementations, and need only two properties in their definition:  -  - ● when​: The value that will be passed as the path argument to ​$routeProvider.when​;  - specifically, the string that will appear in the trailing part of the URL corresponding to this  - route. This property may be omitted, in which case this extension instance will be treated  - as the default route.  - ● templateUrl​: A path to the template to render for this route. Specified as a path  - relative to the bundle’s resource directory (​res​ by default.)  -  -  +New [services](https://docs.angularjs.org/guide/services​) may be registered as  +extensions of the ​services​ category. The implementation is registered via a  +[service call](​https://docs.angularjs.org/api/auto/service/$provide#service​), so  +it will be instantiated with the new​ operator.  - - 20  -Composite Services +#### Angular Constants + +Constant values may be registered as extensions of the [​constants​ category](https://docs.angularjs.org/api/ng/type/angular.Module#constant​).  +These extensions have no implementation; instead, they should contain a property  +​key​, which is the name under which the constant will be registered, and a  +property ​value​, which is the constant value that will be registered. + +#### Angular Runs + +In some cases, you want to register code to run as soon as the application  +starts; these can be registered as extensions of the [​runs​ category](https://docs.angularjs.org/api/ng/type/angular.Module#run​).  +Implementations registered in this category will be invoked (with their declared  +dependencies) when the Open MCT Web application first starts. (Note that, in  +this case, the implementation is better thought of as just a function, as  +opposed to a constructor function.) + +#### Angular Routes + +Extensions of category `​routes`​ will be registered with Angular’s [route provider](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider​).  +Extensions of this category have no implementations, and need only two  +properties in their definition:    - A special category of extensions recognized by the framework are ​components​; these  -are parts of services intended to be fit together in a common pattern.  -  -   -  - Components all implement the same interface, which is the interface expected for  -services of the type that they create. Components fall into three types:  -  - ● provider​: Provides an actual implementation of the service in question.  - ● aggregator​: Makes many implementations of the service in question appear as one.  - ● decorator​: Modifies the inputs or outputs of another implementation of the service.  -  - When the framework layer encounters components, it assembles them into single  -service instances that can be referred to elsewhere as single dependencies. All providers are  -instantiated, and passed to the first available aggregator; decorators are then layered on in  -priority order to create the final form of the service.  - A component should include the following properties in its extension definition:  -  - ● provides​: The symbolic identifier for the service that will be composed. The  - fully­composed service will be registered with Angular under this name.  - ● type​: One of ​provider​, ​aggregator​, or ​decorator​ (as above)  -  - In addition to any declared dependencies, aggregators and decorators both receive one  -more argument (immediately following declared dependencies) that is provided by the  -framework. For an aggregator, this will be an array of all providers of the same service (that is,  -with matching ​provides​ properties); for a decorator, this will be whichever provider, decorator,  -or aggregator is next in the sequence of decorators.  - Services exposed by the Open MCT Web platform are often declared as composite  +* `when​`: The value that will be passed as the path argument to ​ +`$routeProvider.when`​; specifically, the string that will appear in the trailing  +part of the URL corresponding to this route. This property may be omitted, in  +which case this extension instance will be treated as the default route.  +* `templateUrl`​: A path to the template to render for this route. Specified as a  +path relative to the bundle’s resource directory (​`res​` by default.)  + +### Composite Services + +A special category of extensions recognized by the framework are ​`components`​;  +these are parts of services intended to be fit together in a common pattern.  + +INSERT DIAGRAM HERE + +Components all implement the same interface, which is the interface expected for  +services of the type that they create. Components fall into three types: + +* `provider​`: Provides an actual implementation of the service in question.  +* `aggregator`​: Makes many implementations of the service in question appear as  +one.  +* `decorator​`: Modifies the inputs or outputs of another implementation of the  +service.  + +When the framework layer encounters components, it assembles them into single  +service instances that can be referred to elsewhere as single dependencies. All  +providers are instantiated, and passed to the first available aggregator;  +decorators are then layered on in priority order to create the final form of the  +service. + +A component should include the following properties in its extension definition: + +* `provides`​: The symbolic identifier for the service that will be composed. The  + fully­composed service will be registered with Angular under this name. +* `type​`: One of `​provider`​, ​`aggregator​`, or `​decorator​` (as above)  + +In addition to any declared dependencies, aggregators and decorators both  +receive one more argument (immediately following declared dependencies) that is  +provided by the framework. For an aggregator, this will be an array of all  +providers of the same service (that is, with matching `​provides`​ properties);  +for a decorator, this will be whichever provider, decorator, or aggregator is  +next in the sequence of decorators.  + +Services exposed by the Open MCT Web platform are often declared as composite  services, as this form is open for a variety of common modifications.  - - 21  -Core API -  - Most of Open MCT Web’s relevant API is provided and/or mediated by the framework;  -that is, much of developing for Open MCT Web is a matter of adding extensions which access  -other parts of the platform by means of dependency injection.  - The core bundle (​platform/core​) introduces a few additional object types meant to  -be passed along by other services.  -  -Domain Objects -  - Domain objects are the most fundamental component of Open MCT Web’s information  -model. A domain object is some distinct thing relevant to a user’s work flow, such as a telemetry  -channel, display, or similar. Open MCT Web is a tool for viewing, browsing, manipulating, and  -otherwise interacting with a graph of domain objects.  - A domain object should be conceived of as the union of the following:  -   - ● Identifier: A machine­readable string that uniquely identifies the domain object within this  - application instance.  - ● Model: The persistent state of the domain object. A domain object’s model is a  - JavaScript object that can be losslessly converted to JSON.  - ● Capabilities: Dynamic behavior associated with the domain object. Capabilities are  - JavaScript objects which provide additional methods for interacting with the domain  - objects which expose those capabilities. Not all domain objects expose all capabilities.  -  - At run­time, a domain object has the following interface:  -  - ● getId()​: Get the identifier for this domain object.  - ● getModel()​: Get the plain state associated with this domain object. This will return a  - JavaScript object that can be losslessly converted to JSON. Note that the model  - returned here can be modified directly but should not be; instead, use the ​mutation  - capability.  - ● getCapability(key)​: Get the specified capability associated with this domain object.  - This will return a JavaScript object whose interface is specific to the type of capability  - being requested. If the requested capability is not exposed by this domain object, this  - will return ​undefined​.  - 22  - ● hasCapability(key)​: Shorthand for checking if a domain object exposes the  - requested capability.  - ● useCapability(key, arguments…)​: Shorthand for  - getCapability(key).invoke(arguments)​, with additional checking between  - calls. If the provided capability has no invoke method, the return value here functions as  - getCapability​, including returning ​undefined​ if the capability is not exposed.  -  -Actions -  - An ​Action​ is behavior that can be performed upon/using a ​DomainObject​. An Action  -has the following interface:  -  - ● perform()​: Do this action. For example, if one had an instance of a ​RemoveAction​,  - invoking its ​perform​ method would cause the domain object which exposed it to be  - removed from its container.  - ● getMetadata()​: Get metadata associated with this action. Returns an object  - containing:  - ○ name​: Human­readable name.  - ○ description​: Human­readable summary of this action.  - ○ glyph​: Single character to be displayed in Open MCT Web’s icon font set.  - ○ context​: The context in which this action is being performed (see below)  -  - Action instances are typically obtained via a domain object’s ​action​ capability.  -  -Action Contexts -  - An action context is a JavaScript object with the following properties:  -  - ● domainObject​: The domain object being acted upon.  - ● selectedObject​: Optional; the selection at the time of action (e.g. the dragged object  - in a drag­and­drop operation.)  +# Core API - - 23  -Telemetry -  - Telemetry series data in Open MCT Web is represented by a common interface, and  -packaged in a consistent manner to facilitate passing telemetry updates around multiple  -visualizations.  -  -Telemetry Requests -  - A telemetry request is a JavaScript object containing the following properties:  -  - ● source​: A machine­readable identifier for the source of this telemetry. This is useful  - when multiple distinct data sources are in use side­by­side.  - ● key​: A machine­readable identifier for a unique series of telemetry within that source.  - ● Note: This API is still under development; additional properties, such as start and end  - time, should be present in future versions of Open MCT Web.  -  - Additional properties may be included in telemetry requests which have specific  -interpretations for specific sources.  -  -Telemetry Responses -  - When returned from the ​telemetryService​ (see Services section), telemetry series  -data will be packaged in a ​source ­> key ­> TelemetrySeries​ fashion. That is,  -telemetry is passed in an object containing key­value pairs. Keys identify telemetry sources;  -values are objects containing additional key­value pairs. In this object, keys identify individual  -telemetry series (and match they ​key​ property from corresponding requests) and values are  -TelemetrySeries​ objects (see below.)  -  +Most of Open MCT Web’s relevant API is provided and/or mediated by the  +framework; that is, much of developing for Open MCT Web is a matter of adding  +extensions which access other parts of the platform by means of dependency  +injection.  - - 24  -Telemetry Series +The core bundle (`​platform/core`​) introduces a few additional object types meant  +to be passed along by other services.  + +## Domain Objects + +Domain objects are the most fundamental component of Open MCT Web’s information  +model. A domain object is some distinct thing relevant to a user’s work flow,  +such as a telemetry channel, display, or similar. Open MCT Web is a tool for  +viewing, browsing, manipulating, and otherwise interacting with a graph of  +domain objects.  + +A domain object should be conceived of as the union of the following: + +* __Identifier__: A machine­readable string that uniquely identifies the domain  +object within this application instance.  +* __Model__: The persistent state of the domain object. A domain object’s model  +is a JavaScript object that can be losslessly converted to JSON.  +* __Capabilities__: Dynamic behavior associated with the domain object.  +Capabilities are JavaScript objects which provide additional methods for  +interacting with the domain objects which expose those capabilities. Not all  +domain objects expose all capabilities.  + +At run­time, a domain object has the following interface: + +* `getId()`​: Get the identifier for this domain object.  +* `getModel()`​: Get the plain state associated with this domain object. This  +will return a JavaScript object that can be losslessly converted to JSON. Note  +that the model returned here can be modified directly but should not be;  +instead, use the ​mutation capability.  +* `getCapability(key)`​: Get the specified capability associated with this domain  +object. This will return a JavaScript object whose interface is specific to the  +type of capability being requested. If the requested capability is not exposed  +by this domain object, this will return ​undefined​. +* `hasCapability(key)`​: Shorthand for checking if a domain object exposes the  +requested capability. +* `useCapability(key, arguments…)`​: Shorthand for  +`getCapability(key).invoke(arguments)`​, with additional checking between calls.  +If the provided capability has no invoke method, the return value here functions  +as `getCapability​`, including returning ​`undefined​` if the capability is not  +exposed. + +## Actions + +An ​`Action​` is behavior that can be performed upon/using a `​DomainObject​`. An  +Action has the following interface: + +* `perform()`​: Do this action. For example, if one had an instance of a  +`​RemoveAction​`, invoking its ​perform​ method would cause the domain object which  +exposed it to be removed from its container. +* `getMetadata()`​: Get metadata associated with this action. Returns an object  +containing:  + * `name`​: Human­readable name. + * `description`​: Human­readable summary of this action.  + * `glyph​`: Single character to be displayed in Open MCT Web’s icon font set.  + * `context`​: The context in which this action is being performed (see below) + +Action instances are typically obtained via a domain object’s `​action​`  +capability.    - A telemetry series is a specific sequence of data, typically associated with a specific  -instrument. Telemetry is modeled as an ordered sequence of domain and range values, where  -domain values must be non­decreasing but range values do not. (Typically, domain values are  -interpreted as UTC timestamps in milliseconds relative to the UNIX epoch.) A series must have  -at least one domain and one range, and may have more than one.  - Telemetry series data in Open MCT Web is expressed via the following  -TelemetrySeries​ interface:  +### Action Contexts + +An action context is a JavaScript object with the following properties:  + +* `domainObject​`: The domain object being acted upon.  +* `selectedObject`​: Optional; the selection at the time of action (e.g. the  +dragged object in a drag­and­drop operation.) + +## Telemetry + +Telemetry series data in Open MCT Web is represented by a common interface, and  +packaged in a consistent manner to facilitate passing telemetry updates around  +multiple visualizations.    - ● getPointCount()​: Returns the number of unique points/samples in this series.  - ● getDomainValue(index, [domain]):​ Get the domain value at the specified  - index​. If a second ​domain​ argument is provided, this is taken as a string identifier  - indicating which domain option (of, presumably, multiple) should be returned.  - ● getRangeValue(index, [range]):​ Get the domain value at the specified ​index​.  - If a second ​range​ argument is provided, this is taken as a string identifier indicating  - which range option (of, presumably, multiple) should be returned.  +### Telemetry Requests + +A telemetry request is a JavaScript object containing the following properties:  + +* `source​`: A machine­readable identifier for the source of this telemetry. This  +is useful when multiple distinct data sources are in use side­by­side.  +* `key​`: A machine­readable identifier for a unique series of telemetry within  +that source.  +* _Note: This API is still under development; additional properties, such as  +start and end time, should be present in future versions of Open MCT Web._  + +Additional properties may be included in telemetry requests which have specific  +interpretations for specific sources. + +### Telemetry Responses + +When returned from the `​telemetryService​` (see [Services](#Services) section),  +telemetry series data will be packaged in a ​`source ­> key ­> TelemetrySeries​`  +fashion. That is, telemetry is passed in an object containing key­value pairs.  +Keys identify telemetry sources; values are objects containing additional  +key­value pairs. In this object, keys identify individual telemetry series (and  +match they ​`key​` property from corresponding requests) and values are  +`TelemetrySeries​` objects (see below.)  + +### Telemetry Series + +A telemetry series is a specific sequence of data, typically associated with a  +specific instrument. Telemetry is modeled as an ordered sequence of domain and  +range values, where domain values must be non­decreasing but range values do  +not. (Typically, domain values are interpreted as UTC timestamps in milliseconds  +relative to the UNIX epoch.) A series must have at least one domain and one  +range, and may have more than one. + +Telemetry series data in Open MCT Web is expressed via the following  +`TelemetrySeries​` interface:    -Telemetry Metadata +* `getPointCount()`​: Returns the number of unique points/samples in this series.  +* `getDomainValue(index, [domain])`:​ Get the domain value at the specified index​.  +If a second ​domain​ argument is provided, this is taken as a string identifier  +indicating which domain option (of, presumably, multiple) should be returned.  +* `getRangeValue(index, [range])`:​ Get the domain value at the specified ​index​.  +If a second ​range​ argument is provided, this is taken as a string identifier  +indicating which range option (of, presumably, multiple) should be returned.    - Domain objects which have associated telemetry also expose metadata about that  -telemetry; this is retrievable via the ​getMetadata()​ of the telemetry capability. This will return  -a single JavaScript object containing the following properties:  +### Telemetry Metadata + +Domain objects which have associated telemetry also expose metadata about that  +telemetry; this is retrievable via the `​getMetadata()`​ of the telemetry  +capability. This will return a single JavaScript object containing the following  +properties:    - ● source​: The machine­readable identifier for the source of telemetry data for this object.  - ● key​: The machine­readable identifier for the individual telemetry series.  - ● domains​: An array of supported domains (see ​TelemetrySeries​ above.) Each  - domain should be expressed as an object which includes:  - ○ key​: Machine­readable identifier for this domain, as will be passed into a  - getDomainValue(index, domain)​ call.  - ○ name​: Human­readable name for this domain.  - ● ranges​: An array of supported ranges; same format as ​domains​.  -  - Note that this metadata is also used as the prototype for telemetry requests made using  -this capability.  -  -  -Types -  - A domain object’s type is represented as a ​Type​ object, which has the following  +* `source​`: The machine­readable identifier for the source of telemetry data for  +this object.  +* `key​`: The machine­readable identifier for the individual telemetry series.  +* `domains​`: An array of supported domains (see ​TelemetrySeries​ above.) Each  +domain should be expressed as an object which includes:  + * `key​`: Machine­readable identifier for this domain, as will be passed  + into a getDomainValue(index, domain)​ call.  + * `name​`: Human­readable name for this domain.  +* `ranges​`: An array of supported ranges; same format as ​domains​.  + +Note that this metadata is also used as the prototype for telemetry requests  +made using this capability.  + +## Types +A domain object’s type is represented as a ​Type​ object, which has the following  interface:  - 25  -  - ● getKey()​: Get the machine­readable identifier for this type.  - ● getName()​: Get the human­readable name for this type.  - ● getDescription()​: Get a human­readable summary of this type.  - ● getGlyph()​: Get the single character to be rendered as an icon for this type in Open  - MCT Web’s custom font set.  - ● getInitialModel()​: Get a domain object model that represents the initial state  - (before user specification of properties) for domain objects of this type.  - ● getDefinition()​: Get the extension definition for this type, as a JavaScript object.  - ● instanceOf(type)​: Check if this type is (or inherits from) a specified ​type​. This type  - can be either a string, in which case it is taken to be that type’s ​key​, or it may be a ​Type  - instance.  - ● hasFeature(feature)​: Returns a boolean value indicating whether or not this type  - supports the specified ​feature​, which is a symbolic string.  - ● getProperties()​: Get all properties associated with this type, expressed as an array  - of ​TypeProperty​ instances.  -  -Type Features -  - Features of a domain object type are expressed as symbolic string identifiers. They are  -defined in practice by usage; currently, the Open MCT Web platform only uses the ​creation  -feature to determine which domain object types should appear in the Create menu.  -  -Type Properties -  - Types declare the user­editable properties of their domain object instances in order to  -allow the forms which appear in the Create and Edit Properties dialogs to be generated by the  -platform. A ​TypeProperty​ has the following interface:  -  - ● getValue(model)​: Get the current value for this property, as it appears in the  - provided domain object ​model​.  - ● setValue(model, value)​: Set a new ​value​ for this property in the provided  - domain object ​model​.  - ● getDefinition()​: Get the raw definition for this property as a JavaScript object (as it  - was declared in this type’s extension definition.)  -Extension Categories -  - The information in this section is focused on registering new extensions of specific types;  -it does not contain a catalog of the extension instances of these categories provided by the  -platform. Relevant summaries there are provided in subsequent sections.  - 26  -  -Actions -  - An action is a thing that can be done to or using a domain object, typically as initiated by  -the user.  -  - An action’s implementation:  - ● Should take a single ​context​ argument in its constructor. (See Action Contexts, under  - Core API.)  - ● Should provide a method ​perform​, which causes the behavior associated with the  - action to occur.  - ● May provide a method ​getMetadata​, which provides metadata associated with the  - action. If omitted, one will be provided by the platform which includes metadata from the  - action’s extension definition.  - ● May provide a static method ​appliesTo(context)​ (that is, a function available as a  - property of the implementation’s constructor itself), which will be used by the platform to  - filter out actions from contexts in which they are inherently inapplicable.  -  - An action’s bundle definition (and/or ​getMetadata()​ return value) may include:  - ● category​: A string or dearray of strings identifying which category or categories an  - action falls into; used to determine when an action is displayed. Categories supported by  - the platform include:  - ○ contextual​: Actions in a context menu.  - ○ view­control​: Actions triggered by buttons in the top­right of Browse view.  - ● key​: A machine­readable identifier for this action.  - ● name​: A human­readable name for this action (e.g. to show in a menu)  - ● description​: A human­readable summary of the behavior of this action.  - ● glyph​: A single character which will be rendered in Open MCT Web’s custom font set  - as an icon for this action.  -  +* `getKey()`​: Get the machine­readable identifier for this type.  +* `getName()​`: Get the human­readable name for this type.  +* `getDescription()`​: Get a human­readable summary of this type.  +* `getGlyph()​`: Get the single character to be rendered as an icon for this type  +in Open MCT Web’s custom font set.  +* `getInitialModel()`​: Get a domain object model that represents the initial  +state (before user specification of properties) for domain objects of this type.  +* `getDefinition()​`: Get the extension definition for this type, as a JavaScript  +object.  +* `instanceOf(type)`​: Check if this type is (or inherits from) a specified ​type​.  +This type can be either a string, in which case it is taken to be that type’s  +​key​, or it may be a ​Type instance.  +* `hasFeature(feature)`​: Returns a boolean value indicating whether or not this  +type supports the specified ​feature​, which is a symbolic string.  +* `getProperties()​`: Get all properties associated with this type, expressed as  +an array of ​TypeProperty​ instances.    +### Type Features +Features of a domain object type are expressed as symbolic string identifiers.  +They are defined in practice by usage; currently, the Open MCT Web platform only  +uses the ​creation feature to determine which domain object types should appear  +in the Create menu.  +  +### Type Properties + +Types declare the user­editable properties of their domain object instances in  +order to allow the forms which appear in the Create and Edit Properties dialogs  +to be generated by the platform. A ​TypeProperty​ has the following interface: + +* `getValue(model)`​: Get the current value for this property, as it appears in  +the provided domain object ​model​.  +* `setValue(model, value)`​: Set a new ​value​ for this property in the provided  +domain object ​model​.  +* `getDefinition()​`: Get the raw definition for this property as a JavaScript  +object (as it was declared in this type’s extension definition.)  + +#Extension Categories + +The information in this section is focused on registering new extensions of  +specific types; it does not contain a catalog of the extension instances of  +these categories provided by the platform. Relevant summaries there are provided  +in subsequent sections. +  +## Actions + +An action is a thing that can be done to or using a domain object, typically as  +initiated by the user.  + +An action’s implementation: + +* Should take a single `​context​` argument in its constructor. (See Action  +Contexts, under Core API.)  +* Should provide a method ​`perform​`, which causes the behavior associated with  +the action to occur.  +* May provide a method `​getMetadata​`, which provides metadata associated with  +the action. If omitted, one will be provided by the platform which includes  +metadata from the action’s extension definition. +* May provide a static method ​`appliesTo(context)`​ (that is, a function  +available as a property of the implementation’s constructor itself), which will  +be used by the platform to filter out actions from contexts in which they are  +inherently inapplicable. + +An action’s bundle definition (and/or `​getMetadata()`​ return value) may include:  +* `category​`: A string or dearray of strings identifying which category or  +categories an action falls into; used to determine when an action is displayed.  +Categories supported by the platform include:  + * `contextual​`: Actions in a context menu.  + * `view­control​`: Actions triggered by buttons in the top­right of Browse view.  +* `key​`: A machine­readable identifier for this action.  +* `name​`: A human­readable name for this action (e.g. to show in a menu)  +* `description​`: A human­readable summary of the behavior of this action.  +* `glyph`​: A single character which will be rendered in Open MCT Web’s custom font  +set as an icon for this action.  27  Capabilities @@ -869,41 +885,41 @@ Controls   Four standard control types are included in the forms bundle:    - ● textfield​: An area to enter plain text.  - ● select​: A drop­down list of options.  - ● checkbox​: A box which may be checked/unchecked.  - ● color​: A color picker.  - ● button​: A button.  - ● datetime​: An input for UTC date/time entry; gives result as a UNIX timestamp, in  + * textfield​: An area to enter plain text.  + * select​: A drop­down list of options.  + * checkbox​: A box which may be checked/unchecked.  + * color​: A color picker.  + * button​: A button.  + * datetime​: An input for UTC date/time entry; gives result as a UNIX timestamp, in  milliseconds since start of 1970, UTC.    New controls may be added as extensions of the controls category. Extensions of this  category have two properties:    - ● key​: The symbolic name for this control (matched against the control field in rows of the  + * key​: The symbolic name for this control (matched against the control field in rows of the  form structure).  - ● templateUrl​: The URL to the control's Angular template, relative to the resources  + * templateUrl​: The URL to the control's Angular template, relative to the resources  directory of the bundle which exposes the extension.  28    Within the template for a control, the following variables will be included in scope:    - ● ngModel​: The model where form input will be stored. Notably we also need to look at  + * ngModel​: The model where form input will be stored. Notably we also need to look at  field​ (see below) to determine which field in the model should be modified.  - ● ngRequired​: True if input is required.  - ● ngPattern​: The pattern to match against (for text entry.)  - ● options​: The options for this control, as passed from the ​options​ property of an  + * ngRequired​: True if input is required.  + * ngPattern​: The pattern to match against (for text entry.)  + * options​: The options for this control, as passed from the ​options​ property of an  individual row definition.  - ● field​: Name of the field in ​ngModel​ which will hold the value for this control.  + * field​: Name of the field in ​ngModel​ which will hold the value for this control.    Gestures   A gesture is a user action which can be taken upon a representation of a domain object.  Examples of gestures included in the platform are:    - ● drag​: For representations that can be used to initiate drag­and­drop composition.  - ● drop​: For representations that can be drop targets for drag­and­drop composition.  - ● menu​: For representations that can be used to pop up a context menu.  + * drag​: For representations that can be used to initiate drag­and­drop composition.  + * drop​: For representations that can be drop targets for drag­and­drop composition.  + * menu​: For representations that can be used to pop up a context menu.    Gesture definitions have a property ​key​ which is used as a machine­readable identifier  for the gesture (e.g. ​drag​, ​drop​, ​menu​ above.)  @@ -928,15 +944,15 @@ Standard Indicators Indicators which wish to appear in the common form of an icon­text pair should provide  implementations with the following methods:    - ● getText()​: Provides the human­readable text that will be displayed for this indicator.  - ● getGlyph()​: Provides a single­character string that will be displayed as an icon in  + * getText()​: Provides the human­readable text that will be displayed for this indicator.  + * getGlyph()​: Provides a single­character string that will be displayed as an icon in  Open MCT Web’s custom font set.  - ● getDescription()​: Provides a human­readable summary of the current state of this  + * getDescription()​: Provides a human­readable summary of the current state of this  indicator; will be displayed in a tooltip on hover.  - ● getClass()​: Get a CSS class that will be applied to this indicator.  - ● getTextClass()​: Get a CSS class that will be applied to this indicator’s text portion.  - ● getGlyphClass()​: Get a CSS class that will be applied to this indicator’s icon portion.  - ● configure()​: If present, a configuration icon will appear to the right of this indicator,  + * getClass()​: Get a CSS class that will be applied to this indicator.  + * getTextClass()​: Get a CSS class that will be applied to this indicator’s text portion.  + * getGlyphClass()​: Get a CSS class that will be applied to this indicator’s icon portion.  + * configure()​: If present, a configuration icon will appear to the right of this indicator,  and clicking it will invoke this method.    Note that all methods are optional, and are called directly from an Angular template, so  @@ -957,12 +973,12 @@ Licenses information” page, reachable from Open MCT Web’s About dialog.  Licenses may have the following properties, all of which are strings:    - ● name​: Human­readable name of the licensed component. (e.g. “AngularJS”.)  - ● version​: Human­readable version of the licensed component. (e.g. “1.2.26”.)  - ● description​: Human­readable summary of the component.  - ● author​: Name or names of entities to which authorship should be attributed.  - ● copyright​: Copyright text to display for this component.  - ● link​: URL to full license text.  + * name​: Human­readable name of the licensed component. (e.g. “AngularJS”.)  + * version​: Human­readable version of the licensed component. (e.g. “1.2.26”.)  + * description​: Human­readable summary of the component.  + * author​: Name or names of entities to which authorship should be attributed.  + * copyright​: Copyright text to display for this component.  + * link​: URL to full license text.    30    @@ -974,10 +990,10 @@ whether or not a domain object of one type can contain a domain obj the section on the Policies for an overview of Open MCT Web’s policy model.  A policy’s extension definition should include:    - ● category​: The machine­readable identifier for the type of policy decision being  + * category​: The machine­readable identifier for the type of policy decision being  supported here. For a list of categories supported by the platform, see the section on  Policies. Plugins may introduce and utilize additional policy categories not in that list.  - ● message​: Optional; a human­readable message describing the policy, intended for  + * message​: Optional; a human­readable message describing the policy, intended for  display in situations where this specific policy has disallowed something.    A policy’s implementation should include a single method, ​allow(candidate,  @@ -996,17 +1012,17 @@ directive.    A representation definition should include the following properties:    - ● key​: The machine­readable name which identifies the representation.  - ● templateUrl​: The path to the representation's Angular template. This path is relative  + * key​: The machine­readable name which identifies the representation.  + * templateUrl​: The path to the representation's Angular template. This path is relative  to the bundle's resources directory.  - ● uses​: Optional; an array of capability names. Indicates that this representation intends  + * uses​: Optional; an array of capability names. Indicates that this representation intends  to use those capabilities of a domain object (via a ​useCapability​ call), and expects to  find the latest results of that ​useCapability​ call in the scope of the presented  template (under the same name as the capability itself.) Note that, if ​useCapability  returns a promise, this will be resolved before being placed in the representation’s  scope.  31  - ● gestures​: An array of keys identifying gestures (see the ​gestures​ extension  + * gestures​: An array of keys identifying gestures (see the ​gestures​ extension  category) which should be available upon this representation. Examples of gestures  include ​drag​ (for representations that should act as draggable sources for drag­drop  operations) and ​menu​ (for representations which should show a domain­object­specific  @@ -1023,17 +1039,17 @@ https://docs.angularjs.org/guide/controller​ for more information on cont   A representation’s scope will contain:    - ● domainObject​: The represented domain object.  - ● model​: The domain object’s model.  - ● configuration​: An object containing configuration information for this representation  + * domainObject​: The represented domain object.  + * model​: The domain object’s model.  + * configuration​: An object containing configuration information for this representation  (an empty object if there is no saved configuration.) The contents of this object are  managed entirely by the view/representation which receives it.  - ● representation​: An empty object, useful as a “scratch pad” for representation state.  - ● ngModel​: An object passed through the ​ng­model​ attribute of the  + * representation​: An empty object, useful as a “scratch pad” for representation state.  + * ngModel​: An object passed through the ​ng­model​ attribute of the  mct­representation​, if any.  - ● parameters​: An object passed through the ​parameters​ attribute of the  + * parameters​: An object passed through the ​parameters​ attribute of the  mct­representation​, if any.  - ● Any capabilities requested by the ​uses​ property of the representation definition.  + * Any capabilities requested by the ​uses​ property of the representation definition.    Representers   @@ -1062,15 +1078,15 @@ Root­level domain objects appear at the top­level of the tree hierar Items” folder is added as an extension of this category.  Extensions of this category should have the following properties:    - ● id​: The machine­readable identifier for the domain object being exposed.  - ● model​: The model, as a JSON object, for the domain object being exposed.  + * id​: The machine­readable identifier for the domain object being exposed.  + * model​: The model, as a JSON object, for the domain object being exposed.    Stylesheets   The ​stylesheets​ extension category is used to add CSS files to style the application.  Extension definitions for this category should include one property:    - ● stylesheetUrl​: Path and filename, including extension, for the stylesheet to include.  + * stylesheetUrl​: Path and filename, including extension, for the stylesheet to include.  This path is relative to the bundle’s resources folder (by default, ​res​)    To control the order of CSS files, use ​priority​ (see the section on Extension  @@ -1088,9 +1104,9 @@ behaves similarly to ​ng­include​, except that it uses these symbo paths.  A template’s extension definition should include the following properties:    - ● key​: The machine­readable name which identifies this template, matched against the  + * key​: The machine­readable name which identifies this template, matched against the  value given to the key attribute of the mct­include directive.  - ● templateUrl​: The path to the relevant Angular template. This path is relative to the  + * templateUrl​: The path to the relevant Angular template. This path is relative to the  bundle's resources directory.    Note that, when multiple templates are present with the same ​key​, the one with the  @@ -1105,19 +1121,19 @@ Types within Open MCT Web.  A type’s extension definition should have the following properties:    - ● key​: The machine­readable identifier for this domain object type. Will be stored to and  + * key​: The machine­readable identifier for this domain object type. Will be stored to and  matched against the ​type​ property of domain object models.  - ● name​: The human­readable name for this domain object type.  - ● description​: A human­readable summary of this domain object type.  - ● glyph​: A single character to be rendered as an icon in Open MCT Web’s custom font  + * name​: The human­readable name for this domain object type.  + * description​: A human­readable summary of this domain object type.  + * glyph​: A single character to be rendered as an icon in Open MCT Web’s custom font  set.  - ● model​: A domain object model, used as the initial state for created domain objects of  + * model​: A domain object model, used as the initial state for created domain objects of  this type (before any properties are specified.)  - ● features​: Optional; an array of strings describing features of this domain object type.  + * features​: Optional; an array of strings describing features of this domain object type.  Currently, only ​creation​ is recognized by the platform; this is used to determine that  this type should appear in the Create menu. More generally, this is used to support the  hasFeature(...)​ method of the ​type​ capability.  - ● properties​: An array describing individual properties of this domain object (as should  + * properties​: An array describing individual properties of this domain object (as should  appear in the Create or the Edit Properties dialog.) Each property is described by an  object containing the following properties:  34  @@ -1139,9 +1155,9 @@ Versions The ​versions​ extension category is used to introduce line items in Open MCT Web’s  About dialog. These should have the following properties:    - ● name​: The name of this line item, as should appear in the left­hand side of the list of  + * name​: The name of this line item, as should appear in the left­hand side of the list of  version information in the About dialog.  - ● value​: The value which should appear to the right of the name in the About dialog.  + * value​: The value which should appear to the right of the name in the About dialog.    To control the ordering of line items within the About dialog, use ​priority​. (See  section on Extension Definitions above.)  @@ -1155,19 +1171,19 @@ available views of domain objects of specific types. A view’s extens properties as a representation (and views can be utilized via ​mct­representation​);  additionally:    - ● name​: The human­readable name for this view type.  - ● description​: A human­readable summary of this view type.  - ● glyph​: A single character to be rendered as an icon in Open MCT Web’s custom font  + * name​: The human­readable name for this view type.  + * description​: A human­readable summary of this view type.  + * glyph​: A single character to be rendered as an icon in Open MCT Web’s custom font  set.  - ● type​: Optional; if present, this representation is only applicable for domain object’s of  + * type​: Optional; if present, this representation is only applicable for domain object’s of  this type.  35  - ● needs​: Optional array of strings; if present, this representation is only applicable for  + * needs​: Optional array of strings; if present, this representation is only applicable for  domain objects which have the capabilities identified by these strings.  - ● delegation​: Optional boolean, intended to be used in conjunction with ​needs​;  if  + * delegation​: Optional boolean, intended to be used in conjunction with ​needs​;  if  present, allow required capabilities to be satisfied by means of capability delegation.  (See the ​delegation​ capability, in the Capabilities section.)  - ● toolbar​: Optional; a definition for the toolbar which may appear in a toolbar when  + * toolbar​: Optional; a definition for the toolbar which may appear in a toolbar when  using this view in Edit mode. This should be specified as a structure for ​mct­toolbar​,  with additional properties available for each item in that toolbar:  ○ property​: A property name. This will refer to a property in the view’s current  @@ -1186,9 +1202,9 @@ provided for ​representations​.    When a view is in Edit mode, this scope will additionally contain:    - ● commit()​: A function which can be invoked to mark any changes to the view’s  + * commit()​: A function which can be invoked to mark any changes to the view’s  configuration​ as ready to persist.  - ● selection​: An object representing the current selection state.  + * selection​: An object representing the current selection state.    Selection State   @@ -1204,15 +1220,15 @@ within the view. (Future versions of Open MCT Web may support multipl 36  The ​selection​ object made available during Edit mode has the following methods:    - ● proxy([object])​: Get (or set, if called with an argument) the current view proxy.   - ● select(object)​: Make this object the selected object.  - ● deselect()​: Clear the currently selected object.  - ● get()​: Get the currently selected object. Returns ​undefined​ if there is no currently  + * proxy([object])​: Get (or set, if called with an argument) the current view proxy.   + * select(object)​: Make this object the selected object.  + * deselect()​: Clear the currently selected object.  + * get()​: Get the currently selected object. Returns ​undefined​ if there is no currently  selected object.  - ● selected(object)​: Check if the JavaScript object is currently in the selection set.  + * selected(object)​: Check if the JavaScript object is currently in the selection set.  Returns ​true​ if the object is either the currently selected object, or the current view  proxy.  - ● all()​: Get an array of all objects in the selection state. Will include either or both of the  + * all()​: Get an array of all objects in the selection state. Will include either or both of the  view proxy and selected object.    @@ -1242,18 +1258,18 @@ view.  This directive is used at the element level and takes one attribute, ​draw​, which is an  Angular expression which will should evaluate to a drawing object. This drawing object should  contain the following properties:  - ● dimensions​: The size, in logical coordinates, of the chart area. A two­element  + * dimensions​: The size, in logical coordinates, of the chart area. A two­element  array or numbers.  - ● origin​: The position, in logical coordinates, of the lower­left corner of the chart  + * origin​: The position, in logical coordinates, of the lower­left corner of the chart  area. A two­element array or numbers.  - ● lines​: An array of lines (e.g. as a plot line) to draw, where each line is  + * lines​: An array of lines (e.g. as a plot line) to draw, where each line is  expressed as an object containing:  ○ buffer​: A Float32Array containing points in the line, in logical  coordinates, in sequential x,y pairs.  ○ color​: The color of the line, as a four­element RGBA array, where each  element is a number in the range of 0.0­1.0.  ○ points​: The number of points in the line.  - ● boxes​: An array of rectangles to draw in the chart area. Each is an object  + * boxes​: An array of rectangles to draw in the chart area. Each is an object  containing:  ○ start​: The first corner of the rectangle, as a two­element array of  numbers, in logical coordinates.  @@ -1289,11 +1305,11 @@ delegate to other ​mct­control​ instances, and also facilitates usa This directive supports the following additional attributes, all specified as Angular  expressions:    - ● key​: A machine­readable identifier for the specific type of control to display.  - ● options​: A set of options to display in this control.  - ● structure​: In practice, contains the definition object which describes this form row or  + * key​: A machine­readable identifier for the specific type of control to display.  + * options​: A set of options to display in this control.  + * structure​: In practice, contains the definition object which describes this form row or  toolbar item. Used to pass additional control­specific parameters.  - ● field​: The field in the ​ngModel​ under which to read/store the property associated with  + * field​: The field in the ​ngModel​ under which to read/store the property associated with  this control.    Drag @@ -1304,9 +1320,9 @@ Note that this is not “drag” in the “drag­and­drop” sense, b down, mouse move, mouse up” sense.  This takes the form of three attributes:    - ● mct­drag​: An Angular expression to evaluate during drag movement.  - ● mct­drag­down​: An Angular expression to evaluate when the drag starts.  - ● mct­drag­up​: An Angular expression to evaluate when the drag ends.  + * mct­drag​: An Angular expression to evaluate during drag movement.  + * mct­drag­down​: An Angular expression to evaluate when the drag starts.  + * mct­drag­up​: An Angular expression to evaluate when the drag ends.    In each case, a variable ​delta​ will be provided to the expression; this is a two­element  array or the horizontal and vertical pixel offset of the current mouse position relative to the  @@ -1317,12 +1333,12 @@ Form The ​mct­form​ directive is used to generate forms using a declarative structure, and to  gather back user input. It is applicable at the element level and supports the following attributes:    - ● ng­model​: The object which should contain the full form input. Individual fields in this  + * ng­model​: The object which should contain the full form input. Individual fields in this  model are bound to individual controls; the names used for these fields are provided in  the form structure (see below).  - ● structure​: The structure of the form; e.g. sections, rows, their names, and so forth.  + * structure​: The structure of the form; e.g. sections, rows, their names, and so forth.  The value of this attribute should be an Angular expression.  - ● name​: The name in the containing scope under which to publish form "meta­state", e.g.  + * name​: The name in the containing scope under which to publish form "meta­state", e.g.  $valid​, ​$dirty​, etc. This is as the behavior of ​ng­form​. Passed as plain text in the  attribute.    @@ -1371,12 +1387,12 @@ Form Controls   A few standard control types are included in the ​platform/forms​ bundle:    - ● textfield​: An area to enter plain text.  - ● select​: A drop­down list of options.  - ● checkbox​: A box which may be checked/unchecked.  - ● color​: A color picker.  - ● button​: A button.  - ● datetime​: An input for UTC date/time entry; gives result as a UNIX timestamp, in  + * textfield​: An area to enter plain text.  + * select​: A drop­down list of options.  + * checkbox​: A box which may be checked/unchecked.  + * color​: A color picker.  + * button​: A button.  + * datetime​: An input for UTC date/time entry; gives result as a UNIX timestamp, in  milliseconds since start of 1970, UTC.    Include @@ -1387,11 +1403,11 @@ will have an isolated scope.  The directive should be used at the element level and supports the following attributes,  all of which are specified as Angular expressions:    - ● key​: Machine­readable identifier for the template (of extension category ​templates​) to  + * key​: Machine­readable identifier for the template (of extension category ​templates​) to  be displayed.  - ● ng­model​: Optional; will be passed into the template’s scope as ​ngModel​. Intended  + * ng­model​: Optional; will be passed into the template’s scope as ​ngModel​. Intended  usage is for two­way bound user input.  - ● parameters​: Optional; will be passed into the template’s scope as ​parameters​.  + * parameters​: Optional; will be passed into the template’s scope as ​parameters​.  Intended usage is for template­specific display parameters.    @@ -1404,12 +1420,12 @@ represent domain objects. Usage is similar to ​mct­include​.  The directive should be used at the element level and supports the following attributes,  all of which are specified as Angular expressions:    - ● key​: Machine­readable identifier for the representation (of extension category  + * key​: Machine­readable identifier for the representation (of extension category  representations​ or ​views​) to be displayed.  - ● mct­object​: The domain object being represented.  - ● ng­model​: Optional; will be passed into the template’s scope as ​ngModel​. Intended  + * mct­object​: The domain object being represented.  + * ng­model​: Optional; will be passed into the template’s scope as ​ngModel​. Intended  usage is for two­way bound user input.  - ● parameters​: Optional; will be passed into the template’s scope as ​parameters​.  + * parameters​: Optional; will be passed into the template’s scope as ​parameters​.  Intended usage is for template­specific display parameters.    Resize @@ -1440,12 +1456,12 @@ Toolbar and to gather back user input. It is applicable at the element level and supports the following  attributes:    - ● ng­model​: The object which should contain the full toolbar input. Individual fields in this  + * ng­model​: The object which should contain the full toolbar input. Individual fields in this  model are bound to individual controls; the names used for these fields are provided in  the form structure (see below).  - ● structure​: The structure of the toolbar; e.g. sections, rows, their names, and so forth.  + * structure​: The structure of the toolbar; e.g. sections, rows, their names, and so forth.  The value of this attribute should be an Angular expression.  - ● name​: The name in the containing scope under which to publish form "meta­state", e.g.  + * name​: The name in the containing scope under which to publish form "meta­state", e.g.  $valid​, ​$dirty​, etc. This is as the behavior of ​ng­form​. Passed as plain text in the  attribute.    @@ -1484,18 +1500,18 @@ items​.          },          ... and other sections ...      ]  -}  -  -Services -  - The Open MCT Web platform provides a variety of services which can be retrieved and  +} + +# Services + +The Open MCT Web platform provides a variety of services which can be retrieved and  utilized via dependency injection. These services fall into two categories:    - ● Composite Services are defined by a set of ​components​ extensions; plugins may  + * Composite Services are defined by a set of ​components​ extensions; plugins may  introduce additional components with matching interfaces to extend or augment the  functionality of the composed service. (See the Framework section on Composite  Services.)  - ● Other services which are defined as standalone service objects; these can be utilized by  + * Other services which are defined as standalone service objects; these can be utilized by  plugins but are not intended to be modified or augmented.    Composite Services @@ -1524,7 +1540,7 @@ Action Service contexts. See Core API for additional notes on the interface for actions.  The ​actionService​ has the following interface:    - ● getActions(context)​: Returns an array of ​Action​ objects which are applicable in  + * getActions(context)​: Returns an array of ​Action​ objects which are applicable in  the specified action context.        @@ -1534,7 +1550,7 @@ Capability Service for a given domain object.  The ​capabilityService​ has the following interface:    - ● getCapabilities(model)​: Returns a an object containing key­value pairs,  + * getCapabilities(model)​: Returns a an object containing key­value pairs,  representing capabilities which should be exposed by the domain object with this model.  Keys in this object are the capability keys (as used in a ​getCapability(...)​ call)  and values are either:  @@ -1552,12 +1568,12 @@ Dialog Service The ​dialogService​ provides a means for requesting user input via a modal dialog. It  has the following interface:    - ● getUserInput(formStructure, formState)​: Prompt the user to fill out a form.  + * getUserInput(formStructure, formState)​: Prompt the user to fill out a form.  The first argument describes the form’s structure (as will be passed to ​mct­form​) while  the second argument contains the initial state of that form. This returns a ​Promise​ for  the state of the form after the user has filled it in; this promise will be rejected if the user  cancels input.  - ● getUserChoice(dialogStructure)​: Prompt the user to make a single choice from  + * getUserChoice(dialogStructure)​: Prompt the user to make a single choice from  a set of options, which (in the platform implementation) will be expressed as buttons in  the displayed dialog. Returns a ​Promise​ for the user’s choice, which will be rejected if  the user cancels input.  @@ -1568,13 +1584,13 @@ Dialog Structure The object passed as the ​dialogStructure​ to ​getUserChoice​ should have the  following properties:    - ● title​: The title to display at the top of the dialog.  - ● hint​: Short message to display below the title.  - ● template​: Identifying ​key​ (as will be passed to ​mct­include​) for the template which  + * title​: The title to display at the top of the dialog.  + * hint​: Short message to display below the title.  + * template​: Identifying ​key​ (as will be passed to ​mct­include​) for the template which  will be used to populate the inner area of the dialog.  - ● model​: Model to pass in the ​ng­model​ attribute of ​mct­include​.  - ● parameters​: Parameters to pass in the ​parameters​ attribute of ​mct­include​.  - ● options​: An array of options describing each button at the bottom. Each option may  + * model​: Model to pass in the ​ng­model​ attribute of ​mct­include​.  + * parameters​: Parameters to pass in the ​parameters​ attribute of ​mct­include​.  + * options​: An array of options describing each button at the bottom. Each option may  have the following properties:  ○ name​: Human­readable name to display in the button.  ○ key​: Machine­readable key, to pass as the result of the resolved promise when  @@ -1586,7 +1602,7 @@ Domain Object Service   The ​objectService​ provides domain object instances. It has the following interface:    - ● getObjects(ids)​: For the provided array of domain object identifiers, returns a  + * getObjects(ids)​: For the provided array of domain object identifiers, returns a  Promise​ for an object containing key­value pairs, where keys are domain object  identifiers and values are corresponding ​DomainObject​ instances. Note that the result  may contain a superset or subset of the objects requested.  @@ -1597,7 +1613,7 @@ Gesture Service The ​gestureService​ is used to attach gestures (see extension category ​gestures​)  to representations. It has the following interface:    - ● attachGestures(element, domainObject, keys)​: Attach gestures specified  + * attachGestures(element, domainObject, keys)​: Attach gestures specified  by the provided gesture ​keys​ (an array of strings) to this jqLite­wrapped HTML  element​, which represents the specified ​domainObject​. Returns an object with a  single method ​destroy()​, to be invoked when it is time to detach these gestures.  @@ -1608,7 +1624,7 @@ Model Service   The ​modelService​ provides domain object models. It has the following interface:    - ● getModels(ids)​: For the provided array of domain object identifiers, returns a  + * getModels(ids)​: For the provided array of domain object identifiers, returns a  Promise​ for an object containing key­value pairs, where keys are domain object  identifiers and values are corresponding domain object models. Note that the result may  contain a superset or subset of the models requested.  @@ -1620,21 +1636,21 @@ Persistence Service (presumably serializing/deserializing to JSON in the process.) This is used primarily to store  domain object models. It has the following interface:    - ● listSpaces()​: Returns a ​Promise​ for an array of strings identifying the different  + * listSpaces()​: Returns a ​Promise​ for an array of strings identifying the different  persistence spaces this service supports. Spaces are intended to be used to distinguish  between different underlying persistence stores, to allow these to live side by side.  - ● listObjects()​: Returns a Promise for an array of strings identifying all documents  + * listObjects()​: Returns a Promise for an array of strings identifying all documents  stored in this persistence service.  - ● createObject(space, key, value)​: Create a new document in the specified  + * createObject(space, key, value)​: Create a new document in the specified  persistence ​space​, identified by the specified ​key​, the contents of which shall match  the specified ​value​. Returns a promise that will be rejected if creation fails.  - ● readObject(space, key)​: Read an existing document in the specified persistence  + * readObject(space, key)​: Read an existing document in the specified persistence  space​, identified by the specified ​key​. Returns a promise for the specified document;  this promise will resolve to ​undefined​ if the document does not exist.  - ● updateObject(space, key, value)​: Update an existing document in the  + * updateObject(space, key, value)​: Update an existing document in the  specified persistence ​space​, identified by the specified ​key​, such that its contents  match the specified ​value​. Returns a promise that will be rejected if the update fails.  - ● deleteObject(space, key)​: Delete an existing document from the specified  + * deleteObject(space, key)​: Delete an existing document from the specified  persistence ​space​, identified by the specified ​key​. Returns a promise which will be  rejected if deletion fails.    @@ -1647,7 +1663,7 @@ Policy Service The ​policyService​ may be used to determine whether or not certain behaviors are  allowed within the application. It has the following interface:    - ● allow(category, candidate, context, [callback])​: Check if this decision  + * allow(category, candidate, context, [callback])​: Check if this decision  should be allowed. Returns a boolean. Its arguments are interpreted as:  ○ category​: A string identifying which kind of decision is being made. See the  section on Policies for categories supported by the platform; plugins may define  @@ -1676,9 +1692,9 @@ subscribing to and requesting telemetry data associated with domain obj domain objects. See the Other Services section for more information.  The ​telemetryService​ has the following interface:    - ● requestTelemetry(requests)​: Issue a request for telemetry, matching the  + * requestTelemetry(requests)​: Issue a request for telemetry, matching the  specified telemetry ​requests​. Returns a ​Promise​ for a telemetry response object.   - ● subscribe(callback, requests)​: Subscribe to real­time updates for telemetry,  + * subscribe(callback, requests)​: Subscribe to real­time updates for telemetry,  matching the specified ​requests​. The specified ​callback​ will be invoked with  telemetry response objects as they become available. This method returns a function  which can be invoked to terminate the subscription.  @@ -1688,9 +1704,9 @@ Type Service   The ​typeService​ exposes domain object types. It has the following interface:    - ● listTypes()​: Returns all domain object types supported in the application, as an  + * listTypes()​: Returns all domain object types supported in the application, as an  array of ​Type​ instances.  - ● getType(key)​: Returns the ​Type​ instance identified by the provided key, or  + * getType(key)​: Returns the ​Type​ instance identified by the provided key, or  undefined​ if no such type exists.    View Service @@ -1698,7 +1714,7 @@ View Service The ​viewService​ exposes definitions for views of domain objects. It has the following  interface:    - ● getViews(domainObject):​ Get an array of extension definitions of category ​views  + * getViews(domainObject):​ Get an array of extension definitions of category ​views  which are valid and applicable to the specified ​domainObject​.    Other Services @@ -1712,11 +1728,11 @@ as by permitting inspection during drag (which is normally prohibited  reasons.)  The ​dndService​ has the following methods:    - ● setData(key, value)​: Set drag data associated with a given type, specified by the  + * setData(key, value)​: Set drag data associated with a given type, specified by the  key​ argument.  - ● getData(key)​: Get drag data associated with a given type, specified by the ​key  + * getData(key)​: Get drag data associated with a given type, specified by the ​key  argument.  - ● removeData(key)​: Clear drag data associated with a given type, specified by the ​key  + * removeData(key)​: Clear drag data associated with a given type, specified by the ​key  argument.    @@ -1730,13 +1746,13 @@ state and notifies listeners; it does not take immediate action when  although its listeners might.  The ​navigationService​ has the following methods:    - ● getNavigation()​: Get the current navigation state. Returns a ​DomainObject​.  - ● setNavigation(domainObject)​: Set the current navigation state. Returns a  + * getNavigation()​: Get the current navigation state. Returns a ​DomainObject​.  + * setNavigation(domainObject)​: Set the current navigation state. Returns a  DomainObject​.  - ● addListener(callback)​: Listen for changes in navigation state. The provided  + * addListener(callback)​: Listen for changes in navigation state. The provided  callback​ should be a ​Function​ which takes a single ​DomainObject​ as an  argument.  - ● removeListener(callback)​: Stop listening for changes in navigation state. The  + * removeListener(callback)​: Stop listening for changes in navigation state. The  provided ​callback​ should be a ​Function​ which has previously been passed to  addListener​.    @@ -1752,9 +1768,9 @@ Telemetry Formatter from a telemetry series.  The ​telemetryFormatter​ has the following methods:    - ● formatDomainValue(value)​: Format the provided domain value (which will be  + * formatDomainValue(value)​: Format the provided domain value (which will be  assumed to be a timestamp) for display; returns a string.  - ● formatRangeValue(value)​: Format the provided range value (a number) for  + * formatRangeValue(value)​: Format the provided range value (a number) for  display; returns a string.    @@ -1767,7 +1783,7 @@ domain objects; it is particularly useful for dealing with cases where is delegated to contained objects (as occurs in Telemetry Panels.)  The ​telemetryHandler​ has the following methods:    - ● handle(domainObject, callback, [lossless])​: Subscribe to and issue  + * handle(domainObject, callback, [lossless])​: Subscribe to and issue  future requests for telemetry associated with the provided ​domainObject​, invoking the  provided ​callback​ function when streaming data becomes available. Returns a  TelemetryHandle​ (see below.)  @@ -1776,27 +1792,27 @@ Telemetry Handle   A ​TelemetryHandle​ has the following methods:    - ● getTelemetryObjects()​: Get the domain objects (as a ​DomainObject[]​) that  + * getTelemetryObjects()​: Get the domain objects (as a ​DomainObject[]​) that  have a ​telemetry​ capability and are being handled here. Note that these are looked  up asynchronously, so this method may return an empty array if the initial lookup is not  yet completed.  - ● promiseTelemetryObjects()​: As ​getTelemetryObjects()​, but returns a  + * promiseTelemetryObjects()​: As ​getTelemetryObjects()​, but returns a  Promise​ that will be fulfilled when the lookup is complete.  - ● unsubscribe()​: Unsubscribe to streaming telemetry updates associated with this  + * unsubscribe()​: Unsubscribe to streaming telemetry updates associated with this  handle.  - ● getDomainValue(domainObject)​: Get the most recent domain value received via a  + * getDomainValue(domainObject)​: Get the most recent domain value received via a  streaming update for the specified ​domainObject​.  - ● getRangeValue(domainObject)​: Get the most recent range value received via a  + * getRangeValue(domainObject)​: Get the most recent range value received via a  streaming update for the specified ​domainObject​.  - ● getMetadata()​: Get metadata (as reported by the ​getMetadata()​ method of a  + * getMetadata()​: Get metadata (as reported by the ​getMetadata()​ method of a  telemetry​ capability) associated with telemetry­providing domain objects. Returns an  array, which is in the same order as ​getTelemetryObjects()​.  - ● request(request, callback)​: Issue a new ​request​ for historical telemetry data.  + * request(request, callback)​: Issue a new ​request​ for historical telemetry data.  The provided ​callback​ will be invoked when new data becomes available, which may  occur multiple times (e.g. if there are multiple domain objects.) It will be invoked with the  DomainObject​ for which a new series is available, and the ​TelemetrySeries​ itself,  in that order.  - ● getSeries(domainObject)​: Get the latest ​TelemetrySeries​ (as resulted from a  + * getSeries(domainObject)​: Get the latest ​TelemetrySeries​ (as resulted from a  previous ​request(...)​ call) available for this domain object.    52  @@ -1812,7 +1828,7 @@ General Metadata Some properties of domain object models have a ubiquitous meaning through Open  MCT Web and can be utilized directly:    - ● name​: The human­readable name of the domain object.  + * name​: The human­readable name of the domain object.    Extension-specific Properties   @@ -1824,25 +1840,25 @@ Capability-specific Properties Some properties either trigger the presence/absence of certain capabilities, or are  managed by specific capabilities:    - ● composition​: An array of domain object identifiers that represents the contents of this  + * composition​: An array of domain object identifiers that represents the contents of this  domain object (e.g. as will appear in the tree hierarchy.) Understood by the  composition​ capability; the presence or absence of this property determines the  presence or absence of that capability.  - ● modified​: The timestamp (in milliseconds since the UNIX epoch) of the last  + * modified​: The timestamp (in milliseconds since the UNIX epoch) of the last  modification made to this domain object. Managed by the ​mutation​ capability.  - ● persisted​: The timestamp (in milliseconds since the UNIX epoch) of the last time  + * persisted​: The timestamp (in milliseconds since the UNIX epoch) of the last time  when changes to this domain object were persisted. Managed by the ​persistence  capability.  - ● relationships​: An object containing key­value pairs, where keys are symbolic  + * relationships​: An object containing key­value pairs, where keys are symbolic  identifiers for relationship types, and values are arrays of domain object identifiers. Used  by the ​relationship​ capability; the presence or absence of this property determines  the presence or absence of that capability.  - ● telemetry​: An object which serves as a template for telemetry requests associated  + * telemetry​: An object which serves as a template for telemetry requests associated  with this domain object (e.g. specifying ​source​ and ​key​; see Telemetry Requests  53  under Core API.) Used by the ​telemetry​ capability; the presence or absence of this  property determines the presence or absence of that capability.  - ● type​: A string identifying the type of this domain object. Used by the ​type​ capability.  + * type​: A string identifying the type of this domain object. Used by the ​type​ capability.    View Configurations   @@ -1888,11 +1904,11 @@ defined.    This capability has the following interface:    - ● getActions(context)​: Get the actions that are applicable in the specified action  + * getActions(context)​: Get the actions that are applicable in the specified action  context​; the capability will fill in the ​domainObject​ field of this context if necessary. If  context​ is specified as a string, they will instead be used as the ​key​ of the action  context. Returns an array of ​Action​ instances.  - ● perform(context)​: Perform an action. This will find and perform the first matching  + * perform(context)​: Perform an action. This will find and perform the first matching  action available for the specified action ​context​, filling in the ​domainObject​ field as  necessary. If ​context​ is specified as a string, they will instead be used as the ​key​ of  the action context. Returns a ​Promise​ for the result of the action that was performed, or  @@ -1908,7 +1924,7 @@ corresponding ​DomainObject​ instances in the same order. The absenc model will result in the absence of this capability in the domain object.  This capability has the following interface:    - ● invoke()​: Returns a ​Promise​ for an array of ​DomainObject​ instances.  + * invoke()​: Returns a ​Promise​ for an array of ​DomainObject​ instances.  Delegation   @@ -1917,10 +1933,10 @@ delegate responsibilities, which would normally handled by other capabil objects in its composition.  This capability has the following interface:    - ● getDelegates(key)​: Returns a ​Promise​ for an array of ​DomainObject​ instances,  + * getDelegates(key)​: Returns a ​Promise​ for an array of ​DomainObject​ instances,  to which this domain object wishes to delegate the capability with the specified ​key​.  - ● invoke(key)​: Alias of ​getDelegates(key)​.  - ● doesDelegate(key)​: Returns ​true​ if the domain object does delegate the capability  + * invoke(key)​: Alias of ​getDelegates(key)​.  + * doesDelegate(key)​: Returns ​true​ if the domain object does delegate the capability  with the specified ​key​.     The platform implementation of the ​delegation​ capability inspects the domain object’s  @@ -1946,11 +1962,11 @@ Mutation model can be modified. This capability is provided by the platform for all domain objects, and  has the following interface:    - ● mutate(mutator, [timestamp])​: Modify the domain object’s model using the  + * mutate(mutator, [timestamp])​: Modify the domain object’s model using the  specified ​mutator​ function. After changes are made, the ​modified​ property of the  model will be updated with the specified ​timestamp​, if one was provided, or with the  current system time.  - ● invoke(...)​: Alias of ​mutate​.  + * invoke(...)​: Alias of ​mutate​.    Changes to domain object models should only be made via the ​mutation​ capability;  other platform behavior is likely to break (either by exhibiting undesired behavior, or failing to  @@ -1961,13 +1977,13 @@ Mutator Function The ​mutator​ argument above is a function which will receive a cloned copy of the  domain object’s model as a single argument. It may return:    - ● A ​Promise​, in which case the resolved value of the promise will be used to determine  + * A ​Promise​, in which case the resolved value of the promise will be used to determine  which of the following forms is used.  - ● Boolean ​false​, in which case the mutation is cancelled.  - ● A JavaScript object, in which case this object will be used as the new model for this  + * Boolean ​false​, in which case the mutation is cancelled.  + * A JavaScript object, in which case this object will be used as the new model for this  domain object.  57  - ● No value (or, equivalently, ​undefined​), in which case the cloned copy (including any  + * No value (or, equivalently, ​undefined​), in which case the cloned copy (including any  changes made in place by the mutator function) will be used as the new domain object  model.    @@ -1976,12 +1992,12 @@ Persistence The ​persistence​ capability provides a mean for interacting with the underlying  persistence service which stores this domain object’s model. It has the following interface:    - ● persist()​: Store the local version of this domain object, including any changes, to the  + * persist()​: Store the local version of this domain object, including any changes, to the  persistence store. Returns a ​Promise​ for a boolean value, which will be true when the  object was successfully persisted.  - ● refresh()​: Replace this domain object’s model with the most recent version from  + * refresh()​: Replace this domain object’s model with the most recent version from  persistence. Returns a ​Promise​ which will resolve when the change has completed.  - ● getSpace()​: Return the string which identifies the persistence space which stores this  + * getSpace()​: Return the string which identifies the persistence space which stores this  domain object.    Relationship @@ -1989,9 +2005,9 @@ Relationship The ​relationship​ capability provides a means for accessing other domain objects  with which this domain object has some typed relationship. It has the following interface:    - ● listRelationships()​: List all types of relationships exposed by this object. Returns  + * listRelationships()​: List all types of relationships exposed by this object. Returns  an array of strings identifying the types of relationships.  - ● getRelatedObjects(relationship)​: Get all domain objects to which this domain  + * getRelatedObjects(relationship)​: Get all domain objects to which this domain  object has the specified type of ​relationship​, which is a string identifier (as above.)  Returns a ​Promise​ for an array of ​DomainObject​ instances.    @@ -2006,17 +2022,17 @@ Telemetry The ​telemetry​ capability provides a means for accessing telemetry data associated  with a domain object. It has the following interface:    - ● requestData([request])​: Request telemetry data for this specific domain object,  + * requestData([request])​: Request telemetry data for this specific domain object,  using telemetry request parameters from the specified ​request​ if provided. This  capability will fill in telemetry request properties as­needed for this domain object.  Returns a ​Promise​ for a ​TelemetrySeries​.  - ● subscribe(callback, [request])​:  Subscribe to telemetry data updates for this  + * subscribe(callback, [request])​:  Subscribe to telemetry data updates for this  specific domain object, using telemetry request parameters from the specified ​request  if provided. This capability will fill in telemetry request properties as­needed for this  domain object. The specified ​callback​ will be invoked with ​TelemetrySeries  instances as they arrive. Returns a function which can be invoked to terminate the  subscription, or ​undefined​ if no subscription could be obtained.  - ● getMetadata()​: Get metadata associated with this domain object’s telemetry.  + * getMetadata()​: Get metadata associated with this domain object’s telemetry.    The platform implementation of the ​telemetry​ capability is present for domain objects  which has a ​telemetry​ property in their model and/or type definition; this object will serve as a  @@ -2033,7 +2049,7 @@ View The ​view​ capability exposes views which are applicable to a given domain object. It has  the following interface:    - ● invoke()​: Returns an array of extension definitions for views which are applicable for  + * invoke()​: Returns an array of extension definitions for views which are applicable for  this domain object.  59  Actions @@ -2045,8 +2061,8 @@ Action Categories The platform understands the following action categories (specifiable as the ​category  parameter of an action’s extension definition.)    - ● contextual​: Appears in context menus.  - ● view­control​: Appears in top­right area of view (as buttons) in Browse mode  + * contextual​: Appears in context menus.  + * view­control​: Appears in top­right area of view (as buttons) in Browse mode    Platform Actions   @@ -2054,19 +2070,19 @@ Platform Actions action​ capability. Unless otherwise specified, these act upon (and modify) the object  described by the ​domainObject​ property of the action’s context.    - ● cancel​: Cancel the current editing action (invoked from Edit mode.)  - ● compose​: Place an object in another object’s composition. The object to be added  + * cancel​: Cancel the current editing action (invoked from Edit mode.)  + * compose​: Place an object in another object’s composition. The object to be added  should be provided as the ​selectedObject​ of the action context.  - ● edit​: Start editing an object (enter Edit mode.)  - ● fullscreen​: Enter full screen mode.  - ● navigate​: Make this object the focus of navigation (e.g. highlight it within the tree,  + * edit​: Start editing an object (enter Edit mode.)  + * fullscreen​: Enter full screen mode.  + * navigate​: Make this object the focus of navigation (e.g. highlight it within the tree,  display a view of it to the right.)  - ● properties​: Show the “Edit Properties” dialog.  - ● remove​: Remove this domain object from its parent’s composition. (The parent, in this  + * properties​: Show the “Edit Properties” dialog.  + * remove​: Remove this domain object from its parent’s composition. (The parent, in this  case, is whichever other domain object exposed this object by way of its ​composition  capability.)  - ● save​: Save changes (invoked from Edit mode.)  - ● window​: Open this object in a new window.  + * save​: Save changes (invoked from Edit mode.)  + * window​: Open this object in a new window.    @@ -2085,12 +2101,12 @@ Policy Categories The platform understands the following policy categories (specifiable as the ​category  parameter of an policy’s extension definition.)    - ● action​: Determines whether or not a given action is allowable. The candidate  + * action​: Determines whether or not a given action is allowable. The candidate  argument here is an ​Action​; the context is its action context object.  - ● composition​: Determines whether or not domain objects of a given type are allowed  + * composition​: Determines whether or not domain objects of a given type are allowed  to contain domain objects of another type. The candidate argument here is the  container’s ​Type​; the context argument is the ​Type​ of the object to be contained.  - ● view​: Determines whether or not a view is applicable for a domain object. The  + * view​: Determines whether or not a view is applicable for a domain object. The  candidate argument is the view’s extension definition; the context argument is the  DomainObject​ to be viewed.    @@ -2112,10 +2128,10 @@ Command-line Build   Invoking ​mvn clean install​ will:    - ● Check code style using JSLint. The build will fail if JSLint raises any warnings.  - ● Run the test suite (see below.) The build will fail if any tests fail.  - ● Populate version info (e.g. commit hash, build time.)  - ● Produce a web archive (​.war​) artifact in the ​target​ directory.  + * Check code style using JSLint. The build will fail if JSLint raises any warnings.  + * Run the test suite (see below.) The build will fail if any tests fail.  + * Populate version info (e.g. commit hash, build time.)  + * Produce a web archive (​.war​) artifact in the ​target​ directory.    The produced artifact contains a subset of the repository’s own folder hierarchy, omitting  tests and example bundles.   @@ -2129,25 +2145,25 @@ test.html​, included at the top level of the source repository, can perform tests for all active bundles, as defined in ​bundle.json​.  To define tests for a bundle:    - ● Include a directory named ​test​ within that bundle.  - ● In the ​test​ directory, include a file named ​suite.json​. This will identify which scripts  + * Include a directory named ​test​ within that bundle.  + * In the ​test​ directory, include a file named ​suite.json​. This will identify which scripts  will be tested.  - ● The file ​suite.json​ must contain a JSON array of strings, where each string is the  + * The file ​suite.json​ must contain a JSON array of strings, where each string is the  name of a script to be tested. These names should include any directory paths to the  script after (but not including) the ​src​ folder, and should not include the file’s ​.js  extension. (Note that while Open MCT Web’s framework allows a different name to be  chosen for the ​src​ directory, the test runner does not: This directory must be named  src​ for the test runner to find it.)  62  - ● For each script to be tested, a corresponding test script should be located in the bundle’s  + * For each script to be tested, a corresponding test script should be located in the bundle’s  test​ directory. This should include the suffix ​Spec​ at the end of the filename (but  before the ​.js​ extension.) This test script should be an AMD module which uses the  Jasmine API to declare its test behavior. It should declare an AMD dependency on the  script to be tested, using a relative path.    For example, if writing tests for a bundle at ​example/foo​ with two scripts:  - ● example/foo/src/controllers/FooController.js  - ● example/foo/src/directives/FooDirective.js  + * example/foo/src/controllers/FooController.js  + * example/foo/src/directives/FooDirective.js    First, these scripts should be identified in ​example/foo/test/suite.json​, e.g. with  contents:  @@ -2199,9 +2215,9 @@ manner in which they are exposed) that determine how to deploy Open  One important constraint to consider in this context is the browser’s same origin policy. If  external services are not on the same apparent host and port as the client (from the perspective  of the browser) then access may be disallowed. There are two workarounds if this occurs:  - ● Make the external service appear to be on the same host/port, either by actually  + * Make the external service appear to be on the same host/port, either by actually  deploying it there, or by proxying requests to it.  - ● Enable CORS (cross­origin resource sharing) on the external service. This is only  + * Enable CORS (cross­origin resource sharing) on the external service. This is only  possible if the external service can be configured to support CORS. Care should be  exercised if choosing this option to ensure that the chosen configuration does not create  a security vulnerability.  @@ -2209,28 +2225,28 @@ of the browser) then access may be disallowed. There are two workarou Examples of deployment strategies (and the conditions under which they make the most  sense) include:    - ● If the external services that Open MCT Web will utilize are all running on Apache Tomcat  + * If the external services that Open MCT Web will utilize are all running on Apache Tomcat  (​https://tomcat.apache.org/​), then it makes sense to run Open MCT Web from the same  Tomcat instance as a separate web application. The ​.war​ artifact produced by the  command line build facilitates this deployment option. (See  https://tomcat.apache.org/tomcat­8.0­doc/deployer­howto.html ​ for general information on  deploying in Tomcat.)  - ● If a variety of external services will be running from a variety of hosts/ports, then it may  + * If a variety of external services will be running from a variety of hosts/ports, then it may  make sense to use a web server that supports proxying, such as the Apache HTTP  Server (​http://httpd.apache.org/​). In this configuration, the HTTP server would be  configured to proxy (or reverse proxy) requests at specific paths to the various external  services, while providing Open MCT Web as flat files from a different path.  64  - ● If a single server component is being developed to handle all server­side needs of an  + * If a single server component is being developed to handle all server­side needs of an  Open MCT Web instance, it can make sense to serve Open MCT Web (as flat files) from  the same component using an embedded HTTP server such as Nancy  (​http://nancyfx.org/​).  - ● If no external services are needed (or if the “external services” will just be generating flat  + * If no external services are needed (or if the “external services” will just be generating flat  files to read) it makes sense to utilize a lightweight flat file HTTP server such as Lighttpd  (​http://www.lighttpd.net/​). In this configuration, Open MCT Web sources/resources would  be placed at one path, while the files generated by the external service are placed at  another path.  - ● If all external services support CORS, it may make sense to have an HTTP server that is  + * If all external services support CORS, it may make sense to have an HTTP server that is  solely responsible for making Open MCT Web sources/resources available, and to have  Open MCT Web contact these external services directly. Again, lightweight HTTP  servers such as Lighttpd (​http://www.lighttpd.net/​) are useful in this circumstance. The  @@ -2258,25 +2274,25 @@ specifying constants with higher priority.    This permits at least three configuration approaches:    - ● Modify the constants defined in their original bundles when deploying. This is generally  + * Modify the constants defined in their original bundles when deploying. This is generally  undesirable due to the amount of manual work required and potential for error, but is  viable if there are a small number of constants to change.  - ● Add a separate configuration bundle which overrides the values of these constants. This  + * Add a separate configuration bundle which overrides the values of these constants. This  is particularly appropriate when multiple configurations (e.g. development, test,  65  production) need to be managed easily; these can be swapped quickly by changing the  set of active bundles in ​bundles.json​.  - ● Deploy Open MCT Web and its external services in such a fashion that the default paths  + * Deploy Open MCT Web and its external services in such a fashion that the default paths  to reach external services are all correct.    Configuration Constants   The following configuration constants are recognized by Open MCT Web bundles:    - ● CouchDB adapter, ​platform/persistence/coucb  + * CouchDB adapter, ​platform/persistence/coucb  ○ COUCHDB_PATH​: URL or path to the CouchDB database to be used for domain  object persistence. Should not include a trailing slash.  - ● ElasticSearch adapter, ​platform/persistence/elastic  + * ElasticSearch adapter, ​platform/persistence/elastic  ○ ELASTIC_ROOT​: URL or path to the ElasticSearch instance to be used for  domain object persistence. Should not include a trailing slash.  ○ ELASTIC_PATH​: Path relative to the ElasticSearch instance where domain  From b7a612127d25cd2018cd6a09c490cd8f0d538e00 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Fri, 25 Sep 2015 09:09:34 -0700 Subject: [PATCH 006/488] Added additional sections, up to Templates --- docs/src/guide/index.md | 491 +++++++++++++++++++++------------------- 1 file changed, 255 insertions(+), 236 deletions(-) diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 25b18575b8..7774adffb1 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -835,9 +835,9 @@ initiated by the user.  An action’s implementation: * Should take a single `​context​` argument in its constructor. (See Action  -Contexts, under Core API.)  +Contexts, under Core API.) * Should provide a method ​`perform​`, which causes the behavior associated with  -the action to occur.  +the action to occur. * May provide a method `​getMetadata​`, which provides metadata associated with  the action. If omitted, one will be provided by the platform which includes  metadata from the action’s extension definition. @@ -846,259 +846,278 @@ available as a property of the implementation’s constructor itself),  be used by the platform to filter out actions from contexts in which they are  inherently inapplicable. -An action’s bundle definition (and/or `​getMetadata()`​ return value) may include:  +An action’s bundle definition (and/or `​getMetadata()`​ return value) may include: +  * `category​`: A string or dearray of strings identifying which category or  categories an action falls into; used to determine when an action is displayed.  Categories supported by the platform include:  * `contextual​`: Actions in a context menu.  - * `view­control​`: Actions triggered by buttons in the top­right of Browse view.  + * `view­control​`: Actions triggered by buttons in the top­right of Browse  + view.  * `key​`: A machine­readable identifier for this action.  * `name​`: A human­readable name for this action (e.g. to show in a menu)  * `description​`: A human­readable summary of the behavior of this action.  -* `glyph`​: A single character which will be rendered in Open MCT Web’s custom font  -set as an icon for this action.  - - 27  -Capabilities -  - Capabilities are exposed by domain objects (e.g. via the g​ etCapability​ method) but  -most commonly originate as extensions of this category.  -  - Extension definitions for capabilities should include both an implementation, and a  -property named ​key​ whose value should be a string used as a machine­readable identifier for  -that capability, e.g. when passed as the argument to a domain object’s ​getCapability(key)  -call.   -  - A capability’s implementation should have methods specific to that capability; that is,  -there is no common format for capability implementations, aside from support for ​invoke​ via  -the ​useCapability​ shorthand.  - A capability’s implementation will take a single argument (in addition to any declared  -dependencies), which is the domain object that will expose that capability.  - A capability’s implementation may also expose a static method ​appliesTo(model)  -which should return a boolean value, and will be used by the platform to filter down capabilities  -to those which should be exposed by specific domain objects, based on their domain object  -models.  -  -Controls -  - Controls provide options for the ​mct­control​ directive.  -  - Four standard control types are included in the forms bundle:  -   - * textfield​: An area to enter plain text.  - * select​: A drop­down list of options.  - * checkbox​: A box which may be checked/unchecked.  - * color​: A color picker.  - * button​: A button.  - * datetime​: An input for UTC date/time entry; gives result as a UNIX timestamp, in  - milliseconds since start of 1970, UTC.  -  - New controls may be added as extensions of the controls category. Extensions of this  -category have two properties:  -  - * key​: The symbolic name for this control (matched against the control field in rows of the  - form structure).  - * templateUrl​: The URL to the control's Angular template, relative to the resources  - directory of the bundle which exposes the extension.  - 28  -  -Within the template for a control, the following variables will be included in scope:  -  - * ngModel​: The model where form input will be stored. Notably we also need to look at  - field​ (see below) to determine which field in the model should be modified.  - * ngRequired​: True if input is required.  - * ngPattern​: The pattern to match against (for text entry.)  - * options​: The options for this control, as passed from the ​options​ property of an  - individual row definition.  - * field​: Name of the field in ​ngModel​ which will hold the value for this control.  -  -Gestures -  - A gesture is a user action which can be taken upon a representation of a domain object.  -Examples of gestures included in the platform are:  -   - * drag​: For representations that can be used to initiate drag­and­drop composition.  - * drop​: For representations that can be drop targets for drag­and­drop composition.  - * menu​: For representations that can be used to pop up a context menu.  -  - Gesture definitions have a property ​key​ which is used as a machine­readable identifier  -for the gesture (e.g. ​drag​, ​drop​, ​menu​ above.)  -  - A gesture’s implementation is instantiated once per representation that uses the gesture.  -This class will receive the jqLite­wrapped ​mct­representation​ element and the domain  -object being represented as arguments, and should do any necessary "wiring" (e.g. listening for  -events) during its constructor call. The gesture’s implementation may also expose an optional  -destroy()​ method which will be called when the gesture should be removed, to avoid  -memory leaks by way of unremoved listeners.  -  -Indicators -  - An indicator is an element that should appear in the status area at the bottom of a  -running Open MCT Web client instance.  -  +* `glyph`​: A single character which will be rendered in Open MCT Web’s custom  +font set as an icon for this action. - - 29  -Standard Indicators +## Capabilities + +Capabilities are exposed by domain objects (e.g. via the `g​etCapability​` method)  +but most commonly originate as extensions of this category. + +Extension definitions for capabilities should include both an implementation,  +and a property named ​key​ whose value should be a string used as a  +machine­readable identifier for that capability, e.g. when passed as the  +argument to a domain object’s `​getCapability(key)` call.   - Indicators which wish to appear in the common form of an icon­text pair should provide  -implementations with the following methods:  +A capability’s implementation should have methods specific to that capability;  +that is, there is no common format for capability implementations, aside from  +support for ​invoke​ via the ​useCapability​ shorthand. + +A capability’s implementation will take a single argument (in addition to any  +declared dependencies), which is the domain object that will expose that  +capability. + +A capability’s implementation may also expose a static method ​`appliesTo(model)`  +which should return a boolean value, and will be used by the platform to filter  +down capabilities to those which should be exposed by specific domain objects,  +based on their domain object models.    - * getText()​: Provides the human­readable text that will be displayed for this indicator.  - * getGlyph()​: Provides a single­character string that will be displayed as an icon in  - Open MCT Web’s custom font set.  - * getDescription()​: Provides a human­readable summary of the current state of this  - indicator; will be displayed in a tooltip on hover.  - * getClass()​: Get a CSS class that will be applied to this indicator.  - * getTextClass()​: Get a CSS class that will be applied to this indicator’s text portion.  - * getGlyphClass()​: Get a CSS class that will be applied to this indicator’s icon portion.  - * configure()​: If present, a configuration icon will appear to the right of this indicator,  - and clicking it will invoke this method.  +## Controls + +Controls provide options for the ​mct­control​ directive.    - Note that all methods are optional, and are called directly from an Angular template, so  -they should be appropriate to run during digest cycles.  +Six standard control types are included in the forms bundle: + +* `textfield​`: An area to enter plain text. +* `select`​: A drop­down list of options. +* `checkbox​`: A box which may be checked/unchecked. +* `color​`: A color picker. +* `button`​: A button. +* `datetime`​: An input for UTC date/time entry; gives result as a UNIX  +timestamp, in milliseconds since start of 1970, UTC.  + +New controls may be added as extensions of the controls category. Extensions of  +this category have two properties: + +* `key`​: The symbolic name for this control (matched against the control field  +in rows of the form structure). +* `templateUrl`​: The URL to the control's Angular template, relative to the  +resources directory of the bundle which exposes the extension.  + +Within the template for a control, the following variables will be included in  +scope: + +* `ngModel`​: The model where form input will be stored. Notably we also need to  +look at field​ (see below) to determine which field in the model should be  +modified.  +* `ngRequired​`: True if input is required. +* `ngPattern​`: The pattern to match against (for text entry.) +* `options​`: The options for this control, as passed from the `​options​` property  +of an individual row definition.  +* `field​`: Name of the field in ​`ngModel​` which will hold the value for this  +control.    -Custom Indicators +## Gestures + +A gesture is a user action which can be taken upon a representation of a domain  +object.  + +Examples of gestures included in the platform are: + +* `drag`​: For representations that can be used to initiate drag­and­drop  +composition. +* `drop​`: For representations that can be drop targets for drag­and­drop  +composition.  +* `menu`​: For representations that can be used to pop up a context menu.    - Indicators which wish to have an arbitrary appearance (instead of following the icon­text  -convention commonly used) may specify a ​template​ property in their extension definition. The  -value of this property will be used as the ​key​ for an ​mct­include​ directive (so should refer to  -an extension of category ​templates​.) This template will be rendered to the status area.  -Indicators of this variety do not need to provide an implementation.  +Gesture definitions have a property ​`key​` which is used as a machine­readable  +identifier for the gesture (e.g. `​drag​`, `​drop​`, `​menu​` above.)    +A gesture’s implementation is instantiated once per representation that uses the  +gesture. This class will receive the jqLite­wrapped ​`mct­representation​` element  +and the domain object being represented as arguments, and should do any  +necessary "wiring" (e.g. listening for events) during its constructor call. The  +gesture’s implementation may also expose an optional destroy()​ method which will  +be called when the gesture should be removed, to avoid memory leaks by way of  +unremoved listeners. + +## Indicators + +An indicator is an element that should appear in the status area at the bottom  +of a running Open MCT Web client instance.  + +### Standard Indicators   -Licenses +Indicators which wish to appear in the common form of an icon­text pair should  +provide implementations with the following methods: + +* `getText()`​: Provides the human­readable text that will be displayed for this  +indicator.  +* `getGlyph()​`: Provides a single­character string that will be displayed as an  +icon in Open MCT Web’s custom font set.  +* `getDescription()`​: Provides a human­readable summary of the current state of  +this indicator; will be displayed in a tooltip on hover.  +* `getClass()`​: Get a CSS class that will be applied to this indicator.  +* `getTextClass()`​: Get a CSS class that will be applied to this indicator’s  +text portion.  +* `getGlyphClass()​`: Get a CSS class that will be applied to this indicator’s  +icon portion.  +* `configure()`​: If present, a configuration icon will appear to the right of  +this indicator, and clicking it will invoke this method.    - The extension category ​licenses​ can be used to add entries into the “Licensing  +Note that all methods are optional, and are called directly from an Angular  +template, so they should be appropriate to run during digest cycles.  +  +### Custom Indicators + +Indicators which wish to have an arbitrary appearance (instead of following the  +icon­text convention commonly used) may specify a ​`template`​ property in their  +extension definition. The value of this property will be used as the ​`key`​ for  +an `​mct­include​` directive (so should refer to an extension of category  +​templates​.) This template will be rendered to the status area. Indicators of  +this variety do not need to provide an implementation.  + +## Licenses + +The extension category ​`licenses​` can be used to add entries into the “Licensing  information” page, reachable from Open MCT Web’s About dialog.  - Licenses may have the following properties, all of which are strings:  -  - * name​: Human­readable name of the licensed component. (e.g. “AngularJS”.)  - * version​: Human­readable version of the licensed component. (e.g. “1.2.26”.)  - * description​: Human­readable summary of the component.  - * author​: Name or names of entities to which authorship should be attributed.  - * copyright​: Copyright text to display for this component.  - * link​: URL to full license text.  -  - 30  -  -Policies -  - Policies are used to handle decisions made using Open MCT Web’s ​policyService​;  -examples of these decisions are determining the applicability of certain actions, or checking  -whether or not a domain object of one type can contain a domain object of a different type. See  -the section on the Policies for an overview of Open MCT Web’s policy model.  - A policy’s extension definition should include:  -  - * category​: The machine­readable identifier for the type of policy decision being  - supported here. For a list of categories supported by the platform, see the section on  - Policies. Plugins may introduce and utilize additional policy categories not in that list.  - * message​: Optional; a human­readable message describing the policy, intended for  - display in situations where this specific policy has disallowed something.  -  - A policy’s implementation should include a single method, ​allow(candidate,  -context)​. The specific types used for ​candidate​ and ​context​ vary by policy category; in  -general, what is being asked is “is this candidate allowed in this context?” This method should  -return a boolean value.  - Open MCT Web’s policy model requires consensus; a policy decision is allowed when  -and only when all policies choose to allow it. As such, policies should generally be written to  -reject a certain case, and allow (by returning true) anything else.  -  -Representations -  - A representation is an Angular template used to display a domain object. The  -representations​ extension category is used to add options for the ​mct­representation  -directive.  -  - A representation definition should include the following properties:  -   - * key​: The machine­readable name which identifies the representation.  - * templateUrl​: The path to the representation's Angular template. This path is relative  - to the bundle's resources directory.  - * uses​: Optional; an array of capability names. Indicates that this representation intends  - to use those capabilities of a domain object (via a ​useCapability​ call), and expects to  - find the latest results of that ​useCapability​ call in the scope of the presented  - template (under the same name as the capability itself.) Note that, if ​useCapability  - returns a promise, this will be resolved before being placed in the representation’s  - scope.  - 31  - * gestures​: An array of keys identifying gestures (see the ​gestures​ extension  - category) which should be available upon this representation. Examples of gestures  - include ​drag​ (for representations that should act as draggable sources for drag­drop  - operations) and ​menu​ (for representations which should show a domain­object­specific  - context menu on right­click.)  -  -Representation Scope -  - While ​representations​ do not have implementations, per se, they do refer to  -Angular templates which need to interact with information (e.g. the domain object being  -represented) provided by the platform. This information is passed in through the template’s  -scope, such that simple representations may be created by providing only templates. (More  -complex representations will need controllers which are referenced from templates. See  -https://docs.angularjs.org/guide/controller​ for more information on controllers in Angular.)  -  - A representation’s scope will contain:  -  - * domainObject​: The represented domain object.  - * model​: The domain object’s model.  - * configuration​: An object containing configuration information for this representation  - (an empty object if there is no saved configuration.) The contents of this object are  - managed entirely by the view/representation which receives it.  - * representation​: An empty object, useful as a “scratch pad” for representation state.  - * ngModel​: An object passed through the ​ng­model​ attribute of the  - mct­representation​, if any.  - * parameters​: An object passed through the ​parameters​ attribute of the  - mct­representation​, if any.  - * Any capabilities requested by the ​uses​ property of the representation definition.  -  -Representers -  - The ​representers​ extension category is used to add additional behavior to the  -mct­representation​ directive. This extension category is intended primarily for use internal  -to the platform.  - Unlike represent​ations​, which describe specific ways to represent domain objects,  -represent​ers ​are used to modify or augment the process of representing domain objects in  -general. For example, support for the ​gestures​ extension category is added by a representer.  - A representer needs only provide an implementation. When an ​mct­representation  -is linked (see ​https://docs.angularjs.org/guide/directive​) or when the domain object being  -represented changes, a new representer of each declared type is instantiated. The constructor  -arguments for a representer are the same as the arguments to the link function in an Angular  - 32  -directive: ​scope​, the Angular scope for this representation; ​element​, the jqLite­wrapped  -mct­representation​ element, and ​attrs​, a set of key­value pairs of that element’s  -attributes. Representers may wish to populate the scope, attach event listeners to the element,  -etc.  - This implementation must provide a single method, ​destroy()​, which will be invoked  -when the representer is no longer needed.  -  -Roots -  - The extension category ​roots​ is used to provide root­level domain object models.  -Root­level domain objects appear at the top­level of the tree hierarchy. For example, the “My  -Items” folder is added as an extension of this category.  - Extensions of this category should have the following properties:  -  - * id​: The machine­readable identifier for the domain object being exposed.  - * model​: The model, as a JSON object, for the domain object being exposed.  -  -Stylesheets -  - The ​stylesheets​ extension category is used to add CSS files to style the application.  -Extension definitions for this category should include one property:  -   - * stylesheetUrl​: Path and filename, including extension, for the stylesheet to include.  - This path is relative to the bundle’s resources folder (by default, ​res​)  -  - To control the order of CSS files, use ​priority​ (see the section on Extension  -Definitions above.)  -   -  - - 33  -Templates +Licenses may have the following properties, all of which are strings: + +* `name​`: Human­readable name of the licensed component. (e.g. “AngularJS”.) +* `version`​: Human­readable version of the licensed component. (e.g. “1.2.26”.) +* `description​`: Human­readable summary of the component. +* `author​`: Name or names of entities to which authorship should be attributed. +* `copyright​`: Copyright text to display for this component. +* `link​`: URL to full license text.  + +## Policies + +Policies are used to handle decisions made using Open MCT Web’s ​`policyService​`;  +examples of these decisions are determining the applicability of certain  +actions, or checking whether or not a domain object of one type can contain a  +domain object of a different type. See the section on the Policies for an  +overview of Open MCT Web’s policy model. + +A policy’s extension definition should include: + +* `category​`: The machine­readable identifier for the type of policy decision  +being supported here. For a list of categories supported by the platform, see  +the section on Policies. Plugins may introduce and utilize additional policy  +categories not in that list.  +* `message​`: Optional; a human­readable message describing the policy, intended  +for display in situations where this specific policy has disallowed something.    - The ​templates​ extension category is used to expose Angular templates under  +A policy’s implementation should include a single method, `​allow(candidate, +context)`​. The specific types used for `​candidate​` and `​context​` vary by policy  +category; in general, what is being asked is “is this candidate allowed in this  +context?” This method should return a boolean value.  + +Open MCT Web’s policy model requires consensus; a policy decision is allowed  +when and only when all policies choose to allow it. As such, policies should  +generally be written to reject a certain case, and allow (by returning `true`)  +anything else.  +  +## Representations + +A representation is an Angular template used to display a domain object. The  +`representations​` extension category is used to add options for the  +`​mct­representation` directive.  +  +A representation definition should include the following properties: + +* `key​`: The machine­readable name which identifies the representation.  +* `templateUrl​`: The path to the representation's Angular template. This path is  +relative to the bundle's resources directory.  +* `uses​`: Optional; an array of capability names. Indicates that this  +representation intends to use those capabilities of a domain object (via a  +​`useCapability​` call), and expects to find the latest results of that  +`​useCapability​` call in the scope of the presented template (under the same name  +as the capability itself.) Note that, if `​useCapability` returns a promise, this  +will be resolved before being placed in the representation’s scope.  +* `gestures​`: An array of keys identifying gestures (see the `​gestures​`  +extension category) which should be available upon this representation. Examples  +of gestures include `​drag​` (for representations that should act as draggable  +sources for drag­drop operations) and `​menu​` (for representations which should  +show a domain­object­specific context menu on right­click.)  + +### Representation Scope + +While ​_representations​_ do not have implementations, per se, they do refer to  +Angular templates which need to interact with information (e.g. the domain  +object being represented) provided by the platform. This information is passed  +in through the template’s scope, such that simple representations may be created  +by providing only templates. (More complex representations will need controllers  +which are referenced from templates. See [https://docs.angularjs.org/guide/controller​]() +for more information on controllers in Angular.)  +  +A representation’s scope will contain: +* `domainObject​`: The represented domain object. +* `model​`: The domain object’s model. +* `configuration​`: An object containing configuration information for this  +representation (an empty object if there is no saved configuration.) The  +contents of this object are managed entirely by the view/representation which  +receives it.  +* `representation​`: An empty object, useful as a “scratch pad” for  +representation state.  +* `ngModel​`: An object passed through the ​ng­model​ attribute of the  +mct­representation​, if any.  +* `parameters`​: An object passed through the ​parameters​ attribute of the  +mct­representation​, if any.  +* Any capabilities requested by the ​uses​ property of the representation  +definition. +  +## Representers + +The ​`representers​` extension category is used to add additional behavior to the  +`mct­representation​` directive. This extension category is intended primarily  +for use internal to the platform.  + +Unlike _represent​ations​_, which describe specific ways to represent domain  +objects, represent​ers ​are used to modify or augment the process of representing  +domain objects in general. For example, support for the ​_gestures​_ extension  +category is added by a representer. + +A representer needs only provide an implementation. When an ​`mct­representation`  +is linked (see ​[https://docs.angularjs.org/guide/directive​]() or when the domain  +object being represented changes, a new representer of each declared type is  +instantiated. The constructor arguments for a representer are the same as the  +arguments to the link function in an Angular directive: ​`scope​`, the Angular  +scope for this representation; `​element​`, the jqLite­wrapped  +`mct­representation​` element, and `​attrs​`, a set of key­value pairs of that  +element’s attributes. Representers may wish to populate the scope, attach event  +listeners to the element, etc. + +This implementation must provide a single method, `​destroy()`​, which will be  +invoked when the representer is no longer needed.  + +## Roots + +The extension category ​`roots​` is used to provide root­level domain object  +models. Root­level domain objects appear at the top­level of the tree hierarchy.  +For example, the _My Items_ folder is added as an extension of this category.  + +Extensions of this category should have the following properties: +* `id​`: The machine­readable identifier for the domaiwn object being exposed. +* `model`​: The model, as a JSON object, for the domain object being exposed.  +  +## Stylesheets + +The ​stylesheets​ extension category is used to add CSS files to style the  +application. Extension definitions for this category should include one  +property: + +* `stylesheetUrl​`: Path and filename, including extension, for the stylesheet to  +include. This path is relative to the bundle’s resources folder (by default, ​ +`res​`)  +  +To control the order of CSS files, use ​priority​ (see the section on Extension  +Definitions above.)  + +## Templates + +The ​templates​ extension category is used to expose Angular templates under  symbolic identifiers. These can then be utilized using the ​mct­include​ directive, which  behaves similarly to ​ng­include​, except that it uses these symbolic identifiers instead of  paths.  From b3fb06ba3faa69619c3d84c155be5324c125b348 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Fri, 25 Sep 2015 14:28:52 -0700 Subject: [PATCH 007/488] Up to page 52 --- docs/src/guide/index.md | 1279 +++++++++++++++++++-------------------- 1 file changed, 633 insertions(+), 646 deletions(-) diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 7774adffb1..0f83d1dd0b 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -600,25 +600,8 @@ path relative to the bundle’s resource directory (​`res​` by defa ### Composite Services -A special category of extensions recognized by the framework are ​`components`​;  -these are parts of services intended to be fit together in a common pattern.  - -INSERT DIAGRAM HERE - -Components all implement the same interface, which is the interface expected for  -services of the type that they create. Components fall into three types: - -* `provider​`: Provides an actual implementation of the service in question.  -* `aggregator`​: Makes many implementations of the service in question appear as  -one.  -* `decorator​`: Modifies the inputs or outputs of another implementation of the  -service.  - -When the framework layer encounters components, it assembles them into single  -service instances that can be referred to elsewhere as single dependencies. All  -providers are instantiated, and passed to the first available aggregator;  -decorators are then layered on in priority order to create the final form of the  -service. +Composite services are described in the [relevant section](../architecture/Framework.md#Composite-Services) +of the framework guide. A component should include the following properties in its extension definition: @@ -626,7 +609,7 @@ A component should include the following properties in its extension d fully­composed service will be registered with Angular under this name. * `type​`: One of `​provider`​, ​`aggregator​`, or `​decorator​` (as above)  -In addition to any declared dependencies, aggregators and decorators both  +In addition to any declared dependencies, _aggregators_ and _decorators_ both  receive one more argument (immediately following declared dependencies) that is  provided by the framework. For an aggregator, this will be an array of all  providers of the same service (that is, with matching `​provides`​ properties);  @@ -1074,9 +1057,9 @@ The ​`representers​` extension category is used to add additional b `mct­representation​` directive. This extension category is intended primarily  for use internal to the platform.  -Unlike _represent​ations​_, which describe specific ways to represent domain  +Unlike _represent​ations​_, which describe specific ways to represent domain  objects, represent​ers ​are used to modify or augment the process of representing  -domain objects in general. For example, support for the ​_gestures​_ extension  +domain objects in general. For example, support for the  _gestures​_ extension  category is added by a representer. A representer needs only provide an implementation. When an ​`mct­representation`  @@ -1117,647 +1100,651 @@ Definitions above.)  ## Templates -The ​templates​ extension category is used to expose Angular templates under  -symbolic identifiers. These can then be utilized using the ​mct­include​ directive, which  -behaves similarly to ​ng­include​, except that it uses these symbolic identifiers instead of  -paths.  - A template’s extension definition should include the following properties:  -   - * key​: The machine­readable name which identifies this template, matched against the  - value given to the key attribute of the mct­include directive.  - * templateUrl​: The path to the relevant Angular template. This path is relative to the  - bundle's resources directory.  -  - Note that, when multiple templates are present with the same ​key​, the one with the  -highest priority will be used from mct­include. This behavior can be used to override templates  -exposed by the platform (to change the logo which appears in the bottom right, for instance.)  -  - Templates do not have implementations.  -  -Types -  - The ​types​ extension category describes types of domain objects which may appear  -within Open MCT Web.  - A type’s extension definition should have the following properties:  -  - * key​: The machine­readable identifier for this domain object type. Will be stored to and  - matched against the ​type​ property of domain object models.  - * name​: The human­readable name for this domain object type.  - * description​: A human­readable summary of this domain object type.  - * glyph​: A single character to be rendered as an icon in Open MCT Web’s custom font  - set.  - * model​: A domain object model, used as the initial state for created domain objects of  - this type (before any properties are specified.)  - * features​: Optional; an array of strings describing features of this domain object type.  - Currently, only ​creation​ is recognized by the platform; this is used to determine that  - this type should appear in the Create menu. More generally, this is used to support the  - hasFeature(...)​ method of the ​type​ capability.  - * properties​: An array describing individual properties of this domain object (as should  - appear in the Create or the Edit Properties dialog.) Each property is described by an  - object containing the following properties:  - 34  - ○ control​: The key of the control (see mct­control and the controls extension  - category) to use for editing this property.  - ○ property​: A string which will be used as the name of the property in the domain  - object’s model that the value for this property should be stored under. If this value  - should be stored in an object nested within the domain object model, then  - property should be specified as an array of strings identifying these nested  - objects and, finally, the property itself.  - ○ ...other properties as appropriate for a control of this type (each property’s  - definition will also be passed in as the structure for its control.) See  - documentation of ​mct­form​ for more detail on these properties.  -  - Types do not have implementations.  -  -Versions -  - The ​versions​ extension category is used to introduce line items in Open MCT Web’s  -About dialog. These should have the following properties:  -  - * name​: The name of this line item, as should appear in the left­hand side of the list of  - version information in the About dialog.  - * value​: The value which should appear to the right of the name in the About dialog.  -  - To control the ordering of line items within the About dialog, use ​priority​. (See  -section on Extension Definitions above.)  -   - This extension category does not have implementations.  -  -Views -  - The ​views​ extension category is used to determine which options appear to the user as  -available views of domain objects of specific types. A view’s extension definition has the same  -properties as a representation (and views can be utilized via ​mct­representation​);  -additionally:  -  - * name​: The human­readable name for this view type.  - * description​: A human­readable summary of this view type.  - * glyph​: A single character to be rendered as an icon in Open MCT Web’s custom font  - set.  - * type​: Optional; if present, this representation is only applicable for domain object’s of  - this type.  - 35  - * needs​: Optional array of strings; if present, this representation is only applicable for  - domain objects which have the capabilities identified by these strings.  - * delegation​: Optional boolean, intended to be used in conjunction with ​needs​;  if  - present, allow required capabilities to be satisfied by means of capability delegation.  - (See the ​delegation​ capability, in the Capabilities section.)  - * toolbar​: Optional; a definition for the toolbar which may appear in a toolbar when  - using this view in Edit mode. This should be specified as a structure for ​mct­toolbar​,  - with additional properties available for each item in that toolbar:  - ○ property​: A property name. This will refer to a property in the view’s current  - selection; that property on the selected object will be modifiable as the  - ng­model​ of the displayed control in the toolbar. If the value of the property is a  - function, it will be used as a getter­setter (called with no arguments to use as a  - getter, called with a value to use as a setter.)  - ○ method​: A method to invoke (again, on the selected object) from the toolbar  - control. Useful particularly for buttons (which don’t edit a single property,  - necessarily.)  -  -View Scope -  - Views do not have implementations, but do get the same properties in scope that are  -provided for ​representations​.  -  - When a view is in Edit mode, this scope will additionally contain:  -  - * commit()​: A function which can be invoked to mark any changes to the view’s  - configuration​ as ready to persist.  - * selection​: An object representing the current selection state.  -  -Selection State -  - A view’s selection state is, conceptually, a set of JavaScript objects. The presence of  -methods/properties on these objects determine which toolbar controls are visible, and what  -state they manage and/or behavior they invoke.  - This set may contain up to two different objects: The ​view proxy​, which is used to make  -changes to the view as a whole, and the ​selected object​, which is used to represent some state  -within the view. (Future versions of Open MCT Web may support multiple selected objects.)  -  -   -    - 36  - The ​selection​ object made available during Edit mode has the following methods:  -  - * proxy([object])​: Get (or set, if called with an argument) the current view proxy.   - * select(object)​: Make this object the selected object.  - * deselect()​: Clear the currently selected object.  - * get()​: Get the currently selected object. Returns ​undefined​ if there is no currently  - selected object.  - * selected(object)​: Check if the JavaScript object is currently in the selection set.  - Returns ​true​ if the object is either the currently selected object, or the current view  - proxy.  - * all()​: Get an array of all objects in the selection state. Will include either or both of the  - view proxy and selected object.  -  +The ​`templates​` extension category is used to expose Angular templates under  +symbolic identifiers. These can then be utilized using the `​mct­include​`  +directive, which behaves similarly to `​ng­include​`, except that it uses these  +symbolic identifiers instead of paths. - - 37  -Directives +A template’s extension definition should include the following properties: +* `key​`: The machine­readable name which identifies this template, matched  +against the value given to the key attribute of the mct­include directive. +* `templateUrl​`: The path to the relevant Angular template. This path is  +relative to the bundle's resources directory.  + +Note that, when multiple templates are present with the same ​key​, the one with  +the highest priority will be used from mct­include. This behavior can be used to  +override templates exposed by the platform (to change the logo which appears in  +the bottom right, for instance.) + +Templates do not have implementations.  + +## Types + +The ​types​ extension category describes types of domain objects which may appear  +within Open MCT Web. + +A type’s extension definition should have the following properties: + +* `key​`: The machine­readable identifier for this domain object type. Will be  +stored to and matched against the ​type​ property of domain object models. +* `name​`: The human­readable name for this domain object type. +* `description​`: A human­readable summary of this domain object type. +* `glyph​`: A single character to be rendered as an icon in Open MCT Web’s custom  +font set.  +* `model`​: A domain object model, used as the initial state for created domain  +objects of this type (before any properties are specified.) +* `features​`: Optional; an array of strings describing features of this domain  +object type. Currently, only ​creation​ is recognized by the platform; this is  +used to determine that this type should appear in the Create menu. More  +generally, this is used to support the hasFeature(...)​ method of the ​type​  +capability.  +* `properties`​: An array describing individual properties of this domain object +(as should appear in the Create or the Edit Properties dialog.) Each property is  +described by an object containing the following properties: + * `control​`: The key of the control (see mct­control and the controls  + extension category) to use for editing this property.  + * `property​`: A string which will be used as the name of the property in the  + domain object’s model that the value for this property should be stored  + under. If this value should be stored in an object nested within the domain  + object model, then property should be specified as an array of strings  + identifying these nested objects and, finally, the property itself.  + * other properties as appropriate for a control of this type (each  + property’s definition will also be passed in as the structure for its  + control.) See documentation of ​mct­form​ for more detail on these properties. + +Types do not have implementations.    - Open MCT Web defines several Angular directives that are intended for use both  +## Versions +The ​versions​ extension category is used to introduce line items in Open MCT  +Web’s About dialog. These should have the following properties:  + +* `name​`: The name of this line item, as should appear in the left­hand side of  +the list of version information in the About dialog. +* `value​`: The value which should appear to the right of the name in the About  +dialog. + +To control the ordering of line items within the About dialog, use `​priority​`.  +(See section on Extension Definitions above.)  + +This extension category does not have implementations.  +  +## Views + +The ​views​ extension category is used to determine which options appear to the  +user as available views of domain objects of specific types. A view’s extension  +definition has the same properties as a representation (and views can be  +utilized via ​mct­representation​); additionally: + +* `name​`: The human­readable name for this view type. +* description​: A human­readable summary of this view type. +* `glyph​`: A single character to be rendered as an icon in Open MCT Web’s custom  +font set. +* `type`​: Optional; if present, this representation is only applicable for  +domain object’s of this type. +* `needs​`: Optional array of strings; if present, this representation is only  +applicable for domain objects which have the capabilities identified by these  +strings.  +* `delegation​`: Optional boolean, intended to be used in conjunction with ​ +`needs​`;  if present, allow required capabilities to be satisfied by means of  +capability delegation. (See the ​delegation​ capability, in the Capabilities  +section.) +* `toolbar​`: Optional; a definition for the toolbar which may appear in a  +toolbar when using this view in Edit mode. This should be specified as a  +structure for ​mct­toolbar​, with additional properties available for each item in  +that toolbar:  + * `property​`: A property name. This will refer to a property in the view’s  + current selection; that property on the selected object will be modifiable  + as the `ng­model`​ of the displayed control in the toolbar. If the value of  + the property is a function, it will be used as a getter­setter (called with  + no arguments to use as a getter, called with a value to use as a setter.)  + * `method​`: A method to invoke (again, on the selected object) from the  + toolbar control. Useful particularly for buttons (which don’t edit a single  + property, necessarily.)  +  +### View Scope + +Views do not have implementations, but do get the same properties in scope that  +are provided for `​representations​`.  + +When a view is in Edit mode, this scope will additionally contain: +* `commit()`​: A function which can be invoked to mark any changes to the view’s  + configuration​ as ready to persist. +* `selection​`: An object representing the current selection state.  + +#### Selection State + +A view’s selection state is, conceptually, a set of JavaScript objects. The  +presence of methods/properties on these objects determine which toolbar controls  +are visible, and what state they manage and/or behavior they invoke.  + +This set may contain up to two different objects: The  _view proxy​_, which is  +used to make changes to the view as a whole, and the _​selected object​_, which is  +used to represent some state within the view. (Future versions of Open MCT Web  +may support multiple selected objects.)  + +The ​`selection​` object made available during Edit mode has the following  +methods:  + +* `proxy([object])`​: Get (or set, if called with an argument) the current view  +proxy.   +* `select(object)​`: Make this object the selected object.  +* `deselect()`​: Clear the currently selected object.  +* `get()​`: Get the currently selected object. Returns ​undefined​ if there is no  +currently selected object. +* `selected(object)`​: Check if the JavaScript object is currently in the  +selection set. Returns ​true​ if the object is either the currently selected  +object, or the current view proxy.  +* `all()​`: Get an array of all objects in the selection state. Will include  +either or both of the view proxy and selected object.  + +# Directives + +Open MCT Web defines several Angular directives that are intended for use both  internally within the platform, and by plugins.  -  -Before Unload -  - The ​mct­before­unload​ directive is used to listen for (and prompt for user  -confirmation) of navigation changes in the browser. This includes reloading, following links out  -of Open MCT Web, or changing routes. It is used to hook into both ​onbeforeunload​ event  -handling as well as route changes from within Angular.  - This directive is useable as an attribute. Its value should be an Angular expression.  -When an action that would trigger an unload and/or route change occurs, this Angular  -expression is evaluated. Its result should be a message to display to the user to confirm their  -navigation change; if this expression evaluates to a falsy value, no message will be displayed.  -  -Chart -  - The ​mct­chart​ directive is used to support drawing of simple charts. It is present to  -support the Plot view, and its functionality is limited to the functionality that is relevant for that  -view.  - This directive is used at the element level and takes one attribute, ​draw​, which is an  -Angular expression which will should evaluate to a drawing object. This drawing object should  -contain the following properties:  - * dimensions​: The size, in logical coordinates, of the chart area. A two­element  - array or numbers.  - * origin​: The position, in logical coordinates, of the lower­left corner of the chart  - area. A two­element array or numbers.  - * lines​: An array of lines (e.g. as a plot line) to draw, where each line is  - expressed as an object containing:  - ○ buffer​: A Float32Array containing points in the line, in logical  - coordinates, in sequential x,y pairs.  - ○ color​: The color of the line, as a four­element RGBA array, where each  - element is a number in the range of 0.0­1.0.  - ○ points​: The number of points in the line.  - * boxes​: An array of rectangles to draw in the chart area. Each is an object  - containing:  - ○ start​: The first corner of the rectangle, as a two­element array of  - numbers, in logical coordinates.  - 38  - ○ end​: The opposite corner of the rectangle, as a two­element array of  - numbers, in logical coordinates.  - ○ color​: The color of the line, as a four­element RGBA array, where each  - element is a number in the range of 0.0­1.0.  -  - While ​mct­chart​ is intended to support plots specifically, it does perform some useful  -management of canvas objects (e.g. choosing between WebGL and Canvas 2D APIs for  -drawing based on browser support) so its usage is recommended when its supported drawing  -primitives are sufficient for other charting tasks.  -  -Container -  - The ​mct­container​ is similar to the ​mct­include​ directive insofar as it allows  -templates to be referenced by symbolic keys instead of by URL. Unlike ​mct­include​, it  -supports transclusion.  - Unlike ​mct­include​, ​mct­container​ accepts a ​key​ as a plain string attribute,  -instead of as an Angular expression.  -   -Control -  - The ​mct­control​ directive is used to display user input elements. Several controls are  -included with the platform to wrap default input types. This directive is primarily intended for  -internal use by the ​mct­form​ and ​mct­toolbar​ directives.  - When using ​mct­control​, the attributes ​ng­model​, ​ng­disabled​, ​ng­required​,  -and ​ng­pattern​ may also be used. These have the usual meaning (as they would for an input  -element) except for ​ng­model​; when used, it will actually be ​ngModel[field]​ (see below)  -that is two­way bound by this control. This allows ​mct­control​ elements to more easily  -delegate to other ​mct­control​ instances, and also facilitates usage for generated forms.  - This directive supports the following additional attributes, all specified as Angular  -expressions:  -  - * key​: A machine­readable identifier for the specific type of control to display.  - * options​: A set of options to display in this control.  - * structure​: In practice, contains the definition object which describes this form row or  - toolbar item. Used to pass additional control­specific parameters.  - * field​: The field in the ​ngModel​ under which to read/store the property associated with  - this control.  -  -Drag -  - 39  - The ​mct­drag​ directive is used to support drag­based gestures on HTML elements.  -Note that this is not “drag” in the “drag­and­drop” sense, but “drag” in the more general “mouse  -down, mouse move, mouse up” sense.  - This takes the form of three attributes:  -  - * mct­drag​: An Angular expression to evaluate during drag movement.  - * mct­drag­down​: An Angular expression to evaluate when the drag starts.  - * mct­drag­up​: An Angular expression to evaluate when the drag ends.  -  - In each case, a variable ​delta​ will be provided to the expression; this is a two­element  -array or the horizontal and vertical pixel offset of the current mouse position relative to the  -mouse position where dragging began.  -   -Form -  - The ​mct­form​ directive is used to generate forms using a declarative structure, and to  -gather back user input. It is applicable at the element level and supports the following attributes:  -  - * ng­model​: The object which should contain the full form input. Individual fields in this  - model are bound to individual controls; the names used for these fields are provided in  - the form structure (see below).  - * structure​: The structure of the form; e.g. sections, rows, their names, and so forth.  - The value of this attribute should be an Angular expression.  - * name​: The name in the containing scope under which to publish form "meta­state", e.g.  - $valid​, ​$dirty​, etc. This is as the behavior of ​ng­form​. Passed as plain text in the  - attribute.  -  - - 40  -Form Structure +## Before Unload + +The `​mct­before­unload​` directive is used to listen for (and prompt for user  +confirmation) of navigation changes in the browser. This includes reloading,  +following links out of Open MCT Web, or changing routes. It is used to hook into  +both `​onbeforeunload​` event handling as well as route changes from within  +Angular. + +This directive is useable as an attribute. Its value should be an Angular  +expression. When an action that would trigger an unload and/or route change  +occurs, this Angular expression is evaluated. Its result should be a message to  +display to the user to confirm their navigation change; if this expression  +evaluates to a falsy value, no message will be displayed.    - Forms in Open MCT Web have a common structure to permit consistent display. A form  -is broken down into sections, which will be displayed in groups; each section is broken down  -into rows, each of which provides a control for a single property. Input from this form is two­way  -bound to the object passed via ​ng­model​.  - A form’s structure is represented by a JavaScript object in the following form:  -{  -    "name": ... title to display for the form, as a string ...,  -    "sections": [  -        {  -            "name": ... title to display for the section ...,  -            "rows": [  -                {  -                    "name": ... title to display for this row ...,  -                    "control": ... symbolic key for the control ...,  -                    "key": ... field name in ng­model ...  -                    "pattern": ... optional, reg exp to match against ...  -                    "required": ... optional boolean ...  -                    "options": [  -                        "name": ... name to display (e.g. in a select) ...,  -                        "value": ... value to store in the model ...  -                    ]  -                },  -                ... and other rows ...  -            ]  -        },  -        ... and other sections ...  -    ]  -}  -  -Note that ​pattern​ may be specified as a string, to simplify storing for structures as JSON  -when necessary. The string should be given in a form appropriate to pass to a ​RegExp  -constructor.  +## Chart + +The `​mct­chart​` directive is used to support drawing of simple charts. It is  +present to support the Plot view, and its functionality is limited to the  +functionality that is relevant for that view. + +This directive is used at the element level and takes one attribute, `​draw​`,  +which is an Angular expression which will should evaluate to a drawing object.  +This drawing object should contain the following properties: + +* `dimensions​`: The size, in logical coordinates, of the chart area. A  +two­element array or numbers.  +* `origin​`: The position, in logical coordinates, of the lower­left corner of  +the chart area. A two­element array or numbers.  +* `lines​`: An array of lines (e.g. as a plot line) to draw, where each line is  +expressed as an object containing:  + * `buffer`​: A Float32Array containing points in the line, in logical  + coordinates, in sequential x,y pairs.  + * `color​`: The color of the line, as a four­element RGBA array, where  + each element is a number in the range of 0.0­1.0.  + * `points​`: The number of points in the line.  +* `boxes`​: An array of rectangles to draw in the chart area. Each is an object  +containing:  + * `start​`: The first corner of the rectangle, as a two­element array of + numbers, in logical coordinates.  + * `end​`: The opposite corner of the rectangle, as a two­element array of  + numbers, in logical coordinates. color​: The color of the line, as a  + four­element RGBA array, where each element is a number in the range of  + 0.0­1.0.  + +While ​`mct­chart​` is intended to support plots specifically, it does perform  +some useful management of canvas objects (e.g. choosing between WebGL and Canvas  +2D APIs for drawing based on browser support) so its usage is recommended when  +its supported drawing primitives are sufficient for other charting tasks.    +## Container + +The ​`mct­container​` is similar to the `​mct­include​` directive insofar as it allows  +templates to be referenced by symbolic keys instead of by URL. Unlike  +`​mct­include​`, it supports transclusion. + +Unlike `​mct­include​`, `​mct­container​` accepts a ​key​ as a plain string attribute,  +instead of as an Angular expression. + +## Control + +The `​mct­control​` directive is used to display user input elements. Several  +controls are included with the platform to wrap default input types. This  +directive is primarily intended for internal use by the `​mct­form​` and  +`​mct­toolbar​` directives.  + +When using `​mct­control​`, the attributes `​ng­model​`, `​ng­disabled​`,  +`​ng­required​`, and `​ng­pattern​` may also be used. These have the usual meaning +(as they would for an input element) except for `​ng­model​`; when used, it will  +actually be ​`ngModel[field]`​ (see below) that is two­way bound by this control.  +This allows `​mct­control​` elements to more easily delegate to other  +`​mct­control​` instances, and also facilitates usage for generated forms.  + +This directive supports the following additional attributes, all specified as  +Angular expressions: + +* `key​`: A machine­readable identifier for the specific type of control to  +display. +* `options`​: A set of options to display in this control. +* `structure​`: In practice, contains the definition object which describes this  +form row or toolbar item. Used to pass additional control­specific parameters.  +* `field​`: The field in the `​ngModel​` under which to read/store the property  +associated with this control.  + +## Drag + +The ​`mct­drag​` directive is used to support drag­based gestures on HTML  +elements. Note that this is not “drag” in the “drag­and­drop” sense, but “drag”  +in the more general “mouse down, mouse move, mouse up” sense.  + +This takes the form of three attributes:  + +* `mct­drag​`: An Angular expression to evaluate during drag movement. +* `mct­drag­down`​: An Angular expression to evaluate when the drag starts. +* `mct­drag­up​`: An Angular expression to evaluate when the drag ends. + +In each case, a variable ​`delta​` will be provided to the expression; this is a  +two­element array or the horizontal and vertical pixel offset of the current  +mouse position relative to the mouse position where dragging began.  + +## Form + +The ​`mct­form​` directive is used to generate forms using a declarative structure,  +and to gather back user input. It is applicable at the element level and  +supports the following attributes:  + +* `ng­model​`: The object which should contain the full form input. Individual  +fields in this model are bound to individual controls; the names used for these  +fields are provided in the form structure (see below). +* `structure`​: The structure of the form; e.g. sections, rows, their names, and  +so forth. The value of this attribute should be an Angular expression.  +* `name​`: The name in the containing scope under which to publish form  +"meta­state", e.g. `$valid​`, `​$dirty​`, etc. This is as the behavior of `​ng­form​`.  +Passed as plain text in the attribute.  + +### Form Structure + +Forms in Open MCT Web have a common structure to permit consistent display. A  +form is broken down into sections, which will be displayed in groups; each  +section is broken down into rows, each of which provides a control for a single  +property. Input from this form is two­way bound to the object passed via  +​`ng­model​`.  + +A form’s structure is represented by a JavaScript object in the following form: - - 41  -Form Controls -  - A few standard control types are included in the ​platform/forms​ bundle:  -  - * textfield​: An area to enter plain text.  - * select​: A drop­down list of options.  - * checkbox​: A box which may be checked/unchecked.  - * color​: A color picker.  - * button​: A button.  - * datetime​: An input for UTC date/time entry; gives result as a UNIX timestamp, in  - milliseconds since start of 1970, UTC.  -  -Include -  - The ​mct­include​ directive is similar to ​ng­include​, except that it takes a symbolic  -identifier for a template instead of a URL. Additionally, templates included via ​mct­include  -will have an isolated scope.  - The directive should be used at the element level and supports the following attributes,  -all of which are specified as Angular expressions:  -  - * key​: Machine­readable identifier for the template (of extension category ​templates​) to  - be displayed.  - * ng­model​: Optional; will be passed into the template’s scope as ​ngModel​. Intended  - usage is for two­way bound user input.  - * parameters​: Optional; will be passed into the template’s scope as ​parameters​.  - Intended usage is for template­specific display parameters.  -  + {  + "name": ... title to display for the form, as a string ...,  + "sections": [ + {  + "name": ... title to display for the section ...,  + "rows": [  + {  + "name": ... title to display for this row ..., + "control": ... symbolic key for the control ...,  + "key": ... field name in ng­model ...  + "pattern": ... optional, reg exp to match against ...  + "required": ... optional boolean ...  + "options": [  + "name": ... name to display (e.g. in a select) ...,  + "value": ... value to store in the model ...  + ]  + },  + ... and other rows ...  + ]  + },  + ... and other sections ...  + ]  + }  - - 42  -Representation -  - The ​mct­representation​ directive is used to include templates which specifically  -represent domain objects. Usage is similar to ​mct­include​.  - The directive should be used at the element level and supports the following attributes,  -all of which are specified as Angular expressions:  -  - * key​: Machine­readable identifier for the representation (of extension category  - representations​ or ​views​) to be displayed.  - * mct­object​: The domain object being represented.  - * ng­model​: Optional; will be passed into the template’s scope as ​ngModel​. Intended  - usage is for two­way bound user input.  - * parameters​: Optional; will be passed into the template’s scope as ​parameters​.  - Intended usage is for template­specific display parameters.  -  -Resize -  - The ​mct­resize​ directive is used to monitor the size of an HTML element. It is  -specified as an attribute whose value is an Angular expression that will be evaluated when the  -size of the HTML element changes. This expression will be provided a single variable, ​bounds​,  -which is an object containing two properties, ​width​ and ​height​, describing the size in pixels  -of the element.  - When using this directive, an attribute ​mct­resize­interval​ may optionally be  -provided. Its value is an Angular expression describing the number of milliseconds to wait  -before next checking the size of the HTML element; this expression is evaluated when the  -directive is linked and reevaluated whenever the size is checked.  -  -Scroll -  - The ​mct­scroll­x​ and ​mct­scroll­y​ directives are used to both monitor and  -control the horizontal and vertical scroll bar state of an element, respectively. They are intended  -to be used as attributes whose values are assignable Angular expressions which two­way bind  -to the scroll bar state.  -  +Note that ​`pattern​` may be specified as a string, to simplify storing for  +structures as JSON when necessary. The string should be given in a form  +appropriate to pass to a ​`RegExp` constructor.  - - 43  -Toolbar -  - The ​mct­toolbar​ directive is used to generate toolbars using a declarative structure,  -and to gather back user input. It is applicable at the element level and supports the following  -attributes:  -  - * ng­model​: The object which should contain the full toolbar input. Individual fields in this  - model are bound to individual controls; the names used for these fields are provided in  - the form structure (see below).  - * structure​: The structure of the toolbar; e.g. sections, rows, their names, and so forth.  - The value of this attribute should be an Angular expression.  - * name​: The name in the containing scope under which to publish form "meta­state", e.g.  - $valid​, ​$dirty​, etc. This is as the behavior of ​ng­form​. Passed as plain text in the  - attribute.  -  - Toolbars support the same ​control​ options as forms.   -  -Toolbar Structure -  - A toolbar’s structure is defined similarly to forms, except instead of ​rows​ there are  -items​.  -  -{  -    "name": ... title to display for the form, as a string ...,  -    "sections": [  -        {  -            "name": ... title to display for the section ...,  -            "items": [  -                {  -                    "name": ... title to display for this row ...,  -                    "control": ... symbolic key for the control ...,  -                    "key": ... field name in ng­model ...  -                    "pattern": ... optional, reg exp to match against ...  -                    "required": ... optional boolean ...  -                    "options": [  -                        "name": ... name to display (e.g. in a select) ...,  -                        "value": ... value to store in the model ...  -                    ],  -                    "disabled": ... true if control should be disabled ...  -                    "size": ... size of the control (for textfields) ...  -                    "click": ... function to invoke (for buttons) ...  -                    "glyph": ... glyph to display (for buttons) ...  -                    "text": ... text within control (for buttons) ...  - 44  -                },  -                ... and other rows ...  -            ]  -        },  -        ... and other sections ...  -    ]  -} +### Form Controls + +A few standard control types are included in the ​platform/forms​ bundle:  + +* `textfield​`: An area to enter plain text.  +* `select​`: A drop­down list of options.  +* `checkbox`​: A box which may be checked/unchecked.  +* `color​`: A color picker.  +* `button​`: A button.  +* `datetime​`: An input for UTC date/time entry; gives result as a UNIX  +timestamp, in milliseconds since start of 1970, UTC.  + +##Include + +The ​`mct­include​` directive is similar to ​ng­include​, except that it takes a  +symbolic identifier for a template instead of a URL. Additionally, templates  +included via ​mct­include will have an isolated scope.  + +The directive should be used at the element level and supports the following  +attributes, all of which are specified as Angular expressions:  + +* `key​`: Machine­readable identifier for the template (of extension category ​ +templates​) to be displayed.  +* `ng­model`​: _Optional_; will be passed into the template’s scope as ​ngModel​.  +Intended usage is for two­way bound user input. +* `parameters​`: _Optional_; will be passed into the template’s scope as ​parameters​.  +Intended usage is for template­specific display parameters.  + +## Representation + +The `​mct­representation​` directive is used to include templates which  +specifically represent domain objects. Usage is similar to `​mct­include​`.  + +The directive should be used at the element level and supports the following  +attributes, all of which are specified as Angular expressions: + +* `key​`: Machine­readable identifier for the representation (of extension  +category representations​ or ​views​) to be displayed.  +* `mct­object​`: The domain object being represented.  +* `ng­model​`: Optional; will be passed into the template’s scope as ​ngModel​.  +Intended usage is for two­way bound user input.  +* `parameters​`: Optional; will be passed into the template’s scope as ​ +parameters​. Intended usage is for template­specific display parameters.  + +## Resize + +The `​mct­resize​` directive is used to monitor the size of an HTML element. It is  +specified as an attribute whose value is an Angular expression that will be  +evaluated when the size of the HTML element changes. This expression will be  +provided a single variable, ​`bounds​`, which is an object containing two  +properties, `​width​` and `​height​`, describing the size in pixels of the element. + +When using this directive, an attribute `​mct­resize­interval​` may optionally be  +provided. Its value is an Angular expression describing the number of  +milliseconds to wait before next checking the size of the HTML element; this  +expression is evaluated when the directive is linked and reevaluated whenever  +the size is checked. + +## Scroll + +The ​`mct­scroll­x​` and `​mct­scroll­y​` directives are used to both monitor and  +control the horizontal and vertical scroll bar state of an element,  +respectively. They are intended to be used as attributes whose values are  +assignable Angular expressions which two­way bind to the scroll bar state. + +## Toolbar + +The `​mct­toolbar​` directive is used to generate toolbars using a declarative  +structure, and to gather back user input. It is applicable at the element level  +and supports the following attributes:  + +* `ng­model​`: The object which should contain the full toolbar input. Individual  +fields in this model are bound to individual controls; the names used for these  +fields are provided in the form structure (see below).  +* `structure​`: The structure of the toolbar; e.g. sections, rows, their names, and  +so forth. The value of this attribute should be an Angular expression. +* `name​`: The name in the containing scope under which to publish form  +"meta­state", e.g. `$valid​`, `​$dirty​`, etc. This is as the behavior of  +`​ng­form​`. Passed as plain text in the attribute.  + +Toolbars support the same ​control​ options as forms.   + +### Toolbar Structure + +A toolbar’s structure is defined similarly to forms, except instead of ​rows​  +there are items​.  + + {  +     "name": ... title to display for the form, as a string ...,  +     "sections": [  +         {  +             "name": ... title to display for the section ...,  +             "items": [  +                 {  +                     "name": ... title to display for this row ...,  +                     "control": ... symbolic key for the control ...,  +                     "key": ... field name in ng­model ...  +                     "pattern": ... optional, reg exp to match against ...  +                     "required": ... optional boolean ...  +                     "options": [  +                         "name": ... name to display (e.g. in a select) ...,  +                         "value": ... value to store in the model ...  +                     ],  +                     "disabled": ... true if control should be disabled ...  +                     "size": ... size of the control (for textfields) ...  +                     "click": ... function to invoke (for buttons) ...  +                     "glyph": ... glyph to display (for buttons) ...  +                     "text": ... text within control (for buttons) ...  +                 },  +                 ... and other rows ...  +             ]  +         },  +         ... and other sections ...  +     ]  + } # Services -The Open MCT Web platform provides a variety of services which can be retrieved and  -utilized via dependency injection. These services fall into two categories:  -  - * Composite Services are defined by a set of ​components​ extensions; plugins may  - introduce additional components with matching interfaces to extend or augment the  - functionality of the composed service. (See the Framework section on Composite  - Services.)  - * Other services which are defined as standalone service objects; these can be utilized by  - plugins but are not intended to be modified or augmented.  -  -Composite Services -  - This section describes the composite services exposed by Open MCT Web, specifically  -focusing on their interface and contract.  -   - In many cases, the platform will include a provider for a service which consumes a  -specific extension category; for instance, the ​actionService​ depends on ​actions[]​ and  -will expose available actions based on the rules defined for that extension category.   - In these cases, it will usually be simpler to add a new extension of a given category (e.g.  -of category ​actions​) even when the same behavior could be introduced by a service  -component (e.g. an extension of category ​components​ where ​provides​ is ​actionService​,  -and ​type​ is  ​provider​.)   - Occasionally, the extension category does not provide enough expressive power to  -achieve a desired result. For instance, the Create menu is populated with ​create​ actions,  -where one such action exists for each creatable type. Since the framework does not provide a  -declarative means to introduce a new action per type declaratively, the platform implements this  -explicitly in an ​actionService​ component of type ​provider​. Plugins may use a similar  -approach when the normal extension mechanism is insufficient to achieve a desired result.  -  -Action Service -  - 45  - The ​actionService​ provides ​Action​ instances which are applicable in specific  -contexts. See Core API for additional notes on the interface for actions.  - The ​actionService​ has the following interface:  -   - * getActions(context)​: Returns an array of ​Action​ objects which are applicable in  - the specified action context.    -  -  -Capability Service -  - The ​capabilityService​ provides constructors for capabilities which will be exposed  -for a given domain object.  - The ​capabilityService​ has the following interface:  -  - * getCapabilities(model)​: Returns a an object containing key­value pairs,  - representing capabilities which should be exposed by the domain object with this model.  - Keys in this object are the capability keys (as used in a ​getCapability(...)​ call)  - and values are either:  - ○ Functions, in which case they will be used as constructors, which will receive the  - domain object instance to which the capability applies as their sole argument.  - The resulting object will be provided as the result of a domain object’s  - getCapability(...)​call. Note that these instances are cached by each  - object, but may be recreated when an object is mutated.  - ○ Other objects, which will be used directly as the result of a domain object’s  - getCapability(...)​ call.  -  -  -Dialog Service -  - The ​dialogService​ provides a means for requesting user input via a modal dialog. It  -has the following interface:  -  - * getUserInput(formStructure, formState)​: Prompt the user to fill out a form.  - The first argument describes the form’s structure (as will be passed to ​mct­form​) while  - the second argument contains the initial state of that form. This returns a ​Promise​ for  - the state of the form after the user has filled it in; this promise will be rejected if the user  - cancels input.  - * getUserChoice(dialogStructure)​: Prompt the user to make a single choice from  - a set of options, which (in the platform implementation) will be expressed as buttons in  - the displayed dialog. Returns a ​Promise​ for the user’s choice, which will be rejected if  - the user cancels input.  - 46  -  -Dialog Structure -  - The object passed as the ​dialogStructure​ to ​getUserChoice​ should have the  -following properties:  -  - * title​: The title to display at the top of the dialog.  - * hint​: Short message to display below the title.  - * template​: Identifying ​key​ (as will be passed to ​mct­include​) for the template which  - will be used to populate the inner area of the dialog.  - * model​: Model to pass in the ​ng­model​ attribute of ​mct­include​.  - * parameters​: Parameters to pass in the ​parameters​ attribute of ​mct­include​.  - * options​: An array of options describing each button at the bottom. Each option may  - have the following properties:  - ○ name​: Human­readable name to display in the button.  - ○ key​: Machine­readable key, to pass as the result of the resolved promise when  - clicked.  - ○ description​: Description to show in tooltip on hover.  -  -  -Domain Object Service -  - The ​objectService​ provides domain object instances. It has the following interface:  -  - * getObjects(ids)​: For the provided array of domain object identifiers, returns a  - Promise​ for an object containing key­value pairs, where keys are domain object  - identifiers and values are corresponding ​DomainObject​ instances. Note that the result  - may contain a superset or subset of the objects requested.  -   -  -Gesture Service -  - The ​gestureService​ is used to attach gestures (see extension category ​gestures​)  -to representations. It has the following interface:  -  - * attachGestures(element, domainObject, keys)​: Attach gestures specified  - by the provided gesture ​keys​ (an array of strings) to this jqLite­wrapped HTML  - element​, which represents the specified ​domainObject​. Returns an object with a  - single method ​destroy()​, to be invoked when it is time to detach these gestures.  -  -  - 47  -Model Service -  - The ​modelService​ provides domain object models. It has the following interface:  -  - * getModels(ids)​: For the provided array of domain object identifiers, returns a  - Promise​ for an object containing key­value pairs, where keys are domain object  - identifiers and values are corresponding domain object models. Note that the result may  - contain a superset or subset of the models requested.  -   -  -Persistence Service -  - The ​persistenceService​ provides the ability to load/store JavaScript objects  -(presumably serializing/deserializing to JSON in the process.) This is used primarily to store  -domain object models. It has the following interface:  -  - * listSpaces()​: Returns a ​Promise​ for an array of strings identifying the different  - persistence spaces this service supports. Spaces are intended to be used to distinguish  - between different underlying persistence stores, to allow these to live side by side.  - * listObjects()​: Returns a Promise for an array of strings identifying all documents  - stored in this persistence service.  - * createObject(space, key, value)​: Create a new document in the specified  - persistence ​space​, identified by the specified ​key​, the contents of which shall match  - the specified ​value​. Returns a promise that will be rejected if creation fails.  - * readObject(space, key)​: Read an existing document in the specified persistence  - space​, identified by the specified ​key​. Returns a promise for the specified document;  - this promise will resolve to ​undefined​ if the document does not exist.  - * updateObject(space, key, value)​: Update an existing document in the  - specified persistence ​space​, identified by the specified ​key​, such that its contents  - match the specified ​value​. Returns a promise that will be rejected if the update fails.  - * deleteObject(space, key)​: Delete an existing document from the specified  - persistence ​space​, identified by the specified ​key​. Returns a promise which will be  - rejected if deletion fails.  -  -  +The Open MCT Web platform provides a variety of services which can be retrieved  +and utilized via dependency injection. These services fall into two categories:  - - 48  -Policy Service +* _Composite Services_ are defined by a set of ​components​ extensions; plugins may  +introduce additional components with matching interfaces to extend or augment  +the functionality of the composed service. (See the Framework section on  +Composite Services.)  +* _Other services_ which are defined as standalone service objects; these can be  +utilized by plugins but are not intended to be modified or augmented.  + +## Composite Services + +This section describes the composite services exposed by Open MCT Web,  +specifically focusing on their interface and contract.  +   +In many cases, the platform will include a provider for a service which consumes  +a specific extension category; for instance, the `​actionService​` depends on  +`​actions[]​` and will expose available actions based on the rules defined for  +that extension category.   + +In these cases, it will usually be simpler to add a new extension of a given  +category (e.g. of category ​`actions​`) even when the same behavior could be  +introduced by a service component (e.g. an extension of category `​components​`  +where `​provides​` is `​actionService​`, and `​type​` is `​provider​`.)   + +Occasionally, the extension category does not provide enough expressive power to  +achieve a desired result. For instance, the Create menu is populated with  +`​create​` actions, where one such action exists for each creatable type. Since  +the framework does not provide a declarative means to introduce a new action per  +type declaratively, the platform implements this explicitly in an `​actionService​`  +component of type `​provider​`. Plugins may use a similar approach when the normal  +extension mechanism is insufficient to achieve a desired result.    - The ​policyService​ may be used to determine whether or not certain behaviors are  +### Action Service + +The ​`actionService​` provides `​Action​` instances which are applicable in specific  +contexts. See Core API for additional notes on the interface for actions. The  +`​actionService​` has the following interface:  +   +* `getActions(context)`​: Returns an array of ​Action​ objects which are applicable  +in the specified action context.    + +### Capability Service + +The ​capabilityService​ provides constructors for capabilities which will be  +exposed for a given domain object.  + +The ​capabilityService​ has the following interface:  + +* `getCapabilities(model)`​: Returns a an object containing key­value pairs,  +representing capabilities which should be exposed by the domain object with this  +model. Keys in this object are the capability keys (as used in a  +`​getCapability(...)`​ call) and values are either:  + * Functions, in which case they will be used as constructors, which will  + receive the domain object instance to which the capability applies as their  + sole argument.The resulting object will be provided as the result of a  + domain object’s `getCapability(...)` ​call. Note that these instances are cached  + by each object, but may be recreated when an object is mutated.  + * Other objects, which will be used directly as the result of a domain  + object’s `getCapability(...)​` call.  + +### Dialog Service + +The `​dialogService​` provides a means for requesting user input via a modal  +dialog. It has the following interface:  +  +* `getUserInput(formStructure, formState)`​: Prompt the user to fill out a form.  +The first argument describes the form’s structure (as will be passed to  +​mct­form​) while the second argument contains the initial state of that form.  +This returns a ​Promise​ for the state of the form after the user has filled it  +in; this promise will be rejected if the user cancels input.  +* `getUserChoice(dialogStructure)​`: Prompt the user to make a single choice from  +a set of options, which (in the platform implementation) will be expressed as  +buttons in the displayed dialog. Returns a ​Promise​ for the user’s choice, which  +will be rejected if the user cancels input.  + +### Dialog Structure + +The object passed as the ​dialogStructure​ to ​getUserChoice​ should have the  +following properties: + +* `title​`: The title to display at the top of the dialog.  +* `hint​`: Short message to display below the title.  +* `template​`: Identifying ​key​ (as will be passed to ​mct­include​) for the  +template which will be used to populate the inner area of the dialog.  +* `model​`: Model to pass in the ​ng­model​ attribute of ​mct­include​.  +* `parameters​`: Parameters to pass in the ​parameters​ attribute of ​mct­include​.  +* `options​`: An array of options describing each button at the bottom. Each  +option may have the following properties: + * `name`​: Human­readable name to display in the button.  + * `key`​: Machine­readable key, to pass as the result of the resolved promise  + when clicked.  + * `description​`: Description to show in tooltip on hover.  + +## Domain Object Service + +The ​objectService​ provides domain object instances. It has the following  +interface: + +* `getObjects(ids)`​: For the provided array of domain object identifiers,  +returns a Promise​ for an object containing key­value pairs, where keys are  +domain object identifiers and values are corresponding ​DomainObject​ instances.  +Note that the result may contain a superset or subset of the objects requested.  + +## Gesture Service + +The `​gestureService​` is used to attach gestures (see extension category  +​gestures​) to representations. It has the following interface: + +* `attachGestures(element, domainObject, keys)`​: Attach gestures specified by  +the provided gesture ​keys​ (an array of strings) to this jqLite­wrapped HTML  +element​, which represents the specified ​domainObject​. Returns an object with a  +single method `​destroy()`​, to be invoked when it is time to detach these  +gestures.  + +## Model Service + +The ​modelService​ provides domain object models. It has the following interface:  +  +* `getModels(ids)`​: For the provided array of domain object identifiers, returns  +a Promise​ for an object containing key­value pairs, where keys are domain object  +identifiers and values are corresponding domain object models. Note that the  +result may contain a superset or subset of the models requested.  + +## Persistence Service + +The ​persistenceService​ provides the ability to load/store JavaScript objects  +(presumably serializing/deserializing to JSON in the process.) This is used  +primarily to store domain object models. It has the following interface:  +  +* `listSpaces()`​: Returns a ​Promise​ for an array of strings identifying the  +different persistence spaces this service supports. Spaces are intended to be  +used to distinguish between different underlying persistence stores, to allow  +these to live side by side.  +* `listObjects()`​: Returns a Promise for an array of strings identifying all  +documents stored in this persistence service.  +* `createObject(space, key, value)`​: Create a new document in the specified  +persistence ​space​, identified by the specified ​key​, the contents of which shall  +match the specified ​value​. Returns a promise that will be rejected if creation  +fails.  +* `readObject(space, key)`​: Read an existing document in the specified  +persistence space​, identified by the specified ​key​. Returns a promise for the  +specified document; this promise will resolve to ​undefined​ if the document does  +not exist.  +* `updateObject(space, key, value)`​: Update an existing document in the  +specified persistence ​space​, identified by the specified ​key​, such that its  +contents match the specified ​value​. Returns a promise that will be rejected if  +the update fails.  +* `deleteObject(space, key)`​: Delete an existing document from the specified  +persistence ​space​, identified by the specified ​key​. Returns a promise which will  +be rejected if deletion fails.  + +## Policy Service + +The ​policyService​ may be used to determine whether or not certain behaviors are  allowed within the application. It has the following interface:    - * allow(category, candidate, context, [callback])​: Check if this decision  - should be allowed. Returns a boolean. Its arguments are interpreted as:  - ○ category​: A string identifying which kind of decision is being made. See the  - section on Policies for categories supported by the platform; plugins may define  - and utilize policies of additional categories, as well.  - ○ candidate​: An object representing the thing which shall or shall not be allowed.  - Usually, this will be an instance of an extension of the category defined above.  - This does need to be the case; additional policies which are not specific to any  - extension may also be defined and consulted using unique category identifiers. In  - this case, the type of the object delivered for the candidate may be unique to the  - policy type.  - ○ context​: An object representing the context in which the decision is occurring.  - Its contents are specific to each policy category.  - ○ callback​: Optional; a function to call if the policy decision is rejected. This  - function will be called with the message string (which may be undefined) of  - whichever individual policy caused the operation to fail.  -  -  -Telemetry Service -  - The ​telemetryService​ is used to acquire telemetry data. See the section on  -Telemetry in Core API for more information on how both the arguments and responses of this  -service are structured.  - When acquiring telemetry for display, it is recommended that the ​telemetryHandler  -service be used instead of this service. The ​telemetryHandler​ has additional support for  -subscribing to and requesting telemetry data associated with domain objects or groups of  -domain objects. See the Other Services section for more information.  - The ​telemetryService​ has the following interface:  -  - * requestTelemetry(requests)​: Issue a request for telemetry, matching the  - specified telemetry ​requests​. Returns a ​Promise​ for a telemetry response object.   - * subscribe(callback, requests)​: Subscribe to real­time updates for telemetry,  - matching the specified ​requests​. The specified ​callback​ will be invoked with  - telemetry response objects as they become available. This method returns a function  - which can be invoked to terminate the subscription.  -  - 49  -Type Service -  - The ​typeService​ exposes domain object types. It has the following interface:  -  - * listTypes()​: Returns all domain object types supported in the application, as an  - array of ​Type​ instances.  - * getType(key)​: Returns the ​Type​ instance identified by the provided key, or  - undefined​ if no such type exists.  -  -View Service -  - The ​viewService​ exposes definitions for views of domain objects. It has the following  -interface:  -  - * getViews(domainObject):​ Get an array of extension definitions of category ​views  - which are valid and applicable to the specified ​domainObject​.  -  -Other Services -  -Drag and Drop -  - The ​dndService​ provides information about the content of an active drag­and­drop  -gesture within the application. It is intended to complement the ​DataTransfer​ API of HTML5  -drag­and­drop, by providing access to non­serialized JavaScript objects being dragged, as well  -as by permitting inspection during drag (which is normally prohibited by browsers for security  -reasons.)  - The ​dndService​ has the following methods:  -  - * setData(key, value)​: Set drag data associated with a given type, specified by the  - key​ argument.  - * getData(key)​: Get drag data associated with a given type, specified by the ​key  - argument.  - * removeData(key)​: Clear drag data associated with a given type, specified by the ​key  - argument.  -  +* `allow(category, candidate, context, [callback])`​: Check if this decision  +should be allowed. Returns a boolean. Its arguments are interpreted as:  + * `category​`: A string identifying which kind of decision is being made. See  + the section on Policies for categories supported by the platform; plugins  + may define and utilize policies of additional categories, as well.  + * `candidate​`: An object representing the thing which shall or shall not be  + allowed. Usually, this will be an instance of an extension of the category  + defined above. This does need to be the case; additional policies which are  + not specific to any extension may also be defined and consulted using unique  + category identifiers. In this case, the type of the object delivered for the  + candidate may be unique to the policy type.  + * `context​`: An object representing the context in which the decision is  + occurring. Its contents are specific to each policy category.  + * `callback`​: Optional; a function to call if the policy decision is rejected.  + This function will be called with the message string (which may be  + undefined) of whichever individual policy caused the operation to fail.  - - 50  -Navigation +## Telemetry Service + +The ​`telemetryService​` is used to acquire telemetry data. See the section on  +Telemetry in Core API for more information on how both the arguments and  +responses of this service are structured.  + +When acquiring telemetry for display, it is recommended that the  +`​telemetryHandler` service be used instead of this service. The  +`​telemetryHandler​` has additional support for subscribing to and requesting  +telemetry data associated with domain objects or groups of domain objects. See  +the [Other Services](#Other-Services) section for more information.  + +The `​telemetryService​` has the following interface: + +* `requestTelemetry(requests)`​: Issue a request for telemetry, matching the  +specified telemetry ​requests​. Returns a _​Promise​_ for a telemetry response  +object. +* `subscribe(callback, requests)`​: Subscribe to real­time updates for telemetry,  +matching the specified `​requests​`. The specified `​callback​` will be invoked with  +telemetry response objects as they become available. This method returns a  +function which can be invoked to terminate the subscription.  + +## Type Service + +The `​typeService​` exposes domain object types. It has the following interface: + +* `listTypes()`​: Returns all domain object types supported in the application,  +as an array of ​Type​ instances. +* `getType(key)`​: Returns the `​Type​` instance identified by the provided key, or  +undefined​ if no such type exists.  + +## View Service + +The `​viewService​` exposes definitions for views of domain objects. It has the  +following interface: +  +* `getViews(domainObject)`:​ Get an array of extension definitions of category  +`​views` which are valid and applicable to the specified `​domainObject​`.  +  +## Other Services + +### Drag and Drop + +The `​dndService​` provides information about the content of an active  +drag­and­drop gesture within the application. It is intended to complement the  +`​DataTransfer​` API of HTML5 drag­and­drop, by providing access to non­serialized  +JavaScript objects being dragged, as well as by permitting inspection during  +drag (which is normally prohibited by browsers for security reasons.)  + +The `​dndService​` has the following methods:  + +* `setData(key, value)`​: Set drag data associated with a given type, specified  +by the `key​` argument.  +* `getData(key)`​: Get drag data associated with a given type, specified by the ​ +`key` argument.  +* `removeData(key)`​: Clear drag data associated with a given type, specified by  +the ​`key` argument.  + +# Navigation   The ​navigationService​ provides information about the current navigation state of  the application; that is, which object is the user currently viewing? This service merely tracks this  From 37dede568c883cd70e24562cbb7d12874116b4ec Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Tue, 29 Sep 2015 15:30:12 -0700 Subject: [PATCH 008/488] Added Capabilities --- docs/src/guide/index.md | 470 ++++++++++++++++++++-------------------- 1 file changed, 237 insertions(+), 233 deletions(-) diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 0f83d1dd0b..019afb75a7 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -1744,256 +1744,260 @@ by the `key​` argument.  * `removeData(key)`​: Clear drag data associated with a given type, specified by  the ​`key` argument.  -# Navigation -  - The ​navigationService​ provides information about the current navigation state of  -the application; that is, which object is the user currently viewing? This service merely tracks this  -state and notifies listeners; it does not take immediate action when navigation changes,  -although its listeners might.  - The ​navigationService​ has the following methods:  -  - * getNavigation()​: Get the current navigation state. Returns a ​DomainObject​.  - * setNavigation(domainObject)​: Set the current navigation state. Returns a  - DomainObject​.  - * addListener(callback)​: Listen for changes in navigation state. The provided  - callback​ should be a ​Function​ which takes a single ​DomainObject​ as an  - argument.  - * removeListener(callback)​: Stop listening for changes in navigation state. The  - provided ​callback​ should be a ​Function​ which has previously been passed to  - addListener​.  -  -Now -  - The service ​now​ is a function which acts as a simple wrapper for ​Date.now()​. It is  -present mainly so that this functionality may be more easily mocked in tests for scripts which  -use the current time.  -  -Telemetry Formatter -  - The ​telemetryFormatter​ is a utility for formatting domain and range values read  -from a telemetry series.  - The ​telemetryFormatter​ has the following methods:  -  - * formatDomainValue(value)​: Format the provided domain value (which will be  - assumed to be a timestamp) for display; returns a string.  - * formatRangeValue(value)​: Format the provided range value (a number) for  - display; returns a string.  +### Navigation   +The ​`navigationService​` provides information about the current navigation state  +of the application; that is, which object is the user currently viewing? This  +service merely tracks this state and notifies listeners; it does not take  +immediate action when navigation changes, although its listeners might.  - - 51  -Telemetry Handler -  - The ​telemetryHandler​ is a utility for retrieving telemetry data associated with  -domain objects; it is particularly useful for dealing with cases where the ​telemetry​ capability  -is delegated to contained objects (as occurs in Telemetry Panels.)  - The ​telemetryHandler​ has the following methods:  -  - * handle(domainObject, callback, [lossless])​: Subscribe to and issue  - future requests for telemetry associated with the provided ​domainObject​, invoking the  - provided ​callback​ function when streaming data becomes available. Returns a  - TelemetryHandle​ (see below.)  -  -Telemetry Handle -  - A ​TelemetryHandle​ has the following methods:  -  - * getTelemetryObjects()​: Get the domain objects (as a ​DomainObject[]​) that  - have a ​telemetry​ capability and are being handled here. Note that these are looked  - up asynchronously, so this method may return an empty array if the initial lookup is not  - yet completed.  - * promiseTelemetryObjects()​: As ​getTelemetryObjects()​, but returns a  - Promise​ that will be fulfilled when the lookup is complete.  - * unsubscribe()​: Unsubscribe to streaming telemetry updates associated with this  - handle.  - * getDomainValue(domainObject)​: Get the most recent domain value received via a  - streaming update for the specified ​domainObject​.  - * getRangeValue(domainObject)​: Get the most recent range value received via a  - streaming update for the specified ​domainObject​.  - * getMetadata()​: Get metadata (as reported by the ​getMetadata()​ method of a  - telemetry​ capability) associated with telemetry­providing domain objects. Returns an  - array, which is in the same order as ​getTelemetryObjects()​.  - * request(request, callback)​: Issue a new ​request​ for historical telemetry data.  - The provided ​callback​ will be invoked when new data becomes available, which may  - occur multiple times (e.g. if there are multiple domain objects.) It will be invoked with the  - DomainObject​ for which a new series is available, and the ​TelemetrySeries​ itself,  - in that order.  - * getSeries(domainObject)​: Get the latest ​TelemetrySeries​ (as resulted from a  - previous ​request(...)​ call) available for this domain object.  -  - 52  -Models -  - Domain object models in Open MCT Web are JavaScript objects describing the  -persistent state of the domain objects they describe. Their contents include a mix of commonly  -understood metadata attributes; attributes which are recognized by and/or determine the  -applicability of specific extensions; and properties specific to given types.  -  -General Metadata -  - Some properties of domain object models have a ubiquitous meaning through Open  +The `​navigationService​` has the following methods: + +* `getNavigation()`​: Get the current navigation state. Returns a ​`DomainObject​`.  +* `setNavigation(domainObject)`​: Set the current navigation state. Returns a  +`DomainObject​`.  +* `addListener(callback)`​: Listen for changes in navigation state. The provided  +`callback​` should be a `​Function​` which takes a single `​DomainObject​` as an  +argument.  +* `removeListener(callback)`​: Stop listening for changes in navigation state.  +The provided `​callback​` should be a `​Function​` which has previously been passed  +to addListener​. + +### Now + +The service ​now​ is a function which acts as a simple wrapper for ​Date.now()​. It  +is present mainly so that this functionality may be more easily mocked in tests  +for scripts which use the current time.  + +### Telemetry Formatter + +The `​telemetryFormatter​` is a utility for formatting domain and range values  +read from a telemetry series.  + +The ​`telemetryFormatter​` has the following methods:  + +* `formatDomainValue(value)`​: Format the provided domain value (which will be  +assumed to be a timestamp) for display; returns a string.  +* `formatRangeValue(value)`​: Format the provided range value (a number) for  +display; returns a string. + +### Telemetry Handler + +The ​telemetryHandler​ is a utility for retrieving telemetry data associated with  +domain objects; it is particularly useful for dealing with cases where the  +​telemetry​ capability is delegated to contained objects (as occurs in Telemetry  +Panels.)  + +The ​telemetryHandler​ has the following methods:  + +* `handle(domainObject, callback, [lossless])`​: Subscribe to and issue future  +requests for telemetry associated with the provided ​domainObject​, invoking the  +provided ​callback​ function when streaming data becomes available. Returns a  +`TelemetryHandle`​ (see below.)  + +#### Telemetry Handle + +A ​TelemetryHandle​ has the following methods:  + +* `getTelemetryObjects()`​: Get the domain objects (as a ​`DomainObject[]`​) that  +have a ​telemetry​ capability and are being handled here. Note that these are  +looked up asynchronously, so this method may return an empty array if the  +initial lookup is not yet completed.  +* `promiseTelemetryObjects()`​: As `​getTelemetryObjects()`​, but returns a Promise​  +that will be fulfilled when the lookup is complete.  +* `unsubscribe()`​: Unsubscribe to streaming telemetry updates associated with  +this handle.  +* `getDomainValue(domainObject)`​: Get the most recent domain value received via  +a streaming update for the specified `​domainObject​`.  +* `getRangeValue(domainObject)`​: Get the most recent range value received via a  +streaming update for the specified `​domainObject`​.  +* `getMetadata()`​: Get metadata (as reported by the `​getMetadata()`​ method of a  +telemetry​ capability) associated with telemetry­providing domain objects.  +Returns an array, which is in the same order as ​getTelemetryObjects()​.  +* `request(request, callback)`​: Issue a new ​request​ for historical telemetry  +data. The provided ​callback​ will be invoked when new data becomes available,  +which may occur multiple times (e.g. if there are multiple domain objects.) It  +will be invoked with the DomainObject​ for which a new series is available, and  +the ​TelemetrySeries​ itself, in that order.  +* `getSeries(domainObject)`​: Get the latest `​TelemetrySeries​` (as resulted from  +a previous ​`request(...)`​ call) available for this domain object. + + +# Models +Domain object models in Open MCT Web are JavaScript objects describing the  +persistent state of the domain objects they describe. Their contents include a  +mix of commonly understood metadata attributes; attributes which are recognized  +by and/or determine the applicability of specific extensions; and properties  +specific to given types.  + +## General Metadata + +Some properties of domain object models have a ubiquitous meaning through Open  MCT Web and can be utilized directly:  -  - * name​: The human­readable name of the domain object.  -  -Extension-specific Properties -  - Other properties of domain object models have specific meaning imposed by other  + +* `name`​: The human­readable name of the domain object. + +## Extension-specific Properties + +Other properties of domain object models have specific meaning imposed by other  extensions within the Open MCT Web platform.  -  -Capability-specific Properties -  - Some properties either trigger the presence/absence of certain capabilities, or are  -managed by specific capabilities:  -  - * composition​: An array of domain object identifiers that represents the contents of this  - domain object (e.g. as will appear in the tree hierarchy.) Understood by the  - composition​ capability; the presence or absence of this property determines the  - presence or absence of that capability.  - * modified​: The timestamp (in milliseconds since the UNIX epoch) of the last  - modification made to this domain object. Managed by the ​mutation​ capability.  - * persisted​: The timestamp (in milliseconds since the UNIX epoch) of the last time  - when changes to this domain object were persisted. Managed by the ​persistence  - capability.  - * relationships​: An object containing key­value pairs, where keys are symbolic  - identifiers for relationship types, and values are arrays of domain object identifiers. Used  - by the ​relationship​ capability; the presence or absence of this property determines  - the presence or absence of that capability.  - * telemetry​: An object which serves as a template for telemetry requests associated  - with this domain object (e.g. specifying ​source​ and ​key​; see Telemetry Requests  - 53  - under Core API.) Used by the ​telemetry​ capability; the presence or absence of this  - property determines the presence or absence of that capability.  - * type​: A string identifying the type of this domain object. Used by the ​type​ capability.  -  -View Configurations -  - Persistent configurations for specific views of domain objects are stored in the domain  -object model under the property ​configurations​. This is an object containing key­value  -pairs, where keys identify the view, and values are objects containing view­specific (and  -view­managed) configuration properties.  -  -Modifying Models -  - When interacting with a domain object’s model, it is possible to make modifications to it  -directly. ​Don’t! ​These changes may not be properly detected by the platform, meaning that  -other representations of the domain object may not be updated, changes may not be saved at  -the expected times, and generally, that unexpected behavior may occur.  - Instead, use the ​mutation​ capability.  -  - - 54  -Capabilities +### Capability-specific Properties + +Some properties either trigger the presence/absence of certain capabilities, or  +are managed by specific capabilities: + +* `composition​`: An array of domain object identifiers that represents the  +contents of this domain object (e.g. as will appear in the tree hierarchy.)  +Understood by the composition​ capability; the presence or absence of this  +property determines the presence or absence of that capability.  +* `modified​`: The timestamp (in milliseconds since the UNIX epoch) of the last  +modification made to this domain object. Managed by the ​mutation​ capability.  +* `persisted​`: The timestamp (in milliseconds since the UNIX epoch) of the last  +time when changes to this domain object were persisted. Managed by the  +​persistence capability.  +* `relationships​`: An object containing key­value pairs, where keys are symbolic  +identifiers for relationship types, and values are arrays of domain object  +identifiers. Used by the ​relationship​ capability; the presence or absence of  +this property determines the presence or absence of that capability.  +* `telemetry​`: An object which serves as a template for telemetry requests  +associated with this domain object (e.g. specifying ​`source`​ and ​`key​`; see  +Telemetry Requests under Core API.) Used by the ​telemetry​ capability; the  +presence or absence of this property determines the presence or absence of that  +capability. +* `type`​: A string identifying the type of this domain object. Used by the ​type​  +capability.    - Dynamic behavior associated with a domain object is expressed as capabilities. A  -capability is a JavaScript object with an interface that is specific to the type of capability in use.  - Often, there is a relationship between capabilities and services. For instance, there is an  -action​ capability and an ​actionService​, and there is a ​telemetry​ capability as well as a  -telemetryService​. Typically, the pattern here is that the capability will utilize the service ​for  -the specific domain object​.   - When interacting with domain objects, it is generally preferable to use a capability  -instead of a service when the option is available. Capability interfaces are typically easier to use  -and/or more powerful in these situations. Additionally, this usage provides a more robust  -substitutability mechanism; for instance, one could configure a plugin such that it provided a  -totally new implementation of a given capability which might not invoke the underlying service,  -while user code which interacts with capabilities remains indifferent to this detail.  +### View Configurations + +Persistent configurations for specific views of domain objects are stored in the  +domain object model under the property ​configurations​. This is an object  +containing key­value pairs, where keys identify the view, and values are objects  +containing view­specific (and view­managed) configuration properties.    -Action +## Modifying Models +When interacting with a domain object’s model, it is possible to make  +modifications to it directly. __​Don't!__ ​These changes may not be properly detected  +by the platform, meaning that other representations of the domain object may not  +be updated, changes may not be saved at the expected times, and generally, that  +unexpected behavior may occur. Instead, use the `​mutation​` capability.  + +# Capabilities + +Dynamic behavior associated with a domain object is expressed as capabilities. A  +capability is a JavaScript object with an interface that is specific to the type  +of capability in use.  + +Often, there is a relationship between capabilities and services. For instance,  +there is an action​ capability and an ​actionService​, and there is a ​telemetry​  +capability as well as a `telemetryService​`. Typically, the pattern here is that  +the capability will utilize the service ​for the specific domain object​.   + +When interacting with domain objects, it is generally preferable to use a  +capability instead of a service when the option is available. Capability  +interfaces are typically easier to use and/or more powerful in these situations.  +Additionally, this usage provides a more robust substitutability mechanism; for  +instance, one could configure a plugin such that it provided a totally new  +implementation of a given capability which might not invoke the underlying  +service, while user code which interacts with capabilities remains indifferent  +to this detail.  +  +## Action + +The ​`action​` capability is present for all domain objects. It allows applicable  +​`Action` instances to be retrieved and performed for specific domain objects.  + +For example:  + `domainObject.getCapability("action").perform("navigate"); ` + ...will initiate a navigate action upon the domain object, if an action with  + key "navigate" is defined.    - The ​action​ capability is present for all domain objects. It allows applicable ​Action  -instances to be retrieved and performed for specific domain objects.  - For example:  - domainObject.getCapability("action").perform("navigate");  - ...will initiate a navigate action upon the domain object, if an action with key "navigate" is  -defined.  -   - This capability has the following interface:  -   - * getActions(context)​: Get the actions that are applicable in the specified action  - context​; the capability will fill in the ​domainObject​ field of this context if necessary. If  - context​ is specified as a string, they will instead be used as the ​key​ of the action  - context. Returns an array of ​Action​ instances.  - * perform(context)​: Perform an action. This will find and perform the first matching  - action available for the specified action ​context​, filling in the ​domainObject​ field as  - necessary. If ​context​ is specified as a string, they will instead be used as the ​key​ of  - the action context. Returns a ​Promise​ for the result of the action that was performed, or  - undefined if no matching action was found.  +This capability has the following interface:  +* `getActions(context)`​: Get the actions that are applicable in the specified  +action `context​`; the capability will fill in the `​domainObject​` field of this  +context if necessary. If context​ is specified as a string, they will instead be  +used as the ​`key`​ of the action context. Returns an array of ​`Action​` instances. +* `perform(context)​`: Perform an action. This will find and perform the first  +matching action available for the specified action ​context​, filling in the  +`​domainObject​` field as necessary. If ​`context​` is specified as a string, they  +will instead be used as the `​key​` of the action context. Returns a `​Promise​` for  +the result of the action that was performed, or `undefined` if no matching action  +was found.  + +## Composition   - 55  -Composition -  - The ​composition​ capability provides access to domain objects that are contained by  -this domain object. While the ​composition​ property of a domain object’s model describes  -these contents (by their identifiers), the ​composition​ capability provides a means to load the  -corresponding ​DomainObject​ instances in the same order. The absence of this property in the  +The `​composition​` capability provides access to domain objects that are  +contained by this domain object. While the ​`composition​` property of a domain  +object’s model describes these contents (by their identifiers), the ​ +`composition​` capability provides a means to load the corresponding  +`​DomainObject​` instances in the same order. The absence of this property in the  model will result in the absence of this capability in the domain object.  - This capability has the following interface:  -  - * invoke()​: Returns a ​Promise​ for an array of ​DomainObject​ instances.  -Delegation -   - The ​delegation​ capability is used to communicate the intent of a domain object to  -delegate responsibilities, which would normally handled by other capabilities, to the domain  -objects in its composition.  - This capability has the following interface:  -  - * getDelegates(key)​: Returns a ​Promise​ for an array of ​DomainObject​ instances,  - to which this domain object wishes to delegate the capability with the specified ​key​.  - * invoke(key)​: Alias of ​getDelegates(key)​.  - * doesDelegate(key)​: Returns ​true​ if the domain object does delegate the capability  - with the specified ​key​.   -  - The platform implementation of the ​delegation​ capability inspects the domain object’s  -type definition for a property ​delegates​, whose value is an array of strings describing which  -capabilities domain objects of that type wish to delegate. If this property is not present, the  -delegation​ capability will not be present in domain objects of that type.   -  +This capability has the following interface:    +* `invoke()`​: Returns a `​Promise​` for an array of `​DomainObject​` instances. - - 56  -Editor +## Delegation + +The ​delegation​ capability is used to communicate the intent of a domain object  +to delegate responsibilities, which would normally handled by other  +capabilities, to the domain objects in its composition.  + +This capability has the following interface:    - The ​editor​ capability is meant primarily for internal use by Edit mode, and helps to  -manage the behavior associated with exiting Edit mode via Save or Cancel. Its interface is not  -intended for general use. However, ​domainObject.hasCapability(‘editor’)​ is a  -useful way of determining whether or not we are looking at an object in Edit mode.  -   +* `getDelegates(key)`​: Returns a ​Promise​ for an array of ​DomainObject​ instances,  +to which this domain object wishes to delegate the capability with the specified ​ +key​.  +* `invoke(key)`​: Alias of ​getDelegates(key)​.  +* `doesDelegate(key)`​: Returns ​true​ if the domain object does delegate the  +capability with the specified ​key​.   + +The platform implementation of the ​delegation​ capability inspects the domain  +object’s type definition for a property ​delegates​, whose value is an array of  +strings describing which capabilities domain objects of that type wish to  +delegate. If this property is not present, the delegation​ capability will not be  +present in domain objects of that type.   + +## Editor + +The ​editor​ capability is meant primarily for internal use by Edit mode, and  +helps to manage the behavior associated with exiting Edit mode via Save or  +Cancel. Its interface is not intended for general use. However,  +`​domainObject.hasCapability(‘editor’)`​ is a useful way of determining whether or  +not we are looking at an object in Edit mode.   -Mutation +## Mutation + +The ​`mutation​` capability provides a means by which the contents of a domain  +object’s model can be modified. This capability is provided by the platform for  +all domain objects, and has the following interface: + +* `mutate(mutator, [timestamp])`​: Modify the domain object’s model using the  +specified `​mutator​` function. After changes are made, the ​`modified​` property of  +the model will be updated with the specified ​`timestamp​`, if one was provided,  +or with the current system time.  +* `invoke(...)​`: Alias of ​`mutate​`. + +Changes to domain object models should only be made via the ​`mutation​`  +capability; other platform behavior is likely to break (either by exhibiting  +undesired behavior, or failing to exhibit desired behavior) if models are  +modified by other means.   - The ​mutation​ capability provides a means by which the contents of a domain object’s  -model can be modified. This capability is provided by the platform for all domain objects, and  -has the following interface:  -  - * mutate(mutator, [timestamp])​: Modify the domain object’s model using the  - specified ​mutator​ function. After changes are made, the ​modified​ property of the  - model will be updated with the specified ​timestamp​, if one was provided, or with the  - current system time.  - * invoke(...)​: Alias of ​mutate​.  -  - Changes to domain object models should only be made via the ​mutation​ capability;  -other platform behavior is likely to break (either by exhibiting undesired behavior, or failing to  -exhibit desired behavior) if models are modified by other means.  -  -Mutator Function -  - The ​mutator​ argument above is a function which will receive a cloned copy of the  +### Mutator Function + +The ​mutator​ argument above is a function which will receive a cloned copy of the  domain object’s model as a single argument. It may return:  -  - * A ​Promise​, in which case the resolved value of the promise will be used to determine  - which of the following forms is used.  - * Boolean ​false​, in which case the mutation is cancelled.  - * A JavaScript object, in which case this object will be used as the new model for this  - domain object.  - 57  - * No value (or, equivalently, ​undefined​), in which case the cloned copy (including any  - changes made in place by the mutator function) will be used as the new domain object  - model.  -  -Persistence + +* A ​`Promise​`, in which case the resolved value of the promise will be used to  +determine which of the following forms is used.  +* Boolean ​`false​`, in which case the mutation is cancelled.  +* A JavaScript object, in which case this object will be used as the new model  +for this domain object. +* No value (or, equivalently, `​undefined​`), in which case the cloned copy  +(including any changes made in place by the mutator function) will be used as  +the new domain object model.  + +## Persistence   The ​persistence​ capability provides a mean for interacting with the underlying  persistence service which stores this domain object’s model. It has the following interface:  From 7014808c1374e68999e82aa5708f3ffcd50ef751 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 2 Oct 2015 15:41:02 -0700 Subject: [PATCH 009/488] [Frontend] Style and display changes to status block open #159 open #170 Moved classes from _bottom-bar.scss into _layout.scss and new file _messages.scss; Removed _bottom-bar.scss; Revised display behavior of .status.block to show label on hover; --- platform/commonUI/general/res/sass/_main.scss | 2 +- .../_messages.scss} | 58 ++-- .../res/sass/user-environ/_layout.scss | 20 +- .../general/res/templates/indicator.html | 4 +- .../espresso/res/css/theme-espresso.css | 289 +++++++++--------- .../themes/snow/res/css/theme-snow.css | 289 +++++++++--------- 6 files changed, 338 insertions(+), 324 deletions(-) rename platform/commonUI/general/res/sass/{user-environ/_bottom-bar.scss => controls/_messages.scss} (53%) diff --git a/platform/commonUI/general/res/sass/_main.scss b/platform/commonUI/general/res/sass/_main.scss index 80023f55ac..8241078c50 100644 --- a/platform/commonUI/general/res/sass/_main.scss +++ b/platform/commonUI/general/res/sass/_main.scss @@ -39,6 +39,7 @@ @import "controls/controls"; @import "controls/lists"; @import "controls/menus"; +@import "controls/messages"; @import "controls/time-controller"; @import "mobile/controls/menus"; @@ -62,7 +63,6 @@ @import "mobile/tree"; @import "user-environ/frame"; @import "user-environ/top-bar"; -@import "user-environ/bottom-bar"; @import "user-environ/tool-bar"; /********************************* VIEWS */ diff --git a/platform/commonUI/general/res/sass/user-environ/_bottom-bar.scss b/platform/commonUI/general/res/sass/controls/_messages.scss similarity index 53% rename from platform/commonUI/general/res/sass/user-environ/_bottom-bar.scss rename to platform/commonUI/general/res/sass/controls/_messages.scss index dd705b0000..ec83528fba 100644 --- a/platform/commonUI/general/res/sass/user-environ/_bottom-bar.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -19,46 +19,17 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -.ue-bottom-bar { - background: $colorFooterBg; - color: lighten($colorBodyBg, 30%); - font-size: .7rem; - //line-height: $ueFooterH - 4px; - //line-height: $ueFooterH; // New status bar design - .status-holder { - //@include border-radius($basicCr * 1.75); // New status bar design - @include box-sizing(border-box); - //background: $colorFooterBg; - //border-bottom: 1px solid lighten($colorBodyBg, 10%); // New status bar design - @include absPosDefault($interiorMargin); - @include ellipsize(); - line-height: $ueFooterH - ($interiorMargin * 2); - right: 120px; - text-transform: uppercase; - } - .app-logo { - @include box-sizing(border-box); - @include absPosDefault($interiorMargin); - left: auto; - cursor: pointer; - //font-size: 0.8em; - //line-height: $ueFooterH - 10px; - //padding-top: 1px; - //text-transform: uppercase; - &.logo-openmctweb { - background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center; - } - } -} .status.block { - //display: inline-block; - display: inline; // New status bar design. Inline to support ellipsis overflow - margin-right: $interiorMarginLg; + //@include test(); + cursor: help; + display: inline-block; + margin-right: $interiorMargin; + .status-indicator, + .label { + vertical-align: top; + } .status-indicator { - //@include border-radius($controlCr * 0.9); - //@include box-shadow(inset rgba(black, 0.5) 0 0 3px); - //@include text-shadow(rgba(black, 0.3) 0 0 2px); display: inline-block; margin-right: $interiorMarginSm; color: $colorKey; @@ -69,4 +40,17 @@ color: #ffaa00; } } + .label { + // Max-width silliness is necessary for width transition + @include trans-prop-nice(max-width, .25s); + display: inline-block; + overflow: hidden; + max-width: 0px; + } + &:hover { + .label { + max-width: 150px; + width: auto; + } + } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 4661c47214..6a11e22eb0 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -125,19 +125,35 @@ } } + // from _bottom-bar.scss .ue-bottom-bar { - //@include absPosDefault($bodyMargin); @include absPosDefault(0);// New status bar design top: auto; height: $ueFooterH; + line-height: $ueFooterH - ($interiorMargin * 2); + background: $colorFooterBg; + color: lighten($colorBodyBg, 30%); + font-size: .7rem; + .status-holder { - //right: $ueAppLogoW + $bodyMargin; New status bar design + @include box-sizing(border-box); + @include absPosDefault($interiorMargin); + @include ellipsize(); + //line-height: $ueFooterH - ($interiorMargin * 2); + right: 120px; + text-transform: uppercase; z-index: 1; } .app-logo { + @include box-sizing(border-box); + @include absPosDefault($interiorMargin); + cursor: pointer; left: auto; width: $ueAppLogoW; z-index: 2; + &.logo-openmctweb { + background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center; + } } } } diff --git a/platform/commonUI/general/res/templates/indicator.html b/platform/commonUI/general/res/templates/indicator.html index 34ea2fe9c8..963d35ec14 100644 --- a/platform/commonUI/general/res/templates/indicator.html +++ b/platform/commonUI/general/res/templates/indicator.html @@ -20,7 +20,7 @@ at runtime from the About dialog for additional information. -->
@@ -28,7 +28,7 @@ ng-class='ngModel.getGlyphClass()'> {{ngModel.getGlyph()}} - {{ngModel.getText()}} diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 6eb39c0b0a..16cf89a2ea 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -2283,6 +2283,69 @@ label.checkbox.custom { right: 0; width: auto; } +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 23, ../../../../general/res/sass/controls/_messages.scss */ +.status.block { + cursor: help; + display: inline-block; + margin-right: 5px; } + /* line 29, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator, + .status.block .label { + vertical-align: top; } + /* line 34, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator { + display: inline-block; + margin-right: 3px; + color: #0099cc; } + /* line 38, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator.ok { + color: #009900; } + /* line 41, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator.caution { + color: #ffaa00; } + /* line 45, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .label { + -moz-transition-property: max-width; + -o-transition-property: max-width; + -webkit-transition-property: max-width; + transition-property: max-width; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + display: inline-block; + overflow: hidden; + max-width: 0px; } + /* line 54, ../../../../general/res/sass/controls/_messages.scss */ + .status.block:hover .label { + max-width: 150px; + width: auto; } + /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { position: relative; @@ -3106,7 +3169,7 @@ span.req { /* line 123, ../../../../general/res/sass/user-environ/_layout.scss */ .user-environ .edit-area .work-area { top: 40px; } -/* line 128, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 129, ../../../../general/res/sass/user-environ/_layout.scss */ .user-environ .ue-bottom-bar { overflow: hidden; position: absolute; @@ -3117,21 +3180,56 @@ span.req { width: auto; height: auto; top: auto; - height: 25px; } - /* line 133, ../../../../general/res/sass/user-environ/_layout.scss */ + height: 25px; + line-height: 15px; + background: #000; + color: gray; + font-size: .7rem; } + /* line 138, ../../../../general/res/sass/user-environ/_layout.scss */ .user-environ .ue-bottom-bar .status-holder { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; + width: auto; + height: auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + right: 120px; + text-transform: uppercase; z-index: 1; } - /* line 137, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 147, ../../../../general/res/sass/user-environ/_layout.scss */ .user-environ .ue-bottom-bar .app-logo { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; + width: auto; + height: auto; + cursor: pointer; left: auto; width: 105px; z-index: 2; } + /* line 154, ../../../../general/res/sass/user-environ/_layout.scss */ + .user-environ .ue-bottom-bar .app-logo.logo-openmctweb { + background: url("../../../../general/res/images/logo-openmctweb.svg") no-repeat center center; } -/* line 145, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 161, ../../../../general/res/sass/user-environ/_layout.scss */ .cols { overflow: hidden; *zoom: 1; } - /* line 147, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 163, ../../../../general/res/sass/user-environ/_layout.scss */ .cols .col { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -3142,114 +3240,114 @@ span.req { margin-left: 1.5%; padding-left: 5px; position: relative; } - /* line 155, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 171, ../../../../general/res/sass/user-environ/_layout.scss */ .cols .col:first-child { margin-left: 0; padding-left: 0; } - /* line 162, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 178, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-2 .col-1 { min-width: 250px; width: 48.5%; } - /* line 168, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 184, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-2-ff .col-100px { width: 100px; } - /* line 175, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 191, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-6 .col-1 { min-width: 83.33333px; width: 15.16667%; } - /* line 181, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 197, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-16 .col-1 { min-width: 31.25px; width: 4.75%; } - /* line 184, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 200, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-16 .col-2 { min-width: 62.5px; width: 11%; } - /* line 187, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 203, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-16 .col-7 { min-width: 218.75px; width: 42.25%; } - /* line 193, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 209, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-32 .col-2 { min-width: 31.25px; width: 4.75%; } - /* line 196, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 212, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-32 .col-15 { min-width: 234.375px; width: 45.375%; } - /* line 200, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 216, ../../../../general/res/sass/user-environ/_layout.scss */ .cols .l-row { overflow: hidden; *zoom: 1; padding: 5px 0; } -/* line 208, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 224, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-mode .split-layout .split-pane-component.pane.left { min-width: 150px; max-width: 800px; width: 25%; } -/* line 218, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 234, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right { width: 15%; } - /* line 220, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 236, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right .pane.bottom { min-height: 50px; height: 30%; } -/* line 230, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 246, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { position: absolute; } - /* line 233, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 249, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 236, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 252, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 241, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 257, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 244, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 260, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 251, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 267, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .s-menu span.left.l-click-area, .s-menu .pane.items .object-browse-bar span.left.l-click-area, .pane.items .object-browse-bar .right.abs, .pane.items .object-browse-bar .s-menu span.right.l-click-area, .s-menu .pane.items .object-browse-bar span.right.l-click-area { top: auto; } - /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-holder { top: 34px; } - /* line 266, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 282, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .object-holder { overflow: auto; } -/* line 274, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 290, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 277, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 293, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 284, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 300, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 291, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 307, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 293, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 302, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 318, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu, @@ -3261,12 +3359,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 331, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 320, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 336, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3282,39 +3380,28 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 344, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 330, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 346, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/*.object-holder { - .s-btn { - //background: red !important; - $h: 16px; - height: $h; - line-height: $h; - > span { - font-size: 0.7rem; - } - } -}*/ -/* line 350, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 354, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 353, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 357, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 360, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ .vscroll { overflow-y: auto; } @@ -4411,86 +4498,6 @@ ul.tree { bottom: auto; left: auto; } -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ -.ue-bottom-bar { - background: #000; - color: gray; - font-size: .7rem; } - /* line 28, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .ue-bottom-bar .status-holder { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - position: absolute; - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - width: auto; - height: auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - line-height: 15px; - right: 120px; - text-transform: uppercase; } - /* line 39, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .ue-bottom-bar .app-logo { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - position: absolute; - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - width: auto; - height: auto; - left: auto; - cursor: pointer; } - /* line 48, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .ue-bottom-bar .app-logo.logo-openmctweb { - background: url("../../../../general/res/images/logo-openmctweb.svg") no-repeat center center; } - -/* line 54, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ -.status.block { - display: inline; - margin-right: 10px; } - /* line 58, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .status.block .status-indicator { - display: inline-block; - margin-right: 3px; - color: #0099cc; } - /* line 65, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .status.block .status-indicator.ok { - color: #009900; } - /* line 68, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .status.block .status-indicator.caution { - color: #ffaa00; } - /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 3a819210eb..d9463b49ca 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -2231,6 +2231,69 @@ label.checkbox.custom { right: 0; width: auto; } +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 23, ../../../../general/res/sass/controls/_messages.scss */ +.status.block { + cursor: help; + display: inline-block; + margin-right: 5px; } + /* line 29, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator, + .status.block .label { + vertical-align: top; } + /* line 34, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator { + display: inline-block; + margin-right: 3px; + color: #0099cc; } + /* line 38, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator.ok { + color: #009900; } + /* line 41, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator.caution { + color: #ffaa00; } + /* line 45, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .label { + -moz-transition-property: max-width; + -o-transition-property: max-width; + -webkit-transition-property: max-width; + transition-property: max-width; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + display: inline-block; + overflow: hidden; + max-width: 0px; } + /* line 54, ../../../../general/res/sass/controls/_messages.scss */ + .status.block:hover .label { + max-width: 150px; + width: auto; } + /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { position: relative; @@ -3038,7 +3101,7 @@ span.req { /* line 123, ../../../../general/res/sass/user-environ/_layout.scss */ .user-environ .edit-area .work-area { top: 40px; } -/* line 128, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 129, ../../../../general/res/sass/user-environ/_layout.scss */ .user-environ .ue-bottom-bar { overflow: hidden; position: absolute; @@ -3049,21 +3112,56 @@ span.req { width: auto; height: auto; top: auto; - height: 25px; } - /* line 133, ../../../../general/res/sass/user-environ/_layout.scss */ + height: 25px; + line-height: 15px; + background: #000; + color: white; + font-size: .7rem; } + /* line 138, ../../../../general/res/sass/user-environ/_layout.scss */ .user-environ .ue-bottom-bar .status-holder { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; + width: auto; + height: auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + right: 120px; + text-transform: uppercase; z-index: 1; } - /* line 137, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 147, ../../../../general/res/sass/user-environ/_layout.scss */ .user-environ .ue-bottom-bar .app-logo { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; + width: auto; + height: auto; + cursor: pointer; left: auto; width: 105px; z-index: 2; } + /* line 154, ../../../../general/res/sass/user-environ/_layout.scss */ + .user-environ .ue-bottom-bar .app-logo.logo-openmctweb { + background: url("../../../../general/res/images/logo-openmctweb.svg") no-repeat center center; } -/* line 145, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 161, ../../../../general/res/sass/user-environ/_layout.scss */ .cols { overflow: hidden; *zoom: 1; } - /* line 147, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 163, ../../../../general/res/sass/user-environ/_layout.scss */ .cols .col { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -3074,114 +3172,114 @@ span.req { margin-left: 1.5%; padding-left: 5px; position: relative; } - /* line 155, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 171, ../../../../general/res/sass/user-environ/_layout.scss */ .cols .col:first-child { margin-left: 0; padding-left: 0; } - /* line 162, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 178, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-2 .col-1 { min-width: 250px; width: 48.5%; } - /* line 168, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 184, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-2-ff .col-100px { width: 100px; } - /* line 175, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 191, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-6 .col-1 { min-width: 83.33333px; width: 15.16667%; } - /* line 181, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 197, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-16 .col-1 { min-width: 31.25px; width: 4.75%; } - /* line 184, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 200, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-16 .col-2 { min-width: 62.5px; width: 11%; } - /* line 187, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 203, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-16 .col-7 { min-width: 218.75px; width: 42.25%; } - /* line 193, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 209, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-32 .col-2 { min-width: 31.25px; width: 4.75%; } - /* line 196, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 212, ../../../../general/res/sass/user-environ/_layout.scss */ .cols.cols-32 .col-15 { min-width: 234.375px; width: 45.375%; } - /* line 200, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 216, ../../../../general/res/sass/user-environ/_layout.scss */ .cols .l-row { overflow: hidden; *zoom: 1; padding: 5px 0; } -/* line 208, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 224, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-mode .split-layout .split-pane-component.pane.left { min-width: 150px; max-width: 800px; width: 25%; } -/* line 218, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 234, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right { width: 15%; } - /* line 220, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 236, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right .pane.bottom { min-height: 50px; height: 30%; } -/* line 230, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 246, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { position: absolute; } - /* line 233, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 249, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 236, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 252, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 241, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 257, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 244, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 260, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 251, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 267, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .s-menu span.left.l-click-area, .s-menu .pane.items .object-browse-bar span.left.l-click-area, .pane.items .object-browse-bar .right.abs, .pane.items .object-browse-bar .s-menu span.right.l-click-area, .s-menu .pane.items .object-browse-bar span.right.l-click-area { top: auto; } - /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-holder { top: 34px; } - /* line 266, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 282, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .object-holder { overflow: auto; } -/* line 274, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 290, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 277, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 293, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 284, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 300, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 291, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 307, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 293, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 302, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 318, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu, @@ -3193,12 +3291,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 331, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 320, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 336, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3214,39 +3312,28 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 344, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 330, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 346, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/*.object-holder { - .s-btn { - //background: red !important; - $h: 16px; - height: $h; - line-height: $h; - > span { - font-size: 0.7rem; - } - } -}*/ -/* line 350, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 354, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 353, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 357, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 360, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ .vscroll { overflow-y: auto; } @@ -4324,86 +4411,6 @@ ul.tree { bottom: auto; left: auto; } -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ -.ue-bottom-bar { - background: #000; - color: white; - font-size: .7rem; } - /* line 28, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .ue-bottom-bar .status-holder { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - position: absolute; - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - width: auto; - height: auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - line-height: 15px; - right: 120px; - text-transform: uppercase; } - /* line 39, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .ue-bottom-bar .app-logo { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - position: absolute; - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - width: auto; - height: auto; - left: auto; - cursor: pointer; } - /* line 48, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .ue-bottom-bar .app-logo.logo-openmctweb { - background: url("../../../../general/res/images/logo-openmctweb.svg") no-repeat center center; } - -/* line 54, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ -.status.block { - display: inline; - margin-right: 10px; } - /* line 58, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .status.block .status-indicator { - display: inline-block; - margin-right: 3px; - color: #0099cc; } - /* line 65, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .status.block .status-indicator.ok { - color: #009900; } - /* line 68, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .status.block .status-indicator.caution { - color: #ffaa00; } - /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space From 926aed72c309dad09dccd31ea1c6c189fc8b1787 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 2 Oct 2015 17:42:31 -0700 Subject: [PATCH 010/488] [Frontend] Style and display changes to status block open #159 open #170 Refined structure in status block markup and css; Added status-related color constants to theme scss files; --- .../general/res/sass/controls/_messages.scss | 32 ++++++----- .../general/res/templates/indicator.html | 26 ++++----- .../espresso/res/css/theme-espresso.css | 53 +++++++++++++------ .../themes/espresso/res/sass/_constants.scss | 6 +++ .../themes/snow/res/css/theme-snow.css | 53 +++++++++++++------ .../themes/snow/res/sass/_constants.scss | 6 +++ 6 files changed, 117 insertions(+), 59 deletions(-) diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index ec83528fba..a6aa243535 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -21,36 +21,44 @@ *****************************************************************************/ .status.block { - //@include test(); - cursor: help; + color: $colorStatusDefault; + cursor: default; display: inline-block; margin-right: $interiorMargin; .status-indicator, - .label { + .label, + .count { + //@include test(#00ff00); + display: inline-block; vertical-align: top; } .status-indicator { - display: inline-block; margin-right: $interiorMarginSm; - color: $colorKey; - &.ok { - color: #009900; - } - &.caution { - color: #ffaa00; - } + } + &.ok .status-indicator { + color: $colorStatusOk; + } + &.caution .status-indicator { + color: $colorStatusCaution; } .label { // Max-width silliness is necessary for width transition @include trans-prop-nice(max-width, .25s); - display: inline-block; overflow: hidden; max-width: 0px; } + .count { + @include trans-prop-nice(opacity, .25s); + font-weight: bold; + opacity: 1; + } &:hover { .label { max-width: 150px; width: auto; } + .count { + opacity: 0; + } } } \ No newline at end of file diff --git a/platform/commonUI/general/res/templates/indicator.html b/platform/commonUI/general/res/templates/indicator.html index 963d35ec14..40733d875a 100644 --- a/platform/commonUI/general/res/templates/indicator.html +++ b/platform/commonUI/general/res/templates/indicator.html @@ -19,20 +19,20 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> +
- - {{ngModel.getGlyph()}} - - - {{ngModel.getText()}} - - + + {{ngModel.getGlyph()}} + + {{ngModel.getText()}} + + + G diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 16cf89a2ea..316c6a5877 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -2306,25 +2306,26 @@ label.checkbox.custom { *****************************************************************************/ /* line 23, ../../../../general/res/sass/controls/_messages.scss */ .status.block { - cursor: help; + color: #ccc; + cursor: default; display: inline-block; margin-right: 5px; } - /* line 29, ../../../../general/res/sass/controls/_messages.scss */ + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .status.block .status-indicator, - .status.block .label { - vertical-align: top; } - /* line 34, ../../../../general/res/sass/controls/_messages.scss */ - .status.block .status-indicator { + .status.block .label, + .status.block .count { display: inline-block; - margin-right: 3px; - color: #0099cc; } - /* line 38, ../../../../general/res/sass/controls/_messages.scss */ - .status.block .status-indicator.ok { - color: #009900; } - /* line 41, ../../../../general/res/sass/controls/_messages.scss */ - .status.block .status-indicator.caution { - color: #ffaa00; } - /* line 45, ../../../../general/res/sass/controls/_messages.scss */ + vertical-align: top; } + /* line 35, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator { + margin-right: 3px; } + /* line 38, ../../../../general/res/sass/controls/_messages.scss */ + .status.block.ok .status-indicator { + color: #6cb773; } + /* line 41, ../../../../general/res/sass/controls/_messages.scss */ + .status.block.caution .status-indicator { + color: #ffa864; } + /* line 44, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; -o-transition-property: max-width; @@ -2338,13 +2339,31 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; - display: inline-block; overflow: hidden; max-width: 0px; } - /* line 54, ../../../../general/res/sass/controls/_messages.scss */ + /* line 50, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .count { + -moz-transition-property: opacity; + -o-transition-property: opacity; + -webkit-transition-property: opacity; + transition-property: opacity; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + font-weight: bold; + opacity: 1; } + /* line 56, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .label { max-width: 150px; width: auto; } + /* line 60, ../../../../general/res/sass/controls/_messages.scss */ + .status.block:hover .count { + opacity: 0; } /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { diff --git a/platform/commonUI/themes/espresso/res/sass/_constants.scss b/platform/commonUI/themes/espresso/res/sass/_constants.scss index cbc9a67e0a..b80dbb21dd 100644 --- a/platform/commonUI/themes/espresso/res/sass/_constants.scss +++ b/platform/commonUI/themes/espresso/res/sass/_constants.scss @@ -58,6 +58,12 @@ $colorInputFg: pullForward($colorBodyFg, 20%); $colorFormText: rgba(#fff, 0.5); $colorInputIcon: pushBack($colorBodyFg, 15%); +// Status colors, mainly used for messaging and item ancillary symbols +$colorStatusDefault: #ccc; +$colorStatusOk: #6cb773; +$colorStatusCaution: #ffa864; +$colorStatusAlert: $colorAlert; + // Selects $colorSelectBg: $colorBtnBg; $colorSelectFg: $colorBtnFg; diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index d9463b49ca..0107eef7e9 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -2254,25 +2254,26 @@ label.checkbox.custom { *****************************************************************************/ /* line 23, ../../../../general/res/sass/controls/_messages.scss */ .status.block { - cursor: help; + color: #ccc; + cursor: default; display: inline-block; margin-right: 5px; } - /* line 29, ../../../../general/res/sass/controls/_messages.scss */ + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .status.block .status-indicator, - .status.block .label { - vertical-align: top; } - /* line 34, ../../../../general/res/sass/controls/_messages.scss */ - .status.block .status-indicator { + .status.block .label, + .status.block .count { display: inline-block; - margin-right: 3px; - color: #0099cc; } - /* line 38, ../../../../general/res/sass/controls/_messages.scss */ - .status.block .status-indicator.ok { - color: #009900; } - /* line 41, ../../../../general/res/sass/controls/_messages.scss */ - .status.block .status-indicator.caution { - color: #ffaa00; } - /* line 45, ../../../../general/res/sass/controls/_messages.scss */ + vertical-align: top; } + /* line 35, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .status-indicator { + margin-right: 3px; } + /* line 38, ../../../../general/res/sass/controls/_messages.scss */ + .status.block.ok .status-indicator { + color: #090; } + /* line 41, ../../../../general/res/sass/controls/_messages.scss */ + .status.block.caution .status-indicator { + color: #fa0; } + /* line 44, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; -o-transition-property: max-width; @@ -2286,13 +2287,31 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; - display: inline-block; overflow: hidden; max-width: 0px; } - /* line 54, ../../../../general/res/sass/controls/_messages.scss */ + /* line 50, ../../../../general/res/sass/controls/_messages.scss */ + .status.block .count { + -moz-transition-property: opacity; + -o-transition-property: opacity; + -webkit-transition-property: opacity; + transition-property: opacity; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + font-weight: bold; + opacity: 1; } + /* line 56, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .label { max-width: 150px; width: auto; } + /* line 60, ../../../../general/res/sass/controls/_messages.scss */ + .status.block:hover .count { + opacity: 0; } /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { diff --git a/platform/commonUI/themes/snow/res/sass/_constants.scss b/platform/commonUI/themes/snow/res/sass/_constants.scss index 61d76cebb8..94113c25a8 100644 --- a/platform/commonUI/themes/snow/res/sass/_constants.scss +++ b/platform/commonUI/themes/snow/res/sass/_constants.scss @@ -60,6 +60,12 @@ $colorInputIcon: pushBack($colorBodyFg, 25%); $colorSelectBg: #ddd; $colorSelectFg: $colorBodyFg; +// Status colors, mainly used for messaging and item ancillary symbols +$colorStatusDefault: #ccc; +$colorStatusOk: #090; +$colorStatusCaution: #fa0; +$colorStatusAlert: $colorAlert; + // Limits and staleness colors// $colorTelemFresh: pullForward($colorBodyFg, 20%); $colorTelemStale: pushBack($colorBodyFg, 20%); From d606ee421fec04346263e1e8175d2a70e5686c97 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 2 Oct 2015 18:37:01 -0700 Subject: [PATCH 011/488] [Frontend] New template for message-banner open #159 open #170 Added markup and beginning of styling; Temporarily added mct-include elem to bottombar.html; --- platform/commonUI/general/bundle.json | 4 +++ .../commonUI/general/res/sass/_messages.scss | 23 ++++++++++++- .../general/res/templates/bottombar.html | 1 + .../general/res/templates/message-banner.html | 9 +++++ .../espresso/res/css/theme-espresso.css | 33 ++++++++++++++++++- .../themes/snow/res/css/theme-snow.css | 33 ++++++++++++++++++- 6 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 platform/commonUI/general/res/templates/message-banner.html diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index b55ce03b32..d71bb3a393 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -39,6 +39,10 @@ "key": "indicator", "templateUrl": "templates/indicator.html" }, + { + "key": "message-banner", + "templateUrl": "templates/message-banner.html" + }, { "key": "time-controller", "templateUrl": "templates/controls/time-controller.html" diff --git a/platform/commonUI/general/res/sass/_messages.scss b/platform/commonUI/general/res/sass/_messages.scss index db4de4c946..80c9b21d97 100644 --- a/platform/commonUI/general/res/sass/_messages.scss +++ b/platform/commonUI/general/res/sass/_messages.scss @@ -1,4 +1,4 @@ -/* Styles for messages */ +/* Styles for messages and message banners */ .message { &.block { @@ -9,4 +9,25 @@ background-color: rgba($colorAlert,0.3); color: lighten($colorAlert, 20%); } +} + +.l-message-banner { + @include ellipsize(); + display: block; + position: absolute; + top: $interiorMargin; right: auto; bottom: $interiorMargin; left: 50%; + height: auto; width: auto; + //line-height: $ueFooterH; + max-width: 300px; + padding: 0 $interiorMarginLg; + @include transform(translateX(-50%)); + .s-btn { + height: auto !important; + } +} + +.s-message-banner { + @include border-radius($basicCr); + background-color: #999; + color: black; } \ No newline at end of file diff --git a/platform/commonUI/general/res/templates/bottombar.html b/platform/commonUI/general/res/templates/bottombar.html index 4da2686fa1..f0d3799542 100644 --- a/platform/commonUI/general/res/templates/bottombar.html +++ b/platform/commonUI/general/res/templates/bottombar.html @@ -26,5 +26,6 @@ key="indicator.template">
+
\ No newline at end of file diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html new file mode 100644 index 0000000000..c5386311c6 --- /dev/null +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -0,0 +1,9 @@ +
+ + Objects not saved + + x + +
\ No newline at end of file diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 316c6a5877..3fc212468c 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -1153,7 +1153,7 @@ mct-container { top: 0; left: 0; } -/* Styles for messages */ +/* Styles for messages and message banners */ /* line 4, ../../../../general/res/sass/_messages.scss */ .message.block { -moz-border-radius: 2px; @@ -1165,6 +1165,37 @@ mct-container { background-color: rgba(255, 60, 0, 0.3); color: #ff8a66; } +/* line 14, ../../../../general/res/sass/_messages.scss */ +.l-message-banner { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; + position: absolute; + top: 5px; + right: auto; + bottom: 5px; + left: 50%; + height: auto; + width: auto; + max-width: 300px; + padding: 0 10px; + -moz-transform: translateX(-50%); + -ms-transform: translateX(-50%); + -webkit-transform: translateX(-50%); + transform: translateX(-50%); } + /* line 24, ../../../../general/res/sass/_messages.scss */ + .l-message-banner .s-btn, .l-message-banner .s-menu { + height: auto !important; } + +/* line 29, ../../../../general/res/sass/_messages.scss */ +.s-message-banner { + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + background-color: #999; + color: black; } + /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 0107eef7e9..be2972e22e 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -1150,7 +1150,7 @@ mct-container { top: 0; left: 0; } -/* Styles for messages */ +/* Styles for messages and message banners */ /* line 4, ../../../../general/res/sass/_messages.scss */ .message.block { -moz-border-radius: 4px; @@ -1162,6 +1162,37 @@ mct-container { background-color: rgba(255, 60, 0, 0.3); color: #ff8a66; } +/* line 14, ../../../../general/res/sass/_messages.scss */ +.l-message-banner { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; + position: absolute; + top: 5px; + right: auto; + bottom: 5px; + left: 50%; + height: auto; + width: auto; + max-width: 300px; + padding: 0 10px; + -moz-transform: translateX(-50%); + -ms-transform: translateX(-50%); + -webkit-transform: translateX(-50%); + transform: translateX(-50%); } + /* line 24, ../../../../general/res/sass/_messages.scss */ + .l-message-banner .s-btn, .l-message-banner .s-menu { + height: auto !important; } + +/* line 29, ../../../../general/res/sass/_messages.scss */ +.s-message-banner { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background-color: #999; + color: black; } + /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space From 471a25a625563c1325695679761f61b5a86fe242 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Tue, 6 Oct 2015 14:34:53 -0700 Subject: [PATCH 012/488] [Frontend] Progress bar/ message banner markup and styling open #170 Major work on progress bar; --- platform/commonUI/general/res/sass/_main.scss | 1 - .../commonUI/general/res/sass/_messages.scss | 33 -- .../commonUI/general/res/sass/_mixins.scss | 19 +- .../general/res/sass/controls/_controls.scss | 107 +++- .../general/res/sass/controls/_messages.scss | 100 ++++ .../general/res/templates/indicator.html | 1 + .../general/res/templates/message-banner.html | 14 +- .../espresso/res/css/theme-espresso.css | 534 ++++++++++++------ .../themes/espresso/res/sass/_constants.scss | 14 +- .../themes/snow/res/css/theme-snow.css | 524 +++++++++++------ .../themes/snow/res/sass/_constants.scss | 14 +- 11 files changed, 941 insertions(+), 420 deletions(-) delete mode 100644 platform/commonUI/general/res/sass/_messages.scss diff --git a/platform/commonUI/general/res/sass/_main.scss b/platform/commonUI/general/res/sass/_main.scss index 8241078c50..05d814f3be 100644 --- a/platform/commonUI/general/res/sass/_main.scss +++ b/platform/commonUI/general/res/sass/_main.scss @@ -29,7 +29,6 @@ @import "helpers/bubbles"; @import "helpers/splitter"; @import "helpers/wait-spinner"; -@import "messages"; @import "properties"; /********************************* CONTROLS */ diff --git a/platform/commonUI/general/res/sass/_messages.scss b/platform/commonUI/general/res/sass/_messages.scss deleted file mode 100644 index 80c9b21d97..0000000000 --- a/platform/commonUI/general/res/sass/_messages.scss +++ /dev/null @@ -1,33 +0,0 @@ -/* Styles for messages and message banners */ - -.message { - &.block { - @include border-radius($basicCr); - padding: $interiorMarginLg; - } - &.error { - background-color: rgba($colorAlert,0.3); - color: lighten($colorAlert, 20%); - } -} - -.l-message-banner { - @include ellipsize(); - display: block; - position: absolute; - top: $interiorMargin; right: auto; bottom: $interiorMargin; left: 50%; - height: auto; width: auto; - //line-height: $ueFooterH; - max-width: 300px; - padding: 0 $interiorMarginLg; - @include transform(translateX(-50%)); - .s-btn { - height: auto !important; - } -} - -.s-message-banner { - @include border-radius($basicCr); - background-color: #999; - color: black; -} \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_mixins.scss b/platform/commonUI/general/res/sass/_mixins.scss index d0b08491b7..14c56edfb7 100644 --- a/platform/commonUI/general/res/sass/_mixins.scss +++ b/platform/commonUI/general/res/sass/_mixins.scss @@ -94,7 +94,6 @@ } @mixin triangle($dir: "left", $size: 5px, $ratio: 1, $color: red) { - //$size: $size*2; width: 0; height: 0; $slopedB: $size/$ratio solid transparent; @@ -129,6 +128,24 @@ background-size: $d $d; } +@mixin bgVertStripes($c: yellow, $a: 0.1, $d: 40px) { + @include background-image(linear-gradient(-90deg, + rgba($c, $a) 0%, rgba($c, $a) 50%, + transparent 50%, transparent 100% + )); + background-repeat: repeat; + background-size: $d $d; +} + +@mixin bgVertFuzzyStripes($c: yellow, $a: 0.1, $d: 40px) { + @include background-image(linear-gradient(-90deg, + rgba($c, $a) 0%, transparent 50%, + transparent 50%, rgba($c, $a) 100% + )); + background-repeat: repeat; + background-size: $d $d; +} + @mixin bgTicks($c: $colorBodyFg, $repeatDir: 'x') { $deg: 90deg; @if ($repeatDir != 'x') { diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss index af967d6ef3..edb8994566 100644 --- a/platform/commonUI/general/res/sass/controls/_controls.scss +++ b/platform/commonUI/general/res/sass/controls/_controls.scss @@ -19,34 +19,6 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*.control { - // UNUSED? - &.view-control { - .icon { - display: inline-block; - margin: -1px 5px 1px 2px; - vertical-align: middle; - &.triangle-down { - margin: 2px 2px -2px 0px; - } - } - - .label { - display: inline-block; - font-size: 11px; - vertical-align: middle; - } - - .toggle { - @include border-radius(3px); - display: inline-block; - padding: 1px 6px 4px 4px; - &:hover { - background: rgba(white, 0.1); - } - } - } -}*/ .accordion { $accordionHeadH: 18px; @@ -291,6 +263,85 @@ label.checkbox.custom { } } +/******************************************************** PROGRESS BAR */ +@include keyframes(progress) { + 100% { background-position: $progressBarStripeW center; } +} + +@mixin bgProgressAnim($c: yellow, $a: 0.1, $d: 20px) { + @include background-image(linear-gradient(-90deg, + rgba($c, $a) 0%, transparent 50%, + transparent 50%, rgba($c, $a) 100% + )); + background-position: 0 center; + background-repeat: repeat-x; + background-size: $d 40%; +} + +.l-progress-bar { + // Assume will be determinate by default + display: inline-block; + overflow: hidden; + position: relative; + + .progress-amt-holder { + @include absPosDefault(1px); + } + .progress-amt, + .progress-amt:before, + .progress-amt:after { + @include absPosDefault(); + display: block; + content: ''; + } + + .progress-amt { + right: auto; // Allow inline width to control } + } + + &.indeterminate { + .progress-amt { + width: 100% !important; + } + } +} + +.s-progress-bar { + @include border-radius($basicCr); + @include boxIncised(0.3, 4px); + background: $colorProgressBarOuter; + //border:1px solid $colorProgressBarOuter; + .progress-amt { + @include border-radius($basicCr); + @include boxShdw(); + @include border-radius($basicCr - 1); + @include trans-prop-nice(width); + &:before { + background-color: $colorProgressBarAmt; + } + &:after { + // Sheen + @include background-image(linear-gradient( + transparent 5%, rgba(#fff,0.25) 30%, transparent 100% + )); + } + } + + &:not(.indeterminate) { + .progress-amt:before { + // Slower, more subtle anim for determinate progress + @include animation(progress .3s linear infinite); + @include bgProgressAnim(#fff, 0.2, $progressBarStripeW); + } + } + + &.indeterminate .progress-amt:before { + // Faster, more visible anim for indeterminate progress + @include animation(progress .6s linear infinite); + @include bgProgressAnim(#fff, 0.4, $progressBarStripeW); + } +} + /******************************************************** SLIDERS */ .slider { diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index a6aa243535..b8c98ec2d7 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -20,6 +20,22 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ +@mixin statusBannerColors($bg, $fg: $colorStatusFg) { + $bgPb: 30%; + $bgPbD: 10%; + background-color: darken($bg, $bgPb); + color: $fg; + &:hover { + background-color: darken($bg, $bgPb - $bgPbD); + } + .s-action { + background-color: darken($bg, $bgPb + $bgPbD); + &:hover { + background-color: darken($bg, $bgPb); + } + } +} + .status.block { color: $colorStatusDefault; cursor: default; @@ -61,4 +77,88 @@ opacity: 0; } } +} + +/* Styles for messages and message banners */ +.message { + &.block { + @include border-radius($basicCr); + padding: $interiorMarginLg; + } + &.error { + background-color: rgba($colorAlert,0.3); + color: lighten($colorAlert, 20%); + } +} + +.l-message-banner { + $m: $interiorMarginSm; + $lh: $ueFooterH - ($m*2) - 1; + @include box-sizing(border-box); + @include ellipsize(); + @include display-flex; + @include flex-direction(row); + @include align-items(center); + position: absolute; + top: $m; right: auto; bottom: $m; left: 50%; + height: auto; width: auto; + line-height: $lh; + max-width: 300px; + padding: 0 $interiorMargin 0 $interiorMarginLg; + @include transform(translateX(-50%)); + + a, span { + @include flex(0 1 auto); + margin-left: $interiorMargin; + &:first-child { + margin-left: 0; + } + } + a { + display: inline-block; + } + .l-action { + line-height: $lh - 3; + padding: 0 $interiorMargin; + } + .close { + //@include test(red, 0.7); + cursor: pointer; + font-size: 7px; + width: 8px; + } + .l-progress-bar { + $h: $lh - 10; + height: $h; + line-height: $h; + width: 100px; + } + z-index: 2; +} + +.s-message-banner, +.s-message-banner .s-action { + @include trans-prop-nice(background-color, .25s); +} + +.s-message-banner { + @include border-radius($controlCr); + @include statusBannerColors($colorStatusDefault, $colorStatusFg); + cursor: pointer; + a { color: inherit; } + .s-action { + @include border-radius($basicCr); + } + .close { + opacity: 0.5; + &:hover { + opacity: 1; + } + } + &.ok { + @include statusBannerColors($colorStatusOk); + } + &.caution { + @include statusBannerColors($colorStatusCaution); + } } \ No newline at end of file diff --git a/platform/commonUI/general/res/templates/indicator.html b/platform/commonUI/general/res/templates/indicator.html index 40733d875a..ce2adb421c 100644 --- a/platform/commonUI/general/res/templates/indicator.html +++ b/platform/commonUI/general/res/templates/indicator.html @@ -20,6 +20,7 @@ at runtime from the About dialog for additional information. --> +
\ No newline at end of file diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 3fc212468c..cb5a9be609 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -919,7 +919,7 @@ mct-container { right: 0; width: auto; height: 5px; } - /* line 159, ../../../../general/res/sass/_mixins.scss */ + /* line 176, ../../../../general/res/sass/_mixins.scss */ .split-layout.horizontal > .splitter:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -943,7 +943,7 @@ mct-container { top: 2px; left: 5px; right: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ + /* line 198, ../../../../general/res/sass/_mixins.scss */ .split-layout.horizontal > .splitter:not(.disabled):hover:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -973,7 +973,7 @@ mct-container { bottom: 0; cursor: col-resize; width: 5px; } - /* line 159, ../../../../general/res/sass/_mixins.scss */ + /* line 176, ../../../../general/res/sass/_mixins.scss */ .split-layout.vertical > .splitter:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -997,7 +997,7 @@ mct-container { left: 2px; bottom: 5px; top: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ + /* line 198, ../../../../general/res/sass/_mixins.scss */ .split-layout.vertical > .splitter:not(.disabled):hover:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -1153,49 +1153,6 @@ mct-container { top: 0; left: 0; } -/* Styles for messages and message banners */ -/* line 4, ../../../../general/res/sass/_messages.scss */ -.message.block { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; - padding: 10px; } -/* line 8, ../../../../general/res/sass/_messages.scss */ -.message.error { - background-color: rgba(255, 60, 0, 0.3); - color: #ff8a66; } - -/* line 14, ../../../../general/res/sass/_messages.scss */ -.l-message-banner { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - display: block; - position: absolute; - top: 5px; - right: auto; - bottom: 5px; - left: 50%; - height: auto; - width: auto; - max-width: 300px; - padding: 0 10px; - -moz-transform: translateX(-50%); - -ms-transform: translateX(-50%); - -webkit-transform: translateX(-50%); - transform: translateX(-50%); } - /* line 24, ../../../../general/res/sass/_messages.scss */ - .l-message-banner .s-btn, .l-message-banner .s-menu { - height: auto !important; } - -/* line 29, ../../../../general/res/sass/_messages.scss */ -.s-message-banner { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; - background-color: #999; - color: black; } - /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -1344,14 +1301,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.3) 0 1px 1px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .s-btn.major .icon, .major.s-menu .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover, .major.s-menu:not(.disabled):hover { background: linear-gradient(#1ac6ff, #00bfff); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover > .icon, .major.s-menu:not(.disabled):hover > .icon { color: white; } } /* line 62, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1383,14 +1340,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.3) 0 1px 1px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major) .icon, .s-menu:not(.major) .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover, .s-menu:not(.major):not(.disabled):hover { background: linear-gradient(#6b6b6b, #5e5e5e); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu:not(.major):not(.disabled):hover > .icon { color: #33ccff; } } /* line 71, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1425,14 +1382,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.3) 0 1px 1px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu:not(.disabled):hover { background: linear-gradient(#fe9815, #f88c01); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu:not(.disabled):hover > .icon { color: white; } } /* line 76, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1591,41 +1548,13 @@ mct-container { * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*.control { - // UNUSED? - &.view-control { - .icon { - display: inline-block; - margin: -1px 5px 1px 2px; - vertical-align: middle; - &.triangle-down { - margin: 2px 2px -2px 0px; - } - } - - .label { - display: inline-block; - font-size: 11px; - vertical-align: middle; - } - - .toggle { - @include border-radius(3px); - display: inline-block; - padding: 1px 6px 4px 4px; - &:hover { - background: rgba(white, 0.1); - } - } - } -}*/ -/* line 51, ../../../../general/res/sass/controls/_controls.scss */ +/* line 23, ../../../../general/res/sass/controls/_controls.scss */ .accordion { margin-top: 5px; } - /* line 54, ../../../../general/res/sass/controls/_controls.scss */ + /* line 26, ../../../../general/res/sass/controls/_controls.scss */ .accordion:first-child { margin-top: 0; } - /* line 57, ../../../../general/res/sass/controls/_controls.scss */ + /* line 29, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-head { -moz-border-radius: 1.5px; -webkit-border-radius: 1.5px; @@ -1647,10 +1576,10 @@ mct-container { width: auto; height: 18px; text-transform: uppercase; } - /* line 75, ../../../../general/res/sass/controls/_controls.scss */ + /* line 47, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-head:hover { background: rgba(153, 153, 153, 0.4); } - /* line 78, ../../../../general/res/sass/controls/_controls.scss */ + /* line 50, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-head:after { content: "^"; display: block; @@ -1660,10 +1589,10 @@ mct-container { right: 5px; text-transform: none; top: 0; } - /* line 88, ../../../../general/res/sass/controls/_controls.scss */ + /* line 60, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-head:not(.expanded):after { content: "v"; } - /* line 92, ../../../../general/res/sass/controls/_controls.scss */ + /* line 64, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-contents { position: absolute; top: 23px; @@ -1673,14 +1602,14 @@ mct-container { overflow-y: auto; overflow-x: hidden; } -/* line 103, ../../../../general/res/sass/controls/_controls.scss */ +/* line 75, ../../../../general/res/sass/controls/_controls.scss */ .l-composite-control { vertical-align: middle; } - /* line 106, ../../../../general/res/sass/controls/_controls.scss */ + /* line 78, ../../../../general/res/sass/controls/_controls.scss */ .l-composite-control.l-checkbox .composite-control-label { line-height: 18px; } -/* line 112, ../../../../general/res/sass/controls/_controls.scss */ +/* line 84, ../../../../general/res/sass/controls/_controls.scss */ .l-control-group { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -1689,23 +1618,23 @@ mct-container { display: inline-block; padding: 0 5px; position: relative; } - /* line 120, ../../../../general/res/sass/controls/_controls.scss */ + /* line 92, ../../../../general/res/sass/controls/_controls.scss */ .l-control-group:first-child { border-left: none; padding-left: 0; } -/* line 126, ../../../../general/res/sass/controls/_controls.scss */ +/* line 98, ../../../../general/res/sass/controls/_controls.scss */ .l-local-controls { position: absolute; top: 5px; right: 5px; z-index: 5; } -/* line 136, ../../../../general/res/sass/controls/_controls.scss */ +/* line 108, ../../../../general/res/sass/controls/_controls.scss */ .s-local-controls { font-size: 0.7rem; } -/* line 140, ../../../../general/res/sass/controls/_controls.scss */ +/* line 112, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom { cursor: pointer; display: inline-block; @@ -1714,13 +1643,13 @@ label.checkbox.custom { padding-left: 19px; position: relative; vertical-align: middle; } - /* line 150, ../../../../general/res/sass/controls/_controls.scss */ + /* line 122, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom em { color: #999; display: inline-block; height: 14px; min-width: 14px; } - /* line 155, ../../../../general/res/sass/controls/_controls.scss */ + /* line 127, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom em:before { -moz-border-radius: 1.5px; -webkit-border-radius: 1.5px; @@ -1741,58 +1670,58 @@ label.checkbox.custom { top: 0; position: absolute; text-align: center; } - /* line 174, ../../../../general/res/sass/controls/_controls.scss */ + /* line 146, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom.no-text { overflow: hidden; margin-right: 0; padding-left: 0; height: 14px; width: 14px; } - /* line 180, ../../../../general/res/sass/controls/_controls.scss */ + /* line 152, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom.no-text em { overflow: hidden; } - /* line 184, ../../../../general/res/sass/controls/_controls.scss */ + /* line 156, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom input { display: none; } - /* line 186, ../../../../general/res/sass/controls/_controls.scss */ + /* line 158, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom input:checked ~ em:before { background: #0099cc; color: #ccf2ff; content: "2"; } -/* line 194, ../../../../general/res/sass/controls/_controls.scss */ +/* line 166, ../../../../general/res/sass/controls/_controls.scss */ .input-labeled { margin-left: 5px; } - /* line 196, ../../../../general/res/sass/controls/_controls.scss */ + /* line 168, ../../../../general/res/sass/controls/_controls.scss */ .input-labeled label { display: inline-block; margin-right: 3px; } - /* line 200, ../../../../general/res/sass/controls/_controls.scss */ + /* line 172, ../../../../general/res/sass/controls/_controls.scss */ .input-labeled.inline { display: inline-block; } - /* line 203, ../../../../general/res/sass/controls/_controls.scss */ + /* line 175, ../../../../general/res/sass/controls/_controls.scss */ .input-labeled:first-child { margin-left: 0; } -/* line 208, ../../../../general/res/sass/controls/_controls.scss */ +/* line 180, ../../../../general/res/sass/controls/_controls.scss */ .s-menu label.checkbox.custom { margin-left: 5px; } -/* line 213, ../../../../general/res/sass/controls/_controls.scss */ +/* line 185, ../../../../general/res/sass/controls/_controls.scss */ .item .checkbox.checked label { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; border-bottom: none; } -/* line 219, ../../../../general/res/sass/controls/_controls.scss */ +/* line 191, ../../../../general/res/sass/controls/_controls.scss */ .context-available { color: #0099cc; } - /* line 222, ../../../../general/res/sass/controls/_controls.scss */ + /* line 194, ../../../../general/res/sass/controls/_controls.scss */ .context-available:hover { color: deepskyblue; } -/* line 227, ../../../../general/res/sass/controls/_controls.scss */ +/* line 199, ../../../../general/res/sass/controls/_controls.scss */ .view-switcher { -moz-transition-property: visibility, opacity, background-color, border-color; -o-transition-property: visibility, opacity, background-color, border-color; @@ -1808,26 +1737,26 @@ label.checkbox.custom { transition-timing-function: ease-in-out; } /******************************************************** OBJECT-HEADER */ -/* line 232, ../../../../general/res/sass/controls/_controls.scss */ +/* line 204, ../../../../general/res/sass/controls/_controls.scss */ .object-header { font-size: 1em; } - /* line 243, ../../../../general/res/sass/controls/_controls.scss */ + /* line 215, ../../../../general/res/sass/controls/_controls.scss */ .object-header > .type-icon { color: #cccccc; font-size: 120%; float: left; margin-right: 5px; } - /* line 250, ../../../../general/res/sass/controls/_controls.scss */ + /* line 222, ../../../../general/res/sass/controls/_controls.scss */ .object-header .l-elem-wrapper { justify-content: flex-start; -webkit-justify-content: flex-start; } - /* line 253, ../../../../general/res/sass/controls/_controls.scss */ + /* line 225, ../../../../general/res/sass/controls/_controls.scss */ .object-header .l-elem-wrapper mct-representation { min-width: 0.7em; } - /* line 261, ../../../../general/res/sass/controls/_controls.scss */ + /* line 233, ../../../../general/res/sass/controls/_controls.scss */ .object-header .action { margin-right: 5px; } - /* line 265, ../../../../general/res/sass/controls/_controls.scss */ + /* line 237, ../../../../general/res/sass/controls/_controls.scss */ .object-header .title-label { color: #999; overflow: hidden; @@ -1836,13 +1765,13 @@ label.checkbox.custom { flex: 0 1 auto; -webkit-flex: 0 1 auto; padding-right: 0.35em; } - /* line 275, ../../../../general/res/sass/controls/_controls.scss */ + /* line 247, ../../../../general/res/sass/controls/_controls.scss */ .object-header .context-available { font-size: 0.7em; flex: 0 0 1; -webkit-flex: 0 0 1; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 282, ../../../../general/res/sass/controls/_controls.scss */ + /* line 254, ../../../../general/res/sass/controls/_controls.scss */ .object-header .context-available { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -1857,12 +1786,128 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; opacity: 0; } - /* line 287, ../../../../general/res/sass/controls/_controls.scss */ + /* line 259, ../../../../general/res/sass/controls/_controls.scss */ .object-header:hover .context-available { opacity: 1; } } +/******************************************************** PROGRESS BAR */ +@-moz-keyframes progress { + 100% { + background-position: 20px center; } } +@-webkit-keyframes progress { + 100% { + background-position: 20px center; } } +@keyframes progress { + 100% { + background-position: 20px center; } } +/* line 281, ../../../../general/res/sass/controls/_controls.scss */ +.l-progress-bar { + display: inline-block; + overflow: hidden; + position: relative; } + /* line 287, ../../../../general/res/sass/controls/_controls.scss */ + .l-progress-bar .progress-amt-holder { + overflow: hidden; + position: absolute; + top: 1px; + right: 1px; + bottom: 1px; + left: 1px; + width: auto; + height: auto; } + /* line 290, ../../../../general/res/sass/controls/_controls.scss */ + .l-progress-bar .progress-amt, + .l-progress-bar .progress-amt:before, + .l-progress-bar .progress-amt:after { + overflow: hidden; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + width: auto; + height: auto; + display: block; + content: ''; } + /* line 298, ../../../../general/res/sass/controls/_controls.scss */ + .l-progress-bar .progress-amt { + right: auto; } + /* line 303, ../../../../general/res/sass/controls/_controls.scss */ + .l-progress-bar.indeterminate .progress-amt { + width: 100% !important; } + +/* line 309, ../../../../general/res/sass/controls/_controls.scss */ +.s-progress-bar { + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + -moz-box-shadow: inset rgba(0, 0, 0, 0.3) 0 1px 4px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.3) 0 1px 4px; + box-shadow: inset rgba(0, 0, 0, 0.3) 0 1px 4px; + background: rgba(0, 0, 0, 0.1); } + /* line 314, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar .progress-amt { + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; + -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; + box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; + -moz-border-radius: 1px; + -webkit-border-radius: 1px; + border-radius: 1px; + -moz-transition-property: width; + -o-transition-property: width; + -webkit-transition-property: width; + transition-property: width; + -moz-transition-duration: 500ms; + -o-transition-duration: 500ms; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } + /* line 319, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar .progress-amt:before { + background-color: #0099cc; } + /* line 322, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar .progress-amt:after { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSI1JSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjMwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjI1Ii8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(5%, rgba(0, 0, 0, 0)), color-stop(30%, rgba(255, 255, 255, 0.25)), color-stop(100%, rgba(0, 0, 0, 0))); + background-image: -moz-linear-gradient(rgba(0, 0, 0, 0) 5%, rgba(255, 255, 255, 0.25) 30%, rgba(0, 0, 0, 0) 100%); + background-image: -webkit-linear-gradient(rgba(0, 0, 0, 0) 5%, rgba(255, 255, 255, 0.25) 30%, rgba(0, 0, 0, 0) 100%); + background-image: linear-gradient(rgba(0, 0, 0, 0) 5%, rgba(255, 255, 255, 0.25) 30%, rgba(0, 0, 0, 0) 100%); } + /* line 331, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar:not(.indeterminate) .progress-amt:before { + -moz-animation: progress 0.3s linear infinite; + -webkit-animation: progress 0.3s linear infinite; + animation: progress 0.3s linear infinite; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjIiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + background-size: 100%; + background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); + background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); + background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); + background-position: 0 center; + background-repeat: repeat-x; + background-size: 20px 40%; } + /* line 338, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar.indeterminate .progress-amt:before { + -moz-animation: progress 0.6s linear infinite; + -webkit-animation: progress 0.6s linear infinite; + animation: progress 0.6s linear infinite; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjQiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC40Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + background-size: 100%; + background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); + background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); + background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); + background-position: 0 center; + background-repeat: repeat-x; + background-size: 20px 40%; } + /******************************************************** SLIDERS */ -/* line 300, ../../../../general/res/sass/controls/_controls.scss */ +/* line 351, ../../../../general/res/sass/controls/_controls.scss */ .slider .slot { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -1881,7 +1926,7 @@ label.checkbox.custom { right: 0; bottom: auto; left: 0; } -/* line 311, ../../../../general/res/sass/controls/_controls.scss */ +/* line 362, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob { background-color: #333; -moz-border-radius: 3px; @@ -1918,17 +1963,17 @@ label.checkbox.custom { auto: 0; bottom: auto; left: auto; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .slider .knob .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .slider .knob:not(.disabled):hover { background: linear-gradient(#595959, #4d4d4d); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .slider .knob:not(.disabled):hover > .icon { color: #33ccff; } } - /* line 159, ../../../../general/res/sass/_mixins.scss */ + /* line 176, ../../../../general/res/sass/_mixins.scss */ .slider .knob:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -1952,7 +1997,7 @@ label.checkbox.custom { left: 2px; bottom: 5px; top: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ + /* line 198, ../../../../general/res/sass/_mixins.scss */ .slider .knob:not(.disabled):hover:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -1967,12 +2012,12 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; border-color: #0099cc; } - /* line 322, ../../../../general/res/sass/controls/_controls.scss */ + /* line 373, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob:before { top: 1px; bottom: 3px; left: 5px; } -/* line 329, ../../../../general/res/sass/controls/_controls.scss */ +/* line 380, ../../../../general/res/sass/controls/_controls.scss */ .slider .range { background: rgba(0, 153, 204, 0.6); cursor: ew-resize; @@ -1983,13 +2028,13 @@ label.checkbox.custom { left: auto; height: auto; width: auto; } - /* line 339, ../../../../general/res/sass/controls/_controls.scss */ + /* line 390, ../../../../general/res/sass/controls/_controls.scss */ .slider .range:hover { background: rgba(0, 153, 204, 0.7); } /******************************************************** BROWSER ELEMENTS */ @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 348, ../../../../general/res/sass/controls/_controls.scss */ + /* line 399, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -2004,7 +2049,7 @@ label.checkbox.custom { height: 10px; width: 10px; } - /* line 357, ../../../../general/res/sass/controls/_controls.scss */ + /* line 408, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzU5NTk1OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2018,7 +2063,7 @@ label.checkbox.custom { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } - /* line 366, ../../../../general/res/sass/controls/_controls.scss */ + /* line 417, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb:hover { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzVlNWU1ZSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzUyNTI1MiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2027,7 +2072,7 @@ label.checkbox.custom { background-image: -webkit-linear-gradient(#5e5e5e, #525252 20px); background-image: linear-gradient(#5e5e5e, #525252 20px); } - /* line 371, ../../../../general/res/sass/controls/_controls.scss */ + /* line 422, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-corner { background: rgba(0, 0, 0, 0.4); } } /***************************************************************************** @@ -2148,7 +2193,7 @@ label.checkbox.custom { .menu-element .menu ul { margin: 0; padding: 0; } - /* line 329, ../../../../general/res/sass/_mixins.scss */ + /* line 346, ../../../../general/res/sass/_mixins.scss */ .menu-element .menu ul li { list-style-type: none; margin: 0; @@ -2335,28 +2380,28 @@ label.checkbox.custom { * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 23, ../../../../general/res/sass/controls/_messages.scss */ +/* line 39, ../../../../general/res/sass/controls/_messages.scss */ .status.block { color: #ccc; cursor: default; display: inline-block; margin-right: 5px; } - /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + /* line 44, ../../../../general/res/sass/controls/_messages.scss */ .status.block .status-indicator, .status.block .label, .status.block .count { display: inline-block; vertical-align: top; } - /* line 35, ../../../../general/res/sass/controls/_messages.scss */ + /* line 51, ../../../../general/res/sass/controls/_messages.scss */ .status.block .status-indicator { margin-right: 3px; } - /* line 38, ../../../../general/res/sass/controls/_messages.scss */ + /* line 54, ../../../../general/res/sass/controls/_messages.scss */ .status.block.ok .status-indicator { - color: #6cb773; } - /* line 41, ../../../../general/res/sass/controls/_messages.scss */ + color: #60e68e; } + /* line 57, ../../../../general/res/sass/controls/_messages.scss */ .status.block.caution .status-indicator { color: #ffa864; } - /* line 44, ../../../../general/res/sass/controls/_messages.scss */ + /* line 60, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; -o-transition-property: max-width; @@ -2372,7 +2417,7 @@ label.checkbox.custom { transition-timing-function: ease-in-out; overflow: hidden; max-width: 0px; } - /* line 50, ../../../../general/res/sass/controls/_messages.scss */ + /* line 66, ../../../../general/res/sass/controls/_messages.scss */ .status.block .count { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -2388,14 +2433,155 @@ label.checkbox.custom { transition-timing-function: ease-in-out; font-weight: bold; opacity: 1; } - /* line 56, ../../../../general/res/sass/controls/_messages.scss */ + /* line 72, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .label { max-width: 150px; width: auto; } - /* line 60, ../../../../general/res/sass/controls/_messages.scss */ + /* line 76, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .count { opacity: 0; } +/* Styles for messages and message banners */ +/* line 84, ../../../../general/res/sass/controls/_messages.scss */ +.message.block { + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + padding: 10px; } +/* line 88, ../../../../general/res/sass/controls/_messages.scss */ +.message.error { + background-color: rgba(255, 60, 0, 0.3); + color: #ff8a66; } + +/* line 94, ../../../../general/res/sass/controls/_messages.scss */ +.l-message-banner { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: -webkit-flex; + display: flex; + -webkit-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + align-items: center; + position: absolute; + top: 3px; + right: auto; + bottom: 3px; + left: 50%; + height: auto; + width: auto; + line-height: 18px; + max-width: 300px; + padding: 0 5px 0 10px; + -moz-transform: translateX(-50%); + -ms-transform: translateX(-50%); + -webkit-transform: translateX(-50%); + transform: translateX(-50%); + z-index: 2; } + /* line 110, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner a, .l-message-banner span { + -webkit-flex: 0 1 auto; + flex: 0 1 auto; + margin-left: 5px; } + /* line 113, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner a:first-child, .l-message-banner span:first-child { + margin-left: 0; } + /* line 117, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner a { + display: inline-block; } + /* line 120, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner .l-action { + line-height: 15px; + padding: 0 5px; } + /* line 124, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner .close { + cursor: pointer; + font-size: 7px; + width: 8px; } + /* line 130, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner .l-progress-bar { + height: 8px; + line-height: 8px; + width: 100px; } + +/* line 139, ../../../../general/res/sass/controls/_messages.scss */ +.s-message-banner, +.s-message-banner .s-action { + -moz-transition-property: background-color; + -o-transition-property: background-color; + -webkit-transition-property: background-color; + transition-property: background-color; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } + +/* line 144, ../../../../general/res/sass/controls/_messages.scss */ +.s-message-banner { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + background-color: gray; + color: #ccc; + cursor: pointer; } + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner:hover { + background-color: #999999; } + /* line 31, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .s-action { + background-color: #666666; } + /* line 33, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .s-action:hover { + background-color: gray; } + /* line 148, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner a { + color: inherit; } + /* line 149, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .s-action { + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; } + /* line 152, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .close { + opacity: 0.5; } + /* line 154, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .close:hover { + opacity: 1; } + /* line 158, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok { + background-color: #189543; + color: #ccc; } + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok:hover { + background-color: #1ec256; } + /* line 31, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok .s-action { + background-color: #11692f; } + /* line 33, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok .s-action:hover { + background-color: #189543; } + /* line 161, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution { + background-color: #ca5900; + color: #ccc; } + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution:hover { + background-color: #fd6f00; } + /* line 31, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution .s-action { + background-color: #974200; } + /* line 33, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution .s-action:hover { + background-color: #ca5900; } + /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { position: relative; @@ -2647,7 +2833,7 @@ label.checkbox.custom { padding: 0 3px; position: relative; height: 150px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ .form .form-row .selector-list.error { background: rgba(255, 0, 0, 0.5); } /* line 124, ../../../../general/res/sass/forms/_elems.scss */ @@ -2704,7 +2890,7 @@ input[type="text"] { color: #cccccc; outline: none; padding: 0 3px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ input[type="text"].error { background: rgba(255, 0, 0, 0.5); } /* line 172, ../../../../general/res/sass/forms/_elems.scss */ @@ -2732,7 +2918,7 @@ textarea { position: absolute; height: 100%; width: 100%; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ textarea.error { background: rgba(255, 0, 0, 0.5); } @@ -2790,14 +2976,14 @@ textarea { padding: 0 5px; overflow: hidden; position: relative; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .select .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .select:not(.disabled):hover { background: linear-gradient(#6b6b6b, #5e5e5e); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .select:not(.disabled):hover > .icon { color: #33ccff; } } /* line 28, ../../../../general/res/sass/forms/_selects.scss */ @@ -2878,7 +3064,7 @@ textarea { max-height: 400px; overflow: auto; padding: 5px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ .channel-selector .treeview.error { background: rgba(255, 0, 0, 0.5); } /* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ @@ -3026,7 +3212,7 @@ span.req { padding: 0 3px; background: #3b3b3b; border-bottom: 1px solid #4d4d4d; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ .filter input.filter.error, .filter input.t-filter-input.error, .t-filter input.filter.error, @@ -4112,14 +4298,14 @@ span.req { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.3) 0 1px 1px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu:not(.major) .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover { background: linear-gradient(#a6a6a6, #999999); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4204,7 +4390,7 @@ ul.tree { -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 329, ../../../../general/res/sass/_mixins.scss */ + /* line 346, ../../../../general/res/sass/_mixins.scss */ ul.tree li { list-style-type: none; margin: 0; @@ -5404,14 +5590,14 @@ table { margin-bottom: 3px; margin-right: 3px; position: relative; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover { background: linear-gradient(#666666, #595959); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover > .icon { color: #33ccff; } } /* line 45, ../../../../general/res/sass/items/_item.scss */ @@ -5546,14 +5732,14 @@ table { transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.3) 0 1px 1px; color: #80dfff; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected:not(.disabled):hover { background: linear-gradient(#1ac6ff, #00bfff); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected:not(.disabled):hover > .icon { color: #33ccff; } } /* line 137, ../../../../general/res/sass/items/_item.scss */ diff --git a/platform/commonUI/themes/espresso/res/sass/_constants.scss b/platform/commonUI/themes/espresso/res/sass/_constants.scss index b80dbb21dd..e76c2ca68c 100644 --- a/platform/commonUI/themes/espresso/res/sass/_constants.scss +++ b/platform/commonUI/themes/espresso/res/sass/_constants.scss @@ -29,8 +29,6 @@ $colorPausedBg: #c56f01; $colorPausedFg: #fff; $colorCreateBtn: $colorKey; $colorGridLines: rgba(#fff, 0.05); -$colorFormLines: rgba(#fff, 0.1); -$colorFormSectionHeader: rgba(#000, 0.2); $colorInvokeMenu: #fff; $colorObjHdrTxt: $colorBodyFg; $colorObjHdrIc: pullForward($colorObjHdrTxt, 20%); @@ -42,10 +40,10 @@ $colorMenuIc: pullForward($colorKey, 17%); $colorMenuHovBg: pullForward($colorMenuBg, 10%); $colorMenuHovFg: #fff; $colorMenuHovIc: $colorMenuHovFg; -$shdwMenu: none; -$shdwMenuText: rgba(black, 0.1) 0 1px 2px; $colorCreateMenuLgIcon: $colorMenuFg; $colorCreateMenuText: $colorMenuFg; +$shdwMenu: none; +$shdwMenuText: rgba(black, 0.1) 0 1px 2px; // Form colors $colorCheck: $colorKey; @@ -53,16 +51,22 @@ $colorFormRequired: $colorAlt1; $colorFormValid: #33cc33; $colorFormError: #cc0000; $colorFormInvalid: #ff3300; +$colorFormLines: rgba(#fff, 0.1); +$colorFormSectionHeader: rgba(#000, 0.2); $colorInputBg: rgba(#fff, 0.1); $colorInputFg: pullForward($colorBodyFg, 20%); $colorFormText: rgba(#fff, 0.5); $colorInputIcon: pushBack($colorBodyFg, 15%); // Status colors, mainly used for messaging and item ancillary symbols +$colorStatusFg: #ccc; $colorStatusDefault: #ccc; -$colorStatusOk: #6cb773; +$colorStatusOk: #60e68e; $colorStatusCaution: #ffa864; $colorStatusAlert: $colorAlert; +$colorProgressBarOuter: rgba(#000, 0.1); +$colorProgressBarAmt: $colorKey; +$progressBarStripeW: 20px; // Selects $colorSelectBg: $colorBtnBg; diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index be2972e22e..3aca678c0a 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -916,7 +916,7 @@ mct-container { right: 0; width: auto; height: 5px; } - /* line 159, ../../../../general/res/sass/_mixins.scss */ + /* line 176, ../../../../general/res/sass/_mixins.scss */ .split-layout.horizontal > .splitter:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -940,7 +940,7 @@ mct-container { top: 2px; left: 5px; right: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ + /* line 198, ../../../../general/res/sass/_mixins.scss */ .split-layout.horizontal > .splitter:not(.disabled):hover:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -970,7 +970,7 @@ mct-container { bottom: 0; cursor: col-resize; width: 5px; } - /* line 159, ../../../../general/res/sass/_mixins.scss */ + /* line 176, ../../../../general/res/sass/_mixins.scss */ .split-layout.vertical > .splitter:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -994,7 +994,7 @@ mct-container { left: 2px; bottom: 5px; top: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ + /* line 198, ../../../../general/res/sass/_mixins.scss */ .split-layout.vertical > .splitter:not(.disabled):hover:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -1150,49 +1150,6 @@ mct-container { top: 0; left: 0; } -/* Styles for messages and message banners */ -/* line 4, ../../../../general/res/sass/_messages.scss */ -.message.block { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - padding: 10px; } -/* line 8, ../../../../general/res/sass/_messages.scss */ -.message.error { - background-color: rgba(255, 60, 0, 0.3); - color: #ff8a66; } - -/* line 14, ../../../../general/res/sass/_messages.scss */ -.l-message-banner { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - display: block; - position: absolute; - top: 5px; - right: auto; - bottom: 5px; - left: 50%; - height: auto; - width: auto; - max-width: 300px; - padding: 0 10px; - -moz-transform: translateX(-50%); - -ms-transform: translateX(-50%); - -webkit-transform: translateX(-50%); - transform: translateX(-50%); } - /* line 24, ../../../../general/res/sass/_messages.scss */ - .l-message-banner .s-btn, .l-message-banner .s-menu { - height: auto !important; } - -/* line 29, ../../../../general/res/sass/_messages.scss */ -.s-message-banner { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - background-color: #999; - color: black; } - /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -1332,14 +1289,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .s-btn.major .icon, .major.s-menu .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover, .major.s-menu:not(.disabled):hover { background: deepskyblue; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover > .icon, .major.s-menu:not(.disabled):hover > .icon { color: white; } } /* line 62, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1362,14 +1319,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major) .icon, .s-menu:not(.major) .icon { color: #eee; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover, .s-menu:not(.major):not(.disabled):hover { background: #0099cc; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 71, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1395,14 +1352,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu:not(.disabled):hover { background: #ffad33; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu:not(.disabled):hover > .icon { color: white; } } /* line 76, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1561,41 +1518,13 @@ mct-container { * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*.control { - // UNUSED? - &.view-control { - .icon { - display: inline-block; - margin: -1px 5px 1px 2px; - vertical-align: middle; - &.triangle-down { - margin: 2px 2px -2px 0px; - } - } - - .label { - display: inline-block; - font-size: 11px; - vertical-align: middle; - } - - .toggle { - @include border-radius(3px); - display: inline-block; - padding: 1px 6px 4px 4px; - &:hover { - background: rgba(white, 0.1); - } - } - } -}*/ -/* line 51, ../../../../general/res/sass/controls/_controls.scss */ +/* line 23, ../../../../general/res/sass/controls/_controls.scss */ .accordion { margin-top: 5px; } - /* line 54, ../../../../general/res/sass/controls/_controls.scss */ + /* line 26, ../../../../general/res/sass/controls/_controls.scss */ .accordion:first-child { margin-top: 0; } - /* line 57, ../../../../general/res/sass/controls/_controls.scss */ + /* line 29, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-head { -moz-border-radius: 3px; -webkit-border-radius: 3px; @@ -1617,10 +1546,10 @@ mct-container { width: auto; height: 18px; text-transform: uppercase; } - /* line 75, ../../../../general/res/sass/controls/_controls.scss */ + /* line 47, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-head:hover { background: rgba(102, 102, 102, 0.4); } - /* line 78, ../../../../general/res/sass/controls/_controls.scss */ + /* line 50, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-head:after { content: "^"; display: block; @@ -1630,10 +1559,10 @@ mct-container { right: 5px; text-transform: none; top: 0; } - /* line 88, ../../../../general/res/sass/controls/_controls.scss */ + /* line 60, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-head:not(.expanded):after { content: "v"; } - /* line 92, ../../../../general/res/sass/controls/_controls.scss */ + /* line 64, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-contents { position: absolute; top: 23px; @@ -1643,14 +1572,14 @@ mct-container { overflow-y: auto; overflow-x: hidden; } -/* line 103, ../../../../general/res/sass/controls/_controls.scss */ +/* line 75, ../../../../general/res/sass/controls/_controls.scss */ .l-composite-control { vertical-align: middle; } - /* line 106, ../../../../general/res/sass/controls/_controls.scss */ + /* line 78, ../../../../general/res/sass/controls/_controls.scss */ .l-composite-control.l-checkbox .composite-control-label { line-height: 18px; } -/* line 112, ../../../../general/res/sass/controls/_controls.scss */ +/* line 84, ../../../../general/res/sass/controls/_controls.scss */ .l-control-group { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -1659,23 +1588,23 @@ mct-container { display: inline-block; padding: 0 5px; position: relative; } - /* line 120, ../../../../general/res/sass/controls/_controls.scss */ + /* line 92, ../../../../general/res/sass/controls/_controls.scss */ .l-control-group:first-child { border-left: none; padding-left: 0; } -/* line 126, ../../../../general/res/sass/controls/_controls.scss */ +/* line 98, ../../../../general/res/sass/controls/_controls.scss */ .l-local-controls { position: absolute; top: 5px; right: 5px; z-index: 5; } -/* line 136, ../../../../general/res/sass/controls/_controls.scss */ +/* line 108, ../../../../general/res/sass/controls/_controls.scss */ .s-local-controls { font-size: 0.7rem; } -/* line 140, ../../../../general/res/sass/controls/_controls.scss */ +/* line 112, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom { cursor: pointer; display: inline-block; @@ -1684,13 +1613,13 @@ label.checkbox.custom { padding-left: 19px; position: relative; vertical-align: middle; } - /* line 150, ../../../../general/res/sass/controls/_controls.scss */ + /* line 122, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom em { color: #666; display: inline-block; height: 14px; min-width: 14px; } - /* line 155, ../../../../general/res/sass/controls/_controls.scss */ + /* line 127, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom em:before { -moz-border-radius: 3px; -webkit-border-radius: 3px; @@ -1711,58 +1640,58 @@ label.checkbox.custom { top: 0; position: absolute; text-align: center; } - /* line 174, ../../../../general/res/sass/controls/_controls.scss */ + /* line 146, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom.no-text { overflow: hidden; margin-right: 0; padding-left: 0; height: 14px; width: 14px; } - /* line 180, ../../../../general/res/sass/controls/_controls.scss */ + /* line 152, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom.no-text em { overflow: hidden; } - /* line 184, ../../../../general/res/sass/controls/_controls.scss */ + /* line 156, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom input { display: none; } - /* line 186, ../../../../general/res/sass/controls/_controls.scss */ + /* line 158, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom input:checked ~ em:before { background: #0099cc; color: #ccf2ff; content: "2"; } -/* line 194, ../../../../general/res/sass/controls/_controls.scss */ +/* line 166, ../../../../general/res/sass/controls/_controls.scss */ .input-labeled { margin-left: 5px; } - /* line 196, ../../../../general/res/sass/controls/_controls.scss */ + /* line 168, ../../../../general/res/sass/controls/_controls.scss */ .input-labeled label { display: inline-block; margin-right: 3px; } - /* line 200, ../../../../general/res/sass/controls/_controls.scss */ + /* line 172, ../../../../general/res/sass/controls/_controls.scss */ .input-labeled.inline { display: inline-block; } - /* line 203, ../../../../general/res/sass/controls/_controls.scss */ + /* line 175, ../../../../general/res/sass/controls/_controls.scss */ .input-labeled:first-child { margin-left: 0; } -/* line 208, ../../../../general/res/sass/controls/_controls.scss */ +/* line 180, ../../../../general/res/sass/controls/_controls.scss */ .s-menu label.checkbox.custom { margin-left: 5px; } -/* line 213, ../../../../general/res/sass/controls/_controls.scss */ +/* line 185, ../../../../general/res/sass/controls/_controls.scss */ .item .checkbox.checked label { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; border-bottom: none; } -/* line 219, ../../../../general/res/sass/controls/_controls.scss */ +/* line 191, ../../../../general/res/sass/controls/_controls.scss */ .context-available { color: #0099cc; } - /* line 222, ../../../../general/res/sass/controls/_controls.scss */ + /* line 194, ../../../../general/res/sass/controls/_controls.scss */ .context-available:hover { color: deepskyblue; } -/* line 227, ../../../../general/res/sass/controls/_controls.scss */ +/* line 199, ../../../../general/res/sass/controls/_controls.scss */ .view-switcher { -moz-transition-property: visibility, opacity, background-color, border-color; -o-transition-property: visibility, opacity, background-color, border-color; @@ -1778,26 +1707,26 @@ label.checkbox.custom { transition-timing-function: ease-in-out; } /******************************************************** OBJECT-HEADER */ -/* line 232, ../../../../general/res/sass/controls/_controls.scss */ +/* line 204, ../../../../general/res/sass/controls/_controls.scss */ .object-header { font-size: 1em; } - /* line 243, ../../../../general/res/sass/controls/_controls.scss */ + /* line 215, ../../../../general/res/sass/controls/_controls.scss */ .object-header > .type-icon { color: #b3b3b3; font-size: 120%; float: left; margin-right: 5px; } - /* line 250, ../../../../general/res/sass/controls/_controls.scss */ + /* line 222, ../../../../general/res/sass/controls/_controls.scss */ .object-header .l-elem-wrapper { justify-content: flex-start; -webkit-justify-content: flex-start; } - /* line 253, ../../../../general/res/sass/controls/_controls.scss */ + /* line 225, ../../../../general/res/sass/controls/_controls.scss */ .object-header .l-elem-wrapper mct-representation { min-width: 0.7em; } - /* line 261, ../../../../general/res/sass/controls/_controls.scss */ + /* line 233, ../../../../general/res/sass/controls/_controls.scss */ .object-header .action { margin-right: 5px; } - /* line 265, ../../../../general/res/sass/controls/_controls.scss */ + /* line 237, ../../../../general/res/sass/controls/_controls.scss */ .object-header .title-label { color: #666; overflow: hidden; @@ -1806,13 +1735,13 @@ label.checkbox.custom { flex: 0 1 auto; -webkit-flex: 0 1 auto; padding-right: 0.35em; } - /* line 275, ../../../../general/res/sass/controls/_controls.scss */ + /* line 247, ../../../../general/res/sass/controls/_controls.scss */ .object-header .context-available { font-size: 0.7em; flex: 0 0 1; -webkit-flex: 0 0 1; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 282, ../../../../general/res/sass/controls/_controls.scss */ + /* line 254, ../../../../general/res/sass/controls/_controls.scss */ .object-header .context-available { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -1827,12 +1756,128 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; opacity: 0; } - /* line 287, ../../../../general/res/sass/controls/_controls.scss */ + /* line 259, ../../../../general/res/sass/controls/_controls.scss */ .object-header:hover .context-available { opacity: 1; } } +/******************************************************** PROGRESS BAR */ +@-moz-keyframes progress { + 100% { + background-position: 20px center; } } +@-webkit-keyframes progress { + 100% { + background-position: 20px center; } } +@keyframes progress { + 100% { + background-position: 20px center; } } +/* line 281, ../../../../general/res/sass/controls/_controls.scss */ +.l-progress-bar { + display: inline-block; + overflow: hidden; + position: relative; } + /* line 287, ../../../../general/res/sass/controls/_controls.scss */ + .l-progress-bar .progress-amt-holder { + overflow: hidden; + position: absolute; + top: 1px; + right: 1px; + bottom: 1px; + left: 1px; + width: auto; + height: auto; } + /* line 290, ../../../../general/res/sass/controls/_controls.scss */ + .l-progress-bar .progress-amt, + .l-progress-bar .progress-amt:before, + .l-progress-bar .progress-amt:after { + overflow: hidden; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + width: auto; + height: auto; + display: block; + content: ''; } + /* line 298, ../../../../general/res/sass/controls/_controls.scss */ + .l-progress-bar .progress-amt { + right: auto; } + /* line 303, ../../../../general/res/sass/controls/_controls.scss */ + .l-progress-bar.indeterminate .progress-amt { + width: 100% !important; } + +/* line 309, ../../../../general/res/sass/controls/_controls.scss */ +.s-progress-bar { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: inset rgba(0, 0, 0, 0.3) 0 1px 4px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.3) 0 1px 4px; + box-shadow: inset rgba(0, 0, 0, 0.3) 0 1px 4px; + background: rgba(0, 0, 0, 0.1); } + /* line 314, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar .progress-amt { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; + -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; + box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-transition-property: width; + -o-transition-property: width; + -webkit-transition-property: width; + transition-property: width; + -moz-transition-duration: 500ms; + -o-transition-duration: 500ms; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } + /* line 319, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar .progress-amt:before { + background-color: #0a0; } + /* line 322, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar .progress-amt:after { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSI1JSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjMwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjI1Ii8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(5%, rgba(0, 0, 0, 0)), color-stop(30%, rgba(255, 255, 255, 0.25)), color-stop(100%, rgba(0, 0, 0, 0))); + background-image: -moz-linear-gradient(rgba(0, 0, 0, 0) 5%, rgba(255, 255, 255, 0.25) 30%, rgba(0, 0, 0, 0) 100%); + background-image: -webkit-linear-gradient(rgba(0, 0, 0, 0) 5%, rgba(255, 255, 255, 0.25) 30%, rgba(0, 0, 0, 0) 100%); + background-image: linear-gradient(rgba(0, 0, 0, 0) 5%, rgba(255, 255, 255, 0.25) 30%, rgba(0, 0, 0, 0) 100%); } + /* line 331, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar:not(.indeterminate) .progress-amt:before { + -moz-animation: progress 0.3s linear infinite; + -webkit-animation: progress 0.3s linear infinite; + animation: progress 0.3s linear infinite; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjIiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + background-size: 100%; + background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); + background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); + background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); + background-position: 0 center; + background-repeat: repeat-x; + background-size: 20px 40%; } + /* line 338, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar.indeterminate .progress-amt:before { + -moz-animation: progress 0.6s linear infinite; + -webkit-animation: progress 0.6s linear infinite; + animation: progress 0.6s linear infinite; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjQiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC40Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + background-size: 100%; + background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); + background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); + background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); + background-position: 0 center; + background-repeat: repeat-x; + background-size: 20px 40%; } + /******************************************************** SLIDERS */ -/* line 300, ../../../../general/res/sass/controls/_controls.scss */ +/* line 351, ../../../../general/res/sass/controls/_controls.scss */ .slider .slot { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -1851,7 +1896,7 @@ label.checkbox.custom { right: 0; bottom: auto; left: 0; } -/* line 311, ../../../../general/res/sass/controls/_controls.scss */ +/* line 362, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob { background-color: #969696; -moz-border-radius: 4px; @@ -1879,10 +1924,10 @@ label.checkbox.custom { auto: 0; bottom: auto; left: auto; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .slider .knob .icon { color: #eee; } - /* line 159, ../../../../general/res/sass/_mixins.scss */ + /* line 176, ../../../../general/res/sass/_mixins.scss */ .slider .knob:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -1906,7 +1951,7 @@ label.checkbox.custom { left: 2px; bottom: 5px; top: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ + /* line 198, ../../../../general/res/sass/_mixins.scss */ .slider .knob:not(.disabled):hover:before { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -1921,12 +1966,12 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; border-color: #fcfcfc; } - /* line 322, ../../../../general/res/sass/controls/_controls.scss */ + /* line 373, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob:before { top: 1px; bottom: 3px; left: 5px; } -/* line 329, ../../../../general/res/sass/controls/_controls.scss */ +/* line 380, ../../../../general/res/sass/controls/_controls.scss */ .slider .range { background: rgba(0, 153, 204, 0.6); cursor: ew-resize; @@ -1937,13 +1982,13 @@ label.checkbox.custom { left: auto; height: auto; width: auto; } - /* line 339, ../../../../general/res/sass/controls/_controls.scss */ + /* line 390, ../../../../general/res/sass/controls/_controls.scss */ .slider .range:hover { background: rgba(0, 153, 204, 0.7); } /******************************************************** BROWSER ELEMENTS */ @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 348, ../../../../general/res/sass/controls/_controls.scss */ + /* line 399, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -1958,7 +2003,7 @@ label.checkbox.custom { height: 10px; width: 10px; } - /* line 357, ../../../../general/res/sass/controls/_controls.scss */ + /* line 408, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzg5ODk4OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzdkN2Q3ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -1972,7 +2017,7 @@ label.checkbox.custom { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } - /* line 366, ../../../../general/res/sass/controls/_controls.scss */ + /* line 417, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb:hover { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwYWNlNiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwOTljYyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -1981,7 +2026,7 @@ label.checkbox.custom { background-image: -webkit-linear-gradient(#00ace6, #0099cc 20px); background-image: linear-gradient(#00ace6, #0099cc 20px); } - /* line 371, ../../../../general/res/sass/controls/_controls.scss */ + /* line 422, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-corner { background: rgba(0, 0, 0, 0.1); } } /***************************************************************************** @@ -2096,7 +2141,7 @@ label.checkbox.custom { .menu-element .menu ul { margin: 0; padding: 0; } - /* line 329, ../../../../general/res/sass/_mixins.scss */ + /* line 346, ../../../../general/res/sass/_mixins.scss */ .menu-element .menu ul li { list-style-type: none; margin: 0; @@ -2283,28 +2328,28 @@ label.checkbox.custom { * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 23, ../../../../general/res/sass/controls/_messages.scss */ +/* line 39, ../../../../general/res/sass/controls/_messages.scss */ .status.block { color: #ccc; cursor: default; display: inline-block; margin-right: 5px; } - /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + /* line 44, ../../../../general/res/sass/controls/_messages.scss */ .status.block .status-indicator, .status.block .label, .status.block .count { display: inline-block; vertical-align: top; } - /* line 35, ../../../../general/res/sass/controls/_messages.scss */ + /* line 51, ../../../../general/res/sass/controls/_messages.scss */ .status.block .status-indicator { margin-right: 3px; } - /* line 38, ../../../../general/res/sass/controls/_messages.scss */ + /* line 54, ../../../../general/res/sass/controls/_messages.scss */ .status.block.ok .status-indicator { - color: #090; } - /* line 41, ../../../../general/res/sass/controls/_messages.scss */ + color: #60e68e; } + /* line 57, ../../../../general/res/sass/controls/_messages.scss */ .status.block.caution .status-indicator { - color: #fa0; } - /* line 44, ../../../../general/res/sass/controls/_messages.scss */ + color: #ffa864; } + /* line 60, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; -o-transition-property: max-width; @@ -2320,7 +2365,7 @@ label.checkbox.custom { transition-timing-function: ease-in-out; overflow: hidden; max-width: 0px; } - /* line 50, ../../../../general/res/sass/controls/_messages.scss */ + /* line 66, ../../../../general/res/sass/controls/_messages.scss */ .status.block .count { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -2336,14 +2381,155 @@ label.checkbox.custom { transition-timing-function: ease-in-out; font-weight: bold; opacity: 1; } - /* line 56, ../../../../general/res/sass/controls/_messages.scss */ + /* line 72, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .label { max-width: 150px; width: auto; } - /* line 60, ../../../../general/res/sass/controls/_messages.scss */ + /* line 76, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .count { opacity: 0; } +/* Styles for messages and message banners */ +/* line 84, ../../../../general/res/sass/controls/_messages.scss */ +.message.block { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + padding: 10px; } +/* line 88, ../../../../general/res/sass/controls/_messages.scss */ +.message.error { + background-color: rgba(255, 60, 0, 0.3); + color: #ff8a66; } + +/* line 94, ../../../../general/res/sass/controls/_messages.scss */ +.l-message-banner { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: -webkit-flex; + display: flex; + -webkit-flex-direction: row; + flex-direction: row; + -webkit-align-items: center; + align-items: center; + position: absolute; + top: 3px; + right: auto; + bottom: 3px; + left: 50%; + height: auto; + width: auto; + line-height: 18px; + max-width: 300px; + padding: 0 5px 0 10px; + -moz-transform: translateX(-50%); + -ms-transform: translateX(-50%); + -webkit-transform: translateX(-50%); + transform: translateX(-50%); + z-index: 2; } + /* line 110, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner a, .l-message-banner span { + -webkit-flex: 0 1 auto; + flex: 0 1 auto; + margin-left: 5px; } + /* line 113, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner a:first-child, .l-message-banner span:first-child { + margin-left: 0; } + /* line 117, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner a { + display: inline-block; } + /* line 120, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner .l-action { + line-height: 15px; + padding: 0 5px; } + /* line 124, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner .close { + cursor: pointer; + font-size: 7px; + width: 8px; } + /* line 130, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner .l-progress-bar { + height: 8px; + line-height: 8px; + width: 100px; } + +/* line 139, ../../../../general/res/sass/controls/_messages.scss */ +.s-message-banner, +.s-message-banner .s-action { + -moz-transition-property: background-color; + -o-transition-property: background-color; + -webkit-transition-property: background-color; + transition-property: background-color; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } + +/* line 144, ../../../../general/res/sass/controls/_messages.scss */ +.s-message-banner { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background-color: gray; + color: #fff; + cursor: pointer; } + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner:hover { + background-color: #999999; } + /* line 31, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .s-action { + background-color: #666666; } + /* line 33, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .s-action:hover { + background-color: gray; } + /* line 148, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner a { + color: inherit; } + /* line 149, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .s-action { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; } + /* line 152, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .close { + opacity: 0.5; } + /* line 154, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner .close:hover { + opacity: 1; } + /* line 158, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok { + background-color: #189543; + color: #fff; } + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok:hover { + background-color: #1ec256; } + /* line 31, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok .s-action { + background-color: #11692f; } + /* line 33, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok .s-action:hover { + background-color: #189543; } + /* line 161, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution { + background-color: #ca5900; + color: #fff; } + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution:hover { + background-color: #fd6f00; } + /* line 31, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution .s-action { + background-color: #974200; } + /* line 33, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution .s-action:hover { + background-color: #ca5900; } + /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { position: relative; @@ -2595,7 +2781,7 @@ label.checkbox.custom { padding: 0 3px; position: relative; height: 150px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ .form .form-row .selector-list.error { background: rgba(255, 0, 0, 0.5); } /* line 124, ../../../../general/res/sass/forms/_elems.scss */ @@ -2652,7 +2838,7 @@ input[type="text"] { color: #666; outline: none; padding: 0 3px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ input[type="text"].error { background: rgba(255, 0, 0, 0.5); } /* line 172, ../../../../general/res/sass/forms/_elems.scss */ @@ -2680,7 +2866,7 @@ textarea { position: absolute; height: 100%; width: 100%; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ textarea.error { background: rgba(255, 0, 0, 0.5); } @@ -2729,7 +2915,7 @@ textarea { padding: 0 5px; overflow: hidden; position: relative; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .select .icon { color: #eee; } /* line 28, ../../../../general/res/sass/forms/_selects.scss */ @@ -2810,7 +2996,7 @@ textarea { max-height: 400px; overflow: auto; padding: 5px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ .channel-selector .treeview.error { background: rgba(255, 0, 0, 0.5); } /* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ @@ -2958,7 +3144,7 @@ span.req { padding: 0 3px; background: white; border-bottom: 1px solid white; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 313, ../../../../general/res/sass/_mixins.scss */ .filter input.filter.error, .filter input.t-filter-input.error, .t-filter input.filter.error, @@ -4026,14 +4212,14 @@ span.req { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu:not(.major) .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover { background: #7d7d7d; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4118,7 +4304,7 @@ ul.tree { -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 329, ../../../../general/res/sass/_mixins.scss */ + /* line 346, ../../../../general/res/sass/_mixins.scss */ ul.tree li { list-style-type: none; margin: 0; @@ -5308,14 +5494,14 @@ table { margin-bottom: 3px; margin-right: 3px; position: relative; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 294, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover { background: #d0d0d0; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 296, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover > .icon { color: #33ccff; } } /* line 45, ../../../../general/res/sass/items/_item.scss */ @@ -5441,7 +5627,7 @@ table { transition: background, 0.25s; text-shadow: none; color: #80dfff; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 289, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected .icon { color: #eee; } /* line 137, ../../../../general/res/sass/items/_item.scss */ diff --git a/platform/commonUI/themes/snow/res/sass/_constants.scss b/platform/commonUI/themes/snow/res/sass/_constants.scss index 94113c25a8..02897ef236 100644 --- a/platform/commonUI/themes/snow/res/sass/_constants.scss +++ b/platform/commonUI/themes/snow/res/sass/_constants.scss @@ -57,14 +57,20 @@ $colorInputBg: $colorGenBg; $colorInputFg: $colorBodyFg; $colorFormText: pushBack($colorBodyFg, 10%); $colorInputIcon: pushBack($colorBodyFg, 25%); -$colorSelectBg: #ddd; -$colorSelectFg: $colorBodyFg; // Status colors, mainly used for messaging and item ancillary symbols +$colorStatusFg: #fff; $colorStatusDefault: #ccc; -$colorStatusOk: #090; -$colorStatusCaution: #fa0; +$colorStatusOk: #60e68e; +$colorStatusCaution: #ffa864; $colorStatusAlert: $colorAlert; +$colorProgressBarOuter: rgba(#000, 0.1); +$colorProgressBarAmt: #0a0; +$progressBarStripeW: 20px; + +// Selects +$colorSelectBg: #ddd; +$colorSelectFg: $colorBodyFg; // Limits and staleness colors// $colorTelemFresh: pullForward($colorBodyFg, 20%); From 2aeebff652b2098193d348ae8162260f9a67e955 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Tue, 6 Oct 2015 14:41:58 -0700 Subject: [PATCH 013/488] Merging in Andrew's work so far on progress and blocking dialogs open #163 open #170 Squashed commit of the following: commit ec7edb58ca75b409d330b5434999c67a0ec14e33 Author: Henry Date: Mon Oct 5 10:39:06 2015 -0700 Rename dialogSeverity to messageServity for reuse with notifications commit d20abe01dda50f7508444ed65b207cb6210e5fff Author: Henry Date: Fri Oct 2 16:40:29 2015 -0700 Fixed docs commit 227da1849826af54c66932dba55f692e085194b5 Author: Henry Date: Fri Oct 2 16:27:41 2015 -0700 Added semicolon commit 22d424f96e74e44230f100cde2108aff4ef10944 Author: Henry Date: Fri Oct 2 16:26:29 2015 -0700 Fixed code errors commit 2c77c3647c326f91fdbe2d63a8e3f1c3040d7397 Author: Henry Date: Fri Oct 2 16:24:01 2015 -0700 Initial commit of blocking dialog service with test code to demonstrate usage --- bundles.json | 4 +- platform/commonUI/dialog/bundle.json | 14 +++ .../res/templates/blocking-message.html | 30 +++++ platform/commonUI/dialog/src/DialogService.js | 112 ++++++++++++++++-- testing/dialogTest/bundle.json | 28 +++++ testing/dialogTest/res/dialog-launch.html | 1 + .../dialogTest/src/DialogLaunchController.js | 94 +++++++++++++++ .../dialogTest/src/DialogLaunchIndicator.js | 50 ++++++++ 8 files changed, 321 insertions(+), 12 deletions(-) create mode 100644 platform/commonUI/dialog/res/templates/blocking-message.html create mode 100644 testing/dialogTest/bundle.json create mode 100644 testing/dialogTest/res/dialog-launch.html create mode 100644 testing/dialogTest/src/DialogLaunchController.js create mode 100644 testing/dialogTest/src/DialogLaunchIndicator.js diff --git a/bundles.json b/bundles.json index a0f7edd7a8..498718fa58 100644 --- a/bundles.json +++ b/bundles.json @@ -29,5 +29,7 @@ "example/imagery", "example/eventGenerator", - "example/generator" + "example/generator", + + "testing/dialogTest" ] diff --git a/platform/commonUI/dialog/bundle.json b/platform/commonUI/dialog/bundle.json index 9a2d541419..eab8fb000a 100644 --- a/platform/commonUI/dialog/bundle.json +++ b/platform/commonUI/dialog/bundle.json @@ -1,5 +1,15 @@ { "extensions": { + "constants": [ + { + "key": "messageSeverity", + "value": { + "ERROR": "error", + "INFO": "info", + "SUCCESS": "success" + } + } + ], "services": [ { "key": "dialogService", @@ -24,6 +34,10 @@ { "key": "form-dialog", "templateUrl": "templates/dialog.html" + }, + { + "key": "blocking-message", + "templateUrl": "templates/blocking-message.html" } ], "containers": [ diff --git a/platform/commonUI/dialog/res/templates/blocking-message.html b/platform/commonUI/dialog/res/templates/blocking-message.html new file mode 100644 index 0000000000..81a183eb13 --- /dev/null +++ b/platform/commonUI/dialog/res/templates/blocking-message.html @@ -0,0 +1,30 @@ + + +
+
{{ngModel.dialog.title}}
+
+
+
+
{{ngModel.dialog.hint}}
+
+
{{ngModel.dialog.progress}} %
+
{{ngModel.dialog.progressText}}
+
+
+
+ +
+
\ No newline at end of file diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index 25e8943c06..94c8e24844 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -84,17 +84,7 @@ define( model.confirm = confirm; model.cancel = cancel; - if (this.dialogVisible) { - // Only one dialog should be shown at a time. - // The application design should be such that - // we never even try to do this. - this.$log.warn([ - "Dialog already showing; ", - "unable to show ", - model.name - ].join("")); - deferred.reject(); - } else { + if (this.canShowDialog(model)) { // Add the overlay using the OverlayService, which // will handle actual insertion into the DOM this.overlay = this.overlayService.createOverlay( @@ -105,6 +95,8 @@ define( // Track that a dialog is already visible, to // avoid spawning multiple dialogs at once. this.dialogVisible = true; + } else { + deferred.reject(); } return deferred.promise; @@ -157,6 +149,104 @@ define( ); }; + /** + * Tests if a dialog can be displayed. A modal dialog may only be + * displayed if one is not already visible. + * Will log a warning message if it can't display a dialog. + * @returns {boolean} true if dialog is currently visible, false + * otherwise + */ + DialogService.prototype.canShowDialog = function(dialogModel){ + if (this.dialogVisible){ + // Only one dialog should be shown at a time. + // The application design should be such that + // we never even try to do this. + this.$log.warn([ + "Dialog already showing; ", + "unable to show ", + dialogModel.title + ].join("")); + + return false; + } else { + return true; + } + }; + + /** + * dialogModel: { + * severity: string "error" | "info", + * title: string, + * hint: string, + * progress: int, + * progressText: string, + * unknownProgress: boolean, + * actions: [{ + * label: String, + * action: function + * }] + */ + + /** + * A user action that can be performed from a blocking dialog. These + * actions will be rendered as buttons within a blocking dialog. + * + * @typedef DialogAction + * @property {string} label a label to be displayed as the button + * text for this action + * @property {function} action a function to be called when the + * button is clicked + */ + + /** + * A description of the model options that may be passed to the + * showBlockingMessage method + * + * @typedef DialogModel + * @property {string} severity the severity level of this message. + * These are defined in a bundle constant with key 'dialogSeverity' + * @property {string} title the title to use for the dialog + * @property {string} hint the 'hint' message to show below the title + * @property {number} progress a percentage value (1-100) + * indicating the completion of the blocking task + * @property {string} progressText the message to show below a + * progress bar to indicate progress. For example, this might be + * used to indicate time remaining, or items still to process. + * @property {boolean} unknownProgress some tasks may be + * impossible to provide an estimate for. Providing a true value for + * this attribute will indicate to the user that the progress and + * duration cannot be estimated. + * @property {DialogAction[]} actions a list of actions that will + * be added to the dialog as buttons. These buttons are + */ + + /** + * Displays a blocking (modal) dialog. This dialog can be used for + * displaying messages that require the user's + * immediate attention. The message may include an indication of + * progress, as well as a series of actions that + * the user can take if necessary + * @param {DialogModel} dialogModel defines options for the dialog + * @returns {boolean} + */ + DialogService.prototype.showBlockingMessage = function(dialogModel) { + if (this.canShowDialog(dialogModel)) { + // Add the overlay using the OverlayService, which + // will handle actual insertion into the DOM + this.overlay = this.overlayService.createOverlay( + "blocking-message", + {dialog: dialogModel} + ); + this.dialogVisible = true; + return true; + } else { + //Could not show a dialog, so return indication of this to + //client code. + return false; + } + + }; + return DialogService; } diff --git a/testing/dialogTest/bundle.json b/testing/dialogTest/bundle.json new file mode 100644 index 0000000000..36ec16e048 --- /dev/null +++ b/testing/dialogTest/bundle.json @@ -0,0 +1,28 @@ +{ + "extensions": { + "templates": [ + { + "key": "dialogLaunchTemplate", + "templateUrl": "dialog-launch.html" + } + ], + "controllers": [ + { + "key": "DialogLaunchController", + "implementation": "DialogLaunchController.js", + "depends": [ + "$scope", + "dialogService", + "$timeout", + "$log", + "messageSeverity" + ] + } + ], + "indicators": [ + { + "implementation": "DialogLaunchIndicator.js" + } + ] + } +} diff --git a/testing/dialogTest/res/dialog-launch.html b/testing/dialogTest/res/dialog-launch.html new file mode 100644 index 0000000000..b9d0d525ea --- /dev/null +++ b/testing/dialogTest/res/dialog-launch.html @@ -0,0 +1 @@ +   \ No newline at end of file diff --git a/testing/dialogTest/src/DialogLaunchController.js b/testing/dialogTest/src/DialogLaunchController.js new file mode 100644 index 0000000000..bc088274e9 --- /dev/null +++ b/testing/dialogTest/src/DialogLaunchController.js @@ -0,0 +1,94 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define( + [], + function () { + "use strict"; + + function DialogLaunchController($scope, dialogService, $timeout, $log, messageSeverity) { + $scope.launchProgress = function () { + var model = { + title: "Progress dialog example", + progress: 0, + hint: "Calculating...", + unknownDuration: false, + severity: messageSeverity.INFO, + actions: [ + { + label: "Cancel Operation", + action: function () { + $log.debug("Operation cancelled"); + dialogService.dismiss(); + } + }, + { + label: "Do something else...", + action: function () { + $log.debug("Something else pressed"); + } + } + ] + }; + + function incrementProgress() { + model.progress = Math.min(100, Math.floor(model.progress + Math.random() * 30)); + model.progressText = ["Estimated time remaining: about ", 60 - Math.floor((model.progress / 100) * 60), " seconds"].join(" "); + if (model.progress < 100) { + $timeout(incrementProgress, 1000); + } + } + + if (dialogService.showBlockingMessage(model)) { + //Do processing here + model.hint = "Processing 100 objects..."; + $timeout(incrementProgress, 1000); + } else { + $log.error("Could not display modal dialog"); + } + }; + + $scope.launchError = function () { + var model = { + title: "Error Message Title", + hint: "Something happened. It was not so good.", + severity: messageSeverity.ERROR, + actions: [ + { + label: "OK", + action: function () { + $log.debug("OK Pressed"); + dialogService.dismiss(); + } + } + ] + }; + + if (!dialogService.showBlockingMessage(model)) { + $log.error("Could not display modal dialog"); + } + }; + } + return DialogLaunchController; + } +); diff --git a/testing/dialogTest/src/DialogLaunchIndicator.js b/testing/dialogTest/src/DialogLaunchIndicator.js new file mode 100644 index 0000000000..47c516fc70 --- /dev/null +++ b/testing/dialogTest/src/DialogLaunchIndicator.js @@ -0,0 +1,50 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define,window*/ + +define( + [], + function () { + "use strict"; + + function DialogLaunchIndicator() { + + } + + DialogLaunchIndicator.template = 'dialogLaunchTemplate'; + + DialogLaunchIndicator.prototype.getGlyph = function () { + return "i"; + }; + DialogLaunchIndicator.prototype.getGlyphClass = function () { + return 'caution'; + }; + DialogLaunchIndicator.prototype.getText = function () { + return "Launch test dialog"; + }; + DialogLaunchIndicator.prototype.getDescription = function () { + return "Launch test dialog"; + }; + + return DialogLaunchIndicator; + } +); From bf0014f1b90a2cf37b6a8305ce405a1b342c9f25 Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 6 Oct 2015 14:45:10 -0700 Subject: [PATCH 014/488] Temporary commit --- platform/commonUI/dialog/bundle.json | 14 ++ platform/commonUI/general/bundle.json | 5 + .../general/res/templates/message-banner.html | 4 +- .../src/controllers/BannerController.js | 31 ++++ platform/commonUI/notification/bundle.json | 18 +++ .../notification/src/NotificationService.js | 142 ++++++++++++++++++ 6 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 platform/commonUI/general/src/controllers/BannerController.js create mode 100644 platform/commonUI/notification/bundle.json create mode 100644 platform/commonUI/notification/src/NotificationService.js diff --git a/platform/commonUI/dialog/bundle.json b/platform/commonUI/dialog/bundle.json index 9a2d541419..eab8fb000a 100644 --- a/platform/commonUI/dialog/bundle.json +++ b/platform/commonUI/dialog/bundle.json @@ -1,5 +1,15 @@ { "extensions": { + "constants": [ + { + "key": "messageSeverity", + "value": { + "ERROR": "error", + "INFO": "info", + "SUCCESS": "success" + } + } + ], "services": [ { "key": "dialogService", @@ -24,6 +34,10 @@ { "key": "form-dialog", "templateUrl": "templates/dialog.html" + }, + { + "key": "blocking-message", + "templateUrl": "templates/blocking-message.html" } ], "containers": [ diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index d71bb3a393..3a52c92d03 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -96,6 +96,11 @@ "key": "SelectorController", "implementation": "controllers/SelectorController.js", "depends": [ "objectService", "$scope" ] + }, + { + "key": "BannerController", + "implementation": "controllers/BannerController.js", + "depends": ["$scope", "notificationService"] } ], "directives": [ diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html index c5386311c6..8d3a95e0cc 100644 --- a/platform/commonUI/general/res/templates/message-banner.html +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -1,9 +1,9 @@
Objects not saved - + x
\ No newline at end of file diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js new file mode 100644 index 0000000000..1c6193e061 --- /dev/null +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -0,0 +1,31 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define( + [], + function () { + "use strict"; + function BannerController($scope, notificationService){ + $scope.activeNotification = notificationService.active.Notification; + } + }); \ No newline at end of file diff --git a/platform/commonUI/notification/bundle.json b/platform/commonUI/notification/bundle.json new file mode 100644 index 0000000000..56087d9094 --- /dev/null +++ b/platform/commonUI/notification/bundle.json @@ -0,0 +1,18 @@ +{ + "extensions": { + "constants": [ + { + "key": "DEFAULT_AUTO_DISMISS", + "value": 2000 + } + ], + "services": [ + { + "key": "notificationService", + "implementation": "NotificationService.js", + "depends": [ "$log", "$timeout", "messageSeverity", + "DEFAULT_AUTO_DISMISS" ] + } + ] + } +} \ No newline at end of file diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js new file mode 100644 index 0000000000..07aea14da4 --- /dev/null +++ b/platform/commonUI/notification/src/NotificationService.js @@ -0,0 +1,142 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +/** + * This bundle implements the notification service, which can be used to + * show banner notifications to the user. + * @namespace platform/commonUI/dialog + */ +define( + [], + function () { + "use strict"; + /** + * The notification service is responsible for informing the user of + * events via the use of banner notifications. + * @memberof platform/commonUI/notification + * @constructor + */ + function NotificationService($log, $timeout, messageSeverity, DEFAULT_AUTO_DISMISS) { + //maintain an array of notifications. + //expose a method for adding notifications. + //expose a method for dismissing notifications. + //expose a method for minimizing notifications. + //expose a method for getting the 'current' notification. How + //this is determined could be a little nuanced. + //Allow for auto-dismissal of success messages + // + // + // + // Questions: + // 1) What happens when a newer, but lower priority + // message arrives (eg. success). Just show it? It will + // auto-dismissed, and the existing error message will be + // exposed. + // + + this.notifications = []; + this.$log = $log; + this.$timeout = $timeout; + this.messageSeverity = messageSeverity; + this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS; + + /** + * Exposes the current "active" notification. This is a + * notification that is of current highest importance that has + * not been dismissed. The deinition of what is of highest + * importance might be a little nuanced and require tweaking. + * For example, if an important error message is visible and a + * success message is triggered, it may be desirable to + * temporarily show the success message and then auto-dismiss it. + * @type {{notification: undefined}} + */ + this.active = { + notification: undefined + }; + } + /** + var model = { + title: string, + progress: number, + severity: MessageSeverity, + unknownProgress: boolean, + minimized: boolean, + autoDismiss: boolean | number, + actions: { + label: string, + action: function + } + } + */ + + /** + * Possibly refactor this out to a provider? + * @constructor + */ + function Notification (model) { + this.model = model; + } + + Notification.prototype.minimize = function () { + if (typeof setValue !== undefined){ + model.minimized = setValue; + } else { + return model.minimized; + } + }; + + /** + * model = { + * + * } + * @param model + */ + NotificationService.prototype.notify = function (model) { + var notification = new Notification(model); + this.notifications.push(notification); + this.setActiveNotification(notification); + }; + + + + NotificationService.prototype.setActiveNotification = function () { + //If there is a message active, time it out, and then chain a + // new message to appear. + if (this.active.timeout){ + this.active.timeout = this.active.timeout.then(function (){ + this.active.timeout = $timeout(function(){ + this.dismiss(this.active.notification); + }); + }); + } + }; + + NotificationService.prototype.dismiss = function (notification) { + var index = this.notifications.indexOf(notification); + if (index >= 0) { + this.notifications = this.notifications.splice(index, 1); + delete this.active.notification; + } + } + return NotificationService; + }); \ No newline at end of file From 30fd8c451e20826629600f0bf8b0b2e6a446e28e Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Tue, 6 Oct 2015 16:10:27 -0700 Subject: [PATCH 015/488] [Frontend] Progress bar as include; progress dialog open #163 open #170 Progress bar now mct-include; Progress dialog sanded; Dialog launcher modded; --- .../res/templates/blocking-message.html | 38 +++++++++---------- platform/commonUI/general/bundle.json | 4 ++ .../general/res/sass/controls/_messages.scss | 4 +- .../general/res/sass/overlay/_overlay.scss | 10 +++++ .../general/res/templates/indicator.html | 2 +- .../general/res/templates/message-banner.html | 8 ++-- .../espresso/res/css/theme-espresso.css | 27 +++++++------ .../themes/espresso/res/sass/_constants.scss | 1 + .../themes/snow/res/css/theme-snow.css | 27 +++++++------ .../themes/snow/res/sass/_constants.scss | 1 + testing/dialogTest/res/dialog-launch.html | 6 ++- .../dialogTest/src/DialogLaunchController.js | 7 +++- 12 files changed, 81 insertions(+), 54 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/blocking-message.html b/platform/commonUI/dialog/res/templates/blocking-message.html index 81a183eb13..bf2ff3d674 100644 --- a/platform/commonUI/dialog/res/templates/blocking-message.html +++ b/platform/commonUI/dialog/res/templates/blocking-message.html @@ -1,30 +1,26 @@ - - -
+ +
{{ngModel.dialog.title}}
+
Do not navigate away from this page or close this browser tab while this operation is in progress.
-
-
+
+
{{ngModel.dialog.hint}}
-
-
{{ngModel.dialog.progress}} %
+ + + +
{{ngModel.dialog.progressText}}
+ \ No newline at end of file diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index d71bb3a393..b1e95a36df 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -43,6 +43,10 @@ "key": "message-banner", "templateUrl": "templates/message-banner.html" }, + { + "key": "progress-bar", + "templateUrl": "templates/progress-bar.html" + }, { "key": "time-controller", "templateUrl": "templates/controls/time-controller.html" diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index b8c98ec2d7..4037f31323 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -104,14 +104,14 @@ height: auto; width: auto; line-height: $lh; max-width: 300px; - padding: 0 $interiorMargin 0 $interiorMarginLg; + padding: 0 $interiorMargin 0 $interiorMargin; @include transform(translateX(-50%)); a, span { @include flex(0 1 auto); margin-left: $interiorMargin; &:first-child { - margin-left: 0; + //margin-left: 0; } } a { diff --git a/platform/commonUI/general/res/sass/overlay/_overlay.scss b/platform/commonUI/general/res/sass/overlay/_overlay.scss index 2dce492563..a391609821 100644 --- a/platform/commonUI/general/res/sass/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/overlay/_overlay.scss @@ -45,6 +45,7 @@ .title { @include ellipsize(); font-size: 1.2em; + line-height: 120%; margin-bottom: $interiorMargin; } @@ -57,6 +58,15 @@ bottom: $ovrFooterH + $interiorMargin * 2; left: 0; right: 0; } + + .l-progress-bar { + $h: $progressBarHOverlay; + display: block; + height: $h; + line-height: $h; + margin: .5em 0; + width: 100%; + } .bottom-bar { top: auto; right: 0; bottom: 0; left: 0; diff --git a/platform/commonUI/general/res/templates/indicator.html b/platform/commonUI/general/res/templates/indicator.html index ce2adb421c..20bf22668b 100644 --- a/platform/commonUI/general/res/templates/indicator.html +++ b/platform/commonUI/general/res/templates/indicator.html @@ -31,7 +31,7 @@ {{ngModel.getText()}} - + 5 Objects not saved - - - + Try Again x diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index cb5a9be609..c1d844a3bc 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -2476,7 +2476,7 @@ label.checkbox.custom { width: auto; line-height: 18px; max-width: 300px; - padding: 0 5px 0 10px; + padding: 0 5px 0 5px; -moz-transform: translateX(-50%); -ms-transform: translateX(-50%); -webkit-transform: translateX(-50%); @@ -2487,9 +2487,6 @@ label.checkbox.custom { -webkit-flex: 0 1 auto; flex: 0 1 auto; margin-left: 5px; } - /* line 113, ../../../../general/res/sass/controls/_messages.scss */ - .l-message-banner a:first-child, .l-message-banner span:first-child { - margin-left: 0; } /* line 117, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner a { display: inline-block; } @@ -4243,17 +4240,25 @@ span.req { text-overflow: ellipsis; white-space: nowrap; font-size: 1.2em; + line-height: 120%; margin-bottom: 5px; } -/* line 51, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 52, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .top-bar { height: 60px; } -/* line 55, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 56, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor { top: 70px; bottom: 40px; left: 0; right: 0; } -/* line 61, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 62, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .l-progress-bar { + display: block; + height: 15px; + line-height: 15px; + margin: .5em 0; + width: 100%; } +/* line 71, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar { top: auto; right: 0; @@ -4262,14 +4267,14 @@ span.req { overflow: visible; height: 30px; text-align: right; } - /* line 67, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 77, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { font-size: 95%; height: 30px; line-height: 30px; margin-left: 5px; padding: 0 15px; } - /* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 79, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { background-color: gray; -moz-border-radius: 3px; @@ -4308,14 +4313,14 @@ span.req { /* line 296, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } -/* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 95, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .contents.l-dialog { top: 5px; right: 5px; bottom: 5px; left: 5px; overflow: auto; } - /* line 93, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 103, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .contents.l-dialog .field.l-med input[type='text'] { width: 100%; } diff --git a/platform/commonUI/themes/espresso/res/sass/_constants.scss b/platform/commonUI/themes/espresso/res/sass/_constants.scss index e76c2ca68c..c5a8e30456 100644 --- a/platform/commonUI/themes/espresso/res/sass/_constants.scss +++ b/platform/commonUI/themes/espresso/res/sass/_constants.scss @@ -66,6 +66,7 @@ $colorStatusCaution: #ffa864; $colorStatusAlert: $colorAlert; $colorProgressBarOuter: rgba(#000, 0.1); $colorProgressBarAmt: $colorKey; +$progressBarHOverlay: 15px; $progressBarStripeW: 20px; // Selects diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 3aca678c0a..59c1b2887e 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -2424,7 +2424,7 @@ label.checkbox.custom { width: auto; line-height: 18px; max-width: 300px; - padding: 0 5px 0 10px; + padding: 0 5px 0 5px; -moz-transform: translateX(-50%); -ms-transform: translateX(-50%); -webkit-transform: translateX(-50%); @@ -2435,9 +2435,6 @@ label.checkbox.custom { -webkit-flex: 0 1 auto; flex: 0 1 auto; margin-left: 5px; } - /* line 113, ../../../../general/res/sass/controls/_messages.scss */ - .l-message-banner a:first-child, .l-message-banner span:first-child { - margin-left: 0; } /* line 117, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner a { display: inline-block; } @@ -4166,17 +4163,25 @@ span.req { text-overflow: ellipsis; white-space: nowrap; font-size: 1.2em; + line-height: 120%; margin-bottom: 5px; } -/* line 51, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 52, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .top-bar { height: 60px; } -/* line 55, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 56, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor { top: 70px; bottom: 40px; left: 0; right: 0; } -/* line 61, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 62, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .l-progress-bar { + display: block; + height: 15px; + line-height: 15px; + margin: .5em 0; + width: 100%; } +/* line 71, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar { top: auto; right: 0; @@ -4185,14 +4190,14 @@ span.req { overflow: visible; height: 30px; text-align: right; } - /* line 67, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 77, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { font-size: 95%; height: 30px; line-height: 30px; margin-left: 5px; padding: 0 15px; } - /* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 79, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { background-color: #969696; -moz-border-radius: 4px; @@ -4222,14 +4227,14 @@ span.req { /* line 296, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } -/* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 95, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .contents.l-dialog { top: 5px; right: 5px; bottom: 5px; left: 5px; overflow: auto; } - /* line 93, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 103, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .contents.l-dialog .field.l-med input[type='text'] { width: 100%; } diff --git a/platform/commonUI/themes/snow/res/sass/_constants.scss b/platform/commonUI/themes/snow/res/sass/_constants.scss index 02897ef236..cd13385d6b 100644 --- a/platform/commonUI/themes/snow/res/sass/_constants.scss +++ b/platform/commonUI/themes/snow/res/sass/_constants.scss @@ -66,6 +66,7 @@ $colorStatusCaution: #ffa864; $colorStatusAlert: $colorAlert; $colorProgressBarOuter: rgba(#000, 0.1); $colorProgressBarAmt: #0a0; +$progressBarHOverlay: 15px; $progressBarStripeW: 20px; // Selects diff --git a/testing/dialogTest/res/dialog-launch.html b/testing/dialogTest/res/dialog-launch.html index b9d0d525ea..59d3437c10 100644 --- a/testing/dialogTest/res/dialog-launch.html +++ b/testing/dialogTest/res/dialog-launch.html @@ -1 +1,5 @@ -   \ No newline at end of file + +    +    + + \ No newline at end of file diff --git a/testing/dialogTest/src/DialogLaunchController.js b/testing/dialogTest/src/DialogLaunchController.js index bc088274e9..4d7bb4c258 100644 --- a/testing/dialogTest/src/DialogLaunchController.js +++ b/testing/dialogTest/src/DialogLaunchController.js @@ -27,11 +27,12 @@ define( "use strict"; function DialogLaunchController($scope, dialogService, $timeout, $log, messageSeverity) { - $scope.launchProgress = function () { + $scope.launchProgress = function (knownProgress) { var model = { title: "Progress dialog example", progress: 0, hint: "Calculating...", + unknownProgress: !knownProgress, unknownDuration: false, severity: messageSeverity.INFO, actions: [ @@ -62,7 +63,9 @@ define( if (dialogService.showBlockingMessage(model)) { //Do processing here model.hint = "Processing 100 objects..."; - $timeout(incrementProgress, 1000); + if (knownProgress) { + $timeout(incrementProgress, 1000); + } } else { $log.error("Could not display modal dialog"); } From 60f2f9fb6c50e19348f264cb3aae4f3baeea15a5 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:08:48 -0700 Subject: [PATCH 016/488] [Location] Add getOriginal method Add a getOriginal method to the location capability, to simplify loading of original versions of objects. nasa/openmctweb#147 --- platform/entanglement/bundle.json | 3 +- .../src/capabilities/LocationCapability.js | 38 ++++++++++++++++--- .../capabilities/LocationCapabilitySpec.js | 17 ++++++++- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/platform/entanglement/bundle.json b/platform/entanglement/bundle.json index 61c3d90539..52a65071a8 100644 --- a/platform/entanglement/bundle.json +++ b/platform/entanglement/bundle.json @@ -52,7 +52,8 @@ "key": "location", "name": "Location Capability", "description": "Provides a capability for retrieving the location of an object based upon it's context.", - "implementation": "capabilities/LocationCapability" + "implementation": "capabilities/LocationCapability", + "depends": [ "$q", "$injector" ] } ], "services": [ diff --git a/platform/entanglement/src/capabilities/LocationCapability.js b/platform/entanglement/src/capabilities/LocationCapability.js index 17d678f57e..38974ecb0f 100644 --- a/platform/entanglement/src/capabilities/LocationCapability.js +++ b/platform/entanglement/src/capabilities/LocationCapability.js @@ -12,11 +12,41 @@ define( * * @constructor */ - function LocationCapability(domainObject) { + function LocationCapability($q, $injector, domainObject) { this.domainObject = domainObject; + this.$q = $q; + this.$injector = $injector; return this; } + /** + * Get an instance of this domain object in its original location. + * + * @returns {Promise.} a promise for the original + * instance of this domain object + */ + LocationCapability.prototype.getOriginal = function () { + var id; + + if (this.isOriginal()) { + return this.$q.when(this.domainObject); + } + + id = this.domainObject.getId(); + + this.objectService = + this.objectService || this.$injector.get("objectService"); + + // Assume that an object will be correctly contextualized when + // loaded directly from the object service; this is true + // so long as LocatingObjectDecorator is present, and that + // decorator is also contained in this bundle. + return this.objectService.getObjects([id]) + .then(function (objects) { + return objects[id]; + }); + }; + /** * Set the primary location (the parent id) of the current domain * object. @@ -78,10 +108,6 @@ define( return !this.isLink(); }; - function createLocationCapability(domainObject) { - return new LocationCapability(domainObject); - } - - return createLocationCapability; + return LocationCapability; } ); diff --git a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js index 9cbfcc1bea..497158a67e 100644 --- a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js +++ b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js @@ -7,6 +7,7 @@ define( '../ControlledPromise' ], function (LocationCapability, domainObjectFactory, ControlledPromise) { + 'use strict'; describe("LocationCapability", function () { @@ -14,13 +15,16 @@ define( var locationCapability, persistencePromise, mutationPromise, + mockQ, + mockInjector, + mockObjectService, domainObject; beforeEach(function () { domainObject = domainObjectFactory({ capabilities: { context: { - getParent: function() { + getParent: function () { return domainObjectFactory({id: 'root'}); } }, @@ -35,6 +39,11 @@ define( } }); + mockQ = jasmine.createSpyObj("$q", ["when"]); + mockInjector = jasmine.createSpyObj("$injector", ["get"]); + mockObjectService = + jasmine.createSpyObj("objectService", ["getObjects"]); + persistencePromise = new ControlledPromise(); domainObject.capabilities.persistence.persist.andReturn( persistencePromise @@ -49,7 +58,11 @@ define( } ); - locationCapability = new LocationCapability(domainObject); + locationCapability = new LocationCapability( + mockQ, + mockObjectService, + domainObject + ); }); it("returns contextual location", function () { From e3afaf0842afa5abb3f3991f65a20f39c937f0a5 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:22:16 -0700 Subject: [PATCH 017/488] [Entanglement] Add Go To Original nasa/openmctweb#147 --- platform/entanglement/bundle.json | 8 +++ .../src/actions/GoToOriginalAction.js | 62 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 platform/entanglement/src/actions/GoToOriginalAction.js diff --git a/platform/entanglement/bundle.json b/platform/entanglement/bundle.json index 52a65071a8..d8cde0ada6 100644 --- a/platform/entanglement/bundle.json +++ b/platform/entanglement/bundle.json @@ -30,6 +30,14 @@ "category": "contextual", "implementation": "actions/LinkAction.js", "depends": ["locationService", "linkService"] + }, + { + "key": "follow", + "name": "Go To Original", + "description": "Go to the original, un-linked instance of this object.", + "glyph": "\u00F4", + "category": "contextual", + "implementation": "actions/GoToOriginalAction.js" } ], "components": [ diff --git a/platform/entanglement/src/actions/GoToOriginalAction.js b/platform/entanglement/src/actions/GoToOriginalAction.js new file mode 100644 index 0000000000..9722915ad6 --- /dev/null +++ b/platform/entanglement/src/actions/GoToOriginalAction.js @@ -0,0 +1,62 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ + +/*global define */ +define( + function () { + "use strict"; + + /** + * Implements the "Go To Original" action, which follows a link back + * to an original instance of an object. + * + * @implements {Action} + * @constructor + * @private + * @memberof platform/entanglement + * @param {ActionContext} context the context in which the action + * will be performed + */ + function GoToOriginalAction(context) { + this.domainObject = context.domainObject; + } + + GoToOriginalAction.prototype.perform = function () { + return this.domainObject.getCapability("location").getOriginal() + .then(function (originalObject) { + var actionCapability = + originalObject.getCapability("action"); + return actionCapability && + actionCapability.perform("navigate"); + }); + }; + + GoToOriginalAction.appliesTo = function (context) { + var domainObject = context.domainObject; + return domainObject && domainObject.hasCapability("location") + && domainObject.getCapability("location").isLink(); + }; + + return GoToOriginalAction; + } +); + From 70bbd3cf97f43914572df00783763b208cbf08fa Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:37:37 -0700 Subject: [PATCH 018/488] [Entanglement] Add test cases for Go To Original --- .../test/actions/GoToOriginalActionSpec.js | 95 +++++++++++++++++++ platform/entanglement/test/suite.json | 1 + 2 files changed, 96 insertions(+) create mode 100644 platform/entanglement/test/actions/GoToOriginalActionSpec.js diff --git a/platform/entanglement/test/actions/GoToOriginalActionSpec.js b/platform/entanglement/test/actions/GoToOriginalActionSpec.js new file mode 100644 index 0000000000..40c2f213ce --- /dev/null +++ b/platform/entanglement/test/actions/GoToOriginalActionSpec.js @@ -0,0 +1,95 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ + +/*global define,describe,beforeEach,it,jasmine,expect */ + +define( + [ + '../../src/actions/GoToOriginalAction', + '../DomainObjectFactory', + '../ControlledPromise' + ], + function (GoToOriginalAction, domainObjectFactory, ControlledPromise) { + 'use strict'; + + describe("The 'go to original' action", function () { + var testContext, + originalDomainObject, + mockLocationCapability, + mockOriginalActionCapability, + originalPromise, + action; + + beforeEach(function () { + mockLocationCapability = jasmine.createSpyObj( + 'location', + [ 'isLink', 'isOriginal', 'getOriginal' ] + ); + mockOriginalActionCapability = jasmine.createSpyObj( + 'action', + [ 'perform', 'getActions' ] + ); + originalPromise = new ControlledPromise(); + mockLocationCapability.getOriginal.andReturn(originalPromise); + mockLocationCapability.isLink.andReturn(true); + mockLocationCapability.isOriginal.andCallFake(function () { + return !mockLocationCapability.isLink(); + }); + testContext = { + domainObject: domainObjectFactory({ + capabilities: { + location: mockLocationCapability + } + }) + }; + originalDomainObject = domainObjectFactory({ + capabilities: { + action: mockOriginalActionCapability + } + }); + + action = new GoToOriginalAction(testContext); + }); + + it("is applicable to links", function () { + expect(GoToOriginalAction.appliesTo(testContext)) + .toBeTruthy(); + }); + + it("is not applicable to originals", function () { + mockLocationCapability.isLink.andReturn(false); + expect(GoToOriginalAction.appliesTo(testContext)) + .toBeFalsy(); + }); + + it("navigates to original objects when performed", function () { + expect(mockOriginalActionCapability.perform) + .not.toHaveBeenCalled(); + action.perform(); + originalPromise.resolve(originalDomainObject); + expect(mockOriginalActionCapability.perform) + .toHaveBeenCalledWith('navigate'); + }); + + }); + } +); diff --git a/platform/entanglement/test/suite.json b/platform/entanglement/test/suite.json index 12831b407a..2ce90499e8 100644 --- a/platform/entanglement/test/suite.json +++ b/platform/entanglement/test/suite.json @@ -1,5 +1,6 @@ [ "actions/AbstractComposeAction", + "actions/GoToOriginalAction", "services/CopyService", "services/LinkService", "services/MoveService", From a4944717a1eb1bcbe385dd3ddc7321ea8805ab34 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:47:37 -0700 Subject: [PATCH 019/488] [Location] Test getOriginal method --- .../capabilities/LocationCapabilitySpec.js | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js index 497158a67e..938f0d843c 100644 --- a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js +++ b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js @@ -22,6 +22,7 @@ define( beforeEach(function () { domainObject = domainObjectFactory({ + id: "testObject", capabilities: { context: { getParent: function () { @@ -60,7 +61,7 @@ define( locationCapability = new LocationCapability( mockQ, - mockObjectService, + mockInjector, domainObject ); }); @@ -101,6 +102,57 @@ define( expect(whenComplete).toHaveBeenCalled(); }); + describe("when used to load an original instance", function () { + var objectPromise, + qPromise, + originalObjects, + mockCallback; + + function resolvePromises() { + if (mockQ.when.calls.length > 0) { + qPromise.resolve(mockQ.when.mostRecentCall.args[0]); + } + if (mockObjectService.getObjects.calls.length > 0) { + objectPromise.resolve(originalObjects); + } + } + + beforeEach(function () { + objectPromise = new ControlledPromise(); + qPromise = new ControlledPromise(); + originalObjects = { + testObject: domainObjectFactory() + }; + + mockInjector.get.andCallFake(function (key) { + return key === 'objectService' && mockObjectService; + }); + mockObjectService.getObjects.andReturn(objectPromise); + mockQ.when.andReturn(qPromise); + + mockCallback = jasmine.createSpy('callback'); + }); + + it("provides originals directly", function () { + domainObject.model.location = 'root'; + locationCapability.getOriginal().then(mockCallback); + expect(mockCallback).not.toHaveBeenCalled(); + resolvePromises(); + expect(mockCallback) + .toHaveBeenCalledWith(domainObject); + }); + + it("loads from the object service for links", function () { + domainObject.model.location = 'some-other-root'; + locationCapability.getOriginal().then(mockCallback); + expect(mockCallback).not.toHaveBeenCalled(); + resolvePromises(); + expect(mockCallback) + .toHaveBeenCalledWith(originalObjects.testObject); + }); + }); + + }); }); } From bf41d82a78282fdac3acaed1845188bf69eed651 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:50:35 -0700 Subject: [PATCH 020/488] [Entanglement] Restore missing specs Restore specs which had been omitted from suite.json (but currently succeed for the relevant scripts); done in the context of nasa/openmctweb#147 --- platform/entanglement/test/suite.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/entanglement/test/suite.json b/platform/entanglement/test/suite.json index 2ce90499e8..89c082f9c8 100644 --- a/platform/entanglement/test/suite.json +++ b/platform/entanglement/test/suite.json @@ -1,6 +1,9 @@ [ "actions/AbstractComposeAction", + "actions/CopyAction", "actions/GoToOriginalAction", + "actions/LinkAction", + "actions/MoveAction", "services/CopyService", "services/LinkService", "services/MoveService", From 6840e596a5ddaf3cd0364fbf03151e8fe89dd969 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 7 Oct 2015 10:25:47 -0700 Subject: [PATCH 021/488] [Frontend] Adding progress-bar.html template open #159 open #170 --- platform/commonUI/general/res/templates/progress-bar.html | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 platform/commonUI/general/res/templates/progress-bar.html diff --git a/platform/commonUI/general/res/templates/progress-bar.html b/platform/commonUI/general/res/templates/progress-bar.html new file mode 100644 index 0000000000..8f39196341 --- /dev/null +++ b/platform/commonUI/general/res/templates/progress-bar.html @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file From ad4292f1e969f50ddbf390889de13b91c295cd6f Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 7 Oct 2015 11:45:08 -0700 Subject: [PATCH 022/488] [Frontend] Mods to DialogService model and dialogs open #159 open #170 Properly re-cast model.hint and added model.actionText; Moved progressText into progress-bar.html; Tweaks to dialog styles; Tweaked styles for progress bar; Normalized various dialog templates markup; --- .../res/templates/blocking-message.html | 20 ++- .../commonUI/dialog/res/templates/dialog.html | 16 +-- .../dialog/res/templates/overlay-options.html | 12 +- platform/commonUI/dialog/src/DialogService.js | 3 + .../general/res/sass/controls/_controls.scss | 17 +-- .../general/res/sass/controls/_messages.scss | 6 +- .../general/res/sass/overlay/_overlay.scss | 4 + .../general/res/templates/message-banner.html | 7 +- .../general/res/templates/progress-bar.html | 10 +- .../espresso/res/css/theme-espresso.css | 114 ++++++++++-------- .../themes/snow/res/css/theme-snow.css | 114 ++++++++++-------- .../dialogTest/src/DialogLaunchController.js | 22 ++-- 12 files changed, 186 insertions(+), 159 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/blocking-message.html b/platform/commonUI/dialog/res/templates/blocking-message.html index bf2ff3d674..8db939cfdc 100644 --- a/platform/commonUI/dialog/res/templates/blocking-message.html +++ b/platform/commonUI/dialog/res/templates/blocking-message.html @@ -1,19 +1,17 @@
{{ngModel.dialog.title}}
-
Do not navigate away from this page or close this browser tab while this operation is in progress.
+
{{ngModel.dialog.hint}}
-
-
-
{{ngModel.dialog.hint}}
- - - - -
{{ngModel.dialog.progressText}}
+
+
+ {{ngModel.dialog.actionText}}
+ + +
diff --git a/platform/commonUI/dialog/res/templates/dialog.html b/platform/commonUI/dialog/res/templates/dialog.html index e3379b034e..4c7c02e3f2 100644 --- a/platform/commonUI/dialog/res/templates/dialog.html +++ b/platform/commonUI/dialog/res/templates/dialog.html @@ -21,17 +21,13 @@ -->
{{ngModel.title}}
-
- All fields marked * are required. -
+
All fields marked * are required.
-
-
-
- - -
+
+ +
\ No newline at end of file diff --git a/platform/commonUI/general/res/templates/progress-bar.html b/platform/commonUI/general/res/templates/progress-bar.html index 8f39196341..337494164c 100644 --- a/platform/commonUI/general/res/templates/progress-bar.html +++ b/platform/commonUI/general/res/templates/progress-bar.html @@ -1,7 +1,9 @@ - + - \ No newline at end of file + +
+ {{ngModel.dialog.progressText}} +
\ No newline at end of file diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index c1d844a3bc..5caef16706 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -1881,33 +1881,35 @@ label.checkbox.custom { background-image: linear-gradient(rgba(0, 0, 0, 0) 5%, rgba(255, 255, 255, 0.25) 30%, rgba(0, 0, 0, 0) 100%); } /* line 331, ../../../../general/res/sass/controls/_controls.scss */ .s-progress-bar:not(.indeterminate) .progress-amt:before { - -moz-animation: progress 0.3s linear infinite; - -webkit-animation: progress 0.3s linear infinite; - animation: progress 0.3s linear infinite; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjIiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + -moz-animation: progress 0.4s linear infinite; + -webkit-animation: progress 0.4s linear infinite; + animation: progress 0.4s linear infinite; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjEiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4xIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); background-size: 100%; - background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); - background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); - background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); + background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.1) 100%); + background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.1) 100%); + background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.1) 100%); background-position: 0 center; background-repeat: repeat-x; background-size: 20px 40%; } - /* line 338, ../../../../general/res/sass/controls/_controls.scss */ + /* line 339, ../../../../general/res/sass/controls/_controls.scss */ .s-progress-bar.indeterminate .progress-amt:before { -moz-animation: progress 0.6s linear infinite; -webkit-animation: progress 0.6s linear infinite; animation: progress 0.6s linear infinite; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjQiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC40Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjEuMCIgeDI9IjAuMCIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIyNSUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PHN0b3Agb2Zmc2V0PSIyNSUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSI1MCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSI1MCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PHN0b3Agb2Zmc2V0PSI3NSUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PHN0b3Agb2Zmc2V0PSI3NSUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; - background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); - background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); - background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); - background-position: 0 center; - background-repeat: repeat-x; - background-size: 20px 40%; } + background-image: -moz-linear-gradient(135deg, rgba(255, 255, 255, 0.2) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0) 100%); + background-image: -webkit-linear-gradient(135deg, rgba(255, 255, 255, 0.2) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0) 100%); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.2) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0) 100%); + background-repeat: repeat; + background-size: 20px 20px; } + /* line 344, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar.indeterminate .progress-amt:after { + display: none; } /******************************************************** SLIDERS */ -/* line 351, ../../../../general/res/sass/controls/_controls.scss */ +/* line 354, ../../../../general/res/sass/controls/_controls.scss */ .slider .slot { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -1926,7 +1928,7 @@ label.checkbox.custom { right: 0; bottom: auto; left: 0; } -/* line 362, ../../../../general/res/sass/controls/_controls.scss */ +/* line 365, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob { background-color: #333; -moz-border-radius: 3px; @@ -2012,12 +2014,12 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; border-color: #0099cc; } - /* line 373, ../../../../general/res/sass/controls/_controls.scss */ + /* line 376, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob:before { top: 1px; bottom: 3px; left: 5px; } -/* line 380, ../../../../general/res/sass/controls/_controls.scss */ +/* line 383, ../../../../general/res/sass/controls/_controls.scss */ .slider .range { background: rgba(0, 153, 204, 0.6); cursor: ew-resize; @@ -2028,13 +2030,13 @@ label.checkbox.custom { left: auto; height: auto; width: auto; } - /* line 390, ../../../../general/res/sass/controls/_controls.scss */ + /* line 393, ../../../../general/res/sass/controls/_controls.scss */ .slider .range:hover { background: rgba(0, 153, 204, 0.7); } /******************************************************** BROWSER ELEMENTS */ @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 399, ../../../../general/res/sass/controls/_controls.scss */ + /* line 402, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -2049,7 +2051,7 @@ label.checkbox.custom { height: 10px; width: 10px; } - /* line 408, ../../../../general/res/sass/controls/_controls.scss */ + /* line 411, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzU5NTk1OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2063,7 +2065,7 @@ label.checkbox.custom { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } - /* line 417, ../../../../general/res/sass/controls/_controls.scss */ + /* line 420, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb:hover { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzVlNWU1ZSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzUyNTI1MiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2072,7 +2074,7 @@ label.checkbox.custom { background-image: -webkit-linear-gradient(#5e5e5e, #525252 20px); background-image: linear-gradient(#5e5e5e, #525252 20px); } - /* line 422, ../../../../general/res/sass/controls/_controls.scss */ + /* line 425, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-corner { background: rgba(0, 0, 0, 0.4); } } /***************************************************************************** @@ -2483,29 +2485,32 @@ label.checkbox.custom { transform: translateX(-50%); z-index: 2; } /* line 110, ../../../../general/res/sass/controls/_messages.scss */ - .l-message-banner a, .l-message-banner span { + .l-message-banner .banner-elem { -webkit-flex: 0 1 auto; flex: 0 1 auto; margin-left: 5px; } - /* line 117, ../../../../general/res/sass/controls/_messages.scss */ + /* line 114, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner a { display: inline-block; } - /* line 120, ../../../../general/res/sass/controls/_messages.scss */ + /* line 117, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .l-action { line-height: 15px; padding: 0 5px; } - /* line 124, ../../../../general/res/sass/controls/_messages.scss */ + /* line 121, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .close { cursor: pointer; font-size: 7px; width: 8px; } - /* line 130, ../../../../general/res/sass/controls/_messages.scss */ + /* line 127, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .l-progress-bar { height: 8px; line-height: 8px; width: 100px; } + /* line 133, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner .progress-info.progress-estimate { + display: none; } -/* line 139, ../../../../general/res/sass/controls/_messages.scss */ +/* line 137, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner, .s-message-banner .s-action { -moz-transition-property: background-color; @@ -2521,7 +2526,7 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; } -/* line 144, ../../../../general/res/sass/controls/_messages.scss */ +/* line 142, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner { -moz-border-radius: 3px; -webkit-border-radius: 3px; @@ -2538,21 +2543,21 @@ label.checkbox.custom { /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .s-action:hover { background-color: gray; } - /* line 148, ../../../../general/res/sass/controls/_messages.scss */ + /* line 146, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner a { color: inherit; } - /* line 149, ../../../../general/res/sass/controls/_messages.scss */ + /* line 147, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .s-action { -moz-border-radius: 2px; -webkit-border-radius: 2px; border-radius: 2px; } - /* line 152, ../../../../general/res/sass/controls/_messages.scss */ + /* line 150, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .close { opacity: 0.5; } - /* line 154, ../../../../general/res/sass/controls/_messages.scss */ + /* line 152, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .close:hover { opacity: 1; } - /* line 158, ../../../../general/res/sass/controls/_messages.scss */ + /* line 156, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok { background-color: #189543; color: #ccc; } @@ -2565,7 +2570,7 @@ label.checkbox.custom { /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action:hover { background-color: #189543; } - /* line 161, ../../../../general/res/sass/controls/_messages.scss */ + /* line 159, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution { background-color: #ca5900; color: #ccc; } @@ -4243,22 +4248,25 @@ span.req { line-height: 120%; margin-bottom: 5px; } /* line 52, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .hint { + color: #b3b3b3; } +/* line 56, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .top-bar { height: 60px; } -/* line 56, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 60, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor { top: 70px; bottom: 40px; left: 0; right: 0; } -/* line 62, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 66, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .l-progress-bar { display: block; height: 15px; line-height: 15px; margin: .5em 0; width: 100%; } -/* line 71, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 75, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar { top: auto; right: 0; @@ -4267,14 +4275,14 @@ span.req { overflow: visible; height: 30px; text-align: right; } - /* line 77, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 81, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { font-size: 95%; height: 30px; line-height: 30px; margin-left: 5px; padding: 0 15px; } - /* line 79, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 83, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { background-color: gray; -moz-border-radius: 3px; @@ -4313,14 +4321,14 @@ span.req { /* line 296, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } -/* line 95, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 99, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .contents.l-dialog { top: 5px; right: 5px; bottom: 5px; left: 5px; overflow: auto; } - /* line 103, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 107, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .contents.l-dialog .field.l-med input[type='text'] { width: 100%; } diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 59c1b2887e..5c94d22297 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -1851,33 +1851,35 @@ label.checkbox.custom { background-image: linear-gradient(rgba(0, 0, 0, 0) 5%, rgba(255, 255, 255, 0.25) 30%, rgba(0, 0, 0, 0) 100%); } /* line 331, ../../../../general/res/sass/controls/_controls.scss */ .s-progress-bar:not(.indeterminate) .progress-amt:before { - -moz-animation: progress 0.3s linear infinite; - -webkit-animation: progress 0.3s linear infinite; - animation: progress 0.3s linear infinite; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjIiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + -moz-animation: progress 0.4s linear infinite; + -webkit-animation: progress 0.4s linear infinite; + animation: progress 0.4s linear infinite; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjEiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4xIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); background-size: 100%; - background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); - background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); - background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.2) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 100%); + background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.1) 100%); + background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.1) 100%); + background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.1) 100%); background-position: 0 center; background-repeat: repeat-x; background-size: 20px 40%; } - /* line 338, ../../../../general/res/sass/controls/_controls.scss */ + /* line 339, ../../../../general/res/sass/controls/_controls.scss */ .s-progress-bar.indeterminate .progress-amt:before { -moz-animation: progress 0.6s linear infinite; -webkit-animation: progress 0.6s linear infinite; animation: progress 0.6s linear infinite; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjAuNSIgeDI9IjAuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjQiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC40Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjEuMCIgeDI9IjAuMCIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIyNSUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PHN0b3Agb2Zmc2V0PSIyNSUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSI1MCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSI1MCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PHN0b3Agb2Zmc2V0PSI3NSUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMC4yIi8+PHN0b3Agb2Zmc2V0PSI3NSUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; - background-image: -moz-linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); - background-image: -webkit-linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); - background-image: linear-gradient(-90deg, rgba(255, 255, 255, 0.4) 0%, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.4) 100%); - background-position: 0 center; - background-repeat: repeat-x; - background-size: 20px 40%; } + background-image: -moz-linear-gradient(135deg, rgba(255, 255, 255, 0.2) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0) 100%); + background-image: -webkit-linear-gradient(135deg, rgba(255, 255, 255, 0.2) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0) 100%); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.2) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0) 100%); + background-repeat: repeat; + background-size: 20px 20px; } + /* line 344, ../../../../general/res/sass/controls/_controls.scss */ + .s-progress-bar.indeterminate .progress-amt:after { + display: none; } /******************************************************** SLIDERS */ -/* line 351, ../../../../general/res/sass/controls/_controls.scss */ +/* line 354, ../../../../general/res/sass/controls/_controls.scss */ .slider .slot { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -1896,7 +1898,7 @@ label.checkbox.custom { right: 0; bottom: auto; left: 0; } -/* line 362, ../../../../general/res/sass/controls/_controls.scss */ +/* line 365, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob { background-color: #969696; -moz-border-radius: 4px; @@ -1966,12 +1968,12 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; border-color: #fcfcfc; } - /* line 373, ../../../../general/res/sass/controls/_controls.scss */ + /* line 376, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob:before { top: 1px; bottom: 3px; left: 5px; } -/* line 380, ../../../../general/res/sass/controls/_controls.scss */ +/* line 383, ../../../../general/res/sass/controls/_controls.scss */ .slider .range { background: rgba(0, 153, 204, 0.6); cursor: ew-resize; @@ -1982,13 +1984,13 @@ label.checkbox.custom { left: auto; height: auto; width: auto; } - /* line 390, ../../../../general/res/sass/controls/_controls.scss */ + /* line 393, ../../../../general/res/sass/controls/_controls.scss */ .slider .range:hover { background: rgba(0, 153, 204, 0.7); } /******************************************************** BROWSER ELEMENTS */ @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 399, ../../../../general/res/sass/controls/_controls.scss */ + /* line 402, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -2003,7 +2005,7 @@ label.checkbox.custom { height: 10px; width: 10px; } - /* line 408, ../../../../general/res/sass/controls/_controls.scss */ + /* line 411, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzg5ODk4OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzdkN2Q3ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2017,7 +2019,7 @@ label.checkbox.custom { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } - /* line 417, ../../../../general/res/sass/controls/_controls.scss */ + /* line 420, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb:hover { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwYWNlNiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwOTljYyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2026,7 +2028,7 @@ label.checkbox.custom { background-image: -webkit-linear-gradient(#00ace6, #0099cc 20px); background-image: linear-gradient(#00ace6, #0099cc 20px); } - /* line 422, ../../../../general/res/sass/controls/_controls.scss */ + /* line 425, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-corner { background: rgba(0, 0, 0, 0.1); } } /***************************************************************************** @@ -2431,29 +2433,32 @@ label.checkbox.custom { transform: translateX(-50%); z-index: 2; } /* line 110, ../../../../general/res/sass/controls/_messages.scss */ - .l-message-banner a, .l-message-banner span { + .l-message-banner .banner-elem { -webkit-flex: 0 1 auto; flex: 0 1 auto; margin-left: 5px; } - /* line 117, ../../../../general/res/sass/controls/_messages.scss */ + /* line 114, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner a { display: inline-block; } - /* line 120, ../../../../general/res/sass/controls/_messages.scss */ + /* line 117, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .l-action { line-height: 15px; padding: 0 5px; } - /* line 124, ../../../../general/res/sass/controls/_messages.scss */ + /* line 121, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .close { cursor: pointer; font-size: 7px; width: 8px; } - /* line 130, ../../../../general/res/sass/controls/_messages.scss */ + /* line 127, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .l-progress-bar { height: 8px; line-height: 8px; width: 100px; } + /* line 133, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner .progress-info.progress-estimate { + display: none; } -/* line 139, ../../../../general/res/sass/controls/_messages.scss */ +/* line 137, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner, .s-message-banner .s-action { -moz-transition-property: background-color; @@ -2469,7 +2474,7 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; } -/* line 144, ../../../../general/res/sass/controls/_messages.scss */ +/* line 142, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner { -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -2486,21 +2491,21 @@ label.checkbox.custom { /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .s-action:hover { background-color: gray; } - /* line 148, ../../../../general/res/sass/controls/_messages.scss */ + /* line 146, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner a { color: inherit; } - /* line 149, ../../../../general/res/sass/controls/_messages.scss */ + /* line 147, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .s-action { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; } - /* line 152, ../../../../general/res/sass/controls/_messages.scss */ + /* line 150, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .close { opacity: 0.5; } - /* line 154, ../../../../general/res/sass/controls/_messages.scss */ + /* line 152, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .close:hover { opacity: 1; } - /* line 158, ../../../../general/res/sass/controls/_messages.scss */ + /* line 156, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok { background-color: #189543; color: #fff; } @@ -2513,7 +2518,7 @@ label.checkbox.custom { /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action:hover { background-color: #189543; } - /* line 161, ../../../../general/res/sass/controls/_messages.scss */ + /* line 159, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution { background-color: #ca5900; color: #fff; } @@ -4166,22 +4171,25 @@ span.req { line-height: 120%; margin-bottom: 5px; } /* line 52, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .hint { + color: #999999; } +/* line 56, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .top-bar { height: 60px; } -/* line 56, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 60, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor { top: 70px; bottom: 40px; left: 0; right: 0; } -/* line 62, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 66, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .l-progress-bar { display: block; height: 15px; line-height: 15px; margin: .5em 0; width: 100%; } -/* line 71, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 75, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar { top: auto; right: 0; @@ -4190,14 +4198,14 @@ span.req { overflow: visible; height: 30px; text-align: right; } - /* line 77, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 81, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { font-size: 95%; height: 30px; line-height: 30px; margin-left: 5px; padding: 0 15px; } - /* line 79, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 83, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { background-color: #969696; -moz-border-radius: 4px; @@ -4227,14 +4235,14 @@ span.req { /* line 296, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } -/* line 95, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 99, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .contents.l-dialog { top: 5px; right: 5px; bottom: 5px; left: 5px; overflow: auto; } - /* line 103, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 107, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .contents.l-dialog .field.l-med input[type='text'] { width: 100%; } diff --git a/testing/dialogTest/src/DialogLaunchController.js b/testing/dialogTest/src/DialogLaunchController.js index 4d7bb4c258..374e43a150 100644 --- a/testing/dialogTest/src/DialogLaunchController.js +++ b/testing/dialogTest/src/DialogLaunchController.js @@ -29,9 +29,10 @@ define( function DialogLaunchController($scope, dialogService, $timeout, $log, messageSeverity) { $scope.launchProgress = function (knownProgress) { var model = { - title: "Progress dialog example", + title: "Progress Dialog Example", progress: 0, - hint: "Calculating...", + hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.", + actionText: "Calculating...", unknownProgress: !knownProgress, unknownDuration: false, severity: messageSeverity.INFO, @@ -62,7 +63,7 @@ define( if (dialogService.showBlockingMessage(model)) { //Do processing here - model.hint = "Processing 100 objects..."; + model.actionText = "Processing 100 objects..."; if (knownProgress) { $timeout(incrementProgress, 1000); } @@ -73,14 +74,21 @@ define( $scope.launchError = function () { var model = { - title: "Error Message Title", - hint: "Something happened. It was not so good.", + title: "Error Dialog Example", + actionText: "Something happened, and it was not good.", severity: messageSeverity.ERROR, actions: [ { - label: "OK", + label: "Try Again", action: function () { - $log.debug("OK Pressed"); + $log.debug("Try Again Pressed"); + dialogService.dismiss(); + } + }, + { + label: "Cancel", + action: function () { + $log.debug("Cancel Pressed"); dialogService.dismiss(); } } From 7f529eec685a0ef3cf4b225271434306d5892b30 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 7 Oct 2015 12:11:30 -0700 Subject: [PATCH 023/488] [Frontend] Mods to OverlayService open #159 open #170 OverlayService can now be passed typeClass, which is added to the CSS class of the overlay's mct-include tag - intent is to allow the overlay to size itself based on the type of content to be displayed; --- platform/commonUI/dialog/src/DialogService.js | 7 +++++-- platform/commonUI/dialog/src/OverlayService.js | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index f598f0da58..99d361cf81 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -89,7 +89,8 @@ define( // will handle actual insertion into the DOM this.overlay = this.overlayService.createOverlay( key, - model + model, + "t-dialog" ); // Track that a dialog is already visible, to @@ -230,6 +231,7 @@ define( * progress, as well as a series of actions that * the user can take if necessary * @param {DialogModel} dialogModel defines options for the dialog + * @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class * @returns {boolean} */ DialogService.prototype.showBlockingMessage = function(dialogModel) { @@ -238,7 +240,8 @@ define( // will handle actual insertion into the DOM this.overlay = this.overlayService.createOverlay( "blocking-message", - {dialog: dialogModel} + {dialog: dialogModel}, + "t-dialog-sm" ); this.dialogVisible = true; return true; diff --git a/platform/commonUI/dialog/src/OverlayService.js b/platform/commonUI/dialog/src/OverlayService.js index 5faba5dcf6..1874adb271 100644 --- a/platform/commonUI/dialog/src/OverlayService.js +++ b/platform/commonUI/dialog/src/OverlayService.js @@ -28,7 +28,7 @@ define( // Template to inject into the DOM to show the dialog; really just points to // the a specific template that can be included via mct-include - var TEMPLATE = ''; + var TEMPLATE = ''; /** @@ -72,7 +72,7 @@ define( * included overlay template (this will be passed * in via ng-model) */ - OverlayService.prototype.createOverlay = function (key, overlayModel) { + OverlayService.prototype.createOverlay = function (key, overlayModel, typeClass) { // Create a new scope for this overlay var scope = this.newScope(), element; @@ -87,9 +87,13 @@ define( // If no model is supplied, just fill in a default "cancel" overlayModel = overlayModel || { cancel: dismiss }; + // If no typeClass is specified, set to default "t-dialog" + typeClass = typeClass || 't-dialog'; + // Populate the scope; will be passed directly to the template scope.overlay = overlayModel; scope.key = key; + scope.typeClass = typeClass; // Create the overlay element and add it to the document's body element = this.$compile(TEMPLATE)(scope); From 3af23b7bc527a478744f572720a9efce45bc509a Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 7 Oct 2015 14:30:19 -0700 Subject: [PATCH 024/488] Added test cases for notification service --- bundles.json | 1 + .../notification/src/MessageSeverity.js | 9 + .../notification/src/NotificationService.js | 164 +++++++++++++----- .../test/NotificationServiceSpec.js | 146 ++++++++++++++++ .../commonUI/notification/test/suite.json | 3 + 5 files changed, 280 insertions(+), 43 deletions(-) create mode 100644 platform/commonUI/notification/src/MessageSeverity.js create mode 100644 platform/commonUI/notification/test/NotificationServiceSpec.js create mode 100644 platform/commonUI/notification/test/suite.json diff --git a/bundles.json b/bundles.json index a0f7edd7a8..15a05f9334 100644 --- a/bundles.json +++ b/bundles.json @@ -26,6 +26,7 @@ "platform/policy", "platform/entanglement", "platform/search", + "platform/commonUI/notification", "example/imagery", "example/eventGenerator", diff --git a/platform/commonUI/notification/src/MessageSeverity.js b/platform/commonUI/notification/src/MessageSeverity.js new file mode 100644 index 0000000000..5f17b917fe --- /dev/null +++ b/platform/commonUI/notification/src/MessageSeverity.js @@ -0,0 +1,9 @@ +/** + * Created by akhenry on 10/7/15. + */ +define(function(){ + return { + SUCCESS: 0, + ERROR: 1 + } +}) \ No newline at end of file diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 07aea14da4..30e746cc42 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -27,8 +27,8 @@ * @namespace platform/commonUI/dialog */ define( - [], - function () { + ["./MessageSeverity"], + function (MessageSeverity) { "use strict"; /** * The notification service is responsible for informing the user of @@ -36,28 +36,9 @@ define( * @memberof platform/commonUI/notification * @constructor */ - function NotificationService($log, $timeout, messageSeverity, DEFAULT_AUTO_DISMISS) { - //maintain an array of notifications. - //expose a method for adding notifications. - //expose a method for dismissing notifications. - //expose a method for minimizing notifications. - //expose a method for getting the 'current' notification. How - //this is determined could be a little nuanced. - //Allow for auto-dismissal of success messages - // - // - // - // Questions: - // 1) What happens when a newer, but lower priority - // message arrives (eg. success). Just show it? It will - // auto-dismissed, and the existing error message will be - // exposed. - // - + function NotificationService($timeout, DEFAULT_AUTO_DISMISS) { this.notifications = []; - this.$log = $log; this.$timeout = $timeout; - this.messageSeverity = messageSeverity; this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS; /** @@ -71,7 +52,6 @@ define( * @type {{notification: undefined}} */ this.active = { - notification: undefined }; } /** @@ -97,14 +77,18 @@ define( this.model = model; } - Notification.prototype.minimize = function () { + Notification.prototype.minimize = function (setValue) { if (typeof setValue !== undefined){ - model.minimized = setValue; + this.model.minimized = setValue; } else { - return model.minimized; + return this.model.minimized; } }; + NotificationService.prototype.getActiveNotification = function (){ + return this.active.notification; + } + /** * model = { * @@ -112,31 +96,125 @@ define( * @param model */ NotificationService.prototype.notify = function (model) { - var notification = new Notification(model); + var notification = new Notification(model), + that=this; this.notifications.push(notification); - this.setActiveNotification(notification); - }; + /* + Check if there is already an active (ie. visible) notification + */ + if (!this.active.notification){ + setActiveNotification.call(this, notification); + } else if (!this.active.timeout){ + /* + If there is already an active notification, time it out. If it's + already got a timeout in progress (either because it has had + timeout forced because of a queue of messages, or it had an + autodismiss specified), leave it to run. - - NotificationService.prototype.setActiveNotification = function () { - //If there is a message active, time it out, and then chain a - // new message to appear. - if (this.active.timeout){ - this.active.timeout = this.active.timeout.then(function (){ - this.active.timeout = $timeout(function(){ - this.dismiss(this.active.notification); - }); + This notifcation has been added to queue and will be + serviced as soon as possible. + */ + this.active.timeout = this.$timeout(function () { + that.dismissOrMinimize(that.active.notification); }); } + }; - NotificationService.prototype.dismiss = function (notification) { - var index = this.notifications.indexOf(notification); - if (index >= 0) { - this.notifications = this.notifications.splice(index, 1); - delete this.active.notification; + function setActiveNotification (notification) { + var that = this; + this.active.notification = notification; + /* + If autoDismiss has been specified, setup a timeout to + dismiss the dialog. + + If there are other notifications pending in the queue, set this + one to auto-dismiss + */ + if (notification.model.autoDismiss + || selectNextNotification.call(this)) { + var timeout = isNaN(notification.model.autoDismiss) ? + this.DEFAULT_AUTO_DISMISS : notification.model.autoDismiss; + + this.active.timeout = this.$timeout(function () { + that.dismissOrMinimize(notification); + }, timeout); } } + + function selectNextNotification () { + /* + Loop through the notifications queue and find the first one that + has not already been minimized (manually or otherwise). + */ + for (var i=0; i< this.notifications.length; i++) { + var notification = this.notifications[i]; + + if (!notification.model.minimized + && notification!= this.activeNotification) { + + return notification; + } + } + }; + + /** + * Minimize a notification. The notification will still be available + * from the notification list. Typically notifications with a + * severity of SUCCESS should not be minimized, but rather + * dismissed. + * @see dismiss + * @see dismissOrMinimize + * @param notification + */ + NotificationService.prototype.minimize = function (notification) { + //Check this is a known notification + var index = this.notifications.indexOf(notification); + if (index >= 0) { + notification.minimize(true); + delete this.active.notification; + delete this.active.timeout; + setActiveNotification.call(this, selectNextNotification.call(this)); + } + } + + /** + * Completely remove a notification. This will dismiss it from the + * message banner and remove it from the list of notifications. + * Typically only notifications with a severity of SUCCESS should be + * dismissed. If you're not sure whether to dismiss or minimize a + * notification, use the dismissOrMinimize method. + * dismiss + * @see dismissOrMinimize + * @param notification The notification to dismiss + */ + NotificationService.prototype.dismiss = function (notification) { + //Check this is a known notification + var index = this.notifications.indexOf(notification); + if (index >= 0) { + this.notifications.splice(index, 1); + + delete this.active.notification; + delete this.active.timeout; + + setActiveNotification.call(this, selectNextNotification.call(this)); + } + } + + /** + * Depending on the severity of the notification will selectively + * dismiss or minimize where appropriate. + * @see dismiss + * @see minimize + * @param notification + */ + NotificationService.prototype.dismissOrMinimize = function (notification){ + if (notification.model.severity > MessageSeverity.SUCCESS){ + this.minimize(notification); + } else { + this.dismiss(notification); + } + }; return NotificationService; }); \ No newline at end of file diff --git a/platform/commonUI/notification/test/NotificationServiceSpec.js b/platform/commonUI/notification/test/NotificationServiceSpec.js new file mode 100644 index 0000000000..c7a5fee234 --- /dev/null +++ b/platform/commonUI/notification/test/NotificationServiceSpec.js @@ -0,0 +1,146 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */ + +define( + ['../src/NotificationService','../src/MessageSeverity'], + function (NotificationService, MessageSeverity) { + "use strict"; + + describe("The notification service ", function () { + var notificationService, + mockTimeout, + mockAutoDismiss, + successModel = { + title: "Mock Success Notification", + severity: MessageSeverity.SUCCESS + }, + errorModel = { + title: "Mock Error Notification", + severity: MessageSeverity.ERROR + }; + + /** + * 1) Calling .notify results in a new notification being created + * with the provided model and set to the active notification + * + * 2) Calling .notify with autoDismiss results in a SUCCESS notification + * becoming dismissed after timeout has elapsed + * + * 3) Calling .notify with autoDismiss results in an ERROR notification + * being MINIMIZED after a timeout has elapsed + * + * 4) Calling .notify with an active success notification results in that + * notification being auto-dismissed, and the new notification becoming + * active. DONE + * + * 5) Calling .notify with an active error notification results in that + * notification being auto-minimized and the new notification becoming + * active. DONE + * + * 6) Calling .notify with an active error notification, AND a + * queued error notification results in the active notification + * being auto-dismissed, the next message in the queue becoming + * active, then auto-dismissed, and then the provided notification + * becoming active. + */ + + /** + var model = { + title: string, + progress: number, + severity: MessageSeverity, + unknownProgress: boolean, + minimized: boolean, + autoDismiss: boolean | number, + actions: { + label: string, + action: function + } + } + */ + + beforeEach(function(){ + mockTimeout = jasmine.createSpy("$timeout"); + mockAutoDismiss = 0; + notificationService = new NotificationService( + mockTimeout, mockAutoDismiss); + }); + + it("Calls the notification service with a new notification, making" + + " the notification active", function() { + var activeNotification; + notificationService.notify(successModel); + activeNotification = notificationService.getActiveNotification(); + expect(activeNotification.model).toBe(successModel); + }); + + describe(" called with multiple notifications", function(){ + it("auto-dismisses the previously active notification, making" + + " the new notification active", function() { + var activeNotification; + //First pre-load with a success message + notificationService.notify(successModel); + activeNotification = + notificationService.getActiveNotification(); + //Initially expect the active notification to be success + expect(activeNotification.model).toBe(successModel); + //Then notify of an error + notificationService.notify(errorModel); + //But it should be auto-dismissed and replaced with the + // error notification + mockTimeout.mostRecentCall.args[0](); + activeNotification = notificationService.getActiveNotification(); + expect(activeNotification.model).toBe(errorModel); + }); + it("auto-dismisses an active success notification, removing" + + " it completely", function() { + //First pre-load with a success message + notificationService.notify(successModel); + //Then notify of an error + notificationService.notify(errorModel); + expect(notificationService.notifications.length).toEqual(2); + mockTimeout.mostRecentCall.args[0](); + //Previous success message should be completely dismissed + expect(notificationService.notifications.length).toEqual(1); + }); + it("auto-minimizes an active error notification", function() { + var activeNotification; + //First pre-load with a success message + notificationService.notify(errorModel); + //Then notify of an error + notificationService.notify(successModel); + expect(notificationService.notifications.length).toEqual(2); + //Mock the auto-minimize + mockTimeout.mostRecentCall.args[0](); + //Previous error message should be minimized, not + // dismissed + expect(notificationService.notifications.length).toEqual(2); + activeNotification = + notificationService.getActiveNotification(); + expect(activeNotification.model).toBe(successModel); + expect(errorModel.minimized).toEqual(true); + + }); + }); + }); + }); \ No newline at end of file diff --git a/platform/commonUI/notification/test/suite.json b/platform/commonUI/notification/test/suite.json new file mode 100644 index 0000000000..08238b16c0 --- /dev/null +++ b/platform/commonUI/notification/test/suite.json @@ -0,0 +1,3 @@ +[ + "NotificationService" +] \ No newline at end of file From 182eff977c21dfadc44130cb30106e84d6a234c4 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 7 Oct 2015 16:36:55 -0700 Subject: [PATCH 025/488] [Frontend] Dialog and overlay layout/positioning mods open #159 open #170 Restructured overlay size and positioning approach for better flexibility; Getting new dialogs and the overlay to play nicely in mobile context; --- .../res/templates/blocking-message.html | 2 +- .../commonUI/dialog/res/templates/dialog.html | 2 +- .../dialog/res/templates/overlay-options.html | 2 +- .../dialog/res/templates/overlay.html | 8 +- .../general/res/sass/controls/_messages.scss | 2 +- .../general/res/sass/mobile/_constants.scss | 2 +- .../res/sass/mobile/overlay/_overlay.scss | 80 +++++---- .../general/res/sass/overlay/_overlay.scss | 134 ++++++++------- .../res/sass/user-environ/_layout.scss | 6 +- .../espresso/res/css/theme-espresso.css | 161 +++++++++++------- .../themes/snow/res/css/theme-snow.css | 161 +++++++++++------- 11 files changed, 330 insertions(+), 230 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/blocking-message.html b/platform/commonUI/dialog/res/templates/blocking-message.html index 8db939cfdc..fc2b5c1df3 100644 --- a/platform/commonUI/dialog/res/templates/blocking-message.html +++ b/platform/commonUI/dialog/res/templates/blocking-message.html @@ -3,7 +3,7 @@
{{ngModel.dialog.title}}
{{ngModel.dialog.hint}}
-
+
{{ngModel.dialog.actionText}}
diff --git a/platform/commonUI/dialog/res/templates/dialog.html b/platform/commonUI/dialog/res/templates/dialog.html index 4c7c02e3f2..2fe2411875 100644 --- a/platform/commonUI/dialog/res/templates/dialog.html +++ b/platform/commonUI/dialog/res/templates/dialog.html @@ -23,7 +23,7 @@
{{ngModel.title}}
All fields marked * are required.
-
+
diff --git a/platform/commonUI/dialog/res/templates/overlay-options.html b/platform/commonUI/dialog/res/templates/overlay-options.html index 155ad0beab..7de75df31c 100644 --- a/platform/commonUI/dialog/res/templates/overlay-options.html +++ b/platform/commonUI/dialog/res/templates/overlay-options.html @@ -24,7 +24,7 @@
{{ngModel.dialog.title}}
{{ngModel.dialog.hint}}
-
+
diff --git a/platform/commonUI/dialog/res/templates/overlay.html b/platform/commonUI/dialog/res/templates/overlay.html index df8bd9c160..f2b7c0d9ee 100644 --- a/platform/commonUI/dialog/res/templates/overlay.html +++ b/platform/commonUI/dialog/res/templates/overlay.html @@ -25,11 +25,7 @@ - x - -
- -
+ class="clk-icon icon ui-symbol close">x +
\ No newline at end of file diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index 697e841a5f..91dce92a09 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -131,7 +131,7 @@ width: 100px; } .progress-info.progress-estimate { display: none; } - z-index: 2; + z-index: 10; } .s-message-banner, diff --git a/platform/commonUI/general/res/sass/mobile/_constants.scss b/platform/commonUI/general/res/sass/mobile/_constants.scss index bd9443cc50..067bbef6ca 100644 --- a/platform/commonUI/general/res/sass/mobile/_constants.scss +++ b/platform/commonUI/general/res/sass/mobile/_constants.scss @@ -23,7 +23,7 @@ /************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */ $mobileListIconSize: 30px; $mobileTitleDescH: 35px; -$mobileOverlayMargin: 10px; +$mobileOverlayMargin: 20px; $phoneItemH: floor($ueBrowseGridItemLg/4); $tabletItemH: floor($ueBrowseGridItemLg/3); diff --git a/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss b/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss index caa6df0967..af0380a12b 100644 --- a/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss @@ -1,16 +1,12 @@ @include phoneandtablet { .overlay { - $m: 0; .clk-icon.close { top: $mobileOverlayMargin; right: $mobileOverlayMargin; } > .holder { - @include border-radius($m); - top: $m; - right: $m; - bottom: $m; - left: $m; + height: 90%; width: 90%; + > .contents { top: $mobileOverlayMargin; right: $mobileOverlayMargin; @@ -22,35 +18,61 @@ margin-right: 1.2em; } } - - .form.editor { - border: none; - - .contents { - top: 0; - right: 0; - bottom: 0; - left: 0; - } - } } } } } @include phone { - .overlay > .holder > .contents .form.editor .contents .form-row { - > .label, - > .controls { - //@include test(blue); - display: block; - float: none; - width: 100%; + .overlay > .holder { + //@include test(orange); // This works! + $m: 0; + @include border-radius($m); + top: $m; + right: $m; + bottom: $m; + left: $m; + height: auto; width: auto; + min-width: 200px; min-height: 200px; + max-height: 100%; max-width: 100%; + overflow: auto; + @include transform(none); + + .editor .form .form-row { + > .label, + > .controls { + //@include test(blue); + display: block; + float: none; + width: 100%; + } + > .label { + &:after { + float: none; + } + } + } + + .contents .top-bar, + .contents .editor, + .contents .bottom-bar { + //@include test(orange); + top: auto; right: auto; bottom: auto; left: auto; + height: auto; width: auto; + margin-bottom: $interiorMarginLg * 2; + position: relative; + } + } + .t-dialog-sm .overlay > .holder { + //@include test(blue); + height: auto; max-height: 100%; + } +} + +@include phonePortrait { + .overlay > .holder { + .contents .bottom-bar { + text-align: center; } - > .label { - &:after { - float: none; - } - } } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/overlay/_overlay.scss b/platform/commonUI/general/res/sass/overlay/_overlay.scss index 9724d6f463..327c41856d 100644 --- a/platform/commonUI/general/res/sass/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/overlay/_overlay.scss @@ -20,93 +20,113 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ .overlay { - .blocker { - background: $colorOvrBlocker; - z-index: 100; - } + .blocker { + background: $colorOvrBlocker; + z-index: 100; + } .clk-icon.close { font-size: 0.8rem; position: absolute; - top: $interiorMarginLg; right: $interiorMarginLg; bottom: auto; left: auto; - z-index: 100; + top: $interiorMarginLg; + right: $interiorMarginLg; + bottom: auto; + left: auto; + z-index: 100; } - >.holder { - $i: 15%; - @include containerSubtle($colorOvrBg, $colorOvrFg); - @include border-radius($basicCr * 3); - color: $colorOvrFg; - top: $i; right: $i; bottom: $i; left: $i; - z-index: 101; - >.contents { + > .holder { + //$i: 15%; + @include containerSubtle($colorOvrBg, $colorOvrFg); + @include border-radius($basicCr * 3); + color: $colorOvrFg; + top: 50%; + right: auto; + bottom: auto; + left: 50%; + @include transform(translateX(-50%) translateY(-50%)); + height: 70%; + width: 50%; + min-height: 300px; + max-height: 800px; + min-width: 450px; + max-width: 1000px; + z-index: 101; + > .contents { $m: $overlayMargin; - top: $m; right: $m; bottom: $m; left: $m; + top: $m; + right: $m; + bottom: $m; + left: $m; } - } + } .title { - @include ellipsize(); + @include ellipsize(); font-size: 1.2em; line-height: 120%; - margin-bottom: $interiorMargin; + margin-bottom: $interiorMargin; } .hint { color: pushBack($colorOvrFg, 20%); } - + .top-bar { height: $ovrTopBarH; } - + .editor { top: $ovrTopBarH + ($interiorMargin * 2); bottom: $ovrFooterH + $interiorMargin * 2; - left: 0; right: 0; + left: 0; + right: 0; + overflow: auto; + .field.l-med { + input[type='text'] { + width: 100%; + } + } } - .l-progress-bar { - $h: $progressBarHOverlay; + .l-progress-bar { + $h: $progressBarHOverlay; display: block; - height: $h; - line-height: $h; + height: $h; + line-height: $h; margin: .5em 0; - width: 100%; - } - + width: 100%; + } + .bottom-bar { - top: auto; right: 0; bottom: 0; left: 0; - overflow: visible; + top: auto; + right: 0; + bottom: 0; + left: 0; + overflow: visible; //font-size: 1em; height: $ovrFooterH; text-align: right; .s-btn { - $bg: $colorOvrBtnBg; - &:not(.major) { - @include btnSubtle($bg, pullForward($bg, 10%), $colorOvrBtnFg, $colorOvrBtnFg); - } - font-size: 95%; - height: $ovrFooterH; - line-height: $ovrFooterH; + $bg: $colorOvrBtnBg; + &:not(.major) { + @include btnSubtle($bg, pullForward($bg, 10%), $colorOvrBtnFg, $colorOvrBtnFg); + } + font-size: 95%; + height: $ovrFooterH; + line-height: $ovrFooterH; margin-left: $interiorMargin; - padding: 0 $interiorMargin * 3; - //&.major { - // @extend .s-btn.major; - // &:hover { - // @extend .s-btn.major:hover; - // } - //} + padding: 0 $interiorMargin * 3; + //&.major { + // @extend .s-btn.major; + // &:hover { + // @extend .s-btn.major:hover; + // } + //} } } - .contents.l-dialog { - $myM: $interiorMargin; - top: $myM; - right: $myM; - bottom: $myM; - left: $myM; - overflow: auto; - .field.l-med { - input[type='text'] { - width: 100%; - } - } - } +} + +.t-dialog-sm .overlay > .holder { + // Used for blocker and in-progress dialogs, modal alerts, etc. + //@include test(red); + min-height: 225px; + height: 225px; } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 6a11e22eb0..84083dc08d 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -43,9 +43,9 @@ position: absolute; } -.editor { - @include border-radius($basicCr * 1.5); -} +//.editor { +// @include border-radius($basicCr * 1.5); +//} .contents { $myM: 0; //$interiorMargin; diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 5caef16706..70c42472c7 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -2483,7 +2483,7 @@ label.checkbox.custom { -ms-transform: translateX(-50%); -webkit-transform: translateX(-50%); transform: translateX(-50%); - z-index: 2; } + z-index: 10; } /* line 110, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .banner-elem { -webkit-flex: 0 1 auto; @@ -3330,12 +3330,6 @@ span.req { .editor { position: absolute; } -/* line 46, ../../../../general/res/sass/user-environ/_layout.scss */ -.editor { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; } - /* line 50, ../../../../general/res/sass/user-environ/_layout.scss */ .contents { box-sizing: border-box; @@ -4204,7 +4198,7 @@ span.req { bottom: auto; left: auto; z-index: 100; } -/* line 33, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 36, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay > .holder { background-color: #4d4d4d; -moz-border-radius: 3px; @@ -4228,18 +4222,28 @@ span.req { -webkit-border-radius: 6px; border-radius: 6px; color: #e6e6e6; - top: 15%; - right: 15%; - bottom: 15%; - left: 15%; + top: 50%; + right: auto; + bottom: auto; + left: 50%; + -moz-transform: translateX(-50%) translateY(-50%); + -ms-transform: translateX(-50%) translateY(-50%); + -webkit-transform: translateX(-50%) translateY(-50%); + transform: translateX(-50%) translateY(-50%); + height: 70%; + width: 50%; + min-height: 300px; + max-height: 800px; + min-width: 450px; + max-width: 1000px; z-index: 101; } - /* line 40, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 53, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay > .holder > .contents { top: 25px; right: 25px; bottom: 25px; left: 25px; } -/* line 45, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 61, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .title { overflow: hidden; text-overflow: ellipsis; @@ -4247,26 +4251,30 @@ span.req { font-size: 1.2em; line-height: 120%; margin-bottom: 5px; } -/* line 52, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 68, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .hint { color: #b3b3b3; } -/* line 56, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 72, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .top-bar { height: 60px; } -/* line 60, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 76, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor { top: 70px; bottom: 40px; left: 0; - right: 0; } -/* line 66, ../../../../general/res/sass/overlay/_overlay.scss */ + right: 0; + overflow: auto; } + /* line 83, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .editor .field.l-med input[type='text'] { + width: 100%; } +/* line 89, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .l-progress-bar { display: block; height: 15px; line-height: 15px; margin: .5em 0; width: 100%; } -/* line 75, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 98, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar { top: auto; right: 0; @@ -4275,14 +4283,14 @@ span.req { overflow: visible; height: 30px; text-align: right; } - /* line 81, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 107, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { font-size: 95%; height: 30px; line-height: 30px; margin-left: 5px; padding: 0 15px; } - /* line 83, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 109, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { background-color: gray; -moz-border-radius: 3px; @@ -4321,23 +4329,32 @@ span.req { /* line 296, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } -/* line 99, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .contents.l-dialog { - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - overflow: auto; } - /* line 107, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .contents.l-dialog .field.l-med input[type='text'] { - width: 100%; } + +/* line 127, ../../../../general/res/sass/overlay/_overlay.scss */ +.t-dialog-sm .overlay > .holder { + min-height: 225px; + height: 225px; } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 4, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay .clk-icon.close { - top: 10px; - right: 10px; } - /* line 8, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + top: 20px; + right: 20px; } + /* line 7, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder { + height: 90%; + width: 90%; } + /* line 10, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents { + top: 20px; + right: 20px; + bottom: 20px; + left: 20px; } + /* line 17, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents .top-bar > .title { + margin-right: 1.2em; } } +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 27, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder { -moz-border-radius: 0; -webkit-border-radius: 0; @@ -4345,35 +4362,49 @@ span.req { top: 0; right: 0; bottom: 0; - left: 0; } - /* line 14, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents { - top: 10px; - right: 10px; - bottom: 10px; - left: 10px; } - /* line 21, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .top-bar > .title { - margin-right: 1.2em; } - /* line 26, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor { - border: none; } - /* line 29, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor .contents { - top: 0; - right: 0; - bottom: 0; - left: 0; } } -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 43, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor .contents .form-row > .label, - .overlay > .holder > .contents .form.editor .contents .form-row > .controls { - display: block; - float: none; - width: 100%; } - /* line 51, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor .contents .form-row > .label:after { - float: none; } } + left: 0; + height: auto; + width: auto; + min-width: 200px; + min-height: 200px; + max-height: 100%; + max-width: 100%; + overflow: auto; + -moz-transform: none; + -ms-transform: none; + -webkit-transform: none; + transform: none; } + /* line 42, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder .editor .form .form-row > .label, + .overlay > .holder .editor .form .form-row > .controls { + display: block; + float: none; + width: 100%; } + /* line 50, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder .editor .form .form-row > .label:after { + float: none; } + /* line 56, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder .contents .top-bar, + .overlay > .holder .contents .editor, + .overlay > .holder .contents .bottom-bar { + background-color: rgba(255, 165, 0, 0.2); + top: auto; + right: auto; + bottom: auto; + left: auto; + height: auto; + width: auto; + margin-bottom: 20px; + position: relative; } + + /* line 66, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .t-dialog-sm .overlay > .holder { + height: auto; + max-height: 100%; } } +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { + /* line 74, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder .contents .bottom-bar { + text-align: center; } } /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 5c94d22297..0789136f68 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -2431,7 +2431,7 @@ label.checkbox.custom { -ms-transform: translateX(-50%); -webkit-transform: translateX(-50%); transform: translateX(-50%); - z-index: 2; } + z-index: 10; } /* line 110, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .banner-elem { -webkit-flex: 0 1 auto; @@ -3262,12 +3262,6 @@ span.req { .editor { position: absolute; } -/* line 46, ../../../../general/res/sass/user-environ/_layout.scss */ -.editor { - -moz-border-radius: 6px; - -webkit-border-radius: 6px; - border-radius: 6px; } - /* line 50, ../../../../general/res/sass/user-environ/_layout.scss */ .contents { box-sizing: border-box; @@ -4136,7 +4130,7 @@ span.req { bottom: auto; left: auto; z-index: 100; } -/* line 33, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 36, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay > .holder { background-color: #fcfcfc; -moz-border-radius: 4px; @@ -4151,18 +4145,28 @@ span.req { -webkit-border-radius: 12px; border-radius: 12px; color: #666; - top: 15%; - right: 15%; - bottom: 15%; - left: 15%; + top: 50%; + right: auto; + bottom: auto; + left: 50%; + -moz-transform: translateX(-50%) translateY(-50%); + -ms-transform: translateX(-50%) translateY(-50%); + -webkit-transform: translateX(-50%) translateY(-50%); + transform: translateX(-50%) translateY(-50%); + height: 70%; + width: 50%; + min-height: 300px; + max-height: 800px; + min-width: 450px; + max-width: 1000px; z-index: 101; } - /* line 40, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 53, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay > .holder > .contents { top: 25px; right: 25px; bottom: 25px; left: 25px; } -/* line 45, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 61, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .title { overflow: hidden; text-overflow: ellipsis; @@ -4170,26 +4174,30 @@ span.req { font-size: 1.2em; line-height: 120%; margin-bottom: 5px; } -/* line 52, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 68, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .hint { color: #999999; } -/* line 56, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 72, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .top-bar { height: 60px; } -/* line 60, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 76, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor { top: 70px; bottom: 40px; left: 0; - right: 0; } -/* line 66, ../../../../general/res/sass/overlay/_overlay.scss */ + right: 0; + overflow: auto; } + /* line 83, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .editor .field.l-med input[type='text'] { + width: 100%; } +/* line 89, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .l-progress-bar { display: block; height: 15px; line-height: 15px; margin: .5em 0; width: 100%; } -/* line 75, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 98, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar { top: auto; right: 0; @@ -4198,14 +4206,14 @@ span.req { overflow: visible; height: 30px; text-align: right; } - /* line 81, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 107, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { font-size: 95%; height: 30px; line-height: 30px; margin-left: 5px; padding: 0 15px; } - /* line 83, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 109, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { background-color: #969696; -moz-border-radius: 4px; @@ -4235,23 +4243,32 @@ span.req { /* line 296, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } -/* line 99, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .contents.l-dialog { - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - overflow: auto; } - /* line 107, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .contents.l-dialog .field.l-med input[type='text'] { - width: 100%; } + +/* line 127, ../../../../general/res/sass/overlay/_overlay.scss */ +.t-dialog-sm .overlay > .holder { + min-height: 225px; + height: 225px; } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 4, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay .clk-icon.close { - top: 10px; - right: 10px; } - /* line 8, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + top: 20px; + right: 20px; } + /* line 7, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder { + height: 90%; + width: 90%; } + /* line 10, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents { + top: 20px; + right: 20px; + bottom: 20px; + left: 20px; } + /* line 17, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents .top-bar > .title { + margin-right: 1.2em; } } +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 27, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder { -moz-border-radius: 0; -webkit-border-radius: 0; @@ -4259,35 +4276,49 @@ span.req { top: 0; right: 0; bottom: 0; - left: 0; } - /* line 14, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents { - top: 10px; - right: 10px; - bottom: 10px; - left: 10px; } - /* line 21, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .top-bar > .title { - margin-right: 1.2em; } - /* line 26, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor { - border: none; } - /* line 29, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor .contents { - top: 0; - right: 0; - bottom: 0; - left: 0; } } -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 43, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor .contents .form-row > .label, - .overlay > .holder > .contents .form.editor .contents .form-row > .controls { - display: block; - float: none; - width: 100%; } - /* line 51, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor .contents .form-row > .label:after { - float: none; } } + left: 0; + height: auto; + width: auto; + min-width: 200px; + min-height: 200px; + max-height: 100%; + max-width: 100%; + overflow: auto; + -moz-transform: none; + -ms-transform: none; + -webkit-transform: none; + transform: none; } + /* line 42, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder .editor .form .form-row > .label, + .overlay > .holder .editor .form .form-row > .controls { + display: block; + float: none; + width: 100%; } + /* line 50, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder .editor .form .form-row > .label:after { + float: none; } + /* line 56, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder .contents .top-bar, + .overlay > .holder .contents .editor, + .overlay > .holder .contents .bottom-bar { + background-color: rgba(255, 165, 0, 0.2); + top: auto; + right: auto; + bottom: auto; + left: auto; + height: auto; + width: auto; + margin-bottom: 20px; + position: relative; } + + /* line 66, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .t-dialog-sm .overlay > .holder { + height: auto; + max-height: 100%; } } +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { + /* line 74, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder .contents .bottom-bar { + text-align: center; } } /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space From ac59df9595ae212b5825b4d48b069f342864beea Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 7 Oct 2015 17:56:25 -0700 Subject: [PATCH 026/488] [Frontend] Added type-icon to message dialogs open #159 open #170 Percent complete added to progress-bar.html; Refined overlay height; --- .../res/templates/blocking-message.html | 12 +++--- .../general/res/sass/controls/_messages.scss | 18 ++++++++- .../general/res/sass/overlay/_overlay.scss | 7 ++-- .../general/res/templates/indicator.html | 4 +- .../general/res/templates/progress-bar.html | 3 +- .../espresso/res/css/theme-espresso.css | 39 ++++++++++++------- .../themes/snow/res/css/theme-snow.css | 39 ++++++++++++------- testing/dialogTest/res/dialog-launch.html | 7 ++-- 8 files changed, 83 insertions(+), 46 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/blocking-message.html b/platform/commonUI/dialog/res/templates/blocking-message.html index fc2b5c1df3..4f2caf3df5 100644 --- a/platform/commonUI/dialog/res/templates/blocking-message.html +++ b/platform/commonUI/dialog/res/templates/blocking-message.html @@ -1,24 +1,24 @@ - + +
!
+
{{ngModel.dialog.title}}
{{ngModel.dialog.hint}}
-
+
{{ngModel.dialog.actionText}}
- -
- +
\ No newline at end of file diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index 91dce92a09..f3e1c82702 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -130,7 +130,7 @@ line-height: $h; width: 100px; } - .progress-info.progress-estimate { display: none; } + .progress-info { display: none; } z-index: 10; } @@ -159,4 +159,20 @@ &.caution { @include statusBannerColors($colorStatusCaution); } +} + +// Messages in overlays, as singleton or in list +.t-message .overlay { + // Singleton message overlay context + $iconW: 80px; + .type-icon.message-type.abs { + //color: pushBack($colorOvrFg, 40%); + font-size: $iconW; + opacity: 0.5; + right: auto; + width: $iconW; + } + .message-contents.abs { + left: $iconW + $overlayMargin; + } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/overlay/_overlay.scss b/platform/commonUI/general/res/sass/overlay/_overlay.scss index 327c41856d..4790d59768 100644 --- a/platform/commonUI/general/res/sass/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/overlay/_overlay.scss @@ -47,7 +47,7 @@ width: 50%; min-height: 300px; max-height: 800px; - min-width: 450px; + min-width: 600px; max-width: 1000px; z-index: 101; > .contents { @@ -58,6 +58,7 @@ left: $m; } } + .title { @include ellipsize(); font-size: 1.2em; @@ -127,6 +128,6 @@ .t-dialog-sm .overlay > .holder { // Used for blocker and in-progress dialogs, modal alerts, etc. //@include test(red); - min-height: 225px; - height: 225px; + min-height: 275px; + height: 275px; } \ No newline at end of file diff --git a/platform/commonUI/general/res/templates/indicator.html b/platform/commonUI/general/res/templates/indicator.html index 20bf22668b..e9be598b18 100644 --- a/platform/commonUI/general/res/templates/indicator.html +++ b/platform/commonUI/general/res/templates/indicator.html @@ -31,8 +31,8 @@ {{ngModel.getText()}} - 5 - + + diff --git a/platform/commonUI/general/res/templates/progress-bar.html b/platform/commonUI/general/res/templates/progress-bar.html index 337494164c..8337553765 100644 --- a/platform/commonUI/general/res/templates/progress-bar.html +++ b/platform/commonUI/general/res/templates/progress-bar.html @@ -4,6 +4,7 @@ -
+
+ {{ngModel.dialog.progress}}% complete. {{ngModel.dialog.progressText}}
\ No newline at end of file diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 70c42472c7..3f7cc363b9 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -2507,7 +2507,7 @@ label.checkbox.custom { line-height: 8px; width: 100px; } /* line 133, ../../../../general/res/sass/controls/_messages.scss */ - .l-message-banner .progress-info.progress-estimate { + .l-message-banner .progress-info { display: none; } /* line 137, ../../../../general/res/sass/controls/_messages.scss */ @@ -2584,6 +2584,16 @@ label.checkbox.custom { .s-message-banner.caution .s-action:hover { background-color: #ca5900; } +/* line 168, ../../../../general/res/sass/controls/_messages.scss */ +.t-message .overlay .type-icon.message-type.abs, .t-message .overlay .s-menu span.type-icon.message-type.l-click-area, .s-menu .t-message .overlay span.type-icon.message-type.l-click-area { + font-size: 80px; + opacity: 0.5; + right: auto; + width: 80px; } +/* line 175, ../../../../general/res/sass/controls/_messages.scss */ +.t-message .overlay .message-contents.abs, .t-message .overlay .s-menu span.message-contents.l-click-area, .s-menu .t-message .overlay span.message-contents.l-click-area { + left: 105px; } + /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { position: relative; @@ -4234,7 +4244,7 @@ span.req { width: 50%; min-height: 300px; max-height: 800px; - min-width: 450px; + min-width: 600px; max-width: 1000px; z-index: 101; } /* line 53, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4243,7 +4253,7 @@ span.req { right: 25px; bottom: 25px; left: 25px; } -/* line 61, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 62, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .title { overflow: hidden; text-overflow: ellipsis; @@ -4251,30 +4261,30 @@ span.req { font-size: 1.2em; line-height: 120%; margin-bottom: 5px; } -/* line 68, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .hint { color: #b3b3b3; } -/* line 72, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 73, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .top-bar { height: 60px; } -/* line 76, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 77, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor { top: 70px; bottom: 40px; left: 0; right: 0; overflow: auto; } - /* line 83, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 84, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor .field.l-med input[type='text'] { width: 100%; } -/* line 89, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 90, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .l-progress-bar { display: block; height: 15px; line-height: 15px; margin: .5em 0; width: 100%; } -/* line 98, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 99, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar { top: auto; right: 0; @@ -4283,14 +4293,14 @@ span.req { overflow: visible; height: 30px; text-align: right; } - /* line 107, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 108, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { font-size: 95%; height: 30px; line-height: 30px; margin-left: 5px; padding: 0 15px; } - /* line 109, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 110, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { background-color: gray; -moz-border-radius: 3px; @@ -4330,10 +4340,10 @@ span.req { .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } -/* line 127, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 128, ../../../../general/res/sass/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { - min-height: 225px; - height: 225px; } + min-height: 300px; + height: 300px; } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ @@ -4387,7 +4397,6 @@ span.req { .overlay > .holder .contents .top-bar, .overlay > .holder .contents .editor, .overlay > .holder .contents .bottom-bar { - background-color: rgba(255, 165, 0, 0.2); top: auto; right: auto; bottom: auto; diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 0789136f68..3ad64bc37f 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -2455,7 +2455,7 @@ label.checkbox.custom { line-height: 8px; width: 100px; } /* line 133, ../../../../general/res/sass/controls/_messages.scss */ - .l-message-banner .progress-info.progress-estimate { + .l-message-banner .progress-info { display: none; } /* line 137, ../../../../general/res/sass/controls/_messages.scss */ @@ -2532,6 +2532,16 @@ label.checkbox.custom { .s-message-banner.caution .s-action:hover { background-color: #ca5900; } +/* line 168, ../../../../general/res/sass/controls/_messages.scss */ +.t-message .overlay .type-icon.message-type.abs, .t-message .overlay .s-menu span.type-icon.message-type.l-click-area, .s-menu .t-message .overlay span.type-icon.message-type.l-click-area { + font-size: 80px; + opacity: 0.5; + right: auto; + width: 80px; } +/* line 175, ../../../../general/res/sass/controls/_messages.scss */ +.t-message .overlay .message-contents.abs, .t-message .overlay .s-menu span.message-contents.l-click-area, .s-menu .t-message .overlay span.message-contents.l-click-area { + left: 105px; } + /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { position: relative; @@ -4157,7 +4167,7 @@ span.req { width: 50%; min-height: 300px; max-height: 800px; - min-width: 450px; + min-width: 600px; max-width: 1000px; z-index: 101; } /* line 53, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4166,7 +4176,7 @@ span.req { right: 25px; bottom: 25px; left: 25px; } -/* line 61, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 62, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .title { overflow: hidden; text-overflow: ellipsis; @@ -4174,30 +4184,30 @@ span.req { font-size: 1.2em; line-height: 120%; margin-bottom: 5px; } -/* line 68, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .hint { color: #999999; } -/* line 72, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 73, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .top-bar { height: 60px; } -/* line 76, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 77, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor { top: 70px; bottom: 40px; left: 0; right: 0; overflow: auto; } - /* line 83, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 84, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .editor .field.l-med input[type='text'] { width: 100%; } -/* line 89, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 90, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .l-progress-bar { display: block; height: 15px; line-height: 15px; margin: .5em 0; width: 100%; } -/* line 98, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 99, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar { top: auto; right: 0; @@ -4206,14 +4216,14 @@ span.req { overflow: visible; height: 30px; text-align: right; } - /* line 107, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 108, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { font-size: 95%; height: 30px; line-height: 30px; margin-left: 5px; padding: 0 15px; } - /* line 109, ../../../../general/res/sass/overlay/_overlay.scss */ + /* line 110, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { background-color: #969696; -moz-border-radius: 4px; @@ -4244,10 +4254,10 @@ span.req { .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { color: white; } } -/* line 127, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 128, ../../../../general/res/sass/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { - min-height: 225px; - height: 225px; } + min-height: 300px; + height: 300px; } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ @@ -4301,7 +4311,6 @@ span.req { .overlay > .holder .contents .top-bar, .overlay > .holder .contents .editor, .overlay > .holder .contents .bottom-bar { - background-color: rgba(255, 165, 0, 0.2); top: auto; right: auto; bottom: auto; diff --git a/testing/dialogTest/res/dialog-launch.html b/testing/dialogTest/res/dialog-launch.html index 59d3437c10..7c91fd8f1f 100644 --- a/testing/dialogTest/res/dialog-launch.html +++ b/testing/dialogTest/res/dialog-launch.html @@ -1,5 +1,6 @@ -    -    - + Dialogs: +
Known | + Unknown | + Error \ No newline at end of file From 8ee93d9603e3b841ee461b6abcbf614569ee1b6d Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 7 Oct 2015 18:26:11 -0700 Subject: [PATCH 027/488] [Frontend] Tweaks to dialog-launch; added ui-symbols open #159 open #170 --- .../icomoon.io-WTD-symbols-project.json | 300 +++++++++++------- .../general/res/fonts/symbols/wtdsymbols.eot | Bin 11216 -> 11492 bytes .../general/res/fonts/symbols/wtdsymbols.svg | 4 +- .../general/res/fonts/symbols/wtdsymbols.ttf | Bin 11040 -> 11316 bytes .../general/res/fonts/symbols/wtdsymbols.woff | Bin 11116 -> 11392 bytes .../general/res/sass/controls/_messages.scss | 2 +- .../espresso/res/css/theme-espresso.css | 6 +- .../themes/snow/res/css/theme-snow.css | 6 +- testing/dialogTest/res/dialog-launch.html | 13 +- 9 files changed, 211 insertions(+), 120 deletions(-) diff --git a/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json b/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json index 7982d57200..a742a1c8eb 100644 --- a/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json +++ b/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json @@ -1,14 +1,30 @@ { "metadata": { - "name": "WTD Symbols v24", - "lastOpened": 1441992412958, - "created": 1441992410384 + "name": "WTD Symbols v2.3", + "lastOpened": 1444267493342, + "created": 1444266013303 }, "iconSets": [ { "selection": [ { - "order": 86, + "order": 90, + "id": 87, + "prevSize": 32, + "code": 58896, + "name": "icon-bell", + "tempChar": "" + }, + { + "order": 91, + "id": 86, + "prevSize": 32, + "code": 58889, + "name": "icon-hourglass", + "tempChar": "" + }, + { + "order": 92, "id": 85, "prevSize": 32, "code": 58888, @@ -18,119 +34,119 @@ 58890 ], "name": "icon-info-v15", - "tempChar": "" + "tempChar": "" }, { - "order": 82, + "order": 93, "id": 84, "prevSize": 32, "code": 58887, "name": "icon-x-in-circle", - "tempChar": "" + "tempChar": "" }, { - "order": 77, + "order": 94, "id": 83, "prevSize": 32, "code": 58881, "name": "icon-datatable", - "tempChar": "" + "tempChar": "" }, { - "order": 78, + "order": 95, "id": 82, "prevSize": 32, "code": 58882, "name": "icon-tabular-scrolling", - "tempChar": "" + "tempChar": "" }, { - "order": 79, + "order": 96, "id": 81, "prevSize": 32, "code": 58884, "name": "icon-tabular", - "tempChar": "" + "tempChar": "" }, { - "order": 80, + "order": 97, "id": 80, "prevSize": 32, "code": 58885, "name": "icon-calendar", - "tempChar": "" + "tempChar": "" }, { - "order": 83, + "order": 98, "id": 78, "prevSize": 32, "code": 58886, "name": "icon-paint-bucket", - "tempChar": "" + "tempChar": "" }, { - "order": 1, + "order": 99, "id": 75, "prevSize": 32, "code": 123, "name": "icon-pointer-left", - "tempChar": "" + "tempChar": "" }, { - "order": 3, + "order": 100, "id": 74, "prevSize": 32, "code": 125, "name": "icon-pointer-right", - "tempChar": "" + "tempChar": "" }, { - "order": 4, + "order": 101, "id": 73, "prevSize": 32, "code": 80, "name": "icon-person", - "tempChar": "" + "tempChar": "" }, { - "order": 5, + "order": 102, "id": 72, "prevSize": 32, "code": 232, "name": "icon-chain-links", - "tempChar": "" + "tempChar": "" }, { - "order": 6, + "order": 103, "id": 71, "prevSize": 32, "code": 115, "name": "icon-database-in-brackets", - "tempChar": "" + "tempChar": "" }, { - "order": 7, + "order": 104, "id": 70, "prevSize": 32, "code": 114, "name": "icon-refresh", - "tempChar": "" + "tempChar": "" }, { - "order": 8, + "order": 105, "id": 69, "prevSize": 32, "code": 108, "name": "icon-lock", - "tempChar": "" + "tempChar": "" }, { - "order": 9, + "order": 106, "id": 68, "prevSize": 32, "code": 51, "name": "icon-box-with-dashed-lines", - "tempChar": "" + "tempChar": "" }, { "order": 10, @@ -138,7 +154,7 @@ "prevSize": 32, "code": 58880, "name": "icon-box-with-arrow-cursor", - "tempChar": "" + "tempChar": "" }, { "order": 11, @@ -146,7 +162,7 @@ "prevSize": 32, "code": 65, "name": "icon-activity-mode", - "tempChar": "" + "tempChar": "" }, { "order": 12, @@ -154,15 +170,15 @@ "prevSize": 32, "code": 97, "name": "icon-activity", - "tempChar": "" + "tempChar": "" }, { - "order": 13, + "order": 87, "id": 64, "prevSize": 32, "code": 33, "name": "icon-alert-rect", - "tempChar": "" + "tempChar": "" }, { "order": 14, @@ -170,7 +186,7 @@ "prevSize": 32, "code": 58883, "name": "icon-alert-triangle", - "tempChar": "" + "tempChar": "" }, { "order": 15, @@ -178,7 +194,7 @@ "prevSize": 32, "code": 238, "name": "icon-arrow-double-down", - "tempChar": "" + "tempChar": "" }, { "order": 16, @@ -186,7 +202,7 @@ "prevSize": 32, "code": 235, "name": "icon-arrow-double-up", - "tempChar": "" + "tempChar": "" }, { "order": 2, @@ -194,7 +210,7 @@ "prevSize": 32, "code": 118, "name": "icon-arrow-down", - "tempChar": "" + "tempChar": "" }, { "order": 19, @@ -202,7 +218,7 @@ "prevSize": 32, "code": 60, "name": "icon-arrow-left", - "tempChar": "" + "tempChar": "" }, { "order": 20, @@ -210,7 +226,7 @@ "prevSize": 32, "code": 62, "name": "icon-arrow-right", - "tempChar": "" + "tempChar": "" }, { "order": 21, @@ -218,7 +234,7 @@ "prevSize": 32, "code": 236, "name": "icon-arrow-tall-down", - "tempChar": "" + "tempChar": "" }, { "order": 22, @@ -226,7 +242,7 @@ "prevSize": 32, "code": 237, "name": "icon-arrow-tall-up", - "tempChar": "" + "tempChar": "" }, { "order": 23, @@ -234,7 +250,7 @@ "prevSize": 32, "code": 94, "name": "icon-arrow-up", - "tempChar": "" + "tempChar": "" }, { "order": 24, @@ -242,7 +258,7 @@ "prevSize": 32, "code": 73, "name": "icon-arrows-out", - "tempChar": "" + "tempChar": "" }, { "order": 25, @@ -250,7 +266,7 @@ "prevSize": 32, "code": 58893, "name": "icon-arrows-right-left", - "tempChar": "" + "tempChar": "" }, { "order": 33, @@ -258,7 +274,7 @@ "prevSize": 32, "code": 53, "name": "icon-arrows-up-down", - "tempChar": "" + "tempChar": "" }, { "order": 26, @@ -266,7 +282,7 @@ "prevSize": 32, "code": 42, "name": "icon-asterisk", - "tempChar": "" + "tempChar": "" }, { "order": 27, @@ -274,7 +290,7 @@ "prevSize": 32, "code": 72, "name": "icon-autoflow-tabular", - "tempChar": "" + "tempChar": "" }, { "order": 28, @@ -282,7 +298,7 @@ "prevSize": 32, "code": 224, "name": "icon-box", - "tempChar": "" + "tempChar": "" }, { "order": 29, @@ -290,7 +306,7 @@ "prevSize": 32, "code": 50, "name": "icon-check", - "tempChar": "" + "tempChar": "" }, { "order": 30, @@ -298,7 +314,7 @@ "prevSize": 32, "code": 67, "name": "icon-clock", - "tempChar": "" + "tempChar": "" }, { "order": 31, @@ -306,7 +322,7 @@ "prevSize": 32, "code": 46, "name": "icon-connectivity", - "tempChar": "" + "tempChar": "" }, { "order": 32, @@ -314,7 +330,7 @@ "prevSize": 32, "code": 100, "name": "icon-database-query", - "tempChar": "" + "tempChar": "" }, { "order": 17, @@ -322,7 +338,7 @@ "prevSize": 32, "code": 68, "name": "icon-database", - "tempChar": "" + "tempChar": "" }, { "order": 35, @@ -330,7 +346,7 @@ "prevSize": 32, "code": 81, "name": "icon-dictionary", - "tempChar": "" + "tempChar": "" }, { "order": 36, @@ -338,7 +354,7 @@ "prevSize": 32, "code": 242, "name": "icon-duplicate", - "tempChar": "" + "tempChar": "" }, { "order": 37, @@ -346,7 +362,7 @@ "prevSize": 32, "code": 102, "name": "icon-folder-new", - "tempChar": "" + "tempChar": "" }, { "order": 38, @@ -354,7 +370,7 @@ "prevSize": 32, "code": 70, "name": "icon-folder", - "tempChar": "" + "tempChar": "" }, { "order": 39, @@ -362,7 +378,7 @@ "prevSize": 32, "code": 95, "name": "icon-fullscreen-collapse", - "tempChar": "" + "tempChar": "" }, { "order": 40, @@ -370,7 +386,7 @@ "prevSize": 32, "code": 122, "name": "icon-fullscreen-expand", - "tempChar": "" + "tempChar": "" }, { "order": 41, @@ -378,7 +394,7 @@ "prevSize": 32, "code": 71, "name": "icon-gear", - "tempChar": "" + "tempChar": "" }, { "order": 49, @@ -386,7 +402,7 @@ "prevSize": 32, "code": 227, "name": "icon-image", - "tempChar": "" + "tempChar": "" }, { "order": 42, @@ -394,7 +410,7 @@ "prevSize": 32, "code": 225, "name": "icon-layers", - "tempChar": "" + "tempChar": "" }, { "order": 43, @@ -402,7 +418,7 @@ "prevSize": 32, "code": 76, "name": "icon-layout", - "tempChar": "" + "tempChar": "" }, { "order": 44, @@ -410,7 +426,7 @@ "prevSize": 32, "code": 226, "name": "icon-line-horz", - "tempChar": "" + "tempChar": "" }, { "order": 75, @@ -418,7 +434,7 @@ "prevSize": 32, "code": 244, "name": "icon-link", - "tempChar": "" + "tempChar": "" }, { "order": 46, @@ -426,7 +442,7 @@ "prevSize": 32, "code": 88, "name": "icon-magnify-in", - "tempChar": "" + "tempChar": "" }, { "order": 47, @@ -434,7 +450,7 @@ "prevSize": 32, "code": 89, "name": "icon-magnify-out", - "tempChar": "" + "tempChar": "" }, { "order": 48, @@ -442,7 +458,7 @@ "prevSize": 32, "code": 77, "name": "icon-magnify", - "tempChar": "" + "tempChar": "" }, { "order": 34, @@ -450,7 +466,7 @@ "prevSize": 32, "code": 109, "name": "icon-menu", - "tempChar": "" + "tempChar": "" }, { "order": 50, @@ -458,7 +474,7 @@ "prevSize": 32, "code": 243, "name": "icon-move", - "tempChar": "" + "tempChar": "" }, { "order": 51, @@ -466,7 +482,7 @@ "prevSize": 32, "code": 121, "name": "icon-new-window", - "tempChar": "" + "tempChar": "" }, { "order": 52, @@ -474,7 +490,7 @@ "prevSize": 32, "code": 111, "name": "icon-object", - "tempChar": "" + "tempChar": "" }, { "order": 73, @@ -482,7 +498,7 @@ "prevSize": 32, "code": 63, "name": "icon-object-unknown", - "tempChar": "" + "tempChar": "" }, { "order": 53, @@ -490,7 +506,7 @@ "prevSize": 32, "code": 86, "name": "icon-packet", - "tempChar": "" + "tempChar": "" }, { "order": 54, @@ -498,7 +514,7 @@ "prevSize": 32, "code": 234, "name": "icon-page", - "tempChar": "" + "tempChar": "" }, { "order": 55, @@ -506,7 +522,7 @@ "prevSize": 32, "code": 241, "name": "icon-pause", - "tempChar": "" + "tempChar": "" }, { "order": 56, @@ -514,7 +530,7 @@ "prevSize": 32, "code": 112, "name": "icon-pencil", - "tempChar": "" + "tempChar": "" }, { "order": 65, @@ -522,7 +538,7 @@ "prevSize": 32, "code": 79, "name": "icon-people", - "tempChar": "" + "tempChar": "" }, { "order": 57, @@ -530,7 +546,7 @@ "prevSize": 32, "code": 239, "name": "icon-play", - "tempChar": "" + "tempChar": "" }, { "order": 58, @@ -538,7 +554,7 @@ "prevSize": 32, "code": 233, "name": "icon-plot-resource", - "tempChar": "" + "tempChar": "" }, { "order": 59, @@ -546,7 +562,7 @@ "prevSize": 32, "code": 43, "name": "icon-plus", - "tempChar": "" + "tempChar": "" }, { "order": 60, @@ -554,7 +570,7 @@ "prevSize": 32, "code": 45, "name": "icon-minus", - "tempChar": "" + "tempChar": "" }, { "order": 61, @@ -562,7 +578,7 @@ "prevSize": 32, "code": 54, "name": "icon-sine", - "tempChar": "" + "tempChar": "" }, { "order": 62, @@ -570,7 +586,7 @@ "prevSize": 32, "code": 228, "name": "icon-T", - "tempChar": "" + "tempChar": "" }, { "order": 63, @@ -578,7 +594,7 @@ "prevSize": 32, "code": 116, "name": "icon-telemetry-panel", - "tempChar": "" + "tempChar": "" }, { "order": 64, @@ -586,7 +602,7 @@ "prevSize": 32, "code": 84, "name": "icon-telemetry", - "tempChar": "" + "tempChar": "" }, { "order": 18, @@ -594,7 +610,7 @@ "prevSize": 32, "code": 246, "name": "icon-thumbs-strip", - "tempChar": "" + "tempChar": "" }, { "order": 67, @@ -602,7 +618,7 @@ "prevSize": 32, "code": 83, "name": "icon-timeline", - "tempChar": "" + "tempChar": "" }, { "order": 68, @@ -610,7 +626,7 @@ "prevSize": 32, "code": 245, "name": "icon-timer", - "tempChar": "" + "tempChar": "" }, { "order": 69, @@ -618,7 +634,7 @@ "prevSize": 32, "code": 90, "name": "icon-trash", - "tempChar": "" + "tempChar": "" }, { "order": 70, @@ -626,7 +642,7 @@ "prevSize": 32, "code": 229, "name": "icon-two-parts-both", - "tempChar": "" + "tempChar": "" }, { "order": 71, @@ -634,7 +650,7 @@ "prevSize": 32, "code": 231, "name": "icon-two-parts-one-only", - "tempChar": "" + "tempChar": "" }, { "order": 72, @@ -642,7 +658,7 @@ "prevSize": 32, "code": 120, "name": "icon-x-heavy", - "tempChar": "" + "tempChar": "" }, { "order": 66, @@ -650,7 +666,7 @@ "prevSize": 32, "code": 58946, "name": "icon-x", - "tempChar": "" + "tempChar": "" } ], "id": 2, @@ -665,6 +681,58 @@ "height": 1024, "prevSize": 32, "icons": [ + { + "id": 87, + "paths": [ + "M512 1024c106 0 192-86 192-192h-384c0 106 86 192 192 192z", + "M896 448v-64c0-212-172-384-384-384s-384 172-384 384v64c0 70.6-57.4 128-128 128v128h1024v-128c-70.6 0-128-57.4-128-128z" + ], + "attrs": [ + { + "fill": "rgb(6, 161, 75)" + }, + { + "fill": "rgb(6, 161, 75)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-bell" + ], + "colorPermutations": { + "125525525516161751": [ + 1, + 1 + ] + } + }, + { + "id": 86, + "paths": [ + "M1024 0h-1024c0 282.8 229.2 512 512 512s512-229.2 512-512zM512 384c-102.6 0-199-40-271.6-112.4-41.2-41.2-72-90.2-90.8-143.6h724.6c-18.8 53.4-49.6 102.4-90.8 143.6-72.4 72.4-168.8 112.4-271.4 112.4z", + "M512 512c-282.8 0-512 229.2-512 512h1024c0-282.8-229.2-512-512-512z" + ], + "attrs": [ + { + "fill": "rgb(6, 161, 75)" + }, + { + "fill": "rgb(6, 161, 75)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-hourglass" + ], + "colorPermutations": { + "125525525516161751": [ + 1, + 1 + ] + } + }, { "id": 85, "paths": [ @@ -698,7 +766,8 @@ "icon-x-in-circle" ], "colorPermutations": { - "16161751": [] + "16161751": [], + "125525525516161751": [] } }, { @@ -899,6 +968,11 @@ 1, 1, 1 + ], + "125525525516161751": [ + 1, + 1, + 1 ] } }, @@ -1051,18 +1125,28 @@ { "id": 67, "paths": [ - "M832 512.4c0-0.2 0-0.2 0-0.4v-320c0-105.6-86.4-192-192-192h-448c-105.6 0-192 86.4-192 192v320c0 105.6 86.4 192 192 192h263.6l-197.2-445.6 573.6 254z", - "M766.8 659.8l193.8-20.4-576.6-255.4 255.4 576.6 20.4-193.8 257 257.2 107.2-107.2z" + "M894-2h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h400c-2.2-3.8-4-7.6-5.8-11.4l-255.2-576.8c-21.4-48.4-10.8-105 26.6-142.4 24.4-24.4 57.2-37.4 90.4-37.4 17.4 0 35.2 3.6 51.8 11l576.6 255.4c4 1.8 7.8 3.8 11.4 5.8v-400.2c0.2-70.4-57.4-128-127.8-128z", + "M958.6 637.4l-576.6-255.4 255.4 576.6 64.6-128.6 192 192 128-128-192-192z" ], "attrs": [ - {}, - {} + { + "fill": "rgb(0, 0, 0)" + }, + { + "fill": "rgb(0, 0, 0)" + } ], "isMulticolor": false, "grid": 0, "tags": [ "icon-box-with-arrow-cursor" - ] + ], + "colorPermutations": { + "125525525516161751": [ + 0, + 0 + ] + } }, { "id": 66, @@ -1338,6 +1422,9 @@ "colorPermutations": { "16161751": [ 0 + ], + "125525525516161751": [ + 0 ] } }, @@ -14853,6 +14940,5 @@ "gridSize": 16, "showLiga": false }, - "uid": -1, - "time": 1441993324496 + "uid": -1 } \ No newline at end of file diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot index 95887455fe74465bcec2c835663ff125999ddb60..a721dd33847aae41a6a485ab0485f1fe65040ab4 100755 GIT binary patch delta 682 zcmXw0&rcIk5T5sTyTxGJ-Tt6LO6#_;+nT1D(zi{3kgXE1Zb*B=9|yG&q#%_d1ko5- zVxk8)KsFi^;?au-V(gJbHk^znV~F7oAdQ%)Cq0Pm`WE%@GBe*dGvCa-4;S|@bNfkv zOmdx5*p0n|+`78Hbo|pV02HEcaH2F<1ppoZU#GTZV*1`q_lv_}8kYgOAC$*SW6q~% zo&d=Elvm0WaGGooAWrL{t6Z61ywJi5G*)T9OVcwqN^7piqX3ELl;5nB7OOA@cL7eF zr}4?#rONn;$nMvx6rcj=Rdr@=9vDhN(r=W}qt-Zehd>1faK=y6=Qvh()p;X>hY@ew z$LAbF9fKV{!^HWTleraMvYOTo(`0ra4BfP!807+jzpY11A(@es>;URXw*?(x6cjR2 z%-8M92D38rumMEV2wKA!Tf8UaJ>Bl=aAB3<7>(o_+g8rhO=d?Ub{@lFoa-utygmnK zVFsZ!qBWMS#`ZEzLQs1z9YCMYV&bQ;l(oNg2gMBO+b zW>PrD!fz<9Y)wsVtrVLuvYHL2+<4cWiYTiiHpU*YjA{FJUQ_Q?BKdecA5nVs|AqG9 zFo5w+N?f7#-waOZ1aPDkBGOYJVyjWvPUPoe0g*%{f}t?m)T>3L_fM`A3#*g;8e!?K zYPDFH#j0u}Hlq?v*EM2%mtKZ)2YVaWJpDhQm9Gv4sEt delta 423 zcmaD7c_Ezbf;Iz#g7!oyz`(#Dz`(#10i-$7 zb1Kswf4kQSsNsMKVnCZ@3aALnIYP`d+^H_HGDupeL%WnfSj0rFKca!V?l*ja>u zd<&p?0Xg}}i8arQqZk-8I)L)4auX{G7?K#u85lH8fP96##N1Q~`73YZfC3>v4XX_G43je$o4FN$Ocw?R237`^$=?{QnK_>EPBvq5pIp!6#O^932vo~B`683U z$7{Rbg{Rb7qG^ z|9=ktg@yi%9LJdK91s27|6i1mF`MxKBUtp`0slhAegCG2{{OrG(7*rLn|~?(W|>^5 zt}{7QVvaH+&{<3j3}{1t`G4a7p5z8;qX3L&KagWb+3NHAbMy$r4)X qlijtZ$;R - + @@ -85,6 +85,8 @@ + + \ No newline at end of file diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf index 8f0f1d96108c2bb7c738154e5049a7a1c1e23b29..5bc0ca5290beac172e9234d7a030846d4b13f7c1 100755 GIT binary patch delta 664 zcmXw0&rcIk5T5sT`@>+%Zhuf{O6#_Cw>6}i!fQ<-xK#qy4QWr6cn}&vN~N+0K}?KY zqLBj}svC`oG4bNX0|)EDghU#S9t|=60j7xuy?GJa^)2e*WoEu_X1q711 z=U)IQK;QU6X;lRP4gk+mYhPHpd&BwiXp+WdfatyQd}+@1^z0UZ^p*0dG6if+)&~%y z^)OJbu5DblvpkJu+HZPk`Fg4DcvJ+4Kd1a|wX~ta9NYmoeTl}WZk4L@y`jVJ*C;>* z&>MAmbqyFw!Jy~y`v+Yy>P>(Q7O?6QXvA`&6bHMIoq; zF*dmFfa^@3!|%W{V`UVQZSNb|rq*N*6k_HvR*bU)`GCu9u^N~`s7{pjrqSNtq)7-G ze&L9%(FKeq3rrKbpzrS_o**7dj$wkrqC}?=oXF-x5pURuy~1z;M_Bj`h3&mcWpBIC zfvKHzFyX|9&O|7+Gi74zG0QYf-^}Z1nN%nji{(P8jP}3Kyc-74d&T%wYX8k(gMI)@ zGDQS>3PembD%+1d)`(XiVUb|SuXHrEfV9!Y?LvNMaa18J-Bnf#`4ue7CSo!&Q8Y~< z`g!qnAp7mhi<$1xpU}mcs_RU2v(c53~cKuh_`MTj&oDz^F6; delta 406 zcmdlIu^?=MV*RQ2#~BzHWq{ZwJ+Ziefq_AQfq^LkNOPp;RHi-tcCQo2PhenBsmVx9 zOkwvw&db1{b_XbLmH`xCKfof&z@RPyouKs_3g@O41 zgAjux&_D}DLqQWYWkn@6Q9%(gV?}0Gc5_u>b4GJ!heH2<4*rFO{)`;QnCu)6{oVgx zl#wx;@c<)O^xpyhLdJdnrilLkyZ_L?|Jj?p6o0c!KB%TMd8zmuWk#U0m>3w&%y{@Q zp5Nvx12+p$fPvwX#6dS0J()wpoH1mxgN7O-Q03$dP4&ssHK)nO@m~@U5(pERCnzA8 YBsfd(mf!~=JD?tri6NW)w4N~n04y+TfdBvi diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff index d5bc74071ceb1fb9c17b2bc85141299efb8aa372..68386425cca8521ea1d4c29b671055f69ab4db51 100755 GIT binary patch delta 737 zcmXw1&rcIU6n-$8{%zt-^crvx_Upl`#JmhD!~+I5T$(b=}`lv&FD(k37oG z=2p=7%u^&z*D(gmw{x@O$e}TL%i!Ptb-ggRu!ud}+&C&Wg;1D+I9R}HJRyUY#sm9- zV0g%a9#lcqG?lDVN=>1;-FdFn-e#w9&dOX!P$G|0mdWQyC<)7!;N4Ms~bU)VwS9btd8G|D)>zROkEoat)%K-7OR z6uRh-4(R_6;)MYPFvjHWYZy;zut5iaB@$I7yb7W+H?-{_E!Lo0QUkI|Aw6Hy3mKvh zO%*fg($tWq^4M#*e$$@#@$JQp=Al2(#4}g;GPs0YA4lV| z5+m92Lq#UktQngj#dxPQ7<-}TXYVv02(mCOycavgC2>pq?d)-$cNUx<@hJPgS-1;! OmZ$!CUFFx^pU6Kk)V8t! delta 460 zcmZpOd=n;C?(gQtz{mgu+Bpo|U|NBJX>uWx*hC%qdK(4?Mw`p0-XBj-EG}SRU|IrH zzyZYq={c2YK(RFp3@SAs{P^3w&WzN=6b1%$4WJq`5N7v3&YJ-g1d1g9`6?jHet<g@{@t;G_C;EtODVh=SIa*xrr4(i!~d70t#T9#894>n41a| zTL9DuRLHnW{>t09g8bqVpdWlD{^{lhDRg0QU|?lnnLLTnnwjGn@8nC2?vtgNoY-BZ z1R0na7=a$NncTp{xp^&9rd$LA!~X*;49o`@gcu}&23jy03Yw@XD=M*x3W|ssD>AdP zo2v?&Gnz9y6#D;j@GmU%XXH4>WaoJ3@BaUyjEvcg2N=Pk{|@*UGVc2~MfCsQ{fGYj z&)z&u@i)t4UUi+xAI0Y=gF=~!f#J-IhacnlZN4&avj7Dc7%oX1bc4~8Lp018LpJwl ys4)UnPTr!aKKZfcG}$=*O9DayVFL361q71>X9?aC{2*ip)B`dxWb-VoXN&;NRChH1 diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index f3e1c82702..3bea644daa 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -70,7 +70,7 @@ } &:hover { .label { - max-width: 150px; + max-width: 450px; width: auto; } .count { diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 3f7cc363b9..389cdf1490 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -2437,7 +2437,7 @@ label.checkbox.custom { opacity: 1; } /* line 72, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .label { - max-width: 150px; + max-width: 450px; width: auto; } /* line 76, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .count { @@ -4342,8 +4342,8 @@ span.req { /* line 128, ../../../../general/res/sass/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { - min-height: 300px; - height: 300px; } + min-height: 275px; + height: 275px; } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 3ad64bc37f..54a9973a4e 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -2385,7 +2385,7 @@ label.checkbox.custom { opacity: 1; } /* line 72, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .label { - max-width: 150px; + max-width: 450px; width: auto; } /* line 76, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .count { @@ -4256,8 +4256,8 @@ span.req { /* line 128, ../../../../general/res/sass/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { - min-height: 300px; - height: 300px; } + min-height: 275px; + height: 275px; } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ diff --git a/testing/dialogTest/res/dialog-launch.html b/testing/dialogTest/res/dialog-launch.html index 7c91fd8f1f..e3ad984087 100644 --- a/testing/dialogTest/res/dialog-launch.html +++ b/testing/dialogTest/res/dialog-launch.html @@ -1,6 +1,9 @@ - - Dialogs: - Known | - Unknown | - Error + + + + Dialogs: + Known | + Unknown | + Error + \ No newline at end of file From dbcad51325504479ae9f29c3b4b9db6a7bd7c6f3 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 7 Oct 2015 21:32:34 -0700 Subject: [PATCH 028/488] [Frontend] Tiny mod to dialog-launch.html open #159 open #170 --- testing/dialogTest/res/dialog-launch.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/dialogTest/res/dialog-launch.html b/testing/dialogTest/res/dialog-launch.html index e3ad984087..1b117c2ebf 100644 --- a/testing/dialogTest/res/dialog-launch.html +++ b/testing/dialogTest/res/dialog-launch.html @@ -1,9 +1,9 @@ - Dialogs: Known | Unknown | Error + Dialogs \ No newline at end of file From 0ca9e5c95203b64281a6be977abe6d050feb4d95 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 8 Oct 2015 09:37:43 -0700 Subject: [PATCH 029/488] [Frontend] Message list initially working open #159 open #170 Thank you @akhenry; launchMessages function added to DialogLaunchController.js; New message-list.html template; message.html is now its own include; blocking-message.html renamed to overlay-blocking-message.html; --- platform/commonUI/dialog/bundle.json | 12 ++++- ...locking-message.html => message-list.html} | 14 +++--- .../dialog/res/templates/message.html | 22 +++++++++ .../templates/overlay-blocking-message.html | 25 ++++++++++ platform/commonUI/dialog/src/DialogService.js | 20 +++++++- testing/dialogTest/res/dialog-launch.html | 3 +- .../dialogTest/src/DialogLaunchController.js | 47 +++++++++++++++++++ 7 files changed, 132 insertions(+), 11 deletions(-) rename platform/commonUI/dialog/res/templates/{blocking-message.html => message-list.html} (64%) create mode 100644 platform/commonUI/dialog/res/templates/message.html create mode 100644 platform/commonUI/dialog/res/templates/overlay-blocking-message.html diff --git a/platform/commonUI/dialog/bundle.json b/platform/commonUI/dialog/bundle.json index eab8fb000a..87ef06f764 100644 --- a/platform/commonUI/dialog/bundle.json +++ b/platform/commonUI/dialog/bundle.json @@ -36,8 +36,16 @@ "templateUrl": "templates/dialog.html" }, { - "key": "blocking-message", - "templateUrl": "templates/blocking-message.html" + "key": "overlay-blocking-message", + "templateUrl": "templates/overlay-blocking-message.html" + }, + { + "key": "message", + "templateUrl": "templates/message.html" + }, + { + "key": "message-list", + "templateUrl": "templates/message-list.html" } ], "containers": [ diff --git a/platform/commonUI/dialog/res/templates/blocking-message.html b/platform/commonUI/dialog/res/templates/message-list.html similarity index 64% rename from platform/commonUI/dialog/res/templates/blocking-message.html rename to platform/commonUI/dialog/res/templates/message-list.html index 4f2caf3df5..67fbdb28d0 100644 --- a/platform/commonUI/dialog/res/templates/blocking-message.html +++ b/platform/commonUI/dialog/res/templates/message-list.html @@ -1,17 +1,17 @@ -
!
+
{{ngModel.dialog.title}}
{{ngModel.dialog.hint}}
-
- {{ngModel.dialog.actionText}} -
- +
    +
  • + Message: {{msg.title}} + +
  • +
!
+ \ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/overlay-blocking-message.html b/platform/commonUI/dialog/res/templates/overlay-blocking-message.html new file mode 100644 index 0000000000..a4d4dc7276 --- /dev/null +++ b/platform/commonUI/dialog/res/templates/overlay-blocking-message.html @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index 99d361cf81..836805304e 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -239,7 +239,7 @@ define( // Add the overlay using the OverlayService, which // will handle actual insertion into the DOM this.overlay = this.overlayService.createOverlay( - "blocking-message", + "overlay-blocking-message", {dialog: dialogModel}, "t-dialog-sm" ); @@ -253,6 +253,24 @@ define( }; + DialogService.prototype.showMessageList = function(dialogModel) { + if (this.canShowDialog(dialogModel)) { + // Add the overlay using the OverlayService, which + // will handle actual insertion into the DOM + this.overlay = this.overlayService.createOverlay( + "message-list", + {dialog: dialogModel}, + "t-dialog t-message-list" + ); + this.dialogVisible = true; + return true; + } else { + //Could not show a dialog, so return indication of this to + //client code. + return false; + } + + }; return DialogService; } diff --git a/testing/dialogTest/res/dialog-launch.html b/testing/dialogTest/res/dialog-launch.html index 1b117c2ebf..2b01ca60bb 100644 --- a/testing/dialogTest/res/dialog-launch.html +++ b/testing/dialogTest/res/dialog-launch.html @@ -3,7 +3,8 @@ Known | Unknown | - Error + Error | + Messages Dialogs \ No newline at end of file diff --git a/testing/dialogTest/src/DialogLaunchController.js b/testing/dialogTest/src/DialogLaunchController.js index 374e43a150..7f51a91329 100644 --- a/testing/dialogTest/src/DialogLaunchController.js +++ b/testing/dialogTest/src/DialogLaunchController.js @@ -99,6 +99,53 @@ define( $log.error("Could not display modal dialog"); } }; + + $scope.launchMessages = function () { + var model = { + title: "Messages", + severity: messageSeverity.MESSAGES, + actions: [ + { + label: "Done", + action: function () { + $log.debug("Done pressed"); + dialogService.dismiss(); + } + } + ], + messages: [] + }; + + function createMessage (messageNumber) { + var messageModel = { + ngModel: { + dialog: { + title: "Message " + messageNumber, + severity: messageSeverity.INFO, + actions: [ + { + label: "Cancel Duplication", + action: function () { + $log.debug("Cancel Duplication pressed"); + $log.debug("Message should be dismissed"); + } + } + ] + } + } + }; + return messageModel; + } + + if (dialogService.showMessageList(model)) { + //Do processing here + for (var i = 0; i < 4; i++) { + model.messages.push(createMessage(i)); + } + } else { + $log.error("Could not display modal dialog"); + } + }; } return DialogLaunchController; } From 5ff90f7254ffc7a2fe75816cb5d8a64fe01e7178 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 11:03:48 -0700 Subject: [PATCH 030/488] Added more tests, some refactoring --- .../notification/src/NotificationService.js | 203 +++++++++++------- .../test/NotificationServiceSpec.js | 98 +++++++-- 2 files changed, 200 insertions(+), 101 deletions(-) diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 30e746cc42..46950440da 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -23,13 +23,61 @@ /** * This bundle implements the notification service, which can be used to - * show banner notifications to the user. + * show banner notifications to the user. Banner notifications + * are used to inform users of events in a non-intrusive way. As + * much as possible, notifications share a model with blocking + * dialogs so that the same information can be provided in a dialog + * and then minimized to a banner notification if needed. + * * @namespace platform/commonUI/dialog */ define( ["./MessageSeverity"], function (MessageSeverity) { "use strict"; + + /** + * A representation of a user action. Actions are provided to + * dialogs and notifications and are shown as buttons. + * + * @typedef {object} NotificationAction + * @property {string} label the label to appear on the button for + * this action + * @property {function} action a callback function to be invoked + * when the button is clicked + */ + + /** + * A representation of a banner notification. Banner notifications + * are used to inform users of events in a non-intrusive way. As + * much as possible, notifications share a model with blocking + * dialogs so that the same information can be provided in a dialog + * and then minimized to a banner notification if needed. + * + * @typedef {object} Notification + * @property {string} title The title of the message + * @property {number} progress The completion status of a task + * represented numerically + * @property {MessageSeverity} messageSeverity The importance of the + * message (eg. error, success) + * @property {boolean} unknownProgress a boolean indicating that the + * progress of the underlying task is unknown. This will result in a + * visually distinct progress bar. + * @property {boolean | number} autoDismiss If truthy, dialog will + * be automatically minimized or dismissed (depending on severity). + * Additionally, if the provided value is a number, it will be used + * as the delay period before being dismissed. + * @property {NotificationAction} primaryAction the default user + * response to + * this message. Will be represented as a button with the provided + * label and action. May be used by banner notifications to display + * only the most important option to users. + * @property {NotificationAction[]} additionalActions any additional + * actions + * that the user can take. Will be represented as additional buttons + * that may or may not be available from a banner. + */ + /** * The notification service is responsible for informing the user of * events via the use of banner notifications. @@ -41,69 +89,51 @@ define( this.$timeout = $timeout; this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS; - /** - * Exposes the current "active" notification. This is a - * notification that is of current highest importance that has - * not been dismissed. The deinition of what is of highest - * importance might be a little nuanced and require tweaking. - * For example, if an important error message is visible and a - * success message is triggered, it may be desirable to - * temporarily show the success message and then auto-dismiss it. - * @type {{notification: undefined}} + /* + * A context in which to hold the active notification and a + * handle to its timeout. */ this.active = { }; } - /** - var model = { - title: string, - progress: number, - severity: MessageSeverity, - unknownProgress: boolean, - minimized: boolean, - autoDismiss: boolean | number, - actions: { - label: string, - action: function - } - } - */ /** - * Possibly refactor this out to a provider? - * @constructor + * Returns the notification that is currently visible in the banner area + * @returns {Notification} */ - function Notification (model) { - this.model = model; - } - - Notification.prototype.minimize = function (setValue) { - if (typeof setValue !== undefined){ - this.model.minimized = setValue; - } else { - return this.model.minimized; - } - }; - NotificationService.prototype.getActiveNotification = function (){ return this.active.notification; } /** - * model = { - * - * } - * @param model + * A convenience method for success notifications. Notifications + * created via this method will be auto-dismissed after a default + * wait period + * @param {Notification} notification The notification to display */ - NotificationService.prototype.notify = function (model) { - var notification = new Notification(model), - that=this; + NotificationService.prototype.success = function (notification) { + notification.autoDismiss = notification.autoDismiss || true; + NotificationService.prototype.notify(notification); + } + + /** + * Notifies the user of an event. If there is a banner notification + * already active, then it will be dismissed or minimized automatically, + * and the provided notification displayed in its place. + * + * @param {Notification} notification The notification to display + */ + NotificationService.prototype.notify = function (notification) { + /*var notification = new Notification(model), + that=this; */ + var that = this; + this.notifications.push(notification); /* Check if there is already an active (ie. visible) notification */ if (!this.active.notification){ - setActiveNotification.call(this, notification); + this.setActiveNotification(notification); } else if (!this.active.timeout){ /* @@ -122,28 +152,42 @@ define( }; - function setActiveNotification (notification) { - var that = this; - this.active.notification = notification; - /* - If autoDismiss has been specified, setup a timeout to - dismiss the dialog. + /** + * Used internally by the NotificationService + * @private + */ + NotificationService.prototype.setActiveNotification = + function (notification) { - If there are other notifications pending in the queue, set this - one to auto-dismiss - */ - if (notification.model.autoDismiss - || selectNextNotification.call(this)) { - var timeout = isNaN(notification.model.autoDismiss) ? - this.DEFAULT_AUTO_DISMISS : notification.model.autoDismiss; + var that = this; + this.active.notification = notification; + /* + If autoDismiss has been specified, setup a timeout to + dismiss the dialog. - this.active.timeout = this.$timeout(function () { - that.dismissOrMinimize(notification); - }, timeout); - } - } + If there are other notifications pending in the queue, set this + one to auto-dismiss + */ + if (notification && (notification.autoDismiss + || this.selectNextNotification())) { + var timeout = isNaN(notification.autoDismiss) ? + this.DEFAULT_AUTO_DISMISS : + notification.autoDismiss; - function selectNextNotification () { + this.active.timeout = this.$timeout(function () { + that.dismissOrMinimize(notification); + }, timeout); + } else { + delete this.active.timeout; + } + }; + + /** + * Used internally by the NotificationService + * + * @private + */ + NotificationService.prototype.selectNextNotification = function () { /* Loop through the notifications queue and find the first one that has not already been minimized (manually or otherwise). @@ -151,7 +195,7 @@ define( for (var i=0; i< this.notifications.length; i++) { var notification = this.notifications[i]; - if (!notification.model.minimized + if (!notification.minimized && notification!= this.activeNotification) { return notification; @@ -162,8 +206,9 @@ define( /** * Minimize a notification. The notification will still be available * from the notification list. Typically notifications with a - * severity of SUCCESS should not be minimized, but rather - * dismissed. + * severity of 'success' should not be minimized, but rather + * dismissed. If you're not sure which is appropriate, + * use {@link NotificationService#dismissOrMinimize} * @see dismiss * @see dismissOrMinimize * @param notification @@ -172,19 +217,17 @@ define( //Check this is a known notification var index = this.notifications.indexOf(notification); if (index >= 0) { - notification.minimize(true); - delete this.active.notification; - delete this.active.timeout; - setActiveNotification.call(this, selectNextNotification.call(this)); + notification.minimized=true; + this.setActiveNotification(this.selectNextNotification()); } - } + }; /** - * Completely remove a notification. This will dismiss it from the + * Completely removes a notification. This will dismiss it from the * message banner and remove it from the list of notifications. - * Typically only notifications with a severity of SUCCESS should be + * Typically only notifications with a severity of success should be * dismissed. If you're not sure whether to dismiss or minimize a - * notification, use the dismissOrMinimize method. + * notification, use {@link NotificationService#dismissOrMinimize}. * dismiss * @see dismissOrMinimize * @param notification The notification to dismiss @@ -194,11 +237,7 @@ define( var index = this.notifications.indexOf(notification); if (index >= 0) { this.notifications.splice(index, 1); - - delete this.active.notification; - delete this.active.timeout; - - setActiveNotification.call(this, selectNextNotification.call(this)); + this.setActiveNotification(this.selectNextNotification()); } } @@ -210,7 +249,7 @@ define( * @param notification */ NotificationService.prototype.dismissOrMinimize = function (notification){ - if (notification.model.severity > MessageSeverity.SUCCESS){ + if (notification.severity > MessageSeverity.SUCCESS){ this.minimize(notification); } else { this.dismiss(notification); diff --git a/platform/commonUI/notification/test/NotificationServiceSpec.js b/platform/commonUI/notification/test/NotificationServiceSpec.js index c7a5fee234..c54ba08e38 100644 --- a/platform/commonUI/notification/test/NotificationServiceSpec.js +++ b/platform/commonUI/notification/test/NotificationServiceSpec.js @@ -30,24 +30,18 @@ define( var notificationService, mockTimeout, mockAutoDismiss, - successModel = { - title: "Mock Success Notification", - severity: MessageSeverity.SUCCESS - }, - errorModel = { - title: "Mock Error Notification", - severity: MessageSeverity.ERROR - }; + successModel, + errorModel; /** * 1) Calling .notify results in a new notification being created - * with the provided model and set to the active notification + * with the provided model and set to the active notification. DONE * * 2) Calling .notify with autoDismiss results in a SUCCESS notification - * becoming dismissed after timeout has elapsed + * becoming dismissed after timeout has elapsed DONE * * 3) Calling .notify with autoDismiss results in an ERROR notification - * being MINIMIZED after a timeout has elapsed + * being MINIMIZED after a timeout has elapsed DONE * * 4) Calling .notify with an active success notification results in that * notification being auto-dismissed, and the new notification becoming @@ -84,17 +78,49 @@ define( mockAutoDismiss = 0; notificationService = new NotificationService( mockTimeout, mockAutoDismiss); + successModel = { + title: "Mock Success Notification", + severity: MessageSeverity.SUCCESS + }; + errorModel = { + title: "Mock Error Notification", + severity: MessageSeverity.ERROR + }; }); - it("Calls the notification service with a new notification, making" + + it("gets a new success notification, making" + " the notification active", function() { var activeNotification; notificationService.notify(successModel); activeNotification = notificationService.getActiveNotification(); - expect(activeNotification.model).toBe(successModel); + expect(activeNotification).toBe(successModel); }); - describe(" called with multiple notifications", function(){ + it("gets a new success notification with" + + " numerical auto-dismiss specified. ", function() { + var activeNotification; + successModel.autoDismiss = 1000; + notificationService.notify(successModel); + activeNotification = notificationService.getActiveNotification(); + expect(activeNotification).toBe(successModel); + mockTimeout.mostRecentCall.args[0](); + activeNotification = notificationService.getActiveNotification(); + expect(activeNotification).toBeUndefined(); + }); + + it("gets a new notification with" + + " boolean auto-dismiss specified. ", function() { + var activeNotification; + successModel.autoDismiss = true; + notificationService.notify(successModel); + activeNotification = notificationService.getActiveNotification(); + expect(activeNotification).toBe(successModel); + mockTimeout.mostRecentCall.args[0](); + activeNotification = notificationService.getActiveNotification(); + expect(activeNotification).toBeUndefined(); + }); + + describe(" gets called with multiple notifications", function(){ it("auto-dismisses the previously active notification, making" + " the new notification active", function() { var activeNotification; @@ -103,14 +129,14 @@ define( activeNotification = notificationService.getActiveNotification(); //Initially expect the active notification to be success - expect(activeNotification.model).toBe(successModel); + expect(activeNotification).toBe(successModel); //Then notify of an error notificationService.notify(errorModel); //But it should be auto-dismissed and replaced with the // error notification mockTimeout.mostRecentCall.args[0](); activeNotification = notificationService.getActiveNotification(); - expect(activeNotification.model).toBe(errorModel); + expect(activeNotification).toBe(errorModel); }); it("auto-dismisses an active success notification, removing" + " it completely", function() { @@ -125,9 +151,9 @@ define( }); it("auto-minimizes an active error notification", function() { var activeNotification; - //First pre-load with a success message + //First pre-load with an error message notificationService.notify(errorModel); - //Then notify of an error + //Then notify of success notificationService.notify(successModel); expect(notificationService.notifications.length).toEqual(2); //Mock the auto-minimize @@ -137,8 +163,42 @@ define( expect(notificationService.notifications.length).toEqual(2); activeNotification = notificationService.getActiveNotification(); - expect(activeNotification.model).toBe(successModel); + expect(activeNotification).toBe(successModel); expect(errorModel.minimized).toEqual(true); + }); + it("auto-minimizes errors when a number of them arrive in" + + " short succession ", function() { + var activeNotification; + var error2 = { + title: "Second Mock Error Notification", + severity: MessageSeverity.ERROR + } + var error3 = { + title: "Third Mock Error Notification", + severity: MessageSeverity.ERROR + } + //First pre-load with a success message + notificationService.notify(errorModel); + //Then notify of a third error + notificationService.notify(error2); + notificationService.notify(error3); + expect(notificationService.notifications.length).toEqual(3); + //Mock the auto-minimize + mockTimeout.mostRecentCall.args[0](); + //Previous error message should be minimized, not + // dismissed + expect(notificationService.notifications.length).toEqual(3); + activeNotification = + notificationService.getActiveNotification(); + expect(activeNotification).toBe(error2); + expect(errorModel.minimized).toEqual(true); + + //Mock the second auto-minimize + mockTimeout.mostRecentCall.args[0](); + activeNotification = + notificationService.getActiveNotification(); + expect(activeNotification).toBe(error3); + expect(error2.minimized).toEqual(true); }); }); From af1fa6e77aec37277e5cb6601b1e2b1155d7c47e Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 11:13:32 -0700 Subject: [PATCH 031/488] added semicolons to test spec --- .../commonUI/notification/test/NotificationServiceSpec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/commonUI/notification/test/NotificationServiceSpec.js b/platform/commonUI/notification/test/NotificationServiceSpec.js index c54ba08e38..96994f26d2 100644 --- a/platform/commonUI/notification/test/NotificationServiceSpec.js +++ b/platform/commonUI/notification/test/NotificationServiceSpec.js @@ -172,11 +172,11 @@ define( var error2 = { title: "Second Mock Error Notification", severity: MessageSeverity.ERROR - } + }; var error3 = { title: "Third Mock Error Notification", severity: MessageSeverity.ERROR - } + }; //First pre-load with a success message notificationService.notify(errorModel); //Then notify of a third error From e1e5919f686da2c28ee1413f73ccc4ea197ca404 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 13:08:43 -0700 Subject: [PATCH 032/488] Added some semicolons --- platform/commonUI/notification/src/MessageSeverity.js | 5 +++-- platform/commonUI/notification/src/NotificationService.js | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/platform/commonUI/notification/src/MessageSeverity.js b/platform/commonUI/notification/src/MessageSeverity.js index 5f17b917fe..39e8e6d8d0 100644 --- a/platform/commonUI/notification/src/MessageSeverity.js +++ b/platform/commonUI/notification/src/MessageSeverity.js @@ -1,9 +1,10 @@ /** * Created by akhenry on 10/7/15. */ +/*global define*/ define(function(){ return { SUCCESS: 0, ERROR: 1 - } -}) \ No newline at end of file + }; +}); \ No newline at end of file diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 46950440da..ecdb8d6ddd 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -103,7 +103,7 @@ define( */ NotificationService.prototype.getActiveNotification = function (){ return this.active.notification; - } + }; /** * A convenience method for success notifications. Notifications @@ -114,7 +114,7 @@ define( NotificationService.prototype.success = function (notification) { notification.autoDismiss = notification.autoDismiss || true; NotificationService.prototype.notify(notification); - } + }; /** * Notifies the user of an event. If there is a banner notification @@ -239,7 +239,7 @@ define( this.notifications.splice(index, 1); this.setActiveNotification(this.selectNextNotification()); } - } + }; /** * Depending on the severity of the notification will selectively From 0e840ae003dc2e52e8a7740c0e43dbb97b95a131 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 8 Oct 2015 13:09:41 -0700 Subject: [PATCH 033/488] [Frontend] Messages major work open #159 open #170 CSS, markup and JS test files initially complete; Templates renamed for consistency; message-type icons styled; Added severity constant "alert"; TO-DO: check refactored CSS against mobile, see what's broken; --- platform/commonUI/dialog/bundle.json | 5 +- .../dialog/res/templates/message-list.html | 24 - .../dialog/res/templates/message.html | 40 +- .../templates/overlay-blocking-message.html | 2 +- .../res/templates/overlay-message-list.html | 25 + platform/commonUI/dialog/src/DialogService.js | 6 +- .../commonUI/general/res/sass/_constants.scss | 2 +- .../general/res/sass/controls/_messages.scss | 94 ++- .../res/sass/mobile/overlay/_overlay.scss | 20 +- .../general/res/sass/overlay/_overlay.scss | 57 +- .../espresso/res/css/theme-espresso.css | 541 +++++++++++------- .../themes/espresso/res/sass/_constants.scss | 9 +- .../themes/snow/res/css/theme-snow.css | 421 +++++++++----- .../themes/snow/res/sass/_constants.scss | 7 +- .../dialogTest/src/DialogLaunchController.js | 70 ++- 15 files changed, 846 insertions(+), 477 deletions(-) delete mode 100644 platform/commonUI/dialog/res/templates/message-list.html create mode 100644 platform/commonUI/dialog/res/templates/overlay-message-list.html diff --git a/platform/commonUI/dialog/bundle.json b/platform/commonUI/dialog/bundle.json index 87ef06f764..8b53971d4a 100644 --- a/platform/commonUI/dialog/bundle.json +++ b/platform/commonUI/dialog/bundle.json @@ -4,6 +4,7 @@ { "key": "messageSeverity", "value": { + "ALERT": "alert", "ERROR": "error", "INFO": "info", "SUCCESS": "success" @@ -44,8 +45,8 @@ "templateUrl": "templates/message.html" }, { - "key": "message-list", - "templateUrl": "templates/message-list.html" + "key": "overlay-message-list", + "templateUrl": "templates/overlay-message-list.html" } ], "containers": [ diff --git a/platform/commonUI/dialog/res/templates/message-list.html b/platform/commonUI/dialog/res/templates/message-list.html deleted file mode 100644 index 67fbdb28d0..0000000000 --- a/platform/commonUI/dialog/res/templates/message-list.html +++ /dev/null @@ -1,24 +0,0 @@ - -
-
-
-
{{ngModel.dialog.title}}
-
{{ngModel.dialog.hint}}
-
-
-
    -
  • - Message: {{msg.title}} - -
  • -
-
- -
-
\ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index 709a71adbb..d9fe79edfe 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -1,22 +1,24 @@ -
!
-
-
-
Title: {{ngModel.dialog.title}}
-
{{ngModel.dialog.hint}}
-
-
-
- {{ngModel.dialog.actionText}} +
+
+
+
+
{{ngModel.dialog.title}}
+
{{ngModel.dialog.hint}}
+
+
+
+ {{ngModel.dialog.actionText}} message-severity-{{ngModel.dialog.severity}} +
+ +
+ - -
-
\ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/overlay-blocking-message.html b/platform/commonUI/dialog/res/templates/overlay-blocking-message.html index a4d4dc7276..17fdcf152c 100644 --- a/platform/commonUI/dialog/res/templates/overlay-blocking-message.html +++ b/platform/commonUI/dialog/res/templates/overlay-blocking-message.html @@ -19,7 +19,7 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> - + \ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/overlay-message-list.html b/platform/commonUI/dialog/res/templates/overlay-message-list.html new file mode 100644 index 0000000000..550c0bcda2 --- /dev/null +++ b/platform/commonUI/dialog/res/templates/overlay-message-list.html @@ -0,0 +1,25 @@ + +
+
+
{{ngModel.dialog.title}}
+
Displaying {{ngModel.dialog.messages.length}} messages +
+
+
+ + +
+ +
+
\ No newline at end of file diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index 836805304e..2fc6f41a60 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -176,7 +176,7 @@ define( /** * dialogModel: { - * severity: string "error" | "info", + * severity: string "error" | "info" | "alert", * title: string, * hint: string, * actionText: string, @@ -258,9 +258,9 @@ define( // Add the overlay using the OverlayService, which // will handle actual insertion into the DOM this.overlay = this.overlayService.createOverlay( - "message-list", + "overlay-message-list", {dialog: dialogModel}, - "t-dialog t-message-list" + "t-dialog" ); this.dialogVisible = true; return true; diff --git a/platform/commonUI/general/res/sass/_constants.scss b/platform/commonUI/general/res/sass/_constants.scss index 78c7c916dd..2c425493ea 100644 --- a/platform/commonUI/general/res/sass/_constants.scss +++ b/platform/commonUI/general/res/sass/_constants.scss @@ -47,7 +47,7 @@ $ueEditLeftPaneW: 75%; $treeSearchInputBarH: 25px; // Overlay $ovrTopBarH: 60px; -$ovrFooterH: 30px; +$ovrFooterH: 24px; $overlayMargin: 25px; // Items $ueBrowseGridItemLg: 200px; diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index 3bea644daa..d537ee8ff1 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -161,18 +161,92 @@ } } -// Messages in overlays, as singleton or in list -.t-message .overlay { - // Singleton message overlay context - $iconW: 80px; - .type-icon.message-type.abs { - //color: pushBack($colorOvrFg, 40%); +@mixin messageBlock($iconW: 32px) { + .type-icon.message-type { + @include txtShdw($shdwStatusIc); + &:before { content:"\e608"; } + color: $colorStatusDefault; font-size: $iconW; - opacity: 0.5; - right: auto; + padding-left: 1px; width: $iconW; } - .message-contents.abs { - left: $iconW + $overlayMargin; + + .message-severity-info .type-icon.message-type { + &:before { content:"\e608"; } + color: $colorStatusOk; + } + .message-severity-alert .type-icon.message-type { + &:before { content:"\e610"; } + color: $colorStatusCaution; + } + .message-severity-error .type-icon.message-type { + &:before { content:"\21"; } + color: $colorStatusAlert; + } +} +/* Paths: + t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message > + message-type > (icon) + message-contents > + top-bar > + title + hint + editor > + (if displaying list of messages) + ul > li > l-message > + ... same as above + bottom-bar +*/ + +.l-message { + @include display-flex; + @include flex-direction(row); + .type-icon.message-type { + //@include test(red); + @include flex(0 1 auto); + position: relative; + } + .message-contents { + //@include test(blue); + @include flex(1 1 auto); + margin-left: $overlayMargin; + position: relative; + + .top-bar, + .message-body { + margin-bottom: $interiorMarginLg * 2; + } + } +} + + +// Message as singleton +.t-message-single { + @include messageBlock(80px); +} + +// Messages in list +.t-message-list { + @include messageBlock(32px); + .message-contents { + .l-message { + //border-bottom: 1px solid pullForward($colorOvrBg, 20%); + @include border-radius($controlCr); + background: rgba($colorOvrFg, 0.1); + margin-bottom: $interiorMargin; + margin-right: $interiorMarginLg; + padding: $interiorMarginLg; + + .message-contents { + font-size: 0.9em; + .message-action { color: pushBack($colorOvrFg, 20%); } + .bottom-bar { text-align: left; } + } + + .top-bar, + .message-body { + margin-bottom: $interiorMarginLg; + } + } } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss b/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss index af0380a12b..9fe624b2fd 100644 --- a/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss @@ -52,15 +52,17 @@ } } } - - .contents .top-bar, - .contents .editor, - .contents .bottom-bar { - //@include test(orange); - top: auto; right: auto; bottom: auto; left: auto; - height: auto; width: auto; - margin-bottom: $interiorMarginLg * 2; - position: relative; + + .contents { + .top-bar, + .editor, + .bottom-bar { + //@include test(orange); + top: auto; right: auto; bottom: auto; left: auto; + height: auto; width: auto; + margin-bottom: $interiorMarginLg * 2; + position: relative; + } } } .t-dialog-sm .overlay > .holder { diff --git a/platform/commonUI/general/res/sass/overlay/_overlay.scss b/platform/commonUI/general/res/sass/overlay/_overlay.scss index 4790d59768..305534c0d0 100644 --- a/platform/commonUI/general/res/sass/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/overlay/_overlay.scss @@ -20,6 +20,7 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ .overlay { + font-size: 90%; .blocker { background: $colorOvrBlocker; z-index: 100; @@ -56,6 +57,12 @@ right: $m; bottom: $m; left: $m; + + //.top-bar, + //.editor, + //.bottom-bar { + // @include absPosDefault(); + //} } } @@ -70,11 +77,12 @@ color: pushBack($colorOvrFg, 20%); } - .top-bar { + .abs.top-bar { height: $ovrTopBarH; } - .editor { + .abs.editor, + .abs.message-body { top: $ovrTopBarH + ($interiorMargin * 2); bottom: $ovrFooterH + $interiorMargin * 2; left: 0; @@ -87,23 +95,7 @@ } } - .l-progress-bar { - $h: $progressBarHOverlay; - display: block; - height: $h; - line-height: $h; - margin: .5em 0; - width: 100%; - } - .bottom-bar { - top: auto; - right: 0; - bottom: 0; - left: 0; - overflow: visible; - //font-size: 1em; - height: $ovrFooterH; text-align: right; .s-btn { $bg: $colorOvrBtnBg; @@ -115,13 +107,30 @@ line-height: $ovrFooterH; margin-left: $interiorMargin; padding: 0 $interiorMargin * 3; - //&.major { - // @extend .s-btn.major; - // &:hover { - // @extend .s-btn.major:hover; - // } - //} + &:first-child { + margin-left: 0; + } } + + } + + .abs.bottom-bar { + top: auto; + right: 0; + bottom: 0; + left: 0; + overflow: visible; + //font-size: 1em; + height: $ovrFooterH; + } + + .l-progress-bar { + $h: $progressBarHOverlay; + display: block; + height: $h; + line-height: $h; + margin: .5em 0; + width: 100%; } } diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 389cdf1490..1d4d0e0e79 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -600,10 +600,10 @@ mct-container { color: #0099cc; } /* line 34, ../../../../general/res/sass/_icons.scss */ .ui-symbol.icon.alert { - color: #ff3c00; } + color: #ff533a; } /* line 36, ../../../../general/res/sass/_icons.scss */ .ui-symbol.icon.alert:hover { - color: #ff8a66; } + color: #ffaca0; } /* line 40, ../../../../general/res/sass/_icons.scss */ .ui-symbol.icon.major { font-size: 1.65em; } @@ -637,7 +637,7 @@ mct-container { display: none !important; } /* line 76, ../../../../general/res/sass/_icons.scss */ .l-icon-alert:before { - color: #ff3c00; + color: #ff533a; content: "!"; } /* line 13, ../../../../general/res/sass/_limits.scss */ @@ -827,9 +827,9 @@ mct-container { /* line 150, ../../../../general/res/sass/helpers/_bubbles.scss */ .s-infobubble { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; @@ -1198,9 +1198,9 @@ mct-container { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; - -moz-border-radius: 1.5px; - -webkit-border-radius: 1.5px; - border-radius: 1.5px; + -moz-border-radius: 2.25px; + -webkit-border-radius: 2.25px; + border-radius: 2.25px; -moz-transition: background-color 0.25s; -o-transition: background-color 0.25s; -webkit-transition: background-color 0.25s; @@ -1556,9 +1556,9 @@ mct-container { margin-top: 0; } /* line 29, ../../../../general/res/sass/controls/_controls.scss */ .accordion .accordion-head { - -moz-border-radius: 1.5px; - -webkit-border-radius: 1.5px; - border-radius: 1.5px; + -moz-border-radius: 2.25px; + -webkit-border-radius: 2.25px; + border-radius: 2.25px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; @@ -1651,9 +1651,9 @@ label.checkbox.custom { min-width: 14px; } /* line 127, ../../../../general/res/sass/controls/_controls.scss */ label.checkbox.custom em:before { - -moz-border-radius: 1.5px; - -webkit-border-radius: 1.5px; - border-radius: 1.5px; + -moz-border-radius: 2.25px; + -webkit-border-radius: 2.25px; + border-radius: 2.25px; background: #4d4d4d; -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 2px; -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 2px; @@ -1838,24 +1838,24 @@ label.checkbox.custom { /* line 309, ../../../../general/res/sass/controls/_controls.scss */ .s-progress-bar { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; -moz-box-shadow: inset rgba(0, 0, 0, 0.3) 0 1px 4px; -webkit-box-shadow: inset rgba(0, 0, 0, 0.3) 0 1px 4px; box-shadow: inset rgba(0, 0, 0, 0.3) 0 1px 4px; background: rgba(0, 0, 0, 0.1); } /* line 314, ../../../../general/res/sass/controls/_controls.scss */ .s-progress-bar .progress-amt { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; - -moz-border-radius: 1px; - -webkit-border-radius: 1px; - border-radius: 1px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; -moz-transition-property: width; -o-transition-property: width; -webkit-transition-property: width; @@ -2165,9 +2165,9 @@ label.checkbox.custom { position: relative; } /* line 70, ../../../../general/res/sass/controls/_menus.scss */ .menu-element .menu { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; background-color: #6e6e6e; -moz-border-radius: 3px; -webkit-border-radius: 3px; @@ -2399,10 +2399,10 @@ label.checkbox.custom { margin-right: 3px; } /* line 54, ../../../../general/res/sass/controls/_messages.scss */ .status.block.ok .status-indicator { - color: #60e68e; } + color: #44ba53; } /* line 57, ../../../../general/res/sass/controls/_messages.scss */ .status.block.caution .status-indicator { - color: #ffa864; } + color: #ff8545; } /* line 60, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; @@ -2446,14 +2446,14 @@ label.checkbox.custom { /* Styles for messages and message banners */ /* line 84, ../../../../general/res/sass/controls/_messages.scss */ .message.block { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; padding: 10px; } /* line 88, ../../../../general/res/sass/controls/_messages.scss */ .message.error { - background-color: rgba(255, 60, 0, 0.3); - color: #ff8a66; } + background-color: rgba(255, 83, 58, 0.3); + color: #ffaca0; } /* line 94, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner { @@ -2548,9 +2548,9 @@ label.checkbox.custom { color: inherit; } /* line 147, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .s-action { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; } + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; } /* line 150, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .close { opacity: 0.5; } @@ -2559,40 +2559,145 @@ label.checkbox.custom { opacity: 1; } /* line 156, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok { - background-color: #189543; + background-color: #1b4a21; color: #ccc; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok:hover { - background-color: #1ec256; } + background-color: #296f32; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action { - background-color: #11692f; } + background-color: #0d2510; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action:hover { - background-color: #189543; } + background-color: #1b4a21; } /* line 159, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution { - background-color: #ca5900; + background-color: #ab3b00; color: #ccc; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution:hover { - background-color: #fd6f00; } + background-color: #de4c00; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution .s-action { - background-color: #974200; } + background-color: #782900; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution .s-action:hover { - background-color: #ca5900; } + background-color: #ab3b00; } -/* line 168, ../../../../general/res/sass/controls/_messages.scss */ -.t-message .overlay .type-icon.message-type.abs, .t-message .overlay .s-menu span.type-icon.message-type.l-click-area, .s-menu .t-message .overlay span.type-icon.message-type.l-click-area { +/* Paths: + t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message > + message-type > (icon) + message-contents > + top-bar > + title + hint + editor > + (if displaying list of messages) + ul > li > l-message > + ... same as above + bottom-bar +*/ +/* line 201, ../../../../general/res/sass/controls/_messages.scss */ +.l-message { + display: -webkit-flex; + display: flex; + -webkit-flex-direction: row; + flex-direction: row; } + /* line 204, ../../../../general/res/sass/controls/_messages.scss */ + .l-message .type-icon.message-type { + -webkit-flex: 0 1 auto; + flex: 0 1 auto; + position: relative; } + /* line 209, ../../../../general/res/sass/controls/_messages.scss */ + .l-message .message-contents { + -webkit-flex: 1 1 auto; + flex: 1 1 auto; + margin-left: 25px; + position: relative; } + /* line 215, ../../../../general/res/sass/controls/_messages.scss */ + .l-message .message-contents .top-bar, + .l-message .message-contents .message-body { + margin-bottom: 20px; } + +/* line 165, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-single .type-icon.message-type { + text-shadow: rgba(0, 0, 0, 0.4) 0 1px 2px; + color: #ccc; font-size: 80px; - opacity: 0.5; - right: auto; + padding-left: 1px; width: 80px; } -/* line 175, ../../../../general/res/sass/controls/_messages.scss */ -.t-message .overlay .message-contents.abs, .t-message .overlay .s-menu span.message-contents.l-click-area, .s-menu .t-message .overlay span.message-contents.l-click-area { - left: 105px; } + /* line 167, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .type-icon.message-type:before { + content: "\e608"; } +/* line 174, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-single .message-severity-info .type-icon.message-type { + color: #44ba53; } + /* line 175, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .message-severity-info .type-icon.message-type:before { + content: "\e608"; } +/* line 178, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-single .message-severity-alert .type-icon.message-type { + color: #ff8545; } + /* line 179, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .message-severity-alert .type-icon.message-type:before { + content: "\e610"; } +/* line 182, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-single .message-severity-error .type-icon.message-type { + color: #ff533a; } + /* line 183, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .message-severity-error .type-icon.message-type:before { + content: "\21"; } + +/* line 165, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .type-icon.message-type { + text-shadow: rgba(0, 0, 0, 0.4) 0 1px 2px; + color: #ccc; + font-size: 32px; + padding-left: 1px; + width: 32px; } + /* line 167, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .type-icon.message-type:before { + content: "\e608"; } +/* line 174, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .message-severity-info .type-icon.message-type { + color: #44ba53; } + /* line 175, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-severity-info .type-icon.message-type:before { + content: "\e608"; } +/* line 178, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .message-severity-alert .type-icon.message-type { + color: #ff8545; } + /* line 179, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-severity-alert .type-icon.message-type:before { + content: "\e610"; } +/* line 182, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .message-severity-error .type-icon.message-type { + color: #ff533a; } + /* line 183, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-severity-error .type-icon.message-type:before { + content: "\21"; } +/* line 232, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .message-contents .l-message { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + background: rgba(230, 230, 230, 0.1); + margin-bottom: 5px; + margin-right: 10px; + padding: 10px; } + /* line 240, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .message-contents { + font-size: 0.9em; } + /* line 242, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .message-contents .message-action { + color: #b3b3b3; } + /* line 243, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .message-contents .bottom-bar { + text-align: left; } + /* line 246, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .top-bar, + .t-message-list .message-contents .l-message .message-body { + margin-bottom: 10px; } /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { @@ -2749,9 +2854,9 @@ label.checkbox.custom { *****************************************************************************/ /* line 22, ../../../../general/res/sass/forms/_elems.scss */ .section-header { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; background: rgba(0, 0, 0, 0.2); color: #cccccc; font-size: 0.8em; @@ -2876,9 +2981,9 @@ label.form-control.checkbox input { vertical-align: top; } /* line 156, ../../../../general/res/sass/forms/_elems.scss */ .l-result div.s-hint { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; background: rgba(255, 51, 0, 0.8); display: block; color: #ffad99; @@ -4100,9 +4205,9 @@ span.req { visibility: visible; } /* line 178, ../../../../general/res/sass/search/_search.scss */ .search .active-filter-display { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; @@ -4195,152 +4300,166 @@ span.req { * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 23, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .blocker { - background: rgba(0, 0, 0, 0.7); - z-index: 100; } -/* line 27, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .clk-icon.close { - font-size: 0.8rem; - position: absolute; - top: 10px; - right: 10px; - bottom: auto; - left: auto; - z-index: 100; } -/* line 36, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay > .holder { - background-color: #4d4d4d; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #e6e6e6; - display: inline-block; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzU5NTk1OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); - background-size: 100%; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #595959), color-stop(100%, #4d4d4d)); - background-image: -moz-linear-gradient(#595959, #4d4d4d); - background-image: -webkit-linear-gradient(#595959, #4d4d4d); - background-image: linear-gradient(#595959, #4d4d4d); - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; - -moz-border-radius: 6px; - -webkit-border-radius: 6px; - border-radius: 6px; - color: #e6e6e6; - top: 50%; - right: auto; - bottom: auto; - left: 50%; - -moz-transform: translateX(-50%) translateY(-50%); - -ms-transform: translateX(-50%) translateY(-50%); - -webkit-transform: translateX(-50%) translateY(-50%); - transform: translateX(-50%) translateY(-50%); - height: 70%; - width: 50%; - min-height: 300px; - max-height: 800px; - min-width: 600px; - max-width: 1000px; - z-index: 101; } - /* line 53, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay > .holder > .contents { - top: 25px; - right: 25px; - bottom: 25px; - left: 25px; } -/* line 62, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .title { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 1.2em; - line-height: 120%; - margin-bottom: 5px; } -/* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .hint { - color: #b3b3b3; } -/* line 73, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .top-bar { - height: 60px; } -/* line 77, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .editor { - top: 70px; - bottom: 40px; - left: 0; - right: 0; - overflow: auto; } +/* line 22, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay { + font-size: 90%; } + /* line 24, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .blocker { + background: rgba(0, 0, 0, 0.7); + z-index: 100; } + /* line 28, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .clk-icon.close { + font-size: 0.8rem; + position: absolute; + top: 10px; + right: 10px; + bottom: auto; + left: auto; + z-index: 100; } + /* line 37, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay > .holder { + background-color: #4d4d4d; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #e6e6e6; + display: inline-block; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzU5NTk1OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #595959), color-stop(100%, #4d4d4d)); + background-image: -moz-linear-gradient(#595959, #4d4d4d); + background-image: -webkit-linear-gradient(#595959, #4d4d4d); + background-image: linear-gradient(#595959, #4d4d4d); + -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; + -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; + box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; + -moz-border-radius: 9px; + -webkit-border-radius: 9px; + border-radius: 9px; + color: #e6e6e6; + top: 50%; + right: auto; + bottom: auto; + left: 50%; + -moz-transform: translateX(-50%) translateY(-50%); + -ms-transform: translateX(-50%) translateY(-50%); + -webkit-transform: translateX(-50%) translateY(-50%); + transform: translateX(-50%) translateY(-50%); + height: 70%; + width: 50%; + min-height: 300px; + max-height: 800px; + min-width: 600px; + max-width: 1000px; + z-index: 101; } + /* line 54, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay > .holder > .contents { + top: 25px; + right: 25px; + bottom: 25px; + left: 25px; } + /* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 1.2em; + line-height: 120%; + margin-bottom: 5px; } + /* line 76, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .hint { + color: #b3b3b3; } + /* line 80, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .abs.top-bar, .overlay .s-menu span.top-bar.l-click-area, .s-menu .overlay span.top-bar.l-click-area { + height: 60px; } /* line 84, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .editor .field.l-med input[type='text'] { + .overlay .abs.editor, .overlay .s-menu span.editor.l-click-area, .s-menu .overlay span.editor.l-click-area, + .overlay .abs.message-body, + .overlay .s-menu span.message-body.l-click-area, + .s-menu .overlay span.message-body.l-click-area { + top: 70px; + bottom: 34px; + left: 0; + right: 0; + overflow: auto; } + /* line 92, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .abs.editor .field.l-med input[type='text'], .overlay .s-menu span.editor.l-click-area .field.l-med input[type='text'], .s-menu .overlay span.editor.l-click-area .field.l-med input[type='text'], + .overlay .abs.message-body .field.l-med input[type='text'], + .overlay .s-menu span.message-body.l-click-area .field.l-med input[type='text'], + .s-menu .overlay span.message-body.l-click-area .field.l-med input[type='text'] { + width: 100%; } + /* line 98, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar { + text-align: right; } + /* line 100, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { + font-size: 95%; + height: 24px; + line-height: 24px; + margin-left: 5px; + padding: 0 15px; } + /* line 102, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { + background-color: gray; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + display: inline-block; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzhjOGM4YyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzgwODA4MCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #8c8c8c), color-stop(100%, #808080)); + background-image: -moz-linear-gradient(#8c8c8c, #808080); + background-image: -webkit-linear-gradient(#8c8c8c, #808080); + background-image: linear-gradient(#8c8c8c, #808080); + -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; + -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; + box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + -moz-transition: background, 0.25s; + -o-transition: background, 0.25s; + -webkit-transition: background, 0.25s; + transition: background, 0.25s; + text-shadow: rgba(0, 0, 0, 0.3) 0 1px 1px; } + /* line 289, ../../../../general/res/sass/_mixins.scss */ + .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu:not(.major) .icon { + color: #fff; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 294, ../../../../general/res/sass/_mixins.scss */ + .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover { + background: linear-gradient(#a6a6a6, #999999); } + /* line 296, ../../../../general/res/sass/_mixins.scss */ + .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { + color: white; } } + /* line 110, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar .s-btn:first-child, .overlay .bottom-bar .s-menu:first-child { + margin-left: 0; } + /* line 117, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .abs.bottom-bar, .overlay .s-menu span.bottom-bar.l-click-area, .s-menu .overlay span.bottom-bar.l-click-area { + top: auto; + right: 0; + bottom: 0; + left: 0; + overflow: visible; + height: 24px; } + /* line 127, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .l-progress-bar { + display: block; + height: 15px; + line-height: 15px; + margin: .5em 0; width: 100%; } -/* line 90, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .l-progress-bar { - display: block; - height: 15px; - line-height: 15px; - margin: .5em 0; - width: 100%; } -/* line 99, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .bottom-bar { - top: auto; - right: 0; - bottom: 0; - left: 0; - overflow: visible; - height: 30px; - text-align: right; } - /* line 108, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { - font-size: 95%; - height: 30px; - line-height: 30px; - margin-left: 5px; - padding: 0 15px; } - /* line 110, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { - background-color: gray; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #fff; - display: inline-block; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzhjOGM4YyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzgwODA4MCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); - background-size: 100%; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #8c8c8c), color-stop(100%, #808080)); - background-image: -moz-linear-gradient(#8c8c8c, #808080); - background-image: -webkit-linear-gradient(#8c8c8c, #808080); - background-image: linear-gradient(#8c8c8c, #808080); - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; - box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - -moz-transition: background, 0.25s; - -o-transition: background, 0.25s; - -webkit-transition: background, 0.25s; - transition: background, 0.25s; - text-shadow: rgba(0, 0, 0, 0.3) 0 1px 1px; } - /* line 289, ../../../../general/res/sass/_mixins.scss */ - .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu:not(.major) .icon { - color: #fff; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 294, ../../../../general/res/sass/_mixins.scss */ - .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover { - background: linear-gradient(#a6a6a6, #999999); } - /* line 296, ../../../../general/res/sass/_mixins.scss */ - .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { - color: white; } } -/* line 128, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 137, ../../../../general/res/sass/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { min-height: 275px; height: 275px; } @@ -4393,7 +4512,7 @@ span.req { /* line 50, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder .editor .form .form-row > .label:after { float: none; } - /* line 56, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 57, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder .contents .top-bar, .overlay > .holder .contents .editor, .overlay > .holder .contents .bottom-bar { @@ -4406,12 +4525,12 @@ span.req { margin-bottom: 20px; position: relative; } - /* line 66, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 68, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { height: auto; max-height: 100%; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 74, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 76, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder .contents .bottom-bar { text-align: center; } } /***************************************************************************** @@ -4462,9 +4581,9 @@ ul.tree { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; -moz-transition: background-color 0.25s; -o-transition: background-color 0.25s; -webkit-transition: background-color 0.25s; @@ -4524,7 +4643,7 @@ ul.tree { /* line 90, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label .type-icon .icon.l-icon-alert, .search-result-item .label .type-icon .icon.l-icon-alert { - color: #ff3c00; + color: #ff533a; font-size: 8px; line-height: 8px; height: 8px; diff --git a/platform/commonUI/themes/espresso/res/sass/_constants.scss b/platform/commonUI/themes/espresso/res/sass/_constants.scss index c5a8e30456..1a9f5b4ed0 100644 --- a/platform/commonUI/themes/espresso/res/sass/_constants.scss +++ b/platform/commonUI/themes/espresso/res/sass/_constants.scss @@ -8,7 +8,7 @@ $colorKeySelectedBg: #005177; $colorKeyFg: #fff; $colorInteriorBorder: rgba($colorBodyFg, 0.1); $contrastRatioPercent: 7%; -$basicCr: 2px; +$basicCr: 3px; $controlCr: 3px; $smallCr: 2px; @@ -23,7 +23,7 @@ $contrastInvokeMenuPercent: 20%; // General Colors $colorAlt1: #ffc700; -$colorAlert: #ff3c00; +$colorAlert: #ff533a; $colorIconLink: #49dedb; $colorPausedBg: #c56f01; $colorPausedFg: #fff; @@ -61,13 +61,14 @@ $colorInputIcon: pushBack($colorBodyFg, 15%); // Status colors, mainly used for messaging and item ancillary symbols $colorStatusFg: #ccc; $colorStatusDefault: #ccc; -$colorStatusOk: #60e68e; -$colorStatusCaution: #ffa864; +$colorStatusOk: #44ba53; +$colorStatusCaution: #ff8545; $colorStatusAlert: $colorAlert; $colorProgressBarOuter: rgba(#000, 0.1); $colorProgressBarAmt: $colorKey; $progressBarHOverlay: 15px; $progressBarStripeW: 20px; +$shdwStatusIc: rgba(black, 0.4) 0 1px 2px; // Selects $colorSelectBg: $colorBtnBg; diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 54a9973a4e..5db2792f52 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -600,10 +600,10 @@ mct-container { color: #0099cc; } /* line 34, ../../../../general/res/sass/_icons.scss */ .ui-symbol.icon.alert { - color: #ff3c00; } + color: #ff533a; } /* line 36, ../../../../general/res/sass/_icons.scss */ .ui-symbol.icon.alert:hover { - color: #ff8a66; } + color: #ffaca0; } /* line 40, ../../../../general/res/sass/_icons.scss */ .ui-symbol.icon.major { font-size: 1.65em; } @@ -637,7 +637,7 @@ mct-container { display: none !important; } /* line 76, ../../../../general/res/sass/_icons.scss */ .l-icon-alert:before { - color: #ff3c00; + color: #ff533a; content: "!"; } /* line 13, ../../../../general/res/sass/_limits.scss */ @@ -2347,10 +2347,10 @@ label.checkbox.custom { margin-right: 3px; } /* line 54, ../../../../general/res/sass/controls/_messages.scss */ .status.block.ok .status-indicator { - color: #60e68e; } + color: #44ba53; } /* line 57, ../../../../general/res/sass/controls/_messages.scss */ .status.block.caution .status-indicator { - color: #ffa864; } + color: #ff8545; } /* line 60, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; @@ -2400,8 +2400,8 @@ label.checkbox.custom { padding: 10px; } /* line 88, ../../../../general/res/sass/controls/_messages.scss */ .message.error { - background-color: rgba(255, 60, 0, 0.3); - color: #ff8a66; } + background-color: rgba(255, 83, 58, 0.3); + color: #ffaca0; } /* line 94, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner { @@ -2507,40 +2507,145 @@ label.checkbox.custom { opacity: 1; } /* line 156, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok { - background-color: #189543; + background-color: #1b4a21; color: #fff; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok:hover { - background-color: #1ec256; } + background-color: #296f32; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action { - background-color: #11692f; } + background-color: #0d2510; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action:hover { - background-color: #189543; } + background-color: #1b4a21; } /* line 159, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution { - background-color: #ca5900; + background-color: #ab3b00; color: #fff; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution:hover { - background-color: #fd6f00; } + background-color: #de4c00; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution .s-action { - background-color: #974200; } + background-color: #782900; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution .s-action:hover { - background-color: #ca5900; } + background-color: #ab3b00; } -/* line 168, ../../../../general/res/sass/controls/_messages.scss */ -.t-message .overlay .type-icon.message-type.abs, .t-message .overlay .s-menu span.type-icon.message-type.l-click-area, .s-menu .t-message .overlay span.type-icon.message-type.l-click-area { +/* Paths: + t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message > + message-type > (icon) + message-contents > + top-bar > + title + hint + editor > + (if displaying list of messages) + ul > li > l-message > + ... same as above + bottom-bar +*/ +/* line 201, ../../../../general/res/sass/controls/_messages.scss */ +.l-message { + display: -webkit-flex; + display: flex; + -webkit-flex-direction: row; + flex-direction: row; } + /* line 204, ../../../../general/res/sass/controls/_messages.scss */ + .l-message .type-icon.message-type { + -webkit-flex: 0 1 auto; + flex: 0 1 auto; + position: relative; } + /* line 209, ../../../../general/res/sass/controls/_messages.scss */ + .l-message .message-contents { + -webkit-flex: 1 1 auto; + flex: 1 1 auto; + margin-left: 25px; + position: relative; } + /* line 215, ../../../../general/res/sass/controls/_messages.scss */ + .l-message .message-contents .top-bar, + .l-message .message-contents .message-body { + margin-bottom: 20px; } + +/* line 165, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-single .type-icon.message-type { + text-shadow: rgba(255, 255, 255, 0.8) 0 0px 5px; + color: #ccc; font-size: 80px; - opacity: 0.5; - right: auto; + padding-left: 1px; width: 80px; } -/* line 175, ../../../../general/res/sass/controls/_messages.scss */ -.t-message .overlay .message-contents.abs, .t-message .overlay .s-menu span.message-contents.l-click-area, .s-menu .t-message .overlay span.message-contents.l-click-area { - left: 105px; } + /* line 167, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .type-icon.message-type:before { + content: "\e608"; } +/* line 174, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-single .message-severity-info .type-icon.message-type { + color: #44ba53; } + /* line 175, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .message-severity-info .type-icon.message-type:before { + content: "\e608"; } +/* line 178, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-single .message-severity-alert .type-icon.message-type { + color: #ff8545; } + /* line 179, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .message-severity-alert .type-icon.message-type:before { + content: "\e610"; } +/* line 182, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-single .message-severity-error .type-icon.message-type { + color: #ff533a; } + /* line 183, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .message-severity-error .type-icon.message-type:before { + content: "\21"; } + +/* line 165, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .type-icon.message-type { + text-shadow: rgba(255, 255, 255, 0.8) 0 0px 5px; + color: #ccc; + font-size: 32px; + padding-left: 1px; + width: 32px; } + /* line 167, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .type-icon.message-type:before { + content: "\e608"; } +/* line 174, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .message-severity-info .type-icon.message-type { + color: #44ba53; } + /* line 175, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-severity-info .type-icon.message-type:before { + content: "\e608"; } +/* line 178, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .message-severity-alert .type-icon.message-type { + color: #ff8545; } + /* line 179, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-severity-alert .type-icon.message-type:before { + content: "\e610"; } +/* line 182, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .message-severity-error .type-icon.message-type { + color: #ff533a; } + /* line 183, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-severity-error .type-icon.message-type:before { + content: "\21"; } +/* line 232, ../../../../general/res/sass/controls/_messages.scss */ +.t-message-list .message-contents .l-message { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background: rgba(102, 102, 102, 0.1); + margin-bottom: 5px; + margin-right: 10px; + padding: 10px; } + /* line 240, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .message-contents { + font-size: 0.9em; } + /* line 242, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .message-contents .message-action { + color: #999999; } + /* line 243, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .message-contents .bottom-bar { + text-align: left; } + /* line 246, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .top-bar, + .t-message-list .message-contents .l-message .message-body { + margin-bottom: 10px; } /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { @@ -4127,134 +4232,148 @@ span.req { * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 23, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .blocker { - background: rgba(0, 0, 0, 0.7); - z-index: 100; } -/* line 27, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .clk-icon.close { - font-size: 0.8rem; - position: absolute; - top: 10px; - right: 10px; - bottom: auto; - left: auto; - z-index: 100; } -/* line 36, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay > .holder { - background-color: #fcfcfc; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #666; - display: inline-block; - -moz-border-radius: 12px; - -webkit-border-radius: 12px; - border-radius: 12px; - color: #666; - top: 50%; - right: auto; - bottom: auto; - left: 50%; - -moz-transform: translateX(-50%) translateY(-50%); - -ms-transform: translateX(-50%) translateY(-50%); - -webkit-transform: translateX(-50%) translateY(-50%); - transform: translateX(-50%) translateY(-50%); - height: 70%; - width: 50%; - min-height: 300px; - max-height: 800px; - min-width: 600px; - max-width: 1000px; - z-index: 101; } - /* line 53, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay > .holder > .contents { - top: 25px; - right: 25px; - bottom: 25px; - left: 25px; } -/* line 62, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .title { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 1.2em; - line-height: 120%; - margin-bottom: 5px; } -/* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .hint { - color: #999999; } -/* line 73, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .top-bar { - height: 60px; } -/* line 77, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .editor { - top: 70px; - bottom: 40px; - left: 0; - right: 0; - overflow: auto; } +/* line 22, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay { + font-size: 90%; } + /* line 24, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .blocker { + background: rgba(0, 0, 0, 0.7); + z-index: 100; } + /* line 28, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .clk-icon.close { + font-size: 0.8rem; + position: absolute; + top: 10px; + right: 10px; + bottom: auto; + left: auto; + z-index: 100; } + /* line 37, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay > .holder { + background-color: #fcfcfc; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #666; + display: inline-block; + -moz-border-radius: 12px; + -webkit-border-radius: 12px; + border-radius: 12px; + color: #666; + top: 50%; + right: auto; + bottom: auto; + left: 50%; + -moz-transform: translateX(-50%) translateY(-50%); + -ms-transform: translateX(-50%) translateY(-50%); + -webkit-transform: translateX(-50%) translateY(-50%); + transform: translateX(-50%) translateY(-50%); + height: 70%; + width: 50%; + min-height: 300px; + max-height: 800px; + min-width: 600px; + max-width: 1000px; + z-index: 101; } + /* line 54, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay > .holder > .contents { + top: 25px; + right: 25px; + bottom: 25px; + left: 25px; } + /* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 1.2em; + line-height: 120%; + margin-bottom: 5px; } + /* line 76, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .hint { + color: #999999; } + /* line 80, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .abs.top-bar, .overlay .s-menu span.top-bar.l-click-area, .s-menu .overlay span.top-bar.l-click-area { + height: 60px; } /* line 84, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .editor .field.l-med input[type='text'] { + .overlay .abs.editor, .overlay .s-menu span.editor.l-click-area, .s-menu .overlay span.editor.l-click-area, + .overlay .abs.message-body, + .overlay .s-menu span.message-body.l-click-area, + .s-menu .overlay span.message-body.l-click-area { + top: 70px; + bottom: 34px; + left: 0; + right: 0; + overflow: auto; } + /* line 92, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .abs.editor .field.l-med input[type='text'], .overlay .s-menu span.editor.l-click-area .field.l-med input[type='text'], .s-menu .overlay span.editor.l-click-area .field.l-med input[type='text'], + .overlay .abs.message-body .field.l-med input[type='text'], + .overlay .s-menu span.message-body.l-click-area .field.l-med input[type='text'], + .s-menu .overlay span.message-body.l-click-area .field.l-med input[type='text'] { + width: 100%; } + /* line 98, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar { + text-align: right; } + /* line 100, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { + font-size: 95%; + height: 24px; + line-height: 24px; + margin-left: 5px; + padding: 0 15px; } + /* line 102, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { + background-color: #969696; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + display: inline-block; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + -moz-transition: background, 0.25s; + -o-transition: background, 0.25s; + -webkit-transition: background, 0.25s; + transition: background, 0.25s; + text-shadow: none; } + /* line 289, ../../../../general/res/sass/_mixins.scss */ + .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu:not(.major) .icon { + color: #fff; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 294, ../../../../general/res/sass/_mixins.scss */ + .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover { + background: #7d7d7d; } + /* line 296, ../../../../general/res/sass/_mixins.scss */ + .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { + color: white; } } + /* line 110, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar .s-btn:first-child, .overlay .bottom-bar .s-menu:first-child { + margin-left: 0; } + /* line 117, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .abs.bottom-bar, .overlay .s-menu span.bottom-bar.l-click-area, .s-menu .overlay span.bottom-bar.l-click-area { + top: auto; + right: 0; + bottom: 0; + left: 0; + overflow: visible; + height: 24px; } + /* line 127, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .l-progress-bar { + display: block; + height: 15px; + line-height: 15px; + margin: .5em 0; width: 100%; } -/* line 90, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .l-progress-bar { - display: block; - height: 15px; - line-height: 15px; - margin: .5em 0; - width: 100%; } -/* line 99, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .bottom-bar { - top: auto; - right: 0; - bottom: 0; - left: 0; - overflow: visible; - height: 30px; - text-align: right; } - /* line 108, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu { - font-size: 95%; - height: 30px; - line-height: 30px; - margin-left: 5px; - padding: 0 15px; } - /* line 110, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu:not(.major) { - background-color: #969696; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #fff; - display: inline-block; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - -moz-transition: background, 0.25s; - -o-transition: background, 0.25s; - -webkit-transition: background, 0.25s; - transition: background, 0.25s; - text-shadow: none; } - /* line 289, ../../../../general/res/sass/_mixins.scss */ - .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu:not(.major) .icon { - color: #fff; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 294, ../../../../general/res/sass/_mixins.scss */ - .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover { - background: #7d7d7d; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ - .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu:not(.major):not(.disabled):hover > .icon { - color: white; } } -/* line 128, ../../../../general/res/sass/overlay/_overlay.scss */ +/* line 137, ../../../../general/res/sass/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { min-height: 275px; height: 275px; } @@ -4307,7 +4426,7 @@ span.req { /* line 50, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder .editor .form .form-row > .label:after { float: none; } - /* line 56, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 57, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder .contents .top-bar, .overlay > .holder .contents .editor, .overlay > .holder .contents .bottom-bar { @@ -4320,12 +4439,12 @@ span.req { margin-bottom: 20px; position: relative; } - /* line 66, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 68, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { height: auto; max-height: 100%; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 74, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 76, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder .contents .bottom-bar { text-align: center; } } /***************************************************************************** @@ -4437,7 +4556,7 @@ ul.tree { /* line 90, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label .type-icon .icon.l-icon-alert, .search-result-item .label .type-icon .icon.l-icon-alert { - color: #ff3c00; + color: #ff533a; font-size: 8px; line-height: 8px; height: 8px; diff --git a/platform/commonUI/themes/snow/res/sass/_constants.scss b/platform/commonUI/themes/snow/res/sass/_constants.scss index cd13385d6b..ea9d8437f5 100644 --- a/platform/commonUI/themes/snow/res/sass/_constants.scss +++ b/platform/commonUI/themes/snow/res/sass/_constants.scss @@ -23,7 +23,7 @@ $contrastInvokeMenuPercent: 40%; // General Colors $colorAlt1: #ff6600; -$colorAlert: #ff3c00; +$colorAlert: #ff533a; $colorIconLink: #49dedb; $colorPausedBg: #ff9900; $colorPausedFg: #fff; @@ -61,13 +61,14 @@ $colorInputIcon: pushBack($colorBodyFg, 25%); // Status colors, mainly used for messaging and item ancillary symbols $colorStatusFg: #fff; $colorStatusDefault: #ccc; -$colorStatusOk: #60e68e; -$colorStatusCaution: #ffa864; +$colorStatusOk: #44ba53; +$colorStatusCaution: #ff8545; $colorStatusAlert: $colorAlert; $colorProgressBarOuter: rgba(#000, 0.1); $colorProgressBarAmt: #0a0; $progressBarHOverlay: 15px; $progressBarStripeW: 20px; +$shdwStatusIc: rgba(white, 0.8) 0 0px 5px; // Selects $colorSelectBg: #ddd; diff --git a/testing/dialogTest/src/DialogLaunchController.js b/testing/dialogTest/src/DialogLaunchController.js index 7f51a91329..917cb4bc4c 100644 --- a/testing/dialogTest/src/DialogLaunchController.js +++ b/testing/dialogTest/src/DialogLaunchController.js @@ -115,31 +115,71 @@ define( ], messages: [] }; + + + function getExampleActionText() { + var actionTexts = [ + "Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.", + "Eros turpis, pulvinar turpis eros eu", + "Lundium nascetur a, lectus montes ac, parturient in natoque, duis risus risus pulvinar pid rhoncus, habitasse auctor natoque!" + ]; + return actionTexts[Math.floor(Math.random()*3)]; + } + + function getExampleActions() { + var actions = [ + { + label: "Try Again", + action: function () { + $log.debug("Try Again pressed"); + } + }, + { + label: "Remove", + action: function () { + $log.debug("Remove pressed"); + } + }, + { + label: "Cancel", + action: function () { + $log.debug("Cancel pressed"); + } + } + ]; + + // Randomly remove some actions off the top; leave at least one + actions.splice(0,Math.floor(Math.random() * actions.length)); + + return actions; + } + + function getExampleSeverity() { + var severities = [ + messageSeverity.INFO, + messageSeverity.ALERT, + messageSeverity.ERROR + ]; + return severities[Math.floor(Math.random() * severities.length)]; + } function createMessage (messageNumber) { var messageModel = { - ngModel: { - dialog: { - title: "Message " + messageNumber, - severity: messageSeverity.INFO, - actions: [ - { - label: "Cancel Duplication", - action: function () { - $log.debug("Cancel Duplication pressed"); - $log.debug("Message should be dismissed"); - } - } - ] + ngModel: { + dialog: { + title: "Message Title " + messageNumber, + actionText: getExampleActionText(), + severity: getExampleSeverity(), + actions: getExampleActions() + } } - } }; return messageModel; } if (dialogService.showMessageList(model)) { //Do processing here - for (var i = 0; i < 4; i++) { + for (var i = 0; i < 10; i++) { model.messages.push(createMessage(i)); } } else { From 12a94f828a3a806be67b9d89cd9f45cf1ed9865e Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 13:14:30 -0700 Subject: [PATCH 034/488] Added some more semicolons --- platform/commonUI/notification/src/NotificationService.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index ecdb8d6ddd..7d0db198da 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -159,7 +159,8 @@ define( NotificationService.prototype.setActiveNotification = function (notification) { - var that = this; + var that = this, + timeout; this.active.notification = notification; /* If autoDismiss has been specified, setup a timeout to @@ -170,7 +171,7 @@ define( */ if (notification && (notification.autoDismiss || this.selectNextNotification())) { - var timeout = isNaN(notification.autoDismiss) ? + timeout = isNaN(notification.autoDismiss) ? this.DEFAULT_AUTO_DISMISS : notification.autoDismiss; @@ -188,12 +189,13 @@ define( * @private */ NotificationService.prototype.selectNextNotification = function () { + var notification; /* Loop through the notifications queue and find the first one that has not already been minimized (manually or otherwise). */ for (var i=0; i< this.notifications.length; i++) { - var notification = this.notifications[i]; + notification = this.notifications[i]; if (!notification.minimized && notification!= this.activeNotification) { From fc8630dbc633074211a56061a9d2850ba1e882da Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 13:15:31 -0700 Subject: [PATCH 035/488] Moved some vars --- platform/commonUI/notification/src/NotificationService.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 7d0db198da..97761cd227 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -189,12 +189,13 @@ define( * @private */ NotificationService.prototype.selectNextNotification = function () { - var notification; + var notification, + i=0; /* Loop through the notifications queue and find the first one that has not already been minimized (manually or otherwise). */ - for (var i=0; i< this.notifications.length; i++) { + for (; i< this.notifications.length; i++) { notification = this.notifications[i]; if (!notification.minimized From df590107cbd0c721fe3c5a1401c1fb20ad212d22 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 13:17:12 -0700 Subject: [PATCH 036/488] Moved some more vars --- .../notification/src/NotificationService.js | 2 +- .../test/NotificationServiceSpec.js | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 97761cd227..1a521ad4da 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -199,7 +199,7 @@ define( notification = this.notifications[i]; if (!notification.minimized - && notification!= this.activeNotification) { + && notification!== this.activeNotification) { return notification; } diff --git a/platform/commonUI/notification/test/NotificationServiceSpec.js b/platform/commonUI/notification/test/NotificationServiceSpec.js index 96994f26d2..ca4d5eb2dd 100644 --- a/platform/commonUI/notification/test/NotificationServiceSpec.js +++ b/platform/commonUI/notification/test/NotificationServiceSpec.js @@ -168,15 +168,16 @@ define( }); it("auto-minimizes errors when a number of them arrive in" + " short succession ", function() { - var activeNotification; - var error2 = { - title: "Second Mock Error Notification", - severity: MessageSeverity.ERROR - }; - var error3 = { - title: "Third Mock Error Notification", - severity: MessageSeverity.ERROR - }; + var activeNotification, + error2 = { + title: "Second Mock Error Notification", + severity: MessageSeverity.ERROR + }; + error3 = { + title: "Third Mock Error Notification", + severity: MessageSeverity.ERROR + }; + //First pre-load with a success message notificationService.notify(errorModel); //Then notify of a third error From 5e713f279bebd2ddf604017e5d22c370edefc018 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 13:18:47 -0700 Subject: [PATCH 037/488] Moved some more vars --- .../commonUI/notification/test/NotificationServiceSpec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/commonUI/notification/test/NotificationServiceSpec.js b/platform/commonUI/notification/test/NotificationServiceSpec.js index ca4d5eb2dd..1992865c8e 100644 --- a/platform/commonUI/notification/test/NotificationServiceSpec.js +++ b/platform/commonUI/notification/test/NotificationServiceSpec.js @@ -172,12 +172,12 @@ define( error2 = { title: "Second Mock Error Notification", severity: MessageSeverity.ERROR - }; + }, error3 = { title: "Third Mock Error Notification", severity: MessageSeverity.ERROR }; - + //First pre-load with a success message notificationService.notify(errorModel); //Then notify of a third error From ba614fe2d6757184366cced7ccd14087f2437a4e Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 13:46:42 -0700 Subject: [PATCH 038/488] Trying to sort out error --- bundles.json | 2 +- platform/commonUI/notification/bundle.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bundles.json b/bundles.json index 9d6687c4f8..56dbadc44a 100644 --- a/bundles.json +++ b/bundles.json @@ -5,6 +5,7 @@ "platform/commonUI/about", "platform/commonUI/browse", "platform/commonUI/edit", + "platform/commonUI/notification", "platform/commonUI/dialog", "platform/commonUI/general", "platform/commonUI/inspect", @@ -26,7 +27,6 @@ "platform/policy", "platform/entanglement", "platform/search", - "platform/commonUI/notification", "example/imagery", "example/eventGenerator", diff --git a/platform/commonUI/notification/bundle.json b/platform/commonUI/notification/bundle.json index 56087d9094..6c1ac21d88 100644 --- a/platform/commonUI/notification/bundle.json +++ b/platform/commonUI/notification/bundle.json @@ -10,8 +10,7 @@ { "key": "notificationService", "implementation": "NotificationService.js", - "depends": [ "$log", "$timeout", "messageSeverity", - "DEFAULT_AUTO_DISMISS" ] + "depends": [ "$timeout", "DEFAULT_AUTO_DISMISS" ] } ] } From 5ff3c71523e92bcb2e1734985013bbc1ebf1dfbd Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 13:56:59 -0700 Subject: [PATCH 039/488] Fixed the Banner Controller which was not returning reference to controller --- platform/commonUI/general/src/controllers/BannerController.js | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index 1c6193e061..8f1128df1e 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -28,4 +28,5 @@ define( function BannerController($scope, notificationService){ $scope.activeNotification = notificationService.active.Notification; } + return BannerController; }); \ No newline at end of file From 82670584875a38f639a815abf9345a17f6222790 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 8 Oct 2015 15:08:26 -0700 Subject: [PATCH 040/488] Begun integration of Notifications framework with Charles' code --- .../general/res/templates/message-banner.html | 28 +++++++---- .../src/controllers/BannerController.js | 2 +- platform/commonUI/notification/bundle.json | 2 +- .../notification/src/NotificationService.js | 27 ++++++---- testing/dialogTest/bundle.json | 15 ++++++ .../dialogTest/res/notification-launch.html | 9 ++++ .../src/NotificationLaunchController.js | 44 ++++++++++++++++ .../src/NotificationLaunchIndicator.js | 50 +++++++++++++++++++ 8 files changed, 155 insertions(+), 22 deletions(-) create mode 100644 testing/dialogTest/res/notification-launch.html create mode 100644 testing/dialogTest/src/NotificationLaunchController.js create mode 100644 testing/dialogTest/src/NotificationLaunchIndicator.js diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html index 5091f81548..2e53c2c94b 100644 --- a/platform/commonUI/general/res/templates/message-banner.html +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -1,12 +1,18 @@ -
- +
+
+ + + +
\ No newline at end of file diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index 8f1128df1e..b1f26eddce 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -26,7 +26,7 @@ define( function () { "use strict"; function BannerController($scope, notificationService){ - $scope.activeNotification = notificationService.active.Notification; + $scope.active = notificationService.active; } return BannerController; }); \ No newline at end of file diff --git a/platform/commonUI/notification/bundle.json b/platform/commonUI/notification/bundle.json index 6c1ac21d88..6f793e7ee4 100644 --- a/platform/commonUI/notification/bundle.json +++ b/platform/commonUI/notification/bundle.json @@ -3,7 +3,7 @@ "constants": [ { "key": "DEFAULT_AUTO_DISMISS", - "value": 2000 + "value": 3000 } ], "services": [ diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 1a521ad4da..8ecf2ad0b4 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -93,8 +93,7 @@ define( * A context in which to hold the active notification and a * handle to its timeout. */ - this.active = { - }; + this.active = {}; } /** @@ -113,7 +112,7 @@ define( */ NotificationService.prototype.success = function (notification) { notification.autoDismiss = notification.autoDismiss || true; - NotificationService.prototype.notify(notification); + this.notify(notification); }; /** @@ -126,7 +125,12 @@ define( NotificationService.prototype.notify = function (notification) { /*var notification = new Notification(model), that=this; */ - var that = this; + var that = this, + timeout; + + if (notification.autoDismiss === true){ + notification.autoDismiss = this.DEFAULT_AUTO_DISMISS; + } this.notifications.push(notification); /* @@ -145,9 +149,12 @@ define( This notifcation has been added to queue and will be serviced as soon as possible. */ + timeout = notification.autoDismiss ? + notification.autoDismiss : + this.DEFAULT_AUTO_DISMISS; this.active.timeout = this.$timeout(function () { that.dismissOrMinimize(that.active.notification); - }); + }, timeout); } }; @@ -171,9 +178,9 @@ define( */ if (notification && (notification.autoDismiss || this.selectNextNotification())) { - timeout = isNaN(notification.autoDismiss) ? - this.DEFAULT_AUTO_DISMISS : - notification.autoDismiss; + timeout = notification.autoDismiss ? + notification.autoDismiss : + this.DEFAULT_AUTO_DISMISS; this.active.timeout = this.$timeout(function () { that.dismissOrMinimize(notification); @@ -258,5 +265,7 @@ define( this.dismiss(notification); } }; + return NotificationService; - }); \ No newline at end of file + } +); \ No newline at end of file diff --git a/testing/dialogTest/bundle.json b/testing/dialogTest/bundle.json index 36ec16e048..095f668c0f 100644 --- a/testing/dialogTest/bundle.json +++ b/testing/dialogTest/bundle.json @@ -4,6 +4,10 @@ { "key": "dialogLaunchTemplate", "templateUrl": "dialog-launch.html" + }, + { + "key": "notificationLaunchTemplate", + "templateUrl": "notification-launch.html" } ], "controllers": [ @@ -17,11 +21,22 @@ "$log", "messageSeverity" ] + }, + { + "key": "NotificationLaunchController", + "implementation": "NotificationLaunchController.js", + "depends": [ + "$scope", + "notificationService" + ] } ], "indicators": [ { "implementation": "DialogLaunchIndicator.js" + }, + { + "implementation": "NotificationLaunchIndicator.js" } ] } diff --git a/testing/dialogTest/res/notification-launch.html b/testing/dialogTest/res/notification-launch.html new file mode 100644 index 0000000000..73bc7c524f --- /dev/null +++ b/testing/dialogTest/res/notification-launch.html @@ -0,0 +1,9 @@ + + + + Success | + Error | + Progress + + Notifications + \ No newline at end of file diff --git a/testing/dialogTest/src/NotificationLaunchController.js b/testing/dialogTest/src/NotificationLaunchController.js new file mode 100644 index 0000000000..d475df08cb --- /dev/null +++ b/testing/dialogTest/src/NotificationLaunchController.js @@ -0,0 +1,44 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define( + [], + function () { + "use strict"; + + function NotificationLaunchController($scope, notificationService) { + /** + * Success | + Error | + Progress + */ + $scope.newSuccess = function(){ + + notificationService.success({ + title: "Success notification!" + }) + }; + } + return NotificationLaunchController; + } +); diff --git a/testing/dialogTest/src/NotificationLaunchIndicator.js b/testing/dialogTest/src/NotificationLaunchIndicator.js new file mode 100644 index 0000000000..174810d721 --- /dev/null +++ b/testing/dialogTest/src/NotificationLaunchIndicator.js @@ -0,0 +1,50 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define,window*/ + +define( + [], + function () { + "use strict"; + + function NotificationLaunchIndicator() { + + } + + NotificationLaunchIndicator.template = 'notificationLaunchTemplate'; + + NotificationLaunchIndicator.prototype.getGlyph = function () { + return "i"; + }; + NotificationLaunchIndicator.prototype.getGlyphClass = function () { + return 'caution'; + }; + NotificationLaunchIndicator.prototype.getText = function () { + return "Launch notification"; + }; + NotificationLaunchIndicator.prototype.getDescription = function () { + return "Launch notification"; + }; + + return NotificationLaunchIndicator; + } +); From 954fdd5906d8ce547b7e79664dbe8bfcedad6d51 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 8 Oct 2015 19:10:26 -0700 Subject: [PATCH 041/488] [Frontend] Sanding/positioning and Cancel button added to Messasges dialog open #159 open #170 Added Cancel button to Messages dialog in DialogService.js; Important tweaks to layout of messages to allow bottom-bar to align to bottom in singleton dialog view; Revised status colors in theme's constants files; --- .../dialog/res/templates/overlay.html | 3 +- platform/commonUI/dialog/src/DialogService.js | 17 ++- .../commonUI/general/res/sass/_constants.scss | 2 +- .../general/res/sass/controls/_messages.scss | 30 ++++- .../res/sass/mobile/overlay/_overlay.scss | 7 +- .../general/res/sass/overlay/_overlay.scss | 9 +- .../espresso/res/css/theme-espresso.css | 113 +++++++++++------- .../themes/espresso/res/sass/_constants.scss | 6 +- .../themes/snow/res/css/theme-snow.css | 113 +++++++++++------- .../themes/snow/res/sass/_constants.scss | 6 +- .../dialogTest/src/DialogLaunchController.js | 8 +- 11 files changed, 211 insertions(+), 103 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/overlay.html b/platform/commonUI/dialog/res/templates/overlay.html index f2b7c0d9ee..bc19a97b86 100644 --- a/platform/commonUI/dialog/res/templates/overlay.html +++ b/platform/commonUI/dialog/res/templates/overlay.html @@ -22,8 +22,7 @@
- x
diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index 2fc6f41a60..242f311147 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -240,7 +240,7 @@ define( // will handle actual insertion into the DOM this.overlay = this.overlayService.createOverlay( "overlay-blocking-message", - {dialog: dialogModel}, + { dialog: dialogModel }, "t-dialog-sm" ); this.dialogVisible = true; @@ -254,12 +254,25 @@ define( }; DialogService.prototype.showMessageList = function(dialogModel) { + var self = this; + + // Cancel function; this will be passed in to the + // overlay-dialog template and associated with a + // Cancel or X button click + function cancel() { + //deferred.reject(); // Not sure what this does + self.dismiss(); + } + if (this.canShowDialog(dialogModel)) { // Add the overlay using the OverlayService, which // will handle actual insertion into the DOM this.overlay = this.overlayService.createOverlay( "overlay-message-list", - {dialog: dialogModel}, + { + dialog: dialogModel, + cancel: cancel + }, "t-dialog" ); this.dialogVisible = true; diff --git a/platform/commonUI/general/res/sass/_constants.scss b/platform/commonUI/general/res/sass/_constants.scss index 2c425493ea..c3851a715d 100644 --- a/platform/commonUI/general/res/sass/_constants.scss +++ b/platform/commonUI/general/res/sass/_constants.scss @@ -46,7 +46,7 @@ $ueBrowseLeftPaneW: 25%; $ueEditLeftPaneW: 75%; $treeSearchInputBarH: 25px; // Overlay -$ovrTopBarH: 60px; +$ovrTopBarH: 45px; $ovrFooterH: 24px; $overlayMargin: 25px; // Items diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index d537ee8ff1..aa26760c9a 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -167,8 +167,8 @@ &:before { content:"\e608"; } color: $colorStatusDefault; font-size: $iconW; - padding-left: 1px; - width: $iconW; + padding: 1px; + width: $iconW + 2; } .message-severity-info .type-icon.message-type { @@ -201,6 +201,7 @@ .l-message { @include display-flex; @include flex-direction(row); + @include align-items(stretch); .type-icon.message-type { //@include test(red); @include flex(0 1 auto); @@ -223,22 +224,41 @@ // Message as singleton .t-message-single { @include messageBlock(80px); + + @include desktop { + .l-message, + .bottom-bar { + @include absPosDefault(); + } + + .bottom-bar { + top: auto; + height: $ovrFooterH; + } + } } // Messages in list .t-message-list { @include messageBlock(32px); + .message-contents { .l-message { //border-bottom: 1px solid pullForward($colorOvrBg, 20%); @include border-radius($controlCr); background: rgba($colorOvrFg, 0.1); margin-bottom: $interiorMargin; - margin-right: $interiorMarginLg; padding: $interiorMarginLg; + .message-contents, + .bottom-bar { + //@include test(green); + position: relative; + } + .message-contents { font-size: 0.9em; + margin-left: $interiorMarginLg; .message-action { color: pushBack($colorOvrFg, 20%); } .bottom-bar { text-align: left; } } @@ -249,4 +269,8 @@ } } } + + @include desktop { + .message-contents .l-message { margin-right: $interiorMarginLg; } + } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss b/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss index 9fe624b2fd..9fd6721130 100644 --- a/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss @@ -54,9 +54,10 @@ } .contents { - .top-bar, - .editor, - .bottom-bar { + .abs.top-bar, + .abs.editor, + .abs.message-body, + .abs.bottom-bar { //@include test(orange); top: auto; right: auto; bottom: auto; left: auto; height: auto; width: auto; diff --git a/platform/commonUI/general/res/sass/overlay/_overlay.scss b/platform/commonUI/general/res/sass/overlay/_overlay.scss index 305534c0d0..602af62887 100644 --- a/platform/commonUI/general/res/sass/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/overlay/_overlay.scss @@ -83,8 +83,8 @@ .abs.editor, .abs.message-body { - top: $ovrTopBarH + ($interiorMargin * 2); - bottom: $ovrFooterH + $interiorMargin * 2; + top: $ovrTopBarH + $interiorMarginLg; + bottom: $ovrFooterH + $interiorMarginLg; left: 0; right: 0; overflow: auto; @@ -137,6 +137,7 @@ .t-dialog-sm .overlay > .holder { // Used for blocker and in-progress dialogs, modal alerts, etc. //@include test(red); - min-height: 275px; - height: 275px; + $h: 225px; + min-height: $h; + height: $h; } \ No newline at end of file diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 1d4d0e0e79..229e97e44b 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -2399,10 +2399,10 @@ label.checkbox.custom { margin-right: 3px; } /* line 54, ../../../../general/res/sass/controls/_messages.scss */ .status.block.ok .status-indicator { - color: #44ba53; } + color: #62ba72; } /* line 57, ../../../../general/res/sass/controls/_messages.scss */ .status.block.caution .status-indicator { - color: #ff8545; } + color: #ffa66d; } /* line 60, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; @@ -2559,30 +2559,30 @@ label.checkbox.custom { opacity: 1; } /* line 156, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok { - background-color: #1b4a21; + background-color: #285b31; color: #ccc; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok:hover { - background-color: #296f32; } + background-color: #387e44; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action { - background-color: #0d2510; } + background-color: #18381e; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action:hover { - background-color: #1b4a21; } + background-color: #285b31; } /* line 159, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution { - background-color: #ab3b00; + background-color: #d35200; color: #ccc; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution:hover { - background-color: #de4c00; } + background-color: #ff6807; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution .s-action { - background-color: #782900; } + background-color: #a03e00; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution .s-action:hover { - background-color: #ab3b00; } + background-color: #d35200; } /* Paths: t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message > @@ -2602,19 +2602,21 @@ label.checkbox.custom { display: -webkit-flex; display: flex; -webkit-flex-direction: row; - flex-direction: row; } - /* line 204, ../../../../general/res/sass/controls/_messages.scss */ + flex-direction: row; + -webkit-align-items: stretch; + align-items: stretch; } + /* line 205, ../../../../general/res/sass/controls/_messages.scss */ .l-message .type-icon.message-type { -webkit-flex: 0 1 auto; flex: 0 1 auto; position: relative; } - /* line 209, ../../../../general/res/sass/controls/_messages.scss */ + /* line 210, ../../../../general/res/sass/controls/_messages.scss */ .l-message .message-contents { -webkit-flex: 1 1 auto; flex: 1 1 auto; margin-left: 25px; position: relative; } - /* line 215, ../../../../general/res/sass/controls/_messages.scss */ + /* line 216, ../../../../general/res/sass/controls/_messages.scss */ .l-message .message-contents .top-bar, .l-message .message-contents .message-body { margin-bottom: 20px; } @@ -2624,80 +2626,104 @@ label.checkbox.custom { text-shadow: rgba(0, 0, 0, 0.4) 0 1px 2px; color: #ccc; font-size: 80px; - padding-left: 1px; - width: 80px; } + padding: 1px; + width: 82px; } /* line 167, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .type-icon.message-type:before { content: "\e608"; } /* line 174, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-info .type-icon.message-type { - color: #44ba53; } + color: #62ba72; } /* line 175, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-info .type-icon.message-type:before { content: "\e608"; } /* line 178, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-alert .type-icon.message-type { - color: #ff8545; } + color: #ffa66d; } /* line 179, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-alert .type-icon.message-type:before { content: "\e610"; } /* line 182, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-error .type-icon.message-type { - color: #ff533a; } + color: #d4585c; } /* line 183, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-error .type-icon.message-type:before { content: "\21"; } +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 229, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .l-message, + .t-message-single .bottom-bar { + overflow: hidden; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + width: auto; + height: auto; } + /* line 234, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .bottom-bar { + top: auto; + height: 24px; } } /* line 165, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .type-icon.message-type { text-shadow: rgba(0, 0, 0, 0.4) 0 1px 2px; color: #ccc; font-size: 32px; - padding-left: 1px; - width: 32px; } + padding: 1px; + width: 34px; } /* line 167, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .type-icon.message-type:before { content: "\e608"; } /* line 174, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-info .type-icon.message-type { - color: #44ba53; } + color: #62ba72; } /* line 175, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-info .type-icon.message-type:before { content: "\e608"; } /* line 178, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-alert .type-icon.message-type { - color: #ff8545; } + color: #ffa66d; } /* line 179, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-alert .type-icon.message-type:before { content: "\e610"; } /* line 182, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-error .type-icon.message-type { - color: #ff533a; } + color: #d4585c; } /* line 183, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-error .type-icon.message-type:before { content: "\21"; } -/* line 232, ../../../../general/res/sass/controls/_messages.scss */ +/* line 246, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message { -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; background: rgba(230, 230, 230, 0.1); margin-bottom: 5px; - margin-right: 10px; padding: 10px; } - /* line 240, ../../../../general/res/sass/controls/_messages.scss */ + /* line 253, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .message-contents, + .t-message-list .message-contents .l-message .bottom-bar { + position: relative; } + /* line 259, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents { - font-size: 0.9em; } - /* line 242, ../../../../general/res/sass/controls/_messages.scss */ + font-size: 0.9em; + margin-left: 10px; } + /* line 262, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents .message-action { color: #b3b3b3; } - /* line 243, ../../../../general/res/sass/controls/_messages.scss */ + /* line 263, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents .bottom-bar { text-align: left; } - /* line 246, ../../../../general/res/sass/controls/_messages.scss */ + /* line 266, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .top-bar, .t-message-list .message-contents .l-message .message-body { margin-bottom: 10px; } +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 274, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message { + margin-right: 10px; } } /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { @@ -4374,13 +4400,13 @@ span.req { color: #b3b3b3; } /* line 80, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .abs.top-bar, .overlay .s-menu span.top-bar.l-click-area, .s-menu .overlay span.top-bar.l-click-area { - height: 60px; } + height: 45px; } /* line 84, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .abs.editor, .overlay .s-menu span.editor.l-click-area, .s-menu .overlay span.editor.l-click-area, .overlay .abs.message-body, .overlay .s-menu span.message-body.l-click-area, .s-menu .overlay span.message-body.l-click-area { - top: 70px; + top: 55px; bottom: 34px; left: 0; right: 0; @@ -4461,8 +4487,8 @@ span.req { /* line 137, ../../../../general/res/sass/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { - min-height: 275px; - height: 275px; } + min-height: 225px; + height: 225px; } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ @@ -4513,9 +4539,16 @@ span.req { .overlay > .holder .editor .form .form-row > .label:after { float: none; } /* line 57, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder .contents .top-bar, - .overlay > .holder .contents .editor, - .overlay > .holder .contents .bottom-bar { + .overlay > .holder .contents .abs.top-bar, .overlay > .holder .contents .s-menu span.top-bar.l-click-area, .s-menu .overlay > .holder .contents span.top-bar.l-click-area, + .overlay > .holder .contents .abs.editor, + .overlay > .holder .contents .s-menu span.editor.l-click-area, + .s-menu .overlay > .holder .contents span.editor.l-click-area, + .overlay > .holder .contents .abs.message-body, + .overlay > .holder .contents .s-menu span.message-body.l-click-area, + .s-menu .overlay > .holder .contents span.message-body.l-click-area, + .overlay > .holder .contents .abs.bottom-bar, + .overlay > .holder .contents .s-menu span.bottom-bar.l-click-area, + .s-menu .overlay > .holder .contents span.bottom-bar.l-click-area { top: auto; right: auto; bottom: auto; @@ -4525,12 +4558,12 @@ span.req { margin-bottom: 20px; position: relative; } - /* line 68, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 69, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { height: auto; max-height: 100%; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 76, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 77, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder .contents .bottom-bar { text-align: center; } } /***************************************************************************** diff --git a/platform/commonUI/themes/espresso/res/sass/_constants.scss b/platform/commonUI/themes/espresso/res/sass/_constants.scss index 1a9f5b4ed0..896241de5c 100644 --- a/platform/commonUI/themes/espresso/res/sass/_constants.scss +++ b/platform/commonUI/themes/espresso/res/sass/_constants.scss @@ -61,9 +61,9 @@ $colorInputIcon: pushBack($colorBodyFg, 15%); // Status colors, mainly used for messaging and item ancillary symbols $colorStatusFg: #ccc; $colorStatusDefault: #ccc; -$colorStatusOk: #44ba53; -$colorStatusCaution: #ff8545; -$colorStatusAlert: $colorAlert; +$colorStatusOk: #62ba72; +$colorStatusCaution: #ffa66d; +$colorStatusAlert: #d4585c; $colorProgressBarOuter: rgba(#000, 0.1); $colorProgressBarAmt: $colorKey; $progressBarHOverlay: 15px; diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 5db2792f52..0ae366cd0e 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -2347,10 +2347,10 @@ label.checkbox.custom { margin-right: 3px; } /* line 54, ../../../../general/res/sass/controls/_messages.scss */ .status.block.ok .status-indicator { - color: #44ba53; } + color: #60ba7b; } /* line 57, ../../../../general/res/sass/controls/_messages.scss */ .status.block.caution .status-indicator { - color: #ff8545; } + color: #ffb66c; } /* line 60, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; @@ -2507,30 +2507,30 @@ label.checkbox.custom { opacity: 1; } /* line 156, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok { - background-color: #1b4a21; + background-color: #275a36; color: #fff; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok:hover { - background-color: #296f32; } + background-color: #367e4c; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action { - background-color: #0d2510; } + background-color: #183621; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.ok .s-action:hover { - background-color: #1b4a21; } + background-color: #275a36; } /* line 159, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution { - background-color: #ab3b00; + background-color: #d26a00; color: #fff; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution:hover { - background-color: #de4c00; } + background-color: #ff8306; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution .s-action { - background-color: #782900; } + background-color: #9f5000; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner.caution .s-action:hover { - background-color: #ab3b00; } + background-color: #d26a00; } /* Paths: t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message > @@ -2550,19 +2550,21 @@ label.checkbox.custom { display: -webkit-flex; display: flex; -webkit-flex-direction: row; - flex-direction: row; } - /* line 204, ../../../../general/res/sass/controls/_messages.scss */ + flex-direction: row; + -webkit-align-items: stretch; + align-items: stretch; } + /* line 205, ../../../../general/res/sass/controls/_messages.scss */ .l-message .type-icon.message-type { -webkit-flex: 0 1 auto; flex: 0 1 auto; position: relative; } - /* line 209, ../../../../general/res/sass/controls/_messages.scss */ + /* line 210, ../../../../general/res/sass/controls/_messages.scss */ .l-message .message-contents { -webkit-flex: 1 1 auto; flex: 1 1 auto; margin-left: 25px; position: relative; } - /* line 215, ../../../../general/res/sass/controls/_messages.scss */ + /* line 216, ../../../../general/res/sass/controls/_messages.scss */ .l-message .message-contents .top-bar, .l-message .message-contents .message-body { margin-bottom: 20px; } @@ -2572,80 +2574,104 @@ label.checkbox.custom { text-shadow: rgba(255, 255, 255, 0.8) 0 0px 5px; color: #ccc; font-size: 80px; - padding-left: 1px; - width: 80px; } + padding: 1px; + width: 82px; } /* line 167, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .type-icon.message-type:before { content: "\e608"; } /* line 174, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-info .type-icon.message-type { - color: #44ba53; } + color: #60ba7b; } /* line 175, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-info .type-icon.message-type:before { content: "\e608"; } /* line 178, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-alert .type-icon.message-type { - color: #ff8545; } + color: #ffb66c; } /* line 179, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-alert .type-icon.message-type:before { content: "\e610"; } /* line 182, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-error .type-icon.message-type { - color: #ff533a; } + color: #c96b68; } /* line 183, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-error .type-icon.message-type:before { content: "\21"; } +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 229, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .l-message, + .t-message-single .bottom-bar { + overflow: hidden; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + width: auto; + height: auto; } + /* line 234, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-single .bottom-bar { + top: auto; + height: 24px; } } /* line 165, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .type-icon.message-type { text-shadow: rgba(255, 255, 255, 0.8) 0 0px 5px; color: #ccc; font-size: 32px; - padding-left: 1px; - width: 32px; } + padding: 1px; + width: 34px; } /* line 167, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .type-icon.message-type:before { content: "\e608"; } /* line 174, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-info .type-icon.message-type { - color: #44ba53; } + color: #60ba7b; } /* line 175, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-info .type-icon.message-type:before { content: "\e608"; } /* line 178, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-alert .type-icon.message-type { - color: #ff8545; } + color: #ffb66c; } /* line 179, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-alert .type-icon.message-type:before { content: "\e610"; } /* line 182, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-error .type-icon.message-type { - color: #ff533a; } + color: #c96b68; } /* line 183, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-error .type-icon.message-type:before { content: "\21"; } -/* line 232, ../../../../general/res/sass/controls/_messages.scss */ +/* line 246, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; background: rgba(102, 102, 102, 0.1); margin-bottom: 5px; - margin-right: 10px; padding: 10px; } - /* line 240, ../../../../general/res/sass/controls/_messages.scss */ + /* line 253, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message .message-contents, + .t-message-list .message-contents .l-message .bottom-bar { + position: relative; } + /* line 259, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents { - font-size: 0.9em; } - /* line 242, ../../../../general/res/sass/controls/_messages.scss */ + font-size: 0.9em; + margin-left: 10px; } + /* line 262, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents .message-action { color: #999999; } - /* line 243, ../../../../general/res/sass/controls/_messages.scss */ + /* line 263, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents .bottom-bar { text-align: left; } - /* line 246, ../../../../general/res/sass/controls/_messages.scss */ + /* line 266, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .top-bar, .t-message-list .message-contents .l-message .message-body { margin-bottom: 10px; } +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 274, ../../../../general/res/sass/controls/_messages.scss */ + .t-message-list .message-contents .l-message { + margin-right: 10px; } } /* line 1, ../../../../general/res/sass/controls/_time-controller.scss */ .l-time-controller { @@ -4297,13 +4323,13 @@ span.req { color: #999999; } /* line 80, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .abs.top-bar, .overlay .s-menu span.top-bar.l-click-area, .s-menu .overlay span.top-bar.l-click-area { - height: 60px; } + height: 45px; } /* line 84, ../../../../general/res/sass/overlay/_overlay.scss */ .overlay .abs.editor, .overlay .s-menu span.editor.l-click-area, .s-menu .overlay span.editor.l-click-area, .overlay .abs.message-body, .overlay .s-menu span.message-body.l-click-area, .s-menu .overlay span.message-body.l-click-area { - top: 70px; + top: 55px; bottom: 34px; left: 0; right: 0; @@ -4375,8 +4401,8 @@ span.req { /* line 137, ../../../../general/res/sass/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { - min-height: 275px; - height: 275px; } + min-height: 225px; + height: 225px; } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { /* line 3, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ @@ -4427,9 +4453,16 @@ span.req { .overlay > .holder .editor .form .form-row > .label:after { float: none; } /* line 57, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder .contents .top-bar, - .overlay > .holder .contents .editor, - .overlay > .holder .contents .bottom-bar { + .overlay > .holder .contents .abs.top-bar, .overlay > .holder .contents .s-menu span.top-bar.l-click-area, .s-menu .overlay > .holder .contents span.top-bar.l-click-area, + .overlay > .holder .contents .abs.editor, + .overlay > .holder .contents .s-menu span.editor.l-click-area, + .s-menu .overlay > .holder .contents span.editor.l-click-area, + .overlay > .holder .contents .abs.message-body, + .overlay > .holder .contents .s-menu span.message-body.l-click-area, + .s-menu .overlay > .holder .contents span.message-body.l-click-area, + .overlay > .holder .contents .abs.bottom-bar, + .overlay > .holder .contents .s-menu span.bottom-bar.l-click-area, + .s-menu .overlay > .holder .contents span.bottom-bar.l-click-area { top: auto; right: auto; bottom: auto; @@ -4439,12 +4472,12 @@ span.req { margin-bottom: 20px; position: relative; } - /* line 68, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 69, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .t-dialog-sm .overlay > .holder { height: auto; max-height: 100%; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 76, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + /* line 77, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ .overlay > .holder .contents .bottom-bar { text-align: center; } } /***************************************************************************** diff --git a/platform/commonUI/themes/snow/res/sass/_constants.scss b/platform/commonUI/themes/snow/res/sass/_constants.scss index ea9d8437f5..02e0f7ab11 100644 --- a/platform/commonUI/themes/snow/res/sass/_constants.scss +++ b/platform/commonUI/themes/snow/res/sass/_constants.scss @@ -61,9 +61,9 @@ $colorInputIcon: pushBack($colorBodyFg, 25%); // Status colors, mainly used for messaging and item ancillary symbols $colorStatusFg: #fff; $colorStatusDefault: #ccc; -$colorStatusOk: #44ba53; -$colorStatusCaution: #ff8545; -$colorStatusAlert: $colorAlert; +$colorStatusOk: #60ba7b; +$colorStatusCaution: #ffb66c; +$colorStatusAlert: #c96b68; $colorProgressBarOuter: rgba(#000, 0.1); $colorProgressBarAmt: #0a0; $progressBarHOverlay: 15px; diff --git a/testing/dialogTest/src/DialogLaunchController.js b/testing/dialogTest/src/DialogLaunchController.js index 917cb4bc4c..7946463abe 100644 --- a/testing/dialogTest/src/DialogLaunchController.js +++ b/testing/dialogTest/src/DialogLaunchController.js @@ -115,8 +115,7 @@ define( ], messages: [] }; - - + function getExampleActionText() { var actionTexts = [ "Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.", @@ -177,6 +176,11 @@ define( return messageModel; } + function dismiss() { + scope.$destroy(); + element.remove(); + } + if (dialogService.showMessageList(model)) { //Do processing here for (var i = 0; i < 10; i++) { From 2b97d61d6cf840e2f79023e38258b03d87bf00d7 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 9 Oct 2015 10:59:36 -0700 Subject: [PATCH 042/488] Further integration work --- .../general/res/templates/message-banner.html | 22 +++++++++------- .../src/controllers/BannerController.js | 3 +++ .../notification/src/MessageSeverity.js | 3 ++- .../notification/src/NotificationService.js | 3 ++- .../src/NotificationLaunchController.js | 26 +++++++++++++++++-- 5 files changed, 43 insertions(+), 14 deletions(-) diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html index 2e53c2c94b..a896b08702 100644 --- a/platform/commonUI/general/res/templates/message-banner.html +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -3,16 +3,18 @@ class="l-message-banner s-message-banner"> - - +
\ No newline at end of file diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index b1f26eddce..214283633c 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -27,6 +27,9 @@ define( "use strict"; function BannerController($scope, notificationService){ $scope.active = notificationService.active; + $scope.dismiss = function(notification){ + notificationService.dismissOrMinimize(notification); + } } return BannerController; }); \ No newline at end of file diff --git a/platform/commonUI/notification/src/MessageSeverity.js b/platform/commonUI/notification/src/MessageSeverity.js index 39e8e6d8d0..5708dd9554 100644 --- a/platform/commonUI/notification/src/MessageSeverity.js +++ b/platform/commonUI/notification/src/MessageSeverity.js @@ -5,6 +5,7 @@ define(function(){ return { SUCCESS: 0, - ERROR: 1 + INFO: 1, + ERROR: 2 }; }); \ No newline at end of file diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 8ecf2ad0b4..078dde51d5 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -112,6 +112,7 @@ define( */ NotificationService.prototype.success = function (notification) { notification.autoDismiss = notification.autoDismiss || true; + notification.severity = MessageSeverity.SUCCESS; this.notify(notification); }; @@ -206,7 +207,7 @@ define( notification = this.notifications[i]; if (!notification.minimized - && notification!== this.activeNotification) { + && notification!== this.active.notification) { return notification; } diff --git a/testing/dialogTest/src/NotificationLaunchController.js b/testing/dialogTest/src/NotificationLaunchController.js index d475df08cb..0c1aaed657 100644 --- a/testing/dialogTest/src/NotificationLaunchController.js +++ b/testing/dialogTest/src/NotificationLaunchController.js @@ -22,8 +22,8 @@ /*global define*/ define( - [], - function () { + ['../../../platform/commonUI/notification/src/MessageSeverity'], + function (MessageSeverity) { "use strict"; function NotificationLaunchController($scope, notificationService) { @@ -38,6 +38,28 @@ define( title: "Success notification!" }) }; + + $scope.newError = function(){ + + notificationService.notify({ + title: "Error notification!", + severity: MessageSeverity.ERROR + }) + }; + + $scope.newProgress = function(){ + + var notification = { + title: "Progress notification!", + severity: MessageSeverity.INFO, + progress: 0, + progressUnknown: true + + }; + + notificationService.notify(notification) + }; + } return NotificationLaunchController; } From 85300d374369a850aa3d5e5682ab4c8cc2816ab5 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 9 Oct 2015 11:57:47 -0700 Subject: [PATCH 043/488] Refactored DialogService a little --- .../dialog/res/templates/message.html | 12 ++--- .../res/templates/overlay-message-list.html | 2 +- platform/commonUI/dialog/src/DialogService.js | 50 ++----------------- .../general/res/templates/progress-bar.html | 10 ++-- .../notification/src/NotificationService.js | 24 ++++----- .../dialogTest/src/DialogLaunchController.js | 24 ++++++--- .../src/NotificationLaunchController.js | 5 -- 7 files changed, 44 insertions(+), 83 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index d9fe79edfe..5d78e408d8 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -1,20 +1,20 @@ -
+
-
{{ngModel.dialog.title}}
-
{{ngModel.dialog.hint}}
+
{{ngModel.title}}
+
{{ngModel.hint}}
- {{ngModel.dialog.actionText}} message-severity-{{ngModel.dialog.severity}} + {{ngModel.actionText}} message-severity-{{ngModel.severity}}
+ ng-hide="ngModel.progress === undefined">
- + [Core API] + [Core API]<--[Plugin Bundle #2] + [Platform Bundle #1]-->[Core API] + [Platform Bundle #2]-->[Core API] + [Platform Bundle #3]-->[Core API] + [Core API]<--[Platform Bundle #4] + [Core API]<--[Platform Bundle #5] + [Core API]<--[Plugin Bundle #3] + ] + [Open MCT Web] ->[Browser APIs] +] +``` This architectural approach ensures a loose coupling between applications built  using Open MCT Web and the backends which support them.  @@ -465,7 +464,7 @@ similar) followed by a dot, to avoid collisions.  The properties used in extension definitions are typically unique to each  category of extension; a few properties have standard interpretations by the  platform.  -  + * `implementation`​: Identifies a JavaScript source file (in the sources  folder) which implements this extension. This JavaScript file is expected to  contain an AMD module (see ​http://requirejs.org/docs/whyamd.html#amd​) which  @@ -512,7 +511,7 @@ extension's priority may be specified as a ​`priority`​ property in definition; this may be a number, or a symbolic string. Extensions are  registered in reverse order (highest­priority first), and symbolic strings are  mapped to the numeric values as follows:  -  + * `fallback`​: Negative infinity. Used for extensions that are not intended for  use (that is, they are meant to be overridden) but are present as an option of  last resort.  @@ -526,7 +525,7 @@ overridden.  used, but still may be overridden in principle.  * `mandatory`​: Positive infinity. Used when an extension should definitely not  be overridden.  -  + These symbolic names are chosen to support usage where many extensions may  satisfy a given need, but only one may be used; in this case, as a convention it  should be the lowest­ordered (highest­priority) extensions available. In other  @@ -590,7 +589,7 @@ opposed to a constructor function.) Extensions of category `​routes`​ will be registered with Angular’s [route provider](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider​).  Extensions of this category have no implementations, and need only two  properties in their definition:  -  + * `when​`: The value that will be passed as the path argument to ​ `$routeProvider.when`​; specifically, the string that will appear in the trailing  part of the URL corresponding to this route. This property may be omitted, in  @@ -734,7 +733,7 @@ range, and may have more than one. Telemetry series data in Open MCT Web is expressed via the following  `TelemetrySeries​` interface:  -  + * `getPointCount()`​: Returns the number of unique points/samples in this series.  * `getDomainValue(index, [domain])`:​ Get the domain value at the specified index​.  If a second ​domain​ argument is provided, this is taken as a string identifier  @@ -749,7 +748,7 @@ Domain objects which have associated telemetry also expose metadata abo telemetry; this is retrievable via the `​getMetadata()`​ of the telemetry  capability. This will return a single JavaScript object containing the following  properties:  -  + * `source​`: The machine­readable identifier for the source of telemetry data for  this object.  * `key​`: The machine­readable identifier for the individual telemetry series.  @@ -765,7 +764,8 @@ made using this capability.  ## Types A domain object’s type is represented as a ​Type​ object, which has the following  -interface:  +interface: + * `getKey()`​: Get the machine­readable identifier for this type.  * `getName()​`: Get the human­readable name for this type.  * `getDescription()`​: Get a human­readable summary of this type.  @@ -828,9 +828,9 @@ metadata from the action’s extension definition. available as a property of the implementation’s constructor itself), which will  be used by the platform to filter out actions from contexts in which they are  inherently inapplicable. - + An action’s bundle definition (and/or `​getMetadata()`​ return value) may include: -  + * `category​`: A string or dearray of strings identifying which category or  categories an action falls into; used to determine when an action is displayed.  Categories supported by the platform include:  @@ -900,7 +900,7 @@ modified.  of an individual row definition.  * `field​`: Name of the field in ​`ngModel​` which will hold the value for this  control.  -  + ## Gestures A gesture is a user action which can be taken upon a representation of a domain  @@ -951,7 +951,7 @@ this indicator, and clicking it will invoke this method.    Note that all methods are optional, and are called directly from an Angular  template, so they should be appropriate to run during digest cycles.  -  + ### Custom Indicators Indicators which wish to have an arbitrary appearance (instead of following the  @@ -1036,6 +1036,7 @@ which are referenced from templates. See [https://docs.angularjs.org/guide/ for more information on controllers in Angular.)    A representation’s scope will contain: + * `domainObject​`: The represented domain object. * `model​`: The domain object’s model. * `configuration​`: An object containing configuration information for this  @@ -1082,9 +1083,10 @@ models. Root­level domain objects appear at the top­level of the tre For example, the _My Items_ folder is added as an extension of this category.  Extensions of this category should have the following properties: + * `id​`: The machine­readable identifier for the domaiwn object being exposed. * `model`​: The model, as a JSON object, for the domain object being exposed.  -  + ## Stylesheets The ​stylesheets​ extension category is used to add CSS files to style the  @@ -1106,6 +1108,7 @@ directive, which behaves similarly to `​ng­include​`, except that i symbolic identifiers instead of paths. A template’s extension definition should include the following properties: + * `key​`: The machine­readable name which identifies this template, matched  against the value given to the key attribute of the mct­include directive. * `templateUrl​`: The path to the relevant Angular template. This path is  @@ -1199,14 +1202,15 @@ that toolbar:  no arguments to use as a getter, called with a value to use as a setter.)  * `method​`: A method to invoke (again, on the selected object) from the  toolbar control. Useful particularly for buttons (which don’t edit a single  - property, necessarily.)  -  + property, necessarily.) + ### View Scope Views do not have implementations, but do get the same properties in scope that  are provided for `​representations​`.  When a view is in Edit mode, this scope will additionally contain: + * `commit()`​: A function which can be invoked to mark any changes to the view’s  configuration​ as ready to persist. * `selection​`: An object representing the current selection state.  @@ -1550,7 +1554,7 @@ extension mechanism is insufficient to achieve a desired result.  The ​`actionService​` provides `​Action​` instances which are applicable in specific  contexts. See Core API for additional notes on the interface for actions. The  `​actionService​` has the following interface:  -   + * `getActions(context)`​: Returns an array of ​Action​ objects which are applicable  in the specified action context.    @@ -1577,7 +1581,7 @@ model. Keys in this object are the capability keys (as used in a  The `​dialogService​` provides a means for requesting user input via a modal  dialog. It has the following interface:  -  + * `getUserInput(formStructure, formState)`​: Prompt the user to fill out a form.  The first argument describes the form’s structure (as will be passed to  ​mct­form​) while the second argument contains the initial state of that form.  @@ -1604,7 +1608,7 @@ option may have the following properties: * `name`​: Human­readable name to display in the button.  * `key`​: Machine­readable key, to pass as the result of the resolved promise  when clicked.  - * `description​`: Description to show in tooltip on hover.  + * `description​`: Description to show in tooltip on hover. ## Domain Object Service @@ -1630,7 +1634,7 @@ gestures.  ## Model Service The ​modelService​ provides domain object models. It has the following interface:  -  + * `getModels(ids)`​: For the provided array of domain object identifiers, returns  a Promise​ for an object containing key­value pairs, where keys are domain object  identifiers and values are corresponding domain object models. Note that the  @@ -1641,7 +1645,7 @@ result may contain a superset or subset of the models requested.  The ​persistenceService​ provides the ability to load/store JavaScript objects  (presumably serializing/deserializing to JSON in the process.) This is used  primarily to store domain object models. It has the following interface:  -  + * `listSpaces()`​: Returns a ​Promise​ for an array of strings identifying the  different persistence spaces this service supports. Spaces are intended to be  used to distinguish between different underlying persistence stores, to allow  @@ -1925,7 +1929,7 @@ the result of the action that was performed, or `undefined` if no ma was found.  ## Composition -  + The `​composition​` capability provides access to domain objects that are  contained by this domain object. While the ​`composition​` property of a domain  object’s model describes these contents (by their identifiers), the ​ @@ -1934,7 +1938,7 @@ object’s model describes these contents (by their identifiers), the  model will result in the absence of this capability in the domain object.  This capability has the following interface:  -  + * `invoke()`​: Returns a `​Promise​` for an array of `​DomainObject​` instances. ## Delegation @@ -1944,7 +1948,7 @@ to delegate responsibilities, which would normally handled by other  capabilities, to the domain objects in its composition.  This capability has the following interface:  -  + * `getDelegates(key)`​: Returns a ​Promise​ for an array of ​DomainObject​ instances,  to which this domain object wishes to delegate the capability with the specified ​ key​.  @@ -1997,317 +2001,316 @@ for this domain object. (including any changes made in place by the mutator function) will be used as  the new domain object model.  -## Persistence -  - The ​persistence​ capability provides a mean for interacting with the underlying  -persistence service which stores this domain object’s model. It has the following interface:  -  - * persist()​: Store the local version of this domain object, including any changes, to the  - persistence store. Returns a ​Promise​ for a boolean value, which will be true when the  - object was successfully persisted.  - * refresh()​: Replace this domain object’s model with the most recent version from  - persistence. Returns a ​Promise​ which will resolve when the change has completed.  - * getSpace()​: Return the string which identifies the persistence space which stores this  - domain object.  -  -Relationship -  - The ​relationship​ capability provides a means for accessing other domain objects  -with which this domain object has some typed relationship. It has the following interface:  -  - * listRelationships()​: List all types of relationships exposed by this object. Returns  - an array of strings identifying the types of relationships.  - * getRelatedObjects(relationship)​: Get all domain objects to which this domain  - object has the specified type of ​relationship​, which is a string identifier (as above.)  - Returns a ​Promise​ for an array of ​DomainObject​ instances.  -  - The platform implementation of the ​relationship​ capability is present for domain  -objects which has a ​relationships​ property in their model, whose value is an object  -containing key­value pairs, where keys are strings identifying relationship types, and values are  -arrays of domain object identifiers.  -  - 58  -Telemetry -  - The ​telemetry​ capability provides a means for accessing telemetry data associated  -with a domain object. It has the following interface:  -  - * requestData([request])​: Request telemetry data for this specific domain object,  - using telemetry request parameters from the specified ​request​ if provided. This  - capability will fill in telemetry request properties as­needed for this domain object.  - Returns a ​Promise​ for a ​TelemetrySeries​.  - * subscribe(callback, [request])​:  Subscribe to telemetry data updates for this  - specific domain object, using telemetry request parameters from the specified ​request  - if provided. This capability will fill in telemetry request properties as­needed for this  - domain object. The specified ​callback​ will be invoked with ​TelemetrySeries  - instances as they arrive. Returns a function which can be invoked to terminate the  - subscription, or ​undefined​ if no subscription could be obtained.  - * getMetadata()​: Get metadata associated with this domain object’s telemetry.  -   - The platform implementation of the ​telemetry​ capability is present for domain objects  -which has a ​telemetry​ property in their model and/or type definition; this object will serve as a  -template for telemetry requests made using this object, and will also be returned by  -getMetadata() ​above.  -  -Type -   - The ​type​ capability exposes information about the domain object’s type. It has the  -same interface as ​Type​; see Core API.  -   -View -  - The ​view​ capability exposes views which are applicable to a given domain object. It has  -the following interface:  -   - * invoke()​: Returns an array of extension definitions for views which are applicable for  - this domain object.  - 59  -Actions -  - Actions are reusable processes/behaviors performed by users within the system,  -typically upon domain objects.  -Action Categories -  - The platform understands the following action categories (specifiable as the ​category  -parameter of an action’s extension definition.)  -  - * contextual​: Appears in context menus.  - * view­control​: Appears in top­right area of view (as buttons) in Browse mode  -  -Platform Actions -  - The platform defines certain actions which can be utilized by way of a domain object’s  -action​ capability. Unless otherwise specified, these act upon (and modify) the object  -described by the ​domainObject​ property of the action’s context.  -  - * cancel​: Cancel the current editing action (invoked from Edit mode.)  - * compose​: Place an object in another object’s composition. The object to be added  - should be provided as the ​selectedObject​ of the action context.  - * edit​: Start editing an object (enter Edit mode.)  - * fullscreen​: Enter full screen mode.  - * navigate​: Make this object the focus of navigation (e.g. highlight it within the tree,  - display a view of it to the right.)  - * properties​: Show the “Edit Properties” dialog.  - * remove​: Remove this domain object from its parent’s composition. (The parent, in this  - case, is whichever other domain object exposed this object by way of its ​composition  - capability.)  - * save​: Save changes (invoked from Edit mode.)  - * window​: Open this object in a new window.  -  +## Persistence - - 60  -Policies -  - Policies are consulted to determine when certain behavior in Open MCT Web is allowed.  -Policy questions are assigned to certain categories, which broadly describe the type of decision  -being made; within each category, policies have a candidate (the thing which may or may not be  -allowed) and, optionally, a context (describing, generally, the context in which the decision is  -occurring.)   - The types of objects passed for “candidate” and “context” vary by category; these types  -are documented below.  -Policy Categories -  - The platform understands the following policy categories (specifiable as the ​category  -parameter of an policy’s extension definition.)  -  - * action​: Determines whether or not a given action is allowable. The candidate  - argument here is an ​Action​; the context is its action context object.  - * composition​: Determines whether or not domain objects of a given type are allowed  - to contain domain objects of another type. The candidate argument here is the  - container’s ​Type​; the context argument is the ​Type​ of the object to be contained.  - * view​: Determines whether or not a view is applicable for a domain object. The  - candidate argument is the view’s extension definition; the context argument is the  - DomainObject​ to be viewed.  -  -  -  +The persistence capability provides a mean for interacting with the underlying +persistence service which stores this domain object’s model. It has the +following interface: - - 61  -Build, Test, Deploy -  - Open MCT Web is designed to support a broad variety of build and deployment options.  -The sources can be deployed in the same directory structure used during development. A few  -utilities are included to support development processes.  -  -Command-line Build -  - Open MCT Web includes a script for building via command line using Maven 3.0.4  -(​https://maven.apache.org/​).  -   - Invoking ​mvn clean install​ will:  -  - * Check code style using JSLint. The build will fail if JSLint raises any warnings.  - * Run the test suite (see below.) The build will fail if any tests fail.  - * Populate version info (e.g. commit hash, build time.)  - * Produce a web archive (​.war​) artifact in the ​target​ directory.  -  - The produced artifact contains a subset of the repository’s own folder hierarchy, omitting  -tests and example bundles.   - Note that an internet connection is required to run this build, in order to download build  -dependencies.  -  -Test Suite -  - Open MCT Web uses Jasmine (​http://jasmine.github.io/​) for automated testing. The file  -test.html​, included at the top level of the source repository, can be run from the browser to  -perform tests for all active bundles, as defined in ​bundle.json​.  - To define tests for a bundle:  -   - * Include a directory named ​test​ within that bundle.  - * In the ​test​ directory, include a file named ​suite.json​. This will identify which scripts  - will be tested.  - * The file ​suite.json​ must contain a JSON array of strings, where each string is the  - name of a script to be tested. These names should include any directory paths to the  - script after (but not including) the ​src​ folder, and should not include the file’s ​.js  - extension. (Note that while Open MCT Web’s framework allows a different name to be  - chosen for the ​src​ directory, the test runner does not: This directory must be named  - src​ for the test runner to find it.)  - 62  - * For each script to be tested, a corresponding test script should be located in the bundle’s  - test​ directory. This should include the suffix ​Spec​ at the end of the filename (but  - before the ​.js​ extension.) This test script should be an AMD module which uses the  - Jasmine API to declare its test behavior. It should declare an AMD dependency on the  - script to be tested, using a relative path.  -   - For example, if writing tests for a bundle at ​example/foo​ with two scripts:  - * example/foo/src/controllers/FooController.js  - * example/foo/src/directives/FooDirective.js  -  - First, these scripts should be identified in ​example/foo/test/suite.json​, e.g. with  -contents:  - [ "controllers/FooController", "directives/FooDirective" ]  -  - Then, scripts which describe these tests should be written. For example, test  -example/foo/test/controllers/FooControllerSpec.js​ could look like:  -  -/*global define,Promise,describe,it,expect,beforeEach*/  -  -define(  -    ["../../src/controllers/FooController"],  -    function (FooController) {  -        "use strict";  -  -        describe("The foo controller", function () {  -            it("does something", function () {  -                var controller = new FooController();  -                expect(controller.foo()).toEqual("foo");  -            });  -        });  -    }  -);  -  -Code Coverage -  - In addition to running tests, the test runner will also capture code coverage information  -using Blanket.JS (​http://blanketjs.org/​) and display this at the bottom of the screen. Currently,  -only statement coverage is displayed.  - - 63  -Deployment -  - Open MCT Web is built to be flexible in terms of the deployment strategies it supports. In  -order to run in the browser, Open MCT Web needs:  -  - 1. HTTP access to sources/resources for the framework, platform, and all active bundles.  - 2. Access to any external services utilized by active bundles. (This means that external  - services need to support HTTP or some other web­accessible interface, like  - WebSockets.)  -  - Any HTTP server capable of serving flat files is sufficient for the first point. The  -command­line build also packages Open MCT Web into a ​.war​ file for easier deployment on  -containers such as Apache Tomcat.  - The second point may be less flexible, as it depends upon the specific services to be  -utilized by Open MCT Web. Because of this, it is often the set of external services (and the  -manner in which they are exposed) that determine how to deploy Open MCT Web.  -  - One important constraint to consider in this context is the browser’s same origin policy. If  -external services are not on the same apparent host and port as the client (from the perspective  -of the browser) then access may be disallowed. There are two workarounds if this occurs:  - * Make the external service appear to be on the same host/port, either by actually  - deploying it there, or by proxying requests to it.  - * Enable CORS (cross­origin resource sharing) on the external service. This is only  - possible if the external service can be configured to support CORS. Care should be  - exercised if choosing this option to ensure that the chosen configuration does not create  - a security vulnerability.  -  - Examples of deployment strategies (and the conditions under which they make the most  -sense) include:  -  - * If the external services that Open MCT Web will utilize are all running on Apache Tomcat  - (​https://tomcat.apache.org/​), then it makes sense to run Open MCT Web from the same  - Tomcat instance as a separate web application. The ​.war​ artifact produced by the  - command line build facilitates this deployment option. (See  - https://tomcat.apache.org/tomcat­8.0­doc/deployer­howto.html ​ for general information on  - deploying in Tomcat.)  - * If a variety of external services will be running from a variety of hosts/ports, then it may  - make sense to use a web server that supports proxying, such as the Apache HTTP  - Server (​http://httpd.apache.org/​). In this configuration, the HTTP server would be  - configured to proxy (or reverse proxy) requests at specific paths to the various external  - services, while providing Open MCT Web as flat files from a different path.  - 64  - * If a single server component is being developed to handle all server­side needs of an  - Open MCT Web instance, it can make sense to serve Open MCT Web (as flat files) from  - the same component using an embedded HTTP server such as Nancy  - (​http://nancyfx.org/​).  - * If no external services are needed (or if the “external services” will just be generating flat  - files to read) it makes sense to utilize a lightweight flat file HTTP server such as Lighttpd  - (​http://www.lighttpd.net/​). In this configuration, Open MCT Web sources/resources would  - be placed at one path, while the files generated by the external service are placed at  - another path.  - * If all external services support CORS, it may make sense to have an HTTP server that is  - solely responsible for making Open MCT Web sources/resources available, and to have  - Open MCT Web contact these external services directly. Again, lightweight HTTP  - servers such as Lighttpd (​http://www.lighttpd.net/​) are useful in this circumstance. The  - downside of this option is that additional configuration effort is required, both to enable  - CORS on the external services, and to ensure that Open MCT Web can correctly locate  - these services.  -  - Another important consideration is authentication. By design, Open MCT Web does not  -handle user authentication. Instead, this should typically be treated as a deployment­time  -concern, where authentication is handled by the HTTP server which provides Open MCT Web,  -or an external access management system.  -  -Configuration -  - In most of the deployment options above, some level of configuration is likely to be  -needed or desirable to make sure that bundles can reach the external services they need to  -reach. Most commonly this means providing the path or URL to an external service.  - Configurable parameters within Open MCT Web are specified via constants (literally, as  -extensions of the ​constants​ category) and accessed via dependency injection by the scripts  -which need them. Reasonable defaults for these constants are provided in the bundle where  -they are used. Plugins are encouraged to follow the same pattern.  - Constants may be specified in any bundle; if multiple constants are specified with the  -same ​key​, the highest­priority one will be used. This allows default values to be overridden by  -specifying constants with higher priority.  -   - This permits at least three configuration approaches:  -  - * Modify the constants defined in their original bundles when deploying. This is generally  - undesirable due to the amount of manual work required and potential for error, but is  - viable if there are a small number of constants to change.  - * Add a separate configuration bundle which overrides the values of these constants. This  - is particularly appropriate when multiple configurations (e.g. development, test,  - 65  - production) need to be managed easily; these can be swapped quickly by changing the  - set of active bundles in ​bundles.json​.  - * Deploy Open MCT Web and its external services in such a fashion that the default paths  - to reach external services are all correct.  -  -Configuration Constants -  - The following configuration constants are recognized by Open MCT Web bundles:  -  - * CouchDB adapter, ​platform/persistence/coucb  - ○ COUCHDB_PATH​: URL or path to the CouchDB database to be used for domain  - object persistence. Should not include a trailing slash.  - * ElasticSearch adapter, ​platform/persistence/elastic  - ○ ELASTIC_ROOT​: URL or path to the ElasticSearch instance to be used for  - domain object persistence. Should not include a trailing slash.  - ○ ELASTIC_PATH​: Path relative to the ElasticSearch instance where domain  -  object models should be persisted. Should take the form ​/​.  - 66  +* `persist()`: Store the local version of this domain object, including any +changes, to the persistence store. Returns a Promise for a boolean value, which +will be true when the object was successfully persisted. +* `refresh()`: Replace this domain object’s model with the most recent version +from persistence. Returns a Promise which will resolve when the change has +completed. +* `getSpace()`: Return the string which identifies the persistence space which +stores this domain object. + +## Relationship + +The relationship capability provides a means for accessing other domain objects +with which this domain object has some typed relationship. It has the following +interface: + +* `listRelationships()`: List all types of relationships exposed by this object. +Returns an array of strings identifying the types of relationships. +* `getRelatedObjects(relationship)`: Get all domain objects to which this domain +object has the specified type of relationship, which is a string identifier +(as above.) Returns a `Promise` for an array of `DomainObject` instances. + +The platform implementation of the `relationship` capability is present for domain +objects which has a `relationships` property in their model, whose value is an +object containing key-value pairs, where keys are strings identifying +relationship types, and values are arrays of domain object identifiers. + +##Telemetry + +The telemetry capability provides a means for accessing telemetry data +associated with a domain object. It has the following interface: + +* `requestData([request])`: Request telemetry data for this specific domain +object, using telemetry request parameters from the specified request if +provided. This capability will fill in telemetry request properties as-needed +for this domain object. Returns a `Promise` for a `TelemetrySeries`. +* `subscribe(callback, [request])`: Subscribe to telemetry data updates for +this specific domain object, using telemetry request parameters from the +specified request if provided. This capability will fill in telemetry request +properties as-needed for this domain object. The specified callback will be +invoked with TelemetrySeries instances as they arrive. Returns a function which +can be invoked to terminate the subscription, or undefined if no subscription +could be obtained. +* `getMetadata()`: Get metadata associated with this domain object’s telemetry. + +The platform implementation of the `telemetry` capability is present for domain +objects which has a `telemetry` property in their model and/or type definition; +this object will serve as a template for telemetry requests made using this +object, and will also be returned by `getMetadata()` above. + +## Type +The `type` capability exposes information about the domain object’s type. It has +the same interface as `Type`; see Core API. + +## View + +The `view` capability exposes views which are applicable to a given domain +object. It has the following interface: + +* `invoke()`: Returns an array of extension definitions for views which are +applicable for this domain object. + +# Actions + +Actions are reusable processes/behaviors performed by users within the system, +typically upon domain objects. + +## Action Categories + +The platform understands the following action categories (specifiable as the +`category` parameter of an action’s extension definition.) + +* `contextual`: Appears in context menus. +* `view-control`: Appears in top-right area of view (as buttons) in Browse mode + +## Platform Actions +The platform defines certain actions which can be utilized by way of a domain +object’s `action` capability. Unless otherwise specified, these act upon (and +modify) the object described by the `domainObject` property of the action’s +context. + +* `cancel`: Cancel the current editing action (invoked from Edit mode.) +* `compose`: Place an object in another object’s composition. The object to be +added should be provided as the `selectedObject` of the action context. +* `edit`: Start editing an object (enter Edit mode.) +* `fullscreen`: Enter full screen mode. +* `navigate`: Make this object the focus of navigation (e.g. highlight it within +the tree, display a view of it to the right.) +* `properties`: Show the “Edit Properties” dialog. +* `remove`: Remove this domain object from its parent’s composition. (The +parent, in this case, is whichever other domain object exposed this object by +way of its `composition` capability.) +* `save`: Save changes (invoked from Edit mode.) +* `window`: Open this object in a new window. + +# Policies + +Policies are consulted to determine when certain behavior in Open MCT Web is +allowed. Policy questions are assigned to certain categories, which broadly +describe the type of decision being made; within each category, policies have a +candidate (the thing which may or may not be allowed) and, optionally, a context +(describing, generally, the context in which the decision is occurring.) + +The types of objects passed for “candidate” and “context” vary by category; +these types are documented below. + +## Policy Categories + +The platform understands the following policy categories (specifiable as the +`category` parameter of an policy’s extension definition.) + +* `action`: Determines whether or not a given action is allowable. The candidate +argument here is an Action; the context is its action context object. +* `composition`: Determines whether or not domain objects of a given type are +allowed to contain domain objects of another type. The candidate argument here +is the container’s `Type`; the context argument is the `Type` of the object to be +contained. +* `view`: Determines whether or not a view is applicable for a domain object. +The candidate argument is the view’s extension definition; the context argument +is the `DomainObject` to be viewed. + +# Build, Test, Deploy +Open MCT Web is designed to support a broad variety of build and deployment +options. The sources can be deployed in the same directory structure used during +development. A few utilities are included to support development processes. + +## Command-line Build +Open MCT Web includes a script for building via command line using Maven 3.0.4 +[https://maven.apache.org/](). + +Invoking mvn clean install will: + +* Check code style using JSLint. The build will fail if JSLint raises any warnings. +* Run the test suite (see below.) The build will fail if any tests fail. +* Populate version info (e.g. commit hash, build time.) +* Produce a web archive (`.war`) artifact in the `target` directory. + +The produced artifact contains a subset of the repository’s own folder +hierarchy, omitting tests and example bundles. + +Note that an internet connection is required to run this build, in order to +download build dependencies. + +## Test Suite + +Open MCT Web uses Jasmine [http://jasmine.github.io/]() for automated testing. +The file `test.html`, included at the top level of the source repository, can be +run from the browser to perform tests for all active bundles, as defined in +`bundle.json`. + +To define tests for a bundle: + +* Include a directory named `test` within that bundle. +* In the `test` directory, include a file named `suite.json`. This will identify +which scripts will be tested. +* The file `suite.json` must contain a JSON array of strings, where each string +is the name of a script to be tested. These names should include any directory +paths to the script after (but not including) the `src` folder, and should not +include the file’s `.js` extension. (Note that while Open MCT Web’s framework +allows a different name to be chosen for the src directory, the test runner +does not: This directory must be named `src` for the test runner to find it.) +* For each script to be tested, a corresponding test script should be located in +the bundle’s `test` directory. This should include the suffix Spec at the end of +the filename (but before the `.js` extension.) This test script should be an AMD +module which uses the Jasmine API to declare its test behavior. It should +declare an AMD dependency on the script to be tested, using a relative path. + +For example, if writing tests for a bundle at example/foo with two scripts: +* `example/foo/src/controllers/FooController.js` +* `example/foo/src/directives/FooDirective.js` + +First, these scripts should be identified in `example/foo/test/suite.json`, e.g. +with contents:`[ "controllers/FooController", "directives/FooDirective" ]` + +Then, scripts which describe these tests should be written. For example, test +`example/foo/test/controllers/FooControllerSpec.js` could look like: + + /*global define,Promise,describe,it,expect,beforeEach*/ + + define( + ["../../src/controllers/FooController"], + function (FooController) { + "use strict"; + + + describe("The foo controller", function () { + it("does something", function () { + var controller = new FooController(); + expect(controller.foo()).toEqual("foo"); + }); + }); + } + ); +## Code Coverage +In addition to running tests, the test runner will also capture code coverage +information using [Blanket.JS](http://blanketjs.org/) and display this at the +bottom of the screen. Currently, only statement coverage is displayed. + +## Deployment +Open MCT Web is built to be flexible in terms of the deployment strategies it +supports. In order to run in the browser, Open MCT Web needs: + +1. HTTP access to sources/resources for the framework, platform, and all active +bundles. +2. Access to any external services utilized by active bundles. (This means that +external services need to support HTTP or some other web-accessible interface, +like WebSockets.) + +Any HTTP server capable of serving flat files is sufficient for the first point. +The command-line build also packages Open MCT Web into a `.war` file for easier +deployment on containers such as Apache Tomcat. + +The second point may be less flexible, as it depends upon the specific services +to be utilized by Open MCT Web. Because of this, it is often the set of external +services (and the manner in which they are exposed) that determine how to deploy +Open MCT Web. + +One important constraint to consider in this context is the browser’s same +origin policy. If external services are not on the same apparent host and port +as the client (from the perspective of the browser) then access may be +disallowed. There are two workarounds if this occurs: + +* Make the external service appear to be on the same host/port, either by +actually deploying it there, or by proxying requests to it. +* Enable CORS (cross-origin resource sharing) on the external service. This is +only possible if the external service can be configured to support CORS. Care +should be exercised if choosing this option to ensure that the chosen +configuration does not create a security vulnerability. + +Examples of deployment strategies (and the conditions under which they make the +most sense) include: + +* If the external services that Open MCT Web will utilize are all running on +Apache Tomcat [https://tomcat.apache.org/](), then it makes sense to run Open +MCT Web from the same Tomcat instance as a separate web application. The +`.war` artifact produced by the command line build facilitates this deployment +option. (See `https://tomcat.apache.org/tomcat-8.0-doc/deployer-howto.html` for +general information on deploying in Tomcat.) +* If a variety of external services will be running from a variety of +hosts/ports, then it may make sense to use a web server that supports proxying, +such as the Apache HTTP Server [http://httpd.apache.org/](). In this +configuration, the HTTP server would be configured to proxy (or reverse proxy) +requests at specific paths to the various external services, while providing +Open MCT Web as flat files from a different path. +* If a single server component is being developed to handle all server-side +needs of an Open MCT Web instance, it can make sense to serve Open MCT Web (as +flat files) from the same component using an embedded HTTP server such as Nancy +[http://nancyfx.org/](). +* If no external services are needed (or if the “external services” will just +be generating flat files to read) it makes sense to utilize a lightweight flat +file HTTP server such as Lighttpd [http://www.lighttpd.net/](). In this +configuration, Open MCT Web sources/resources would be placed at one path, while +the files generated by the external service are placed at another path. +* If all external services support CORS, it may make sense to have an HTTP +server that is solely responsible for making Open MCT Web sources/resources +available, and to have Open MCT Web contact these external services directly. +Again, lightweight HTTP servers such as Lighttpd [http://www.lighttpd.net/]() +are useful in this circumstance. The downside of this option is that additional +configuration effort is required, both to enable CORS on the external services, +and to ensure that Open MCT Web can correctly locate these services. + +Another important consideration is authentication. By design, Open MCT Web does +not handle user authentication. Instead, this should typically be treated as a +deployment-time concern, where authentication is handled by the HTTP server +which provides Open MCT Web, or an external access management system. + +### Configuration +In most of the deployment options above, some level of configuration is likely +to be needed or desirable to make sure that bundles can reach the external +services they need to reach. Most commonly this means providing the path or URL +to an external service. + +Configurable parameters within Open MCT Web are specified via constants +(literally, as extensions of the `constants` category) and accessed via +dependency injection by the scripts which need them. Reasonable defaults for +these constants are provided in the bundle where they are used. Plugins are +encouraged to follow the same pattern. + +Constants may be specified in any bundle; if multiple constants are specified +with the same `key`, the highest-priority one will be used. This allows default +values to be overridden by specifying constants with higher priority. + +This permits at least three configuration approaches: + +* Modify the constants defined in their original bundles when deploying. This is +generally undesirable due to the amount of manual work required and potential +for error, but is viable if there are a small number of constants to change. +* Add a separate configuration bundle which overrides the values of these +constants. This is particularly appropriate when multiple configurations (e.g. +development, test, production) need to be managed easily; these can be swapped +quickly by changing the set of active bundles in bundles.json. +* Deploy Open MCT Web and its external services in such a fashion that the +default paths to reach external services are all correct. + +### Configuration Constants + +The following configuration constants are recognized by Open MCT Web bundles: +* CouchDB adapter, `platform/persistence/couch` + * `COUCHDB_PATH`: URL or path to the CouchDB database to be used for domain + object persistence. Should not include a trailing slash. +* ElasticSearch adapter, platform/persistence/elastic + * `ELASTIC_ROOT`: URL or path to the ElasticSearch instance to be used for + domain object persistence. Should not include a trailing slash. + * `ELASTIC_PATH`: Path relative to the ElasticSearch instance where domain + object models should be persisted. Should take the form `/`. \ No newline at end of file diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md new file mode 100644 index 0000000000..e69de29bb2 From e52f53b7ff2526438d7d1cd22cd9b9f6671bf9a7 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Sun, 4 Oct 2015 15:26:55 -0700 Subject: [PATCH 046/488] Fixed markdown --- docs/src/guide/index.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 7765c8296f..9381152431 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -13,9 +13,6 @@ May 12, 2015 | 0.1 | | Victor Woeltjen June 4, 2015 | 1.0 | Name Changes | Victor Woeltjen September 23, 2015 | 1.1 | Conversion to MarkDown | Andrew Henry -# Table of Contents -```generated_toc``` - # Introduction The purpose of this guide is to familiarize software developers with the Open MCT Web platform. From 1922e1e241074a6b0811fc7250bbccecd3a9851a Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Sun, 4 Oct 2015 18:55:10 -0700 Subject: [PATCH 047/488] Finished developer guide Fixed date of modification --- docs/src/guide/index.md | 2859 ++++++++++++++++++++------------------- 1 file changed, 1436 insertions(+), 1423 deletions(-) diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 9381152431..d06e962b44 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -11,7 +11,7 @@ Date | Version | Summary of Changes | Author April 29, 2015 | 0 | Initial Draft | Victor Woeltjen May 12, 2015 | 0.1 | | Victor Woeltjen June 4, 2015 | 1.0 | Name Changes | Victor Woeltjen -September 23, 2015 | 1.1 | Conversion to MarkDown | Andrew Henry +October 4, 2015 | 1.1 | Conversion to MarkDown | Andrew Henry # Introduction The purpose of this guide is to familiarize software developers with the Open @@ -21,8 +21,8 @@ MCT Web platform. Open MCT Web is a platform for building user interface and display tools, developed at the NASA Ames Research Center in collaboration with teams at the Jet Propulsion Laboratory. It is written in HTML5, CSS3, and JavaScript, using -[AngularJS](h​ttp://www.angularjs.org) as a framework. Its intended use is to -create single­page web applications which integrate data and behavior from a +[AngularJS](http://www.angularjs.org) as a framework. Its intended use is to +create single-page web applications which integrate data and behavior from a variety of sources and domains. Open MCT Web has been developed to support the remote operation of space @@ -35,72 +35,72 @@ Open MCT Web provides: * A common user interface paradigm which can be applied to a variety of domains and tasks. Open MCT Web is more than a widget toolkit - it provides a standard -tree­on­the­left, view­on­the­right browsing environment which you customize by -adding new browsable object types, visualizations, and back­end adapters. +tree-on-the-left, view-on-the-right browsing environment which you customize by +adding new browsable object types, visualizations, and back-end adapters. * A plugin framework and an extensible API for introducing new application features of a variety of types. * A set of general-purpose object types and visualizations, as well as some visualizations and infrastructure specific to telemetry display. ## Client-Server Relationship -Open MCT Web is client software - it runs entirely in the user’s web browser. As -such, it is largely “server agnostic”; any web server capable of serving files +Open MCT Web is client software - it runs entirely in the user's web browser. As +such, it is largely 'server agnostic'; any web server capable of serving files from paths is capable of providing Open MCT Web. While Open MCT Web can be configured to run as a standalone client, this is rarely very useful. Instead, it is intended to be used as a display and -interaction layer for information obtained from a variety of back­end services. +interaction layer for information obtained from a variety of back-end services. Doing so requires authoring or utilizing adapter plugins which allow Open MCT Web to interact with these services. Typically, the pattern here is to provide a known interface that Open MCT Web -can utilize, and implement it such that it interacts with whatever back­end -provides the relevant information. Examples of back­ends that can be utilized in -this fashion include databases for the persistence of user­created objects, or +can utilize, and implement it such that it interacts with whatever back-end +provides the relevant information. Examples of back-ends that can be utilized in +this fashion include databases for the persistence of user-created objects, or sources of telemetry data. -See the [Architecture Guide](../architecture/index.md#Overview) for more details +See the [Architecture Guide](../architecture/index.md#Overview) for information on the client-server relationship. ## Developing with Open MCT Web Building applications with Open MCT Web typically means authoring and utilizing -a set of plugins which provide application­specific details about how Open MCT +a set of plugins which provide application-specific details about how Open MCT Web should behave. ### Technologies Open MCT Web sources are written in JavaScript, with a number of configuration files written in JSON. Displayable components are written in HTML5 and CSS3. -Open MCT Web is built using [AngularJS](h​ttp://www.angularjs.org) ​from Google. A +Open MCT Web is built using [AngularJS](http://www.angularjs.org) from Google. A good understanding of Angular is recommended for developers working with Open MCT Web. ### Forking -Open MCT Web does not currently have a single stand­alone artifact that can be +Open MCT Web does not currently have a single stand-alone artifact that can be used as a library. Instead, the recommended approach for creating a new application is to start by forking/branching Open MCT Web, and then adding new -features from there. Put another way, Open MCT Web’s source structure is built +features from there. Put another way, Open MCT Web's source structure is built to serve as a template for specific applications. -Forking in this manner should not require that you edit Open MCT Web’s sources. -The preferred approach is to create a new directory (peer to ​`index.html`)​for +Forking in this manner should not require that you edit Open MCT Web's sources. +The preferred approach is to create a new directory (peer to `index.html`) for the new application, then add new bundles (as described in the Framework chapter) within that directory. To initially clone the Open MCT Web repository: -`git clone ­b open­master` +`git clone -b open-master` To create a fork to begin working on a new application using Open MCT Web: cd - git checkout open­master - git checkout ­b + git checkout open-master + git checkout -b As a convention used internally, applications built using Open MCT Web have master branch names with an identifying prefix. For instance, if building an -application called “Foo”, the last statement above would look like: +application called 'Foo', the last statement above would look like: - git checkout ­b foo­master + git checkout -b foo-master This convention is not enforced or understood by Open MCT Web in any way; it is mentioned here as a more general recommendation. @@ -108,26 +108,25 @@ mentioned here as a more general recommendation. # Overview Open MCT Web is implemented as a framework component which manages a set of -other components. These components, called “bundles”, act as containers to group +other components. These components, called _bundles_, act as containers to group sets of related functionality; individual units of functionality are expressed -within these bundles as "extensions." +within these bundles as _extensions_. Extensions declare dependencies on other extensions (either individually or categorically), and the framework provides actual extension instances at -run­time to satisfy these declared dependency. This dependency injection +run-time to satisfy these declared dependency. This dependency injection approach allows software components which have been authored separately (e.g. as -plugins) but to collaborate at run­time. +plugins) but to collaborate at run-time. -Open MCT Web’s framework layer is implemented on top of AngularJS’s [dependency -injection mechanism](https://docs.angularjs.org/guide/di)​ and is modelled after -[OSGi](hhttp://www.osgi.org/)​ and its [Declarative Services component model] -(h​ttp://wiki.osgi.org/wiki/Declarative_Services)​. In particular, this is where -the term "bundle" comes from. +Open MCT Web's framework layer is implemented on top of AngularJS's [dependency +injection mechanism](https://docs.angularjs.org/guide/di) and is modelled after +[OSGi](hhttp://www.osgi.org/) and its [Declarative Services component model](http://wiki.osgi.org/wiki/Declarative_Services). +In particular, this is where the term _bundle_ comes from. ## Framework Overview -The framework’s role in the application is to manage connections between -bundles. All application­specific behavior is provided by individual bundles, or +The framework's role in the application is to manage connections between +bundles. All application-specific behavior is provided by individual bundles, or as the result of their collaboration. The framework is described in more detail in the [Framework Overview](../architecture/Framework.md#Overview) of the @@ -145,59 +144,59 @@ specificity to the application. ``` * __Framework__ : This tier is responsible for wiring together the set of -configured components (called bundles) together to instantiate the running +configured components (called _bundles_) together to instantiate the running application. It is responsible for mediating between AngularJS (in particular, -its dependency injection mechanism) and RequireJS (to load scripts at run­time.) +its dependency injection mechanism) and RequireJS (to load scripts at run-time.) It additionally interprets bundle definitions (see explanation below, as well as further detail in the Framework chapter.) At this tier, we are at our most -general: We know only that we are a plugin­based application.
 +general: We know only that we are a plugin-based application. * __Platform__: Components in the Platform tier describe both the general user -interface and corresponding developer­facing interfaces of Open MCT Web. This +interface and corresponding developer-facing interfaces of Open MCT Web. This tier provides the general infrastructure for applications. It is less general than the framework tier, insofar as this tier introduces a specific user interface paradigm, but it is still non-specific as to what useful features will be provided. Although they can be removed or replaced easily, bundles -provided by the Platform tier generally should not be thought of as optional.
 +provided by the Platform tier generally should not be thought of as optional. * __Application__: The application tier consists of components which utilize the infrastructure provided by the Platform to provide functionality which will (or could) be useful to specific applications built using Open MCT Web. These -include adapters to specific persistence back­ends (such as ElasticSearch or -CouchDB) as well as bundles which describe more user­facing features (such as -Plot views for visualizing time series data, or Layout objects for -display­building.) Bundles from this tier can be added or removed without +include adapters to specific persistence back-ends (such as ElasticSearch or +CouchDB) as well as bundles which describe more user-facing features (such as +_Plot_ views for visualizing time series data, or _Layout_ objects for +display-building.) Bundles from this tier can be added or removed without compromising basic application functionality, with the caveat that at least one persistence adapter needs to be present. * __Plugins__: Conceptually, this tier is not so different from the application -tier; it consists of bundles describing new features, back­end adapters, that +tier; it consists of bundles describing new features, back-end adapters, that are specific to the application being built on Open MCT Web. It is described as a separate tier here because it has one important distinction from the application tier: It consists of bundles that are not included with the platform (either authored anew for the specific application, or obtained from elsewhere.) -Note that bundles in any tier can go off and consult back­end services. In +Note that bundles in any tier can go off and consult back-end services. In practice, this responsibility is handled at the Application and/or Plugin tiers; -Open MCT Web is built to be server­agnostic, so any back­end is considered an -application­specific detail. +Open MCT Web is built to be server-agnostic, so any back-end is considered an +application-specific detail. ## Platform Overview The "tiered" architecture described in the preceding text describes a way of thinking of and categorizing software components of a Open MCT Web application, -as well as the framework layer’s role in mediating between these components. +as well as the framework layer's role in mediating between these components. Once the framework layer has wired these software components together, however, -the application’s logical architecture emerges. +the application's logical architecture emerges. An overview of the logical architecture of the platform is given in the [Platform Architecture](../architecture/Platform.md#PlatformArchitecture) section of the Platform guide ### Web Services -As mentioned in the Introduction, Open MCT Web is a platform single­page  -applications which runs entirely in the browser. Most applications will want to  -additionally interact with server­side resources, to (for example) read  -telemetry data or store user­created objects. This interaction is handled by  -individual bundles using APIs which are supported in browser (such as  -`XMLHttpRequest`​, typically wrapped by Angular’s '`$http​`.) +As mentioned in the Introduction, Open MCT Web is a platform single-page +applications which runs entirely in the browser. Most applications will want to +additionally interact with server-side resources, to (for example) read +telemetry data or store user-created objects. This interaction is handled by +individual bundles using APIs which are supported in browser (such as +`XMLHttpRequest`, typically wrapped by Angular's `$http`.) ```nomnoml #direction: right @@ -219,1795 +218,1809 @@ individual bundles using APIs which are supported in browser (such as ] ``` -This architectural approach ensures a loose coupling between applications built  -using Open MCT Web and the backends which support them.  -  +This architectural approach ensures a loose coupling between applications built +using Open MCT Web and the backends which support them. + ### Glossary -  -Certain terms are used throughout Open MCT Web with consistent meanings or  -conventions. Other developer documentation, particularly in­line documentation,  -may presume an understanding of these terms. + +Certain terms are used throughout Open MCT Web with consistent meanings or +conventions. Other developer documentation, particularly in-line documentation, +may presume an understanding of these terms. -* __bundle__​: A bundle is a removable, reusable grouping of software elements.  -The application is composed of bundles. Plug­ins are bundles. -* __capability__​: A JavaScript object which exposes dynamic behavior or  -non­persistent state associated with a domain object. -* __category__​: A machine­readable identifier for a group that something may  -belong to. -* __composition​__: In the context of a domain object, this refers to the set of -other domain objects that compose or are contained by that object. A domain  -object's composition is the set of domain objects that should appear immediately - beneath it in a tree hierarchy. A domain object's composition is described in  -its model as an array of identifiers; its composition capability provides a  -means to retrieve the actual domain object instances associated with these  -identifiers asynchronously.  -* __description__​: When used as an object property, this refers to the human­ -readable description of a thing; usually a single sentence or short paragraph.  -(Most often used in the context of extensions, domain object models, or other  -similar application­specific objects.)  -* __domain object​__: A meaningful object to the user; a distinct thing in the  -work support by Open MCT Web. Anything that appears in the left­hand tree is a  -domain object.  -* __extension​__: An extension is a unit of functionality exposed to the platform  -in a declarative fashion by a bundle. The term “extension category” is used to  -distinguish types of extensions from specific extension instances.  -* __id__​: A string which uniquely identifies a domain object.  -* __key__​: When used as an object property, this refers to the machine­readable  -identifier for a specific thing in a set of things. (Most often used in the  -context of extensions or other similar application­specific object sets.) This  -term is chosen to avoid attaching ambiguous meanings to “id”.  -* __model__​: The persistent state associated with a domain object. A domain  -object's model is a JavaScript object which can be converted to JSON without  -losing information (that is, it contains no methods.)  -* __name__​: When used as an object property, this refers to the human­readable  -name for a thing. (Most often used in the context of extensions, domain object  -models, or other similar application­specific objects.)  -* __navigation__​: Refers to the current state of the application with respect to  -the user's expressed interest in a specific domain object; e.g. when a user  -clicks on a domain object in the tree, they are ​navigating​ to it, and it is  -thereafter considered the ​navigated object (until the user makes another such  -choice.) This term is used to distinguish navigation from selection, which  -occurs in an editing context.  -* __space__​: A machine­readable name used to identify a persistence store.  -Interactions with persistence with generally involve a space parameter in some  -form, to distinguish multiple persistence stores from one another (for cases  -where there are multiple valid persistence locations available.)  -* __source__​: A machine­readable name used to identify a source of telemetry  -data. Similar to "space", this allows multiple telemetry sources to operate  -side­by­side without conflicting.  +* __bundle__: A bundle is a removable, reusable grouping of software elements. +The application is composed of bundles. Plug-ins are bundles. +* __capability__: A JavaScript object which exposes dynamic behavior or +non-persistent state associated with a domain object. +* __category__: A machine-readable identifier for a group that something may +belong to. +* __composition __: In the context of a domain object, this refers to the set of +other domain objects that compose or are contained by that object. A domain +object's composition is the set of domain objects that should appear immediately + beneath it in a tree hierarchy. A domain object's composition is described in +its model as an array of identifiers; its composition capability provides a +means to retrieve the actual domain object instances associated with these +identifiers asynchronously. +* __description__: When used as an object property, this refers to the human- +readable description of a thing; usually a single sentence or short paragraph. +(Most often used in the context of extensions, domain object models, or other +similar application-specific objects.) +* __domain object __: A meaningful object to the user; a distinct thing in the +work support by Open MCT Web. Anything that appears in the left-hand tree is a +domain object. +* __extension __: An extension is a unit of functionality exposed to the platform +in a declarative fashion by a bundle. The term 'extension category' is used to +distinguish types of extensions from specific extension instances. +* __id__: A string which uniquely identifies a domain object. +* __key__: When used as an object property, this refers to the machine-readable +identifier for a specific thing in a set of things. (Most often used in the +context of extensions or other similar application-specific object sets.) This +term is chosen to avoid attaching ambiguous meanings to 'id'. +* __model__: The persistent state associated with a domain object. A domain +object's model is a JavaScript object which can be converted to JSON without +losing information (that is, it contains no methods.) +* __name__: When used as an object property, this refers to the human-readable +name for a thing. (Most often used in the context of extensions, domain object +models, or other similar application-specific objects.) +* __navigation__: Refers to the current state of the application with respect to +the user's expressed interest in a specific domain object; e.g. when a user +clicks on a domain object in the tree, they are navigating to it, and it is +thereafter considered the navigated object (until the user makes another such +choice.) This term is used to distinguish navigation from selection, which +occurs in an editing context. +* __space__: A machine-readable name used to identify a persistence store. +Interactions with persistence with generally involve a space parameter in some +form, to distinguish multiple persistence stores from one another (for cases +where there are multiple valid persistence locations available.) +* __source__: A machine-readable name used to identify a source of telemetry +data. Similar to "space", this allows multiple telemetry sources to operate +side-by-side without conflicting. # Framework -   -Open MCT Web is built on the [AngularJS framework](​http://www.angularjs.org​). A  -good understanding of that framework is recommended.  + +Open MCT Web is built on the [AngularJS framework]( http://www.angularjs.org ). A +good understanding of that framework is recommended. -Open MCT Web adds an extra layer on top of AngularJS to (a) generalize its  -dependency injection mechanism slightly, particularly to handle many­to­one  -relationships; and (b) handle script loading. Combined, these features become a  -plugin mechanism.  -   -This framework layer operates on two key concepts: +Open MCT Web adds an extra layer on top of AngularJS to (a) generalize its +dependency injection mechanism slightly, particularly to handle many-to-one +relationships; and (b) handle script loading. Combined, these features become a +plugin mechanism. + +This framework layer operates on two key concepts: -* __Bundle:__ ​A bundle is a collection of related functionality that can be  -added to the application as a group. More concretely, a bundle is a directory  -containing a JSON file declaring its contents, as well as JavaScript sources,  -HTML templates, and other resources used to support that functionality. (The  -term bundle is borrowed from [OSGi](http://www.osgi.org/)​ ­ which has also  -inspired many of the concepts used in the framework layer. A familiarity with  -OSGi, particularly Declarative Services, may be useful when working with Open  -MCT Web.) -* __Extension:__ ​An extension is an individual unit of functionality. Extensions  -are collected together in bundles, and may interact with other extensions.  +* __Bundle:__ A bundle is a collection of related functionality that can be +added to the application as a group. More concretely, a bundle is a directory +containing a JSON file declaring its contents, as well as JavaScript sources, +HTML templates, and other resources used to support that functionality. (The +term bundle is borrowed from [OSGi](http://www.osgi.org/) - which has also +inspired many of the concepts used in the framework layer. A familiarity with +OSGi, particularly Declarative Services, may be useful when working with Open +MCT Web.) +* __Extension:__ An extension is an individual unit of functionality. Extensions +are collected together in bundles, and may interact with other extensions. -The framework layer, loaded and initiated from ​`index.html`​, is the main point  -of entry for an application built on Open MCT Web. It is responsible for wiring  -together the application at run time (much of this responsibility is actually  -delegated to Angular); at a high­level, the framework does this by proceeding  -through four stages: +The framework layer, loaded and initiated from `index.html`, is the main point +of entry for an application built on Open MCT Web. It is responsible for wiring +together the application at run time (much of this responsibility is actually +delegated to Angular); at a high-level, the framework does this by proceeding +through four stages: -1. __Loading definitions:__​ JSON declarations are loaded for all bundles which  -will constitute the application, and wrapped in a useful API for subsequent  -stages.  -2. __Resolving extensions:__​ Any scripts which provide implementations for  -extensions exposed by bundles are loaded, using Require.  -3. __Registering extensions__​ Resolved extensions are registered with Angular,  -such that they can be used by the application at run­time. This stage includes  -both registration of Angular built­ins (directives, controllers, routes,  -constants, and services) as well as registration of non­Angular extensions.  -4. __Bootstrapping__​ The Angular application is bootstrapped; at that point,  -Angular takes over and populates the body of the page using the extensions that  -have been registered.  +1. __Loading definitions:__ JSON declarations are loaded for all bundles which +will constitute the application, and wrapped in a useful API for subsequent +stages. +2. __Resolving extensions:__ Any scripts which provide implementations for +extensions exposed by bundles are loaded, using Require. +3. __Registering extensions__ Resolved extensions are registered with Angular, +such that they can be used by the application at run-time. This stage includes +both registration of Angular built-ins (directives, controllers, routes, +constants, and services) as well as registration of non-Angular extensions. +4. __Bootstrapping__ The Angular application is bootstrapped; at that point, +Angular takes over and populates the body of the page using the extensions that +have been registered. ## Bundles -The basic configurable unit of Open MCT Web is the bundle. This term has been  -used a bit already; now we’ll get to a more formal definition.  +The basic configurable unit of Open MCT Web is the _bundle_. This term has been +used a bit already; now we'll get to a more formal definition. -A bundle is a directory which contains: +A bundle is a directory which contains: -* A bundle definition; a file named `​bundle.json​`. -* Subdirectories for sources, resources, and tests.  -* Optionally, a ​`README.md`​ Markdown file describing its contents (this is not  -used by Open MCT Web in any way, but it’s a helpful convention to follow.) +* A bundle definition; a file named `bundle.json`. +* Subdirectories for sources, resources, and tests. +* Optionally, a `README.md` Markdown file describing its contents (this is not +used by Open MCT Web in any way, but it's a helpful convention to follow.) -The bundle definition is the main point of entry for the bundle. The framework  -looks at this to determine which components need to be loaded and how they  +The bundle definition is the main point of entry for the bundle. The framework +looks at this to determine which components need to be loaded and how they interact. -A plugin in Open MCT Web is a bundle. The platform itself is also decomposed  -into bundles, each of which provides some category of functionality. The  -difference between a _bundle_ and a _plugin_ is purely a matter of the intended  -use; a plugin is just a bundle that is meant to be easily added or removed. When  -developing, it is typically more useful to think in terms of bundles.  -   +A plugin in Open MCT Web is a bundle. The platform itself is also decomposed +into bundles, each of which provides some category of functionality. The +difference between a _bundle_ and a _plugin_ is purely a matter of the intended +use; a plugin is just a bundle that is meant to be easily added or removed. When +developing, it is typically more useful to think in terms of bundles. + ### Configuring Active Bundles -  -To decide ​which​ bundles should be loaded, the framework loads a file named  -`bundles.json`​ (peer to the `index.html` file which serves the application) to  -determine which bundles should be loaded. This file should contain a single JSON  -array of strings, where each is the path to a bundle. These paths should not  -include ​bundle.json​ (this is implicit) or a trailing slash.  + +To decide which bundles should be loaded, the framework loads a file named +`bundles.json` (peer to the `index.html` file which serves the application) to +determine which bundles should be loaded. This file should contain a single JSON +array of strings, where each is the path to a bundle. These paths should not +include bundle.json (this is implicit) or a trailing slash. -For instance, if `bundles.json` contained:  +For instance, if `bundles.json` contained: - [  - "example/builtins",  - "example/extensions"  - ]  -   -...then the Open MCT Web framework would look for bundle definitions at  -`example/builtins/bundle.json`​ and `​example/extensions/bundle.json`​, relative  -to the path of `​index.html`​. No other bundles would be loaded.   + [ + "example/builtins", + "example/extensions" + ] + +...then the Open MCT Web framework would look for bundle definitions at +`example/builtins/bundle.json` and `example/extensions/bundle.json`, relative +to the path of `index.html`. No other bundles would be loaded. ### Bundle Definition -  -A bundle definition (the ​`bundle.json`​ file located within a bundle) contains a  -description of the bundle itself, as well as the information exposed by the  -bundle.  -  -This definition is expressed as a single JSON object with the following  -properties (all of which are optional, falling back to reasonable defaults): + +A bundle definition (the `bundle.json` file located within a bundle) contains a +description of the bundle itself, as well as the information exposed by the +bundle. + +This definition is expressed as a single JSON object with the following +properties (all of which are optional, falling back to reasonable defaults): -* `key​`: A machine­readable name for the bundle. (Currently used only in  -logging.)  -* `name​`: A human­readable name for the bundle. (Also only used in logging.)  -* `sources​`: Names a directory in which source scripts (which will implement  -extensions) are located. Defaults to “src”  -* `resources​`: Names a directory in which resource files (such as HTML templates,  -images, CS files, and other non­JavaScript files needed by this bundle) are  -located. Defaults to “res”   -* `libraries`​: Names a directory in which third­party libraries are located.  -Defaults to “lib”  -* `configuration`​: A bundle’s configuration object, which should be formatted as  -would be passed to require.config (see [RequireJS documentation](http://requirejs.org/docs/api.html​) );  -note that only paths and shim have been tested.  -* `extensions`​: An object containing key­value pairs, where keys are extension  -categories, and values are extension definitions. See the section on Extensions  -for more information.   +* `key`: A machine-readable name for the bundle. (Currently used only in +logging.) +* `name`: A human-readable name for the bundle. (Also only used in logging.) +* `sources`: Names a directory in which source scripts (which will implement +extensions) are located. Defaults to 'src' +* `resources`: Names a directory in which resource files (such as HTML templates, +images, CS files, and other non-JavaScript files needed by this bundle) are +located. Defaults to 'res' +* `libraries`: Names a directory in which third-party libraries are located. +Defaults to 'lib' +* `configuration`: A bundle's configuration object, which should be formatted as +would be passed to require.config (see [RequireJS documentation](http://requirejs.org/docs/api.html ) ); +note that only paths and shim have been tested. +* `extensions`: An object containing key-value pairs, where keys are extension +categories, and values are extension definitions. See the section on Extensions +for more information. -For example, the bundle definition for ​example/policy​ looks like:   +For example, the bundle definition for example/policy looks like: { - "name": "Example Policy",  - "description": "Provides an example of using policies.",  - "sources": "src",  - "extensions": {  - "policies": [  - {  - "implementation": "ExamplePolicy.js",  - "category": "action"  + "name": "Example Policy", + "description": "Provides an example of using policies.", + "sources": "src", + "extensions": { + "policies": [ + { + "implementation": "ExamplePolicy.js", + "category": "action" } - ]  - }  + ] + } } ### Bundle Directory Structure -  -In addition to the directories defined in the bundle definition, a bundle will  -typically contain other directories not used at run­time. Additionally, some  -useful development scripts (such as the command line build and the test suite)  -expect this directory structure to be in use, and may ignore options chosen by  -`b​undle.json`​. It is recommended that the directory structure described below be  -used for new bundles. + +In addition to the directories defined in the bundle definition, a bundle will +typically contain other directories not used at run-time. Additionally, some +useful development scripts (such as the command line build and the test suite) +expect this directory structure to be in use, and may ignore options chosen by +`b undle.json`. It is recommended that the directory structure described below be +used for new bundles. -* `src`​: Contains JavaScript sources for this bundle. May contain additional  -subdirectories to organize these sources; typically, these subdirectories are  -named to correspond to the extension categories they contain and/or support, but  -this is only a convention.  -* `res`​: Contains other files needed by this bundle, such as HTML templates. May  -contain additional subdirectories to organize these sources.  -* `lib`​: Contains JavaScript sources from third­party libraries. These are  -separated from bundle sources in order to ignore them during code style checking  -from the command line build. -* `test`​: Contains JavaScript sources implementing [Jasmine](http://jasmine.github.io/)  -tests, as well as a file named `​suite.json`​ describing which files to test.  -Should have the same folder structure as the `src` directory; see the section on  -automated testing for more information.  -  -For example, the directory structure for bundle ​`platform/commonUI/about` ​looks  -like:  +* `src`: Contains JavaScript sources for this bundle. May contain additional +subdirectories to organize these sources; typically, these subdirectories are +named to correspond to the extension categories they contain and/or support, but +this is only a convention. +* `res`: Contains other files needed by this bundle, such as HTML templates. May +contain additional subdirectories to organize these sources. +* `lib`: Contains JavaScript sources from third-party libraries. These are +separated from bundle sources in order to ignore them during code style checking +from the command line build. +* `test`: Contains JavaScript sources implementing [Jasmine](http://jasmine.github.io/) +tests, as well as a file named `suite.json` describing which files to test. +Should have the same folder structure as the `src` directory; see the section on +automated testing for more information. + +For example, the directory structure for bundle `platform/commonUI/about` looks +like: -INSERT DIAGRAM HERE + Platform + | + |-commonUI + | + +-about + | + |-res + | + |-src + | + |-test + | + |-bundle.json + | + +-README.md ## Extensions -While bundles provide groupings of related behaviors, the individual units of  -behavior are called extensions.  +While bundles provide groupings of related behaviors, the individual units of +behavior are called extensions. -Extensions belong to categories; an extension category is the machine­readable  -identifier used to identify groups of extensions. In the ​`extensions`​ property  -of a bundle definition, the keys are extension categories and the values are  -arrays of extension definitions.  -  +Extensions belong to categories; an extension category is the machine-readable +identifier used to identify groups of extensions. In the `extensions` property +of a bundle definition, the keys are extension categories and the values are +arrays of extension definitions. + ### General Extensions -Extensions are intended as a general­purpose mechanism for adding new types of  -functionality to Open MCT Web.  +Extensions are intended as a general-purpose mechanism for adding new types of +functionality to Open MCT Web. -An extension category is registered with Angular under the name of the  -extension, plus a suffix of two square brackets; so, an Angular service (or,  -generally, any other extension) can access the full set of registered  -extensions, from all bundles, by including this string (e.g. `types[]`​ to get  -all type definitions) in a dependency declaration.  +An extension category is registered with Angular under the name of the +extension, plus a suffix of two square brackets; so, an Angular service (or, +generally, any other extension) can access the full set of registered +extensions, from all bundles, by including this string (e.g. `types[]` to get +all type definitions) in a dependency declaration. -As a convention, extension categories are given single­word, plural nouns for  -names within Open MCT Web (e.g. ​`types`​.) This convention is not enforced by the  -platform in any way. For extension categories introduced by external plugins, it  -is recommended to prefix the extension category with a vendor identifier (or  -similar) followed by a dot, to avoid collisions.  -  +As a convention, extension categories are given single-word, plural nouns for +names within Open MCT Web (e.g. `types`.) This convention is not enforced by the +platform in any way. For extension categories introduced by external plugins, it +is recommended to prefix the extension category with a vendor identifier (or +similar) followed by a dot, to avoid collisions. + ### Extension Definitions -The properties used in extension definitions are typically unique to each  -category of extension; a few properties have standard interpretations by the  -platform.  +The properties used in extension definitions are typically unique to each +category of extension; a few properties have standard interpretations by the +platform. -* `implementation`​: Identifies a JavaScript source file (in the sources  -folder) which implements this extension. This JavaScript file is expected to  -contain an AMD module (see ​http://requirejs.org/docs/whyamd.html#amd​) which  -gives as its result a single constructor function.  -* `depends`​: An array of dependencies needed by this extension; these will be  -passed on to Angular’s [dependency injector](https://docs.angularjs.org/guide/di​)​.  -By default, this is treated as an empty array. Note that ​depends​ does not make  -sense without `implementation`​ (since these dependencies will be passed to the  -implementation when it is instantiated.)  -* `priority`​: A number or string indicating the priority order (see below) of  -this extension instance. Before an extension category is registered with  -AngularJS, the extensions of this category from all bundles will be concatenated  -into a single array, and then sorted by priority.  +* `implementation`: Identifies a JavaScript source file (in the sources +folder) which implements this extension. This JavaScript file is expected to +contain an AMD module (see http://requirejs.org/docs/whyamd.html#amd ) which +gives as its result a single constructor function. +* `depends`: An array of dependencies needed by this extension; these will be +passed on to Angular's [dependency injector](https://docs.angularjs.org/guide/di ) . +By default, this is treated as an empty array. Note that depends does not make +sense without `implementation` (since these dependencies will be passed to the +implementation when it is instantiated.) +* `priority`: A number or string indicating the priority order (see below) of +this extension instance. Before an extension category is registered with +AngularJS, the extensions of this category from all bundles will be concatenated +into a single array, and then sorted by priority. -Extensions do not need to have an implementation. If no implementation is  -provided, consumers of the extension category will receive the extension  -definition as a plain JavaScript object. Otherwise, they will receive the  -partialized (see below) constructor for that implementation, which will  -additionally have all properties from the extension definition attached.  +Extensions do not need to have an implementation. If no implementation is +provided, consumers of the extension category will receive the extension +definition as a plain JavaScript object. Otherwise, they will receive the +partialized (see below) constructor for that implementation, which will +additionally have all properties from the extension definition attached. #### Partial Construction -In general, extensions are intended to be implemented as constructor functions,  -which will be used elsewhere to instantiate new objects of that type. However,  -the Angular­supported method for dependency injection is (effectively)  -constructor­style injection; so, both declared dependencies and run­time  -arguments are competing for space in a constructor’s arguments.  +In general, extensions are intended to be implemented as constructor functions, +which will be used elsewhere to instantiate new objects of that type. However, +the Angular-supported method for dependency injection is (effectively) +constructor-style injection; so, both declared dependencies and run-time +arguments are competing for space in a constructor's arguments. -To resolve this, the Open MCT Web framework registers extension instances in a  -partially constructed​ form. That is, the constructor exposed by the extension’s  -implementation is effectively decomposed into two calls; the first takes the  -dependencies, and returns the constructor in its second form, which takes the  -remaining arguments.  +To resolve this, the Open MCT Web framework registers extension instances in a +partially constructed form. That is, the constructor exposed by the extension's +implementation is effectively decomposed into two calls; the first takes the +dependencies, and returns the constructor in its second form, which takes the +remaining arguments. -This means that, when writing implementations, the constructor function should  -be written to include all declared dependencies, followed by all run­time  -arguments. When using extensions, only the run­time arguments need to be  -provided.  -  +This means that, when writing implementations, the constructor function should +be written to include all declared dependencies, followed by all run-time +arguments. When using extensions, only the run-time arguments need to be +provided. + #### Priority -Within each extension category, registration occurs in priority order. An  -extension's priority may be specified as a ​`priority`​ property in its extension  -definition; this may be a number, or a symbolic string. Extensions are  -registered in reverse order (highest­priority first), and symbolic strings are  -mapped to the numeric values as follows:  +Within each extension category, registration occurs in priority order. An +extension's priority may be specified as a `priority` property in its extension +definition; this may be a number, or a symbolic string. Extensions are +registered in reverse order (highest-priority first), and symbolic strings are +mapped to the numeric values as follows: -* `fallback`​: Negative infinity. Used for extensions that are not intended for  -use (that is, they are meant to be overridden) but are present as an option of  -last resort.  -* `default​`: ­100. Used for extensions that are expected to be overridden, but  -need a useful default.  -* `none`​: 0. Also used if no priority is specified, or if an unknown or  -malformed priority is specified.  -* `optional`​: 100. Used for extensions that are meant to be used, but may be  -overridden.  -* `preferred​`: 1000. Used for extensions that are specifically intended to be  -used, but still may be overridden in principle.  -* `mandatory`​: Positive infinity. Used when an extension should definitely not  -be overridden.  +* `fallback`: Negative infinity. Used for extensions that are not intended for +use (that is, they are meant to be overridden) but are present as an option of +last resort. +* `default`: `-100`. Used for extensions that are expected to be overridden, but +need a useful default. +* `none`: `0`. Also used if no priority is specified, or if an unknown or +malformed priority is specified. +* `optional`: `100`. Used for extensions that are meant to be used, but may be +overridden. +* `preferred`: `1000`. Used for extensions that are specifically intended to be +used, but still may be overridden in principle. +* `mandatory`: Positive infinity. Used when an extension should definitely not +be overridden. -These symbolic names are chosen to support usage where many extensions may  -satisfy a given need, but only one may be used; in this case, as a convention it  -should be the lowest­ordered (highest­priority) extensions available. In other  -cases, a full set (or multi­element subset) of extensions may be desired, with a  -specific ordering; in these cases, it is preferable to specify priority  -numerically when declaring extensions, and to understand that extensions will be  -sorted according to these conventions when using them.  -   +These symbolic names are chosen to support usage where many extensions may +satisfy a given need, but only one may be used; in this case, as a convention it +should be the lowest-ordered (highest-priority) extensions available. In other +cases, a full set (or multi-element subset) of extensions may be desired, with a +specific ordering; in these cases, it is preferable to specify priority +numerically when declaring extensions, and to understand that extensions will be +sorted according to these conventions when using them. + ### Angular Built-ins -Several entities supported Angular are expressed and managed as extensions in  -Open MCT Web. Specifically, these extension categories are _directives​_,  -_​controllers​_, _services​_, _​constants​_, _​runs​_, and _​routes​_.  -  +Several entities supported Angular are expressed and managed as extensions in +Open MCT Web. Specifically, these extension categories are _directives_, +_controllers_, _services_, _constants_, _runs_, and _routes_. + #### Angular Directives -New [directives](​https://docs.angularjs.org/guide/directive​) may be  -registered as extensions of the ​directives​ category. Implementations of  -directives in this category should take only dependencies as arguments, and  -should return a directive definition object.  +New [directives]( https://docs.angularjs.org/guide/directive ) may be +registered as extensions of the directives category. Implementations of +directives in this category should take only dependencies as arguments, and +should return a directive definition object. -The directive’s name should be provided as a ​key​ property of its extension  -definition, in camel­case format.  -  +The directive's name should be provided as a key property of its extension +definition, in camel-case format. + #### Angular Controllers -New [controllers](​https://docs.angularjs.org/guide/controller​) may be registered  -as extensions of the ​controllers​ category. The implementation is registered  -directly as the controller; its only constructor arguments are its declared  -dependencies.  +New [controllers]( https://docs.angularjs.org/guide/controller ) may be registered +as extensions of the controllers category. The implementation is registered +directly as the controller; its only constructor arguments are its declared +dependencies. -The directive’s identifier should be provided as a ​key​ property of its extension  -definition.  -   -  +The directive's identifier should be provided as a key property of its extension +definition. + + #### Angular Services -New [services](https://docs.angularjs.org/guide/services​) may be registered as  -extensions of the ​services​ category. The implementation is registered via a  -[service call](​https://docs.angularjs.org/api/auto/service/$provide#service​), so  -it will be instantiated with the new​ operator.  +New [services](https://docs.angularjs.org/guide/services ) may be registered as +extensions of the services category. The implementation is registered via a +[service call]( https://docs.angularjs.org/api/auto/service/$provide#service ), so +it will be instantiated with the new operator. #### Angular Constants -Constant values may be registered as extensions of the [​constants​ category](https://docs.angularjs.org/api/ng/type/angular.Module#constant​).  -These extensions have no implementation; instead, they should contain a property  -​key​, which is the name under which the constant will be registered, and a  -property ​value​, which is the constant value that will be registered. +Constant values may be registered as extensions of the [ constants category](https://docs.angularjs.org/api/ng/type/angular.Module#constant ). +These extensions have no implementation; instead, they should contain a property + key , which is the name under which the constant will be registered, and a +property value , which is the constant value that will be registered. #### Angular Runs -In some cases, you want to register code to run as soon as the application  -starts; these can be registered as extensions of the [​runs​ category](https://docs.angularjs.org/api/ng/type/angular.Module#run​).  -Implementations registered in this category will be invoked (with their declared  -dependencies) when the Open MCT Web application first starts. (Note that, in  -this case, the implementation is better thought of as just a function, as  -opposed to a constructor function.) +In some cases, you want to register code to run as soon as the application +starts; these can be registered as extensions of the [ runs category](https://docs.angularjs.org/api/ng/type/angular.Module#run ). +Implementations registered in this category will be invoked (with their declared +dependencies) when the Open MCT Web application first starts. (Note that, in +this case, the implementation is better thought of as just a function, as +opposed to a constructor function.) #### Angular Routes -Extensions of category `​routes`​ will be registered with Angular’s [route provider](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider​).  -Extensions of this category have no implementations, and need only two  -properties in their definition:  +Extensions of category `routes` will be registered with Angular's [route provider](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider ). +Extensions of this category have no implementations, and need only two +properties in their definition: -* `when​`: The value that will be passed as the path argument to ​ -`$routeProvider.when`​; specifically, the string that will appear in the trailing  -part of the URL corresponding to this route. This property may be omitted, in  -which case this extension instance will be treated as the default route.  -* `templateUrl`​: A path to the template to render for this route. Specified as a  -path relative to the bundle’s resource directory (​`res​` by default.)  +* `when`: The value that will be passed as the path argument to `$routeProvider.when`; +specifically, the string that will appear in the trailing +part of the URL corresponding to this route. This property may be omitted, in +which case this extension instance will be treated as the default route. +* `templateUrl`: A path to the template to render for this route. Specified as a +path relative to the bundle's resource directory (`res` by default.) ### Composite Services Composite services are described in the [relevant section](../architecture/Framework.md#Composite-Services) of the framework guide. -A component should include the following properties in its extension definition: +A component should include the following properties in its extension definition: -* `provides`​: The symbolic identifier for the service that will be composed. The  - fully­composed service will be registered with Angular under this name. -* `type​`: One of `​provider`​, ​`aggregator​`, or `​decorator​` (as above)  +* `provides`: The symbolic identifier for the service that will be composed. The + fully-composed service will be registered with Angular under this name. +* `type`: One of `provider`, `aggregator` or `decorator` (as above) -In addition to any declared dependencies, _aggregators_ and _decorators_ both  -receive one more argument (immediately following declared dependencies) that is  -provided by the framework. For an aggregator, this will be an array of all  -providers of the same service (that is, with matching `​provides`​ properties);  -for a decorator, this will be whichever provider, decorator, or aggregator is  -next in the sequence of decorators.  +In addition to any declared dependencies, _aggregators_ and _decorators_ both +receive one more argument (immediately following declared dependencies) that is +provided by the framework. For an aggregator, this will be an array of all +providers of the same service (that is, with matching `provides` properties); +for a decorator, this will be whichever provider, decorator, or aggregator is +next in the sequence of decorators. -Services exposed by the Open MCT Web platform are often declared as composite  -services, as this form is open for a variety of common modifications.  +Services exposed by the Open MCT Web platform are often declared as composite +services, as this form is open for a variety of common modifications. # Core API -Most of Open MCT Web’s relevant API is provided and/or mediated by the  -framework; that is, much of developing for Open MCT Web is a matter of adding  -extensions which access other parts of the platform by means of dependency  -injection.  +Most of Open MCT Web's relevant API is provided and/or mediated by the +framework; that is, much of developing for Open MCT Web is a matter of adding +extensions which access other parts of the platform by means of dependency +injection. -The core bundle (`​platform/core`​) introduces a few additional object types meant  -to be passed along by other services.  +The core bundle (`platform/core`) introduces a few additional object types meant +to be passed along by other services. ## Domain Objects -Domain objects are the most fundamental component of Open MCT Web’s information  -model. A domain object is some distinct thing relevant to a user’s work flow,  -such as a telemetry channel, display, or similar. Open MCT Web is a tool for  -viewing, browsing, manipulating, and otherwise interacting with a graph of  -domain objects.  +Domain objects are the most fundamental component of Open MCT Web's information +model. A domain object is some distinct thing relevant to a user's work flow, +such as a telemetry channel, display, or similar. Open MCT Web is a tool for +viewing, browsing, manipulating, and otherwise interacting with a graph of +domain objects. -A domain object should be conceived of as the union of the following: +A domain object should be conceived of as the union of the following: -* __Identifier__: A machine­readable string that uniquely identifies the domain  -object within this application instance.  -* __Model__: The persistent state of the domain object. A domain object’s model  -is a JavaScript object that can be losslessly converted to JSON.  -* __Capabilities__: Dynamic behavior associated with the domain object.  -Capabilities are JavaScript objects which provide additional methods for  -interacting with the domain objects which expose those capabilities. Not all  -domain objects expose all capabilities.  +* __Identifier__: A machine-readable string that uniquely identifies the domain +object within this application instance. +* __Model__: The persistent state of the domain object. A domain object's model +is a JavaScript object that can be losslessly converted to JSON. +* __Capabilities__: Dynamic behavior associated with the domain object. +Capabilities are JavaScript objects which provide additional methods for +interacting with the domain objects which expose those capabilities. Not all +domain objects expose all capabilities. -At run­time, a domain object has the following interface: +At run-time, a domain object has the following interface: -* `getId()`​: Get the identifier for this domain object.  -* `getModel()`​: Get the plain state associated with this domain object. This  -will return a JavaScript object that can be losslessly converted to JSON. Note  -that the model returned here can be modified directly but should not be;  -instead, use the ​mutation capability.  -* `getCapability(key)`​: Get the specified capability associated with this domain  -object. This will return a JavaScript object whose interface is specific to the  -type of capability being requested. If the requested capability is not exposed  -by this domain object, this will return ​undefined​. -* `hasCapability(key)`​: Shorthand for checking if a domain object exposes the  -requested capability. -* `useCapability(key, arguments…)`​: Shorthand for  -`getCapability(key).invoke(arguments)`​, with additional checking between calls.  -If the provided capability has no invoke method, the return value here functions  -as `getCapability​`, including returning ​`undefined​` if the capability is not  +* `getId()`: Get the identifier for this domain object. +* `getModel()`: Get the plain state associated with this domain object. This +will return a JavaScript object that can be losslessly converted to JSON. Note +that the model returned here can be modified directly but should not be; +instead, use the mutation capability. +* `getCapability(key)`: Get the specified capability associated with this domain +object. This will return a JavaScript object whose interface is specific to the +type of capability being requested. If the requested capability is not exposed +by this domain object, this will return undefined . +* `hasCapability(key)`: Shorthand for checking if a domain object exposes the +requested capability. +* `useCapability(key, arguments )`: Shorthand for +`getCapability(key).invoke(arguments)`, with additional checking between calls. +If the provided capability has no invoke method, the return value here functions +as `getCapability` including returning `undefined` if the capability is not exposed. ## Actions -An ​`Action​` is behavior that can be performed upon/using a `​DomainObject​`. An  -Action has the following interface: +An `Action` is behavior that can be performed upon/using a `DomainObject`. An +Action has the following interface: -* `perform()`​: Do this action. For example, if one had an instance of a  -`​RemoveAction​`, invoking its ​perform​ method would cause the domain object which  -exposed it to be removed from its container. -* `getMetadata()`​: Get metadata associated with this action. Returns an object  -containing:  - * `name`​: Human­readable name. - * `description`​: Human­readable summary of this action.  - * `glyph​`: Single character to be displayed in Open MCT Web’s icon font set.  - * `context`​: The context in which this action is being performed (see below) +* `perform()`: Do this action. For example, if one had an instance of a +`RemoveAction` invoking its perform method would cause the domain object which +exposed it to be removed from its container. +* `getMetadata()`: Get metadata associated with this action. Returns an object +containing: + * `name`: Human-readable name. + * `description`: Human-readable summary of this action. + * `glyph`: Single character to be displayed in Open MCT Web's icon font set. + * `context`: The context in which this action is being performed (see below) -Action instances are typically obtained via a domain object’s `​action​`  -capability.  -  +Action instances are typically obtained via a domain object's `action` +capability. + ### Action Contexts -An action context is a JavaScript object with the following properties:  +An action context is a JavaScript object with the following properties: -* `domainObject​`: The domain object being acted upon.  -* `selectedObject`​: Optional; the selection at the time of action (e.g. the  -dragged object in a drag­and­drop operation.) +* `domainObject`: The domain object being acted upon. +* `selectedObject`: Optional; the selection at the time of action (e.g. the +dragged object in a drag-and-drop operation.) ## Telemetry -Telemetry series data in Open MCT Web is represented by a common interface, and  -packaged in a consistent manner to facilitate passing telemetry updates around  -multiple visualizations.  -  +Telemetry series data in Open MCT Web is represented by a common interface, and +packaged in a consistent manner to facilitate passing telemetry updates around +multiple visualizations. + ### Telemetry Requests -A telemetry request is a JavaScript object containing the following properties:  +A telemetry request is a JavaScript object containing the following properties: -* `source​`: A machine­readable identifier for the source of this telemetry. This  -is useful when multiple distinct data sources are in use side­by­side.  -* `key​`: A machine­readable identifier for a unique series of telemetry within  -that source.  -* _Note: This API is still under development; additional properties, such as  -start and end time, should be present in future versions of Open MCT Web._  +* `source`: A machine-readable identifier for the source of this telemetry. This +is useful when multiple distinct data sources are in use side-by-side. +* `key`: A machine-readable identifier for a unique series of telemetry within +that source. +* _Note: This API is still under development; additional properties, such as +start and end time, should be present in future versions of Open MCT Web._ -Additional properties may be included in telemetry requests which have specific  -interpretations for specific sources. +Additional properties may be included in telemetry requests which have specific +interpretations for specific sources. ### Telemetry Responses -When returned from the `​telemetryService​` (see [Services](#Services) section),  -telemetry series data will be packaged in a ​`source ­> key ­> TelemetrySeries​`  -fashion. That is, telemetry is passed in an object containing key­value pairs.  -Keys identify telemetry sources; values are objects containing additional  -key­value pairs. In this object, keys identify individual telemetry series (and  -match they ​`key​` property from corresponding requests) and values are  -`TelemetrySeries​` objects (see below.)  +When returned from the `telemetryService` (see [Services](#Services) section), +telemetry series data will be packaged in a `source -> key -> TelemetrySeries` +fashion. That is, telemetry is passed in an object containing key-value pairs. +Keys identify telemetry sources; values are objects containing additional +key-value pairs. In this object, keys identify individual telemetry series (and +match they `key` property from corresponding requests) and values are +`TelemetrySeries` objects (see below.) ### Telemetry Series -A telemetry series is a specific sequence of data, typically associated with a  -specific instrument. Telemetry is modeled as an ordered sequence of domain and  -range values, where domain values must be non­decreasing but range values do  -not. (Typically, domain values are interpreted as UTC timestamps in milliseconds  -relative to the UNIX epoch.) A series must have at least one domain and one  -range, and may have more than one. +A telemetry series is a specific sequence of data, typically associated with a +specific instrument. Telemetry is modeled as an ordered sequence of domain and +range values, where domain values must be non-decreasing but range values do +not. (Typically, domain values are interpreted as UTC timestamps in milliseconds +relative to the UNIX epoch.) A series must have at least one domain and one +range, and may have more than one. -Telemetry series data in Open MCT Web is expressed via the following  -`TelemetrySeries​` interface:  +Telemetry series data in Open MCT Web is expressed via the following +`TelemetrySeries` interface: -* `getPointCount()`​: Returns the number of unique points/samples in this series.  -* `getDomainValue(index, [domain])`:​ Get the domain value at the specified index​.  -If a second ​domain​ argument is provided, this is taken as a string identifier  -indicating which domain option (of, presumably, multiple) should be returned.  -* `getRangeValue(index, [range])`:​ Get the domain value at the specified ​index​.  -If a second ​range​ argument is provided, this is taken as a string identifier  -indicating which range option (of, presumably, multiple) should be returned.  -  +* `getPointCount()`: Returns the number of unique points/samples in this series. +* `getDomainValue(index, [domain])`: Get the domain value at the specified index . +If a second domain argument is provided, this is taken as a string identifier +indicating which domain option (of, presumably, multiple) should be returned. +* `getRangeValue(index, [range])`: Get the domain value at the specified index . +If a second range argument is provided, this is taken as a string identifier +indicating which range option (of, presumably, multiple) should be returned. + ### Telemetry Metadata -Domain objects which have associated telemetry also expose metadata about that  -telemetry; this is retrievable via the `​getMetadata()`​ of the telemetry  -capability. This will return a single JavaScript object containing the following  -properties:  +Domain objects which have associated telemetry also expose metadata about that +telemetry; this is retrievable via the `getMetadata()` of the telemetry +capability. This will return a single JavaScript object containing the following +properties: -* `source​`: The machine­readable identifier for the source of telemetry data for  -this object.  -* `key​`: The machine­readable identifier for the individual telemetry series.  -* `domains​`: An array of supported domains (see ​TelemetrySeries​ above.) Each  -domain should be expressed as an object which includes:  - * `key​`: Machine­readable identifier for this domain, as will be passed  - into a getDomainValue(index, domain)​ call.  - * `name​`: Human­readable name for this domain.  -* `ranges​`: An array of supported ranges; same format as ​domains​.  +* `source`: The machine-readable identifier for the source of telemetry data for +this object. +* `key`: The machine-readable identifier for the individual telemetry series. +* `domains`: An array of supported domains (see TelemetrySeries above.) Each +domain should be expressed as an object which includes: + * `key`: Machine-readable identifier for this domain, as will be passed into + a getDomainValue(index, domain) call. + * `name`: Human-readable name for this domain. +* `ranges`: An array of supported ranges; same format as domains . -Note that this metadata is also used as the prototype for telemetry requests  -made using this capability.  +Note that this metadata is also used as the prototype for telemetry requests +made using this capability. ## Types -A domain object’s type is represented as a ​Type​ object, which has the following  +A domain object's type is represented as a Type object, which has the following interface: -* `getKey()`​: Get the machine­readable identifier for this type.  -* `getName()​`: Get the human­readable name for this type.  -* `getDescription()`​: Get a human­readable summary of this type.  -* `getGlyph()​`: Get the single character to be rendered as an icon for this type  -in Open MCT Web’s custom font set.  -* `getInitialModel()`​: Get a domain object model that represents the initial  -state (before user specification of properties) for domain objects of this type.  -* `getDefinition()​`: Get the extension definition for this type, as a JavaScript  -object.  -* `instanceOf(type)`​: Check if this type is (or inherits from) a specified ​type​.  -This type can be either a string, in which case it is taken to be that type’s  -​key​, or it may be a ​Type instance.  -* `hasFeature(feature)`​: Returns a boolean value indicating whether or not this  -type supports the specified ​feature​, which is a symbolic string.  -* `getProperties()​`: Get all properties associated with this type, expressed as  -an array of ​TypeProperty​ instances.  -  +* `getKey()`: Get the machine-readable identifier for this type. +* `getName()`: Get the human-readable name for this type. +* `getDescription()`: Get a human-readable summary of this type. +* `getGlyph()`: Get the single character to be rendered as an icon for this type +in Open MCT Web's custom font set. +* `getInitialModel()`: Get a domain object model that represents the initial +state (before user specification of properties) for domain objects of this type. +* `getDefinition()`: Get the extension definition for this type, as a JavaScript +object. +* `instanceOf(type)`: Check if this type is (or inherits from) a specified type . +This type can be either a string, in which case it is taken to be that type's + key , or it may be a `Type` instance. +* `hasFeature(feature)`: Returns a boolean value indicating whether or not this +type supports the specified feature, which is a symbolic string. +* `getProperties()`: Get all properties associated with this type, expressed as +an array of `TypeProperty` instances. + ### Type Features -Features of a domain object type are expressed as symbolic string identifiers.  -They are defined in practice by usage; currently, the Open MCT Web platform only  -uses the ​creation feature to determine which domain object types should appear  -in the Create menu.  -  +Features of a domain object type are expressed as symbolic string identifiers. +They are defined in practice by usage; currently, the Open MCT Web platform only +uses the creation feature to determine which domain object types should appear +in the Create menu. + ### Type Properties -Types declare the user­editable properties of their domain object instances in  -order to allow the forms which appear in the Create and Edit Properties dialogs  -to be generated by the platform. A ​TypeProperty​ has the following interface: +Types declare the user-editable properties of their domain object instances in +order to allow the forms which appear in the __Create__ and __Edit Properties__ +dialogs to be generated by the platform. A `TypeProperty` has the following interface: -* `getValue(model)`​: Get the current value for this property, as it appears in  -the provided domain object ​model​.  -* `setValue(model, value)`​: Set a new ​value​ for this property in the provided  -domain object ​model​.  -* `getDefinition()​`: Get the raw definition for this property as a JavaScript  -object (as it was declared in this type’s extension definition.)  +* `getValue(model)`: Get the current value for this property, as it appears in +the provided domain object model. +* `setValue(model, value)`: Set a new value for this property in the provided +domain object model . +* `getDefinition()`: Get the raw definition for this property as a JavaScript +object (as it was declared in this type's extension definition.) -#Extension Categories +# Extension Categories -The information in this section is focused on registering new extensions of  -specific types; it does not contain a catalog of the extension instances of  -these categories provided by the platform. Relevant summaries there are provided  -in subsequent sections. -  +The information in this section is focused on registering new extensions of +specific types; it does not contain a catalog of the extension instances of +these categories provided by the platform. Relevant summaries there are provided +in subsequent sections. + ## Actions -An action is a thing that can be done to or using a domain object, typically as  -initiated by the user.  +An action is a thing that can be done to or using a domain object, typically as +initiated by the user. -An action’s implementation: +An action's implementation: -* Should take a single `​context​` argument in its constructor. (See Action  -Contexts, under Core API.) -* Should provide a method ​`perform​`, which causes the behavior associated with  -the action to occur. -* May provide a method `​getMetadata​`, which provides metadata associated with  -the action. If omitted, one will be provided by the platform which includes  -metadata from the action’s extension definition. -* May provide a static method ​`appliesTo(context)`​ (that is, a function  -available as a property of the implementation’s constructor itself), which will  -be used by the platform to filter out actions from contexts in which they are  -inherently inapplicable. +* Should take a single `context` argument in its constructor. (See Action +Contexts, under Core API.) +* Should provide a method `perform` which causes the behavior associated with +the action to occur. +* May provide a method `getMetadata` which provides metadata associated with +the action. If omitted, one will be provided by the platform which includes +metadata from the action's extension definition. +* May provide a static method `appliesTo(context)` (that is, a function +available as a property of the implementation's constructor itself), which will +be used by the platform to filter out actions from contexts in which they are +inherently inapplicable. -An action’s bundle definition (and/or `​getMetadata()`​ return value) may include: +An action's bundle definition (and/or `getMetadata()` return value) may include: -* `category​`: A string or dearray of strings identifying which category or  -categories an action falls into; used to determine when an action is displayed.  -Categories supported by the platform include:  - * `contextual​`: Actions in a context menu.  - * `view­control​`: Actions triggered by buttons in the top­right of Browse  - view.  -* `key​`: A machine­readable identifier for this action.  -* `name​`: A human­readable name for this action (e.g. to show in a menu)  -* `description​`: A human­readable summary of the behavior of this action.  -* `glyph`​: A single character which will be rendered in Open MCT Web’s custom  -font set as an icon for this action. +* `category`: A string or array of strings identifying which category or +categories an action falls into; used to determine when an action is displayed. +Categories supported by the platform include: + * `contextual`: Actions in a context menu. + * `view-control`: Actions triggered by buttons in the top-right of Browse + view. +* `key`: A machine-readable identifier for this action. +* `name`: A human-readable name for this action (e.g. to show in a menu) +* `description`: A human-readable summary of the behavior of this action. +* `glyph`: A single character which will be rendered in Open MCT Web's custom +font set as an icon for this action. ## Capabilities -Capabilities are exposed by domain objects (e.g. via the `g​etCapability​` method)  -but most commonly originate as extensions of this category. +Capabilities are exposed by domain objects (e.g. via the `getCapability` method) +but most commonly originate as extensions of this category. -Extension definitions for capabilities should include both an implementation,  -and a property named ​key​ whose value should be a string used as a  -machine­readable identifier for that capability, e.g. when passed as the  -argument to a domain object’s `​getCapability(key)` call. -  -A capability’s implementation should have methods specific to that capability;  -that is, there is no common format for capability implementations, aside from  -support for ​invoke​ via the ​useCapability​ shorthand. +Extension definitions for capabilities should include both an implementation, +and a property named key whose value should be a string used as a +machine-readable identifier for that capability, e.g. when passed as the +argument to a domain object's `getCapability(key)` call. + +A capability's implementation should have methods specific to that capability; +that is, there is no common format for capability implementations, aside from +support for invocation via the `useCapability` shorthand. -A capability’s implementation will take a single argument (in addition to any  -declared dependencies), which is the domain object that will expose that  +A capability's implementation will take a single argument (in addition to any +declared dependencies), which is the domain object that will expose that capability. -A capability’s implementation may also expose a static method ​`appliesTo(model)`  -which should return a boolean value, and will be used by the platform to filter  -down capabilities to those which should be exposed by specific domain objects,  -based on their domain object models.  -  +A capability's implementation may also expose a static method `appliesTo(model)` +which should return a boolean value, and will be used by the platform to filter +down capabilities to those which should be exposed by specific domain objects, +based on their domain object models. + ## Controls -Controls provide options for the ​mct­control​ directive.  -  -Six standard control types are included in the forms bundle: +Controls provide options for the `mct-control` directive. + +Six standard control types are included in the forms bundle: -* `textfield​`: An area to enter plain text. -* `select`​: A drop­down list of options. -* `checkbox​`: A box which may be checked/unchecked. -* `color​`: A color picker. -* `button`​: A button. -* `datetime`​: An input for UTC date/time entry; gives result as a UNIX  -timestamp, in milliseconds since start of 1970, UTC.  +* `textfield`: An area to enter plain text. +* `select`: A drop-down list of options. +* `checkbox`: A box which may be checked/unchecked. +* `color`: A color picker. +* `button`: A button. +* `datetime`: An input for UTC date/time entry; gives result as a UNIX +timestamp, in milliseconds since start of 1970, UTC. -New controls may be added as extensions of the controls category. Extensions of  -this category have two properties: +New controls may be added as extensions of the controls category. Extensions of +this category have two properties: -* `key`​: The symbolic name for this control (matched against the control field  -in rows of the form structure). -* `templateUrl`​: The URL to the control's Angular template, relative to the  -resources directory of the bundle which exposes the extension.  +* `key`: The symbolic name for this control (matched against the control field +in rows of the form structure). +* `templateUrl`: The URL to the control's Angular template, relative to the +resources directory of the bundle which exposes the extension. -Within the template for a control, the following variables will be included in  +Within the template for a control, the following variables will be included in scope: -* `ngModel`​: The model where form input will be stored. Notably we also need to  -look at field​ (see below) to determine which field in the model should be  -modified.  -* `ngRequired​`: True if input is required. -* `ngPattern​`: The pattern to match against (for text entry.) -* `options​`: The options for this control, as passed from the `​options​` property  -of an individual row definition.  -* `field​`: Name of the field in ​`ngModel​` which will hold the value for this  -control.  +* `ngModel`: The model where form input will be stored. Notably we also need to +look at field (see below) to determine which field in the model should be +modified. +* `ngRequired`: True if input is required. +* `ngPattern`: The pattern to match against (for text entry) +* `options`: The options for this control, as passed from the `options` property +of an individual row definition. +* `field`: Name of the field in `ngModel` which will hold the value for this +control. ## Gestures -A gesture is a user action which can be taken upon a representation of a domain  -object.  +A _gesture_ is a user action which can be taken upon a representation of a +domain object. -Examples of gestures included in the platform are: +Examples of gestures included in the platform are: -* `drag`​: For representations that can be used to initiate drag­and­drop  +* `drag`: For representations that can be used to initiate drag-and-drop composition. -* `drop​`: For representations that can be drop targets for drag­and­drop  -composition.  -* `menu`​: For representations that can be used to pop up a context menu.  -  -Gesture definitions have a property ​`key​` which is used as a machine­readable  -identifier for the gesture (e.g. `​drag​`, `​drop​`, `​menu​` above.)  -  -A gesture’s implementation is instantiated once per representation that uses the  -gesture. This class will receive the jqLite­wrapped ​`mct­representation​` element  -and the domain object being represented as arguments, and should do any  -necessary "wiring" (e.g. listening for events) during its constructor call. The  -gesture’s implementation may also expose an optional destroy()​ method which will  -be called when the gesture should be removed, to avoid memory leaks by way of  -unremoved listeners. +* `drop`: For representations that can be drop targets for drag-and-drop +composition. +* `menu`: For representations that can be used to pop up a context menu. + +Gesture definitions have a property `key` which is used as a machine-readable +identifier for the gesture (e.g. `drag`, `drop`, `menu` above.) + +A gesture's implementation is instantiated once per representation that uses the +gesture. This class will receive the jqLite-wrapped `mct-representation` element +and the domain object being represented as arguments, and should do any +necessary "wiring" (e.g. listening for events) during its constructor call. The +gesture's implementation may also expose an optional `destroy()` method which +will be called when the gesture should be removed, to avoid memory leaks by way +of unremoved listeners. ## Indicators -An indicator is an element that should appear in the status area at the bottom  -of a running Open MCT Web client instance.  +An indicator is an element that should appear in the status area at the bottom +of a running Open MCT Web client instance. ### Standard Indicators -  -Indicators which wish to appear in the common form of an icon­text pair should  -provide implementations with the following methods: + +Indicators which wish to appear in the common form of an icon-text pair should +provide implementations with the following methods: -* `getText()`​: Provides the human­readable text that will be displayed for this  -indicator.  -* `getGlyph()​`: Provides a single­character string that will be displayed as an  -icon in Open MCT Web’s custom font set.  -* `getDescription()`​: Provides a human­readable summary of the current state of  -this indicator; will be displayed in a tooltip on hover.  -* `getClass()`​: Get a CSS class that will be applied to this indicator.  -* `getTextClass()`​: Get a CSS class that will be applied to this indicator’s  -text portion.  -* `getGlyphClass()​`: Get a CSS class that will be applied to this indicator’s  -icon portion.  -* `configure()`​: If present, a configuration icon will appear to the right of  -this indicator, and clicking it will invoke this method.  -  -Note that all methods are optional, and are called directly from an Angular  -template, so they should be appropriate to run during digest cycles.  +* `getText()`: Provides the human-readable text that will be displayed for this +indicator. +* `getGlyph()`: Provides a single-character string that will be displayed as an +icon in Open MCT Web's custom font set. +* `getDescription()`: Provides a human-readable summary of the current state of +this indicator; will be displayed in a tooltip on hover. +* `getClass()`: Get a CSS class that will be applied to this indicator. +* `getTextClass()`: Get a CSS class that will be applied to this indicator's +text portion. +* `getGlyphClass()`: Get a CSS class that will be applied to this indicator's +icon portion. +* `configure()`: If present, a configuration icon will appear to the right of +this indicator, and clicking it will invoke this method. + +Note that all methods are optional, and are called directly from an Angular +template, so they should be appropriate to run during digest cycles. ### Custom Indicators -Indicators which wish to have an arbitrary appearance (instead of following the  -icon­text convention commonly used) may specify a ​`template`​ property in their  -extension definition. The value of this property will be used as the ​`key`​ for  -an `​mct­include​` directive (so should refer to an extension of category  -​templates​.) This template will be rendered to the status area. Indicators of  -this variety do not need to provide an implementation.  +Indicators which wish to have an arbitrary appearance (instead of following the +icon-text convention commonly used) may specify a `template` property in their +extension definition. The value of this property will be used as the `key` for +an `mct-include` directive (so should refer to an extension of category + templates .) This template will be rendered to the status area. Indicators of +this variety do not need to provide an implementation. ## Licenses -The extension category ​`licenses​` can be used to add entries into the “Licensing  -information” page, reachable from Open MCT Web’s About dialog.  +The extension category `licenses` can be used to add entries into the 'Licensing +information' page, reachable from Open MCT Web's About dialog. -Licenses may have the following properties, all of which are strings: +Licenses may have the following properties, all of which are strings: -* `name​`: Human­readable name of the licensed component. (e.g. “AngularJS”.) -* `version`​: Human­readable version of the licensed component. (e.g. “1.2.26”.) -* `description​`: Human­readable summary of the component. -* `author​`: Name or names of entities to which authorship should be attributed. -* `copyright​`: Copyright text to display for this component. -* `link​`: URL to full license text.  +* `name`: Human-readable name of the licensed component. (e.g. 'AngularJS'.) +* `version`: Human-readable version of the licensed component. (e.g. '1.2.26'.) +* `description`: Human-readable summary of the component. +* `author`: Name or names of entities to which authorship should be attributed. +* `copyright`: Copyright text to display for this component. +* `link`: URL to full license text. ## Policies -Policies are used to handle decisions made using Open MCT Web’s ​`policyService​`;  -examples of these decisions are determining the applicability of certain  -actions, or checking whether or not a domain object of one type can contain a  -domain object of a different type. See the section on the Policies for an  -overview of Open MCT Web’s policy model. +Policies are used to handle decisions made using Open MCT Web's `policyService`; +examples of these decisions are determining the applicability of certain +actions, or checking whether or not a domain object of one type can contain a +domain object of a different type. See the section on the Policies for an +overview of Open MCT Web's policy model. -A policy’s extension definition should include: +A policy's extension definition should include: -* `category​`: The machine­readable identifier for the type of policy decision  -being supported here. For a list of categories supported by the platform, see  -the section on Policies. Plugins may introduce and utilize additional policy  -categories not in that list.  -* `message​`: Optional; a human­readable message describing the policy, intended  -for display in situations where this specific policy has disallowed something.  -  -A policy’s implementation should include a single method, `​allow(candidate, -context)`​. The specific types used for `​candidate​` and `​context​` vary by policy  -category; in general, what is being asked is “is this candidate allowed in this  -context?” This method should return a boolean value.  +* `category`: The machine-readable identifier for the type of policy decision +being supported here. For a list of categories supported by the platform, see +the section on Policies. Plugins may introduce and utilize additional policy +categories not in that list. +* `message`: Optional; a human-readable message describing the policy, intended +for display in situations where this specific policy has disallowed something. + +A policy's implementation should include a single method, `allow(candidate, +context)`. The specific types used for `candidate` and `context` vary by policy +category; in general, what is being asked is 'is this candidate allowed in this +context?' This method should return a boolean value. -Open MCT Web’s policy model requires consensus; a policy decision is allowed  -when and only when all policies choose to allow it. As such, policies should  -generally be written to reject a certain case, and allow (by returning `true`)  -anything else.  -  +Open MCT Web's policy model requires consensus; a policy decision is allowed +when and only when all policies choose to allow it. As such, policies should +generally be written to reject a certain case, and allow (by returning `true`) +anything else. + ## Representations -A representation is an Angular template used to display a domain object. The  -`representations​` extension category is used to add options for the  -`​mct­representation` directive.  -  -A representation definition should include the following properties: +A representation is an Angular template used to display a domain object. The +`representations` extension category is used to add options for the +`mct-representation` directive. + +A representation definition should include the following properties: -* `key​`: The machine­readable name which identifies the representation.  -* `templateUrl​`: The path to the representation's Angular template. This path is  -relative to the bundle's resources directory.  -* `uses​`: Optional; an array of capability names. Indicates that this  -representation intends to use those capabilities of a domain object (via a  -​`useCapability​` call), and expects to find the latest results of that  -`​useCapability​` call in the scope of the presented template (under the same name  -as the capability itself.) Note that, if `​useCapability` returns a promise, this  -will be resolved before being placed in the representation’s scope.  -* `gestures​`: An array of keys identifying gestures (see the `​gestures​`  -extension category) which should be available upon this representation. Examples  -of gestures include `​drag​` (for representations that should act as draggable  -sources for drag­drop operations) and `​menu​` (for representations which should  -show a domain­object­specific context menu on right­click.)  +* `key`: The machine-readable name which identifies the representation. +* `templateUrl`: The path to the representation's Angular template. This path is +relative to the bundle's resources directory. +* `uses`: Optional; an array of capability names. Indicates that this +representation intends to use those capabilities of a domain object (via a +`useCapability` call), and expects to find the latest results of that +`useCapability` call in the scope of the presented template (under the same name +as the capability itself.) Note that, if `useCapability` returns a promise, this +will be resolved before being placed in the representation's scope. +* `gestures`: An array of keys identifying gestures (see the `gestures` +extension category) which should be available upon this representation. Examples +of gestures include `drag` (for representations that should act as draggable +sources for drag-drop operations) and `menu` (for representations which should +show a domain-object-specific context menu on right-click.) ### Representation Scope -While ​_representations​_ do not have implementations, per se, they do refer to  -Angular templates which need to interact with information (e.g. the domain  -object being represented) provided by the platform. This information is passed  -in through the template’s scope, such that simple representations may be created  -by providing only templates. (More complex representations will need controllers  -which are referenced from templates. See [https://docs.angularjs.org/guide/controller​]() -for more information on controllers in Angular.)  -  -A representation’s scope will contain: +While _representations_ do not have implementations, per se, they do refer to +Angular templates which need to interact with information (e.g. the domain +object being represented) provided by the platform. This information is passed +in through the template's scope, such that simple representations may be created +by providing only templates. (More complex representations will need controllers +which are referenced from templates. See [https://docs.angularjs.org/guide/controller ]() +for more information on controllers in Angular.) + +A representation's scope will contain: -* `domainObject​`: The represented domain object. -* `model​`: The domain object’s model. -* `configuration​`: An object containing configuration information for this  -representation (an empty object if there is no saved configuration.) The  -contents of this object are managed entirely by the view/representation which  -receives it.  -* `representation​`: An empty object, useful as a “scratch pad” for  -representation state.  -* `ngModel​`: An object passed through the ​ng­model​ attribute of the  -mct­representation​, if any.  -* `parameters`​: An object passed through the ​parameters​ attribute of the  -mct­representation​, if any.  -* Any capabilities requested by the ​uses​ property of the representation  +* `domainObject`: The represented domain object. +* `model`: The domain object's model. +* `configuration`: An object containing configuration information for this +representation (an empty object if there is no saved configuration.) The +contents of this object are managed entirely by the view/representation which +receives it. +* `representation`: An empty object, useful as a 'scratch pad' for +representation state. +* `ngModel`: An object passed through the ng-model attribute of the +`mct-representation` , if any. +* `parameters`: An object passed through the parameters attribute of the +`mct-representation`, if any. +* Any capabilities requested by the uses property of the representation definition. -  + ## Representers -The ​`representers​` extension category is used to add additional behavior to the  -`mct­representation​` directive. This extension category is intended primarily  -for use internal to the platform.  +The `representers` extension category is used to add additional behavior to the +`mct-representation` directive. This extension category is intended primarily +for use internal to the platform. -Unlike _represent​ations​_, which describe specific ways to represent domain  -objects, represent​ers ​are used to modify or augment the process of representing  -domain objects in general. For example, support for the  _gestures​_ extension  -category is added by a representer. +Unlike _representations_, which describe specific ways to represent domain +objects, _representers_ are used to modify or augment the process of +representing domain objects in general. For example, support for the _gestures_ +extension category is added by a _representer_. -A representer needs only provide an implementation. When an ​`mct­representation`  -is linked (see ​[https://docs.angularjs.org/guide/directive​]() or when the domain  -object being represented changes, a new representer of each declared type is  -instantiated. The constructor arguments for a representer are the same as the  -arguments to the link function in an Angular directive: ​`scope​`, the Angular  -scope for this representation; `​element​`, the jqLite­wrapped  -`mct­representation​` element, and `​attrs​`, a set of key­value pairs of that  -element’s attributes. Representers may wish to populate the scope, attach event  -listeners to the element, etc. +A representer needs only provide an implementation. When an `mct-representation` +is linked (see [https://docs.angularjs.org/guide/directive ]() or when the +domain object being represented changes, a new _representer_ of each declared +type is instantiated. The constructor arguments for a _representer_ are the same +as the arguments to the link function in an Angular directive: `scope` the +Angular scope for this representation; `element` the jqLite-wrapped +`mct-representation` element, and `attrs` a set of key-value pairs of that +element's attributes. _Representers_ may wish to populate the scope, attach +event listeners to the element, etc. -This implementation must provide a single method, `​destroy()`​, which will be  -invoked when the representer is no longer needed.  +This implementation must provide a single method, `destroy()`, which will be +invoked when the representer is no longer needed. ## Roots -The extension category ​`roots​` is used to provide root­level domain object  -models. Root­level domain objects appear at the top­level of the tree hierarchy.  -For example, the _My Items_ folder is added as an extension of this category.  +The extension category `roots` is used to provide root-level domain object +models. Root-level domain objects appear at the top-level of the tree hierarchy. +For example, the _My Items_ folder is added as an extension of this category. -Extensions of this category should have the following properties: +Extensions of this category should have the following properties: -* `id​`: The machine­readable identifier for the domaiwn object being exposed. -* `model`​: The model, as a JSON object, for the domain object being exposed.  +* `id`: The machine-readable identifier for the domaiwn object being exposed. +* `model`: The model, as a JSON object, for the domain object being exposed. ## Stylesheets -The ​stylesheets​ extension category is used to add CSS files to style the  -application. Extension definitions for this category should include one  +The stylesheets extension category is used to add CSS files to style the +application. Extension definitions for this category should include one property: -* `stylesheetUrl​`: Path and filename, including extension, for the stylesheet to  -include. This path is relative to the bundle’s resources folder (by default, ​ -`res​`)  -  -To control the order of CSS files, use ​priority​ (see the section on Extension  -Definitions above.)  +* `stylesheetUrl`: Path and filename, including extension, for the stylesheet to +include. This path is relative to the bundle's resources folder (by default, +`res`) + +To control the order of CSS files, use priority (see the section on Extension +Definitions above.) ## Templates -The ​`templates​` extension category is used to expose Angular templates under  -symbolic identifiers. These can then be utilized using the `​mct­include​`  -directive, which behaves similarly to `​ng­include​`, except that it uses these  -symbolic identifiers instead of paths. +The `templates` extension category is used to expose Angular templates under +symbolic identifiers. These can then be utilized using the `mct-include` +directive, which behaves similarly to `ng-include` except that it uses these +symbolic identifiers instead of paths. -A template’s extension definition should include the following properties: +A template's extension definition should include the following properties: -* `key​`: The machine­readable name which identifies this template, matched  -against the value given to the key attribute of the mct­include directive. -* `templateUrl​`: The path to the relevant Angular template. This path is  -relative to the bundle's resources directory.  +* `key`: The machine-readable name which identifies this template, matched +against the value given to the key attribute of the `mct-include` directive. +* `templateUrl`: The path to the relevant Angular template. This path is +relative to the bundle's resources directory. -Note that, when multiple templates are present with the same ​key​, the one with  -the highest priority will be used from mct­include. This behavior can be used to  -override templates exposed by the platform (to change the logo which appears in  -the bottom right, for instance.) +Note that, when multiple templates are present with the same key , the one with +the highest priority will be used from `mct-include`. This behavior can be used +to override templates exposed by the platform (to change the logo which appears +in the bottom right, for instance.) -Templates do not have implementations.  +Templates do not have implementations. ## Types -The ​types​ extension category describes types of domain objects which may appear  -within Open MCT Web. +The types extension category describes types of domain objects which may +appear within Open MCT Web. -A type’s extension definition should have the following properties: +A type's extension definition should have the following properties: -* `key​`: The machine­readable identifier for this domain object type. Will be  -stored to and matched against the ​type​ property of domain object models. -* `name​`: The human­readable name for this domain object type. -* `description​`: A human­readable summary of this domain object type. -* `glyph​`: A single character to be rendered as an icon in Open MCT Web’s custom  -font set.  -* `model`​: A domain object model, used as the initial state for created domain  -objects of this type (before any properties are specified.) -* `features​`: Optional; an array of strings describing features of this domain  -object type. Currently, only ​creation​ is recognized by the platform; this is  -used to determine that this type should appear in the Create menu. More  -generally, this is used to support the hasFeature(...)​ method of the ​type​  -capability.  -* `properties`​: An array describing individual properties of this domain object -(as should appear in the Create or the Edit Properties dialog.) Each property is  -described by an object containing the following properties: - * `control​`: The key of the control (see mct­control and the controls  - extension category) to use for editing this property.  - * `property​`: A string which will be used as the name of the property in the  - domain object’s model that the value for this property should be stored  - under. If this value should be stored in an object nested within the domain  - object model, then property should be specified as an array of strings  - identifying these nested objects and, finally, the property itself.  - * other properties as appropriate for a control of this type (each  - property’s definition will also be passed in as the structure for its  - control.) See documentation of ​mct­form​ for more detail on these properties. +* `key`: The machine-readable identifier for this domain object type. Will be +stored to and matched against the type property of domain object models. +* `name`: The human-readable name for this domain object type. +* `description`: A human-readable summary of this domain object type. +* `glyph`: A single character to be rendered as an icon in Open MCT Web's custom +font set. +* `model`: A domain object model, used as the initial state for created domain +objects of this type (before any properties are specified.) +* `features`: Optional; an array of strings describing features of this domain +object type. Currently, only creation is recognized by the platform; this is +used to determine that this type should appear in the Create menu. More +generally, this is used to support the `hasFeature(...)` method of the type +capability. +* `properties`: An array describing individual properties of this domain object +(as should appear in the _Create_ or the _Edit Properties_ dialog.) Each +property is described by an object containing the following properties: + * `control`: The key of the control (see `mct-control` and the `controls` + [extension category](#Controls)) to use for editing this property. + * `property`: A string which will be used as the name of the property in the + domain object's model that the value for this property should be stored + under. If this value should be stored in an object nested within the domain + object model, then property should be specified as an array of strings + identifying these nested objects and, finally, the property itself. + * other properties as appropriate for a control of this type (each + property's definition will also be passed in as the structure for its + control.) See documentation of mct-form for more detail on these + properties. -Types do not have implementations.  -  +Types do not have implementations. + ## Versions -The ​versions​ extension category is used to introduce line items in Open MCT  -Web’s About dialog. These should have the following properties:  +The versions extension category is used to introduce line items in Open MCT +Web's About dialog. These should have the following properties: -* `name​`: The name of this line item, as should appear in the left­hand side of  -the list of version information in the About dialog. -* `value​`: The value which should appear to the right of the name in the About  +* `name`: The name of this line item, as should appear in the left-hand side of +the list of version information in the About dialog. +* `value`: The value which should appear to the right of the name in the About dialog. -To control the ordering of line items within the About dialog, use `​priority​`.  -(See section on Extension Definitions above.)  +To control the ordering of line items within the About dialog, use `priority`. +(See section on [Extension Definitions](#ExtensionDefinitions) above.) -This extension category does not have implementations.  -  +This extension category does not have implementations. + ## Views -The ​views​ extension category is used to determine which options appear to the  -user as available views of domain objects of specific types. A view’s extension  -definition has the same properties as a representation (and views can be  -utilized via ​mct­representation​); additionally: +The views extension category is used to determine which options appear to the +user as available views of domain objects of specific types. A view's extension +definition has the same properties as a representation (and views can be +utilized via `mct-representation`); additionally: -* `name​`: The human­readable name for this view type. -* description​: A human­readable summary of this view type. -* `glyph​`: A single character to be rendered as an icon in Open MCT Web’s custom  -font set. -* `type`​: Optional; if present, this representation is only applicable for  -domain object’s of this type. -* `needs​`: Optional array of strings; if present, this representation is only  -applicable for domain objects which have the capabilities identified by these  -strings.  -* `delegation​`: Optional boolean, intended to be used in conjunction with ​ -`needs​`;  if present, allow required capabilities to be satisfied by means of  -capability delegation. (See the ​delegation​ capability, in the Capabilities  -section.) -* `toolbar​`: Optional; a definition for the toolbar which may appear in a  -toolbar when using this view in Edit mode. This should be specified as a  -structure for ​mct­toolbar​, with additional properties available for each item in  -that toolbar:  - * `property​`: A property name. This will refer to a property in the view’s  - current selection; that property on the selected object will be modifiable  - as the `ng­model`​ of the displayed control in the toolbar. If the value of  - the property is a function, it will be used as a getter­setter (called with  - no arguments to use as a getter, called with a value to use as a setter.)  - * `method​`: A method to invoke (again, on the selected object) from the  - toolbar control. Useful particularly for buttons (which don’t edit a single  - property, necessarily.) +* `name`: The human-readable name for this view type. +* description : A human-readable summary of this view type. +* `glyph`: A single character to be rendered as an icon in Open MCT Web's custom +font set. +* `type`: Optional; if present, this representation is only applicable for +domain object's of this type. +* `needs`: Optional array of strings; if present, this representation is only +applicable for domain objects which have the capabilities identified by these +strings. +* `delegation`: Optional boolean, intended to be used in conjunction with +`needs`; if present, allow required capabilities to be satisfied by means of +capability delegation. (See [Delegation](#Delegation)) +* `toolbar`: Optional; a definition for the toolbar which may appear in a +toolbar when using this view in Edit mode. This should be specified as a +structure for mct-toolbar , with additional properties available for each item in +that toolbar: + * `property`: A property name. This will refer to a property in the view's + current selection; that property on the selected object will be modifiable + as the `ng-model` of the displayed control in the toolbar. If the value of + the property is a function, it will be used as a getter-setter (called with + no arguments to use as a getter, called with a value to use as a setter.) + * `method`: A method to invoke (again, on the selected object) from the + toolbar control. Useful particularly for buttons (which don't edit a single + property, necessarily.) ### View Scope -Views do not have implementations, but do get the same properties in scope that  -are provided for `​representations​`.  +Views do not have implementations, but do get the same properties in scope that +are provided for `representations`. -When a view is in Edit mode, this scope will additionally contain: +When a view is in Edit mode, this scope will additionally contain: -* `commit()`​: A function which can be invoked to mark any changes to the view’s  - configuration​ as ready to persist. -* `selection​`: An object representing the current selection state.  +* `commit()`: A function which can be invoked to mark any changes to the view's + configuration as ready to persist. +* `selection`: An object representing the current selection state. #### Selection State -A view’s selection state is, conceptually, a set of JavaScript objects. The  -presence of methods/properties on these objects determine which toolbar controls  -are visible, and what state they manage and/or behavior they invoke.  +A view's selection state is, conceptually, a set of JavaScript objects. The +presence of methods/properties on these objects determine which toolbar controls +are visible, and what state they manage and/or behavior they invoke. -This set may contain up to two different objects: The  _view proxy​_, which is  -used to make changes to the view as a whole, and the _​selected object​_, which is  -used to represent some state within the view. (Future versions of Open MCT Web  -may support multiple selected objects.)  +This set may contain up to two different objects: The _view proxy _, which is +used to make changes to the view as a whole, and the _ selected object _, which is +used to represent some state within the view. (Future versions of Open MCT Web +may support multiple selected objects.) -The ​`selection​` object made available during Edit mode has the following  -methods:  +The `selection` object made available during Edit mode has the following +methods: -* `proxy([object])`​: Get (or set, if called with an argument) the current view  -proxy.   -* `select(object)​`: Make this object the selected object.  -* `deselect()`​: Clear the currently selected object.  -* `get()​`: Get the currently selected object. Returns ​undefined​ if there is no  -currently selected object. -* `selected(object)`​: Check if the JavaScript object is currently in the  -selection set. Returns ​true​ if the object is either the currently selected  -object, or the current view proxy.  -* `all()​`: Get an array of all objects in the selection state. Will include  -either or both of the view proxy and selected object.  +* `proxy([object])`: Get (or set, if called with an argument) the current view +proxy. +* `select(object)`: Make this object the selected object. +* `deselect()`: Clear the currently selected object. +* `get()`: Get the currently selected object. Returns undefined if there is no +currently selected object. +* `selected(object)`: Check if the JavaScript object is currently in the +selection set. Returns true if the object is either the currently selected +object, or the current view proxy. +* `all()`: Get an array of all objects in the selection state. Will include +either or both of the view proxy and selected object. # Directives -Open MCT Web defines several Angular directives that are intended for use both  -internally within the platform, and by plugins.  +Open MCT Web defines several Angular directives that are intended for use both +internally within the platform, and by plugins. ## Before Unload -The `​mct­before­unload​` directive is used to listen for (and prompt for user  -confirmation) of navigation changes in the browser. This includes reloading,  -following links out of Open MCT Web, or changing routes. It is used to hook into  -both `​onbeforeunload​` event handling as well as route changes from within  +The `mct-before-unload` directive is used to listen for (and prompt for user +confirmation) of navigation changes in the browser. This includes reloading, +following links out of Open MCT Web, or changing routes. It is used to hook into +both `onbeforeunload` event handling as well as route changes from within Angular. -This directive is useable as an attribute. Its value should be an Angular  -expression. When an action that would trigger an unload and/or route change  -occurs, this Angular expression is evaluated. Its result should be a message to  -display to the user to confirm their navigation change; if this expression  -evaluates to a falsy value, no message will be displayed.  -  +This directive is useable as an attribute. Its value should be an Angular +expression. When an action that would trigger an unload and/or route change +occurs, this Angular expression is evaluated. Its result should be a message to +display to the user to confirm their navigation change; if this expression +evaluates to a falsy value, no message will be displayed. + ## Chart -The `​mct­chart​` directive is used to support drawing of simple charts. It is  -present to support the Plot view, and its functionality is limited to the  -functionality that is relevant for that view. +The `mct-chart` directive is used to support drawing of simple charts. It is +present to support the Plot view, and its functionality is limited to the +functionality that is relevant for that view. -This directive is used at the element level and takes one attribute, `​draw​`,  -which is an Angular expression which will should evaluate to a drawing object.  -This drawing object should contain the following properties: +This directive is used at the element level and takes one attribute, `draw` +which is an Angular expression which will should evaluate to a drawing object. +This drawing object should contain the following properties: -* `dimensions​`: The size, in logical coordinates, of the chart area. A  -two­element array or numbers.  -* `origin​`: The position, in logical coordinates, of the lower­left corner of  -the chart area. A two­element array or numbers.  -* `lines​`: An array of lines (e.g. as a plot line) to draw, where each line is  -expressed as an object containing:  - * `buffer`​: A Float32Array containing points in the line, in logical  - coordinates, in sequential x,y pairs.  - * `color​`: The color of the line, as a four­element RGBA array, where  - each element is a number in the range of 0.0­1.0.  - * `points​`: The number of points in the line.  -* `boxes`​: An array of rectangles to draw in the chart area. Each is an object  -containing:  - * `start​`: The first corner of the rectangle, as a two­element array of - numbers, in logical coordinates.  - * `end​`: The opposite corner of the rectangle, as a two­element array of  - numbers, in logical coordinates. color​: The color of the line, as a  - four­element RGBA array, where each element is a number in the range of  - 0.0­1.0.  +* `dimensions`: The size, in logical coordinates, of the chart area. A +two-element array or numbers. +* `origin`: The position, in logical coordinates, of the lower-left corner of +the chart area. A two-element array or numbers. +* `lines`: An array of lines (e.g. as a plot line) to draw, where each line is +expressed as an object containing: + * `buffer`: A Float32Array containing points in the line, in logical + coordinates, in sequential x,y pairs. + * `color`: The color of the line, as a four-element RGBA array, where + each element is a number in the range of 0.0-1.0. + * `points`: The number of points in the line. +* `boxes`: An array of rectangles to draw in the chart area. Each is an object +containing: + * `start`: The first corner of the rectangle, as a two-element array of + numbers, in logical coordinates. + * `end`: The opposite corner of the rectangle, as a two-element array of + numbers, in logical coordinates. color : The color of the line, as a + four-element RGBA array, where each element is a number in the range of + 0.0-1.0. -While ​`mct­chart​` is intended to support plots specifically, it does perform  -some useful management of canvas objects (e.g. choosing between WebGL and Canvas  -2D APIs for drawing based on browser support) so its usage is recommended when  -its supported drawing primitives are sufficient for other charting tasks.  -  +While `mct-chart` is intended to support plots specifically, it does perform +some useful management of canvas objects (e.g. choosing between WebGL and Canvas +2D APIs for drawing based on browser support) so its usage is recommended when +its supported drawing primitives are sufficient for other charting tasks. + ## Container -The ​`mct­container​` is similar to the `​mct­include​` directive insofar as it allows  -templates to be referenced by symbolic keys instead of by URL. Unlike  -`​mct­include​`, it supports transclusion. +The `mct-container` is similar to the `mct-include` directive insofar as it allows +templates to be referenced by symbolic keys instead of by URL. Unlike +`mct-include` it supports transclusion. -Unlike `​mct­include​`, `​mct­container​` accepts a ​key​ as a plain string attribute,  -instead of as an Angular expression. +Unlike `mct-include` `mct-container` accepts a key as a plain string attribute, +instead of as an Angular expression. ## Control -The `​mct­control​` directive is used to display user input elements. Several  -controls are included with the platform to wrap default input types. This  -directive is primarily intended for internal use by the `​mct­form​` and  -`​mct­toolbar​` directives.  +The `mct-control` directive is used to display user input elements. Several +controls are included with the platform to wrap default input types. This +directive is primarily intended for internal use by the `mct-form` and +`mct-toolbar` directives. -When using `​mct­control​`, the attributes `​ng­model​`, `​ng­disabled​`,  -`​ng­required​`, and `​ng­pattern​` may also be used. These have the usual meaning -(as they would for an input element) except for `​ng­model​`; when used, it will  -actually be ​`ngModel[field]`​ (see below) that is two­way bound by this control.  -This allows `​mct­control​` elements to more easily delegate to other  -`​mct­control​` instances, and also facilitates usage for generated forms.  +When using `mct-control` the attributes `ng-model` `ng-disabled` +`ng-required` and `ng-pattern` may also be used. These have the usual meaning +(as they would for an input element) except for `ng-model`; when used, it will +actually be `ngModel[field]` (see below) that is two-way bound by this control. +This allows `mct-control` elements to more easily delegate to other +`mct-control` instances, and also facilitates usage for generated forms. -This directive supports the following additional attributes, all specified as  -Angular expressions: +This directive supports the following additional attributes, all specified as +Angular expressions: -* `key​`: A machine­readable identifier for the specific type of control to  +* `key`: A machine-readable identifier for the specific type of control to display. -* `options`​: A set of options to display in this control. -* `structure​`: In practice, contains the definition object which describes this  -form row or toolbar item. Used to pass additional control­specific parameters.  -* `field​`: The field in the `​ngModel​` under which to read/store the property  -associated with this control.  +* `options`: A set of options to display in this control. +* `structure`: In practice, contains the definition object which describes this +form row or toolbar item. Used to pass additional control-specific parameters. +* `field`: The field in the `ngModel` under which to read/store the property +associated with this control. ## Drag -The ​`mct­drag​` directive is used to support drag­based gestures on HTML  -elements. Note that this is not “drag” in the “drag­and­drop” sense, but “drag”  -in the more general “mouse down, mouse move, mouse up” sense.  +The `mct-drag` directive is used to support drag-based gestures on HTML +elements. Note that this is not 'drag' in the 'drag-and-drop' sense, but 'drag' +in the more general 'mouse down, mouse move, mouse up' sense. -This takes the form of three attributes:  +This takes the form of three attributes: -* `mct­drag​`: An Angular expression to evaluate during drag movement. -* `mct­drag­down`​: An Angular expression to evaluate when the drag starts. -* `mct­drag­up​`: An Angular expression to evaluate when the drag ends. +* `mct-drag`: An Angular expression to evaluate during drag movement. +* `mct-drag-down`: An Angular expression to evaluate when the drag starts. +* `mct-drag-up`: An Angular expression to evaluate when the drag ends. -In each case, a variable ​`delta​` will be provided to the expression; this is a  -two­element array or the horizontal and vertical pixel offset of the current  -mouse position relative to the mouse position where dragging began.  +In each case, a variable `delta` will be provided to the expression; this is a +two-element array or the horizontal and vertical pixel offset of the current +mouse position relative to the mouse position where dragging began. ## Form -The ​`mct­form​` directive is used to generate forms using a declarative structure,  -and to gather back user input. It is applicable at the element level and  -supports the following attributes:  +The `mct-form` directive is used to generate forms using a declarative structure, +and to gather back user input. It is applicable at the element level and +supports the following attributes: -* `ng­model​`: The object which should contain the full form input. Individual  -fields in this model are bound to individual controls; the names used for these  -fields are provided in the form structure (see below). -* `structure`​: The structure of the form; e.g. sections, rows, their names, and  -so forth. The value of this attribute should be an Angular expression.  -* `name​`: The name in the containing scope under which to publish form  -"meta­state", e.g. `$valid​`, `​$dirty​`, etc. This is as the behavior of `​ng­form​`.  -Passed as plain text in the attribute.  +* `ng-model`: The object which should contain the full form input. Individual +fields in this model are bound to individual controls; the names used for these +fields are provided in the form structure (see below). +* `structure`: The structure of the form; e.g. sections, rows, their names, and +so forth. The value of this attribute should be an Angular expression. +* `name`: The name in the containing scope under which to publish form +"meta-state", e.g. `$valid` `$dirty` etc. This is as the behavior of `ng-form`. +Passed as plain text in the attribute. ### Form Structure -Forms in Open MCT Web have a common structure to permit consistent display. A  -form is broken down into sections, which will be displayed in groups; each  -section is broken down into rows, each of which provides a control for a single  -property. Input from this form is two­way bound to the object passed via  -​`ng­model​`.  +Forms in Open MCT Web have a common structure to permit consistent display. A +form is broken down into sections, which will be displayed in groups; each +section is broken down into rows, each of which provides a control for a single +property. Input from this form is two-way bound to the object passed via +`ng-model`. -A form’s structure is represented by a JavaScript object in the following form: +A form's structure is represented by a JavaScript object in the following form: - {  - "name": ... title to display for the form, as a string ...,  + { + "name": ... title to display for the form, as a string ..., "sections": [ - {  - "name": ... title to display for the section ...,  - "rows": [  - {  - "name": ... title to display for this row ..., - "control": ... symbolic key for the control ...,  - "key": ... field name in ng­model ...  - "pattern": ... optional, reg exp to match against ...  - "required": ... optional boolean ...  - "options": [  - "name": ... name to display (e.g. in a select) ...,  - "value": ... value to store in the model ...  - ]  - },  - ... and other rows ...  - ]  - },  - ... and other sections ...  - ]  - }  + { + "name": ... title to display for the section ..., + "rows": [ + { + "name": ... title to display for this row ..., + "control": ... symbolic key for the control ..., + "key": ... field name in ng-model ... + "pattern": ... optional, reg exp to match against ... + "required": ... optional boolean ... + "options": [ + "name": ... name to display (e.g. in a select) ..., + "value": ... value to store in the model ... + ] + }, + ... and other rows ... + ] + }, + ... and other sections ... + ] + } -Note that ​`pattern​` may be specified as a string, to simplify storing for  -structures as JSON when necessary. The string should be given in a form  -appropriate to pass to a ​`RegExp` constructor.  +Note that `pattern` may be specified as a string, to simplify storing for +structures as JSON when necessary. The string should be given in a form +appropriate to pass to a `RegExp` constructor. ### Form Controls -A few standard control types are included in the ​platform/forms​ bundle:  +A few standard control types are included in the platform/forms bundle: -* `textfield​`: An area to enter plain text.  -* `select​`: A drop­down list of options.  -* `checkbox`​: A box which may be checked/unchecked.  -* `color​`: A color picker.  -* `button​`: A button.  -* `datetime​`: An input for UTC date/time entry; gives result as a UNIX  -timestamp, in milliseconds since start of 1970, UTC.  +* `textfield`: An area to enter plain text. +* `select`: A drop-down list of options. +* `checkbox`: A box which may be checked/unchecked. +* `color`: A color picker. +* `button`: A button. +* `datetime`: An input for UTC date/time entry; gives result as a UNIX +timestamp, in milliseconds since start of 1970, UTC. -##Include +## Include -The ​`mct­include​` directive is similar to ​ng­include​, except that it takes a  -symbolic identifier for a template instead of a URL. Additionally, templates  -included via ​mct­include will have an isolated scope.  +The `mct-include` directive is similar to ng-include , except that it takes a +symbolic identifier for a template instead of a URL. Additionally, templates +included via mct-include will have an isolated scope. -The directive should be used at the element level and supports the following  -attributes, all of which are specified as Angular expressions:  +The directive should be used at the element level and supports the following +attributes, all of which are specified as Angular expressions: -* `key​`: Machine­readable identifier for the template (of extension category ​ -templates​) to be displayed.  -* `ng­model`​: _Optional_; will be passed into the template’s scope as ​ngModel​.  -Intended usage is for two­way bound user input. -* `parameters​`: _Optional_; will be passed into the template’s scope as ​parameters​.  -Intended usage is for template­specific display parameters.  +* `key`: Machine-readable identifier for the template (of extension category +templates ) to be displayed. +* `ng-model`: _Optional_; will be passed into the template's scope as `ngModel`. +Intended usage is for two-way bound user input. +* `parameters`: _Optional_; will be passed into the template's scope as +parameters. Intended usage is for template-specific display parameters. ## Representation -The `​mct­representation​` directive is used to include templates which  -specifically represent domain objects. Usage is similar to `​mct­include​`.  +The `mct-representation` directive is used to include templates which +specifically represent domain objects. Usage is similar to `mct-include`. -The directive should be used at the element level and supports the following  -attributes, all of which are specified as Angular expressions: +The directive should be used at the element level and supports the following +attributes, all of which are specified as Angular expressions: -* `key​`: Machine­readable identifier for the representation (of extension  -category representations​ or ​views​) to be displayed.  -* `mct­object​`: The domain object being represented.  -* `ng­model​`: Optional; will be passed into the template’s scope as ​ngModel​.  -Intended usage is for two­way bound user input.  -* `parameters​`: Optional; will be passed into the template’s scope as ​ -parameters​. Intended usage is for template­specific display parameters.  +* `key`: Machine-readable identifier for the representation (of extension +category _representations_ or _views_ ) to be displayed. +* `mct-object`: The domain object being represented. +* `ng-model`: Optional; will be passed into the template's scope as `ngModel`. +Intended usage is for two-way bound user input. +* `parameters`: Optional; will be passed into the template's scope as +parameters . Intended usage is for template-specific display parameters. ## Resize -The `​mct­resize​` directive is used to monitor the size of an HTML element. It is  -specified as an attribute whose value is an Angular expression that will be  -evaluated when the size of the HTML element changes. This expression will be  -provided a single variable, ​`bounds​`, which is an object containing two  -properties, `​width​` and `​height​`, describing the size in pixels of the element. +The `mct-resize` directive is used to monitor the size of an HTML element. It is +specified as an attribute whose value is an Angular expression that will be +evaluated when the size of the HTML element changes. This expression will be +provided a single variable, `bounds` which is an object containing two +properties, `width` and `height` describing the size in pixels of the element. -When using this directive, an attribute `​mct­resize­interval​` may optionally be  -provided. Its value is an Angular expression describing the number of  -milliseconds to wait before next checking the size of the HTML element; this  -expression is evaluated when the directive is linked and reevaluated whenever  -the size is checked. +When using this directive, an attribute `mct-resize-interval` may optionally be +provided. Its value is an Angular expression describing the number of +milliseconds to wait before next checking the size of the HTML element; this +expression is evaluated when the directive is linked and reevaluated whenever +the size is checked. ## Scroll -The ​`mct­scroll­x​` and `​mct­scroll­y​` directives are used to both monitor and  -control the horizontal and vertical scroll bar state of an element,  -respectively. They are intended to be used as attributes whose values are  -assignable Angular expressions which two­way bind to the scroll bar state. +The `mct-scroll-x` and `mct-scroll-y` directives are used to both monitor and +control the horizontal and vertical scroll bar state of an element, +respectively. They are intended to be used as attributes whose values are +assignable Angular expressions which two-way bind to the scroll bar state. ## Toolbar -The `​mct­toolbar​` directive is used to generate toolbars using a declarative  -structure, and to gather back user input. It is applicable at the element level  -and supports the following attributes:  +The `mct-toolbar` directive is used to generate toolbars using a declarative +structure, and to gather back user input. It is applicable at the element level +and supports the following attributes: -* `ng­model​`: The object which should contain the full toolbar input. Individual  -fields in this model are bound to individual controls; the names used for these  -fields are provided in the form structure (see below).  -* `structure​`: The structure of the toolbar; e.g. sections, rows, their names, and  -so forth. The value of this attribute should be an Angular expression. -* `name​`: The name in the containing scope under which to publish form  -"meta­state", e.g. `$valid​`, `​$dirty​`, etc. This is as the behavior of  -`​ng­form​`. Passed as plain text in the attribute.  +* `ng-model`: The object which should contain the full toolbar input. Individual +fields in this model are bound to individual controls; the names used for these +fields are provided in the form structure (see below). +* `structure`: The structure of the toolbar; e.g. sections, rows, their names, and +so forth. The value of this attribute should be an Angular expression. +* `name`: The name in the containing scope under which to publish form +"meta-state", e.g. `$valid`, `$dirty` etc. This is as the behavior of +`ng-form`. Passed as plain text in the attribute. -Toolbars support the same ​control​ options as forms.   +Toolbars support the same control options as forms. ### Toolbar Structure -A toolbar’s structure is defined similarly to forms, except instead of ​rows​  -there are items​.  +A toolbar's structure is defined similarly to forms, except instead of rows +there are items . - {  -     "name": ... title to display for the form, as a string ...,  -     "sections": [  -         {  -             "name": ... title to display for the section ...,  -             "items": [  -                 {  -                     "name": ... title to display for this row ...,  -                     "control": ... symbolic key for the control ...,  -                     "key": ... field name in ng­model ...  -                     "pattern": ... optional, reg exp to match against ...  -                     "required": ... optional boolean ...  -                     "options": [  -                         "name": ... name to display (e.g. in a select) ...,  -                         "value": ... value to store in the model ...  -                     ],  -                     "disabled": ... true if control should be disabled ...  -                     "size": ... size of the control (for textfields) ...  -                     "click": ... function to invoke (for buttons) ...  -                     "glyph": ... glyph to display (for buttons) ...  -                     "text": ... text within control (for buttons) ...  -                 },  -                 ... and other rows ...  -             ]  -         },  -         ... and other sections ...  -     ]  + { + "name": ... title to display for the form, as a string ..., + "sections": [ + { + "name": ... title to display for the section ..., + "items": [ + { + "name": ... title to display for this row ..., + "control": ... symbolic key for the control ..., + "key": ... field name in ng-model ... + "pattern": ... optional, reg exp to match against ... + "required": ... optional boolean ... + "options": [ + "name": ... name to display (e.g. in a select) ..., + "value": ... value to store in the model ... + ], + "disabled": ... true if control should be disabled ... + "size": ... size of the control (for textfields) ... + "click": ... function to invoke (for buttons) ... + "glyph": ... glyph to display (for buttons) ... + "text": ... text within control (for buttons) ... + }, + ... and other rows ... + ] + }, + ... and other sections ... + ] } # Services -The Open MCT Web platform provides a variety of services which can be retrieved  -and utilized via dependency injection. These services fall into two categories:  +The Open MCT Web platform provides a variety of services which can be retrieved +and utilized via dependency injection. These services fall into two categories: -* _Composite Services_ are defined by a set of ​components​ extensions; plugins may  -introduce additional components with matching interfaces to extend or augment  -the functionality of the composed service. (See the Framework section on  -Composite Services.)  -* _Other services_ which are defined as standalone service objects; these can be  -utilized by plugins but are not intended to be modified or augmented.  +* _Composite Services_ are defined by a set of components extensions; plugins may +introduce additional components with matching interfaces to extend or augment +the functionality of the composed service. (See the Framework section on +Composite Services.) +* _Other services_ which are defined as standalone service objects; these can be +utilized by plugins but are not intended to be modified or augmented. ## Composite Services -This section describes the composite services exposed by Open MCT Web,  -specifically focusing on their interface and contract.  -   -In many cases, the platform will include a provider for a service which consumes  -a specific extension category; for instance, the `​actionService​` depends on  -`​actions[]​` and will expose available actions based on the rules defined for  -that extension category.   +This section describes the composite services exposed by Open MCT Web, +specifically focusing on their interface and contract. + +In many cases, the platform will include a provider for a service which consumes +a specific extension category; for instance, the `actionService` depends on +`actions[]` and will expose available actions based on the rules defined for +that extension category. -In these cases, it will usually be simpler to add a new extension of a given  -category (e.g. of category ​`actions​`) even when the same behavior could be  -introduced by a service component (e.g. an extension of category `​components​`  -where `​provides​` is `​actionService​`, and `​type​` is `​provider​`.)   +In these cases, it will usually be simpler to add a new extension of a given +category (e.g. of category `actions`) even when the same behavior could be +introduced by a service component (e.g. an extension of category `components` +where `provides` is `actionService` and `type` is `provider`.) -Occasionally, the extension category does not provide enough expressive power to  -achieve a desired result. For instance, the Create menu is populated with  -`​create​` actions, where one such action exists for each creatable type. Since  -the framework does not provide a declarative means to introduce a new action per  -type declaratively, the platform implements this explicitly in an `​actionService​`  -component of type `​provider​`. Plugins may use a similar approach when the normal  -extension mechanism is insufficient to achieve a desired result.  -  +Occasionally, the extension category does not provide enough expressive power to +achieve a desired result. For instance, the Create menu is populated with +`create` actions, where one such action exists for each creatable type. Since +the framework does not provide a declarative means to introduce a new action per +type declaratively, the platform implements this explicitly in an `actionService` +component of type `provider`. Plugins may use a similar approach when the normal +extension mechanism is insufficient to achieve a desired result. + ### Action Service -The ​`actionService​` provides `​Action​` instances which are applicable in specific  -contexts. See Core API for additional notes on the interface for actions. The  -`​actionService​` has the following interface:  +The `actionService` provides `Action` instances which are applicable in specific +contexts. See Core API for additional notes on the interface for actions. The +`actionService` has the following interface: -* `getActions(context)`​: Returns an array of ​Action​ objects which are applicable  -in the specified action context.    +* `getActions(context)`: Returns an array of Action objects which are applicable +in the specified action context. ### Capability Service -The ​capabilityService​ provides constructors for capabilities which will be  -exposed for a given domain object.  +The capabilityServic e provides constructors for capabilities which will be +exposed for a given domain object. -The ​capabilityService​ has the following interface:  +Th e capabilityService has the following interface: -* `getCapabilities(model)`​: Returns a an object containing key­value pairs,  -representing capabilities which should be exposed by the domain object with this  -model. Keys in this object are the capability keys (as used in a  -`​getCapability(...)`​ call) and values are either:  - * Functions, in which case they will be used as constructors, which will  - receive the domain object instance to which the capability applies as their  - sole argument.The resulting object will be provided as the result of a  - domain object’s `getCapability(...)` ​call. Note that these instances are cached  - by each object, but may be recreated when an object is mutated.  - * Other objects, which will be used directly as the result of a domain  - object’s `getCapability(...)​` call.  +* `getCapabilities(model)`: Returns a an object containing key-value pairs, +representing capabilities which should be exposed by the domain object with this +model. Keys in this object are the capability keys (as used in a +`getCapability(...)` call) and values are either: + * Functions, in which case they will be used as constructors, which will + receive the domain object instance to which the capability applies as their + sole argument.The resulting object will be provided as the result of a + domain object's `getCapability(...)` call. Note that these instances are cached + by each object, but may be recreated when an object is mutated. + * Other objects, which will be used directly as the result of a domain + object's `getCapability(...)` call. ### Dialog Service -The `​dialogService​` provides a means for requesting user input via a modal  -dialog. It has the following interface:  +The `dialogService` provides a means for requesting user input via a modal +dialog. It has the following interface: -* `getUserInput(formStructure, formState)`​: Prompt the user to fill out a form.  -The first argument describes the form’s structure (as will be passed to  -​mct­form​) while the second argument contains the initial state of that form.  -This returns a ​Promise​ for the state of the form after the user has filled it  -in; this promise will be rejected if the user cancels input.  -* `getUserChoice(dialogStructure)​`: Prompt the user to make a single choice from  -a set of options, which (in the platform implementation) will be expressed as  -buttons in the displayed dialog. Returns a ​Promise​ for the user’s choice, which  -will be rejected if the user cancels input.  +* `getUserInput(formStructure, formState)`: Prompt the user to fill out a form. +The first argument describes the form's structure (as will be passed to + mct-form ) while the second argument contains the initial state of that form. +This returns a Promise for the state of the form after the user has filled it +in; this promise will be rejected if the user cancels input. +* `getUserChoice(dialogStructure)`: Prompt the user to make a single choice from +a set of options, which (in the platform implementation) will be expressed as +buttons in the displayed dialog. Returns a Promise for the user's choice, which +will be rejected if the user cancels input. ### Dialog Structure -The object passed as the ​dialogStructure​ to ​getUserChoice​ should have the  -following properties: +The object passed as the `dialogStructure` to `getUserChoice` should have the +following properties: -* `title​`: The title to display at the top of the dialog.  -* `hint​`: Short message to display below the title.  -* `template​`: Identifying ​key​ (as will be passed to ​mct­include​) for the  -template which will be used to populate the inner area of the dialog.  -* `model​`: Model to pass in the ​ng­model​ attribute of ​mct­include​.  -* `parameters​`: Parameters to pass in the ​parameters​ attribute of ​mct­include​.  -* `options​`: An array of options describing each button at the bottom. Each  -option may have the following properties: - * `name`​: Human­readable name to display in the button.  - * `key`​: Machine­readable key, to pass as the result of the resolved promise  - when clicked.  - * `description​`: Description to show in tooltip on hover. +* `title`: The title to display at the top of the dialog. +* `hint`: Short message to display below the title. +* `template`: Identifying key (as will be passed to mct-include ) for the +template which will be used to populate the inner area of the dialog. +* `model`: Model to pass in the ng-model attribute of mct-include . +* `parameters`: Parameters to pass in the parameters attribute of mct-include . +* `options`: An array of options describing each button at the bottom. Each +option may have the following properties: + * `name`: Human-readable name to display in the button. + * `key`: Machine-readable key, to pass as the result of the resolved promise + when clicked. + * `description`: Description to show in tooltip on hover. ## Domain Object Service -The ​objectService​ provides domain object instances. It has the following  +The objectService provides domain object instances. It has the following interface: -* `getObjects(ids)`​: For the provided array of domain object identifiers,  -returns a Promise​ for an object containing key­value pairs, where keys are  -domain object identifiers and values are corresponding ​DomainObject​ instances.  -Note that the result may contain a superset or subset of the objects requested.  +* `getObjects(ids)`: For the provided array of domain object identifiers, +returns a Promise for an object containing key-value pairs, where keys are +domain object identifiers and values are corresponding DomainObject instances. +Note that the result may contain a superset or subset of the objects requested. ## Gesture Service -The `​gestureService​` is used to attach gestures (see extension category  -​gestures​) to representations. It has the following interface: +The `gestureService` is used to attach gestures (see extension category + gestures ) to representations. It has the following interface: -* `attachGestures(element, domainObject, keys)`​: Attach gestures specified by  -the provided gesture ​keys​ (an array of strings) to this jqLite­wrapped HTML  -element​, which represents the specified ​domainObject​. Returns an object with a  -single method `​destroy()`​, to be invoked when it is time to detach these  -gestures.  +* `attachGestures(element, domainObject, keys)`: Attach gestures specified by +the provided gesture keys (an array of strings) to this jqLite-wrapped HTML +element , which represents the specified domainObject . Returns an object with a +single method `destroy()`, to be invoked when it is time to detach these +gestures. ## Model Service -The ​modelService​ provides domain object models. It has the following interface:  +The modelService provides domain object models. It has the following interface: -* `getModels(ids)`​: For the provided array of domain object identifiers, returns  -a Promise​ for an object containing key­value pairs, where keys are domain object  -identifiers and values are corresponding domain object models. Note that the  -result may contain a superset or subset of the models requested.  +* `getModels(ids)`: For the provided array of domain object identifiers, returns +a Promise for an object containing key-value pairs, where keys are domain object +identifiers and values are corresponding domain object models. Note that the +result may contain a superset or subset of the models requested. ## Persistence Service -The ​persistenceService​ provides the ability to load/store JavaScript objects  -(presumably serializing/deserializing to JSON in the process.) This is used  -primarily to store domain object models. It has the following interface:  +The persistenceService provides the ability to load/store JavaScript objects +(presumably serializing/deserializing to JSON in the process.) This is used +primarily to store domain object models. It has the following interface: -* `listSpaces()`​: Returns a ​Promise​ for an array of strings identifying the  -different persistence spaces this service supports. Spaces are intended to be  -used to distinguish between different underlying persistence stores, to allow  -these to live side by side.  -* `listObjects()`​: Returns a Promise for an array of strings identifying all  -documents stored in this persistence service.  -* `createObject(space, key, value)`​: Create a new document in the specified  -persistence ​space​, identified by the specified ​key​, the contents of which shall  -match the specified ​value​. Returns a promise that will be rejected if creation  -fails.  -* `readObject(space, key)`​: Read an existing document in the specified  -persistence space​, identified by the specified ​key​. Returns a promise for the  -specified document; this promise will resolve to ​undefined​ if the document does  -not exist.  -* `updateObject(space, key, value)`​: Update an existing document in the  -specified persistence ​space​, identified by the specified ​key​, such that its  -contents match the specified ​value​. Returns a promise that will be rejected if  -the update fails.  -* `deleteObject(space, key)`​: Delete an existing document from the specified  -persistence ​space​, identified by the specified ​key​. Returns a promise which will  -be rejected if deletion fails.  +* `listSpaces()`: Returns a Promise for an array of strings identifying the +different persistence spaces this service supports. Spaces are intended to be +used to distinguish between different underlying persistence stores, to allow +these to live side by side. +* `listObjects()`: Returns a Promise for an array of strings identifying all +documents stored in this persistence service. +* `createObject(space, key, value)`: Create a new document in the specified +persistence space , identified by the specified key , the contents of which shall +match the specified value . Returns a promise that will be rejected if creation +fails. +* `readObject(space, key)`: Read an existing document in the specified +persistence space , identified by the specified key . Returns a promise for the +specified document; this promise will resolve to undefined if the document does +not exist. +* `updateObject(space, key, value)`: Update an existing document in the +specified persistence space , identified by the specified key , such that its +contents match the specified value . Returns a promise that will be rejected if +the update fails. +* `deleteObject(space, key)`: Delete an existing document from the specified +persistence space , identified by the specified key . Returns a promise which will +be rejected if deletion fails. ## Policy Service -The ​policyService​ may be used to determine whether or not certain behaviors are  -allowed within the application. It has the following interface:  -  -* `allow(category, candidate, context, [callback])`​: Check if this decision  -should be allowed. Returns a boolean. Its arguments are interpreted as:  - * `category​`: A string identifying which kind of decision is being made. See  - the section on Policies for categories supported by the platform; plugins  - may define and utilize policies of additional categories, as well.  - * `candidate​`: An object representing the thing which shall or shall not be  - allowed. Usually, this will be an instance of an extension of the category  - defined above. This does need to be the case; additional policies which are  - not specific to any extension may also be defined and consulted using unique  - category identifiers. In this case, the type of the object delivered for the  - candidate may be unique to the policy type.  - * `context​`: An object representing the context in which the decision is  - occurring. Its contents are specific to each policy category.  - * `callback`​: Optional; a function to call if the policy decision is rejected.  - This function will be called with the message string (which may be  - undefined) of whichever individual policy caused the operation to fail.  +The policyService may be used to determine whether or not certain behaviors are +allowed within the application. It has the following interface: + +* `allow(category, candidate, context, [callback])`: Check if this decision +should be allowed. Returns a boolean. Its arguments are interpreted as: + * `category`: A string identifying which kind of decision is being made. See + the [section on Categories](#PolicyCategories) for categories supported by the platform; plugins + may define and utilize policies of additional categories, as well. + * `candidate`: An object representing the thing which shall or shall not be + allowed. Usually, this will be an instance of an extension of the category + defined above. This does need to be the case; additional policies which are + not specific to any extension may also be defined and consulted using unique + category identifiers. In this case, the type of the object delivered for the + candidate may be unique to the policy type. + * `context`: An object representing the context in which the decision is + occurring. Its contents are specific to each policy category. + * `callback`: Optional; a function to call if the policy decision is rejected. + This function will be called with the message string (which may be + undefined) of whichever individual policy caused the operation to fail. ## Telemetry Service -The ​`telemetryService​` is used to acquire telemetry data. See the section on  -Telemetry in Core API for more information on how both the arguments and  -responses of this service are structured.  +The `telemetryService` is used to acquire telemetry data. See the section on +Telemetry in Core API for more information on how both the arguments and +responses of this service are structured. -When acquiring telemetry for display, it is recommended that the  -`​telemetryHandler` service be used instead of this service. The  -`​telemetryHandler​` has additional support for subscribing to and requesting  -telemetry data associated with domain objects or groups of domain objects. See  -the [Other Services](#Other-Services) section for more information.  +When acquiring telemetry for display, it is recommended that the +`telemetryHandler` service be used instead of this service. The +`telemetryHandler` has additional support for subscribing to and requesting +telemetry data associated with domain objects or groups of domain objects. See +the [Other Services](#Other-Services) section for more information. -The `​telemetryService​` has the following interface: +The `telemetryService` has the following interface: -* `requestTelemetry(requests)`​: Issue a request for telemetry, matching the  -specified telemetry ​requests​. Returns a _​Promise​_ for a telemetry response  +* `requestTelemetry(requests)`: Issue a request for telemetry, matching the +specified telemetry requests . Returns a _ Promise _ for a telemetry response object. -* `subscribe(callback, requests)`​: Subscribe to real­time updates for telemetry,  -matching the specified `​requests​`. The specified `​callback​` will be invoked with  -telemetry response objects as they become available. This method returns a  -function which can be invoked to terminate the subscription.  +* `subscribe(callback, requests)`: Subscribe to real-time updates for telemetry, +matching the specified `requests`. The specified `callback` will be invoked with +telemetry response objects as they become available. This method returns a +function which can be invoked to terminate the subscription. ## Type Service -The `​typeService​` exposes domain object types. It has the following interface: +The `typeService` exposes domain object types. It has the following interface: -* `listTypes()`​: Returns all domain object types supported in the application,  -as an array of ​Type​ instances. -* `getType(key)`​: Returns the `​Type​` instance identified by the provided key, or  -undefined​ if no such type exists.  +* `listTypes()`: Returns all domain object types supported in the application, +as an array of `Type` instances. +* `getType(key)`: Returns the `Type` instance identified by the provided key, or +undefined if no such type exists. ## View Service -The `​viewService​` exposes definitions for views of domain objects. It has the  -following interface: -  -* `getViews(domainObject)`:​ Get an array of extension definitions of category  -`​views` which are valid and applicable to the specified `​domainObject​`.  -  +The `viewService` exposes definitions for views of domain objects. It has the +following interface: + +* `getViews(domainObject)`: Get an array of extension definitions of category +`views` which are valid and applicable to the specified `domainObject`. + ## Other Services ### Drag and Drop -The `​dndService​` provides information about the content of an active  -drag­and­drop gesture within the application. It is intended to complement the  -`​DataTransfer​` API of HTML5 drag­and­drop, by providing access to non­serialized  -JavaScript objects being dragged, as well as by permitting inspection during  -drag (which is normally prohibited by browsers for security reasons.)  +The `dndService` provides information about the content of an active +drag-and-drop gesture within the application. It is intended to complement the +`DataTransfer` API of HTML5 drag-and-drop, by providing access to non-serialized +JavaScript objects being dragged, as well as by permitting inspection during +drag (which is normally prohibited by browsers for security reasons.) -The `​dndService​` has the following methods:  +The `dndService` has the following methods: -* `setData(key, value)`​: Set drag data associated with a given type, specified  -by the `key​` argument.  -* `getData(key)`​: Get drag data associated with a given type, specified by the ​ -`key` argument.  -* `removeData(key)`​: Clear drag data associated with a given type, specified by  -the ​`key` argument.  +* `setData(key, value)`: Set drag data associated with a given type, specified +by the `key` argument. +* `getData(key)`: Get drag data associated with a given type, specified by the +`key` argument. +* `removeData(key)`: Clear drag data associated with a given type, specified by +the `key` argument. ### Navigation -  -The ​`navigationService​` provides information about the current navigation state  -of the application; that is, which object is the user currently viewing? This  -service merely tracks this state and notifies listeners; it does not take  -immediate action when navigation changes, although its listeners might.  + +The _Navigation_ service provides information about the current navigation state +of the application; that is, which object is the user currently viewing? This +service merely tracks this state and notifies listeners; it does not take +immediate action when navigation changes, although its listeners might. -The `​navigationService​` has the following methods: +The `navigationService` has the following methods: -* `getNavigation()`​: Get the current navigation state. Returns a ​`DomainObject​`.  -* `setNavigation(domainObject)`​: Set the current navigation state. Returns a  -`DomainObject​`.  -* `addListener(callback)`​: Listen for changes in navigation state. The provided  -`callback​` should be a `​Function​` which takes a single `​DomainObject​` as an  -argument.  -* `removeListener(callback)`​: Stop listening for changes in navigation state.  -The provided `​callback​` should be a `​Function​` which has previously been passed  -to addListener​. +* `getNavigation()`: Get the current navigation state. Returns a `DomainObject`. +* `setNavigation(domainObject)`: Set the current navigation state. Returns a +`DomainObject`. +* `addListener(callback)`: Listen for changes in navigation state. The provided +`callback` should be a `Function` which takes a single `DomainObject` as an +argument. +* `removeListener(callback)`: Stop listening for changes in navigation state. +The provided `callback` should be a `Function` which has previously been passed +to addListener . ### Now -The service ​now​ is a function which acts as a simple wrapper for ​Date.now()​. It  -is present mainly so that this functionality may be more easily mocked in tests  -for scripts which use the current time.  +The service now is a function which acts as a simple wrapper for `Date.now()`. +It is present mainly so that this functionality may be more easily mocked in +tests for scripts which use the current time. ### Telemetry Formatter -The `​telemetryFormatter​` is a utility for formatting domain and range values  -read from a telemetry series.  +The _Telemetry Formatter_ is a utility for formatting domain and range values +read from a telemetry series. -The ​`telemetryFormatter​` has the following methods:  +`telemetryFormatter` has the following methods: -* `formatDomainValue(value)`​: Format the provided domain value (which will be  -assumed to be a timestamp) for display; returns a string.  -* `formatRangeValue(value)`​: Format the provided range value (a number) for  -display; returns a string. +* `formatDomainValue(value)`: Format the provided domain value (which will be +assumed to be a timestamp) for display; returns a string. +* `formatRangeValue(value)`: Format the provided range value (a number) for +display; returns a string. ### Telemetry Handler -The ​telemetryHandler​ is a utility for retrieving telemetry data associated with  -domain objects; it is particularly useful for dealing with cases where the  -​telemetry​ capability is delegated to contained objects (as occurs in Telemetry  -Panels.)  +The _Telemetry Handler_ is a utility for retrieving telemetry data associated +with domain objects; it is particularly useful for dealing with cases where the +telemetry capability is delegated to contained objects (as occurs +in _Telemetry Panels_.) -The ​telemetryHandler​ has the following methods:  +The `telemetryHandler` has the following methods: -* `handle(domainObject, callback, [lossless])`​: Subscribe to and issue future  -requests for telemetry associated with the provided ​domainObject​, invoking the  -provided ​callback​ function when streaming data becomes available. Returns a  -`TelemetryHandle`​ (see below.)  +* `handle(domainObject, callback, [lossless])`: Subscribe to and issue future +requests for telemetry associated with the provided `domainObject`, invoking the +provided callback function when streaming data becomes available. Returns a +`TelemetryHandle` (see below.) #### Telemetry Handle -A ​TelemetryHandle​ has the following methods:  +A TelemetryHandle has the following methods: -* `getTelemetryObjects()`​: Get the domain objects (as a ​`DomainObject[]`​) that  -have a ​telemetry​ capability and are being handled here. Note that these are  -looked up asynchronously, so this method may return an empty array if the  -initial lookup is not yet completed.  -* `promiseTelemetryObjects()`​: As `​getTelemetryObjects()`​, but returns a Promise​  -that will be fulfilled when the lookup is complete.  -* `unsubscribe()`​: Unsubscribe to streaming telemetry updates associated with  -this handle.  -* `getDomainValue(domainObject)`​: Get the most recent domain value received via  -a streaming update for the specified `​domainObject​`.  -* `getRangeValue(domainObject)`​: Get the most recent range value received via a  -streaming update for the specified `​domainObject`​.  -* `getMetadata()`​: Get metadata (as reported by the `​getMetadata()`​ method of a  -telemetry​ capability) associated with telemetry­providing domain objects.  -Returns an array, which is in the same order as ​getTelemetryObjects()​.  -* `request(request, callback)`​: Issue a new ​request​ for historical telemetry  -data. The provided ​callback​ will be invoked when new data becomes available,  -which may occur multiple times (e.g. if there are multiple domain objects.) It  -will be invoked with the DomainObject​ for which a new series is available, and  -the ​TelemetrySeries​ itself, in that order.  -* `getSeries(domainObject)`​: Get the latest `​TelemetrySeries​` (as resulted from  -a previous ​`request(...)`​ call) available for this domain object. +* `getTelemetryObjects()`: Get the domain objects (as a `DomainObject[]`) that +have a telemetry capability and are being handled here. Note that these are +looked up asynchronously, so this method may return an empty array if the +initial lookup is not yet completed. +* `promiseTelemetryObjects()`: As `getTelemetryObjects()`, but returns a Promise +that will be fulfilled when the lookup is complete. +* `unsubscribe()`: Unsubscribe to streaming telemetry updates associated with +this handle. +* `getDomainValue(domainObject)`: Get the most recent domain value received via +a streaming update for the specified `domainObject`. +* `getRangeValue(domainObject)`: Get the most recent range value received via a +streaming update for the specified `domainObject`. +* `getMetadata()`: Get metadata (as reported by the `getMetadata()` method of a +telemetry capability) associated with telemetry-providing domain objects. +Returns an array, which is in the same order as getTelemetryObjects() . +* `request(request, callback)`: Issue a new request for historical telemetry +data. The provided callback will be invoked when new data becomes available, +which may occur multiple times (e.g. if there are multiple domain objects.) It +will be invoked with the DomainObject for which a new series is available, and +the TelemetrySeries itself, in that order. +* `getSeries(domainObject)`: Get the latest `TelemetrySeries` (as resulted from +a previous `request(...)` call) available for this domain object. # Models -Domain object models in Open MCT Web are JavaScript objects describing the  -persistent state of the domain objects they describe. Their contents include a  -mix of commonly understood metadata attributes; attributes which are recognized  -by and/or determine the applicability of specific extensions; and properties  -specific to given types.  +Domain object models in Open MCT Web are JavaScript objects describing the +persistent state of the domain objects they describe. Their contents include a +mix of commonly understood metadata attributes; attributes which are recognized +by and/or determine the applicability of specific extensions; and properties +specific to given types. ## General Metadata -Some properties of domain object models have a ubiquitous meaning through Open  -MCT Web and can be utilized directly:  +Some properties of domain object models have a ubiquitous meaning through Open +MCT Web and can be utilized directly: -* `name`​: The human­readable name of the domain object. +* `name`: The human-readable name of the domain object. ## Extension-specific Properties -Other properties of domain object models have specific meaning imposed by other  -extensions within the Open MCT Web platform.  +Other properties of domain object models have specific meaning imposed by other +extensions within the Open MCT Web platform. ### Capability-specific Properties -Some properties either trigger the presence/absence of certain capabilities, or  -are managed by specific capabilities: +Some properties either trigger the presence/absence of certain capabilities, or +are managed by specific capabilities: -* `composition​`: An array of domain object identifiers that represents the  -contents of this domain object (e.g. as will appear in the tree hierarchy.)  -Understood by the composition​ capability; the presence or absence of this  -property determines the presence or absence of that capability.  -* `modified​`: The timestamp (in milliseconds since the UNIX epoch) of the last  -modification made to this domain object. Managed by the ​mutation​ capability.  -* `persisted​`: The timestamp (in milliseconds since the UNIX epoch) of the last  -time when changes to this domain object were persisted. Managed by the  -​persistence capability.  -* `relationships​`: An object containing key­value pairs, where keys are symbolic  -identifiers for relationship types, and values are arrays of domain object  -identifiers. Used by the ​relationship​ capability; the presence or absence of  -this property determines the presence or absence of that capability.  -* `telemetry​`: An object which serves as a template for telemetry requests  -associated with this domain object (e.g. specifying ​`source`​ and ​`key​`; see  -Telemetry Requests under Core API.) Used by the ​telemetry​ capability; the  -presence or absence of this property determines the presence or absence of that  +* `composition`: An array of domain object identifiers that represents the +contents of this domain object (e.g. as will appear in the tree hierarchy.) +Understood by the composition capability; the presence or absence of this +property determines the presence or absence of that capability. +* `modified`: The timestamp (in milliseconds since the UNIX epoch) of the last +modification made to this domain object. Managed by the mutation capability. +* `persisted`: The timestamp (in milliseconds since the UNIX epoch) of the last +time when changes to this domain object were persisted. Managed by the + persistence capability. +* `relationships`: An object containing key-value pairs, where keys are symbolic +identifiers for relationship types, and values are arrays of domain object +identifiers. Used by the relationship capability; the presence or absence of +this property determines the presence or absence of that capability. +* `telemetry`: An object which serves as a template for telemetry requests +associated with this domain object (e.g. specifying `source` and `key`; see +Telemetry Requests under Core API.) Used by the telemetry capability; the +presence or absence of this property determines the presence or absence of that capability. -* `type`​: A string identifying the type of this domain object. Used by the ​type​  -capability.  -  +* `type`: A string identifying the type of this domain object. Used by the `type` +capability. + ### View Configurations -Persistent configurations for specific views of domain objects are stored in the  -domain object model under the property ​configurations​. This is an object  -containing key­value pairs, where keys identify the view, and values are objects  -containing view­specific (and view­managed) configuration properties.  -  +Persistent configurations for specific views of domain objects are stored in the +domain object model under the property configurations . This is an object +containing key-value pairs, where keys identify the view, and values are objects +containing view-specific (and view-managed) configuration properties. + ## Modifying Models -When interacting with a domain object’s model, it is possible to make  -modifications to it directly. __​Don't!__ ​These changes may not be properly detected  -by the platform, meaning that other representations of the domain object may not  -be updated, changes may not be saved at the expected times, and generally, that  -unexpected behavior may occur. Instead, use the `​mutation​` capability.  +When interacting with a domain object's model, it is possible to make +modifications to it directly. __Don't!__ These changes may not be properly detected +by the platform, meaning that other representations of the domain object may not +be updated, changes may not be saved at the expected times, and generally, that +unexpected behavior may occur. Instead, use the `mutation` capability. # Capabilities -Dynamic behavior associated with a domain object is expressed as capabilities. A  -capability is a JavaScript object with an interface that is specific to the type  -of capability in use.  +Dynamic behavior associated with a domain object is expressed as capabilities. A +capability is a JavaScript object with an interface that is specific to the type +of capability in use. -Often, there is a relationship between capabilities and services. For instance,  -there is an action​ capability and an ​actionService​, and there is a ​telemetry​  -capability as well as a `telemetryService​`. Typically, the pattern here is that  -the capability will utilize the service ​for the specific domain object​.   +Often, there is a relationship between capabilities and services. For instance, +there is an action capability and an actionService , and there is a telemetry +capability as well as a `telemetryService`. Typically, the pattern here is that +the capability will utilize the service for the specific domain object. -When interacting with domain objects, it is generally preferable to use a  -capability instead of a service when the option is available. Capability  -interfaces are typically easier to use and/or more powerful in these situations.  -Additionally, this usage provides a more robust substitutability mechanism; for  -instance, one could configure a plugin such that it provided a totally new  -implementation of a given capability which might not invoke the underlying  -service, while user code which interacts with capabilities remains indifferent  -to this detail.  -  +When interacting with domain objects, it is generally preferable to use a +capability instead of a service when the option is available. Capability +interfaces are typically easier to use and/or more powerful in these situations. +Additionally, this usage provides a more robust substitutability mechanism; for +instance, one could configure a plugin such that it provided a totally new +implementation of a given capability which might not invoke the underlying +service, while user code which interacts with capabilities remains indifferent +to this detail. + ## Action -The ​`action​` capability is present for all domain objects. It allows applicable  -​`Action` instances to be retrieved and performed for specific domain objects.  +The `action` capability is present for all domain objects. It allows applicable +`Action` instances to be retrieved and performed for specific domain objects. -For example:  - `domainObject.getCapability("action").perform("navigate"); ` - ...will initiate a navigate action upon the domain object, if an action with  - key "navigate" is defined.  -   -This capability has the following interface:  -* `getActions(context)`​: Get the actions that are applicable in the specified  -action `context​`; the capability will fill in the `​domainObject​` field of this  -context if necessary. If context​ is specified as a string, they will instead be  -used as the ​`key`​ of the action context. Returns an array of ​`Action​` instances. -* `perform(context)​`: Perform an action. This will find and perform the first  -matching action available for the specified action ​context​, filling in the  -`​domainObject​` field as necessary. If ​`context​` is specified as a string, they  -will instead be used as the `​key​` of the action context. Returns a `​Promise​` for  -the result of the action that was performed, or `undefined` if no matching action  -was found.  +For example: + `domainObject.getCapability("action").perform("navigate"); ` + ...will initiate a navigate action upon the domain object, if an action with + key "navigate" is defined. + +This capability has the following interface: +* `getActions(context)`: Get the actions that are applicable in the specified +action `context`; the capability will fill in the `domainObject` field of this +context if necessary. If context is specified as a string, they will instead be +used as the `key` of the action context. Returns an array of `Action` instances. +* `perform(context)`: Perform an action. This will find and perform the first +matching action available for the specified action context , filling in the +`domainObject` field as necessary. If `context` is specified as a string, they +will instead be used as the `key` of the action context. Returns a `Promise` for +the result of the action that was performed, or `undefined` if no matching action +was found. ## Composition -The `​composition​` capability provides access to domain objects that are  -contained by this domain object. While the ​`composition​` property of a domain  -object’s model describes these contents (by their identifiers), the ​ -`composition​` capability provides a means to load the corresponding  -`​DomainObject​` instances in the same order. The absence of this property in the  -model will result in the absence of this capability in the domain object.  +The `composition` capability provides access to domain objects that are +contained by this domain object. While the `composition` property of a domain +object's model describes these contents (by their identifiers), the +`composition` capability provides a means to load the corresponding +`DomainObject` instances in the same order. The absence of this property in the +model will result in the absence of this capability in the domain object. -This capability has the following interface:  +This capability has the following interface: -* `invoke()`​: Returns a `​Promise​` for an array of `​DomainObject​` instances. +* `invoke()`: Returns a `Promise` for an array of `DomainObject` instances. ## Delegation -The ​delegation​ capability is used to communicate the intent of a domain object  -to delegate responsibilities, which would normally handled by other  -capabilities, to the domain objects in its composition.  +The delegation capability is used to communicate the intent of a domain object +to delegate responsibilities, which would normally handled by other +capabilities, to the domain objects in its composition. -This capability has the following interface:  +This capability has the following interface: -* `getDelegates(key)`​: Returns a ​Promise​ for an array of ​DomainObject​ instances,  -to which this domain object wishes to delegate the capability with the specified ​ -key​.  -* `invoke(key)`​: Alias of ​getDelegates(key)​.  -* `doesDelegate(key)`​: Returns ​true​ if the domain object does delegate the  -capability with the specified ​key​.   +* `getDelegates(key)`: Returns a Promise for an array of DomainObject instances, +to which this domain object wishes to delegate the capability with the specified +key . +* `invoke(key)`: Alias of getDelegates(key) . +* `doesDelegate(key)`: Returns true if the domain object does delegate the +capability with the specified key . -The platform implementation of the ​delegation​ capability inspects the domain  -object’s type definition for a property ​delegates​, whose value is an array of  -strings describing which capabilities domain objects of that type wish to  -delegate. If this property is not present, the delegation​ capability will not be  -present in domain objects of that type.   +The platform implementation of the delegation capability inspects the domain +object's type definition for a property delegates , whose value is an array of +strings describing which capabilities domain objects of that type wish to +delegate. If this property is not present, the delegation capability will not be +present in domain objects of that type. ## Editor -The ​editor​ capability is meant primarily for internal use by Edit mode, and  -helps to manage the behavior associated with exiting Edit mode via Save or  -Cancel. Its interface is not intended for general use. However,  -`​domainObject.hasCapability(‘editor’)`​ is a useful way of determining whether or  -not we are looking at an object in Edit mode. -  +The editor capability is meant primarily for internal use by Edit mode, and +helps to manage the behavior associated with exiting _Edit_ mode via _Save_ or +_Cancel_. Its interface is not intended for general use. However, +`domainObject.hasCapability(editor)` is a useful way of determining whether or +not we are looking at an object in _Edit_ mode. + ## Mutation -The ​`mutation​` capability provides a means by which the contents of a domain  -object’s model can be modified. This capability is provided by the platform for  -all domain objects, and has the following interface: +The `mutation` capability provides a means by which the contents of a domain +object's model can be modified. This capability is provided by the platform for +all domain objects, and has the following interface: -* `mutate(mutator, [timestamp])`​: Modify the domain object’s model using the  -specified `​mutator​` function. After changes are made, the ​`modified​` property of  -the model will be updated with the specified ​`timestamp​`, if one was provided,  -or with the current system time.  -* `invoke(...)​`: Alias of ​`mutate​`. +* `mutate(mutator, [timestamp])`: Modify the domain object's model using the +specified `mutator` function. After changes are made, the `modified` property of +the model will be updated with the specified `timestamp` if one was provided, +or with the current system time. +* `invoke(...)`: Alias of `mutate`. -Changes to domain object models should only be made via the ​`mutation​`  -capability; other platform behavior is likely to break (either by exhibiting  -undesired behavior, or failing to exhibit desired behavior) if models are  -modified by other means. -  +Changes to domain object models should only be made via the `mutation` +capability; other platform behavior is likely to break (either by exhibiting +undesired behavior, or failing to exhibit desired behavior) if models are +modified by other means. + ### Mutator Function -The ​mutator​ argument above is a function which will receive a cloned copy of the  -domain object’s model as a single argument. It may return:  +The mutator argument above is a function which will receive a cloned copy of the +domain object's model as a single argument. It may return: -* A ​`Promise​`, in which case the resolved value of the promise will be used to  -determine which of the following forms is used.  -* Boolean ​`false​`, in which case the mutation is cancelled.  -* A JavaScript object, in which case this object will be used as the new model  -for this domain object. -* No value (or, equivalently, `​undefined​`), in which case the cloned copy  -(including any changes made in place by the mutator function) will be used as  -the new domain object model.  +* A `Promise` in which case the resolved value of the promise will be used to +determine which of the following forms is used. +* Boolean `false` in which case the mutation is cancelled. +* A JavaScript object, in which case this object will be used as the new model +for this domain object. +* No value (or, equivalently, `undefined`), in which case the cloned copy +(including any changes made in place by the mutator function) will be used as +the new domain object model. ## Persistence The persistence capability provides a mean for interacting with the underlying -persistence service which stores this domain object’s model. It has the +persistence service which stores this domain object's model. It has the following interface: * `persist()`: Store the local version of this domain object, including any changes, to the persistence store. Returns a Promise for a boolean value, which will be true when the object was successfully persisted. -* `refresh()`: Replace this domain object’s model with the most recent version +* `refresh()`: Replace this domain object's model with the most recent version from persistence. Returns a Promise which will resolve when the change has completed. * `getSpace()`: Return the string which identifies the persistence space which @@ -2030,7 +2043,7 @@ objects which has a `relationships` property in their model, whose value is an object containing key-value pairs, where keys are strings identifying relationship types, and values are arrays of domain object identifiers. -##Telemetry +## Telemetry The telemetry capability provides a means for accessing telemetry data associated with a domain object. It has the following interface: @@ -2046,7 +2059,7 @@ properties as-needed for this domain object. The specified callback will be invoked with TelemetrySeries instances as they arrive. Returns a function which can be invoked to terminate the subscription, or undefined if no subscription could be obtained. -* `getMetadata()`: Get metadata associated with this domain object’s telemetry. +* `getMetadata()`: Get metadata associated with this domain object's telemetry. The platform implementation of the `telemetry` capability is present for domain objects which has a `telemetry` property in their model and/or type definition; @@ -2054,7 +2067,7 @@ this object will serve as a template for telemetry requests made using this object, and will also be returned by `getMetadata()` above. ## Type -The `type` capability exposes information about the domain object’s type. It has +The `type` capability exposes information about the domain object's type. It has the same interface as `Type`; see Core API. ## View @@ -2073,26 +2086,26 @@ typically upon domain objects. ## Action Categories The platform understands the following action categories (specifiable as the -`category` parameter of an action’s extension definition.) +`category` parameter of an action's extension definition.) * `contextual`: Appears in context menus. * `view-control`: Appears in top-right area of view (as buttons) in Browse mode ## Platform Actions The platform defines certain actions which can be utilized by way of a domain -object’s `action` capability. Unless otherwise specified, these act upon (and -modify) the object described by the `domainObject` property of the action’s +object's `action` capability. Unless otherwise specified, these act upon (and +modify) the object described by the `domainObject` property of the action's context. * `cancel`: Cancel the current editing action (invoked from Edit mode.) -* `compose`: Place an object in another object’s composition. The object to be +* `compose`: Place an object in another object's composition. The object to be added should be provided as the `selectedObject` of the action context. * `edit`: Start editing an object (enter Edit mode.) * `fullscreen`: Enter full screen mode. * `navigate`: Make this object the focus of navigation (e.g. highlight it within the tree, display a view of it to the right.) -* `properties`: Show the “Edit Properties” dialog. -* `remove`: Remove this domain object from its parent’s composition. (The +* `properties`: Show the 'Edit Properties' dialog. +* `remove`: Remove this domain object from its parent's composition. (The parent, in this case, is whichever other domain object exposed this object by way of its `composition` capability.) * `save`: Save changes (invoked from Edit mode.) @@ -2106,22 +2119,22 @@ describe the type of decision being made; within each category, policies have a candidate (the thing which may or may not be allowed) and, optionally, a context (describing, generally, the context in which the decision is occurring.) -The types of objects passed for “candidate” and “context” vary by category; +The types of objects passed for 'candidate' and 'context' vary by category; these types are documented below. ## Policy Categories The platform understands the following policy categories (specifiable as the -`category` parameter of an policy’s extension definition.) +`category` parameter of an policy's extension definition.) * `action`: Determines whether or not a given action is allowable. The candidate argument here is an Action; the context is its action context object. * `composition`: Determines whether or not domain objects of a given type are allowed to contain domain objects of another type. The candidate argument here -is the container’s `Type`; the context argument is the `Type` of the object to be +is the container's `Type`; the context argument is the `Type` of the object to be contained. * `view`: Determines whether or not a view is applicable for a domain object. -The candidate argument is the view’s extension definition; the context argument +The candidate argument is the view's extension definition; the context argument is the `DomainObject` to be viewed. # Build, Test, Deploy @@ -2140,7 +2153,7 @@ Invoking mvn clean install will: * Populate version info (e.g. commit hash, build time.) * Produce a web archive (`.war`) artifact in the `target` directory. -The produced artifact contains a subset of the repository’s own folder +The produced artifact contains a subset of the repository's own folder hierarchy, omitting tests and example bundles. Note that an internet connection is required to run this build, in order to @@ -2149,7 +2162,7 @@ download build dependencies. ## Test Suite Open MCT Web uses Jasmine [http://jasmine.github.io/]() for automated testing. -The file `test.html`, included at the top level of the source repository, can be +The file `test.html` included at the top level of the source repository, can be run from the browser to perform tests for all active bundles, as defined in `bundle.json`. @@ -2161,11 +2174,11 @@ which scripts will be tested. * The file `suite.json` must contain a JSON array of strings, where each string is the name of a script to be tested. These names should include any directory paths to the script after (but not including) the `src` folder, and should not -include the file’s `.js` extension. (Note that while Open MCT Web’s framework +include the file's `.js` extension. (Note that while Open MCT Web's framework allows a different name to be chosen for the src directory, the test runner does not: This directory must be named `src` for the test runner to find it.) * For each script to be tested, a corresponding test script should be located in -the bundle’s `test` directory. This should include the suffix Spec at the end of +the bundle's `test` directory. This should include the suffix Spec at the end of the filename (but before the `.js` extension.) This test script should be an AMD module which uses the Jasmine API to declare its test behavior. It should declare an AMD dependency on the script to be tested, using a relative path. @@ -2174,7 +2187,7 @@ For example, if writing tests for a bundle at example/foo with two scripts: * `example/foo/src/controllers/FooController.js` * `example/foo/src/directives/FooDirective.js` -First, these scripts should be identified in `example/foo/test/suite.json`, e.g. +First, these scripts should be identified in `example/foo/test/suite.json` e.g. with contents:`[ "controllers/FooController", "directives/FooDirective" ]` Then, scripts which describe these tests should be written. For example, test @@ -2223,7 +2236,7 @@ to be utilized by Open MCT Web. Because of this, it is often the set of external services (and the manner in which they are exposed) that determine how to deploy Open MCT Web. -One important constraint to consider in this context is the browser’s same +One important constraint to consider in this context is the browser's same origin policy. If external services are not on the same apparent host and port as the client (from the perspective of the browser) then access may be disallowed. There are two workarounds if this occurs: @@ -2242,7 +2255,7 @@ most sense) include: Apache Tomcat [https://tomcat.apache.org/](), then it makes sense to run Open MCT Web from the same Tomcat instance as a separate web application. The `.war` artifact produced by the command line build facilitates this deployment -option. (See `https://tomcat.apache.org/tomcat-8.0-doc/deployer-howto.html` for +option. (See [https://tomcat.apache.org/tomcat-8.0-doc/deployer-howto.html() for general information on deploying in Tomcat.) * If a variety of external services will be running from a variety of hosts/ports, then it may make sense to use a web server that supports proxying, @@ -2254,7 +2267,7 @@ Open MCT Web as flat files from a different path. needs of an Open MCT Web instance, it can make sense to serve Open MCT Web (as flat files) from the same component using an embedded HTTP server such as Nancy [http://nancyfx.org/](). -* If no external services are needed (or if the “external services” will just +* If no external services are needed (or if the 'external services' will just be generating flat files to read) it makes sense to utilize a lightweight flat file HTTP server such as Lighttpd [http://www.lighttpd.net/](). In this configuration, Open MCT Web sources/resources would be placed at one path, while @@ -2285,7 +2298,7 @@ these constants are provided in the bundle where they are used. Plugins are encouraged to follow the same pattern. Constants may be specified in any bundle; if multiple constants are specified -with the same `key`, the highest-priority one will be used. This allows default +with the same `key` the highest-priority one will be used. This allows default values to be overridden by specifying constants with higher priority. This permits at least three configuration approaches: @@ -2303,10 +2316,10 @@ default paths to reach external services are all correct. ### Configuration Constants The following configuration constants are recognized by Open MCT Web bundles: -* CouchDB adapter, `platform/persistence/couch` +* CouchDB adapter - `platform/persistence/couch` * `COUCHDB_PATH`: URL or path to the CouchDB database to be used for domain object persistence. Should not include a trailing slash. -* ElasticSearch adapter, platform/persistence/elastic +* ElasticSearch adapter - `platform/persistence/elastic` * `ELASTIC_ROOT`: URL or path to the ElasticSearch instance to be used for domain object persistence. Should not include a trailing slash. * `ELASTIC_PATH`: Path relative to the ElasticSearch instance where domain From d787e84fd4ecd0e7d6e676672c59916b1c06e438 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Thu, 8 Oct 2015 17:17:30 -0700 Subject: [PATCH 048/488] Updated tutorials --- docs/src/tutorials/images/chrome.png | Bin 0 -> 143095 bytes docs/src/tutorials/images/todo-list.png | Bin 0 -> 26034 bytes docs/src/tutorials/images/todo.png | Bin 0 -> 44219 bytes docs/src/tutorials/index.md | 2768 +++++++++++++++++++++++ 4 files changed, 2768 insertions(+) create mode 100644 docs/src/tutorials/images/chrome.png create mode 100644 docs/src/tutorials/images/todo-list.png create mode 100644 docs/src/tutorials/images/todo.png diff --git a/docs/src/tutorials/images/chrome.png b/docs/src/tutorials/images/chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..1b9b7b80d242017ef189d36e40c51c3c1a348e76 GIT binary patch literal 143095 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 b;=#xD5musP>ydtfm1TsZF-OHm5`O*%Sp`nk literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/images/todo-list.png b/docs/src/tutorials/images/todo-list.png new file mode 100644 index 0000000000000000000000000000000000000000..48c84c63e4cb448e30e1125ccbc8931385784a22 GIT binary patch literal 26034 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 literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index e69de29bb2..2920b27f05 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -0,0 +1,2768 @@ +Open MCT Web Tutorials + +Victor Woeltjen +victor.woeltjen@nasa.gov + +October 6, 2015 +Document Version 2.2 + +Date | Version | Summary of Changes | Author +--------------- | ------- | --------------------------------- | --------------- +May 12, 2015 | 0 | Initial Draft | Victor Woeltjen +June 4, 2015 | 1.0 | Name changes | Victor Woeltjen +July 28, 2015 | 2.0 | Telemetry adapter tutorial | Victor Woeltjen +July 31, 2015 | 2.1 | Clarify telemetry adapter details | Victor Woeltjen +October 6, 2015 | 2.2 | Conversion to markdown | Andrew Henry + +# Introduction + +## Setting Up Open MCT Web + +In this section, we will cover the steps necessary to get a minimal Open MCT Web +developer environment up and running. Once we have this, we will be able to +proceed with writing plugins as described in this tutorial. + +## Prerequisites + +This tutorial assumes you have the following software installed. Version numbers +record what was used in writing this tutorial; the same steps should work with +more recent versions, but this cannot be guaranteed. + +* Node.js v0.12.2: https://nodejs.org/ +* git v1.8.3.4: http://git-scm.com/ +* Google Chrome v42: https://www.google.com/chrome/ +* A text editor. + +Open MCT Web can be run without any of these tools, provided suitable +alternatives are taken; see the [Open MCT Web Developer Guide](../guide/index.md) +for a more general overview of how to run and deploy a Open MCT Web application. + +## Check out Open MCT Web Sources + +First step is to check out Open MCT Web from the source repository. + +`git clone https://github.com/nasa/openmctweb.git openmctweb` + +This will create a copy of the Open MCT Web source code repository in the folder +`openmctweb` (relative to the path from which you ran the command.) +If you have a repository URL, use that as the “path to repo” above. Alternately, +if you received Open MCT Web as a git bundle, the path to that bundle on the +local filesystem can be used instead. +At this point, it will also be useful to branch off of Open MCT Web v0.6.2 +(which was used when writing these tutorials) to begin adding plugins. + + cd openmctweb + git branch open-v0.6.2 + git checkout + +## Configuring Persistence + +In its default configuration, Open MCT Web will try to use ElasticSearch +(expected to be deployed at /elastic on the same HTTP server running Open MCT +Web) to persist user-created domain objects. We don’t need that for these +tutorials, so we will replace the ElasticSearch plugin with the example +persistence plugin. This doesn’t actually persist, so anything we create within +Open MCT Web will be lost on reload, but that’s fine for purposes of these +tutorials. + +To change this configuration, edit bundles.json (at the top level of the Open +MCT Web repository) and replace platform/persistence/elastic with +example/persistence. + +### Before + + [ + "platform/framework", + "platform/core", + "platform/representation", + "platform/commonUI/about", + "platform/commonUI/browse", + "platform/commonUI/edit", + "platform/commonUI/dialog", + "platform/commonUI/general", + "platform/containment", + "platform/telemetry", + "platform/features/layout", + "platform/features/pages", + "platform/features/plot", + "platform/features/scrolling", + "platform/forms", + "platform/persistence/queue", + -- "platform/persistence/elastic", + "platform/policy", + + "example/generator" + ] +__bundles.json__ + +### After + + [ + "platform/framework", + "platform/core", + "platform/representation", + "platform/commonUI/about", + "platform/commonUI/browse", + "platform/commonUI/edit", + "platform/commonUI/dialog", + "platform/commonUI/general", + "platform/containment", + "platform/telemetry", + "platform/features/layout", + "platform/features/pages", + "platform/features/plot", + "platform/features/scrolling", + "platform/forms", + "platform/persistence/queue", + "platform/policy", + + ++ "example/persistence", + "example/generator" + ] +__bundles.json__ + +### Run a Web Server + +The next step is to run a web server so that you can view the Open MCT Web client (including the plugins you add to it) in browser. The HTTP server option that is recommended here, for simplicity, is http-server, https://www.npmjs.com/package/http-server. + +To run: + + npm install http-server -g + http-server + +### Viewing in Browser + +Once running, you should be able to view Open MCT Web from your browser at +[http://localhost:8080/](). [Google Chrome](https://www.google.com/chrome/) is +recommended for these tutorials, as Chrome is Open MCT Web’s “test-to” browser. +The browser cache can sometimes interfere with development (masking changes by +using older versions of sources); to avoid this, it is easiest to run Chrome +with Developer Tools expanded, and “Disable cache” selected from the Network +tab, as shown below. + +![Chrome Developer Tools](images/chrome.png) + +# Tutorials + +These tutorials cover three of the common tasks in Open MCT Web: + +* The “to-do list” tutorial illustrates how to add a new application feature. +* The “bar graph” tutorial illustrates how to add a new telemetry visualization. +* The “data set reader” tutorial illustrates how to integrate with a telemetry +backend. + +## To-do List + +The goal of this tutorial is to add a new application feature to Open MCT Web: +To-do lists. Users should be able to create and manage these to track items that +they need to do. This is modelled after the to-do lists at [http://todomvc.com/](). + +### Step 1. Create the Plugin + +The first step to adding a new feature to Open MCT Web is to create the plugin +which will expose that feature. A plugin in Open MCT Web is represented by what +is called a bundle; a bundle, in turn, is a directory which contains a file +bundle.json, which in turn describes where other relevant sources & resources +will be. The syntax of this file is described in more detail in the Open MCT Web +Developer Guide. + +We will create this file in the directory tutorials/todo (we can hereafter refer +to this plugin as tutorials/todo as well.) We will start with an “empty bundle” +- one which exposes no extensions - which looks like: + + { + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + + } + } +__tutorials/todo/bundle.json__ + +We will also include this in our list of active bundles. + +#### Before + [ + "platform/framework", + "platform/core", + "platform/representation", + "platform/commonUI/about", + "platform/commonUI/browse", + "platform/commonUI/edit", + "platform/commonUI/dialog", + "platform/commonUI/general", + "platform/containment", + "platform/telemetry", + "platform/features/layout", + "platform/features/pages", + "platform/features/plot", + "platform/features/scrolling", + "platform/forms", + "platform/persistence/queue", + "platform/policy", + + "example/persistence", + "example/generator" + ] +__bundles.json__ + +#### After + [ + "platform/framework", + "platform/core", + "platform/representation", + "platform/commonUI/about", + "platform/commonUI/browse", + "platform/commonUI/edit", + "platform/commonUI/dialog", + "platform/commonUI/general", + "platform/containment", + "platform/telemetry", + "platform/features/layout", + "platform/features/pages", + "platform/features/plot", + "platform/features/scrolling", + "platform/forms", + "platform/persistence/queue", + "platform/policy", + + "example/persistence", + "example/generator", + + ++ "tutorials/todo" + ] + +__bundles.json__ + +At this point, we can reload Open MCT Web. We haven’t introduced any new +functionality, so we don’t see anything different, but if we run with logging +enabled ([http://localhost:8080/?log=info]()) and check the browser console, we +should see: + +`Resolving extensions for bundle tutorials/todo(To-do Plugin)...which shows that +our plugin has loaded.` + +### Step 2. Add a Domain Object Type + +Features in a Open MCT Web application are most commonly expressed as domain +objects and/or views thereof. A domain object is some thing that is relevant to +the work that the Open MCT Web application is meant to support. Domain objects +can be created, organized, edited, placed in layouts, and so forth. (For a +deeper explanation of domain objects, see the Open MCT Web Developer Guide.) + +In the case of our to-do list feature, the to-do list itself is the thing we’ll +want users to be able to create and edit. So, we will add that as a new type in +our bundle definition: + + { + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + ++ "types": [ + ++ { + ++ "key": "example.todo", + ++ "name": "To-Do List", + ++ "glyph": "j", + ++ "description": "A list of things that need to be done.", + ++ "features": ["creation"] + ++ } + ] + } + } +__tutorials/todo/bundle.json__ + +What have we done here? We’ve stated that this bundle includes extensions of the +category _types_, which is used to describe domain object types. Then, we’ve +included a definition for one such extension, which is the to-do list object. + +Going through the properties we’ve defined: + +* The `key` of `example.todo` will be stored as the machine-readable name for +domain objects of this type. +* The `name` of “To-Do List” is the human-readable name for this type, and will +be shown to users. +* The `glyph` refers to a special character in Open MCT Web’s custom font set; +this will be used as an icon. +* The `description` is also human-readable, and will be used whenever a longer +explanation of what this type is should be shown. +* Finally, the `features` property describes some special features of objects of +this type. Including `creation` here means that we want users to be able to +create this (in other cases, we may wish to expose things as domain objects +which aren’t user-created, in which case we would omit this.) + +If we reload Open MCT Web, we see that our new domain object type appears in the +Create menu: + +![To-Do List](images/todo.png) + +At this point, our to-do list doesn’t do much of anything; we can create them +and give them names, but they don’t have any specific functionality attached, +because we haven’t defined any yet. + +### Step 3. Add a View + +In order to allow a to-do list to be used, we need to define and display its +contents. In Open MCT Web, the pattern that the user expects is that they’ll +click on an object in the left-hand tree, and see a visualization of it to the +right; in Open MCT Web, these visualizations are called views. +A view in Open MCT Web is defined by an Angular template. We’ll add that in the +directory `tutorials/todo/res/templates` (`res` is, by default, the directory +where bundle-related resources are kept, and `templates` is where HTML templates +are stored by convention.) + + + +
    +
  • + + {{task.description}} +
  • +
+__tutorials/todo/res/templates/todo.html__ + +A summary of what’s included: + +At the top, we have some buttons that we will later wire in to allow the user to +filter down to either complete or incomplete tasks. +After that, we have a list of tasks. The scope variable model is the model of +the domain object being viewed; this contains all of the persistent state +associated with that object. This model is effectively just a JSON document, so +we can choose what goes into it (so long as we take care not to collide with +platform-defined properties; see the Open MCT Web Developer Guide.) Here, we +assume that all tasks will be stored in a property tasks, and that each will be +an object containing a description (the readable summary of the task) and a +boolean completed flag. + +To expose this view in Open MCT Web, we need to declare it in our bundle +definition: + + { + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + "types": [ + { + "key": "example.todo", + "name": "To-Do List", + "glyph": "j", + "description": "A list of things that need to be done.", + "features": ["creation"] + } + ], + ++ "views": [ + ++ { + ++ "key": "example.todo", + ++ "type": "example.todo", + ++ "glyph": "j", + ++ "name": "List", + ++ "templateUrl": "templates/todo.html" + ++ } + ++ ] + } + } +__tutorials/todo/bundle.json__ + +Here, we’ve added another extension, this time belonging to category views. It +contains the following properties: + +Its key is its machine-readable name; we’ve given it the same name here as the +domain object type, but could have chosen any unique name. The type property +tells Open MCT Web that this view is only applicable to domain objects of that +type. This means that we’ll see this view for To-do Lists that we create, but +not for other domain objects (such as Folders.) + +The glyph and name properties describe the icon and human-readable name for this +view to display in the UI where needed (if multiple views are available for +To-do Lists, the user will be able to choose one.) + +Finally, the templateUrl points to the Angular template we wrote; this path is +relative to the bundle’s res folder. + +This template looks like it should display tasks, but we don’t have any way for +the user to create these yet. As a temporary workaround to test the view, we +will specify an initial state for To-do List domain object models in the +definition of that type. + + { + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + "types": [ + { + "key": "example.todo", + "name": "To-Do List", + "glyph": "j", + "description": "A list of things that need to be done.", + "features": ["creation"], + ++ "model": { + ++ "tasks": [ + ++ { "description": "Add a type", "completed": true }, + ++ { "description": "Add a view" } + ++ ] + } + } + ], + "views": [ + { + "key": "example.todo", + "type": "example.todo", + "glyph": "j", + "name": "List", + "templateUrl": "templates/todo.html" + } + ] + } + } +__tutorials/todo/bundle.json__ + +Now, when To-do List objects are created in Open MCT Web, they will initially +have the state described by that model property. + +If we reload Open MCT Web, create a To-do List, and navigate to it in the tree, +we should now see: + +![To-Do List](images/todo-list.png) + +This looks roughly like what we want. We’ll handle styling later, so let’s work +on adding functionality. Currently, the filter choices do nothing, and while the +checkboxes can be checked/unchecked, we’re not actually making the changes in +the domain object - if we click over to My Items and come back to our +To-Do List, for instance, we’ll see that those check boxes have returned to +their initial state. + +### Step 4. Add a Controller + +We need to do some scripting to add dynamic behavior to that view. In +particular, we want to: + +* Filter by complete/incomplete status. +* Change the completion state of tasks in the model. + +To do this, we will support this by adding an Angular controller. (See +[https://docs.angularjs.org/guide/controller]() for an overview of controllers.) +We will define that in an AMD module (see [http://requirejs.org/docs/whyamd.html]()) +in the directory `tutorials/todo/src/controllers` (`src` is, by default, the +directory where bundle-related source code is kept, and controllers is where +Angular controllers are stored by convention.) + + define(function () { + function TodoController($scope) { + var showAll = true, + showCompleted; + + // Persist changes made to a domain object's model + function persist() { + var persistence = + $scope.domainObject.getCapability('persistence'); + return persistence && persistence.persist(); + } + + // Change which tasks are visible + $scope.setVisibility = function (all, completed) { + showAll = all; + showCompleted = completed; + }; + + // Toggle the completion state of a task + $scope.toggleCompletion = function (taskIndex) { + $scope.domainObject.useCapability('mutation', function (model) { + var task = model.tasks[taskIndex]; + task.completed = !task.completed; + }); + persist(); + }; + + // Check whether a task should be visible + $scope.showTask = function (task) { + return showAll || (showCompleted === !!(task.completed)); + }; + } + + return TodoController; + }); +__tutorials/todo/src/controllers/TodoController.js__ + +Here, we’ve defined three new functions and placed them in our `$scope`, which +will make them available from the template: + +`setVisibility` changes which tasks are meant to be visible. The first argument +is a boolean, which, if true, means we want to show everything; the second +argument is the completion state we want to show (which is only relevant if the +first argument is falsy.) + +`toggleCompletion` changes whether or not a task is complete. We make the change +via the domain object’s mutation capability, and then persist the change via its +persistence capability. See the Open MCT Web Developer Guide for more +information on these capabilities. + +`showTask` is meant to be used to help decide if a task should be shown, based +on the current visibility settings. It is true when we have decided to show +everything, or when the completion state matches the state we’ve chosen. (Note +the use of the double-not !! to coerce the completed flag to a boolean, for +equality testing.) + +Note that these functions make reference to `$scope.domainObject;` this is the +domain object being viewed, which is passed into the scope by Open MCT Web +prior to our template being utilized. + +On its own, this controller merely exposes these functions; the next step is to +use them from our template: + + ++
+
+ ++ All + ++ Incomplete + ++ Complete +
+ +
    +
  • + + {{task.description}} +
  • +
+ ++
+__tutorials/todo/res/templates/todo.html__ + +Summary of changes here: + +* First, we surround everything in a `div` which we use to utilize our +`TodoController`. This `div` will also come in handy later for styling. +* From our filters at the top, we change the visibility settings when a different +option is clicked. +* When showing tasks, we check with `showTask` to see if the task matches current +filter settings. +* Finally, when the checkbox for a task is clicked, we make the change in the +model via `toggleCompletion`. + +If we were to try to run at this point, we’d run into problems because the +`TodoController` has not been registered with Angular. We need to first declare +it in our bundle definition, as an extension of category `controllers`: + + { + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + "types": [ + { + "key": "example.todo", + "name": "To-Do List", + "glyph": "j", + "description": "A list of things that need to be done.", + "features": ["creation"], + "model": { + "tasks": [ + { "description": "Add a type", "completed": true }, + { "description": "Add a view" } + ] + } + } + ], + "views": [ + { + "key": "example.todo", + "type": "example.todo", + "glyph": "j", + "name": "List", + "templateUrl": "templates/todo.html" + } + ], + + "controllers": [ + + { + + "key": "TodoController", + + "implementation": "controllers/TodoController.js", + + "depends": [ "$scope" ] + + } + + ] + } + } +__tutorials/todo/bundle.json__ + +In this extension definition we have: + +* A `key`, which again is a machine-readable identifier. This is the name that +templates will reference. +* An `implementation`, which refers to an AMD module. The path is relative to the +src directory within the bundle. +* The `depends` property declares the dependencies of this controller. Here, we +want Angular to inject `$scope`, the current Angular scope (which, going back +to our controller, is expected as our first argument.) + +If we reload the browser now, our To-do List looks much the same, but now we are able to filter down the visible list, and the changes we make will stick around if we go to My Items and come back. + + +### Step 5. Support Editing + +We now have a somewhat-functional view of our To-Do List, but we’re still +missing some important functionality: Adding and removing tasks! + +This is a good place to discuss the user interface style of Open MCT Web. Open +MCT Web draws a distinction between “using” and “editing” a domain object; in +general, you can only make changes to a domain object while in Edit mode, which +is reachable from the button with a pencil icon. This distinction helps users +keep these tasks separate. + +The distinction between “using” and “editing” may vary depending on what domain +objects or views are being used. While it may be convenient for a developer to +think of “editing” as “any changes made to a domain object,” in practice some of +these activities will be thought of as “using.” + +For this tutorial we’ll consider checking/unchecking tasks as “using” To-Do +Lists, and adding/removing tasks as “editing.” We’ve already implemented the +“using” part, in this case, so let’s focus on editing. + +There are two new pieces of functionality we’ll want out of this step: + +* The ability to add new tasks. +* The ability to remove existing tasks. + +An Editing user interface is typically handled in a tool bar associated with a +view. The contents of this tool bar are defined declaratively in a view’s +extension definition. + + { + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + "types": [ + { + "key": "example.todo", + "name": "To-Do List", + "glyph": "j", + "description": "A list of things that need to be done.", + "features": ["creation"], + "model": { + "tasks": [ + { "description": "Add a type", "completed": true }, + { "description": "Add a view" } + ] + } + } + ], + "views": [ + { + "key": "example.todo", + "type": "example.todo", + "glyph": "j", + "name": "List", + "templateUrl": "templates/todo.html", + + "toolbar": { + + "sections": [ + + { + + "items": [ + + { + + "text": "Add Task", + + "glyph": "+", + + "method": "addTask", + + "control": "button" + + } + + ] + + }, + + { + + "items": [ + + { + + "glyph": "Z", + + "method": "removeTask", + + "control": "button" + + } + + ] + + } + + ] + + } + } + ], + "controllers": [ + { + "key": "TodoController", + "implementation": "controllers/TodoController.js", + "depends": [ "$scope" ] + } + ] + } + } +__tutorials/todo/bundle.json__ + +What we’ve stated here is that the To-Do List’s view will have a toolbar which +contains two sections (which will be visually separated by a divider), each of +which contains one button. The first is a button labelled “Add Task” that will +invoke an addTask method; the second is a button with a glyph (which will appear +as a trash can in Open MCT Web’s custom font set) which will invoke a removeTask +method. For more information on forms and tool bars in Open MCT Web, see the +Open MCT Web Developer Guide. + +If we reload and run Open MCT Web, we won’t see any tool bar when we switch over +to Edit mode. This is because the aforementioned methods are expected to be +found on currently-selected elements; we haven’t done anything with selections +in our view yet, so the Open MCT Web platform will filter this tool bar down to +all the applicable controls, which means no controls at all. + +To support selection, we will need to make some changes to our controller: + + + define(function () { + + // Form to display when adding new tasks + + var NEW_TASK_FORM = { + + name: "Add a Task", + + sections: [{ + + rows: [{ + + name: 'Description', + + key: 'description', + + control: 'textfield', + + required: true + + }] + + }] + + }; + + function TodoController($scope, dialogService) { + var showAll = true, + showCompleted; + + // Persist changes made to a domain object's model + function persist() { + var persistence = + $scope.domainObject.getCapability('persistence'); + return persistence && persistence.persist(); + } + + // Remove a task + function removeTaskAtIndex(taskIndex) { + $scope.domainObject.useCapability('mutation', function (model) { + model.tasks.splice(taskIndex, 1); + }); + persist(); + } + + // Add a task + function addNewTask(task) { + $scope.domainObject.useCapability('mutation', function (model) { + model.tasks.push(task); + }); + persist(); + } + + // Change which tasks are visible + $scope.setVisibility = function (all, completed) { + showAll = all; + showCompleted = completed; + }; + + // Toggle the completion state of a task + $scope.toggleCompletion = function (taskIndex) { + $scope.domainObject.useCapability('mutation', function (model) { + var task = model.tasks[taskIndex]; + task.completed = !task.completed; + }); + persist(); + }; + + // Check whether a task should be visible + $scope.showTask = function (task) { + return showAll || (showCompleted === !!(task.completed)); + }; + + // Handle selection state in edit mode + if ($scope.selection) { + // Expose the ability to select tasks + $scope.selectTask = function (taskIndex) { + $scope.selection.select({ + removeTask: function () { + removeTaskAtIndex(taskIndex); + $scope.selection.deselect(); + } + }); + }; + + // Expose a view-level selection proxy + $scope.selection.proxy({ + addTask: function () { + dialogService.getUserInput(NEW_TASK_FORM, {}) + .then(addNewTask); + } + }); + } + } + + return TodoController; + }); +__tutorials/todo/src/controllers/TodoController.js__ + + There are a few changes to pay attention to here. Let’s review them: + +At the top, we describe the form that should be shown to the user when they click the Add Task button. This form is described declaratively, and populates an object that has the same format as tasks in the tasks array of our To-Do List’s model. +We’ve added an argument to the TodoController: The dialogService, which is exposed by the Open MCT Web platform to handle showing dialogs. +Some utility functions for handling the actual adding and removing of tasks. These use the mutation capability to modify the tasks in the To-Do List’s model. +Finally, we check for the presence of a selection object in our scope. This object is provided by Edit mode to manage current selections for editing. When it is present, we expose a selectTask function to our scope to allow selecting individual tasks; when this occurs, we expose an object to selection which has a removeTask method, as expected by the tool bar we’ve defined. We additionally expose a view proxy, to handle view-level changes (e.g. not associated with any specific selected object); this has an addTask method, which again is expected by the tool bar we’ve defined. + + Additionally, we need to make changes to our template to select specific tasks in response to some user gesture. Here, we will select tasks when a user clicks the description. + +
+ + +
    +
  • + + + {{task.description}} + +
  • +
+
+tutorials/todo/res/templates/todo.html + + Finally, the TodoController uses the dialogService now, so we need to declare that dependency in its extension definition: + +{ + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + "types": [ + { + "key": "example.todo", + "name": "To-Do List", + "glyph": "j", + "description": "A list of things that need to be done.", + "features": ["creation"], + "model": { + "tasks": [ + { "description": "Add a type", "completed": true }, + { "description": "Add a view" } + ] + } + } + ], + "views": [ + { + "key": "example.todo", + "type": "example.todo", + "glyph": "j", + "name": "List", + "templateUrl": "templates/todo.html", + "toolbar": { + "sections": [ + { + "items": [ + { + "text": "Add Task", + "glyph": "+", + "method": "addTask", + "control": "button" + } + ] + }, + { + "items": [ + { + "glyph": "Z", + "method": "removeTask", + "control": "button" + } + ] + } + ] + } + } + ], + "controllers": [ + { + "key": "TodoController", + "implementation": "controllers/TodoController.js", + "depends": [ "$scope", "dialogService" ] + } + ] + } +} +tutorials/todo/bundle.json + + If we now reload Open MCT Web, we’ll be able to see the new functionality we’ve added. If we Create a new To-Do List, navigate to it, and click the button with the Pencil icon in the top-right, we’ll be in edit mode. We see, first, that our “Add Task” button appears in the tool bar: + + + +If we click on this, we’ll get a dialog allowing us to add a new task: + + + + Finally, if we click on the description of a specific task, we’ll see a new button appear, which we can then click on to remove that task: + + + + As always in Edit mode, the user will be able to Save or Cancel any changes they have made. +In terms of functionality, our To-Do List can do all the things we want, but the appearance is still lacking. In particular, we can’t distinguish our current filter choice or our current selection state. + +Step 6. Customizing Look and Feel + + In this section, our goal is to: + +Display the current filter choice. +Display the current task selection (when in Edit mode.) +Tweak the general aesthetics to our liking. +Get rid of those default tasks (we can create our own now.) + + To support the first two, we’ll need to expose some methods for checking these states in the controller: + + +define(function () { + // Form to display when adding new tasks + var NEW_TASK_FORM = { + name: "Add a Task", + sections: [{ + rows: [{ + name: 'Description', + key: 'description', + control: 'textfield', + required: true + }] + }] + }; + + function TodoController($scope, dialogService) { + var showAll = true, + showCompleted; + + // Persist changes made to a domain object's model + function persist() { + var persistence = + $scope.domainObject.getCapability('persistence'); + return persistence && persistence.persist(); + } + + // Remove a task + function removeTaskAtIndex(taskIndex) { + $scope.domainObject.useCapability('mutation', function (model) { + model.tasks.splice(taskIndex, 1); + }); + persist(); + } + + // Add a task + function addNewTask(task) { + $scope.domainObject.useCapability('mutation', function (model) { + model.tasks.push(task); + }); + persist(); + } + + // Change which tasks are visible + $scope.setVisibility = function (all, completed) { + showAll = all; + showCompleted = completed; + }; + + // Check if current visibility settings match + $scope.checkVisibility = function (all, completed) { + return showAll ? all : (completed === showCompleted); + }; + + // Toggle the completion state of a task + $scope.toggleCompletion = function (taskIndex) { + $scope.domainObject.useCapability('mutation', function (model) { + var task = model.tasks[taskIndex]; + task.completed = !task.completed; + }); + persist(); + }; + + // Check whether a task should be visible + $scope.showTask = function (task) { + return showAll || (showCompleted === !!(task.completed)); + }; + + // Handle selection state in edit mode + if ($scope.selection) { + // Expose the ability to select tasks + $scope.selectTask = function (taskIndex) { + $scope.selection.select({ + removeTask: function () { + removeTaskAtIndex(taskIndex); + $scope.selection.deselect(); + }, + taskIndex: taskIndex + }); + }; + + // Expose a check for current selection state + $scope.isSelected = function (taskIndex) { + return ($scope.selection.get() || {}).taskIndex === taskIndex; + }; + + // Expose a view-level selection proxy + $scope.selection.proxy({ + addTask: function () { + dialogService.getUserInput(NEW_TASK_FORM, {}) + .then(addNewTask); + } + }); + } + } + + return TodoController; +}); +tutorials/todo/src/controllers/TodoController.js + + A summary of these changes: + +checkVisibility has the same arguments as setVisibility, but instead of making a change, it simply returns a boolean true/false indicating whether those settings are in effect. The logic reflects the fact that the second parameter is ignored when showing all. +To support checking for selection, the index of the currently-selected task is tracked as part of the selection object. +Finally, an isSelected function is exposed which checks if the indicated task is currently selected, using the index from above. + + Additionally, we will want to define some CSS rules in order to reflect these states visually, and to generally improve the appearance of our view. We add another file to the res directory of our bundle; this time, it is css/todo.css (with the css directory again being a convention.) + +.example-todo div.example-button-group { + margin-top: 12px; + margin-bottom: 12px; +} + +.example-todo .example-button-group a { + padding: 3px; + margin: 3px; +} + +.example-todo .example-button-group a.selected { + border: 1px gray solid; + border-radius: 3px; + background: #444; +} + +.example-todo .example-task-completed .example-task-description { + text-decoration: line-through; + opacity: 0.75; +} + +.example-todo .example-task-description.selected { + background: #46A; + border-radius: 3px; +} + +.example-todo .example-message { + font-style: italic; +} +tutorials/todo/res/css/todo.css + + Here, we have defined classes and appearances for: + +Our filter choosers (example-button-group). +Our selected and/or completed tasks (example-task-description). +A message, which we will add next, to display when there are no tasks (example-message). + + To include this CSS file in our running instance of Open MCT Web, we need to declare it in our bundle definition, this time as an extension of category stylesheets: + + +{ + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + "types": [ + { + "key": "example.todo", + "name": "To-Do List", + "glyph": "j", + "description": "A list of things that need to be done.", + "features": ["creation"], + "model": { + "tasks": [] + } + } + ], + "views": [ + { + "key": "example.todo", + "type": "example.todo", + "glyph": "j", + "name": "List", + "templateUrl": "templates/todo.html", + "toolbar": { + "sections": [ + { + "items": [ + { + "text": "Add Task", + "glyph": "+", + "method": "addTask", + "control": "button" + } + ] + }, + { + "items": [ + { + "glyph": "Z", + "method": "removeTask", + "control": "button" + } + ] + } + ] + } + } + ], + "controllers": [ + { + "key": "TodoController", + "implementation": "controllers/TodoController.js", + "depends": [ "$scope", "dialogService" ] + } + ], + "stylesheets": [ + { + "stylesheetUrl": "css/todo.css" + } + ] + } +} +tutorials/todo/bundle.json + + Note that we’ve also removed our placeholder tasks from the model of the To-Do List’s type above; now To-Do Lists will start off empty. + + Finally, let’s utilize these changes from our view’s template: + + +
+ + +
    +
  • + + + {{task.description}} + +
  • +
+ +
+ There are no tasks to show. +
+
+tutorials/todo/res/templates/todo.html + + Now, if we reload our page and create a new To-Do List, we will initially see: + + + + If we then go into Edit mode, add some tasks, and select one, it will now be much clearer what the current selection is (e.g. before we hit the remove button in the toolbar): + + + +Bar Graph + + In this tutorial, we will look at creating a bar graph plugin for visualizing telemetry data. Specifically, we want some bars that raise and lower to match the observed state of real-time telemetry; this is particularly useful for monitoring things like battery charge levels. + It is recommended that the reader completes (or is familiar with) the To-Do List tutorial before completing this tutorial, as certain concepts discussed there will be addressed in more brevity here. + +Step 1. Define the View + + Since the goal is to introduce a new view and expose it from a plugin, we will want to create a new bundle which declares an extension of category views. We’ll also be defining some custom styles, so we’ll include that extension as well. We’ll be creating this plugin in tutorials/bargraph, so our initial bundle definition looks like: + +{ + "name": "Bar Graph", + "description": "Provides the Bar Graph view of telemetry elements.", + "extensions": { + "views": [ + { + "name": "Bar Graph", + "key": "example.bargraph", + "glyph": "H", + "templateUrl": "templates/bargraph.html", + "needs": [ "telemetry" ], + "delegation": true + } + ], + "stylesheets": [ + { + "stylesheetUrl": "css/bargraph.css" + } + ] + } +} +tutorials/bargraph/bundle.json + + The view definition should look familiar after the To-Do List tutorial, with some additions: + +The needs property indicates that this view is only applicable to domain objects with a telemetry capability. This ensures that this view is available for telemetry points, but not for other objects (like folders.) +The delegation property indicates that the above constraint can be satisfied via capability delegation; that is, by domain objects which delegate the telemetry capability to their contained objects. This allows this view to be used for Telemetry Panel objects as well as for individual telemetry-providing domain objects. + + For this tutorial, we’ll assume that we’ve sketched out our template and CSS file ahead of time to describe the general look we want for the view. These look like: + + +
+
+
High
+
Middle
+
Low
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ Label A +
+
+ Label B +
+
+ Label C +
+
+
+tutorials/bargraph/res/templates/bargraph.html + + Here, three regions are defined. The first will be for tick labels along the vertical axis, showing the numeric value that certain heights correspond to. The second will be for the actual bar graphs themselves; three are included here. The third is for labels along the horizontal axis, which will indicate which bar corresponds to which telemetry point. Inline style attributes are used wherever dynamic positioning (handled by a script) is anticipated. + The corresponding CSS file which styles and positions these elements: + +.example-bargraph { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + mid-width: 160px; + min-height: 160px; +} + +.example-bargraph .example-tick-labels { + position: absolute; + left: 0; + top: 24px; + bottom: 32px; + width: 72px; + font-size: 75%; +} + +.example-bargraph .example-tick-label { + position: absolute; + right: 0; + height: 1em; + margin-bottom: -0.5em; + padding-right: 6px; + text-align: right; +} + +.example-bargraph .example-graph-area { + position: absolute; + border: 1px gray solid; + left: 72px; + top: 24px; + bottom: 32px; + right: 0; +} + +.example-bargraph .example-bar-labels { + position: absolute; + left: 72px; + bottom: 0; + right: 0; + height: 32px; +} + +.example-bargraph .example-bar-holder { + position: absolute; + top: 0; + bottom: 0; +} + +.example-bargraph .example-graph-tick { + position: absolute; + width: 100%; + height: 1px; + border-bottom: 1px gray dashed; +} + +.example-bargraph .example-bar { + position: absolute; + background: darkcyan; + right: 4px; + left: 4px; +} + +.example-bargraph .example-label { + text-align: center; + font-size: 85%; + padding-top: 6px; +} +tutorials/bargraph/res/css/bargraph.css + + This is already enough that, if we add “tutorials/bargraph” to bundles.json, we should be able to run Open MCT Web and see our Bar Graph as an available view for domain objects which provide telemetry (such as the example Sine Wave Generator) as well as for Telemetry Panel objects: + + + This means that our remaining work will be to populate and position these elements based on the actual contents of the domain object. + +Step 2. Add a Controller + + Our next step will be to begin dynamically populating this template’s contents. Specifically, our goals for this step will be to: + +Show one bar per telemetry-providing domain object (for which we’ll be getting actual telemetry data in subsequent steps.) +Show correct labels for these objects at the bottom. +Show numeric labels on the left-hand side. + + Notably, we will not try to show telemetry data after this step. + + To support this, we will add a new controller which supports our Bar Graph view: + + +define(function () { + function BarGraphController($scope, telemetryHandler) { + var handle; + + // Add min/max defaults + $scope.low = -1; + $scope.middle = 0; + $scope.high = 1; + + // Convert value to a percent between 0-100, keeping values in points + $scope.toPercent = function (value) { + var pct = 100 * (value - $scope.low) / ($scope.high - $scope.low); + return Math.min(100, Math.max(0, pct)); + }; + + // Use the telemetryHandler to get telemetry objects here + handle = telemetryHandler.handle($scope.domainObject, function () { + $scope.telemetryObjects = handle.getTelemetryObjects(); + $scope.barWidth = + 100 / Math.max(($scope.telemetryObjects).length, 1); + }); + + // Release subscriptions when scope is destroyed + $scope.$on('$destroy', handle.unsubscribe); + } + + return BarGraphController; +}); +tutorials/bargraph/src/controllers/BarGraphController.js + + A summary of what we’ve done here: + +We’re exposing some numeric values that will correspond to the low, middle, and high end of the graph. (The medium attribute will be useful for positioning the middle line, which are graphs will ultimately descend down or push up from.) +Add a utility function which converts from numeric values to percentages. This will help support some positioning in the template. +Utilize the telemetryHandler, provided by the platform, to start listening to real-time telemetry updates. This will deal with most of the complexity of dealing with telemetry (e.g. differentiating between individual telemetry points and telemetry panels, monitoring latest values) and provide us with a useful interface for populating our view. The the Open MCT Web Developer Guide for more information on dealing with telemetry. + + Whenever the telemetry handler invokes its callbacks, we update the set of telemetry objects in view, as well as the width for each bar. + + We will also utilize this from our template: + +
+
+
+ {{value}} +
+
+ +
+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+tutorials/bargraph/res/templates/bargraph.html + + Summarizing these changes: + +Utilize the exposed low, middle, and high values to populate our labels along the vertical axis. Additionally, use the toPercent function to position these from the bottom. +Replace our three hard-coded bars with a repeater that looks at the telemetryObjects exposed by the controller and adds one bar each. +Position the dashed tick-line using the middle value and the toPercent function, lining it up with its label to the left. +At the bottom, repeat a set of labels for the telemetry-providing domain objects, with matching alignment to the bars above. We use an existing representation, label, to make this easier. + + Finally, we expose our controller from our bundle definition. Note that the depends declaration includes both $scope as well as the telemetryHandler service we made use of. + + +{ + "name": "Bar Graph", + "description": "Provides the Bar Graph view of telemetry elements.", + "extensions": { + "views": [ + { + "name": "Bar Graph", + "key": "example.bargraph", + "glyph": "H", + "templateUrl": "templates/bargraph.html", + "needs": [ "telemetry" ], + "delegation": true + } + ], + "stylesheets": [ + { + "stylesheetUrl": "css/bargraph.css" + } + ], + "controllers": [ + { + "key": "BarGraphController", + "implementation": "controllers/BarGraphController.js", + "depends": [ "$scope", "telemetryHandler" ] + } + ] + } +} +tutorials/bargraph/bundle.json + + When we reload Open MCT Web, we are now able to see that our bar graph view correctly labels one bar per telemetry-providing domain object, as shown for this Telemetry Panel containing four Sine Wave Generators. + + + +Step 3. Using Telemetry Data + + Now that our bar graph is labeled correctly, it’s time to start putting data into the view. + + First, let’s add expose some more functionality from our controller. To make it simple, we’ll expose the top and bottom for a bar graph for a given telemetry-providing domain object, as percentages. + + +define(function () { + function BarGraphController($scope, telemetryHandler) { + var handle; + + // Add min/max defaults + $scope.low = -1; + $scope.middle = 0; + $scope.high = 1; + + // Convert value to a percent between 0-100, keeping values in points + $scope.toPercent = function (value) { + var pct = 100 * (value - $scope.low) / ($scope.high - $scope.low); + return Math.min(100, Math.max(0, pct)); + }; + + // Get bottom and top (as percentages) for current value + $scope.getBottom = function (telemetryObject) { + var value = handle.getRangeValue(telemetryObject); + return $scope.toPercent(Math.min($scope.middle, value)); + } + $scope.getTop = function (telemetryObject) { + var value = handle.getRangeValue(telemetryObject); + return 100 - $scope.toPercent(Math.max($scope.middle, value)); + } + + // Use the telemetryHandler to get telemetry objects here + handle = telemetryHandler.handle($scope.domainObject, function () { + $scope.telemetryObjects = handle.getTelemetryObjects(); + $scope.barWidth = + 100 / Math.max(($scope.telemetryObjects).length, 1); + }); + + // Release subscriptions when scope is destroyed + $scope.$on('$destroy', handle.unsubscribe); + } + + return BarGraphController; +}); +tutorials/bargraph/src/controllers/BarGraphController.js + + The telemetryHandler exposes a method to provide us with our latest data value (the getRangeValue method), and we already have a function to convert from a numeric value to a percentage within the view, so we just use those. The only slight complication is that we want our bar to move up or down from the middle value, so either of our top or bottom position for the bar itself could be either the middle line, or the data value. We let Math.min and Math.max decide this. + + Next, we utilize this functionality from the template: + +
+
+
+ {{value}} +
+
+ +
+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+tutorials/bargraph/res/templates/bargraph.html + + Here, we utilize the functions we just provided from the controller to position the bar, using an ng-style attribute. + + When we reload Open MCT Web, our bar graph view now looks like: + + + +Step 4. View Configuration + + The default minimum and maximum values we’ve provided happen to make sense for sine waves, but what about other values? We want to provide the user with a means of configuring these boundaries. + +This is normally done via Edit mode. Since view configuration is a common problem, the Open MCT Web platform exposes a configuration object - called configuration - into our view’s scope. We can populate it as we please, and when we return to our view later, those changes will be persisted. + +First, let’s add a tool bar for changing these three values in Edit mode: + +{ + "name": "Bar Graph", + "description": "Provides the Bar Graph view of telemetry elements.", + "extensions": { + "views": [ + { + "name": "Bar Graph", + "key": "example.bargraph", + "glyph": "H", + "templateUrl": "templates/bargraph.html", + "needs": [ "telemetry" ], + "delegation": true, + "toolbar": { + "sections": [ + { + "items": [ + { + "name": "Low", + "property": "low", + "required": true, + "control": "textfield", + "size": 4 + }, + { + "name": "Middle", + "property": "middle", + "required": true, + "control": "textfield", + "size": 4 + }, + { + "name": "High", + "property": "high", + "required": true, + "control": "textfield", + "size": 4 + } + ] + } + ] + } + } + ], + "stylesheets": [ + { + "stylesheetUrl": "css/bargraph.css" + } + ], + "controllers": [ + { + "key": "BarGraphController", + "implementation": "controllers/BarGraphController.js", + "depends": [ "$scope", "telemetryHandler" ] + } + ] + } +} +tutorials/bargraph/bundle.json + + As we saw in to To-Do List plugin, a tool bar needs either a selected object or a view proxy to work from. We will add this to our controller, and additionally will start reading/writing those properties to the view’s configuration object. + +define(function () { + function BarGraphController($scope, telemetryHandler) { + var handle; + + // Expose configuration constants directly in scope + function exposeConfiguration() { + $scope.low = $scope.configuration.low; + $scope.middle = $scope.configuration.middle; + $scope.high = $scope.configuration.high; + } + + // Populate a default value in the configuration + function setDefault(key, value) { + if ($scope.configuration[key] === undefined) { + $scope.configuration[key] = value; + } + } + + // Getter-setter for configuration properties (for view proxy) + function getterSetter(property) { + return function (value) { + value = parseFloat(value); + if (!isNaN(value)) { + $scope.configuration[property] = value; + exposeConfiguration(); + } + return $scope.configuration[property]; + }; + } + + // Add min/max defaults + setDefault('low', -1); + setDefault('middle', 0); + setDefault('high', 1); + exposeConfiguration($scope.configuration); + + // Expose view configuration options + if ($scope.selection) { + $scope.selection.proxy({ + low: getterSetter('low'), + middle: getterSetter('middle'), + high: getterSetter('high') + }); + } + + // Convert value to a percent between 0-100 + $scope.toPercent = function (value) { + var pct = 100 * (value - $scope.low) / + ($scope.high - $scope.low); + return Math.min(100, Math.max(0, pct)); + }; + + // Get bottom and top (as percentages) for current value + $scope.getBottom = function (telemetryObject) { + var value = handle.getRangeValue(telemetryObject); + return $scope.toPercent(Math.min($scope.middle, value)); + } + $scope.getTop = function (telemetryObject) { + var value = handle.getRangeValue(telemetryObject); + return 100 - $scope.toPercent(Math.max($scope.middle, value)); + } + + // Use the telemetryHandler to get telemetry objects here + handle = telemetryHandler.handle($scope.domainObject, function () { + $scope.telemetryObjects = handle.getTelemetryObjects(); + $scope.barWidth = + 100 / Math.max(($scope.telemetryObjects).length, 1); + }); + + // Release subscriptions when scope is destroyed + $scope.$on('$destroy', handle.unsubscribe); + } + + return BarGraphController; +}); +tutorials/bargraph/src/controllers/BarGraphController.js + + A summary of these changes: + +First, read low, middle, and high from the view configuration instead of initializing them to explicit values. This is placed into its own function, since it will be called a lot. +The function setDefault is included; it will be used to set the default values for low, middle, and high in the view configuration, but only if they aren’t present. +The tool bar will treat properties in a view proxy as getter-setters if they are functions; that is, they will be called with an argument to be used as a setter, and with no argument to use as a getter. We provide ourselves a function for making these getter-setters (since we’ll need three) that additionally handles some checking to ensure that these are actually numbers. +After that, we actually initialize both the view configuration object with defaults (if needed), and expose its state into the scope. +Finally, we expose a view proxy which will handle changes to low, middle, and high as entered by the user from the tool bar. This uses the getter-setters we defined previously. + + If we reload Open MCT Web and go to a Bar Graph view in Edit mode, we now see that we can change these bounds from the tool bar. + + + +Telemetry Adapter + + The goal of this tutorial is to demonstrate how to integrate Open MCT Web with an existing telemetry system. + A summary of the steps we will take: + +Expose the telemetry dictionary within the user interface. +Support subscription/unsubscription to real-time streaming data. +Support historical retrieval of telemetry data. + +Step 0. Expose Your Telemetry + + As a precondition to integrating telemetry data into Open MCT Web, this information needs to be available over web-based interfaces. In practice, this will most likely mean exposing data over HTTP, or over WebSockets. + For purposes of this tutorial, a simple node server is provided to stand in place of this existing telemetry system. It generates real-time data and exposes it over a WebSocket connection. + + +/*global require,process,console*/ + +var CONFIG = { + port: 8081, + dictionary: "dictionary.json", + interval: 1000 +}; + +(function () { + "use strict"; + + var WebSocketServer = require('ws').Server, + fs = require('fs'), + wss = new WebSocketServer({ port: CONFIG.port }), + dictionary = JSON.parse(fs.readFileSync(CONFIG.dictionary, "utf8")), + spacecraft = { + "prop.fuel": 77, + "prop.thrusters": "OFF", + "comms.recd": 0, + "comms.sent": 0, + "pwr.temp": 245, + "pwr.c": 8.15, + "pwr.v": 30 + }, + histories = {}, + listeners = []; + + function updateSpacecraft() { + spacecraft["prop.fuel"] = Math.max( + 0, + spacecraft["prop.fuel"] - + (spacecraft["prop.thrusters"] === "ON" ? 0.5 : 0) + ); + spacecraft["pwr.temp"] = spacecraft["pwr.temp"] * 0.985 + + Math.random() * 0.25 + Math.sin(Date.now()); + spacecraft["pwr.c"] = spacecraft["pwr.c"] * 0.985; + spacecraft["pwr.v"] = 30 + Math.pow(Math.random(), 3); + } + + function generateTelemetry() { + var timestamp = Date.now(), sent = 0; + Object.keys(spacecraft).forEach(function (id) { + var state = { timestamp: timestamp, value: spacecraft[id] }; + histories[id] = histories[id] || []; // Initialize + histories[id].push(state); + spacecraft["comms.sent"] += JSON.stringify(state).length; + }); + listeners.forEach(function (listener) { + listener(); + }); + } + + function update() { + updateSpacecraft(); + generateTelemetry(); + } + + function handleConnection(ws) { + var subscriptions = {}, // Active subscriptions for this connection + handlers = { // Handlers for specific requests + dictionary: function () { + ws.send(JSON.stringify({ + type: "dictionary", + value: dictionary + })); + }, + subscribe: function (id) { + subscriptions[id] = true; + }, + unsubscribe: function (id) { + delete subscriptions[id]; + }, + history: function (id) { + ws.send(JSON.stringify({ + type: "history", + id: id, + value: histories[id] + })); + } + }; + + function notifySubscribers() { + Object.keys(subscriptions).forEach(function (id) { + var history = histories[id]; + if (history) { + ws.send(JSON.stringify({ + type: "data", + id: id, + value: history[history.length - 1] + })); + } + }); + } + + // Listen for requests + ws.on('message', function (message) { + var parts = message.split(' '), + handler = handlers[parts[0]]; + if (handler) { + handler.apply(handlers, parts.slice(1)); + } + }); + + // Stop sending telemetry updates for this connection when closed + ws.on('close', function () { + listeners = listeners.filter(function (listener) { + return listener !== notifySubscribers; + }); + }); + + // Notify subscribers when telemetry is updated + listeners.push(notifySubscribers); + } + + update(); + setInterval(update, CONFIG.interval); + + wss.on('connection', handleConnection); + + console.log("Example spacecraft running on port "); + console.log("Press Enter to toggle thruster state."); + process.stdin.on('data', function (data) { + spacecraft['prop.thrusters'] = + (spacecraft['prop.thrusters'] === "OFF") ? "ON" : "OFF"; + console.log("Thrusters " + spacecraft["prop.thrusters"]); + }); +}()); + + +tutorial-server/app.js + + For purposes of this tutorial, how this server has been implemented is not important; it has just enough functionality to resemble a WebSocket interface to a real telemetry system, and niceties such as error-handling have been omitted. (For more information on using WebSockets, both in the client and on the server, https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API is an excellent starting point.) + What does matter for this tutorial is the interfaces that are exposed. Once a WebSocket connection has been established to this server, it accepts plain text messages in the following formats, and issues JSON-formatted responses. + + The requests it handles are: + +dictionary: Responds with a JSON response with the following fields: +type: “dictionary” +value: … the telemetry dictionary (see below) … +subscribe : Subscribe to new telemetry data for the measurement with the provided identifier. The server will begin sending messages of the following form: +type: “data” +id: The identifier for the measurement. +value: An object containing the actual measurement, in two fields: +timestamp: A UNIX timestamp (in milliseconds) for the “measurement” +value: The data value for the measurement (either a number, or a string) +unsubscribe : Stop receiving new data for the identified measurement. +history : Request a history of all telemetry data for the identified measurement. +type: “history” +id: The identifier for the measurement. +value: An array of objects containing the actual measurement, each of which having two fields: +timestamp: A UNIX timestamp (in milliseconds) for the “measurement” +value: The data value for the measurement (either a number, or a string) + + (Note that the term “measurement” is used to describe a distinct data series within this system; in other systems, these have been called channels, mnemonics, telemetry points, or other names. No preference is made here; Open MCT Web is easily adapted to use the terminology appropriate to your system.) + Additionally, while running the server from the terminal we can toggle the state of the “spacecraft” by hitting enter; this will turn the “thrusters” on and off, having observable changes in telemetry. + + The telemetry dictionary referenced previously is contained in a separate file, used by the server. It uses a custom format and, for purposes of example, contains three “subsystems” containing a mix of numeric and string-based telemetry. + + + +{ + "name": "Example Spacecraft", + "identifier": "sc", + "subsystems": [ + { + "name": "Propulsion", + "identifier": "prop", + "measurements": [ + { + "name": "Fuel", + "identifier": "prop.fuel", + "units": "kilograms", + "type": "float" + }, + { + "name": "Thrusters", + "identifier": "prop.thrusters", + "units": "None", + "type": "string" + } + ] + }, + { + "name": "Communications", + "identifier": "comms", + "measurements": [ + { + "name": "Received", + "identifier": "comms.recd", + "units": "bytes", + "type": "integer" + }, + { + "name": "Sent", + "identifier": "comms.sent", + "units": "bytes", + "type": "integer" + } + ] + }, + { + "name": "Power", + "identifier": "pwr", + "measurements": [ + { + "name": "Generator Temperature", + "identifier": "pwr.temp", + "units": "\u0080C", + "type": "float" + }, + { + "name": "Generator Current", + "identifier": "pwr.c", + "units": "A", + "type": "float" + }, + { + "name": "Generator Voltage", + "identifier": "pwr.v", + "units": "V", + "type": "float" + } + ] + } + ] +} +tutorial-server/dictionary.json + + It should be noted that neither the interface for the example server nor the dictionary format are expected by Open MCT Web; rather, these are intended to stand in for some existing source of telemetry data to which we wish to adapt Open MCT Web. + + We can run this example server by: + +cd tutorial-server +npm install ws +node app.js + +To verify that this is running and try out its interface, we can use a tool like https://www.npmjs.com/package/wscat: + +wscat -c ws://localhost:8081 +connected (press CTRL+C to quit) +> dictionary +< {"type":"dictionary","value":{"name":"Example Spacecraft","identifier":"sc","subsystems":[{"name":"Propulsion","identifier":"prop","measurements":[{"name":"Fuel","identifier":"prop.fuel","units":"kilograms","type":"float"},{"name":"Thrusters","identifier":"prop.thrusters","units":"None","type":"string"}]},{"name":"Communications","identifier":"comms","measurements":[{"name":"Received","identifier":"comms.recd","units":"bytes","type":"integer"},{"name":"Sent","identifier":"comms.sent","units":"bytes","type":"integer"}]},{"name":"Power","identifier":"pwr","measurements":[{"name":"Generator Temperature","identifier":"pwr.temp","units":"€C","type":"float"},{"name":"Generator Current","identifier":"pwr.c","units":"A","type":"float"},{"name":"Generator Voltage","identifier":"pwr.v","units":"V","type":"float"}]}]}} + + Now that the example server’s interface is reasonably well-understood, a plugin can be written to adapt Open MCT Web to utilize it. + +Step 1. Add a Top-level Object + + Since Open MCT Web uses an “object-first” approach to accessing data, before we’ll be able to do anything with this new data source, we’ll need to have a way to explore the available measurements in the tree. In this step, we will add a top-level object which will serve as a container; in the next step, we will populate this with the contents of the telemetry dictionary (which we will retrieve from the server.) + +{ + "name": "Example Telemetry Adapter", + "extensions": { + "types": [ + { + "name": "Spacecraft", + "key": "example.spacecraft", + "glyph": "o" + } + ], + "roots": [ + { + "id": "example:sc", + "priority": "preferred", + "model": { + "type": "example.spacecraft", + "name": "My Spacecraft", + "composition": [] + } + } + ] + } +} + + + +tutorials/telemetry/bundle.json + + Here, we’ve created our initial telemetry plugin. This exposes a new domain object type (the “Spacecraft”, which will be represented by the contents of the telemetry dictionary) and also adds one instance of it as a root-level object (by declaring an extension of category roots.) We have also set priority to preferred so that this shows up near the top, instead of below My Items. + + If we include this in our set of active bundles: + +[ + "platform/framework", + "platform/core", + "platform/representation", + "platform/commonUI/about", + "platform/commonUI/browse", + "platform/commonUI/edit", + "platform/commonUI/dialog", + "platform/commonUI/general", + "platform/containment", + "platform/telemetry", + "platform/features/layout", + "platform/features/pages", + "platform/features/plot", + "platform/features/scrolling", + "platform/forms", + "platform/persistence/queue", + "platform/policy", + + "example/persistence", + "example/generator" +] +[ + "platform/framework", + "platform/core", + "platform/representation", + "platform/commonUI/about", + "platform/commonUI/browse", + "platform/commonUI/edit", + "platform/commonUI/dialog", + "platform/commonUI/general", + "platform/containment", + "platform/telemetry", + "platform/features/layout", + "platform/features/pages", + "platform/features/plot", + "platform/features/scrolling", + "platform/forms", + "platform/persistence/queue", + "platform/policy", + + "example/persistence", + "example/generator", + + "tutorials/telemetry" +] +bundles.json + + ...we will be able to reload Open MCT Web and see that it is present: + + + + Now, we have somewhere in the UI to put the contents of our telemetry dictionary. + +Step 2. Expose the Telemetry Dictionary + + In order to expose the telemetry dictionary, we first need to read it from the server. Our first step will be to add a service that will handle interactions with the server; this will not be used by Open MCT Web directly, but will be used by subsequent components we add. + +/*global define,WebSocket*/ + +define( + [], + function () { + "use strict"; + + function ExampleTelemetryServerAdapter($q, wsUrl) { + var ws = new WebSocket(wsUrl), + dictionary = $q.defer(); + + // Handle an incoming message from the server + ws.onmessage = function (event) { + var message = JSON.parse(event.data); + + switch (message.type) { + case "dictionary": + dictionary.resolve(message.value); + break; + } + }; + + // Request dictionary once connection is established + ws.onopen = function () { + ws.send("dictionary"); + }; + + return { + dictionary: function () { + return dictionary.promise; + } + }; + } + + return ExampleTelemetryServerAdapter; + } +); +tutorials/telemetry/src/ExampleTelemetryServerAdapter.js + + When created, this service initiates a connection to the server, and begins loading the dictionary. This will occur asynchronously, so the dictionary() method it exposes returns a Promise for the loaded dictionary (dictionary.json from above), using Angular’s $q (see https://docs.angularjs.org/api/ng/service/$q.) Note that error- and close-handling for this WebSocket connection have been omitted for brevity. + +Once the dictionary has been loaded, we will want to represent its contents as domain objects. Specifically, we want subsystems to appear as objects under My Spacecraft, and measurements to appear as objects within those subsystems. This means that we need to convert the data from the dictionary into domain object models, and expose these to Open MCT Web via a modelService. + + +/*global define*/ + +define( + function () { + "use strict"; + + var PREFIX = "example_tlm:", + FORMAT_MAPPINGS = { + float: "number", + integer: "number", + string: "string" + }; + + function ExampleTelemetryModelProvider(adapter, $q) { + var modelPromise, empty = $q.when({}); + + // Check if this model is in our dictionary (by prefix) + function isRelevant(id) { + return id.indexOf(PREFIX) === 0; + } + + // Build a domain object identifier by adding a prefix + function makeId(element) { + return PREFIX + element.identifier; + } + + // Create domain object models from this dictionary + function buildTaxonomy(dictionary) { + var models = {}; + + // Create & store a domain object model for a measurement + function addMeasurement(measurement) { + var format = FORMAT_MAPPINGS[measurement.type]; + models[makeId(measurement)] = { + type: "example.measurement", + name: measurement.name, + telemetry: { + key: measurement.identifier, + ranges: [{ + key: "value", + name: "Value", + units: measurement.units, + format: format + }] + } + }; + } + + // Create & store a domain object model for a subsystem + function addSubsystem(subsystem) { + var measurements = + (subsystem.measurements || []); + models[makeId(subsystem)] = { + type: "example.subsystem", + name: subsystem.name, + composition: measurements.map(makeId) + }; + measurements.forEach(addMeasurement); + } + + (dictionary.subsystems || []).forEach(addSubsystem); + + return models; + } + + // Begin generating models once the dictionary is available + modelPromise = adapter.dictionary().then(buildTaxonomy); + + return { + getModels: function (ids) { + // Return models for the dictionary only when they + // are relevant to the request. + return ids.some(isRelevant) ? modelPromise : empty; + } + }; + } + + return ExampleTelemetryModelProvider; + } +); +tutorials/telemetry/src/ExampleTelemetryModelProvider.js + + This script implements a provider for modelService; the modelService is a composite service, meaning that multiple such services can exist side by side. (For example, there is another provider for modelService that reads domain object models from the persistence store.) +Here, we read the dictionary using the server adapter from above; since this will be loaded asynchronously, we use promise-chaining (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Chaining) to take that result and build up an object mapping identifiers to new domain object models. This is returned from our modelService, but only when the request actually calls for identifiers that look like they’re from the dictionary. This means that loading other models is not blocked by loading the dictionary. (Note that the modelService contract allows us to return either a sub- or superset of the requested models, so it is fine to always return the whole dictionary.) +Some notable points to call out here: + +Every subsystem and every measurement from the dictionary has an identifier field declared. We use this as part of the domain object identifier, but we also prefix it with example_tlm:. This accomplishes a few things: +We can easily tell whether an identifier is expected to be in the dictionary or not. +We avoid naming collisions with other model providers. +Finally, Open MCT Web uses the colon prefix as a hint that this domain object will not be in the persistence store. +A couple of new types are introduced here (in the type field of the domain object models we create); we will need to define these as extensions as well in order for them to display correctly. +The composition field of each subsystem contained the Open MCT Web identifiers of all the measurements in that subsystem. This composition field will be used by Open MCT Web to determine what domain objects contain other domain objects (e.g. to populate the tree.) +The telemetry field of each measurement will be used by Open MCT Web to understand how to request and interpret telemetry data for this object. The key is the machine-readable identifier for this measurement within the telemetry system; the ranges provide metadata about the values for this data. (A separate field, domains, provides metadata about timestamps or other ordering properties of the data, but this will be the same for all measurements, so we will define that later at the type level.) +This field (whose contents will be merged atop the telemetry property we define at the type-level) will serve as a template for later telemetry requests to the telemetryService, so we’ll see the properties we define here again later in Steps 3 and 4. + + This allows our telemetry dictionary to be expressed as domain object models (and, in turn, as domain objects), but these objects still aren’t reachable. To fix this, we will need another script which will add these subsystems to the root-level object we added in Step 1. + + + +/*global define*/ + +define( + function () { + "use strict"; + + var TAXONOMY_ID = "example:sc", + PREFIX = "example_tlm:"; + + function ExampleTelemetryInitializer(adapter, objectService) { + // Generate a domain object identifier for a dictionary element + function makeId(element) { + return PREFIX + element.identifier; + } + + // When the dictionary is available, add all subsystems + // to the composition of My Spacecraft + function initializeTaxonomy(dictionary) { + // Get the top-level container for dictionary objects + // from a group of domain objects. + function getTaxonomyObject(domainObjects) { + return domainObjects[TAXONOMY_ID]; + } + + // Populate + function populateModel(taxonomyObject) { + return taxonomyObject.useCapability( + "mutation", + function (model) { + model.name = + dictionary.name; + model.composition = + dictionary.subsystems.map(makeId); + } + ); + } + + // Look up My Spacecraft, and populate it accordingly. + objectService.getObjects([TAXONOMY_ID]) + .then(getTaxonomyObject) + .then(populateModel); + } + + adapter.dictionary().then(initializeTaxonomy); + } + + return ExampleTelemetryInitializer; + } +); +tutorials/telemetry/src/ExampleTelemetryInitializer.js + + At the conclusion of Step 1, the top-level My Spacecraft object was empty. This script will wait for the dictionary to be loaded, then load My Spacecraft (by its identifier), and “mutate” it. The mutation capability allows changes to be made to a domain object’s model. Here, we take this top-level object, update its name to match what was in the dictionary, and set its composition to an array of domain object identifiers for all subsystems contained in the dictionary (using the same identifier prefix as before.) + + Finally, we wire in these changes by modifying our plugin’s bundle.json to provide metadata about how these pieces interact (both with each other, and with the platform): + + +{ + "name": "Example Telemetry Adapter", + "extensions": { + "types": [ + { + "name": "Spacecraft", + "key": "example.spacecraft", + "glyph": "o" + }, + { + "name": "Subsystem", + "key": "example.subsystem", + "glyph": "o", + "model": { "composition": [] } + }, + { + "name": "Measurement", + "key": "example.measurement", + "glyph": "T", + "model": { "telemetry": {} }, + "telemetry": { + "source": "example.source", + "domains": [ + { + "name": "Time", + "key": "timestamp" + } + ] + } + } + ], + "roots": [ + { + "id": "example:sc", + "priority": "preferred", + "model": { + "type": "example.spacecraft", + "name": "My Spacecraft", + "composition": [] + } + } + ], + "services": [ + { + "key": "example.adapter", + "implementation": "ExampleTelemetryServerAdapter.js", + "depends": [ "$q", "EXAMPLE_WS_URL" ] + } + ], + "constants": [ + { + "key": "EXAMPLE_WS_URL", + "priority": "fallback", + "value": "ws://localhost:8081" + } + ], + "runs": [ + { + "implementation": "ExampleTelemetryInitializer.js", + "depends": [ "example.adapter", "objectService" ] + } + ], + "components": [ + { + "provides": "modelService", + "type": "provider", + "implementation": "ExampleTelemetryModelProvider.js", + "depends": [ "example.adapter", "$q" ] + } + ] + } +} +tutorials/telemetry/bundle.json + + A summary of what we’ve added here: + +New type definitions have been added to represent Subsystems and Measurements, respectively. +Measurements have a telemetry field; this is similar to the telemetry field added in the model, but contains properties that will be common among all Measurements. In particular, the source field will be used later as a symbolic identifier for the telemetry data source. +We have also added some “initial models” for these two types using the model field. While domain objects of these types cannot be created via the Create menu, some policies will look at initial models to predict what capabilities domain objects of certain types would have, so we want to ensure that Subsystems and Measurements will be recognized as having composition and telemetry capabilities, respectively. +The adapter to the WebSocket server has been added as a service with the symbolic name example.adapter; it is depended-upon elsewhere within this plugin. +A constant, EXAMPLE_WS_URL, is defined, and depended-upon by example.server. Setting priority to fallback means this constant will be overridden if defined anywhere else, allowing configuration bundles to specify different URLs for the WebSocket connection. +The initializer script is registered using the runs category of extension, to ensure that this executes (and populates the contents of the top-level My Spacecraft object) once Open MCT Web is started. +This depends upon the example.adapter service we exposed above, as well as Angular’s $q; these services will be made available in the constructor call. +Finally, the modelService provider which presents dictionary elements as domain object models is exposed. Since modelService is a composite service, this is registered under the extension category components. +As with the initializer, this depends upon the example.adapter service we exposed above, as well as Angular’s $q; these services will be made available in the constructor call. + + Now if we run Open MCT Web (assuming our example telemetry server is also running) and expand our top-level node completely, we see the contents of our dictionary: + + + + Note that “My Spacecraft” has changed its name to “Example Spacecraft”, which is the name it had in the dictionary. + +Step 3. Historical Telemetry + + After Step 2, we are able to see our dictionary in the user interface and click around our different measurements, but we don’t see any data. We need to give ourselves the ability to retrieve this data from the server. In this step, we will do so for the server’s historical telemetry. + Our first step will be to add a method to our server adapter which allows us to send history requests to the server: + +/*global define,WebSocket*/ + +define( + [], + function () { + "use strict"; + + function ExampleTelemetryServerAdapter($q, wsUrl) { + var ws = new WebSocket(wsUrl), + histories = {}, + dictionary = $q.defer(); + + // Handle an incoming message from the server + ws.onmessage = function (event) { + var message = JSON.parse(event.data); + + switch (message.type) { + case "dictionary": + dictionary.resolve(message.value); + break; + case "history": + histories[message.id].resolve(message); + delete histories[message.id]; + break; + } + }; + + // Request dictionary once connection is established + ws.onopen = function () { + ws.send("dictionary"); + }; + + return { + dictionary: function () { + return dictionary.promise; + }, + history: function (id) { + histories[id] = histories[id] || $q.defer(); + ws.send("history " + id); + return histories[id].promise; + } + }; + } + + return ExampleTelemetryServerAdapter; + } +); + + + +tutorials/telemetry/src/ExampleTelemetryServerAdapter.js + + When the history method is called, a new request is issued to the server for historical telemetry, unless a request for the same historical telemetry is still pending. Similarly, when historical telemetry arrives for a given identifier, the pending promise is resolved. + + This history method will be used by a telemetryService provider which we will implement: + +/*global define*/ + +define( + ['./ExampleTelemetrySeries'], + function (ExampleTelemetrySeries) { + "use strict"; + + var SOURCE = "example.source"; + + function ExampleTelemetryProvider(adapter, $q) { + // Used to filter out requests for telemetry + // from some other source + function matchesSource(request) { + return (request.source === SOURCE); + } + + return { + requestTelemetry: function (requests) { + var packaged = {}, + relevantReqs = requests.filter(matchesSource); + + // Package historical telemetry that has been received + function addToPackage(history) { + packaged[SOURCE][history.id] = + new ExampleTelemetrySeries(history.value); + } + + // Retrieve telemetry for a specific measurement + function handleRequest(request) { + var key = request.key; + return adapter.history(key).then(addToPackage); + } + + packaged[SOURCE] = {}; + return $q.all(relevantReqs.map(handleRequest)) + .then(function () { return packaged; }); + }, + subscribe: function (callback, requests) { + return function () {}; + } + }; + } + + return ExampleTelemetryProvider; + } +); + + + +tutorials/telemetry/src/ExampleTelemetryProvider.js + + The requestTelemetry method of a telemetryService is expected to take an array of requests (each with source and key parameters, identifying the general source of data and the specific element within that source, respectively) and return a Promise for any telemetry data it knows of which satisfies those requests, packaged in a specific way. This packaging is as an object containing key-value pairs, where keys correspond to source properties of requests and values are key-value pairs, where keys correspond to key properties of requests and values are TelemetrySeries objects. (We will see our implementation below.) + To do this, we create a container for our telemetry source, and consult the adapter to get telemetry histories for any relevant requests, then package them as they come in. The $q.all method is used to return a single Promise that will resolve only when all histories have been packaged. Promise-chaining is used to ensure that the resolved value will be the fully-packaged data. + +It is worth mentioning here that the requests we receive should look a little familiar. When Open MCT Web generates a request object associated with a domain object, it does so by merging together three JavaScript objects: + +First, the telemetry property from that domain object’s type definition. +Second, the telemetry property from that domain object’s model. +Finally, the request object that was passed in via that domain object’s telemetry capability. + + As such, the source and key properties we observe here will come from the type definition and domain object model, respectively, as we specified them during Step 2. (Or, they might come from somewhere else entirely, if we have other telemetry-providing domain objects in our system; that is something we check for using the source property.) + + Finally, note that we also have a subscribe method, to satisfy the interface of telemetryService, but this subscribe method currently does nothing. + This script uses an ExampleTelemetrySeries class, which looks like: + + +/*global define*/ + +define( + function () { + "use strict"; + + function ExampleTelemetrySeries(data) { + return { + getPointCount: function () { + return data.length; + }, + getDomainValue: function (index) { + return (data[index] || {}).timestamp; + }, + getRangeValue: function (index) { + return (data[index] || {}).value; + } + }; + } + + return ExampleTelemetrySeries; + } +); +tutorials/telemetry/src/ExampleTelemetrySeries.js + + This takes the array of telemetry values (as returned by the server) and wraps it with the interface expected by the platform (the methods shown.) + + Finally, we expose this telemetryService provider declaratively: + + +{ + "name": "Example Telemetry Adapter", + "extensions": { + "types": [ + { + "name": "Spacecraft", + "key": "example.spacecraft", + "glyph": "o" + }, + { + "name": "Subsystem", + "key": "example.subsystem", + "glyph": "o", + "model": { "composition": [] } + }, + { + "name": "Measurement", + "key": "example.measurement", + "glyph": "T", + "model": { "telemetry": {} }, + "telemetry": { + "source": "example.source", + "domains": [ + { + "name": "Time", + "key": "timestamp" + } + ] + } + } + ], + "roots": [ + { + "id": "example:sc", + "priority": "preferred", + "model": { + "type": "example.spacecraft", + "name": "My Spacecraft", + "composition": [] + } + } + ], + "services": [ + { + "key": "example.adapter", + "implementation": "ExampleTelemetryServerAdapter.js", + "depends": [ "$q", "EXAMPLE_WS_URL" ] + } + ], + "constants": [ + { + "key": "EXAMPLE_WS_URL", + "priority": "fallback", + "value": "ws://localhost:8081" + } + ], + "runs": [ + { + "implementation": "ExampleTelemetryInitializer.js", + "depends": [ "example.adapter", "objectService" ] + } + ], + "components": [ + { + "provides": "modelService", + "type": "provider", + "implementation": "ExampleTelemetryModelProvider.js", + "depends": [ "example.adapter", "$q" ] + }, + { + "provides": "telemetryService", + "type": "provider", + "implementation": "ExampleTelemetryProvider.js", + "depends": [ "example.adapter", "$q" ] + } + ] + } +} +tutorials/telemetry/bundle.json + + Now, if we navigate to one of our numeric measurements, we should see a plot of its historical telemetry: + + + + We can now visualize our data, but it doesn’t update over time - we know the server is continually producing new data, but we have to click away and come back to see it. We can fix this by adding support for telemetry subscriptions. + +Step 4. Real-time Telemetry + + Finally, we want to utilize the server’s ability to subscribe to telemetry from Open MCT Web. To do this, first we want to expose some new methods for this from our server adapter: + + +/*global define,WebSocket*/ + +define( + [], + function () { + "use strict"; + + function ExampleTelemetryServerAdapter($q, wsUrl) { + var ws = new WebSocket(wsUrl), + histories = {}, + listeners = [], + dictionary = $q.defer(); + + // Handle an incoming message from the server + ws.onmessage = function (event) { + var message = JSON.parse(event.data); + + switch (message.type) { + case "dictionary": + dictionary.resolve(message.value); + break; + case "history": + histories[message.id].resolve(message); + delete histories[message.id]; + break; + case "data": + listeners.forEach(function (listener) { + listener(message); + }); + break; + } + }; + + // Request dictionary once connection is established + ws.onopen = function () { + ws.send("dictionary"); + }; + + return { + dictionary: function () { + return dictionary.promise; + }, + history: function (id) { + histories[id] = histories[id] || $q.defer(); + ws.send("history " + id); + return histories[id].promise; + }, + subscribe: function (id) { + ws.send("subscribe " + id); + }, + unsubscribe: function (id) { + ws.send("unsubscribe " + id); + }, + listen: function (callback) { + listeners.push(callback); + } + }; + } + + return ExampleTelemetryServerAdapter; + } +); +tutorials/telemetry/src/ExampleTelemetryServerAdapter.js + + Here, we have added subscribe and unsubscribe methods which issue the corresponding requests to the server. Seperately, we introduce the ability to listen for data messages as they come in: These will contain the data associated with these subscriptions. + + We then need only to utilize these methods from our telemetryService: + +/*global define*/ + +define( + ['./ExampleTelemetrySeries'], + function (ExampleTelemetrySeries) { + "use strict"; + + var SOURCE = "example.source"; + + function ExampleTelemetryProvider(adapter, $q) { + var subscribers = {}; + + // Used to filter out requests for telemetry + // from some other source + function matchesSource(request) { + return (request.source === SOURCE); + } + + // Listen for data, notify subscribers + adapter.listen(function (message) { + var packaged = {}; + packaged[SOURCE] = {}; + packaged[SOURCE][message.id] = + new ExampleTelemetrySeries([message.value]); + (subscribers[message.id] || []).forEach(function (cb) { + cb(packaged); + }); + }); + + return { + requestTelemetry: function (requests) { + var packaged = {}, + relevantReqs = requests.filter(matchesSource); + + // Package historical telemetry that has been received + function addToPackage(history) { + packaged[SOURCE][history.id] = + new ExampleTelemetrySeries(history.value); + } + + // Retrieve telemetry for a specific measurement + function handleRequest(request) { + var key = request.key; + return adapter.history(key).then(addToPackage); + } + + packaged[SOURCE] = {}; + return $q.all(relevantReqs.map(handleRequest)) + .then(function () { return packaged; }); + }, + subscribe: function (callback, requests) { + var keys = requests.filter(matchesSource) + .map(function (req) { return req.key; }); + + function notCallback(cb) { + return cb !== callback; + } + + function unsubscribe(key) { + subscribers[key] = + (subscribers[key] || []).filter(notCallback); + if (subscribers[key].length < 1) { + adapter.unsubscribe(key); + } + } + + keys.forEach(function (key) { + subscribers[key] = subscribers[key] || []; + adapter.subscribe(key); + subscribers[key].push(callback); + }); + + return function () { + keys.forEach(unsubscribe); + }; + } + }; + } + + return ExampleTelemetryProvider; + } +); + + + +tutorials/telemetry/src/ExampleTelemetryProvider.js + + A quick summary of these changes: + +First, we maintain current subscribers (callbacks) in an object containing key-value pairs, where keys are request key properties, and values are callback arrays. +We listen to new data coming in from the server adapter, and invoke any relevant callbacks when this happens. We package the data in the same manner that historical telemetry is packaged (even though in this case we are providing single-element series objects.) +Finally, in our subscribe method we add callbacks to the lists of active subscribers. This method is expected to return a function which terminates the subscription when called, so we do some work to remove subscribers in this situations. When our subscriber count for a given measurement drops to zero, we issue an unsubscribe request. (We don’t take any care to avoid issuing multiple subscribe requests to the server, because we happen to know that the server can handle this.) + + Running Open MCT Web again, we can still plot our historical telemetry - but now we also see that it updates in real-time as more data comes in from the server. From 3701fd75dd728c70e113606fc0878cae01624036 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Thu, 8 Oct 2015 19:46:25 -0700 Subject: [PATCH 049/488] Finished first pass of tutorials --- docs/src/tutorials/images/add-tasl.png | Bin 0 -> 23938 bytes docs/src/tutorials/images/bar-plot-2.png | Bin 0 -> 39736 bytes docs/src/tutorials/images/bar-plot-3.png | Bin 0 -> 42557 bytes docs/src/tutorials/images/bar-plot-4.png | Bin 0 -> 31252 bytes docs/src/tutorials/images/bar-plot.png | Bin 0 -> 35055 bytes docs/src/tutorials/images/remove-task.png | Bin 0 -> 11357 bytes docs/src/tutorials/images/telemetry-1.png | Bin 0 -> 16915 bytes docs/src/tutorials/images/telemetry-2.png | Bin 0 -> 39792 bytes docs/src/tutorials/images/telemetry-3.png | Bin 0 -> 51914 bytes docs/src/tutorials/images/todo-edit.png | Bin 0 -> 25216 bytes docs/src/tutorials/images/todo-restyled.png | Bin 0 -> 25700 bytes docs/src/tutorials/images/todo-selection.png | Bin 0 -> 32170 bytes docs/src/tutorials/index.md | 4051 ++++++++++-------- 13 files changed, 2160 insertions(+), 1891 deletions(-) create mode 100644 docs/src/tutorials/images/add-tasl.png create mode 100644 docs/src/tutorials/images/bar-plot-2.png create mode 100644 docs/src/tutorials/images/bar-plot-3.png create mode 100644 docs/src/tutorials/images/bar-plot-4.png create mode 100644 docs/src/tutorials/images/bar-plot.png create mode 100644 docs/src/tutorials/images/remove-task.png create mode 100644 docs/src/tutorials/images/telemetry-1.png create mode 100644 docs/src/tutorials/images/telemetry-2.png create mode 100644 docs/src/tutorials/images/telemetry-3.png create mode 100644 docs/src/tutorials/images/todo-edit.png create mode 100644 docs/src/tutorials/images/todo-restyled.png create mode 100644 docs/src/tutorials/images/todo-selection.png diff --git a/docs/src/tutorials/images/add-tasl.png b/docs/src/tutorials/images/add-tasl.png new file mode 100644 index 0000000000000000000000000000000000000000..7780365c5a5539c09064d2d372181e1575f48f2f GIT binary patch literal 23938 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 LNkvXXu0mjf6F|b0 literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/images/bar-plot-3.png b/docs/src/tutorials/images/bar-plot-3.png new file mode 100644 index 0000000000000000000000000000000000000000..0899984a336468dbec9f21cf76c9660f9b554a99 GIT binary patch literal 42557 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 literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/images/bar-plot-4.png b/docs/src/tutorials/images/bar-plot-4.png new file mode 100644 index 0000000000000000000000000000000000000000..50a9091fa706e56c3679ee3ef72c8b0994098ae3 GIT binary patch literal 31252 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 Qe*pVPiOIh$6w&wnU;irY2mk;8 literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/images/bar-plot.png b/docs/src/tutorials/images/bar-plot.png new file mode 100644 index 0000000000000000000000000000000000000000..2f113d4c6da09568fe23c87a426863909b1b05b3 GIT binary patch literal 35055 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 literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/images/telemetry-1.png b/docs/src/tutorials/images/telemetry-1.png new file mode 100644 index 0000000000000000000000000000000000000000..2a606e83c772eae06bee198e6a45ae9acff27d70 GIT binary patch literal 16915 zcmeIaRa9L~@Gl65gS)$1aCdhI?(Po33GSZY?ht|#+}+*X-8Hz|oP7Ve5BFi#npyLD zU(PNIsfM58+>!~eef2KboFtftlq(#K62B5)E;9wF5hF}mc*qCNSQ)$b$ z+P3Q6x_+(|Jj|tCw8}IsP(|CRHp$n^)Q?D-!bQhMqy9nVKx08cFr8LLMG=vN#<55n z`gH$mT$$?G&5WGoMR>N545z4e@Z*>c;_^_^Q!~{FC5Gi~P z)>5S{5v3SnGTW^*4JEu^@>@s*B^p`n4{viM9sv5D%sJXp81Vtt%n2IO>D-72VN9phyugTVe=;i0Khi)f>2I9)X1`?P$4`Ly zS!l(G{nJdNZ20=b$+_M=ZEeVXPJQUmfQ`j4!BnSK*MIgC{J@?WqrHwRX|pgczs|JlBbbkJkA%aW+K-gS)AcGym4qUOm-px6C#*}j?;`z z%i;>9s^7x-0C5ZqO|s{|z5;b5kf_alGlxkV)_x@eUy?=fi-&vcVU1$6&g*-8f#*T2 zzkCVsCh-j7p0p#;@ZvvC6f*-mm`MQ>sml@NJvPoMZh^BsxS}*TpSGLSD=C1 zf0-lzE&}j^J$yk(tADg401WT~({`kPM5qxkz_=K46TE2{(2FqW|25u!>?C9;!n`pN z*23ifkqPq-+WyN&35*a9`0&9+|4&Zej{tPPMgrHMttYPq)OKgXjJ&pLTH=tH;L9~S z0~QtT?t(!W0JogDa0J-Z51k4k4x1!BM(h;mlyJ+n`i6#dIjRQ{_&n!npXZx{$w`F| zasrKmx2ujhmOI}H$Loe!e`kuq+uv!pv;Z5`dMvMoDa&M|m% zHSH1gAT^;OjbFW`S}S_a0Xp1#(2xKvN53t>Kx_%J|1cvq(H+bbck&p*79FcsO>M+7 zmW9LnkFufHRBgHIYqvLm^!waGvu{OB_^&4^c&+}N(nCE*aW%>Ak zJe9M1Hhhf(W|YewI8M=Rlu5dNQze*YeMYVU8uNVzUky6Umbu@{YJb=f@C;M11!~FY zCh^g*`qp$GGCCHn%+Y0uXl7)PI^Pdzs+`NlrHB|B@gM9~*1EZ1DH5N1KPqJB>kEx9 zlAuR7)XW5%{Lff(8|-^~=YD^8sL0JHNvWl_>{OFsjZ9%;n5aK}>})>>P&%cWgvbx& z{GjQltsf1|ASKo~C}+xbU3VwmE=aS*-gnxUzTu`vLUpcQaT+IkkXj2t-Cy+Z0PDDI zPoGcUUIat#D5hzR$r|C>BE8N`XX}VQt&ErF*&F+?>~?Gs?^)N(EL>9n=9Gt|F>Jbe zrL0`!n3wk}{DK4ccb>j8C@MN`*to)Lnx{C-!{ak*8H_uzkYbsjX&_`YJ;*SB3U;{e z^z!f~$eM)57{B?h_xhR>kX$B>b8D*2pE6sh+u<6YK2V-OwJ@=0Ac2QhuH~+!=E(*9 z=@-FZ4!o))i!^@u5Rz??VCA}nQvVx=$+^jKs|RRT5aYe+jX!64lMFC&aH6F_uPMJg6NNlV8pd&TMhNu-$QAL|lvNcZj02hzbeh>O~@OA%nkW7^MD z_;nUC4i9IA0>l)j`|Mim0PQioqxsCt?9>!S z{N$U3f(#*{s`K1=F+B38ldBY6mIrHs<8m-9w6VwYI!v4DrtO(p*{Zf_C;%@g@u*0z z1otLqG_bUCUHt~~$UI26>MPF(IFq__2719#^WcRwPe*##+MQwdO7|#_)0Qg&i~CDw zffAt|)*r%>+&xW8OOi_op>`jKRjrXK)lzqe)Dun3adF1B?`l~(jL(lU3TLQe zjHUcQjr&#qxJn!;uP*L2Z#k$Y7N(StU)V?jP_FDmdu^@if*nc@5DCMpyWnyr%CX8^ zF2s}$Dn7WtK-lX^@tdR{1FE+w>~~Q191V-=gq3#Z`}hxI6j7NRe1@sO&9aU8RmbvG z@s7PSw3{EQk+)PuB?tZXoE-B6fA|;V=qOJlZsE>fMn|dSWlrW<@Wuk|`0&~#gs+MPY;dYWp? z3)D?~50m}zo{2?Xz4Kr~%RyHn^nUllcj}+Pnxo(+gTBv$<>*x5-+D@)RbM+-y2o~n ztY;dMMXhr1g?nj-`Lfa5xUp5e3wajI5}R_tJ&Wf`thqC(DFsUR&XNmE#c79G7!Peu z+`Ydz&5bKS-^Ao8D~YHYZ>M*nESQ7EC1qDudM$0qeq`_M9v;F!`g_F>tCL8z?tL8X zj1!dJos6W!b~Q!@j0V?NEZNl3ZQFTk(pvyr2?{uOqy=s(ousA=TgRSs*OmqhTftJ} zKP~zs+#*l&4u?7?6z{w3F2FjQlH#;m%e97<`Ug%d#+T&FKc4bUqkTxnq*ZQGm~sXU z`<+_fLiPAGhspD-PHO3Mcrl$?>ab5$9_;w0h4)WE3mAK;>u=GDU#(v{JY}fFqG<#) zGxgf-dj>gt!S|0+rm>IH5bni?PAbbuPv~6XYK=~2qm$Rt64b}O*45jPDc=09tNKDi z$Mt7wNQ2_cbp-ZA4adsoJ%L`64ssn)x1zk?zl$9Yht2q9B$I(cm07N1-yRY`EHjvE-50Jw_a z#d8Rk=OOH;naHylIw#?wehZwG^PQMSj;@N{H3sn6vlA319?B)eBgH7^&cVp)FtHjj zfjo)FV-g)UP;IiTly|ArDKW1?XC$S2(it%Y-OBfobNclabxX!I9XU+z+Zsde-**)+ z6MQ)(d{Q?KB)34cPl>d zj1U~H!)19A1PPDTGW-6R8UwD96stSzRCAy*@i5`PdRJT#j_A6jZ{ugJ5R@sYKEfXp zDIZqb^jU8`cNmL|AA)9TF>&-%d$B#)#AWv~HzdU*T*3Rt%;qN$Fh0+4)ZAS9aoia@ z*4{SgEGq%|o#s0mmc>JDUgOm?1b%bww@g%?M;+sIP=XD9;_0l6vO|<=%7C z+kJ!Wm`#blh4mkuF1Po=8|Q@QbsbRg_ud*znYG_8G$1-^O0YYxplKy<Ued6RhUxgEYC-lro zW?}dx+pzwwa|XOB_weQPqC;N9!6fWZ@JvQ~oz?>>(*LT(INGgjuW=quAp1+tUscDH1xis(dzR` z@xE?ut;q$`J`ApQ5K}Xl6Uar-djE#{YU}owd*Lgh6@SZ_>3#5R9Y^}JX^i?bnsnID z3h#TtUXrW;zrKee^A?@&NY(2Z5CvB)kUC$u7VKBnwA+IcNeX;a2Tpg*Z6pp8APdlZ zjtgZ=ENxhl?hV1t6W!iM#q82a zPPG5k^b0?W-&byvWP5k+_HVv>*N4oSEBm7ru5Y@)K|!FPQci=CiK6Y;$MDJ&e0BUu(NA^SRzhEt>#t$BN!8XxAqBnU_*(cndb)#@?5nwX zN&V+iE`HN%Q!v|4k3q=yY?xXleU9_AV~FY`;@x?n<>WQnUyqkZYV|pof!anYs`K-& z+$G+mmZB`F5hgd%`%#I*aQw$m&I}719W!HC9WV)}=Th(R zk?>Q-XK3KCD#;G;bLk2R8y7V0B)Ifca{Lea&0#e)vyv4)%iv>(g*FY-wyf<~)YKl- zPkICxmDm?l8Qo-|Z88~*kVa??YCAn(Ytj&2$}aGUHxF%2sfb{Uh21nQ{B#w)q|mg( zyFs>Ji%kivI~jBb#0S|uy z(4}sd@sa!j$tkwgn;>#4c!Hzj+=nRjLgi zDtF*2&COc$9t0IHha_TvI_z3AWqq0ZS@q!X8{_wk53~=h=&CoR(8oizP3f?i6Tpy; zqV{sY(6iw$-U%n?(fWgxj@~2;u6qq%l?Xqt%!oFnMxPiDMt*00o|~7ARypU#ECOA) z7(rVb|B^ZeVZA$Wd|>^HxxT4`>It;rC>ws}sxD;t7)PVlmln6CM>dh}CIG<-!=^pv z;%c-cJbL^c!*S1^QDsw&?m1d$x_#mv-de@R(wWa8XVMb=SVrC?4G_6O7~Z4^ZkZ}e zjrLo0QMub>B4n1_LX;c_H@Lbk7aZi$TVWcEA@KdUDnM@wRfpL3on<+v`?W3w z;O})yaKmFD%8IGdvQr4x-%k=TG_vxp3NGnyDx@3H#+kj}B>XxvxrUklt>aTgWWY$f zC8!{-AH$@fu4S%}@~jzdNre9Lud7*{g&}Q-x z=5h>hXctk0ajavuThZ&PC6Ue3t+9th%&S*-LIvjaZ$ImZy%J<&1re|jd;CZ>_}Fcg z^RyfQPZ+_5Wt~p89M!9~@pSUKeHc0vgiKDC)nqUPbhc%fVFt`_rNszTgrbo!tu2pW z%M2CJ8Gd!k+_U%Jj!;0<^Ld@=V(nV~^LnD5JS7@lq4=Z1tAC+1m7C`j!r9OWVh-8j zk=6{Qwu$dK+z$37l{Swy2R<3*EA;3~fvFYnEIN>QU{}cB?Qm*vtRS_`m+$=Zqgy~$ zauzwMaBp{mIO{Z$a10apHivrk2W1hP<&Jw>jqB^hTahM(af=K(IH_C6tA|s@J7a89Jw=N8q7%-r?)~d!r zd#l!;>EsWalSY5jw%L@=!;w(7ZaXUjmu05VEF~}FlPnVknt9N)t*3Ce##w3obxKD_ z>ZmXI!O#hYLcmu}>C|=5J&G7HJ*@3B*ZgkSqFL8~pY?ed<;}z*z1Ud?AaqAkKzTpu z@Ut#GjnbZCteSk!WU5gVci0=Q+m8kMh&-#y*YnY=^n7+}Xn}}~qN2lb#T@cPS{KAlDnHcemLvL$nR#<`CCY)wbeOzTNC;~whYeAQl(jE7}${{g&N?S z{_(=RVO;TSQa#!Fa4}4_`P7C)Ds1cob+{F z+5{-c)sGo+_+9IbuIrnlaD}vT*5+-ZWJji_Pyd>#Pl*36$|bM*;JFwVuv=4WscpXG znp@*io3$Ct`*{X$xyGqp9s((#v()%l2ys_s>CD3Pho`zJXM6_rB5Q9b@@&lS=1TSq znQ4ocTwA%_>+It|{zAM$w&;ca-kZy-c`v8+IRWB8J%vXnl+5@)0b72`e{I^cg&;Fgwh_u2|ZXxLKRv@ zmK1Hl8u4K0Y-F{(wtHx61IM86yNqT`m9zE&j(1OojEE#PvY+SL*@Aw;6n1%yCv3QWr^jp~D)T(#?5ao;^<4VwZ(jO?#R z2KzODdjVZI5^RWH)i*1(4sJ68d&X#D@O( zYNBnPZ#{8hi!49aY}>r34cpYvc}Ar~5OjsE@3>L#|BMo}24g$6MOp}Nah$N1@XBeB zmSHnGN>}0QytLMk^LA`H&_4TyV6Ep2)nvc!wv#|bp3H!hu$Z_eNChujY>i=K2Q`$K z(wP71He(1Fy>L`=&3Ho`J2tGe(E-c(Q+n#S)#U48#$;8kwGD4=IO|Zt!Vuhnn(_jn zt7rb#KQmJ#$6%bEu%z6YZn99XRB5=U7eaAD|2-3VadNtJVWiq2y{Pf!XDYEDZliuR zmc0~S%C%fF)4SOpw~JZ;hcboj_wvpPzbRG4Z&yi#hiEyOzYJ5Lk7fi#IaVE6&;G!* zv_OsqHj>mt6}MZ$3IVIsXX~maF+Nq#xT9^&;TFpxnv2QbpNJ{Dg)2*HmKxhFhCSef zf4oeu%@xrHw84~P(XKJMuy&NC#QeC*M~_g`+VPs}z?)@#PfRg<9Vsdsx@UNO$H5(oy3~e z?Ll|RWn@$x_LO@xD?6L~nwaqLsH%drGHzG=-S^w_^8`3`MxMEQwv$zoFM*JSduCx0 zB_w?qzEJ*xKt3CLdyCw45eCaqW0-NjVHnvb=i_){zA6HnXZ8|zpN@l(e@dCj2qv#sh_~9qNSv3)4l1w^F?GlLfBIS$MldLQmU3 zLiJiKDkb`OZne{@&K94`>(+5yNy>r5N!wJLRrKLuwW}=u*2g~_&Q8W?Z$b5E()qYL z2MRbBuCCcBQG&itiP_iW{2URIh1`YCxo&WA_pERt46z&*VPOFqt1By; zD<_KNuiXh6E|zU8OcEcihw?>^3oei+knjPKq^unb(u`q9cgY7YUmXY$E94A4Nib}} z7EiiouW*6jJE#gf=>0Kq#-3cbdF_@&A=)=O0~j~s@CoH+(=mEF(8@^Jtu&810G*J- z78*;CXBX#zFxZr!HeXuB@7$-QCOV8-k^((?Jk{bfj&3&#kLCxl@|;CQAf@b=cm3Km$f2JLvS~ zJfd&x!RFte>-xaNit37$U`eZje9|Da=H*6asN2@D4{jm;CRDB>sAH47q5rYjsvEw)XVk5+PBr_XH7__E1D!HgBy zJ<2r>31GBn&OQ-ur`T~KFm(1b_rPCZAR&WA%~oPbVYn)*u!&tRB7h6%0qp=!hAb z4N|T%xM=^FBv%QPtrlQO>r3=ADJhsLjEHYa*gP{@2t_H8s)va&D?Q<-K7uHyYp}7A6nE)}8@_w|AQ71_L>M>Z||6o!S6A->BhVZ3K>>of% z!VG$Y=tKN}$b-@zg_omg{P_jAOv~wS22a>)Dy|(lNCIGq1eJEYodAD z_(Ekt42{bGLqaGuCI@K`+L8zlkYFav+EJ2OI2u}1O7t<2({%0 z>>z4%TM~IWN5EB1`re&$do=9J$?v6_emrEX%NSw@ZY4cicO(1<&7yg%7^W#1eWdc( z-nT#>8aQ_d)mw_&oF#d(&Z(=fc^+fq^2lw&FZ7CcH=%o!5 zBDxX9N$6e2vrN3#cgME;Z_W$U{=7a>Xbv5@n<*&gKI^T@(m|7%%Y5A&r)esp(P3b7^0?Dg=yKS#;QOkO7~~&bs9yIfcE9wn zIj+`NVy0QLc5}#!30vl3I7|KaS!Y)>=6%xFy{PD9IP^49ztXbc>!7duOw`?swT)^= z=S1+ut8ohJ<+cCt9M#yxhW_sm7E{*R8`fx!2EyP(4#*Vji2>aygG=wcly z-`DF)#gacZFgpxl(nC>^#@y0F#@VWBF)+XY;Rw^AtC6Ne?bMHFVqM_Rp(*Llt)|}iT7L>gT2R8n%m|~@ z)zNR+3JUC}4#keX*vhIDeHc0U8SgZQK^$>I*2@(ptW_NPl~Fh+>+Lp7+i*UfsaC4U zMCg**+HtmRQ2!!_rArz=^;bIVuSRKU*a zH>tu)qA2{&0;ipGM$9GYjAil~ns4(GhsiK(Ntxd$6jD<~@pP<1`Kb+Mt8Ak=x_ ziE{JoHe0PVsVlywH92dy=Iu^&czGRub#lTJwNmvytNDqSTvke$G_KQ1ri&#yH+=X*@R}E< zKt|lO-NVBuGB;CuL7hoiI7Y0_F;7!l$5CK?c36fhi}k=kR#5t~XY+jiyrL#^{WH1A zkTv#e;V*jKmfDl;#T(W2v@{x0UNY=(7-_X&m=GdxAx-Di$Ih-Qd0NPu@3I?V!J>>% zm&+DT%_eXZ*Ov~|IedjGrAc2Y*@49C_410C!;m9``!>>qZM=~9_8Go>4o4Qpnw{gEi?v6bsGsKvp?mu`+Oh$W3>>ecjrLnhX3C1X%_O00 zlpK6~c@vKHzis4T9cv1`rY4)3QgG$OGjrGStGM-`ITK#`UXlDiPK>`$+J!$}@K`Y4 z=EE36weO+Wh^hbUN20*D0@DcithJiwJ=u5fznp6Ab&qPFwSJbGvN^3+{ZMTd6kS1z z{yCXXMn|JUTobr`kx0V*BoN}ZT?Ins6sCfn-Rb!!5 zJJ@CTxXLaIMh`_3uQ7}ItgEVQuFrKxdr3u(Sa!6 zADU{ZRC@H25ht=Q5Zgu{7Zy(1c<7|v-}(3+a^|^m|E#(@ZdH~^Ch8DMEv%jES4_OX zJ9<}hoZ)9=FrMN^F;n^WwJd&!pcYX*k$VmGm`32C1TvF$hhx4?JJ`Kh-3~=+j4K|P z@h}ptFVs?Ps&u_I)MvcYgl}RdMpbIlDIOKQ&S(pv{+L(O_a*J(cFAfd)x@?etvas1 z`4e)X{(a-N&Lu&5&MdoEO_kv-tmG1H)rU8MWB+TVaLQW5~ZGvkXs3w$@Yp;{zyg__n11;_yt%H zM`N(JY+^^p4$6fp`cp-$>He$ccq|)%PqDv`n-S)E!p3}j;}gM~*8bWnF`$Y2RykeckoGvDk+X;e9iyOd`qPOgD>t zuejEYZR;-v#s%wf%(bSowF-(-$Ag3sxm5N%hl_aW!Hur=X5THt(x0;7T)t{!uoMRq zqvz}^Jv)lh0m97DY{NqnUIi2f-HrNczZ{$tIJ`gDqf7l>U!jeb3`Xanv5#CTHjs%> zz1#!4r)Kz&2>8DJfC<3`?+V6jCtMsP%;ZGG$d4P=vnSpP8T!~7yKKHdaMpj08Bug`aA=!=dzA1dJD((;3EHyU6a5$tS zrhd%6%9i1a<@o#vdZN*gBo5kOOdl9+nK)21xErNouZK5u19dm&Q~sHv;-GVUvnQm* z>+JZ`Tc}b>&c}N~IMZ#?a9%SvBRk%^v^8ZQjHfE)-ngoiV;}nrf!cQcg`Ml1LH0{F zmpqquOp5wSR9NjrxRDgDyN&XR1=HM2`G}*3rd?Y>M;V{#>UrqcuYxtJdTaJ)c-hU( z`nta#lq=6JDNd8+JC3-N@1wP+1(eUvl;tQIpD_)YBM-ijDM|ZN#s*c_j@qFuQ>exI z0ZV$mccO#r8={r6`8B%vGWy(1w->7QWtsS~Erg3VthJV@{n}fbYty-7JeVRx(|ky1 z!rx+GLXg3gMMOkk8v1_gIXF};z`c4D!#`qsUj$u9#h}l~W3#4@FG+G$*=@iLjqC6V zt<}2BA=y)B=~41w+;S9do}%SvufGTZ25gK_biTT*&S zjC9{+Rx@qUTBE9Q)16rAO%NgQWb||fOBDL9^~0xH)8>dd6pF=HoL|(=I4%S2{nf-H z5q!*dh)IE)EP{1pI#fs(Q5--~g7WPh2)Ka$PAakUUf!E{TUl(k_`!k-q3FwFD6BA` z4~+V%@ZfW z9vT&4g(>D11&d;3ds=o%T8Ta+1F!@LHy`8tGF5|j9Vaku>LnWpJfPQ@CGalp7) zW+G$$0ubAB=Pw!k07Q8PhPFHb5tYD1@>SP~3wHP*EeUIM;j|gl@W2&@ zXVqu8ry0w1wt7*J?qM9%&&}(&ZBb)!Bkq4xeNqA_d;t-JrA$>H53Y#(=p~mjb^`j@ zZIA7@t1`#3z_l%aa-^LP3G=)zmDiA9K);Fj_8|FoVhjYwgbduzoUTm443)G)tV68N z9nGz^@1ZCxthFfV#*_K({Nn+p?hyf>Fi!{?PgTEjDLjTY9&-F7&cz#_ek>4A;>!@Y zG36mJ-DqK80Ua$Rw2UMQBZR-8;QV%{SMTPFggQ-yq9YIl?nmZ6HKEVlGjxy@>Orvz z=v$ap`940lH)W;1xTb%XXv-O%yE8C&t+Vq6dilTaB@f`$zIMnnslk8kFoSpN;Ggg~ zPb?(fog)6idrauvA_=-t8G!%{lOFPPkr_jEMm4~~(@HoxI%sb2IsWT9;cnn5UwJn< zOtadeeYNj#al7?r`8MxtK(=*2D2o$3XfDGW-k_w?S#|l+)#h-$ye4eS)qnW6`qV{x zDt)JapVT{r$r;desD@*Ud$mN=D53|>gU9mk28Fqr^iZ1MJ0qLZ%T;MbNT^`ZRK|e& z%bk!}O=`GDyx$RJCpA0q*@0t0t%XQNO&*zn~VvAc(cX0i~-=!;_ z?LV!?ez_Cav4uShq5#D1Vou->DfTDPZUzKr;m({l6ZGayt$ps}514!)Mg<;}Vca!@ z&Gr_d6*@Z9O6yg4-H_@>iwSa{O-21g_{Q~|hNNfBsjgiwhr%7&Yq<-*54__yPL~C# z@^uQ8UKbpf+TBITMm76)eH;l9D3ViTy0_OpOn=5~vAoPrMN$v2q0Sv@E(%!a+k9vn z$mYv%g|*IRRur9TSbyL&cJ$v3`j@|^o}Rxu&@(9HeI>!QL5}{^<{6WkLG9T4l0nbT zv5100OW1aGJvuC06^Ax~`C7*Q`8M`GvK@nT)yuCPTe@b|QtPrL6RmZ+ig7f%^?A9} zVCl+(u2DYvF)+wmYh=-xrAEvrWUYZEZC7ch@8d9vOf0i?qu{bh4w(qUFzWb*hi zH9K5CuxH07^nN24grC2zukrAGwBE9)a6gvi z(_AW@v~c@bdvyRl(rtr{$(<-{vz}z|rq9C;M*RG!k?3^;X5ErFpk0q~G&thEdtk!JEudYj|RyvfaEqh*nW@B+Srr*^|@iI00MqxZGP z&ER0RcM+p6We(2NDP2?QF~Ls!~&gL!Clsq*$H1Rs$FO78I%3vEJJ448p25t$-ic*7c9SI_02Vbu4j6{RhlRA8(32gheG#?h z`r*RDt{j!4yZji8n&)L~K-2a%`a=UP_xklM-DPOZYx1;7ZA=RzC~oRQWlMHY&LaZ@ zBQ_q__N=)iwEDYY&d6N;f<*|)%~QK35rf~yj3@F836!rl;ejus*zhw}>vb?1d;43g zt?0ws4p-G;V?sO)@&wK1+kQLorpp=yp(e39^j%E8P3!dXvM7lXZ|A(Qg3n(h%t-i5 z=pBtm4adJYBt<MlZr+41O5wSIa&<$iiC#J}811>{+q^q5TXYk%~s=$|QL4u1}PiiU{uIG?jUhTl-G zmybCTJ8d~Nqe{L<2zSE(#fOq2yRJ4V5s8trV@`NCyDI`H$K1Kl%-f^1G7UDix^7J| zF$u-F{3uSEtM9ZvX?JipwBu_ZS}uhcK3}AVxG|_)Jpv+_QX$*Io)FEL>_F48 zAG8nNah(^o^`K$cWfLl7n9D5=x}#-qPVUR*(LHC0`Q}*mo^v0LprL);amy(rNGH_U z^!vQe5Cvblmr3=d=`Y{e35S3EPU6qS&HAi%&D=LZ`n&u39PPFq(Cvh!!+6z*O{e{F zNSz>L$jv%a_I3-f$TxuclpJxa!(&?dD{4=54%mH zGQanm$SJdxoG$+jCa6S=#lB9{>N&^qzXjE<+x+Kz3h@v1ix(^i^jtm-kVite&1^o2 zcim4$3}h&zFnY1wFXq$TdgrM>mEaGU?EU<_GO}>akP&}G_QVJbksR~)n@#(h;g}>! zqYY(a#K+mCzKd{kjh(EQX04?0Vc*1py9%wfrFZ_FbhdxY06C%2q$+zeD!j=Y*Pol5;zHzx> z;{c8cp~LVSMS)@9;JKUE%y-fH2tuGlWTC@la;XuuK_ z#kJvSzK1Zl6jA_cAjm5!F4tF$8@PF0EfP&g3J?Ry_9%&vXyW%8ni24~dY6v|o4uA7 z=pYIfyIh7qqZbB}L4x8^q|<-w)Ifj~-h=Bc0t5(yop6Iq|89o$8dB!>w0fC{0dY+T z_Pq~7Ikbvq#Zp9kbgK-xV4sLc1K!rg73F>x?T+}!wlsX&F+h7R*)dxYY z)+pkb5Wc&co(x6%pMQ4ICu9xpE~YP<`BVxBkOUQFbo{%P6jT(Cot5i1k^ol%{M&Op zO0OYCiYq2E6sLbo>q#LCBp@a@CR!W;w%w zz)p=6l%PI`SziOPAtEWQFt9fiRGH?uXt@qh?GD?2f!rwnm~>OYz5dsZ1SGVqh|bCs zbQ;AIRSSR#fd@6nkrY4OYlbgo+*9Na3iv<8G%`Zm#hBW5I@K%xlc^@JIX(E>oq#>0 z1Pm1h8)V%bQBKx-P)ho3kOnE>pAx?5Lmd+iE&A+P<->twmSj}^ik7(Kwt`UxCqrQb zX|gZ$o7pKSh4l!sLKFxFqFHW504`+T9Qq>ok(*A_D8B*XBzho~gu|~4 z&x2CTCgF8LVZcB%HQ#YTn;+fV==^9}kWA3B5X6bqvGG$U7K@*0ZgVY0#` z0|U_vEt&^xI(?hqis3B#7oSvi0=KGl7ZV(Yf=L3{5d|qoZ&P zGwZK@o)ry)v)^U3Vm$v-t!OM*_0`Aw>rTyTFOX7+=w%XS=EQY+=1*1UsXEWRo2%=$ z&30Vr=ZdCHw2c0v$jHd&_9cWW&l&i`=PAzH;+*vQ@yj5&N#Cx!auZ7JIzKqTi0S16 zWxy2QLnuPZKe%ri1?&al5b`gd``@e{Bctd%lrNY4KhWLRous~hzn^ADqCy-M**_`H zg_1n<-p6MlgG#r|@w6EDiK$s-aL?Xt#eQ=GVOeSL!pd^*maa0lHn&8^0c75hlnA2;kt`;@i9TRh`!#dCF1AXsY&?4FuT*p1xzm{`Y5w zpD#Jyf5E?ZN9_$p@7)6={O)F?&gU!OM)R)FHU2;53P>%t>f0Mbv zc@77J|CL!$V)S8y;2yT8bR3;s(D*vU{JZhiKOAj)cXudOufw%=cj812%!btEVpitY zdA%-nKcxY&>DY;LoSBIukSiT!6_vsmbC#tK7z?sA3OyhaivfUn`8P3U;@!Tk6JdSi zaP9#8Qs@4j67^{;3O!Kf_U2~Y3&8bIGemS-uG8jrVO!M!MPr=Wbs z`yA7#CY-_&%uW5N=){^*kn4C~&_8?|1}hK(8i~1z+4KCHBgG#3UZ-F)qv!gDaOgHF zged?!m*3O*XfoSJaT`@^wayY*Es&2M0SHkjN&;{Vx=LZt^$>&<*nSS8*waG^P<4zO ziY1Z6>uQrr_&DJ>{i*M#G`c1YbFjdB-k|-CO>z?`O2@IK=-{mj%#~V%x3PA)skK zD$cdgJ+S?76WU!NoMCNktppHcy4(?6hrrqe)#~v3A3$pQ3`-J(s+cguyl7AuQ|A+z zRWQFJ`<8MXuA{ezn8{bhbqA`=d-7r8wU6zPHjTj7T$e5X+~>C@B>*aHdQc2@a~jV~ z9JFTylNb=YBXz7RjF@`aZbCvqcXoL=#8y+=Hj+Fv`YqA6T5B~>Q4F^0zR5(j>qYl~% zK1sM6qw@y@Ip69B6asu6gg<+5FapN-Y~7R~JjIzJosmIf+|y1tF@UCz3eI>s*cxTF zNr+d#JS`aEgC0mZNw5V2=$_%oeG?iMCM8G*vt4%kAbCYLc8x%S7cimfjv3Fi@Vtu& z9X2A$T{!OneHWnT-?sojcMSkiVCyPIeaM^7nEfb!GlYJ|X$q%ON!0CH+LHDG;N|#$ zkpgdU$~Xy! z@V@4&lkeG(SPDs>Y+YT5fFa?wa)L(n>gnf@Hotkdn6P&3pz2yWc&8%{nWNX==qMwB zIIhgzdmarS2QbJF`->PL9AV%u*W;tU{j~&g5lRQ+rwFVScqbNCEq@Z5@nD^gWqFF` z@Tm6Xwqc#B2K+>iCA8#&8WN?!!c9lBxluZjD2i%o`=tgF2ExH@Zef>D2)E?*p486# z#V45h)hlX|(tZ)P;{TWe3Vd5|c?bRn4LA`{G=w}0@ECXmudMGF_&g1+R-=Owpy9(~ zi)5!w8dpBOW6lm2szf&r=Ew5I5>5njUX)I9+J1cO&RZQmyH=-(TF|j$ZZQHh!iEZ0ZB|o3{o?LcLo9? z2qGmar0NcO(TyOeJ(@w!dnzd*DXG~asL2fAk`^T*R}TaS6_JyMWwNf(U3^%vRqnK` z?R?Uscxzs_bL%|wm3#E<-uBuo-ekYraoJpoQ0a6Ds?~+Av9xd?1~wA+2MEWD&_fbjA-;lw4iKd~II@}_=eU{Wa!onyyl^F{Dk}Qd4MH0L7{RbWqx+bN z`m`Z)G=GSLkoqI{eK%VG7)Sq{{J&qRaG+C6lOeP#QU7z35iH&KhUI@BWCWWG0Xa@T z62>?m{=aZjOwZW=#rO^#a3t*Cs}otT7Y*_qx{e=s^WrU2YoV;HEc!!6gFueOq_uit z{qNKulMI89=+cVRA3V8Ed0^l25OlR6Am1xlx+IJJ7m1YP+48+Rud%VQYiG_S$9_2; zYg}xsysRw1tJqYH08GpzHUMRde#fozmPW6gnGCci20wu8?Kw0wO@(I$B8&a8>0BpK zdY9wJuK%1f2o&hE$75Vl3_jKr2>G*H2(rH~GaDN^fn@6@f<~L}K6=x3k587*z(JJ` zc@o4roT9e+5%F)76l2>|0j_4Lv!c|dG+rE=+K0tvIn1koim3Sy{YyH zoB&dLD4;(SdiLshNY}o*>2kv=XJv?R19woasWL&VHy{>9J&_K*haMl)pB2v&4+R}m zVF`=eNQBoS)Sv0zUIOT3K0Souyuo!y#E!w{WN{;58P#vutOUr=439&>XG@oF{UfT> zya)YQ%cHjnODA51VS6d@ck3NNX#i!`jA-p z^2Md|KcjgMH|h=!2;vF1?TFWlKDU3gv-peRtJs~-q;-2mIXtA6KJG=9n=7w*Gh9&O z&{`sc2aby2#!kSov&do=-sr-C!8IVP5E{InL{uD`4i(HzS;ujD%bqD8$6oH>5qv+< zp|Gp%P?vA3eqi%Zata%pnpJWf9<1R_)IPkbQoluF-XPi8(AdrnVoMW{sbTnMT9WJsx(!gGa z=bSF2OmF_>v6{gT5mC zQ{vDM-f*_ZEo{Ju8d4dRA06A<>d90>#ooeMX}@3@PuNzZsKZ3_?{`Me7cP0KNNX9@ z?9E}XX;jv-j;-t|j9MGM(&+jq!S@_OHMRSB=#q?y&(gLJ^RoktHkh+lL0rrskCdP~ z%#{`;e#Z@#1lop%h!9~(=t$vYC$=B~9WLiC4#FDuxO#w~!Ovvxyj3{WqvRVqmWbb3 zt#Z}~Pv^Tqo$VtF}bEvIVqerDg456&vDr19p$baJUch5^AuSk0GgnA(d zioQ;= zNLE!&JC)o{pTp0C<#2;QT+s0cOPB%5<*H)(Nt;Pl@n5|@zc%8aJ{^Pa-chXoT#eoP z?Yfc4)$@QxFDf+b3*F}0quSE@m}W+oajnJCK>JN4+Ca6GdfDzjexreM$9o$|d=^w?|J{4Nu~ zm>uc*Ymg!wEdb9k-0v0QlAZs{&QUjpP+6)^L56m-X;U5sS!mmt3)y}OrFl9=Ie+Jw z!whh`&G@wB!((cKm3y47BE<6A63%b;xnLeRyRl_SW$_^?U-+9>wFYq{eE4soRdZ?Q zKkqUotT9jM;m5(Z$Ur|aqvo1WDNHPfqzi6$N{TTkVFzEWn4k0BuM0#ZB{(UO47E$p zCBi>3x6LK&T}9$_Gis%2z8-Va^6nHSyt`BRHRf!w1g*r~B#@K|l;GxbgP`=UIH#$( zT+5jyeux$G2Fm-z^HK{N6 znu(f)B1vE@JDlshCV<_73lX+lUi>mHJea>eeR{vWJaI~o6r#mXNP4_r*r~gQ&(ee? zlb_pd@6;$yj$7k;em4FnSWc3I?!qnO0%t1#yY~@2>M0IXAo%U6FWW0qZ~G>(YV%}+ z54$Do2LnS|iV*2fq0@Kb^qP+H>-98dnjG$^uf7PjFKIQ!w-klW}d zA)Ua(`LthKtmxF+ns-KJ72?%;qq$w*<*t4P3?E;H?jSJQy149WXg2QaU9~enPdcZd zYKt6Zp0aXn8W_E;=y!WE8kc8b!(sn}S*8jb1~e$IqXqX?`wJF)OvN23)^}+cvb}KS7+&oWo$=Eg@2dKl62mH!aN0 ztvCtdVZMGn&)?0iKFwYC)YR8t=K1|Q3WY5Gc)f$~UxT9O_p%r*I8ng1Xh~l2KG?Nn z+CZAwaok_W=gIxW@p3)tNk8yWaKfEVO1HBT-w>up} zizAjieR~fO)RMlVkdv7QIp7e;n)IY$4{hf8)@17!u=cmEh6e4+#0MO%n1=)+4%aIgNjH%-8zxHy1Hi#VYe&BL4ivo?~%;@NKXMT&RK_aEn+WxX~LSXYmJ@zFBQ=cESa zJ0}Zv#V&uw$_F8%0iH;UQHm7??l`o`w2^ziDW_9U5R((RXV%|hsGe+wgL7%t?yY4b zlIFxSsjOMnj58*_iM7ooN`WyHIN?Ix7Xl3&ni1-_5cpuNt2PLxH0I{z5R`66aw@od}U1iJZj2A7h1trqDUl=|H>_{cL9 zr?r+?_M#hv1$6kCge~P(oclbTftB~W^#G0ng?U#Ui@QRPQ}g%8)i9H<|Gvr(%FJqp z?DDk8S>}7V$hF&B6KV1hrB`(l7U=`ccv8+BKaV^ak%`=650Q<9c1VkPE@K|!?mVX` z#XNq!ZQs8^WX^m&gP%<+!5Q!PV{vkGcEDlYyi7Gr3It1FxI^G^P;HU>Ilt$|r8Am= zW#RI)3VfgDxQqpE4-0;kQEh<`N_43Ftane7%&^*6J1wg2y7xR0CbtwApm7RwNtjDb z*`YgcI|vNYp+oTVLY&cb(nnGx5eyN6xny5Ke;dA@TA^@80B!O5T>jaV~K@5950B@Tay%@}6(2uAfRv zufLMn8$#o@HC`C4IOC*mDhoA9pg z7#UK8nsDM;Ig~QL{heF-`DZwBmbXpE!XPFCG*H#+B~!!%^dI*OXNnWF>;$=9nm{s2 z@{5!{dy1y`+zk(qJ}@PKvH*O8znMt}e~$7*5$M2Y2g0?-3ye<}CYz7A;%An8rQ;_0 z58PhUHV~TCtPF*wnU-#DS82}RM?JOQgRDU-ZuC(gqz8slPAJdxE&DsKdw8@t+epVu zEzWKv&ido2fSHgMfiMnfp40S{h!a$`KJ{G~?H7h}YXdp~OyV{AFUW**zi_KXV2=@$2hcMl9TsylNQ-qx!cw-$&7?KId zlKNf^RiGhA^3n{jLT4~p$zUYYIir ztz2+w2W(U8Fl7rcyZofir`tE?oMKUOW3}w^*R)2E_A^66Ahv1p2nCjlX4Wj&B5(3* zWK>GfOTo|9e6`j0IcDGc7tRz1Ov&H(TXVnrtK8R<-kX80A|NNP)||kya_YH{fQ=No~?XM_G;&_ z*a-=m!=v;|296HW`sdGhuwRNq9K$X7o=%yW5zspB#l{0!E~~3|?1O|`(MA3>!Tf8T z5-gYva4Nw5s8XR*2QMllcrkh$s6Kmg>x(D^&{|fu?-A4O373G;GScL1!ff}bmMrIg z()2@zC`0sQKl!oo(*?AI(4K zvK?wX!^6`jXQJFbHmeOvWW@{10ub_LO8pz{COjoyX3-KxQkU$xmu@94$i#u$;9-4_ zuGO8-XSPksOtT%QuBBa{tND$Xd%_Cq_auex`Rlwm;t*)s2-UU3@#7HqZ*HPym-xz` zDNhn?)3$oj?S|T@JHoR(NgdpyO)2cXW`Ag?U(Ed~;JG+XVMsf0LLBmL18TM1c0_Tn zpVj(=dajjpIW5rX_c-+N0-QY@HX;qc_&4v0)CNSb!Yam+5k~3t&JT|251xk8Hx~21 z+aa`01h)5W>PgzuwqgWfpD`^m=J=%6ODqrTUi@cpbqD zd7MU79>|5lI;Vu0WOV!hJ941u)-m~R(=J_J^S;x+=r>8v%KtuCW-a~4bn0ElOBNQJ z*wn#PtsNG<3W2A0&i}ql-*={X%A;NyfA};)_g`fad@E#rIKTeH^NRBgyzk#Hml8YH zLAYUx>%*Q#5V+9a=53DL09P#%XVQa$xQRPWrZjibbHiHN>EN=X34Z|{5R5O+jMegU zBs|x{TL@CWiK|~C7LCo%bLBueDMKCD_CLdZ0p}C{qfpcvV|a)K_q7t!No7lKQW_w& zmz5gcX1f@$+wA;BC12cQq^FX6{0Ci^e(EPRv|jc2Rk}tgC;#G-`_Tt7yawbKz5V>^ zJRPT9Je52A@v%a(wP?Ae+uF6s{;z?9j6>Ij{2vI#zl^UYt(a2TLFZWd&6u(cTe6Zd z@0Sd#@|sa=vpXYLV+cQGR)iYFgI|1IXpFZ};z8q3`Y2GKGUlC@+7Xd?Dh`y^o#WzC zYS>Nx{4c=i3aoo+@zW&ySxr3D6e>oi)TI+r`h9A97hKy7^bbci{+*Bm88oo4^eY&rih$*|^{GMf-yLU|AL#yGz za;zwrR4YM&b;|X7x-=$dAxW{cbe`T;w^lTpc0a92#Lj}%>lSTK@TCwk#(mZn=|idn_F3UJPnTP1?#wMQs6I#B_W9`yZAe$KPesl$4P zLRc9WsAO+xrtNt5t63khC#W(WMlf>YQ-yTL(hN*DD2zFdcRz|wwKK+V>|1Sb?&Y7i z-pSI<3i1@=KC*IL^&3daBj}0yzy7JcokX#ht7c5cwRe`ryMn{mCDv%8UbmnAfD&Fm zW{I<7rGO!jHch_yVIJw>w|-^9uTA;c>L?U8|GBlina7YAV%5es4;p>x=ZO(Dh@w{x zZojvCBuvB^h@L29H+Wv|s1U&|mwl}7YR2)f#Npib;mC4Et1-g?>!$Iy;d#G_)+}Ul z?IxU-CP-JzpH@SjqcC%VBtx-s!id{%b?xXF?!Fa55|(}d#;dw0@b%g`>}jSz=bxgI^9rm>A|q*ZtJN z?B`9)iOiC+CC)`{!So$(hZxU(+My*bPvW4^uHJz^n267hevALsHr8no*T zeTQta=A=7IZMWNb$a4jdW=15X2v(2^Z6LvtoAMZ!Yv4=Jldy@ui=wBRPDk1xUA+ec z*KdxdSNZ*4if@{9rinj`1Hm>Lj=3cjlvHQGym1vlhaK8I%kaMHMD((xS=y}!e!8v) ziB2)9rTf-;QJ)>h^sf1?uCggVBAZW|5%#`pWxdAU%dRcmP8eIh@QJw zocT6ign2aNcWwLL@gM~CVixIKS-Mo@QTAs6Tr{RN3ZKXpF_~qA&xReu2pKv;6f)6ubEp~u1XN3zEFsd8J3SrxMwN)Vrnhs) z{Y^}BdinD`*8E+xN%LW2pYP`%?9PI!0+C+LAB^ZKT{2bb#7RdPbqu~ZoAVGTkZv5jh z)JW(`{rem96*`RJzu9NxLqERXRC{=`m91TFKXwIB|gw+dS7v zau3+dHogVpl>8WMg9Gozv)S2%E+a1Q6Qf)FB2?w0-JGjCdA%PCwJm<{8^5rqMWk>S zys0Yoa1z<6yuF#NxVWCh%ONP(0S?|-jL?c#5isXNqO?aP$EnUd6xB27Xv2wGWEuO$ z`V5K~7odRw>$!_EWLRT@yYu4Jtanv6MwbdGBe$J!2 zpItmIwxpCegDBH%!x<&ghnZhm>Jr=OsSg$kdT%#oAqhDH9+ll-@a2%YBM>Zo-cXv7 z$nXhXT7F(Zj#QI59*_qqDgJJ<#(zc%7(4jeh7dI_YQaKiwZyU~o$3TB3(c#ZP%m)+kpI8?Vs>+tW8wb?@HjH}f*#r?ao1Binv7-Ta zD_H!KTKK0{1%4-b#2oAZ-mM$Wcj6xi3Io(gX8qHiR~N#p5@?}s^Kr%44W((AgQ4jrW7%3gQ{X)}wS3_4 ziHRx}d;v|-yzu~USNNZ3vXZZhAfHsPCiSanvI-l6Wp0`XepJNaQ$JY9iQ zu^P712jv*){#DrTla<+78s9?n3{D%To<;{MCNy%ld2a5JdAXhM2dh#ag}G5$n2y0e z$Y0$v$=RT8m!GLmtRTM{s85d3Q;Y#ExVGzNo{W*cX``!R zB=I&K6Ct>7Ct@H3KJj~QUw$1(Zf(pB$0B1~16zxw*%8FaH`~ z(n|taLSl;?HH8?9(*Ds^Ioj$Wb3%TO^;i7$<5d3lqwJO-zSO+=#$^v0M8Ul40XB*P zoVaHw62w2WbVrT=_QgtFc3dm4fXyHbS(6>G)_Wci5s{pX7C?k(PBFj{MovKyv<>0D zUxG(DXWQz;#*lYw-$h!j3rS3gdfpcfqYxxwBnpCqQaY)LX^cXPA!;x`OVCH48p!wU zEs7A$$qjH(I-F97bVPAdBhAD6(^lOA`$*TAi274uY8?9M$tEA1;6NH|KUBG5*&t5$ z%Mq#upoj25-9`5hP-+~`DAs#Op4Uc|Ji^WT47F0i{@|q;uT_pfbjH)J&eBEgy;d;P zP>CDp68AlBD;~AoClPOp;iZIGgn}_t-Nu^}F^>X52H-HG2MGSh;q*ra0i-tP$?6>jzh+JPZ$p(9vq6*L|5>cD{j>jhqW`(5w3y$9YAT#oHRk`hug4j8qacQW z)L4+tzvS+wCyec@CFTKa|5>V4sNYs>Rip08&x$s$9)Ddh9+i6R?iC!IRVY$m;I|VC z6ukNMjtl|^$!WG&DyOr|VYdTl03Agb^8N0LK@73|i5^)&k>NIu2vCCp1K@8gJTxF8 z47j2D&!B|_wxQyIFM;7lnosVHcRDgnR|kZd6{v-;og#x{R$@;Y|JMb)fFpdGAYZ`W zYdPw+hv%f#hGFQ-WNd*k?>ngCIQ}wVTvGLI7&s=<|BenBRJbJ2DJpHao}qFk+dPc_ z={a1s@0cki({R0B+o1OcPA4L9x>+j3t6C{qE`ZTw5A}4*kqZwokjUUR4>$bdKZhDe z#6ar;YsI@@=xyYY|51t>8xq^&V}6hnBCq}J-Ayc3Dm*93@>`EL`kh=AD46;w-#t8y z5Y?$)LpPP8#lsCHqVO0IpCqZRSw5W{g>2fG>BY@6EyEwfFH9~2OTyfWeq3cbs5{+# zd^G%P$LQ~a=WlhMS}ctJjO-Oiuzx35$0aV4CTH_iFI$a{+YP%P>VL-TFURxD;+;w= zU-RBSk8=g6H+;v)U1QB4`O}72Czr(oFwe;bo^w6d66Kyfr+J znG!ZCDZ9vra!V|F@8AD{eL8G}l`~6KPdg+!bi79;nZBZ+DNh>BQjdgU>YuX0^$tST zJckax-;w!E4}=eqmiWBGw6jN}!1Y@2eV|J$3=-`&eWd7N&80GQ>PYxHjoJF;A@CJu zLWsPcX?PL7cbtR?X?%$;r7Q|eUgWCEqL54o7etl+3G+P82s%XO#FJCKC#*yeyu~68 zpTvuW8!S~k588jP%RA35YA!(u+uNm}p_9$$Zb?Zb5-v)1>YQT7YwWbI}}$DerBs)Pq!YIW*Jl+OAo?uw0L1O%*=Y-)c~!NON_ zb>Nc>f>~}U; z{3f-v%u=0JsT9)QrrRn; zJ%s;$ddE2qcaEfLTbX(p7d2jk6tf}H%@r1Fd=UCqd(YlH;) z6N2U~UzwgpPHkTM`WkZ`MUQgsZDXs?UmrY#8%s~yNjZ+4)^lmKonDKO&h7tN38N#g zyLnVv`qzJc9YbM|m|M3@v26*^uh;7$1i6H+jEuMmq*gSqKUv{{Ym`?*U8in;=36s% zw>{QpBZN+3W-%Z|s&PFXSB!dk+CfmxvYd^tY!^-R)aDy#my#F<_1*2gX~?$rl@edm ztkECb*?|AHWAFZsQZmz{v4{0+qT=!J=&bTI{JQJ5!CrnnzTwz;{c$Mq*>GVSQBl_3 z^8zm2!$!dohjsPHUnfAT@8f%Ua+TWVnaRM~YnN<&H90+rclBz~XzNwVoFicN95-{7 z9GH1|R&y1F(0kq0A(WGplCyR9$aA3Qq2A%1-~BT2XPHSk<40pFN9*4(J9*E2dqJDJ zvy&Uf!Lm;-zdGYXM=t0Ct}ia&pb}O9Tae&XmFJd-xdLnKR88yP+^wg|*F~L{TPt-g z_UB)jWE*4q7vKkS0s<6yY>!#x1bn10A=-VISH1wIxd|1xd`<-l>ew$E>kC}es>v#6 z%a)#~v`_=!)cW$4XMMrCfrN4eT4)e&^fCm2%6U7DcvXGtXz1KG9rJBm-r6<1$#0_N zvL;%8!^wJ$L0u_;JJ~=+sZ{$c;KkaUoJ}F#6wF(vBgygMgA6YA_-jB5Uxtvw=Ue#V zxEmfbF{XSzQ5lQpQ^}fz>>qvULp$ov zgw%Eu8T|v0WM_4LdPu)Wxb3Ep5P{P>#bQ;^gkoT2eR?>l+8PZ0tGMgwIACjM1vpo@(`)RZ|r(Dy2J`l3l+3i zr3rOxk2z6bz2Z~O=^tYTfCmZOaXy98frt6^&0O9os;~X@_ed0Bg&CBXrl1d&E5hZe z<-DaH8e^He&U+{Np@oiaDw|?(GkKjJo88P~GuN9a-=ZD*2OFg!6FkTmHPleGP}O`& z6+UsovGS=uyM0$F8&o7gxd>zPt+ok4&oanD>iF^R-@>lShMEI9`}6utaLCzU-?bv; zO0jYVn%=@_s-jT{)B8~`n*cB(YjbTVdu_d7?`}nltmT>1pa#U2d5=gSD8LA?7~xt> z=BFX11#wxj%o1yWn^D6wHlu@mozwH@C7maRa|VgDt`NFzTVYDfz{2b+!K%LC!Nt>+ zSbcQr64Um+IGSQK)1(D&lf!W^eBXz)zfV+perH=eJ)O8YMa-(Kpri)oxJ7BNzo-M- z!Ou~ig9jBVr(K6iB1oKDGC2jpb@m22kZ?sPGvV>z#4Cg6bGvNtohSbURK=5sn-H zf8zw!VKZqUN;wK&M`La>xj~v|!-*TD5CQKrwd%TT57rMxPjE;`0Rfn&1~?;wang)V zG09A|ugWp%Ma##pIqFma5ISCWrepU1mNjM`hR7Vb1K51asj=178p3?61bR3i{D~^DJPx;$I z!LUWNka9;QVT58T^&j+0M`$!Xu$p#CT@r0$8{NbEGdQ9&3w!hwGm#{M*=>~|Cpy%B zn%SVA@ghwty((dlxiiJyV6>+z8Cjwlbx0zEdpOkr%cR}EZTrv%_kP6#A zN+>;}zP1dy%5@CDy8IKi*~pDYpYBs2f`{m$6_be}6#5vdM>FC4%wy*y;nUisURK39 zv<6_*-NnXR0Shv!tI$(!$94Rz%v6yuYF1twB)iV$M4!;hJ7#OqJ7@i)S~s@3upv`~ z=%Q|&mpP-RbpP<5lh2#=owJ$^M(Cp_p9ul6K1HSVj<$9|mC+@twwF(olX^(@c3N0t znCT=^UHWY=B0!o8a;in*RRWB&iOZGpcgYSmE!Q~YY%%R0Nf2pnmyGBat(}(c@YU15G&urO(|RJ~)X>wHqGDQgR^JrzC*d+rV!#T21@O=@#9GFHSV_!Bn3b*(?_LX5h}T zD`k5|RKh)a+?UNR>+9Ux{E~7)mc+isF^B?RY=k+pzjc*Ne5oXX$jpptY8JCh!M>goAyr#wt~gtlLZv8JdIG;5>{u3ioaCY zl}7XU|LmL3V$z7$4Ky9jH5K>gQOB5qjZ1+j=p*>X`r<^N#-_BI)w*Co26ZPWZNYY5 z=r2jDNBo4r@kUIzh>0|2J=J^Uy4z}SGo#g4M*9nf-@N3XkU={qr1a=5ls&w4L!CC} zx*_X`XT+;Z!}AinwN?uB_Zh0TPTQvVUsoPhAIYuxhqPl2Tcn&vNY8}>|^Y#aa?Nws1N!!tC5m|?n6G4zoCMW-( zB1SN4q;FnkT`Q;I67!v&Dzl(45lMk|-=uE{SE<@g9#DtRLcz<4LRz;1Y!kU-f(t=- zT&a_~5uFh(&_23di`6C<35R4@B*37AFLt6j@EuBL?>h!{>tqwjA^XR z4>;hU&zO7HDSKhx{L>a$bFZHH7{Tp-nU=IXZkuWE+wZ<4;31$O_ie-DqjK`Y>7UD=K)Eg|c~f1|;Xw zrUv+yxzWAOvCe9Eb}K456$@DNz6*LI#x6UpqZ;1Vqwm@B>~KvS6R^ca|H2dJOxTw5 zY@-osNa$2!N}o}eU8<-E$lg#1P6BkO2XNLa}z(X>Wq2mYyC*!g>gTf z9S0Q!vSs1T(}aYdL&vh>59+MD|ZgNP_@8&8?%xc*Uuj+c)vR)75g!uL!cbRqbP+#BTgP z`AnNT6Uf2tnVZ_QH!#{Yx3x*vf&<3rYBu*VRVHRKqybp|-LtlGD`9N$K+@;7vj&T9 zlogMysO&DuC@ipMlg1HH&znfW+j2|k8|(IBJ8RB1QzNEOH44A_g=ZX5N|LEt-Lbj) zC}@8$TDEesEL8_nD$t0uT(QV?+uK=kQt)_E%2bKp<6G5QV+GH%%5+wU%1x!~+6=Jy z4!cg_*cs^XMfceNQp1dikXrxohM67zz-m#wLePdp{PNG)JSnKIYX)hYBsfEaN6^-n zY21-tAcrz-)XWxyC)-ZYj>h1IfcT0Zol%6{l24Ztl=(|r9EO{=zF(FAGnii{n^GcO znk4FU)!!lhas{jFtEI+2)+YWF2ghtaepDefe)>-x1q}^NN!rZTC1xR&RVZ)KQA5i> zJ$_@$2I?v#DI*ftq~o<&l$%JrU*l*1jLEcXi7s zmfg%e3&U85-VXV8X_RWvG4S9YCd6c_)Zs#;(;y_xp>e!}r4_V-l#KPobCkfs--Ij3 zi8jBtJU^EY(1fU$J0f9qJ?dMy1ofKW30)H#3;`Mre*10Zs2&>2o6F2 zVW4@6pgt6Rex$bg^E@jop`jFhtk1-u!<2n!c50c@2JxkZ`n&QDEv;}!Ow1I*YB=PW zpmnkzi^3NUg4jH7r)FmHa@W-Eytx97MsT}s2ZUTj(qUzjuElsoJL=%I#tBg9eCP&1 z*W?=crgr#jl0{`V27oA&BfM+W{wC-0gp^}oP z?-WizErPf>Y{sXi*f&IMyxBgv;3LLm`j&1p0{N5vULk%R$fbgD^P58ja+B%eAfOg2 zz$LIx`syxh{q5|`vK5y-j(1=i3KiMYbvK$bLeG&L2u*Sw7m>~^Z7?>1Zl+e>@N!a% zIN*x8CURh687wCoPDX~$I*6!KauJ(ZN5}YCTpUgo*lFH2z7j`xeWx$*E5@EvL*3)v4%4ZSUw+Rsh7b4T}5)N%uXXq^+`NFan0`Am!?X z{m)QG`KiRC(jwzQ6~GVSMi}vG6{PRUwH!RKs?`9f-)5y((+&7yCG_lgQ?!=^CI7|C zGJhy@MXDn$)`oEW$H*YVJtEspaNq!$O!1ozcp zS$BeT`rkOFGi)&%d@&RIpBSgYXjWs8*1aNY#@db3mabF>fa!o0gQ>%L!-&&j47#*W zrwu2gmuZ248H~~rv&lz(=Xx?MFdE(y!axk`in<~*8?+W(VIt{!?JfcGd2L`CA!!{E zKs^e7kdD=Qu};I5^RX868|O4a;vj2)k^*_asuGNi82D0UIlo(z?Bzn?qLDRW|C71{ z3FJEs{(ID5%pNRSkc8Bqi#$P_0dz9KOsisf0-`kADUzU zK*0=HZmzK@;B4r$YOA>6W~Ilenv1(kS6icOll&h)K#42V6jT2;wqojS+bAI}`*^c@ zV6PQG&uM^iy`s|tWmb{dAKTB`s0wuOj27G{ig(8wY=BE~wYZ;0SuE{SGWH#+Qn5p% z^82c#YLSBOW1pt>Mr7)*9uV&r8-S)I1cPBG1P6f6Bzz5|Y}_&i7ASReQ+fTV?Geb- z7?Y+awAZ}b2lm}DMGYd94M3Aof<>5&)-^GuJk1iOQd+i^MMt8R`NVlUsE%ecj93Qj zHPk+r+Lj>sLK`am#ul#c)}%QqCW-ItEm}l*iN}h?JO6VhZxT zCrujze|MP)EM)i_#)1c|`&>}hpkguI zs2xWrTsN_Vp!qD37arCa3&>aUkbLS=e9@E`p7a!NJ>Tm`Wch;Y3q;vWFdmQE-;Kis zF*U%F>;=odiZUOE%fbr9qO}-eJWm<$m<9M7Iz$T^k;XIPfitS82IyY4YAEyg$=ked zxWh^%^JeI|@w-&sn>kv@tSvvusi4`*S^!RRg&Va3mk9WkZ7sQ6gcP(XGA9N*$6_^b z?!y(;3CfikNv(4eju%g$%*zhShj!Y5eXdhFr++x3`@Z>R0cYq~#w~}xn8~5irs{qF zRK`>BogVu6s2zVDQk=*=aA=CpW17cuDHD10aqEp-sO8s@(>aL-*$-hHFWHh?rwgONlz`;yvJ z=;zc#+3Inzu`a@$8GvPn1q>>E<2Yc*%o0G%0#!Uap~ShJC>)5Qu01P@w@RUWG7Ta-XYgwWJ}}_p}KTC2&F_zpRA01wUOG9bNu5pOs6w zV1!XRKCKlU`CFhMAiGI!!Cdy_vbS{!NQK$Ahi6e1SF(YwP`c$aZVt?g zw}^B>IltfXbg(h`e$`R2G4UdiHH)Ch76@doRjs9qT?S&O-Yd@P4{dRUi^e^x&I{hY zH}dI<+GmrSA;L+M2x7)2EEBz^X~S!d$49%#P7Jvvh@?=O07y7&h zOUM)i3^zRs+sUN@_MxN70_)MAz8O5a9heSCbe}YFyyp3YMY6xlnu9kH%4t`h5YZew z?u>W<;g+;v^Fg^+-_P6;O&czla(L=MQ3s{WD!a)F(4bHKZml8RwBB$#?2I62&|pfv)BT zpu`z#x)v-T#_<0%+CXz0Gj;hpO?-6y#Wu_iBWYZ!>-R#dcznGZMkJ0{Pn2^yY&?x&O1NMX-yo#<40E{9_}qv<$BDCql$2B> zi~3RdZ%;`PP4=;A&G5B+B6k0{n4BT@__HZS?Emz_0Lz{YO&VVgO@=ZGGD2`0|4#+J zKOg33ayFc@=V`z++0}}b#bgd1Ti|ZB(0a6(U#KZ{{_sd@aDQmU!9_lHd;uLZc3zcW zN6fj)5%iin8x#jAdDg>)TmDD}sn*R&OKuJV9t1is3wYZOzH zNyI8yLVB5&{4dg|a?-3~mw$O*B8BucwSS{FO6!(Jm1Q~Qx+}{SX}jKA7~Vx_AT<1f zdQ7QHO){^(o?35#QTBS4tS=bNPO23r{T*W_*DOCwZ6EFYeKzYPnR6NQg#I$}9)_+) z1)x~^!HXTrzGMxPFyEHTrhpTB9rJ~A(0=Qt7L2~HXaa|AhtSHzLe60Pe=Tcjt!-LK zEgdpa(Z|LnlcKTdTb<=d!2n>frW8tACGr>8w!9vrY@h(25bajBCM=}+9N)_68R&>raW$khIVt&?1HMfNHDGH3jHnt zGdVVj8hCL7et;SGkU~hb=V1^3B3l3xvi|mQrSYUx z+BH3x(&=7<#Q0&ZjL>x+C5fxMw(*Wy=LPHC_h9&IkQ|y3^5a~{=1GA%g9|g4_sQ4N zZTnmfjaaFClA7jr0vu@zBEcR(ZpI8uO+BL^yV0r1`e@~@mY@t; zv7)McE(O#20pX+Re&at$Mf?9F?k$7jjJCB~Xc~8ScXxLucyO2C?k>UI-6gnN@Zjzi z916xqm5&qM>=$nB#fY= zb-`q8lPg3j`A%b5{DBo1JX=%sYUL1dIqz`R0y{XvFiZ1id_z#(I!o#YQ*5p2@9i#~ zPz2Ti^MvT%P-AOp*|cQ{e#&i0ex00fkpeEd3X-4mr208kXk_)&)SNsW8dM}Y^pU8d z@Oy4m>aY+!h0L431DT+uw&F+2AQs%2Pk(mw@My$7a?OmElirZuIup<}%&B<48<31coVcrh|0}Q}sh-L8jR?M-J#9 zXjLTUDYnWv3YojQs*#{fwl?}Ut3#$kgNi&_IDg;QBJB1Vab@?herHYFO>8VLWSx}t zc1CH5UNPFgKeAFV`a0OWw{hPQZu;}0c;sAosmk$ODcL-s=mNkZzaxFTjy?7T!6J`5B)l{+M_(G|-c(sFvl&KY{f&445HaBCfTx3#JC2~p$i#uWvP5q*NKWF~3OzKz;0V^R|Owh_396z%Ht*{tnueSrFJz z3l~CnI*GfTzf!fy> zh!fZx+dJ-pVAHL<<5%?c>9+ZWtzW22eq*f?fB8JY|CRW1QO+{PD>E#?R7kJe#bSGa zH;Z8iD)4SNBbzHhraSJSi5#JUsURB@t8&e2+7kY3I&kD zA%qsdh~KqW8@D(90=1c>Q7CWNkzb4a>Fav;jDRUNlrX|>+S~bujBs&jGw$i79$eP_l`fUn9RZ1BdWG-W=xX@k z>r7c}rcC}13buz2vrmm9Y9G-HgnkfjT^A3r5iP&7yVT8nzB=Yi0pcntAO+$rxDHT? zDMBUesZvNJ$0!Iocqbk+HA%F(PiYq;GLt}H~g7KK=Xb3-0(9i zpb9QZ~xfeiab($wB=TTNEg!%w`77f0r*i7H4kgGh=01>&e#IJr1NX&#!w zMxUhN zp>17W!gyMp>w?a)-(#!xb23YW$?JMG`zeIB)1vtgjE zT5K+c;+%=6#C;%mRh$J?c6vD1zpqMI(!967pR5q9xOczFDQQcZXj5HxZo3ZrMvshCi?FBuYLsX^JBa0hfC*MSdpo`bf#Cy)q7a_&!c;kyOYajW zui*XuFyY({mY>Mfzo@CH#l?6iplTiI@t&{3gmHtn9s$HR$YDT1x@JJfsj*yu@gajFLq(=cQg{p;iXZFc49wJgP6O$25k_g8^LP@g-OKsl%v#Q$^Rr>dG^Z1~j zfId(8%>%{UY6nh|LfJA>o#|PtM7x5gFE=ouMihzfv##c}3M^a$LoRMpn^eX+zXCVg zUGKMflm4%y#Vm5oa1HaR@5Mv|=$->=*Os7YA;pP~3cB7)?2exD@%{}_?q-m(-9RZy z=5}4T#26RUL15niC_&jdv?;BeETCagoh9B6^k3QjqarUNRy+-XURKs!yKKpd?4Dyo zEc(j8nSfBt(#&;-S2Ii>%oQ@s1Zqmu?7uSw!e&=>Y(y19< zVnW{;@as*qMs(DF2#piH330m=6L&Gp|Fuojy|T78yJ07-(vX4$MdhZ11H!~VEXYW> z)PSr-FT8@>XLbc~tP>*(2vuZ6!Eho{X}D7RL0P+QDdQ8XDu-$Z*6KFgV2}gxM7m@K zm5E7E2+E^8>^6sQsPP_%DyEG<$(49K)zsMrehUJ@d*8pBq~hAiCDL{K^aqOlv2MHt z$f*;Oqy0y6&8g7QlwmHKmZIUCztxp+pgw;Jxs&1QKB$td=Np-_5(RK3qe@Ej|3ju1Uk@>fN8NN1>Oi#r@|_X zT;IEYaR~PT!Q1~)J?)RpOS$@Eq$N*uQvklVcLLY+!i@NYjwi-<}ff@4G*3I%myZk|mBBaTueNfsDMnl&?eI!{f9 zL3lPz);u}uT>A<6wvfls^>>T{hVryEc>sB_Hr&rX^W>7xfuvA5S(s_KN=bh4|1I9p z_&rNZB&Ys|9766vwc6+^r;Vlv#h> zb1%^&)3kmN_zdayBohCNcLh33{Jr~aoefh~VY?d*^BMtJ=O{?3OuGk@wJc!WUi@`Z zFIKE}a#Oto>zjz*w^C;$d}D&z@nNAf zA1rr8{BF?3@kh!M#{ABvDq6O(Pr?W)?l_AI1(uzr@6Mr#-v~}>iDcCmpr&c#VIN!j z#gK7y?UV?Yzh?TgoW%%_ISsP!bZN(aS2Jcur5_*P#*)ErHv2?mZ1ai<>z^oF_52AO z;;sCx2bj@)5hQKi%+F@*c6YPG#=5&^R&Jja-RzJy`a2rK;El2(+V)I=r1|gBf?1WX zuUX=^%DB!MOk5DcHT47TlW^Q%(?sNs7N~#4TU!(F^dG3EhlPa&`_}a%|H1dc^kMcR z+Ivf+`>Eo$W8EDJ@3Bk~l882UFDgdbBspWfNwX#>>WR0+df_mUl!Z?V(ys)za->!z zO;*gx-u80Qg*0$@e1d|6KY<4( zQqt0egcce*jEwL(cz>xl7p9tJWOgE5kSNVcPdA@PSQ#qXZD8Xcanosb;N2uezLG<3 zX1Gbe+ExIjM8E`V-xs4eSg}CR1qM!#XF0#I@B;iYHI8KS>v~djmV>5U}+DWg&rrLX6oGC-JO5Yn;W>-@67Ka&!(PdIB*bzNlLJZ>;bP4Ez{SKgE>J*-ZzW zCjpB_SBueLE38$mj%}u(Ucbl>R4qn#gjNGj`IzWoyc>|qjpd}NMUpbI2Wq4fvU_Md zo#Op%+1M1II$^Q^8vaH_?$!0C1R&KfParZ#2O777K{{^Gs*3Zo3P{IAnh&P-!VE_B zTIa+^HlM=u4M(FG3#6k1My)$Rfw#2{svf9f)JT?44vK!FnY~8T_TTIqMx_;R>@>D7 zbphCKXa18M6VI~@lDxl&n=o!2bz(&N{~E;qNQ$T)D=C)M%YG54yuTZ&+9sn!Wro2| z(OrSQ`&14f6O`1}G)aQd4XX&t7QqvK0V-bKha&{RM+-(TEPua)V#;OrB}d?T$XO9Ll-2lo3*_nx-1 z=r;@JaFc@Vpx=Xozv(*%wsuch0QAu!L9wY`qvFF#pt+hVg;|*V+v6|z^v`P*bCU_8 z?53}ihS9Zz$ywd8pV4PfMa$j1j^%CYP~(tv+sMs2 za(t6ZEXwnFF}%0oG`!#hh?~aoVL&%QPz-_QcMVtSgobvfotJ}U9{q`9TmQGKv)YQ; zHEN;d7JAd%DgE7>Zbg>OHo0vuFh$Lb@XSluc`Krxr@?4_VgZpsKCq#9P_5bvqXH}o zpF;YR!wA*RpRJUKR>kLEWd$?TI_= z+6>gfY60b4?AsWQd?DdO40QsYVh--s!X3Egw~ebCEFHauheM%??VS^aeV8oCRUc$E zGZ-sg45?K4!?ku0L7#o6tc8^#fdU!1>3lNh(pUAyh!_DnRmgDjHTDb9AI1&-0%B%Q zN2;&w41fr9+QyXe$y3`{cU>pDR6NqEso0buPCo4-*+&*Mrx@It8%-o=3Yy@;d%r3h(j}XU^G~XzKfagRhxW ztVA`#cv1b*Y1OjNI<$>$+^z^gt`_1DbcazTV|nQJqgx9c*Oi7dzJC3G|>`c{%6 zLXA1ce!P9(8GO8Kmqa3(n`$5u@iN>J_fnjItE*%h<7t9b4ptuqiCT@{83BmM8uo1>3Yi_rfVz9Fz5z$onBc26t1ge((? z1!&W>625zgBVQ05AL3Ops+dP+MEtY7t#*A@vIe{U9ZK;yrM`)iB%$lWvHfn|tUgg{ z?PU64enVV_V~8(uG4`iCTN_TNx3c1AV=HZ_>+i1F#-?%8Y!Ww&22{#1IAl1~Nqr$o z3FEJUHiyul2XQ&8lR69FhCD&$hV@s)Qi=?`H{t(g=`MV6fP5Z@htwkc8+<;`b~Ht% z9)(h5Ne)%j4C52&MUkRK5G9yrw3BHv7(+gE^N%5`uId-qYBGN^F*z~4yzt135@w$i zZy#1aYZzcP-myn7lUe1xYdJ_;bqJQ|lt|RBHkCq;gQ3}uOg@P0c!B~u`Z8vvcS~J+PVb~6NFt2Hba`AvUPMWdX_26K zeU)#EOF&7&Kn3o(&y!n$OFP7qqH=EMY@=q32i7pMtZ?}($dgDG|CY%z4h2MXZ0Zdq z&QlrTpzv1zlu>$c5!MZ!oBJFng7Pwc4Xoy7V*7jV5ba2p4MBZlrz^aCXQMuk$Lz#NBfG}(iHjC*;SCDXK5=RH^ z*rXa8G)0E`k|L}=0n^CVMJlDM>Q282h&TSmJ8Cgh z8O0O@h(sMshtIK(9XSb6eza&&+j#Ha`OO}SV()}kf8K}D@Z!Qi86=DP7e*L8wUPJk z;efJwLW61)^7<@PnT)3ll89O3z6`V&g+3Hvz3t#`6hH&R+w3|!B%tj}5G+~C4v{4@ z=*Zfs34jN&9V$o~>f<7!I>PeDAxntO7_qqE{5631kp7PJ0Ger6d*Ee9WBXNK1Bm6q zM0T7IIKU=|MX0yPTA-;iBI>KdL_i@1=XU@T1jr_nCX`osTk=zObeddl$6!T)C8k8jY%uD5jYIs07;y%gu70mH&86Q-X0d>3T9Q{BNu^%y@X#{3N8`LM>(EhRP zXAgR6Jj8=R8TpYWtPnso$5E)97kS?c+x&`VS+Wg;rnCFSf zdOV}6gsqf`NKmCD4PCz)pS=DUMMfabcr{d(pX1pw(n@udG<~sxJ3Qs1wW~GIW|!~g z4f!w@wSP1bHB83Xc4)MmhK3Y8TjVq&1z6ivdT+1ug1%hadb0m(s>R9Y;-K{7*Jf#Z z>&Fz=CdHdW$NC*tjlR#>Pevq+WiI=+zCcP=ed;z8)D)V;Sb$UjqplX3H|vMRegKNt z&J+~Kr96StK4@8Sw{-r~5%0ldj_{AH(;u+KpRIPzPoVG>bs1>bomQB&C@EN^fg!g{ zxFRCb!vYUom)pBi*bj}nqb3obk^Tsg-rtTBy0?X~p~G5|P8Tk3xgm^=jVbeV*V&SK zgH1>P;H5-l@Wdg9Z9KP&b`X#->?{@nBNu;VyVXJeyKG`=fk>~udQ8~#;#%%i`0 z-nF-?s=a;33gA)Z8U~2V7{Q{T#0<~=?hno;a=+^8_njXEs*BGMz-}<6=37N_)ui~E zEu?STnJ2JV3$yVhYsgQ!K_e6Le|C8tJz1;?DXuom$7Bn8o5HdBye43>ckFlRq~wbpIK@_Q(b2X8{h0GsjmS4M3*y0ugd^`=Q( zUryS_Urg>b!ZvILNHEZZjEHs!3y9ft=!tv}W8>d2ev!>O2>E}rQqJn~ zQEC|2|Jg|YXsNk7XML93sTF59*6jy*2aT*<Jvd9Ao0Ms)i=si!z8$NiZqUlWX)>2mP(PPNe;`fme7)7j#!FjNG3{*A z1)CWWEzz7GCT2oIQ{Am{mCC@le503_Ta;|6k=E|68RY~JUna5c_?m)fy>(h$S9KAq zt(fA?xJPqqlIDD2vYt)?PWr?e8E9zs`uN+38qGc#ry@n`ovV`^M)eui@p*~8O7ZE? z=iQ1~jKfxyDtSz%nilJK2|6^=@imW>z6dRcn=y}?t;5^z?%1gj5e~EVZa^)o_`4)OSbsP3Wz$;|^ASW2D2EYaM0*k_MBciYvsLeFI9Z5h$j zR^cE_2G5NM-}F>MnWSC+0~A%RmPZ%ZhK`vyoRr*`0o*?>CYi5aVd>no?X@uR%w%4kv#`+ra(^@%X^a&svPd7_2H4Yh!@m4|H)G+(NCTpiM8y34yGih%;@g1$@OWvz zyAx)l-eTWB+p}Fi46`y0wLcnb5|%UGAC?JxVTqhTSzWTwu~KminIsZY%m~Can!AWj z0%ina^uU%aOz%HJy0qatIZL6^x;iDYMEUMUU#?JNJc{fccZZ6cy9-r{WflQn3rge%QhZ*C5*K^3?x)SFBug{#uE-5 z=2i*?WsdUOVe_6QRR{K%no$P5-NTRDYOy4saI#gU= zBcFi356md){a_$aLPX2ueM2rqlvhMp#BxwjIQ@+ej5)THg1$P3*o?iPa`}qsh=HASTbK?T;c0$z`87m3nrIy*&`6ls);1*L^KAG%XQ;(|31V3`mg;+0mZ& z!P=R|KOq9u-#&8bLdYUOOYy`*3HjlOWQc8Z_)O4yr^d-gC`ObHt*{M zqYnV?C-(v_hzCCUyI#z%y5nPUji>HuXI>T2X8F1C-;5jR*MDhnzjbDOCDhh()k+xq z)oEiAVte1RwDkQp1lQQH8@88!Ps}GJbbR>DM2vVxsOWt0E()91bm2|y+OBOS& ze;&({k4noE6@sT7h%j$!rfkJGpT%yFq(c3K`;j)CC9 zR4lI1y7I%s6&D1GT|dG7#Z^?$C_)szY@sV=$_Xp@jR;vwhDjUl6Gy=!2T+qI(e+IQ zh?k7fo0{b;MRKI(^zvjYsV^_JwwoQ{=QC(Nc65ZvbmWEpq0@lKr`KX6p2k8?ZXbIPVSa28&PKr{-lPc@A0H`vty&IMY zmzS$H{if!!frUW73J;I-ci7x@C5kmz8}QP7gk4ib7xAtk$~~#cAjR+jbc&cbxkLBB zUdNGwDcPub?V4|!wp68Lxm0ppL9SdJ=JpJQ+vig3u+D5H%oVdYpW2WO9!WIAH&LSo zdgWn37ewVF3kA_oI0AVWa0>b-CR+qCoppEr5Xl2Y{q1{)Y9{|&YEcW{)oqnob`S)c&RXNsjnmQw^)*H4gB-2O$jsFTRhx9tCVjW<2B& zDF5OoLf_{@LZ!%g=xO=2;{PK=toCF9*-`&oVEVR}&1ut)&+#3N@b+bXG4JyV@V+`Dr(-Z#QGxx^jF!G;f=l?1T3XMG4!8IS(Jn*L>Cd3Iuwk!lLoMp1>muh z>sk7C0%zmf^@B2qU}2)btYZo~48C~Hk=-p#QbwyEi316dUTTzrb6O}tO^0{$AKn8 z6A_~=NBpmjPA|i-fPv2)PMR7A>4rz_7|Ijv5aGNtaUf!yjKeP#0{|#VD1^ht!|u)>h3e=$qzkJef?H^1gD-+2bH#`Sa2aBskj6wS^f|a99nleHdwr3 zWufviUslNI*9&Do+iz5%Y+0$7Cq(e@=`bMu1}xio4;2x!3o2TM0pos&4$`ZcDX~bKH*l-z~HxpT1-%yO7zXiq2C7%d2IDh{2cb9%aj{;9%e_)=d;%q~m#K^90UD#C&X(S?ez-?@VN)l7Th z0vGyJF3M2 zcMSI4*3s0x zmt1GN3ZH`7k@;kY5mV>Hi>Lh>8zan754nkj=Qu{&Wrra2r{&M!riV9e@DrN8r-(Rg zCgO#+*lN<^!=SLfVF91 z-MGpol{+Zme=^FXRJ#{6cUK;#Oew6~J;^i3iOnNIN%oxi4nQiqBv9U5X?H2U#a z@i44ff_vL+l{ZzVxUS&D_re(3uqbxqYhGVznUY2qwC(B}S}I!_k|P#wTz+yiXq|?P z9(QQ8dPX9IBSA-*)-!bu z)6b&?$mzbDtW*{1AP1vFnq%RE^kGjP&CHr3+7<}Rv!`Q~zn?y%EW!rFhR0jEqztZF zonNue7qm|er`1Do@xsu0XDhWh7OBT#cdVlESj2p0?Can3`UB+NALcp1XJm@rEK#v= zHiHWCp9yFl=qQv~Flk=%lV3Hbsk(byb(i_Wqg}Jns+v88b{?~G9G8Rmq^&1$8$5@b zTA)>99-l%gXuggzaeO=3$wgDs+3@_$FJWapN78o7*H3x|?8s}m#bCx-y*PMc%D1wz z2wKgppyP8_6NUIhLR$KcC`QGEl(jPnid5xq?i@SjSq(ColU+E4mZ@v#=jAdhR7TEtVbbR=MmC=sR*VOsY9^s zm~-IM!!0_FKw<~b++n6}L|_S)2SEC&rNF={WJ@$-B#OlQ4XRbL*wZl5?lt1VP2ow# zxIy+(7L61%(-$mSC$E$KCS8xn=JaM=GSo7Wis&x>Qr+R}U%*<_apKxvkQbf?m@9DA zL!$GrAjlk}3 zCHms8Z_u4)5(^JdCUtG!j|xercS^h8A{aI}7;Ia$BKuMdzn$>dgz8>x1wfb!Tt^uL zBYO_|{n}ceL=Z0&P}B{a?h6*`;N^s{V$8;>VA@zIF+*iFEf%j_SQZMIJR9dZ(p>)t zA)|hIvaF`lg@;G&uuqcDa1Hz@bx}^W6!I=0ToZ{9OSe!#c$PwoH|_h#%*bUBXKY++ zN5IjbrV6Y*Z@>KlBPwjpM;OH=V>E|?GVeRd=wQP36*;!x;$KGPvE)~}DK3t;5hF88 zh~9_RX%D8cfcvqf)j87W>O7$tBraX3Y_9qbFArlrOnoKY@Z@6bRO~~H7`oQ#>RBVT zt2K7fWD}h*xSfj=Hrk@m(bf7@povZl+BY6Zw1YnfrsWZQbU!yq0q5 zLsH2~KSPvTw!!nK5Mr7Ci=z<@rQTo=Ah3=o{KBs$mYArk4WQ&FAWBmIVRw-{#Igts z=cw|0=q7Bp?k1bBw)1&NI|xLWc?Wr;`%@N5G!&A+*%~Pnx1N8qImcQ1Iz^xC-p@9< z-zeIShyAR68c8l(P;13STMBzoXtlJ5aM~GHxNM)>p8d@HzGZOxLwCJ$&7Ljw{7;>0 z>ryj9LL&(-o{OMVW0XCwMqJsQZ+c6@W@B#bTu1esi)_){C z=(Sq*4-qKH?U_l8>+NcTt9pKTOJlTqvJK7>lagQBV~Q z9FAO;UqQe!U;<#Do}Pj%=>>?&bD;@%o-*-7vv40RE@w2xg9K_B6=mOwjd!j6M#r4G1TcZcqXNPz)@n^Da^D=;nGLXKo3gAv$7&zQ%b(OyZc0uq(whbmd&{aLO+z>f1iuJ zZ!&ae+V>OOelI;7#u`#~Z};GPZjtP%hEiDdHea6}&=g7fzjlrsMin?X8j-A#5Xhgt zl=Ablwo?jV^JaZ!!I3m<%L-i`EI<4?m#56Cx;}r!hs!Wa&MwE5TWzvdK#Y!#XdnWJ zP7I)7N0-`$bW}D33ZdIK%l$f1oXz|Z1@o5Ro!o}AFRQz+)KitfTJO%w%IXA1Ed{2K zUqy$MJV-IWc~wFFeZ?+L_S=P}jG|)L>ozHcPSpBYm?7A#sEZnnc{DHq)VBr+cQ6J; zBQ&(F+7uZkIi^=Be{D+HL}Y6Ewh;{@z~+VA`Ln6lnegw$TU{#J$ggg4)vcq05Kp0X z1N9bL*L%F!IF=S|!vVzNW>B2w+dbe~5ELvAKp;CYx)?Y&1J3X8c3hmDa3#-3+`(RT zI^m7oOf*>Eeh1SC*pmKU02+vvAVcCM$pb?vNs6CvK%1R|;~D}IdZ*x#BA_!1uWmuI zOG~R!%Ucb=LyV3IFGUtOBrD>H2O%hW2k{3OS56oVK*y95kl4BT57`yMw>$ns+Ys+H z5Dqz&0`|ztt`KxD0|uIO1C%+X+6L1-r{?0dcMQR7-&M#1@S1*l`v$Iqp7{U|Fsyjs zema=-)7i7=|Nk-eE#)h!*YIM6OyL+a zTO*xW?-{{igMQmAE06t5V_W5E+c?N0AVKGm1P&>R$Xw`$b;HPb7}Rp~-^Yc3D7%As z)kX69>VmS0ZgYankj_OsJ^lLq6K>||REEmb^X6BBq;|4JVV;a$8cplQTd#KU7WeRQ zpRxT7B9H+3+P-Si&BJnoeZsCirM7`oAWozH$UcXC6@4xIdz zCqOfO-s2^hIQT+8Z@ms{KklzAHX0s3^s{zc7;yDq+fSwfUDoj4m)Wwfc$Jsq@L+fv zFJ&IUXD2mA*aAfxMH#nCgaVGIgOR%btr&NvKriM(C1yokFk|I->QHzvW>NEG+ z$SWxW5viovg3lI*&4#+H?OxZHjELpy<3_Q#(30cgK_DhzPZD^(wp_TzNgemLb4kli zNmG?2HR znlaWzr(N`j{YwpPgD z9IduKsM-DD;gfgj3QI<-zw~8&um`Noe5-33j%}fFq?7)H>DRkNpU;`6lm5fzbz7*`;#aK%HrbluLzAjk%+F0&E<8zf*dnUu;_lk4 z_qF<`5x3Rarfsp{qM=ZM_dT)(aQ}_UX!vn_z9=UjQ4N-~r&G9(F)n|*#0G(Qk;e{f zdNW_fPp5UC^8N@6mY^b)cE;E;{F($R6F*9rm@{8}b@kjSTJ{wCF@PI98f6usWldcr zpP37~yPpvHL{frlu;`ov|8GZ8RX70qPaRFaN6VTiNj8OH3TYp|CPeF>t6Em8d%Ve{7nkeY^09f9gAlLxxCCN2`0)p&TDdoT`~v(M+N3kPe3l-mX< z5hsVtFt{)=3+DEgM?rexm~ydeQniUXdm_4LbCu@V{Gs|i&%oCUf8FJY+BgZH7OQ#$&O_-Mzm#X-8KiMbXfFS zTn2~ca$7m9Qq4ekkApKfwrP$QgBl)xY)5By$hHC)Hu4-J3+IykJNQrH@;iZU#(lQMU*uPR zMcS!pwY_#K<@sV0e9s$(mppw2I(XVqGn(UJgHnKhIbBe=LnUf^TerT|!$}|`b9ohi z^BeD;GAPCOm0nx;>i5TD!3MPt=szg%Pk4NAePzSF-%KzNK&z??)7v62tw%Tt=pmYz zs|3e&V$X`j47EN7t@xq;uef~``)j{QCrYMZMCc2hIu3VAT8i^3La6!UQ#;Ok@Qe@` z6RsdsjXrpdM)n5M6Vj^GM)h1Y%3j!Ur5b`$%o7(yr|pP$=-JAr+0X`)@BTTKJg0Gq z@Y|TtIxf>2+$#7h4k=X8`0(l+Qc*ibnaw=URPK|3E%)#qG<^;QaHQ(nm zM(=uEDGomemis8VY$CiJ2K(dn_{#;j?R|3N&LLTZ(pNP75p8;zaS(M=v4W_ZBC3aX zAh+eeQg?I`;)SL}GXW|}Y4JDZqvL8me|r*{pO8u#S;NlT50`(GhOXzd)LXEy$Xqum zGWC{}W3Qy+7o`xSR7EwQ57IBgY3TD665Tp|e>kzolqghApg?;|p~qL?{^84_eT}Vt zieQMN!hi6nZrS~2;U4twY7v}tYGmV@iz*mNo@WEJJMwq_&tr;W<}o&w+g=?5BbfI0 zhLx{2K~I(;MJ4V0Bo=ZWDVV0^7<6tLSNofxM{2Huc7%#a25JTcsw|Zojg4Z#pNDK_ zfC_O|ygCS}u$d{JP5;=g*apG+NWpS@oJ%oJkIjrJqc#mmrj**@JO8YV3A%QkO=F6V zr6N`jRyLZunXot#~#QC>i!Ay1_q`pKF| zrEt!9#2TJmxz(+J*hTk|g%u42FA(-&vdzBN?8qlSdE`7#d4yvbPr>@e;iCJ9x=)?^ zT&@U;KFeRY&sk66WsJP9af-6q3SKT3-owJCY`Qp9*JY-&xu)`rO7%$bUDm@!e*%ma zKB*A4_3SXsWY1G)#={DJm62aN!i8^B*6ca$`V@ki&|Qnuh=Eq_ijYs&PpG6(*-%V+ z|Ct|O(F1{vkL97wE8m)do#~IygJ-IxO?x2YMnBeddKe$RQvPnKoj0DYPisa@Dt|1? z$qpP!$0?sIk#A!;=L~>0cST0Y)2_z%Slb_TKFc{~Ai%;T|1+kwM*7b&Z2&up7mq9R zMLHFg_fn57{U6TKk2g+e!ysx!qrhH__~+c(ng~aDV?>5`vrmL=VuDEPt~b=O?1%cfBD+u@4!vME|fn*Sbh5Yqa^b070hox zn4C(lCq@D8XY?(b%=Dq{1Nb6nz20O9O5sL|8783i@+=$Htk(Ha}P#` zAjIG&G!O;Ih+G1JMXVxO=oulvHhRjWi6r=*L_>{8l|KVxaOVw}86poaNu5I41}`%n zoNsBLWC4MJx;QKF-O(Yx0~}+)K~59FI_8oeOE&;m3W}W@8kJSI!XQnUS-2s@UoT;q zbwhC4WcR{-1XRfal`t|;8X%EF;!{KEf=$dwF>pZapAk^MlIs>rAK-ibtb(Jsf-AI44AMC zG=CK^CQjXUXnYhqkEKE9^T7ul_U{veiU=dnzqJCtYL4I2d?EFR%QR+#BVzJuy99sh zs`_pf(^==i@}1CGqV&p_yZQqTJl;9B;EYG5>KG;H%*P4C0x`lv^-DSp#Y z-1E%P*PF!&u06qe>0+~asCYRF0N$WCga_Apx|UlN^?n|pJncb6GWDy=!Q)=uBNZVD zp{u)m5Q9sioMy#x{=o8^MwYRHlz3rQlll1lIsHr=#xDOtTIQdi8&>wh(3XF_rEgm= zw@Sm0rdcPc737sUZzmIbH&vc8(<~Qc$27V8aDSOY!J6DAb^Pe3q3QzFs|fg=b{-qV z>ohq#BdaZ4u2u*M?q7D?U-7<1;DiPR#tsh?{=-8{v7z^A7;S4i)%D?iP!X&YAykt$ zh6?sh3xR`y;iG+$5LKHh-cF2LQshoNSxMljK-3ZIVE!IIYAZptF|h&IfZrq`zIwbS zW2iv<4JX8N4q7?dV7@C~>JxM_UMdH7meBi+1m$d>pbilp>PG)`l+ovZbka-fL$!C% z!41Exs1`HlVUMc$r|eWFiTeWYn-18c7oZ>6t?XCt$+$w1kuy)Kva0(UyE;?unZg#w zAGLH%7FUs2;>_E9uuV885TGcJguE}s=I$BEc>{^Q=kcvqt>3f}UDK>K*A;hO^?tH3 zc)Ay3WQ@MeIh8ng_$JH`(*qzHlvhN}wNMB==a2TJ`&}HJ=;-II-Z~0=? z_5qIZm`;I8oD$RrLfl?XtL>TjWp1X|Mj0>%c~L@i85b;LjVOmHrK0=iL`coKumI_Kn;Zn!(fV7PrQ<4=M74O^zgGPBbR~`2)Y-R^z_x&m(CPHh;?uoth3_^QB|Kg)4C#Hf{0_;1UU*P&UJA~ zgW%13>(W|h9g^Rvl6-_5b)QNo!zxShM(G=y(BxD^-QgAQGV-X8_ zkMAen2n+Vk&D%l;I;{ruSD0seh88-DFKH>5V}fcL8Y?WQXef05x#qTx^S{>IG(q^R z3gyF?j5=c>hV1m8@tN^o4%<){c{YIeiiL{1ALd(tj!Y2CMR2rlivIqX5+(LARm zZXG?WPPvllKjR3%w>)s0a5wS9(h=+CmyE_KWsQ@orn7`mr$(K2hcd2g$_?CxMM7Tu z>!vms9sv*$EtM~P%4U$YFPLLGP9jtTi(5KJ@>u#k;u# zc>7e`OZ0AtcW(yiDk#&zDN8O5jLP5ysCUg{db)O0MsHD$!bAd zJZDJ3?*&!6%}Q& z?Kq;n2(wQY$04*8G-@4uRdMxb(R@(>Y{m)}p3Jes>O+lf0aaQXV${9yDk~WvgudzYIQSqnqtL5|fWegqxkHlb z7ws(g-iq+o3H_sL$Wz(CF&85Y8k$(y6eZK~}-cUgIeTzX-5^QSo_v=a#fn?Ku z>RspYQO4PFskE^^K5liX>7^Q}2u-?Rj{noxmB&N*egDB2+mOho5g9w5jD4~*)*9KO zg(O5F6hgMi%w*3_$`&S}M7FHa3_|v0Ldq^g*|#y6`91o+e!qWz-}&pg^E~&SJNKSB z=iGDN=REr(vx4yuQFVT3N?yMUq|hjBUZYySpmV~?)@|V5CzwZ7G{8Z4Bfm8qts4zM zprI}bf#=n(dOzN&WXnsHI9kj=?I`6u>Rfy!fUTLAlxLP%cCfY+z;$NXB;M9Qz#Fb* zp8wFYLoB0S-dGt-*s` zrSeJR)skN4_sJ0!@5l0QOy<^l4(v8dm5ax*DutchRdu^QF%*+j<8(ZmRzM)iZ*;GHiZC z-voi>u(|fT9&x5A_puFX-0Q|y$KD|`&qsco4C$z}B~tC12J`waHO`o#Q(Tv?tu^g` z6eu2sxNT%2I^$`MDdWxu>!~!FMTn6HH2BCi|91TKh1K^5x8`;Ryx|V3emX6=&96^5 z>YuCq!D&L{;f>gqan*=75&&?NEo|aH7Z;;)JMP0)ui$k_bCwQA)L5ozsG$(oME5~K zOia#>{10|=UF^|XnL33>Yg93&J?^JS%DHF%R*Ka64)6w{qM~dKU*+}lW@2IxDtypE+H zRLQMpaVG@@K2ecQF1ZS6%s{cYp}J7E-j416^}V}Z2ib}NXM?i;{%qipUPPt6eW>as z-zEOu{s-vxm|tLA5tU@T%8;$;6YgOujry9-%?=gD8=2}VIN!Z0rB+`zEEiOyspq1?C^HY3QBRw*_@5XYZ1=vB4_q~er+A-k3x#j( zyKc5SNCnh}9WXL7%_&GLY^+xsp(KTZ8GZwZz;IHyzZHj7?7h?MnG9WeXHWU`_unOQ zkxb_F0)?6C!1f$_fs!beFh=G3Z?@cFnTZ*rRuu}s*8UTXBOxKumKlVHjHsj;96E{r z%!wiWj*e+Re3DZ|>M=A;GCyn-FsQ_Ywc_z|d4Ex(UxoDWF;69gL_zgyq?F1Nqqya*T>cu)4=Rg#~$X zwL^58rh{EkcxmFroKt&~f?8JseoCDtlz`Jqa?NVeCGFXIwxd`96zum@8+maBDX^1! zkEe-%oNO*`dhO+TFc&;6LwDH2+A))lFv0@fWYaz6?gy9Ij!!G)G zt)+3_A(a$xv(+gdyk$XuOsJYKJ?k}_GA8Y}fMP#N-*-)r5XCc#D;UIghCHzgO2kZR z{y1Y|7F2)o9&r;Lxk+rK@ZPiX%dm9SAw<0tQO-TEGMLE9|G{3BW^wv`BpZCb@Nlih zKKhII_&vQ{UTTQve4C4~H zcWwoI_|E?uC~P+an@mg`NUrs7Ya{3rckXJlLInpQ^ZM5@I13%8_B90Wr>3zv=Ezd! zu{qu(t<>8Ac>D!@wIN*Zq^;wZSaetsSrR~~eXt~m@Yq0)SI#@+d+Zmcf}cmhvpBXx zw?o6JEzywKq=8 z9^X#7^I-l*7AmB|YUNwi%DlmN!C?X~wqhhyaVD#yPMMbHzAaCu5OeL@I0w#ZZOm?& z5pVd5md;_l?6OSF>{uKQXGSraTG$r>ZL3b_v&p^#i|+o>W@j4e`gW>Lhf*AFa(dA{ zJ1(3jCWAz3b_=gz15ye*`j-H7mJ79HfQJpi$f!J=J}7xHjeh{DW~I%!bNU%}XXu9v z$!9%LGM!>Q>tFxU!1|qq!&S+r7f01CkaJEt?(}E9x0)9DTTzWJl38#SV`Go*`Rcad zI=}J>%X;IZ?JN9I3EMxl(}>TL_H?3^f*&oc&~1#_WvrQt>1$DI)#%9uaJz3|%?z}D zabMQq+tvzww>@@LSoM0UUt!IznEy;wgnMfHR(p(XmIl3kwx>&0pzqXT7zyL|X{Nqb zyFWA^leb}GE|xM`JS@Nw+v%}@4LT)6^=+hL%Z}pUTJM5Z4%5tT zj_`SkUrf-xnHOP#I=K66=_oVRwXuZokZ*s?W7wZr)816FuOGo_?jli-S*ZF`R?^^_ zm_vr$yyCwvulLGoH~Vr3D$t`NnJ)Z#EoQc<@S}250ti+<7Fnj&%aWM=peZX`56{1n zzeES3bpN*@*@1+AE_^y<6d`v^z@cP!&4N+AM+}wb(u+cx$v-=%#ePnWnz(HT)lMmY28;nB+%d zMVNoKD~T2{MFQO#+6>8=cy6(>?n?y&-jt;(eG~C=R@R1Gf8s?hyfm zSaLKvSGJJU!^zjTweI75L%s&Xo@=dBrweZvypi0)_kC+Qii@%}$YM4=MiIMn-Fb<@ z;qEOT8kxEIBE6z>A=PvIYN)qY#lK@ymk$oXi(f?%ovZg@q=n zDwaWpu`$6S7pvYi`{YfhqLLwyn($Zs7w8I$piop?e?*8->`-WRj+#dP=crdt7+%qR z`x8fed|_m5j2FghEuqFw^y$$D`qdjvn|o8(>+rw_KTgnZpX*-#y^eu?C673Lf(dB` z!EzEUW1F~qU*3QCQNr&$ee-5&tqKfEJ)(X1@WH$4(oQP(UFpte4 z*yW8PnNevNO#5t#Q9LbY@gY9WC%8=QmU%}}|158EvfJ#2;G*+vg>|_(+Odyy{9Zi1 z;O-jya4h1F>lV4_hU*XPr|P-KY?eLOT|8Y^u~eWUgnkvX6u_QO0vN7?*m|V!>H{$2 zA(|J~m3RPQGjAo})nP9D$wevQo%Z6&xrXH>>Jq@?*|Tju$v~BhI`SDNdFe1!=~jkB zbju>gG04H?0{DeM$ENFO~DC>{{zU=(`POZFM zKJbm&u8k6p4G8e8a)2KHdpahx$7=Pl=K+s6fIa|gp3RB_1140u6mWkFd?uCv_AY@E z+0c&0$G`)tiTHRUMgYFhBg61DJ*X`{0&tY$z|T(sEs5a?4)QlJEm~UB{Z2S%jwrxW ze!wXDYkIf8qE6bkXw%YR$~27}pbL<(^BJ?Y>EtF@Bcj8K>t=lVaA$e=X6*yc6DP=5 zqVc+ztK1%V08(uSP|c93O!rb_D3%sYj&_II>EqcJ!)}Y0+W?MW#7Itj{v%6uJYa908sAHPwse_31$6vN=)Z4}hD3#JZF}2B{?5q7oODNNX@@%B{lcOC z;oXI|Yc}i!R!cjBhNF?Fl?9q^+NmW5AOEs=Eld!&u*m6uW^L(QXc!JTrf9_IozH^b@36)` zYFv^FerI-VY7wqMyuNP{6-t}4whx*gya;EO^ZwZ1)y2;6n#~6j1TZE8>m>QLK3VS!Y{SnZVYPq7d^XpNZys&<}6uo0>pH z?kbjpzk`nk<#_HKTyP_f^qh}&2xQ@~M?-0y|eMCJ6L?$8qu zK)3_@KF8)c(ZDB4YqG~AZuAEus@GeIi*uR2j4cg7aB|zI1mB%4X z-=8g5%*E2AQ2T6d&|Y;dK3G&N`yH-}1_lOSAe+#Ld&0#e!*8Y!mmk?bc~=}FL^$J1 z$Et&h@{sS~)GP}+#R3V<6*fAtrp2_u&2`qpgf@C0wHNU+R36cDYuQ~>@Tp!r8-etf zC4saC-^IScsWq9^fAP785ATj#j7Qle@34+fH3u;8gYfl%8&3ZI{?hyuIq$E-!_5-j zb}drYx>AmU&`t21K@?YnA>$?koEh;p3CEEAM5fAp_*|weG(pct*mH*K%A3z)%n-G` z`H%roG*<#^pse4v=lu=3Z`-CO44GOq$uuco`X`KJL#KS_Gm(`=XTL^_(|%XCO?@1H z6y6R>y?l1$Ii{Ziyh`3#Ul+(ZJC-<^IP5RTc*4=-@08bHSv|O6-k0^0g<*MM8SD4q zuyu3&Ppr_7&2f*>;=1Oda(G{Z^N?A+P)9A!$_OT&Q2N1C)VMITh4|0<_7WV+4pdT-O!iq}#uHY#!kdK_JRL!tE!)35si=HC zMn-6`phm?1nw`MJU}B(@={?%XzL}^-!y2fh=JX@X#Gs0c;x$&V7&4Ku=4(t0+E8j) z&qRMiSSl?o&2KZetLH@MdRWpFuhPetpkH9LBt=@`666WPCsvBwOEXl@7n(9fD%mHE zp6zYjC~u1eaP0W|PoDu#M{-%b zBI$|EEN}$`QNpj(5yBD~l_jQ2!Y{m^=4oPwB%M2Dk!oOjhOO)5YUbtpICR~a9Hu~p zB=uhI$|vuVo)(*jXAd0ayKqDdy*rtk2Y(00KrVFZo11>YErTgYB&6`ZD5P261SFcX z6=i&ua=MF!=|74^!GzKK1oN0r%qPQIq~xe^3?jE&3<-|IU*`5!H)eBuBA literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/images/telemetry-3.png b/docs/src/tutorials/images/telemetry-3.png new file mode 100644 index 0000000000000000000000000000000000000000..c235b1d543e5a2ac2b0a5cbfe168dc2ec4659f01 GIT binary patch literal 51914 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 YdfBfr63qAO7Bc{Wr>mdKI;Vst0Nm`%*#H0l literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/images/todo-edit.png b/docs/src/tutorials/images/todo-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..3c1ba3f5cc73089d259f8d047628686059dcb546 GIT binary patch literal 25216 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 literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/images/todo-restyled.png b/docs/src/tutorials/images/todo-restyled.png new file mode 100644 index 0000000000000000000000000000000000000000..9fd7008c2f22d8f040649e3104871c63c1a5363a GIT binary patch literal 25700 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 Z+itTPWnY5(1q%3)msXalkuVAVe*k%0+b#eA literal 0 HcmV?d00001 diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index 2920b27f05..88bedd19cd 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -123,7 +123,10 @@ __bundles.json__ ### Run a Web Server -The next step is to run a web server so that you can view the Open MCT Web client (including the plugins you add to it) in browser. The HTTP server option that is recommended here, for simplicity, is http-server, https://www.npmjs.com/package/http-server. +The next step is to run a web server so that you can view the Open MCT Web +client (including the plugins you add to it) in browser. The HTTP server option +that is recommended here, for simplicity, is http-server, +[https://www.npmjs.com/package/http-server](). To run: @@ -792,1977 +795,2243 @@ To support selection, we will need to make some changes to our controller: }); __tutorials/todo/src/controllers/TodoController.js__ - There are a few changes to pay attention to here. Let’s review them: +There are a few changes to pay attention to here. Let’s review them: -At the top, we describe the form that should be shown to the user when they click the Add Task button. This form is described declaratively, and populates an object that has the same format as tasks in the tasks array of our To-Do List’s model. -We’ve added an argument to the TodoController: The dialogService, which is exposed by the Open MCT Web platform to handle showing dialogs. -Some utility functions for handling the actual adding and removing of tasks. These use the mutation capability to modify the tasks in the To-Do List’s model. -Finally, we check for the presence of a selection object in our scope. This object is provided by Edit mode to manage current selections for editing. When it is present, we expose a selectTask function to our scope to allow selecting individual tasks; when this occurs, we expose an object to selection which has a removeTask method, as expected by the tool bar we’ve defined. We additionally expose a view proxy, to handle view-level changes (e.g. not associated with any specific selected object); this has an addTask method, which again is expected by the tool bar we’ve defined. +* At the top, we describe the form that should be shown to the user when they +click the _Add Task_ button. This form is described declaratively, and populates +an object that has the same format as tasks in the `tasks` array of our +To-Do List’s model. +* We’ve added an argument to the `TodoController`: The `dialogService`, which is +exposed by the Open MCT Web platform to handle showing dialogs. +* Some utility functions for handling the actual adding and removing of tasks. +These use the `mutation` capability to modify the tasks in the To-Do List’s +model. +* Finally, we check for the presence of a `selection` object in our scope. This +object is provided by Edit mode to manage current selections for editing. When +it is present, we expose a `selectTask` function to our scope to allow selecting +individual tasks; when this occurs, we expose an object to `selection` which has +a `removeTask` method, as expected by the tool bar we’ve defined. We additionally +expose a view proxy, to handle view-level changes (e.g. not associated with any +specific selected object); this has an `addTask` method, which again is expected +by the tool bar we’ve defined. - Additionally, we need to make changes to our template to select specific tasks in response to some user gesture. Here, we will select tasks when a user clicks the description. +Additionally, we need to make changes to our template to select specific tasks +in response to some user gesture. Here, we will select tasks when a user clicks +the description. -
-
- All - Incomplete - Complete +
+ + +
    +
  • + + + + {{task.description}} + + +
  • +
+__tutorials/todo/res/templates/todo.html__ -
    -
  • - - - {{task.description}} - -
  • -
-
-tutorials/todo/res/templates/todo.html +Finally, the `TodoController` uses the `dialogService` now, so we need to +declare that dependency in its extension definition: - Finally, the TodoController uses the dialogService now, so we need to declare that dependency in its extension definition: - -{ - "name": "To-do Plugin", - "description": "Allows creating and editing to-do lists.", - "extensions": { - "types": [ - { - "key": "example.todo", - "name": "To-Do List", - "glyph": "j", - "description": "A list of things that need to be done.", - "features": ["creation"], - "model": { - "tasks": [ - { "description": "Add a type", "completed": true }, - { "description": "Add a view" } - ] + { + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + "types": [ + { + "key": "example.todo", + "name": "To-Do List", + "glyph": "j", + "description": "A list of things that need to be done.", + "features": ["creation"], + "model": { + "tasks": [ + { "description": "Add a type", "completed": true }, + { "description": "Add a view" } + ] + } } - } - ], - "views": [ - { - "key": "example.todo", - "type": "example.todo", - "glyph": "j", - "name": "List", - "templateUrl": "templates/todo.html", - "toolbar": { - "sections": [ - { - "items": [ - { - "text": "Add Task", - "glyph": "+", - "method": "addTask", - "control": "button" - } - ] - }, - { - "items": [ - { - "glyph": "Z", - "method": "removeTask", - "control": "button" - } - ] - } - ] + ], + "views": [ + { + "key": "example.todo", + "type": "example.todo", + "glyph": "j", + "name": "List", + "templateUrl": "templates/todo.html", + "toolbar": { + "sections": [ + { + "items": [ + { + "text": "Add Task", + "glyph": "+", + "method": "addTask", + "control": "button" + } + ] + }, + { + "items": [ + { + "glyph": "Z", + "method": "removeTask", + "control": "button" + } + ] + } + ] + } } - } - ], - "controllers": [ - { - "key": "TodoController", - "implementation": "controllers/TodoController.js", - "depends": [ "$scope", "dialogService" ] - } - ] + ], + "controllers": [ + { + "key": "TodoController", + "implementation": "controllers/TodoController.js", + + "depends": [ "$scope", "dialogService" ] + } + ] + } } -} -tutorials/todo/bundle.json - - If we now reload Open MCT Web, we’ll be able to see the new functionality we’ve added. If we Create a new To-Do List, navigate to it, and click the button with the Pencil icon in the top-right, we’ll be in edit mode. We see, first, that our “Add Task” button appears in the tool bar: +__tutorials/todo/bundle.json__ +If we now reload Open MCT Web, we’ll be able to see the new functionality we’ve +added. If we Create a new To-Do List, navigate to it, and click the button with +the Pencil icon in the top-right, we’ll be in edit mode. We see, first, that our +“Add Task” button appears in the tool bar: +![Edit](images/todo-edit.png) If we click on this, we’ll get a dialog allowing us to add a new task: +![Add task](images/add-task.png) +Finally, if we click on the description of a specific task, we’ll see a new +button appear, which we can then click on to remove that task: - Finally, if we click on the description of a specific task, we’ll see a new button appear, which we can then click on to remove that task: +![Remove task](images/remove-task.png) - - - As always in Edit mode, the user will be able to Save or Cancel any changes they have made. +As always in Edit mode, the user will be able to Save or Cancel any changes they have made. In terms of functionality, our To-Do List can do all the things we want, but the appearance is still lacking. In particular, we can’t distinguish our current filter choice or our current selection state. -Step 6. Customizing Look and Feel +### Step 6. Customizing Look and Feel - In this section, our goal is to: +In this section, our goal is to: -Display the current filter choice. -Display the current task selection (when in Edit mode.) -Tweak the general aesthetics to our liking. -Get rid of those default tasks (we can create our own now.) +* Display the current filter choice. +* Display the current task selection (when in Edit mode.) +* Tweak the general aesthetics to our liking. +* Get rid of those default tasks (we can create our own now.) - To support the first two, we’ll need to expose some methods for checking these states in the controller: +To support the first two, we’ll need to expose some methods for checking these +states in the controller: -define(function () { - // Form to display when adding new tasks - var NEW_TASK_FORM = { - name: "Add a Task", - sections: [{ - rows: [{ - name: 'Description', - key: 'description', - control: 'textfield', - required: true + define(function () { + // Form to display when adding new tasks + var NEW_TASK_FORM = { + name: "Add a Task", + sections: [{ + rows: [{ + name: 'Description', + key: 'description', + control: 'textfield', + required: true + }] }] - }] - }; - - function TodoController($scope, dialogService) { - var showAll = true, - showCompleted; - - // Persist changes made to a domain object's model - function persist() { - var persistence = - $scope.domainObject.getCapability('persistence'); - return persistence && persistence.persist(); - } - - // Remove a task - function removeTaskAtIndex(taskIndex) { - $scope.domainObject.useCapability('mutation', function (model) { - model.tasks.splice(taskIndex, 1); - }); - persist(); - } - - // Add a task - function addNewTask(task) { - $scope.domainObject.useCapability('mutation', function (model) { - model.tasks.push(task); - }); - persist(); - } - - // Change which tasks are visible - $scope.setVisibility = function (all, completed) { - showAll = all; - showCompleted = completed; }; - - // Check if current visibility settings match - $scope.checkVisibility = function (all, completed) { - return showAll ? all : (completed === showCompleted); - }; - - // Toggle the completion state of a task - $scope.toggleCompletion = function (taskIndex) { - $scope.domainObject.useCapability('mutation', function (model) { - var task = model.tasks[taskIndex]; - task.completed = !task.completed; - }); - persist(); - }; - - // Check whether a task should be visible - $scope.showTask = function (task) { - return showAll || (showCompleted === !!(task.completed)); - }; - - // Handle selection state in edit mode - if ($scope.selection) { - // Expose the ability to select tasks - $scope.selectTask = function (taskIndex) { - $scope.selection.select({ - removeTask: function () { - removeTaskAtIndex(taskIndex); - $scope.selection.deselect(); - }, - taskIndex: taskIndex + + function TodoController($scope, dialogService) { + var showAll = true, + showCompleted; + + // Persist changes made to a domain object's model + function persist() { + var persistence = + $scope.domainObject.getCapability('persistence'); + return persistence && persistence.persist(); + } + + // Remove a task + function removeTaskAtIndex(taskIndex) { + $scope.domainObject.useCapability('mutation', function (model) { + model.tasks.splice(taskIndex, 1); }); - }; - - // Expose a check for current selection state - $scope.isSelected = function (taskIndex) { - return ($scope.selection.get() || {}).taskIndex === taskIndex; - }; - - // Expose a view-level selection proxy - $scope.selection.proxy({ - addTask: function () { - dialogService.getUserInput(NEW_TASK_FORM, {}) - .then(addNewTask); - } - }); - } - } - - return TodoController; -}); -tutorials/todo/src/controllers/TodoController.js - - A summary of these changes: - -checkVisibility has the same arguments as setVisibility, but instead of making a change, it simply returns a boolean true/false indicating whether those settings are in effect. The logic reflects the fact that the second parameter is ignored when showing all. -To support checking for selection, the index of the currently-selected task is tracked as part of the selection object. -Finally, an isSelected function is exposed which checks if the indicated task is currently selected, using the index from above. - - Additionally, we will want to define some CSS rules in order to reflect these states visually, and to generally improve the appearance of our view. We add another file to the res directory of our bundle; this time, it is css/todo.css (with the css directory again being a convention.) - -.example-todo div.example-button-group { - margin-top: 12px; - margin-bottom: 12px; -} - -.example-todo .example-button-group a { - padding: 3px; - margin: 3px; -} - -.example-todo .example-button-group a.selected { - border: 1px gray solid; - border-radius: 3px; - background: #444; -} - -.example-todo .example-task-completed .example-task-description { - text-decoration: line-through; - opacity: 0.75; -} - -.example-todo .example-task-description.selected { - background: #46A; - border-radius: 3px; -} - -.example-todo .example-message { - font-style: italic; -} -tutorials/todo/res/css/todo.css - - Here, we have defined classes and appearances for: - -Our filter choosers (example-button-group). -Our selected and/or completed tasks (example-task-description). -A message, which we will add next, to display when there are no tasks (example-message). - - To include this CSS file in our running instance of Open MCT Web, we need to declare it in our bundle definition, this time as an extension of category stylesheets: - - -{ - "name": "To-do Plugin", - "description": "Allows creating and editing to-do lists.", - "extensions": { - "types": [ - { - "key": "example.todo", - "name": "To-Do List", - "glyph": "j", - "description": "A list of things that need to be done.", - "features": ["creation"], - "model": { - "tasks": [] - } + persist(); } - ], - "views": [ - { - "key": "example.todo", - "type": "example.todo", - "glyph": "j", - "name": "List", - "templateUrl": "templates/todo.html", - "toolbar": { - "sections": [ - { - "items": [ - { - "text": "Add Task", - "glyph": "+", - "method": "addTask", - "control": "button" - } - ] + + // Add a task + function addNewTask(task) { + $scope.domainObject.useCapability('mutation', function (model) { + model.tasks.push(task); + }); + persist(); + } + + // Change which tasks are visible + $scope.setVisibility = function (all, completed) { + showAll = all; + showCompleted = completed; + }; + + // Check if current visibility settings match + $scope.checkVisibility = function (all, completed) { + return showAll ? all : (completed === showCompleted); + }; + + // Toggle the completion state of a task + $scope.toggleCompletion = function (taskIndex) { + $scope.domainObject.useCapability('mutation', function (model) { + var task = model.tasks[taskIndex]; + task.completed = !task.completed; + }); + persist(); + }; + + // Check whether a task should be visible + $scope.showTask = function (task) { + return showAll || (showCompleted === !!(task.completed)); + }; + + // Handle selection state in edit mode + if ($scope.selection) { + // Expose the ability to select tasks + $scope.selectTask = function (taskIndex) { + $scope.selection.select({ + removeTask: function () { + removeTaskAtIndex(taskIndex); + $scope.selection.deselect(); }, - { - "items": [ - { - "glyph": "Z", - "method": "removeTask", - "control": "button" - } - ] - } - ] - } + taskIndex: taskIndex + }); + }; + + // Expose a check for current selection state + $scope.isSelected = function (taskIndex) { + return ($scope.selection.get() || {}).taskIndex === taskIndex; + }; + + // Expose a view-level selection proxy + $scope.selection.proxy({ + addTask: function () { + dialogService.getUserInput(NEW_TASK_FORM, {}) + .then(addNewTask); + } + }); } - ], - "controllers": [ - { - "key": "TodoController", - "implementation": "controllers/TodoController.js", - "depends": [ "$scope", "dialogService" ] - } - ], - "stylesheets": [ - { - "stylesheetUrl": "css/todo.css" - } - ] - } -} -tutorials/todo/bundle.json - - Note that we’ve also removed our placeholder tasks from the model of the To-Do List’s type above; now To-Do Lists will start off empty. - - Finally, let’s utilize these changes from our view’s template: - - -
- - -
    -
  • - - - {{task.description}} - -
  • -
- -
- There are no tasks to show. -
-
-tutorials/todo/res/templates/todo.html - - Now, if we reload our page and create a new To-Do List, we will initially see: - - - - If we then go into Edit mode, add some tasks, and select one, it will now be much clearer what the current selection is (e.g. before we hit the remove button in the toolbar): - - - -Bar Graph - - In this tutorial, we will look at creating a bar graph plugin for visualizing telemetry data. Specifically, we want some bars that raise and lower to match the observed state of real-time telemetry; this is particularly useful for monitoring things like battery charge levels. - It is recommended that the reader completes (or is familiar with) the To-Do List tutorial before completing this tutorial, as certain concepts discussed there will be addressed in more brevity here. - -Step 1. Define the View - - Since the goal is to introduce a new view and expose it from a plugin, we will want to create a new bundle which declares an extension of category views. We’ll also be defining some custom styles, so we’ll include that extension as well. We’ll be creating this plugin in tutorials/bargraph, so our initial bundle definition looks like: - -{ - "name": "Bar Graph", - "description": "Provides the Bar Graph view of telemetry elements.", - "extensions": { - "views": [ - { - "name": "Bar Graph", - "key": "example.bargraph", - "glyph": "H", - "templateUrl": "templates/bargraph.html", - "needs": [ "telemetry" ], - "delegation": true - } - ], - "stylesheets": [ - { - "stylesheetUrl": "css/bargraph.css" - } - ] - } -} -tutorials/bargraph/bundle.json - - The view definition should look familiar after the To-Do List tutorial, with some additions: - -The needs property indicates that this view is only applicable to domain objects with a telemetry capability. This ensures that this view is available for telemetry points, but not for other objects (like folders.) -The delegation property indicates that the above constraint can be satisfied via capability delegation; that is, by domain objects which delegate the telemetry capability to their contained objects. This allows this view to be used for Telemetry Panel objects as well as for individual telemetry-providing domain objects. - - For this tutorial, we’ll assume that we’ve sketched out our template and CSS file ahead of time to describe the general look we want for the view. These look like: - - -
-
-
High
-
Middle
-
Low
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
- Label A -
-
- Label B -
-
- Label C -
-
-
-tutorials/bargraph/res/templates/bargraph.html - - Here, three regions are defined. The first will be for tick labels along the vertical axis, showing the numeric value that certain heights correspond to. The second will be for the actual bar graphs themselves; three are included here. The third is for labels along the horizontal axis, which will indicate which bar corresponds to which telemetry point. Inline style attributes are used wherever dynamic positioning (handled by a script) is anticipated. - The corresponding CSS file which styles and positions these elements: - -.example-bargraph { - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - mid-width: 160px; - min-height: 160px; -} - -.example-bargraph .example-tick-labels { - position: absolute; - left: 0; - top: 24px; - bottom: 32px; - width: 72px; - font-size: 75%; -} - -.example-bargraph .example-tick-label { - position: absolute; - right: 0; - height: 1em; - margin-bottom: -0.5em; - padding-right: 6px; - text-align: right; -} - -.example-bargraph .example-graph-area { - position: absolute; - border: 1px gray solid; - left: 72px; - top: 24px; - bottom: 32px; - right: 0; -} - -.example-bargraph .example-bar-labels { - position: absolute; - left: 72px; - bottom: 0; - right: 0; - height: 32px; -} - -.example-bargraph .example-bar-holder { - position: absolute; - top: 0; - bottom: 0; -} - -.example-bargraph .example-graph-tick { - position: absolute; - width: 100%; - height: 1px; - border-bottom: 1px gray dashed; -} - -.example-bargraph .example-bar { - position: absolute; - background: darkcyan; - right: 4px; - left: 4px; -} - -.example-bargraph .example-label { - text-align: center; - font-size: 85%; - padding-top: 6px; -} -tutorials/bargraph/res/css/bargraph.css - - This is already enough that, if we add “tutorials/bargraph” to bundles.json, we should be able to run Open MCT Web and see our Bar Graph as an available view for domain objects which provide telemetry (such as the example Sine Wave Generator) as well as for Telemetry Panel objects: - - - This means that our remaining work will be to populate and position these elements based on the actual contents of the domain object. - -Step 2. Add a Controller - - Our next step will be to begin dynamically populating this template’s contents. Specifically, our goals for this step will be to: - -Show one bar per telemetry-providing domain object (for which we’ll be getting actual telemetry data in subsequent steps.) -Show correct labels for these objects at the bottom. -Show numeric labels on the left-hand side. - - Notably, we will not try to show telemetry data after this step. - - To support this, we will add a new controller which supports our Bar Graph view: - - -define(function () { - function BarGraphController($scope, telemetryHandler) { - var handle; - - // Add min/max defaults - $scope.low = -1; - $scope.middle = 0; - $scope.high = 1; - - // Convert value to a percent between 0-100, keeping values in points - $scope.toPercent = function (value) { - var pct = 100 * (value - $scope.low) / ($scope.high - $scope.low); - return Math.min(100, Math.max(0, pct)); - }; - - // Use the telemetryHandler to get telemetry objects here - handle = telemetryHandler.handle($scope.domainObject, function () { - $scope.telemetryObjects = handle.getTelemetryObjects(); - $scope.barWidth = - 100 / Math.max(($scope.telemetryObjects).length, 1); - }); - - // Release subscriptions when scope is destroyed - $scope.$on('$destroy', handle.unsubscribe); - } - - return BarGraphController; -}); -tutorials/bargraph/src/controllers/BarGraphController.js - - A summary of what we’ve done here: - -We’re exposing some numeric values that will correspond to the low, middle, and high end of the graph. (The medium attribute will be useful for positioning the middle line, which are graphs will ultimately descend down or push up from.) -Add a utility function which converts from numeric values to percentages. This will help support some positioning in the template. -Utilize the telemetryHandler, provided by the platform, to start listening to real-time telemetry updates. This will deal with most of the complexity of dealing with telemetry (e.g. differentiating between individual telemetry points and telemetry panels, monitoring latest values) and provide us with a useful interface for populating our view. The the Open MCT Web Developer Guide for more information on dealing with telemetry. - - Whenever the telemetry handler invokes its callbacks, we update the set of telemetry objects in view, as well as the width for each bar. - - We will also utilize this from our template: - -
-
-
- {{value}} -
-
- -
-
-
-
-
-
-
-
- -
-
- - -
-
-
-tutorials/bargraph/res/templates/bargraph.html - - Summarizing these changes: - -Utilize the exposed low, middle, and high values to populate our labels along the vertical axis. Additionally, use the toPercent function to position these from the bottom. -Replace our three hard-coded bars with a repeater that looks at the telemetryObjects exposed by the controller and adds one bar each. -Position the dashed tick-line using the middle value and the toPercent function, lining it up with its label to the left. -At the bottom, repeat a set of labels for the telemetry-providing domain objects, with matching alignment to the bars above. We use an existing representation, label, to make this easier. - - Finally, we expose our controller from our bundle definition. Note that the depends declaration includes both $scope as well as the telemetryHandler service we made use of. - - -{ - "name": "Bar Graph", - "description": "Provides the Bar Graph view of telemetry elements.", - "extensions": { - "views": [ - { - "name": "Bar Graph", - "key": "example.bargraph", - "glyph": "H", - "templateUrl": "templates/bargraph.html", - "needs": [ "telemetry" ], - "delegation": true - } - ], - "stylesheets": [ - { - "stylesheetUrl": "css/bargraph.css" - } - ], - "controllers": [ - { - "key": "BarGraphController", - "implementation": "controllers/BarGraphController.js", - "depends": [ "$scope", "telemetryHandler" ] - } - ] - } -} -tutorials/bargraph/bundle.json - - When we reload Open MCT Web, we are now able to see that our bar graph view correctly labels one bar per telemetry-providing domain object, as shown for this Telemetry Panel containing four Sine Wave Generators. - - - -Step 3. Using Telemetry Data - - Now that our bar graph is labeled correctly, it’s time to start putting data into the view. - - First, let’s add expose some more functionality from our controller. To make it simple, we’ll expose the top and bottom for a bar graph for a given telemetry-providing domain object, as percentages. - - -define(function () { - function BarGraphController($scope, telemetryHandler) { - var handle; - - // Add min/max defaults - $scope.low = -1; - $scope.middle = 0; - $scope.high = 1; - - // Convert value to a percent between 0-100, keeping values in points - $scope.toPercent = function (value) { - var pct = 100 * (value - $scope.low) / ($scope.high - $scope.low); - return Math.min(100, Math.max(0, pct)); - }; - - // Get bottom and top (as percentages) for current value - $scope.getBottom = function (telemetryObject) { - var value = handle.getRangeValue(telemetryObject); - return $scope.toPercent(Math.min($scope.middle, value)); } - $scope.getTop = function (telemetryObject) { - var value = handle.getRangeValue(telemetryObject); - return 100 - $scope.toPercent(Math.max($scope.middle, value)); - } + + return TodoController; + }); +__tutorials/todo/src/controllers/TodoController.js__ - // Use the telemetryHandler to get telemetry objects here - handle = telemetryHandler.handle($scope.domainObject, function () { - $scope.telemetryObjects = handle.getTelemetryObjects(); - $scope.barWidth = - 100 / Math.max(($scope.telemetryObjects).length, 1); - }); +A summary of these changes: - // Release subscriptions when scope is destroyed - $scope.$on('$destroy', handle.unsubscribe); +* `checkVisibility` has the same arguments as `setVisibility`, but instead of +making a change, it simply returns a boolean true/false indicating whether those +settings are in effect. The logic reflects the fact that the second parameter is +ignored when showing all. +* To support checking for selection, the index of the currently-selected task is +tracked as part of the selection object. +* Finally, an isSelected function is exposed which checks if the indicated task +is currently selected, using the index from above. + +Additionally, we will want to define some CSS rules in order to reflect these +states visually, and to generally improve the appearance of our view. We add +another file to the res directory of our bundle; this time, it is `css/todo.css` +(with the `css` directory again being a convention.) + + .example-todo div.example-button-group { + margin-top: 12px; + margin-bottom: 12px; } + + .example-todo .example-button-group a { + padding: 3px; + margin: 3px; + } + + .example-todo .example-button-group a.selected { + border: 1px gray solid; + border-radius: 3px; + background: #444; + } + + .example-todo .example-task-completed .example-task-description { + text-decoration: line-through; + opacity: 0.75; + } + + .example-todo .example-task-description.selected { + background: #46A; + border-radius: 3px; + } + + .example-todo .example-message { + font-style: italic; + } +__tutorials/todo/res/css/todo.css__ - return BarGraphController; -}); -tutorials/bargraph/src/controllers/BarGraphController.js +Here, we have defined classes and appearances for: - The telemetryHandler exposes a method to provide us with our latest data value (the getRangeValue method), and we already have a function to convert from a numeric value to a percentage within the view, so we just use those. The only slight complication is that we want our bar to move up or down from the middle value, so either of our top or bottom position for the bar itself could be either the middle line, or the data value. We let Math.min and Math.max decide this. +* Our filter choosers (`example-button-group`). +* Our selected and/or completed tasks (`example-task-description`). +* A message, which we will add next, to display when there are no tasks +(`example-message`). - Next, we utilize this functionality from the template: +To include this CSS file in our running instance of Open MCT Web, we need to +declare it in our bundle definition, this time as an extension of category +`stylesheets`: + + { + "name": "To-do Plugin", + "description": "Allows creating and editing to-do lists.", + "extensions": { + "types": [ + { + "key": "example.todo", + "name": "To-Do List", + "glyph": "j", + "description": "A list of things that need to be done.", + "features": ["creation"], + "model": { + "tasks": [] + } + } + ], + "views": [ + { + "key": "example.todo", + "type": "example.todo", + "glyph": "j", + "name": "List", + "templateUrl": "templates/todo.html", + "toolbar": { + "sections": [ + { + "items": [ + { + "text": "Add Task", + "glyph": "+", + "method": "addTask", + "control": "button" + } + ] + }, + { + "items": [ + { + "glyph": "Z", + "method": "removeTask", + "control": "button" + } + ] + } + ] + } + } + ], + "controllers": [ + { + "key": "TodoController", + "implementation": "controllers/TodoController.js", + "depends": [ "$scope", "dialogService" ] + } + ], + + "stylesheets": [ + + { + + "stylesheetUrl": "css/todo.css" + + } + + ] + } + } +__tutorials/todo/bundle.json__ -
-
-
- {{value}} +Note that we’ve also removed our placeholder tasks from the `model` of the +To-Do List’s type above; now To-Do Lists will start off empty. + +Finally, let’s utilize these changes from our view’s template: + + +
+ +
+ + All + + Incomplete + + Complete +
+ +
    +
  • + + + {{task.description}} + +
  • +
+ +
+ + There are no tasks to show. + +
+ +
+__tutorials/todo/res/templates/todo.html__ + +Now, if we reload our page and create a new To-Do List, we will initially see: + +![Todo Restyled](images/todo-restyled.png) + +If we then go into Edit mode, add some tasks, and select one, it will now be +much clearer what the current selection is (e.g. before we hit the remove button +in the toolbar): + +![Todo Restyled](images/todo-selection.png) + +## Bar Graph + +In this tutorial, we will look at creating a bar graph plugin for visualizing +telemetry data. Specifically, we want some bars that raise and lower to match +the observed state of real-time telemetry; this is particularly useful for +monitoring things like battery charge levels. +It is recommended that the reader completes (or is familiar with) the To-Do +List tutorial before completing this tutorial, as certain concepts discussed +there will be addressed in more brevity here. + +### Step 1. Define the View + +Since the goal is to introduce a new view and expose it from a plugin, we will +want to create a new bundle which declares an extension of category `views`. +We’ll also be defining some custom styles, so we’ll include that extension as +well. We’ll be creating this plugin in `tutorials/bargraph`, so our initial +bundle definition looks like: + + { + "name": "Bar Graph", + "description": "Provides the Bar Graph view of telemetry elements.", + "extensions": { + "views": [ + { + "name": "Bar Graph", + "key": "example.bargraph", + "glyph": "H", + "templateUrl": "templates/bargraph.html", + "needs": [ "telemetry" ], + "delegation": true + } + ], + "stylesheets": [ + { + "stylesheetUrl": "css/bargraph.css" + } + ] + } + } +__tutorials/bargraph/bundle.json__ + +The view definition should look familiar after the To-Do List tutorial, with +some additions: + +* The `needs` property indicates that this view is only applicable to domain +objects with a `telemetry` capability. This ensures that this view is available +for telemetry points, but not for other objects (like folders.) +* The `delegation` property indicates that the above constraint can be satisfied +via capability delegation; that is, by domain objects which delegate the +`telemetry` capability to their contained objects. This allows this view to be +used for Telemetry Panel objects as well as for individual telemetry-providing +domain objects. + +For this tutorial, we’ll assume that we’ve sketched out our template and CSS +file ahead of time to describe the general look we want for the view. These +look like: + +
+
+
High
+
Middle
+
Low
-
- -
-
-
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ +
+
+ Label A +
+
+ Label B +
+
+ Label C +
+__tutorials/bargraph/res/templates/bargraph.html__ -
-
- - +Here, three regions are defined. The first will be for tick labels along the +vertical axis, showing the numeric value that certain heights correspond to. The +second will be for the actual bar graphs themselves; three are included here. +The third is for labels along the horizontal axis, which will indicate which +bar corresponds to which telemetry point. Inline `style` attributes are used +wherever dynamic positioning (handled by a script) is anticipated. +The corresponding CSS file which styles and positions these elements: + + .example-bargraph { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + mid-width: 160px; + min-height: 160px; + } + + .example-bargraph .example-tick-labels { + position: absolute; + left: 0; + top: 24px; + bottom: 32px; + width: 72px; + font-size: 75%; + } + + .example-bargraph .example-tick-label { + position: absolute; + right: 0; + height: 1em; + margin-bottom: -0.5em; + padding-right: 6px; + text-align: right; + } + + .example-bargraph .example-graph-area { + position: absolute; + border: 1px gray solid; + left: 72px; + top: 24px; + bottom: 32px; + right: 0; + } + + .example-bargraph .example-bar-labels { + position: absolute; + left: 72px; + bottom: 0; + right: 0; + height: 32px; + } + + .example-bargraph .example-bar-holder { + position: absolute; + top: 0; + bottom: 0; + } + + .example-bargraph .example-graph-tick { + position: absolute; + width: 100%; + height: 1px; + border-bottom: 1px gray dashed; + } + + .example-bargraph .example-bar { + position: absolute; + background: darkcyan; + right: 4px; + left: 4px; + } + + .example-bargraph .example-label { + text-align: center; + font-size: 85%; + padding-top: 6px; + } +__tutorials/bargraph/res/css/bargraph.css__ + +This is already enough that, if we add `“tutorials/bargraph”` to `bundles.json`, +we should be able to run Open MCT Web and see our Bar Graph as an available view +for domain objects which provide telemetry (such as the example +_Sine Wave Generator_) as well as for _Telemetry Panel_ objects: + +![Bar Plot](images/bar-plot.png) + +This means that our remaining work will be to populate and position these +elements based on the actual contents of the domain object. + +### Step 2. Add a Controller + +Our next step will be to begin dynamically populating this template’s contents. +Specifically, our goals for this step will be to: + +* Show one bar per telemetry-providing domain object (for which we’ll be getting +actual telemetry data in subsequent steps.) +* Show correct labels for these objects at the bottom. +* Show numeric labels on the left-hand side. + +Notably, we will not try to show telemetry data after this step. + +To support this, we will add a new controller which supports our Bar Graph view: + + define(function () { + function BarGraphController($scope, telemetryHandler) { + var handle; + + // Add min/max defaults + $scope.low = -1; + $scope.middle = 0; + $scope.high = 1; + + // Convert value to a percent between 0-100, keeping values in points + $scope.toPercent = function (value) { + var pct = 100 * (value - $scope.low) / ($scope.high - $scope.low); + return Math.min(100, Math.max(0, pct)); + }; + + // Use the telemetryHandler to get telemetry objects here + handle = telemetryHandler.handle($scope.domainObject, function () { + $scope.telemetryObjects = handle.getTelemetryObjects(); + $scope.barWidth = + 100 / Math.max(($scope.telemetryObjects).length, 1); + }); + + // Release subscriptions when scope is destroyed + $scope.$on('$destroy', handle.unsubscribe); + } + + return BarGraphController; + }); +__tutorials/bargraph/src/controllers/BarGraphController.js__ + +A summary of what we’ve done here: + +* We’re exposing some numeric values that will correspond to the _low_, _middle_, +and _high_ end of the graph. (The `medium` attribute will be useful for +positioning the middle line, which are graphs will ultimately descend down or +push up from.) +* Add a utility function which converts from numeric values to percentages. This +will help support some positioning in the template. +* Utilize the `telemetryHandler`, provided by the platform, to start listening +to real-time telemetry updates. This will deal with most of the complexity of +dealing with telemetry (e.g. differentiating between individual telemetry points +and telemetry panels, monitoring latest values) and provide us with a useful +interface for populating our view. The the Open MCT Web Developer Guide for more +information on dealing with telemetry. + +Whenever the telemetry handler invokes its callbacks, we update the set of +telemetry objects in view, as well as the width for each bar. + +We will also utilize this from our template: + +
+
+ +
+ + {{value}} + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ + + + + +
-
-tutorials/bargraph/res/templates/bargraph.html +__tutorials/bargraph/res/templates/bargraph.html__ - Here, we utilize the functions we just provided from the controller to position the bar, using an ng-style attribute. +Summarizing these changes: - When we reload Open MCT Web, our bar graph view now looks like: +* Utilize the exposed `low`, `middle`, and `high` values to populate our labels +along the vertical axis. Additionally, use the toPercent function to position +these from the bottom. +* Replace our three hard-coded bars with a repeater that looks at the +`telemetryObjects` exposed by the controller and adds one bar each. +* Position the dashed tick-line using the middle value and the `toPercent` +function, lining it up with its `label` to the left. +* At the bottom, repeat a set of labels for the telemetry-providing domain +objects, with matching alignment to the bars above. We use an existing +representation, label, to make this easier. + +Finally, we expose our controller from our bundle definition. Note that the +depends declaration includes both `$scope` as well as the `telemetryHandler` +service we made use of. + + { + "name": "Bar Graph", + "description": "Provides the Bar Graph view of telemetry elements.", + "extensions": { + "views": [ + { + "name": "Bar Graph", + "key": "example.bargraph", + "glyph": "H", + "templateUrl": "templates/bargraph.html", + "needs": [ "telemetry" ], + "delegation": true + } + ], + "stylesheets": [ + { + "stylesheetUrl": "css/bargraph.css" + } + ], + + "controllers": [ + + { + + "key": "BarGraphController", + + "implementation": "controllers/BarGraphController.js", + + "depends": [ "$scope", "telemetryHandler" ] + + } + + ] + } + } +__tutorials/bargraph/bundle.json__ + +When we reload Open MCT Web, we are now able to see that our bar graph view +correctly labels one bar per telemetry-providing domain object, as shown for +this Telemetry Panel containing four Sine Wave Generators. + +![Bar Plot](images/bar-plot-2.png) + +### Step 3. Using Telemetry Data + +Now that our bar graph is labeled correctly, it’s time to start putting data +into the view. + +First, let’s add expose some more functionality from our controller. To make it +simple, we’ll expose the top and bottom for a bar graph for a given +telemetry-providing domain object, as percentages. + define(function () { + function BarGraphController($scope, telemetryHandler) { + var handle; + + // Add min/max defaults + $scope.low = -1; + $scope.middle = 0; + $scope.high = 1; + + // Convert value to a percent between 0-100, keeping values in points + $scope.toPercent = function (value) { + var pct = 100 * (value - $scope.low) / ($scope.high - $scope.low); + return Math.min(100, Math.max(0, pct)); + }; + + // Get bottom and top (as percentages) for current value + + $scope.getBottom = function (telemetryObject) { + + var value = handle.getRangeValue(telemetryObject); + + return $scope.toPercent(Math.min($scope.middle, value)); + + } + $scope.getTop = function (telemetryObject) { + var value = handle.getRangeValue(telemetryObject); + return 100 - $scope.toPercent(Math.max($scope.middle, value)); + } + + // Use the telemetryHandler to get telemetry objects here + handle = telemetryHandler.handle($scope.domainObject, function () { + $scope.telemetryObjects = handle.getTelemetryObjects(); + $scope.barWidth = + 100 / Math.max(($scope.telemetryObjects).length, 1); + }); + + // Release subscriptions when scope is destroyed + $scope.$on('$destroy', handle.unsubscribe); + } + + return BarGraphController; + }); +__tutorials/bargraph/src/controllers/BarGraphController.js__ -Step 4. View Configuration +The `telemetryHandler` exposes a method to provide us with our latest data value +(the `getRangeValue` method), and we already have a function to convert from a +numeric value to a percentage within the view, so we just use those. The only +slight complication is that we want our bar to move up or down from the middle +value, so either of our top or bottom position for the bar itself could be +either the middle line, or the data value. We let `Math.min` and `Math.max` +decide this. - The default minimum and maximum values we’ve provided happen to make sense for sine waves, but what about other values? We want to provide the user with a means of configuring these boundaries. +Next, we utilize this functionality from the template: -This is normally done via Edit mode. Since view configuration is a common problem, the Open MCT Web platform exposes a configuration object - called configuration - into our view’s scope. We can populate it as we please, and when we return to our view later, those changes will be persisted. +
+
+
+ {{value}} +
+
+ +
+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+__tutorials/bargraph/res/templates/bargraph.html__ + +Here, we utilize the functions we just provided from the controller to position +the bar, using an ng-style attribute. + +When we reload Open MCT Web, our bar graph view now looks like: + +![Bar Plot](images/bar-plot-3.png) + +### Step 4. View Configuration + +The default minimum and maximum values we’ve provided happen to make sense for +sine waves, but what about other values? We want to provide the user with a +means of configuring these boundaries. + +This is normally done via Edit mode. Since view configuration is a common +problem, the Open MCT Web platform exposes a configuration object - called +`configuration` - into our view’s scope. We can populate it as we please, and +when we return to our view later, those changes will be persisted. First, let’s add a tool bar for changing these three values in Edit mode: -{ - "name": "Bar Graph", - "description": "Provides the Bar Graph view of telemetry elements.", - "extensions": { - "views": [ - { - "name": "Bar Graph", - "key": "example.bargraph", - "glyph": "H", - "templateUrl": "templates/bargraph.html", - "needs": [ "telemetry" ], - "delegation": true, - "toolbar": { - "sections": [ - { - "items": [ - { - "name": "Low", - "property": "low", - "required": true, - "control": "textfield", - "size": 4 - }, - { - "name": "Middle", - "property": "middle", - "required": true, - "control": "textfield", - "size": 4 - }, - { - "name": "High", - "property": "high", - "required": true, - "control": "textfield", - "size": 4 - } - ] - } - ] + { + "name": "Bar Graph", + "description": "Provides the Bar Graph view of telemetry elements.", + "extensions": { + "views": [ + { + "name": "Bar Graph", + "key": "example.bargraph", + "glyph": "H", + "templateUrl": "templates/bargraph.html", + "needs": [ "telemetry" ], + "delegation": true, + + "toolbar": { + + "sections": [ + + { + + "items": [ + + { + + "name": "Low", + + "property": "low", + + "required": true, + + "control": "textfield", + + "size": 4 + + }, + + { + + "name": "Middle", + + "property": "middle", + + "required": true, + + "control": "textfield", + + "size": 4 + + }, + + { + + "name": "High", + + "property": "high", + + "required": true, + + "control": "textfield", + + "size": 4 + + } + + ] + + } + ] + } } - } - ], - "stylesheets": [ - { - "stylesheetUrl": "css/bargraph.css" - } - ], - "controllers": [ - { - "key": "BarGraphController", - "implementation": "controllers/BarGraphController.js", - "depends": [ "$scope", "telemetryHandler" ] - } - ] + ], + "stylesheets": [ + { + "stylesheetUrl": "css/bargraph.css" + } + ], + "controllers": [ + { + "key": "BarGraphController", + "implementation": "controllers/BarGraphController.js", + "depends": [ "$scope", "telemetryHandler" ] + } + ] + } } -} -tutorials/bargraph/bundle.json +__tutorials/bargraph/bundle.json__ - As we saw in to To-Do List plugin, a tool bar needs either a selected object or a view proxy to work from. We will add this to our controller, and additionally will start reading/writing those properties to the view’s configuration object. +As we saw in to To-Do List plugin, a tool bar needs either a selected object or +a view proxy to work from. We will add this to our controller, and additionally +will start reading/writing those properties to the view’s `configuration` +object. -define(function () { - function BarGraphController($scope, telemetryHandler) { - var handle; - - // Expose configuration constants directly in scope - function exposeConfiguration() { - $scope.low = $scope.configuration.low; - $scope.middle = $scope.configuration.middle; - $scope.high = $scope.configuration.high; - } - - // Populate a default value in the configuration - function setDefault(key, value) { - if ($scope.configuration[key] === undefined) { - $scope.configuration[key] = value; + define(function () { + function BarGraphController($scope, telemetryHandler) { + var handle; + + + // Expose configuration constants directly in scope + + function exposeConfiguration() { + + $scope.low = $scope.configuration.low; + + $scope.middle = $scope.configuration.middle; + + $scope.high = $scope.configuration.high; + + } + + + // Populate a default value in the configuration + + function setDefault(key, value) { + + if ($scope.configuration[key] === undefined) { + + $scope.configuration[key] = value; + + } + + } + + + // Getter-setter for configuration properties (for view proxy) + + function getterSetter(property) { + + return function (value) { + + value = parseFloat(value); + + if (!isNaN(value)) { + + $scope.configuration[property] = value; + + exposeConfiguration(); + + } + + return $scope.configuration[property]; + + }; } - } - - // Getter-setter for configuration properties (for view proxy) - function getterSetter(property) { - return function (value) { - value = parseFloat(value); - if (!isNaN(value)) { - $scope.configuration[property] = value; - exposeConfiguration(); - } - return $scope.configuration[property]; + + + // Add min/max defaults + + setDefault('low', -1); + + setDefault('middle', 0); + + setDefault('high', 1); + + exposeConfiguration($scope.configuration); + + + // Expose view configuration options + + if ($scope.selection) { + + $scope.selection.proxy({ + + low: getterSetter('low'), + + middle: getterSetter('middle'), + + high: getterSetter('high') + + }); + + } + + // Convert value to a percent between 0-100 + $scope.toPercent = function (value) { + var pct = 100 * (value - $scope.low) / + ($scope.high - $scope.low); + return Math.min(100, Math.max(0, pct)); }; - } - - // Add min/max defaults - setDefault('low', -1); - setDefault('middle', 0); - setDefault('high', 1); - exposeConfiguration($scope.configuration); - - // Expose view configuration options - if ($scope.selection) { - $scope.selection.proxy({ - low: getterSetter('low'), - middle: getterSetter('middle'), - high: getterSetter('high') - }); - } - - // Convert value to a percent between 0-100 - $scope.toPercent = function (value) { - var pct = 100 * (value - $scope.low) / - ($scope.high - $scope.low); - return Math.min(100, Math.max(0, pct)); - }; - - // Get bottom and top (as percentages) for current value - $scope.getBottom = function (telemetryObject) { - var value = handle.getRangeValue(telemetryObject); - return $scope.toPercent(Math.min($scope.middle, value)); - } - $scope.getTop = function (telemetryObject) { - var value = handle.getRangeValue(telemetryObject); - return 100 - $scope.toPercent(Math.max($scope.middle, value)); - } - - // Use the telemetryHandler to get telemetry objects here - handle = telemetryHandler.handle($scope.domainObject, function () { - $scope.telemetryObjects = handle.getTelemetryObjects(); - $scope.barWidth = - 100 / Math.max(($scope.telemetryObjects).length, 1); - }); - - // Release subscriptions when scope is destroyed - $scope.$on('$destroy', handle.unsubscribe); - } - - return BarGraphController; -}); -tutorials/bargraph/src/controllers/BarGraphController.js - - A summary of these changes: - -First, read low, middle, and high from the view configuration instead of initializing them to explicit values. This is placed into its own function, since it will be called a lot. -The function setDefault is included; it will be used to set the default values for low, middle, and high in the view configuration, but only if they aren’t present. -The tool bar will treat properties in a view proxy as getter-setters if they are functions; that is, they will be called with an argument to be used as a setter, and with no argument to use as a getter. We provide ourselves a function for making these getter-setters (since we’ll need three) that additionally handles some checking to ensure that these are actually numbers. -After that, we actually initialize both the view configuration object with defaults (if needed), and expose its state into the scope. -Finally, we expose a view proxy which will handle changes to low, middle, and high as entered by the user from the tool bar. This uses the getter-setters we defined previously. - - If we reload Open MCT Web and go to a Bar Graph view in Edit mode, we now see that we can change these bounds from the tool bar. - - - -Telemetry Adapter - - The goal of this tutorial is to demonstrate how to integrate Open MCT Web with an existing telemetry system. - A summary of the steps we will take: - -Expose the telemetry dictionary within the user interface. -Support subscription/unsubscription to real-time streaming data. -Support historical retrieval of telemetry data. - -Step 0. Expose Your Telemetry - - As a precondition to integrating telemetry data into Open MCT Web, this information needs to be available over web-based interfaces. In practice, this will most likely mean exposing data over HTTP, or over WebSockets. - For purposes of this tutorial, a simple node server is provided to stand in place of this existing telemetry system. It generates real-time data and exposes it over a WebSocket connection. - - -/*global require,process,console*/ - -var CONFIG = { - port: 8081, - dictionary: "dictionary.json", - interval: 1000 -}; - -(function () { - "use strict"; - - var WebSocketServer = require('ws').Server, - fs = require('fs'), - wss = new WebSocketServer({ port: CONFIG.port }), - dictionary = JSON.parse(fs.readFileSync(CONFIG.dictionary, "utf8")), - spacecraft = { - "prop.fuel": 77, - "prop.thrusters": "OFF", - "comms.recd": 0, - "comms.sent": 0, - "pwr.temp": 245, - "pwr.c": 8.15, - "pwr.v": 30 - }, - histories = {}, - listeners = []; - - function updateSpacecraft() { - spacecraft["prop.fuel"] = Math.max( - 0, - spacecraft["prop.fuel"] - - (spacecraft["prop.thrusters"] === "ON" ? 0.5 : 0) - ); - spacecraft["pwr.temp"] = spacecraft["pwr.temp"] * 0.985 - + Math.random() * 0.25 + Math.sin(Date.now()); - spacecraft["pwr.c"] = spacecraft["pwr.c"] * 0.985; - spacecraft["pwr.v"] = 30 + Math.pow(Math.random(), 3); - } - - function generateTelemetry() { - var timestamp = Date.now(), sent = 0; - Object.keys(spacecraft).forEach(function (id) { - var state = { timestamp: timestamp, value: spacecraft[id] }; - histories[id] = histories[id] || []; // Initialize - histories[id].push(state); - spacecraft["comms.sent"] += JSON.stringify(state).length; - }); - listeners.forEach(function (listener) { - listener(); - }); - } - - function update() { - updateSpacecraft(); - generateTelemetry(); - } - - function handleConnection(ws) { - var subscriptions = {}, // Active subscriptions for this connection - handlers = { // Handlers for specific requests - dictionary: function () { - ws.send(JSON.stringify({ - type: "dictionary", - value: dictionary - })); - }, - subscribe: function (id) { - subscriptions[id] = true; - }, - unsubscribe: function (id) { - delete subscriptions[id]; - }, - history: function (id) { - ws.send(JSON.stringify({ - type: "history", - id: id, - value: histories[id] - })); - } - }; - - function notifySubscribers() { - Object.keys(subscriptions).forEach(function (id) { - var history = histories[id]; - if (history) { - ws.send(JSON.stringify({ - type: "data", - id: id, - value: history[history.length - 1] - })); - } - }); - } - - // Listen for requests - ws.on('message', function (message) { - var parts = message.split(' '), - handler = handlers[parts[0]]; - if (handler) { - handler.apply(handlers, parts.slice(1)); + + // Get bottom and top (as percentages) for current value + $scope.getBottom = function (telemetryObject) { + var value = handle.getRangeValue(telemetryObject); + return $scope.toPercent(Math.min($scope.middle, value)); } - }); - - // Stop sending telemetry updates for this connection when closed - ws.on('close', function () { - listeners = listeners.filter(function (listener) { - return listener !== notifySubscribers; + $scope.getTop = function (telemetryObject) { + var value = handle.getRangeValue(telemetryObject); + return 100 - $scope.toPercent(Math.max($scope.middle, value)); + } + + // Use the telemetryHandler to get telemetry objects here + handle = telemetryHandler.handle($scope.domainObject, function () { + $scope.telemetryObjects = handle.getTelemetryObjects(); + $scope.barWidth = + 100 / Math.max(($scope.telemetryObjects).length, 1); }); - }); - - // Notify subscribers when telemetry is updated - listeners.push(notifySubscribers); - } - - update(); - setInterval(update, CONFIG.interval); - - wss.on('connection', handleConnection); - - console.log("Example spacecraft running on port "); - console.log("Press Enter to toggle thruster state."); - process.stdin.on('data', function (data) { - spacecraft['prop.thrusters'] = - (spacecraft['prop.thrusters'] === "OFF") ? "ON" : "OFF"; - console.log("Thrusters " + spacecraft["prop.thrusters"]); + + // Release subscriptions when scope is destroyed + $scope.$on('$destroy', handle.unsubscribe); + } + + return BarGraphController; }); -}()); +__tutorials/bargraph/src/controllers/BarGraphController.js__ + +A summary of these changes: + +* First, read `low`, `middle`, and `high` from the view configuration instead of +initializing them to explicit values. This is placed into its own function, +since it will be called a lot. +* The function 'setDefault' is included; it will be used to set the default +values for `low`, `middle`, and `high` in the view configuration, but only if +they aren’t present. +* The tool bar will treat properties in a view proxy as getter-setters if +they are functions; that is, they will be called with an argument to be used +as a setter, and with no argument to use as a getter. We provide ourselves a +function for making these getter-setters (since we’ll need three) that +additionally handles some checking to ensure that these are actually numbers. +* After that, we actually initialize both the view `configuration` object with +defaults (if needed), and expose its state into the scope. +* Finally, we expose a view proxy which will handle changes to `low`, `middle`, +and `high` as entered by the user from the tool bar. This uses the +getter-setters we defined previously. + +If we reload Open MCT Web and go to a Bar Graph view in Edit mode, we now see +that we can change these bounds from the tool bar. + +![Bar plot](images/bar-plot-4.png) + +## Telemetry Adapter + +The goal of this tutorial is to demonstrate how to integrate Open MCT Web +with an existing telemetry system. + +A summary of the steps we will take: + +* Expose the telemetry dictionary within the user interface. +* Support subscription/unsubscription to real-time streaming data. +* Support historical retrieval of telemetry data. + +### Step 0. Expose Your Telemetry + +As a precondition to integrating telemetry data into Open MCT Web, this +information needs to be available over web-based interfaces. In practice, +this will most likely mean exposing data over HTTP, or over WebSockets. +For purposes of this tutorial, a simple node server is provided to stand +in place of this existing telemetry system. It generates real-time data +and exposes it over a WebSocket connection. -tutorial-server/app.js - - For purposes of this tutorial, how this server has been implemented is not important; it has just enough functionality to resemble a WebSocket interface to a real telemetry system, and niceties such as error-handling have been omitted. (For more information on using WebSockets, both in the client and on the server, https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API is an excellent starting point.) - What does matter for this tutorial is the interfaces that are exposed. Once a WebSocket connection has been established to this server, it accepts plain text messages in the following formats, and issues JSON-formatted responses. - - The requests it handles are: - -dictionary: Responds with a JSON response with the following fields: -type: “dictionary” -value: … the telemetry dictionary (see below) … -subscribe : Subscribe to new telemetry data for the measurement with the provided identifier. The server will begin sending messages of the following form: -type: “data” -id: The identifier for the measurement. -value: An object containing the actual measurement, in two fields: -timestamp: A UNIX timestamp (in milliseconds) for the “measurement” -value: The data value for the measurement (either a number, or a string) -unsubscribe : Stop receiving new data for the identified measurement. -history : Request a history of all telemetry data for the identified measurement. -type: “history” -id: The identifier for the measurement. -value: An array of objects containing the actual measurement, each of which having two fields: -timestamp: A UNIX timestamp (in milliseconds) for the “measurement” -value: The data value for the measurement (either a number, or a string) - - (Note that the term “measurement” is used to describe a distinct data series within this system; in other systems, these have been called channels, mnemonics, telemetry points, or other names. No preference is made here; Open MCT Web is easily adapted to use the terminology appropriate to your system.) - Additionally, while running the server from the terminal we can toggle the state of the “spacecraft” by hitting enter; this will turn the “thrusters” on and off, having observable changes in telemetry. - - The telemetry dictionary referenced previously is contained in a separate file, used by the server. It uses a custom format and, for purposes of example, contains three “subsystems” containing a mix of numeric and string-based telemetry. - - - -{ - "name": "Example Spacecraft", - "identifier": "sc", - "subsystems": [ - { - "name": "Propulsion", - "identifier": "prop", - "measurements": [ - { - "name": "Fuel", - "identifier": "prop.fuel", - "units": "kilograms", - "type": "float" - }, - { - "name": "Thrusters", - "identifier": "prop.thrusters", - "units": "None", - "type": "string" - } - ] - }, - { - "name": "Communications", - "identifier": "comms", - "measurements": [ - { - "name": "Received", - "identifier": "comms.recd", - "units": "bytes", - "type": "integer" - }, - { - "name": "Sent", - "identifier": "comms.sent", - "units": "bytes", - "type": "integer" - } - ] - }, - { - "name": "Power", - "identifier": "pwr", - "measurements": [ - { - "name": "Generator Temperature", - "identifier": "pwr.temp", - "units": "\u0080C", - "type": "float" - }, - { - "name": "Generator Current", - "identifier": "pwr.c", - "units": "A", - "type": "float" - }, - { - "name": "Generator Voltage", - "identifier": "pwr.v", - "units": "V", - "type": "float" - } - ] - } - ] -} -tutorial-server/dictionary.json - - It should be noted that neither the interface for the example server nor the dictionary format are expected by Open MCT Web; rather, these are intended to stand in for some existing source of telemetry data to which we wish to adapt Open MCT Web. - - We can run this example server by: - -cd tutorial-server -npm install ws -node app.js - -To verify that this is running and try out its interface, we can use a tool like https://www.npmjs.com/package/wscat: - -wscat -c ws://localhost:8081 -connected (press CTRL+C to quit) -> dictionary -< {"type":"dictionary","value":{"name":"Example Spacecraft","identifier":"sc","subsystems":[{"name":"Propulsion","identifier":"prop","measurements":[{"name":"Fuel","identifier":"prop.fuel","units":"kilograms","type":"float"},{"name":"Thrusters","identifier":"prop.thrusters","units":"None","type":"string"}]},{"name":"Communications","identifier":"comms","measurements":[{"name":"Received","identifier":"comms.recd","units":"bytes","type":"integer"},{"name":"Sent","identifier":"comms.sent","units":"bytes","type":"integer"}]},{"name":"Power","identifier":"pwr","measurements":[{"name":"Generator Temperature","identifier":"pwr.temp","units":"€C","type":"float"},{"name":"Generator Current","identifier":"pwr.c","units":"A","type":"float"},{"name":"Generator Voltage","identifier":"pwr.v","units":"V","type":"float"}]}]}} - - Now that the example server’s interface is reasonably well-understood, a plugin can be written to adapt Open MCT Web to utilize it. - -Step 1. Add a Top-level Object - - Since Open MCT Web uses an “object-first” approach to accessing data, before we’ll be able to do anything with this new data source, we’ll need to have a way to explore the available measurements in the tree. In this step, we will add a top-level object which will serve as a container; in the next step, we will populate this with the contents of the telemetry dictionary (which we will retrieve from the server.) - -{ - "name": "Example Telemetry Adapter", - "extensions": { - "types": [ - { - "name": "Spacecraft", - "key": "example.spacecraft", - "glyph": "o" - } - ], - "roots": [ - { - "id": "example:sc", - "priority": "preferred", - "model": { - "type": "example.spacecraft", - "name": "My Spacecraft", - "composition": [] - } - } - ] - } -} - - - -tutorials/telemetry/bundle.json - - Here, we’ve created our initial telemetry plugin. This exposes a new domain object type (the “Spacecraft”, which will be represented by the contents of the telemetry dictionary) and also adds one instance of it as a root-level object (by declaring an extension of category roots.) We have also set priority to preferred so that this shows up near the top, instead of below My Items. - - If we include this in our set of active bundles: - -[ - "platform/framework", - "platform/core", - "platform/representation", - "platform/commonUI/about", - "platform/commonUI/browse", - "platform/commonUI/edit", - "platform/commonUI/dialog", - "platform/commonUI/general", - "platform/containment", - "platform/telemetry", - "platform/features/layout", - "platform/features/pages", - "platform/features/plot", - "platform/features/scrolling", - "platform/forms", - "platform/persistence/queue", - "platform/policy", - - "example/persistence", - "example/generator" -] -[ - "platform/framework", - "platform/core", - "platform/representation", - "platform/commonUI/about", - "platform/commonUI/browse", - "platform/commonUI/edit", - "platform/commonUI/dialog", - "platform/commonUI/general", - "platform/containment", - "platform/telemetry", - "platform/features/layout", - "platform/features/pages", - "platform/features/plot", - "platform/features/scrolling", - "platform/forms", - "platform/persistence/queue", - "platform/policy", - - "example/persistence", - "example/generator", - - "tutorials/telemetry" -] -bundles.json - - ...we will be able to reload Open MCT Web and see that it is present: - - - - Now, we have somewhere in the UI to put the contents of our telemetry dictionary. - -Step 2. Expose the Telemetry Dictionary - - In order to expose the telemetry dictionary, we first need to read it from the server. Our first step will be to add a service that will handle interactions with the server; this will not be used by Open MCT Web directly, but will be used by subsequent components we add. - -/*global define,WebSocket*/ - -define( - [], - function () { + /*global require,process,console*/ + + var CONFIG = { + port: 8081, + dictionary: "dictionary.json", + interval: 1000 + }; + + (function () { "use strict"; - - function ExampleTelemetryServerAdapter($q, wsUrl) { - var ws = new WebSocket(wsUrl), - dictionary = $q.defer(); - - // Handle an incoming message from the server - ws.onmessage = function (event) { - var message = JSON.parse(event.data); - - switch (message.type) { - case "dictionary": - dictionary.resolve(message.value); - break; - } - }; - - // Request dictionary once connection is established - ws.onopen = function () { - ws.send("dictionary"); - }; - - return { - dictionary: function () { - return dictionary.promise; - } - }; - } - - return ExampleTelemetryServerAdapter; - } -); -tutorials/telemetry/src/ExampleTelemetryServerAdapter.js - - When created, this service initiates a connection to the server, and begins loading the dictionary. This will occur asynchronously, so the dictionary() method it exposes returns a Promise for the loaded dictionary (dictionary.json from above), using Angular’s $q (see https://docs.angularjs.org/api/ng/service/$q.) Note that error- and close-handling for this WebSocket connection have been omitted for brevity. - -Once the dictionary has been loaded, we will want to represent its contents as domain objects. Specifically, we want subsystems to appear as objects under My Spacecraft, and measurements to appear as objects within those subsystems. This means that we need to convert the data from the dictionary into domain object models, and expose these to Open MCT Web via a modelService. - - -/*global define*/ - -define( - function () { - "use strict"; - - var PREFIX = "example_tlm:", - FORMAT_MAPPINGS = { - float: "number", - integer: "number", - string: "string" - }; - - function ExampleTelemetryModelProvider(adapter, $q) { - var modelPromise, empty = $q.when({}); - - // Check if this model is in our dictionary (by prefix) - function isRelevant(id) { - return id.indexOf(PREFIX) === 0; - } - - // Build a domain object identifier by adding a prefix - function makeId(element) { - return PREFIX + element.identifier; - } - - // Create domain object models from this dictionary - function buildTaxonomy(dictionary) { - var models = {}; - - // Create & store a domain object model for a measurement - function addMeasurement(measurement) { - var format = FORMAT_MAPPINGS[measurement.type]; - models[makeId(measurement)] = { - type: "example.measurement", - name: measurement.name, - telemetry: { - key: measurement.identifier, - ranges: [{ - key: "value", - name: "Value", - units: measurement.units, - format: format - }] - } - }; - } - - // Create & store a domain object model for a subsystem - function addSubsystem(subsystem) { - var measurements = - (subsystem.measurements || []); - models[makeId(subsystem)] = { - type: "example.subsystem", - name: subsystem.name, - composition: measurements.map(makeId) - }; - measurements.forEach(addMeasurement); - } - - (dictionary.subsystems || []).forEach(addSubsystem); - - return models; - } - - // Begin generating models once the dictionary is available - modelPromise = adapter.dictionary().then(buildTaxonomy); - - return { - getModels: function (ids) { - // Return models for the dictionary only when they - // are relevant to the request. - return ids.some(isRelevant) ? modelPromise : empty; - } - }; - } - - return ExampleTelemetryModelProvider; - } -); -tutorials/telemetry/src/ExampleTelemetryModelProvider.js - - This script implements a provider for modelService; the modelService is a composite service, meaning that multiple such services can exist side by side. (For example, there is another provider for modelService that reads domain object models from the persistence store.) -Here, we read the dictionary using the server adapter from above; since this will be loaded asynchronously, we use promise-chaining (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Chaining) to take that result and build up an object mapping identifiers to new domain object models. This is returned from our modelService, but only when the request actually calls for identifiers that look like they’re from the dictionary. This means that loading other models is not blocked by loading the dictionary. (Note that the modelService contract allows us to return either a sub- or superset of the requested models, so it is fine to always return the whole dictionary.) -Some notable points to call out here: - -Every subsystem and every measurement from the dictionary has an identifier field declared. We use this as part of the domain object identifier, but we also prefix it with example_tlm:. This accomplishes a few things: -We can easily tell whether an identifier is expected to be in the dictionary or not. -We avoid naming collisions with other model providers. -Finally, Open MCT Web uses the colon prefix as a hint that this domain object will not be in the persistence store. -A couple of new types are introduced here (in the type field of the domain object models we create); we will need to define these as extensions as well in order for them to display correctly. -The composition field of each subsystem contained the Open MCT Web identifiers of all the measurements in that subsystem. This composition field will be used by Open MCT Web to determine what domain objects contain other domain objects (e.g. to populate the tree.) -The telemetry field of each measurement will be used by Open MCT Web to understand how to request and interpret telemetry data for this object. The key is the machine-readable identifier for this measurement within the telemetry system; the ranges provide metadata about the values for this data. (A separate field, domains, provides metadata about timestamps or other ordering properties of the data, but this will be the same for all measurements, so we will define that later at the type level.) -This field (whose contents will be merged atop the telemetry property we define at the type-level) will serve as a template for later telemetry requests to the telemetryService, so we’ll see the properties we define here again later in Steps 3 and 4. - - This allows our telemetry dictionary to be expressed as domain object models (and, in turn, as domain objects), but these objects still aren’t reachable. To fix this, we will need another script which will add these subsystems to the root-level object we added in Step 1. - - - -/*global define*/ - -define( - function () { - "use strict"; - - var TAXONOMY_ID = "example:sc", - PREFIX = "example_tlm:"; - - function ExampleTelemetryInitializer(adapter, objectService) { - // Generate a domain object identifier for a dictionary element - function makeId(element) { - return PREFIX + element.identifier; - } - - // When the dictionary is available, add all subsystems - // to the composition of My Spacecraft - function initializeTaxonomy(dictionary) { - // Get the top-level container for dictionary objects - // from a group of domain objects. - function getTaxonomyObject(domainObjects) { - return domainObjects[TAXONOMY_ID]; - } - - // Populate - function populateModel(taxonomyObject) { - return taxonomyObject.useCapability( - "mutation", - function (model) { - model.name = - dictionary.name; - model.composition = - dictionary.subsystems.map(makeId); - } - ); - } - - // Look up My Spacecraft, and populate it accordingly. - objectService.getObjects([TAXONOMY_ID]) - .then(getTaxonomyObject) - .then(populateModel); - } - - adapter.dictionary().then(initializeTaxonomy); - } - - return ExampleTelemetryInitializer; - } -); -tutorials/telemetry/src/ExampleTelemetryInitializer.js - - At the conclusion of Step 1, the top-level My Spacecraft object was empty. This script will wait for the dictionary to be loaded, then load My Spacecraft (by its identifier), and “mutate” it. The mutation capability allows changes to be made to a domain object’s model. Here, we take this top-level object, update its name to match what was in the dictionary, and set its composition to an array of domain object identifiers for all subsystems contained in the dictionary (using the same identifier prefix as before.) - - Finally, we wire in these changes by modifying our plugin’s bundle.json to provide metadata about how these pieces interact (both with each other, and with the platform): - - -{ - "name": "Example Telemetry Adapter", - "extensions": { - "types": [ - { - "name": "Spacecraft", - "key": "example.spacecraft", - "glyph": "o" + + var WebSocketServer = require('ws').Server, + fs = require('fs'), + wss = new WebSocketServer({ port: CONFIG.port }), + dictionary = JSON.parse(fs.readFileSync(CONFIG.dictionary, "utf8")), + spacecraft = { + "prop.fuel": 77, + "prop.thrusters": "OFF", + "comms.recd": 0, + "comms.sent": 0, + "pwr.temp": 245, + "pwr.c": 8.15, + "pwr.v": 30 }, - { - "name": "Subsystem", - "key": "example.subsystem", - "glyph": "o", - "model": { "composition": [] } - }, - { - "name": "Measurement", - "key": "example.measurement", - "glyph": "T", - "model": { "telemetry": {} }, - "telemetry": { - "source": "example.source", - "domains": [ - { - "name": "Time", - "key": "timestamp" - } - ] - } - } - ], - "roots": [ - { - "id": "example:sc", - "priority": "preferred", - "model": { - "type": "example.spacecraft", - "name": "My Spacecraft", - "composition": [] - } - } - ], - "services": [ - { - "key": "example.adapter", - "implementation": "ExampleTelemetryServerAdapter.js", - "depends": [ "$q", "EXAMPLE_WS_URL" ] - } - ], - "constants": [ - { - "key": "EXAMPLE_WS_URL", - "priority": "fallback", - "value": "ws://localhost:8081" - } - ], - "runs": [ - { - "implementation": "ExampleTelemetryInitializer.js", - "depends": [ "example.adapter", "objectService" ] - } - ], - "components": [ - { - "provides": "modelService", - "type": "provider", - "implementation": "ExampleTelemetryModelProvider.js", - "depends": [ "example.adapter", "$q" ] - } - ] - } -} -tutorials/telemetry/bundle.json - - A summary of what we’ve added here: - -New type definitions have been added to represent Subsystems and Measurements, respectively. -Measurements have a telemetry field; this is similar to the telemetry field added in the model, but contains properties that will be common among all Measurements. In particular, the source field will be used later as a symbolic identifier for the telemetry data source. -We have also added some “initial models” for these two types using the model field. While domain objects of these types cannot be created via the Create menu, some policies will look at initial models to predict what capabilities domain objects of certain types would have, so we want to ensure that Subsystems and Measurements will be recognized as having composition and telemetry capabilities, respectively. -The adapter to the WebSocket server has been added as a service with the symbolic name example.adapter; it is depended-upon elsewhere within this plugin. -A constant, EXAMPLE_WS_URL, is defined, and depended-upon by example.server. Setting priority to fallback means this constant will be overridden if defined anywhere else, allowing configuration bundles to specify different URLs for the WebSocket connection. -The initializer script is registered using the runs category of extension, to ensure that this executes (and populates the contents of the top-level My Spacecraft object) once Open MCT Web is started. -This depends upon the example.adapter service we exposed above, as well as Angular’s $q; these services will be made available in the constructor call. -Finally, the modelService provider which presents dictionary elements as domain object models is exposed. Since modelService is a composite service, this is registered under the extension category components. -As with the initializer, this depends upon the example.adapter service we exposed above, as well as Angular’s $q; these services will be made available in the constructor call. - - Now if we run Open MCT Web (assuming our example telemetry server is also running) and expand our top-level node completely, we see the contents of our dictionary: - - - - Note that “My Spacecraft” has changed its name to “Example Spacecraft”, which is the name it had in the dictionary. - -Step 3. Historical Telemetry - - After Step 2, we are able to see our dictionary in the user interface and click around our different measurements, but we don’t see any data. We need to give ourselves the ability to retrieve this data from the server. In this step, we will do so for the server’s historical telemetry. - Our first step will be to add a method to our server adapter which allows us to send history requests to the server: - -/*global define,WebSocket*/ - -define( - [], - function () { - "use strict"; - - function ExampleTelemetryServerAdapter($q, wsUrl) { - var ws = new WebSocket(wsUrl), - histories = {}, - dictionary = $q.defer(); - - // Handle an incoming message from the server - ws.onmessage = function (event) { - var message = JSON.parse(event.data); - - switch (message.type) { - case "dictionary": - dictionary.resolve(message.value); - break; - case "history": - histories[message.id].resolve(message); - delete histories[message.id]; - break; - } - }; - - // Request dictionary once connection is established - ws.onopen = function () { - ws.send("dictionary"); - }; - - return { - dictionary: function () { - return dictionary.promise; - }, - history: function (id) { - histories[id] = histories[id] || $q.defer(); - ws.send("history " + id); - return histories[id].promise; - } - }; + histories = {}, + listeners = []; + + function updateSpacecraft() { + spacecraft["prop.fuel"] = Math.max( + 0, + spacecraft["prop.fuel"] - + (spacecraft["prop.thrusters"] === "ON" ? 0.5 : 0) + ); + spacecraft["pwr.temp"] = spacecraft["pwr.temp"] * 0.985 + + Math.random() * 0.25 + Math.sin(Date.now()); + spacecraft["pwr.c"] = spacecraft["pwr.c"] * 0.985; + spacecraft["pwr.v"] = 30 + Math.pow(Math.random(), 3); } - - return ExampleTelemetryServerAdapter; - } -); - - - -tutorials/telemetry/src/ExampleTelemetryServerAdapter.js - - When the history method is called, a new request is issued to the server for historical telemetry, unless a request for the same historical telemetry is still pending. Similarly, when historical telemetry arrives for a given identifier, the pending promise is resolved. - - This history method will be used by a telemetryService provider which we will implement: - -/*global define*/ - -define( - ['./ExampleTelemetrySeries'], - function (ExampleTelemetrySeries) { - "use strict"; - - var SOURCE = "example.source"; - - function ExampleTelemetryProvider(adapter, $q) { - // Used to filter out requests for telemetry - // from some other source - function matchesSource(request) { - return (request.source === SOURCE); - } - - return { - requestTelemetry: function (requests) { - var packaged = {}, - relevantReqs = requests.filter(matchesSource); - - // Package historical telemetry that has been received - function addToPackage(history) { - packaged[SOURCE][history.id] = - new ExampleTelemetrySeries(history.value); + + function generateTelemetry() { + var timestamp = Date.now(), sent = 0; + Object.keys(spacecraft).forEach(function (id) { + var state = { timestamp: timestamp, value: spacecraft[id] }; + histories[id] = histories[id] || []; // Initialize + histories[id].push(state); + spacecraft["comms.sent"] += JSON.stringify(state).length; + }); + listeners.forEach(function (listener) { + listener(); + }); + } + + function update() { + updateSpacecraft(); + generateTelemetry(); + } + + function handleConnection(ws) { + var subscriptions = {}, // Active subscriptions for this connection + handlers = { // Handlers for specific requests + dictionary: function () { + ws.send(JSON.stringify({ + type: "dictionary", + value: dictionary + })); + }, + subscribe: function (id) { + subscriptions[id] = true; + }, + unsubscribe: function (id) { + delete subscriptions[id]; + }, + history: function (id) { + ws.send(JSON.stringify({ + type: "history", + id: id, + value: histories[id] + })); } - - // Retrieve telemetry for a specific measurement - function handleRequest(request) { - var key = request.key; - return adapter.history(key).then(addToPackage); + }; + + function notifySubscribers() { + Object.keys(subscriptions).forEach(function (id) { + var history = histories[id]; + if (history) { + ws.send(JSON.stringify({ + type: "data", + id: id, + value: history[history.length - 1] + })); } - - packaged[SOURCE] = {}; - return $q.all(relevantReqs.map(handleRequest)) - .then(function () { return packaged; }); - }, - subscribe: function (callback, requests) { - return function () {}; + }); + } + + // Listen for requests + ws.on('message', function (message) { + var parts = message.split(' '), + handler = handlers[parts[0]]; + if (handler) { + handler.apply(handlers, parts.slice(1)); } - }; - } - - return ExampleTelemetryProvider; - } -); - - - -tutorials/telemetry/src/ExampleTelemetryProvider.js - - The requestTelemetry method of a telemetryService is expected to take an array of requests (each with source and key parameters, identifying the general source of data and the specific element within that source, respectively) and return a Promise for any telemetry data it knows of which satisfies those requests, packaged in a specific way. This packaging is as an object containing key-value pairs, where keys correspond to source properties of requests and values are key-value pairs, where keys correspond to key properties of requests and values are TelemetrySeries objects. (We will see our implementation below.) - To do this, we create a container for our telemetry source, and consult the adapter to get telemetry histories for any relevant requests, then package them as they come in. The $q.all method is used to return a single Promise that will resolve only when all histories have been packaged. Promise-chaining is used to ensure that the resolved value will be the fully-packaged data. - -It is worth mentioning here that the requests we receive should look a little familiar. When Open MCT Web generates a request object associated with a domain object, it does so by merging together three JavaScript objects: - -First, the telemetry property from that domain object’s type definition. -Second, the telemetry property from that domain object’s model. -Finally, the request object that was passed in via that domain object’s telemetry capability. - - As such, the source and key properties we observe here will come from the type definition and domain object model, respectively, as we specified them during Step 2. (Or, they might come from somewhere else entirely, if we have other telemetry-providing domain objects in our system; that is something we check for using the source property.) - - Finally, note that we also have a subscribe method, to satisfy the interface of telemetryService, but this subscribe method currently does nothing. - This script uses an ExampleTelemetrySeries class, which looks like: - - -/*global define*/ - -define( - function () { - "use strict"; - - function ExampleTelemetrySeries(data) { - return { - getPointCount: function () { - return data.length; - }, - getDomainValue: function (index) { - return (data[index] || {}).timestamp; - }, - getRangeValue: function (index) { - return (data[index] || {}).value; - } - }; - } - - return ExampleTelemetrySeries; - } -); -tutorials/telemetry/src/ExampleTelemetrySeries.js - - This takes the array of telemetry values (as returned by the server) and wraps it with the interface expected by the platform (the methods shown.) - - Finally, we expose this telemetryService provider declaratively: - - -{ - "name": "Example Telemetry Adapter", - "extensions": { - "types": [ - { - "name": "Spacecraft", - "key": "example.spacecraft", - "glyph": "o" - }, - { - "name": "Subsystem", - "key": "example.subsystem", - "glyph": "o", - "model": { "composition": [] } - }, - { - "name": "Measurement", - "key": "example.measurement", - "glyph": "T", - "model": { "telemetry": {} }, - "telemetry": { - "source": "example.source", - "domains": [ - { - "name": "Time", - "key": "timestamp" - } - ] - } - } - ], - "roots": [ - { - "id": "example:sc", - "priority": "preferred", - "model": { - "type": "example.spacecraft", - "name": "My Spacecraft", - "composition": [] - } - } - ], - "services": [ - { - "key": "example.adapter", - "implementation": "ExampleTelemetryServerAdapter.js", - "depends": [ "$q", "EXAMPLE_WS_URL" ] - } - ], - "constants": [ - { - "key": "EXAMPLE_WS_URL", - "priority": "fallback", - "value": "ws://localhost:8081" - } - ], - "runs": [ - { - "implementation": "ExampleTelemetryInitializer.js", - "depends": [ "example.adapter", "objectService" ] - } - ], - "components": [ - { - "provides": "modelService", - "type": "provider", - "implementation": "ExampleTelemetryModelProvider.js", - "depends": [ "example.adapter", "$q" ] - }, - { - "provides": "telemetryService", - "type": "provider", - "implementation": "ExampleTelemetryProvider.js", - "depends": [ "example.adapter", "$q" ] - } - ] - } -} -tutorials/telemetry/bundle.json - - Now, if we navigate to one of our numeric measurements, we should see a plot of its historical telemetry: - - - - We can now visualize our data, but it doesn’t update over time - we know the server is continually producing new data, but we have to click away and come back to see it. We can fix this by adding support for telemetry subscriptions. - -Step 4. Real-time Telemetry - - Finally, we want to utilize the server’s ability to subscribe to telemetry from Open MCT Web. To do this, first we want to expose some new methods for this from our server adapter: - - -/*global define,WebSocket*/ - -define( - [], - function () { - "use strict"; - - function ExampleTelemetryServerAdapter($q, wsUrl) { - var ws = new WebSocket(wsUrl), - histories = {}, - listeners = [], - dictionary = $q.defer(); - - // Handle an incoming message from the server - ws.onmessage = function (event) { - var message = JSON.parse(event.data); - - switch (message.type) { - case "dictionary": - dictionary.resolve(message.value); - break; - case "history": - histories[message.id].resolve(message); - delete histories[message.id]; - break; - case "data": - listeners.forEach(function (listener) { - listener(message); - }); - break; - } - }; - - // Request dictionary once connection is established - ws.onopen = function () { - ws.send("dictionary"); - }; - - return { - dictionary: function () { - return dictionary.promise; - }, - history: function (id) { - histories[id] = histories[id] || $q.defer(); - ws.send("history " + id); - return histories[id].promise; - }, - subscribe: function (id) { - ws.send("subscribe " + id); - }, - unsubscribe: function (id) { - ws.send("unsubscribe " + id); - }, - listen: function (callback) { - listeners.push(callback); - } - }; - } - - return ExampleTelemetryServerAdapter; - } -); -tutorials/telemetry/src/ExampleTelemetryServerAdapter.js - - Here, we have added subscribe and unsubscribe methods which issue the corresponding requests to the server. Seperately, we introduce the ability to listen for data messages as they come in: These will contain the data associated with these subscriptions. - - We then need only to utilize these methods from our telemetryService: - -/*global define*/ - -define( - ['./ExampleTelemetrySeries'], - function (ExampleTelemetrySeries) { - "use strict"; - - var SOURCE = "example.source"; - - function ExampleTelemetryProvider(adapter, $q) { - var subscribers = {}; - - // Used to filter out requests for telemetry - // from some other source - function matchesSource(request) { - return (request.source === SOURCE); - } - - // Listen for data, notify subscribers - adapter.listen(function (message) { - var packaged = {}; - packaged[SOURCE] = {}; - packaged[SOURCE][message.id] = - new ExampleTelemetrySeries([message.value]); - (subscribers[message.id] || []).forEach(function (cb) { - cb(packaged); + }); + + // Stop sending telemetry updates for this connection when closed + ws.on('close', function () { + listeners = listeners.filter(function (listener) { + return listener !== notifySubscribers; }); }); - - return { - requestTelemetry: function (requests) { - var packaged = {}, - relevantReqs = requests.filter(matchesSource); - - // Package historical telemetry that has been received - function addToPackage(history) { - packaged[SOURCE][history.id] = - new ExampleTelemetrySeries(history.value); - } - - // Retrieve telemetry for a specific measurement - function handleRequest(request) { - var key = request.key; - return adapter.history(key).then(addToPackage); - } - - packaged[SOURCE] = {}; - return $q.all(relevantReqs.map(handleRequest)) - .then(function () { return packaged; }); - }, - subscribe: function (callback, requests) { - var keys = requests.filter(matchesSource) - .map(function (req) { return req.key; }); - - function notCallback(cb) { - return cb !== callback; - } - - function unsubscribe(key) { - subscribers[key] = - (subscribers[key] || []).filter(notCallback); - if (subscribers[key].length < 1) { - adapter.unsubscribe(key); - } - } - - keys.forEach(function (key) { - subscribers[key] = subscribers[key] || []; - adapter.subscribe(key); - subscribers[key].push(callback); - }); - - return function () { - keys.forEach(unsubscribe); - }; - } - }; + + // Notify subscribers when telemetry is updated + listeners.push(notifySubscribers); } + + update(); + setInterval(update, CONFIG.interval); + + wss.on('connection', handleConnection); + + console.log("Example spacecraft running on port "); + console.log("Press Enter to toggle thruster state."); + process.stdin.on('data', function (data) { + spacecraft['prop.thrusters'] = + (spacecraft['prop.thrusters'] === "OFF") ? "ON" : "OFF"; + console.log("Thrusters " + spacecraft["prop.thrusters"]); + }); + }()); +__tutorial-server/app.js__ - return ExampleTelemetryProvider; +For purposes of this tutorial, how this server has been implemented is +not important; it has just enough functionality to resemble a WebSocket +interface to a real telemetry system, and niceties such as error-handling +have been omitted. (For more information on using WebSockets, both in the +client and on the server, +[https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API]() is an +excellent starting point.) + +What does matter for this tutorial is the interfaces that are exposed. Once a +WebSocket connection has been established to this server, it accepts plain text +messages in the following formats, and issues JSON-formatted responses. + +The requests it handles are: + +* `dictionary`: Responds with a JSON response with the following fields: + * `type`: “dictionary” + * `value`: … the telemetry dictionary (see below) … +* `subscribe `: Subscribe to new telemetry data for the measurement with +the provided identifier. The server will begin sending messages of the +following form: + * `type`: “data” + * `id`: The identifier for the measurement. + * `value`: An object containing the actual measurement, in two fields: + * `timestamp`: A UNIX timestamp (in milliseconds) for the “measurement” + * `value`: The data value for the measurement (either a number, or a + string) +* `unsubscribe `: Stop receiving new data for the identified measurement. +* `history `: Request a history of all telemetry data for the identified +measurement. + * `type`: “history” + * `id`: The identifier for the measurement. + * `value`: An array of objects containing the actual measurement, each of + which having two fields: + * `timestamp`: A UNIX timestamp (in milliseconds) for the “measurement” + * `value`: The data value for the measurement (either a number, or + a string) + +(Note that the term “measurement” is used to describe a distinct data series +within this system; in other systems, these have been called channels, +mnemonics, telemetry points, or other names. No preference is made here; +Open MCT Web is easily adapted to use the terminology appropriate to your +system.) +Additionally, while running the server from the terminal we can toggle the +state of the “spacecraft” by hitting enter; this will turn the “thrusters” +on and off, having observable changes in telemetry. + +The telemetry dictionary referenced previously is contained in a separate file, +used by the server. It uses a custom format and, for purposes of example, +contains three “subsystems” containing a mix of numeric and string-based +telemetry. + + { + "name": "Example Spacecraft", + "identifier": "sc", + "subsystems": [ + { + "name": "Propulsion", + "identifier": "prop", + "measurements": [ + { + "name": "Fuel", + "identifier": "prop.fuel", + "units": "kilograms", + "type": "float" + }, + { + "name": "Thrusters", + "identifier": "prop.thrusters", + "units": "None", + "type": "string" + } + ] + }, + { + "name": "Communications", + "identifier": "comms", + "measurements": [ + { + "name": "Received", + "identifier": "comms.recd", + "units": "bytes", + "type": "integer" + }, + { + "name": "Sent", + "identifier": "comms.sent", + "units": "bytes", + "type": "integer" + } + ] + }, + { + "name": "Power", + "identifier": "pwr", + "measurements": [ + { + "name": "Generator Temperature", + "identifier": "pwr.temp", + "units": "\u0080C", + "type": "float" + }, + { + "name": "Generator Current", + "identifier": "pwr.c", + "units": "A", + "type": "float" + }, + { + "name": "Generator Voltage", + "identifier": "pwr.v", + "units": "V", + "type": "float" + } + ] + } + ] } -); +__tutorial-server/dictionary.json__ +It should be noted that neither the interface for the example server nor the +dictionary format are expected by Open MCT Web; rather, these are intended to +stand in for some existing source of telemetry data to which we wish to adapt +Open MCT Web. +We can run this example server by: -tutorials/telemetry/src/ExampleTelemetryProvider.js + cd tutorial-server + npm install ws + node app.js - A quick summary of these changes: +To verify that this is running and try out its interface, we can use a tool +like [https://www.npmjs.com/package/wscat](): -First, we maintain current subscribers (callbacks) in an object containing key-value pairs, where keys are request key properties, and values are callback arrays. -We listen to new data coming in from the server adapter, and invoke any relevant callbacks when this happens. We package the data in the same manner that historical telemetry is packaged (even though in this case we are providing single-element series objects.) -Finally, in our subscribe method we add callbacks to the lists of active subscribers. This method is expected to return a function which terminates the subscription when called, so we do some work to remove subscribers in this situations. When our subscriber count for a given measurement drops to zero, we issue an unsubscribe request. (We don’t take any care to avoid issuing multiple subscribe requests to the server, because we happen to know that the server can handle this.) + wscat -c ws://localhost:8081 + connected (press CTRL+C to quit) + > dictionary + < {"type":"dictionary","value":{"name":"Example Spacecraft","identifier":"sc","subsystems":[{"name":"Propulsion","identifier":"prop","measurements":[{"name":"Fuel","identifier":"prop.fuel","units":"kilograms","type":"float"},{"name":"Thrusters","identifier":"prop.thrusters","units":"None","type":"string"}]},{"name":"Communications","identifier":"comms","measurements":[{"name":"Received","identifier":"comms.recd","units":"bytes","type":"integer"},{"name":"Sent","identifier":"comms.sent","units":"bytes","type":"integer"}]},{"name":"Power","identifier":"pwr","measurements":[{"name":"Generator Temperature","identifier":"pwr.temp","units":"€C","type":"float"},{"name":"Generator Current","identifier":"pwr.c","units":"A","type":"float"},{"name":"Generator Voltage","identifier":"pwr.v","units":"V","type":"float"}]}]}} - Running Open MCT Web again, we can still plot our historical telemetry - but now we also see that it updates in real-time as more data comes in from the server. +Now that the example server’s interface is reasonably well-understood, a plugin +can be written to adapt Open MCT Web to utilize it. + +### Step 1. Add a Top-level Object + +Since Open MCT Web uses an “object-first” approach to accessing data, before +we’ll be able to do anything with this new data source, we’ll need to have a +way to explore the available measurements in the tree. In this step, we will +add a top-level object which will serve as a container; in the next step, we +will populate this with the contents of the telemetry dictionary (which we +will retrieve from the server.) + + { + "name": "Example Telemetry Adapter", + "extensions": { + "types": [ + { + "name": "Spacecraft", + "key": "example.spacecraft", + "glyph": "o" + } + ], + "roots": [ + { + "id": "example:sc", + "priority": "preferred", + "model": { + "type": "example.spacecraft", + "name": "My Spacecraft", + "composition": [] + } + } + ] + } + } +__tutorials/telemetry/bundle.json__ + +Here, we’ve created our initial telemetry plugin. This exposes a new domain +object type (the “Spacecraft”, which will be represented by the contents of the +telemetry dictionary) and also adds one instance of it as a root-level object +(by declaring an extension of category roots.) We have also set priority to +preferred so that this shows up near the top, instead of below My Items. + +If we include this in our set of active bundles: + + [ + "platform/framework", + "platform/core", + "platform/representation", + "platform/commonUI/about", + "platform/commonUI/browse", + "platform/commonUI/edit", + "platform/commonUI/dialog", + "platform/commonUI/general", + "platform/containment", + "platform/telemetry", + "platform/features/layout", + "platform/features/pages", + "platform/features/plot", + "platform/features/scrolling", + "platform/forms", + "platform/persistence/queue", + "platform/policy", + + "example/persistence", + "example/generator" + ] + [ + "platform/framework", + "platform/core", + "platform/representation", + "platform/commonUI/about", + "platform/commonUI/browse", + "platform/commonUI/edit", + "platform/commonUI/dialog", + "platform/commonUI/general", + "platform/containment", + "platform/telemetry", + "platform/features/layout", + "platform/features/pages", + "platform/features/plot", + "platform/features/scrolling", + "platform/forms", + "platform/persistence/queue", + "platform/policy", + + "example/persistence", + "example/generator", + + + "tutorials/telemetry" + ] +__bundles.json__ + +...we will be able to reload Open MCT Web and see that it is present: + +![](images/telemetry-1.png) + +Now, we have somewhere in the UI to put the contents of our telemetry +dictionary. + +### Step 2. Expose the Telemetry Dictionary + +In order to expose the telemetry dictionary, we first need to read it from the +server. Our first step will be to add a service that will handle interactions +with the server; this will not be used by Open MCT Web directly, but will be +used by subsequent components we add. + + /*global define,WebSocket*/ + + define( + [], + function () { + "use strict"; + + function ExampleTelemetryServerAdapter($q, wsUrl) { + var ws = new WebSocket(wsUrl), + dictionary = $q.defer(); + + // Handle an incoming message from the server + ws.onmessage = function (event) { + var message = JSON.parse(event.data); + + switch (message.type) { + case "dictionary": + dictionary.resolve(message.value); + break; + } + }; + + // Request dictionary once connection is established + ws.onopen = function () { + ws.send("dictionary"); + }; + + return { + dictionary: function () { + return dictionary.promise; + } + }; + } + + return ExampleTelemetryServerAdapter; + } + ); +__tutorials/telemetry/src/ExampleTelemetryServerAdapter.js__ + +When created, this service initiates a connection to the server, and begins +loading the dictionary. This will occur asynchronously, so the `dictionary()` +method it exposes returns a `Promise` for the loaded dictionary +(`dictionary.json` from above), using Angular’s `$q` +(see [https://docs.angularjs.org/api/ng/service/$q]().) Note that error- and +close-handling for this WebSocket connection have been omitted for brevity. + +Once the dictionary has been loaded, we will want to represent its contents +as domain objects. Specifically, we want subsystems to appear as objects +under My Spacecraft, and measurements to appear as objects within those +subsystems. This means that we need to convert the data from the dictionary +into domain object models, and expose these to Open MCT Web via a +`modelService`. + + /*global define*/ + + define( + function () { + "use strict"; + + var PREFIX = "example_tlm:", + FORMAT_MAPPINGS = { + float: "number", + integer: "number", + string: "string" + }; + + function ExampleTelemetryModelProvider(adapter, $q) { + var modelPromise, empty = $q.when({}); + + // Check if this model is in our dictionary (by prefix) + function isRelevant(id) { + return id.indexOf(PREFIX) === 0; + } + + // Build a domain object identifier by adding a prefix + function makeId(element) { + return PREFIX + element.identifier; + } + + // Create domain object models from this dictionary + function buildTaxonomy(dictionary) { + var models = {}; + + // Create & store a domain object model for a measurement + function addMeasurement(measurement) { + var format = FORMAT_MAPPINGS[measurement.type]; + models[makeId(measurement)] = { + type: "example.measurement", + name: measurement.name, + telemetry: { + key: measurement.identifier, + ranges: [{ + key: "value", + name: "Value", + units: measurement.units, + format: format + }] + } + }; + } + + // Create & store a domain object model for a subsystem + function addSubsystem(subsystem) { + var measurements = + (subsystem.measurements || []); + models[makeId(subsystem)] = { + type: "example.subsystem", + name: subsystem.name, + composition: measurements.map(makeId) + }; + measurements.forEach(addMeasurement); + } + + (dictionary.subsystems || []).forEach(addSubsystem); + + return models; + } + + // Begin generating models once the dictionary is available + modelPromise = adapter.dictionary().then(buildTaxonomy); + + return { + getModels: function (ids) { + // Return models for the dictionary only when they + // are relevant to the request. + return ids.some(isRelevant) ? modelPromise : empty; + } + }; + } + + return ExampleTelemetryModelProvider; + } + ); +__tutorials/telemetry/src/ExampleTelemetryModelProvider.js__ + +This script implements a `provider` for `modelService`; the `modelService` is a +composite service, meaning that multiple such services can exist side by side. +(For example, there is another `provider` for `modelService` that reads domain +object models from the persistence store.) + +Here, we read the dictionary using the server adapter from above; since this +will be loaded asynchronously, we use promise-chaining (see +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Chaining]()) +to take that result and build up an object mapping identifiers to new domain +object models. This is returned from our `modelService`, but only when the +request actually calls for identifiers that look like they’re from the +dictionary. This means that loading other models is not blocked by loading the +dictionary. (Note that the `modelService` contract allows us to return either a +sub- or superset of the requested models, so it is fine to always return the +whole dictionary.) + +Some notable points to call out here: + +* Every subsystem and every measurement from the dictionary has an `identifier` +field declared. We use this as part of the domain object identifier, but we +also prefix it with `example_tlm`:. This accomplishes a few things: + * We can easily tell whether an identifier is expected to be in the + dictionary or not. + * We avoid naming collisions with other model providers. + * Finally, Open MCT Web uses the colon prefix as a hint that this domain + object will not be in the persistence store. +* A couple of new types are introduced here (in the `type` field of the domain +object models we create); we will need to define these as extensions as well in +order for them to display correctly. +* The `composition` field of each subsystem contained the Open MCT Web +identifiers of all the measurements in that subsystem. This `composition` field +will be used by Open MCT Web to determine what domain objects contain other +domain objects (e.g. to populate the tree.) +* The `telemetry` field of each measurement will be used by Open MCT Web to +understand how to request and interpret telemetry data for this object. The +`key` is the machine-readable identifier for this measurement within the +telemetry system; the `ranges` provide metadata about the values for this data. +(A separate field, `domains`, provides metadata about timestamps or other +ordering properties of the data, but this will be the same for all +measurements, so we will define that later at the type level.) + * This field (whose contents will be merged atop the telemetry property we +define at the type-level) will serve as a template for later `telemetry` +requests to the `telemetryService`, so we’ll see the properties we define here +again later in Steps 3 and 4. + +This allows our telemetry dictionary to be expressed as domain object models +(and, in turn, as domain objects), but these objects still aren’t reachable. To +fix this, we will need another script which will add these subsystems to the +root-level object we added in Step 1. + + /*global define*/ + + define( + function () { + "use strict"; + + var TAXONOMY_ID = "example:sc", + PREFIX = "example_tlm:"; + + function ExampleTelemetryInitializer(adapter, objectService) { + // Generate a domain object identifier for a dictionary element + function makeId(element) { + return PREFIX + element.identifier; + } + + // When the dictionary is available, add all subsystems + // to the composition of My Spacecraft + function initializeTaxonomy(dictionary) { + // Get the top-level container for dictionary objects + // from a group of domain objects. + function getTaxonomyObject(domainObjects) { + return domainObjects[TAXONOMY_ID]; + } + + // Populate + function populateModel(taxonomyObject) { + return taxonomyObject.useCapability( + "mutation", + function (model) { + model.name = + dictionary.name; + model.composition = + dictionary.subsystems.map(makeId); + } + ); + } + + // Look up My Spacecraft, and populate it accordingly. + objectService.getObjects([TAXONOMY_ID]) + .then(getTaxonomyObject) + .then(populateModel); + } + + adapter.dictionary().then(initializeTaxonomy); + } + + return ExampleTelemetryInitializer; + } + ); +__tutorials/telemetry/src/ExampleTelemetryInitializer.js__ + +At the conclusion of Step 1, the top-level My Spacecraft object was empty. This +script will wait for the dictionary to be loaded, then load My Spacecraft (by +its identifier), and “mutate” it. The `mutation` capability allows changes to be +made to a domain object’s model. Here, we take this top-level object, update its +name to match what was in the dictionary, and set its `composition` to an array +of domain object identifiers for all subsystems contained in the dictionary +(using the same identifier prefix as before.) + +Finally, we wire in these changes by modifying our plugin’s `bundle.json` to +provide metadata about how these pieces interact (both with each other, and +with the platform): + + { + "name": "Example Telemetry Adapter", + "extensions": { + "types": [ + { + "name": "Spacecraft", + "key": "example.spacecraft", + "glyph": "o" + }, + { + + "name": "Subsystem", + + "key": "example.subsystem", + + "glyph": "o", + + "model": { "composition": [] } + + }, + + { + + "name": "Measurement", + + "key": "example.measurement", + + "glyph": "T", + + "model": { "telemetry": {} }, + + "telemetry": { + + "source": "example.source", + + "domains": [ + + { + + "name": "Time", + + "key": "timestamp" + + } + + ] + + } + + } + ], + "roots": [ + { + "id": "example:sc", + "priority": "preferred", + "model": { + "type": "example.spacecraft", + "name": "My Spacecraft", + "composition": [] + } + } + ], + + "services": [ + + { + + "key": "example.adapter", + + "implementation": "ExampleTelemetryServerAdapter.js", + + "depends": [ "$q", "EXAMPLE_WS_URL" ] + + } + + ], + + "constants": [ + + { + + "key": "EXAMPLE_WS_URL", + + "priority": "fallback", + + "value": "ws://localhost:8081" + + } + + ], + + "runs": [ + + { + + "implementation": "ExampleTelemetryInitializer.js", + + "depends": [ "example.adapter", "objectService" ] + + } + + ], + + "components": [ + + { + + "provides": "modelService", + + "type": "provider", + + "implementation": "ExampleTelemetryModelProvider.js", + + "depends": [ "example.adapter", "$q" ] + + } + + ] + } + } +__tutorials/telemetry/bundle.json__ + +A summary of what we’ve added here: + +* New type definitions have been added to represent Subsystems and Measurements, +respectively. + * Measurements have a `telemetry` field; this is similar to the `telemetry` + field added in the model, but contains properties that will be common among + all Measurements. In particular, the `source` field will be used later as a + symbolic identifier for the telemetry data source. + * We have also added some “initial models” for these two types using the + `model` field. While domain objects of these types cannot be created via the + Create menu, some policies will look at initial models to predict what + capabilities domain objects of certain types would have, so we want to + ensure that Subsystems and Measurements will be recognized as having + `composition` and `telemetry` capabilities, respectively. +* The adapter to the WebSocket server has been added as a service with the +symbolic name `example.adapter`; it is depended-upon elsewhere within this +plugin. +* A constant, `EXAMPLE_WS_URL`, is defined, and depended-upon by +`example.server`. Setting `priority` to `fallback` means this constant will be +overridden if defined anywhere else, allowing configuration bundles to specify +different URLs for the WebSocket connection. +* The initializer script is registered using the `runs` category of extension, +to ensure that this executes (and populates the contents of the top-level My +Spacecraft object) once Open MCT Web is started. + * This depends upon the `example.adapter` service we exposed above, as well + as Angular’s `$q`; these services will be made available in the constructor + call. +* Finally, the `modelService` provider which presents dictionary elements as +domain object models is exposed. Since `modelService` is a composite service, +this is registered under the extension category `components`. + * As with the initializer, this depends upon the `example.adapter` service + we exposed above, as well as Angular’s `$q`; these services will be made + available in the constructor call. + +Now if we run Open MCT Web (assuming our example telemetry server is also +running) and expand our top-level node completely, we see the contents of our +dictionary: + +[](images/telemetry-2.png) + +Note that “My Spacecraft” has changed its name to “Example Spacecraft”, which +is the name it had in the dictionary. + +### Step 3. Historical Telemetry + +After Step 2, we are able to see our dictionary in the user interface and click +around our different measurements, but we don’t see any data. We need to give +ourselves the ability to retrieve this data from the server. In this step, we +will do so for the server’s historical telemetry. + +Our first step will be to add a method to our server adapter which allows us to +send history requests to the server: + + /*global define,WebSocket*/ + + define( + [], + function () { + "use strict"; + + function ExampleTelemetryServerAdapter($q, wsUrl) { + var ws = new WebSocket(wsUrl), + + histories = {}, + dictionary = $q.defer(); + + // Handle an incoming message from the server + ws.onmessage = function (event) { + var message = JSON.parse(event.data); + + switch (message.type) { + case "dictionary": + dictionary.resolve(message.value); + break; + + case "history": + + histories[message.id].resolve(message); + + delete histories[message.id]; + + break; + } + }; + + // Request dictionary once connection is established + ws.onopen = function () { + ws.send("dictionary"); + }; + + return { + dictionary: function () { + return dictionary.promise; + }, + + history: function (id) { + + histories[id] = histories[id] || $q.defer(); + + ws.send("history " + id); + + return histories[id].promise; + + } + }; + } + + return ExampleTelemetryServerAdapter; + } + ); + +__tutorials/telemetry/src/ExampleTelemetryServerAdapter.js__ + +When the `history` method is called, a new request is issued to the server for +historical telemetry, _unless_ a request for the same historical telemetry is +still pending. Similarly, when historical telemetry arrives for a given +identifier, the pending promise is resolved. + +This `history` method will be used by a `telemetryService` provider which we +will implement: + + /*global define*/ + + define( + ['./ExampleTelemetrySeries'], + function (ExampleTelemetrySeries) { + "use strict"; + + var SOURCE = "example.source"; + + function ExampleTelemetryProvider(adapter, $q) { + // Used to filter out requests for telemetry + // from some other source + function matchesSource(request) { + return (request.source === SOURCE); + } + + return { + requestTelemetry: function (requests) { + var packaged = {}, + relevantReqs = requests.filter(matchesSource); + + // Package historical telemetry that has been received + function addToPackage(history) { + packaged[SOURCE][history.id] = + new ExampleTelemetrySeries(history.value); + } + + // Retrieve telemetry for a specific measurement + function handleRequest(request) { + var key = request.key; + return adapter.history(key).then(addToPackage); + } + + packaged[SOURCE] = {}; + return $q.all(relevantReqs.map(handleRequest)) + .then(function () { return packaged; }); + }, + subscribe: function (callback, requests) { + return function () {}; + } + }; + } + + return ExampleTelemetryProvider; + } + ); +__tutorials/telemetry/src/ExampleTelemetryProvider.js__ + +The `requestTelemetry` method of a `telemetryService` is expected to take an +array of requests (each with `source` and `key` parameters, identifying the +general source of data and the specific element within that source, respectively) and +return a Promise for any telemetry data it knows of which satisfies those +requests, packaged in a specific way. This packaging is as an object containing +key-value pairs, where keys correspond to `source` properties of requests and +values are key-value pairs, where keys correspond to `key` properties of requests +and values are `TelemetrySeries` objects. (We will see our implementation +below.) + +To do this, we create a container for our telemetry source, and consult the +adapter to get telemetry histories for any relevant requests, then package +them as they come in. The `$q.all` method is used to return a single Promise +that will resolve only when all histories have been packaged. Promise-chaining +is used to ensure that the resolved value will be the fully-packaged data. + +It is worth mentioning here that the `requests` we receive should look a little +familiar. When Open MCT Web generates a `request` object associated with a +domain object, it does so by merging together three JavaScript objects: + +* First, the `telemetry` property from that domain object’s type definition. +* Second, the `telemetry` property from that domain object’s model. +* Finally, the `request` object that was passed in via that domain object’s +`telemetry` capability. + +As such, the `source` and `key` properties we observe here will come from the +type definition and domain object model, respectively, as we specified them +during Step 2. (Or, they might come from somewhere else entirely, if we have +other telemetry-providing domain objects in our system; that is something we +check for using the `source` property.) + +Finally, note that we also have a subscribe method, to satisfy the interface of +telemetryService, but this subscribe method currently does nothing. + +This script uses an `ExampleTelemetrySeries` class, which looks like: + + /*global define*/ + + define( + function () { + "use strict"; + + function ExampleTelemetrySeries(data) { + return { + getPointCount: function () { + return data.length; + }, + getDomainValue: function (index) { + return (data[index] || {}).timestamp; + }, + getRangeValue: function (index) { + return (data[index] || {}).value; + } + }; + } + + return ExampleTelemetrySeries; + } + ); +__tutorials/telemetry/src/ExampleTelemetrySeries.js__ + +This takes the array of telemetry values (as returned by the server) and wraps +it with the interface expected by the platform (the methods shown.) + +Finally, we expose this telemetryService provider declaratively: + + { + "name": "Example Telemetry Adapter", + "extensions": { + "types": [ + { + "name": "Spacecraft", + "key": "example.spacecraft", + "glyph": "o" + }, + { + "name": "Subsystem", + "key": "example.subsystem", + "glyph": "o", + "model": { "composition": [] } + }, + { + "name": "Measurement", + "key": "example.measurement", + "glyph": "T", + "model": { "telemetry": {} }, + "telemetry": { + "source": "example.source", + "domains": [ + { + "name": "Time", + "key": "timestamp" + } + ] + } + } + ], + "roots": [ + { + "id": "example:sc", + "priority": "preferred", + "model": { + "type": "example.spacecraft", + "name": "My Spacecraft", + "composition": [] + } + } + ], + "services": [ + { + "key": "example.adapter", + "implementation": "ExampleTelemetryServerAdapter.js", + "depends": [ "$q", "EXAMPLE_WS_URL" ] + } + ], + "constants": [ + { + "key": "EXAMPLE_WS_URL", + "priority": "fallback", + "value": "ws://localhost:8081" + } + ], + "runs": [ + { + "implementation": "ExampleTelemetryInitializer.js", + "depends": [ "example.adapter", "objectService" ] + } + ], + "components": [ + { + "provides": "modelService", + "type": "provider", + "implementation": "ExampleTelemetryModelProvider.js", + "depends": [ "example.adapter", "$q" ] + }, + + { + + "provides": "telemetryService", + + "type": "provider", + + "implementation": "ExampleTelemetryProvider.js", + + "depends": [ "example.adapter", "$q" ] + + } + ] + } + } +__tutorials/telemetry/bundle.json__ + +Now, if we navigate to one of our numeric measurements, we should see a plot of +its historical telemetry: + +![Telemetry](images/telemetry-3.png) + +We can now visualize our data, but it doesn’t update over time - we know the +server is continually producing new data, but we have to click away and come +back to see it. We can fix this by adding support for telemetry subscriptions. + +### Step 4. Real-time Telemetry + +Finally, we want to utilize the server’s ability to subscribe to telemetry +from Open MCT Web. To do this, first we want to expose some new methods for +this from our server adapter: + + /*global define,WebSocket*/ + + define( + [], + function () { + "use strict"; + + function ExampleTelemetryServerAdapter($q, wsUrl) { + var ws = new WebSocket(wsUrl), + histories = {}, + + listeners = [], + dictionary = $q.defer(); + + // Handle an incoming message from the server + ws.onmessage = function (event) { + var message = JSON.parse(event.data); + + switch (message.type) { + case "dictionary": + dictionary.resolve(message.value); + break; + case "history": + histories[message.id].resolve(message); + delete histories[message.id]; + break; + + case "data": + + listeners.forEach(function (listener) { + + listener(message); + + }); + + break; + } + }; + + // Request dictionary once connection is established + ws.onopen = function () { + ws.send("dictionary"); + }; + + return { + dictionary: function () { + return dictionary.promise; + }, + history: function (id) { + histories[id] = histories[id] || $q.defer(); + ws.send("history " + id); + return histories[id].promise; + }, + + subscribe: function (id) { + + ws.send("subscribe " + id); + + }, + + unsubscribe: function (id) { + + ws.send("unsubscribe " + id); + + }, + + listen: function (callback) { + + listeners.push(callback); + + } + }; + } + + return ExampleTelemetryServerAdapter; + } + ); +__tutorials/telemetry/src/ExampleTelemetryServerAdapter.js__ + +Here, we have added `subscribe` and `unsubscribe` methods which issue the +corresponding requests to the server. Seperately, we introduce the ability to +listen for `data` messages as they come in: These will contain the data associated +with these subscriptions. + +We then need only to utilize these methods from our `telemetryService`: + + /*global define*/ + + define( + ['./ExampleTelemetrySeries'], + function (ExampleTelemetrySeries) { + "use strict"; + + var SOURCE = "example.source"; + + function ExampleTelemetryProvider(adapter, $q) { + + var subscribers = {}; + + // Used to filter out requests for telemetry + // from some other source + function matchesSource(request) { + return (request.source === SOURCE); + } + + + // Listen for data, notify subscribers + + adapter.listen(function (message) { + + var packaged = {}; + + packaged[SOURCE] = {}; + + packaged[SOURCE][message.id] = + + new ExampleTelemetrySeries([message.value]); + + (subscribers[message.id] || []).forEach(function (cb) { + + cb(packaged); + + }); + + }); + + return { + requestTelemetry: function (requests) { + var packaged = {}, + relevantReqs = requests.filter(matchesSource); + + // Package historical telemetry that has been received + function addToPackage(history) { + packaged[SOURCE][history.id] = + new ExampleTelemetrySeries(history.value); + } + + // Retrieve telemetry for a specific measurement + function handleRequest(request) { + var key = request.key; + return adapter.history(key).then(addToPackage); + } + + packaged[SOURCE] = {}; + return $q.all(relevantReqs.map(handleRequest)) + .then(function () { return packaged; }); + }, + subscribe: function (callback, requests) { + + var keys = requests.filter(matchesSource) + + .map(function (req) { return req.key; }); + + + + function notCallback(cb) { + + return cb !== callback; + + } + + + + function unsubscribe(key) { + + subscribers[key] = + + (subscribers[key] || []).filter(notCallback); + + if (subscribers[key].length < 1) { + + adapter.unsubscribe(key); + + } + + } + + + + keys.forEach(function (key) { + + subscribers[key] = subscribers[key] || []; + + adapter.subscribe(key); + + subscribers[key].push(callback); + + }); + + + + return function () { + + keys.forEach(unsubscribe); + + }; + } + }; + } + + return ExampleTelemetryProvider; + } + ); +__tutorials/telemetry/src/ExampleTelemetryProvider.js__ + +A quick summary of these changes: + +First, we maintain current subscribers (callbacks) in an object containing +key-value pairs, where keys are request key properties, and values are callback +arrays. +We listen to new data coming in from the server adapter, and invoke any +relevant callbacks when this happens. We package the data in the same manner +that historical telemetry is packaged (even though in this case we are +providing single-element series objects.) +Finally, in our `subscribe` method we add callbacks to the lists of active +subscribers. This method is expected to return a function which terminates the +subscription when called, so we do some work to remove subscribers in this +situations. When our subscriber count for a given measurement drops to zero, +we issue an unsubscribe request. (We don’t take any care to avoid issuing +multiple subscribe requests to the server, because we happen to know that the +server can handle this.) + +Running Open MCT Web again, we can still plot our historical telemetry - but +now we also see that it updates in real-time as more data comes in from the +server. From fa487e026ec1d1b1de633730498ab4b6310ac589 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Mon, 12 Oct 2015 20:09:02 -0700 Subject: [PATCH 050/488] Fixed code formatting error --- docs/src/index.html | 3 ++- docs/src/tutorials/index.md | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/src/index.html b/docs/src/index.html index e84b405234..e80a6138b2 100644 --- a/docs/src/index.html +++ b/docs/src/index.html @@ -29,8 +29,9 @@ Sections: diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index 88bedd19cd..6ec8e8d3b3 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -22,7 +22,7 @@ In this section, we will cover the steps necessary to get a minimal Open MCT Web developer environment up and running. Once we have this, we will be able to proceed with writing plugins as described in this tutorial. -## Prerequisites +### Prerequisites This tutorial assumes you have the following software installed. Version numbers record what was used in writing this tutorial; the same steps should work with @@ -37,7 +37,7 @@ Open MCT Web can be run without any of these tools, provided suitable alternatives are taken; see the [Open MCT Web Developer Guide](../guide/index.md) for a more general overview of how to run and deploy a Open MCT Web application. -## Check out Open MCT Web Sources +### Check out Open MCT Web Sources First step is to check out Open MCT Web from the source repository. @@ -55,7 +55,7 @@ At this point, it will also be useful to branch off of Open MCT Web v0.6.2 git branch open-v0.6.2 git checkout -## Configuring Persistence +### Configuring Persistence In its default configuration, Open MCT Web will try to use ElasticSearch (expected to be deployed at /elastic on the same HTTP server running Open MCT @@ -69,7 +69,7 @@ To change this configuration, edit bundles.json (at the top level of the Open MCT Web repository) and replace platform/persistence/elastic with example/persistence. -### Before +#### Before [ "platform/framework", @@ -95,7 +95,7 @@ example/persistence. ] __bundles.json__ -### After +#### After [ "platform/framework", From e43a788a6d922fd57bc0650b39a8ebfee2b9382e Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Mon, 12 Oct 2015 20:11:31 -0700 Subject: [PATCH 051/488] Fixed code formatting error --- docs/src/tutorials/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index 6ec8e8d3b3..9a6d58fff2 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -180,6 +180,7 @@ to this plugin as tutorials/todo as well.) We will start with an “empty bundle } } + __tutorials/todo/bundle.json__ We will also include this in our list of active bundles. From 87f48aac354b835c2f09451919aaf2d13f46e544 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Mon, 12 Oct 2015 20:12:18 -0700 Subject: [PATCH 052/488] Fixed code formatting error --- docs/src/tutorials/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index 9a6d58fff2..d83431ddea 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -170,8 +170,8 @@ will be. The syntax of this file is described in more detail in the Open MCT Web Developer Guide. We will create this file in the directory tutorials/todo (we can hereafter refer -to this plugin as tutorials/todo as well.) We will start with an “empty bundle” -- one which exposes no extensions - which looks like: +to this plugin as tutorials/todo as well.) We will start with an “empty bundle”, +one which exposes no extensions - which looks like: { "name": "To-do Plugin", From 64fae21d1634c45ebb1926d706a8d70ec02d4061 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Mon, 12 Oct 2015 20:15:50 -0700 Subject: [PATCH 053/488] Fixed code formatting error --- docs/src/tutorials/index.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index d83431ddea..eebb989440 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -16,6 +16,13 @@ October 6, 2015 | 2.2 | Conversion to markdown | Andrew Henry # Introduction +## This document +This document contains a number of code examples in formatted code blocks. In +many cases these code blocks are repeated in order to highlight code that has +been added or removed as part of the tutorial. In these cases, any lines added +will be indicated with a '+' at the start of the line. Any lines removed will +be indicated with a '-'. + ## Setting Up Open MCT Web In this section, we will cover the steps necessary to get a minimal Open MCT Web From 53a3a2f459b3e632da25b4c5d337372395de6102 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Tue, 13 Oct 2015 10:25:20 -0700 Subject: [PATCH 054/488] Additional editing --- docs/src/tutorials/index.md | 64 ++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index eebb989440..9eb935b9e4 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -250,8 +250,9 @@ functionality, so we don’t see anything different, but if we run with logging enabled ([http://localhost:8080/?log=info]()) and check the browser console, we should see: -`Resolving extensions for bundle tutorials/todo(To-do Plugin)...which shows that -our plugin has loaded.` +`Resolving extensions for bundle tutorials/todo(To-do Plugin)` + +...which shows that our plugin has loaded. ### Step 2. Add a Domain Object Type @@ -337,16 +338,16 @@ __tutorials/todo/res/templates/todo.html__ A summary of what’s included: -At the top, we have some buttons that we will later wire in to allow the user to -filter down to either complete or incomplete tasks. -After that, we have a list of tasks. The scope variable model is the model of -the domain object being viewed; this contains all of the persistent state +* At the top, we have some buttons that we will later wire in to allow the user +to filter down to either complete or incomplete tasks. +* After that, we have a list of tasks. The scope variable `model` is the model +of the domain object being viewed; this contains all of the persistent state associated with that object. This model is effectively just a JSON document, so we can choose what goes into it (so long as we take care not to collide with platform-defined properties; see the Open MCT Web Developer Guide.) Here, we -assume that all tasks will be stored in a property tasks, and that each will be -an object containing a description (the readable summary of the task) and a -boolean completed flag. +assume that all tasks will be stored in a property `tasks`, and that each will be +an object containing a `description` (the readable summary of the task) and a +boolean `completed` flag. To expose this view in Open MCT Web, we need to declare it in our bundle definition: @@ -377,21 +378,22 @@ definition: } __tutorials/todo/bundle.json__ -Here, we’ve added another extension, this time belonging to category views. It +Here, we’ve added another extension, this time belonging to category `views`. It contains the following properties: -Its key is its machine-readable name; we’ve given it the same name here as the -domain object type, but could have chosen any unique name. The type property -tells Open MCT Web that this view is only applicable to domain objects of that -type. This means that we’ll see this view for To-do Lists that we create, but -not for other domain objects (such as Folders.) +* Its `key` is its machine-readable name; we’ve given it the same name here as +the domain object type, but could have chosen any unique name. -The glyph and name properties describe the icon and human-readable name for this -view to display in the UI where needed (if multiple views are available for -To-do Lists, the user will be able to choose one.) +* The `type` property tells Open MCT Web that this view is only applicable to +domain objects of that type. This means that we’ll see this view for To-do Lists +that we create, but not for other domain objects (such as Folders.) -Finally, the templateUrl points to the Angular template we wrote; this path is -relative to the bundle’s res folder. +* The `glyph` and `name` properties describe the icon and human-readable name +for this view to display in the UI where needed (if multiple views are available +for To-do Lists, the user will be able to choose one.) + +* Finally, the `templateUrl` points to the Angular template we wrote; this path is +relative to the bundle’s `res` folder. This template looks like it should display tasks, but we don’t have any way for the user to create these yet. As a temporary workaround to test the view, we @@ -500,17 +502,17 @@ __tutorials/todo/src/controllers/TodoController.js__ Here, we’ve defined three new functions and placed them in our `$scope`, which will make them available from the template: -`setVisibility` changes which tasks are meant to be visible. The first argument +* `setVisibility` changes which tasks are meant to be visible. The first argument is a boolean, which, if true, means we want to show everything; the second argument is the completion state we want to show (which is only relevant if the first argument is falsy.) -`toggleCompletion` changes whether or not a task is complete. We make the change -via the domain object’s mutation capability, and then persist the change via its -persistence capability. See the Open MCT Web Developer Guide for more -information on these capabilities. +* `toggleCompletion` changes whether or not a task is complete. We make the +change via the domain object’s `mutation` capability, and then persist the +change via its `persistence` capability. See the Open MCT Web Developer Guide +for more information on these capabilities. -`showTask` is meant to be used to help decide if a task should be shown, based +* `showTask` is meant to be used to help decide if a task should be shown, based on the current visibility settings. It is true when we have decided to show everything, or when the completion state matches the state we’ve chosen. (Note the use of the double-not !! to coerce the completed flag to a boolean, for @@ -601,12 +603,14 @@ In this extension definition we have: * A `key`, which again is a machine-readable identifier. This is the name that templates will reference. * An `implementation`, which refers to an AMD module. The path is relative to the -src directory within the bundle. +`src` directory within the bundle. * The `depends` property declares the dependencies of this controller. Here, we want Angular to inject `$scope`, the current Angular scope (which, going back to our controller, is expected as our first argument.) -If we reload the browser now, our To-do List looks much the same, but now we are able to filter down the visible list, and the changes we make will stick around if we go to My Items and come back. +If we reload the browser now, our To-do List looks much the same, but now we are +able to filter down the visible list, and the changes we make will stick around +if we go to My Items and come back. ### Step 5. Support Editing @@ -703,8 +707,8 @@ __tutorials/todo/bundle.json__ What we’ve stated here is that the To-Do List’s view will have a toolbar which contains two sections (which will be visually separated by a divider), each of which contains one button. The first is a button labelled “Add Task” that will -invoke an addTask method; the second is a button with a glyph (which will appear -as a trash can in Open MCT Web’s custom font set) which will invoke a removeTask +invoke an `addTask` method; the second is a button with a glyph (which will appear +as a trash can in Open MCT Web’s custom font set) which will invoke a `removeTask` method. For more information on forms and tool bars in Open MCT Web, see the Open MCT Web Developer Guide. From 5b617295e94cd352711465f0f4431600c17549db Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 13 Oct 2015 12:08:58 -0700 Subject: [PATCH 055/488] Did review of tutorials --- .../images/{add-tasl.png => add-task.png} | Bin docs/src/tutorials/index.md | 126 +++++++++--------- 2 files changed, 65 insertions(+), 61 deletions(-) rename docs/src/tutorials/images/{add-tasl.png => add-task.png} (100%) diff --git a/docs/src/tutorials/images/add-tasl.png b/docs/src/tutorials/images/add-task.png similarity index 100% rename from docs/src/tutorials/images/add-tasl.png rename to docs/src/tutorials/images/add-task.png diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index 9eb935b9e4..3211b2dd49 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -720,7 +720,7 @@ all the applicable controls, which means no controls at all. To support selection, we will need to make some changes to our controller: - + define(function () { + define(function () { + // Form to display when adding new tasks + var NEW_TASK_FORM = { + name: "Add a Task", @@ -734,7 +734,7 @@ To support selection, we will need to make some changes to our controller: + }] + }; - function TodoController($scope, dialogService) { + + function TodoController($scope, dialogService) { var showAll = true, showCompleted; @@ -745,21 +745,23 @@ To support selection, we will need to make some changes to our controller: return persistence && persistence.persist(); } - // Remove a task - function removeTaskAtIndex(taskIndex) { - $scope.domainObject.useCapability('mutation', function (model) { - model.tasks.splice(taskIndex, 1); - }); - persist(); - } + + // Remove a task + + function removeTaskAtIndex(taskIndex) { + + $scope.domainObject.useCapability('mutation', function + + (model) { + + model.tasks.splice(taskIndex, 1); + + }); + + persist(); + + } - // Add a task - function addNewTask(task) { - $scope.domainObject.useCapability('mutation', function (model) { - model.tasks.push(task); - }); - persist(); - } + + // Add a task + + function addNewTask(task) { + + $scope.domainObject.useCapability('mutation', function + + (model) { + + model.tasks.push(task); + + }); + + persist(); + + } // Change which tasks are visible $scope.setVisibility = function (all, completed) { @@ -782,25 +784,25 @@ To support selection, we will need to make some changes to our controller: }; // Handle selection state in edit mode - if ($scope.selection) { - // Expose the ability to select tasks - $scope.selectTask = function (taskIndex) { - $scope.selection.select({ - removeTask: function () { - removeTaskAtIndex(taskIndex); - $scope.selection.deselect(); - } - }); - }; + + if ($scope.selection) { + + // Expose the ability to select tasks + + $scope.selectTask = function (taskIndex) { + + $scope.selection.select({ + + removeTask: function () { + + removeTaskAtIndex(taskIndex); + + $scope.selection.deselect(); + + } + + }); + + }; - // Expose a view-level selection proxy - $scope.selection.proxy({ - addTask: function () { - dialogService.getUserInput(NEW_TASK_FORM, {}) - .then(addNewTask); - } - }); - } + + // Expose a view-level selection proxy + + $scope.selection.proxy({ + + addTask: function () { + + dialogService.getUserInput(NEW_TASK_FORM, {}) + + .then(addNewTask); + + } + + }); + + } } return TodoController; @@ -996,10 +998,10 @@ states in the controller: showCompleted = completed; }; - // Check if current visibility settings match - $scope.checkVisibility = function (all, completed) { - return showAll ? all : (completed === showCompleted); - }; + + // Check if current visibility settings match + + $scope.checkVisibility = function (all, completed) { + + return showAll ? all : (completed === showCompleted); + + }; // Toggle the completion state of a task $scope.toggleCompletion = function (taskIndex) { @@ -1024,14 +1026,15 @@ states in the controller: removeTaskAtIndex(taskIndex); $scope.selection.deselect(); }, - taskIndex: taskIndex + + taskIndex: taskIndex }); }; - // Expose a check for current selection state - $scope.isSelected = function (taskIndex) { - return ($scope.selection.get() || {}).taskIndex === taskIndex; - }; + + // Expose a check for current selection state + + $scope.isSelected = function (taskIndex) { + + return ($scope.selection.get() || {}).taskIndex === + + taskIndex; + + }; // Expose a view-level selection proxy $scope.selection.proxy({ @@ -1503,15 +1506,15 @@ __tutorials/bargraph/res/templates/bargraph.html__ Summarizing these changes: * Utilize the exposed `low`, `middle`, and `high` values to populate our labels -along the vertical axis. Additionally, use the toPercent function to position +along the vertical axis. Additionally, use the `toPercent` function to position these from the bottom. * Replace our three hard-coded bars with a repeater that looks at the `telemetryObjects` exposed by the controller and adds one bar each. -* Position the dashed tick-line using the middle value and the `toPercent` -function, lining it up with its `label` to the left. +* Position the dashed tick-line using the `middle` value and the `toPercent` +function, lining it up with its label to the left. * At the bottom, repeat a set of labels for the telemetry-providing domain objects, with matching alignment to the bars above. We use an existing -representation, label, to make this easier. +representation, `label`, to make this easier. Finally, we expose our controller from our bundle definition. Note that the depends declaration includes both `$scope` as well as the `telemetryHandler` @@ -1583,10 +1586,10 @@ telemetry-providing domain object, as percentages. + var value = handle.getRangeValue(telemetryObject); + return $scope.toPercent(Math.min($scope.middle, value)); + } - $scope.getTop = function (telemetryObject) { - var value = handle.getRangeValue(telemetryObject); - return 100 - $scope.toPercent(Math.max($scope.middle, value)); - } + + $scope.getTop = function (telemetryObject) { + + var value = handle.getRangeValue(telemetryObject); + + return 100 - $scope.toPercent(Math.max($scope.middle, value)); + + } // Use the telemetryHandler to get telemetry objects here handle = telemetryHandler.handle($scope.domainObject, function () { @@ -1816,7 +1819,7 @@ A summary of these changes: * First, read `low`, `middle`, and `high` from the view configuration instead of initializing them to explicit values. This is placed into its own function, since it will be called a lot. -* The function 'setDefault' is included; it will be used to set the default +* The function `setDefault` is included; it will be used to set the default values for `low`, `middle`, and `high` in the view configuration, but only if they aren’t present. * The tool bar will treat properties in a view proxy as getter-setters if @@ -2218,10 +2221,10 @@ __bundles.json__ ...we will be able to reload Open MCT Web and see that it is present: -![](images/telemetry-1.png) +![Telemetry](images/telemetry-1.png) Now, we have somewhere in the UI to put the contents of our telemetry -dictionary. +dictionary. ### Step 2. Expose the Telemetry Dictionary @@ -2590,7 +2593,8 @@ Now if we run Open MCT Web (assuming our example telemetry server is also running) and expand our top-level node completely, we see the contents of our dictionary: -[](images/telemetry-2.png) +![Telemetry 2](images/telemetry-2.png) + Note that “My Spacecraft” has changed its name to “Example Spacecraft”, which is the name it had in the dictionary. @@ -2742,8 +2746,8 @@ during Step 2. (Or, they might come from somewhere else entirely, if we have other telemetry-providing domain objects in our system; that is something we check for using the `source` property.) -Finally, note that we also have a subscribe method, to satisfy the interface of -telemetryService, but this subscribe method currently does nothing. +Finally, note that we also have a `subscribe` method, to satisfy the interface of +`telemetryService`, but this `subscribe` method currently does nothing. This script uses an `ExampleTelemetrySeries` class, which looks like: @@ -2775,7 +2779,7 @@ __tutorials/telemetry/src/ExampleTelemetrySeries.js__ This takes the array of telemetry values (as returned by the server) and wraps it with the interface expected by the platform (the methods shown.) -Finally, we expose this telemetryService provider declaratively: +Finally, we expose this `telemetryService` provider declaratively: { "name": "Example Telemetry Adapter", @@ -3029,14 +3033,14 @@ __tutorials/telemetry/src/ExampleTelemetryProvider.js__ A quick summary of these changes: -First, we maintain current subscribers (callbacks) in an object containing +* First, we maintain current subscribers (callbacks) in an object containing key-value pairs, where keys are request key properties, and values are callback arrays. -We listen to new data coming in from the server adapter, and invoke any +* We listen to new data coming in from the server adapter, and invoke any relevant callbacks when this happens. We package the data in the same manner that historical telemetry is packaged (even though in this case we are providing single-element series objects.) -Finally, in our `subscribe` method we add callbacks to the lists of active +* Finally, in our `subscribe` method we add callbacks to the lists of active subscribers. This method is expected to return a function which terminates the subscription when called, so we do some work to remove subscribers in this situations. When our subscriber count for a given measurement drops to zero, From 60dda8a7a4216703c2c6a59401fc38f31d391098 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 9 Oct 2015 15:27:40 -0700 Subject: [PATCH 056/488] Updated --- .../dialog/res/templates/message.html | 9 ++++- platform/commonUI/dialog/src/DialogService.js | 24 ++++++++++--- platform/commonUI/general/bundle.json | 2 +- .../general/res/templates/message-banner.html | 36 +++++++++---------- .../src/controllers/BannerController.js | 10 ++++-- platform/commonUI/notification/bundle.json | 7 +++- .../notification/src/NotificationService.js | 21 ++++++----- .../test/NotificationServiceSpec.js | 16 ++++----- testing/dialogTest/bundle.json | 1 + .../src/NotificationLaunchController.js | 28 +++++++++++---- 10 files changed, 101 insertions(+), 53 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index 876a2d956a..5ebdf7b757 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -12,7 +12,7 @@
- {{ngModel.actionText}} message-severity-{{ngModel.severity}} + {{ngModel.actionText}}
{{dialogAction.label}} + + {{ngModel.primaryAction.label}} + +
+
\ No newline at end of file diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index 7e9d7f9b8e..0f95d151de 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -202,7 +202,9 @@ define( /** * A description of the model options that may be passed to the - * showBlockingMessage method + * showBlockingMessage method. Note that the DialogModel desribed + * here is shared with the Notifications framework. + * @see NotificationService * * @typedef DialogModel * @property {string} severity the severity level of this message. @@ -220,8 +222,13 @@ define( * impossible to provide an estimate for. Providing a true value for * this attribute will indicate to the user that the progress and * duration cannot be estimated. + * @property {DialogAction} primaryAction an action that will + * be added to the dialog as a button. The primary action can be + * used as the suggested course of action for the user. Making it + * distinct from other actions allows it to be styled differently, + * and treated preferentially in banner mode. * @property {DialogAction[]} actions a list of actions that will - * be added to the dialog as buttons. These buttons are + * be added to the dialog as buttons. */ /** @@ -235,8 +242,17 @@ define( * @returns {boolean} */ DialogService.prototype.showBlockingMessage = function(dialogModel) { - if (this.canShowDialog) { - this.getDialogResponse("overlay-blocking-message", dialogModel, undefined, "t-dialog-sm"); + if (this.canShowDialog(dialogModel)) { + // Add the overlay using the OverlayService, which + // will handle actual insertion into the DOM + this.overlay = this.overlayService.createOverlay( + "overlay-blocking-message", + dialogModel, + "t-dialog-sm" + ); + // Track that a dialog is already visible, to + // avoid spawning multiple dialogs at once. + this.dialogVisible = true; return true; } else { return false; diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index a006591195..ce3e5af710 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -104,7 +104,7 @@ { "key": "BannerController", "implementation": "controllers/BannerController.js", - "depends": ["$scope", "notificationService"] + "depends": ["$scope", "notificationService", "dialogService"] } ], "directives": [ diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html index a896b08702..a9053a1736 100644 --- a/platform/commonUI/general/res/templates/message-banner.html +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -1,20 +1,18 @@ -
-
- - - - - - -
+
+ + + + + +
\ No newline at end of file diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index 214283633c..8fe5ef04dd 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -25,10 +25,16 @@ define( [], function () { "use strict"; - function BannerController($scope, notificationService){ + function BannerController($scope, notificationService, dialogService) { $scope.active = notificationService.active; - $scope.dismiss = function(notification){ + $scope.dismiss = function(notification) { notificationService.dismissOrMinimize(notification); + }; + $scope.maximize = function(notification) { + notification.cancel = function(){ + dialogService.dismiss(); + } + dialogService.showBlockingMessage(notification); } } return BannerController; diff --git a/platform/commonUI/notification/bundle.json b/platform/commonUI/notification/bundle.json index 92301a72d6..5f70f5a0bf 100644 --- a/platform/commonUI/notification/bundle.json +++ b/platform/commonUI/notification/bundle.json @@ -4,6 +4,10 @@ { "key": "DEFAULT_AUTO_DISMISS", "value": 3000 + }, + { + "key": "FORCE_AUTO_DISMISS", + "value": 1000 } ], "controllers": [ @@ -17,7 +21,8 @@ { "key": "notificationService", "implementation": "NotificationService.js", - "depends": [ "$timeout", "DEFAULT_AUTO_DISMISS" ] + "depends": [ "$timeout", "DEFAULT_AUTO_DISMISS", + "FORCE_AUTO_DISMISS" ] } ] } diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index b4aabf2de6..c2386948eb 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -59,7 +59,7 @@ define( * @property {number} progress The completion status of a task * represented numerically * @property {MessageSeverity} messageSeverity The importance of the - * message (eg. error, success) + * message (eg. error, info) * @property {boolean} unknownProgress a boolean indicating self the * progress of the underlying task is unknown. This will result in a * visually distinct progress bar. @@ -84,10 +84,11 @@ define( * @memberof platform/commonUI/notification * @constructor */ - function NotificationService($timeout, DEFAULT_AUTO_DISMISS) { + function NotificationService($timeout, DEFAULT_AUTO_DISMISS, FORCE_AUTO_DISMISS) { this.notifications = []; this.$timeout = $timeout; this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS; + this.FORCE_AUTO_DISMISS = FORCE_AUTO_DISMISS; /* * A context in which to hold the active notification and a @@ -105,12 +106,12 @@ define( }; /** - * A convenience method for success notifications. Notifications + * A convenience method for info notifications. Notifications * created via this method will be auto-dismissed after a default * wait period * @param {Notification} notification The notification to display */ - NotificationService.prototype.success = function (notification) { + NotificationService.prototype.info = function (notification) { notification.autoDismiss = notification.autoDismiss || true; notification.severity = MessageSeverity.INFO; this.notify(notification); @@ -145,17 +146,15 @@ define( If there is already an active notification, time it out. If it's already got a timeout in progress (either because it has had timeout forced because of a queue of messages, or it had an - autodismiss specified), leave it to run. + autodismiss specified), leave it to run. Otherwise force a + timeout. This notifcation has been added to queue and will be serviced as soon as possible. */ - timeout = notification.autoDismiss ? - notification.autoDismiss : - this.DEFAULT_AUTO_DISMISS; this.active.timeout = this.$timeout(function () { self.dismissOrMinimize(self.active.notification); - }, timeout); + }, this.FORCE_AUTO_DISMISS); } }; @@ -217,7 +216,7 @@ define( /** * Minimize a notification. The notification will still be available * from the notification list. Typically notifications with a - * severity of 'success' should not be minimized, but rather + * severity of 'info' should not be minimized, but rather * dismissed. If you're not sure which is appropriate, * use {@link NotificationService#dismissOrMinimize} * @see dismiss @@ -236,7 +235,7 @@ define( /** * Completely removes a notification. This will dismiss it from the * message banner and remove it from the list of notifications. - * Typically only notifications with a severity of success should be + * Typically only notifications with a severity of info should be * dismissed. If you're not sure whether to dismiss or minimize a * notification, use {@link NotificationService#dismissOrMinimize}. * dismiss diff --git a/platform/commonUI/notification/test/NotificationServiceSpec.js b/platform/commonUI/notification/test/NotificationServiceSpec.js index 1992865c8e..12540387b7 100644 --- a/platform/commonUI/notification/test/NotificationServiceSpec.js +++ b/platform/commonUI/notification/test/NotificationServiceSpec.js @@ -43,7 +43,7 @@ define( * 3) Calling .notify with autoDismiss results in an ERROR notification * being MINIMIZED after a timeout has elapsed DONE * - * 4) Calling .notify with an active success notification results in that + * 4) Calling .notify with an active info notification results in that * notification being auto-dismissed, and the new notification becoming * active. DONE * @@ -80,7 +80,7 @@ define( mockTimeout, mockAutoDismiss); successModel = { title: "Mock Success Notification", - severity: MessageSeverity.SUCCESS + severity: MessageSeverity.INFO }; errorModel = { title: "Mock Error Notification", @@ -124,11 +124,11 @@ define( it("auto-dismisses the previously active notification, making" + " the new notification active", function() { var activeNotification; - //First pre-load with a success message + //First pre-load with a info message notificationService.notify(successModel); activeNotification = notificationService.getActiveNotification(); - //Initially expect the active notification to be success + //Initially expect the active notification to be info expect(activeNotification).toBe(successModel); //Then notify of an error notificationService.notify(errorModel); @@ -140,20 +140,20 @@ define( }); it("auto-dismisses an active success notification, removing" + " it completely", function() { - //First pre-load with a success message + //First pre-load with a info message notificationService.notify(successModel); //Then notify of an error notificationService.notify(errorModel); expect(notificationService.notifications.length).toEqual(2); mockTimeout.mostRecentCall.args[0](); - //Previous success message should be completely dismissed + //Previous info message should be completely dismissed expect(notificationService.notifications.length).toEqual(1); }); it("auto-minimizes an active error notification", function() { var activeNotification; //First pre-load with an error message notificationService.notify(errorModel); - //Then notify of success + //Then notify of info notificationService.notify(successModel); expect(notificationService.notifications.length).toEqual(2); //Mock the auto-minimize @@ -178,7 +178,7 @@ define( severity: MessageSeverity.ERROR }; - //First pre-load with a success message + //First pre-load with a info message notificationService.notify(errorModel); //Then notify of a third error notificationService.notify(error2); diff --git a/testing/dialogTest/bundle.json b/testing/dialogTest/bundle.json index fe4883ed7d..0e55477bda 100644 --- a/testing/dialogTest/bundle.json +++ b/testing/dialogTest/bundle.json @@ -27,6 +27,7 @@ "implementation": "NotificationLaunchController.js", "depends": [ "$scope", + "$timeout", "notificationService" ] } diff --git a/testing/dialogTest/src/NotificationLaunchController.js b/testing/dialogTest/src/NotificationLaunchController.js index 4bb71074c3..99caa8a71a 100644 --- a/testing/dialogTest/src/NotificationLaunchController.js +++ b/testing/dialogTest/src/NotificationLaunchController.js @@ -26,10 +26,11 @@ define( function (MessageSeverity) { "use strict"; - function NotificationLaunchController($scope, notificationService) { + function NotificationLaunchController($scope, $timeout, notificationService) { + var messageCounter = 1; $scope.newSuccess = function(){ - notificationService.success({ + notificationService.info({ title: "Success notification!" }) }; @@ -37,9 +38,14 @@ define( $scope.newError = function(){ notificationService.notify({ - title: "Error notification!", - severity: MessageSeverity.ERROR - }) + title: "Error notification " + messageCounter++ + "!", + severity: MessageSeverity.ERROR, + primaryAction: { + label: 'Retry', + action: function() { + console.log('Retry clicked'); + } + }}); }; $scope.newProgress = function(){ @@ -52,7 +58,17 @@ define( }; - notificationService.notify(notification) + function incrementProgress(notification) { + notification.progress = Math.min(100, Math.floor(notification.progress + Math.random() * 30)); + notification.progressText = ["Estimated time remaining:" + + " about ", 60 - Math.floor((notification.progress / 100) * 60), " seconds"].join(" "); + if (notification.progress < 100) { + $timeout(function(){incrementProgress(notification)}, 1000); + } + } + + notificationService.notify(notification); + incrementProgress(notification); }; } From c0ff6de27bf3b63427af51cf84743475c5838a1b Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Sat, 10 Oct 2015 11:28:19 -0700 Subject: [PATCH 057/488] Added notification indicator --- .../dialog/res/templates/message.html | 5 +- .../res/templates/overlay-message-list.html | 8 +-- .../general/res/templates/message-banner.html | 2 +- platform/commonUI/notification/bundle.json | 16 +++++ .../res/notification-indicator.html | 7 +++ .../notification/src/NotificationIndicator.js | 50 ++++++++++++++++ .../src/NotificationIndicatorController.js | 59 +++++++++++++++++++ .../dialogTest/src/DialogLaunchController.js | 13 +--- .../src/NotificationLaunchController.js | 52 +++++++++++++++- 9 files changed, 188 insertions(+), 24 deletions(-) create mode 100644 platform/commonUI/notification/res/notification-indicator.html create mode 100644 platform/commonUI/notification/src/NotificationIndicator.js create mode 100644 platform/commonUI/notification/src/NotificationIndicatorController.js diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index 5ebdf7b757..78ae8be035 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -11,12 +11,9 @@
{{ngModel.hint}}
-
- {{ngModel.actionText}} -
+ ng-show="ngModel.progress !== undefined || ngModel.unknownProgress">
- - +
{{notifications.length}} Notifications + + {{notifications.length}} + \ No newline at end of file diff --git a/platform/commonUI/notification/src/NotificationIndicator.js b/platform/commonUI/notification/src/NotificationIndicator.js new file mode 100644 index 0000000000..b1d20e8264 --- /dev/null +++ b/platform/commonUI/notification/src/NotificationIndicator.js @@ -0,0 +1,50 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define,window*/ + +define( + [], + function () { + "use strict"; + + function NotificationIndicator() { + + } + + NotificationIndicator.template = 'notificationIndicatorTemplate'; + + NotificationIndicator.prototype.getGlyph = function () { + return "A"; + }; + NotificationIndicator.prototype.getGlyphClass = function () { + return 'caution'; + }; + NotificationIndicator.prototype.getText = function () { + return "Notifications"; + }; + NotificationIndicator.prototype.getDescription = function () { + return "Notifications"; + }; + + return NotificationIndicator; + } +); diff --git a/platform/commonUI/notification/src/NotificationIndicatorController.js b/platform/commonUI/notification/src/NotificationIndicatorController.js new file mode 100644 index 0000000000..ae417b1ba7 --- /dev/null +++ b/platform/commonUI/notification/src/NotificationIndicatorController.js @@ -0,0 +1,59 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define( + ['./MessageSeverity'], + function (MessageSeverity) { + "use strict"; + + function NotificationIndicatorController($scope, notificationService, dialogService) { + $scope.notifications = notificationService.notifications; + $scope.showNotificationsList = function(){ + + var model = { + title: "Messages", + severity: MessageSeverity.INFO, + actions: [ + { + label: "Done", + action: function () { + dialogService.dismiss(); + } + } + ], + messages: [] + }; + + model.messages = notificationService.notifications; + dialogService.getDialogResponse('overlay-message-list', { + dialog: model, + cancel: function(){ + dialogService.dismiss(); + } + }); + + }; + } + return NotificationIndicatorController; + } +); diff --git a/testing/dialogTest/src/DialogLaunchController.js b/testing/dialogTest/src/DialogLaunchController.js index 11bba39da2..bc8cdcb419 100644 --- a/testing/dialogTest/src/DialogLaunchController.js +++ b/testing/dialogTest/src/DialogLaunchController.js @@ -108,7 +108,6 @@ define( { label: "Done", action: function () { - $log.debug("Done pressed"); dialogService.dismiss(); } } @@ -173,19 +172,13 @@ define( } function dismiss() { - scope.$destroy(); - element.remove(); + dialogService.dismiss(); } - - //for (var i = 0; i < 10; i++) { - // model.messages.push(createMessage(i)); - //} + model.messages = notificationService.notifications; dialogService.getDialogResponse('overlay-message-list', { dialog: model, - cancel: function(){ - dialogService.dismiss(); - } + cancel: dismiss }); }; } diff --git a/testing/dialogTest/src/NotificationLaunchController.js b/testing/dialogTest/src/NotificationLaunchController.js index 99caa8a71a..8150c2cfe5 100644 --- a/testing/dialogTest/src/NotificationLaunchController.js +++ b/testing/dialogTest/src/NotificationLaunchController.js @@ -35,17 +35,65 @@ define( }) }; + function getExampleActionText() { + var actionTexts = [ + "Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.", + "Eros turpis, pulvinar turpis eros eu", + "Lundium nascetur a, lectus montes ac, parturient in natoque, duis risus risus pulvinar pid rhoncus, habitasse auctor natoque!" + ]; + return actionTexts[Math.floor(Math.random()*3)]; + } + + function getExampleActions() { + var actions = [ + { + label: "Try Again", + action: function () { + $log.debug("Try Again pressed"); + } + }, + { + label: "Remove", + action: function () { + $log.debug("Remove pressed"); + } + }, + { + label: "Cancel", + action: function () { + $log.debug("Cancel pressed"); + } + } + ]; + + // Randomly remove some actions off the top; leave at least one + actions.splice(0,Math.floor(Math.random() * actions.length)); + + return actions; + } + + function getExampleSeverity() { + var severities = [ + MessageSeverity.INFO, + MessageSeverity.ALERT, + MessageSeverity.ERROR + ]; + return severities[Math.floor(Math.random() * severities.length)]; + } + $scope.newError = function(){ notificationService.notify({ title: "Error notification " + messageCounter++ + "!", + hint: "An error has occurred", severity: MessageSeverity.ERROR, primaryAction: { label: 'Retry', action: function() { console.log('Retry clicked'); } - }}); + }, + actions: getExampleActions}); }; $scope.newProgress = function(){ @@ -54,7 +102,7 @@ define( title: "Progress notification!", severity: MessageSeverity.INFO, progress: 0, - progressUnknown: true + unknownProgress: false }; From 11264759eccd41d177748b58f3a9b04dc382e61e Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 13 Oct 2015 11:14:28 -0700 Subject: [PATCH 058/488] Added highest severity calculation in NotifiationService --- .../commonUI/notification/res/notification-indicator.html | 3 ++- platform/commonUI/notification/src/NotificationService.js | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/commonUI/notification/res/notification-indicator.html b/platform/commonUI/notification/res/notification-indicator.html index 1cf8f91881..f0c7ee48bb 100644 --- a/platform/commonUI/notification/res/notification-indicator.html +++ b/platform/commonUI/notification/res/notification-indicator.html @@ -1,4 +1,5 @@ - + ! {{notifications.length}} Notifications diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index c2386948eb..cadbea29f2 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -87,6 +87,7 @@ define( function NotificationService($timeout, DEFAULT_AUTO_DISMISS, FORCE_AUTO_DISMISS) { this.notifications = []; this.$timeout = $timeout; + this.highest ={ severity: MessageSeverity.INFO }; this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS; this.FORCE_AUTO_DISMISS = FORCE_AUTO_DISMISS; @@ -198,12 +199,18 @@ define( NotificationService.prototype.selectNextNotification = function () { var notification, i=0; + + this.highest.severity = MessageSeverity.INFO; + /* Loop through the notifications queue and find the first one self has not already been minimized (manually or otherwise). */ for (; i< this.notifications.length; i++) { notification = this.notifications[i]; + if (notification.severity > this.highest.severity){ + this.highest.severity = notification.severity; + } if (!notification.minimized && notification!== this.active.notification) { From b2a09599a07f3cb7a72f3e29bb2c60c9cc103f2f Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 13 Oct 2015 11:21:56 -0700 Subject: [PATCH 059/488] Modified messages dialog launcher --- platform/commonUI/dialog/res/templates/message.html | 3 +++ testing/dialogTest/res/dialog-launch.html | 3 +-- testing/dialogTest/src/NotificationLaunchController.js | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index 78ae8be035..ee2b60c58f 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -11,6 +11,9 @@
{{ngModel.hint}}
+
+ {{ngModel.actionText}} +
diff --git a/testing/dialogTest/res/dialog-launch.html b/testing/dialogTest/res/dialog-launch.html index 2b01ca60bb..1b117c2ebf 100644 --- a/testing/dialogTest/res/dialog-launch.html +++ b/testing/dialogTest/res/dialog-launch.html @@ -3,8 +3,7 @@ Known | Unknown | - Error | - Messages + Error Dialogs \ No newline at end of file diff --git a/testing/dialogTest/src/NotificationLaunchController.js b/testing/dialogTest/src/NotificationLaunchController.js index 8150c2cfe5..ccd2543252 100644 --- a/testing/dialogTest/src/NotificationLaunchController.js +++ b/testing/dialogTest/src/NotificationLaunchController.js @@ -93,7 +93,7 @@ define( console.log('Retry clicked'); } }, - actions: getExampleActions}); + actions: getExampleActions()}); }; $scope.newProgress = function(){ @@ -102,6 +102,7 @@ define( title: "Progress notification!", severity: MessageSeverity.INFO, progress: 0, + actionText: getExampleActionText(), unknownProgress: false }; From 2ba6f18c59cc58d34f1df188a886380ad2d09fe7 Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 13 Oct 2015 17:13:24 -0700 Subject: [PATCH 060/488] Making changes to implement display queue --- .../commonUI/dialog/res/templates/message.html | 3 ++- .../general/res/templates/message-banner.html | 5 ++++- .../notification/res/notification-indicator.html | 10 +++++++--- .../src/NotificationIndicatorController.js | 3 +++ .../notification/src/NotificationService.js | 2 +- testing/dialogTest/res/notification-launch.html | 1 + .../src/NotificationLaunchController.js | 15 +++++++++++++++ 7 files changed, 33 insertions(+), 6 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index ee2b60c58f..28bd09cbb1 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -1,7 +1,8 @@
diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html index d3d4a71ed5..5f032143b0 100644 --- a/platform/commonUI/general/res/templates/message-banner.html +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -1,5 +1,8 @@
+ class="l-message-banner s-message-banner" ng-class="{ + 'error': highest.severity===MessageSeverity.ERROR, + 'alert': highest.severity===MessageSeverity.ALERT }" + ng-click="maximize(active.notification)"> diff --git a/platform/commonUI/notification/res/notification-indicator.html b/platform/commonUI/notification/res/notification-indicator.html index f0c7ee48bb..79ceb97302 100644 --- a/platform/commonUI/notification/res/notification-indicator.html +++ b/platform/commonUI/notification/res/notification-indicator.html @@ -1,8 +1,12 @@ - - ! + - {{notifications.length}} Notifications + {{notifications.length}} + Notifications {{highest.severity}} {{notifications.length}} \ No newline at end of file diff --git a/platform/commonUI/notification/src/NotificationIndicatorController.js b/platform/commonUI/notification/src/NotificationIndicatorController.js index ae417b1ba7..9571b0f6a4 100644 --- a/platform/commonUI/notification/src/NotificationIndicatorController.js +++ b/platform/commonUI/notification/src/NotificationIndicatorController.js @@ -28,6 +28,9 @@ define( function NotificationIndicatorController($scope, notificationService, dialogService) { $scope.notifications = notificationService.notifications; + $scope.highest = notificationService.highest; + $scope.MessageSeverity = MessageSeverity; + $scope.showNotificationsList = function(){ var model = { diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index cadbea29f2..00457f959d 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -254,8 +254,8 @@ define( var index = this.notifications.indexOf(notification); if (index >= 0) { this.notifications.splice(index, 1); - this.setActiveNotification(this.selectNextNotification()); } + this.setActiveNotification(this.selectNextNotification()); }; /** diff --git a/testing/dialogTest/res/notification-launch.html b/testing/dialogTest/res/notification-launch.html index 73bc7c524f..e9b7e8f84e 100644 --- a/testing/dialogTest/res/notification-launch.html +++ b/testing/dialogTest/res/notification-launch.html @@ -3,6 +3,7 @@ Success | Error | + Alert | Progress Notifications diff --git a/testing/dialogTest/src/NotificationLaunchController.js b/testing/dialogTest/src/NotificationLaunchController.js index ccd2543252..14709579b3 100644 --- a/testing/dialogTest/src/NotificationLaunchController.js +++ b/testing/dialogTest/src/NotificationLaunchController.js @@ -96,6 +96,21 @@ define( actions: getExampleActions()}); }; + $scope.newAlert = function(){ + + notificationService.notify({ + title: "Error notification " + messageCounter++ + "!", + hint: "An error has occurred", + severity: MessageSeverity.ALERT, + primaryAction: { + label: 'Retry', + action: function() { + console.log('Retry clicked'); + } + }, + actions: getExampleActions()}); + }; + $scope.newProgress = function(){ var notification = { From 34ea3ad9bbcfa3304a5f20cfd9eb0ce9e66930f6 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Tue, 13 Oct 2015 17:12:23 -0700 Subject: [PATCH 061/488] [Frontend] Banner message animations; class renaming open #163 open #170 Added transition animations to message banners; Renamed $colorStatus* class names to map more closely to severity constants; --- .../general/res/sass/controls/_messages.scss | 56 +++-- .../espresso/res/css/theme-espresso.css | 221 +++++++++++------- .../themes/espresso/res/sass/_constants.scss | 6 +- .../themes/snow/res/css/theme-snow.css | 221 +++++++++++------- .../themes/snow/res/sass/_constants.scss | 6 +- 5 files changed, 319 insertions(+), 191 deletions(-) diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index aa26760c9a..740df6ba8d 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -51,11 +51,17 @@ .status-indicator { margin-right: $interiorMarginSm; } - &.ok .status-indicator { - color: $colorStatusOk; + &.ok .status-indicator, + &.info .status-indicator { + color: $colorStatusInfo; } + &.alert .status-indicator, + &.warning .status-indicator, &.caution .status-indicator { - color: $colorStatusCaution; + color: $colorStatusAlert; + } + &.error .status-indicator { + color: $colorStatusError; } .label { // Max-width silliness is necessary for width transition @@ -107,6 +113,22 @@ padding: 0 $interiorMargin 0 $interiorMargin; @include transform(translateX(-50%)); + &.minimized { + @include transition-property(left, opacity); + @include transition-duration(0.3s); + @include transition-timing-function(ease-in-out); + left: 0; + opacity: 0; + } + + &.new { + left: 50%; + opacity: 1; + &:not(.info) { + @include pulse(100ms, 10); + } + } + .banner-elem { @include flex(0 1 auto); margin-left: $interiorMargin; @@ -134,9 +156,10 @@ z-index: 10; } -.s-message-banner, -.s-message-banner .s-action { - @include trans-prop-nice(background-color, .25s); +.s-message-banner { + //@include transition-property(left, opacity); + //@include transition-duration(0.35s); + //@include transition-timing-function(ease-in-out); } .s-message-banner { @@ -146,6 +169,7 @@ a { color: inherit; } .s-action { @include border-radius($basicCr); + @include trans-prop-nice(background-color); } .close { opacity: 0.5; @@ -153,12 +177,18 @@ opacity: 1; } } - &.ok { - @include statusBannerColors($colorStatusOk); + &.ok, + &.info { + @include statusBannerColors($colorStatusInfo); } - &.caution { - @include statusBannerColors($colorStatusCaution); + &.caution, + &.warning, + &.alert { + @include statusBannerColors($colorStatusAlert); } + &.error { + @include statusBannerColors($colorStatusError); + } } @mixin messageBlock($iconW: 32px) { @@ -173,15 +203,15 @@ .message-severity-info .type-icon.message-type { &:before { content:"\e608"; } - color: $colorStatusOk; + color: $colorStatusInfo; } .message-severity-alert .type-icon.message-type { &:before { content:"\e610"; } - color: $colorStatusCaution; + color: $colorStatusAlert; } .message-severity-error .type-icon.message-type { &:before { content:"\21"; } - color: $colorStatusAlert; + color: $colorStatusError; } } /* Paths: diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 229e97e44b..13334809a2 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -2398,12 +2398,15 @@ label.checkbox.custom { .status.block .status-indicator { margin-right: 3px; } /* line 54, ../../../../general/res/sass/controls/_messages.scss */ - .status.block.ok .status-indicator { + .status.block.ok .status-indicator, .status.block.info .status-indicator { color: #62ba72; } - /* line 57, ../../../../general/res/sass/controls/_messages.scss */ - .status.block.caution .status-indicator { + /* line 58, ../../../../general/res/sass/controls/_messages.scss */ + .status.block.alert .status-indicator, .status.block.warning .status-indicator, .status.block.caution .status-indicator { color: #ffa66d; } - /* line 60, ../../../../general/res/sass/controls/_messages.scss */ + /* line 63, ../../../../general/res/sass/controls/_messages.scss */ + .status.block.error .status-indicator { + color: #d4585c; } + /* line 66, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; -o-transition-property: max-width; @@ -2419,7 +2422,7 @@ label.checkbox.custom { transition-timing-function: ease-in-out; overflow: hidden; max-width: 0px; } - /* line 66, ../../../../general/res/sass/controls/_messages.scss */ + /* line 72, ../../../../general/res/sass/controls/_messages.scss */ .status.block .count { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -2435,27 +2438,27 @@ label.checkbox.custom { transition-timing-function: ease-in-out; font-weight: bold; opacity: 1; } - /* line 72, ../../../../general/res/sass/controls/_messages.scss */ + /* line 78, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .label { max-width: 450px; width: auto; } - /* line 76, ../../../../general/res/sass/controls/_messages.scss */ + /* line 82, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .count { opacity: 0; } /* Styles for messages and message banners */ -/* line 84, ../../../../general/res/sass/controls/_messages.scss */ +/* line 90, ../../../../general/res/sass/controls/_messages.scss */ .message.block { -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; padding: 10px; } -/* line 88, ../../../../general/res/sass/controls/_messages.scss */ +/* line 94, ../../../../general/res/sass/controls/_messages.scss */ .message.error { background-color: rgba(255, 83, 58, 0.3); color: #ffaca0; } -/* line 94, ../../../../general/res/sass/controls/_messages.scss */ +/* line 100, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -2484,49 +2487,70 @@ label.checkbox.custom { -webkit-transform: translateX(-50%); transform: translateX(-50%); z-index: 10; } - /* line 110, ../../../../general/res/sass/controls/_messages.scss */ + /* line 116, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner.minimized { + -moz-transition-property: left, opacity; + -o-transition-property: left, opacity; + -webkit-transition-property: left, opacity; + transition-property: left, opacity; + -moz-transition-duration: 0.3s; + -o-transition-duration: 0.3s; + -webkit-transition-duration: 0.3s; + transition-duration: 0.3s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + left: 0; + opacity: 0; } + /* line 124, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner.new { + left: 50%; + opacity: 1; } + /* line 127, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner.new:not(.info) { + -moz-animation-name: pulse; + -webkit-animation-name: pulse; + animation-name: pulse; + -moz-animation-duration: 100ms; + -webkit-animation-duration: 100ms; + animation-duration: 100ms; + -moz-animation-direction: alternate; + -webkit-animation-direction: alternate; + animation-direction: alternate; + -moz-animation-iteration-count: 10; + -webkit-animation-iteration-count: 10; + animation-iteration-count: 10; + -moz-animation-timing-function: ease-in-out; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; } + /* line 132, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .banner-elem { -webkit-flex: 0 1 auto; flex: 0 1 auto; margin-left: 5px; } - /* line 114, ../../../../general/res/sass/controls/_messages.scss */ + /* line 136, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner a { display: inline-block; } - /* line 117, ../../../../general/res/sass/controls/_messages.scss */ + /* line 139, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .l-action { line-height: 15px; padding: 0 5px; } - /* line 121, ../../../../general/res/sass/controls/_messages.scss */ + /* line 143, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .close { cursor: pointer; font-size: 7px; width: 8px; } - /* line 127, ../../../../general/res/sass/controls/_messages.scss */ + /* line 149, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .l-progress-bar { height: 8px; line-height: 8px; width: 100px; } - /* line 133, ../../../../general/res/sass/controls/_messages.scss */ + /* line 155, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .progress-info { display: none; } -/* line 137, ../../../../general/res/sass/controls/_messages.scss */ -.s-message-banner, -.s-message-banner .s-action { - -moz-transition-property: background-color; - -o-transition-property: background-color; - -webkit-transition-property: background-color; - transition-property: background-color; - -moz-transition-duration: 0.25s; - -o-transition-duration: 0.25s; - -webkit-transition-duration: 0.25s; - transition-duration: 0.25s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; } - -/* line 142, ../../../../general/res/sass/controls/_messages.scss */ +/* line 165, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner { -moz-border-radius: 3px; -webkit-border-radius: 3px; @@ -2543,46 +2567,71 @@ label.checkbox.custom { /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .s-action:hover { background-color: gray; } - /* line 146, ../../../../general/res/sass/controls/_messages.scss */ + /* line 169, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner a { color: inherit; } - /* line 147, ../../../../general/res/sass/controls/_messages.scss */ + /* line 170, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .s-action { -moz-border-radius: 3px; -webkit-border-radius: 3px; - border-radius: 3px; } - /* line 150, ../../../../general/res/sass/controls/_messages.scss */ + border-radius: 3px; + -moz-transition-property: background-color; + -o-transition-property: background-color; + -webkit-transition-property: background-color; + transition-property: background-color; + -moz-transition-duration: 500ms; + -o-transition-duration: 500ms; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } + /* line 174, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .close { opacity: 0.5; } - /* line 152, ../../../../general/res/sass/controls/_messages.scss */ + /* line 176, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .close:hover { opacity: 1; } - /* line 156, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.ok { + /* line 180, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok, .s-message-banner.info { background-color: #285b31; color: #ccc; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.ok:hover { + .s-message-banner.ok:hover, .s-message-banner.info:hover { background-color: #387e44; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.ok .s-action { + .s-message-banner.ok .s-action, .s-message-banner.info .s-action { background-color: #18381e; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.ok .s-action:hover { + .s-message-banner.ok .s-action:hover, .s-message-banner.info .s-action:hover { background-color: #285b31; } - /* line 159, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.caution { + /* line 184, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution, .s-message-banner.warning, .s-message-banner.alert { background-color: #d35200; color: #ccc; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.caution:hover { + .s-message-banner.caution:hover, .s-message-banner.warning:hover, .s-message-banner.alert:hover { background-color: #ff6807; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.caution .s-action { + .s-message-banner.caution .s-action, .s-message-banner.warning .s-action, .s-message-banner.alert .s-action { background-color: #a03e00; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.caution .s-action:hover { + .s-message-banner.caution .s-action:hover, .s-message-banner.warning .s-action:hover, .s-message-banner.alert .s-action:hover { background-color: #d35200; } + /* line 189, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.error { + background-color: #751e21; + color: #ccc; } + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.error:hover { + background-color: #9d292c; } + /* line 31, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.error .s-action { + background-color: #4c1415; } + /* line 33, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.error .s-action:hover { + background-color: #751e21; } /* Paths: t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message > @@ -2597,7 +2646,7 @@ label.checkbox.custom { ... same as above bottom-bar */ -/* line 201, ../../../../general/res/sass/controls/_messages.scss */ +/* line 231, ../../../../general/res/sass/controls/_messages.scss */ .l-message { display: -webkit-flex; display: flex; @@ -2605,52 +2654,52 @@ label.checkbox.custom { flex-direction: row; -webkit-align-items: stretch; align-items: stretch; } - /* line 205, ../../../../general/res/sass/controls/_messages.scss */ + /* line 235, ../../../../general/res/sass/controls/_messages.scss */ .l-message .type-icon.message-type { -webkit-flex: 0 1 auto; flex: 0 1 auto; position: relative; } - /* line 210, ../../../../general/res/sass/controls/_messages.scss */ + /* line 240, ../../../../general/res/sass/controls/_messages.scss */ .l-message .message-contents { -webkit-flex: 1 1 auto; flex: 1 1 auto; margin-left: 25px; position: relative; } - /* line 216, ../../../../general/res/sass/controls/_messages.scss */ + /* line 246, ../../../../general/res/sass/controls/_messages.scss */ .l-message .message-contents .top-bar, .l-message .message-contents .message-body { margin-bottom: 20px; } -/* line 165, ../../../../general/res/sass/controls/_messages.scss */ +/* line 195, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .type-icon.message-type { text-shadow: rgba(0, 0, 0, 0.4) 0 1px 2px; color: #ccc; font-size: 80px; padding: 1px; width: 82px; } - /* line 167, ../../../../general/res/sass/controls/_messages.scss */ + /* line 197, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .type-icon.message-type:before { content: "\e608"; } -/* line 174, ../../../../general/res/sass/controls/_messages.scss */ +/* line 204, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-info .type-icon.message-type { color: #62ba72; } - /* line 175, ../../../../general/res/sass/controls/_messages.scss */ + /* line 205, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-info .type-icon.message-type:before { content: "\e608"; } -/* line 178, ../../../../general/res/sass/controls/_messages.scss */ +/* line 208, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-alert .type-icon.message-type { color: #ffa66d; } - /* line 179, ../../../../general/res/sass/controls/_messages.scss */ + /* line 209, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-alert .type-icon.message-type:before { content: "\e610"; } -/* line 182, ../../../../general/res/sass/controls/_messages.scss */ +/* line 212, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-error .type-icon.message-type { color: #d4585c; } - /* line 183, ../../../../general/res/sass/controls/_messages.scss */ + /* line 213, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-error .type-icon.message-type:before { content: "\21"; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 229, ../../../../general/res/sass/controls/_messages.scss */ + /* line 259, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .l-message, .t-message-single .bottom-bar { overflow: hidden; @@ -2661,40 +2710,40 @@ label.checkbox.custom { left: 0px; width: auto; height: auto; } - /* line 234, ../../../../general/res/sass/controls/_messages.scss */ + /* line 264, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .bottom-bar { top: auto; height: 24px; } } -/* line 165, ../../../../general/res/sass/controls/_messages.scss */ +/* line 195, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .type-icon.message-type { text-shadow: rgba(0, 0, 0, 0.4) 0 1px 2px; color: #ccc; font-size: 32px; padding: 1px; width: 34px; } - /* line 167, ../../../../general/res/sass/controls/_messages.scss */ + /* line 197, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .type-icon.message-type:before { content: "\e608"; } -/* line 174, ../../../../general/res/sass/controls/_messages.scss */ +/* line 204, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-info .type-icon.message-type { color: #62ba72; } - /* line 175, ../../../../general/res/sass/controls/_messages.scss */ + /* line 205, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-info .type-icon.message-type:before { content: "\e608"; } -/* line 178, ../../../../general/res/sass/controls/_messages.scss */ +/* line 208, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-alert .type-icon.message-type { color: #ffa66d; } - /* line 179, ../../../../general/res/sass/controls/_messages.scss */ + /* line 209, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-alert .type-icon.message-type:before { content: "\e610"; } -/* line 182, ../../../../general/res/sass/controls/_messages.scss */ +/* line 212, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-error .type-icon.message-type { color: #d4585c; } - /* line 183, ../../../../general/res/sass/controls/_messages.scss */ + /* line 213, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-error .type-icon.message-type:before { content: "\21"; } -/* line 246, ../../../../general/res/sass/controls/_messages.scss */ +/* line 276, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message { -moz-border-radius: 3px; -webkit-border-radius: 3px; @@ -2702,26 +2751,26 @@ label.checkbox.custom { background: rgba(230, 230, 230, 0.1); margin-bottom: 5px; padding: 10px; } - /* line 253, ../../../../general/res/sass/controls/_messages.scss */ + /* line 283, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents, .t-message-list .message-contents .l-message .bottom-bar { position: relative; } - /* line 259, ../../../../general/res/sass/controls/_messages.scss */ + /* line 289, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents { font-size: 0.9em; margin-left: 10px; } - /* line 262, ../../../../general/res/sass/controls/_messages.scss */ + /* line 292, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents .message-action { color: #b3b3b3; } - /* line 263, ../../../../general/res/sass/controls/_messages.scss */ + /* line 293, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents .bottom-bar { text-align: left; } - /* line 266, ../../../../general/res/sass/controls/_messages.scss */ + /* line 296, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .top-bar, .t-message-list .message-contents .l-message .message-body { margin-bottom: 10px; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/controls/_messages.scss */ + /* line 304, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message { margin-right: 10px; } } diff --git a/platform/commonUI/themes/espresso/res/sass/_constants.scss b/platform/commonUI/themes/espresso/res/sass/_constants.scss index 896241de5c..db68d1ab11 100644 --- a/platform/commonUI/themes/espresso/res/sass/_constants.scss +++ b/platform/commonUI/themes/espresso/res/sass/_constants.scss @@ -61,9 +61,9 @@ $colorInputIcon: pushBack($colorBodyFg, 15%); // Status colors, mainly used for messaging and item ancillary symbols $colorStatusFg: #ccc; $colorStatusDefault: #ccc; -$colorStatusOk: #62ba72; -$colorStatusCaution: #ffa66d; -$colorStatusAlert: #d4585c; +$colorStatusInfo: #62ba72; +$colorStatusAlert: #ffa66d; +$colorStatusError: #d4585c; $colorProgressBarOuter: rgba(#000, 0.1); $colorProgressBarAmt: $colorKey; $progressBarHOverlay: 15px; diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 0ae366cd0e..de35232d0c 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -2346,12 +2346,15 @@ label.checkbox.custom { .status.block .status-indicator { margin-right: 3px; } /* line 54, ../../../../general/res/sass/controls/_messages.scss */ - .status.block.ok .status-indicator { + .status.block.ok .status-indicator, .status.block.info .status-indicator { color: #60ba7b; } - /* line 57, ../../../../general/res/sass/controls/_messages.scss */ - .status.block.caution .status-indicator { + /* line 58, ../../../../general/res/sass/controls/_messages.scss */ + .status.block.alert .status-indicator, .status.block.warning .status-indicator, .status.block.caution .status-indicator { color: #ffb66c; } - /* line 60, ../../../../general/res/sass/controls/_messages.scss */ + /* line 63, ../../../../general/res/sass/controls/_messages.scss */ + .status.block.error .status-indicator { + color: #c96b68; } + /* line 66, ../../../../general/res/sass/controls/_messages.scss */ .status.block .label { -moz-transition-property: max-width; -o-transition-property: max-width; @@ -2367,7 +2370,7 @@ label.checkbox.custom { transition-timing-function: ease-in-out; overflow: hidden; max-width: 0px; } - /* line 66, ../../../../general/res/sass/controls/_messages.scss */ + /* line 72, ../../../../general/res/sass/controls/_messages.scss */ .status.block .count { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -2383,27 +2386,27 @@ label.checkbox.custom { transition-timing-function: ease-in-out; font-weight: bold; opacity: 1; } - /* line 72, ../../../../general/res/sass/controls/_messages.scss */ + /* line 78, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .label { max-width: 450px; width: auto; } - /* line 76, ../../../../general/res/sass/controls/_messages.scss */ + /* line 82, ../../../../general/res/sass/controls/_messages.scss */ .status.block:hover .count { opacity: 0; } /* Styles for messages and message banners */ -/* line 84, ../../../../general/res/sass/controls/_messages.scss */ +/* line 90, ../../../../general/res/sass/controls/_messages.scss */ .message.block { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; padding: 10px; } -/* line 88, ../../../../general/res/sass/controls/_messages.scss */ +/* line 94, ../../../../general/res/sass/controls/_messages.scss */ .message.error { background-color: rgba(255, 83, 58, 0.3); color: #ffaca0; } -/* line 94, ../../../../general/res/sass/controls/_messages.scss */ +/* line 100, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -2432,49 +2435,70 @@ label.checkbox.custom { -webkit-transform: translateX(-50%); transform: translateX(-50%); z-index: 10; } - /* line 110, ../../../../general/res/sass/controls/_messages.scss */ + /* line 116, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner.minimized { + -moz-transition-property: left, opacity; + -o-transition-property: left, opacity; + -webkit-transition-property: left, opacity; + transition-property: left, opacity; + -moz-transition-duration: 0.3s; + -o-transition-duration: 0.3s; + -webkit-transition-duration: 0.3s; + transition-duration: 0.3s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + left: 0; + opacity: 0; } + /* line 124, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner.new { + left: 50%; + opacity: 1; } + /* line 127, ../../../../general/res/sass/controls/_messages.scss */ + .l-message-banner.new:not(.info) { + -moz-animation-name: pulse; + -webkit-animation-name: pulse; + animation-name: pulse; + -moz-animation-duration: 100ms; + -webkit-animation-duration: 100ms; + animation-duration: 100ms; + -moz-animation-direction: alternate; + -webkit-animation-direction: alternate; + animation-direction: alternate; + -moz-animation-iteration-count: 10; + -webkit-animation-iteration-count: 10; + animation-iteration-count: 10; + -moz-animation-timing-function: ease-in-out; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; } + /* line 132, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .banner-elem { -webkit-flex: 0 1 auto; flex: 0 1 auto; margin-left: 5px; } - /* line 114, ../../../../general/res/sass/controls/_messages.scss */ + /* line 136, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner a { display: inline-block; } - /* line 117, ../../../../general/res/sass/controls/_messages.scss */ + /* line 139, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .l-action { line-height: 15px; padding: 0 5px; } - /* line 121, ../../../../general/res/sass/controls/_messages.scss */ + /* line 143, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .close { cursor: pointer; font-size: 7px; width: 8px; } - /* line 127, ../../../../general/res/sass/controls/_messages.scss */ + /* line 149, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .l-progress-bar { height: 8px; line-height: 8px; width: 100px; } - /* line 133, ../../../../general/res/sass/controls/_messages.scss */ + /* line 155, ../../../../general/res/sass/controls/_messages.scss */ .l-message-banner .progress-info { display: none; } -/* line 137, ../../../../general/res/sass/controls/_messages.scss */ -.s-message-banner, -.s-message-banner .s-action { - -moz-transition-property: background-color; - -o-transition-property: background-color; - -webkit-transition-property: background-color; - transition-property: background-color; - -moz-transition-duration: 0.25s; - -o-transition-duration: 0.25s; - -webkit-transition-duration: 0.25s; - transition-duration: 0.25s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; } - -/* line 142, ../../../../general/res/sass/controls/_messages.scss */ +/* line 165, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner { -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -2491,46 +2515,71 @@ label.checkbox.custom { /* line 33, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .s-action:hover { background-color: gray; } - /* line 146, ../../../../general/res/sass/controls/_messages.scss */ + /* line 169, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner a { color: inherit; } - /* line 147, ../../../../general/res/sass/controls/_messages.scss */ + /* line 170, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .s-action { -moz-border-radius: 4px; -webkit-border-radius: 4px; - border-radius: 4px; } - /* line 150, ../../../../general/res/sass/controls/_messages.scss */ + border-radius: 4px; + -moz-transition-property: background-color; + -o-transition-property: background-color; + -webkit-transition-property: background-color; + transition-property: background-color; + -moz-transition-duration: 500ms; + -o-transition-duration: 500ms; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } + /* line 174, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .close { opacity: 0.5; } - /* line 152, ../../../../general/res/sass/controls/_messages.scss */ + /* line 176, ../../../../general/res/sass/controls/_messages.scss */ .s-message-banner .close:hover { opacity: 1; } - /* line 156, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.ok { + /* line 180, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.ok, .s-message-banner.info { background-color: #275a36; color: #fff; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.ok:hover { + .s-message-banner.ok:hover, .s-message-banner.info:hover { background-color: #367e4c; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.ok .s-action { + .s-message-banner.ok .s-action, .s-message-banner.info .s-action { background-color: #183621; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.ok .s-action:hover { + .s-message-banner.ok .s-action:hover, .s-message-banner.info .s-action:hover { background-color: #275a36; } - /* line 159, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.caution { + /* line 184, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.caution, .s-message-banner.warning, .s-message-banner.alert { background-color: #d26a00; color: #fff; } /* line 28, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.caution:hover { + .s-message-banner.caution:hover, .s-message-banner.warning:hover, .s-message-banner.alert:hover { background-color: #ff8306; } /* line 31, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.caution .s-action { + .s-message-banner.caution .s-action, .s-message-banner.warning .s-action, .s-message-banner.alert .s-action { background-color: #9f5000; } /* line 33, ../../../../general/res/sass/controls/_messages.scss */ - .s-message-banner.caution .s-action:hover { + .s-message-banner.caution .s-action:hover, .s-message-banner.warning .s-action:hover, .s-message-banner.alert .s-action:hover { background-color: #d26a00; } + /* line 189, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.error { + background-color: #702a28; + color: #fff; } + /* line 28, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.error:hover { + background-color: #963835; } + /* line 31, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.error .s-action { + background-color: #4a1c1b; } + /* line 33, ../../../../general/res/sass/controls/_messages.scss */ + .s-message-banner.error .s-action:hover { + background-color: #702a28; } /* Paths: t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message > @@ -2545,7 +2594,7 @@ label.checkbox.custom { ... same as above bottom-bar */ -/* line 201, ../../../../general/res/sass/controls/_messages.scss */ +/* line 231, ../../../../general/res/sass/controls/_messages.scss */ .l-message { display: -webkit-flex; display: flex; @@ -2553,52 +2602,52 @@ label.checkbox.custom { flex-direction: row; -webkit-align-items: stretch; align-items: stretch; } - /* line 205, ../../../../general/res/sass/controls/_messages.scss */ + /* line 235, ../../../../general/res/sass/controls/_messages.scss */ .l-message .type-icon.message-type { -webkit-flex: 0 1 auto; flex: 0 1 auto; position: relative; } - /* line 210, ../../../../general/res/sass/controls/_messages.scss */ + /* line 240, ../../../../general/res/sass/controls/_messages.scss */ .l-message .message-contents { -webkit-flex: 1 1 auto; flex: 1 1 auto; margin-left: 25px; position: relative; } - /* line 216, ../../../../general/res/sass/controls/_messages.scss */ + /* line 246, ../../../../general/res/sass/controls/_messages.scss */ .l-message .message-contents .top-bar, .l-message .message-contents .message-body { margin-bottom: 20px; } -/* line 165, ../../../../general/res/sass/controls/_messages.scss */ +/* line 195, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .type-icon.message-type { text-shadow: rgba(255, 255, 255, 0.8) 0 0px 5px; color: #ccc; font-size: 80px; padding: 1px; width: 82px; } - /* line 167, ../../../../general/res/sass/controls/_messages.scss */ + /* line 197, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .type-icon.message-type:before { content: "\e608"; } -/* line 174, ../../../../general/res/sass/controls/_messages.scss */ +/* line 204, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-info .type-icon.message-type { color: #60ba7b; } - /* line 175, ../../../../general/res/sass/controls/_messages.scss */ + /* line 205, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-info .type-icon.message-type:before { content: "\e608"; } -/* line 178, ../../../../general/res/sass/controls/_messages.scss */ +/* line 208, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-alert .type-icon.message-type { color: #ffb66c; } - /* line 179, ../../../../general/res/sass/controls/_messages.scss */ + /* line 209, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-alert .type-icon.message-type:before { content: "\e610"; } -/* line 182, ../../../../general/res/sass/controls/_messages.scss */ +/* line 212, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-error .type-icon.message-type { color: #c96b68; } - /* line 183, ../../../../general/res/sass/controls/_messages.scss */ + /* line 213, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .message-severity-error .type-icon.message-type:before { content: "\21"; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 229, ../../../../general/res/sass/controls/_messages.scss */ + /* line 259, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .l-message, .t-message-single .bottom-bar { overflow: hidden; @@ -2609,40 +2658,40 @@ label.checkbox.custom { left: 0px; width: auto; height: auto; } - /* line 234, ../../../../general/res/sass/controls/_messages.scss */ + /* line 264, ../../../../general/res/sass/controls/_messages.scss */ .t-message-single .bottom-bar { top: auto; height: 24px; } } -/* line 165, ../../../../general/res/sass/controls/_messages.scss */ +/* line 195, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .type-icon.message-type { text-shadow: rgba(255, 255, 255, 0.8) 0 0px 5px; color: #ccc; font-size: 32px; padding: 1px; width: 34px; } - /* line 167, ../../../../general/res/sass/controls/_messages.scss */ + /* line 197, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .type-icon.message-type:before { content: "\e608"; } -/* line 174, ../../../../general/res/sass/controls/_messages.scss */ +/* line 204, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-info .type-icon.message-type { color: #60ba7b; } - /* line 175, ../../../../general/res/sass/controls/_messages.scss */ + /* line 205, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-info .type-icon.message-type:before { content: "\e608"; } -/* line 178, ../../../../general/res/sass/controls/_messages.scss */ +/* line 208, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-alert .type-icon.message-type { color: #ffb66c; } - /* line 179, ../../../../general/res/sass/controls/_messages.scss */ + /* line 209, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-alert .type-icon.message-type:before { content: "\e610"; } -/* line 182, ../../../../general/res/sass/controls/_messages.scss */ +/* line 212, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-error .type-icon.message-type { color: #c96b68; } - /* line 183, ../../../../general/res/sass/controls/_messages.scss */ + /* line 213, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-severity-error .type-icon.message-type:before { content: "\21"; } -/* line 246, ../../../../general/res/sass/controls/_messages.scss */ +/* line 276, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message { -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -2650,26 +2699,26 @@ label.checkbox.custom { background: rgba(102, 102, 102, 0.1); margin-bottom: 5px; padding: 10px; } - /* line 253, ../../../../general/res/sass/controls/_messages.scss */ + /* line 283, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents, .t-message-list .message-contents .l-message .bottom-bar { position: relative; } - /* line 259, ../../../../general/res/sass/controls/_messages.scss */ + /* line 289, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents { font-size: 0.9em; margin-left: 10px; } - /* line 262, ../../../../general/res/sass/controls/_messages.scss */ + /* line 292, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents .message-action { color: #999999; } - /* line 263, ../../../../general/res/sass/controls/_messages.scss */ + /* line 293, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .message-contents .bottom-bar { text-align: left; } - /* line 266, ../../../../general/res/sass/controls/_messages.scss */ + /* line 296, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message .top-bar, .t-message-list .message-contents .l-message .message-body { margin-bottom: 10px; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/controls/_messages.scss */ + /* line 304, ../../../../general/res/sass/controls/_messages.scss */ .t-message-list .message-contents .l-message { margin-right: 10px; } } diff --git a/platform/commonUI/themes/snow/res/sass/_constants.scss b/platform/commonUI/themes/snow/res/sass/_constants.scss index 02e0f7ab11..8a1d54642b 100644 --- a/platform/commonUI/themes/snow/res/sass/_constants.scss +++ b/platform/commonUI/themes/snow/res/sass/_constants.scss @@ -61,9 +61,9 @@ $colorInputIcon: pushBack($colorBodyFg, 25%); // Status colors, mainly used for messaging and item ancillary symbols $colorStatusFg: #fff; $colorStatusDefault: #ccc; -$colorStatusOk: #60ba7b; -$colorStatusCaution: #ffb66c; -$colorStatusAlert: #c96b68; +$colorStatusInfo: #60ba7b; +$colorStatusAlert: #ffb66c; +$colorStatusError: #c96b68; $colorProgressBarOuter: rgba(#000, 0.1); $colorProgressBarAmt: #0a0; $progressBarHOverlay: 15px; From ee382be38d49fbe2fe55ddba1960efcf223f5173 Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 14 Oct 2015 11:45:39 -0700 Subject: [PATCH 062/488] Added transitions and severity classes --- .../dialog/res/templates/message.html | 1 - platform/commonUI/dialog/src/DialogService.js | 17 +---- .../general/res/templates/message-banner.html | 7 +- .../src/controllers/BannerController.js | 5 +- platform/commonUI/notification/bundle.json | 6 +- .../res/notification-indicator.html | 3 +- .../notification/src/NotificationService.js | 66 +++++++++++-------- .../src/NotificationLaunchController.js | 4 +- 8 files changed, 56 insertions(+), 53 deletions(-) diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index 28bd09cbb1..ecf598daf4 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -1,6 +1,5 @@
-
\ No newline at end of file diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index 7089e3a714..8bbf5e21c1 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -28,7 +28,12 @@ define( function BannerController($scope, notificationService, dialogService) { $scope.active = notificationService.active; $scope.MessageSeverity = MessageSeverity; - $scope.dismiss = function(notification) { + $scope.action = function (action, $event){ + $event.stopPropagation(); + return action(); + } + $scope.dismiss = function(notification, $event) { + $event.stopPropagation(); notificationService.dismissOrMinimize(notification); }; $scope.maximize = function(notification) { diff --git a/platform/commonUI/notification/bundle.json b/platform/commonUI/notification/bundle.json index d8958d0ac1..d729e42673 100644 --- a/platform/commonUI/notification/bundle.json +++ b/platform/commonUI/notification/bundle.json @@ -34,7 +34,8 @@ ], "indicators": [ { - "implementation": "NotificationIndicator.js" + "implementation": "NotificationIndicator.js", + "priority": "fallback" } ], "services": [ diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 8a74fc7a3f..411e3727cb 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -180,7 +180,7 @@ define( notifications queued for display, setup a timeout to dismiss the dialog. */ - if (notification && (notification.autoDismiss !== false + if (notification && (notification.autoDismiss || this.selectNextNotification())) { timeout = notification.autoDismiss || this.DEFAULT_AUTO_DISMISS; diff --git a/testing/dialogTest/bundle.json b/testing/dialogTest/bundle.json index 0e55477bda..1b1acf6cc0 100644 --- a/testing/dialogTest/bundle.json +++ b/testing/dialogTest/bundle.json @@ -34,10 +34,12 @@ ], "indicators": [ { - "implementation": "DialogLaunchIndicator.js" + "implementation": "DialogLaunchIndicator.js", + "priority": "fallback" }, { - "implementation": "NotificationLaunchIndicator.js" + "implementation": "NotificationLaunchIndicator.js", + "priority": "fallback" } ] } From 301b73c6c605fff4cc4beb5dacf6bc213cb3c11f Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 14 Oct 2015 14:16:37 -0700 Subject: [PATCH 065/488] Banner notifications are not maximized if 'info' message --- .../commonUI/general/src/controllers/BannerController.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index 8bbf5e21c1..2baa18f9b5 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -37,10 +37,12 @@ define( notificationService.dismissOrMinimize(notification); }; $scope.maximize = function(notification) { - notification.cancel = function(){ - dialogService.dismiss(); + if (notification.severity > MessageSeverity.INFO){ + notification.cancel = function(){ + dialogService.dismiss(); + } + dialogService.showBlockingMessage(notification); } - dialogService.showBlockingMessage(notification); } } return BannerController; From f08725b6a27cacbe45f85d1282d16fbe0f0f4974 Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 14 Oct 2015 15:03:29 -0700 Subject: [PATCH 066/488] Fixed jshint errors --- .../general/src/controllers/BannerController.js | 6 +++--- testing/dialogTest/bundle.json | 1 + testing/dialogTest/src/NotificationLaunchController.js | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index 2baa18f9b5..a59860f884 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -31,7 +31,7 @@ define( $scope.action = function (action, $event){ $event.stopPropagation(); return action(); - } + }; $scope.dismiss = function(notification, $event) { $event.stopPropagation(); notificationService.dismissOrMinimize(notification); @@ -40,10 +40,10 @@ define( if (notification.severity > MessageSeverity.INFO){ notification.cancel = function(){ dialogService.dismiss(); - } + }; dialogService.showBlockingMessage(notification); } - } + }; } return BannerController; }); \ No newline at end of file diff --git a/testing/dialogTest/bundle.json b/testing/dialogTest/bundle.json index 1b1acf6cc0..bb2d464d64 100644 --- a/testing/dialogTest/bundle.json +++ b/testing/dialogTest/bundle.json @@ -28,6 +28,7 @@ "depends": [ "$scope", "$timeout", + "$log", "notificationService" ] } diff --git a/testing/dialogTest/src/NotificationLaunchController.js b/testing/dialogTest/src/NotificationLaunchController.js index eb38fcb3c3..63b436a894 100644 --- a/testing/dialogTest/src/NotificationLaunchController.js +++ b/testing/dialogTest/src/NotificationLaunchController.js @@ -26,13 +26,13 @@ define( function (MessageSeverity) { "use strict"; - function NotificationLaunchController($scope, $timeout, notificationService) { + function NotificationLaunchController($scope, $timeout, $log, notificationService) { var messageCounter = 1; $scope.newSuccess = function(){ notificationService.info({ title: "Success notification!" - }) + }); }; function getExampleActionText() { @@ -90,7 +90,7 @@ define( primaryAction: { label: 'Retry', action: function() { - console.log('Retry clicked'); + $log.info('Retry clicked'); } }, actions: getExampleActions()}); @@ -105,7 +105,7 @@ define( primaryAction: { label: 'Retry', action: function() { - console.log('Retry clicked'); + $log.info('Retry clicked'); } }, actions: getExampleActions()}); @@ -127,7 +127,7 @@ define( notification.progressText = ["Estimated time remaining:" + " about ", 60 - Math.floor((notification.progress / 100) * 60), " seconds"].join(" "); if (notification.progress < 100) { - $timeout(function(){incrementProgress(notification)}, 1000); + $timeout(function(){incrementProgress(notification);}, 1000); } } From 821a1a485c246e27e94ebf3cf7dc984dc7ddcdac Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 14 Oct 2015 16:44:02 -0700 Subject: [PATCH 067/488] [Production] Updated symbols font to v25 open #188 Added icon-dataset e611; --- .../icomoon.io-WTD-symbols-project.json | 324 ++++++++++++------ .../general/res/fonts/symbols/wtdsymbols.eot | Bin 11216 -> 11672 bytes .../general/res/fonts/symbols/wtdsymbols.svg | 5 +- .../general/res/fonts/symbols/wtdsymbols.ttf | Bin 11040 -> 11496 bytes .../general/res/fonts/symbols/wtdsymbols.woff | Bin 11116 -> 11572 bytes 5 files changed, 221 insertions(+), 108 deletions(-) diff --git a/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json b/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json index 7982d57200..9ff51f9f04 100644 --- a/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json +++ b/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json @@ -1,14 +1,38 @@ { "metadata": { - "name": "WTD Symbols v24", - "lastOpened": 1441992412958, - "created": 1441992410384 + "name": "WTD Symbols v25", + "lastOpened": 1444865544920, + "created": 1444865541293 }, "iconSets": [ { "selection": [ { - "order": 86, + "order": 108, + "id": 88, + "prevSize": 32, + "code": 58897, + "name": "icon-dataset", + "tempChar": "" + }, + { + "order": 90, + "id": 87, + "prevSize": 32, + "code": 58896, + "name": "icon-bell", + "tempChar": "" + }, + { + "order": 91, + "id": 86, + "prevSize": 32, + "code": 58889, + "name": "icon-hourglass", + "tempChar": "" + }, + { + "order": 92, "id": 85, "prevSize": 32, "code": 58888, @@ -18,119 +42,119 @@ 58890 ], "name": "icon-info-v15", - "tempChar": "" + "tempChar": "" }, { - "order": 82, + "order": 93, "id": 84, "prevSize": 32, "code": 58887, "name": "icon-x-in-circle", - "tempChar": "" + "tempChar": "" }, { - "order": 77, + "order": 94, "id": 83, "prevSize": 32, "code": 58881, "name": "icon-datatable", - "tempChar": "" + "tempChar": "" }, { - "order": 78, + "order": 95, "id": 82, "prevSize": 32, "code": 58882, "name": "icon-tabular-scrolling", - "tempChar": "" + "tempChar": "" }, { - "order": 79, + "order": 96, "id": 81, "prevSize": 32, "code": 58884, "name": "icon-tabular", - "tempChar": "" + "tempChar": "" }, { - "order": 80, + "order": 97, "id": 80, "prevSize": 32, "code": 58885, "name": "icon-calendar", - "tempChar": "" + "tempChar": "" }, { - "order": 83, + "order": 98, "id": 78, "prevSize": 32, "code": 58886, "name": "icon-paint-bucket", - "tempChar": "" + "tempChar": "" }, { - "order": 1, + "order": 99, "id": 75, "prevSize": 32, "code": 123, "name": "icon-pointer-left", - "tempChar": "" + "tempChar": "" }, { - "order": 3, + "order": 100, "id": 74, "prevSize": 32, "code": 125, "name": "icon-pointer-right", - "tempChar": "" + "tempChar": "" }, { - "order": 4, + "order": 101, "id": 73, "prevSize": 32, "code": 80, "name": "icon-person", - "tempChar": "" + "tempChar": "" }, { - "order": 5, + "order": 102, "id": 72, "prevSize": 32, "code": 232, "name": "icon-chain-links", - "tempChar": "" + "tempChar": "" }, { - "order": 6, + "order": 103, "id": 71, "prevSize": 32, "code": 115, "name": "icon-database-in-brackets", - "tempChar": "" + "tempChar": "" }, { - "order": 7, + "order": 104, "id": 70, "prevSize": 32, "code": 114, "name": "icon-refresh", - "tempChar": "" + "tempChar": "" }, { - "order": 8, + "order": 105, "id": 69, "prevSize": 32, "code": 108, "name": "icon-lock", - "tempChar": "" + "tempChar": "" }, { - "order": 9, + "order": 106, "id": 68, "prevSize": 32, "code": 51, "name": "icon-box-with-dashed-lines", - "tempChar": "" + "tempChar": "" }, { "order": 10, @@ -138,7 +162,7 @@ "prevSize": 32, "code": 58880, "name": "icon-box-with-arrow-cursor", - "tempChar": "" + "tempChar": "" }, { "order": 11, @@ -146,7 +170,7 @@ "prevSize": 32, "code": 65, "name": "icon-activity-mode", - "tempChar": "" + "tempChar": "" }, { "order": 12, @@ -154,15 +178,15 @@ "prevSize": 32, "code": 97, "name": "icon-activity", - "tempChar": "" + "tempChar": "" }, { - "order": 13, + "order": 87, "id": 64, "prevSize": 32, "code": 33, "name": "icon-alert-rect", - "tempChar": "" + "tempChar": "" }, { "order": 14, @@ -170,7 +194,7 @@ "prevSize": 32, "code": 58883, "name": "icon-alert-triangle", - "tempChar": "" + "tempChar": "" }, { "order": 15, @@ -178,7 +202,7 @@ "prevSize": 32, "code": 238, "name": "icon-arrow-double-down", - "tempChar": "" + "tempChar": "" }, { "order": 16, @@ -186,7 +210,7 @@ "prevSize": 32, "code": 235, "name": "icon-arrow-double-up", - "tempChar": "" + "tempChar": "" }, { "order": 2, @@ -194,7 +218,7 @@ "prevSize": 32, "code": 118, "name": "icon-arrow-down", - "tempChar": "" + "tempChar": "" }, { "order": 19, @@ -202,7 +226,7 @@ "prevSize": 32, "code": 60, "name": "icon-arrow-left", - "tempChar": "" + "tempChar": "" }, { "order": 20, @@ -210,7 +234,7 @@ "prevSize": 32, "code": 62, "name": "icon-arrow-right", - "tempChar": "" + "tempChar": "" }, { "order": 21, @@ -218,7 +242,7 @@ "prevSize": 32, "code": 236, "name": "icon-arrow-tall-down", - "tempChar": "" + "tempChar": "" }, { "order": 22, @@ -226,7 +250,7 @@ "prevSize": 32, "code": 237, "name": "icon-arrow-tall-up", - "tempChar": "" + "tempChar": "" }, { "order": 23, @@ -234,7 +258,7 @@ "prevSize": 32, "code": 94, "name": "icon-arrow-up", - "tempChar": "" + "tempChar": "" }, { "order": 24, @@ -242,7 +266,7 @@ "prevSize": 32, "code": 73, "name": "icon-arrows-out", - "tempChar": "" + "tempChar": "" }, { "order": 25, @@ -250,7 +274,7 @@ "prevSize": 32, "code": 58893, "name": "icon-arrows-right-left", - "tempChar": "" + "tempChar": "" }, { "order": 33, @@ -258,7 +282,7 @@ "prevSize": 32, "code": 53, "name": "icon-arrows-up-down", - "tempChar": "" + "tempChar": "" }, { "order": 26, @@ -266,7 +290,7 @@ "prevSize": 32, "code": 42, "name": "icon-asterisk", - "tempChar": "" + "tempChar": "" }, { "order": 27, @@ -274,7 +298,7 @@ "prevSize": 32, "code": 72, "name": "icon-autoflow-tabular", - "tempChar": "" + "tempChar": "" }, { "order": 28, @@ -282,7 +306,7 @@ "prevSize": 32, "code": 224, "name": "icon-box", - "tempChar": "" + "tempChar": "" }, { "order": 29, @@ -290,7 +314,7 @@ "prevSize": 32, "code": 50, "name": "icon-check", - "tempChar": "" + "tempChar": "" }, { "order": 30, @@ -298,7 +322,7 @@ "prevSize": 32, "code": 67, "name": "icon-clock", - "tempChar": "" + "tempChar": "" }, { "order": 31, @@ -306,7 +330,7 @@ "prevSize": 32, "code": 46, "name": "icon-connectivity", - "tempChar": "" + "tempChar": "" }, { "order": 32, @@ -314,7 +338,7 @@ "prevSize": 32, "code": 100, "name": "icon-database-query", - "tempChar": "" + "tempChar": "" }, { "order": 17, @@ -322,7 +346,7 @@ "prevSize": 32, "code": 68, "name": "icon-database", - "tempChar": "" + "tempChar": "" }, { "order": 35, @@ -330,7 +354,7 @@ "prevSize": 32, "code": 81, "name": "icon-dictionary", - "tempChar": "" + "tempChar": "" }, { "order": 36, @@ -338,7 +362,7 @@ "prevSize": 32, "code": 242, "name": "icon-duplicate", - "tempChar": "" + "tempChar": "" }, { "order": 37, @@ -346,7 +370,7 @@ "prevSize": 32, "code": 102, "name": "icon-folder-new", - "tempChar": "" + "tempChar": "" }, { "order": 38, @@ -354,7 +378,7 @@ "prevSize": 32, "code": 70, "name": "icon-folder", - "tempChar": "" + "tempChar": "" }, { "order": 39, @@ -362,7 +386,7 @@ "prevSize": 32, "code": 95, "name": "icon-fullscreen-collapse", - "tempChar": "" + "tempChar": "" }, { "order": 40, @@ -370,7 +394,7 @@ "prevSize": 32, "code": 122, "name": "icon-fullscreen-expand", - "tempChar": "" + "tempChar": "" }, { "order": 41, @@ -378,7 +402,7 @@ "prevSize": 32, "code": 71, "name": "icon-gear", - "tempChar": "" + "tempChar": "" }, { "order": 49, @@ -386,7 +410,7 @@ "prevSize": 32, "code": 227, "name": "icon-image", - "tempChar": "" + "tempChar": "" }, { "order": 42, @@ -394,7 +418,7 @@ "prevSize": 32, "code": 225, "name": "icon-layers", - "tempChar": "" + "tempChar": "" }, { "order": 43, @@ -402,7 +426,7 @@ "prevSize": 32, "code": 76, "name": "icon-layout", - "tempChar": "" + "tempChar": "" }, { "order": 44, @@ -410,7 +434,7 @@ "prevSize": 32, "code": 226, "name": "icon-line-horz", - "tempChar": "" + "tempChar": "" }, { "order": 75, @@ -418,7 +442,7 @@ "prevSize": 32, "code": 244, "name": "icon-link", - "tempChar": "" + "tempChar": "" }, { "order": 46, @@ -426,7 +450,7 @@ "prevSize": 32, "code": 88, "name": "icon-magnify-in", - "tempChar": "" + "tempChar": "" }, { "order": 47, @@ -434,7 +458,7 @@ "prevSize": 32, "code": 89, "name": "icon-magnify-out", - "tempChar": "" + "tempChar": "" }, { "order": 48, @@ -442,7 +466,7 @@ "prevSize": 32, "code": 77, "name": "icon-magnify", - "tempChar": "" + "tempChar": "" }, { "order": 34, @@ -450,7 +474,7 @@ "prevSize": 32, "code": 109, "name": "icon-menu", - "tempChar": "" + "tempChar": "" }, { "order": 50, @@ -458,7 +482,7 @@ "prevSize": 32, "code": 243, "name": "icon-move", - "tempChar": "" + "tempChar": "" }, { "order": 51, @@ -466,7 +490,7 @@ "prevSize": 32, "code": 121, "name": "icon-new-window", - "tempChar": "" + "tempChar": "" }, { "order": 52, @@ -474,7 +498,7 @@ "prevSize": 32, "code": 111, "name": "icon-object", - "tempChar": "" + "tempChar": "" }, { "order": 73, @@ -482,7 +506,7 @@ "prevSize": 32, "code": 63, "name": "icon-object-unknown", - "tempChar": "" + "tempChar": "" }, { "order": 53, @@ -490,7 +514,7 @@ "prevSize": 32, "code": 86, "name": "icon-packet", - "tempChar": "" + "tempChar": "" }, { "order": 54, @@ -498,7 +522,7 @@ "prevSize": 32, "code": 234, "name": "icon-page", - "tempChar": "" + "tempChar": "" }, { "order": 55, @@ -506,7 +530,7 @@ "prevSize": 32, "code": 241, "name": "icon-pause", - "tempChar": "" + "tempChar": "" }, { "order": 56, @@ -514,7 +538,7 @@ "prevSize": 32, "code": 112, "name": "icon-pencil", - "tempChar": "" + "tempChar": "" }, { "order": 65, @@ -522,7 +546,7 @@ "prevSize": 32, "code": 79, "name": "icon-people", - "tempChar": "" + "tempChar": "" }, { "order": 57, @@ -530,7 +554,7 @@ "prevSize": 32, "code": 239, "name": "icon-play", - "tempChar": "" + "tempChar": "" }, { "order": 58, @@ -538,7 +562,7 @@ "prevSize": 32, "code": 233, "name": "icon-plot-resource", - "tempChar": "" + "tempChar": "" }, { "order": 59, @@ -546,7 +570,7 @@ "prevSize": 32, "code": 43, "name": "icon-plus", - "tempChar": "" + "tempChar": "" }, { "order": 60, @@ -554,7 +578,7 @@ "prevSize": 32, "code": 45, "name": "icon-minus", - "tempChar": "" + "tempChar": "" }, { "order": 61, @@ -562,7 +586,7 @@ "prevSize": 32, "code": 54, "name": "icon-sine", - "tempChar": "" + "tempChar": "" }, { "order": 62, @@ -570,7 +594,7 @@ "prevSize": 32, "code": 228, "name": "icon-T", - "tempChar": "" + "tempChar": "" }, { "order": 63, @@ -578,7 +602,7 @@ "prevSize": 32, "code": 116, "name": "icon-telemetry-panel", - "tempChar": "" + "tempChar": "" }, { "order": 64, @@ -586,7 +610,7 @@ "prevSize": 32, "code": 84, "name": "icon-telemetry", - "tempChar": "" + "tempChar": "" }, { "order": 18, @@ -594,7 +618,7 @@ "prevSize": 32, "code": 246, "name": "icon-thumbs-strip", - "tempChar": "" + "tempChar": "" }, { "order": 67, @@ -602,7 +626,7 @@ "prevSize": 32, "code": 83, "name": "icon-timeline", - "tempChar": "" + "tempChar": "" }, { "order": 68, @@ -610,7 +634,7 @@ "prevSize": 32, "code": 245, "name": "icon-timer", - "tempChar": "" + "tempChar": "" }, { "order": 69, @@ -618,7 +642,7 @@ "prevSize": 32, "code": 90, "name": "icon-trash", - "tempChar": "" + "tempChar": "" }, { "order": 70, @@ -626,7 +650,7 @@ "prevSize": 32, "code": 229, "name": "icon-two-parts-both", - "tempChar": "" + "tempChar": "" }, { "order": 71, @@ -634,7 +658,7 @@ "prevSize": 32, "code": 231, "name": "icon-two-parts-one-only", - "tempChar": "" + "tempChar": "" }, { "order": 72, @@ -642,7 +666,7 @@ "prevSize": 32, "code": 120, "name": "icon-x-heavy", - "tempChar": "" + "tempChar": "" }, { "order": 66, @@ -650,7 +674,7 @@ "prevSize": 32, "code": 58946, "name": "icon-x", - "tempChar": "" + "tempChar": "" } ], "id": 2, @@ -665,6 +689,74 @@ "height": 1024, "prevSize": 32, "icons": [ + { + "id": 88, + "paths": [ + "M896 192h-320c-16.4-16.4-96.8-96.8-109.2-109.2l-37.4-37.4c-25-25-74.2-45.4-109.4-45.4h-256c-35.2 0-64 28.8-64 64v384c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v-128c0-70.4-57.6-128-128-128z", + "M896 448h-768c-70.4 0-128 57.6-128 128v320c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-320c0-70.4-57.6-128-128-128zM320 896h-128v-320h128v320zM576 896h-128v-320h128v320zM832 896h-128v-320h128v320z" + ], + "attrs": [], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-dataset" + ], + "colorPermutations": { + "125525525516161751": [] + } + }, + { + "id": 87, + "paths": [ + "M512 1024c106 0 192-86 192-192h-384c0 106 86 192 192 192z", + "M896 448v-64c0-212-172-384-384-384s-384 172-384 384v64c0 70.6-57.4 128-128 128v128h1024v-128c-70.6 0-128-57.4-128-128z" + ], + "attrs": [ + { + "fill": "rgb(6, 161, 75)" + }, + { + "fill": "rgb(6, 161, 75)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-bell" + ], + "colorPermutations": { + "125525525516161751": [ + 1, + 1 + ] + } + }, + { + "id": 86, + "paths": [ + "M1024 0h-1024c0 282.8 229.2 512 512 512s512-229.2 512-512zM512 384c-102.6 0-199-40-271.6-112.4-41.2-41.2-72-90.2-90.8-143.6h724.6c-18.8 53.4-49.6 102.4-90.8 143.6-72.4 72.4-168.8 112.4-271.4 112.4z", + "M512 512c-282.8 0-512 229.2-512 512h1024c0-282.8-229.2-512-512-512z" + ], + "attrs": [ + { + "fill": "rgb(6, 161, 75)" + }, + { + "fill": "rgb(6, 161, 75)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-hourglass" + ], + "colorPermutations": { + "125525525516161751": [ + 1, + 1 + ] + } + }, { "id": 85, "paths": [ @@ -698,7 +790,8 @@ "icon-x-in-circle" ], "colorPermutations": { - "16161751": [] + "16161751": [], + "125525525516161751": [] } }, { @@ -899,6 +992,11 @@ 1, 1, 1 + ], + "125525525516161751": [ + 1, + 1, + 1 ] } }, @@ -1051,18 +1149,28 @@ { "id": 67, "paths": [ - "M832 512.4c0-0.2 0-0.2 0-0.4v-320c0-105.6-86.4-192-192-192h-448c-105.6 0-192 86.4-192 192v320c0 105.6 86.4 192 192 192h263.6l-197.2-445.6 573.6 254z", - "M766.8 659.8l193.8-20.4-576.6-255.4 255.4 576.6 20.4-193.8 257 257.2 107.2-107.2z" + "M894-2h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h400c-2.2-3.8-4-7.6-5.8-11.4l-255.2-576.8c-21.4-48.4-10.8-105 26.6-142.4 24.4-24.4 57.2-37.4 90.4-37.4 17.4 0 35.2 3.6 51.8 11l576.6 255.4c4 1.8 7.8 3.8 11.4 5.8v-400.2c0.2-70.4-57.4-128-127.8-128z", + "M958.6 637.4l-576.6-255.4 255.4 576.6 64.6-128.6 192 192 128-128-192-192z" ], "attrs": [ - {}, - {} + { + "fill": "rgb(0, 0, 0)" + }, + { + "fill": "rgb(0, 0, 0)" + } ], "isMulticolor": false, "grid": 0, "tags": [ "icon-box-with-arrow-cursor" - ] + ], + "colorPermutations": { + "125525525516161751": [ + 0, + 0 + ] + } }, { "id": 66, @@ -1338,6 +1446,9 @@ "colorPermutations": { "16161751": [ 0 + ], + "125525525516161751": [ + 0 ] } }, @@ -14853,6 +14964,5 @@ "gridSize": 16, "showLiga": false }, - "uid": -1, - "time": 1441993324496 + "uid": -1 } \ No newline at end of file diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot index 95887455fe74465bcec2c835663ff125999ddb60..987277675a6c19a701daa242bc478b161fae67b5 100755 GIT binary patch delta 797 zcmX|7OHUI~6h8OPbc#IMP9GGS(mE|nTdhMvnbsg7gAxpmwGA6$G)jX=K`Miwq9%5T zOIVbQF}QR^;=&laF~NihD`H$2CH{nlm_T%)3#A?JfUr3C-0%C&`}4@^HD>h;z)pOH zNz!T0aA`$)RdnHx@|{g7Qc1FI^;?7&;A<4h_(HIEg!e6J32+L?J#x zrr~VJYC~a9$c8;!w}8Vm>_c{GeQIibDO-c|aw;I$@U~3|CYRGDM<1w^ZvM@5s>&iMc3is!Vjw^Q&YIU2P$v>l_~7cAuikG* zVK%{X$B)+L4mVbHRYh=U+SPi_R+Era(~nHs<=6bBVNd#|z4sS1(B?|;WnlIFk=Ncv zQfQ>S6X6jyYMLV!(d^MiEfssg-LqF5ykpAoj%{Zjuy5EsXWV(-sW`V>&45V$NcnrL Gg8l(n4$XrA delta 459 zcmbOceIcCff;Iz#g7!o}okbYPw*Z zf8}i)kRJlnu&N-xxP*ZjD8m4BG*F&_nfdMH0>)}?1t8Cb!GVF5fo1YHMr&q{XS|cm znA|7VGdZ!lN(ln>FiyV6ka6F? zDWd=X?mzVJfA;2IioaPV7pm(_4waar%m{QA69dDU84o|k^V@u7;AQ~|Ffd$_IOqnW zCueAyGlpzlpsB_PR5|&Cmipx1TGQ0y_%8_v34{sE6BH0k5}YM?OYnn`9Z(O*#1Mv3 T24ENi0Ta+u!kf=)pJW68i>`Yr diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg index 596a5dd3e9..021abf31c6 100755 --- a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg +++ b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg @@ -76,7 +76,7 @@ - + @@ -85,6 +85,9 @@ + + + \ No newline at end of file diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf index 8f0f1d96108c2bb7c738154e5049a7a1c1e23b29..d08b3099cc34de7d659c5f8cfb69586cb70da40b 100755 GIT binary patch delta 796 zcmX|A-%ry}6h8O1+aSQU?gxWqY<7iiC@E&68$l$U5)9V4MH7jM5f((oWHJO5W8z9; z;sZQPYmC0>6VV4_+=~e&G&~XGlM?@g#TSV__@b=!7KA3Jr{8x@&;8Dq+?SWWUoHaz zfF@9Yg5#Nsy)oCe%|@7yBqk<{xzbnrw?6?e;H#=u=WpdmCkdXXOZj05x6hakIt}h$)jb+k=pz3G;(q*!Uq0vBT#Q*kvC7vLT$A++o z!hD3NVeQCfN1Qio$6mHiz#I*`kX>0X6xLU=I;2;VLBWo2b&4Z)l>;mEgh}G)H<)W!mF!_d0?61O^6`nvB%M6n6jPybKI#cYyL{89)K{11zEp4C*34 zzDh=JNre+Ti!hLH0W>clCqLOSvF3Sk6a#}s2T;MP+{B6kh9rh^1_n(NAYUObF*jBI z%G)>~KLn^@RY87n3DBWHpba7v7?_#gPCQV}tpMb>FgP%O_GG)pEUH|_83j^~31|bGXpurZ5hJq$)%8E*CqJkn~ z#){0W?B=S%=8Wdd4u$^z9Q+Fl{TVrqG1)mD`n&(XC?jJw;{isn=)VL0g^c_DO%eV7 zcmJV(|FbuHDgI`ed{9ki@>20R%AlZSVqiEk@)e!C8X01V0Gb0rh}P3}Gl`00uD- OFabR!yjfoRBqIRaymVLq diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff index d5bc74071ceb1fb9c17b2bc85141299efb8aa372..816eda299a06edbe316c6ff0e3657a744174bc1c 100755 GIT binary patch delta 856 zcmX|AOH30%82)E=+k()?cKZNQTDL9RR%%Ho+Zt?0QBuVUZ9GU~jHcmLNGnA^FeY|M zycjrCH!*lr4xIzu;HXPBgCtEAs&>(gBP`}GZmQRoByA0zWraD`F?rhifu8K zNB{z|PWC_(wZU(Il?=8$2=YbUXg*T_Kv{wTiXFqJ-;Q0+i(EWWbXC^;Whxo zAjRl~N-EZ)fE#|cLZd`{uo{c)`@GdEBAu^j0b;xWv=(v0pj z`H7!^IA}ntJVd88jeGh%PWgb&A(LXmXB!i318o*y0FfCCl%L|9vskOvwwWrk4NmAF zIRv-X%2{k-A%uL47>V|waJS8_aUwg{BYOA_K5CEpn7b8_l1T}bFvc~L(P8dt)wk(! zoY68#slT#S>sMu!*-lDS$7nI`_of_XUZbsH2BDIatSr|mTgya*pgbY`VT+`Tn#uyB zlK<5DzY>cRhr9c*gxsQ!R3jLW&VW2tw*gy)o)Gr1@E6i6o72;qE9oi>twx<81Ku@+ zT#?lw%CYrZMx{R0Rps7@YtZi>bVYjQ!w<>800SsvVxUOuK@B=+2hem!B!P?qNi-Wt z+m0++k5!P|qJ$weTa^oGB*&&!(y7&{SW;q1ukk`UHH+hM%Fvs*l$7P9M1mc~Zh-)V zAPlGIv_q&1g^;H{b*02RjL5@(9<~eH^Y~o1>U27XjIO~ delta 482 zcmdlI^(IWL+~3WOfsp|Sv~w7^!L$Mc)8s-Xv57ju^=!$xi3JP{j2S>|1HzY2y+59w zSPT?nS^`we0mTC8IhAQZu{8_~Dm5Vd_}jhCjMT&w1_pHvpc*p}X7@kNn*kIAiX{N~ zDj>{$fJHPTx1<6n)&ev?0EC^`S%h=)lY#2|G_C*@tpeei=fzREi4{PLH5-6@1u#xx zD9=mGO$CZA0BQuPV_YSF`1^V`WhjMdxka6F?DWd=X?mzVJfA;2SioaPV^Q!AicGXxcb7scFkMaCAUm3VrfDU9}xFm7V z4MtC9(KKfa*=(b!#>m9LFgZm_ee!gzX|i$rmjr|a!UX0C3J4|%&Jw&O_(8}Hs0S3A LA)EcQpD_XejS_dG From 8c2a29e895eb1177783666bc87e3c29422a2ba7c Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Wed, 14 Oct 2015 17:11:08 -0700 Subject: [PATCH 068/488] Modified file copy prcess to prevent encoding of png's as utf8 --- docs/gendocs.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/gendocs.js b/docs/gendocs.js index 2fcda7214e..329a9b607f 100644 --- a/docs/gendocs.js +++ b/docs/gendocs.js @@ -179,13 +179,17 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define glob(options['in'] + "/**/*.@(html|css|png)", {}, function (err, files) { files.forEach(function (file) { var destination = file.replace(options['in'], options.out), - destPath = path.dirname(destination); - + destPath = path.dirname(destination), + streamOptions = {}; + if (file.match(/png$/)){ + streamOptions.encoding = 'binary'; + } else { + streamOptions.encoding = 'utf8'; + } + mkdirp(destPath, function (err) { - fs.createReadStream(file, { encoding: 'utf8' }) - .pipe(fs.createWriteStream(destination, { - encoding: 'utf8' - })); + fs.createReadStream(file, streamOptions) + .pipe(fs.createWriteStream(destination, streamOptions)); }); }); }); From 4f18663c71c92e422af82fd412d3044a6a350b65 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Wed, 14 Oct 2015 17:14:00 -0700 Subject: [PATCH 069/488] Fixed title in tutorials --- docs/src/tutorials/index.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index 3211b2dd49..5c6ac98691 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -1,18 +1,18 @@ -Open MCT Web Tutorials +# Open MCT Web Tutorials Victor Woeltjen victor.woeltjen@nasa.gov -October 6, 2015 +October 14, 2015 Document Version 2.2 -Date | Version | Summary of Changes | Author ---------------- | ------- | --------------------------------- | --------------- -May 12, 2015 | 0 | Initial Draft | Victor Woeltjen -June 4, 2015 | 1.0 | Name changes | Victor Woeltjen -July 28, 2015 | 2.0 | Telemetry adapter tutorial | Victor Woeltjen -July 31, 2015 | 2.1 | Clarify telemetry adapter details | Victor Woeltjen -October 6, 2015 | 2.2 | Conversion to markdown | Andrew Henry +Date | Version | Summary of Changes | Author +---------------- | ------- | --------------------------------- | --------------- +May 12, 2015 | 0 | Initial Draft | Victor Woeltjen +June 4, 2015 | 1.0 | Name changes | Victor Woeltjen +July 28, 2015 | 2.0 | Telemetry adapter tutorial | Victor Woeltjen +July 31, 2015 | 2.1 | Clarify telemetry adapter details | Victor Woeltjen +October 14, 2015 | 2.2 | Conversion to markdown | Andrew Henry # Introduction From d3ff0a258e9a78b8598bed6c98710e229730c57e Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Wed, 14 Oct 2015 19:18:27 -0700 Subject: [PATCH 070/488] Added links to architecture document from services --- docs/src/guide/index.md | 62 +++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index d06e962b44..c1a48797ab 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -1561,19 +1561,21 @@ extension mechanism is insufficient to achieve a desired result. ### Action Service -The `actionService` provides `Action` instances which are applicable in specific -contexts. See Core API for additional notes on the interface for actions. The -`actionService` has the following interface: +The [Action Service](../architecture/platform#action-service) (`actionService`) +provides `Action` instances which are applicable in specific contexts. See Core +API for additional notes on the interface for actions. The `actionService` has +the following interface: * `getActions(context)`: Returns an array of Action objects which are applicable in the specified action context. ### Capability Service -The capabilityServic e provides constructors for capabilities which will be -exposed for a given domain object. +The [Capability Service](../architecture/platform#capability-service) (`capabilityService`) +provides constructors for capabilities which will be exposed for a given domain +object. -Th e capabilityService has the following interface: +The capabilityService has the following interface: * `getCapabilities(model)`: Returns a an object containing key-value pairs, representing capabilities which should be exposed by the domain object with this @@ -1620,20 +1622,20 @@ option may have the following properties: when clicked. * `description`: Description to show in tooltip on hover. -## Domain Object Service +### Domain Object Service -The objectService provides domain object instances. It has the following -interface: +The [Object Service](../architecture/platform.md#object-service) (`objectService`) +provides domain object instances. It has the following interface: * `getObjects(ids)`: For the provided array of domain object identifiers, returns a Promise for an object containing key-value pairs, where keys are domain object identifiers and values are corresponding DomainObject instances. Note that the result may contain a superset or subset of the objects requested. -## Gesture Service +### Gesture Service -The `gestureService` is used to attach gestures (see extension category - gestures ) to representations. It has the following interface: +The `gestureService` is used to attach gestures (see extension category gestures) +to representations. It has the following interface: * `attachGestures(element, domainObject, keys)`: Attach gestures specified by the provided gesture keys (an array of strings) to this jqLite-wrapped HTML @@ -1641,18 +1643,20 @@ element , which represents the specified domainObject . Returns an object with a single method `destroy()`, to be invoked when it is time to detach these gestures. -## Model Service +### Model Service -The modelService provides domain object models. It has the following interface: +The [Model Service](../architecture/platform.md#model-service) (`modelService`) +provides domain object models. It has the following interface: * `getModels(ids)`: For the provided array of domain object identifiers, returns a Promise for an object containing key-value pairs, where keys are domain object identifiers and values are corresponding domain object models. Note that the result may contain a superset or subset of the models requested. -## Persistence Service +### Persistence Service -The persistenceService provides the ability to load/store JavaScript objects +The [Persistence Service](../architecture/platform.md#persistence-service) (`persistenceService`) +provides the ability to load/store JavaScript objects (presumably serializing/deserializing to JSON in the process.) This is used primarily to store domain object models. It has the following interface: @@ -1678,16 +1682,18 @@ the update fails. persistence space , identified by the specified key . Returns a promise which will be rejected if deletion fails. -## Policy Service +### Policy Service -The policyService may be used to determine whether or not certain behaviors are +The [Policy Service](../architecture/platform.md#policy-service) (`policyService`) +may be used to determine whether or not certain behaviors are allowed within the application. It has the following interface: * `allow(category, candidate, context, [callback])`: Check if this decision should be allowed. Returns a boolean. Its arguments are interpreted as: * `category`: A string identifying which kind of decision is being made. See - the [section on Categories](#PolicyCategories) for categories supported by the platform; plugins - may define and utilize policies of additional categories, as well. + the [section on Categories](#PolicyCategories) for categories supported by + the platform; plugins may define and utilize policies of additional + categories, as well. * `candidate`: An object representing the thing which shall or shall not be allowed. Usually, this will be an instance of an extension of the category defined above. This does need to be the case; additional policies which are @@ -1700,9 +1706,10 @@ should be allowed. Returns a boolean. Its arguments are interpreted as: This function will be called with the message string (which may be undefined) of whichever individual policy caused the operation to fail. -## Telemetry Service +### Telemetry Service -The `telemetryService` is used to acquire telemetry data. See the section on +The [Telemetry Service](../architecture/platform.md#telemetry-service) (`telemetryService`) +is used to acquire telemetry data. See the section on Telemetry in Core API for more information on how both the arguments and responses of this service are structured. @@ -1722,19 +1729,20 @@ matching the specified `requests`. The specified `callback` will be invoked with telemetry response objects as they become available. This method returns a function which can be invoked to terminate the subscription. -## Type Service +### Type Service -The `typeService` exposes domain object types. It has the following interface: +The [Type Service](../architecture/platform.md#type-service) (`typeService`) exposes +domain object types. It has the following interface: * `listTypes()`: Returns all domain object types supported in the application, as an array of `Type` instances. * `getType(key)`: Returns the `Type` instance identified by the provided key, or undefined if no such type exists. -## View Service +### View Service -The `viewService` exposes definitions for views of domain objects. It has the -following interface: +The [View Service](../architecture/platform.md#view-service) (`viewService`) exposes +definitions for views of domain objects. It has the following interface: * `getViews(domainObject)`: Get an array of extension definitions of category `views` which are valid and applicable to the specified `domainObject`. From efb7611f6ed43d2c2b72827680023534c93b1ccd Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Thu, 15 Oct 2015 12:35:38 -0700 Subject: [PATCH 071/488] Added Table of Contents generation --- docs/gendocs.js | 17 +++ docs/src/architecture/Framework.md | 12 ++ docs/src/architecture/Platform.md | 25 ++++ docs/src/architecture/index.md | 10 ++ docs/src/guide/index.md | 198 ++++++++++++++++++++++++----- docs/src/tutorials/index.md | 74 ++++++++--- package.json | 6 +- 7 files changed, 293 insertions(+), 49 deletions(-) diff --git a/docs/gendocs.js b/docs/gendocs.js index 329a9b607f..71d8ac6bf7 100644 --- a/docs/gendocs.js +++ b/docs/gendocs.js @@ -117,6 +117,22 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define }; return transform; } + + function strip() { + var patternsToStrip = [ + "^ + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Overview](#overview) + - [Application Initialization](#application-initialization) + - [Architectural Paradigm](#architectural-paradigm) + - [Extension Categories](#extension-categories) + - [Composite Services](#composite-services) + + + # Overview The framework layer's most basic responsibility is allowing individual diff --git a/docs/src/architecture/Platform.md b/docs/src/architecture/Platform.md index a59a6ebf9c..cd788c3cd8 100644 --- a/docs/src/architecture/Platform.md +++ b/docs/src/architecture/Platform.md @@ -1,3 +1,28 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Overview](#overview) + - [Platform Architecture](#platform-architecture) + - [Application Start-up](#application-start-up) +- [Presentation Layer](#presentation-layer) + - [Angular built-ins](#angular-built-ins) + - [Domain object representation](#domain-object-representation) +- [Information Model](#information-model) + - [Capabilities and Services](#capabilities-and-services) +- [Service Infrastructure](#service-infrastructure) + - [Object Service](#object-service) + - [Model Service](#model-service) + - [Capability Service](#capability-service) + - [Telemetry Service](#telemetry-service) + - [Persistence Service](#persistence-service) + - [Action Service](#action-service) + - [View Service](#view-service) + - [Policy Service](#policy-service) + - [Type Service](#type-service) + + + # Overview The Open MCT Web platform utilizes the [framework layer](Framework.md) diff --git a/docs/src/architecture/index.md b/docs/src/architecture/index.md index fd9e0961e1..cbf73592db 100644 --- a/docs/src/architecture/index.md +++ b/docs/src/architecture/index.md @@ -1,3 +1,13 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Introduction](#introduction) +- [Overview](#overview) + - [Software Architecture](#software-architecture) + + + # Introduction The purpose of this document is to familiarize developers with the diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index c1a48797ab..2261511280 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -13,11 +13,149 @@ May 12, 2015 | 0.1 | | Victor Woeltjen June 4, 2015 | 1.0 | Name Changes | Victor Woeltjen October 4, 2015 | 1.1 | Conversion to MarkDown | Andrew Henry +# Contents + + + + + +- [Introduction](#introduction) + - [What is Open MCT Web](#what-is-open-mct-web) + - [Client-Server Relationship](#client-server-relationship) + - [Developing with Open MCT Web](#developing-with-open-mct-web) + - [Technologies](#technologies) + - [Forking](#forking) +- [Overview](#overview) + - [Framework Overview](#framework-overview) + - [Tiers](#tiers) + - [Platform Overview](#platform-overview) + - [Web Services](#web-services) + - [Glossary](#glossary) +- [Framework](#framework) + - [Bundles](#bundles) + - [Configuring Active Bundles](#configuring-active-bundles) + - [Bundle Definition](#bundle-definition) + - [Bundle Directory Structure](#bundle-directory-structure) + - [Extensions](#extensions) + - [General Extensions](#general-extensions) + - [Extension Definitions](#extension-definitions) + - [Partial Construction](#partial-construction) + - [Priority](#priority) + - [Angular Built-ins](#angular-built-ins) + - [Angular Directives](#angular-directives) + - [Angular Controllers](#angular-controllers) + - [Angular Services](#angular-services) + - [Angular Constants](#angular-constants) + - [Angular Runs](#angular-runs) + - [Angular Routes](#angular-routes) + - [Composite Services](#composite-services) +- [Core API](#core-api) + - [Domain Objects](#domain-objects) + - [Domain Object Actions](#domain-object-actions) + - [Action Contexts](#action-contexts) + - [Telemetry](#telemetry) + - [Telemetry Requests](#telemetry-requests) + - [Telemetry Responses](#telemetry-responses) + - [Telemetry Series](#telemetry-series) + - [Telemetry Metadata](#telemetry-metadata) + - [Types](#types) + - [Type Features](#type-features) + - [Type Properties](#type-properties) +- [Extension Categories](#extension-categories) + - [Actions Category](#actions-category) + - [Capabilities Category](#capabilities-category) + - [Controls Category](#controls-category) + - [Gestures Category](#gestures-category) + - [Indicators Category](#indicators-category) + - [Standard Indicators](#standard-indicators) + - [Custom Indicators](#custom-indicators) + - [Licenses Category](#licenses-category) + - [Policies Category](#policies-category) + - [Representations Category](#representations-category) + - [Representation Scope](#representation-scope) + - [Representers Category](#representers-category) + - [Roots Category](#roots-category) + - [Stylesheets Category](#stylesheets-category) + - [Templates Category](#templates-category) + - [Types Category](#types-category) + - [Versions Category](#versions-category) + - [Views Category](#views-category) + - [View Scope](#view-scope) + - [Selection State](#selection-state) +- [Directives](#directives) + - [Before Unload](#before-unload) + - [Chart](#chart) + - [Container](#container) + - [Control](#control) + - [Drag](#drag) + - [Form](#form) + - [Form Structure](#form-structure) + - [Form Controls](#form-controls) + - [Include](#include) + - [Representation](#representation) + - [Resize](#resize) + - [Scroll](#scroll) + - [Toolbar](#toolbar) + - [Toolbar Structure](#toolbar-structure) +- [Services](#services) + - [Composite Type Services](#composite-type-services) + - [Action Service](#action-service) + - [Capability Service](#capability-service) + - [Dialog Service](#dialog-service) + - [Dialog Structure](#dialog-structure) + - [Domain Object Service](#domain-object-service) + - [Gesture Service](#gesture-service) + - [Model Service](#model-service) + - [Persistence Service](#persistence-service) + - [Policy Service](#policy-service) + - [Telemetry Service](#telemetry-service) + - [Type Service](#type-service) + - [View Service](#view-service) + - [Other Services](#other-services) + - [Drag and Drop](#drag-and-drop) + - [Navigation](#navigation) + - [Now](#now) + - [Telemetry Formatter](#telemetry-formatter) + - [Telemetry Handler](#telemetry-handler) + - [Telemetry Handle](#telemetry-handle) +- [Models](#models) + - [General Metadata](#general-metadata) + - [Extension-specific Properties](#extension-specific-properties) + - [Capability-specific Properties](#capability-specific-properties) + - [View Configurations](#view-configurations) + - [Modifying Models](#modifying-models) +- [Capabilities](#capabilities) + - [Action Capability](#action-capability) + - [Composition Capability](#composition-capability) + - [Delegation Capability](#delegation-capability) + - [Editor Capability](#editor-capability) + - [Mutation Capability](#mutation-capability) + - [Mutator Function](#mutator-function) + - [Persistence Capability](#persistence-capability) + - [Relationship Capability](#relationship-capability) + - [Telemetry Capability](#telemetry-capability) + - [Type Capability](#type-capability) + - [View Capability](#view-capability) +- [Actions](#actions) + - [Action Categories](#action-categories) + - [Platform Actions](#platform-actions) +- [Policies](#policies) + - [Policy Categories](#policy-categories) +- [Build-Test-Deploy](#build-test-deploy) + - [Command-line Build](#command-line-build) + - [Test Suite](#test-suite) + - [Code Coverage](#code-coverage) + - [Deployment](#deployment) + - [Configuration](#configuration) + - [Configuration Constants](#configuration-constants) + + + # Introduction The purpose of this guide is to familiarize software developers with the Open MCT Web platform. -## What is Open MCT Web? +## What is Open MCT Web Open MCT Web is a platform for building user interface and display tools, developed at the NASA Ames Research Center in collaboration with teams at the Jet Propulsion Laboratory. It is written in HTML5, CSS3, and JavaScript, using @@ -676,7 +814,7 @@ If the provided capability has no invoke method, the return value here functions as `getCapability` including returning `undefined` if the capability is not exposed. -## Actions +## Domain Object Actions An `Action` is behavior that can be performed upon/using a `DomainObject`. An Action has the following interface: @@ -820,7 +958,7 @@ specific types; it does not contain a catalog of the extension instances of these categories provided by the platform. Relevant summaries there are provided in subsequent sections. -## Actions +## Actions Category An action is a thing that can be done to or using a domain object, typically as initiated by the user. @@ -853,7 +991,7 @@ Categories supported by the platform include: * `glyph`: A single character which will be rendered in Open MCT Web's custom font set as an icon for this action. -## Capabilities +## Capabilities Category Capabilities are exposed by domain objects (e.g. via the `getCapability` method) but most commonly originate as extensions of this category. @@ -876,7 +1014,7 @@ which should return a boolean value, and will be used by the platform to filter down capabilities to those which should be exposed by specific domain objects, based on their domain object models. -## Controls +## Controls Category Controls provide options for the `mct-control` directive. @@ -911,7 +1049,7 @@ of an individual row definition. * `field`: Name of the field in `ngModel` which will hold the value for this control. -## Gestures +## Gestures Category A _gesture_ is a user action which can be taken upon a representation of a domain object. @@ -935,7 +1073,7 @@ gesture's implementation may also expose an optional `destroy()` method which will be called when the gesture should be removed, to avoid memory leaks by way of unremoved listeners. -## Indicators +## Indicators Category An indicator is an element that should appear in the status area at the bottom of a running Open MCT Web client instance. @@ -971,7 +1109,7 @@ an `mct-include` directive (so should refer to an extension of category templates .) This template will be rendered to the status area. Indicators of this variety do not need to provide an implementation. -## Licenses +## Licenses Category The extension category `licenses` can be used to add entries into the 'Licensing information' page, reachable from Open MCT Web's About dialog. @@ -985,7 +1123,7 @@ Licenses may have the following properties, all of which are strings: * `copyright`: Copyright text to display for this component. * `link`: URL to full license text. -## Policies +## Policies Category Policies are used to handle decisions made using Open MCT Web's `policyService`; examples of these decisions are determining the applicability of certain @@ -1012,7 +1150,7 @@ when and only when all policies choose to allow it. As such, policies should generally be written to reject a certain case, and allow (by returning `true`) anything else. -## Representations +## Representations Category A representation is an Angular template used to display a domain object. The `representations` extension category is used to add options for the @@ -1062,7 +1200,7 @@ representation state. * Any capabilities requested by the uses property of the representation definition. -## Representers +## Representers Category The `representers` extension category is used to add additional behavior to the `mct-representation` directive. This extension category is intended primarily @@ -1086,7 +1224,7 @@ event listeners to the element, etc. This implementation must provide a single method, `destroy()`, which will be invoked when the representer is no longer needed. -## Roots +## Roots Category The extension category `roots` is used to provide root-level domain object models. Root-level domain objects appear at the top-level of the tree hierarchy. @@ -1097,7 +1235,7 @@ Extensions of this category should have the following properties: * `id`: The machine-readable identifier for the domaiwn object being exposed. * `model`: The model, as a JSON object, for the domain object being exposed. -## Stylesheets +## Stylesheets Category The stylesheets extension category is used to add CSS files to style the application. Extension definitions for this category should include one @@ -1110,7 +1248,7 @@ include. This path is relative to the bundle's resources folder (by default, To control the order of CSS files, use priority (see the section on Extension Definitions above.) -## Templates +## Templates Category The `templates` extension category is used to expose Angular templates under symbolic identifiers. These can then be utilized using the `mct-include` @@ -1131,7 +1269,7 @@ in the bottom right, for instance.) Templates do not have implementations. -## Types +## Types Category The types extension category describes types of domain objects which may appear within Open MCT Web. @@ -1168,7 +1306,7 @@ property is described by an object containing the following properties: Types do not have implementations. -## Versions +## Versions Category The versions extension category is used to introduce line items in Open MCT Web's About dialog. These should have the following properties: @@ -1182,7 +1320,7 @@ To control the ordering of line items within the About dialog, use `priority`. This extension category does not have implementations. -## Views +## Views Category The views extension category is used to determine which options appear to the user as available views of domain objects of specific types. A view's extension @@ -1536,7 +1674,7 @@ Composite Services.) * _Other services_ which are defined as standalone service objects; these can be utilized by plugins but are not intended to be modified or augmented. -## Composite Services +## Composite Type Services This section describes the composite services exposed by Open MCT Web, specifically focusing on their interface and contract. @@ -1924,7 +2062,7 @@ implementation of a given capability which might not invoke the underlying service, while user code which interacts with capabilities remains indifferent to this detail. -## Action +## Action Capability The `action` capability is present for all domain objects. It allows applicable `Action` instances to be retrieved and performed for specific domain objects. @@ -1946,7 +2084,7 @@ will instead be used as the `key` of the action context. Returns a `Promise` for the result of the action that was performed, or `undefined` if no matching action was found. -## Composition +## Composition Capability The `composition` capability provides access to domain objects that are contained by this domain object. While the `composition` property of a domain @@ -1959,7 +2097,7 @@ This capability has the following interface: * `invoke()`: Returns a `Promise` for an array of `DomainObject` instances. -## Delegation +## Delegation Capability The delegation capability is used to communicate the intent of a domain object to delegate responsibilities, which would normally handled by other @@ -1980,7 +2118,7 @@ strings describing which capabilities domain objects of that type wish to delegate. If this property is not present, the delegation capability will not be present in domain objects of that type. -## Editor +## Editor Capability The editor capability is meant primarily for internal use by Edit mode, and helps to manage the behavior associated with exiting _Edit_ mode via _Save_ or @@ -1988,7 +2126,7 @@ _Cancel_. Its interface is not intended for general use. However, `domainObject.hasCapability(editor)` is a useful way of determining whether or not we are looking at an object in _Edit_ mode. -## Mutation +## Mutation Capability The `mutation` capability provides a means by which the contents of a domain object's model can be modified. This capability is provided by the platform for @@ -2005,7 +2143,7 @@ capability; other platform behavior is likely to break (either by exhibiting undesired behavior, or failing to exhibit desired behavior) if models are modified by other means. -### Mutator Function +### Mutator Function The mutator argument above is a function which will receive a cloned copy of the domain object's model as a single argument. It may return: @@ -2019,7 +2157,7 @@ for this domain object. (including any changes made in place by the mutator function) will be used as the new domain object model. -## Persistence +## Persistence Capability The persistence capability provides a mean for interacting with the underlying persistence service which stores this domain object's model. It has the @@ -2034,7 +2172,7 @@ completed. * `getSpace()`: Return the string which identifies the persistence space which stores this domain object. -## Relationship +## Relationship Capability The relationship capability provides a means for accessing other domain objects with which this domain object has some typed relationship. It has the following @@ -2051,7 +2189,7 @@ objects which has a `relationships` property in their model, whose value is an object containing key-value pairs, where keys are strings identifying relationship types, and values are arrays of domain object identifiers. -## Telemetry +## Telemetry Capability The telemetry capability provides a means for accessing telemetry data associated with a domain object. It has the following interface: @@ -2074,11 +2212,11 @@ objects which has a `telemetry` property in their model and/or type definition; this object will serve as a template for telemetry requests made using this object, and will also be returned by `getMetadata()` above. -## Type +## Type Capability The `type` capability exposes information about the domain object's type. It has the same interface as `Type`; see Core API. -## View +## View Capability The `view` capability exposes views which are applicable to a given domain object. It has the following interface: @@ -2145,7 +2283,7 @@ contained. The candidate argument is the view's extension definition; the context argument is the `DomainObject` to be viewed. -# Build, Test, Deploy +# Build-Test-Deploy Open MCT Web is designed to support a broad variety of build and deployment options. The sources can be deployed in the same directory structure used during development. A few utilities are included to support development processes. diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index 5c6ac98691..c459066969 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -14,6 +14,46 @@ July 28, 2015 | 2.0 | Telemetry adapter tutorial | Victor Woeltjen July 31, 2015 | 2.1 | Clarify telemetry adapter details | Victor Woeltjen October 14, 2015 | 2.2 | Conversion to markdown | Andrew Henry +# Contents + + + + + +- [Introduction](#introduction) + - [This document](#this-document) + - [Setting Up Open MCT Web](#setting-up-open-mct-web) + - [Prerequisites](#prerequisites) + - [Check out Open MCT Web Sources](#check-out-open-mct-web-sources) + - [Configuring Persistence](#configuring-persistence) + - [Bundle Before](#bundle-before) + - [Bundle After](#bundle-after) + - [Run a Web Server](#run-a-web-server) + - [Viewing in Browser](#viewing-in-browser) +- [Tutorials](#tutorials) + - [To-do List](#to-do-list) + - [Step 1-Create the Plugin](#step-1-create-the-plugin) + - [Before](#before) + - [After](#after) + - [Step 2-Add a Domain Object Type](#step-2-add-a-domain-object-type) + - [Step 3-Add a View](#step-3-add-a-view) + - [Step 4-Add a Controller](#step-4-add-a-controller) + - [Step 5-Support Editing](#step-5-support-editing) + - [Step 6-Customizing Look and Feel](#step-6-customizing-look-and-feel) + - [Bar Graph](#bar-graph) + - [Step 1-Define the View](#step-1-define-the-view) + - [Step 2-Add a Controller](#step-2-add-a-controller) + - [Step 3-Using Telemetry Data](#step-3-using-telemetry-data) + - [Step 4-View Configuration](#step-4-view-configuration) + - [Telemetry Adapter](#telemetry-adapter) + - [Step 0-Expose Your Telemetry](#step-0-expose-your-telemetry) + - [Step 1-Add a Top-level Object](#step-1-add-a-top-level-object) + - [Step 2-Expose the Telemetry Dictionary](#step-2-expose-the-telemetry-dictionary) + - [Step 3-Historical Telemetry](#step-3-historical-telemetry) + - [Step 4-Real-time Telemetry](#step-4-real-time-telemetry) + + + # Introduction ## This document @@ -76,7 +116,7 @@ To change this configuration, edit bundles.json (at the top level of the Open MCT Web repository) and replace platform/persistence/elastic with example/persistence. -#### Before +#### Bundle Before [ "platform/framework", @@ -102,7 +142,7 @@ example/persistence. ] __bundles.json__ -#### After +#### Bundle After [ "platform/framework", @@ -167,7 +207,7 @@ The goal of this tutorial is to add a new application feature to Open MCT Web: To-do lists. Users should be able to create and manage these to track items that they need to do. This is modelled after the to-do lists at [http://todomvc.com/](). -### Step 1. Create the Plugin +### Step 1-Create the Plugin The first step to adding a new feature to Open MCT Web is to create the plugin which will expose that feature. A plugin in Open MCT Web is represented by what @@ -254,7 +294,7 @@ should see: ...which shows that our plugin has loaded. -### Step 2. Add a Domain Object Type +### Step 2-Add a Domain Object Type Features in a Open MCT Web application are most commonly expressed as domain objects and/or views thereof. A domain object is some thing that is relevant to @@ -311,7 +351,7 @@ At this point, our to-do list doesn’t do much of anything; we can create them and give them names, but they don’t have any specific functionality attached, because we haven’t defined any yet. -### Step 3. Add a View +### Step 3-Add a View In order to allow a to-do list to be used, we need to define and display its contents. In Open MCT Web, the pattern that the user expects is that they’ll @@ -447,7 +487,7 @@ the domain object - if we click over to My Items and come back to our To-Do List, for instance, we’ll see that those check boxes have returned to their initial state. -### Step 4. Add a Controller +### Step 4-Add a Controller We need to do some scripting to add dynamic behavior to that view. In particular, we want to: @@ -613,7 +653,7 @@ able to filter down the visible list, and the changes we make will stick around if we go to My Items and come back. -### Step 5. Support Editing +### Step 5-Support Editing We now have a somewhat-functional view of our To-Do List, but we’re still missing some important functionality: Adding and removing tasks! @@ -938,7 +978,7 @@ button appear, which we can then click on to remove that task: As always in Edit mode, the user will be able to Save or Cancel any changes they have made. In terms of functionality, our To-Do List can do all the things we want, but the appearance is still lacking. In particular, we can’t distinguish our current filter choice or our current selection state. -### Step 6. Customizing Look and Feel +### Step 6-Customizing Look and Feel In this section, our goal is to: @@ -1227,7 +1267,7 @@ It is recommended that the reader completes (or is familiar with) the To-Do List tutorial before completing this tutorial, as certain concepts discussed there will be addressed in more brevity here. -### Step 1. Define the View +### Step 1-Define the View Since the goal is to introduce a new view and expose it from a plugin, we will want to create a new bundle which declares an extension of category `views`. @@ -1405,7 +1445,7 @@ _Sine Wave Generator_) as well as for _Telemetry Panel_ objects: This means that our remaining work will be to populate and position these elements based on the actual contents of the domain object. -### Step 2. Add a Controller +### Step 2-Add a Controller Our next step will be to begin dynamically populating this template’s contents. Specifically, our goals for this step will be to: @@ -1556,7 +1596,7 @@ this Telemetry Panel containing four Sine Wave Generators. ![Bar Plot](images/bar-plot-2.png) -### Step 3. Using Telemetry Data +### Step 3-Using Telemetry Data Now that our bar graph is labeled correctly, it’s time to start putting data into the view. @@ -1660,7 +1700,7 @@ When we reload Open MCT Web, our bar graph view now looks like: ![Bar Plot](images/bar-plot-3.png) -### Step 4. View Configuration +### Step 4-View Configuration The default minimum and maximum values we’ve provided happen to make sense for sine waves, but what about other values? We want to provide the user with a @@ -1849,7 +1889,7 @@ A summary of the steps we will take: * Support subscription/unsubscription to real-time streaming data. * Support historical retrieval of telemetry data. -### Step 0. Expose Your Telemetry +### Step 0-Expose Your Telemetry As a precondition to integrating telemetry data into Open MCT Web, this information needs to be available over web-based interfaces. In practice, @@ -2129,7 +2169,7 @@ like [https://www.npmjs.com/package/wscat](): Now that the example server’s interface is reasonably well-understood, a plugin can be written to adapt Open MCT Web to utilize it. -### Step 1. Add a Top-level Object +### Step 1-Add a Top-level Object Since Open MCT Web uses an “object-first” approach to accessing data, before we’ll be able to do anything with this new data source, we’ll need to have a @@ -2226,7 +2266,7 @@ __bundles.json__ Now, we have somewhere in the UI to put the contents of our telemetry dictionary. -### Step 2. Expose the Telemetry Dictionary +### Step 2-Expose the Telemetry Dictionary In order to expose the telemetry dictionary, we first need to read it from the server. Our first step will be to add a service that will handle interactions @@ -2599,7 +2639,7 @@ dictionary: Note that “My Spacecraft” has changed its name to “Example Spacecraft”, which is the name it had in the dictionary. -### Step 3. Historical Telemetry +### Step 3-Historical Telemetry After Step 2, we are able to see our dictionary in the user interface and click around our different measurements, but we don’t see any data. We need to give @@ -2870,7 +2910,7 @@ We can now visualize our data, but it doesn’t update over time - we know the server is continually producing new data, but we have to click away and come back to see it. We can fix this by adding support for telemetry subscriptions. -### Step 4. Real-time Telemetry +### Step 4-Real-time Telemetry Finally, we want to utilize the server’s ability to subscribe to telemetry from Open MCT Web. To do this, first we want to expose some new methods for diff --git a/package.json b/package.json index a1ac01f437..9ddfdefa69 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "split": "^1.0.0", "mkdirp": "^0.5.1", "nomnoml": "^0.0.3", - "canvas": "^1.2.7" + "canvas": "^1.2.7", + "doctoc": "0.15.0" }, "scripts": { "start": "node app.js", @@ -30,8 +31,9 @@ "jshint": "jshint platform example || exit 0", "watch": "karma start", "jsdoc": "jsdoc -c jsdoc.json -r -d target/docs/api", + "doctoc": "doctoc docs/src", "otherdoc": "node docs/gendocs.js --in docs/src --out target/docs", - "docs": "npm run jsdoc ; npm run otherdoc" + "docs": "npm run jsdoc ; npm run doctoc; npm run otherdoc" }, "repository": { "type": "git", From 688718cad0070acc5d0452cef7a3e384971dc9f6 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 15 Oct 2015 13:10:03 -0700 Subject: [PATCH 072/488] Fixed jslint errors --- docs/gendocs.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/gendocs.js b/docs/gendocs.js index 71d8ac6bf7..8f375aec0f 100644 --- a/docs/gendocs.js +++ b/docs/gendocs.js @@ -120,9 +120,8 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define function strip() { var patternsToStrip = [ - "^ - +
- + Filtered by: {{ ngModel.filtersString }} - - +
- + - -
\ No newline at end of file + +
diff --git a/platform/search/src/controllers/SearchController.js b/platform/search/src/controllers/SearchController.js index 10cf056b4f..88c9663f51 100644 --- a/platform/search/src/controllers/SearchController.js +++ b/platform/search/src/controllers/SearchController.js @@ -26,146 +26,157 @@ */ define(function () { "use strict"; - + var INITIAL_LOAD_NUMBER = 20, LOAD_INCREMENT = 20; - + + /** + * Controller for search in Tree View. + * + * Filtering is currently buggy; it filters after receiving results from + * search providers, the downside of this is that it requires search + * providers to provide objects for all possible results, which is + * potentially a hit to persistence, thus can be very very slow. + * + * Ideally, filtering should be handled before loading objects from the persistence + * store, the downside to this is that filters must be applied to object + * models, not object instances. + * + * @constructor + * @param $scope + * @param searchService + */ function SearchController($scope, searchService) { - // numResults is the amount of results to display. Will get increased. - // fullResults holds the most recent complete searchService response object - var numResults = INITIAL_LOAD_NUMBER, - fullResults = {hits: []}; - - // Scope variables are: - // Variables used only in SearchController: - // results, an array of searchResult objects - // loading, whether search() is loading - // ngModel.input, the text of the search query - // ngModel.search, a boolean of whether to display search or the tree - // Variables used also in SearchMenuController: - // ngModel.filter, the function filter defined below - // ngModel.types, an array of type objects - // ngModel.checked, a dictionary of which type filter options are checked - // ngModel.checkAll, a boolean of whether to search all types - // ngModel.filtersString, a string list of what filters on the results are active - $scope.results = []; - $scope.loading = false; - - - // Filters searchResult objects by type. Allows types that are - // checked. (ngModel.checked['typekey'] === true) - // If hits is not provided, will use fullResults.hits - function filter(hits) { - var newResults = [], - i = 0; - - if (!hits) { - hits = fullResults.hits; - } - - // If checkAll is checked, search everything no matter what the other - // checkboxes' statuses are. Otherwise filter the search by types. - if ($scope.ngModel.checkAll) { - newResults = fullResults.hits.slice(0, numResults); - } else { - while (newResults.length < numResults && i < hits.length) { - // If this is of an acceptable type, add it to the list - if ($scope.ngModel.checked[hits[i].object.getModel().type]) { - newResults.push(fullResults.hits[i]); - } - i += 1; - } - } - - $scope.results = newResults; - return newResults; - } - - // Make function accessible from SearchMenuController - $scope.ngModel.filter = filter; - - // For documentation, see search below - function search(maxResults) { - var inputText = $scope.ngModel.input; - - if (inputText !== '' && inputText !== undefined) { - // We are starting to load. - $scope.loading = true; - - // Update whether the file tree should be displayed - // Hide tree only when starting search - $scope.ngModel.search = true; - } - - if (!maxResults) { - // Reset 'load more' - numResults = INITIAL_LOAD_NUMBER; - } - - // Send the query - searchService.query(inputText, maxResults).then(function (result) { - // Store all the results before splicing off the front, so that - // we can load more to display later. - fullResults = result; - $scope.results = filter(result.hits); - - // Update whether the file tree should be displayed - // Reveal tree only when finishing search - if (inputText === '' || inputText === undefined) { - $scope.ngModel.search = false; - } - - // Now we are done loading. - $scope.loading = false; - }); - } - - return { - /** - * Search the filetree. Assumes that any search text will - * be in ngModel.input - * - * @param maxResults (optional) The maximum number of results - * that this function should return. If not provided, search - * service default will be used. - */ - search: search, - - /** - * Checks to see if there are more search results to display. If the answer is - * unclear, this function will err toward saying that there are more results. - */ - areMore: function () { - var i; - - // Check to see if any of the not displayed results are of an allowed type - for (i = numResults; i < fullResults.hits.length; i += 1) { - if ($scope.ngModel.checkAll || $scope.ngModel.checked[fullResults.hits[i].object.getModel().type]) { - return true; - } - } - - // If none of the ones at hand are correct, there still may be more if we - // re-search with a larger maxResults - return fullResults.hits.length < fullResults.total; - }, - - /** - * Increases the number of search results to display, and then - * loads them, adding to the displayed results. - */ - loadMore: function () { - numResults += LOAD_INCREMENT; - - if (numResults > fullResults.hits.length && fullResults.hits.length < fullResults.total) { - // Resend the query if we are out of items to display, but there are more to get - search(numResults); - } else { - // Otherwise just take from what we already have - $scope.results = filter(fullResults.hits); - } - } + var controller = this; + this.$scope = $scope; + this.searchService = searchService; + this.numberToDisplay = INITIAL_LOAD_NUMBER; + this.fullResults = []; + this.filteredResults = []; + this.$scope.results = []; + this.$scope.loading = false; + this.pendingQuery = undefined; + this.$scope.ngModel.filter = function () { + return controller.onFilterChange.apply(controller, arguments); }; } + + /** + * Returns true if there are more results than currently displayed for the + * for the current query and filters. + */ + SearchController.prototype.areMore = function () { + return this.$scope.results.length < this.filteredResults.length; + }; + + /** + * Display more results for the currently displayed query and filters. + */ + SearchController.prototype.loadMore = function () { + this.numberToDisplay += LOAD_INCREMENT; + this.updateResults(); + }; + + /** + * Search for the query string specified in scope. + */ + SearchController.prototype.search = function () { + var inputText = this.$scope.ngModel.input, + controller = this; + + this.clearResults(); + + if (inputText) { + this.$scope.loading = true; + this.$scope.ngModel.search = true; + } else { + this.pendingQuery = undefined; + this.$scope.ngModel.search = false; + this.$scope.loading = false; + return; + } + + if (this.pendingQuery === inputText) { + return; // don't issue multiple queries for the same term. + } + + this.pendingQuery = inputText; + + this + .searchService + .query(inputText, 60) // TODO: allow filter in search service. + .then(function (results) { + if (controller.pendingQuery !== inputText) { + return; // another query in progress, so skip this one. + } + controller.onSearchComplete(results); + }); + }; + + /** + * Refilter results and update visible results when filters have changed. + */ + SearchController.prototype.onFilterChange = function () { + this.filter(); + this.updateVisibleResults(); + }; + + /** + * Filter `fullResults` based on currenly active filters, storing the result + * in `filteredResults`. + * + * @private + */ + SearchController.prototype.filter = function () { + var includeTypes = this.$scope.ngModel.checked; + + if (this.$scope.ngModel.checkAll) { + this.filteredResults = this.fullResults; + return; + } + + this.filteredResults = this.fullResults.filter(function (hit) { + return includeTypes[hit.object.getModel().type]; + }); + }; + + + /** + * Clear the search results. + * + * @private + */ + SearchController.prototype.clearResults = function () { + this.$scope.results = []; + this.fullResults = []; + this.filteredResults = []; + this.numberToDisplay = INITIAL_LOAD_NUMBER; + }; + + + + /** + * Update search results from given `results`. + * + * @private + */ + SearchController.prototype.onSearchComplete = function (results) { + this.fullResults = results.hits; + this.filter(); + this.updateVisibleResults(); + this.$scope.loading = false; + this.pendingQuery = undefined; + }; + + /** + * Update visible results from filtered results. + * + * @private + */ + SearchController.prototype.updateVisibleResults = function () { + this.$scope.results = + this.filteredResults.slice(0, this.numberToDisplay); + }; + return SearchController; }); From b5505f372f6a737a2a00a1f987aee97ee5facecf Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 16 Oct 2015 12:39:41 -0700 Subject: [PATCH 076/488] [Search] Generic Worker Performance Tweaks The generic search worker now does indexing work during the index operation, ensuring that queries do not have to do extraneous or repeat calculations. Change the return format slightly and fixed a bug in the GenericSearchProvider which caused more objects than intended to be returned from the provider. --- .../src/services/GenericSearchProvider.js | 39 ++-- .../src/services/GenericSearchWorker.js | 205 +++++++++--------- 2 files changed, 123 insertions(+), 121 deletions(-) diff --git a/platform/search/src/services/GenericSearchProvider.js b/platform/search/src/services/GenericSearchProvider.js index 9574d156fb..d38326bb9d 100644 --- a/platform/search/src/services/GenericSearchProvider.js +++ b/platform/search/src/services/GenericSearchProvider.js @@ -96,36 +96,33 @@ define( // Handles responses from the web worker. Namely, the results of // a search request. function handleResponse(event) { - var ids = [], - id; + if (event.data.request !== 'search') { + return; // no idea how to handle anything else. + } - // If we have the results from a search - if (event.data.request === 'search') { - // Convert the ids given from the web worker into domain objects - for (id in event.data.results) { - ids.push(id); - } - objectService.getObjects(ids).then(function (objects) { - var searchResults = [], - id; + var workerResults = event.data.results, + ids = Object.keys(workerResults); - // Create searchResult objects - for (id in objects) { - searchResults.push({ - object: objects[id], - id: id, - score: event.data.results[id] + objectService + .getObjects(ids) + .then(function (objects) { + var searchResults = Object + .keys(objects) + .map(function (id) { + return { + object: objects[id], + id: id, + score: workerResults[id].matchCount + }; }); - } // Resove the promise corresponding to this pendingQueries[event.data.timestamp].resolve({ hits: searchResults, - total: event.data.total, + total: searchResults.length, timedOut: event.data.timedOut }); }); - } } function requestAndIndex(id) { @@ -212,7 +209,7 @@ define( var message = { request: 'search', input: searchInput, - maxNumber: maxResults, + maxResults: maxResults, timestamp: timestamp, timeout: timeout }; diff --git a/platform/search/src/services/GenericSearchWorker.js b/platform/search/src/services/GenericSearchWorker.js index 57be98b423..cd45929d4f 100644 --- a/platform/search/src/services/GenericSearchWorker.js +++ b/platform/search/src/services/GenericSearchWorker.js @@ -26,78 +26,55 @@ */ (function () { "use strict"; - + // An array of objects composed of domain object IDs and models // {id: domainObject's ID, model: domainObject's model} - var indexedItems = []; - - // Helper function for serach() - function convertToTerms(input) { - var terms = input; - // Shave any spaces off of the ends of the input - while (terms.substr(0, 1) === ' ') { - terms = terms.substring(1, terms.length); - } - while (terms.substr(terms.length - 1, 1) === ' ') { - terms = terms.substring(0, terms.length - 1); - } - - // Then split it at spaces and asterisks - terms = terms.split(/ |\*/); - - // Remove any empty strings from the terms - while (terms.indexOf('') !== -1) { - terms.splice(terms.indexOf(''), 1); - } - - return terms; + var indexedItems = [], + TERM_SPLITTER = /[ _\*]/; + + function indexItem(id, model) { + var vector = { + name: model.name + }; + vector.cleanName = model.name.trim(); + vector.lowerCaseName = vector.cleanName.toLocaleLowerCase(); + vector.terms = vector.lowerCaseName.split(TERM_SPLITTER); + + indexedItems.push({ + id: id, + vector: vector, + model: model + }); } - + // Helper function for search() - function scoreItem(item, input, terms) { - var name = item.model.name.toLocaleLowerCase(), - weight = 0.65, - score = 0.0, - i; - - // Make the score really big if the item name and - // the original search input are the same - if (name === input) { - score = 42; - } - - for (i = 0; i < terms.length; i += 1) { - // Increase the score if the term is in the item name - if (name.indexOf(terms[i]) !== -1) { - score += 1; - - // Add extra to the score if the search term exists - // as its own term within the items - if (name.split(' ').indexOf(terms[i]) !== -1) { - score += 0.5; - } - } - } - - return score * weight; + function convertToTerms(input) { + var query = { + exactInput: input + }; + query.inputClean = input.trim(); + query.inputLowerCase = query.inputClean.toLocaleLowerCase(); + query.terms = query.inputLowerCase.split(TERM_SPLITTER); + query.exactTerms = query.inputClean.split(TERM_SPLITTER); + return query; } - - /** + + /** * Gets search results from the indexedItems based on provided search * input. Returns matching results from indexedItems, as well as the * timestamp that was passed to it. - * + * * @param data An object which contains: * * input: The original string which we are searching with - * * maxNumber: The maximum number of search results desired + * * maxResults: The maximum number of search results desired * * timestamp: The time identifier from when the query was made */ function search(data) { - // This results dictionary will have domain object ID keys which - // point to the value the domain object's score. - var results = {}, - input = data.input.toLocaleLowerCase(), - terms = convertToTerms(input), + // This results dictionary will have domain object ID keys which + // point to the value the domain object's score. + var results, + input = data.input, + query = convertToTerms(input), message = { request: 'search', results: {}, @@ -105,54 +82,82 @@ timestamp: data.timestamp, timedOut: false }, - score, - i, - id; - - // If the user input is empty, we want to have no search results. - if (input !== '') { - for (i = 0; i < indexedItems.length; i += 1) { - // If this is taking too long, then stop - if (Date.now() > data.timestamp + data.timeout) { - message.timedOut = true; - break; - } - - // Score and add items - score = scoreItem(indexedItems[i], input, terms); - if (score > 0) { - results[indexedItems[i].id] = score; - message.total += 1; - } - } + matches = {}; + + if (!query.inputClean) { + // No search terms, no results; + return message; } - - // Truncate results if there are more than maxResults - if (message.total > data.maxResults) { - i = 0; - for (id in results) { - message.results[id] = results[id]; - i += 1; - if (i >= data.maxResults) { - break; + + // Two phases: find matches, then score matches. + // Idea being that match finding should be fast, so that future scoring + // operations process fewer objects. + + query.terms.forEach(function findMatchingItems(term) { + indexedItems + .filter(function matchesItem(item) { + return item.vector.lowerCaseName.indexOf(term) !== -1; + }) + .forEach(function trackMatch(matchedItem) { + if (!matches[matchedItem.id]) { + matches[matchedItem.id] = { + matchCount: 0, + item: matchedItem + }; + } + matches[matchedItem.id].matchCount += 1; + }); + }); + + // Then, score matching items. + results = Object + .keys(matches) + .map(function asMatches(matchId) { + return matches[matchId]; + }) + .map(function prioritizeExactMatches(match) { + if (match.item.vector.name === query.exactInput) { + match.matchCount += 100; + } else if (match.item.vector.lowerCaseName === + query.inputLowerCase) { + match.matchCount += 50; } - } - // TODO: This seems inefficient. - } else { - message.results = results; - } - + return match; + }) + .map(function prioritizeCompleteTermMatches(match) { + match.item.vector.terms.forEach(function (term) { + if (query.terms.indexOf(term) !== -1) { + match.matchCount += 0.5; + } + }); + return match; + }) + .sort(function compare(a, b) { + if (a.matchCount > b.matchCount) { + return -1; + } + if (a.matchCount < b.matchCount) { + return 1; + } + return 0; + }); + + message.total = results.length; + message.results = results + .slice(0, data.maxResults) + .reduce(function arrayToObject(obj, match) { + obj[match.item.id] = match; + return obj; + }, {}); + return message; } - + self.onmessage = function (event) { if (event.data.request === 'index') { - indexedItems.push({ - id: event.data.id, - model: event.data.model - }); + indexItem(event.data.id, event.data.model); } else if (event.data.request === 'search') { self.postMessage(search(event.data)); } }; -}()); \ No newline at end of file +}()); From 099591ad2eb427ecde5d3dbde11a5c0c52b974c7 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 16 Oct 2015 15:26:04 -0700 Subject: [PATCH 077/488] [Search] Aggregator returns objects, providers return models Search providers return search results as models for domain objects, as the actual number of max results is enforced by the aggregator, and because the individual providers store and return the models for their objects already. This lowers the amount of resources consumed instantiating domain objects, and also allows the individual search providers to implement function-based filtering on domain object models, which is beneficial as it allows the search filtering in the search controller to be done before paginating of results. --- platform/search/bundle.json | 2 +- .../search/src/services/SearchAggregator.js | 290 +++++++++++------- 2 files changed, 182 insertions(+), 110 deletions(-) diff --git a/platform/search/bundle.json b/platform/search/bundle.json index 67dc058ed9..1117c3d161 100644 --- a/platform/search/bundle.json +++ b/platform/search/bundle.json @@ -59,7 +59,7 @@ "provides": "searchService", "type": "aggregator", "implementation": "services/SearchAggregator.js", - "depends": [ "$q" ] + "depends": [ "$q", "objectService" ] } ], "workers": [ diff --git a/platform/search/src/services/SearchAggregator.js b/platform/search/src/services/SearchAggregator.js index 2324090595..abb6478936 100644 --- a/platform/search/src/services/SearchAggregator.js +++ b/platform/search/src/services/SearchAggregator.js @@ -24,122 +24,194 @@ /** * Module defining SearchAggregator. Created by shale on 07/16/2015. */ -define( - [], - function () { - "use strict"; +define([ - var DEFUALT_TIMEOUT = 1000, - DEFAULT_MAX_RESULTS = 100; - - /** - * Allows multiple services which provide search functionality - * to be treated as one. - * - * @constructor - * @param $q Angular's $q, for promise consolidation. - * @param {SearchProvider[]} providers The search providers to be - * aggregated. - */ - function SearchAggregator($q, providers) { - this.$q = $q; - this.providers = providers; +], function ( + +) { + "use strict"; + + var DEFAULT_TIMEOUT = 1000, + DEFAULT_MAX_RESULTS = 100; + + /** + * Aggregates multiple search providers as a singular search provider. + * Search providers are expected to implement a `query` method which returns + * a promise for a `modelResults` object. + * + * The search aggregator combines the results from multiple providers, + * removes aggregates, and converts the results to domain objects. + * + * @constructor + * @param $q Angular's $q, for promise consolidation. + * @param objectService + * @param {SearchProvider[]} providers The search providers to be + * aggregated. + */ + function SearchAggregator($q, objectService, providers) { + this.$q = $q; + this.objectService = objectService; + this.providers = providers; + } + + /** + * Sends a query to each of the providers. Returns a promise for + * a result object that has the format + * {hits: searchResult[], total: number, timedOut: boolean} + * where a searchResult has the format + * {id: string, object: domainObject, score: number} + * + * @param {String} inputText The text input that is the query. + * @param {Number} maxResults (optional) The maximum number of results + * that this function should return. If not provided, a + * default of 100 will be used. + * @param {Function} [filter] if provided, will be called for every + * potential modelResult. If it returns false, the model result will be + * excluded from the search results. + * @returns {Promise} A Promise for a search result object. + */ + SearchAggregator.prototype.query = function ( + inputText, + maxResults, + filter + ) { + + var aggregator = this, + timestamp = Date.now(), + resultPromises; + + if (!maxResults) { + maxResults = DEFAULT_MAX_RESULTS; } - /** - * Sends a query to each of the providers. Returns a promise for - * a result object that has the format - * {hits: searchResult[], total: number, timedOut: boolean} - * where a searchResult has the format - * {id: string, object: domainObject, score: number} - * - * @param inputText The text input that is the query. - * @param maxResults (optional) The maximum number of results - * that this function should return. If not provided, a - * default of 100 will be used. - */ - SearchAggregator.prototype.query = function queryAll(inputText, maxResults) { - var $q = this.$q, - providers = this.providers, - i, - timestamp = Date.now(), - resultPromises = []; + resultPromises = this.providers.map(function (provider) { + return provider.query( + inputText, + timestamp, + maxResults, + DEFAULT_TIMEOUT + ); + }); - // Remove duplicate objects that have the same ID. Modifies the passed - // array, and returns the number that were removed. - function filterDuplicates(results, total) { - var ids = {}, - numRemoved = 0, - i; + return this.$q + .all(resultPromises) + .then(function (providerResults) { + var modelResults = { + hits: [], + totals: 0, + timedOut: false + }; - for (i = 0; i < results.length; i += 1) { - if (ids[results[i].id]) { - // If this result's ID is already there, remove the object - results.splice(i, 1); - numRemoved += 1; - - // Reduce loop index because we shortened the array - i -= 1; - } else { - // Otherwise add the ID to the list of the ones we have seen - ids[results[i].id] = true; - } - } - - return numRemoved; - } - - // Order the objects from highest to lowest score in the array. - // Modifies the passed array, as well as returns the modified array. - function orderByScore(results) { - results.sort(function (a, b) { - if (a.score > b.score) { - return -1; - } else if (b.score > a.score) { - return 1; - } else { - return 0; - } + providerResults.forEach(function (providerResult) { + modelResults.hits = + modelResults.hits.concat(providerResult.hits); + modelResults.totals += providerResult.totals; + modelResults.timedOut = + modelResults.timedOut || providerResult.timedOut; }); - return results; - } - if (!maxResults) { - maxResults = DEFAULT_MAX_RESULTS; - } + aggregator.orderByScore(modelResults); + aggregator.applyFilter(modelResults, filter); + aggregator.removeDuplicates(modelResults); - // Send the query to all the providers - for (i = 0; i < providers.length; i += 1) { - resultPromises.push( - providers[i].query(inputText, timestamp, maxResults, DEFUALT_TIMEOUT) - ); - } - - // Get promises for results arrays - return $q.all(resultPromises).then(function (resultObjects) { - var results = [], - totalSum = 0, - i; - - // Merge results - for (i = 0; i < resultObjects.length; i += 1) { - results = results.concat(resultObjects[i].hits); - totalSum += resultObjects[i].total; - } - // Order by score first, so that when removing repeats we keep the higher scored ones - orderByScore(results); - totalSum -= filterDuplicates(results, totalSum); - - return { - hits: results, - total: totalSum, - timedOut: resultObjects.some(function (obj) { - return obj.timedOut; - }) - }; + return aggregator.asObjectResults(modelResults); }); - }; + }; - return SearchAggregator; - } -); \ No newline at end of file + /** + * Order model results by score descending and return them. + */ + SearchAggregator.prototype.orderByScore = function (modelResults) { + modelResults.hits.sort(function (a, b) { + if (a.score > b.score) { + return -1; + } else if (b.score > a.score) { + return 1; + } else { + return 0; + } + }); + return modelResults; + }; + + /** + * Apply a filter to each model result, removing it from search results + * if it does not match. + */ + SearchAggregator.prototype.applyFilter = function (modelResults, filter) { + if (!filter) { + return modelResults; + } + var initialLength = modelResults.hits.length, + finalLength, + removedByFilter; + + modelResults.hits = modelResults.hits.filter(function (hit) { + return filter(hit.model); + }); + + finalLength = modelResults.hits; + removedByFilter = initialLength - finalLength; + modelResults.totals -= removedByFilter; + + return modelResults; + }; + + /** + * Remove duplicate hits in a modelResults object, and decrement `totals` + * each time a duplicate is removed. + */ + SearchAggregator.prototype.removeDuplicates = function (modelResults) { + var includedIds = {}; + + modelResults.hits = modelResults + .hits + .filter(function alreadyInResults(hit) { + if (includedIds[hit.id]) { + modelResults.totals -= 1; + return false; + } + includedIds[hit.id] = true; + return true; + }); + + return modelResults; + }; + + /** + * Convert modelResults to objectResults by fetching them from the object + * service. + * + * @returns {Promise} for an objectResults object. + */ + SearchAggregator.prototype.asObjectResults = function (modelResults) { + var objectIds = modelResults.hits.map(function (modelResult) { + return modelResult.id; + }); + + return this + .objectService + .getObjects(objectIds) + .then(function (objects) { + + var objectResults = { + totals: modelResults.totals, + timedOut: modelResults.timedOut + }; + + objectResults.hits = modelResults + .hits + .map(function asObjectResult(hit) { + return { + id: hit.id, + object: objects[hit.id], + score: hit.score + }; + }); + + return objectResults; + }); + }; + + return SearchAggregator; +}); From 78e5c0143b67c48c00adb40702e54829839eeda0 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 16 Oct 2015 15:26:46 -0700 Subject: [PATCH 078/488] [Search] Overhaul generic search provider Rewrite the generic search provider to use prototypes. Increase performance by utilizing the model service instead of the object service, and use a simplified method of request queueing. --- platform/search/bundle.json | 3 +- .../src/services/GenericSearchProvider.js | 442 ++++++++++-------- .../src/services/GenericSearchWorker.js | 10 +- 3 files changed, 241 insertions(+), 214 deletions(-) diff --git a/platform/search/bundle.json b/platform/search/bundle.json index 1117c3d161..9e39e28d03 100644 --- a/platform/search/bundle.json +++ b/platform/search/bundle.json @@ -48,8 +48,7 @@ "depends": [ "$q", "$log", - "throttle", - "objectService", + "modelService", "workerService", "topic", "GENERIC_SEARCH_ROOTS" diff --git a/platform/search/src/services/GenericSearchProvider.js b/platform/search/src/services/GenericSearchProvider.js index d38326bb9d..907ab33189 100644 --- a/platform/search/src/services/GenericSearchProvider.js +++ b/platform/search/src/services/GenericSearchProvider.js @@ -24,226 +24,258 @@ /** * Module defining GenericSearchProvider. Created by shale on 07/16/2015. */ -define( - [], - function () { - "use strict"; +define([ - var DEFAULT_MAX_RESULTS = 100, - DEFAULT_TIMEOUT = 1000, - MAX_CONCURRENT_REQUESTS = 100, - FLUSH_INTERVAL = 0, - stopTime; +], function ( - /** - * A search service which searches through domain objects in - * the filetree without using external search implementations. - * - * @constructor - * @param $q Angular's $q, for promise consolidation. - * @param $log Anglar's $log, for logging. - * @param {Function} throttle a function to throttle function invocations - * @param {ObjectService} objectService The service from which - * domain objects can be gotten. - * @param {WorkerService} workerService The service which allows - * more easy creation of web workers. - * @param {GENERIC_SEARCH_ROOTS} ROOTS An array of the root - * domain objects' IDs. - */ - function GenericSearchProvider($q, $log, throttle, objectService, workerService, topic, ROOTS) { - var indexed = {}, - pendingIndex = {}, - pendingQueries = {}, - toRequest = [], - worker = workerService.run('genericSearchWorker'), - mutationTopic = topic("mutation"), - indexingStarted = Date.now(), - pendingRequests = 0, - scheduleFlush; +) { + "use strict"; - this.worker = worker; - this.pendingQueries = pendingQueries; - this.$q = $q; - // pendingQueries is a dictionary with the key value pairs st - // the key is the timestamp and the value is the promise + var DEFAULT_MAX_RESULTS = 100, + MAX_CONCURRENT_REQUESTS = 100; - function scheduleIdsForIndexing(ids) { - ids.forEach(function (id) { - if (!indexed[id] && !pendingIndex[id]) { - indexed[id] = true; - pendingIndex[id] = true; - toRequest.push(id); - } - }); - scheduleFlush(); - } + /** + * A search service which searches through domain objects in + * the filetree without using external search implementations. + * + * @constructor + * @param $q Angular's $q, for promise consolidation. + * @param $log Anglar's $log, for logging. + * @param {ModelService} modelService the model service. + * @param {WorkerService} workerService the workerService. + * @param {TopicService} topic the topic service. + * @param {Array} ROOTS An array of object Ids to begin indexing. + */ + function GenericSearchProvider($q, $log, modelService, workerService, topic, ROOTS) { + var provider = this; + this.$q = $q; + this.$log = $log; + this.modelService = modelService; - // Tell the web worker to add a domain object's model to its list of items. - function indexItem(domainObject) { - var model = domainObject.getModel(); + this.indexedIds = {}; + this.idsToIndex = []; + this.pendingIndex = {}; + this.pendingRequests = 0; - worker.postMessage({ - request: 'index', - model: model, - id: domainObject.getId() - }); + this.pendingQueries = {}; - if (Array.isArray(model.composition)) { - scheduleIdsForIndexing(model.composition); - } - } + this.worker = this.startWorker(workerService); - // Handles responses from the web worker. Namely, the results of - // a search request. - function handleResponse(event) { - if (event.data.request !== 'search') { - return; // no idea how to handle anything else. - } + ROOTS.forEach(function indexRoot(rootId) { + provider.scheduleForIndexing(rootId); + }); + } - var workerResults = event.data.results, - ids = Object.keys(workerResults); - - objectService - .getObjects(ids) - .then(function (objects) { - var searchResults = Object - .keys(objects) - .map(function (id) { - return { - object: objects[id], - id: id, - score: workerResults[id].matchCount - }; - }); - - // Resove the promise corresponding to this - pendingQueries[event.data.timestamp].resolve({ - hits: searchResults, - total: searchResults.length, - timedOut: event.data.timedOut - }); - }); - } - - function requestAndIndex(id) { - pendingRequests += 1; - objectService.getObjects([id]).then(function (objects) { - delete pendingIndex[id]; - if (objects[id]) { - indexItem(objects[id]); - } - }, function () { - $log.warn("Failed to index domain object " + id); - }).then(function () { - pendingRequests -= 1; - scheduleFlush(); - }); - } - - scheduleFlush = throttle(function flush() { - var batchSize = - Math.max(MAX_CONCURRENT_REQUESTS - pendingRequests, 0); - - if (toRequest.length + pendingRequests < 1) { - $log.info([ - 'GenericSearch finished indexing after ', - ((Date.now() - indexingStarted) / 1000).toFixed(2), - ' seconds.' - ].join('')); - } else { - toRequest.splice(-batchSize, batchSize) - .forEach(requestAndIndex); - } - }, FLUSH_INTERVAL); - - worker.onmessage = handleResponse; - - // Index the tree's contents once at the beginning - scheduleIdsForIndexing(ROOTS); - - // Re-index items when they are mutated - mutationTopic.listen(function (domainObject) { - var id = domainObject.getId(); - indexed[id] = false; - scheduleIdsForIndexing([id]); - }); + /** + * Query the search provider for results. + * + * @param {String} input the string to search by. + * @param {Number} timestamp part of the SearchProvider interface, ignored. + * @param {Number} maxResults max number of results to return. + * @returns {Promise} a promise for a modelResults object. + */ + GenericSearchProvider.prototype.query = function ( + input, + timestamp, + maxResults + ) { + if (!maxResults) { + maxResults = DEFAULT_MAX_RESULTS; } - /** - * Searches through the filetree for domain objects which match - * the search term. This function is to be used as a fallback - * in the case where other search services are not avaliable. - * Returns a promise for a result object that has the format - * {hits: searchResult[], total: number, timedOut: boolean} - * where a searchResult has the format - * {id: string, object: domainObject, score: number} - * - * Notes: - * * The order of the results is not guarenteed. - * * A domain object qualifies as a match for a search input if - * the object's name property contains any of the search terms - * (which are generated by splitting the input at spaces). - * * Scores are higher for matches that have more of the terms - * as substrings. - * - * @param input The text input that is the query. - * @param timestamp The time at which this function was called. - * This timestamp is used as a unique identifier for this - * query and the corresponding results. - * @param maxResults (optional) The maximum number of results - * that this function should return. - * @param timeout (optional) The time after which the search should - * stop calculations and return partial results. - */ - GenericSearchProvider.prototype.query = function query(input, timestamp, maxResults, timeout) { - var terms = [], - searchResults = [], - pendingQueries = this.pendingQueries, - worker = this.worker, - defer = this.$q.defer(); + var queryId = this.dispatchSearch(input, maxResults), + pendingQuery = this.$q.defer(); - // Tell the worker to search for items it has that match this searchInput. - // Takes the searchInput, as well as a max number of results (will return - // less than that if there are fewer matches). - function workerSearch(searchInput, maxResults, timestamp, timeout) { - var message = { - request: 'search', - input: searchInput, - maxResults: maxResults, - timestamp: timestamp, - timeout: timeout - }; - worker.postMessage(message); - } + this.pendingQueries[queryId] = pendingQuery; - // If the input is nonempty, do a search - if (input !== '' && input !== undefined) { + return pendingQuery.promise; + }; - // Allow us to access this promise later to resolve it later - pendingQueries[timestamp] = defer; + /** + * Creates a search worker and attaches handlers. + * + * @private + * @param workerService + * @returns worker the created search worker. + */ + GenericSearchProvider.prototype.startWorker = function (workerService) { + var worker = workerService.run('genericSearchWorker'), + provider = this; - // Check to see if the user provided a maximum - // number of results to display - if (!maxResults) { - // Else, we provide a default value - maxResults = DEFAULT_MAX_RESULTS; - } - // Similarly, check if timeout was provided - if (!timeout) { - timeout = DEFAULT_TIMEOUT; - } - - // Send the query to the worker - workerSearch(input, maxResults, timestamp, timeout); - - return defer.promise; - } else { - // Otherwise return an empty result - return { hits: [], total: 0 }; - } + worker.onmessage = function (messageEvent) { + provider.onWorkerMessage(messageEvent); }; + return worker; + }; - return GenericSearchProvider; - } -); + /** + * Listen to the mutation topic and re-index objects when they are + * mutated. + * + * @private + * @param topic the topicService. + */ + GenericSearchProvider.prototype.indexOnMutation = function (topic) { + var mutationTopic = topic('mutation'), + provider = this; + + mutationTopic.listen(function (mutatedObject) { + var id = mutatedObject.getId(); + provider.indexed[id] = false; + provider.scheduleForIndexing(id); + }); + }; + + /** + * Schedule an id to be indexed at a later date. If there are less + * pending requests then allowed, will kick off an indexing request. + * + * @private + * @param {String} id to be indexed. + */ + GenericSearchProvider.prototype.scheduleForIndexing = function (id) { + if (!this.indexedIds[id] && !this.pendingIndex[id]) { + this.indexedIds[id] = true; + this.pendingIndex[id] = true; + this.idsToIndex.push(id); + } + this.keepIndexing(); + }; + + /** + * If there are less pending requests than concurrent requests, keep + * firing requests. + * + * @private + */ + GenericSearchProvider.prototype.keepIndexing = function () { + if (this.pendingRequests < MAX_CONCURRENT_REQUESTS) { + this.beginIndexRequest(); + } + }; + + /** + * Pass an id and model to the worker to be indexed. If the model has + * composition, schedule those ids for later indexing. + * + * @private + * @param id a model id + * @param model a model + */ + GenericSearchProvider.prototype.index = function (id, model) { + var provider = this; + + this.worker.postMessage({ + request: 'index', + model: model, + id: id + }); + + if (Array.isArray(model.composition)) { + model.composition.forEach(function (id) { + provider.scheduleForIndexing(id); + }); + } + }; + + /** + * Pulls an id from the indexing queue, loads it from the model service, + * and indexes it. Upon completion, tells the provider to keep + * indexing. + * + * @private + */ + GenericSearchProvider.prototype.beginIndexRequest = function () { + var idToIndex = this.idsToIndex.shift(), + provider = this; + + if (!idToIndex) { + return; + } + + this.pendingRequests += 1; + this.modelService + .getModels([idToIndex]) + .then(function (models) { + delete provider.pendingIndex[idToIndex]; + if (models[idToIndex]) { + provider.index(idToIndex, models[idToIndex]); + } + }, function () { + provider + .$log + .warn('Failed to index domain object ' + idToIndex); + }) + .then(function () { + provider.keepIndexing(); + }); + }; + + /** + * Handle messages from the worker. Only really knows how to handle search + * results, which are parsed, transformed into a modelResult object, which + * is used to resolve the corresponding promise. + * @private + */ + GenericSearchProvider.prototype.onWorkerMessage = function (event) { + if (event.data.request !== 'search') { + return; + } + + var pendingQuery = this.pendingQueries[event.data.queryId], + modelResults = { + timedOut: event.data.timedOut, + total: event.data.total + }; + + modelResults.hits = event.data.results.map(function (hit) { + return { + id: hit.item.id, + model: hit.item.model, + score: hit.matchCount + }; + }); + + + pendingQuery.resolve(modelResults); + delete this.pendingQueries[event.data.queryId]; + }; + + /** + * @private + * @returns {Number} a unique, unusued query Id. + */ + GenericSearchProvider.prototype.makeQueryId = function () { + var queryId = Math.ceil(Math.random() * 100000); + while (this.pendingQueries[queryId]) { + queryId = Math.ceil(Math.random() * 100000); + } + return queryId; + }; + + /** + * Dispatch a search query to the worker and return a queryId. + * + * @private + * @returns {Number} a unique query Id for the query. + */ + GenericSearchProvider.prototype.dispatchSearch = function ( + searchInput, + maxResults + ) { + var queryId = this.makeQueryId(); + + this.worker.postMessage({ + request: 'search', + input: searchInput, + maxResults: maxResults, + queryId: queryId + }); + + return queryId; + }; + + + return GenericSearchProvider; +}); diff --git a/platform/search/src/services/GenericSearchWorker.js b/platform/search/src/services/GenericSearchWorker.js index cd45929d4f..4ef44e29ab 100644 --- a/platform/search/src/services/GenericSearchWorker.js +++ b/platform/search/src/services/GenericSearchWorker.js @@ -79,8 +79,8 @@ request: 'search', results: {}, total: 0, - timestamp: data.timestamp, - timedOut: false + timedOut: false, + queryId: data.queryId }, matches = {}; @@ -144,11 +144,7 @@ message.total = results.length; message.results = results - .slice(0, data.maxResults) - .reduce(function arrayToObject(obj, match) { - obj[match.item.id] = match; - return obj; - }, {}); + .slice(0, data.maxResults); return message; } From a2fce8e56c824c458fb625155abc92c086438c08 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 16 Oct 2015 16:05:31 -0700 Subject: [PATCH 079/488] [Search] Rewrite elasticsearch provider with prototype Rewrite the elasticsearch provider to use prototypes and clean up the implementation. Now returns a modelResults object to keep it in line with the general search provider. --- .../elastic/src/ElasticSearchProvider.js | 287 +++++++----------- 1 file changed, 112 insertions(+), 175 deletions(-) diff --git a/platform/persistence/elastic/src/ElasticSearchProvider.js b/platform/persistence/elastic/src/ElasticSearchProvider.js index 604e3d0ed3..bc313f4deb 100644 --- a/platform/persistence/elastic/src/ElasticSearchProvider.js +++ b/platform/persistence/elastic/src/ElasticSearchProvider.js @@ -24,190 +24,127 @@ /** * Module defining ElasticSearchProvider. Created by shale on 07/16/2015. */ -define( - [], - function () { - "use strict"; +define([ - // JSLint doesn't like underscore-prefixed properties, - // so hide them here. - var ID = "_id", - SCORE = "_score", - DEFAULT_MAX_RESULTS = 100; - - /** - * A search service which searches through domain objects in - * the filetree using ElasticSearch. - * - * @constructor - * @param $http Angular's $http service, for working with urls. - * @param {ObjectService} objectService the service from which - * domain objects can be gotten. - * @param ROOT the constant `ELASTIC_ROOT` which allows us to - * interact with ElasticSearch. - */ - function ElasticSearchProvider($http, objectService, ROOT) { - this.$http = $http; - this.objectService = objectService; - this.root = ROOT; +], function ( + +) { + "use strict"; + + var DEFAULT_MAX_RESULTS = 100; + + /** + * A search service which searches through domain objects in + * the filetree using ElasticSearch. + * + * @constructor + * @param $http Angular's $http service, for working with urls. + * @param {ObjectService} objectService the service from which + * domain objects can be gotten. + * @param ROOT the constant `ELASTIC_ROOT` which allows us to + * interact with ElasticSearch. + */ + function ElasticSearchProvider($http, objectService, ROOT) { + this.$http = $http; + this.objectService = objectService; + this.root = ROOT; + } + + /** + * Search for domain objects using elasticsearch as a search provider. + * + * @param {String} searchTerm the term to search by. + * @param {Number} [maxResults] the max numer of results to return. + * @returns {Promise} promise for a modelResults object. + */ + ElasticSearchProvider.prototype.query = function (searchTerm, maxResults) { + var searchUrl = this.root + '/_search/', + params = {}, + provider = this; + + if (!maxResults) { + maxResults = DEFAULT_MAX_RESULTS; } - /** - * Searches through the filetree for domain objects using a search - * term. This is done through querying elasticsearch. Returns a - * promise for a result object that has the format - * {hits: searchResult[], total: number, timedOut: boolean} - * where a searchResult has the format - * {id: string, object: domainObject, score: number} - * - * Notes: - * * The order of the results is from highest to lowest score, - * as elsaticsearch determines them to be. - * * Uses the fuzziness operator to get more results. - * * More about this search's behavior at - * https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html - * - * @param searchTerm The text input that is the query. - * @param timestamp The time at which this function was called. - * This timestamp is used as a unique identifier for this - * query and the corresponding results. - * @param maxResults (optional) The maximum number of results - * that this function should return. - * @param timeout (optional) The time after which the search should - * stop calculations and return partial results. Elasticsearch - * does not guarentee that this timeout will be strictly followed. - */ - ElasticSearchProvider.prototype.query = function query(searchTerm, timestamp, maxResults, timeout) { - var $http = this.$http, - objectService = this.objectService, - root = this.root, - esQuery; - - function addFuzziness(searchTerm, editDistance) { - if (!editDistance) { - editDistance = ''; - } + searchTerm = this.cleanTerm(searchTerm); + searchTerm = this.fuzzyMatchUnquotedTerms(searchTerm); - return searchTerm.split(' ').map(function (s) { - // Don't add fuzziness for quoted strings - if (s.indexOf('"') !== -1) { - return s; - } else { - return s + '~' + editDistance; - } - }).join(' '); - } + params.q = searchTerm; + params.size = maxResults; - // Currently specific to elasticsearch - function processSearchTerm(searchTerm) { - var spaceIndex; - - // Cut out any extra spaces - while (searchTerm.substr(0, 1) === ' ') { - searchTerm = searchTerm.substring(1, searchTerm.length); - } - while (searchTerm.substr(searchTerm.length - 1, 1) === ' ') { - searchTerm = searchTerm.substring(0, searchTerm.length - 1); - } - spaceIndex = searchTerm.indexOf(' '); - while (spaceIndex !== -1) { - searchTerm = searchTerm.substring(0, spaceIndex) + - searchTerm.substring(spaceIndex + 1, searchTerm.length); - spaceIndex = searchTerm.indexOf(' '); - } - - // Add fuzziness for completeness - searchTerm = addFuzziness(searchTerm); - - return searchTerm; - } - - // Processes results from the format that elasticsearch returns to - // a list of searchResult objects, then returns a result object - // (See documentation for query for object descriptions) - function processResults(rawResults, timestamp) { - var results = rawResults.data.hits.hits, - resultsLength = results.length, - ids = [], - scores = {}, - searchResults = [], - i; - - // Get the result objects' IDs - for (i = 0; i < resultsLength; i += 1) { - ids.push(results[i][ID]); - } - - // Get the result objects' scores - for (i = 0; i < resultsLength; i += 1) { - scores[ids[i]] = results[i][SCORE]; - } - - // Get the domain objects from their IDs - return objectService.getObjects(ids).then(function (objects) { - var j, - id; - - for (j = 0; j < resultsLength; j += 1) { - id = ids[j]; - - // Include items we can get models for - if (objects[id].getModel) { - // Format the results as searchResult objects - searchResults.push({ - id: id, - object: objects[id], - score: scores[id] - }); - } - } - - return { - hits: searchResults, - total: rawResults.data.hits.total, - timedOut: rawResults.data.timed_out - }; - }); - } + return this + .$http({ + method: "GET", + url: searchUrl, + params: params + }) + .then(function success(succesResponse) { + return provider.parseResponse(succesResponse); + }, function error(errorResponse) { + // Gracefully fail. + return { + hits: [], + total: 0 + }; + }); + }; - // Check to see if the user provided a maximum - // number of results to display - if (!maxResults) { - // Else, we provide a default value. - maxResults = DEFAULT_MAX_RESULTS; - } + /** + * Clean excess whitespace from a search term and return the cleaned + * version. + * + * @private + * @param {string} the search term to clean. + * @returns {string} search terms cleaned of excess whitespace. + */ + ElasticSearchProvider.prototype.cleanTerm = function (term) { + return term.trim().replace(/ +/, ' '); + }; - // If the user input is empty, we want to have no search results. - if (searchTerm !== '' && searchTerm !== undefined) { - // Process the search term - searchTerm = processSearchTerm(searchTerm); + /** + * Add fuzzy matching markup to search terms that are not quoted. + * + * The following: + * hello welcome "to quoted village" have fun + * will become + * hello~ welcome~ "to quoted village" have~ fun~ + * + * @private + */ + ElasticSearchProvider.prototype.fuzzyMatchUnquotedTerms = function (query) { + var matchUnquotedSpaces = '\\s+(?=([^"]*"[^"]*")*[^"]*$)', + matcher = new RegExp(matchUnquotedSpaces, 'g'); - // Create the query to elasticsearch - esQuery = root + "/_search/?q=" + searchTerm + - "&size=" + maxResults; - if (timeout) { - esQuery += "&timeout=" + timeout; - } + return query + .replace(matcher, '~ ') + .replace('"~', '"'); + }; - // Get the data... - return this.$http({ - method: "GET", - url: esQuery - }).then(function (rawResults) { - // ...then process the data - return processResults(rawResults, timestamp); - }, function (err) { - // In case of error, return nothing. (To prevent - // infinite loading time.) - return {hits: [], total: 0}; - }); - } else { - return {hits: [], total: 0}; - } + /** + * Parse the response from ElasticSearch and convert it to a + * modelResults object. + * + * @private + * @param response a ES response object from $http + * @returns modelResults + */ + ElasticSearchProvider.prototype.parseResponse = function (response) { + var results = response.data.hits.hits, + searchResults = results.map(function (result) { + return { + id: result['_id'], + model: result['_source'], + score: result['_score'] + }; + }); + + return { + hits: searchResults, + total: response.data.hits.total, + timedOut: response.data.timed_out }; + }; - - return ElasticSearchProvider; - } -); \ No newline at end of file + return ElasticSearchProvider; +}); From 12efb47be74bc52ca67a7600e588c08ad8c67590 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 16 Oct 2015 16:09:51 -0700 Subject: [PATCH 080/488] [Search] Remove timeouts and timestamps Remove timeouts and timestamps which were not effectively doing anything. --- .../elastic/src/ElasticSearchProvider.js | 1 - .../src/services/GenericSearchProvider.js | 3 --- .../search/src/services/GenericSearchWorker.js | 6 ++---- .../search/src/services/SearchAggregator.js | 17 +++++------------ 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/platform/persistence/elastic/src/ElasticSearchProvider.js b/platform/persistence/elastic/src/ElasticSearchProvider.js index bc313f4deb..e42cd6553d 100644 --- a/platform/persistence/elastic/src/ElasticSearchProvider.js +++ b/platform/persistence/elastic/src/ElasticSearchProvider.js @@ -142,7 +142,6 @@ define([ return { hits: searchResults, total: response.data.hits.total, - timedOut: response.data.timed_out }; }; diff --git a/platform/search/src/services/GenericSearchProvider.js b/platform/search/src/services/GenericSearchProvider.js index 907ab33189..6e7a7168a0 100644 --- a/platform/search/src/services/GenericSearchProvider.js +++ b/platform/search/src/services/GenericSearchProvider.js @@ -70,13 +70,11 @@ define([ * Query the search provider for results. * * @param {String} input the string to search by. - * @param {Number} timestamp part of the SearchProvider interface, ignored. * @param {Number} maxResults max number of results to return. * @returns {Promise} a promise for a modelResults object. */ GenericSearchProvider.prototype.query = function ( input, - timestamp, maxResults ) { if (!maxResults) { @@ -225,7 +223,6 @@ define([ var pendingQuery = this.pendingQueries[event.data.queryId], modelResults = { - timedOut: event.data.timedOut, total: event.data.total }; diff --git a/platform/search/src/services/GenericSearchWorker.js b/platform/search/src/services/GenericSearchWorker.js index 4ef44e29ab..928f66cab8 100644 --- a/platform/search/src/services/GenericSearchWorker.js +++ b/platform/search/src/services/GenericSearchWorker.js @@ -61,13 +61,12 @@ /** * Gets search results from the indexedItems based on provided search - * input. Returns matching results from indexedItems, as well as the - * timestamp that was passed to it. + * input. Returns matching results from indexedItems * * @param data An object which contains: * * input: The original string which we are searching with * * maxResults: The maximum number of search results desired - * * timestamp: The time identifier from when the query was made + * * queryId: an id identifying this query, will be returned. */ function search(data) { // This results dictionary will have domain object ID keys which @@ -79,7 +78,6 @@ request: 'search', results: {}, total: 0, - timedOut: false, queryId: data.queryId }, matches = {}; diff --git a/platform/search/src/services/SearchAggregator.js b/platform/search/src/services/SearchAggregator.js index abb6478936..b22bfbae18 100644 --- a/platform/search/src/services/SearchAggregator.js +++ b/platform/search/src/services/SearchAggregator.js @@ -31,8 +31,7 @@ define([ ) { "use strict"; - var DEFAULT_TIMEOUT = 1000, - DEFAULT_MAX_RESULTS = 100; + var DEFAULT_MAX_RESULTS = 100; /** * Aggregates multiple search providers as a singular search provider. @@ -57,7 +56,7 @@ define([ /** * Sends a query to each of the providers. Returns a promise for * a result object that has the format - * {hits: searchResult[], total: number, timedOut: boolean} + * {hits: searchResult[], total: number} * where a searchResult has the format * {id: string, object: domainObject, score: number} * @@ -87,9 +86,7 @@ define([ resultPromises = this.providers.map(function (provider) { return provider.query( inputText, - timestamp, - maxResults, - DEFAULT_TIMEOUT + maxResults ); }); @@ -98,16 +95,13 @@ define([ .then(function (providerResults) { var modelResults = { hits: [], - totals: 0, - timedOut: false + totals: 0 }; providerResults.forEach(function (providerResult) { modelResults.hits = modelResults.hits.concat(providerResult.hits); modelResults.totals += providerResult.totals; - modelResults.timedOut = - modelResults.timedOut || providerResult.timedOut; }); aggregator.orderByScore(modelResults); @@ -195,8 +189,7 @@ define([ .then(function (objects) { var objectResults = { - totals: modelResults.totals, - timedOut: modelResults.timedOut + totals: modelResults.totals }; objectResults.hits = modelResults From 0f63e4dde9673c7edd85c0ca187a74a4add1fe53 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 16 Oct 2015 17:06:23 -0700 Subject: [PATCH 081/488] [Tests] Rewrite search aggregator specs --- .../search/src/services/SearchAggregator.js | 21 +- .../test/services/SearchAggregatorSpec.js | 293 +++++++++++++----- 2 files changed, 230 insertions(+), 84 deletions(-) diff --git a/platform/search/src/services/SearchAggregator.js b/platform/search/src/services/SearchAggregator.js index b22bfbae18..0cdcc5b922 100644 --- a/platform/search/src/services/SearchAggregator.js +++ b/platform/search/src/services/SearchAggregator.js @@ -76,7 +76,6 @@ define([ ) { var aggregator = this, - timestamp = Date.now(), resultPromises; if (!maxResults) { @@ -95,18 +94,18 @@ define([ .then(function (providerResults) { var modelResults = { hits: [], - totals: 0 + total: 0 }; providerResults.forEach(function (providerResult) { modelResults.hits = modelResults.hits.concat(providerResult.hits); - modelResults.totals += providerResult.totals; + modelResults.total += providerResult.total; }); - aggregator.orderByScore(modelResults); - aggregator.applyFilter(modelResults, filter); - aggregator.removeDuplicates(modelResults); + modelResults = aggregator.orderByScore(modelResults); + modelResults = aggregator.applyFilter(modelResults, filter); + modelResults = aggregator.removeDuplicates(modelResults); return aggregator.asObjectResults(modelResults); }); @@ -144,15 +143,15 @@ define([ return filter(hit.model); }); - finalLength = modelResults.hits; + finalLength = modelResults.hits.length; removedByFilter = initialLength - finalLength; - modelResults.totals -= removedByFilter; + modelResults.total -= removedByFilter; return modelResults; }; /** - * Remove duplicate hits in a modelResults object, and decrement `totals` + * Remove duplicate hits in a modelResults object, and decrement `total` * each time a duplicate is removed. */ SearchAggregator.prototype.removeDuplicates = function (modelResults) { @@ -162,7 +161,7 @@ define([ .hits .filter(function alreadyInResults(hit) { if (includedIds[hit.id]) { - modelResults.totals -= 1; + modelResults.total -= 1; return false; } includedIds[hit.id] = true; @@ -189,7 +188,7 @@ define([ .then(function (objects) { var objectResults = { - totals: modelResults.totals + total: modelResults.total }; objectResults.hits = modelResults diff --git a/platform/search/test/services/SearchAggregatorSpec.js b/platform/search/test/services/SearchAggregatorSpec.js index 3205f0f9ec..044a5f4609 100644 --- a/platform/search/test/services/SearchAggregatorSpec.js +++ b/platform/search/test/services/SearchAggregatorSpec.js @@ -19,83 +19,230 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global define,describe,it,expect,beforeEach,jasmine*/ +/*global define,describe,it,expect,beforeEach,jasmine,Promise,waitsFor,spyOn*/ /** * SearchSpec. Created by shale on 07/31/2015. */ -define( - ["../../src/services/SearchAggregator"], - function (SearchAggregator) { - "use strict"; +define([ + "../../src/services/SearchAggregator" +], function (SearchAggregator) { + "use strict"; - describe("The search aggregator ", function () { - var mockQ, - mockPromise, - mockProviders = [], - aggregator, - mockProviderResults = [], - mockAggregatorResults, - i; + describe("SearchAggregator", function () { + var $q, + objectService, + providers, + aggregator; - beforeEach(function () { - mockQ = jasmine.createSpyObj( - "$q", - [ "all" ] - ); - mockPromise = jasmine.createSpyObj( - "promise", - [ "then" ] - ); - for (i = 0; i < 3; i += 1) { - mockProviders.push( - jasmine.createSpyObj( - "mockProvider" + i, - [ "query" ] - ) - ); - mockProviders[i].query.andReturn(mockPromise); - } - mockQ.all.andReturn(mockPromise); - - aggregator = new SearchAggregator(mockQ, mockProviders); - aggregator.query(); - - for (i = 0; i < mockProviders.length; i += 1) { - mockProviderResults.push({ - hits: [ - { - id: i, - score: 42 - i - }, - { - id: i + 1, - score: 42 - (2 * i) - } - ] - }); - } - mockAggregatorResults = mockPromise.then.mostRecentCall.args[0](mockProviderResults); - }); - - it("sends queries to all providers", function () { - for (i = 0; i < mockProviders.length; i += 1) { - expect(mockProviders[i].query).toHaveBeenCalled(); - } - }); - - it("filters out duplicate objects", function () { - expect(mockAggregatorResults.hits.length).toEqual(mockProviders.length + 1); - expect(mockAggregatorResults.total).not.toBeLessThan(mockAggregatorResults.hits.length); - }); - - it("orders results by score", function () { - for (i = 1; i < mockAggregatorResults.hits.length; i += 1) { - expect(mockAggregatorResults.hits[i].score) - .not.toBeGreaterThan(mockAggregatorResults.hits[i - 1].score); - } - }); - + beforeEach(function () { + $q = jasmine.createSpyObj( + '$q', + ['all'] + ); + $q.all.andReturn(Promise.resolve([])); + objectService = jasmine.createSpyObj( + 'objectService', + ['getObjects'] + ); + providers = [], + aggregator = new SearchAggregator($q, objectService, providers); }); - } -); \ No newline at end of file + + + it("can order model results by score", function () { + var modelResults = { + hits: [ + {score: 1}, + {score: 23}, + {score: 11} + ] + }, + sorted = aggregator.orderByScore(modelResults); + + expect(sorted.hits).toEqual([ + {score: 23}, + {score: 11}, + {score: 1} + ]); + }); + + it('filters results without a function', function () { + var modelResults = { + hits: [ + {thing: 1}, + {thing: 2} + ], + total: 2 + }, + filtered = aggregator.applyFilter(modelResults); + + expect(filtered.hits).toEqual([ + {thing: 1}, + {thing: 2} + ]); + + expect(filtered.total).toBe(2); + }); + + it('filters results with a function', function () { + var modelResults = { + hits: [ + {model: {thing: 1}}, + {model: {thing: 2}}, + {model: {thing: 3}} + ], + total: 3 + }, + filterFunc = function (model) { + return model.thing < 2; + }, + filtered = aggregator.applyFilter(modelResults, filterFunc); + + expect(filtered.hits).toEqual([ + {model: {thing: 1}} + ]); + expect(filtered.total).toBe(1); + }); + + it('can remove duplicates', function () { + var modelResults = { + hits: [ + {id: 15}, + {id: 23}, + {id: 14}, + {id: 23} + ], + total: 4 + }, + deduped = aggregator.removeDuplicates(modelResults); + + expect(deduped.hits).toEqual([ + {id: 15}, + {id: 23}, + {id: 14} + ]); + expect(deduped.total).toBe(3); + }); + + it('can convert model results to object results', function () { + var modelResults = { + hits: [ + {id: 123, score: 5}, + {id: 234, score: 1} + ], + total: 2 + }, + objects = { + 123: '123-object-hey', + 234: '234-object-hello' + }, + promiseChainComplete = false; + + objectService.getObjects.andReturn(Promise.resolve(objects)); + + aggregator + .asObjectResults(modelResults) + .then(function (objectResults) { + expect(objectResults).toEqual({ + hits: [ + {id: 123, score: 5, object: '123-object-hey'}, + {id: 234, score: 1, object: '234-object-hello'} + ], + total: 2 + }); + }) + .then(function () { + promiseChainComplete = true; + }); + + waitsFor(function () { + return promiseChainComplete; + }); + }); + + it('can send queries to providers', function () { + var provider = jasmine.createSpyObj( + 'provider', + ['query'] + ); + provider.query.andReturn('i prooomise!'); + providers.push(provider); + + aggregator.query('find me', 123, 'filter'); + expect(provider.query).toHaveBeenCalledWith('find me', 123); + expect($q.all).toHaveBeenCalledWith(['i prooomise!']); + }); + + it('supplies max results when none is provided', function () { + var provider = jasmine.createSpyObj( + 'provider', + ['query'] + ); + providers.push(provider); + aggregator.query('find me'); + expect(provider.query).toHaveBeenCalledWith('find me', 100); + }); + + it('can combine responses from multiple providers', function () { + var providerResponses = [ + { + hits: [ + 'oneHit', + 'twoHit' + ], + total: 2 + }, + { + hits: [ + 'redHit', + 'blueHit', + 'by', + 'Pete' + ], + total: 4 + } + ], + promiseChainResolved = false; + + $q.all.andReturn(Promise.resolve(providerResponses)); + spyOn(aggregator, 'orderByScore').andReturn('orderedByScore!'); + spyOn(aggregator, 'applyFilter').andReturn('filterApplied!'); + spyOn(aggregator, 'removeDuplicates') + .andReturn('duplicatesRemoved!'); + spyOn(aggregator, 'asObjectResults').andReturn('objectResults'); + + aggregator + .query('something', 10, 'filter') + .then(function (objectResults) { + expect(aggregator.orderByScore).toHaveBeenCalledWith({ + hits: [ + 'oneHit', + 'twoHit', + 'redHit', + 'blueHit', + 'by', + 'Pete' + ], + total: 6 + }); + expect(aggregator.applyFilter) + .toHaveBeenCalledWith('orderedByScore!', 'filter'); + expect(aggregator.removeDuplicates) + .toHaveBeenCalledWith('filterApplied!'); + expect(aggregator.asObjectResults) + .toHaveBeenCalledWith('duplicatesRemoved!'); + + expect(objectResults).toBe('objectResults'); + }) + .then(function () { + promiseChainResolved = true; + }); + + waitsFor(function () { + return promiseChainResolved; + }); + }); + + }); +}); From fa3821b50f9095ebd5ed1b4f297c59c227e1015d Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Sun, 18 Oct 2015 20:58:17 -0700 Subject: [PATCH 082/488] Update of CopyService with new copy algorithm (incomplete) --- .../entanglement/src/services/CopyService.js | 88 ++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/platform/entanglement/src/services/CopyService.js b/platform/entanglement/src/services/CopyService.js index 48ba3e7ce5..f9bf53a0b9 100644 --- a/platform/entanglement/src/services/CopyService.js +++ b/platform/entanglement/src/services/CopyService.js @@ -23,7 +23,8 @@ /*global define */ define( - function () { + ["../../../commonUI/browse/lib/uuid"], + function (uuid) { "use strict"; /** @@ -53,8 +54,61 @@ define( object.getCapability('type') ); }; + + /** + * Will build a graph of an object and all of its composed objects in memory + * @private + * @param domainObject + */ + function buildCopyGraph(domainObject, parent) { + var clonedModels = []; + + function clone(object) { + return JSON.parse(JSON.stringify(object)); + } + + function copy(object, parent) { + var modelClone = clone(object.getModel()); + modelClone.composition = []; + if (domainObject.hasCapability('composition')) { + return domainObject.useCapability('composition').then(function(composees){ + return composees.reduce(function(promise, composee){ + return promise.then(function(){ + return copy(composee, object).then(function(composeeClone){ + /* + TODO: Use the composition capability for this. Just not sure how to contextualize the as-yet non-existent modelClone object. + */ + return modelClone.composition.push(composeeClone.id); + }); + }); + }, $q.when(undefined)).then(function (){ + modelClone.id = uuid(); + clonedModels.push(modelClone); + return modelClone; + }); + }); + } else { + return Q.when(modelClone); + } + }; + return copy(domainObject, parent).then(function(){ + return clonedModels; + }); + } - CopyService.prototype.perform = function (domainObject, parent) { + function newPerform (domainObject, parent) { + return buildCopyGraph.then(function(clonedModels){ + return clonedModels.reduce(function(promise, clonedModel){ + /* + TODO: Persist the clone. We need to bypass the creation service on this because it wants to create the composition along the way, which we want to avoid. The composition has already been done directly in the model. + */ + }, this.q.when(undefined)); + }) + } + + CopyService.prototype.perform = oldPerform; + + function oldPerform (domainObject, parent) { var model = JSON.parse(JSON.stringify(domainObject.getModel())), $q = this.$q, self = this; @@ -74,6 +128,36 @@ define( model.composition = []; } + /* + * 1) Traverse to leaf of object tree + * 2) Copy object and persist + * 3) Go up to parent + * 4) Update parent in memory with new composition + * 4) If parent has more children + * 5) Visit next child + * 6) Go to 2) + * 7) else + * 8) Persist parent + */ + + /* + * copy(object, parent) { + * 1) objectClone = clone(object); // Clone object + * 2) objectClone.composition = []; // Reset the clone's composition + * 3) composees = object.composition; + * 3) composees.reduce(function (promise, composee) { // For each child in original composition + * 4) return promise.then(function () { + * 5) return copy(composee, object).then(function(clonedComposee){ + * 6) objectClone.composition.push(clonedComposee); + * 7) return objectClone; + * 8) ); // Copy the child + * 9) }; + * 10) }) + * 11) objectClone.id = newId(); + * 12) return persist(objectClone); + * } + */ + return this.creationService .createObject(model, parent) .then(function (newObject) { From 8e2a2eeba5db5c9c14769c75bfe67fcfb63491a9 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 19 Oct 2015 12:08:49 -0700 Subject: [PATCH 083/488] [Entanglement] Add license headers ...per code review feedback from nasa/openmctweb#175 --- .../src/capabilities/LocationCapability.js | 22 +++++++++++++++++++ .../capabilities/LocationCapabilitySpec.js | 22 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/platform/entanglement/src/capabilities/LocationCapability.js b/platform/entanglement/src/capabilities/LocationCapability.js index 38974ecb0f..27e1f74c74 100644 --- a/platform/entanglement/src/capabilities/LocationCapability.js +++ b/platform/entanglement/src/capabilities/LocationCapability.js @@ -1,3 +1,25 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ + /*global define */ define( diff --git a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js index 938f0d843c..442bfe20aa 100644 --- a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js +++ b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js @@ -1,3 +1,25 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ + /*global define,describe,it,expect,beforeEach,jasmine */ define( From 2a1388772af83c1851c495519f53fe9cf0cc2b6f Mon Sep 17 00:00:00 2001 From: Henry Date: Mon, 19 Oct 2015 17:32:43 -0700 Subject: [PATCH 084/488] Incremental commit of duplication --- bundles.json | 2 +- platform/entanglement/bundle.json | 3 +- .../entanglement/src/services/CopyService.js | 38 +++++++++++++------ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/bundles.json b/bundles.json index c85b681bf5..3940336656 100644 --- a/bundles.json +++ b/bundles.json @@ -22,7 +22,7 @@ "platform/features/events", "platform/forms", "platform/identity", - "platform/persistence/local", + "platform/persistence/elastic", "platform/persistence/queue", "platform/policy", "platform/entanglement", diff --git a/platform/entanglement/bundle.json b/platform/entanglement/bundle.json index 61c3d90539..9594c29b4f 100644 --- a/platform/entanglement/bundle.json +++ b/platform/entanglement/bundle.json @@ -75,7 +75,8 @@ "name": "Copy Service", "description": "Provides a service for copying objects", "implementation": "services/CopyService.js", - "depends": ["$q", "creationService", "policyService"] + "depends": ["$q", "creationService", "policyService", + "persistenceService"] }, { "key": "locationService", diff --git a/platform/entanglement/src/services/CopyService.js b/platform/entanglement/src/services/CopyService.js index f9bf53a0b9..dd558dbec0 100644 --- a/platform/entanglement/src/services/CopyService.js +++ b/platform/entanglement/src/services/CopyService.js @@ -35,10 +35,11 @@ define( * @memberof platform/entanglement * @implements {platform/entanglement.AbstractComposeService} */ - function CopyService($q, creationService, policyService) { + function CopyService($q, creationService, policyService, persistenceService) { this.$q = $q; this.creationService = creationService; this.policyService = policyService; + this.persistenceService = persistenceService; } CopyService.prototype.validate = function (object, parentCandidate) { @@ -61,15 +62,18 @@ define( * @param domainObject */ function buildCopyGraph(domainObject, parent) { - var clonedModels = []; + var clones = [], + $q = this.$q; function clone(object) { return JSON.parse(JSON.stringify(object)); } function copy(object, parent) { + var self = this; var modelClone = clone(object.getModel()); modelClone.composition = []; + if (domainObject.hasCapability('composition')) { return domainObject.useCapability('composition').then(function(composees){ return composees.reduce(function(promise, composee){ @@ -83,27 +87,37 @@ define( }); }, $q.when(undefined)).then(function (){ modelClone.id = uuid(); - clonedModels.push(modelClone); + clones.push({persistence: parent.getCapability('persistence'), model: modelClone}); return modelClone; }); }); } else { - return Q.when(modelClone); + return $q.when(modelClone); } }; return copy(domainObject, parent).then(function(){ - return clonedModels; + return clones; }); } function newPerform (domainObject, parent) { - return buildCopyGraph.then(function(clonedModels){ - return clonedModels.reduce(function(promise, clonedModel){ - /* - TODO: Persist the clone. We need to bypass the creation service on this because it wants to create the composition along the way, which we want to avoid. The composition has already been done directly in the model. - */ - }, this.q.when(undefined)); - }) + var $q = this.$q; + if (this.validate(domainObject, parent)) { + return buildCopyGraph(domainObject, parent).then(function(clones){ + return clones.reduce(function(promise, clone){ + /* + TODO: Persist the clone. We need to bypass the creation service on this because it wants to create the composition along the way, which we want to avoid. The composition has already been done directly in the model. + */ + promise.then(function(){ + return this.persistenceService.createObject(clone.persistence.getSpace(), clone.model.id, clone.model); + }); + }, $q.when(undefined)); + }) + } else { + throw new Error( + "Tried to copy objects without validating first." + ); + } } CopyService.prototype.perform = oldPerform; From 89e763b5158856144a3accaecc3d1ed2c03c2f40 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Tue, 20 Oct 2015 09:25:31 -0700 Subject: [PATCH 085/488] Incremental commit of Duplication --- bundles.json | 2 +- .../entanglement/src/services/CopyService.js | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/bundles.json b/bundles.json index 3940336656..c85b681bf5 100644 --- a/bundles.json +++ b/bundles.json @@ -22,7 +22,7 @@ "platform/features/events", "platform/forms", "platform/identity", - "platform/persistence/elastic", + "platform/persistence/local", "platform/persistence/queue", "platform/policy", "platform/entanglement", diff --git a/platform/entanglement/src/services/CopyService.js b/platform/entanglement/src/services/CopyService.js index dd558dbec0..6798443b48 100644 --- a/platform/entanglement/src/services/CopyService.js +++ b/platform/entanglement/src/services/CopyService.js @@ -61,27 +61,28 @@ define( * @private * @param domainObject */ - function buildCopyGraph(domainObject, parent) { + CopyService.prototype.buildCopyGraph = function(domainObject, parent) { var clones = [], - $q = this.$q; + $q = this.$q, + self = this; function clone(object) { return JSON.parse(JSON.stringify(object)); } function copy(object, parent) { - var self = this; var modelClone = clone(object.getModel()); modelClone.composition = []; - if (domainObject.hasCapability('composition')) { - return domainObject.useCapability('composition').then(function(composees){ + if (object.hasCapability('composition')) { + return object.useCapability('composition').then(function(composees){ return composees.reduce(function(promise, composee){ return promise.then(function(){ return copy(composee, object).then(function(composeeClone){ /* TODO: Use the composition capability for this. Just not sure how to contextualize the as-yet non-existent modelClone object. */ + composeeClone.location = modelClone.id; return modelClone.composition.push(composeeClone.id); }); }); @@ -101,15 +102,16 @@ define( } function newPerform (domainObject, parent) { - var $q = this.$q; + var $q = this.$q, + self = this; if (this.validate(domainObject, parent)) { - return buildCopyGraph(domainObject, parent).then(function(clones){ + return this.buildCopyGraph(domainObject, parent).then(function(clones){ return clones.reduce(function(promise, clone){ /* TODO: Persist the clone. We need to bypass the creation service on this because it wants to create the composition along the way, which we want to avoid. The composition has already been done directly in the model. */ - promise.then(function(){ - return this.persistenceService.createObject(clone.persistence.getSpace(), clone.model.id, clone.model); + return promise.then(function(){ + return self.persistenceService.createObject(clone.persistence.getSpace(), clone.model.id, clone.model); }); }, $q.when(undefined)); }) @@ -120,7 +122,7 @@ define( } } - CopyService.prototype.perform = oldPerform; + CopyService.prototype.perform = newPerform; function oldPerform (domainObject, parent) { var model = JSON.parse(JSON.stringify(domainObject.getModel())), From 6d08c81b3b83b6e3563b4e4579fa5fa422a798ab Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 20 Oct 2015 12:18:30 -0700 Subject: [PATCH 086/488] First iteration of duplication complete --- bundles.json | 2 +- .../entanglement/src/services/CopyService.js | 45 ++++++++++++------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/bundles.json b/bundles.json index c85b681bf5..3940336656 100644 --- a/bundles.json +++ b/bundles.json @@ -22,7 +22,7 @@ "platform/features/events", "platform/forms", "platform/identity", - "platform/persistence/local", + "platform/persistence/elastic", "platform/persistence/queue", "platform/policy", "platform/entanglement", diff --git a/platform/entanglement/src/services/CopyService.js b/platform/entanglement/src/services/CopyService.js index 6798443b48..843ec6cd2a 100644 --- a/platform/entanglement/src/services/CopyService.js +++ b/platform/entanglement/src/services/CopyService.js @@ -62,6 +62,13 @@ define( * @param domainObject */ CopyService.prototype.buildCopyGraph = function(domainObject, parent) { + /* TODO: Use contextualized objects here. + Parent should be fully contextualized, and either the + original parent or a contextualized clone. The subsequent + composition changes can then be performed regardless of + whether it is the top level composition of the original + parent being updated, or of one of the cloned children. */ + var clones = [], $q = this.$q, self = this; @@ -70,15 +77,16 @@ define( return JSON.parse(JSON.stringify(object)); } - function copy(object, parent) { - var modelClone = clone(object.getModel()); + function copy(originalObject, originalParent) { + var modelClone = clone(originalObject.getModel()); modelClone.composition = []; + modelClone.id = uuid(); - if (object.hasCapability('composition')) { - return object.useCapability('composition').then(function(composees){ + if (originalObject.hasCapability('composition')) { + return originalObject.useCapability('composition').then(function(composees){ return composees.reduce(function(promise, composee){ return promise.then(function(){ - return copy(composee, object).then(function(composeeClone){ + return copy(composee, originalObject).then(function(composeeClone){ /* TODO: Use the composition capability for this. Just not sure how to contextualize the as-yet non-existent modelClone object. */ @@ -87,12 +95,14 @@ define( }); }); }, $q.when(undefined)).then(function (){ - modelClone.id = uuid(); - clones.push({persistence: parent.getCapability('persistence'), model: modelClone}); + /* Todo: Move this outside of promise and avoid + duplication below */ + clones.push({persistence: originalParent.getCapability('persistence'), model: modelClone}); return modelClone; }); }); } else { + clones.push({persistence: originalParent.getCapability('persistence'), model: modelClone}); return $q.when(modelClone); } }; @@ -106,15 +116,20 @@ define( self = this; if (this.validate(domainObject, parent)) { return this.buildCopyGraph(domainObject, parent).then(function(clones){ - return clones.reduce(function(promise, clone){ - /* - TODO: Persist the clone. We need to bypass the creation service on this because it wants to create the composition along the way, which we want to avoid. The composition has already been done directly in the model. - */ - return promise.then(function(){ + return $q.all(clones.map(function(clone){ return self.persistenceService.createObject(clone.persistence.getSpace(), clone.model.id, clone.model); - }); - }, $q.when(undefined)); - }) + })).then(function(){ return clones}); + }).then(function(clones) { + var parentClone = clones[clones.length-1]; + parentClone.model.location = parent.getId() + return $q.when( + parent.hasCapability('composition') && + parent.getCapability('composition').add(parentClone.model.id) + .then( + function(){ + parent.getCapability("persistence").persist() + })); + }); } else { throw new Error( "Tried to copy objects without validating first." From be79c104fba007679aa550d1df54b7b905d099e5 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Tue, 20 Oct 2015 12:36:11 -0700 Subject: [PATCH 087/488] [Frontend] Added new glyph prod-uisymbols Added e612 Save icon; --- .../icomoon.io-WTD-symbols-project.json | 206 ++++++++++-------- .../general/res/fonts/symbols/wtdsymbols.eot | Bin 11672 -> 11836 bytes .../general/res/fonts/symbols/wtdsymbols.svg | 1 + .../general/res/fonts/symbols/wtdsymbols.ttf | Bin 11496 -> 11660 bytes .../general/res/fonts/symbols/wtdsymbols.woff | Bin 11572 -> 11736 bytes 5 files changed, 121 insertions(+), 86 deletions(-) diff --git a/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json b/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json index 9ff51f9f04..72c9a01fa2 100644 --- a/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json +++ b/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json @@ -1,19 +1,27 @@ { "metadata": { - "name": "WTD Symbols v25", - "lastOpened": 1444865544920, - "created": 1444865541293 + "name": "WTD Symbols", + "lastOpened": 1445369623428, + "created": 1445367449289 }, "iconSets": [ { "selection": [ + { + "order": 109, + "id": 89, + "prevSize": 32, + "code": 58898, + "name": "icon-save", + "tempChar": "" + }, { "order": 108, "id": 88, "prevSize": 32, "code": 58897, "name": "icon-dataset", - "tempChar": "" + "tempChar": "" }, { "order": 90, @@ -21,7 +29,7 @@ "prevSize": 32, "code": 58896, "name": "icon-bell", - "tempChar": "" + "tempChar": "" }, { "order": 91, @@ -29,7 +37,7 @@ "prevSize": 32, "code": 58889, "name": "icon-hourglass", - "tempChar": "" + "tempChar": "" }, { "order": 92, @@ -42,7 +50,7 @@ 58890 ], "name": "icon-info-v15", - "tempChar": "" + "tempChar": "" }, { "order": 93, @@ -50,7 +58,7 @@ "prevSize": 32, "code": 58887, "name": "icon-x-in-circle", - "tempChar": "" + "tempChar": "" }, { "order": 94, @@ -58,7 +66,7 @@ "prevSize": 32, "code": 58881, "name": "icon-datatable", - "tempChar": "" + "tempChar": "" }, { "order": 95, @@ -66,7 +74,7 @@ "prevSize": 32, "code": 58882, "name": "icon-tabular-scrolling", - "tempChar": "" + "tempChar": "" }, { "order": 96, @@ -74,7 +82,7 @@ "prevSize": 32, "code": 58884, "name": "icon-tabular", - "tempChar": "" + "tempChar": "" }, { "order": 97, @@ -82,7 +90,7 @@ "prevSize": 32, "code": 58885, "name": "icon-calendar", - "tempChar": "" + "tempChar": "" }, { "order": 98, @@ -90,7 +98,7 @@ "prevSize": 32, "code": 58886, "name": "icon-paint-bucket", - "tempChar": "" + "tempChar": "" }, { "order": 99, @@ -98,7 +106,7 @@ "prevSize": 32, "code": 123, "name": "icon-pointer-left", - "tempChar": "" + "tempChar": "" }, { "order": 100, @@ -106,7 +114,7 @@ "prevSize": 32, "code": 125, "name": "icon-pointer-right", - "tempChar": "" + "tempChar": "" }, { "order": 101, @@ -114,7 +122,7 @@ "prevSize": 32, "code": 80, "name": "icon-person", - "tempChar": "" + "tempChar": "" }, { "order": 102, @@ -122,7 +130,7 @@ "prevSize": 32, "code": 232, "name": "icon-chain-links", - "tempChar": "" + "tempChar": "" }, { "order": 103, @@ -130,7 +138,7 @@ "prevSize": 32, "code": 115, "name": "icon-database-in-brackets", - "tempChar": "" + "tempChar": "" }, { "order": 104, @@ -138,7 +146,7 @@ "prevSize": 32, "code": 114, "name": "icon-refresh", - "tempChar": "" + "tempChar": "" }, { "order": 105, @@ -146,7 +154,7 @@ "prevSize": 32, "code": 108, "name": "icon-lock", - "tempChar": "" + "tempChar": "" }, { "order": 106, @@ -154,7 +162,7 @@ "prevSize": 32, "code": 51, "name": "icon-box-with-dashed-lines", - "tempChar": "" + "tempChar": "" }, { "order": 10, @@ -162,7 +170,7 @@ "prevSize": 32, "code": 58880, "name": "icon-box-with-arrow-cursor", - "tempChar": "" + "tempChar": "" }, { "order": 11, @@ -170,7 +178,7 @@ "prevSize": 32, "code": 65, "name": "icon-activity-mode", - "tempChar": "" + "tempChar": "" }, { "order": 12, @@ -178,7 +186,7 @@ "prevSize": 32, "code": 97, "name": "icon-activity", - "tempChar": "" + "tempChar": "" }, { "order": 87, @@ -186,7 +194,7 @@ "prevSize": 32, "code": 33, "name": "icon-alert-rect", - "tempChar": "" + "tempChar": "" }, { "order": 14, @@ -194,7 +202,7 @@ "prevSize": 32, "code": 58883, "name": "icon-alert-triangle", - "tempChar": "" + "tempChar": "" }, { "order": 15, @@ -202,7 +210,7 @@ "prevSize": 32, "code": 238, "name": "icon-arrow-double-down", - "tempChar": "" + "tempChar": "" }, { "order": 16, @@ -210,7 +218,7 @@ "prevSize": 32, "code": 235, "name": "icon-arrow-double-up", - "tempChar": "" + "tempChar": "" }, { "order": 2, @@ -218,7 +226,7 @@ "prevSize": 32, "code": 118, "name": "icon-arrow-down", - "tempChar": "" + "tempChar": "" }, { "order": 19, @@ -226,7 +234,7 @@ "prevSize": 32, "code": 60, "name": "icon-arrow-left", - "tempChar": "" + "tempChar": "" }, { "order": 20, @@ -234,7 +242,7 @@ "prevSize": 32, "code": 62, "name": "icon-arrow-right", - "tempChar": "" + "tempChar": "" }, { "order": 21, @@ -242,7 +250,7 @@ "prevSize": 32, "code": 236, "name": "icon-arrow-tall-down", - "tempChar": "" + "tempChar": "" }, { "order": 22, @@ -250,7 +258,7 @@ "prevSize": 32, "code": 237, "name": "icon-arrow-tall-up", - "tempChar": "" + "tempChar": "" }, { "order": 23, @@ -258,7 +266,7 @@ "prevSize": 32, "code": 94, "name": "icon-arrow-up", - "tempChar": "" + "tempChar": "" }, { "order": 24, @@ -266,7 +274,7 @@ "prevSize": 32, "code": 73, "name": "icon-arrows-out", - "tempChar": "" + "tempChar": "" }, { "order": 25, @@ -274,7 +282,7 @@ "prevSize": 32, "code": 58893, "name": "icon-arrows-right-left", - "tempChar": "" + "tempChar": "" }, { "order": 33, @@ -282,7 +290,7 @@ "prevSize": 32, "code": 53, "name": "icon-arrows-up-down", - "tempChar": "" + "tempChar": "" }, { "order": 26, @@ -290,7 +298,7 @@ "prevSize": 32, "code": 42, "name": "icon-asterisk", - "tempChar": "" + "tempChar": "" }, { "order": 27, @@ -298,7 +306,7 @@ "prevSize": 32, "code": 72, "name": "icon-autoflow-tabular", - "tempChar": "" + "tempChar": "" }, { "order": 28, @@ -306,7 +314,7 @@ "prevSize": 32, "code": 224, "name": "icon-box", - "tempChar": "" + "tempChar": "" }, { "order": 29, @@ -314,7 +322,7 @@ "prevSize": 32, "code": 50, "name": "icon-check", - "tempChar": "" + "tempChar": "" }, { "order": 30, @@ -322,7 +330,7 @@ "prevSize": 32, "code": 67, "name": "icon-clock", - "tempChar": "" + "tempChar": "" }, { "order": 31, @@ -330,7 +338,7 @@ "prevSize": 32, "code": 46, "name": "icon-connectivity", - "tempChar": "" + "tempChar": "" }, { "order": 32, @@ -338,7 +346,7 @@ "prevSize": 32, "code": 100, "name": "icon-database-query", - "tempChar": "" + "tempChar": "" }, { "order": 17, @@ -346,7 +354,7 @@ "prevSize": 32, "code": 68, "name": "icon-database", - "tempChar": "" + "tempChar": "" }, { "order": 35, @@ -354,7 +362,7 @@ "prevSize": 32, "code": 81, "name": "icon-dictionary", - "tempChar": "" + "tempChar": "" }, { "order": 36, @@ -362,7 +370,7 @@ "prevSize": 32, "code": 242, "name": "icon-duplicate", - "tempChar": "" + "tempChar": "" }, { "order": 37, @@ -370,7 +378,7 @@ "prevSize": 32, "code": 102, "name": "icon-folder-new", - "tempChar": "" + "tempChar": "" }, { "order": 38, @@ -378,7 +386,7 @@ "prevSize": 32, "code": 70, "name": "icon-folder", - "tempChar": "" + "tempChar": "" }, { "order": 39, @@ -386,7 +394,7 @@ "prevSize": 32, "code": 95, "name": "icon-fullscreen-collapse", - "tempChar": "" + "tempChar": "" }, { "order": 40, @@ -394,7 +402,7 @@ "prevSize": 32, "code": 122, "name": "icon-fullscreen-expand", - "tempChar": "" + "tempChar": "" }, { "order": 41, @@ -402,7 +410,7 @@ "prevSize": 32, "code": 71, "name": "icon-gear", - "tempChar": "" + "tempChar": "" }, { "order": 49, @@ -410,7 +418,7 @@ "prevSize": 32, "code": 227, "name": "icon-image", - "tempChar": "" + "tempChar": "" }, { "order": 42, @@ -418,7 +426,7 @@ "prevSize": 32, "code": 225, "name": "icon-layers", - "tempChar": "" + "tempChar": "" }, { "order": 43, @@ -426,7 +434,7 @@ "prevSize": 32, "code": 76, "name": "icon-layout", - "tempChar": "" + "tempChar": "" }, { "order": 44, @@ -434,7 +442,7 @@ "prevSize": 32, "code": 226, "name": "icon-line-horz", - "tempChar": "" + "tempChar": "" }, { "order": 75, @@ -442,7 +450,7 @@ "prevSize": 32, "code": 244, "name": "icon-link", - "tempChar": "" + "tempChar": "" }, { "order": 46, @@ -450,7 +458,7 @@ "prevSize": 32, "code": 88, "name": "icon-magnify-in", - "tempChar": "" + "tempChar": "" }, { "order": 47, @@ -458,7 +466,7 @@ "prevSize": 32, "code": 89, "name": "icon-magnify-out", - "tempChar": "" + "tempChar": "" }, { "order": 48, @@ -466,7 +474,7 @@ "prevSize": 32, "code": 77, "name": "icon-magnify", - "tempChar": "" + "tempChar": "" }, { "order": 34, @@ -474,7 +482,7 @@ "prevSize": 32, "code": 109, "name": "icon-menu", - "tempChar": "" + "tempChar": "" }, { "order": 50, @@ -482,7 +490,7 @@ "prevSize": 32, "code": 243, "name": "icon-move", - "tempChar": "" + "tempChar": "" }, { "order": 51, @@ -490,7 +498,7 @@ "prevSize": 32, "code": 121, "name": "icon-new-window", - "tempChar": "" + "tempChar": "" }, { "order": 52, @@ -498,7 +506,7 @@ "prevSize": 32, "code": 111, "name": "icon-object", - "tempChar": "" + "tempChar": "" }, { "order": 73, @@ -506,7 +514,7 @@ "prevSize": 32, "code": 63, "name": "icon-object-unknown", - "tempChar": "" + "tempChar": "" }, { "order": 53, @@ -514,7 +522,7 @@ "prevSize": 32, "code": 86, "name": "icon-packet", - "tempChar": "" + "tempChar": "" }, { "order": 54, @@ -522,7 +530,7 @@ "prevSize": 32, "code": 234, "name": "icon-page", - "tempChar": "" + "tempChar": "" }, { "order": 55, @@ -530,7 +538,7 @@ "prevSize": 32, "code": 241, "name": "icon-pause", - "tempChar": "" + "tempChar": "" }, { "order": 56, @@ -538,7 +546,7 @@ "prevSize": 32, "code": 112, "name": "icon-pencil", - "tempChar": "" + "tempChar": "" }, { "order": 65, @@ -546,7 +554,7 @@ "prevSize": 32, "code": 79, "name": "icon-people", - "tempChar": "" + "tempChar": "" }, { "order": 57, @@ -554,7 +562,7 @@ "prevSize": 32, "code": 239, "name": "icon-play", - "tempChar": "" + "tempChar": "" }, { "order": 58, @@ -562,7 +570,7 @@ "prevSize": 32, "code": 233, "name": "icon-plot-resource", - "tempChar": "" + "tempChar": "" }, { "order": 59, @@ -570,7 +578,7 @@ "prevSize": 32, "code": 43, "name": "icon-plus", - "tempChar": "" + "tempChar": "" }, { "order": 60, @@ -578,7 +586,7 @@ "prevSize": 32, "code": 45, "name": "icon-minus", - "tempChar": "" + "tempChar": "" }, { "order": 61, @@ -586,7 +594,7 @@ "prevSize": 32, "code": 54, "name": "icon-sine", - "tempChar": "" + "tempChar": "" }, { "order": 62, @@ -594,7 +602,7 @@ "prevSize": 32, "code": 228, "name": "icon-T", - "tempChar": "" + "tempChar": "" }, { "order": 63, @@ -602,7 +610,7 @@ "prevSize": 32, "code": 116, "name": "icon-telemetry-panel", - "tempChar": "" + "tempChar": "" }, { "order": 64, @@ -610,7 +618,7 @@ "prevSize": 32, "code": 84, "name": "icon-telemetry", - "tempChar": "" + "tempChar": "" }, { "order": 18, @@ -618,7 +626,7 @@ "prevSize": 32, "code": 246, "name": "icon-thumbs-strip", - "tempChar": "" + "tempChar": "" }, { "order": 67, @@ -626,7 +634,7 @@ "prevSize": 32, "code": 83, "name": "icon-timeline", - "tempChar": "" + "tempChar": "" }, { "order": 68, @@ -634,7 +642,7 @@ "prevSize": 32, "code": 245, "name": "icon-timer", - "tempChar": "" + "tempChar": "" }, { "order": 69, @@ -642,7 +650,7 @@ "prevSize": 32, "code": 90, "name": "icon-trash", - "tempChar": "" + "tempChar": "" }, { "order": 70, @@ -650,7 +658,7 @@ "prevSize": 32, "code": 229, "name": "icon-two-parts-both", - "tempChar": "" + "tempChar": "" }, { "order": 71, @@ -658,7 +666,7 @@ "prevSize": 32, "code": 231, "name": "icon-two-parts-one-only", - "tempChar": "" + "tempChar": "" }, { "order": 72, @@ -666,7 +674,7 @@ "prevSize": 32, "code": 120, "name": "icon-x-heavy", - "tempChar": "" + "tempChar": "" }, { "order": 66, @@ -674,7 +682,7 @@ "prevSize": 32, "code": 58946, "name": "icon-x", - "tempChar": "" + "tempChar": "" } ], "id": 2, @@ -689,6 +697,32 @@ "height": 1024, "prevSize": 32, "icons": [ + { + "id": 89, + "paths": [ + "M192.2 576c-0.2 0-0.2 0 0 0l-0.2 448h640v-447.8c0 0 0 0-0.2-0.2h-639.6z", + "M978.8 210.8l-165.4-165.4c-25-25-74.2-45.4-109.4-45.4h-576c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128v-448c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v448c70.4 0 128-57.6 128-128v-576c0-35.2-20.4-84.4-45.2-109.2zM704 256c0 35.2-28.8 64-64 64h-448c-35.2 0-64-28.8-64-64v-192h320v192h128v-192h128v192z" + ], + "attrs": [ + { + "fill": "rgb(0, 0, 0)" + }, + { + "fill": "rgb(0, 0, 0)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-save-v2" + ], + "colorPermutations": { + "125525525516161751": [ + 0, + 0 + ] + } + }, { "id": 88, "paths": [ diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot index 987277675a6c19a701daa242bc478b161fae67b5..491c4b12ca8dcdd9d8ba76bfa45591db87e65314 100755 GIT binary patch delta 443 zcmbOcy(fmvMvsA^M|UEd8B4i%>%oZ*6YB-8{bFEXlmTLw^u*!<1_lNJ1_q`CAkC4U zQ<=8eYGEFbpTWSOb|ND+F@=5GrCtUGO#`63Sq4ym{Q!$3kPnn+P|3(GsR-g=5d`vk zfaWFSRCuAZ60z@YU8D8DH;v7&$>jiH=@L3;s^uaK9Rn<^oH~|9EEg2yaPZmIZ$GOG9%CjObiSMTgA)c`E9;3aI=8? z#Bj;Soef4$&d@YxjM%(DGn#Socde}=!YM!*kiH0pQU+d-Dh4K?mxVW<*FMPz03Rf5 A-2eap delta 278 zcmdlJGb5U9hAsoc3!RBSq-JiZM>U$mB5j0TcUXLFO5%lU+3z%S;Fo^^NDZ`O3h} z0@A{8$z{e47(JOq%bYQMvyE0Xzrf+02!u7 A0ssI2 diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg index 021abf31c6..628b025ded 100755 --- a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg +++ b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg @@ -89,5 +89,6 @@ + \ No newline at end of file diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf index d08b3099cc34de7d659c5f8cfb69586cb70da40b..20417d579a7a9729a8f70b746c31f594a9113f88 100755 GIT binary patch delta 426 zcmXw!yGtWc6vn@E6BAKnok_;WDCqc@_&`zPM3EIn5G=;ZN)gn-SBgXfDk>IE<5n&z z(`c6!VBjfzVCO=f$!hk@%*WASo`t;P&3!$y1SJF zPz#79?v?AA{k7s)VS{xVFdx#Z?n?Cb={L|lz&w>^BPzpsSGQukNMtG zW^>uSJh;dJJ^z{CWZk_S*02lo=2^FHxY^Z)mgm<6HY6`x=Qg*t5n_bC%-P*vQvF|a zQlYU$z9DRXKtzkKIPW~3@vFTH7UH37m8h}6M9Knj$@g+@O0C1n$+^Esh4ftB-``IE88R~ zd!;rxRtZ+4LsB;b?pVUsC)B<`Nq&BzKHp9pKmhkg^}CBde!inR97Mv?^i<@#`*&R< hG+TV@nkRp%=TAJg!X((6#SUtMDF}`nE4K7L(ibKAX5#<= delta 261 zcmeB)ei1oAv7YbRF9rri86b8^Pb@BAU|oB(j4hIm1)UdjSGSN3=6$K2b4CO%nA0S^LFEKY&LjKCzI0go7kONi~y-h5d5BqIPX214Kf diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff index 816eda299a06edbe316c6ff0e3657a744174bc1c..879197340048aa1cb7be6f8142ee67d248d8d271 100755 GIT binary patch delta 452 zcmXw$&o2W(6vyA&t`e27TU#s%>&I3p3AH6s$+~b@A#oB-(D>CuONFF_#9rKp$X+;# zL`1kK;y7;34bs2BiKB~XdDAe-XI|#}{Z8`U%Pt_Ii~s_yt{ixZbsq$0P4FEB z+jgs7zm{X`DFES$tg=6AOPn^u)=}LStwg3>=p-h-ldY?UrlSmrnpp{d~ z#JYHLW0SNT0M2nH%S0)$I#0RI0m|HP&bChy$=GHpNa8yQO+}Uw8!1Q&yH@bd<}1K1;J673rL zyRk)Ub}Le~FC_JaLt3v-k$S_1)TjGPs@>%2L#QbsBcw3F&f6~Uxjd}qnPFLmt_rrQ z8&4fJzE+1 n1dm9A0%XTV-T&1Q1N}Iai3FnPI30K8UV7x>e@$h^JKyjZKcCcOb8P7 zjpw)d%D~M6G>?JdlFN)8FnaO=O>@TZ%{Mfo87G@-ZxjFrNjO6(120%9C_sca^Xr^s F1OQV?Np1iD From 14094a48fc542eca54d02f6db84cd4233305ffa5 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 16 Oct 2015 17:33:23 -0700 Subject: [PATCH 088/488] [Search] Remove old specs in prep for rewrite Remove old specs in prep for rewrite. --- .../elastic/test/ElasticSearchProviderSpec.js | 93 +----- .../services/GenericSearchProviderSpec.js | 283 ++---------------- .../test/services/GenericSearchWorkerSpec.js | 32 +- 3 files changed, 42 insertions(+), 366 deletions(-) diff --git a/platform/persistence/elastic/test/ElasticSearchProviderSpec.js b/platform/persistence/elastic/test/ElasticSearchProviderSpec.js index decb1a5c98..af515e9431 100644 --- a/platform/persistence/elastic/test/ElasticSearchProviderSpec.js +++ b/platform/persistence/elastic/test/ElasticSearchProviderSpec.js @@ -24,92 +24,9 @@ /** * SearchSpec. Created by shale on 07/31/2015. */ -define( - ["../src/ElasticSearchProvider"], - function (ElasticSearchProvider) { - "use strict"; +define([ + "../src/ElasticSearchProvider" +], function (ElasticSearchProvider) { + "use strict"; - // JSLint doesn't like underscore-prefixed properties, - // so hide them here. - var ID = "_id", - SCORE = "_score"; - - describe("The ElasticSearch search provider ", function () { - var mockHttp, - mockHttpPromise, - mockObjectPromise, - mockObjectService, - mockDomainObject, - provider, - mockProviderResults; - - beforeEach(function () { - mockHttp = jasmine.createSpy("$http"); - mockHttpPromise = jasmine.createSpyObj( - "promise", - [ "then" ] - ); - mockHttp.andReturn(mockHttpPromise); - // allow chaining of promise.then().catch(); - mockHttpPromise.then.andReturn(mockHttpPromise); - - mockObjectService = jasmine.createSpyObj( - "objectService", - [ "getObjects" ] - ); - mockObjectPromise = jasmine.createSpyObj( - "promise", - [ "then" ] - ); - mockObjectService.getObjects.andReturn(mockObjectPromise); - - mockDomainObject = jasmine.createSpyObj( - "domainObject", - [ "getId", "getModel" ] - ); - - provider = new ElasticSearchProvider(mockHttp, mockObjectService, ""); - provider.query(' test "query" ', 0, undefined, 1000); - }); - - it("sends a query to ElasticSearch", function () { - expect(mockHttp).toHaveBeenCalled(); - }); - - it("gets data from ElasticSearch", function () { - var data = { - hits: { - hits: [ - {}, - {} - ], - total: 0 - }, - timed_out: false - }; - data.hits.hits[0][ID] = 1; - data.hits.hits[0][SCORE] = 1; - data.hits.hits[1][ID] = 2; - data.hits.hits[1][SCORE] = 2; - - mockProviderResults = mockHttpPromise.then.mostRecentCall.args[0]({data: data}); - - expect( - mockObjectPromise.then.mostRecentCall.args[0]({ - 1: mockDomainObject, - 2: mockDomainObject - }).hits.length - ).toEqual(2); - }); - - it("returns nothing for an empty string query", function () { - expect(provider.query("").hits).toEqual([]); - }); - - it("returns something when there is an ElasticSearch error", function () { - mockProviderResults = mockHttpPromise.then.mostRecentCall.args[1](); - expect(mockProviderResults).toBeDefined(); - }); - }); - } -); \ No newline at end of file +}); diff --git a/platform/search/test/services/GenericSearchProviderSpec.js b/platform/search/test/services/GenericSearchProviderSpec.js index e3ee0a97ba..5c48eb2969 100644 --- a/platform/search/test/services/GenericSearchProviderSpec.js +++ b/platform/search/test/services/GenericSearchProviderSpec.js @@ -24,270 +24,29 @@ /** * SearchSpec. Created by shale on 07/31/2015. */ -define( - ["../../src/services/GenericSearchProvider"], - function (GenericSearchProvider) { - "use strict"; +define([ + "../../src/services/GenericSearchProvider" +], function (GenericSearchProvider) { + "use strict"; - describe("The generic search provider ", function () { - var mockQ, - mockLog, - mockThrottle, - mockDeferred, - mockObjectService, - mockObjectPromise, - mockChainedPromise, - mockDomainObjects, - mockCapability, - mockCapabilityPromise, - mockWorkerService, - mockWorker, - mockTopic, - mockMutationTopic, - mockRoots = ['root1', 'root2'], - mockThrottledFn, - throttledCallCount, - provider, - mockProviderResults; + describe('GenericSearchProvider', function () { + var $q, + $log, + modelService, + workerService, + topic, + ROOTS; - function resolveObjectPromises() { - var i; - for (i = 0; i < mockObjectPromise.then.calls.length; i += 1) { - mockChainedPromise.then.calls[i].args[0]( - mockObjectPromise.then.calls[i] - .args[0](mockDomainObjects) - ); - } - } + beforeEach(function () { + $q = jasmine.createSpyObj( + '$q', + ['defer'] + ); + // TODO: continue - function resolveThrottledFn() { - if (mockThrottledFn.calls.length > throttledCallCount) { - mockThrottle.mostRecentCall.args[0](); - throttledCallCount = mockThrottledFn.calls.length; - } - } - - function resolveAsyncTasks() { - resolveThrottledFn(); - resolveObjectPromises(); - } - - beforeEach(function () { - mockQ = jasmine.createSpyObj( - "$q", - [ "defer" ] - ); - mockLog = jasmine.createSpyObj( - "$log", - [ "error", "warn", "info", "debug" ] - ); - mockDeferred = jasmine.createSpyObj( - "deferred", - [ "resolve", "reject"] - ); - mockDeferred.promise = "mock promise"; - mockQ.defer.andReturn(mockDeferred); - - mockThrottle = jasmine.createSpy("throttle"); - mockThrottledFn = jasmine.createSpy("throttledFn"); - throttledCallCount = 0; - - mockObjectService = jasmine.createSpyObj( - "objectService", - [ "getObjects" ] - ); - mockObjectPromise = jasmine.createSpyObj( - "promise", - [ "then", "catch" ] - ); - mockChainedPromise = jasmine.createSpyObj( - "chainedPromise", - [ "then" ] - ); - mockObjectService.getObjects.andReturn(mockObjectPromise); - - mockTopic = jasmine.createSpy('topic'); - - mockWorkerService = jasmine.createSpyObj( - "workerService", - [ "run" ] - ); - mockWorker = jasmine.createSpyObj( - "worker", - [ "postMessage" ] - ); - mockWorkerService.run.andReturn(mockWorker); - - mockCapabilityPromise = jasmine.createSpyObj( - "promise", - [ "then", "catch" ] - ); - - mockDomainObjects = {}; - ['a', 'root1', 'root2'].forEach(function (id) { - mockDomainObjects[id] = ( - jasmine.createSpyObj( - "domainObject", - [ - "getId", - "getModel", - "hasCapability", - "getCapability", - "useCapability" - ] - ) - ); - mockDomainObjects[id].getId.andReturn(id); - mockDomainObjects[id].getCapability.andReturn(mockCapability); - mockDomainObjects[id].useCapability.andReturn(mockCapabilityPromise); - mockDomainObjects[id].getModel.andReturn({}); - }); - - mockCapability = jasmine.createSpyObj( - "capability", - [ "invoke", "listen" ] - ); - mockCapability.invoke.andReturn(mockCapabilityPromise); - mockDomainObjects.a.getCapability.andReturn(mockCapability); - mockMutationTopic = jasmine.createSpyObj( - 'mutationTopic', - [ 'listen' ] - ); - mockTopic.andCallFake(function (key) { - return key === 'mutation' && mockMutationTopic; - }); - mockThrottle.andReturn(mockThrottledFn); - mockObjectPromise.then.andReturn(mockChainedPromise); - - provider = new GenericSearchProvider( - mockQ, - mockLog, - mockThrottle, - mockObjectService, - mockWorkerService, - mockTopic, - mockRoots - ); - }); - - it("indexes tree on initialization", function () { - var i; - - resolveThrottledFn(); - - expect(mockObjectService.getObjects).toHaveBeenCalled(); - expect(mockObjectPromise.then).toHaveBeenCalled(); - - // Call through the root-getting part - resolveObjectPromises(); - - mockRoots.forEach(function (id) { - expect(mockWorker.postMessage).toHaveBeenCalledWith({ - request: 'index', - model: mockDomainObjects[id].getModel(), - id: id - }); - }); - }); - - it("indexes members of composition", function () { - mockDomainObjects.root1.getModel.andReturn({ - composition: ['a'] - }); - - resolveAsyncTasks(); - resolveAsyncTasks(); - - expect(mockWorker.postMessage).toHaveBeenCalledWith({ - request: 'index', - model: mockDomainObjects.a.getModel(), - id: 'a' - }); - }); - - it("listens for changes to mutation", function () { - expect(mockMutationTopic.listen) - .toHaveBeenCalledWith(jasmine.any(Function)); - mockMutationTopic.listen.mostRecentCall - .args[0](mockDomainObjects.a); - - resolveAsyncTasks(); - - expect(mockWorker.postMessage).toHaveBeenCalledWith({ - request: 'index', - model: mockDomainObjects.a.getModel(), - id: mockDomainObjects.a.getId() - }); - }); - - it("sends search queries to the worker", function () { - var timestamp = Date.now(); - provider.query(' test "query" ', timestamp, 1, 2); - expect(mockWorker.postMessage).toHaveBeenCalledWith({ - request: "search", - input: ' test "query" ', - timestamp: timestamp, - maxNumber: 1, - timeout: 2 - }); - }); - - it("gives an empty result for an empty query", function () { - var timestamp = Date.now(), - queryOutput; - - queryOutput = provider.query('', timestamp, 1, 2); - expect(queryOutput.hits).toEqual([]); - expect(queryOutput.total).toEqual(0); - - queryOutput = provider.query(); - expect(queryOutput.hits).toEqual([]); - expect(queryOutput.total).toEqual(0); - }); - - it("handles responses from the worker", function () { - var timestamp = Date.now(), - event = { - data: { - request: "search", - results: { - 1: 1, - 2: 2 - }, - total: 2, - timedOut: false, - timestamp: timestamp - } - }; - - provider.query(' test "query" ', timestamp); - mockWorker.onmessage(event); - mockObjectPromise.then.mostRecentCall.args[0](mockDomainObjects); - expect(mockDeferred.resolve).toHaveBeenCalled(); - }); - - it("warns when objects are unavailable", function () { - resolveAsyncTasks(); - expect(mockLog.warn).not.toHaveBeenCalled(); - mockChainedPromise.then.mostRecentCall.args[0]( - mockObjectPromise.then.mostRecentCall.args[1]() - ); - expect(mockLog.warn).toHaveBeenCalled(); - }); - - it("throttles the loading of objects to index", function () { - expect(mockObjectService.getObjects).not.toHaveBeenCalled(); - resolveThrottledFn(); - expect(mockObjectService.getObjects).toHaveBeenCalled(); - }); - - it("logs when all objects have been processed", function () { - expect(mockLog.info).not.toHaveBeenCalled(); - resolveAsyncTasks(); - resolveThrottledFn(); - expect(mockLog.info).toHaveBeenCalled(); - }); }); - } -); + + }); + +}); diff --git a/platform/search/test/services/GenericSearchWorkerSpec.js b/platform/search/test/services/GenericSearchWorkerSpec.js index b95ec5a1bb..07e8cf4eb6 100644 --- a/platform/search/test/services/GenericSearchWorkerSpec.js +++ b/platform/search/test/services/GenericSearchWorkerSpec.js @@ -33,7 +33,7 @@ define( // If this test fails, make sure this path is correct var worker = new Worker(require.toUrl('platform/search/src/services/GenericSearchWorker.js')), numObjects = 5; - + beforeEach(function () { var i; for (i = 0; i < numObjects; i += 1) { @@ -50,77 +50,77 @@ define( ); } }); - + it("searches can reach all objects", function () { var flag = false, workerOutput, resultsLength = 0; - + // Search something that should return all objects runs(function () { worker.postMessage( { request: "search", input: "object", - maxNumber: 100, + maxResults: 100, timestamp: Date.now(), timeout: 1000 } ); }); - + worker.onmessage = function (event) { var id; - + workerOutput = event.data; for (id in workerOutput.results) { resultsLength += 1; } flag = true; }; - + waitsFor(function () { return flag; }, "The worker should be searching", 1000); - + runs(function () { expect(workerOutput).toBeDefined(); expect(resultsLength).toEqual(numObjects); }); }); - + it("searches return only matches", function () { var flag = false, workerOutput, resultsLength = 0; - + // Search something that should return 1 object runs(function () { worker.postMessage( { request: "search", input: "2", - maxNumber: 100, + maxResults: 100, timestamp: Date.now(), timeout: 1000 } ); }); - + worker.onmessage = function (event) { var id; - + workerOutput = event.data; for (id in workerOutput.results) { resultsLength += 1; } flag = true; }; - + waitsFor(function () { return flag; }, "The worker should be searching", 1000); - + runs(function () { expect(workerOutput).toBeDefined(); expect(resultsLength).toEqual(1); @@ -129,4 +129,4 @@ define( }); }); } -); \ No newline at end of file +); From 98b5ff3c77bb6fe88b6bcbd13d0db840e7bc33df Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 16 Oct 2015 18:14:33 -0700 Subject: [PATCH 089/488] [Search] Decrement number of pending requests --- platform/search/src/services/GenericSearchProvider.js | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/search/src/services/GenericSearchProvider.js b/platform/search/src/services/GenericSearchProvider.js index 6e7a7168a0..c34a0a2646 100644 --- a/platform/search/src/services/GenericSearchProvider.js +++ b/platform/search/src/services/GenericSearchProvider.js @@ -206,6 +206,7 @@ define([ .warn('Failed to index domain object ' + idToIndex); }) .then(function () { + provider.pendingRequests -=1; provider.keepIndexing(); }); }; From 1ddce48f7ec6b58627c7fea3e66cf8b2d5d94e6b Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 20 Oct 2015 13:12:04 -0700 Subject: [PATCH 090/488] [Search] Specs for GenericSearchProvider Write specs for GenericSearchProvider and resolve some implementation bugs they uncovered. --- .../src/services/GenericSearchProvider.js | 20 +- .../services/GenericSearchProviderSpec.js | 271 +++++++++++++++++- 2 files changed, 277 insertions(+), 14 deletions(-) diff --git a/platform/search/src/services/GenericSearchProvider.js b/platform/search/src/services/GenericSearchProvider.js index c34a0a2646..e1c90f3f51 100644 --- a/platform/search/src/services/GenericSearchProvider.js +++ b/platform/search/src/services/GenericSearchProvider.js @@ -31,9 +31,6 @@ define([ ) { "use strict"; - var DEFAULT_MAX_RESULTS = 100, - MAX_CONCURRENT_REQUESTS = 100; - /** * A search service which searches through domain objects in * the filetree without using external search implementations. @@ -60,12 +57,18 @@ define([ this.pendingQueries = {}; this.worker = this.startWorker(workerService); + this.indexOnMutation(topic); ROOTS.forEach(function indexRoot(rootId) { provider.scheduleForIndexing(rootId); }); } + /** + * Maximum number of concurrent index requests to allow. + */ + GenericSearchProvider.prototype.MAX_CONCURRENT_REQUESTS = 100; + /** * Query the search provider for results. * @@ -77,9 +80,6 @@ define([ input, maxResults ) { - if (!maxResults) { - maxResults = DEFAULT_MAX_RESULTS; - } var queryId = this.dispatchSearch(input, maxResults), pendingQuery = this.$q.defer(); @@ -100,9 +100,9 @@ define([ var worker = workerService.run('genericSearchWorker'), provider = this; - worker.onmessage = function (messageEvent) { + worker.addEventListener('message', function (messageEvent) { provider.onWorkerMessage(messageEvent); - }; + }); return worker; }; @@ -148,7 +148,7 @@ define([ * @private */ GenericSearchProvider.prototype.keepIndexing = function () { - if (this.pendingRequests < MAX_CONCURRENT_REQUESTS) { + if (this.pendingRequests < this.MAX_CONCURRENT_REQUESTS) { this.beginIndexRequest(); } }; @@ -206,7 +206,7 @@ define([ .warn('Failed to index domain object ' + idToIndex); }) .then(function () { - provider.pendingRequests -=1; + provider.pendingRequests -= 1; provider.keepIndexing(); }); }; diff --git a/platform/search/test/services/GenericSearchProviderSpec.js b/platform/search/test/services/GenericSearchProviderSpec.js index 5c48eb2969..fb3d595345 100644 --- a/platform/search/test/services/GenericSearchProviderSpec.js +++ b/platform/search/test/services/GenericSearchProviderSpec.js @@ -26,27 +26,290 @@ */ define([ "../../src/services/GenericSearchProvider" -], function (GenericSearchProvider) { +], function ( + GenericSearchProvider +) { "use strict"; describe('GenericSearchProvider', function () { var $q, $log, modelService, + models, workerService, + worker, topic, - ROOTS; + mutationTopic, + ROOTS, + provider; beforeEach(function () { $q = jasmine.createSpyObj( '$q', ['defer'] ); - // TODO: continue + $log = jasmine.createSpyObj( + '$log', + ['warn'] + ); + models = {}; + modelService = jasmine.createSpyObj( + 'modelService', + ['getModels'] + ); + modelService.getModels.andReturn(Promise.resolve(models)); + workerService = jasmine.createSpyObj( + 'workerService', + ['run'] + ); + worker = jasmine.createSpyObj( + 'worker', + [ + 'postMessage', + 'addEventListener' + ] + ); + workerService.run.andReturn(worker); + topic = jasmine.createSpy('topic'); + mutationTopic = jasmine.createSpyObj( + 'mutationTopic', + ['listen'] + ); + topic.andReturn(mutationTopic); + ROOTS = [ + 'mine' + ]; + spyOn(GenericSearchProvider.prototype, 'scheduleForIndexing'); + + provider = new GenericSearchProvider( + $q, + $log, + modelService, + workerService, + topic, + ROOTS + ); + }); + + it('listens for general mutation', function () { + expect(topic).toHaveBeenCalledWith('mutation'); + expect(mutationTopic.listen) + .toHaveBeenCalledWith(jasmine.any(Function)); + }); + + it('starts indexing roots', function () { + expect(provider.scheduleForIndexing).toHaveBeenCalledWith('mine'); + }); + + it('runs a worker', function () { + expect(workerService.run) + .toHaveBeenCalledWith('genericSearchWorker'); + }); + + it('listens for messages from worker', function () { + expect(worker.addEventListener) + .toHaveBeenCalledWith('message', jasmine.any(Function)); + spyOn(provider, 'onWorkerMessage'); + worker.addEventListener.mostRecentCall.args[1]('mymessage'); + expect(provider.onWorkerMessage).toHaveBeenCalledWith('mymessage'); + }); + + it('has a maximum number of concurrent requests', function () { + expect(provider.MAX_CONCURRENT_REQUESTS).toBe(100); + }); + + describe('scheduleForIndexing', function () { + beforeEach(function () { + provider.scheduleForIndexing.andCallThrough(); + spyOn(provider, 'keepIndexing'); + }); + + it('tracks ids to index', function () { + expect(provider.indexedIds['a']).not.toBeDefined(); + expect(provider.pendingIndex['a']).not.toBeDefined(); + expect(provider.idsToIndex).not.toContain('a'); + provider.scheduleForIndexing('a'); + expect(provider.indexedIds['a']).toBeDefined(); + expect(provider.pendingIndex['a']).toBeDefined(); + expect(provider.idsToIndex).toContain('a'); + }); + + it('calls keep indexing', function () { + provider.scheduleForIndexing('a'); + expect(provider.keepIndexing).toHaveBeenCalled(); + }); + }); + + describe('keepIndexing', function () { + it('kicks off an index request when not at maximum', function () { + spyOn(provider, 'beginIndexRequest'); + provider.pendingRequests = 0; + provider.MAX_CONCURRENT_REQUESTS = 10; + provider.keepIndexing(); + expect(provider.beginIndexRequest).toHaveBeenCalled(); + }); + + it('does not index when at capacity', function () { + spyOn(provider, 'beginIndexRequest'); + provider.pendingRequests = 10; + provider.MAX_CONCURRENT_REQUESTS = 10; + provider.keepIndexing(); + expect(provider.beginIndexRequest).not.toHaveBeenCalled(); + }); + }); + + describe('index', function () { + it('sends index message to worker', function () { + var id = 'anId', + model = {}; + + provider.index(id, model); + expect(worker.postMessage).toHaveBeenCalledWith({ + request: 'index', + id: id, + model: model + }); + }); + + it('schedules composed ids for indexing', function () { + var id = 'anId', + model = {composition: ['abc', 'def']}; + + provider.index(id, model); + expect(provider.scheduleForIndexing) + .toHaveBeenCalledWith('abc'); + expect(provider.scheduleForIndexing) + .toHaveBeenCalledWith('def'); + }); + }); + + describe('beginIndexRequest', function () { + + beforeEach(function () { + provider.pendingRequests = 0; + provider.pendingIds = {'abc': true}; + provider.idsToIndex = ['abc']; + models.abc = {}; + spyOn(provider, 'index'); + }); + + it('removes items from queue', function () { + provider.beginIndexRequest(); + expect(provider.idsToIndex.length).toBe(0); + }); + + it('tracks number of pending requests', function () { + provider.beginIndexRequest(); + expect(provider.pendingRequests).toBe(1); + waitsFor(function () { + return provider.pendingRequests === 0; + }); + runs(function () { + expect(provider.pendingRequests).toBe(0); + }); + }); + + it('indexes objects', function () { + provider.beginIndexRequest(); + waitsFor(function () { + return provider.pendingRequests === 0; + }) + runs(function () { + expect(provider.index) + .toHaveBeenCalledWith('abc', models.abc); + }); + }); + + it('does not error if no objects queued', function () { + provider.idsToIndex = []; + expect(function () { + provider.beginIndexRequest() + }).not.toThrow(); + }); + }); + + + it('can dispatch searches to worker', function () { + spyOn(provider, 'makeQueryId').andReturn(428); + expect(provider.dispatchSearch('searchTerm', 100)) + .toBe(428); + + expect(worker.postMessage).toHaveBeenCalledWith({ + request: 'search', + input: 'searchTerm', + maxResults: 100, + queryId: 428 + }); + }); + + it('can generate queryIds', function () { + expect(provider.makeQueryId()).toEqual(jasmine.any(Number)); + }); + + it('can query for terms', function () { + var deferred = {promise: {}}; + spyOn(provider, 'dispatchSearch').andReturn(303); + $q.defer.andReturn(deferred); + + expect(provider.query('someTerm', 100)).toBe(deferred.promise); + expect(provider.pendingQueries[303]).toBe(deferred); + }); + + describe('onWorkerMessage', function () { + var pendingQuery; + beforeEach(function () { + pendingQuery = jasmine.createSpyObj( + 'pendingQuery', + ['resolve'] + ); + provider.pendingQueries[143] = pendingQuery; + }); + + it('resolves pending searches', function () { + provider.onWorkerMessage({ + data: { + request: 'search', + total: 2, + results: [ + { + item: { + id: 'abc', + model: {id: 'abc'} + }, + matchCount: 4 + }, + { + item: { + id: 'def', + model: {id: 'def'} + }, + matchCount: 2 + } + ], + queryId: 143 + } + }); + + expect(pendingQuery.resolve) + .toHaveBeenCalledWith({ + total: 2, + hits: [{ + id: 'abc', + model: {id: 'abc'}, + score: 4 + }, { + id: 'def', + model: {id: 'def'}, + score: 2 + }] + }); + + expect(provider.pendingQueries[143]).not.toBeDefined(); + + }); }); }); - }); From ec7e6cc5b445439630ce56121383f47eb8849cde Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 20 Oct 2015 13:55:46 -0700 Subject: [PATCH 091/488] [Search] Update spec for Generic Search Worker --- .../test/services/GenericSearchWorkerSpec.js | 281 ++++++++++++------ 1 file changed, 186 insertions(+), 95 deletions(-) diff --git a/platform/search/test/services/GenericSearchWorkerSpec.js b/platform/search/test/services/GenericSearchWorkerSpec.js index 07e8cf4eb6..20afb4c781 100644 --- a/platform/search/test/services/GenericSearchWorkerSpec.js +++ b/platform/search/test/services/GenericSearchWorkerSpec.js @@ -4,12 +4,12 @@ * Administration. All rights reserved. * * Open MCT Web is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. + * '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 + * 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. @@ -19,114 +19,205 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global define,describe,it,expect,runs,waitsFor,beforeEach,jasmine,Worker,require*/ +/*global define,describe,it,expect,runs,waitsFor,beforeEach,jasmine,Worker, + require,afterEach*/ /** * SearchSpec. Created by shale on 07/31/2015. */ -define( - [], - function () { - "use strict"; +define([ - describe("The generic search worker ", function () { - // If this test fails, make sure this path is correct - var worker = new Worker(require.toUrl('platform/search/src/services/GenericSearchWorker.js')), - numObjects = 5; +], function ( - beforeEach(function () { - var i; - for (i = 0; i < numObjects; i += 1) { - worker.postMessage( - { - request: "index", - id: i, - model: { - name: "object " + i, - id: i, - type: "something" - } - } - ); - } - }); +) { + 'use strict'; - it("searches can reach all objects", function () { - var flag = false, - workerOutput, - resultsLength = 0; + describe('GenericSearchWorker', function () { + // If this test fails, make sure this path is correct + var worker, + objectX, + objectY, + objectZ, + itemsToIndex, + onMessage, + data, + waitForResult; - // Search something that should return all objects - runs(function () { - worker.postMessage( - { - request: "search", - input: "object", - maxResults: 100, - timestamp: Date.now(), - timeout: 1000 - } - ); - }); + beforeEach(function () { + worker = new Worker( + require.toUrl('platform/search/src/services/GenericSearchWorker.js') + ); - worker.onmessage = function (event) { - var id; + objectX = { + id: 'x', + model: {name: 'object xx'} + }; + objectY = { + id: 'y', + model: {name: 'object yy'} + }; + objectZ = { + id: 'z', + model: {name: 'object zz'} + }; + itemsToIndex = [ + objectX, + objectY, + objectZ + ]; - workerOutput = event.data; - for (id in workerOutput.results) { - resultsLength += 1; - } - flag = true; - }; - - waitsFor(function () { - return flag; - }, "The worker should be searching", 1000); - - runs(function () { - expect(workerOutput).toBeDefined(); - expect(resultsLength).toEqual(numObjects); + itemsToIndex.forEach(function (item) { + worker.postMessage({ + request: 'index', + id: item.id, + model: item.model }); }); - it("searches return only matches", function () { - var flag = false, - workerOutput, - resultsLength = 0; - - // Search something that should return 1 object - runs(function () { - worker.postMessage( - { - request: "search", - input: "2", - maxResults: 100, - timestamp: Date.now(), - timeout: 1000 - } - ); - }); - - worker.onmessage = function (event) { - var id; - - workerOutput = event.data; - for (id in workerOutput.results) { - resultsLength += 1; - } - flag = true; - }; + onMessage = jasmine.createSpy('onMessage'); + worker.addEventListener('message', onMessage); + waitForResult = function () { waitsFor(function () { - return flag; - }, "The worker should be searching", 1000); - - runs(function () { - expect(workerOutput).toBeDefined(); - expect(resultsLength).toEqual(1); - expect(workerOutput.results[2]).toBeDefined(); + if (onMessage.calls.length > 0) { + data = onMessage.calls[0].args[0].data; + return true; + } + return false; }); + }; + }); + + afterEach(function () { + worker.terminate(); + }); + + it('returns search results for partial term matches', function () { + + worker.postMessage({ + request: 'search', + input: 'obj', + maxResults: 100, + queryId: 123 + }); + + waitForResult(); + + runs(function () { + expect(onMessage).toHaveBeenCalled(); + + expect(data.request).toBe('search'); + expect(data.total).toBe(3); + expect(data.queryId).toBe(123); + expect(data.results.length).toBe(3); + expect(data.results[0].item.id).toBe('x'); + expect(data.results[0].item.model).toEqual(objectX.model); + expect(data.results[0].matchCount).toBe(1); + expect(data.results[1].item.id).toBe('y'); + expect(data.results[1].item.model).toEqual(objectY.model); + expect(data.results[1].matchCount).toBe(1); + expect(data.results[2].item.id).toBe('z'); + expect(data.results[2].item.model).toEqual(objectZ.model); + expect(data.results[2].matchCount).toBe(1); }); }); - } -); + + it('scores exact term matches higher', function () { + worker.postMessage({ + request: 'search', + input: 'object', + maxResults: 100, + queryId: 234 + }); + + waitForResult(); + + runs(function () { + expect(data.queryId).toBe(234); + expect(data.results.length).toBe(3); + expect(data.results[0].item.id).toBe('x'); + expect(data.results[0].matchCount).toBe(1.5); + }); + }); + + it('can find partial term matches', function () { + worker.postMessage({ + request: 'search', + input: 'x', + maxResults: 100, + queryId: 345 + }); + + waitForResult(); + + runs(function () { + expect(data.queryId).toBe(345); + expect(data.results.length).toBe(1); + expect(data.results[0].item.id).toBe('x'); + expect(data.results[0].matchCount).toBe(1); + }); + }); + + it('matches individual terms', function () { + worker.postMessage({ + request: 'search', + input: 'x y z', + maxResults: 100, + queryId: 456 + }); + + waitForResult(); + + runs(function () { + expect(data.queryId).toBe(456); + expect(data.results.length).toBe(3); + expect(data.results[0].item.id).toBe('x'); + expect(data.results[0].matchCount).toBe(1); + expect(data.results[1].item.id).toBe('y'); + expect(data.results[1].matchCount).toBe(1); + expect(data.results[2].item.id).toBe('z'); + expect(data.results[1].matchCount).toBe(1); + }); + }); + + it('scores exact matches highest', function () { + worker.postMessage({ + request: 'search', + input: 'object xx', + maxResults: 100, + queryId: 567 + }); + + waitForResult(); + + runs(function () { + expect(data.queryId).toBe(567); + expect(data.results.length).toBe(3); + expect(data.results[0].item.id).toBe('x'); + expect(data.results[0].matchCount).toBe(103); + expect(data.results[1].matchCount).toBe(1.5); + expect(data.results[2].matchCount).toBe(1.5); + }); + }); + + it('scores multiple term match above single match', function () { + worker.postMessage({ + request: 'search', + input: 'obj x', + maxResults: 100, + queryId: 678 + }); + + waitForResult(); + + runs(function () { + expect(data.queryId).toBe(678); + expect(data.results.length).toBe(3); + expect(data.results[0].item.id).toBe('x'); + expect(data.results[0].matchCount).toBe(2); + expect(data.results[1].matchCount).toBe(1); + expect(data.results[2].matchCount).toBe(1); + }); + }); + }); +}); From 76151d09a0a589fb646494079ebf8a483ecb2d11 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 20 Oct 2015 15:13:37 -0700 Subject: [PATCH 092/488] [Search] use service for filters, add spec Add a spec for the SearchController, and use the SearchService to execute filters by supplying a filterPredicate. --- .../src/controllers/SearchController.js | 92 +++-- .../test/controllers/SearchControllerSpec.js | 380 ++++++++++-------- 2 files changed, 250 insertions(+), 222 deletions(-) diff --git a/platform/search/src/controllers/SearchController.js b/platform/search/src/controllers/SearchController.js index 88c9663f51..629e495331 100644 --- a/platform/search/src/controllers/SearchController.js +++ b/platform/search/src/controllers/SearchController.js @@ -27,9 +27,6 @@ define(function () { "use strict"; - var INITIAL_LOAD_NUMBER = 20, - LOAD_INCREMENT = 20; - /** * Controller for search in Tree View. * @@ -50,9 +47,8 @@ define(function () { var controller = this; this.$scope = $scope; this.searchService = searchService; - this.numberToDisplay = INITIAL_LOAD_NUMBER; - this.fullResults = []; - this.filteredResults = []; + this.numberToDisplay = this.RESULTS_PER_PAGE; + this.availabileResults = 0; this.$scope.results = []; this.$scope.loading = false; this.pendingQuery = undefined; @@ -61,28 +57,30 @@ define(function () { }; } + SearchController.prototype.RESULTS_PER_PAGE = 20; + /** * Returns true if there are more results than currently displayed for the * for the current query and filters. */ SearchController.prototype.areMore = function () { - return this.$scope.results.length < this.filteredResults.length; + return this.$scope.results.length < this.availableResults; }; /** * Display more results for the currently displayed query and filters. */ SearchController.prototype.loadMore = function () { - this.numberToDisplay += LOAD_INCREMENT; - this.updateResults(); + this.numberToDisplay += this.RESULTS_PER_PAGE; + this.dispatchSearch(); }; /** - * Search for the query string specified in scope. + * Reset search results, then search for the query string specified in + * scope. */ SearchController.prototype.search = function () { - var inputText = this.$scope.ngModel.input, - controller = this; + var inputText = this.$scope.ngModel.input; this.clearResults(); @@ -96,51 +94,64 @@ define(function () { return; } - if (this.pendingQuery === inputText) { + this.dispatchSearch(); + }; + + /** + * Dispatch a search to the search service if it hasn't already been + * dispatched. + * + * @private + */ + SearchController.prototype.dispatchSearch = function () { + var inputText = this.$scope.ngModel.input, + controller = this, + queryId = inputText + this.numberToDisplay; + + if (this.pendingQuery === queryId) { return; // don't issue multiple queries for the same term. } - this.pendingQuery = inputText; + this.pendingQuery = queryId; this .searchService - .query(inputText, 60) // TODO: allow filter in search service. + .query(inputText, this.numberToDisplay, this.filterPredicate()) .then(function (results) { - if (controller.pendingQuery !== inputText) { + if (controller.pendingQuery !== queryId) { return; // another query in progress, so skip this one. } controller.onSearchComplete(results); }); }; + SearchController.prototype.filter = SearchController.prototype.onFilterChange; + /** * Refilter results and update visible results when filters have changed. */ SearchController.prototype.onFilterChange = function () { - this.filter(); - this.updateVisibleResults(); + this.pendingQuery = undefined; + this.search(); }; /** - * Filter `fullResults` based on currenly active filters, storing the result - * in `filteredResults`. + * Returns a predicate function that can be used to filter object models. * * @private */ - SearchController.prototype.filter = function () { - var includeTypes = this.$scope.ngModel.checked; - + SearchController.prototype.filterPredicate = function () { if (this.$scope.ngModel.checkAll) { - this.filteredResults = this.fullResults; - return; + return function () { + return true; + }; } - - this.filteredResults = this.fullResults.filter(function (hit) { - return includeTypes[hit.object.getModel().type]; - }); + var includeTypes = this.$scope.ngModel.checked; + return function (model) { + return !!includeTypes[model.type]; + }; }; - /** * Clear the search results. * @@ -148,35 +159,22 @@ define(function () { */ SearchController.prototype.clearResults = function () { this.$scope.results = []; - this.fullResults = []; - this.filteredResults = []; - this.numberToDisplay = INITIAL_LOAD_NUMBER; + this.availableResults = 0; + this.numberToDisplay = this.RESULTS_PER_PAGE; }; - /** * Update search results from given `results`. * * @private */ SearchController.prototype.onSearchComplete = function (results) { - this.fullResults = results.hits; - this.filter(); - this.updateVisibleResults(); + this.availableResults = results.total; + this.$scope.results = results.hits; this.$scope.loading = false; this.pendingQuery = undefined; }; - /** - * Update visible results from filtered results. - * - * @private - */ - SearchController.prototype.updateVisibleResults = function () { - this.$scope.results = - this.filteredResults.slice(0, this.numberToDisplay); - }; - return SearchController; }); diff --git a/platform/search/test/controllers/SearchControllerSpec.js b/platform/search/test/controllers/SearchControllerSpec.js index 720d9bd64a..c28d2c540f 100644 --- a/platform/search/test/controllers/SearchControllerSpec.js +++ b/platform/search/test/controllers/SearchControllerSpec.js @@ -4,12 +4,12 @@ * Administration. All rights reserved. * * Open MCT Web is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. + * '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 + * 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. @@ -24,185 +24,215 @@ /** * SearchSpec. Created by shale on 07/31/2015. */ -define( - ["../../src/controllers/SearchController"], - function (SearchController) { - "use strict"; +define([ + '../../src/controllers/SearchController' +], function ( + SearchController +) { + 'use strict'; - // These should be the same as the ones on the top of the search controller - var INITIAL_LOAD_NUMBER = 20, - LOAD_INCREMENT = 20; - - describe("The search controller", function () { - var mockScope, - mockSearchService, - mockPromise, - mockSearchResult, - mockDomainObject, - mockTypes, - controller; + describe('The search controller', function () { + var mockScope, + mockSearchService, + mockPromise, + mockSearchResult, + mockDomainObject, + mockTypes, + controller; - function bigArray(size) { - var array = [], - i; - for (i = 0; i < size; i += 1) { - array.push(mockSearchResult); - } - return array; + function bigArray(size) { + var array = [], + i; + for (i = 0; i < size; i += 1) { + array.push(mockSearchResult); } - - - beforeEach(function () { - mockScope = jasmine.createSpyObj( - "$scope", - [ "$watch" ] - ); - mockScope.ngModel = {}; - mockScope.ngModel.input = "test input"; - mockScope.ngModel.checked = {}; - mockScope.ngModel.checked['mock.type'] = true; + return array; + } + + + beforeEach(function () { + mockScope = jasmine.createSpyObj( + '$scope', + [ '$watch' ] + ); + mockScope.ngModel = {}; + mockScope.ngModel.input = 'test input'; + mockScope.ngModel.checked = {}; + mockScope.ngModel.checked['mock.type'] = true; + mockScope.ngModel.checkAll = true; + + mockSearchService = jasmine.createSpyObj( + 'searchService', + [ 'query' ] + ); + mockPromise = jasmine.createSpyObj( + 'promise', + [ 'then' ] + ); + mockSearchService.query.andReturn(mockPromise); + + mockTypes = [{key: 'mock.type', name: 'Mock Type', glyph: '?'}]; + + mockSearchResult = jasmine.createSpyObj( + 'searchResult', + [ '' ] + ); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + [ 'getModel' ] + ); + mockSearchResult.object = mockDomainObject; + mockDomainObject.getModel.andReturn({name: 'Mock Object', type: 'mock.type'}); + + controller = new SearchController(mockScope, mockSearchService, mockTypes); + controller.search(); + }); + + it('has a default number of results per page', function () { + expect(controller.RESULTS_PER_PAGE).toBe(20); + }); + + it('sends queries to the search service', function () { + expect(mockSearchService.query).toHaveBeenCalledWith( + 'test input', + controller.RESULTS_PER_PAGE, + jasmine.any(Function) + ); + }); + + describe('filter query function', function () { + it('returns true when all types allowed', function () { mockScope.ngModel.checkAll = true; - - mockSearchService = jasmine.createSpyObj( - "searchService", - [ "query" ] - ); - mockPromise = jasmine.createSpyObj( - "promise", - [ "then" ] - ); - mockSearchService.query.andReturn(mockPromise); - - mockTypes = [{key: 'mock.type', name: 'Mock Type', glyph: '?'}]; - - mockSearchResult = jasmine.createSpyObj( - "searchResult", - [ "" ] - ); - mockDomainObject = jasmine.createSpyObj( - "domainObject", - [ "getModel" ] - ); - mockSearchResult.object = mockDomainObject; - mockDomainObject.getModel.andReturn({name: 'Mock Object', type: 'mock.type'}); - - controller = new SearchController(mockScope, mockSearchService, mockTypes); - controller.search(); - }); - - it("sends queries to the search service", function () { - expect(mockSearchService.query).toHaveBeenCalled(); - }); - - it("populates the results with results from the search service", function () { - expect(mockPromise.then).toHaveBeenCalledWith(jasmine.any(Function)); - mockPromise.then.mostRecentCall.args[0]({hits: []}); - - expect(mockScope.results).toBeDefined(); - }); - - it("is loading until the service's promise fufills", function () { - // Send query - controller.search(); - expect(mockScope.loading).toBeTruthy(); - - // Then resolve the promises - mockPromise.then.mostRecentCall.args[0]({hits: []}); - expect(mockScope.loading).toBeFalsy(); + controller.onFilterChange(); + var filterFn = mockSearchService.query.mostRecentCall.args[2]; + expect(filterFn('askbfa')).toBe(true); }); - - it("displays only some results when there are many", function () { - expect(mockPromise.then).toHaveBeenCalledWith(jasmine.any(Function)); - mockPromise.then.mostRecentCall.args[0]({hits: bigArray(100)}); - - expect(mockScope.results).toBeDefined(); - expect(mockScope.results.length).toBeLessThan(100); - }); - - it("detects when there are more results", function () { + it('returns true only for matching checked types', function () { mockScope.ngModel.checkAll = false; - - expect(mockPromise.then).toHaveBeenCalledWith(jasmine.any(Function)); - mockPromise.then.mostRecentCall.args[0]({ - hits: bigArray(INITIAL_LOAD_NUMBER + 5), - total: INITIAL_LOAD_NUMBER + 5 - }); - // bigArray gives searchResults of type 'mock.type' - mockScope.ngModel.checked['mock.type'] = false; - mockScope.ngModel.checked['mock.type.2'] = true; - - expect(controller.areMore()).toBeFalsy(); - - mockScope.ngModel.checked['mock.type'] = true; - - expect(controller.areMore()).toBeTruthy(); - }); - - it("can load more results", function () { - var oldSize; - - expect(mockPromise.then).toHaveBeenCalled(); - mockPromise.then.mostRecentCall.args[0]({ - hits: bigArray(INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1), - total: INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1 - }); - // These hits and total lengths are the case where the controller - // DOES NOT have to re-search to load more results - oldSize = mockScope.results.length; - - expect(controller.areMore()).toBeTruthy(); - - controller.loadMore(); - expect(mockScope.results.length).toBeGreaterThan(oldSize); - }); - - it("can re-search to load more results", function () { - var oldSize, - oldCallCount; - - expect(mockPromise.then).toHaveBeenCalled(); - mockPromise.then.mostRecentCall.args[0]({ - hits: bigArray(INITIAL_LOAD_NUMBER + LOAD_INCREMENT - 1), - total: INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1 - }); - // These hits and total lengths are the case where the controller - // DOES have to re-search to load more results - oldSize = mockScope.results.length; - oldCallCount = mockPromise.then.callCount; - expect(controller.areMore()).toBeTruthy(); - - controller.loadMore(); - expect(mockPromise.then).toHaveBeenCalled(); - // Make sure that a NEW call to search has been made - expect(oldCallCount).toBeLessThan(mockPromise.then.callCount); - mockPromise.then.mostRecentCall.args[0]({ - hits: bigArray(INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1), - total: INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1 - }); - expect(mockScope.results.length).toBeGreaterThan(oldSize); - }); - - it("sets the ngModel.search flag", function () { - // Flag should be true with nonempty input - expect(mockScope.ngModel.search).toEqual(true); - - // Flag should be flase with empty input - mockScope.ngModel.input = ""; - controller.search(); - mockPromise.then.mostRecentCall.args[0]({hits: [], total: 0}); - expect(mockScope.ngModel.search).toEqual(false); - - // Both the empty string and undefined should be 'empty input' - mockScope.ngModel.input = undefined; - controller.search(); - mockPromise.then.mostRecentCall.args[0]({hits: [], total: 0}); - expect(mockScope.ngModel.search).toEqual(false); - }); - - it("has a default results list to filter from", function () { - expect(mockScope.ngModel.filter()).toBeDefined(); + controller.onFilterChange(); + var filterFn = mockSearchService.query.mostRecentCall.args[2]; + expect(filterFn({type: 'mock.type'})).toBe(true); + expect(filterFn({type: 'other.type'})).toBe(false); }); }); - } -); \ No newline at end of file + + it('populates the results with results from the search service', function () { + expect(mockPromise.then).toHaveBeenCalledWith(jasmine.any(Function)); + mockPromise.then.mostRecentCall.args[0]({hits: ['a']}); + + expect(mockScope.results.length).toBe(1); + expect(mockScope.results).toContain('a'); + }); + + it('is loading until the service\'s promise fufills', function () { + expect(mockScope.loading).toBeTruthy(); + + // Then resolve the promises + mockPromise.then.mostRecentCall.args[0]({hits: []}); + expect(mockScope.loading).toBeFalsy(); + }); + + + xit('displays only some results when there are many', function () { + expect(mockPromise.then).toHaveBeenCalledWith(jasmine.any(Function)); + mockPromise.then.mostRecentCall.args[0]({hits: bigArray(100)}); + + expect(mockScope.results).toBeDefined(); + expect(mockScope.results.length).toBeLessThan(100); + }); + + it('detects when there are more results', function () { + mockPromise.then.mostRecentCall.args[0]({ + hits: bigArray(controller.RESULTS_PER_PAGE), + total: controller.RESULTS_PER_PAGE + 5 + }); + + expect(mockScope.results.length).toBe(controller.RESULTS_PER_PAGE); + expect(controller.areMore()).toBeTruthy; + + controller.loadMore(); + + expect(mockSearchService.query).toHaveBeenCalledWith( + 'test input', + controller.RESULTS_PER_PAGE * 2, + jasmine.any(Function) + ); + + mockPromise.then.mostRecentCall.args[0]({ + hits: bigArray(controller.RESULTS_PER_PAGE + 5), + total: controller.RESULTS_PER_PAGE + 5 + }); + + expect(mockScope.results.length) + .toBe(controller.RESULTS_PER_PAGE + 5); + + expect(controller.areMore()).toBe(false); + }); + + xit('can load more results', function () { + var oldSize; + + expect(mockPromise.then).toHaveBeenCalled(); + mockPromise.then.mostRecentCall.args[0]({ + hits: bigArray(INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1), + total: INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1 + }); + // These hits and total lengths are the case where the controller + // DOES NOT have to re-search to load more results + oldSize = mockScope.results.length; + + expect(controller.areMore()).toBeTruthy(); + + controller.loadMore(); + expect(mockScope.results.length).toBeGreaterThan(oldSize); + }); + + xit('can re-search to load more results', function () { + var oldSize, + oldCallCount; + + expect(mockPromise.then).toHaveBeenCalled(); + mockPromise.then.mostRecentCall.args[0]({ + hits: bigArray(INITIAL_LOAD_NUMBER + LOAD_INCREMENT - 1), + total: INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1 + }); + // These hits and total lengths are the case where the controller + // DOES have to re-search to load more results + oldSize = mockScope.results.length; + oldCallCount = mockPromise.then.callCount; + expect(controller.areMore()).toBeTruthy(); + + controller.loadMore(); + expect(mockPromise.then).toHaveBeenCalled(); + // Make sure that a NEW call to search has been made + expect(oldCallCount).toBeLessThan(mockPromise.then.callCount); + mockPromise.then.mostRecentCall.args[0]({ + hits: bigArray(INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1), + total: INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1 + }); + expect(mockScope.results.length).toBeGreaterThan(oldSize); + }); + + it('sets the ngModel.search flag', function () { + // Flag should be true with nonempty input + expect(mockScope.ngModel.search).toEqual(true); + + // Flag should be flase with empty input + mockScope.ngModel.input = ''; + controller.search(); + mockPromise.then.mostRecentCall.args[0]({hits: [], total: 0}); + expect(mockScope.ngModel.search).toEqual(false); + + // Both the empty string and undefined should be 'empty input' + mockScope.ngModel.input = undefined; + controller.search(); + mockPromise.then.mostRecentCall.args[0]({hits: [], total: 0}); + expect(mockScope.ngModel.search).toEqual(false); + }); + + it('attaches a filter function to scope', function () { + expect(mockScope.ngModel.filter).toEqual(jasmine.any(Function)); + }); + }); +}); From ce42429fbdd23bc5eb52cb542e0f3c1be87398f0 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 20 Oct 2015 15:14:43 -0700 Subject: [PATCH 093/488] [Search] expose constants, add fudge factor The SearchAggregator exposes it's constants to add stability to tests. It also has a fudge factor which increaases the number of results it requests from providers to better support pagination when using client side filtering. --- .../search/src/services/SearchAggregator.js | 23 +++++++++++++++---- .../test/services/SearchAggregatorSpec.js | 18 +++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/platform/search/src/services/SearchAggregator.js b/platform/search/src/services/SearchAggregator.js index 0cdcc5b922..40e77c271b 100644 --- a/platform/search/src/services/SearchAggregator.js +++ b/platform/search/src/services/SearchAggregator.js @@ -31,8 +31,6 @@ define([ ) { "use strict"; - var DEFAULT_MAX_RESULTS = 100; - /** * Aggregates multiple search providers as a singular search provider. * Search providers are expected to implement a `query` method which returns @@ -53,6 +51,23 @@ define([ this.providers = providers; } + /** + * If max results is not specified in query, use this as default. + */ + SearchAggregator.prototype.DEFAULT_MAX_RESULTS = 100; + + /** + * Because filtering isn't implemented inside each provider, the fudge + * factor is a multiplier on the number of results returned-- more results + * than requested will be fetched, and then will be fetched. This helps + * provide more predictable pagination when large numbers of matches exist + * but very few matches match filters. + * + * If a provider level filter implementation is implemented in the future, + * remove this. + */ + SearchAggregator.prototype.FUDGE_FACTOR = 5; + /** * Sends a query to each of the providers. Returns a promise for * a result object that has the format @@ -79,13 +94,13 @@ define([ resultPromises; if (!maxResults) { - maxResults = DEFAULT_MAX_RESULTS; + maxResults = this.DEFAULT_MAX_RESULTS; } resultPromises = this.providers.map(function (provider) { return provider.query( inputText, - maxResults + maxResults * aggregator.FUDGE_FACTOR ); }); diff --git a/platform/search/test/services/SearchAggregatorSpec.js b/platform/search/test/services/SearchAggregatorSpec.js index 044a5f4609..d63b672b2b 100644 --- a/platform/search/test/services/SearchAggregatorSpec.js +++ b/platform/search/test/services/SearchAggregatorSpec.js @@ -49,6 +49,13 @@ define([ aggregator = new SearchAggregator($q, objectService, providers); }); + it("has a fudge factor", function () { + expect(aggregator.FUDGE_FACTOR).toBe(5); + }); + + it("has default max results", function () { + expect(aggregator.DEFAULT_MAX_RESULTS).toBe(100); + }); it("can order model results by score", function () { var modelResults = { @@ -170,7 +177,11 @@ define([ providers.push(provider); aggregator.query('find me', 123, 'filter'); - expect(provider.query).toHaveBeenCalledWith('find me', 123); + expect(provider.query) + .toHaveBeenCalledWith( + 'find me', + 123 * aggregator.FUDGE_FACTOR + ); expect($q.all).toHaveBeenCalledWith(['i prooomise!']); }); @@ -181,7 +192,10 @@ define([ ); providers.push(provider); aggregator.query('find me'); - expect(provider.query).toHaveBeenCalledWith('find me', 100); + expect(provider.query).toHaveBeenCalledWith( + 'find me', + aggregator.DEFAULT_MAX_RESULTS * aggregator.FUDGE_FACTOR + ); }); it('can combine responses from multiple providers', function () { From fe3263fdfe8a1af779a37a4664ddd30c4e2380f4 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 20 Oct 2015 15:27:46 -0700 Subject: [PATCH 094/488] [Search] Remove invalid specs --- .../test/controllers/SearchControllerSpec.js | 55 +------------------ 1 file changed, 1 insertion(+), 54 deletions(-) diff --git a/platform/search/test/controllers/SearchControllerSpec.js b/platform/search/test/controllers/SearchControllerSpec.js index c28d2c540f..a755594d58 100644 --- a/platform/search/test/controllers/SearchControllerSpec.js +++ b/platform/search/test/controllers/SearchControllerSpec.js @@ -133,15 +133,6 @@ define([ expect(mockScope.loading).toBeFalsy(); }); - - xit('displays only some results when there are many', function () { - expect(mockPromise.then).toHaveBeenCalledWith(jasmine.any(Function)); - mockPromise.then.mostRecentCall.args[0]({hits: bigArray(100)}); - - expect(mockScope.results).toBeDefined(); - expect(mockScope.results.length).toBeLessThan(100); - }); - it('detects when there are more results', function () { mockPromise.then.mostRecentCall.args[0]({ hits: bigArray(controller.RESULTS_PER_PAGE), @@ -149,7 +140,7 @@ define([ }); expect(mockScope.results.length).toBe(controller.RESULTS_PER_PAGE); - expect(controller.areMore()).toBeTruthy; + expect(controller.areMore()).toBeTruthy(); controller.loadMore(); @@ -170,50 +161,6 @@ define([ expect(controller.areMore()).toBe(false); }); - xit('can load more results', function () { - var oldSize; - - expect(mockPromise.then).toHaveBeenCalled(); - mockPromise.then.mostRecentCall.args[0]({ - hits: bigArray(INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1), - total: INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1 - }); - // These hits and total lengths are the case where the controller - // DOES NOT have to re-search to load more results - oldSize = mockScope.results.length; - - expect(controller.areMore()).toBeTruthy(); - - controller.loadMore(); - expect(mockScope.results.length).toBeGreaterThan(oldSize); - }); - - xit('can re-search to load more results', function () { - var oldSize, - oldCallCount; - - expect(mockPromise.then).toHaveBeenCalled(); - mockPromise.then.mostRecentCall.args[0]({ - hits: bigArray(INITIAL_LOAD_NUMBER + LOAD_INCREMENT - 1), - total: INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1 - }); - // These hits and total lengths are the case where the controller - // DOES have to re-search to load more results - oldSize = mockScope.results.length; - oldCallCount = mockPromise.then.callCount; - expect(controller.areMore()).toBeTruthy(); - - controller.loadMore(); - expect(mockPromise.then).toHaveBeenCalled(); - // Make sure that a NEW call to search has been made - expect(oldCallCount).toBeLessThan(mockPromise.then.callCount); - mockPromise.then.mostRecentCall.args[0]({ - hits: bigArray(INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1), - total: INITIAL_LOAD_NUMBER + LOAD_INCREMENT + 1 - }); - expect(mockScope.results.length).toBeGreaterThan(oldSize); - }); - it('sets the ngModel.search flag', function () { // Flag should be true with nonempty input expect(mockScope.ngModel.search).toEqual(true); From 77d81f899bf2c1fb91d03bfc24399d8ba0f505a8 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 20 Oct 2015 15:31:33 -0700 Subject: [PATCH 095/488] [Style] JSLint compliance --- .../elastic/src/ElasticSearchProvider.js | 13 ++++++++----- .../test/services/GenericSearchProviderSpec.js | 15 ++++++++------- .../search/test/services/SearchAggregatorSpec.js | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/platform/persistence/elastic/src/ElasticSearchProvider.js b/platform/persistence/elastic/src/ElasticSearchProvider.js index e42cd6553d..5523ebe7bd 100644 --- a/platform/persistence/elastic/src/ElasticSearchProvider.js +++ b/platform/persistence/elastic/src/ElasticSearchProvider.js @@ -31,7 +31,10 @@ define([ ) { "use strict"; - var DEFAULT_MAX_RESULTS = 100; + var DEFAULT_MAX_RESULTS = 100, + ID_PROPERTY = '_id', + SOURCE_PROPERTY = '_source', + SCORE_PROPERTY = '_score'; /** * A search service which searches through domain objects in @@ -133,15 +136,15 @@ define([ var results = response.data.hits.hits, searchResults = results.map(function (result) { return { - id: result['_id'], - model: result['_source'], - score: result['_score'] + id: result[ID_PROPERTY], + model: result[SOURCE_PROPERTY], + score: result[SCORE_PROPERTY] }; }); return { hits: searchResults, - total: response.data.hits.total, + total: response.data.hits.total }; }; diff --git a/platform/search/test/services/GenericSearchProviderSpec.js b/platform/search/test/services/GenericSearchProviderSpec.js index fb3d595345..b958657e01 100644 --- a/platform/search/test/services/GenericSearchProviderSpec.js +++ b/platform/search/test/services/GenericSearchProviderSpec.js @@ -19,7 +19,8 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global define,describe,it,expect,beforeEach,jasmine*/ +/*global define,describe,it,expect,beforeEach,jasmine,Promise,spyOn,waitsFor, + runs*/ /** * SearchSpec. Created by shale on 07/31/2015. @@ -126,12 +127,12 @@ define([ }); it('tracks ids to index', function () { - expect(provider.indexedIds['a']).not.toBeDefined(); - expect(provider.pendingIndex['a']).not.toBeDefined(); + expect(provider.indexedIds.a).not.toBeDefined(); + expect(provider.pendingIndex.a).not.toBeDefined(); expect(provider.idsToIndex).not.toContain('a'); provider.scheduleForIndexing('a'); - expect(provider.indexedIds['a']).toBeDefined(); - expect(provider.pendingIndex['a']).toBeDefined(); + expect(provider.indexedIds.a).toBeDefined(); + expect(provider.pendingIndex.a).toBeDefined(); expect(provider.idsToIndex).toContain('a'); }); @@ -214,7 +215,7 @@ define([ provider.beginIndexRequest(); waitsFor(function () { return provider.pendingRequests === 0; - }) + }); runs(function () { expect(provider.index) .toHaveBeenCalledWith('abc', models.abc); @@ -224,7 +225,7 @@ define([ it('does not error if no objects queued', function () { provider.idsToIndex = []; expect(function () { - provider.beginIndexRequest() + provider.beginIndexRequest(); }).not.toThrow(); }); }); diff --git a/platform/search/test/services/SearchAggregatorSpec.js b/platform/search/test/services/SearchAggregatorSpec.js index d63b672b2b..f8bee0dcc0 100644 --- a/platform/search/test/services/SearchAggregatorSpec.js +++ b/platform/search/test/services/SearchAggregatorSpec.js @@ -45,7 +45,7 @@ define([ 'objectService', ['getObjects'] ); - providers = [], + providers = []; aggregator = new SearchAggregator($q, objectService, providers); }); From 21739fffd91c4892d427502e299550e49bb2e12a Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 20 Oct 2015 15:52:49 -0700 Subject: [PATCH 096/488] [Documentation] Add table of contents Add table of contents to generated documents, without modifying document sources; nasa/openmctweb#189. --- docs/gendocs.js | 11 ++++++++--- package.json | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/gendocs.js b/docs/gendocs.js index 2fcda7214e..455937e8b9 100644 --- a/docs/gendocs.js +++ b/docs/gendocs.js @@ -30,7 +30,8 @@ var CONSTANTS = { DIAGRAM_WIDTH: 800, DIAGRAM_HEIGHT: 500 - }; + }, + TOC_HEAD = "# Table of Contents"; GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be defined (function () { @@ -44,6 +45,7 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define split = require("split"), stream = require("stream"), nomnoml = require('nomnoml'), + toc = require("markdown-toc"), Canvas = require('canvas'), options = require("minimist")(process.argv.slice(2)); @@ -110,6 +112,9 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define done(); }; transform._flush = function (done) { + // Prepend table of contents + markdown = + [ TOC_HEAD, toc(markdown).content, "", markdown ].join("\n"); this.push("\n"); this.push(marked(markdown)); this.push("\n\n"); @@ -133,8 +138,8 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define customRenderer.link = function (href, title, text) { // ...but only if they look like relative paths return (href || "").indexOf(":") === -1 && href[0] !== "/" ? - renderer.link(href.replace(/\.md/, ".html"), title, text) : - renderer.link.apply(renderer, arguments); + renderer.link(href.replace(/\.md/, ".html"), title, text) : + renderer.link.apply(renderer, arguments); }; return customRenderer; } diff --git a/package.json b/package.json index a1ac01f437..c96642129c 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "split": "^1.0.0", "mkdirp": "^0.5.1", "nomnoml": "^0.0.3", - "canvas": "^1.2.7" + "canvas": "^1.2.7", + "markdown-toc": "^0.11.7" }, "scripts": { "start": "node app.js", From 9a63e997108000278b973c2128afc91309bac2b8 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 20 Oct 2015 16:01:42 -0700 Subject: [PATCH 097/488] [Search] Add spec for ElasticSearchProvider Add spec coverage for ElasticSearchProvider. Also remove unneeded guards for max number of results, as the aggregator will always provide a max number of results. --- platform/persistence/elastic/bundle.json | 2 +- .../elastic/src/ElasticSearchProvider.js | 17 +- .../elastic/test/ElasticSearchProviderSpec.js | 145 +++++++++++++++++- 3 files changed, 147 insertions(+), 17 deletions(-) diff --git a/platform/persistence/elastic/bundle.json b/platform/persistence/elastic/bundle.json index f78d8504f2..3e1383351e 100644 --- a/platform/persistence/elastic/bundle.json +++ b/platform/persistence/elastic/bundle.json @@ -13,7 +13,7 @@ "provides": "searchService", "type": "provider", "implementation": "ElasticSearchProvider.js", - "depends": [ "$http", "objectService", "ELASTIC_ROOT" ] + "depends": [ "$http", "ELASTIC_ROOT" ] } ], "constants": [ diff --git a/platform/persistence/elastic/src/ElasticSearchProvider.js b/platform/persistence/elastic/src/ElasticSearchProvider.js index 5523ebe7bd..84290d999a 100644 --- a/platform/persistence/elastic/src/ElasticSearchProvider.js +++ b/platform/persistence/elastic/src/ElasticSearchProvider.js @@ -31,8 +31,7 @@ define([ ) { "use strict"; - var DEFAULT_MAX_RESULTS = 100, - ID_PROPERTY = '_id', + var ID_PROPERTY = '_id', SOURCE_PROPERTY = '_source', SCORE_PROPERTY = '_score'; @@ -42,14 +41,11 @@ define([ * * @constructor * @param $http Angular's $http service, for working with urls. - * @param {ObjectService} objectService the service from which - * domain objects can be gotten. * @param ROOT the constant `ELASTIC_ROOT` which allows us to * interact with ElasticSearch. */ - function ElasticSearchProvider($http, objectService, ROOT) { + function ElasticSearchProvider($http, ROOT) { this.$http = $http; - this.objectService = objectService; this.root = ROOT; } @@ -65,10 +61,6 @@ define([ params = {}, provider = this; - if (!maxResults) { - maxResults = DEFAULT_MAX_RESULTS; - } - searchTerm = this.cleanTerm(searchTerm); searchTerm = this.fuzzyMatchUnquotedTerms(searchTerm); @@ -102,7 +94,7 @@ define([ * @returns {string} search terms cleaned of excess whitespace. */ ElasticSearchProvider.prototype.cleanTerm = function (term) { - return term.trim().replace(/ +/, ' '); + return term.trim().replace(/ +/g, ' '); }; /** @@ -121,7 +113,8 @@ define([ return query .replace(matcher, '~ ') - .replace('"~', '"'); + .replace(/$/, '~') + .replace(/"~+/, '"'); }; /** diff --git a/platform/persistence/elastic/test/ElasticSearchProviderSpec.js b/platform/persistence/elastic/test/ElasticSearchProviderSpec.js index af515e9431..f8337e0862 100644 --- a/platform/persistence/elastic/test/ElasticSearchProviderSpec.js +++ b/platform/persistence/elastic/test/ElasticSearchProviderSpec.js @@ -19,14 +19,151 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global define,describe,it,expect,beforeEach,jasmine*/ +/*global define,describe,it,expect,beforeEach,jasmine,spyOn,Promise,waitsFor*/ /** * SearchSpec. Created by shale on 07/31/2015. */ define([ - "../src/ElasticSearchProvider" -], function (ElasticSearchProvider) { - "use strict"; + '../src/ElasticSearchProvider' +], function ( + ElasticSearchProvider +) { + 'use strict'; + + describe('ElasticSearchProvider', function () { + var $http, + ROOT, + provider; + + beforeEach(function () { + $http = jasmine.createSpy('$http'); + ROOT = 'http://localhost:9200'; + + provider = new ElasticSearchProvider($http, ROOT); + }); + + describe('query', function () { + beforeEach(function () { + spyOn(provider, 'cleanTerm').andReturn('cleanedTerm'); + spyOn(provider, 'fuzzyMatchUnquotedTerms').andReturn('fuzzy'); + spyOn(provider, 'parseResponse').andReturn('parsedResponse'); + $http.andReturn(Promise.resolve({})); + }); + + it('cleans terms and adds fuzzyness', function () { + provider.query('hello', 10); + expect(provider.cleanTerm).toHaveBeenCalledWith('hello'); + expect(provider.fuzzyMatchUnquotedTerms) + .toHaveBeenCalledWith('cleanedTerm'); + }); + + it('calls through to $http', function () { + provider.query('hello', 10); + expect($http).toHaveBeenCalledWith({ + method: 'GET', + params: { + q: 'fuzzy', + size: 10 + }, + url: 'http://localhost:9200/_search/' + }); + }); + + it('gracefully fails when http fails', function () { + var promiseChainResolved = false; + $http.andReturn(Promise.reject()); + + provider + .query('hello', 10) + .then(function (results) { + expect(results).toEqual({ + hits: [], + total: 0 + }); + promiseChainResolved = true; + }); + + waitsFor(function () { + return promiseChainResolved; + }); + }); + + it('parses and returns when http succeeds', function () { + var promiseChainResolved = false; + $http.andReturn(Promise.resolve('successResponse')); + + provider + .query('hello', 10) + .then(function (results) { + expect(provider.parseResponse) + .toHaveBeenCalledWith('successResponse'); + expect(results).toBe('parsedResponse'); + promiseChainResolved = true; + }); + + waitsFor(function () { + return promiseChainResolved; + }); + }); + }); + + it('can clean terms', function () { + expect(provider.cleanTerm(' asdhs ')).toBe('asdhs'); + expect(provider.cleanTerm(' and some words')) + .toBe('and some words'); + expect(provider.cleanTerm('Nice input')).toBe('Nice input'); + }); + + it('can create fuzzy term matchers', function () { + expect(provider.fuzzyMatchUnquotedTerms('pwr dvc 43')) + .toBe('pwr~ dvc~ 43~'); + + expect(provider.fuzzyMatchUnquotedTerms( + 'hello welcome "to quoted village" have fun' + )).toBe( + 'hello~ welcome~ "to quoted village" have~ fun~' + ); + }); + + it('can parse responses', function () { + var elasticSearchResponse = { + data: { + hits: { + total: 2, + hits: [ + { + '_id': 'hit1Id', + '_source': 'hit1Model', + '_score': 0.56 + }, + { + '_id': 'hit2Id', + '_source': 'hit2Model', + '_score': 0.34 + } + ] + } + } + }; + + expect(provider.parseResponse(elasticSearchResponse)) + .toEqual({ + hits: [ + { + id: 'hit1Id', + model: 'hit1Model', + score: 0.56 + }, + { + id: 'hit2Id', + model: 'hit2Model', + score: 0.34 + } + ], + total: 2 + }); + }); + }); }); From 93f8e61c405f0be29a10ecccc615b377627d1180 Mon Sep 17 00:00:00 2001 From: Andrew Henry Date: Tue, 20 Oct 2015 21:12:14 -0700 Subject: [PATCH 098/488] roll back TOC generation --- docs/gendocs.js | 16 ---------------- package.json | 6 ++---- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/docs/gendocs.js b/docs/gendocs.js index 8f375aec0f..329a9b607f 100644 --- a/docs/gendocs.js +++ b/docs/gendocs.js @@ -117,21 +117,6 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define }; return transform; } - - function strip() { - var patternsToStrip = [ - "^ - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [Overview](#overview) - - [Application Initialization](#application-initialization) - - [Architectural Paradigm](#architectural-paradigm) - - [Extension Categories](#extension-categories) - - [Composite Services](#composite-services) - - - # Overview The framework layer's most basic responsibility is allowing individual diff --git a/docs/src/architecture/Platform.md b/docs/src/architecture/Platform.md index cd788c3cd8..a59a6ebf9c 100644 --- a/docs/src/architecture/Platform.md +++ b/docs/src/architecture/Platform.md @@ -1,28 +1,3 @@ - - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [Overview](#overview) - - [Platform Architecture](#platform-architecture) - - [Application Start-up](#application-start-up) -- [Presentation Layer](#presentation-layer) - - [Angular built-ins](#angular-built-ins) - - [Domain object representation](#domain-object-representation) -- [Information Model](#information-model) - - [Capabilities and Services](#capabilities-and-services) -- [Service Infrastructure](#service-infrastructure) - - [Object Service](#object-service) - - [Model Service](#model-service) - - [Capability Service](#capability-service) - - [Telemetry Service](#telemetry-service) - - [Persistence Service](#persistence-service) - - [Action Service](#action-service) - - [View Service](#view-service) - - [Policy Service](#policy-service) - - [Type Service](#type-service) - - - # Overview The Open MCT Web platform utilizes the [framework layer](Framework.md) diff --git a/docs/src/architecture/index.md b/docs/src/architecture/index.md index cbf73592db..fd9e0961e1 100644 --- a/docs/src/architecture/index.md +++ b/docs/src/architecture/index.md @@ -1,13 +1,3 @@ - - -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [Introduction](#introduction) -- [Overview](#overview) - - [Software Architecture](#software-architecture) - - - # Introduction The purpose of this document is to familiarize developers with the diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 2261511280..7b35dd66cc 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -13,144 +13,6 @@ May 12, 2015 | 0.1 | | Victor Woeltjen June 4, 2015 | 1.0 | Name Changes | Victor Woeltjen October 4, 2015 | 1.1 | Conversion to MarkDown | Andrew Henry -# Contents - - - - - -- [Introduction](#introduction) - - [What is Open MCT Web](#what-is-open-mct-web) - - [Client-Server Relationship](#client-server-relationship) - - [Developing with Open MCT Web](#developing-with-open-mct-web) - - [Technologies](#technologies) - - [Forking](#forking) -- [Overview](#overview) - - [Framework Overview](#framework-overview) - - [Tiers](#tiers) - - [Platform Overview](#platform-overview) - - [Web Services](#web-services) - - [Glossary](#glossary) -- [Framework](#framework) - - [Bundles](#bundles) - - [Configuring Active Bundles](#configuring-active-bundles) - - [Bundle Definition](#bundle-definition) - - [Bundle Directory Structure](#bundle-directory-structure) - - [Extensions](#extensions) - - [General Extensions](#general-extensions) - - [Extension Definitions](#extension-definitions) - - [Partial Construction](#partial-construction) - - [Priority](#priority) - - [Angular Built-ins](#angular-built-ins) - - [Angular Directives](#angular-directives) - - [Angular Controllers](#angular-controllers) - - [Angular Services](#angular-services) - - [Angular Constants](#angular-constants) - - [Angular Runs](#angular-runs) - - [Angular Routes](#angular-routes) - - [Composite Services](#composite-services) -- [Core API](#core-api) - - [Domain Objects](#domain-objects) - - [Domain Object Actions](#domain-object-actions) - - [Action Contexts](#action-contexts) - - [Telemetry](#telemetry) - - [Telemetry Requests](#telemetry-requests) - - [Telemetry Responses](#telemetry-responses) - - [Telemetry Series](#telemetry-series) - - [Telemetry Metadata](#telemetry-metadata) - - [Types](#types) - - [Type Features](#type-features) - - [Type Properties](#type-properties) -- [Extension Categories](#extension-categories) - - [Actions Category](#actions-category) - - [Capabilities Category](#capabilities-category) - - [Controls Category](#controls-category) - - [Gestures Category](#gestures-category) - - [Indicators Category](#indicators-category) - - [Standard Indicators](#standard-indicators) - - [Custom Indicators](#custom-indicators) - - [Licenses Category](#licenses-category) - - [Policies Category](#policies-category) - - [Representations Category](#representations-category) - - [Representation Scope](#representation-scope) - - [Representers Category](#representers-category) - - [Roots Category](#roots-category) - - [Stylesheets Category](#stylesheets-category) - - [Templates Category](#templates-category) - - [Types Category](#types-category) - - [Versions Category](#versions-category) - - [Views Category](#views-category) - - [View Scope](#view-scope) - - [Selection State](#selection-state) -- [Directives](#directives) - - [Before Unload](#before-unload) - - [Chart](#chart) - - [Container](#container) - - [Control](#control) - - [Drag](#drag) - - [Form](#form) - - [Form Structure](#form-structure) - - [Form Controls](#form-controls) - - [Include](#include) - - [Representation](#representation) - - [Resize](#resize) - - [Scroll](#scroll) - - [Toolbar](#toolbar) - - [Toolbar Structure](#toolbar-structure) -- [Services](#services) - - [Composite Type Services](#composite-type-services) - - [Action Service](#action-service) - - [Capability Service](#capability-service) - - [Dialog Service](#dialog-service) - - [Dialog Structure](#dialog-structure) - - [Domain Object Service](#domain-object-service) - - [Gesture Service](#gesture-service) - - [Model Service](#model-service) - - [Persistence Service](#persistence-service) - - [Policy Service](#policy-service) - - [Telemetry Service](#telemetry-service) - - [Type Service](#type-service) - - [View Service](#view-service) - - [Other Services](#other-services) - - [Drag and Drop](#drag-and-drop) - - [Navigation](#navigation) - - [Now](#now) - - [Telemetry Formatter](#telemetry-formatter) - - [Telemetry Handler](#telemetry-handler) - - [Telemetry Handle](#telemetry-handle) -- [Models](#models) - - [General Metadata](#general-metadata) - - [Extension-specific Properties](#extension-specific-properties) - - [Capability-specific Properties](#capability-specific-properties) - - [View Configurations](#view-configurations) - - [Modifying Models](#modifying-models) -- [Capabilities](#capabilities) - - [Action Capability](#action-capability) - - [Composition Capability](#composition-capability) - - [Delegation Capability](#delegation-capability) - - [Editor Capability](#editor-capability) - - [Mutation Capability](#mutation-capability) - - [Mutator Function](#mutator-function) - - [Persistence Capability](#persistence-capability) - - [Relationship Capability](#relationship-capability) - - [Telemetry Capability](#telemetry-capability) - - [Type Capability](#type-capability) - - [View Capability](#view-capability) -- [Actions](#actions) - - [Action Categories](#action-categories) - - [Platform Actions](#platform-actions) -- [Policies](#policies) - - [Policy Categories](#policy-categories) -- [Build-Test-Deploy](#build-test-deploy) - - [Command-line Build](#command-line-build) - - [Test Suite](#test-suite) - - [Code Coverage](#code-coverage) - - [Deployment](#deployment) - - [Configuration](#configuration) - - [Configuration Constants](#configuration-constants) - - - # Introduction The purpose of this guide is to familiarize software developers with the Open MCT Web platform. diff --git a/docs/src/tutorials/index.md b/docs/src/tutorials/index.md index 720c80c828..d07466abac 100644 --- a/docs/src/tutorials/index.md +++ b/docs/src/tutorials/index.md @@ -14,46 +14,6 @@ July 28, 2015 | 2.0 | Telemetry adapter tutorial | Victor Woeltjen July 31, 2015 | 2.1 | Clarify telemetry adapter details | Victor Woeltjen October 14, 2015 | 2.2 | Conversion to markdown | Andrew Henry -# Contents - - - - - -- [Introduction](#introduction) - - [This document](#this-document) - - [Setting Up Open MCT Web](#setting-up-open-mct-web) - - [Prerequisites](#prerequisites) - - [Check out Open MCT Web Sources](#check-out-open-mct-web-sources) - - [Configuring Persistence](#configuring-persistence) - - [Bundle Before](#bundle-before) - - [Bundle After](#bundle-after) - - [Run a Web Server](#run-a-web-server) - - [Viewing in Browser](#viewing-in-browser) -- [Tutorials](#tutorials) - - [To-do List](#to-do-list) - - [Step 1-Create the Plugin](#step-1-create-the-plugin) - - [Before](#before) - - [After](#after) - - [Step 2-Add a Domain Object Type](#step-2-add-a-domain-object-type) - - [Step 3-Add a View](#step-3-add-a-view) - - [Step 4-Add a Controller](#step-4-add-a-controller) - - [Step 5-Support Editing](#step-5-support-editing) - - [Step 6-Customizing Look and Feel](#step-6-customizing-look-and-feel) - - [Bar Graph](#bar-graph) - - [Step 1-Define the View](#step-1-define-the-view) - - [Step 2-Add a Controller](#step-2-add-a-controller) - - [Step 3-Using Telemetry Data](#step-3-using-telemetry-data) - - [Step 4-View Configuration](#step-4-view-configuration) - - [Telemetry Adapter](#telemetry-adapter) - - [Step 0-Expose Your Telemetry](#step-0-expose-your-telemetry) - - [Step 1-Add a Top-level Object](#step-1-add-a-top-level-object) - - [Step 2-Expose the Telemetry Dictionary](#step-2-expose-the-telemetry-dictionary) - - [Step 3-Historical Telemetry](#step-3-historical-telemetry) - - [Step 4-Real-time Telemetry](#step-4-real-time-telemetry) - - - # Introduction ## This document From 833f57e284d3f68cb70dd49ab7e27f20c7610c4c Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Wed, 21 Oct 2015 07:39:59 -0700 Subject: [PATCH 100/488] [Search] Don't block UI between requests Timeout subsequent calls to keepIndexing at the end of a indexRequest, so that UI operations are not blocked. --- .../src/services/GenericSearchProvider.js | 21 ++++++------ .../services/GenericSearchProviderSpec.js | 33 ++++++++++++++----- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/platform/search/src/services/GenericSearchProvider.js b/platform/search/src/services/GenericSearchProvider.js index e1c90f3f51..2b99abcae2 100644 --- a/platform/search/src/services/GenericSearchProvider.js +++ b/platform/search/src/services/GenericSearchProvider.js @@ -19,7 +19,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global define*/ +/*global define,setTimeout*/ /** * Module defining GenericSearchProvider. Created by shale on 07/16/2015. @@ -62,6 +62,8 @@ define([ ROOTS.forEach(function indexRoot(rootId) { provider.scheduleForIndexing(rootId); }); + + } /** @@ -148,8 +150,10 @@ define([ * @private */ GenericSearchProvider.prototype.keepIndexing = function () { - if (this.pendingRequests < this.MAX_CONCURRENT_REQUESTS) { - this.beginIndexRequest(); + while (this.pendingRequests < this.MAX_CONCURRENT_REQUESTS && + this.idsToIndex.length + ) { + this.beginIndexRequest(); } }; @@ -188,10 +192,6 @@ define([ var idToIndex = this.idsToIndex.shift(), provider = this; - if (!idToIndex) { - return; - } - this.pendingRequests += 1; this.modelService .getModels([idToIndex]) @@ -206,8 +206,10 @@ define([ .warn('Failed to index domain object ' + idToIndex); }) .then(function () { - provider.pendingRequests -= 1; - provider.keepIndexing(); + setTimeout(function () { + provider.pendingRequests -= 1; + provider.keepIndexing(); + }, 0); }); }; @@ -235,7 +237,6 @@ define([ }; }); - pendingQuery.resolve(modelResults); delete this.pendingQueries[event.data.queryId]; }; diff --git a/platform/search/test/services/GenericSearchProviderSpec.js b/platform/search/test/services/GenericSearchProviderSpec.js index b958657e01..7b8e52274b 100644 --- a/platform/search/test/services/GenericSearchProviderSpec.js +++ b/platform/search/test/services/GenericSearchProviderSpec.js @@ -143,17 +143,38 @@ define([ }); describe('keepIndexing', function () { - it('kicks off an index request when not at maximum', function () { - spyOn(provider, 'beginIndexRequest'); - provider.pendingRequests = 0; + it('calls beginIndexRequest until at maximum', function () { + spyOn(provider, 'beginIndexRequest').andCallThrough(); + provider.pendingRequests = 9; + provider.idsToIndex = ['a', 'b', 'c']; provider.MAX_CONCURRENT_REQUESTS = 10; provider.keepIndexing(); expect(provider.beginIndexRequest).toHaveBeenCalled(); + expect(provider.beginIndexRequest.calls.length).toBe(1); + }); + + it('calls beginIndexRequest for all ids to index', function () { + spyOn(provider, 'beginIndexRequest').andCallThrough(); + provider.pendingRequests = 0; + provider.idsToIndex = ['a', 'b', 'c']; + provider.MAX_CONCURRENT_REQUESTS = 10; + provider.keepIndexing(); + expect(provider.beginIndexRequest).toHaveBeenCalled(); + expect(provider.beginIndexRequest.calls.length).toBe(3); }); it('does not index when at capacity', function () { spyOn(provider, 'beginIndexRequest'); provider.pendingRequests = 10; + provider.idsToIndex.push('a'); + provider.MAX_CONCURRENT_REQUESTS = 10; + provider.keepIndexing(); + expect(provider.beginIndexRequest).not.toHaveBeenCalled(); + }); + + it('does not index when no ids to index', function () { + spyOn(provider, 'beginIndexRequest'); + provider.pendingRequests = 0; provider.MAX_CONCURRENT_REQUESTS = 10; provider.keepIndexing(); expect(provider.beginIndexRequest).not.toHaveBeenCalled(); @@ -222,12 +243,6 @@ define([ }); }); - it('does not error if no objects queued', function () { - provider.idsToIndex = []; - expect(function () { - provider.beginIndexRequest(); - }).not.toThrow(); - }); }); From 496cf85b7e8b55f5b432444bd63058e0b0a93d5a Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Wed, 21 Oct 2015 09:46:32 -0700 Subject: [PATCH 101/488] [JSDoc] Correct mistake --- platform/search/src/services/SearchAggregator.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/search/src/services/SearchAggregator.js b/platform/search/src/services/SearchAggregator.js index 40e77c271b..00988f81a8 100644 --- a/platform/search/src/services/SearchAggregator.js +++ b/platform/search/src/services/SearchAggregator.js @@ -59,9 +59,9 @@ define([ /** * Because filtering isn't implemented inside each provider, the fudge * factor is a multiplier on the number of results returned-- more results - * than requested will be fetched, and then will be fetched. This helps - * provide more predictable pagination when large numbers of matches exist - * but very few matches match filters. + * than requested will be fetched, and then will be filtered. This helps + * provide more predictable pagination when large numbers of results are + * returned but very few results match filters. * * If a provider level filter implementation is implemented in the future, * remove this. From dc5feb8b1a816f1f74d15db0a7d7277bc6d32cd5 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 22 Oct 2015 10:54:48 -0700 Subject: [PATCH 102/488] Added some documentation to notification and dialog launchers --- testing/dialogTest/res/dialog-launch.html | 3 +- .../dialogTest/res/notification-launch.html | 2 +- .../dialogTest/src/DialogLaunchController.js | 105 ++++++------------ .../dialogTest/src/DialogLaunchIndicator.js | 6 + .../src/NotificationLaunchController.js | 54 +++++++-- 5 files changed, 86 insertions(+), 84 deletions(-) diff --git a/testing/dialogTest/res/dialog-launch.html b/testing/dialogTest/res/dialog-launch.html index 1b117c2ebf..bc56e6b4f2 100644 --- a/testing/dialogTest/res/dialog-launch.html +++ b/testing/dialogTest/res/dialog-launch.html @@ -3,7 +3,8 @@ Known | Unknown | - Error + Error | + Info Dialogs \ No newline at end of file diff --git a/testing/dialogTest/res/notification-launch.html b/testing/dialogTest/res/notification-launch.html index e9b7e8f84e..1e077bf3be 100644 --- a/testing/dialogTest/res/notification-launch.html +++ b/testing/dialogTest/res/notification-launch.html @@ -1,7 +1,7 @@ - Success | + Success | Error | Alert | Progress diff --git a/testing/dialogTest/src/DialogLaunchController.js b/testing/dialogTest/src/DialogLaunchController.js index bc8cdcb419..42882a0dfe 100644 --- a/testing/dialogTest/src/DialogLaunchController.js +++ b/testing/dialogTest/src/DialogLaunchController.js @@ -26,7 +26,23 @@ define( function (MessageSeverity) { "use strict"; + /** + * A controller for the dialog launch view. This view allows manual + * launching of dialogs for demonstration and testing purposes. It + * also demonstrates the use of the DialogService. + * @param $scope + * @param $timeout + * @param $log + * @param dialogService + * @param notificationService + * @constructor + */ function DialogLaunchController($scope, $timeout, $log, dialogService, notificationService) { + + /* + Demonstrates launching a progress dialog and updating it + periodically with the progress of an ongoing process. + */ $scope.launchProgress = function (knownProgress) { var model = { title: "Progress Dialog Example", @@ -72,6 +88,10 @@ define( } }; + + /* + Demonstrates launching an error dialog + */ $scope.launchError = function () { var model = { title: "Error Dialog Example", @@ -100,87 +120,32 @@ define( } }; - $scope.launchMessages = function () { + /* + Demonstrates launching an error dialog + */ + $scope.launchInfo = function () { var model = { - title: "Messages", + title: "Info Dialog Example", + actionText: "This is an example of a blocking info" + + " dialog. This dialog can be used to draw the user's" + + " attention to an event.", severity: MessageSeverity.INFO, actions: [ { - label: "Done", + label: "OK", action: function () { + $log.debug("OK Pressed"); dialogService.dismiss(); } - } - ], - messages: [] + }, + ] }; - function getExampleActionText() { - var actionTexts = [ - "Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.", - "Eros turpis, pulvinar turpis eros eu", - "Lundium nascetur a, lectus montes ac, parturient in natoque, duis risus risus pulvinar pid rhoncus, habitasse auctor natoque!" - ]; - return actionTexts[Math.floor(Math.random()*3)]; + if (!dialogService.showBlockingMessage(model)) { + $log.error("Could not display modal dialog"); } - - function getExampleActions() { - var actions = [ - { - label: "Try Again", - action: function () { - $log.debug("Try Again pressed"); - } - }, - { - label: "Remove", - action: function () { - $log.debug("Remove pressed"); - } - }, - { - label: "Cancel", - action: function () { - $log.debug("Cancel pressed"); - } - } - ]; - - // Randomly remove some actions off the top; leave at least one - actions.splice(0,Math.floor(Math.random() * actions.length)); - - return actions; - } - - function getExampleSeverity() { - var severities = [ - MessageSeverity.INFO, - MessageSeverity.ALERT, - MessageSeverity.ERROR - ]; - return severities[Math.floor(Math.random() * severities.length)]; - } - - function createMessage (messageNumber) { - var messageModel = { - title: "Message Title " + messageNumber, - actionText: getExampleActionText(), - severity: getExampleSeverity(), - actions: getExampleActions() - }; - return messageModel; - } - - function dismiss() { - dialogService.dismiss(); - } - - model.messages = notificationService.notifications; - dialogService.getDialogResponse('overlay-message-list', { - dialog: model, - cancel: dismiss - }); }; + } return DialogLaunchController; } diff --git a/testing/dialogTest/src/DialogLaunchIndicator.js b/testing/dialogTest/src/DialogLaunchIndicator.js index 47c516fc70..9330ce2194 100644 --- a/testing/dialogTest/src/DialogLaunchIndicator.js +++ b/testing/dialogTest/src/DialogLaunchIndicator.js @@ -26,6 +26,12 @@ define( function () { "use strict"; + /** + * A tool for manually invoking dialogs. When included this + * indicator will allow for dialogs of different types to be + * launched for demonstration and testing purposes. + * @constructor + */ function DialogLaunchIndicator() { } diff --git a/testing/dialogTest/src/NotificationLaunchController.js b/testing/dialogTest/src/NotificationLaunchController.js index 63b436a894..b45bbfc094 100644 --- a/testing/dialogTest/src/NotificationLaunchController.js +++ b/testing/dialogTest/src/NotificationLaunchController.js @@ -26,14 +26,21 @@ define( function (MessageSeverity) { "use strict"; + /** + * Allows launching of notification messages for the purposes of + * demonstration and testing. Also demonstrates use of + * the NotificationService. Notifications are non-blocking messages that + * appear at the bottom of the screen to inform the user of events + * in a non-intrusive way. For more information see the + * {@link NotificationService} + * @param $scope + * @param $timeout + * @param $log + * @param notificationService + * @constructor + */ function NotificationLaunchController($scope, $timeout, $log, notificationService) { var messageCounter = 1; - $scope.newSuccess = function(){ - - notificationService.info({ - title: "Success notification!" - }); - }; function getExampleActionText() { var actionTexts = [ @@ -80,11 +87,14 @@ define( ]; return severities[Math.floor(Math.random() * severities.length)]; } - + + /** + * Launch a new notification with a severity level of 'Error'. + */ $scope.newError = function(){ notificationService.notify({ - title: "Error notification " + messageCounter++ + "!", + title: "Example error notification " + messageCounter++, hint: "An error has occurred", severity: MessageSeverity.ERROR, primaryAction: { @@ -95,11 +105,13 @@ define( }, actions: getExampleActions()}); }; - + /** + * Launch a new notification with a severity of 'Alert'. + */ $scope.newAlert = function(){ notificationService.notify({ - title: "Alert notification " + (messageCounter++) + "!", + title: "Alert notification " + (messageCounter++), hint: "This is an alert message", severity: MessageSeverity.ALERT, primaryAction: { @@ -111,17 +123,25 @@ define( actions: getExampleActions()}); }; + + /** + * Launch a new notification with a progress bar that is updated + * periodically, tracking an ongoing process. + */ $scope.newProgress = function(){ var notification = { - title: "Progress notification!", + title: "Progress notification example", severity: MessageSeverity.INFO, progress: 0, actionText: getExampleActionText(), unknownProgress: false - }; + /** + * Simulate an ongoing process and update the progress bar. + * @param notification + */ function incrementProgress(notification) { notification.progress = Math.min(100, Math.floor(notification.progress + Math.random() * 30)); notification.progressText = ["Estimated time remaining:" + @@ -135,6 +155,16 @@ define( incrementProgress(notification); }; + /** + * Launch a new notification with severity level of INFO. + */ + $scope.newInfo = function(){ + + notificationService.info({ + title: "Example Info notification " + messageCounter++ + }); + }; + } return NotificationLaunchController; } From 1c5101eca60dde69b2d8c9c8ca353ed3f12ee06b Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 11:30:57 -0700 Subject: [PATCH 103/488] [Time Conductor] Add DateAggregator Add DateAggregator, to allow composite services to expose different ways of parsing/formatting dates. nasa/openmctweb#182. --- .../general/src/services/DateAggregator.js | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 platform/commonUI/general/src/services/DateAggregator.js diff --git a/platform/commonUI/general/src/services/DateAggregator.js b/platform/commonUI/general/src/services/DateAggregator.js new file mode 100644 index 0000000000..3c2762d1bd --- /dev/null +++ b/platform/commonUI/general/src/services/DateAggregator.js @@ -0,0 +1,116 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define([ + +], function ( +) { + "use strict"; + + /** + * Formats dates for display and parses dates from user input, + * varying by a chosen time system. + * + * Time systems are typically domain keys from telemetry metadata. + * If omitted/left undefined, the time system is presumed to be UTC time, + * with its numeric interpretation being milliseconds since the + * start of 1970. + * + * @interface DateService + */ + + /** + * Check if the provided text can be parsed into a numeric + * representation of a time in the specified time system. + * @method validate + * @memberof DateService# + * @param {string} text the text to validate + * @param {string} [key] a key identifying the time system + * @returns {boolean} true if the text can be parsed + */ + + /** + * Parse the provided into a numeric representation of a time + * in the specified time system. + * + * Behavior of this method for invalid text is undefined; use + * the `validate` method to check for validity first. + * + * @method parse + * @memberof DateService# + * @param {string} text the text to parse + * @param {string} [key] a key identifying the time system + * @returns {number} a numeric representation of the date/time + */ + + /** + * Format the provided numeric representation of a time + * into a human-readable string appropriate for that time system. + * + * If the time system is not recognized, the return value will be + * `undefined`. + * + * @method format + * @memberof DateService# + * @param {number} value the time value to format + * @param {string} [key] a key identifying the time system + * @returns {string} a human-readable representation of the date/time + */ + + /** + * Composites multiple DateService implementations such that + * they can be used as one. + * @memberof platform/commonUI/general + * @constructor + */ + function DateAggregator(dateProviders) { + this.dateProviders = dateProviders; + } + + DateAggregator.prototype.validate = function (text, key) { + return this.dateProviders.some(function (provider) { + return provider.validate(text, key); + }); + }; + + DateAggregator.prototype.format = function (value, key) { + var i, text; + for (i = 0; i < this.dateProviders.length; i += 1) { + text = this.dateProviders[i].format(value, key); + if (text !== undefined) { + return text; + } + } + }; + + DateAggregator.prototype.parse = function (text, key) { + var i; + for (i = 0; i < this.dateProviders.length; i += 1) { + if (this.dateProviders[i].validate(text, key)) { + return this.dateProviders[i].parse(text, key); + } + } + }; + + return DateAggregator; +}); From 6b42d3bf4b5bf1856994acdbb128e6fb781760fd Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 11:43:42 -0700 Subject: [PATCH 104/488] [Time Conductor] Test DateAggregator --- .../test/services/DateAggregatorSpec.js | 94 +++++++++++++++++++ platform/commonUI/general/test/suite.json | 1 + 2 files changed, 95 insertions(+) create mode 100644 platform/commonUI/general/test/services/DateAggregatorSpec.js diff --git a/platform/commonUI/general/test/services/DateAggregatorSpec.js b/platform/commonUI/general/test/services/DateAggregatorSpec.js new file mode 100644 index 0000000000..ba55bc907a --- /dev/null +++ b/platform/commonUI/general/test/services/DateAggregatorSpec.js @@ -0,0 +1,94 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + + +define( + ["../../src/services/DateAggregator"], + function (DateAggregator) { + 'use strict'; + + var DATE_SERVICE_METHODS = [ "format", "validate", "parse" ]; + + describe("DateAggregator", function () { + var mockProviders, + dateAggregator; + + beforeEach(function () { + mockProviders = [ 'a', 'b', 'c', undefined ].map(function (k, i) { + var mockProvider = jasmine.createSpyObj( + 'provider-' + k, + DATE_SERVICE_METHODS + ); + + mockProvider.format.andCallFake(function (value, key) { + return key === k ? + ("Formatted " + value + " for " + k) : + undefined; + }); + + mockProvider.parse.andCallFake(function (text, key) { + return key === k ? i : undefined; + }); + + mockProvider.validate.andCallFake(function (text, key) { + return key === k; + }); + + return mockProvider; + }); + + dateAggregator = new DateAggregator(mockProviders); + }); + + it("formats dates using the first provider which gives a result", function () { + expect(dateAggregator.format(42, "a")) + .toEqual("Formatted 42 for a"); + expect(dateAggregator.format(12321, "b")) + .toEqual("Formatted 12321 for b"); + expect(dateAggregator.format(1977, "c")) + .toEqual("Formatted 1977 for c"); + expect(dateAggregator.format(0)) + .toEqual("Formatted 0 for undefined"); + }); + + it("parses dates using the first provider which validates", function () { + expect(dateAggregator.parse("x", "a")).toEqual(0); + expect(dateAggregator.parse("x", "b")).toEqual(1); + expect(dateAggregator.parse("x", "c")).toEqual(2); + expect(dateAggregator.parse("x")).toEqual(3); + }); + + it("validates across all providers", function () { + expect(dateAggregator.validate("x", "a")).toBeTruthy(); + expect(dateAggregator.validate("x", "b")).toBeTruthy(); + expect(dateAggregator.validate("x", "c")).toBeTruthy(); + expect(dateAggregator.validate("x")).toBeTruthy(); + expect(dateAggregator.validate("x", "z")).toBeFalsy(); + + mockProviders[3].validate.andReturn(false); + expect(dateAggregator.validate("x")).toBeFalsy(); + }); + + }); + } +); diff --git a/platform/commonUI/general/test/suite.json b/platform/commonUI/general/test/suite.json index 0d19fbb9e4..3ab06212d4 100644 --- a/platform/commonUI/general/test/suite.json +++ b/platform/commonUI/general/test/suite.json @@ -17,6 +17,7 @@ "directives/MCTPopup", "directives/MCTResize", "directives/MCTScroll", + "services/DateAggregator", "services/Popup", "services/PopupService", "services/UrlService", From 794231143ecbd484951060f27d329dfda075c1fa Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 11:55:38 -0700 Subject: [PATCH 105/488] [Time Conductor] Add spec for UTCDateProvider Add test cases for UTCDateProvider, which will provide default date/time formatting/parsing. --- .../general/src/services/UTCDateProvider.js | 51 ++++++++++++++ .../test/services/UTCDateProviderSpec.js | 67 +++++++++++++++++++ platform/commonUI/general/test/suite.json | 1 + 3 files changed, 119 insertions(+) create mode 100644 platform/commonUI/general/src/services/UTCDateProvider.js create mode 100644 platform/commonUI/general/test/services/UTCDateProviderSpec.js diff --git a/platform/commonUI/general/src/services/UTCDateProvider.js b/platform/commonUI/general/src/services/UTCDateProvider.js new file mode 100644 index 0000000000..54fcbf4737 --- /dev/null +++ b/platform/commonUI/general/src/services/UTCDateProvider.js @@ -0,0 +1,51 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define([ + 'moment' +], function ( + moment +) { + "use strict"; + + /** + * Composites multiple DateService implementations such that + * they can be used as one. + * @memberof platform/commonUI/general + * @constructor + * @implements {DateService} + */ + function UTCDateProvider() { + } + + UTCDateProvider.prototype.validate = function (text, key) { + }; + + UTCDateProvider.prototype.format = function (value, key) { + }; + + UTCDateProvider.prototype.parse = function (text, key) { + }; + + return UTCDateProvider; +}); diff --git a/platform/commonUI/general/test/services/UTCDateProviderSpec.js b/platform/commonUI/general/test/services/UTCDateProviderSpec.js new file mode 100644 index 0000000000..1a1873030d --- /dev/null +++ b/platform/commonUI/general/test/services/UTCDateProviderSpec.js @@ -0,0 +1,67 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + + +define( + ["../../src/services/UTCDateProvider", "moment"], + function (UTCDateProvider, moment) { + 'use strict'; + + describe("UTCDateProvider", function () { + var testDate, testTimestamp, dateProvider; + + beforeEach(function () { + testDate = "1977-05-25 17:30:00"; + testTimestamp = moment.utc(testDate).valueOf(); + dateProvider = new UTCDateProvider(); + }); + + it("distinguishes valid dates from invalid dates", function () { + expect(dateProvider.validate(testDate)) + .toBeTruthy(); + expect(dateProvider.validate("2015-garbage :00:00")) + .toBeFalsy(); + }); + + it("parses dates to their numeric representations", function () { + expect(dateProvider.parse(testDate)).toEqual(testTimestamp); + }); + + it("formats to text representing UTC date/times", function () { + var formatted = dateProvider.format(testTimestamp); + expect(formatted).toEqual(jasmine.any(String)); + // Use moment to verify that formatted value is equal + // to the original date/time + expect(moment.utc(formatted).valueOf()).toEqual(testTimestamp); + }); + + it("does not handle defined keys", function () { + expect(dateProvider.validate(testDate, 'someKey')) + .toBeFalsy(); + expect(dateProvider.format(testTimestamp, 'someKey')) + .toBeUndefined(); + }); + + }); + } +); diff --git a/platform/commonUI/general/test/suite.json b/platform/commonUI/general/test/suite.json index 3ab06212d4..9f80aaabc2 100644 --- a/platform/commonUI/general/test/suite.json +++ b/platform/commonUI/general/test/suite.json @@ -18,6 +18,7 @@ "directives/MCTResize", "directives/MCTScroll", "services/DateAggregator", + "services/UTCDateProvider", "services/Popup", "services/PopupService", "services/UrlService", From c882b2d4c3c14b93a147aba7ed5c47c4d544ece2 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 12:08:34 -0700 Subject: [PATCH 106/488] [Time Conductor] Implement date provider Implement UTCDateProvider sufficient to pass spec. --- .../general/src/services/UTCDateProvider.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/platform/commonUI/general/src/services/UTCDateProvider.js b/platform/commonUI/general/src/services/UTCDateProvider.js index 54fcbf4737..38b6c61b8b 100644 --- a/platform/commonUI/general/src/services/UTCDateProvider.js +++ b/platform/commonUI/general/src/services/UTCDateProvider.js @@ -28,6 +28,14 @@ define([ ) { "use strict"; + var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss", + DATE_FORMATS = [ + DATE_FORMAT, + "YYYY-MM-DD HH:mm:ss", + "YYYY-MM-DD HH:mm", + "YYYY-MM-DD" + ]; + /** * Composites multiple DateService implementations such that * they can be used as one. @@ -39,12 +47,17 @@ define([ } UTCDateProvider.prototype.validate = function (text, key) { + return key === undefined && moment.utc(text, DATE_FORMATS).isValid(); }; UTCDateProvider.prototype.format = function (value, key) { + return key === undefined ? + moment.utc(value).format(DATE_FORMAT) : + undefined; }; UTCDateProvider.prototype.parse = function (text, key) { + return key === undefined && moment.utc(text, DATE_FORMATS).valueOf(); }; return UTCDateProvider; From 0d47b7c47dc8bd186e024587f4b70b505330611b Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 12:10:12 -0700 Subject: [PATCH 107/488] [Time Conductor] Expose dateService in bundle definition --- platform/commonUI/general/bundle.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index 1aa0b1dfc1..a69c810195 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -15,6 +15,19 @@ "depends": [ "$document", "$window" ] } ], + "components": [ + { + "type": "aggregator", + "provides": "dateService", + "implementation": "services/DateAggregator.js" + }, + { + "type": "provider", + "provides": "dateService", + "implementation": "services/UTCDateProvider.js", + "priority": "fallback" + } + ], "runs": [ { "implementation": "StyleSheetLoader.js", From 117470068a01951c90151a7c8fe8fe942d42c357 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 12:17:08 -0700 Subject: [PATCH 108/488] [Time Conductor] Use dateService from TimeRangeController --- platform/commonUI/general/bundle.json | 2 +- .../src/controllers/TimeRangeController.js | 17 +++++++++------ .../controllers/TimeRangeControllerSpec.js | 21 ++++++++++++++++++- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index a69c810195..05c86cbcf7 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -66,7 +66,7 @@ { "key": "TimeRangeController", "implementation": "controllers/TimeRangeController.js", - "depends": [ "$scope", "now" ] + "depends": [ "$scope", "dateService", "now" ] }, { "key": "DateTimePickerController", diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index d4fb21be08..9ec95bac23 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -30,23 +30,28 @@ define( TICK_SPACING_PX = 150; /** + * Controller used by the `time-controller` template. * @memberof platform/commonUI/general * @constructor */ - function TimeConductorController($scope, now) { + function TimeRangeController($scope, dateService, now) { var tickCount = 2, innerMinimumSpan = 1000, // 1 second outerMinimumSpan = 1000 * 60 * 60, // 1 hour initialDragValue; + function timeSystemKey() { + return ($scope.parameters || {}).domain; + } + function formatTimestamp(ts) { - return moment.utc(ts).format(DATE_FORMAT); + return dateService.format(ts, timeSystemKey()); } function parseTimestamp(text) { - var m = moment.utc(text, DATE_FORMAT); - if (m.isValid()) { - return m.valueOf(); + var key = timeSystemKey(); + if (dateService.validate(text, key)) { + return dateService.parse(text, key); } else { throw new Error("Could not parse " + text); } @@ -297,6 +302,6 @@ define( $scope.$watch("boundsModel.end", updateEndFromText); } - return TimeConductorController; + return TimeRangeController; } ); diff --git a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js b/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js index 91d3ecb9db..30cddab922 100644 --- a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js +++ b/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js @@ -33,6 +33,7 @@ define( describe("The TimeRangeController", function () { var mockScope, + mockDateService, mockNow, controller; @@ -57,8 +58,26 @@ define( "$scope", [ "$apply", "$watch", "$watchCollection" ] ); + mockDateService = jasmine.createSpyObj( + "dateService", + [ "validate", "format", "parse" ] + ); + mockDateService.validate.andCallFake(function (text) { + return moment.utc(text).isValid(); + }); + mockDateService.parse.andCallFake(function (text) { + return moment.utc(text).valueOf(); + }); + mockDateService.format.andCallFake(function (value) { + return moment.utc(value).format("YYYY-MM-DD HH:mm:ss"); + }); mockNow = jasmine.createSpy('now'); - controller = new TimeRangeController(mockScope, mockNow); + + controller = new TimeRangeController( + mockScope, + mockDateService, + mockNow + ); }); it("watches the model that was passed in", function () { From 950578f09b9762c8a60a1c199c7f61d2a31529ed Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 12:20:15 -0700 Subject: [PATCH 109/488] [Time Conductor] Hide datetime pickers for non-UTC domains --- .../general/res/templates/controls/time-controller.html | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/platform/commonUI/general/res/templates/controls/time-controller.html b/platform/commonUI/general/res/templates/controls/time-controller.html index 300e56c381..e0d7804b3b 100644 --- a/platform/commonUI/general/res/templates/controls/time-controller.html +++ b/platform/commonUI/general/res/templates/controls/time-controller.html @@ -29,7 +29,10 @@ ng-model="boundsModel.start" ng-class="{ error: !boundsModel.startValid }"> - + +
- +
From c0fda5b5729116798392f7060fad7ad10b86bcc7 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 12:41:46 -0700 Subject: [PATCH 110/488] [Time Conductor] Add JSDoc Add typedefs relevant to the date aggregator; in particular, document properties used to determine how to format timestamps associated with a telemetry point. --- .../general/src/services/DateAggregator.js | 4 +- platform/telemetry/src/TelemetryCapability.js | 58 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/platform/commonUI/general/src/services/DateAggregator.js b/platform/commonUI/general/src/services/DateAggregator.js index 3c2762d1bd..ee01929b27 100644 --- a/platform/commonUI/general/src/services/DateAggregator.js +++ b/platform/commonUI/general/src/services/DateAggregator.js @@ -31,7 +31,9 @@ define([ * Formats dates for display and parses dates from user input, * varying by a chosen time system. * - * Time systems are typically domain keys from telemetry metadata. + * Time systems are typically specified as `system` properties + * of domains in {@link TelemetryDomainMetadata}. + * * If omitted/left undefined, the time system is presumed to be UTC time, * with its numeric interpretation being milliseconds since the * start of 1970. diff --git a/platform/telemetry/src/TelemetryCapability.js b/platform/telemetry/src/TelemetryCapability.js index 1fbd12a691..d89b3cd3bf 100644 --- a/platform/telemetry/src/TelemetryCapability.js +++ b/platform/telemetry/src/TelemetryCapability.js @@ -36,6 +36,64 @@ define( getRangeValue: ZERO }; + /** + * Provides metadata about telemetry associated with a + * given domain object. + * + * @typedef TelemetryMetadata + * @property {string} source the machine-readable identifier for + * the source of telemetry data for this object; used by + * {@link TelemetryService} implementations to determine + * whether or not they provide data for this object. + * @property {string} key the machine-readable identifier for + * telemetry data associated with this specific object, + * within that `source`. + * @property {TelemetryDomainMetadata[]} domains supported domain + * options for telemetry data associated with this object, + * to use in interpreting a {@link TelemetrySeries} + * @property {TelemetryRangeMetadata[]} ranges supported range + * options for telemetry data associated with this object, + * to use in interpreting a {@link TelemetrySeries} + */ + + /** + * Provides metadata about range options within a telemetry series. + * Range options describe distinct properties within any given datum + * of a telemetry series; for instance, a telemetry series containing + * both raw and uncalibrated values may provide separate ranges for + * each. + * + * @typedef TelemetryRangeMetadata + * @property {string} key machine-readable identifier for this range + * @property {string} name human-readable name for this range + * @property {string} [units] human-readable units for this range + * @property {string} [format] data format for this range; usually, + * one of `number`, or `string`. If `undefined`, + * should presume to be a `number`. Custom formats + * may be indicated here. + */ + + /** + * Provides metadata about domain options within a telemetry series. + * Domain options describe distinct properties within any given datum + * of a telemtry series; for instance, a telemetry series containing + * both spacecraft event time and earth received times may provide + * separate domains for each. + * + * Domains are typically used to represent timestamps in a telemetry + * series, but more generally may express any property which will + * have unique values for each datum in a series. It is this property + * which makes domains distinct from ranges, as it makes these values + * appropriate and meaningful for use to sort and bound a series. + * + * @typedef TelemetryDomainMetadata + * @property {string} key machine-readable identifier for this range + * @property {string} name human-readable name for this range + * @property {string} [system] machine-readable identifier for the + * time/date system associated with this domain; + * used by {@link DateService} + */ + /** * A telemetry capability provides a means of requesting telemetry * for a specific object, and for unwrapping the response (to get From 9bc4327c597261ead0c7becf35d3cebfbddc2752 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 12:51:10 -0700 Subject: [PATCH 111/488] [Time Conductor] Add non-time-like domain Add a non-time-like domain to sine wave generator telemetry, to support integration of custom domain formatting into time conductor. --- example/generator/bundle.json | 10 ++++ example/generator/src/SinewaveDateProvider.js | 55 +++++++++++++++++++ .../generator/src/SinewaveTelemetrySeries.js | 3 + 3 files changed, 68 insertions(+) create mode 100644 example/generator/src/SinewaveDateProvider.js diff --git a/example/generator/bundle.json b/example/generator/bundle.json index cdb4736957..fc18f31193 100644 --- a/example/generator/bundle.json +++ b/example/generator/bundle.json @@ -8,6 +8,11 @@ "type": "provider", "provides": "telemetryService", "depends": [ "$q", "$timeout" ] + }, + { + "implementation": "SinewaveDateProvider.js", + "type": "provider", + "provides": "dateService" } ], "capabilities": [ @@ -38,6 +43,11 @@ { "key": "yesterday", "name": "Yesterday" + }, + { + "key": "index", + "name": "Index", + "system": "generator.index" } ], "ranges": [ diff --git a/example/generator/src/SinewaveDateProvider.js b/example/generator/src/SinewaveDateProvider.js new file mode 100644 index 0000000000..9b819a8796 --- /dev/null +++ b/example/generator/src/SinewaveDateProvider.js @@ -0,0 +1,55 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define([ +], function ( +) { + "use strict"; + + /** + * Provides date-time formatting for the Sine Wave Generator's + * `index` domain; demonstrates how to support domains which should + * not necessarily be formatted as UTC dates. + * @memberof example/generator + * @constructor + * @implements {DateService} + */ + function SinewaveDateProvider() { + } + + SinewaveDateProvider.prototype.validate = function (text, key) { + return key === 'generator.index' && /^#\d+$/.test(text); + }; + + SinewaveDateProvider.prototype.format = function (value, key) { + return key === 'generator.index' ? + ('#' + Math.floor(value)) : + undefined; + }; + + SinewaveDateProvider.prototype.parse = function (text, key) { + return key === 'generator.index' && parseInt(key.substring(1), 10); + }; + + return SinewaveDateProvider; +}); diff --git a/example/generator/src/SinewaveTelemetrySeries.js b/example/generator/src/SinewaveTelemetrySeries.js index 1e84034766..17084a07da 100644 --- a/example/generator/src/SinewaveTelemetrySeries.js +++ b/example/generator/src/SinewaveTelemetrySeries.js @@ -58,6 +58,9 @@ define( }; generatorData.getDomainValue = function (i, domain) { + if (domain === 'index') { + return i; + } return (i + offset) * 1000 + firstTime * 1000 - (domain === 'yesterday' ? ONE_DAY : 0); }; From e8d7093eb57393f2c9e58925fa3c48b29987e54e Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 13:07:04 -0700 Subject: [PATCH 112/488] [Time Conductor] Use time systems from time conductor --- example/generator/bundle.json | 12 ++++++++++ .../src/controllers/TimeRangeController.js | 4 ++-- platform/features/conductor/bundle.json | 4 ++-- .../res/templates/time-conductor.html | 1 + .../conductor/src/ConductorRepresenter.js | 23 +++++++++++-------- .../src/ConductorTelemetryDecorator.js | 2 +- .../features/conductor/src/TimeConductor.js | 20 ++++++++-------- 7 files changed, 42 insertions(+), 24 deletions(-) diff --git a/example/generator/bundle.json b/example/generator/bundle.json index fc18f31193..b2050e07e9 100644 --- a/example/generator/bundle.json +++ b/example/generator/bundle.json @@ -73,6 +73,18 @@ } ] } + ], + "constants": [ + { + "key": "TIME_CONDUCTOR_DOMAINS", + "value": [ + { "key": "time", "name": "Time" }, + { "key": "yesterday", "name": "Yesterday" }, + { "key": "index", "name": "Index", "system": "generator.index" } + ], + "priority": -1, + "comment": "Placeholder; to be replaced by inspection of available domains." + } ] } } diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index 9ec95bac23..5f2f0fa3e6 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -41,7 +41,7 @@ define( initialDragValue; function timeSystemKey() { - return ($scope.parameters || {}).domain; + return ($scope.parameters || {}).system; } function formatTimestamp(ts) { @@ -57,7 +57,7 @@ define( } } - // From 0.0-1.0 to "0%"-"1%" + // From 0.0-1.0 to "0%"-"100%" function toPercent(p) { return (100 * p) + "%"; } diff --git a/platform/features/conductor/bundle.json b/platform/features/conductor/bundle.json index de903cfb93..10d3f32665 100644 --- a/platform/features/conductor/bundle.json +++ b/platform/features/conductor/bundle.json @@ -36,9 +36,9 @@ { "key": "TIME_CONDUCTOR_DOMAINS", "value": [ - { "key": "time", "name": "Time" }, - { "key": "yesterday", "name": "Yesterday" } + { "key": "time", "name": "Time" } ], + "priority": "fallback", "comment": "Placeholder; to be replaced by inspection of available domains." } ] diff --git a/platform/features/conductor/res/templates/time-conductor.html b/platform/features/conductor/res/templates/time-conductor.html index 4126652d5b..16cc6296c8 100644 --- a/platform/features/conductor/res/templates/time-conductor.html +++ b/platform/features/conductor/res/templates/time-conductor.html @@ -1,4 +1,5 @@ ", + "", "" ].join(''), THROTTLE_MS = 200, @@ -74,11 +77,12 @@ define( broadcastBounds; // Combine start/end times into a single object - function bounds(start, end) { + function bounds() { + var domain = conductor.domain(); return { start: conductor.displayStart(), end: conductor.displayEnd(), - domain: conductor.domain() + domain: domain && domain.key }; } @@ -97,12 +101,10 @@ define( } function updateDomain(value) { - conductor.domain(value); - repScope.$broadcast('telemetry:display:bounds', bounds( - conductor.displayStart(), - conductor.displayEnd(), - conductor.domain() - )); + var newDomain = conductor.domain(value); + conductorScope.parameters.system = + newDomain && newDomain.system; + repScope.$broadcast('telemetry:display:bounds', bounds()); } // telemetry domain metadata -> option for a select control @@ -130,7 +132,8 @@ define( { outer: bounds(), inner: bounds() }; conductorScope.ngModel.options = conductor.domainOptions().map(makeOption); - conductorScope.ngModel.domain = conductor.domain(); + conductorScope.ngModel.domain = (conductor.domain() || {}).key; + conductorScope.parameters = {}; conductorScope .$watch('ngModel.conductor.inner.start', updateConductorInner); diff --git a/platform/features/conductor/src/ConductorTelemetryDecorator.js b/platform/features/conductor/src/ConductorTelemetryDecorator.js index ab2d958d7e..f359199ce2 100644 --- a/platform/features/conductor/src/ConductorTelemetryDecorator.js +++ b/platform/features/conductor/src/ConductorTelemetryDecorator.js @@ -51,7 +51,7 @@ define( request = request || {}; request.start = start; request.end = end; - request.domain = domain; + request.domain = domain && domain.key; return request; } diff --git a/platform/features/conductor/src/TimeConductor.js b/platform/features/conductor/src/TimeConductor.js index 0fa0403fd9..400cb06f97 100644 --- a/platform/features/conductor/src/TimeConductor.js +++ b/platform/features/conductor/src/TimeConductor.js @@ -43,7 +43,7 @@ define( function TimeConductor(start, end, domains) { this.range = { start: start, end: end }; this.domains = domains; - this.activeDomain = domains[0].key; + this.activeDomain = domains[0]; } /** @@ -73,7 +73,7 @@ define( /** * Get available domain options which can be used to bound time * selection. - * @returns {TelemetryDomain[]} available domains + * @returns {TelemetryDomainMetadata[]} available domains */ TimeConductor.prototype.domainOptions = function () { return this.domains; @@ -82,19 +82,21 @@ define( /** * Get or set (if called with an argument) the active domain. * @param {string} [key] the key identifying the domain choice - * @returns {TelemetryDomain} the active telemetry domain + * @returns {TelemetryDomainMetadata} the active telemetry domain */ TimeConductor.prototype.domain = function (key) { - function matchesKey(domain) { - return domain.key === key; - } + var i; if (arguments.length > 0) { - if (!this.domains.some(matchesKey)) { - throw new Error("Unknown domain " + key); + for (i = 0; i < this.domains.length; i += 1) { + if (this.domains[i].key === key) { + return (this.activeDomain = this.domains[i]); + } } - this.activeDomain = key; + + throw new Error("Unknown domain " + key); } + return this.activeDomain; }; From 4ff03b081dac5d7e6dcec577eae7acd74d0674e0 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 13:08:33 -0700 Subject: [PATCH 113/488] [Time Conductor] Suppress date picker for non-UTC time systems --- .../general/res/templates/controls/time-controller.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/commonUI/general/res/templates/controls/time-controller.html b/platform/commonUI/general/res/templates/controls/time-controller.html index e0d7804b3b..87b6c682b2 100644 --- a/platform/commonUI/general/res/templates/controls/time-controller.html +++ b/platform/commonUI/general/res/templates/controls/time-controller.html @@ -30,7 +30,7 @@ ng-class="{ error: !boundsModel.startValid }"> @@ -55,7 +55,7 @@ ng-class="{ error: !boundsModel.endValid }"> From 0628398b01ba4ac9c97eb05488d64ee4ead4891e Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 22 Oct 2015 13:30:34 -0700 Subject: [PATCH 114/488] Moved notifications examples from 'testing' to examples --- bundles.json | 3 +-- {testing/dialogTest => example/notifications}/bundle.json | 0 .../notifications}/res/dialog-launch.html | 0 .../notifications}/res/notification-launch.html | 0 .../notifications}/src/DialogLaunchController.js | 0 .../notifications}/src/DialogLaunchIndicator.js | 0 .../notifications}/src/NotificationLaunchController.js | 0 .../notifications}/src/NotificationLaunchIndicator.js | 0 8 files changed, 1 insertion(+), 2 deletions(-) rename {testing/dialogTest => example/notifications}/bundle.json (100%) rename {testing/dialogTest => example/notifications}/res/dialog-launch.html (100%) rename {testing/dialogTest => example/notifications}/res/notification-launch.html (100%) rename {testing/dialogTest => example/notifications}/src/DialogLaunchController.js (100%) rename {testing/dialogTest => example/notifications}/src/DialogLaunchIndicator.js (100%) rename {testing/dialogTest => example/notifications}/src/NotificationLaunchController.js (100%) rename {testing/dialogTest => example/notifications}/src/NotificationLaunchIndicator.js (100%) diff --git a/bundles.json b/bundles.json index c85b681bf5..7119dc690a 100644 --- a/bundles.json +++ b/bundles.json @@ -31,6 +31,5 @@ "example/imagery", "example/eventGenerator", "example/generator", - - "testing/dialogTest" + "example/notifications" ] diff --git a/testing/dialogTest/bundle.json b/example/notifications/bundle.json similarity index 100% rename from testing/dialogTest/bundle.json rename to example/notifications/bundle.json diff --git a/testing/dialogTest/res/dialog-launch.html b/example/notifications/res/dialog-launch.html similarity index 100% rename from testing/dialogTest/res/dialog-launch.html rename to example/notifications/res/dialog-launch.html diff --git a/testing/dialogTest/res/notification-launch.html b/example/notifications/res/notification-launch.html similarity index 100% rename from testing/dialogTest/res/notification-launch.html rename to example/notifications/res/notification-launch.html diff --git a/testing/dialogTest/src/DialogLaunchController.js b/example/notifications/src/DialogLaunchController.js similarity index 100% rename from testing/dialogTest/src/DialogLaunchController.js rename to example/notifications/src/DialogLaunchController.js diff --git a/testing/dialogTest/src/DialogLaunchIndicator.js b/example/notifications/src/DialogLaunchIndicator.js similarity index 100% rename from testing/dialogTest/src/DialogLaunchIndicator.js rename to example/notifications/src/DialogLaunchIndicator.js diff --git a/testing/dialogTest/src/NotificationLaunchController.js b/example/notifications/src/NotificationLaunchController.js similarity index 100% rename from testing/dialogTest/src/NotificationLaunchController.js rename to example/notifications/src/NotificationLaunchController.js diff --git a/testing/dialogTest/src/NotificationLaunchIndicator.js b/example/notifications/src/NotificationLaunchIndicator.js similarity index 100% rename from testing/dialogTest/src/NotificationLaunchIndicator.js rename to example/notifications/src/NotificationLaunchIndicator.js From b98c1cdfe8dbdaa8c1ea273b1389e707fe93622e Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 22 Oct 2015 13:33:43 -0700 Subject: [PATCH 115/488] [Frontend] Manual re-do of collapse/expand panes open #90 Renamed BrowseTreeController to PaneController; Ensuring mobile functionality parity; --- platform/commonUI/browse/bundle.json | 4 +- .../commonUI/browse/res/templates/browse.html | 10 +- .../res/templates/browse/object-header.html | 2 +- .../commonUI/browse/src/BrowseController.js | 5 + ...wseTreeController.js => PaneController.js} | 1 + ...ontrollerSpec.js => PaneControllerSpec.js} | 8 +- platform/commonUI/browse/test/suite.json | 2 +- .../general/res/sass/mobile/_layout.scss | 22 +- .../res/sass/user-environ/_layout.scss | 50 +++++ .../espresso/res/css/theme-espresso.css | 209 +++++++----------- .../themes/snow/res/css/theme-snow.css | 209 +++++++----------- 11 files changed, 247 insertions(+), 275 deletions(-) rename platform/commonUI/browse/src/{BrowseTreeController.js => PaneController.js} (98%) rename platform/commonUI/browse/test/{BrowseTreeControllerSpec.js => PaneControllerSpec.js} (95%) diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json index e3284d99b5..156ba7ec6a 100644 --- a/platform/commonUI/browse/bundle.json +++ b/platform/commonUI/browse/bundle.json @@ -26,8 +26,8 @@ ] }, { - "key": "BrowseTreeController", - "implementation": "BrowseTreeController.js", + "key": "PaneController", + "implementation": "PaneController.js", "priority": "preferred", "depends": [ "$scope", "agentService" ] }, diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 9a1be7e771..5f96c98ed5 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -23,8 +23,8 @@
+ ng-controller="PaneController as treePane" + ng-class="treePane.visible() ? 'browse-showtree' : 'browse-hidetree'">
@@ -58,9 +58,9 @@ key="'browse-object'">
-
m
+ m
diff --git a/platform/commonUI/browse/res/templates/browse/object-header.html b/platform/commonUI/browse/res/templates/browse/object-header.html index 7ea43a6b9f..79fca1b76c 100644 --- a/platform/commonUI/browse/res/templates/browse/object-header.html +++ b/platform/commonUI/browse/res/templates/browse/object-header.html @@ -19,7 +19,7 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> -
+
{{type.getGlyph()}} diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js index e0c00d9521..9674cbb214 100644 --- a/platform/commonUI/browse/src/BrowseController.js +++ b/platform/commonUI/browse/src/BrowseController.js @@ -154,6 +154,11 @@ define( navigationService.removeListener(setNavigation); }); + // Models for panes + $scope.paneModelTree = {}; + $scope.paneModelInspect = {}; + + } return BrowseController; diff --git a/platform/commonUI/browse/src/BrowseTreeController.js b/platform/commonUI/browse/src/PaneController.js similarity index 98% rename from platform/commonUI/browse/src/BrowseTreeController.js rename to platform/commonUI/browse/src/PaneController.js index 31875de4fa..4436493eb6 100644 --- a/platform/commonUI/browse/src/BrowseTreeController.js +++ b/platform/commonUI/browse/src/PaneController.js @@ -63,6 +63,7 @@ define( */ BrowseTreeController.prototype.toggle = function () { this.state = !this.state; + console.log('tree toggled: ' + this.state); }; /** diff --git a/platform/commonUI/browse/test/BrowseTreeControllerSpec.js b/platform/commonUI/browse/test/PaneControllerSpec.js similarity index 95% rename from platform/commonUI/browse/test/BrowseTreeControllerSpec.js rename to platform/commonUI/browse/test/PaneControllerSpec.js index 077855febf..f7d51f1714 100644 --- a/platform/commonUI/browse/test/BrowseTreeControllerSpec.js +++ b/platform/commonUI/browse/test/PaneControllerSpec.js @@ -22,11 +22,11 @@ /*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ define( - ["../src/BrowseTreeController"], - function (BrowseTreeController) { + ["../src/PaneController"], + function (PaneController) { 'use strict'; - describe("The BrowseTreeController", function () { + describe("The PaneController", function () { var mockScope, mockAgentService, mockDomainObjects, @@ -35,7 +35,7 @@ define( // We want to reinstantiate for each test case // because device state can influence constructor-time behavior function instantiateController() { - return new BrowseTreeController( + return new PaneController( mockScope, mockAgentService ); diff --git a/platform/commonUI/browse/test/suite.json b/platform/commonUI/browse/test/suite.json index aa73dd358d..b9292b6ef1 100644 --- a/platform/commonUI/browse/test/suite.json +++ b/platform/commonUI/browse/test/suite.json @@ -1,7 +1,7 @@ [ "BrowseController", "BrowseObjectController", - "BrowseTreeController", + "PaneController", "MenuArrowController", "creation/CreateAction", "creation/CreateActionProvider", diff --git a/platform/commonUI/general/res/sass/mobile/_layout.scss b/platform/commonUI/general/res/sass/mobile/_layout.scss index 17ff8c4213..cf252f6205 100644 --- a/platform/commonUI/general/res/sass/mobile/_layout.scss +++ b/platform/commonUI/general/res/sass/mobile/_layout.scss @@ -55,25 +55,19 @@ left: $bodyMargin !important; } - // When the tree is hidden, these are the + +// When the tree is hidden, these are the // classes used for the left menu and the // right representation. .browse-hidetree { - @include user-select(none); // Sets the left tree menu when the tree // is hidden. .pane.left.treeview { - opacity: 0; right: 100% !important; width: auto !important; overflow-y: hidden; overflow-x: hidden; } - // Sets the right represenation when - // the tree is hidden. - .pane.right-repr { - left: 0 !important; - } } .browse-showtree { @@ -82,14 +76,14 @@ // causing cut/copy/paste menu to // not appear. Should me moved in // future to properly work - @include user-select(none); + //@include user-select(none); // Sets the left tree menu when the tree is shown. .pane.left.treeview { - @include trans-prop-nice(opacity, .4s); + //@include trans-prop-nice(opacity, .4s); @include background-image(linear-gradient(90deg, rgba(black, 0) 98%, rgba(black, 0.3) 100%)); - opacity: 1; - display: block !important; + //opacity: 1; + //display: block !important; //width: auto !important; // CH CO right: auto !important; width: $proporMenuWithView !important; @@ -97,10 +91,6 @@ // Sets the right representation when the tree is shown. .pane.right-repr { left: $proporMenuWithView !important; - //width: auto !important; - - //left: 0 !important; - //transform: translateX($proporMenuWithView); } } diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 0d8983c182..2f49367de4 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -343,4 +343,54 @@ @include webkitProp(flex, '1 1 0'); padding-right: $interiorMarginLg; } +} + +// MOVED from mobile/_layout.scss +// When the tree is hidden, these are the +// classes used for the left menu and the +// right representation. +.browse-hidetree { + @include user-select(none); + // Sets the left tree menu when the tree + // is hidden. + .pane.left.treeview { + opacity: 0; + //right: 100% !important; + //width: auto !important; + //overflow-y: hidden; + //overflow-x: hidden; + } + // Sets the right represenation when + // the tree is hidden. + .pane.right-repr { + left: 0 !important; + } +} + +.browse-showtree { + // NOTE: DISABLED SELECTION + // Selection disabled in both panes + // causing cut/copy/paste menu to + // not appear. Should me moved in + // future to properly work + //@include user-select(none); + + // Sets the left tree menu when the tree is shown. + .pane.left.treeview { + @include trans-prop-nice(opacity, .4s); + //@include background-image(linear-gradient(90deg, rgba(black, 0) 98%, rgba(black, 0.3) 100%)); + opacity: 1; + //display: block !important; + //width: auto !important; // CH CO + //right: auto; + //width: $proporMenuWithView; + } + // Sets the right representation when the tree is shown. + .pane.right-repr { + //left: $proporMenuWithView; + //width: auto !important; + + //left: 0 !important; + //transform: translateX($proporMenuWithView); + } } \ No newline at end of file diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index fe402962fd..6d8a6137d3 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -3651,6 +3651,35 @@ span.req { -webkit-flex: 1 1 0; padding-right: 10px; } +/* line 352, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-hidetree { + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; } + /* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ + .browse-hidetree .pane.left.treeview { + opacity: 0; } + /* line 365, ../../../../general/res/sass/user-environ/_layout.scss */ + .browse-hidetree .pane.right-repr { + left: 0 !important; } + +/* line 379, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-showtree .pane.left.treeview { + -moz-transition-property: opacity; + -o-transition-property: opacity; + -webkit-transition-property: opacity; + transition-property: opacity; + -moz-transition-duration: 0.4s; + -o-transition-duration: 0.4s; + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + opacity: 1; } + /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -3720,93 +3749,63 @@ span.req { bottom: 10px !important; left: 10px !important; } - /* line 61, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-hidetree { - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; } - /* line 65, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-hidetree .pane.left.treeview { - opacity: 0; - right: 100% !important; - width: auto !important; - overflow-y: hidden; - overflow-x: hidden; } - /* line 74, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-hidetree .pane.right-repr { - left: 0 !important; } + /* line 65, ../../../../general/res/sass/mobile/_layout.scss */ + .browse-hidetree .pane.left.treeview { + right: 100% !important; + width: auto !important; + overflow-y: hidden; + overflow-x: hidden; } - /* line 79, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree { - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; } - /* line 88, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.left.treeview { - -moz-transition-property: opacity; - -o-transition-property: opacity; - -webkit-transition-property: opacity; - transition-property: opacity; - -moz-transition-duration: 0.4s; - -o-transition-duration: 0.4s; - -webkit-transition-duration: 0.4s; - transition-duration: 0.4s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSI5OCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); - background-size: 100%; - background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); - background-image: -webkit-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); - background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); - opacity: 1; - display: block !important; - right: auto !important; - width: 40% !important; } - /* line 98, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.right-repr { - left: 40% !important; } + /* line 82, ../../../../general/res/sass/mobile/_layout.scss */ + .browse-showtree .pane.left.treeview { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSI5OCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); + background-image: -webkit-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); + background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); + right: auto !important; + width: 40% !important; } + /* line 92, ../../../../general/res/sass/mobile/_layout.scss */ + .browse-showtree .pane.right-repr { + left: 40% !important; } - /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 97, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-menu-icon { font-size: 110%; position: absolute; top: 12px; left: 10px; } - /* line 114, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 104, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar { left: 30px !important; } - /* line 117, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .context-available { opacity: 1 !important; } - /* line 120, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher { margin-right: 0 !important; } - /* line 122, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 112, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher .title-label { display: none; } - /* line 129, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 119, ../../../../general/res/sass/mobile/_layout.scss */ .tree-holder { overflow-x: hidden !important; } - /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-disable-select { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 128, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-hide, .mobile-hide-important { display: none !important; } - /* line 143, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-hide { pointer-events: none; -moz-transition-property: opacity; @@ -3823,7 +3822,7 @@ span.req { transition-timing-function: ease-in-out; opacity: 0; } - /* line 148, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-unhide { pointer-events: all; -moz-transition-property: opacity; @@ -3840,19 +3839,19 @@ span.req { transition-timing-function: ease-in-out; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 157, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.left.treeview { width: 90% !important; } - /* line 160, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr { left: 0 !important; transform: translateX(90%); -webkit-transform: translateX(90%); } - /* line 163, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 171, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ .desktop-hide { display: none; } } /***************************************************************************** @@ -4142,32 +4141,10 @@ span.req { height: auto; max-height: 100%; position: relative; } - /* line 228, ../../../../general/res/sass/search/_search.scss */ + /* line 226, ../../../../general/res/sass/search/_search.scss */ .search .search-scroll .load-icon { - position: relative; - /* &.loading { - pointer-events: none; - margin-left: $leftMargin; - - .title-label { - // Text styling - font-style: italic; - font-size: .9em; - opacity: 0.5; - - // Text positioning - margin-left: $iconWidth + $leftMargin; - line-height: 24px; - } - .wait-spinner { - margin-left: $leftMargin; - } - } - - &:not(.loading) { - cursor: pointer; - }*/ } - /* line 255, ../../../../general/res/sass/search/_search.scss */ + position: relative; } + /* line 230, ../../../../general/res/sass/search/_search.scss */ .search .search-scroll .load-more-button { margin-top: 5px 0; font-size: 0.8em; @@ -4438,21 +4415,7 @@ ul.tree { height: 1.5rem; line-height: 1.5rem; margin-bottom: 3px; - position: relative; - /* - &.loading { - pointer-events: none; - .label { - opacity: 0.5; - .title-label { - font-style: italic; - } - } - .wait-spinner { - margin-left: 14px; - } - } - */ } + position: relative; } /* line 48, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .view-control, .search-result-item .view-control { @@ -4499,7 +4462,7 @@ ul.tree { .search-result-item .label .type-icon .icon.l-icon-alert { position: absolute; z-index: 2; } - /* line 90, ../../../../general/res/sass/tree/_tree.scss */ + /* line 89, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label .type-icon .icon.l-icon-alert, .search-result-item .label .type-icon .icon.l-icon-alert { color: #ff3c00; @@ -4509,7 +4472,7 @@ ul.tree { width: 8px; top: 1px; right: -2px; } - /* line 96, ../../../../general/res/sass/tree/_tree.scss */ + /* line 95, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label .type-icon .icon.l-icon-link, .search-result-item .label .type-icon .icon.l-icon-link { color: #49dedb; @@ -4519,7 +4482,7 @@ ul.tree { width: 8px; left: -3px; bottom: 0px; } - /* line 104, ../../../../general/res/sass/tree/_tree.scss */ + /* line 103, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label .title-label, .search-result-item .label .title-label { overflow: hidden; @@ -4535,47 +4498,47 @@ ul.tree { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - /* line 130, ../../../../general/res/sass/tree/_tree.scss */ + /* line 113, ../../../../general/res/sass/tree/_tree.scss */ .tree-item.selected, .search-result-item.selected { background: #006080; color: #cccccc; } - /* line 133, ../../../../general/res/sass/tree/_tree.scss */ + /* line 116, ../../../../general/res/sass/tree/_tree.scss */ .tree-item.selected .view-control, .search-result-item.selected .view-control { color: rgba(255, 255, 255, 0.3); } - /* line 136, ../../../../general/res/sass/tree/_tree.scss */ + /* line 119, ../../../../general/res/sass/tree/_tree.scss */ .tree-item.selected .label .type-icon, .search-result-item.selected .label .type-icon { color: #cccccc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 144, ../../../../general/res/sass/tree/_tree.scss */ + /* line 127, ../../../../general/res/sass/tree/_tree.scss */ .tree-item:not(.selected):hover, .search-result-item:not(.selected):hover { background: rgba(153, 153, 153, 0.1); color: #cccccc; } - /* line 150, ../../../../general/res/sass/tree/_tree.scss */ + /* line 130, ../../../../general/res/sass/tree/_tree.scss */ .tree-item:not(.selected):hover .icon, .search-result-item:not(.selected):hover .icon { color: #33ccff; } } - /* line 157, ../../../../general/res/sass/tree/_tree.scss */ + /* line 137, ../../../../general/res/sass/tree/_tree.scss */ .tree-item:not(.loading), .search-result-item:not(.loading) { cursor: pointer; } - /* line 161, ../../../../general/res/sass/tree/_tree.scss */ + /* line 141, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .context-trigger, .search-result-item .context-trigger { top: -1px; position: absolute; right: 3px; } - /* line 167, ../../../../general/res/sass/tree/_tree.scss */ + /* line 146, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .context-trigger .invoke-menu, .search-result-item .context-trigger .invoke-menu { font-size: 0.75em; height: 0.9rem; line-height: 0.9rem; } -/* line 176, ../../../../general/res/sass/tree/_tree.scss */ +/* line 155, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label { left: 15px; } diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 69af739435..ad5e3a07c2 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -20,7 +20,7 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* line 5, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, @@ -41,38 +41,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } @@ -3598,6 +3598,35 @@ span.req { -webkit-flex: 1 1 0; padding-right: 10px; } +/* line 352, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-hidetree { + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; } + /* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ + .browse-hidetree .pane.left.treeview { + opacity: 0; } + /* line 365, ../../../../general/res/sass/user-environ/_layout.scss */ + .browse-hidetree .pane.right-repr { + left: 0 !important; } + +/* line 379, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-showtree .pane.left.treeview { + -moz-transition-property: opacity; + -o-transition-property: opacity; + -webkit-transition-property: opacity; + transition-property: opacity; + -moz-transition-duration: 0.4s; + -o-transition-duration: 0.4s; + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + opacity: 1; } + /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -3667,93 +3696,63 @@ span.req { bottom: 10px !important; left: 10px !important; } - /* line 61, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-hidetree { - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; } - /* line 65, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-hidetree .pane.left.treeview { - opacity: 0; - right: 100% !important; - width: auto !important; - overflow-y: hidden; - overflow-x: hidden; } - /* line 74, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-hidetree .pane.right-repr { - left: 0 !important; } + /* line 65, ../../../../general/res/sass/mobile/_layout.scss */ + .browse-hidetree .pane.left.treeview { + right: 100% !important; + width: auto !important; + overflow-y: hidden; + overflow-x: hidden; } - /* line 79, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree { - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; } - /* line 88, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.left.treeview { - -moz-transition-property: opacity; - -o-transition-property: opacity; - -webkit-transition-property: opacity; - transition-property: opacity; - -moz-transition-duration: 0.4s; - -o-transition-duration: 0.4s; - -webkit-transition-duration: 0.4s; - transition-duration: 0.4s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSI5OCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); - background-size: 100%; - background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); - background-image: -webkit-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); - background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); - opacity: 1; - display: block !important; - right: auto !important; - width: 40% !important; } - /* line 98, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.right-repr { - left: 40% !important; } + /* line 82, ../../../../general/res/sass/mobile/_layout.scss */ + .browse-showtree .pane.left.treeview { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSI5OCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); + background-image: -webkit-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); + background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); + right: auto !important; + width: 40% !important; } + /* line 92, ../../../../general/res/sass/mobile/_layout.scss */ + .browse-showtree .pane.right-repr { + left: 40% !important; } - /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 97, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-menu-icon { font-size: 110%; position: absolute; top: 12px; left: 10px; } - /* line 114, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 104, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar { left: 30px !important; } - /* line 117, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .context-available { opacity: 1 !important; } - /* line 120, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher { margin-right: 0 !important; } - /* line 122, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 112, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher .title-label { display: none; } - /* line 129, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 119, ../../../../general/res/sass/mobile/_layout.scss */ .tree-holder { overflow-x: hidden !important; } - /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-disable-select { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 128, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-hide, .mobile-hide-important { display: none !important; } - /* line 143, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-hide { pointer-events: none; -moz-transition-property: opacity; @@ -3770,7 +3769,7 @@ span.req { transition-timing-function: ease-in-out; opacity: 0; } - /* line 148, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-unhide { pointer-events: all; -moz-transition-property: opacity; @@ -3787,19 +3786,19 @@ span.req { transition-timing-function: ease-in-out; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 157, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.left.treeview { width: 90% !important; } - /* line 160, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr { left: 0 !important; transform: translateX(90%); -webkit-transform: translateX(90%); } - /* line 163, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 171, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ .desktop-hide { display: none; } } /***************************************************************************** @@ -4089,32 +4088,10 @@ span.req { height: auto; max-height: 100%; position: relative; } - /* line 228, ../../../../general/res/sass/search/_search.scss */ + /* line 226, ../../../../general/res/sass/search/_search.scss */ .search .search-scroll .load-icon { - position: relative; - /* &.loading { - pointer-events: none; - margin-left: $leftMargin; - - .title-label { - // Text styling - font-style: italic; - font-size: .9em; - opacity: 0.5; - - // Text positioning - margin-left: $iconWidth + $leftMargin; - line-height: 24px; - } - .wait-spinner { - margin-left: $leftMargin; - } - } - - &:not(.loading) { - cursor: pointer; - }*/ } - /* line 255, ../../../../general/res/sass/search/_search.scss */ + position: relative; } + /* line 230, ../../../../general/res/sass/search/_search.scss */ .search .search-scroll .load-more-button { margin-top: 5px 0; font-size: 0.8em; @@ -4367,21 +4344,7 @@ ul.tree { height: 1.5rem; line-height: 1.5rem; margin-bottom: 3px; - position: relative; - /* - &.loading { - pointer-events: none; - .label { - opacity: 0.5; - .title-label { - font-style: italic; - } - } - .wait-spinner { - margin-left: 14px; - } - } - */ } + position: relative; } /* line 48, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .view-control, .search-result-item .view-control { @@ -4427,7 +4390,7 @@ ul.tree { .search-result-item .label .type-icon .icon.l-icon-alert { position: absolute; z-index: 2; } - /* line 90, ../../../../general/res/sass/tree/_tree.scss */ + /* line 89, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label .type-icon .icon.l-icon-alert, .search-result-item .label .type-icon .icon.l-icon-alert { color: #ff3c00; @@ -4437,7 +4400,7 @@ ul.tree { width: 8px; top: 1px; right: -2px; } - /* line 96, ../../../../general/res/sass/tree/_tree.scss */ + /* line 95, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label .type-icon .icon.l-icon-link, .search-result-item .label .type-icon .icon.l-icon-link { color: #49dedb; @@ -4447,7 +4410,7 @@ ul.tree { width: 8px; left: -3px; bottom: 0px; } - /* line 104, ../../../../general/res/sass/tree/_tree.scss */ + /* line 103, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label .title-label, .search-result-item .label .title-label { overflow: hidden; @@ -4463,47 +4426,47 @@ ul.tree { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - /* line 130, ../../../../general/res/sass/tree/_tree.scss */ + /* line 113, ../../../../general/res/sass/tree/_tree.scss */ .tree-item.selected, .search-result-item.selected { background: #1ac6ff; color: #fcfcfc; } - /* line 133, ../../../../general/res/sass/tree/_tree.scss */ + /* line 116, ../../../../general/res/sass/tree/_tree.scss */ .tree-item.selected .view-control, .search-result-item.selected .view-control { color: #fcfcfc; } - /* line 136, ../../../../general/res/sass/tree/_tree.scss */ + /* line 119, ../../../../general/res/sass/tree/_tree.scss */ .tree-item.selected .label .type-icon, .search-result-item.selected .label .type-icon { color: #fcfcfc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 144, ../../../../general/res/sass/tree/_tree.scss */ + /* line 127, ../../../../general/res/sass/tree/_tree.scss */ .tree-item:not(.selected):hover, .search-result-item:not(.selected):hover { background: rgba(102, 102, 102, 0.1); color: #333333; } - /* line 150, ../../../../general/res/sass/tree/_tree.scss */ + /* line 130, ../../../../general/res/sass/tree/_tree.scss */ .tree-item:not(.selected):hover .icon, .search-result-item:not(.selected):hover .icon { color: #0099cc; } } - /* line 157, ../../../../general/res/sass/tree/_tree.scss */ + /* line 137, ../../../../general/res/sass/tree/_tree.scss */ .tree-item:not(.loading), .search-result-item:not(.loading) { cursor: pointer; } - /* line 161, ../../../../general/res/sass/tree/_tree.scss */ + /* line 141, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .context-trigger, .search-result-item .context-trigger { top: -1px; position: absolute; right: 3px; } - /* line 167, ../../../../general/res/sass/tree/_tree.scss */ + /* line 146, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .context-trigger .invoke-menu, .search-result-item .context-trigger .invoke-menu { font-size: 0.75em; height: 0.9rem; line-height: 0.9rem; } -/* line 176, ../../../../general/res/sass/tree/_tree.scss */ +/* line 155, ../../../../general/res/sass/tree/_tree.scss */ .tree-item .label { left: 15px; } From 0053989de893b4ef9feee891e8e940a0a4825104 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 22 Oct 2015 14:14:21 -0700 Subject: [PATCH 116/488] renamed dialog and notification 'actions' to 'options' --- .../src/DialogLaunchController.js | 16 ++++++++-------- .../src/NotificationLaunchController.js | 18 +++++++++--------- .../commonUI/dialog/res/templates/message.html | 12 ++++++------ platform/commonUI/dialog/src/DialogService.js | 6 +++--- .../general/res/templates/message-banner.html | 6 +++--- .../notification/src/NotificationService.js | 10 +++++----- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/example/notifications/src/DialogLaunchController.js b/example/notifications/src/DialogLaunchController.js index 42882a0dfe..5679f990a0 100644 --- a/example/notifications/src/DialogLaunchController.js +++ b/example/notifications/src/DialogLaunchController.js @@ -52,17 +52,17 @@ define( unknownProgress: !knownProgress, unknownDuration: false, severity: MessageSeverity.INFO, - actions: [ + options: [ { label: "Cancel Operation", - action: function () { + callback: function () { $log.debug("Operation cancelled"); dialogService.dismiss(); } }, { label: "Do something else...", - action: function () { + callback: function () { $log.debug("Something else pressed"); } } @@ -97,17 +97,17 @@ define( title: "Error Dialog Example", actionText: "Something happened, and it was not good.", severity: MessageSeverity.ERROR, - actions: [ + options: [ { label: "Try Again", - action: function () { + callback: function () { $log.debug("Try Again Pressed"); dialogService.dismiss(); } }, { label: "Cancel", - action: function () { + callback: function () { $log.debug("Cancel Pressed"); dialogService.dismiss(); } @@ -130,10 +130,10 @@ define( " dialog. This dialog can be used to draw the user's" + " attention to an event.", severity: MessageSeverity.INFO, - actions: [ + options: [ { label: "OK", - action: function () { + callback: function () { $log.debug("OK Pressed"); dialogService.dismiss(); } diff --git a/example/notifications/src/NotificationLaunchController.js b/example/notifications/src/NotificationLaunchController.js index b45bbfc094..e59c009422 100644 --- a/example/notifications/src/NotificationLaunchController.js +++ b/example/notifications/src/NotificationLaunchController.js @@ -55,19 +55,19 @@ define( var actions = [ { label: "Try Again", - action: function () { + callback: function () { $log.debug("Try Again pressed"); } }, { label: "Remove", - action: function () { + callback: function () { $log.debug("Remove pressed"); } }, { label: "Cancel", - action: function () { + callback: function () { $log.debug("Cancel pressed"); } } @@ -97,13 +97,13 @@ define( title: "Example error notification " + messageCounter++, hint: "An error has occurred", severity: MessageSeverity.ERROR, - primaryAction: { + primaryOption: { label: 'Retry', - action: function() { + callback: function() { $log.info('Retry clicked'); } }, - actions: getExampleActions()}); + options: getExampleActions()}); }; /** * Launch a new notification with a severity of 'Alert'. @@ -114,13 +114,13 @@ define( title: "Alert notification " + (messageCounter++), hint: "This is an alert message", severity: MessageSeverity.ALERT, - primaryAction: { + primaryOption: { label: 'Retry', - action: function() { + callback: function() { $log.info('Retry clicked'); } }, - actions: getExampleActions()}); + options: getExampleActions()}); }; diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index ecf598daf4..6b8c5c99cc 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -19,15 +19,15 @@ ng-show="ngModel.progress !== undefined || ngModel.unknownProgress">
diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index 215d64fead..27ffa9ae8b 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -178,7 +178,7 @@ define( * A user action that can be performed from a blocking dialog. These * actions will be rendered as buttons within a blocking dialog. * - * @typedef DialogAction + * @typedef DialogOption * @property {string} label a label to be displayed as the button * text for this action * @property {function} action a function to be called when the @@ -207,12 +207,12 @@ define( * impossible to provide an estimate for. Providing a true value for * this attribute will indicate to the user that the progress and * duration cannot be estimated. - * @property {DialogAction} primaryAction an action that will + * @property {DialogOption} primaryOption an action that will * be added to the dialog as a button. The primary action can be * used as the suggested course of action for the user. Making it * distinct from other actions allows it to be styled differently, * and treated preferentially in banner mode. - * @property {DialogAction[]} actions a list of actions that will + * @property {DialogOption[]} options a list of actions that will * be added to the dialog as buttons. */ diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html index 629fc1080d..0af92aeba5 100644 --- a/platform/commonUI/general/res/templates/message-banner.html +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -14,10 +14,10 @@ ng-model="active.notification"> - diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 411e3727cb..c11b92c651 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -37,13 +37,13 @@ define( "use strict"; /** - * A representation of a user action. Actions are provided to + * A representation of a user action. Options are provided to * dialogs and notifications and are shown as buttons. * - * @typedef {object} NotificationAction + * @typedef {object} NotificationOption * @property {string} label the label to appear on the button for * this action - * @property {function} action a callback function to be invoked + * @property {function} callback a callback function to be invoked * when the button is clicked */ @@ -67,12 +67,12 @@ define( * be automatically minimized or dismissed (depending on severity). * Additionally, if the provided value is a number, it will be used * as the delay period before being dismissed. - * @property {NotificationAction} primaryAction the default user + * @property {NotificationOption} primaryOption the default user * response to * this message. Will be represented as a button with the provided * label and action. May be used by banner notifications to display * only the most important option to users. - * @property {NotificationAction[]} actions any additional + * @property {NotificationOption[]} options any additional * actions the user can take. Will be represented as additional buttons * that may or may not be available from a banner. */ From 4f27104663c4d3c929e665ca220fc9f97644aacb Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 22 Oct 2015 14:26:22 -0700 Subject: [PATCH 117/488] [Frontend] Manual re-do of collapse/expand panes open #90 Implementing PaneController; Added inspection pane and toggle button in browse-object.html; --- .../browse/res/templates/browse-object.html | 21 ++++-- .../commonUI/browse/res/templates/browse.html | 20 +++--- .../commonUI/browse/src/BrowseController.js | 6 -- .../commonUI/general/res/sass/_constants.scss | 3 +- .../res/sass/user-environ/_layout.scss | 15 +++-- .../espresso/res/css/theme-espresso.css | 65 ++++++++++--------- .../themes/snow/res/css/theme-snow.css | 65 ++++++++++--------- 7 files changed, 111 insertions(+), 84 deletions(-) diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html index 3bd6138da6..8e980581fe 100644 --- a/platform/commonUI/browse/res/templates/browse-object.html +++ b/platform/commonUI/browse/res/templates/browse-object.html @@ -41,9 +41,22 @@
+ +
+ + + F +
- - + + +
+ Inspector goes here +
+
diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 5f96c98ed5..235585de64 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -23,9 +23,10 @@
- + ng-controller="PaneController as modelPaneTree" + ng-class="modelPaneTree.visible() ? 'browse-showtree' : 'browse-hidetree'"> +
- +
-
+ m +
+ key="'browse-object'" + ng-model="modelPaneInspect">
- m
diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js index 9674cbb214..8c032f7de3 100644 --- a/platform/commonUI/browse/src/BrowseController.js +++ b/platform/commonUI/browse/src/BrowseController.js @@ -153,12 +153,6 @@ define( $scope.$on("$destroy", function () { navigationService.removeListener(setNavigation); }); - - // Models for panes - $scope.paneModelTree = {}; - $scope.paneModelInspect = {}; - - } return BrowseController; diff --git a/platform/commonUI/general/res/sass/_constants.scss b/platform/commonUI/general/res/sass/_constants.scss index 918ace8c0f..6cc8312a3c 100644 --- a/platform/commonUI/general/res/sass/_constants.scss +++ b/platform/commonUI/general/res/sass/_constants.scss @@ -42,7 +42,8 @@ $ueFooterH: 25px; $ueColMargin: 1.5%; $ueAppLogoW: 105px; $ueEditToolBarH: 25px; -$ueBrowseLeftPaneW: 25%; +$ueBrowseLeftPaneTreeW: 25%; +$ueBrowseRightPaneInspectW: 10%; $ueEditLeftPaneW: 75%; $treeSearchInputBarH: 25px; $ueTimeControlH: (33px, 20px, 20px); diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 2f49367de4..179979bd2f 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -205,10 +205,17 @@ .browse-mode { .split-layout { - .split-pane-component.pane.left { - min-width: 150px; - max-width: 800px; - width: $ueBrowseLeftPaneW; + .split-pane-component.pane { + &.treeview.left { + min-width: 150px; + max-width: 800px; + width: $ueBrowseLeftPaneTreeW; + } + &.t-inspector.right { + min-width: 150px; + max-width: 800px; + width: $ueBrowseRightPaneInspectW; + } } } } diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 6d8a6137d3..ea4789e283 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -3513,40 +3513,45 @@ span.req { *zoom: 1; padding: 5px 0; } -/* line 208, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-mode .split-layout .split-pane-component.pane.left { +/* line 209, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-mode .split-layout .split-pane-component.pane.treeview.left { min-width: 150px; max-width: 800px; width: 25%; } +/* line 214, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-mode .split-layout .split-pane-component.pane.t-inspector.right { + min-width: 150px; + max-width: 800px; + width: 10%; } -/* line 218, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 225, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right { width: 15%; } - /* line 220, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 227, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right .pane.bottom { min-height: 50px; height: 30%; } -/* line 228, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { position: absolute; } - /* line 231, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 242, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 240, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 243, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 257, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3559,31 +3564,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 271, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 271, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 274, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 281, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 285, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 289, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 296, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 299, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3593,11 +3598,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 296, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 302, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3609,12 +3614,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 320, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 327, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3630,41 +3635,41 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 335, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 330, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 337, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 345, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 341, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 348, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 352, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 359, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 363, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.left.treeview { opacity: 0; } - /* line 365, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 372, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.right-repr { left: 0 !important; } -/* line 379, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 386, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-showtree .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index ad5e3a07c2..a2aa51e183 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -3460,40 +3460,45 @@ span.req { *zoom: 1; padding: 5px 0; } -/* line 208, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-mode .split-layout .split-pane-component.pane.left { +/* line 209, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-mode .split-layout .split-pane-component.pane.treeview.left { min-width: 150px; max-width: 800px; width: 25%; } +/* line 214, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-mode .split-layout .split-pane-component.pane.t-inspector.right { + min-width: 150px; + max-width: 800px; + width: 10%; } -/* line 218, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 225, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right { width: 15%; } - /* line 220, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 227, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right .pane.bottom { min-height: 50px; height: 30%; } -/* line 228, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { position: absolute; } - /* line 231, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 242, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 240, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 243, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 257, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3506,31 +3511,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 271, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 271, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 274, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 281, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 285, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 289, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 296, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 299, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3540,11 +3545,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 296, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 302, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3556,12 +3561,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 320, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 327, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3577,41 +3582,41 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 335, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 330, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 337, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 345, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 341, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 348, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 352, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 359, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 363, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.left.treeview { opacity: 0; } - /* line 365, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 372, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.right-repr { left: 0 !important; } -/* line 379, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 386, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-showtree .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; From 109ae3323d89192ad636e95825260f3e807d2c0d Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 22 Oct 2015 15:10:53 -0700 Subject: [PATCH 118/488] Added jsdoc and moved MessageController to dialogs from notifications --- platform/commonUI/dialog/bundle.json | 7 ++++++ .../src/MessageController.js | 15 ++++++++++- .../src/controllers/BannerController.js | 19 ++++++++++++++ platform/commonUI/notification/bundle.json | 5 ---- .../src/NotificationIndicatorController.js | 25 +++++++++++-------- 5 files changed, 55 insertions(+), 16 deletions(-) rename platform/commonUI/{notification => dialog}/src/MessageController.js (68%) diff --git a/platform/commonUI/dialog/bundle.json b/platform/commonUI/dialog/bundle.json index 80cd456c20..fb94f14b61 100644 --- a/platform/commonUI/dialog/bundle.json +++ b/platform/commonUI/dialog/bundle.json @@ -43,6 +43,13 @@ "key": "overlay", "templateUrl": "templates/overlay.html" } + ], + "controllers": [ + { + "key": "MessageController", + "implementation": "MessageController.js", + "depends": ["$scope"] + } ] } } \ No newline at end of file diff --git a/platform/commonUI/notification/src/MessageController.js b/platform/commonUI/dialog/src/MessageController.js similarity index 68% rename from platform/commonUI/notification/src/MessageController.js rename to platform/commonUI/dialog/src/MessageController.js index 390f4463d3..595f429f9e 100644 --- a/platform/commonUI/notification/src/MessageController.js +++ b/platform/commonUI/dialog/src/MessageController.js @@ -22,10 +22,23 @@ /*global define*/ define( - ['./MessageSeverity'], + ['../../notification/src/MessageSeverity'], function (MessageSeverity) { "use strict"; + /** + * A controller for the message view which used to represent a + * message to the user in the dialogs and notifications systems. The + * message view (../res/templates/message.html) is included + * from the blocking message dialog + * (../res/templates/overlay-blocking-message.html), + * and the message list (../res/templates/overlay-message-list.html) + * shown from the notifications indicator + * @param $scope + * @constructor + * @see DialogService#showBlockingMessage + * @see NotificationService + */ function MessageController($scope) { $scope.MessageSeverity = MessageSeverity; } diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index a59860f884..c3f698fc38 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -25,10 +25,29 @@ define( ['../../../notification/src/MessageSeverity'], function (MessageSeverity) { "use strict"; + + /** + * A controller for banner notifications. Banner notifications are a + * non-blocking way of drawing the user's attention to an event such + * as system errors, or the progress or successful completion of an + * ongoing task. This controller provides scoped functions for + * dismissing and 'maximizing' notifications. See {@link NotificationService} + * for more details on Notifications. + * + * @param $scope + * @param notificationService + * @param dialogService + * @constructor + */ function BannerController($scope, notificationService, dialogService) { $scope.active = notificationService.active; $scope.MessageSeverity = MessageSeverity; + $scope.action = function (action, $event){ + /* + Prevents default 'maximize' behaviour when clicking on + notification button + */ $event.stopPropagation(); return action(); }; diff --git a/platform/commonUI/notification/bundle.json b/platform/commonUI/notification/bundle.json index d729e42673..f4dbd5c71a 100644 --- a/platform/commonUI/notification/bundle.json +++ b/platform/commonUI/notification/bundle.json @@ -21,11 +21,6 @@ } ], "controllers": [ - { - "key": "MessageController", - "implementation": "MessageController.js", - "depends": ["$scope"] - }, { "key": "NotificationIndicatorController", "implementation": "NotificationIndicatorController.js", diff --git a/platform/commonUI/notification/src/NotificationIndicatorController.js b/platform/commonUI/notification/src/NotificationIndicatorController.js index 9571b0f6a4..e225fd41bf 100644 --- a/platform/commonUI/notification/src/NotificationIndicatorController.js +++ b/platform/commonUI/notification/src/NotificationIndicatorController.js @@ -26,25 +26,30 @@ define( function (MessageSeverity) { "use strict"; + /** + * Provides an indicator that is visible when there are + * banner notifications that have been minimized. Will also indicate + * the number of notifications. Notifications can be viewed by + * clicking on the indicator to launch a dialog showing a list of + * notifications. + * @param $scope + * @param notificationService + * @param dialogService + * @constructor + */ function NotificationIndicatorController($scope, notificationService, dialogService) { $scope.notifications = notificationService.notifications; $scope.highest = notificationService.highest; $scope.MessageSeverity = MessageSeverity; + /** + * Launch a dialog showing a list of current notifications. + */ $scope.showNotificationsList = function(){ var model = { title: "Messages", - severity: MessageSeverity.INFO, - actions: [ - { - label: "Done", - action: function () { - dialogService.dismiss(); - } - } - ], - messages: [] + severity: MessageSeverity.INFO }; model.messages = notificationService.notifications; From 552435b009e828aab2b9f3494fbce950dff73804 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 15:20:12 -0700 Subject: [PATCH 119/488] [Time Conductor] Add bundle for time handling --- platform/time/bundle.json | 18 ++++++ platform/time/src/TimeAggregator.js | 57 +++++++++++++++++++ platform/time/src/UTCTimeProvider.js | 44 ++++++++++++++ platform/time/src/UTCTimeSystem.js | 85 ++++++++++++++++++++++++++++ 4 files changed, 204 insertions(+) create mode 100644 platform/time/bundle.json create mode 100644 platform/time/src/TimeAggregator.js create mode 100644 platform/time/src/UTCTimeProvider.js create mode 100644 platform/time/src/UTCTimeSystem.js diff --git a/platform/time/bundle.json b/platform/time/bundle.json new file mode 100644 index 0000000000..9ab4fb1e97 --- /dev/null +++ b/platform/time/bundle.json @@ -0,0 +1,18 @@ +{ + "name": "Time services bundle", + "description": "Defines interfaces and provides default implementations for handling different time systems.", + "extensions": { + "components": [ + { + "provides": "timeService", + "type": "aggregator", + "implementation": "TimeAggregator.js" + }, + { + "provides": "timeService", + "type": "provider", + "implementation": "UTCTimeProvider.js" + } + ] + } +} diff --git a/platform/time/src/TimeAggregator.js b/platform/time/src/TimeAggregator.js new file mode 100644 index 0000000000..f3e0d5cfc3 --- /dev/null +++ b/platform/time/src/TimeAggregator.js @@ -0,0 +1,57 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define([ + +], function ( + +) { + "use strict"; + + function TimeAggregator(timeProviders) { + var systemMap = {}, + systemKeys = []; + + timeProviders.forEach(function (provider) { + provider.systems().forEach(function (key) { + if (!systemMap[key]) { + systemMap[key] = provider.system(key); + systemKeys.push(key); + } + }); + }); + + this.systemMap = systemMap; + this.systemKeys = systemKeys; + } + + TimeAggregator.prototype.systems = function () { + return this.systemKeys; + }; + + TimeAggregator.prototype.system = function (key) { + return this.systemMap[key || 'utc']; + }; + + return TimeAggregator; +}); diff --git a/platform/time/src/UTCTimeProvider.js b/platform/time/src/UTCTimeProvider.js new file mode 100644 index 0000000000..36c266c621 --- /dev/null +++ b/platform/time/src/UTCTimeProvider.js @@ -0,0 +1,44 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define([ + './UTCTimeSystem' +], function ( + UTCTimeSystem +) { + "use strict"; + + function UTCTimeProvider(now) { + this.utcTimeSystem = new UTCTimeSystem(now); + } + + UTCTimeProvider.prototype.systems = function () { + return [ 'utc' ]; + }; + + UTCTimeProvider.prototype.system = function (key) { + return key === 'utc' ? this.utcTimeSystem : undefined; + }; + + return UTCTimeProvider; +}); diff --git a/platform/time/src/UTCTimeSystem.js b/platform/time/src/UTCTimeSystem.js new file mode 100644 index 0000000000..22c3488b50 --- /dev/null +++ b/platform/time/src/UTCTimeSystem.js @@ -0,0 +1,85 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define([ + 'moment' +], function ( + moment +) { + "use strict"; + + var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss", + DATE_FORMATS = [ + DATE_FORMAT, + "YYYY-MM-DD HH:mm:ss", + "YYYY-MM-DD HH:mm", + "YYYY-MM-DD" + ], + SECOND = 1000, + MINUTE = 60 * SECOND, + HOUR = 60 * MINUTE, + DAY = 24 * HOUR, + WEEK = 7 * DAY, + MONTH_APPROX = 30 * DAY, + YEAR = 365 * DAY, + INCREMENTS = [ + SECOND, + MINUTE, + HOUR, + DAY, + WEEK, + MONTH_APPROX, + YEAR + ], + DEFAULT_INCREMENT = 3; + + + function UTCTimeSystem(now) { + this.nowFn = now; + } + + UTCTimeSystem.prototype.format = function (value) { + return moment.utc(value).format(DATE_FORMAT); + }; + + UTCTimeSystem.prototype.parse = function (text) { + return moment.utc(text, DATE_FORMATS).valueOf(); + }; + + UTCTimeSystem.prototype.validate = function (text) { + return moment.utc(text, DATE_FORMATS).isValid(); + }; + + UTCTimeSystem.prototype.now = function () { + return this.nowFn(); + }; + + UTCTimeSystem.prototype.increment = function (scale) { + var index = (scale || 0) + DEFAULT_INCREMENT; + index = Math.max(index, 0); + index = Math.min(index, INCREMENTS.length - 1); + return INCREMENTS[index]; + }; + + return UTCTimeSystem; +}); From 5e9f38dadd87bf38330df2bbafe34edcb0afc18e Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 15:20:54 -0700 Subject: [PATCH 120/488] [Time Conductor] Add dependency to UTCTimeProvider --- platform/time/bundle.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/time/bundle.json b/platform/time/bundle.json index 9ab4fb1e97..9e831b5723 100644 --- a/platform/time/bundle.json +++ b/platform/time/bundle.json @@ -11,7 +11,8 @@ { "provides": "timeService", "type": "provider", - "implementation": "UTCTimeProvider.js" + "implementation": "UTCTimeProvider.js", + "depends": [ "now" ] } ] } From 1e71df5ce9cb9636a41ff2564ece27dad01b607e Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 15:26:22 -0700 Subject: [PATCH 121/488] [Time Conductor] Implement timeService in sinewave telemetry Switch from initial dateService interface to more practical timeService interface. --- example/generator/bundle.json | 4 +- ...ateProvider.js => SinewaveTimeProvider.js} | 23 ++++---- example/generator/src/SinewaveTimeSystem.js | 55 +++++++++++++++++++ 3 files changed, 67 insertions(+), 15 deletions(-) rename example/generator/src/{SinewaveDateProvider.js => SinewaveTimeProvider.js} (73%) create mode 100644 example/generator/src/SinewaveTimeSystem.js diff --git a/example/generator/bundle.json b/example/generator/bundle.json index b2050e07e9..6a182e6a7a 100644 --- a/example/generator/bundle.json +++ b/example/generator/bundle.json @@ -10,9 +10,9 @@ "depends": [ "$q", "$timeout" ] }, { - "implementation": "SinewaveDateProvider.js", + "implementation": "SinewaveTimeProvider.js", "type": "provider", - "provides": "dateService" + "provides": "timeService" } ], "capabilities": [ diff --git a/example/generator/src/SinewaveDateProvider.js b/example/generator/src/SinewaveTimeProvider.js similarity index 73% rename from example/generator/src/SinewaveDateProvider.js rename to example/generator/src/SinewaveTimeProvider.js index 9b819a8796..2f2bcc2391 100644 --- a/example/generator/src/SinewaveDateProvider.js +++ b/example/generator/src/SinewaveTimeProvider.js @@ -22,7 +22,9 @@ /*global define*/ define([ + './SinewaveTimeSystem' ], function ( + SinewaveTimeSystem ) { "use strict"; @@ -32,24 +34,19 @@ define([ * not necessarily be formatted as UTC dates. * @memberof example/generator * @constructor - * @implements {DateService} + * @implements {TimeService} */ - function SinewaveDateProvider() { + function SinewaveTimeProvider() { + this.indexTimeSystem = new SinewaveTimeSystem(); } - SinewaveDateProvider.prototype.validate = function (text, key) { - return key === 'generator.index' && /^#\d+$/.test(text); + SinewaveTimeProvider.prototype.systems = function () { + return [ 'generator.index' ]; }; - SinewaveDateProvider.prototype.format = function (value, key) { - return key === 'generator.index' ? - ('#' + Math.floor(value)) : - undefined; + SinewaveTimeProvider.prototype.system = function (key) { + return key === 'generator.index' ? this.indexTimeSystem : undefined; }; - SinewaveDateProvider.prototype.parse = function (text, key) { - return key === 'generator.index' && parseInt(key.substring(1), 10); - }; - - return SinewaveDateProvider; + return SinewaveTimeProvider; }); diff --git a/example/generator/src/SinewaveTimeSystem.js b/example/generator/src/SinewaveTimeSystem.js new file mode 100644 index 0000000000..c248de4bfd --- /dev/null +++ b/example/generator/src/SinewaveTimeSystem.js @@ -0,0 +1,55 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define*/ + +define([ + './SinewaveTelemetrySeries' +], function ( + SinewaveTelemetrySeries +) { + "use strict"; + + function SinewaveTimeSystem(now) { + } + + SinewaveTimeSystem.prototype.format = function (value) { + return ('#' + Math.floor(value)); + }; + + SinewaveTimeSystem.prototype.parse = function (text) { + return parseInt(text.substring(1), 10); + }; + + SinewaveTimeSystem.prototype.validate = function (text) { + return (/^#\d+$/).test(text); + }; + + SinewaveTimeSystem.prototype.now = function () { + return new SinewaveTelemetrySeries().getPointCount(); + }; + + SinewaveTimeSystem.prototype.increment = function (scale) { + return Math.pow(10, (scale || 0) + 1); + }; + + return SinewaveTimeSystem; +}); From a154c9c8708580cc376adceb3777ef312858b6ac Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 15:51:12 -0700 Subject: [PATCH 122/488] [Time Conductor] Use timeService from control --- bundles.json | 1 + example/generator/src/SinewaveTimeSystem.js | 4 +- platform/commonUI/general/bundle.json | 2 +- .../templates/controls/time-controller.html | 2 +- .../src/controllers/TimeRangeController.js | 41 ++++++++++++++----- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/bundles.json b/bundles.json index a0f7edd7a8..3c2eb90340 100644 --- a/bundles.json +++ b/bundles.json @@ -26,6 +26,7 @@ "platform/policy", "platform/entanglement", "platform/search", + "platform/time", "example/imagery", "example/eventGenerator", diff --git a/example/generator/src/SinewaveTimeSystem.js b/example/generator/src/SinewaveTimeSystem.js index c248de4bfd..24a9e35e62 100644 --- a/example/generator/src/SinewaveTimeSystem.js +++ b/example/generator/src/SinewaveTimeSystem.js @@ -44,11 +44,11 @@ define([ }; SinewaveTimeSystem.prototype.now = function () { - return new SinewaveTelemetrySeries().getPointCount(); + return new SinewaveTelemetrySeries({}).getPointCount(); }; SinewaveTimeSystem.prototype.increment = function (scale) { - return Math.pow(10, (scale || 0) + 1); + return Math.max(Math.pow(10, (scale || 0) + 1), 1); }; return SinewaveTimeSystem; diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index 05c86cbcf7..c0f5141694 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -66,7 +66,7 @@ { "key": "TimeRangeController", "implementation": "controllers/TimeRangeController.js", - "depends": [ "$scope", "dateService", "now" ] + "depends": [ "$scope", "timeService", "now" ] }, { "key": "DateTimePickerController", diff --git a/platform/commonUI/general/res/templates/controls/time-controller.html b/platform/commonUI/general/res/templates/controls/time-controller.html index 87b6c682b2..5b41beeebf 100644 --- a/platform/commonUI/general/res/templates/controls/time-controller.html +++ b/platform/commonUI/general/res/templates/controls/time-controller.html @@ -102,7 +102,7 @@
diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index 5f2f0fa3e6..27efeeb3fc 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -34,24 +34,20 @@ define( * @memberof platform/commonUI/general * @constructor */ - function TimeRangeController($scope, dateService, now) { + function TimeRangeController($scope, timeService, now) { var tickCount = 2, innerMinimumSpan = 1000, // 1 second outerMinimumSpan = 1000 * 60 * 60, // 1 hour - initialDragValue; - - function timeSystemKey() { - return ($scope.parameters || {}).system; - } + initialDragValue, + timeSystem = timeService.system(); // Start with default function formatTimestamp(ts) { - return dateService.format(ts, timeSystemKey()); + return timeSystem.format(ts); } function parseTimestamp(text) { - var key = timeSystemKey(); - if (dateService.validate(text, key)) { - return dateService.parse(text, key); + if (timeSystem.validate(text)) { + return timeSystem.parse(text); } else { throw new Error("Could not parse " + text); } @@ -270,6 +266,30 @@ define( } } + function reinitializeBounds(now, increment) { + var end = Math.ceil(now / increment) * increment, + start = end - increment; + $scope.ngModel.outer.start = start; + $scope.ngModel.outer.end = end; + $scope.ngModel.inner.start = start; + $scope.ngModel.inner.end = end; + $scope.boundsModel = {}; + updateViewFromModel(); + } + + function updateTimeSystem(key) { + timeSystem = timeService.system(key) || timeService.system(); + + // One second / one hour in UTC; should be + // similarly useful in other time systems. + innerMinimumSpan = timeSystem.increment(-3); + outerMinimumSpan = timeSystem.increment(-1); + reinitializeBounds( + timeSystem.now(), + timeSystem.increment() + ); + } + function updateStartFromPicker(value) { updateOuterStart(value); updateBoundsText($scope.ngModel); @@ -300,6 +320,7 @@ define( $scope.$watch("ngModel.outer.end", updateEndFromPicker); $scope.$watch("boundsModel.start", updateStartFromText); $scope.$watch("boundsModel.end", updateEndFromText); + $scope.$watch("parameters.system", updateTimeSystem); } return TimeRangeController; From 16e4c327095d9a61dbd2b698bea32a2b8050de20 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 16:05:09 -0700 Subject: [PATCH 123/488] [Time Conductor] Remove now dependency, fix domain switch Remove dependency on now (current time can be retrieved via timeService); fix domain-switching behavior such that changes to time systems are reflected in changes to default bounds. --- platform/commonUI/general/bundle.json | 2 +- .../general/src/controllers/TimeRangeController.js | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index c0f5141694..30057dcff1 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -66,7 +66,7 @@ { "key": "TimeRangeController", "implementation": "controllers/TimeRangeController.js", - "depends": [ "$scope", "timeService", "now" ] + "depends": [ "$scope", "timeService" ] }, { "key": "DateTimePickerController", diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index 27efeeb3fc..321698c1b9 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -34,7 +34,7 @@ define( * @memberof platform/commonUI/general * @constructor */ - function TimeRangeController($scope, timeService, now) { + function TimeRangeController($scope, timeService) { var tickCount = 2, innerMinimumSpan = 1000, // 1 second outerMinimumSpan = 1000 * 60 * 60, // 1 hour @@ -91,7 +91,7 @@ define( } function defaultBounds() { - var t = now(); + var t = timeSystem.now(); return { start: t - 24 * 3600 * 1000, // One day end: t @@ -122,8 +122,6 @@ define( } function updateViewFromModel(ngModel) { - var t = now(); - ngModel = ngModel || {}; ngModel.outer = ngModel.outer || defaultBounds(); ngModel.inner = ngModel.inner || copyBounds(ngModel.outer); @@ -274,7 +272,7 @@ define( $scope.ngModel.inner.start = start; $scope.ngModel.inner.end = end; $scope.boundsModel = {}; - updateViewFromModel(); + updateViewFromModel($scope.ngModel); } function updateTimeSystem(key) { From b4dd95490c6ee216c23855b87c0749024678767f Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 16:07:21 -0700 Subject: [PATCH 124/488] [Time Conductor] Don't treat defaults as invalid --- .../commonUI/general/src/controllers/TimeRangeController.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index 321698c1b9..550ea49175 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -109,6 +109,9 @@ define( ngModel.outer[property]) { $scope.boundsModel[property] = formatTimestamp(ngModel.outer[property]); + // Never want to flag machine-generated text + // as invalid here. + $scope.boundsModel[property + 'Valid'] = true; } } catch (e) { // User-entered text is invalid, so leave it be From 67cf8d8cee1daf1ec7df41c595ff2e59861516d8 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 16:24:55 -0700 Subject: [PATCH 125/488] [Time Conductor] Support index domain from sine wave --- example/generator/src/SinewaveTelemetrySeries.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/example/generator/src/SinewaveTelemetrySeries.js b/example/generator/src/SinewaveTelemetrySeries.js index 17084a07da..529d4e80a2 100644 --- a/example/generator/src/SinewaveTelemetrySeries.js +++ b/example/generator/src/SinewaveTelemetrySeries.js @@ -49,6 +49,11 @@ define( Math.max(Math.floor(request.start / 1000), firstTime), offset = requestStart - firstTime; + if (request.domain === 'index') { + offset = Math.floor(request.start || 0); + count = Math.ceil(request.end || endTime); + } + if (request.size !== undefined) { offset = Math.max(offset, count - request.size); } @@ -59,7 +64,7 @@ define( generatorData.getDomainValue = function (i, domain) { if (domain === 'index') { - return i; + return i + offset; } return (i + offset) * 1000 + firstTime * 1000 - (domain === 'yesterday' ? ONE_DAY : 0); From 111b3bac09a841235274cc8564dc6425f31a0b65 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 22 Oct 2015 16:27:21 -0700 Subject: [PATCH 126/488] [Time Conductor] Remove dateService Replaced by timeService --- platform/commonUI/general/bundle.json | 13 -- .../general/src/services/DateAggregator.js | 118 ------------------ .../general/src/services/UTCDateProvider.js | 64 ---------- .../test/services/DateAggregatorSpec.js | 94 -------------- .../test/services/UTCDateProviderSpec.js | 67 ---------- platform/commonUI/general/test/suite.json | 2 - 6 files changed, 358 deletions(-) delete mode 100644 platform/commonUI/general/src/services/DateAggregator.js delete mode 100644 platform/commonUI/general/src/services/UTCDateProvider.js delete mode 100644 platform/commonUI/general/test/services/DateAggregatorSpec.js delete mode 100644 platform/commonUI/general/test/services/UTCDateProviderSpec.js diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index 30057dcff1..a4d4008f3d 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -15,19 +15,6 @@ "depends": [ "$document", "$window" ] } ], - "components": [ - { - "type": "aggregator", - "provides": "dateService", - "implementation": "services/DateAggregator.js" - }, - { - "type": "provider", - "provides": "dateService", - "implementation": "services/UTCDateProvider.js", - "priority": "fallback" - } - ], "runs": [ { "implementation": "StyleSheetLoader.js", diff --git a/platform/commonUI/general/src/services/DateAggregator.js b/platform/commonUI/general/src/services/DateAggregator.js deleted file mode 100644 index ee01929b27..0000000000 --- a/platform/commonUI/general/src/services/DateAggregator.js +++ /dev/null @@ -1,118 +0,0 @@ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/*global define*/ - -define([ - -], function ( -) { - "use strict"; - - /** - * Formats dates for display and parses dates from user input, - * varying by a chosen time system. - * - * Time systems are typically specified as `system` properties - * of domains in {@link TelemetryDomainMetadata}. - * - * If omitted/left undefined, the time system is presumed to be UTC time, - * with its numeric interpretation being milliseconds since the - * start of 1970. - * - * @interface DateService - */ - - /** - * Check if the provided text can be parsed into a numeric - * representation of a time in the specified time system. - * @method validate - * @memberof DateService# - * @param {string} text the text to validate - * @param {string} [key] a key identifying the time system - * @returns {boolean} true if the text can be parsed - */ - - /** - * Parse the provided into a numeric representation of a time - * in the specified time system. - * - * Behavior of this method for invalid text is undefined; use - * the `validate` method to check for validity first. - * - * @method parse - * @memberof DateService# - * @param {string} text the text to parse - * @param {string} [key] a key identifying the time system - * @returns {number} a numeric representation of the date/time - */ - - /** - * Format the provided numeric representation of a time - * into a human-readable string appropriate for that time system. - * - * If the time system is not recognized, the return value will be - * `undefined`. - * - * @method format - * @memberof DateService# - * @param {number} value the time value to format - * @param {string} [key] a key identifying the time system - * @returns {string} a human-readable representation of the date/time - */ - - /** - * Composites multiple DateService implementations such that - * they can be used as one. - * @memberof platform/commonUI/general - * @constructor - */ - function DateAggregator(dateProviders) { - this.dateProviders = dateProviders; - } - - DateAggregator.prototype.validate = function (text, key) { - return this.dateProviders.some(function (provider) { - return provider.validate(text, key); - }); - }; - - DateAggregator.prototype.format = function (value, key) { - var i, text; - for (i = 0; i < this.dateProviders.length; i += 1) { - text = this.dateProviders[i].format(value, key); - if (text !== undefined) { - return text; - } - } - }; - - DateAggregator.prototype.parse = function (text, key) { - var i; - for (i = 0; i < this.dateProviders.length; i += 1) { - if (this.dateProviders[i].validate(text, key)) { - return this.dateProviders[i].parse(text, key); - } - } - }; - - return DateAggregator; -}); diff --git a/platform/commonUI/general/src/services/UTCDateProvider.js b/platform/commonUI/general/src/services/UTCDateProvider.js deleted file mode 100644 index 38b6c61b8b..0000000000 --- a/platform/commonUI/general/src/services/UTCDateProvider.js +++ /dev/null @@ -1,64 +0,0 @@ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/*global define*/ - -define([ - 'moment' -], function ( - moment -) { - "use strict"; - - var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss", - DATE_FORMATS = [ - DATE_FORMAT, - "YYYY-MM-DD HH:mm:ss", - "YYYY-MM-DD HH:mm", - "YYYY-MM-DD" - ]; - - /** - * Composites multiple DateService implementations such that - * they can be used as one. - * @memberof platform/commonUI/general - * @constructor - * @implements {DateService} - */ - function UTCDateProvider() { - } - - UTCDateProvider.prototype.validate = function (text, key) { - return key === undefined && moment.utc(text, DATE_FORMATS).isValid(); - }; - - UTCDateProvider.prototype.format = function (value, key) { - return key === undefined ? - moment.utc(value).format(DATE_FORMAT) : - undefined; - }; - - UTCDateProvider.prototype.parse = function (text, key) { - return key === undefined && moment.utc(text, DATE_FORMATS).valueOf(); - }; - - return UTCDateProvider; -}); diff --git a/platform/commonUI/general/test/services/DateAggregatorSpec.js b/platform/commonUI/general/test/services/DateAggregatorSpec.js deleted file mode 100644 index ba55bc907a..0000000000 --- a/platform/commonUI/general/test/services/DateAggregatorSpec.js +++ /dev/null @@ -1,94 +0,0 @@ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ - - -define( - ["../../src/services/DateAggregator"], - function (DateAggregator) { - 'use strict'; - - var DATE_SERVICE_METHODS = [ "format", "validate", "parse" ]; - - describe("DateAggregator", function () { - var mockProviders, - dateAggregator; - - beforeEach(function () { - mockProviders = [ 'a', 'b', 'c', undefined ].map(function (k, i) { - var mockProvider = jasmine.createSpyObj( - 'provider-' + k, - DATE_SERVICE_METHODS - ); - - mockProvider.format.andCallFake(function (value, key) { - return key === k ? - ("Formatted " + value + " for " + k) : - undefined; - }); - - mockProvider.parse.andCallFake(function (text, key) { - return key === k ? i : undefined; - }); - - mockProvider.validate.andCallFake(function (text, key) { - return key === k; - }); - - return mockProvider; - }); - - dateAggregator = new DateAggregator(mockProviders); - }); - - it("formats dates using the first provider which gives a result", function () { - expect(dateAggregator.format(42, "a")) - .toEqual("Formatted 42 for a"); - expect(dateAggregator.format(12321, "b")) - .toEqual("Formatted 12321 for b"); - expect(dateAggregator.format(1977, "c")) - .toEqual("Formatted 1977 for c"); - expect(dateAggregator.format(0)) - .toEqual("Formatted 0 for undefined"); - }); - - it("parses dates using the first provider which validates", function () { - expect(dateAggregator.parse("x", "a")).toEqual(0); - expect(dateAggregator.parse("x", "b")).toEqual(1); - expect(dateAggregator.parse("x", "c")).toEqual(2); - expect(dateAggregator.parse("x")).toEqual(3); - }); - - it("validates across all providers", function () { - expect(dateAggregator.validate("x", "a")).toBeTruthy(); - expect(dateAggregator.validate("x", "b")).toBeTruthy(); - expect(dateAggregator.validate("x", "c")).toBeTruthy(); - expect(dateAggregator.validate("x")).toBeTruthy(); - expect(dateAggregator.validate("x", "z")).toBeFalsy(); - - mockProviders[3].validate.andReturn(false); - expect(dateAggregator.validate("x")).toBeFalsy(); - }); - - }); - } -); diff --git a/platform/commonUI/general/test/services/UTCDateProviderSpec.js b/platform/commonUI/general/test/services/UTCDateProviderSpec.js deleted file mode 100644 index 1a1873030d..0000000000 --- a/platform/commonUI/general/test/services/UTCDateProviderSpec.js +++ /dev/null @@ -1,67 +0,0 @@ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ - - -define( - ["../../src/services/UTCDateProvider", "moment"], - function (UTCDateProvider, moment) { - 'use strict'; - - describe("UTCDateProvider", function () { - var testDate, testTimestamp, dateProvider; - - beforeEach(function () { - testDate = "1977-05-25 17:30:00"; - testTimestamp = moment.utc(testDate).valueOf(); - dateProvider = new UTCDateProvider(); - }); - - it("distinguishes valid dates from invalid dates", function () { - expect(dateProvider.validate(testDate)) - .toBeTruthy(); - expect(dateProvider.validate("2015-garbage :00:00")) - .toBeFalsy(); - }); - - it("parses dates to their numeric representations", function () { - expect(dateProvider.parse(testDate)).toEqual(testTimestamp); - }); - - it("formats to text representing UTC date/times", function () { - var formatted = dateProvider.format(testTimestamp); - expect(formatted).toEqual(jasmine.any(String)); - // Use moment to verify that formatted value is equal - // to the original date/time - expect(moment.utc(formatted).valueOf()).toEqual(testTimestamp); - }); - - it("does not handle defined keys", function () { - expect(dateProvider.validate(testDate, 'someKey')) - .toBeFalsy(); - expect(dateProvider.format(testTimestamp, 'someKey')) - .toBeUndefined(); - }); - - }); - } -); diff --git a/platform/commonUI/general/test/suite.json b/platform/commonUI/general/test/suite.json index 9f80aaabc2..0d19fbb9e4 100644 --- a/platform/commonUI/general/test/suite.json +++ b/platform/commonUI/general/test/suite.json @@ -17,8 +17,6 @@ "directives/MCTPopup", "directives/MCTResize", "directives/MCTScroll", - "services/DateAggregator", - "services/UTCDateProvider", "services/Popup", "services/PopupService", "services/UrlService", From 83276d70d3942a9b46d9a176e4398841e725e51b Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 22 Oct 2015 16:29:10 -0700 Subject: [PATCH 127/488] Removed MessageSeverity enum and MessageController --- .../src/DialogLaunchController.js | 10 +-- .../src/NotificationLaunchController.js | 16 ++--- platform/commonUI/dialog/bundle.json | 7 -- .../dialog/res/templates/message.html | 8 +-- .../commonUI/dialog/src/MessageController.js | 47 ------------- .../general/res/templates/message-banner.html | 5 +- .../src/controllers/BannerController.js | 7 +- .../res/notification-indicator.html | 5 +- .../notification/src/MessageSeverity.js | 11 --- .../src/NotificationIndicatorController.js | 8 +-- .../notification/src/NotificationService.js | 29 ++++---- .../test/NotificationServiceSpec.js | 69 ++----------------- 12 files changed, 44 insertions(+), 178 deletions(-) delete mode 100644 platform/commonUI/dialog/src/MessageController.js delete mode 100644 platform/commonUI/notification/src/MessageSeverity.js diff --git a/example/notifications/src/DialogLaunchController.js b/example/notifications/src/DialogLaunchController.js index 5679f990a0..e6a8b4e132 100644 --- a/example/notifications/src/DialogLaunchController.js +++ b/example/notifications/src/DialogLaunchController.js @@ -22,8 +22,8 @@ /*global define*/ define( - ['../../../platform/commonUI/notification/src/MessageSeverity'], - function (MessageSeverity) { + [], + function () { "use strict"; /** @@ -51,7 +51,7 @@ define( actionText: "Calculating...", unknownProgress: !knownProgress, unknownDuration: false, - severity: MessageSeverity.INFO, + severity: "info", options: [ { label: "Cancel Operation", @@ -96,7 +96,7 @@ define( var model = { title: "Error Dialog Example", actionText: "Something happened, and it was not good.", - severity: MessageSeverity.ERROR, + severity: "error", options: [ { label: "Try Again", @@ -129,7 +129,7 @@ define( actionText: "This is an example of a blocking info" + " dialog. This dialog can be used to draw the user's" + " attention to an event.", - severity: MessageSeverity.INFO, + severity: "info", options: [ { label: "OK", diff --git a/example/notifications/src/NotificationLaunchController.js b/example/notifications/src/NotificationLaunchController.js index e59c009422..7d0618ada4 100644 --- a/example/notifications/src/NotificationLaunchController.js +++ b/example/notifications/src/NotificationLaunchController.js @@ -22,8 +22,8 @@ /*global define*/ define( - ['../../../platform/commonUI/notification/src/MessageSeverity'], - function (MessageSeverity) { + [], + function () { "use strict"; /** @@ -81,9 +81,9 @@ define( function getExampleSeverity() { var severities = [ - MessageSeverity.INFO, - MessageSeverity.ALERT, - MessageSeverity.ERROR + "info", + "alert", + "error" ]; return severities[Math.floor(Math.random() * severities.length)]; } @@ -96,7 +96,7 @@ define( notificationService.notify({ title: "Example error notification " + messageCounter++, hint: "An error has occurred", - severity: MessageSeverity.ERROR, + severity: "error", primaryOption: { label: 'Retry', callback: function() { @@ -113,7 +113,7 @@ define( notificationService.notify({ title: "Alert notification " + (messageCounter++), hint: "This is an alert message", - severity: MessageSeverity.ALERT, + severity: "alert", primaryOption: { label: 'Retry', callback: function() { @@ -132,7 +132,7 @@ define( var notification = { title: "Progress notification example", - severity: MessageSeverity.INFO, + severity: "info", progress: 0, actionText: getExampleActionText(), unknownProgress: false diff --git a/platform/commonUI/dialog/bundle.json b/platform/commonUI/dialog/bundle.json index fb94f14b61..80cd456c20 100644 --- a/platform/commonUI/dialog/bundle.json +++ b/platform/commonUI/dialog/bundle.json @@ -43,13 +43,6 @@ "key": "overlay", "templateUrl": "templates/overlay.html" } - ], - "controllers": [ - { - "key": "MessageController", - "implementation": "MessageController.js", - "depends": ["$scope"] - } ] } } \ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/message.html b/platform/commonUI/dialog/res/templates/message.html index 6b8c5c99cc..8568fe5fb1 100644 --- a/platform/commonUI/dialog/res/templates/message.html +++ b/platform/commonUI/dialog/res/templates/message.html @@ -1,9 +1,5 @@ -
+
diff --git a/platform/commonUI/dialog/src/MessageController.js b/platform/commonUI/dialog/src/MessageController.js deleted file mode 100644 index 595f429f9e..0000000000 --- a/platform/commonUI/dialog/src/MessageController.js +++ /dev/null @@ -1,47 +0,0 @@ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/*global define*/ - -define( - ['../../notification/src/MessageSeverity'], - function (MessageSeverity) { - "use strict"; - - /** - * A controller for the message view which used to represent a - * message to the user in the dialogs and notifications systems. The - * message view (../res/templates/message.html) is included - * from the blocking message dialog - * (../res/templates/overlay-blocking-message.html), - * and the message list (../res/templates/overlay-message-list.html) - * shown from the notifications indicator - * @param $scope - * @constructor - * @see DialogService#showBlockingMessage - * @see NotificationService - */ - function MessageController($scope) { - $scope.MessageSeverity = MessageSeverity; - } - return MessageController; - } -); diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html index 0af92aeba5..29b107126e 100644 --- a/platform/commonUI/general/res/templates/message-banner.html +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -1,8 +1,5 @@
diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index c3f698fc38..511bf23b38 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -22,8 +22,8 @@ /*global define*/ define( - ['../../../notification/src/MessageSeverity'], - function (MessageSeverity) { + [], + function () { "use strict"; /** @@ -41,7 +41,6 @@ define( */ function BannerController($scope, notificationService, dialogService) { $scope.active = notificationService.active; - $scope.MessageSeverity = MessageSeverity; $scope.action = function (action, $event){ /* @@ -56,7 +55,7 @@ define( notificationService.dismissOrMinimize(notification); }; $scope.maximize = function(notification) { - if (notification.severity > MessageSeverity.INFO){ + if (notification.severity != "info"){ notification.cancel = function(){ dialogService.dismiss(); }; diff --git a/platform/commonUI/notification/res/notification-indicator.html b/platform/commonUI/notification/res/notification-indicator.html index 1d2bf90960..9c7e80a639 100644 --- a/platform/commonUI/notification/res/notification-indicator.html +++ b/platform/commonUI/notification/res/notification-indicator.html @@ -1,8 +1,5 @@ diff --git a/platform/commonUI/notification/src/MessageSeverity.js b/platform/commonUI/notification/src/MessageSeverity.js deleted file mode 100644 index 44064bf92f..0000000000 --- a/platform/commonUI/notification/src/MessageSeverity.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Created by akhenry on 10/7/15. - */ -/*global define*/ -define(function(){ - return { - INFO: 0, - ALERT: 1, - ERROR: 2 - }; -}); \ No newline at end of file diff --git a/platform/commonUI/notification/src/NotificationIndicatorController.js b/platform/commonUI/notification/src/NotificationIndicatorController.js index e225fd41bf..21da649a8f 100644 --- a/platform/commonUI/notification/src/NotificationIndicatorController.js +++ b/platform/commonUI/notification/src/NotificationIndicatorController.js @@ -22,8 +22,8 @@ /*global define*/ define( - ['./MessageSeverity'], - function (MessageSeverity) { + [], + function () { "use strict"; /** @@ -40,7 +40,6 @@ define( function NotificationIndicatorController($scope, notificationService, dialogService) { $scope.notifications = notificationService.notifications; $scope.highest = notificationService.highest; - $scope.MessageSeverity = MessageSeverity; /** * Launch a dialog showing a list of current notifications. @@ -48,8 +47,7 @@ define( $scope.showNotificationsList = function(){ var model = { - title: "Messages", - severity: MessageSeverity.INFO + title: "Messages" }; model.messages = notificationService.notifications; diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index c11b92c651..2e1a44a1a8 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -32,8 +32,8 @@ * @namespace platform/commonUI/dialog */ define( - ["./MessageSeverity"], - function (MessageSeverity) { + [], + function () { "use strict"; /** @@ -56,8 +56,9 @@ define( * * @typedef {object} Notification * @property {string} title The title of the message - * @property {MessageSeverity} severity The importance of the - * message (eg. error, info) + * @property {string} severity The importance of the + * message (one of 'info', 'alert', or 'error' where info < alert < + * error) * @property {number} progress The completion status of a task * represented numerically * @property {boolean} unknownProgress a boolean indicating that the @@ -91,7 +92,7 @@ define( function NotificationService($timeout, DEFAULT_AUTO_DISMISS, MINIMIZE_TIMEOUT) { this.notifications = []; this.$timeout = $timeout; - this.highest ={ severity: MessageSeverity.INFO }; + this.highest ={ severity: "info" }; this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS; this.MINIMIZE_TIMEOUT = MINIMIZE_TIMEOUT; @@ -118,7 +119,7 @@ define( */ NotificationService.prototype.info = function (notification) { notification.autoDismiss = notification.autoDismiss || true; - notification.severity = MessageSeverity.INFO; + notification.severity = "info"; this.notify(notification); }; @@ -130,13 +131,18 @@ define( * @param {Notification} notification The notification to display */ NotificationService.prototype.notify = function (notification) { - var self = this; - + var self = this, + ordinality = { + "info": 1, + "alert": 2, + "error": 3 + }; + notification.severity = notification.severity || "info" if (notification.autoDismiss === true){ notification.autoDismiss = this.DEFAULT_AUTO_DISMISS; } - if (notification.severity > this.highest.severity){ + if (ordinality[notification.severity.toLowerCase()] > ordinality[this.highest.severity.toLowerCase()]){ this.highest.severity = notification.severity; } @@ -271,11 +277,6 @@ define( //For now minimize everything, and have discussion around which //kind of messages should or should not be in the minimized //notifications list - /*if (notification.severity > MessageSeverity.INFO){ - this.minimize(notification); - } else { - this.dismiss(notification); - }*/ this.minimize(notification); }; diff --git a/platform/commonUI/notification/test/NotificationServiceSpec.js b/platform/commonUI/notification/test/NotificationServiceSpec.js index 3957fe622e..dca76652ce 100644 --- a/platform/commonUI/notification/test/NotificationServiceSpec.js +++ b/platform/commonUI/notification/test/NotificationServiceSpec.js @@ -22,8 +22,8 @@ /*global define,describe,it,expect,beforeEach,waitsFor,jasmine */ define( - ['../src/NotificationService','../src/MessageSeverity'], - function (NotificationService, MessageSeverity) { + ['../src/NotificationService'], + function (NotificationService) { "use strict"; describe("The notification service ", function () { @@ -34,46 +34,6 @@ define( successModel, errorModel; - /** - * 1) Calling .notify results in a new notification being created - * with the provided model and set to the active notification. DONE - * - * 2) Calling .notify with autoDismiss results in a SUCCESS notification - * becoming dismissed after timeout has elapsed DONE - * - * 3) Calling .notify with autoDismiss results in an ERROR notification - * being MINIMIZED after a timeout has elapsed DONE - * - * 4) Calling .notify with an active info notification results in that - * notification being auto-dismissed, and the new notification becoming - * active. DONE - * - * 5) Calling .notify with an active error notification results in that - * notification being auto-minimized and the new notification becoming - * active. DONE - * - * 6) Calling .notify with an active error notification, AND a - * queued error notification results in the active notification - * being auto-dismissed, the next message in the queue becoming - * active, then auto-dismissed, and then the provided notification - * becoming active. - */ - - /** - var model = { - title: string, - progress: number, - severity: MessageSeverity, - unknownProgress: boolean, - minimized: boolean, - autoDismiss: boolean | number, - actions: { - label: string, - action: function - } - } - */ - beforeEach(function(){ mockTimeout = jasmine.createSpy("$timeout"); mockAutoDismiss = mockMinimizeTimeout = 1000; @@ -81,11 +41,11 @@ define( mockTimeout, mockAutoDismiss, mockMinimizeTimeout); successModel = { title: "Mock Success Notification", - severity: MessageSeverity.INFO + severity: "info" }; errorModel = { title: "Mock Error Notification", - severity: MessageSeverity.ERROR + severity: "error" }; }); @@ -147,23 +107,6 @@ define( activeNotification = notificationService.getActiveNotification(); expect(activeNotification).toBe(errorModel); }); - /* Test is temporarily invalid as info messages are being - minimized - it("auto-dismisses an active success notification, removing" + - " it completely", function() { - //First pre-load with a info message - notificationService.notify(successModel); - //Then notify of an error - notificationService.notify(errorModel); - expect(notificationService.notifications.length).toEqual(2); - mockTimeout.mostRecentCall.args[0](); - //Two timeouts, one is to force minimization after - // displaying the message for a minimum period, the - // second is to allow minimization animation to take place. - mockTimeout.mostRecentCall.args[0](); - //Previous info message should be completely dismissed - expect(notificationService.notifications.length).toEqual(1); - });*/ it("auto-minimizes an active error notification", function() { var activeNotification; //First pre-load with an error message @@ -190,11 +133,11 @@ define( var activeNotification, error2 = { title: "Second Mock Error Notification", - severity: MessageSeverity.ERROR + severity: "error" }, error3 = { title: "Third Mock Error Notification", - severity: MessageSeverity.ERROR + severity: "error" }; //First pre-load with a info message From e3ef68bc6f711a30973b274e6a58a55d9299755e Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 22 Oct 2015 16:41:09 -0700 Subject: [PATCH 128/488] Fixed jslint errors --- example/notifications/src/DialogLaunchController.js | 6 ++---- .../commonUI/general/src/controllers/BannerController.js | 2 +- platform/commonUI/notification/src/NotificationService.js | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/example/notifications/src/DialogLaunchController.js b/example/notifications/src/DialogLaunchController.js index e6a8b4e132..f35d008cd0 100644 --- a/example/notifications/src/DialogLaunchController.js +++ b/example/notifications/src/DialogLaunchController.js @@ -130,15 +130,13 @@ define( " dialog. This dialog can be used to draw the user's" + " attention to an event.", severity: "info", - options: [ - { + primaryOption: { label: "OK", callback: function () { $log.debug("OK Pressed"); dialogService.dismiss(); } - }, - ] + } }; if (!dialogService.showBlockingMessage(model)) { diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index 511bf23b38..068b5cda81 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -55,7 +55,7 @@ define( notificationService.dismissOrMinimize(notification); }; $scope.maximize = function(notification) { - if (notification.severity != "info"){ + if (notification.severity !== "info"){ notification.cancel = function(){ dialogService.dismiss(); }; diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 2e1a44a1a8..8887e7cb9a 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -137,7 +137,7 @@ define( "alert": 2, "error": 3 }; - notification.severity = notification.severity || "info" + notification.severity = notification.severity || "info"; if (notification.autoDismiss === true){ notification.autoDismiss = this.DEFAULT_AUTO_DISMISS; } From 536d5616d4a23f95838c7b9ac05665a8906e75c7 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 22 Oct 2015 18:28:25 -0700 Subject: [PATCH 129/488] [Frontend] Manual re-do of collapse/expand panes open #90 Major work on new .mini-tab elements, used as controls to expand/collapse panes; Cleaned up splitter hover behavior; Changed mixin controlGrippy to not be tied to :before; --- .../browse/res/templates/browse-object.html | 51 +- .../commonUI/browse/res/templates/browse.html | 4 +- .../commonUI/general/res/sass/_global.scss | 4 - .../commonUI/general/res/sass/_icons.scss | 1 + .../commonUI/general/res/sass/_mixins.scss | 48 +- .../general/res/sass/controls/_buttons.scss | 80 ++- .../general/res/sass/controls/_controls.scss | 2 - .../general/res/sass/helpers/_splitter.scss | 24 +- .../general/res/sass/mobile/_layout.scss | 5 +- .../res/sass/user-environ/_layout.scss | 61 +- .../espresso/res/css/theme-espresso.css | 581 ++++++++++-------- .../themes/snow/res/css/theme-snow.css | 573 ++++++++++------- 12 files changed, 855 insertions(+), 579 deletions(-) diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html index 8e980581fe..12bf5af841 100644 --- a/platform/commonUI/browse/res/templates/browse-object.html +++ b/platform/commonUI/browse/res/templates/browse-object.html @@ -20,43 +20,42 @@ at runtime from the About dialog for additional information. --> -
-
- - - -
-
- - - - - -
-
- +
+
+
+ + + +
+
+ + + + + +
+
- F +
- Inspector goes here +
diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 235585de64..678b15be87 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -54,9 +54,7 @@
- m +
+ $xpos: $iconD + 2px; + &:before { + content:'\3e'; + left: $xpos; + } + &:hover:before { left: $xpos + 3; } + } +} + .l-btn-set { // Buttons that have a very tight conceptual grouping - no internal space between them. // Structure: .btn-set > mct-representation class=first|last > .s-btn diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss index eef87c3439..bc61fbc35e 100644 --- a/platform/commonUI/general/res/sass/controls/_controls.scss +++ b/platform/commonUI/general/res/sass/controls/_controls.scss @@ -306,8 +306,6 @@ label.checkbox.custom { left: 0; } .knob { - //@include btnSubtle(); - //@include controlGrippy(rgba(black, 0.3), vertical, 1px, solid); @include trans-prop-nice-fade(.25s); background-color: $sliderColorKnob; &:hover { diff --git a/platform/commonUI/general/res/sass/helpers/_splitter.scss b/platform/commonUI/general/res/sass/helpers/_splitter.scss index f91f5936d4..b329f6fa11 100644 --- a/platform/commonUI/general/res/sass/helpers/_splitter.scss +++ b/platform/commonUI/general/res/sass/helpers/_splitter.scss @@ -26,14 +26,16 @@ background-color: $b; @include border-radius($splitterEndCr); @include boxShdw($splitterShdw); - overflow: hidden; + //overflow: hidden; position: absolute; z-index: 1; - //@if $colorSplitterHover != 'none' { - &:hover { - background-color: $colorSplitterHover; - } - //} + &:hover { + background-color: $colorSplitterHover; + &:after { + //@include trans-prop-nice("border-color", 25ms); + border-color: $colorGrippyInteriorHover !important; + } + } } &.horizontal { // Slides vertically up and down, splitting the element horizontally @@ -49,7 +51,9 @@ } } >.splitter { - @include controlGrippy($colorSplitterInterior, horizontal); + &:after { + @include controlGrippy($colorSplitterInterior, horizontal); + } cursor: row-resize; left: 0; right: 0; width: auto; @@ -69,16 +73,18 @@ } } >.splitter { - @include controlGrippy($colorBodyBg, vertical); bottom: 0; cursor: col-resize; width: $splitterW; + &:after { + @include controlGrippy($colorBodyBg, vertical); + } } } } .browse-area .splitter { - top: $ueTopBarH + $interiorMarginLg; + top: 0; //$ueTopBarH + $interiorMarginLg; } .edit-area .splitter { diff --git a/platform/commonUI/general/res/sass/mobile/_layout.scss b/platform/commonUI/general/res/sass/mobile/_layout.scss index cf252f6205..f6627e6a5d 100644 --- a/platform/commonUI/general/res/sass/mobile/_layout.scss +++ b/platform/commonUI/general/res/sass/mobile/_layout.scss @@ -94,11 +94,14 @@ } } - .mobile-menu-icon { + .toggle-tree { font-size: 110%; position: absolute; top: $bodyMargin + 2; left: $bodyMargin; + &:before { + content:'m' !important; + } } .object-browse-bar { diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 179979bd2f..a3ce8275bd 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -252,6 +252,36 @@ top: $ueTopBarH + $interiorMarginLg + $treeSearchInputBarH + $interiorMargin; } } + .mini-tab.toggle-pane { + $h: $ueTopBarH; + $paneOffset: $interiorMarginLg * -3; + //font-size: 0.7rem; + //position: absolute; + //height: $h; + //line-height: $h; + top: 5px; + z-index: 2; + &.toggle-tree.anchor-left { + //@include test(green); + left: $paneOffset; +/* &:hover { + left: $paneOffset - 2; + }*/ + &:after { + content:'F'; + } + } + &.toggle-inspect.anchor-right { + $xpos: $paneOffset + $interiorMargin; + right: $xpos; +/* &:hover { + right: $xpos - 2; + }*/ + &:after { + content:'F'; //\e612 + } + } + } &.items { .object-browse-bar { .left.abs, @@ -289,7 +319,6 @@ } } } - } } @@ -358,46 +387,20 @@ // right representation. .browse-hidetree { @include user-select(none); - // Sets the left tree menu when the tree - // is hidden. + // Sets the left tree menu when the tree is hidden. .pane.left.treeview { opacity: 0; - //right: 100% !important; - //width: auto !important; - //overflow-y: hidden; - //overflow-x: hidden; } - // Sets the right represenation when - // the tree is hidden. + // Sets the right represenation when the tree is hidden. .pane.right-repr { left: 0 !important; } } .browse-showtree { - // NOTE: DISABLED SELECTION - // Selection disabled in both panes - // causing cut/copy/paste menu to - // not appear. Should me moved in - // future to properly work - //@include user-select(none); - // Sets the left tree menu when the tree is shown. .pane.left.treeview { @include trans-prop-nice(opacity, .4s); - //@include background-image(linear-gradient(90deg, rgba(black, 0) 98%, rgba(black, 0.3) 100%)); opacity: 1; - //display: block !important; - //width: auto !important; // CH CO - //right: auto; - //width: $proporMenuWithView; - } - // Sets the right representation when the tree is shown. - .pane.right-repr { - //left: $proporMenuWithView; - //width: auto !important; - - //left: 0 !important; - //transform: translateX($proporMenuWithView); } } \ No newline at end of file diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index ea4789e283..d02fb40bb2 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -313,21 +313,17 @@ a.disabled { src: url("../../../../general/res/fonts/symbols/wtdsymbols.eot?#iefix") format("embedded-opentype"), url("../../../../general/res/fonts/symbols/wtdsymbols.woff") format("woff"), url("../../../../general/res/fonts/symbols/wtdsymbols.ttf") format("truetype"), url("../../../../general/res/fonts/symbols/wtdsymbols.svg#armataregular") format("svg"); font-weight: normal; font-style: normal; } -/* line 37, ../../../../general/res/sass/_global.scss */ -.ui-symbol, .l-datetime-picker .l-month-year-pager .pager { - font-family: 'symbolsfont'; } - /************************** HTML ENTITIES */ -/* line 42, ../../../../general/res/sass/_global.scss */ +/* line 38, ../../../../general/res/sass/_global.scss */ a { color: #ccc; cursor: pointer; text-decoration: none; } - /* line 46, ../../../../general/res/sass/_global.scss */ + /* line 42, ../../../../general/res/sass/_global.scss */ a:hover { color: #fff; } -/* line 51, ../../../../general/res/sass/_global.scss */ +/* line 47, ../../../../general/res/sass/_global.scss */ body, html { -webkit-font-smoothing: subpixel-antialiased; -moz-osx-font-smoothing: grayscale; @@ -339,24 +335,24 @@ body, html { width: 100%; overflow: hidden; } -/* line 64, ../../../../general/res/sass/_global.scss */ +/* line 60, ../../../../general/res/sass/_global.scss */ em { font-style: normal; } -/* line 68, ../../../../general/res/sass/_global.scss */ +/* line 64, ../../../../general/res/sass/_global.scss */ input, textarea { font-family: Helvetica, Arial, sans-serif; } -/* line 72, ../../../../general/res/sass/_global.scss */ +/* line 68, ../../../../general/res/sass/_global.scss */ input[type="text"] { vertical-align: baseline; padding: 3px 5px !important; } -/* line 77, ../../../../general/res/sass/_global.scss */ +/* line 73, ../../../../general/res/sass/_global.scss */ h1, h2, h3 { margin: 0; } -/* line 81, ../../../../general/res/sass/_global.scss */ +/* line 77, ../../../../general/res/sass/_global.scss */ h1 { font-size: 1.7em; font-weight: normal !important; @@ -364,15 +360,15 @@ h1 { margin-bottom: 20px; margin-top: 0; } -/* line 89, ../../../../general/res/sass/_global.scss */ +/* line 85, ../../../../general/res/sass/_global.scss */ p { margin-bottom: 10px; } -/* line 93, ../../../../general/res/sass/_global.scss */ +/* line 89, ../../../../general/res/sass/_global.scss */ mct-container { display: block; } -/* line 97, ../../../../general/res/sass/_global.scss */ +/* line 93, ../../../../general/res/sass/_global.scss */ .abs, .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .val, .s-menu-btn span.l-click-area { position: absolute; @@ -383,50 +379,50 @@ mct-container { height: auto; width: auto; } -/* line 107, ../../../../general/res/sass/_global.scss */ +/* line 103, ../../../../general/res/sass/_global.scss */ .code, .codehilite { font-family: "Lucida Console", monospace; font-size: 0.7em; line-height: 150%; white-space: pre; } -/* line 114, ../../../../general/res/sass/_global.scss */ +/* line 110, ../../../../general/res/sass/_global.scss */ .codehilite { background-color: rgba(153, 153, 153, 0.1); padding: 1em; } -/* line 120, ../../../../general/res/sass/_global.scss */ +/* line 116, ../../../../general/res/sass/_global.scss */ .align-right { text-align: right; } -/* line 124, ../../../../general/res/sass/_global.scss */ +/* line 120, ../../../../general/res/sass/_global.scss */ .centered { text-align: center; } -/* line 128, ../../../../general/res/sass/_global.scss */ +/* line 124, ../../../../general/res/sass/_global.scss */ .scrolling { overflow: auto; } -/* line 132, ../../../../general/res/sass/_global.scss */ +/* line 128, ../../../../general/res/sass/_global.scss */ .vscroll { overflow-y: auto; } -/* line 136, ../../../../general/res/sass/_global.scss */ +/* line 132, ../../../../general/res/sass/_global.scss */ .no-margin { margin: 0; } -/* line 140, ../../../../general/res/sass/_global.scss */ +/* line 136, ../../../../general/res/sass/_global.scss */ .ds { -moz-box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; } -/* line 144, ../../../../general/res/sass/_global.scss */ +/* line 140, ../../../../general/res/sass/_global.scss */ .hide, .hidden { display: none !important; } -/* line 149, ../../../../general/res/sass/_global.scss */ +/* line 145, ../../../../general/res/sass/_global.scss */ .sep { color: rgba(255, 255, 255, 0.2); } @@ -606,53 +602,56 @@ mct-container { border-top: 5px solid #0099cc; border-right: 5px solid transparent; } -/* line 32, ../../../../general/res/sass/_icons.scss */ -.ui-symbol.type-icon, .l-datetime-picker .l-month-year-pager .type-icon.pager { - color: #cccccc; } -/* line 35, ../../../../general/res/sass/_icons.scss */ -.ui-symbol.icon, .l-datetime-picker .l-month-year-pager .icon.pager { - color: #0099cc; } - /* line 37, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.alert, .l-datetime-picker .l-month-year-pager .icon.alert.pager { - color: #ff3c00; } - /* line 39, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.alert:hover, .l-datetime-picker .l-month-year-pager .icon.alert.pager:hover { - color: #ff8a66; } - /* line 43, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.major, .l-datetime-picker .l-month-year-pager .icon.major.pager { - font-size: 1.65em; } -/* line 47, ../../../../general/res/sass/_icons.scss */ -.ui-symbol.icon-calendar:after, .l-datetime-picker .l-month-year-pager .icon-calendar.pager:after { - content: "\e605"; } +/* line 31, ../../../../general/res/sass/_icons.scss */ +.ui-symbol, .s-icon-btn, .mini-tab, .l-datetime-picker .l-month-year-pager .pager { + font-family: 'symbolsfont'; } + /* line 33, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.type-icon, .type-icon.s-icon-btn, .type-icon.mini-tab, .l-datetime-picker .l-month-year-pager .type-icon.pager { + color: #cccccc; } + /* line 36, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon, .icon.s-icon-btn, .icon.mini-tab, .l-datetime-picker .l-month-year-pager .icon.pager { + color: #0099cc; } + /* line 38, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon.alert, .icon.alert.s-icon-btn, .icon.alert.mini-tab, .l-datetime-picker .l-month-year-pager .icon.alert.pager { + color: #ff3c00; } + /* line 40, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon.alert:hover, .icon.alert.s-icon-btn:hover, .icon.alert.mini-tab:hover, .l-datetime-picker .l-month-year-pager .icon.alert.pager:hover { + color: #ff8a66; } + /* line 44, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon.major, .icon.major.s-icon-btn, .icon.major.mini-tab, .l-datetime-picker .l-month-year-pager .icon.major.pager { + font-size: 1.65em; } + /* line 48, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon-calendar:after, .icon-calendar.s-icon-btn:after, .icon-calendar.mini-tab:after, .l-datetime-picker .l-month-year-pager .icon-calendar.pager:after { + content: "\e605"; } -/* line 52, ../../../../general/res/sass/_icons.scss */ -.bar .ui-symbol, .bar .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .bar .pager { +/* line 53, ../../../../general/res/sass/_icons.scss */ +.bar .ui-symbol, .bar .s-icon-btn, .bar .mini-tab, .bar .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .bar .pager { display: inline-block; } -/* line 56, ../../../../general/res/sass/_icons.scss */ +/* line 57, ../../../../general/res/sass/_icons.scss */ .invoke-menu { text-shadow: none; display: inline-block; } -/* line 61, ../../../../general/res/sass/_icons.scss */ +/* line 62, ../../../../general/res/sass/_icons.scss */ .s-menu-btn .invoke-menu, .icon.major .invoke-menu { margin-left: 3px; } -/* line 66, ../../../../general/res/sass/_icons.scss */ +/* line 67, ../../../../general/res/sass/_icons.scss */ .menu .type-icon, .tree-item .type-icon, .super-menu.menu .type-icon { position: absolute; } -/* line 76, ../../../../general/res/sass/_icons.scss */ +/* line 77, ../../../../general/res/sass/_icons.scss */ .l-icon-link:before { content: "\f4"; } -/* line 80, ../../../../general/res/sass/_icons.scss */ +/* line 81, ../../../../general/res/sass/_icons.scss */ .l-icon-alert { display: none !important; } - /* line 82, ../../../../general/res/sass/_icons.scss */ + /* line 83, ../../../../general/res/sass/_icons.scss */ .l-icon-alert:before { color: #ff3c00; content: "!"; } @@ -910,58 +909,36 @@ mct-container { -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; box-shadow: rgba(0, 0, 0, 0.4) 0 0 3px; - overflow: hidden; position: absolute; z-index: 1; } - /* line 33, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 32, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout .splitter:hover { background-color: none; } -/* line 38, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 34, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout .splitter:hover:after { + border-color: #0099cc !important; } +/* line 40, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal { overflow: hidden; } - /* line 41, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 43, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane { left: 0; right: 0; } - /* line 44, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 46, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane.top { bottom: auto; } - /* line 47, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 49, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane.bottom { top: auto; } - /* line 51, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 53, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal > .splitter { cursor: row-resize; left: 0; right: 0; width: auto; height: 5px; } - /* line 159, ../../../../general/res/sass/_mixins.scss */ - .split-layout.horizontal > .splitter:before { - -moz-transition-property: "border-color"; - -o-transition-property: "border-color"; - -webkit-transition-property: "border-color"; - transition-property: "border-color"; - -moz-transition-duration: 0.75s; - -o-transition-duration: 0.75s; - -webkit-transition-duration: 0.75s; - transition-duration: 0.75s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - content: ''; - display: block; - height: auto; - pointer-events: none; - position: absolute; - z-index: 2; - border-top: 1px dotted #0d0d0d; - top: 2px; - left: 5px; - right: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ - .split-layout.horizontal > .splitter:not(.disabled):hover:before { + /* line 54, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.horizontal > .splitter:after { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; -webkit-transition-property: "border-color"; @@ -974,48 +951,33 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; - border-color: #0099cc; } -/* line 61, ../../../../general/res/sass/helpers/_splitter.scss */ + content: ''; + display: block; + pointer-events: none; + position: absolute; + z-index: 2; + border-top: 1px dotted #0d0d0d; + top: 2px; + left: 5px; + right: 5px; + height: 1px; } +/* line 65, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane { top: 0; bottom: 0; } - /* line 64, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 68, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane.left { right: auto; } - /* line 67, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane.right { left: auto; } -/* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 75, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical > .splitter { bottom: 0; cursor: col-resize; width: 5px; } - /* line 159, ../../../../general/res/sass/_mixins.scss */ - .split-layout.vertical > .splitter:before { - -moz-transition-property: "border-color"; - -o-transition-property: "border-color"; - -webkit-transition-property: "border-color"; - transition-property: "border-color"; - -moz-transition-duration: 0.75s; - -o-transition-duration: 0.75s; - -webkit-transition-duration: 0.75s; - transition-duration: 0.75s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - content: ''; - display: block; - height: auto; - pointer-events: none; - position: absolute; - z-index: 2; - border-left: 1px dotted #0d0d0d; - left: 2px; - bottom: 5px; - top: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ - .split-layout.vertical > .splitter:not(.disabled):hover:before { + /* line 79, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.vertical > .splitter:after { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; -webkit-transition-property: "border-color"; @@ -1028,13 +990,22 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; - border-color: #0099cc; } + content: ''; + display: block; + pointer-events: none; + position: absolute; + z-index: 2; + border-left: 1px dotted #0d0d0d; + left: 2px; + bottom: 5px; + top: 5px; + width: 1px; } -/* line 80, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 86, ../../../../general/res/sass/helpers/_splitter.scss */ .browse-area .splitter { - top: 34px; } + top: 0; } -/* line 84, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 90, ../../../../general/res/sass/helpers/_splitter.scss */ .edit-area .splitter { top: 0; } @@ -1343,10 +1314,8 @@ mct-container { * at runtime from the About dialog for additional information. *****************************************************************************/ /* line 25, ../../../../general/res/sass/controls/_buttons.scss */ -.s-btn, .s-menu-btn { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; +.s-btn, .s-menu-btn, +.s-icon-btn { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; @@ -1354,26 +1323,32 @@ mct-container { cursor: pointer; text-decoration: none; height: 25px; - line-height: 25px; + line-height: 25px; } + +/* line 34, ../../../../general/res/sass/controls/_buttons.scss */ +.s-btn, .s-menu-btn { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; padding: 0 7.5px; font-size: 0.7rem; } - /* line 35, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 39, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn .icon, .s-menu-btn .icon { font-size: 0.8rem; color: #0099cc; } - /* line 40, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 44, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn .title-label, .s-menu-btn .title-label { vertical-align: top; } - /* line 44, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 48, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.lg, .lg.s-menu-btn { font-size: 1rem; } - /* line 48, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 52, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.sm, .sm.s-menu-btn { padding: 0 5px; } - /* line 52, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 56, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.vsm, .vsm.s-menu-btn { padding: 0 2.5px; } - /* line 56, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 60, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.major, .major.s-menu-btn { background-color: #0099cc; -moz-border-radius: 3px; @@ -1402,17 +1377,17 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.major .icon, .major.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover { background: linear-gradient(#1ac6ff, #00bfff); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover > .icon, .major.s-menu-btn:not(.disabled):hover > .icon { color: white; } } - /* line 62, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 66, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn:not(.major), .s-menu-btn:not(.major) { background-color: #454545; -moz-border-radius: 3px; @@ -1441,20 +1416,20 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover { background: linear-gradient(#6b6b6b, #5e5e5e); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: #33ccff; } } - /* line 71, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 75, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.pause-play .icon:before, .pause-play.s-menu-btn .icon:before { content: "\0000F1"; } - /* line 74, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 78, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.pause-play.paused, .pause-play.paused.s-menu-btn { background-color: #c56f01; -moz-border-radius: 3px; @@ -1483,17 +1458,17 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover { background: linear-gradient(#fe9815, #f88c01); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu-btn:not(.disabled):hover > .icon { color: white; } } - /* line 76, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 80, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { -moz-animation-name: pulse; -webkit-animation-name: pulse; @@ -1510,23 +1485,110 @@ mct-container { -moz-animation-timing-function: ease-in-out; -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } - /* line 78, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 82, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.pause-play.paused .icon :before, .pause-play.paused.s-menu-btn .icon :before { content: "\0000EF"; } - /* line 86, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 90, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.show-thumbs .icon:before, .show-thumbs.s-menu-btn .icon:before { content: "\000039"; } -/* line 92, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 96, ../../../../general/res/sass/controls/_buttons.scss */ +.s-icon-btn { + color: #0099cc; } + /* line 99, ../../../../general/res/sass/controls/_buttons.scss */ + .s-icon-btn:hover { + color: #33ccff; } + +/* line 104, ../../../../general/res/sass/controls/_buttons.scss */ +.mini-tab { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + color: #595959; + cursor: pointer; + display: block; + position: absolute; + font-size: 12px; + line-height: 12px; + height: 12px; + width: 12px; } + /* line 120, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover { + color: #8c8c8c; } + /* line 122, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover:after { + color: #0099cc; } + /* line 127, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before, .mini-tab:after { + -moz-transition-property: color; + -o-transition-property: color; + -webkit-transition-property: color; + transition-property: color; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + display: block; + position: absolute; } + /* line 134, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + font-size: 7px; + height: 100%; + width: 7px; } + /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:after { + width: 100%; + height: 100%; } + /* line 151, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:before { + content: '\3c'; + left: -8px; } + /* line 155, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:hover:before { + left: -11px; } + /* line 160, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:before { + content: '\3e'; + left: 14px; } + /* line 164, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:hover:before { + left: 17px; } + +/* line 168, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { font-size: 0; } - /* line 98, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 174, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .s-btn, .l-btn-set .s-menu-btn { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; margin-left: 1px; } - /* line 104, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 180, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; @@ -1535,7 +1597,7 @@ mct-container { -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; margin-left: 0; } - /* line 111, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 187, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; @@ -1544,7 +1606,7 @@ mct-container { -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } -/* line 118, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 194, ../../../../general/res/sass/controls/_buttons.scss */ .paused:not(.s-btn):not(.s-menu-btn) { border-color: #c56f01 !important; color: #c56f01 !important; } @@ -1950,10 +2012,10 @@ label.checkbox.custom { auto: 0; bottom: auto; left: auto; } - /* line 313, ../../../../general/res/sass/controls/_controls.scss */ + /* line 311, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob:hover { background-color: #0099cc; } -/* line 324, ../../../../general/res/sass/controls/_controls.scss */ +/* line 322, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob-l { -moz-border-radius-topleft: 10px; -webkit-border-top-left-radius: 10px; @@ -1962,7 +2024,7 @@ label.checkbox.custom { -webkit-border-bottom-left-radius: 10px; border-bottom-left-radius: 10px; cursor: w-resize; } -/* line 328, ../../../../general/res/sass/controls/_controls.scss */ +/* line 326, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob-r { -moz-border-radius-topright: 10px; -webkit-border-top-right-radius: 10px; @@ -1971,7 +2033,7 @@ label.checkbox.custom { -webkit-border-bottom-right-radius: 10px; border-bottom-right-radius: 10px; cursor: e-resize; } -/* line 332, ../../../../general/res/sass/controls/_controls.scss */ +/* line 330, ../../../../general/res/sass/controls/_controls.scss */ .slider .range { -moz-transition-property: visibility, opacity, background-color, border-color; -o-transition-property: visibility, opacity, background-color, border-color; @@ -1994,12 +2056,12 @@ label.checkbox.custom { left: auto; height: auto; width: auto; } - /* line 343, ../../../../general/res/sass/controls/_controls.scss */ + /* line 341, ../../../../general/res/sass/controls/_controls.scss */ .slider .range:hover { background-color: rgba(0, 153, 204, 0.5); } /******************************************************** DATETIME PICKER */ -/* line 350, ../../../../general/res/sass/controls/_controls.scss */ +/* line 348, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker { -moz-user-select: -moz-none; -ms-user-select: none; @@ -2008,65 +2070,65 @@ label.checkbox.custom { font-size: 0.8rem; padding: 10px !important; width: 230px; } - /* line 356, ../../../../general/res/sass/controls/_controls.scss */ + /* line 354, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager { height: 15px; margin-bottom: 5px; position: relative; } - /* line 368, ../../../../general/res/sass/controls/_controls.scss */ + /* line 366, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager { width: 20px; } - /* line 371, ../../../../general/res/sass/controls/_controls.scss */ + /* line 369, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager.prev { right: auto; } - /* line 373, ../../../../general/res/sass/controls/_controls.scss */ + /* line 371, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager.prev:before { content: "\3c"; } - /* line 377, ../../../../general/res/sass/controls/_controls.scss */ + /* line 375, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager.next { left: auto; text-align: right; } - /* line 380, ../../../../general/res/sass/controls/_controls.scss */ + /* line 378, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager.next:before { content: "\3e"; } - /* line 385, ../../../../general/res/sass/controls/_controls.scss */ + /* line 383, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .val { text-align: center; left: 25px; right: 25px; } - /* line 391, ../../../../general/res/sass/controls/_controls.scss */ + /* line 389, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-calendar, .l-datetime-picker .l-time-selects { border-top: 1px solid rgba(153, 153, 153, 0.1); } - /* line 395, ../../../../general/res/sass/controls/_controls.scss */ + /* line 393, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-time-selects { line-height: 22px; } /******************************************************** CALENDAR */ -/* line 403, ../../../../general/res/sass/controls/_controls.scss */ +/* line 401, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row { display: -webkit-flex; display: flex; -webkit-flex-flow: row nowrap; flex-flow: row nowrap; margin-top: 1px; } - /* line 407, ../../../../general/res/sass/controls/_controls.scss */ + /* line 405, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row:first-child { margin-top: 0; } - /* line 410, ../../../../general/res/sass/controls/_controls.scss */ + /* line 408, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row li { -webkit-flex: 1 0; flex: 1 0; margin-left: 1px; padding: 5px; text-align: center; } - /* line 416, ../../../../general/res/sass/controls/_controls.scss */ + /* line 414, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row li:first-child { margin-left: 0; } - /* line 420, ../../../../general/res/sass/controls/_controls.scss */ + /* line 418, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-header li { color: #b3b3b3; } - /* line 423, ../../../../general/res/sass/controls/_controls.scss */ + /* line 421, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li { -moz-transition-property: background-color; -o-transition-property: background-color; @@ -2081,31 +2143,31 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; cursor: pointer; } - /* line 426, ../../../../general/res/sass/controls/_controls.scss */ + /* line 424, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li.in-month { background-color: #616161; } - /* line 429, ../../../../general/res/sass/controls/_controls.scss */ + /* line 427, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li .sub { color: #b3b3b3; font-size: 0.8em; } - /* line 433, ../../../../general/res/sass/controls/_controls.scss */ + /* line 431, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li.selected { background: #006080; color: #cccccc; } - /* line 436, ../../../../general/res/sass/controls/_controls.scss */ + /* line 434, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li.selected .sub { color: inherit; } - /* line 440, ../../../../general/res/sass/controls/_controls.scss */ + /* line 438, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li:hover { background-color: #0099cc; color: #fff; } - /* line 443, ../../../../general/res/sass/controls/_controls.scss */ + /* line 441, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li:hover .sub { color: inherit; } /******************************************************** BROWSER ELEMENTS */ @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 454, ../../../../general/res/sass/controls/_controls.scss */ + /* line 452, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -2120,7 +2182,7 @@ label.checkbox.custom { height: 10px; width: 10px; } - /* line 463, ../../../../general/res/sass/controls/_controls.scss */ + /* line 461, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzU5NTk1OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzRkNGQ0ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2134,7 +2196,7 @@ label.checkbox.custom { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } - /* line 472, ../../../../general/res/sass/controls/_controls.scss */ + /* line 470, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb:hover { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzVlNWU1ZSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzUyNTI1MiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2143,7 +2205,7 @@ label.checkbox.custom { background-image: -webkit-linear-gradient(#5e5e5e, #525252 20px); background-image: linear-gradient(#5e5e5e, #525252 20px); } - /* line 477, ../../../../general/res/sass/controls/_controls.scss */ + /* line 475, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-corner { background: rgba(0, 0, 0, 0.4); } } /***************************************************************************** @@ -2224,7 +2286,7 @@ label.checkbox.custom { left: 0; text-align: left; } /* line 57, ../../../../general/res/sass/controls/_menus.scss */ - .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { + .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .icon.s-icon-btn, .s-menu-btn .menu .icon.mini-tab, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { width: 12px; } /******************************************************** MENUS THEMSELVES */ @@ -2268,7 +2330,7 @@ label.checkbox.custom { .menu ul { margin: 0; padding: 0; } - /* line 329, ../../../../general/res/sass/_mixins.scss */ + /* line 331, ../../../../general/res/sass/_mixins.scss */ .menu ul li { list-style-type: none; margin: 0; @@ -2512,8 +2574,10 @@ mct-include.l-time-controller { mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .lbl { color: #666666; } /* line 69, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .ui-symbol.icon, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.s-icon-btn, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.pager { font-size: 11px; @@ -2820,7 +2884,7 @@ mct-include.l-time-controller { padding: 0 3px; position: relative; height: 150px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .form .form-row .selector-list.error { background: rgba(255, 0, 0, 0.5); } /* line 124, ../../../../general/res/sass/forms/_elems.scss */ @@ -2877,7 +2941,7 @@ input[type="text"] { color: #cccccc; outline: none; padding: 0 3px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ input[type="text"].error { background: rgba(255, 0, 0, 0.5); } /* line 172, ../../../../general/res/sass/forms/_elems.scss */ @@ -2905,7 +2969,7 @@ textarea { position: absolute; height: 100%; width: 100%; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ textarea.error { background: rgba(255, 0, 0, 0.5); } @@ -2964,14 +3028,14 @@ textarea { overflow: hidden; position: relative; line-height: 22px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .select .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .select:not(.disabled):hover { background: linear-gradient(#6b6b6b, #5e5e5e); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .select:not(.disabled):hover > .icon { color: #33ccff; } } /* line 31, ../../../../general/res/sass/forms/_selects.scss */ @@ -3053,7 +3117,7 @@ textarea { max-height: 400px; overflow: auto; padding: 5px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .channel-selector .treeview.error { background: rgba(255, 0, 0, 0.5); } /* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ @@ -3219,7 +3283,7 @@ span.req { padding: 0 3px; background: #3b3b3b; border-bottom: 1px solid #4d4d4d; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .filter input.filter.error, .filter input.t-filter-input.error, .t-filter input.filter.error, @@ -3235,8 +3299,10 @@ span.req { .t-filter input.t-filter-input:not(.ng-dirty) + .t-a-clear { display: none; } /* line 42, ../../../../general/res/sass/forms/_filter.scss */ -.filter .icon.ui-symbol, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, +.filter .icon.ui-symbol, .filter .icon.s-icon-btn, .filter .icon.mini-tab, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, .t-filter .icon.ui-symbol, +.t-filter .icon.s-icon-btn, +.t-filter .icon.mini-tab, .t-filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .t-filter .icon.pager { -moz-border-radius: 3px; @@ -3249,14 +3315,18 @@ span.req { padding: 0px 5px; vertical-align: middle; } /* line 50, ../../../../general/res/sass/forms/_filter.scss */ - .filter .icon.ui-symbol:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, + .filter .icon.ui-symbol:hover, .filter .icon.s-icon-btn:hover, .filter .icon.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, .t-filter .icon.ui-symbol:hover, + .t-filter .icon.s-icon-btn:hover, + .t-filter .icon.mini-tab:hover, .t-filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .t-filter .icon.pager:hover { background: rgba(255, 255, 255, 0.1); } /* line 54, ../../../../general/res/sass/forms/_filter.scss */ -.filter .s-a-clear.ui-symbol, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, +.filter .s-a-clear.ui-symbol, .filter .s-a-clear.s-icon-btn, .filter .s-a-clear.mini-tab, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, .t-filter .s-a-clear.ui-symbol, +.t-filter .s-a-clear.s-icon-btn, +.t-filter .s-a-clear.mini-tab, .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager { -moz-border-radius: 3px; @@ -3282,8 +3352,10 @@ span.req { text-align: center; z-index: 5; } /* line 74, ../../../../general/res/sass/forms/_filter.scss */ - .filter .s-a-clear.ui-symbol:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, + .filter .s-a-clear.ui-symbol:hover, .filter .s-a-clear.s-icon-btn:hover, .filter .s-a-clear.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, .t-filter .s-a-clear.ui-symbol:hover, + .t-filter .s-a-clear.s-icon-btn:hover, + .t-filter .s-a-clear.mini-tab:hover, .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager:hover { filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); @@ -3551,7 +3623,29 @@ span.req { .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 257, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane { + top: 5px; + z-index: 2; } + /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { + left: -30px; + /* &:hover { + left: $paneOffset - 2; + }*/ } + /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { + content: 'F'; } + /* line 274, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { + right: -25px; + /* &:hover { + right: $xpos - 2; + }*/ } + /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { + content: 'F'; } + /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3564,31 +3658,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 271, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 281, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 285, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 296, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 299, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3598,11 +3692,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3614,12 +3708,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 327, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3635,41 +3729,41 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 335, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 337, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 345, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 348, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 359, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 388, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 363, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 391, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.left.treeview { opacity: 0; } - /* line 372, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.right-repr { left: 0 !important; } -/* line 386, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-showtree .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -3775,42 +3869,45 @@ span.req { left: 40% !important; } /* line 97, ../../../../general/res/sass/mobile/_layout.scss */ - .mobile-menu-icon { + .toggle-tree { font-size: 110%; position: absolute; top: 12px; left: 10px; } + /* line 102, ../../../../general/res/sass/mobile/_layout.scss */ + .toggle-tree:before { + content: 'm' !important; } - /* line 104, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar { left: 30px !important; } - /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .context-available { opacity: 1 !important; } - /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 113, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher { margin-right: 0 !important; } - /* line 112, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 115, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher .title-label { display: none; } - /* line 119, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 122, ../../../../general/res/sass/mobile/_layout.scss */ .tree-holder { overflow-x: hidden !important; } - /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 126, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-disable-select { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 128, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 131, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-hide, .mobile-hide-important { display: none !important; } - /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 136, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-hide { pointer-events: none; -moz-transition-property: opacity; @@ -3827,7 +3924,7 @@ span.req { transition-timing-function: ease-in-out; opacity: 0; } - /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 141, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-unhide { pointer-events: all; -moz-transition-property: opacity; @@ -3844,19 +3941,19 @@ span.req { transition-timing-function: ease-in-out; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.left.treeview { width: 90% !important; } - /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr { left: 0 !important; transform: translateX(90%); -webkit-transform: translateX(90%); } - /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 156, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 164, ../../../../general/res/sass/mobile/_layout.scss */ .desktop-hide { display: none; } } /***************************************************************************** @@ -4297,14 +4394,14 @@ span.req { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover { background: linear-gradient(#a6a6a6, #999999); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4389,7 +4486,7 @@ ul.tree { -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 329, ../../../../general/res/sass/_mixins.scss */ + /* line 331, ../../../../general/res/sass/_mixins.scss */ ul.tree li { list-style-type: none; margin: 0; @@ -5657,14 +5754,14 @@ table { margin-bottom: 3px; margin-right: 3px; position: relative; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover { background: linear-gradient(#666666, #595959); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover > .icon { color: #33ccff; } } /* line 45, ../../../../general/res/sass/items/_item.scss */ @@ -5799,14 +5896,14 @@ table { transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; color: #80dfff; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected:not(.disabled):hover { background: linear-gradient(#1ac6ff, #00bfff); } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected:not(.disabled):hover > .icon { color: #33ccff; } } /* line 137, ../../../../general/res/sass/items/_item.scss */ @@ -6251,7 +6348,7 @@ table { left: 0; z-index: 1; } /* line 22, ../../../../general/res/sass/features/_time-display.scss */ - .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { + .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .direction.s-icon-btn, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { font-size: 0.8em; } /* line 26, ../../../../general/res/sass/features/_time-display.scss */ .l-time-display.l-timer:hover .l-elem.l-value { diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index a2aa51e183..b1efcd8cff 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -313,21 +313,17 @@ a.disabled { src: url("../../../../general/res/fonts/symbols/wtdsymbols.eot?#iefix") format("embedded-opentype"), url("../../../../general/res/fonts/symbols/wtdsymbols.woff") format("woff"), url("../../../../general/res/fonts/symbols/wtdsymbols.ttf") format("truetype"), url("../../../../general/res/fonts/symbols/wtdsymbols.svg#armataregular") format("svg"); font-weight: normal; font-style: normal; } -/* line 37, ../../../../general/res/sass/_global.scss */ -.ui-symbol, .l-datetime-picker .l-month-year-pager .pager { - font-family: 'symbolsfont'; } - /************************** HTML ENTITIES */ -/* line 42, ../../../../general/res/sass/_global.scss */ +/* line 38, ../../../../general/res/sass/_global.scss */ a { color: #999; cursor: pointer; text-decoration: none; } - /* line 46, ../../../../general/res/sass/_global.scss */ + /* line 42, ../../../../general/res/sass/_global.scss */ a:hover { color: #0099cc; } -/* line 51, ../../../../general/res/sass/_global.scss */ +/* line 47, ../../../../general/res/sass/_global.scss */ body, html { -webkit-font-smoothing: subpixel-antialiased; -moz-osx-font-smoothing: grayscale; @@ -339,24 +335,24 @@ body, html { width: 100%; overflow: hidden; } -/* line 64, ../../../../general/res/sass/_global.scss */ +/* line 60, ../../../../general/res/sass/_global.scss */ em { font-style: normal; } -/* line 68, ../../../../general/res/sass/_global.scss */ +/* line 64, ../../../../general/res/sass/_global.scss */ input, textarea { font-family: Helvetica, Arial, sans-serif; } -/* line 72, ../../../../general/res/sass/_global.scss */ +/* line 68, ../../../../general/res/sass/_global.scss */ input[type="text"] { vertical-align: baseline; padding: 3px 5px !important; } -/* line 77, ../../../../general/res/sass/_global.scss */ +/* line 73, ../../../../general/res/sass/_global.scss */ h1, h2, h3 { margin: 0; } -/* line 81, ../../../../general/res/sass/_global.scss */ +/* line 77, ../../../../general/res/sass/_global.scss */ h1 { font-size: 1.7em; font-weight: normal !important; @@ -364,15 +360,15 @@ h1 { margin-bottom: 20px; margin-top: 0; } -/* line 89, ../../../../general/res/sass/_global.scss */ +/* line 85, ../../../../general/res/sass/_global.scss */ p { margin-bottom: 10px; } -/* line 93, ../../../../general/res/sass/_global.scss */ +/* line 89, ../../../../general/res/sass/_global.scss */ mct-container { display: block; } -/* line 97, ../../../../general/res/sass/_global.scss */ +/* line 93, ../../../../general/res/sass/_global.scss */ .abs, .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .val, .s-menu-btn span.l-click-area { position: absolute; @@ -383,50 +379,50 @@ mct-container { height: auto; width: auto; } -/* line 107, ../../../../general/res/sass/_global.scss */ +/* line 103, ../../../../general/res/sass/_global.scss */ .code, .codehilite { font-family: "Lucida Console", monospace; font-size: 0.7em; line-height: 150%; white-space: pre; } -/* line 114, ../../../../general/res/sass/_global.scss */ +/* line 110, ../../../../general/res/sass/_global.scss */ .codehilite { background-color: rgba(102, 102, 102, 0.1); padding: 1em; } -/* line 120, ../../../../general/res/sass/_global.scss */ +/* line 116, ../../../../general/res/sass/_global.scss */ .align-right { text-align: right; } -/* line 124, ../../../../general/res/sass/_global.scss */ +/* line 120, ../../../../general/res/sass/_global.scss */ .centered { text-align: center; } -/* line 128, ../../../../general/res/sass/_global.scss */ +/* line 124, ../../../../general/res/sass/_global.scss */ .scrolling { overflow: auto; } -/* line 132, ../../../../general/res/sass/_global.scss */ +/* line 128, ../../../../general/res/sass/_global.scss */ .vscroll { overflow-y: auto; } -/* line 136, ../../../../general/res/sass/_global.scss */ +/* line 132, ../../../../general/res/sass/_global.scss */ .no-margin { margin: 0; } -/* line 140, ../../../../general/res/sass/_global.scss */ +/* line 136, ../../../../general/res/sass/_global.scss */ .ds { -moz-box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; } -/* line 144, ../../../../general/res/sass/_global.scss */ +/* line 140, ../../../../general/res/sass/_global.scss */ .hide, .hidden { display: none !important; } -/* line 149, ../../../../general/res/sass/_global.scss */ +/* line 145, ../../../../general/res/sass/_global.scss */ .sep { color: rgba(255, 255, 255, 0.2); } @@ -606,53 +602,56 @@ mct-container { border-top: 5px solid #0099cc; border-right: 5px solid transparent; } -/* line 32, ../../../../general/res/sass/_icons.scss */ -.ui-symbol.type-icon, .l-datetime-picker .l-month-year-pager .type-icon.pager { - color: #b3b3b3; } -/* line 35, ../../../../general/res/sass/_icons.scss */ -.ui-symbol.icon, .l-datetime-picker .l-month-year-pager .icon.pager { - color: #0099cc; } - /* line 37, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.alert, .l-datetime-picker .l-month-year-pager .icon.alert.pager { - color: #ff3c00; } - /* line 39, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.alert:hover, .l-datetime-picker .l-month-year-pager .icon.alert.pager:hover { - color: #ff8a66; } - /* line 43, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.major, .l-datetime-picker .l-month-year-pager .icon.major.pager { - font-size: 1.65em; } -/* line 47, ../../../../general/res/sass/_icons.scss */ -.ui-symbol.icon-calendar:after, .l-datetime-picker .l-month-year-pager .icon-calendar.pager:after { - content: "\e605"; } +/* line 31, ../../../../general/res/sass/_icons.scss */ +.ui-symbol, .s-icon-btn, .mini-tab, .l-datetime-picker .l-month-year-pager .pager { + font-family: 'symbolsfont'; } + /* line 33, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.type-icon, .type-icon.s-icon-btn, .type-icon.mini-tab, .l-datetime-picker .l-month-year-pager .type-icon.pager { + color: #b3b3b3; } + /* line 36, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon, .icon.s-icon-btn, .icon.mini-tab, .l-datetime-picker .l-month-year-pager .icon.pager { + color: #0099cc; } + /* line 38, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon.alert, .icon.alert.s-icon-btn, .icon.alert.mini-tab, .l-datetime-picker .l-month-year-pager .icon.alert.pager { + color: #ff3c00; } + /* line 40, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon.alert:hover, .icon.alert.s-icon-btn:hover, .icon.alert.mini-tab:hover, .l-datetime-picker .l-month-year-pager .icon.alert.pager:hover { + color: #ff8a66; } + /* line 44, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon.major, .icon.major.s-icon-btn, .icon.major.mini-tab, .l-datetime-picker .l-month-year-pager .icon.major.pager { + font-size: 1.65em; } + /* line 48, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon-calendar:after, .icon-calendar.s-icon-btn:after, .icon-calendar.mini-tab:after, .l-datetime-picker .l-month-year-pager .icon-calendar.pager:after { + content: "\e605"; } -/* line 52, ../../../../general/res/sass/_icons.scss */ -.bar .ui-symbol, .bar .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .bar .pager { +/* line 53, ../../../../general/res/sass/_icons.scss */ +.bar .ui-symbol, .bar .s-icon-btn, .bar .mini-tab, .bar .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .bar .pager { display: inline-block; } -/* line 56, ../../../../general/res/sass/_icons.scss */ +/* line 57, ../../../../general/res/sass/_icons.scss */ .invoke-menu { text-shadow: none; display: inline-block; } -/* line 61, ../../../../general/res/sass/_icons.scss */ +/* line 62, ../../../../general/res/sass/_icons.scss */ .s-menu-btn .invoke-menu, .icon.major .invoke-menu { margin-left: 3px; } -/* line 66, ../../../../general/res/sass/_icons.scss */ +/* line 67, ../../../../general/res/sass/_icons.scss */ .menu .type-icon, .tree-item .type-icon, .super-menu.menu .type-icon { position: absolute; } -/* line 76, ../../../../general/res/sass/_icons.scss */ +/* line 77, ../../../../general/res/sass/_icons.scss */ .l-icon-link:before { content: "\f4"; } -/* line 80, ../../../../general/res/sass/_icons.scss */ +/* line 81, ../../../../general/res/sass/_icons.scss */ .l-icon-alert { display: none !important; } - /* line 82, ../../../../general/res/sass/_icons.scss */ + /* line 83, ../../../../general/res/sass/_icons.scss */ .l-icon-alert:before { color: #ff3c00; content: "!"; } @@ -907,58 +906,36 @@ mct-container { -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; - overflow: hidden; position: absolute; z-index: 1; } - /* line 33, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 32, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout .splitter:hover { background-color: #0099cc; } -/* line 38, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 34, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout .splitter:hover:after { + border-color: #fcfcfc !important; } +/* line 40, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal { overflow: hidden; } - /* line 41, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 43, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane { left: 0; right: 0; } - /* line 44, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 46, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane.top { bottom: auto; } - /* line 47, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 49, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane.bottom { top: auto; } - /* line 51, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 53, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal > .splitter { cursor: row-resize; left: 0; right: 0; width: auto; height: 5px; } - /* line 159, ../../../../general/res/sass/_mixins.scss */ - .split-layout.horizontal > .splitter:before { - -moz-transition-property: "border-color"; - -o-transition-property: "border-color"; - -webkit-transition-property: "border-color"; - transition-property: "border-color"; - -moz-transition-duration: 0.75s; - -o-transition-duration: 0.75s; - -webkit-transition-duration: 0.75s; - transition-duration: 0.75s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - content: ''; - display: block; - height: auto; - pointer-events: none; - position: absolute; - z-index: 2; - border-top: 1px dotted #d6d6d6; - top: 2px; - left: 5px; - right: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ - .split-layout.horizontal > .splitter:not(.disabled):hover:before { + /* line 54, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.horizontal > .splitter:after { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; -webkit-transition-property: "border-color"; @@ -971,48 +948,33 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; - border-color: #fcfcfc; } -/* line 61, ../../../../general/res/sass/helpers/_splitter.scss */ + content: ''; + display: block; + pointer-events: none; + position: absolute; + z-index: 2; + border-top: 1px dotted #d6d6d6; + top: 2px; + left: 5px; + right: 5px; + height: 1px; } +/* line 65, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane { top: 0; bottom: 0; } - /* line 64, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 68, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane.left { right: auto; } - /* line 67, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane.right { left: auto; } -/* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 75, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical > .splitter { bottom: 0; cursor: col-resize; width: 5px; } - /* line 159, ../../../../general/res/sass/_mixins.scss */ - .split-layout.vertical > .splitter:before { - -moz-transition-property: "border-color"; - -o-transition-property: "border-color"; - -webkit-transition-property: "border-color"; - transition-property: "border-color"; - -moz-transition-duration: 0.75s; - -o-transition-duration: 0.75s; - -webkit-transition-duration: 0.75s; - transition-duration: 0.75s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - content: ''; - display: block; - height: auto; - pointer-events: none; - position: absolute; - z-index: 2; - border-left: 1px dotted #d6d6d6; - left: 2px; - bottom: 5px; - top: 5px; } - /* line 181, ../../../../general/res/sass/_mixins.scss */ - .split-layout.vertical > .splitter:not(.disabled):hover:before { + /* line 79, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.vertical > .splitter:after { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; -webkit-transition-property: "border-color"; @@ -1025,13 +987,22 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; - border-color: #fcfcfc; } + content: ''; + display: block; + pointer-events: none; + position: absolute; + z-index: 2; + border-left: 1px dotted #d6d6d6; + left: 2px; + bottom: 5px; + top: 5px; + width: 1px; } -/* line 80, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 86, ../../../../general/res/sass/helpers/_splitter.scss */ .browse-area .splitter { - top: 34px; } + top: 0; } -/* line 84, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 90, ../../../../general/res/sass/helpers/_splitter.scss */ .edit-area .splitter { top: 0; } @@ -1340,10 +1311,8 @@ mct-container { * at runtime from the About dialog for additional information. *****************************************************************************/ /* line 25, ../../../../general/res/sass/controls/_buttons.scss */ -.s-btn, .s-menu-btn { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; +.s-btn, .s-menu-btn, +.s-icon-btn { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; @@ -1351,26 +1320,32 @@ mct-container { cursor: pointer; text-decoration: none; height: 25px; - line-height: 25px; + line-height: 25px; } + +/* line 34, ../../../../general/res/sass/controls/_buttons.scss */ +.s-btn, .s-menu-btn { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; padding: 0 7.5px; font-size: 0.7rem; } - /* line 35, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 39, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn .icon, .s-menu-btn .icon { font-size: 0.8rem; color: #0099cc; } - /* line 40, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 44, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn .title-label, .s-menu-btn .title-label { vertical-align: top; } - /* line 44, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 48, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.lg, .lg.s-menu-btn { font-size: 1rem; } - /* line 48, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 52, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.sm, .sm.s-menu-btn { padding: 0 5px; } - /* line 52, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 56, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.vsm, .vsm.s-menu-btn { padding: 0 2.5px; } - /* line 56, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 60, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.major, .major.s-menu-btn { background-color: #0099cc; -moz-border-radius: 4px; @@ -1390,17 +1365,17 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.major .icon, .major.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover { background: deepskyblue; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover > .icon, .major.s-menu-btn:not(.disabled):hover > .icon { color: white; } } - /* line 62, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 66, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn:not(.major), .s-menu-btn:not(.major) { background-color: #969696; -moz-border-radius: 4px; @@ -1420,20 +1395,20 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon { color: #eee; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover { background: #0099cc; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: white; } } - /* line 71, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 75, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.pause-play .icon:before, .pause-play.s-menu-btn .icon:before { content: "\0000F1"; } - /* line 74, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 78, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.pause-play.paused, .pause-play.paused.s-menu-btn { background-color: #ff9900; -moz-border-radius: 4px; @@ -1453,17 +1428,17 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover { background: #ffad33; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu-btn:not(.disabled):hover > .icon { color: white; } } - /* line 76, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 80, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { -moz-animation-name: pulse; -webkit-animation-name: pulse; @@ -1480,23 +1455,110 @@ mct-container { -moz-animation-timing-function: ease-in-out; -webkit-animation-timing-function: ease-in-out; animation-timing-function: ease-in-out; } - /* line 78, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 82, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.pause-play.paused .icon :before, .pause-play.paused.s-menu-btn .icon :before { content: "\0000EF"; } - /* line 86, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 90, ../../../../general/res/sass/controls/_buttons.scss */ .s-btn.show-thumbs .icon:before, .show-thumbs.s-menu-btn .icon:before { content: "\000039"; } -/* line 92, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 96, ../../../../general/res/sass/controls/_buttons.scss */ +.s-icon-btn { + color: #eee; } + /* line 99, ../../../../general/res/sass/controls/_buttons.scss */ + .s-icon-btn:hover { + color: white; } + +/* line 104, ../../../../general/res/sass/controls/_buttons.scss */ +.mini-tab { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + color: #d6d6d6; + cursor: pointer; + display: block; + position: absolute; + font-size: 12px; + line-height: 12px; + height: 12px; + width: 12px; } + /* line 120, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover { + color: #a3a3a3; } + /* line 122, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover:after { + color: #0099cc; } + /* line 127, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before, .mini-tab:after { + -moz-transition-property: color; + -o-transition-property: color; + -webkit-transition-property: color; + transition-property: color; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + display: block; + position: absolute; } + /* line 134, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + font-size: 7px; + height: 100%; + width: 7px; } + /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:after { + width: 100%; + height: 100%; } + /* line 151, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:before { + content: '\3c'; + left: -8px; } + /* line 155, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:hover:before { + left: -11px; } + /* line 160, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:before { + content: '\3e'; + left: 14px; } + /* line 164, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:hover:before { + left: 17px; } + +/* line 168, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { font-size: 0; } - /* line 98, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 174, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .s-btn, .l-btn-set .s-menu-btn { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; margin-left: 1px; } - /* line 104, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 180, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; @@ -1505,7 +1567,7 @@ mct-container { -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; margin-left: 0; } - /* line 111, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 187, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; @@ -1514,7 +1576,7 @@ mct-container { -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } -/* line 118, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 194, ../../../../general/res/sass/controls/_buttons.scss */ .paused:not(.s-btn):not(.s-menu-btn) { border-color: #ff9900 !important; color: #ff9900 !important; } @@ -1920,10 +1982,10 @@ label.checkbox.custom { auto: 0; bottom: auto; left: auto; } - /* line 313, ../../../../general/res/sass/controls/_controls.scss */ + /* line 311, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob:hover { background-color: rgba(0, 153, 204, 0.7); } -/* line 324, ../../../../general/res/sass/controls/_controls.scss */ +/* line 322, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob-l { -moz-border-radius-topleft: 10px; -webkit-border-top-left-radius: 10px; @@ -1932,7 +1994,7 @@ label.checkbox.custom { -webkit-border-bottom-left-radius: 10px; border-bottom-left-radius: 10px; cursor: w-resize; } -/* line 328, ../../../../general/res/sass/controls/_controls.scss */ +/* line 326, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob-r { -moz-border-radius-topright: 10px; -webkit-border-top-right-radius: 10px; @@ -1941,7 +2003,7 @@ label.checkbox.custom { -webkit-border-bottom-right-radius: 10px; border-bottom-right-radius: 10px; cursor: e-resize; } -/* line 332, ../../../../general/res/sass/controls/_controls.scss */ +/* line 330, ../../../../general/res/sass/controls/_controls.scss */ .slider .range { -moz-transition-property: visibility, opacity, background-color, border-color; -o-transition-property: visibility, opacity, background-color, border-color; @@ -1964,12 +2026,12 @@ label.checkbox.custom { left: auto; height: auto; width: auto; } - /* line 343, ../../../../general/res/sass/controls/_controls.scss */ + /* line 341, ../../../../general/res/sass/controls/_controls.scss */ .slider .range:hover { background-color: rgba(0, 153, 204, 0.4); } /******************************************************** DATETIME PICKER */ -/* line 350, ../../../../general/res/sass/controls/_controls.scss */ +/* line 348, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker { -moz-user-select: -moz-none; -ms-user-select: none; @@ -1978,65 +2040,65 @@ label.checkbox.custom { font-size: 0.8rem; padding: 10px !important; width: 230px; } - /* line 356, ../../../../general/res/sass/controls/_controls.scss */ + /* line 354, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager { height: 15px; margin-bottom: 5px; position: relative; } - /* line 368, ../../../../general/res/sass/controls/_controls.scss */ + /* line 366, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager { width: 20px; } - /* line 371, ../../../../general/res/sass/controls/_controls.scss */ + /* line 369, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager.prev { right: auto; } - /* line 373, ../../../../general/res/sass/controls/_controls.scss */ + /* line 371, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager.prev:before { content: "\3c"; } - /* line 377, ../../../../general/res/sass/controls/_controls.scss */ + /* line 375, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager.next { left: auto; text-align: right; } - /* line 380, ../../../../general/res/sass/controls/_controls.scss */ + /* line 378, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .pager.next:before { content: "\3e"; } - /* line 385, ../../../../general/res/sass/controls/_controls.scss */ + /* line 383, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-month-year-pager .val { text-align: center; left: 25px; right: 25px; } - /* line 391, ../../../../general/res/sass/controls/_controls.scss */ + /* line 389, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-calendar, .l-datetime-picker .l-time-selects { border-top: 1px solid rgba(102, 102, 102, 0.2); } - /* line 395, ../../../../general/res/sass/controls/_controls.scss */ + /* line 393, ../../../../general/res/sass/controls/_controls.scss */ .l-datetime-picker .l-time-selects { line-height: 22px; } /******************************************************** CALENDAR */ -/* line 403, ../../../../general/res/sass/controls/_controls.scss */ +/* line 401, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row { display: -webkit-flex; display: flex; -webkit-flex-flow: row nowrap; flex-flow: row nowrap; margin-top: 1px; } - /* line 407, ../../../../general/res/sass/controls/_controls.scss */ + /* line 405, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row:first-child { margin-top: 0; } - /* line 410, ../../../../general/res/sass/controls/_controls.scss */ + /* line 408, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row li { -webkit-flex: 1 0; flex: 1 0; margin-left: 1px; padding: 5px; text-align: center; } - /* line 416, ../../../../general/res/sass/controls/_controls.scss */ + /* line 414, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row li:first-child { margin-left: 0; } - /* line 420, ../../../../general/res/sass/controls/_controls.scss */ + /* line 418, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-header li { color: #999999; } - /* line 423, ../../../../general/res/sass/controls/_controls.scss */ + /* line 421, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li { -moz-transition-property: background-color; -o-transition-property: background-color; @@ -2051,31 +2113,31 @@ label.checkbox.custom { -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; cursor: pointer; } - /* line 426, ../../../../general/res/sass/controls/_controls.scss */ + /* line 424, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li.in-month { background-color: #f2f2f2; } - /* line 429, ../../../../general/res/sass/controls/_controls.scss */ + /* line 427, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li .sub { color: #999999; font-size: 0.8em; } - /* line 433, ../../../../general/res/sass/controls/_controls.scss */ + /* line 431, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li.selected { background: #1ac6ff; color: #fcfcfc; } - /* line 436, ../../../../general/res/sass/controls/_controls.scss */ + /* line 434, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li.selected .sub { color: inherit; } - /* line 440, ../../../../general/res/sass/controls/_controls.scss */ + /* line 438, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li:hover { background-color: #0099cc; color: #fff; } - /* line 443, ../../../../general/res/sass/controls/_controls.scss */ + /* line 441, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li:hover .sub { color: inherit; } /******************************************************** BROWSER ELEMENTS */ @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 454, ../../../../general/res/sass/controls/_controls.scss */ + /* line 452, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -2090,7 +2152,7 @@ label.checkbox.custom { height: 10px; width: 10px; } - /* line 463, ../../../../general/res/sass/controls/_controls.scss */ + /* line 461, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzg5ODk4OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzdkN2Q3ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2104,7 +2166,7 @@ label.checkbox.custom { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } - /* line 472, ../../../../general/res/sass/controls/_controls.scss */ + /* line 470, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-thumb:hover { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwYWNlNiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwOTljYyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; @@ -2113,7 +2175,7 @@ label.checkbox.custom { background-image: -webkit-linear-gradient(#00ace6, #0099cc 20px); background-image: linear-gradient(#00ace6, #0099cc 20px); } - /* line 477, ../../../../general/res/sass/controls/_controls.scss */ + /* line 475, ../../../../general/res/sass/controls/_controls.scss */ ::-webkit-scrollbar-corner { background: rgba(0, 0, 0, 0.1); } } /***************************************************************************** @@ -2194,7 +2256,7 @@ label.checkbox.custom { left: 0; text-align: left; } /* line 57, ../../../../general/res/sass/controls/_menus.scss */ - .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { + .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .icon.s-icon-btn, .s-menu-btn .menu .icon.mini-tab, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { width: 12px; } /******************************************************** MENUS THEMSELVES */ @@ -2232,7 +2294,7 @@ label.checkbox.custom { .menu ul { margin: 0; padding: 0; } - /* line 329, ../../../../general/res/sass/_mixins.scss */ + /* line 331, ../../../../general/res/sass/_mixins.scss */ .menu ul li { list-style-type: none; margin: 0; @@ -2476,8 +2538,10 @@ mct-include.l-time-controller { mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .lbl { color: #999999; } /* line 69, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .ui-symbol.icon, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.s-icon-btn, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.pager { font-size: 11px; @@ -2784,7 +2848,7 @@ mct-include.l-time-controller { padding: 0 3px; position: relative; height: 150px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .form .form-row .selector-list.error { background: rgba(255, 0, 0, 0.5); } /* line 124, ../../../../general/res/sass/forms/_elems.scss */ @@ -2841,7 +2905,7 @@ input[type="text"] { color: #666; outline: none; padding: 0 3px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ input[type="text"].error { background: rgba(255, 0, 0, 0.5); } /* line 172, ../../../../general/res/sass/forms/_elems.scss */ @@ -2869,7 +2933,7 @@ textarea { position: absolute; height: 100%; width: 100%; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ textarea.error { background: rgba(255, 0, 0, 0.5); } @@ -2918,7 +2982,7 @@ textarea { overflow: hidden; position: relative; line-height: 22px; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .select .icon { color: #eee; } /* line 31, ../../../../general/res/sass/forms/_selects.scss */ @@ -3000,7 +3064,7 @@ textarea { max-height: 400px; overflow: auto; padding: 5px; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .channel-selector .treeview.error { background: rgba(255, 0, 0, 0.5); } /* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ @@ -3166,7 +3230,7 @@ span.req { padding: 0 3px; background: white; border-bottom: 1px solid white; } - /* line 296, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .filter input.filter.error, .filter input.t-filter-input.error, .t-filter input.filter.error, @@ -3182,8 +3246,10 @@ span.req { .t-filter input.t-filter-input:not(.ng-dirty) + .t-a-clear { display: none; } /* line 42, ../../../../general/res/sass/forms/_filter.scss */ -.filter .icon.ui-symbol, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, +.filter .icon.ui-symbol, .filter .icon.s-icon-btn, .filter .icon.mini-tab, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, .t-filter .icon.ui-symbol, +.t-filter .icon.s-icon-btn, +.t-filter .icon.mini-tab, .t-filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .t-filter .icon.pager { -moz-border-radius: 4px; @@ -3196,14 +3262,18 @@ span.req { padding: 0px 5px; vertical-align: middle; } /* line 50, ../../../../general/res/sass/forms/_filter.scss */ - .filter .icon.ui-symbol:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, + .filter .icon.ui-symbol:hover, .filter .icon.s-icon-btn:hover, .filter .icon.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, .t-filter .icon.ui-symbol:hover, + .t-filter .icon.s-icon-btn:hover, + .t-filter .icon.mini-tab:hover, .t-filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .t-filter .icon.pager:hover { background: rgba(255, 255, 255, 0.1); } /* line 54, ../../../../general/res/sass/forms/_filter.scss */ -.filter .s-a-clear.ui-symbol, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, +.filter .s-a-clear.ui-symbol, .filter .s-a-clear.s-icon-btn, .filter .s-a-clear.mini-tab, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, .t-filter .s-a-clear.ui-symbol, +.t-filter .s-a-clear.s-icon-btn, +.t-filter .s-a-clear.mini-tab, .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager { -moz-border-radius: 4px; @@ -3229,8 +3299,10 @@ span.req { text-align: center; z-index: 5; } /* line 74, ../../../../general/res/sass/forms/_filter.scss */ - .filter .s-a-clear.ui-symbol:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, + .filter .s-a-clear.ui-symbol:hover, .filter .s-a-clear.s-icon-btn:hover, .filter .s-a-clear.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, .t-filter .s-a-clear.ui-symbol:hover, + .t-filter .s-a-clear.s-icon-btn:hover, + .t-filter .s-a-clear.mini-tab:hover, .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager:hover { filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); @@ -3498,7 +3570,29 @@ span.req { .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 257, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane { + top: 5px; + z-index: 2; } + /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { + left: -30px; + /* &:hover { + left: $paneOffset - 2; + }*/ } + /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { + content: 'F'; } + /* line 274, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { + right: -25px; + /* &:hover { + right: $xpos - 2; + }*/ } + /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { + content: 'F'; } + /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3511,31 +3605,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 271, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 281, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 285, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 296, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 299, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3545,11 +3639,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3561,12 +3655,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 327, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3582,41 +3676,41 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 335, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 337, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 345, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 348, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 359, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 388, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 363, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 391, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.left.treeview { opacity: 0; } - /* line 372, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.right-repr { left: 0 !important; } -/* line 386, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-showtree .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -3722,42 +3816,45 @@ span.req { left: 40% !important; } /* line 97, ../../../../general/res/sass/mobile/_layout.scss */ - .mobile-menu-icon { + .toggle-tree { font-size: 110%; position: absolute; top: 12px; left: 10px; } + /* line 102, ../../../../general/res/sass/mobile/_layout.scss */ + .toggle-tree:before { + content: 'm' !important; } - /* line 104, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar { left: 30px !important; } - /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .context-available { opacity: 1 !important; } - /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 113, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher { margin-right: 0 !important; } - /* line 112, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 115, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher .title-label { display: none; } - /* line 119, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 122, ../../../../general/res/sass/mobile/_layout.scss */ .tree-holder { overflow-x: hidden !important; } - /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 126, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-disable-select { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 128, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 131, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-hide, .mobile-hide-important { display: none !important; } - /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 136, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-hide { pointer-events: none; -moz-transition-property: opacity; @@ -3774,7 +3871,7 @@ span.req { transition-timing-function: ease-in-out; opacity: 0; } - /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 141, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-unhide { pointer-events: all; -moz-transition-property: opacity; @@ -3791,19 +3888,19 @@ span.req { transition-timing-function: ease-in-out; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.left.treeview { width: 90% !important; } - /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr { left: 0 !important; transform: translateX(90%); -webkit-transform: translateX(90%); } - /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 156, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 164, ../../../../general/res/sass/mobile/_layout.scss */ .desktop-hide { display: none; } } /***************************************************************************** @@ -4226,14 +4323,14 @@ span.req { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover { background: #7d7d7d; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4318,7 +4415,7 @@ ul.tree { -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 329, ../../../../general/res/sass/_mixins.scss */ + /* line 331, ../../../../general/res/sass/_mixins.scss */ ul.tree li { list-style-type: none; margin: 0; @@ -5576,14 +5673,14 @@ table { margin-bottom: 3px; margin-right: 3px; position: relative; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 277, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover { background: #d0d0d0; } - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover > .icon { color: #33ccff; } } /* line 45, ../../../../general/res/sass/items/_item.scss */ @@ -5709,7 +5806,7 @@ table { transition: background, 0.25s; text-shadow: none; color: #80dfff; } - /* line 272, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected .icon { color: #eee; } /* line 137, ../../../../general/res/sass/items/_item.scss */ @@ -6154,7 +6251,7 @@ table { left: 0; z-index: 1; } /* line 22, ../../../../general/res/sass/features/_time-display.scss */ - .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { + .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .direction.s-icon-btn, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { font-size: 0.8em; } /* line 26, ../../../../general/res/sass/features/_time-display.scss */ .l-time-display.l-timer:hover .l-elem.l-value { From 3687cc5edd051a406fc9d0b00baea435a9b3b5f0 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 22 Oct 2015 18:45:47 -0700 Subject: [PATCH 130/488] [Frontend] Manual re-do of collapse/expand panes open #90 Fixed broken mobile styles; Removed commented out scss; --- .../commonUI/general/res/sass/_mixins.scss | 5 - .../general/res/sass/controls/_buttons.scss | 108 ++++---- .../general/res/sass/mobile/_layout.scss | 7 +- .../res/sass/user-environ/_layout.scss | 38 ++- .../espresso/res/css/theme-espresso.css | 255 +++++++++--------- .../themes/snow/res/css/theme-snow.css | 255 +++++++++--------- 6 files changed, 327 insertions(+), 341 deletions(-) diff --git a/platform/commonUI/general/res/sass/_mixins.scss b/platform/commonUI/general/res/sass/_mixins.scss index c3e62fee7a..1a1d651b6c 100644 --- a/platform/commonUI/general/res/sass/_mixins.scss +++ b/platform/commonUI/general/res/sass/_mixins.scss @@ -179,11 +179,6 @@ top: 5px; width: 1px; } - //} - //&:not(.disabled):hover:before { - // @include trans-prop-nice("border-color", 25ms); - // border-color: $colorGrippyInteriorHover; - //} } @mixin boxIncised($sVal: 0.6, $inset: 5px) { diff --git a/platform/commonUI/general/res/sass/controls/_buttons.scss b/platform/commonUI/general/res/sass/controls/_buttons.scss index 4932ff6fc3..b7ef4fe28b 100644 --- a/platform/commonUI/general/res/sass/controls/_buttons.scss +++ b/platform/commonUI/general/res/sass/controls/_buttons.scss @@ -104,64 +104,66 @@ $pad: $interiorMargin * $baseRatio; .mini-tab { // Meant to be used as pane hide/show control elements in concert with mct-splitter @extend .ui-symbol; - @include trans-prop-nice(left, 150ms); - //@include test(green); - $iconD: 12px; - $arwD: 7px; - $c: pullForward($colorBodyBg, 15%); - color: $c; - cursor: pointer; - display: block; - position: absolute; - font-size: $iconD; - line-height: $iconD; - height: $iconD; width: $iconD; - - &:hover { - color: pullForward($c, 20%); - &:after { - color: $colorKey; - } - } - - &:before, - &:after { - @include trans-prop-nice(color, 200ms); - display: block; - position: absolute; - } - - &:before { - // Always the arrow icon + @include desktop { @include trans-prop-nice(left, 150ms); //@include test(green); - font-size: $arwD; - height: 100%; width: $arwD; - } - &:after { - // Representative icon - //@include test(red); - width: 100%; - height: 100%; - } + $iconD: 12px; + $arwD: 7px; + $c: pullForward($colorBodyBg, 15%); + color: $c; + cursor: pointer; + display: block; + position: absolute; + font-size: $iconD; + line-height: $iconD; + height: $iconD; width: $iconD; - &.anchor-left { - // < [] - $xpos: (1px + $arwD) * -1; - &:before { - content:'\3c'; - left: $xpos; + &:hover { + color: pullForward($c, 20%); + &:after { + color: $colorKey; + } } - &:hover:before { left: $xpos - 3; } - } - &.anchor-right { - // [] > - $xpos: $iconD + 2px; - &:before { - content:'\3e'; - left: $xpos; + + &:before, + &:after { + @include trans-prop-nice(color, 200ms); + display: block; + position: absolute; + } + + &:before { + // Always the arrow icon + @include trans-prop-nice(left, 150ms); + //@include test(green); + font-size: $arwD; + height: 100%; width: $arwD; + } + &:after { + // Representative icon + //@include test(red); + width: 100%; + height: 100%; + } + + &.anchor-left { + // < [] + $xpos: (1px + $arwD) * -1; + &:before { + content:'\3c'; + left: $xpos; + } + &:hover:before { left: $xpos - 3; } + } + &.anchor-right { + // [] > + $xpos: $iconD + 2px; + &:before { + content:'\3e'; + left: $xpos; + } + &:hover:before { left: $xpos + 3; } } - &:hover:before { left: $xpos + 3; } } } diff --git a/platform/commonUI/general/res/sass/mobile/_layout.scss b/platform/commonUI/general/res/sass/mobile/_layout.scss index f6627e6a5d..acbf1499b6 100644 --- a/platform/commonUI/general/res/sass/mobile/_layout.scss +++ b/platform/commonUI/general/res/sass/mobile/_layout.scss @@ -80,11 +80,7 @@ // Sets the left tree menu when the tree is shown. .pane.left.treeview { - //@include trans-prop-nice(opacity, .4s); @include background-image(linear-gradient(90deg, rgba(black, 0) 98%, rgba(black, 0.3) 100%)); - //opacity: 1; - //display: block !important; - //width: auto !important; // CH CO right: auto !important; width: $proporMenuWithView !important; } @@ -95,11 +91,12 @@ } .toggle-tree { + color: $colorKey !important; font-size: 110%; position: absolute; top: $bodyMargin + 2; left: $bodyMargin; - &:before { + &:after { content:'m' !important; } } diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index a3ce8275bd..11dc10dc4c 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -252,36 +252,30 @@ top: $ueTopBarH + $interiorMarginLg + $treeSearchInputBarH + $interiorMargin; } } + .mini-tab.toggle-pane { $h: $ueTopBarH; $paneOffset: $interiorMarginLg * -3; - //font-size: 0.7rem; - //position: absolute; - //height: $h; - //line-height: $h; - top: 5px; z-index: 2; - &.toggle-tree.anchor-left { - //@include test(green); - left: $paneOffset; -/* &:hover { - left: $paneOffset - 2; - }*/ - &:after { - content:'F'; + @include desktop { + top: 5px; + &.toggle-tree.anchor-left { + //@include test(green); + left: $paneOffset; + &:after { + content: 'F'; + } } - } - &.toggle-inspect.anchor-right { - $xpos: $paneOffset + $interiorMargin; - right: $xpos; -/* &:hover { - right: $xpos - 2; - }*/ - &:after { - content:'F'; //\e612 + &.toggle-inspect.anchor-right { + $xpos: $paneOffset + $interiorMargin; + right: $xpos; + &:after { + content: '\e608'; + } } } } + &.items { .object-browse-bar { .left.abs, diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index d02fb40bb2..7dcadd87a0 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -1499,52 +1499,9 @@ mct-container { .s-icon-btn:hover { color: #33ccff; } -/* line 104, ../../../../general/res/sass/controls/_buttons.scss */ -.mini-tab { - -moz-transition-property: left; - -o-transition-property: left; - -webkit-transition-property: left; - transition-property: left; - -moz-transition-duration: 150ms; - -o-transition-duration: 150ms; - -webkit-transition-duration: 150ms; - transition-duration: 150ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - color: #595959; - cursor: pointer; - display: block; - position: absolute; - font-size: 12px; - line-height: 12px; - height: 12px; - width: 12px; } - /* line 120, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:hover { - color: #8c8c8c; } - /* line 122, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:hover:after { - color: #0099cc; } - /* line 127, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:before, .mini-tab:after { - -moz-transition-property: color; - -o-transition-property: color; - -webkit-transition-property: color; - transition-property: color; - -moz-transition-duration: 200ms; - -o-transition-duration: 200ms; - -webkit-transition-duration: 200ms; - transition-duration: 200ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - display: block; - position: absolute; } - /* line 134, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:before { +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 104, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab { -moz-transition-property: left; -o-transition-property: left; -webkit-transition-property: left; @@ -1557,38 +1514,82 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; - font-size: 7px; - height: 100%; - width: 7px; } - /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:after { - width: 100%; - height: 100%; } - /* line 151, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-left:before { - content: '\3c'; - left: -8px; } - /* line 155, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-left:hover:before { - left: -11px; } - /* line 160, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-right:before { - content: '\3e'; - left: 14px; } - /* line 164, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-right:hover:before { - left: 17px; } + color: #595959; + cursor: pointer; + display: block; + position: absolute; + font-size: 12px; + line-height: 12px; + height: 12px; + width: 12px; } + /* line 121, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover { + color: #8c8c8c; } + /* line 123, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover:after { + color: #0099cc; } + /* line 128, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before, .mini-tab:after { + -moz-transition-property: color; + -o-transition-property: color; + -webkit-transition-property: color; + transition-property: color; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + display: block; + position: absolute; } + /* line 135, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + font-size: 7px; + height: 100%; + width: 7px; } + /* line 142, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:after { + width: 100%; + height: 100%; } + /* line 152, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:before { + content: '\3c'; + left: -8px; } + /* line 156, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:hover:before { + left: -11px; } + /* line 161, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:before { + content: '\3e'; + left: 14px; } + /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:hover:before { + left: 17px; } } -/* line 168, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 170, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { font-size: 0; } - /* line 174, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 176, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .s-btn, .l-btn-set .s-menu-btn { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; margin-left: 1px; } - /* line 180, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; @@ -1597,7 +1598,7 @@ mct-container { -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; margin-left: 0; } - /* line 187, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 189, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; @@ -1606,7 +1607,7 @@ mct-container { -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } -/* line 194, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ .paused:not(.s-btn):not(.s-menu-btn) { border-color: #c56f01 !important; color: #c56f01 !important; } @@ -3623,29 +3624,26 @@ span.req { .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { - top: 5px; z-index: 2; } - /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { - left: -30px; - /* &:hover { - left: $paneOffset - 2; - }*/ } - /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { - content: 'F'; } - /* line 274, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { - right: -25px; - /* &:hover { - right: $xpos - 2; - }*/ } - /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { - content: 'F'; } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane { + top: 5px; } + /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { + left: -30px; } + /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { + content: 'F'; } + /* line 269, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { + right: -25px; } + /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { + content: '\e608'; } } + /* line 281, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3658,31 +3656,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 295, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 302, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 305, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 319, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3692,11 +3690,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 326, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3708,12 +3706,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 345, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 350, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3729,41 +3727,41 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 358, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 360, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 368, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 371, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 388, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 391, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 385, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.left.treeview { opacity: 0; } - /* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 389, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.right-repr { left: 0 !important; } -/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 396, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-showtree .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -3870,44 +3868,45 @@ span.req { /* line 97, ../../../../general/res/sass/mobile/_layout.scss */ .toggle-tree { + color: #0099cc !important; font-size: 110%; position: absolute; top: 12px; left: 10px; } - /* line 102, ../../../../general/res/sass/mobile/_layout.scss */ - .toggle-tree:before { + /* line 103, ../../../../general/res/sass/mobile/_layout.scss */ + .toggle-tree:after { content: 'm' !important; } - /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 108, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar { left: 30px !important; } - /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 111, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .context-available { opacity: 1 !important; } - /* line 113, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 114, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher { margin-right: 0 !important; } - /* line 115, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 116, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher .title-label { display: none; } - /* line 122, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ .tree-holder { overflow-x: hidden !important; } - /* line 126, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 127, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-disable-select { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 131, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 132, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-hide, .mobile-hide-important { display: none !important; } - /* line 136, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 137, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-hide { pointer-events: none; -moz-transition-property: opacity; @@ -3924,7 +3923,7 @@ span.req { transition-timing-function: ease-in-out; opacity: 0; } - /* line 141, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 142, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-unhide { pointer-events: all; -moz-transition-property: opacity; @@ -3941,19 +3940,19 @@ span.req { transition-timing-function: ease-in-out; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 151, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.left.treeview { width: 90% !important; } - /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 154, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr { left: 0 !important; transform: translateX(90%); -webkit-transform: translateX(90%); } - /* line 156, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 157, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 164, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 165, ../../../../general/res/sass/mobile/_layout.scss */ .desktop-hide { display: none; } } /***************************************************************************** diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index b1efcd8cff..5c99b3a6cb 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -1469,52 +1469,9 @@ mct-container { .s-icon-btn:hover { color: white; } -/* line 104, ../../../../general/res/sass/controls/_buttons.scss */ -.mini-tab { - -moz-transition-property: left; - -o-transition-property: left; - -webkit-transition-property: left; - transition-property: left; - -moz-transition-duration: 150ms; - -o-transition-duration: 150ms; - -webkit-transition-duration: 150ms; - transition-duration: 150ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - color: #d6d6d6; - cursor: pointer; - display: block; - position: absolute; - font-size: 12px; - line-height: 12px; - height: 12px; - width: 12px; } - /* line 120, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:hover { - color: #a3a3a3; } - /* line 122, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:hover:after { - color: #0099cc; } - /* line 127, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:before, .mini-tab:after { - -moz-transition-property: color; - -o-transition-property: color; - -webkit-transition-property: color; - transition-property: color; - -moz-transition-duration: 200ms; - -o-transition-duration: 200ms; - -webkit-transition-duration: 200ms; - transition-duration: 200ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - display: block; - position: absolute; } - /* line 134, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:before { +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 104, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab { -moz-transition-property: left; -o-transition-property: left; -webkit-transition-property: left; @@ -1527,38 +1484,82 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; - font-size: 7px; - height: 100%; - width: 7px; } - /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:after { - width: 100%; - height: 100%; } - /* line 151, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-left:before { - content: '\3c'; - left: -8px; } - /* line 155, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-left:hover:before { - left: -11px; } - /* line 160, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-right:before { - content: '\3e'; - left: 14px; } - /* line 164, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-right:hover:before { - left: 17px; } + color: #d6d6d6; + cursor: pointer; + display: block; + position: absolute; + font-size: 12px; + line-height: 12px; + height: 12px; + width: 12px; } + /* line 121, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover { + color: #a3a3a3; } + /* line 123, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover:after { + color: #0099cc; } + /* line 128, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before, .mini-tab:after { + -moz-transition-property: color; + -o-transition-property: color; + -webkit-transition-property: color; + transition-property: color; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + display: block; + position: absolute; } + /* line 135, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + font-size: 7px; + height: 100%; + width: 7px; } + /* line 142, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:after { + width: 100%; + height: 100%; } + /* line 152, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:before { + content: '\3c'; + left: -8px; } + /* line 156, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:hover:before { + left: -11px; } + /* line 161, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:before { + content: '\3e'; + left: 14px; } + /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:hover:before { + left: 17px; } } -/* line 168, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 170, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { font-size: 0; } - /* line 174, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 176, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .s-btn, .l-btn-set .s-menu-btn { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; margin-left: 1px; } - /* line 180, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; @@ -1567,7 +1568,7 @@ mct-container { -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; margin-left: 0; } - /* line 187, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 189, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; @@ -1576,7 +1577,7 @@ mct-container { -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } -/* line 194, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ .paused:not(.s-btn):not(.s-menu-btn) { border-color: #ff9900 !important; color: #ff9900 !important; } @@ -3570,29 +3571,26 @@ span.req { .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { - top: 5px; z-index: 2; } - /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { - left: -30px; - /* &:hover { - left: $paneOffset - 2; - }*/ } - /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { - content: 'F'; } - /* line 274, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { - right: -25px; - /* &:hover { - right: $xpos - 2; - }*/ } - /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { - content: 'F'; } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane { + top: 5px; } + /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { + left: -30px; } + /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { + content: 'F'; } + /* line 269, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { + right: -25px; } + /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { + content: '\e608'; } } + /* line 281, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3605,31 +3603,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 295, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 302, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 305, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 319, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3639,11 +3637,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 326, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3655,12 +3653,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 345, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 350, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3676,41 +3674,41 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 358, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 360, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 368, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 371, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 388, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 391, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 385, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.left.treeview { opacity: 0; } - /* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 389, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-hidetree .pane.right-repr { left: 0 !important; } -/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 396, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-showtree .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; @@ -3817,44 +3815,45 @@ span.req { /* line 97, ../../../../general/res/sass/mobile/_layout.scss */ .toggle-tree { + color: #0099cc !important; font-size: 110%; position: absolute; top: 12px; left: 10px; } - /* line 102, ../../../../general/res/sass/mobile/_layout.scss */ - .toggle-tree:before { + /* line 103, ../../../../general/res/sass/mobile/_layout.scss */ + .toggle-tree:after { content: 'm' !important; } - /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 108, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar { left: 30px !important; } - /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 111, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .context-available { opacity: 1 !important; } - /* line 113, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 114, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher { margin-right: 0 !important; } - /* line 115, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 116, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher .title-label { display: none; } - /* line 122, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ .tree-holder { overflow-x: hidden !important; } - /* line 126, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 127, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-disable-select { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 131, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 132, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-hide, .mobile-hide-important { display: none !important; } - /* line 136, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 137, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-hide { pointer-events: none; -moz-transition-property: opacity; @@ -3871,7 +3870,7 @@ span.req { transition-timing-function: ease-in-out; opacity: 0; } - /* line 141, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 142, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-unhide { pointer-events: all; -moz-transition-property: opacity; @@ -3888,19 +3887,19 @@ span.req { transition-timing-function: ease-in-out; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 151, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.left.treeview { width: 90% !important; } - /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 154, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr { left: 0 !important; transform: translateX(90%); -webkit-transform: translateX(90%); } - /* line 156, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 157, ../../../../general/res/sass/mobile/_layout.scss */ .browse-showtree .pane.right-repr #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 164, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 165, ../../../../general/res/sass/mobile/_layout.scss */ .desktop-hide { display: none; } } /***************************************************************************** From 57f11a9767b4dfc0f29c25d914037d6358a24a6a Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 22 Oct 2015 19:26:59 -0700 Subject: [PATCH 131/488] [Frontend] Manual re-do of collapse/expand panes open #90 Refined positioning of .mini-tab elements when panes are collapsed; --- .../browse/res/templates/browse-object.html | 3 +- .../commonUI/browse/res/templates/browse.html | 4 +- .../general/res/sass/controls/_buttons.scss | 34 ++- .../general/res/sass/mobile/_layout.scss | 6 +- .../res/sass/user-environ/_layout.scss | 10 +- .../espresso/res/css/theme-espresso.css | 208 ++++++++++-------- .../themes/snow/res/css/theme-snow.css | 200 +++++++++-------- 7 files changed, 269 insertions(+), 196 deletions(-) diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html index 12bf5af841..8e99e218a1 100644 --- a/platform/commonUI/browse/res/templates/browse-object.html +++ b/platform/commonUI/browse/res/templates/browse-object.html @@ -48,7 +48,8 @@ class="abs object-holder"> + ng-click="ngModel.toggle()" + ng-class="{ collapsed : !ngModel.visible() }">
diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 678b15be87..88454399f1 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -24,7 +24,7 @@
+ ng-class="modelPaneTree.visible() ? 'pane-tree-showing' : 'pane-tree-hidden'">
@@ -54,7 +54,7 @@
- +
- $xpos: $iconD + 2px; + $xpos: $arwToRightX; &:before { content:'\3e'; left: $xpos; } - &:hover:before { left: $xpos + 3; } + &:hover:before { left: $xpos + $arwToRightAnimX; } + &.collapsed { + $xpos: $arwToLeftX; + &:before { + content:'\3c'; + left: $xpos; + } + &:hover:before { left: $xpos + $arwToLeftAnimX; } + } + } + + &.collapsed { + // State when the pane this element controls has been collapsed } } } diff --git a/platform/commonUI/general/res/sass/mobile/_layout.scss b/platform/commonUI/general/res/sass/mobile/_layout.scss index acbf1499b6..df730e7559 100644 --- a/platform/commonUI/general/res/sass/mobile/_layout.scss +++ b/platform/commonUI/general/res/sass/mobile/_layout.scss @@ -59,7 +59,7 @@ // When the tree is hidden, these are the // classes used for the left menu and the // right representation. - .browse-hidetree { + .pane-tree-hidden { // Sets the left tree menu when the tree // is hidden. .pane.left.treeview { @@ -70,7 +70,7 @@ } } - .browse-showtree { + .pane-tree-showing { // NOTE: DISABLED SELECTION // Selection disabled in both panes // causing cut/copy/paste menu to @@ -143,7 +143,7 @@ } @include phonePortrait { - .browse-showtree { + .pane-tree-showing { .pane.left.treeview { width: $proporMenuOnly !important; } diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 11dc10dc4c..1c12d13c45 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -265,6 +265,9 @@ &:after { content: 'F'; } + &.collapsed { + left: -1 * $bodyMargin; + } } &.toggle-inspect.anchor-right { $xpos: $paneOffset + $interiorMargin; @@ -272,6 +275,9 @@ &:after { content: '\e608'; } + &.collapsed { + right: -1 * $interiorMargin; + } } } } @@ -379,7 +385,7 @@ // When the tree is hidden, these are the // classes used for the left menu and the // right representation. -.browse-hidetree { +.pane-tree-hidden { @include user-select(none); // Sets the left tree menu when the tree is hidden. .pane.left.treeview { @@ -391,7 +397,7 @@ } } -.browse-showtree { +.pane-tree-showing { // Sets the left tree menu when the tree is shown. .pane.left.treeview { @include trans-prop-nice(opacity, .4s); diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 7dcadd87a0..1a3300cd00 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -1377,14 +1377,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .s-btn.major .icon, .major.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover { background: linear-gradient(#1ac6ff, #00bfff); } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover > .icon, .major.s-menu-btn:not(.disabled):hover > .icon { color: white; } } /* line 66, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1416,14 +1416,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover { background: linear-gradient(#6b6b6b, #5e5e5e); } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: #33ccff; } } /* line 75, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1458,14 +1458,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover { background: linear-gradient(#fe9815, #f88c01); } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu-btn:not(.disabled):hover > .icon { color: white; } } /* line 80, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1522,13 +1522,13 @@ mct-container { line-height: 12px; height: 12px; width: 12px; } - /* line 121, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 127, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:hover { color: #8c8c8c; } - /* line 123, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 129, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:hover:after { color: #0099cc; } - /* line 128, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 134, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:before, .mini-tab:after { -moz-transition-property: color; -o-transition-property: color; @@ -1544,7 +1544,7 @@ mct-container { transition-timing-function: ease-in-out; display: block; position: absolute; } - /* line 135, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:before { -moz-transition-property: left; -o-transition-property: left; @@ -1561,35 +1561,49 @@ mct-container { font-size: 7px; height: 100%; width: 7px; } - /* line 142, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 148, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:after { width: 100%; height: 100%; } - /* line 152, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 158, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:before { content: '\3c'; left: -8px; } - /* line 156, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 162, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:hover:before { left: -11px; } - /* line 161, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left.collapsed:before { + content: '\3e'; + left: 13px; } + /* line 169, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left.collapsed:hover:before { + left: 16px; } + /* line 175, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:before { content: '\3e'; - left: 14px; } - /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ + left: 13px; } + /* line 179, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:hover:before { - left: 17px; } } + left: 16px; } + /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right.collapsed:before { + content: '\3c'; + left: -8px; } + /* line 186, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right.collapsed:hover:before { + left: -11px; } } -/* line 170, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { font-size: 0; } - /* line 176, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 202, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .s-btn, .l-btn-set .s-menu-btn { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; margin-left: 1px; } - /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 208, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; @@ -1598,7 +1612,7 @@ mct-container { -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; margin-left: 0; } - /* line 189, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 215, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; @@ -1607,7 +1621,7 @@ mct-container { -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } -/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 222, ../../../../general/res/sass/controls/_buttons.scss */ .paused:not(.s-btn):not(.s-menu-btn) { border-color: #c56f01 !important; color: #c56f01 !important; } @@ -2331,7 +2345,7 @@ label.checkbox.custom { .menu ul { margin: 0; padding: 0; } - /* line 331, ../../../../general/res/sass/_mixins.scss */ + /* line 326, ../../../../general/res/sass/_mixins.scss */ .menu ul li { list-style-type: none; margin: 0; @@ -2885,7 +2899,7 @@ mct-include.l-time-controller { padding: 0 3px; position: relative; height: 150px; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ .form .form-row .selector-list.error { background: rgba(255, 0, 0, 0.5); } /* line 124, ../../../../general/res/sass/forms/_elems.scss */ @@ -2942,7 +2956,7 @@ input[type="text"] { color: #cccccc; outline: none; padding: 0 3px; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ input[type="text"].error { background: rgba(255, 0, 0, 0.5); } /* line 172, ../../../../general/res/sass/forms/_elems.scss */ @@ -2970,7 +2984,7 @@ textarea { position: absolute; height: 100%; width: 100%; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ textarea.error { background: rgba(255, 0, 0, 0.5); } @@ -3029,14 +3043,14 @@ textarea { overflow: hidden; position: relative; line-height: 22px; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .select .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .select:not(.disabled):hover { background: linear-gradient(#6b6b6b, #5e5e5e); } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .select:not(.disabled):hover > .icon { color: #33ccff; } } /* line 31, ../../../../general/res/sass/forms/_selects.scss */ @@ -3118,7 +3132,7 @@ textarea { max-height: 400px; overflow: auto; padding: 5px; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ .channel-selector .treeview.error { background: rgba(255, 0, 0, 0.5); } /* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ @@ -3284,7 +3298,7 @@ span.req { padding: 0 3px; background: #3b3b3b; border-bottom: 1px solid #4d4d4d; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ .filter input.filter.error, .filter input.t-filter-input.error, .t-filter input.filter.error, @@ -3637,13 +3651,19 @@ span.req { /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { content: 'F'; } - /* line 269, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { + left: -10px; } + /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { right: -25px; } - /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { - content: '\e608'; } } - /* line 281, ../../../../general/res/sass/user-environ/_layout.scss */ + content: '\e608'; } + /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { + right: -5px; } } + /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3656,31 +3676,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 295, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 302, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 305, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 319, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3690,11 +3710,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 326, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3706,12 +3726,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 345, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 350, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3727,42 +3747,42 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 358, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 360, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 368, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 371, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-hidetree { +/* line 388, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-hidden { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 385, ../../../../general/res/sass/user-environ/_layout.scss */ - .browse-hidetree .pane.left.treeview { + /* line 391, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane-tree-hidden .pane.left.treeview { opacity: 0; } - /* line 389, ../../../../general/res/sass/user-environ/_layout.scss */ - .browse-hidetree .pane.right-repr { + /* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane-tree-hidden .pane.right-repr { left: 0 !important; } -/* line 396, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-showtree .pane.left.treeview { +/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-showing .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; -webkit-transition-property: opacity; @@ -3847,14 +3867,14 @@ span.req { left: 10px !important; } /* line 65, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-hidetree .pane.left.treeview { + .pane-tree-hidden .pane.left.treeview { right: 100% !important; width: auto !important; overflow-y: hidden; overflow-x: hidden; } /* line 82, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.left.treeview { + .pane-tree-showing .pane.left.treeview { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSI5OCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); @@ -3862,51 +3882,51 @@ span.req { background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); right: auto !important; width: 40% !important; } - /* line 92, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.right-repr { + /* line 88, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.right-repr { left: 40% !important; } - /* line 97, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 93, ../../../../general/res/sass/mobile/_layout.scss */ .toggle-tree { color: #0099cc !important; font-size: 110%; position: absolute; top: 12px; left: 10px; } - /* line 103, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 99, ../../../../general/res/sass/mobile/_layout.scss */ .toggle-tree:after { content: 'm' !important; } - /* line 108, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 104, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar { left: 30px !important; } - /* line 111, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .context-available { opacity: 1 !important; } - /* line 114, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher { margin-right: 0 !important; } - /* line 116, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 112, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher .title-label { display: none; } - /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 119, ../../../../general/res/sass/mobile/_layout.scss */ .tree-holder { overflow-x: hidden !important; } - /* line 127, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-disable-select { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 132, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 128, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-hide, .mobile-hide-important { display: none !important; } - /* line 137, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-hide { pointer-events: none; -moz-transition-property: opacity; @@ -3923,7 +3943,7 @@ span.req { transition-timing-function: ease-in-out; opacity: 0; } - /* line 142, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-unhide { pointer-events: all; -moz-transition-property: opacity; @@ -3940,19 +3960,19 @@ span.req { transition-timing-function: ease-in-out; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 151, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.left.treeview { + /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.left.treeview { width: 90% !important; } - /* line 154, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.right-repr { + /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.right-repr { left: 0 !important; transform: translateX(90%); -webkit-transform: translateX(90%); } - /* line 157, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.right-repr #content-area { + /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.right-repr #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 165, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ .desktop-hide { display: none; } } /***************************************************************************** @@ -4393,14 +4413,14 @@ span.req { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover { background: linear-gradient(#a6a6a6, #999999); } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4485,7 +4505,7 @@ ul.tree { -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 331, ../../../../general/res/sass/_mixins.scss */ + /* line 326, ../../../../general/res/sass/_mixins.scss */ ul.tree li { list-style-type: none; margin: 0; @@ -5753,14 +5773,14 @@ table { margin-bottom: 3px; margin-right: 3px; position: relative; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover { background: linear-gradient(#666666, #595959); } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover > .icon { color: #33ccff; } } /* line 45, ../../../../general/res/sass/items/_item.scss */ @@ -5895,14 +5915,14 @@ table { transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; color: #80dfff; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected:not(.disabled):hover { background: linear-gradient(#1ac6ff, #00bfff); } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected:not(.disabled):hover > .icon { color: #33ccff; } } /* line 137, ../../../../general/res/sass/items/_item.scss */ diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 5c99b3a6cb..ce2f71b5d8 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -1365,14 +1365,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .s-btn.major .icon, .major.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover { background: deepskyblue; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover > .icon, .major.s-menu-btn:not(.disabled):hover > .icon { color: white; } } /* line 66, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1395,14 +1395,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon { color: #eee; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover { background: #0099cc; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 75, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1428,14 +1428,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover { background: #ffad33; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu-btn:not(.disabled):hover > .icon { color: white; } } /* line 80, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1492,13 +1492,13 @@ mct-container { line-height: 12px; height: 12px; width: 12px; } - /* line 121, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 127, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:hover { color: #a3a3a3; } - /* line 123, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 129, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:hover:after { color: #0099cc; } - /* line 128, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 134, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:before, .mini-tab:after { -moz-transition-property: color; -o-transition-property: color; @@ -1514,7 +1514,7 @@ mct-container { transition-timing-function: ease-in-out; display: block; position: absolute; } - /* line 135, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:before { -moz-transition-property: left; -o-transition-property: left; @@ -1531,35 +1531,49 @@ mct-container { font-size: 7px; height: 100%; width: 7px; } - /* line 142, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 148, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab:after { width: 100%; height: 100%; } - /* line 152, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 158, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:before { content: '\3c'; left: -8px; } - /* line 156, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 162, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:hover:before { left: -11px; } - /* line 161, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left.collapsed:before { + content: '\3e'; + left: 13px; } + /* line 169, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left.collapsed:hover:before { + left: 16px; } + /* line 175, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:before { content: '\3e'; - left: 14px; } - /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ + left: 13px; } + /* line 179, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:hover:before { - left: 17px; } } + left: 16px; } + /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right.collapsed:before { + content: '\3c'; + left: -8px; } + /* line 186, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right.collapsed:hover:before { + left: -11px; } } -/* line 170, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { font-size: 0; } - /* line 176, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 202, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .s-btn, .l-btn-set .s-menu-btn { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; margin-left: 1px; } - /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 208, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; @@ -1568,7 +1582,7 @@ mct-container { -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; margin-left: 0; } - /* line 189, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 215, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; @@ -1577,7 +1591,7 @@ mct-container { -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } -/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 222, ../../../../general/res/sass/controls/_buttons.scss */ .paused:not(.s-btn):not(.s-menu-btn) { border-color: #ff9900 !important; color: #ff9900 !important; } @@ -2295,7 +2309,7 @@ label.checkbox.custom { .menu ul { margin: 0; padding: 0; } - /* line 331, ../../../../general/res/sass/_mixins.scss */ + /* line 326, ../../../../general/res/sass/_mixins.scss */ .menu ul li { list-style-type: none; margin: 0; @@ -2849,7 +2863,7 @@ mct-include.l-time-controller { padding: 0 3px; position: relative; height: 150px; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ .form .form-row .selector-list.error { background: rgba(255, 0, 0, 0.5); } /* line 124, ../../../../general/res/sass/forms/_elems.scss */ @@ -2906,7 +2920,7 @@ input[type="text"] { color: #666; outline: none; padding: 0 3px; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ input[type="text"].error { background: rgba(255, 0, 0, 0.5); } /* line 172, ../../../../general/res/sass/forms/_elems.scss */ @@ -2934,7 +2948,7 @@ textarea { position: absolute; height: 100%; width: 100%; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ textarea.error { background: rgba(255, 0, 0, 0.5); } @@ -2983,7 +2997,7 @@ textarea { overflow: hidden; position: relative; line-height: 22px; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .select .icon { color: #eee; } /* line 31, ../../../../general/res/sass/forms/_selects.scss */ @@ -3065,7 +3079,7 @@ textarea { max-height: 400px; overflow: auto; padding: 5px; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ .channel-selector .treeview.error { background: rgba(255, 0, 0, 0.5); } /* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ @@ -3231,7 +3245,7 @@ span.req { padding: 0 3px; background: white; border-bottom: 1px solid white; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ + /* line 293, ../../../../general/res/sass/_mixins.scss */ .filter input.filter.error, .filter input.t-filter-input.error, .t-filter input.filter.error, @@ -3584,13 +3598,19 @@ span.req { /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { content: 'F'; } - /* line 269, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { + left: -10px; } + /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { right: -25px; } - /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { - content: '\e608'; } } - /* line 281, ../../../../general/res/sass/user-environ/_layout.scss */ + content: '\e608'; } + /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { + right: -5px; } } + /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3603,31 +3623,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 295, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 302, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 305, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 319, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3637,11 +3657,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 326, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3653,12 +3673,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 345, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 350, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3674,42 +3694,42 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 358, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 360, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 368, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 371, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-hidetree { +/* line 388, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-hidden { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 385, ../../../../general/res/sass/user-environ/_layout.scss */ - .browse-hidetree .pane.left.treeview { + /* line 391, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane-tree-hidden .pane.left.treeview { opacity: 0; } - /* line 389, ../../../../general/res/sass/user-environ/_layout.scss */ - .browse-hidetree .pane.right-repr { + /* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane-tree-hidden .pane.right-repr { left: 0 !important; } -/* line 396, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-showtree .pane.left.treeview { +/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-showing .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; -webkit-transition-property: opacity; @@ -3794,14 +3814,14 @@ span.req { left: 10px !important; } /* line 65, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-hidetree .pane.left.treeview { + .pane-tree-hidden .pane.left.treeview { right: 100% !important; width: auto !important; overflow-y: hidden; overflow-x: hidden; } /* line 82, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.left.treeview { + .pane-tree-showing .pane.left.treeview { background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSI5OCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); @@ -3809,51 +3829,51 @@ span.req { background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); right: auto !important; width: 40% !important; } - /* line 92, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.right-repr { + /* line 88, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.right-repr { left: 40% !important; } - /* line 97, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 93, ../../../../general/res/sass/mobile/_layout.scss */ .toggle-tree { color: #0099cc !important; font-size: 110%; position: absolute; top: 12px; left: 10px; } - /* line 103, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 99, ../../../../general/res/sass/mobile/_layout.scss */ .toggle-tree:after { content: 'm' !important; } - /* line 108, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 104, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar { left: 30px !important; } - /* line 111, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .context-available { opacity: 1 !important; } - /* line 114, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher { margin-right: 0 !important; } - /* line 116, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 112, ../../../../general/res/sass/mobile/_layout.scss */ .object-browse-bar .view-switcher .title-label { display: none; } - /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 119, ../../../../general/res/sass/mobile/_layout.scss */ .tree-holder { overflow-x: hidden !important; } - /* line 127, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-disable-select { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 132, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 128, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-hide, .mobile-hide-important { display: none !important; } - /* line 137, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-hide { pointer-events: none; -moz-transition-property: opacity; @@ -3870,7 +3890,7 @@ span.req { transition-timing-function: ease-in-out; opacity: 0; } - /* line 142, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ .mobile-back-unhide { pointer-events: all; -moz-transition-property: opacity; @@ -3887,19 +3907,19 @@ span.req { transition-timing-function: ease-in-out; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 151, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.left.treeview { + /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.left.treeview { width: 90% !important; } - /* line 154, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.right-repr { + /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.right-repr { left: 0 !important; transform: translateX(90%); -webkit-transform: translateX(90%); } - /* line 157, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-showtree .pane.right-repr #content-area { + /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.right-repr #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 165, ../../../../general/res/sass/mobile/_layout.scss */ + /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ .desktop-hide { display: none; } } /***************************************************************************** @@ -4322,14 +4342,14 @@ span.req { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover { background: #7d7d7d; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4414,7 +4434,7 @@ ul.tree { -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 331, ../../../../general/res/sass/_mixins.scss */ + /* line 326, ../../../../general/res/sass/_mixins.scss */ ul.tree li { list-style-type: none; margin: 0; @@ -5672,14 +5692,14 @@ table { margin-bottom: 3px; margin-right: 3px; position: relative; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover { background: #d0d0d0; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ + /* line 276, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover > .icon { color: #33ccff; } } /* line 45, ../../../../general/res/sass/items/_item.scss */ @@ -5805,7 +5825,7 @@ table { transition: background, 0.25s; text-shadow: none; color: #80dfff; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 269, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected .icon { color: #eee; } /* line 137, ../../../../general/res/sass/items/_item.scss */ From 827e1af581521bccd119556c2d8886ad36f16625 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 23 Oct 2015 09:18:59 -0700 Subject: [PATCH 132/488] [Frontend] Manual re-do of collapse/expand panes open #90 Added code to MCTSplitter and MCTSplitPane to allow toggling of CSS class 'resizing' on pane elements when the user is actively using the splitter; Added resize transition animation to split pane elements when user not actively resizing; --- .../general/res/sass/helpers/_splitter.scss | 3 + .../res/sass/user-environ/_layout.scss | 5 + .../general/src/directives/MCTSplitPane.js | 9 ++ .../general/src/directives/MCTSplitter.js | 8 +- .../espresso/res/css/theme-espresso.css | 110 ++++++++++-------- .../themes/snow/res/css/theme-snow.css | 110 ++++++++++-------- 6 files changed, 152 insertions(+), 93 deletions(-) diff --git a/platform/commonUI/general/res/sass/helpers/_splitter.scss b/platform/commonUI/general/res/sass/helpers/_splitter.scss index b329f6fa11..a765b34ec9 100644 --- a/platform/commonUI/general/res/sass/helpers/_splitter.scss +++ b/platform/commonUI/general/res/sass/helpers/_splitter.scss @@ -36,6 +36,9 @@ border-color: $colorGrippyInteriorHover !important; } } + &:active { + background-color: pullForward($b, 5%); + } } &.horizontal { // Slides vertically up and down, splitting the element horizontally diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 1c12d13c45..8cb48282b8 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -232,6 +232,11 @@ } } +.pane:not(.resizing) { + // Add transition CSS + @include trans-prop-nice-resize-w(250ms); +} + .pane { position: absolute; &.treeview.left { diff --git a/platform/commonUI/general/src/directives/MCTSplitPane.js b/platform/commonUI/general/src/directives/MCTSplitPane.js index abc54f772e..19c17bcb1c 100644 --- a/platform/commonUI/general/src/directives/MCTSplitPane.js +++ b/platform/commonUI/general/src/directives/MCTSplitPane.js @@ -178,6 +178,14 @@ define( return position; } + // Dynamically apply a CSS class to elements when the user is actively resizing + function splitterState(state) { + var children = $element.children(); + for (var i = 0; i < children.length; i++) { + children.eq(i).toggleClass('resizing'); + } + } + // Make sure anchor parameter is something we know if (!ANCHORS[anchorKey]) { $log.warn(ANCHOR_WARNING_MESSAGE); @@ -208,6 +216,7 @@ define( // Interface exposed by controller, for mct-splitter to user return { position: getSetPosition, + action: splitterState, anchor: function () { return anchor; } diff --git a/platform/commonUI/general/src/directives/MCTSplitter.js b/platform/commonUI/general/src/directives/MCTSplitter.js index c163c107e0..3c8147b7e4 100644 --- a/platform/commonUI/general/src/directives/MCTSplitter.js +++ b/platform/commonUI/general/src/directives/MCTSplitter.js @@ -29,7 +29,8 @@ define( // Pixel width to allocate for the splitter itself var SPLITTER_TEMPLATE = "
", + "mct-drag=\"splitter.move(delta)\" " + + "mct-drag-up=\"splitter.endMove()\">
", OFFSETS_BY_EDGE = { left: "offsetLeft", right: "offsetRight", @@ -53,6 +54,7 @@ define( startMove: function () { var splitter = element[0]; initialPosition = mctSplitPane.position(); + mctSplitPane.action('startMove'); }, // Handle user changes to splitter position move: function (delta) { @@ -63,6 +65,10 @@ define( // Update the position of this splitter mctSplitPane.position(initialPosition + pixelDelta); + }, + // Grab the event when the user is done moving the splitter and pass it on + endMove: function() { + mctSplitPane.action('endMove'); } }; } diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 1a3300cd00..1137623cf8 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -917,27 +917,30 @@ mct-container { /* line 34, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout .splitter:hover:after { border-color: #0099cc !important; } -/* line 40, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 39, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout .splitter:active { + background-color: #525252; } +/* line 43, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal { overflow: hidden; } - /* line 43, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 46, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane { left: 0; right: 0; } - /* line 46, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 49, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane.top { bottom: auto; } - /* line 49, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 52, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane.bottom { top: auto; } - /* line 53, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 56, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal > .splitter { cursor: row-resize; left: 0; right: 0; width: auto; height: 5px; } - /* line 54, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 57, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal > .splitter:after { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -961,22 +964,22 @@ mct-container { left: 5px; right: 5px; height: 1px; } -/* line 65, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 68, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane { top: 0; bottom: 0; } - /* line 68, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane.left { right: auto; } - /* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 74, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane.right { left: auto; } -/* line 75, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 78, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical > .splitter { bottom: 0; cursor: col-resize; width: 5px; } - /* line 79, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 82, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical > .splitter:after { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -1001,11 +1004,11 @@ mct-container { top: 5px; width: 1px; } -/* line 86, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 89, ../../../../general/res/sass/helpers/_splitter.scss */ .browse-area .splitter { top: 0; } -/* line 90, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 93, ../../../../general/res/sass/helpers/_splitter.scss */ .edit-area .splitter { top: 0; } @@ -3620,50 +3623,65 @@ span.req { height: 30%; } /* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane:not(.resizing) { + -moz-transition-property: width, left, right; + -o-transition-property: width, left, right; + -webkit-transition-property: width, left, right; + transition-property: width, left, right; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } + +/* line 240, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { position: absolute; } - /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 243, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 242, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 252, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { z-index: 2; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { top: 5px; } - /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 267, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { left: -30px; } - /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { content: 'F'; } - /* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 273, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { left: -10px; } - /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 277, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { right: -25px; } - /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { content: '\e608'; } - /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 283, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { right: -5px; } } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3676,31 +3694,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 306, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 313, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 316, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 320, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 330, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 333, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3710,11 +3728,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 337, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 343, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3726,12 +3744,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 361, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3747,41 +3765,41 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 369, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 371, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 379, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 388, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 393, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 391, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 396, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.left.treeview { opacity: 0; } - /* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 400, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.right-repr { left: 0 !important; } -/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 407, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-showing .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index ce2f71b5d8..8c98b9e44e 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -914,27 +914,30 @@ mct-container { /* line 34, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout .splitter:hover:after { border-color: #fcfcfc !important; } -/* line 40, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 39, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout .splitter:active { + background-color: #898989; } +/* line 43, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal { overflow: hidden; } - /* line 43, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 46, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane { left: 0; right: 0; } - /* line 46, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 49, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane.top { bottom: auto; } - /* line 49, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 52, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal .pane.bottom { top: auto; } - /* line 53, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 56, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal > .splitter { cursor: row-resize; left: 0; right: 0; width: auto; height: 5px; } - /* line 54, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 57, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.horizontal > .splitter:after { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -958,22 +961,22 @@ mct-container { left: 5px; right: 5px; height: 1px; } -/* line 65, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 68, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane { top: 0; bottom: 0; } - /* line 68, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane.left { right: auto; } - /* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 74, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical .pane.right { left: auto; } -/* line 75, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 78, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical > .splitter { bottom: 0; cursor: col-resize; width: 5px; } - /* line 79, ../../../../general/res/sass/helpers/_splitter.scss */ + /* line 82, ../../../../general/res/sass/helpers/_splitter.scss */ .split-layout.vertical > .splitter:after { -moz-transition-property: "border-color"; -o-transition-property: "border-color"; @@ -998,11 +1001,11 @@ mct-container { top: 5px; width: 1px; } -/* line 86, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 89, ../../../../general/res/sass/helpers/_splitter.scss */ .browse-area .splitter { top: 0; } -/* line 90, ../../../../general/res/sass/helpers/_splitter.scss */ +/* line 93, ../../../../general/res/sass/helpers/_splitter.scss */ .edit-area .splitter { top: 0; } @@ -3567,50 +3570,65 @@ span.req { height: 30%; } /* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane:not(.resizing) { + -moz-transition-property: width, left, right; + -o-transition-property: width, left, right; + -webkit-transition-property: width, left, right; + transition-property: width, left, right; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } + +/* line 240, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { position: absolute; } - /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 243, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 242, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 252, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { z-index: 2; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { top: 5px; } - /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 267, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { left: -30px; } - /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { content: 'F'; } - /* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 273, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { left: -10px; } - /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 277, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { right: -25px; } - /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { content: '\e608'; } - /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 283, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { right: -5px; } } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3623,31 +3641,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 306, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 313, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 316, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 320, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 330, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 333, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3657,11 +3675,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 337, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 343, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3673,12 +3691,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 361, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3694,41 +3712,41 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 369, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 371, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 379, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 388, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 393, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden { -moz-user-select: -moz-none; -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 391, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 396, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.left.treeview { opacity: 0; } - /* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 400, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.right-repr { left: 0 !important; } -/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 407, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-showing .pane.left.treeview { -moz-transition-property: opacity; -o-transition-property: opacity; From a283a2a0d3f4dc43dac453717b61fe4977892087 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 23 Oct 2015 10:52:28 -0700 Subject: [PATCH 133/488] [Frontend] Manual re-do of collapse/expand panes open #90 Animation, styles, hide-show added panes and splitters; Finessed spacing; trans-prop* mixins normalized and added delay arg; --- .../browse/res/templates/browse-object.html | 7 +- .../commonUI/browse/res/templates/browse.html | 8 +- .../commonUI/general/res/sass/_constants.scss | 1 + .../commonUI/general/res/sass/_mixins.scss | 17 +- .../general/res/sass/controls/_buttons.scss | 4 +- .../general/res/sass/mobile/_layout.scss | 8 +- .../res/sass/user-environ/_layout.scss | 57 ++- .../espresso/res/css/theme-espresso.css | 405 +++++++++++------- .../themes/snow/res/css/theme-snow.css | 397 +++++++++++------ 9 files changed, 584 insertions(+), 320 deletions(-) diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html index 8e99e218a1..9464aaee45 100644 --- a/platform/commonUI/browse/res/templates/browse-object.html +++ b/platform/commonUI/browse/res/templates/browse-object.html @@ -49,13 +49,12 @@ + ng-class="{ collapsed : !ngModel.visible() }">
- + -
+
diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 88454399f1..c1eff0b907 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -51,11 +51,13 @@
- + -
+
-
+
diff --git a/platform/commonUI/general/res/sass/_constants.scss b/platform/commonUI/general/res/sass/_constants.scss index 6cc8312a3c..6365119894 100644 --- a/platform/commonUI/general/res/sass/_constants.scss +++ b/platform/commonUI/general/res/sass/_constants.scss @@ -47,6 +47,7 @@ $ueBrowseRightPaneInspectW: 10%; $ueEditLeftPaneW: 75%; $treeSearchInputBarH: 25px; $ueTimeControlH: (33px, 20px, 20px); +$ueCollapsedEdgeMargin: 15px; // Overlay $ovrTopBarH: 60px; $ovrFooterH: 30px; diff --git a/platform/commonUI/general/res/sass/_mixins.scss b/platform/commonUI/general/res/sass/_mixins.scss index 1a1d651b6c..360ea8e17c 100644 --- a/platform/commonUI/general/res/sass/_mixins.scss +++ b/platform/commonUI/general/res/sass/_mixins.scss @@ -41,36 +41,41 @@ width: $d; } -@mixin trans-prop-nice($props, $t: 500ms) { +@mixin trans-prop-nice($prop, $t: 500ms, $delay: 0) { + // This only works for a single property right now @if $t == 0 { @include transition-property(none); } @else { - @include transition-property($props); + @include transition-property($prop); @include transition-duration($t); @include transition-timing-function(ease-in-out); + @include transition-delay($delay); } } -@mixin trans-prop-nice-fade($t: 0.5s) { +@mixin trans-prop-nice-fade($t: 500ms, $delay: 0) { @if $t == 0 { @include transition-property(none); } @else { - @include transition-property(visibility, opacity, background-color, border-color); + @include transition-property(opacity, background-color, border-color, color); @include transition-duration($t); @include transition-timing-function(ease-in-out); + @include transition-delay($delay); } } -@mixin trans-prop-nice-resize-h($t: 0.5s) { +@mixin trans-prop-nice-resize-h($t: 500ms, $delay: 0) { @include transition-property(height, bottom, top); @include transition-duration($t); @include transition-timing-function(ease-in-out); + @include transition-delay($delay); } -@mixin trans-prop-nice-resize-w($t: 0.5s) { +@mixin trans-prop-nice-resize-w($t: 500ms, $delay: 0) { @include transition-property(width, left, right); @include transition-duration($t); @include transition-timing-function(ease-in-out); + @include transition-delay($delay); } @mixin triangle-right($size, $color) { diff --git a/platform/commonUI/general/res/sass/controls/_buttons.scss b/platform/commonUI/general/res/sass/controls/_buttons.scss index c73e42040c..c33e2306ac 100644 --- a/platform/commonUI/general/res/sass/controls/_buttons.scss +++ b/platform/commonUI/general/res/sass/controls/_buttons.scss @@ -109,8 +109,8 @@ $pad: $interiorMargin * $baseRatio; //@include test(green); $iconD: 12px; $arwD: 7px; - $arwOffsetX: 1px; - $arwAnimOffsetX: 3px; + $arwOffsetX: 0px; + $arwAnimOffsetX: 2px; $arwToLeftAnimX: -1 * $arwAnimOffsetX; $arwToRightAnimX: $arwAnimOffsetX; $arwToLeftX: ($arwOffsetX + $arwD) * -1; diff --git a/platform/commonUI/general/res/sass/mobile/_layout.scss b/platform/commonUI/general/res/sass/mobile/_layout.scss index df730e7559..d3b101370b 100644 --- a/platform/commonUI/general/res/sass/mobile/_layout.scss +++ b/platform/commonUI/general/res/sass/mobile/_layout.scss @@ -32,7 +32,7 @@ background-color: $colorMobilePaneLeft; } - .pane.right-repr { + .pane.right.items { //@include test(); @include slMenuTransitions; margin-left: 0 !important; @@ -85,7 +85,7 @@ width: $proporMenuWithView !important; } // Sets the right representation when the tree is shown. - .pane.right-repr { + .pane.right.items { left: $proporMenuWithView !important; } } @@ -147,9 +147,9 @@ .pane.left.treeview { width: $proporMenuOnly !important; } - .pane.right-repr { + .pane.right.items { left: 0 !important; - @include webkitProp(transform, translateX($proporMenuOnly)); + @include transform(translateX($proporMenuOnly)); #content-area { opacity: 0; } diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 8cb48282b8..6d7895ec83 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -211,7 +211,7 @@ max-width: 800px; width: $ueBrowseLeftPaneTreeW; } - &.t-inspector.right { + &.t-inspect.right { min-width: 150px; max-width: 800px; width: $ueBrowseRightPaneInspectW; @@ -232,11 +232,6 @@ } } -.pane:not(.resizing) { - // Add transition CSS - @include trans-prop-nice-resize-w(250ms); -} - .pane { position: absolute; &.treeview.left { @@ -271,7 +266,7 @@ content: 'F'; } &.collapsed { - left: -1 * $bodyMargin; + left: (-1 * $bodyMargin) - $ueCollapsedEdgeMargin; } } &.toggle-inspect.anchor-right { @@ -281,7 +276,7 @@ content: '\e608'; } &.collapsed { - right: -1 * $interiorMargin; + right: -1 * $interiorMargin - $ueCollapsedEdgeMargin; } } } @@ -391,21 +386,53 @@ // classes used for the left menu and the // right representation. .pane-tree-hidden { - @include user-select(none); // Sets the left tree menu when the tree is hidden. - .pane.left.treeview { + .pane.left.treeview, + .splitter-treeview { opacity: 0; } - // Sets the right represenation when the tree is hidden. - .pane.right-repr { - left: 0 !important; + // Sets the right representation when the tree is hidden. + .pane.right.items { + left: $ueCollapsedEdgeMargin !important; } } .pane-tree-showing { // Sets the left tree menu when the tree is shown. - .pane.left.treeview { - @include trans-prop-nice(opacity, .4s); + .pane.left.treeview, + .splitter-treeview { + @include trans-prop-nice-fade(250ms, 250ms); opacity: 1; } +} + +.pane-inspect-showing { + .l-object-and-inspector { + .pane.right, + .splitter-inspect { + @include trans-prop-nice-fade(250ms, 250ms); + opacity: 1; + } + } +} +.pane-inspect-hidden { + .l-object-and-inspector { + .pane.right, + .splitter-inspect { + opacity: 0; + } + .pane.left { + right: $ueCollapsedEdgeMargin !important; + } + } +} + +.pane.right.t-inspect { + @include test(orange, 0.3); // TEMP! +} + +@include desktop { + .pane:not(.resizing) { + @include trans-prop-nice-resize-w(250ms); + } } \ No newline at end of file diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 1137623cf8..772729448b 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -954,6 +954,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; content: ''; display: block; pointer-events: none; @@ -993,6 +997,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; content: ''; display: block; pointer-events: none; @@ -1380,14 +1388,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.major .icon, .major.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover { background: linear-gradient(#1ac6ff, #00bfff); } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover > .icon, .major.s-menu-btn:not(.disabled):hover > .icon { color: white; } } /* line 66, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1419,14 +1427,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover { background: linear-gradient(#6b6b6b, #5e5e5e); } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: #33ccff; } } /* line 75, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1461,14 +1469,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover { background: linear-gradient(#fe9815, #f88c01); } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu-btn:not(.disabled):hover > .icon { color: white; } } /* line 80, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1517,6 +1525,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; color: #595959; cursor: pointer; display: block; @@ -1545,6 +1557,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; display: block; position: absolute; } /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1561,6 +1577,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; font-size: 7px; height: 100%; width: 7px; } @@ -1571,31 +1591,31 @@ mct-container { /* line 158, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:before { content: '\3c'; - left: -8px; } + left: -7px; } /* line 162, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:hover:before { - left: -11px; } + left: -9px; } /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left.collapsed:before { content: '\3e'; - left: 13px; } + left: 12px; } /* line 169, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left.collapsed:hover:before { - left: 16px; } + left: 14px; } /* line 175, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:before { content: '\3e'; - left: 13px; } + left: 12px; } /* line 179, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:hover:before { - left: 16px; } + left: 14px; } /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right.collapsed:before { content: '\3c'; - left: -8px; } + left: -7px; } /* line 186, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right.collapsed:hover:before { - left: -11px; } } + left: -9px; } } /* line 196, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { @@ -1668,10 +1688,10 @@ mct-container { -webkit-box-sizing: border-box; box-sizing: border-box; text-shadow: rgba(0, 0, 0, 0.8) 0 1px 2px; - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 0.25s; -o-transition-duration: 0.25s; -webkit-transition-duration: 0.25s; @@ -1680,6 +1700,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; border: 1px solid transparent; color: #fff; display: block; @@ -1932,10 +1956,10 @@ label.checkbox.custom { /* line 227, ../../../../general/res/sass/controls/_controls.scss */ .view-switcher { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 100ms; -o-transition-duration: 100ms; -webkit-transition-duration: 100ms; @@ -1943,7 +1967,11 @@ label.checkbox.custom { -moz-transition-timing-function: ease-in-out; -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; } + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; } /******************************************************** OBJECT-HEADER */ /* line 232, ../../../../general/res/sass/controls/_controls.scss */ @@ -1994,6 +2022,10 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; } /* line 287, ../../../../general/res/sass/controls/_controls.scss */ .object-header:hover .context-available { @@ -2010,10 +2042,10 @@ label.checkbox.custom { left: 0; } /* line 308, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 0.25s; -o-transition-duration: 0.25s; -webkit-transition-duration: 0.25s; @@ -2022,6 +2054,10 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; background-color: rgba(0, 153, 204, 0.6); position: absolute; height: 100%; @@ -2053,10 +2089,10 @@ label.checkbox.custom { cursor: e-resize; } /* line 330, ../../../../general/res/sass/controls/_controls.scss */ .slider .range { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 0.25s; -o-transition-duration: 0.25s; -webkit-transition-duration: 0.25s; @@ -2065,6 +2101,10 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; background-color: rgba(0, 153, 204, 0.3); cursor: ew-resize; position: absolute; @@ -2160,6 +2200,10 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; cursor: pointer; } /* line 424, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li.in-month { @@ -2348,7 +2392,7 @@ label.checkbox.custom { .menu ul { margin: 0; padding: 0; } - /* line 326, ../../../../general/res/sass/_mixins.scss */ + /* line 331, ../../../../general/res/sass/_mixins.scss */ .menu ul li { list-style-type: none; margin: 0; @@ -2703,10 +2747,10 @@ mct-include.l-time-controller { z-index: 2; } /* line 161, ../../../../general/res/sass/controls/_time-controller.scss */ mct-include.l-time-controller .knob .range-value { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 0.25s; -o-transition-duration: 0.25s; -webkit-transition-duration: 0.25s; @@ -2715,6 +2759,10 @@ mct-include.l-time-controller { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; padding: 0 10px; position: absolute; height: 20px; @@ -2902,7 +2950,7 @@ mct-include.l-time-controller { padding: 0 3px; position: relative; height: 150px; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .form .form-row .selector-list.error { background: rgba(255, 0, 0, 0.5); } /* line 124, ../../../../general/res/sass/forms/_elems.scss */ @@ -2959,7 +3007,7 @@ input[type="text"] { color: #cccccc; outline: none; padding: 0 3px; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ input[type="text"].error { background: rgba(255, 0, 0, 0.5); } /* line 172, ../../../../general/res/sass/forms/_elems.scss */ @@ -2987,7 +3035,7 @@ textarea { position: absolute; height: 100%; width: 100%; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ textarea.error { background: rgba(255, 0, 0, 0.5); } @@ -3046,14 +3094,14 @@ textarea { overflow: hidden; position: relative; line-height: 22px; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .select .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .select:not(.disabled):hover { background: linear-gradient(#6b6b6b, #5e5e5e); } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .select:not(.disabled):hover > .icon { color: #33ccff; } } /* line 31, ../../../../general/res/sass/forms/_selects.scss */ @@ -3135,7 +3183,7 @@ textarea { max-height: 400px; overflow: auto; padding: 5px; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .channel-selector .treeview.error { background: rgba(255, 0, 0, 0.5); } /* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ @@ -3301,7 +3349,7 @@ span.req { padding: 0 3px; background: #3b3b3b; border-bottom: 1px solid #4d4d4d; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .filter input.filter.error, .filter input.t-filter-input.error, .t-filter input.filter.error, @@ -3609,7 +3657,7 @@ span.req { max-width: 800px; width: 25%; } /* line 214, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-mode .split-layout .split-pane-component.pane.t-inspector.right { +.browse-mode .split-layout .split-pane-component.pane.t-inspect.right { min-width: 150px; max-width: 800px; width: 10%; } @@ -3623,65 +3671,50 @@ span.req { height: 30%; } /* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane:not(.resizing) { - -moz-transition-property: width, left, right; - -o-transition-property: width, left, right; - -webkit-transition-property: width, left, right; - transition-property: width, left, right; - -moz-transition-duration: 250ms; - -o-transition-duration: 250ms; - -webkit-transition-duration: 250ms; - transition-duration: 250ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; } - -/* line 240, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { position: absolute; } - /* line 243, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 242, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 252, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { z-index: 2; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { top: 5px; } - /* line 267, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { left: -30px; } - /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { content: 'F'; } - /* line 273, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { - left: -10px; } - /* line 277, ../../../../general/res/sass/user-environ/_layout.scss */ + left: -25px; } + /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { right: -25px; } - /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { content: '\e608'; } - /* line 283, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { - right: -5px; } } - /* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ + right: -20px; } } + /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3694,31 +3727,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 306, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 313, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 316, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 320, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 330, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 333, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3728,11 +3761,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 337, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 343, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3744,12 +3777,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 361, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3765,56 +3798,108 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 369, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 371, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 379, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 393, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-tree-hidden { - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; } - /* line 396, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane-tree-hidden .pane.left.treeview { - opacity: 0; } - /* line 400, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane-tree-hidden .pane.right-repr { - left: 0 !important; } +/* line 390, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-hidden .pane.left.treeview, +.pane-tree-hidden .splitter-treeview { + opacity: 0; } +/* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-hidden .pane.right.items { + left: 15px !important; } -/* line 407, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-tree-showing .pane.left.treeview { - -moz-transition-property: opacity; - -o-transition-property: opacity; - -webkit-transition-property: opacity; - transition-property: opacity; - -moz-transition-duration: 0.4s; - -o-transition-duration: 0.4s; - -webkit-transition-duration: 0.4s; - transition-duration: 0.4s; +/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-showing .pane.left.treeview, +.pane-tree-showing .splitter-treeview { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; -moz-transition-timing-function: ease-in-out; -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 250ms; + -o-transition-delay: 250ms; + -webkit-transition-delay: 250ms; + transition-delay: 250ms; opacity: 1; } +/* line 411, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-inspect-showing .l-object-and-inspector .pane.right, +.pane-inspect-showing .l-object-and-inspector .splitter-inspect { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 250ms; + -o-transition-delay: 250ms; + -webkit-transition-delay: 250ms; + transition-delay: 250ms; + opacity: 1; } + +/* line 420, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-inspect-hidden .l-object-and-inspector .pane.right, +.pane-inspect-hidden .l-object-and-inspector .splitter-inspect { + opacity: 0; } +/* line 424, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-inspect-hidden .l-object-and-inspector .pane.left { + right: 15px !important; } + +/* line 430, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane.right.t-inspect { + background-color: rgba(255, 165, 0, 0.3) !important; } + +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 435, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane:not(.resizing) { + -moz-transition-property: width, left, right; + -o-transition-property: width, left, right; + -webkit-transition-property: width, left, right; + transition-property: width, left, right; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; } } /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -3850,7 +3935,7 @@ span.req { background-color: #262626; } /* line 35, ../../../../general/res/sass/mobile/_layout.scss */ - .pane.right-repr { + .pane.right.items { -moz-transition-duration: 0.35s; -o-transition-duration: 0.35s; -webkit-transition-duration: 0.35s; @@ -3859,7 +3944,7 @@ span.req { backface-visibility: hidden; margin-left: 0 !important; } /* line 39, ../../../../general/res/sass/mobile/_layout.scss */ - .pane.right-repr #content-area { + .pane.right.items #content-area { -moz-transition-duration: 0.35s; -o-transition-duration: 0.35s; -webkit-transition-duration: 0.35s; @@ -3901,7 +3986,7 @@ span.req { right: auto !important; width: 40% !important; } /* line 88, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.right-repr { + .pane-tree-showing .pane.right.items { left: 40% !important; } /* line 93, ../../../../general/res/sass/mobile/_layout.scss */ @@ -3959,6 +4044,10 @@ span.req { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; } /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ @@ -3976,18 +4065,24 @@ span.req { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ .pane-tree-showing .pane.left.treeview { width: 90% !important; } /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.right-repr { + .pane-tree-showing .pane.right.items { left: 0 !important; - transform: translateX(90%); - -webkit-transform: translateX(90%); } + -moz-transform: translateX(90%); + -ms-transform: translateX(90%); + -webkit-transform: translateX(90%); + transform: translateX(90%); } /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.right-repr #content-area { + .pane-tree-showing .pane.right.items #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ @@ -4431,14 +4526,14 @@ span.req { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover { background: linear-gradient(#a6a6a6, #999999); } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4523,7 +4618,7 @@ ul.tree { -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 326, ../../../../general/res/sass/_mixins.scss */ + /* line 331, ../../../../general/res/sass/_mixins.scss */ ul.tree li { list-style-type: none; margin: 0; @@ -5791,14 +5886,14 @@ table { margin-bottom: 3px; margin-right: 3px; position: relative; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover { background: linear-gradient(#666666, #595959); } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover > .icon { color: #33ccff; } } /* line 45, ../../../../general/res/sass/items/_item.scss */ @@ -5881,6 +5976,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; color: #8c8c8c; font-size: 3em; @@ -5933,14 +6032,14 @@ table { transition: background, 0.25s; text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; color: #80dfff; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected:not(.disabled):hover { background: linear-gradient(#1ac6ff, #00bfff); } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected:not(.disabled):hover > .icon { color: #33ccff; } } /* line 137, ../../../../general/res/sass/items/_item.scss */ @@ -6085,10 +6184,10 @@ table { font-size: 0.75rem; } /* line 32, ../../../../general/res/sass/_autoflow.scss */ .autoflow:hover .l-autoflow-header .s-btn.change-column-width, .autoflow:hover .l-autoflow-header .change-column-width.s-menu-btn { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 50ms; -o-transition-duration: 50ms; -webkit-transition-duration: 50ms; @@ -6097,6 +6196,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 1; } /* line 40, ../../../../general/res/sass/_autoflow.scss */ .autoflow .l-autoflow-header { @@ -6109,10 +6212,10 @@ table { vertical-align: middle; } /* line 48, ../../../../general/res/sass/_autoflow.scss */ .autoflow .l-autoflow-header .s-btn.change-column-width, .autoflow .l-autoflow-header .change-column-width.s-menu-btn { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 500ms; -o-transition-duration: 500ms; -webkit-transition-duration: 500ms; @@ -6121,6 +6224,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; } /* line 52, ../../../../general/res/sass/_autoflow.scss */ .autoflow .l-autoflow-header .l-filter { @@ -6381,6 +6488,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; position: absolute; left: 0; z-index: 1; } @@ -6395,10 +6506,10 @@ table { color: #fff; } /* line 38, ../../../../general/res/sass/features/_time-display.scss */ .l-time-display .l-btn.control { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 200ms; -o-transition-duration: 200ms; -webkit-transition-duration: 200ms; @@ -6407,6 +6518,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; font-size: 0.65em; vertical-align: top; } diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 8c98b9e44e..e7de0af509 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -951,6 +951,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; content: ''; display: block; pointer-events: none; @@ -990,6 +994,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; content: ''; display: block; pointer-events: none; @@ -1368,14 +1376,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.major .icon, .major.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover { background: deepskyblue; } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn.major:not(.disabled):hover > .icon, .major.s-menu-btn:not(.disabled):hover > .icon { color: white; } } /* line 66, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1398,14 +1406,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon { color: #eee; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover { background: #0099cc; } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 75, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1431,14 +1439,14 @@ mct-container { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover { background: #ffad33; } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu-btn:not(.disabled):hover > .icon { color: white; } } /* line 80, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1487,6 +1495,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; color: #d6d6d6; cursor: pointer; display: block; @@ -1515,6 +1527,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; display: block; position: absolute; } /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ @@ -1531,6 +1547,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; font-size: 7px; height: 100%; width: 7px; } @@ -1541,31 +1561,31 @@ mct-container { /* line 158, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:before { content: '\3c'; - left: -8px; } + left: -7px; } /* line 162, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:hover:before { - left: -11px; } + left: -9px; } /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left.collapsed:before { content: '\3e'; - left: 13px; } + left: 12px; } /* line 169, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left.collapsed:hover:before { - left: 16px; } + left: 14px; } /* line 175, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:before { content: '\3e'; - left: 13px; } + left: 12px; } /* line 179, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:hover:before { - left: 16px; } + left: 14px; } /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right.collapsed:before { content: '\3c'; - left: -8px; } + left: -7px; } /* line 186, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right.collapsed:hover:before { - left: -11px; } } + left: -9px; } } /* line 196, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { @@ -1638,10 +1658,10 @@ mct-container { -webkit-box-sizing: border-box; box-sizing: border-box; text-shadow: rgba(0, 0, 0, 0.8) 0 1px 2px; - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 0.25s; -o-transition-duration: 0.25s; -webkit-transition-duration: 0.25s; @@ -1650,6 +1670,10 @@ mct-container { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; border: 1px solid transparent; color: #fff; display: block; @@ -1902,10 +1926,10 @@ label.checkbox.custom { /* line 227, ../../../../general/res/sass/controls/_controls.scss */ .view-switcher { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 100ms; -o-transition-duration: 100ms; -webkit-transition-duration: 100ms; @@ -1913,7 +1937,11 @@ label.checkbox.custom { -moz-transition-timing-function: ease-in-out; -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; } + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; } /******************************************************** OBJECT-HEADER */ /* line 232, ../../../../general/res/sass/controls/_controls.scss */ @@ -1964,6 +1992,10 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; } /* line 287, ../../../../general/res/sass/controls/_controls.scss */ .object-header:hover .context-available { @@ -1980,10 +2012,10 @@ label.checkbox.custom { left: 0; } /* line 308, ../../../../general/res/sass/controls/_controls.scss */ .slider .knob { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 0.25s; -o-transition-duration: 0.25s; -webkit-transition-duration: 0.25s; @@ -1992,6 +2024,10 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; background-color: rgba(0, 153, 204, 0.5); position: absolute; height: 100%; @@ -2023,10 +2059,10 @@ label.checkbox.custom { cursor: e-resize; } /* line 330, ../../../../general/res/sass/controls/_controls.scss */ .slider .range { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 0.25s; -o-transition-duration: 0.25s; -webkit-transition-duration: 0.25s; @@ -2035,6 +2071,10 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; background-color: rgba(0, 153, 204, 0.2); cursor: ew-resize; position: absolute; @@ -2130,6 +2170,10 @@ label.checkbox.custom { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; cursor: pointer; } /* line 424, ../../../../general/res/sass/controls/_controls.scss */ .l-calendar ul.l-cal-row.l-body li.in-month { @@ -2312,7 +2356,7 @@ label.checkbox.custom { .menu ul { margin: 0; padding: 0; } - /* line 326, ../../../../general/res/sass/_mixins.scss */ + /* line 331, ../../../../general/res/sass/_mixins.scss */ .menu ul li { list-style-type: none; margin: 0; @@ -2667,10 +2711,10 @@ mct-include.l-time-controller { z-index: 2; } /* line 161, ../../../../general/res/sass/controls/_time-controller.scss */ mct-include.l-time-controller .knob .range-value { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 0.25s; -o-transition-duration: 0.25s; -webkit-transition-duration: 0.25s; @@ -2679,6 +2723,10 @@ mct-include.l-time-controller { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; padding: 0 10px; position: absolute; height: 20px; @@ -2866,7 +2914,7 @@ mct-include.l-time-controller { padding: 0 3px; position: relative; height: 150px; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .form .form-row .selector-list.error { background: rgba(255, 0, 0, 0.5); } /* line 124, ../../../../general/res/sass/forms/_elems.scss */ @@ -2923,7 +2971,7 @@ input[type="text"] { color: #666; outline: none; padding: 0 3px; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ input[type="text"].error { background: rgba(255, 0, 0, 0.5); } /* line 172, ../../../../general/res/sass/forms/_elems.scss */ @@ -2951,7 +2999,7 @@ textarea { position: absolute; height: 100%; width: 100%; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ textarea.error { background: rgba(255, 0, 0, 0.5); } @@ -3000,7 +3048,7 @@ textarea { overflow: hidden; position: relative; line-height: 22px; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .select .icon { color: #eee; } /* line 31, ../../../../general/res/sass/forms/_selects.scss */ @@ -3082,7 +3130,7 @@ textarea { max-height: 400px; overflow: auto; padding: 5px; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .channel-selector .treeview.error { background: rgba(255, 0, 0, 0.5); } /* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ @@ -3248,7 +3296,7 @@ span.req { padding: 0 3px; background: white; border-bottom: 1px solid white; } - /* line 293, ../../../../general/res/sass/_mixins.scss */ + /* line 298, ../../../../general/res/sass/_mixins.scss */ .filter input.filter.error, .filter input.t-filter-input.error, .t-filter input.filter.error, @@ -3556,7 +3604,7 @@ span.req { max-width: 800px; width: 25%; } /* line 214, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-mode .split-layout .split-pane-component.pane.t-inspector.right { +.browse-mode .split-layout .split-pane-component.pane.t-inspect.right { min-width: 150px; max-width: 800px; width: 10%; } @@ -3570,65 +3618,50 @@ span.req { height: 30%; } /* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane:not(.resizing) { - -moz-transition-property: width, left, right; - -o-transition-property: width, left, right; - -webkit-transition-property: width, left, right; - transition-property: width, left, right; - -moz-transition-duration: 250ms; - -o-transition-duration: 250ms; - -webkit-transition-duration: 250ms; - transition-duration: 250ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; } - -/* line 240, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { position: absolute; } - /* line 243, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 242, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 252, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { z-index: 2; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 261, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { top: 5px; } - /* line 267, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { left: -30px; } - /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { content: 'F'; } - /* line 273, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { - left: -10px; } - /* line 277, ../../../../general/res/sass/user-environ/_layout.scss */ + left: -25px; } + /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { right: -25px; } - /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { content: '\e608'; } - /* line 283, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { - right: -5px; } } - /* line 292, ../../../../general/res/sass/user-environ/_layout.scss */ + right: -20px; } } + /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3641,31 +3674,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 303, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 306, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 313, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 316, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 320, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 322, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 330, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 333, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3675,11 +3708,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 337, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 343, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3691,12 +3724,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 361, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3712,56 +3745,108 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 369, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 371, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 379, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 393, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-tree-hidden { - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; } - /* line 396, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane-tree-hidden .pane.left.treeview { - opacity: 0; } - /* line 400, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane-tree-hidden .pane.right-repr { - left: 0 !important; } +/* line 390, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-hidden .pane.left.treeview, +.pane-tree-hidden .splitter-treeview { + opacity: 0; } +/* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-hidden .pane.right.items { + left: 15px !important; } -/* line 407, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-tree-showing .pane.left.treeview { - -moz-transition-property: opacity; - -o-transition-property: opacity; - -webkit-transition-property: opacity; - transition-property: opacity; - -moz-transition-duration: 0.4s; - -o-transition-duration: 0.4s; - -webkit-transition-duration: 0.4s; - transition-duration: 0.4s; +/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-showing .pane.left.treeview, +.pane-tree-showing .splitter-treeview { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; -moz-transition-timing-function: ease-in-out; -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 250ms; + -o-transition-delay: 250ms; + -webkit-transition-delay: 250ms; + transition-delay: 250ms; opacity: 1; } +/* line 411, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-inspect-showing .l-object-and-inspector .pane.right, +.pane-inspect-showing .l-object-and-inspector .splitter-inspect { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 250ms; + -o-transition-delay: 250ms; + -webkit-transition-delay: 250ms; + transition-delay: 250ms; + opacity: 1; } + +/* line 420, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-inspect-hidden .l-object-and-inspector .pane.right, +.pane-inspect-hidden .l-object-and-inspector .splitter-inspect { + opacity: 0; } +/* line 424, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-inspect-hidden .l-object-and-inspector .pane.left { + right: 15px !important; } + +/* line 430, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane.right.t-inspect { + background-color: rgba(255, 165, 0, 0.3) !important; } + +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 435, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane:not(.resizing) { + -moz-transition-property: width, left, right; + -o-transition-property: width, left, right; + -webkit-transition-property: width, left, right; + transition-property: width, left, right; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; } } /***************************************************************************** * Open MCT Web, Copyright (c) 2014-2015, United States Government * as represented by the Administrator of the National Aeronautics and Space @@ -3797,7 +3882,7 @@ span.req { background-color: #f7f7f7; } /* line 35, ../../../../general/res/sass/mobile/_layout.scss */ - .pane.right-repr { + .pane.right.items { -moz-transition-duration: 0.35s; -o-transition-duration: 0.35s; -webkit-transition-duration: 0.35s; @@ -3806,7 +3891,7 @@ span.req { backface-visibility: hidden; margin-left: 0 !important; } /* line 39, ../../../../general/res/sass/mobile/_layout.scss */ - .pane.right-repr #content-area { + .pane.right.items #content-area { -moz-transition-duration: 0.35s; -o-transition-duration: 0.35s; -webkit-transition-duration: 0.35s; @@ -3848,7 +3933,7 @@ span.req { right: auto !important; width: 40% !important; } /* line 88, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.right-repr { + .pane-tree-showing .pane.right.items { left: 40% !important; } /* line 93, ../../../../general/res/sass/mobile/_layout.scss */ @@ -3906,6 +3991,10 @@ span.req { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; } /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ @@ -3923,18 +4012,24 @@ span.req { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 1; } } @media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ .pane-tree-showing .pane.left.treeview { width: 90% !important; } /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.right-repr { + .pane-tree-showing .pane.right.items { left: 0 !important; - transform: translateX(90%); - -webkit-transform: translateX(90%); } + -moz-transform: translateX(90%); + -ms-transform: translateX(90%); + -webkit-transform: translateX(90%); + transform: translateX(90%); } /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.right-repr #content-area { + .pane-tree-showing .pane.right.items #content-area { opacity: 0; } } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ @@ -4360,14 +4455,14 @@ span.req { -webkit-transition: background, 0.25s; transition: background, 0.25s; text-shadow: none; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon { color: #fff; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover { background: #7d7d7d; } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover > .icon { color: white; } } /* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ @@ -4452,7 +4547,7 @@ ul.tree { -ms-user-select: none; -webkit-user-select: none; user-select: none; } - /* line 326, ../../../../general/res/sass/_mixins.scss */ + /* line 331, ../../../../general/res/sass/_mixins.scss */ ul.tree li { list-style-type: none; margin: 0; @@ -5710,14 +5805,14 @@ table { margin-bottom: 3px; margin-right: 3px; position: relative; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item .icon { color: #0099cc; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 274, ../../../../general/res/sass/_mixins.scss */ + /* line 279, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover { background: #d0d0d0; } - /* line 276, ../../../../general/res/sass/_mixins.scss */ + /* line 281, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item:not(.disabled):hover > .icon { color: #33ccff; } } /* line 45, ../../../../general/res/sass/items/_item.scss */ @@ -5800,6 +5895,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; color: #8c8c8c; font-size: 3em; @@ -5843,7 +5942,7 @@ table { transition: background, 0.25s; text-shadow: none; color: #80dfff; } - /* line 269, ../../../../general/res/sass/_mixins.scss */ + /* line 274, ../../../../general/res/sass/_mixins.scss */ .items-holder .item.grid-item.selected .icon { color: #eee; } /* line 137, ../../../../general/res/sass/items/_item.scss */ @@ -5988,10 +6087,10 @@ table { font-size: 0.75rem; } /* line 32, ../../../../general/res/sass/_autoflow.scss */ .autoflow:hover .l-autoflow-header .s-btn.change-column-width, .autoflow:hover .l-autoflow-header .change-column-width.s-menu-btn { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 50ms; -o-transition-duration: 50ms; -webkit-transition-duration: 50ms; @@ -6000,6 +6099,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 1; } /* line 40, ../../../../general/res/sass/_autoflow.scss */ .autoflow .l-autoflow-header { @@ -6012,10 +6115,10 @@ table { vertical-align: middle; } /* line 48, ../../../../general/res/sass/_autoflow.scss */ .autoflow .l-autoflow-header .s-btn.change-column-width, .autoflow .l-autoflow-header .change-column-width.s-menu-btn { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 500ms; -o-transition-duration: 500ms; -webkit-transition-duration: 500ms; @@ -6024,6 +6127,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; } /* line 52, ../../../../general/res/sass/_autoflow.scss */ .autoflow .l-autoflow-header .l-filter { @@ -6284,6 +6391,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; position: absolute; left: 0; z-index: 1; } @@ -6298,10 +6409,10 @@ table { color: #fff; } /* line 38, ../../../../general/res/sass/features/_time-display.scss */ .l-time-display .l-btn.control { - -moz-transition-property: visibility, opacity, background-color, border-color; - -o-transition-property: visibility, opacity, background-color, border-color; - -webkit-transition-property: visibility, opacity, background-color, border-color; - transition-property: visibility, opacity, background-color, border-color; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; -moz-transition-duration: 200ms; -o-transition-duration: 200ms; -webkit-transition-duration: 200ms; @@ -6310,6 +6421,10 @@ table { -o-transition-timing-function: ease-in-out; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; opacity: 0; font-size: 0.65em; vertical-align: top; } From 0c4d422e2d26f04c98a32e467a939e7d92105ca0 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 23 Oct 2015 11:13:51 -0700 Subject: [PATCH 134/488] [Frontend] Manual re-do of collapse/expand panes open #90 Refactored templates to move all split-pane elements into browse.html; --- .../browse/res/templates/browse-object.html | 59 ++++++++----------- .../commonUI/browse/res/templates/browse.html | 20 +++++-- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html index 9464aaee45..9dc1c8f83a 100644 --- a/platform/commonUI/browse/res/templates/browse-object.html +++ b/platform/commonUI/browse/res/templates/browse-object.html @@ -20,42 +20,29 @@ at runtime from the About dialog for additional information. --> - -
-
-
- - - -
-
- - - - - -
-
- +
+
+ + - -
- - - -
-
- +
+ + + + + +
+
+ + diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index c1eff0b907..d59ac222c0 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -58,10 +58,22 @@
- - + +
+ + + +
+ + + +
+ +
+
From 2452b25fd47288f8fed6ff9fd5987d9e9b1b5dd6 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 23 Oct 2015 11:37:02 -0700 Subject: [PATCH 135/488] [Frontend] Manual integration of Inspector open #90 open #73 In-progress manual integrated Sarah Hale's work on ObjectInspector; --- platform/commonUI/browse/res/templates/browse.html | 5 ++++- platform/commonUI/general/bundle.json | 9 +++++++++ platform/commonUI/general/test/suite.json | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index d59ac222c0..79644b5bb5 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -71,7 +71,10 @@
- + +
diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index 1aa0b1dfc1..a927ee8a51 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -107,6 +107,11 @@ "key": "SelectorController", "implementation": "controllers/SelectorController.js", "depends": [ "objectService", "$scope" ] + }, + { + "key": "ObjectInspectorController", + "implementation": "controllers/ObjectInspectorController.js", + "depends": [ "$scope", "objectService" ] } ], "directives": [ @@ -232,6 +237,10 @@ "key": "switcher", "templateUrl": "templates/controls/switcher.html", "uses": [ "view" ] + }, + { + "key": "object-inspector", + "templateUrl": "templates/object-inspector.html" } ], "controls": [ diff --git a/platform/commonUI/general/test/suite.json b/platform/commonUI/general/test/suite.json index 0d19fbb9e4..08e9c28e61 100644 --- a/platform/commonUI/general/test/suite.json +++ b/platform/commonUI/general/test/suite.json @@ -5,6 +5,7 @@ "controllers/ContextMenuController", "controllers/DateTimePickerController", "controllers/GetterSetterController", + "controllers/ObjectInspectorController", "controllers/SelectorController", "controllers/SplitPaneController", "controllers/TimeRangeController", From 1d3870d07f8d5c11a84b39d588c1a22ca3252821 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 23 Oct 2015 11:42:06 -0700 Subject: [PATCH 136/488] [Frontend] Manual integration of Inspector open #90 open #73 Adding missed new files; --- .../res/templates/object-inspector.html | 55 ++++++++ .../controllers/ObjectInspectorController.js | 117 ++++++++++++++++++ .../ObjectInspectorControllerSpec.js | 112 +++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 platform/commonUI/general/res/templates/object-inspector.html create mode 100644 platform/commonUI/general/src/controllers/ObjectInspectorController.js create mode 100644 platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js diff --git a/platform/commonUI/general/res/templates/object-inspector.html b/platform/commonUI/general/res/templates/object-inspector.html new file mode 100644 index 0000000000..02e7f3fcc1 --- /dev/null +++ b/platform/commonUI/general/res/templates/object-inspector.html @@ -0,0 +1,55 @@ + + +
    +
  • + {{ data.name }} + {{ data.value }} +
  • +
  • + + Location + + + + + +
  • +
  • + + Original Location + + + + + +
  • +
+
diff --git a/platform/commonUI/general/src/controllers/ObjectInspectorController.js b/platform/commonUI/general/src/controllers/ObjectInspectorController.js new file mode 100644 index 0000000000..5b04304af0 --- /dev/null +++ b/platform/commonUI/general/src/controllers/ObjectInspectorController.js @@ -0,0 +1,117 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define,Promise*/ + +/** + * Module defining ObjectInspectorController. Created by shale on 08/21/2015. + */ +define( + [], + function () { + "use strict"; + + /** + * The ObjectInspectorController gets and formats the data for + * the inspector display + * + * @constructor + */ + function ObjectInspectorController($scope, objectService) { + $scope.primaryParents = []; + $scope.contextutalParents = []; + //$scope.isLink = false; + + // Gets an array of the contextual parents/anscestors of the selected object + function getContextualPath() { + var currentObj = $scope.ngModel.selectedObject, + currentParent, + parents = []; + + currentParent = currentObj && + currentObj.hasCapability('context') && + currentObj.getCapability('context').getParent(); + + while (currentParent && currentParent.getModel().type !== 'root' && + currentParent.hasCapability('context')) { + // Record this object + parents.unshift(currentParent); + + // Get the next one up the tree + currentObj = currentParent; + currentParent = currentObj.getCapability('context').getParent(); + } + + $scope.contextutalParents = parents; + } + + // Gets an array of the parents/anscestors of the selected object's + // primary location (locational of original non-link) + function getPrimaryPath(current) { + var location; + + // If this the the initial call of this recursive function + if (!current) { + current = $scope.ngModel.selectedObject; + $scope.primaryParents = []; + } + + location = current.getModel().location; + + if (location && location !== 'root') { + objectService.getObjects([location]).then(function (obj) { + var next = obj[location]; + + $scope.primaryParents.unshift(next); + getPrimaryPath(next); + }); + } + + } + + // Gets the metadata for the selected object + function getMetadata() { + $scope.metadata = $scope.ngModel.selectedObject && + $scope.ngModel.selectedObject.hasCapability('metadata') && + $scope.ngModel.selectedObject.useCapability('metadata'); + } + + // Set scope variables when the selected object changes + $scope.$watch('ngModel.selectedObject', function () { + $scope.isLink = $scope.ngModel.selectedObject && + $scope.ngModel.selectedObject.hasCapability('location') && + $scope.ngModel.selectedObject.getCapability('location').isLink(); + + if ($scope.isLink) { + getPrimaryPath(); + getContextualPath(); + } else { + $scope.primaryParents = []; + getContextualPath(); + } + + getMetadata(); + }); + } + + return ObjectInspectorController; + } +); \ No newline at end of file diff --git a/platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js b/platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js new file mode 100644 index 0000000000..496467ea2d --- /dev/null +++ b/platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js @@ -0,0 +1,112 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +/** + * Created by shale on 08/24/2015. + */ +define( + ["../../src/controllers/ObjectInspectorController"], + function (ObjectInspectorController) { + "use strict"; + + describe("The object inspector controller ", function () { + var mockScope, + mockObjectService, + mockPromise, + mockDomainObject, + mockContextCapability, + mockLocationCapability, + controller; + + beforeEach(function () { + mockScope = jasmine.createSpyObj( + "$scope", + [ "$watch" ] + ); + mockScope.ngModel = {}; + mockScope.ngModel.selectedObject = 'mock selected object'; + + mockObjectService = jasmine.createSpyObj( + "objectService", + [ "getObjects" ] + ); + mockPromise = jasmine.createSpyObj( + "promise", + [ "then" ] + ); + mockObjectService.getObjects.andReturn(mockPromise); + + mockDomainObject = jasmine.createSpyObj( + "selectedObject", + [ "hasCapability", "getCapability", "useCapability", "getModel" ] + ); + mockDomainObject.getModel.andReturn({location: 'somewhere'}); + mockDomainObject.hasCapability.andReturn(true); + + mockContextCapability = jasmine.createSpyObj( + "context capability", + [ "getParent" ] + ); + mockLocationCapability = jasmine.createSpyObj( + "location capability", + [ "isLink" ] + ); + mockDomainObject.getCapability.andCallFake(function (param) { + if (param === 'location') { + return mockLocationCapability; + } else if (param === 'context') { + return mockContextCapability; + } + }); + + controller = new ObjectInspectorController(mockScope, mockObjectService); + + // Change the selected object to trigger the watch call + mockScope.ngModel.selectedObject = mockDomainObject; + }); + + it("watches for changes to the selected object", function () { + expect(mockScope.$watch).toHaveBeenCalledWith('ngModel.selectedObject', jasmine.any(Function)); + }); + + it("looks for contextual parent objects", function () { + mockScope.$watch.mostRecentCall.args[1](); + expect(mockContextCapability.getParent).toHaveBeenCalled(); + }); + + it("if link, looks for primary parent objects", function () { + mockLocationCapability.isLink.andReturn(true); + + mockScope.$watch.mostRecentCall.args[1](); + expect(mockDomainObject.getModel).toHaveBeenCalled(); + expect(mockObjectService.getObjects).toHaveBeenCalled(); + mockPromise.then.mostRecentCall.args[0]({'somewhere': mockDomainObject}); + }); + + it("gets metadata", function () { + mockScope.$watch.mostRecentCall.args[1](); + expect(mockDomainObject.useCapability).toHaveBeenCalledWith('metadata'); + }); + }); + } +); \ No newline at end of file From 78b528b4a575f606f2c481f8f7707a7fd2f23232 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 23 Oct 2015 11:48:25 -0700 Subject: [PATCH 137/488] Added additional test cases, and some tidying --- .../notification/src/NotificationIndicator.js | 17 +--- .../src/NotificationIndicatorController.js | 11 +-- .../NotificationIndicatorControllerSpec.js | 80 +++++++++++++++++++ .../commonUI/notification/test/suite.json | 3 +- 4 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js diff --git a/platform/commonUI/notification/src/NotificationIndicator.js b/platform/commonUI/notification/src/NotificationIndicator.js index b1d20e8264..29a831d251 100644 --- a/platform/commonUI/notification/src/NotificationIndicator.js +++ b/platform/commonUI/notification/src/NotificationIndicator.js @@ -26,25 +26,10 @@ define( function () { "use strict"; - function NotificationIndicator() { - - } + function NotificationIndicator() {} NotificationIndicator.template = 'notificationIndicatorTemplate'; - NotificationIndicator.prototype.getGlyph = function () { - return "A"; - }; - NotificationIndicator.prototype.getGlyphClass = function () { - return 'caution'; - }; - NotificationIndicator.prototype.getText = function () { - return "Notifications"; - }; - NotificationIndicator.prototype.getDescription = function () { - return "Notifications"; - }; - return NotificationIndicator; } ); diff --git a/platform/commonUI/notification/src/NotificationIndicatorController.js b/platform/commonUI/notification/src/NotificationIndicatorController.js index 21da649a8f..685152de9c 100644 --- a/platform/commonUI/notification/src/NotificationIndicatorController.js +++ b/platform/commonUI/notification/src/NotificationIndicatorController.js @@ -45,14 +45,11 @@ define( * Launch a dialog showing a list of current notifications. */ $scope.showNotificationsList = function(){ - - var model = { - title: "Messages" - }; - - model.messages = notificationService.notifications; dialogService.getDialogResponse('overlay-message-list', { - dialog: model, + dialog: { + title: "Messages", + messages: notificationService.notifications + }, cancel: function(){ dialogService.dismiss(); } diff --git a/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js b/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js new file mode 100644 index 0000000000..fa906012ae --- /dev/null +++ b/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js @@ -0,0 +1,80 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */ + +define( + ['../src/NotificationIndicatorController'], + function (NotificationIndicatorController) { + "use strict"; + + describe("The notification indicator controller ", function () { + var mockNotificationService, + mockScope, + mockDialogService; + + beforeEach(function(){ + mockNotificationService = jasmine.createSpy("notificationService"); + mockScope = jasmine.createSpy("$scope"); + mockDialogService = { + getDialogResponse: function(template, model){}, + dismiss: function(){} + } + + spyOn(mockDialogService, 'getDialogResponse'); + spyOn(mockDialogService, 'dismiss'); + }); + + it("exposes the highest notification severity to the template", function() { + mockNotificationService.highest = { + severity: "error" + }; + var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService); + expect(mockScope.highest).toBeTruthy(); + expect(mockScope.highest.severity).toBe("error"); + }); + + it("invokes the dialog service to show list of messages", function() { + var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService); + expect(mockScope.showNotificationsList).toBeDefined(); + mockScope.showNotificationsList(); + expect(mockDialogService.getDialogResponse).toHaveBeenCalled(); + expect(mockDialogService.getDialogResponse.mostRecentCall.args[0]).toBe('overlay-message-list'); + expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].dialog).toBeDefined(); + expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined(); + //Invoke the cancel callback + mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel(); + expect(mockDialogService.dismiss).toHaveBeenCalled(); + }); + + it("provides a means of dismissing the message list", function() { + var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService); + expect(mockScope.showNotificationsList).toBeDefined(); + mockScope.showNotificationsList(); + expect(mockDialogService.getDialogResponse).toHaveBeenCalled(); + expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined(); + //Invoke the cancel callback + mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel(); + expect(mockDialogService.dismiss).toHaveBeenCalled(); + }) + + }); + }); \ No newline at end of file diff --git a/platform/commonUI/notification/test/suite.json b/platform/commonUI/notification/test/suite.json index 08238b16c0..2b52722303 100644 --- a/platform/commonUI/notification/test/suite.json +++ b/platform/commonUI/notification/test/suite.json @@ -1,3 +1,4 @@ [ - "NotificationService" + "NotificationService", + "NotificationIndicatorController" ] \ No newline at end of file From fa7431d68bb8070710c3e072661595b67e9ad23d Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 23 Oct 2015 12:01:48 -0700 Subject: [PATCH 138/488] Added test case to DialogServiceSpec --- platform/commonUI/dialog/test/DialogServiceSpec.js | 11 +++++++++++ .../test/NotificationIndicatorControllerSpec.js | 11 ++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/platform/commonUI/dialog/test/DialogServiceSpec.js b/platform/commonUI/dialog/test/DialogServiceSpec.js index 949c1102a8..ddd826ef39 100644 --- a/platform/commonUI/dialog/test/DialogServiceSpec.js +++ b/platform/commonUI/dialog/test/DialogServiceSpec.js @@ -121,6 +121,17 @@ define( ); }); + it("invokes the overlay service with the correct parameters when" + + " a blocking dialog is requested", function() { + var dialogModel = {}; + expect(dialogService.showBlockingMessage(dialogModel)).toBe(true); + expect(mockOverlayService.createOverlay).toHaveBeenCalledWith( + "overlay-blocking-message", + dialogModel, + "t-dialog-sm" + ); + }); + }); } ); \ No newline at end of file diff --git a/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js b/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js index fa906012ae..f20e3dd357 100644 --- a/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js +++ b/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js @@ -34,13 +34,10 @@ define( beforeEach(function(){ mockNotificationService = jasmine.createSpy("notificationService"); mockScope = jasmine.createSpy("$scope"); - mockDialogService = { - getDialogResponse: function(template, model){}, - dismiss: function(){} - } - - spyOn(mockDialogService, 'getDialogResponse'); - spyOn(mockDialogService, 'dismiss'); + mockDialogService = jasmine.createSpyObj( + "dialogService", + ["getDialogResponse","dismiss"] + ); }); it("exposes the highest notification severity to the template", function() { From 89046ecad563319d06d4972329ca5c6b30ed85f7 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 23 Oct 2015 12:03:56 -0700 Subject: [PATCH 139/488] fixed jslint error --- .../notification/test/NotificationIndicatorControllerSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js b/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js index f20e3dd357..1c85704fc4 100644 --- a/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js +++ b/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js @@ -71,7 +71,7 @@ define( //Invoke the cancel callback mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel(); expect(mockDialogService.dismiss).toHaveBeenCalled(); - }) + }); }); }); \ No newline at end of file From e05858e26bc82d00ee4d15109dbff9e40b876f92 Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 23 Oct 2015 12:11:37 -0700 Subject: [PATCH 140/488] Added newline to end of source files --- platform/commonUI/dialog/test/DialogServiceSpec.js | 2 +- platform/commonUI/notification/bundle.json | 2 +- .../notification/src/NotificationIndicatorController.js | 1 + platform/commonUI/notification/src/NotificationService.js | 2 +- .../notification/test/NotificationIndicatorControllerSpec.js | 3 ++- platform/commonUI/notification/test/NotificationServiceSpec.js | 3 ++- platform/commonUI/notification/test/suite.json | 2 +- 7 files changed, 9 insertions(+), 6 deletions(-) diff --git a/platform/commonUI/dialog/test/DialogServiceSpec.js b/platform/commonUI/dialog/test/DialogServiceSpec.js index ddd826ef39..2dc109ac44 100644 --- a/platform/commonUI/dialog/test/DialogServiceSpec.js +++ b/platform/commonUI/dialog/test/DialogServiceSpec.js @@ -134,4 +134,4 @@ define( }); } -); \ No newline at end of file +); diff --git a/platform/commonUI/notification/bundle.json b/platform/commonUI/notification/bundle.json index f4dbd5c71a..4851dd28b6 100644 --- a/platform/commonUI/notification/bundle.json +++ b/platform/commonUI/notification/bundle.json @@ -42,4 +42,4 @@ } ] } -} \ No newline at end of file +} diff --git a/platform/commonUI/notification/src/NotificationIndicatorController.js b/platform/commonUI/notification/src/NotificationIndicatorController.js index 685152de9c..66ad825a32 100644 --- a/platform/commonUI/notification/src/NotificationIndicatorController.js +++ b/platform/commonUI/notification/src/NotificationIndicatorController.js @@ -60,3 +60,4 @@ define( return NotificationIndicatorController; } ); + diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 8887e7cb9a..140c4537c8 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -282,4 +282,4 @@ define( return NotificationService; } -); \ No newline at end of file +); diff --git a/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js b/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js index 1c85704fc4..59450fd060 100644 --- a/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js +++ b/platform/commonUI/notification/test/NotificationIndicatorControllerSpec.js @@ -74,4 +74,5 @@ define( }); }); - }); \ No newline at end of file + } +); diff --git a/platform/commonUI/notification/test/NotificationServiceSpec.js b/platform/commonUI/notification/test/NotificationServiceSpec.js index dca76652ce..695275bc70 100644 --- a/platform/commonUI/notification/test/NotificationServiceSpec.js +++ b/platform/commonUI/notification/test/NotificationServiceSpec.js @@ -174,4 +174,5 @@ define( }); }); }); - }); \ No newline at end of file + } +); diff --git a/platform/commonUI/notification/test/suite.json b/platform/commonUI/notification/test/suite.json index 2b52722303..75e88599fd 100644 --- a/platform/commonUI/notification/test/suite.json +++ b/platform/commonUI/notification/test/suite.json @@ -1,4 +1,4 @@ [ "NotificationService", "NotificationIndicatorController" -] \ No newline at end of file +] From bf24ac7c933fb14cb9bd108e9b1b50070ac8a507 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 12:14:46 -0700 Subject: [PATCH 141/488] [Search] Update field name Update field name in GenericSearchProvider to reflect changes from nasa/openmctweb#193. Avoids exceptions on mutation. Additionally, add test case exercising relevant code and verifying that reindexing is scheduled upon mutation as expected. --- platform/search/src/services/GenericSearchProvider.js | 2 +- .../search/test/services/GenericSearchProviderSpec.js | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/search/src/services/GenericSearchProvider.js b/platform/search/src/services/GenericSearchProvider.js index 2b99abcae2..71dfe8c0ed 100644 --- a/platform/search/src/services/GenericSearchProvider.js +++ b/platform/search/src/services/GenericSearchProvider.js @@ -122,7 +122,7 @@ define([ mutationTopic.listen(function (mutatedObject) { var id = mutatedObject.getId(); - provider.indexed[id] = false; + provider.indexedIds[id] = false; provider.scheduleForIndexing(id); }); }; diff --git a/platform/search/test/services/GenericSearchProviderSpec.js b/platform/search/test/services/GenericSearchProviderSpec.js index 7b8e52274b..cc80e4210d 100644 --- a/platform/search/test/services/GenericSearchProviderSpec.js +++ b/platform/search/test/services/GenericSearchProviderSpec.js @@ -99,6 +99,14 @@ define([ .toHaveBeenCalledWith(jasmine.any(Function)); }); + it('reschedules indexing when mutation occurs', function () { + var mockDomainObject = + jasmine.createSpyObj('domainObj', ['getId']); + mockDomainObject.getId.andReturn("some-id"); + mutationTopic.listen.mostRecentCall.args[0](mockDomainObject); + expect(provider.scheduleForIndexing).toHaveBeenCalledWith('some-id'); + }); + it('starts indexing roots', function () { expect(provider.scheduleForIndexing).toHaveBeenCalledWith('mine'); }); From a7ea4c3c6e2c43d18101745f6182f68d50722972 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 23 Oct 2015 12:35:55 -0700 Subject: [PATCH 142/488] [Frontend] Inspector styling, in progress open #90 --- .../commonUI/browse/res/templates/browse.html | 3 +- .../{_properties.scss => _inspector.scss} | 54 +- platform/commonUI/general/res/sass/_main.scss | 2 +- .../res/sass/user-environ/_layout.scss | 4 - .../res/templates/object-inspector.html | 19 +- .../espresso/res/css/theme-espresso.css | 59 +- .../themes/espresso/res/sass/_constants.scss | 7 + .../themes/snow/res/css/theme-snow.css | 6535 +---------------- 8 files changed, 210 insertions(+), 6473 deletions(-) rename platform/commonUI/general/res/sass/{_properties.scss => _inspector.scss} (51%) diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 79644b5bb5..0ea964780c 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -73,7 +73,8 @@
+ ng-model="treeModel" + class="t-inspector">
diff --git a/platform/commonUI/general/res/sass/_properties.scss b/platform/commonUI/general/res/sass/_inspector.scss similarity index 51% rename from platform/commonUI/general/res/sass/_properties.scss rename to platform/commonUI/general/res/sass/_inspector.scss index b48fe66406..69e00dbab6 100644 --- a/platform/commonUI/general/res/sass/_properties.scss +++ b/platform/commonUI/general/res/sass/_inspector.scss @@ -19,17 +19,55 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* Classes to be used for lists of properties and values */ +/* Styles for the Inspector pane */ -.properties { - .s-row { - border-top: 1px solid $colorInteriorBorder; - font-size: 0.8em; - &:first-child { +//.pane.right.t-inspect { @include test(green, 0.3); } // TEMP! + +.t-inspector, +.t-inspector table tr td { + font-size: 0.7rem; +} + +.t-inspector { + ul li, + em { + display: block; + position: relative; + } + + ul li { + line-height: 130%; + margin-bottom: $interiorMarginLg * 2; + } + + em { + @include border-radius($basicCr); + background-color: $colorInspectorSectionHeaderBg; + color: $colorInspectorSectionHeaderFg; + margin-bottom: $interiorMargin; + padding: $formTBPad $formLRPad; + text-transform: uppercase; + } + + table tr { + td { border: none; + border-top: 1px solid $colorInteriorBorder !important; + padding: 2px 0; + vertical-align: top; + &.label { + color: $colorInspectorPropName !important; + padding-right: $interiorMargin !important; + white-space: nowrap; + } + &.value { + //word-wrap: break-word; // Doesn't work in ? + word-break: break-all; + } } - .s-value { - color: #fff; + + &:first-child td { + border-top: none !important; } } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_main.scss b/platform/commonUI/general/res/sass/_main.scss index 80023f55ac..f22d5a4231 100644 --- a/platform/commonUI/general/res/sass/_main.scss +++ b/platform/commonUI/general/res/sass/_main.scss @@ -30,7 +30,7 @@ @import "helpers/splitter"; @import "helpers/wait-spinner"; @import "messages"; -@import "properties"; +@import "inspector"; /********************************* CONTROLS */ @import "controls/breadcrumb"; diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 6d7895ec83..49944adc44 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -427,10 +427,6 @@ } } -.pane.right.t-inspect { - @include test(orange, 0.3); // TEMP! -} - @include desktop { .pane:not(.resizing) { @include trans-prop-nice-resize-w(250ms); diff --git a/platform/commonUI/general/res/templates/object-inspector.html b/platform/commonUI/general/res/templates/object-inspector.html index 02e7f3fcc1..f5f8e6921b 100644 --- a/platform/commonUI/general/res/templates/object-inspector.html +++ b/platform/commonUI/general/res/templates/object-inspector.html @@ -21,14 +21,17 @@ -->
    -
  • - {{ data.name }} - {{ data.value }} +
  • + Properties + + + + + +
    {{ data.name }}{{ data.value }}
  • - - Location - + Location
  • - - Original Location - + Original Location .splitter { - cursor: row-resize; - left: 0; - right: 0; - width: auto; - height: 5px; } - /* line 57, ../../../../general/res/sass/helpers/_splitter.scss */ - .split-layout.horizontal > .splitter:after { - -moz-transition-property: "border-color"; - -o-transition-property: "border-color"; - -webkit-transition-property: "border-color"; - transition-property: "border-color"; - -moz-transition-duration: 25ms; - -o-transition-duration: 25ms; - -webkit-transition-duration: 25ms; - transition-duration: 25ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - content: ''; - display: block; - pointer-events: none; - position: absolute; - z-index: 2; - border-top: 1px dotted #d6d6d6; - top: 2px; - left: 5px; - right: 5px; - height: 1px; } -/* line 68, ../../../../general/res/sass/helpers/_splitter.scss */ -.split-layout.vertical .pane { - top: 0; - bottom: 0; } - /* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ - .split-layout.vertical .pane.left { - right: auto; } - /* line 74, ../../../../general/res/sass/helpers/_splitter.scss */ - .split-layout.vertical .pane.right { - left: auto; } -/* line 78, ../../../../general/res/sass/helpers/_splitter.scss */ -.split-layout.vertical > .splitter { - bottom: 0; - cursor: col-resize; - width: 5px; } - /* line 82, ../../../../general/res/sass/helpers/_splitter.scss */ - .split-layout.vertical > .splitter:after { - -moz-transition-property: "border-color"; - -o-transition-property: "border-color"; - -webkit-transition-property: "border-color"; - transition-property: "border-color"; - -moz-transition-duration: 25ms; - -o-transition-duration: 25ms; - -webkit-transition-duration: 25ms; - transition-duration: 25ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - content: ''; - display: block; - pointer-events: none; - position: absolute; - z-index: 2; - border-left: 1px dotted #d6d6d6; - left: 2px; - bottom: 5px; - top: 5px; - width: 1px; } - -/* line 89, ../../../../general/res/sass/helpers/_splitter.scss */ -.browse-area .splitter { - top: 0; } - -/* line 93, ../../../../general/res/sass/helpers/_splitter.scss */ -.edit-area .splitter { - top: 0; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -@-moz-keyframes rotation { - 0% { - transform: rotate(0deg); } - 100% { - transform: rotate(359deg); } } -@-webkit-keyframes rotation { - 0% { - transform: rotate(0deg); } - 100% { - transform: rotate(359deg); } } -@keyframes rotation { - 0% { - transform: rotate(0deg); } - 100% { - transform: rotate(359deg); } } -/* line 63, ../../../../general/res/sass/helpers/_wait-spinner.scss */ -.t-wait-spinner, -.wait-spinner { - display: block; - position: absolute; - -webkit-animation: rotation .6s infinite linear; - -moz-animation: rotation .6s infinite linear; - -o-animation: rotation .6s infinite linear; - animation: rotation .6s infinite linear; - border-color: rgba(0, 153, 204, 0.25); - border-top-color: #0099cc; - border-style: solid; - border-width: 0.5em; - -moz-border-radius: 100%; - -webkit-border-radius: 100%; - border-radius: 100%; - top: 50%; - left: 50%; - height: auto; - width: auto; - padding: 5%; - pointer-events: none; - margin-top: -5%; - margin-left: -5%; - z-index: 2; } - /* line 74, ../../../../general/res/sass/helpers/_wait-spinner.scss */ - .t-wait-spinner.inline, - .wait-spinner.inline { - display: inline-block !important; - margin-right: 5px; - position: relative !important; - vertical-align: middle; } - -/* line 82, ../../../../general/res/sass/helpers/_wait-spinner.scss */ -.l-wait-spinner-holder { - pointer-events: none; - position: absolute; } - /* line 86, ../../../../general/res/sass/helpers/_wait-spinner.scss */ - .l-wait-spinner-holder.align-left .t-wait-spinner { - left: 0; - margin-left: 0; } - /* line 91, ../../../../general/res/sass/helpers/_wait-spinner.scss */ - .l-wait-spinner-holder.full-size { - display: inline-block; - height: 100%; - width: 100%; } - /* line 94, ../../../../general/res/sass/helpers/_wait-spinner.scss */ - .l-wait-spinner-holder.full-size .t-wait-spinner { - top: 0; - margin-top: 0; - padding: 30%; } - -/* line 103, ../../../../general/res/sass/helpers/_wait-spinner.scss */ -.treeview .wait-spinner { - display: block; - position: absolute; - -webkit-animation: rotation .6s infinite linear; - -moz-animation: rotation .6s infinite linear; - -o-animation: rotation .6s infinite linear; - animation: rotation .6s infinite linear; - border-color: rgba(0, 153, 204, 0.25); - border-top-color: #0099cc; - border-style: solid; - border-width: 0.25em; - -moz-border-radius: 100%; - -webkit-border-radius: 100%; - border-radius: 100%; - height: 10px; - width: 10px; - margin: 0 !important; - padding: 0 !important; - top: 2px; - left: 0; } - -/* line 112, ../../../../general/res/sass/helpers/_wait-spinner.scss */ -.wait-spinner.sm { - display: block; - position: absolute; - -webkit-animation: rotation .6s infinite linear; - -moz-animation: rotation .6s infinite linear; - -o-animation: rotation .6s infinite linear; - animation: rotation .6s infinite linear; - border-color: rgba(0, 153, 204, 0.25); - border-top-color: #0099cc; - border-style: solid; - border-width: 0.25em; - -moz-border-radius: 100%; - -webkit-border-radius: 100%; - border-radius: 100%; - height: 13px; - width: 13px; - margin-left: 0 !important; - margin-top: 0 !important; - padding: 0 !important; - top: 0; - left: 0; } - -/* line 122, ../../../../general/res/sass/helpers/_wait-spinner.scss */ -.loading { - pointer-events: none; } - /* line 125, ../../../../general/res/sass/helpers/_wait-spinner.scss */ - .loading:before, .loading:after { - content: ''; } - /* line 129, ../../../../general/res/sass/helpers/_wait-spinner.scss */ - .loading:before { - -moz-animation-name: rotateCentered; - -webkit-animation-name: rotateCentered; - animation-name: rotateCentered; - -moz-animation-duration: 0.5s; - -webkit-animation-duration: 0.5s; - animation-duration: 0.5s; - -moz-animation-iteration-count: infinite; - -webkit-animation-iteration-count: infinite; - animation-iteration-count: infinite; - -moz-animation-timing-function: linear; - -webkit-animation-timing-function: linear; - animation-timing-function: linear; - border-color: rgba(119, 107, 162, 0.25); - border-top-color: #776ba2; - border-style: solid; - border-width: 5px; - -moz-border-radius: 100%; - -webkit-border-radius: 100%; - border-radius: 100%; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - display: block; - position: absolute; - height: 0; - width: 0; - padding: 7%; - left: 50%; - top: 50%; - z-index: 10; } -@-moz-keyframes rotateCentered { - 0% { - transform: translateX(-50%) translateY(-50%) rotate(0deg); } - 100% { - transform: translateX(-50%) translateY(-50%) rotate(359deg); } } -@-webkit-keyframes rotateCentered { - 0% { - transform: translateX(-50%) translateY(-50%) rotate(0deg); } - 100% { - transform: translateX(-50%) translateY(-50%) rotate(359deg); } } -@keyframes rotateCentered { - 0% { - transform: translateX(-50%) translateY(-50%) rotate(0deg); } - 100% { - transform: translateX(-50%) translateY(-50%) rotate(359deg); } } - /* line 133, ../../../../general/res/sass/helpers/_wait-spinner.scss */ - .loading:after { - overflow: hidden; - position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; - width: auto; - height: auto; - background: rgba(119, 107, 162, 0.1); - display: block; - z-index: 9; } - /* line 139, ../../../../general/res/sass/helpers/_wait-spinner.scss */ - .loading.tree-item:before { - padding: 0.375rem; - border-width: 2px; } - -/* Styles for messages */ -/* line 4, ../../../../general/res/sass/_messages.scss */ -.message.block { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - padding: 10px; } -/* line 8, ../../../../general/res/sass/_messages.scss */ -.message.error { - background-color: rgba(255, 60, 0, 0.3); - color: #ff8a66; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* Classes to be used for lists of properties and values */ -/* line 25, ../../../../general/res/sass/_properties.scss */ -.properties .s-row { - border-top: 1px solid rgba(102, 102, 102, 0.2); - font-size: 0.8em; } - /* line 28, ../../../../general/res/sass/_properties.scss */ - .properties .s-row:first-child { - border: none; } - /* line 31, ../../../../general/res/sass/_properties.scss */ - .properties .s-row .s-value { - color: #fff; } - -/********************************* CONTROLS */ -/* line 1, ../../../../general/res/sass/controls/_breadcrumb.scss */ -.l-breadcrumb { - font-size: 0.7rem; - line-height: 1em; - margin-bottom: 5px; - margin-left: -4px; } - /* line 10, ../../../../general/res/sass/controls/_breadcrumb.scss */ - .l-breadcrumb .l-breadcrumb-item a { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - -moz-transition: background-color 0.25s; - -o-transition: background-color 0.25s; - -webkit-transition: background-color 0.25s; - transition: background-color 0.25s; - color: #404040; - display: inline-block; - padding: 2px 4px; } - /* line 18, ../../../../general/res/sass/controls/_breadcrumb.scss */ - .l-breadcrumb .l-breadcrumb-item a .icon { - color: #0099cc; - margin-right: 5px; } - /* line 22, ../../../../general/res/sass/controls/_breadcrumb.scss */ - .l-breadcrumb .l-breadcrumb-item a:hover { - background: white; - color: gray; } - /* line 25, ../../../../general/res/sass/controls/_breadcrumb.scss */ - .l-breadcrumb .l-breadcrumb-item a:hover .icon { - color: #0099cc; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 25, ../../../../general/res/sass/controls/_buttons.scss */ -.s-btn, .s-menu-btn, -.s-icon-btn { - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - cursor: pointer; - text-decoration: none; - height: 25px; - line-height: 25px; } - -/* line 34, ../../../../general/res/sass/controls/_buttons.scss */ -.s-btn, .s-menu-btn { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - padding: 0 7.5px; - font-size: 0.7rem; } - /* line 39, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn .icon, .s-menu-btn .icon { - font-size: 0.8rem; - color: #0099cc; } - /* line 44, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn .title-label, .s-menu-btn .title-label { - vertical-align: top; } - /* line 48, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn.lg, .lg.s-menu-btn { - font-size: 1rem; } - /* line 52, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn.sm, .sm.s-menu-btn { - padding: 0 5px; } - /* line 56, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn.vsm, .vsm.s-menu-btn { - padding: 0 2.5px; } - /* line 60, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn.major, .major.s-menu-btn { - background-color: #0099cc; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #fff; - display: inline-block; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - -moz-transition: background, 0.25s; - -o-transition: background, 0.25s; - -webkit-transition: background, 0.25s; - transition: background, 0.25s; - text-shadow: none; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ - .s-btn.major .icon, .major.s-menu-btn .icon { - color: #fff; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ - .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover { - background: deepskyblue; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ - .s-btn.major:not(.disabled):hover > .icon, .major.s-menu-btn:not(.disabled):hover > .icon { - color: white; } } - /* line 66, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn:not(.major), .s-menu-btn:not(.major) { - background-color: #969696; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #fff; - display: inline-block; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - -moz-transition: background, 0.25s; - -o-transition: background, 0.25s; - -webkit-transition: background, 0.25s; - transition: background, 0.25s; - text-shadow: none; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ - .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon { - color: #eee; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ - .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover { - background: #0099cc; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ - .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu-btn:not(.major):not(.disabled):hover > .icon { - color: white; } } - /* line 75, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn.pause-play .icon:before, .pause-play.s-menu-btn .icon:before { - content: "\0000F1"; } - /* line 78, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn.pause-play.paused, .pause-play.paused.s-menu-btn { - background-color: #ff9900; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #fff; - display: inline-block; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - -moz-transition: background, 0.25s; - -o-transition: background, 0.25s; - -webkit-transition: background, 0.25s; - transition: background, 0.25s; - text-shadow: none; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ - .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { - color: #fff; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ - .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover { - background: #ffad33; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ - .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu-btn:not(.disabled):hover > .icon { - color: white; } } - /* line 80, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { - -moz-animation-name: pulse; - -webkit-animation-name: pulse; - animation-name: pulse; - -moz-animation-duration: 1000ms; - -webkit-animation-duration: 1000ms; - animation-duration: 1000ms; - -moz-animation-direction: alternate; - -webkit-animation-direction: alternate; - animation-direction: alternate; - -moz-animation-iteration-count: infinite; - -webkit-animation-iteration-count: infinite; - animation-iteration-count: infinite; - -moz-animation-timing-function: ease-in-out; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; } - /* line 82, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn.pause-play.paused .icon :before, .pause-play.paused.s-menu-btn .icon :before { - content: "\0000EF"; } - /* line 90, ../../../../general/res/sass/controls/_buttons.scss */ - .s-btn.show-thumbs .icon:before, .show-thumbs.s-menu-btn .icon:before { - content: "\000039"; } - -/* line 96, ../../../../general/res/sass/controls/_buttons.scss */ -.s-icon-btn { - color: #eee; } - /* line 99, ../../../../general/res/sass/controls/_buttons.scss */ - .s-icon-btn:hover { - color: white; } - -@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 104, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab { - -moz-transition-property: left; - -o-transition-property: left; - -webkit-transition-property: left; - transition-property: left; - -moz-transition-duration: 150ms; - -o-transition-duration: 150ms; - -webkit-transition-duration: 150ms; - transition-duration: 150ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - color: #d6d6d6; - cursor: pointer; - display: block; - position: absolute; - font-size: 12px; - line-height: 12px; - height: 12px; - width: 12px; } - /* line 127, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:hover { - color: #a3a3a3; } - /* line 129, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:hover:after { - color: #0099cc; } - /* line 134, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:before, .mini-tab:after { - -moz-transition-property: color; - -o-transition-property: color; - -webkit-transition-property: color; - transition-property: color; - -moz-transition-duration: 200ms; - -o-transition-duration: 200ms; - -webkit-transition-duration: 200ms; - transition-duration: 200ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - display: block; - position: absolute; } - /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:before { - -moz-transition-property: left; - -o-transition-property: left; - -webkit-transition-property: left; - transition-property: left; - -moz-transition-duration: 150ms; - -o-transition-duration: 150ms; - -webkit-transition-duration: 150ms; - transition-duration: 150ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - font-size: 7px; - height: 100%; - width: 7px; } - /* line 148, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab:after { - width: 100%; - height: 100%; } - /* line 158, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-left:before { - content: '\3c'; - left: -7px; } - /* line 162, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-left:hover:before { - left: -9px; } - /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-left.collapsed:before { - content: '\3e'; - left: 12px; } - /* line 169, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-left.collapsed:hover:before { - left: 14px; } - /* line 175, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-right:before { - content: '\3e'; - left: 12px; } - /* line 179, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-right:hover:before { - left: 14px; } - /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-right.collapsed:before { - content: '\3c'; - left: -7px; } - /* line 186, ../../../../general/res/sass/controls/_buttons.scss */ - .mini-tab.anchor-right.collapsed:hover:before { - left: -9px; } } - -/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ -.l-btn-set { - font-size: 0; } - /* line 202, ../../../../general/res/sass/controls/_buttons.scss */ - .l-btn-set .s-btn, .l-btn-set .s-menu-btn { - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - margin-left: 1px; } - /* line 208, ../../../../general/res/sass/controls/_buttons.scss */ - .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { - -moz-border-radius-topleft: 4px; - -webkit-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - -webkit-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; - margin-left: 0; } - /* line 215, ../../../../general/res/sass/controls/_buttons.scss */ - .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { - -moz-border-radius-topright: 4px; - -webkit-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - -webkit-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; } - -/* line 222, ../../../../general/res/sass/controls/_buttons.scss */ -.paused:not(.s-btn):not(.s-menu-btn) { - border-color: #ff9900 !important; - color: #ff9900 !important; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/controls/_color-palette.scss */ -.l-color-palette { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - padding: 5px !important; } - /* line 31, ../../../../general/res/sass/controls/_color-palette.scss */ - .l-color-palette .l-palette-row { - overflow: hidden; - *zoom: 1; - line-height: 16px; - width: 170px; } - /* line 36, ../../../../general/res/sass/controls/_color-palette.scss */ - .l-color-palette .l-palette-row .l-palette-item { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - text-shadow: rgba(0, 0, 0, 0.8) 0 1px 2px; - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 0.25s; - -o-transition-duration: 0.25s; - -webkit-transition-duration: 0.25s; - transition-duration: 0.25s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - border: 1px solid transparent; - color: #fff; - display: block; - font-family: 'symbolsfont'; - float: left; - height: 16px; - width: 16px; - line-height: 16px; - margin: 0 1px 1px 0; - text-align: center; - vertical-align: middle; } - /* line 53, ../../../../general/res/sass/controls/_color-palette.scss */ - .l-color-palette .l-palette-row .s-palette-item:hover { - -moz-transition-property: none; - -o-transition-property: none; - -webkit-transition-property: none; - transition-property: none; - border-color: #fff !important; } - /* line 59, ../../../../general/res/sass/controls/_color-palette.scss */ - .l-color-palette .l-palette-row .l-palette-item-label { - margin-left: 5px; } - /* line 63, ../../../../general/res/sass/controls/_color-palette.scss */ - .l-color-palette .l-palette-row.l-option-row { - margin-bottom: 5px; } - /* line 65, ../../../../general/res/sass/controls/_color-palette.scss */ - .l-color-palette .l-palette-row.l-option-row .s-palette-item { - border-color: #666; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/*.control { - // UNUSED? - &.view-control { - .icon { - display: inline-block; - margin: -1px 5px 1px 2px; - vertical-align: middle; - &.triangle-down { - margin: 2px 2px -2px 0px; - } - } - - .label { - display: inline-block; - font-size: 11px; - vertical-align: middle; - } - - .toggle { - @include border-radius(3px); - display: inline-block; - padding: 1px 6px 4px 4px; - &:hover { - background: rgba(white, 0.1); - } - } - } -}*/ -/* line 51, ../../../../general/res/sass/controls/_controls.scss */ -.accordion { - margin-top: 5px; } - /* line 54, ../../../../general/res/sass/controls/_controls.scss */ - .accordion:first-child { - margin-top: 0; } - /* line 57, ../../../../general/res/sass/controls/_controls.scss */ - .accordion .accordion-head { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - background: rgba(102, 102, 102, 0.2); - cursor: pointer; - font-size: 0.75em; - line-height: 18px; - margin-bottom: 5px; - padding: 0 5px; - position: absolute; - top: 0; - right: 0; - bottom: auto; - left: 0; - width: auto; - height: 18px; - text-transform: uppercase; } - /* line 75, ../../../../general/res/sass/controls/_controls.scss */ - .accordion .accordion-head:hover { - background: rgba(102, 102, 102, 0.4); } - /* line 78, ../../../../general/res/sass/controls/_controls.scss */ - .accordion .accordion-head:after { - content: "^"; - display: block; - font-family: 'symbolsfont'; - font-size: 0.9em; - position: absolute; - right: 5px; - text-transform: none; - top: 0; } - /* line 88, ../../../../general/res/sass/controls/_controls.scss */ - .accordion .accordion-head:not(.expanded):after { - content: "v"; } - /* line 92, ../../../../general/res/sass/controls/_controls.scss */ - .accordion .accordion-contents { - position: absolute; - top: 23px; - right: 0; - bottom: 0; - left: 0; - overflow-y: auto; - overflow-x: hidden; } - -/* line 103, ../../../../general/res/sass/controls/_controls.scss */ -.l-composite-control { - vertical-align: middle; } - /* line 106, ../../../../general/res/sass/controls/_controls.scss */ - .l-composite-control.l-checkbox .composite-control-label { - line-height: 18px; } - -/* line 112, ../../../../general/res/sass/controls/_controls.scss */ -.l-control-group { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - border-left: 1px solid rgba(102, 102, 102, 0.2); - display: inline-block; - padding: 0 5px; - position: relative; } - /* line 120, ../../../../general/res/sass/controls/_controls.scss */ - .l-control-group:first-child { - border-left: none; - padding-left: 0; } - -/* line 126, ../../../../general/res/sass/controls/_controls.scss */ -.l-local-controls { - position: absolute; - top: 5px; - right: 5px; - z-index: 5; } - -/* line 136, ../../../../general/res/sass/controls/_controls.scss */ -.s-local-controls { - font-size: 0.7rem; } - -/* line 140, ../../../../general/res/sass/controls/_controls.scss */ -label.checkbox.custom { - cursor: pointer; - display: inline-block; - line-height: 14px; - margin-right: 20px; - padding-left: 19px; - position: relative; - vertical-align: middle; } - /* line 150, ../../../../general/res/sass/controls/_controls.scss */ - label.checkbox.custom em { - color: #666; - display: inline-block; - height: 14px; - min-width: 14px; } - /* line 155, ../../../../general/res/sass/controls/_controls.scss */ - label.checkbox.custom em:before { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - background: #e3e3e3; - -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 2px; - -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 2px; - box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 2px; - box-sizing: border-box; - content: " "; - font-family: 'symbolsfont'; - font-size: 0.8em; - display: inline-block; - margin-right: 5px; - height: 14px; - width: 14px; - left: 0; - top: 0; - position: absolute; - text-align: center; } - /* line 174, ../../../../general/res/sass/controls/_controls.scss */ - label.checkbox.custom.no-text { - overflow: hidden; - margin-right: 0; - padding-left: 0; - height: 14px; - width: 14px; } - /* line 180, ../../../../general/res/sass/controls/_controls.scss */ - label.checkbox.custom.no-text em { - overflow: hidden; } - /* line 184, ../../../../general/res/sass/controls/_controls.scss */ - label.checkbox.custom input { - display: none; } - /* line 186, ../../../../general/res/sass/controls/_controls.scss */ - label.checkbox.custom input:checked ~ em:before { - background: #0099cc; - color: #ccf2ff; - content: "2"; } - -/* line 194, ../../../../general/res/sass/controls/_controls.scss */ -.input-labeled { - margin-left: 5px; } - /* line 196, ../../../../general/res/sass/controls/_controls.scss */ - .input-labeled label { - display: inline-block; - margin-right: 3px; } - /* line 200, ../../../../general/res/sass/controls/_controls.scss */ - .input-labeled.inline { - display: inline-block; } - /* line 203, ../../../../general/res/sass/controls/_controls.scss */ - .input-labeled:first-child { - margin-left: 0; } - -/* line 208, ../../../../general/res/sass/controls/_controls.scss */ -.s-menu-btn label.checkbox.custom { - margin-left: 5px; } - -/* line 213, ../../../../general/res/sass/controls/_controls.scss */ -.item .checkbox.checked label { - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; - border-bottom: none; } - -/* line 219, ../../../../general/res/sass/controls/_controls.scss */ -.context-available { - color: #0099cc; } - /* line 222, ../../../../general/res/sass/controls/_controls.scss */ - .context-available:hover { - color: deepskyblue; } - -/* line 227, ../../../../general/res/sass/controls/_controls.scss */ -.view-switcher { - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 100ms; - -o-transition-duration: 100ms; - -webkit-transition-duration: 100ms; - transition-duration: 100ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; } - -/******************************************************** OBJECT-HEADER */ -/* line 232, ../../../../general/res/sass/controls/_controls.scss */ -.object-header { - font-size: 1em; } - /* line 243, ../../../../general/res/sass/controls/_controls.scss */ - .object-header > .type-icon { - color: #b3b3b3; - font-size: 120%; - float: left; - margin-right: 5px; } - /* line 250, ../../../../general/res/sass/controls/_controls.scss */ - .object-header .l-elem-wrapper { - justify-content: flex-start; - -webkit-justify-content: flex-start; } - /* line 253, ../../../../general/res/sass/controls/_controls.scss */ - .object-header .l-elem-wrapper mct-representation { - min-width: 0.7em; } - /* line 261, ../../../../general/res/sass/controls/_controls.scss */ - .object-header .action { - margin-right: 5px; } - /* line 265, ../../../../general/res/sass/controls/_controls.scss */ - .object-header .title-label { - color: #666; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex: 0 1 auto; - -webkit-flex: 0 1 auto; - padding-right: 0.35em; } - /* line 275, ../../../../general/res/sass/controls/_controls.scss */ - .object-header .context-available { - font-size: 0.7em; - flex: 0 0 1; - -webkit-flex: 0 0 1; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 282, ../../../../general/res/sass/controls/_controls.scss */ - .object-header .context-available { - -moz-transition-property: opacity; - -o-transition-property: opacity; - -webkit-transition-property: opacity; - transition-property: opacity; - -moz-transition-duration: 0.25s; - -o-transition-duration: 0.25s; - -webkit-transition-duration: 0.25s; - transition-duration: 0.25s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - opacity: 0; } - /* line 287, ../../../../general/res/sass/controls/_controls.scss */ - .object-header:hover .context-available { - opacity: 1; } } - -/******************************************************** SLIDERS */ -/* line 298, ../../../../general/res/sass/controls/_controls.scss */ -.slider .slot { - width: auto; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; } -/* line 308, ../../../../general/res/sass/controls/_controls.scss */ -.slider .knob { - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 0.25s; - -o-transition-duration: 0.25s; - -webkit-transition-duration: 0.25s; - transition-duration: 0.25s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - background-color: rgba(0, 153, 204, 0.5); - position: absolute; - height: 100%; - width: 10px; - top: 0; - auto: 0; - bottom: auto; - left: auto; } - /* line 311, ../../../../general/res/sass/controls/_controls.scss */ - .slider .knob:hover { - background-color: rgba(0, 153, 204, 0.7); } -/* line 322, ../../../../general/res/sass/controls/_controls.scss */ -.slider .knob-l { - -moz-border-radius-topleft: 10px; - -webkit-border-top-left-radius: 10px; - border-top-left-radius: 10px; - -moz-border-radius-bottomleft: 10px; - -webkit-border-bottom-left-radius: 10px; - border-bottom-left-radius: 10px; - cursor: w-resize; } -/* line 326, ../../../../general/res/sass/controls/_controls.scss */ -.slider .knob-r { - -moz-border-radius-topright: 10px; - -webkit-border-top-right-radius: 10px; - border-top-right-radius: 10px; - -moz-border-radius-bottomright: 10px; - -webkit-border-bottom-right-radius: 10px; - border-bottom-right-radius: 10px; - cursor: e-resize; } -/* line 330, ../../../../general/res/sass/controls/_controls.scss */ -.slider .range { - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 0.25s; - -o-transition-duration: 0.25s; - -webkit-transition-duration: 0.25s; - transition-duration: 0.25s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - background-color: rgba(0, 153, 204, 0.2); - cursor: ew-resize; - position: absolute; - top: 0; - right: auto; - bottom: 0; - left: auto; - height: auto; - width: auto; } - /* line 341, ../../../../general/res/sass/controls/_controls.scss */ - .slider .range:hover { - background-color: rgba(0, 153, 204, 0.4); } - -/******************************************************** DATETIME PICKER */ -/* line 348, ../../../../general/res/sass/controls/_controls.scss */ -.l-datetime-picker { - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - font-size: 0.8rem; - padding: 10px !important; - width: 230px; } - /* line 354, ../../../../general/res/sass/controls/_controls.scss */ - .l-datetime-picker .l-month-year-pager { - height: 15px; - margin-bottom: 5px; - position: relative; } - /* line 366, ../../../../general/res/sass/controls/_controls.scss */ - .l-datetime-picker .l-month-year-pager .pager { - width: 20px; } - /* line 369, ../../../../general/res/sass/controls/_controls.scss */ - .l-datetime-picker .l-month-year-pager .pager.prev { - right: auto; } - /* line 371, ../../../../general/res/sass/controls/_controls.scss */ - .l-datetime-picker .l-month-year-pager .pager.prev:before { - content: "\3c"; } - /* line 375, ../../../../general/res/sass/controls/_controls.scss */ - .l-datetime-picker .l-month-year-pager .pager.next { - left: auto; - text-align: right; } - /* line 378, ../../../../general/res/sass/controls/_controls.scss */ - .l-datetime-picker .l-month-year-pager .pager.next:before { - content: "\3e"; } - /* line 383, ../../../../general/res/sass/controls/_controls.scss */ - .l-datetime-picker .l-month-year-pager .val { - text-align: center; - left: 25px; - right: 25px; } - /* line 389, ../../../../general/res/sass/controls/_controls.scss */ - .l-datetime-picker .l-calendar, - .l-datetime-picker .l-time-selects { - border-top: 1px solid rgba(102, 102, 102, 0.2); } - /* line 393, ../../../../general/res/sass/controls/_controls.scss */ - .l-datetime-picker .l-time-selects { - line-height: 22px; } - -/******************************************************** CALENDAR */ -/* line 401, ../../../../general/res/sass/controls/_controls.scss */ -.l-calendar ul.l-cal-row { - display: -webkit-flex; - display: flex; - -webkit-flex-flow: row nowrap; - flex-flow: row nowrap; - margin-top: 1px; } - /* line 405, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row:first-child { - margin-top: 0; } - /* line 408, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row li { - -webkit-flex: 1 0; - flex: 1 0; - margin-left: 1px; - padding: 5px; - text-align: center; } - /* line 414, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row li:first-child { - margin-left: 0; } - /* line 418, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row.l-header li { - color: #999999; } - /* line 421, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row.l-body li { - -moz-transition-property: background-color; - -o-transition-property: background-color; - -webkit-transition-property: background-color; - transition-property: background-color; - -moz-transition-duration: 0.25s; - -o-transition-duration: 0.25s; - -webkit-transition-duration: 0.25s; - transition-duration: 0.25s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - cursor: pointer; } - /* line 424, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row.l-body li.in-month { - background-color: #f2f2f2; } - /* line 427, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row.l-body li .sub { - color: #999999; - font-size: 0.8em; } - /* line 431, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row.l-body li.selected { - background: #1ac6ff; - color: #fcfcfc; } - /* line 434, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row.l-body li.selected .sub { - color: inherit; } - /* line 438, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row.l-body li:hover { - background-color: #0099cc; - color: #fff; } - /* line 441, ../../../../general/res/sass/controls/_controls.scss */ - .l-calendar ul.l-cal-row.l-body li:hover .sub { - color: inherit; } - -/******************************************************** BROWSER ELEMENTS */ -@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 452, ../../../../general/res/sass/controls/_controls.scss */ - ::-webkit-scrollbar { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -moz-box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 2px; - -webkit-box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 2px; - box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 2px; - background-color: rgba(0, 0, 0, 0.1); - height: 10px; - width: 10px; } - - /* line 461, ../../../../general/res/sass/controls/_controls.scss */ - ::-webkit-scrollbar-thumb { - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzg5ODk4OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzdkN2Q3ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); - background-size: 100%; - background-image: -webkit-gradient(linear, 50% 0%, 50% 20, color-stop(0%, #898989), color-stop(100%, #7d7d7d)); - background-image: -moz-linear-gradient(#898989, #7d7d7d 20px); - background-image: -webkit-linear-gradient(#898989, #7d7d7d 20px); - background-image: linear-gradient(#898989, #7d7d7d 20px); - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - /* line 470, ../../../../general/res/sass/controls/_controls.scss */ - ::-webkit-scrollbar-thumb:hover { - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwYWNlNiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwOTljYyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); - background-size: 100%; - background-image: -webkit-gradient(linear, 50% 0%, 50% 20, color-stop(0%, #00ace6), color-stop(100%, #0099cc)); - background-image: -moz-linear-gradient(#00ace6, #0099cc 20px); - background-image: -webkit-linear-gradient(#00ace6, #0099cc 20px); - background-image: linear-gradient(#00ace6, #0099cc 20px); } - - /* line 475, ../../../../general/res/sass/controls/_controls.scss */ - ::-webkit-scrollbar-corner { - background: rgba(0, 0, 0, 0.1); } } -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 23, ../../../../general/res/sass/controls/_lists.scss */ -.checkbox-list label.checkbox.custom { - display: block; - margin-bottom: 5px; } -/* line 27, ../../../../general/res/sass/controls/_lists.scss */ -.checkbox-list li { - margin-bottom: 5px; } - -/* line 35, ../../../../general/res/sass/controls/_lists.scss */ -.l-tree-item-flat-list .tree-item .label { - left: 5px !important; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/******************************************************** MENU BUTTONS */ -/* line 31, ../../../../general/res/sass/controls/_menus.scss */ -.s-menu-btn .icon { - font-size: 120%; } -/* line 35, ../../../../general/res/sass/controls/_menus.scss */ -.s-menu-btn .title-label { - margin-left: 3px; } -/* line 39, ../../../../general/res/sass/controls/_menus.scss */ -.s-menu-btn:after { - text-shadow: none; - content: '\76'; - display: inline-block; - font-family: 'symbolsfont'; - margin-left: 3px; - vertical-align: top; - color: rgba(255, 255, 255, 0.4); } -/* line 46, ../../../../general/res/sass/controls/_menus.scss */ -.s-menu-btn.create-btn .title-label { - font-size: 1rem; } -/* line 54, ../../../../general/res/sass/controls/_menus.scss */ -.s-menu-btn .menu { - left: 0; - text-align: left; } - /* line 57, ../../../../general/res/sass/controls/_menus.scss */ - .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .icon.s-icon-btn, .s-menu-btn .menu .icon.mini-tab, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { - width: 12px; } - -/******************************************************** MENUS THEMSELVES */ -/* line 64, ../../../../general/res/sass/controls/_menus.scss */ -.menu-element { - cursor: pointer; - position: relative; } - -/* line 69, ../../../../general/res/sass/controls/_menus.scss */ -.s-menu, .menu { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - background-color: white; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #4d4d4d; - display: inline-block; - -moz-box-shadow: rgba(0, 0, 0, 0.5) 0 1px 5px; - -webkit-box-shadow: rgba(0, 0, 0, 0.5) 0 1px 5px; - box-shadow: rgba(0, 0, 0, 0.5) 0 1px 5px; - text-shadow: none; - padding: 3px 0; } - -/* line 77, ../../../../general/res/sass/controls/_menus.scss */ -.menu { - display: block; - position: absolute; - z-index: 10; } - /* line 82, ../../../../general/res/sass/controls/_menus.scss */ - .menu ul { - margin: 0; - padding: 0; } - /* line 331, ../../../../general/res/sass/_mixins.scss */ - .menu ul li { - list-style-type: none; - margin: 0; - padding: 0; } - /* line 84, ../../../../general/res/sass/controls/_menus.scss */ - .menu ul li { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - border-top: 1px solid white; - color: #666666; - line-height: 1.5rem; - padding: 3px 10px 3px 30px; - position: relative; - white-space: nowrap; } - /* line 92, ../../../../general/res/sass/controls/_menus.scss */ - .menu ul li:first-child { - border: none; } - /* line 95, ../../../../general/res/sass/controls/_menus.scss */ - .menu ul li:hover { - background: #e6e6e6; - color: #4d4d4d; } - /* line 98, ../../../../general/res/sass/controls/_menus.scss */ - .menu ul li:hover .icon { - color: #0099cc; } - /* line 102, ../../../../general/res/sass/controls/_menus.scss */ - .menu ul li .type-icon { - left: 10px; } - -/* line 109, ../../../../general/res/sass/controls/_menus.scss */ -.menu, -.context-menu, -.checkbox-menu, -.super-menu { - pointer-events: auto; } - /* line 115, ../../../../general/res/sass/controls/_menus.scss */ - .menu ul li a, - .context-menu ul li a, - .checkbox-menu ul li a, - .super-menu ul li a { - color: #4d4d4d; } - /* line 118, ../../../../general/res/sass/controls/_menus.scss */ - .menu ul li .icon, - .context-menu ul li .icon, - .checkbox-menu ul li .icon, - .super-menu ul li .icon { - color: #0099cc; } - /* line 121, ../../../../general/res/sass/controls/_menus.scss */ - .menu ul li .type-icon, - .context-menu ul li .type-icon, - .checkbox-menu ul li .type-icon, - .super-menu ul li .type-icon { - left: 5px; } - -/* line 133, ../../../../general/res/sass/controls/_menus.scss */ -.checkbox-menu ul li { - padding-left: 50px; } - /* line 135, ../../../../general/res/sass/controls/_menus.scss */ - .checkbox-menu ul li .checkbox { - position: absolute; - left: 5px; - top: 0.53333rem; } - /* line 140, ../../../../general/res/sass/controls/_menus.scss */ - .checkbox-menu ul li .checkbox em { - height: 0.7rem; - width: 0.7rem; } - /* line 143, ../../../../general/res/sass/controls/_menus.scss */ - .checkbox-menu ul li .checkbox em:before { - font-size: 7px !important; - height: 0.7rem; - width: 0.7rem; - line-height: 0.7rem; } - /* line 151, ../../../../general/res/sass/controls/_menus.scss */ - .checkbox-menu ul li .type-icon { - left: 25px; } - -/* line 157, ../../../../general/res/sass/controls/_menus.scss */ -.super-menu { - display: block; - width: 500px; - height: 480px; } - /* line 165, ../../../../general/res/sass/controls/_menus.scss */ - .super-menu .contents { - overflow: hidden; - position: absolute; - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - width: auto; - height: auto; } - /* line 168, ../../../../general/res/sass/controls/_menus.scss */ - .super-menu .pane { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - /* line 170, ../../../../general/res/sass/controls/_menus.scss */ - .super-menu .pane.left { - border-right: 1px solid #e6e6e6; - left: 0; - padding-right: 5px; - right: auto; - width: 50%; - overflow-x: hidden; - overflow-y: auto; } - /* line 180, ../../../../general/res/sass/controls/_menus.scss */ - .super-menu .pane.left ul li { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - padding-left: 30px; - border-top: none; } - /* line 187, ../../../../general/res/sass/controls/_menus.scss */ - .super-menu .pane.right { - left: auto; - right: 0; - padding: 25px; - width: 50%; } - /* line 197, ../../../../general/res/sass/controls/_menus.scss */ - .super-menu .menu-item-description .desc-area.icon { - color: #0099cc; - position: relative; - font-size: 8em; - left: 0; - height: 150px; - line-height: 150px; - margin-bottom: 25px; - text-align: center; } - /* line 208, ../../../../general/res/sass/controls/_menus.scss */ - .super-menu .menu-item-description .desc-area.title { - color: #666; - font-size: 1.2em; - margin-bottom: 0.5em; } - /* line 213, ../../../../general/res/sass/controls/_menus.scss */ - .super-menu .menu-item-description .desc-area.description { - color: #666; - font-size: 0.8em; - line-height: 1.5em; } - -/* line 222, ../../../../general/res/sass/controls/_menus.scss */ -.context-menu, .checkbox-menu { - font-size: 0.80rem; } - -/* line 226, ../../../../general/res/sass/controls/_menus.scss */ -.context-menu-holder, -.menu-holder { - position: absolute; - z-index: 70; } - /* line 230, ../../../../general/res/sass/controls/_menus.scss */ - .context-menu-holder .context-menu-wrapper, - .menu-holder .context-menu-wrapper { - position: absolute; - height: 100%; - width: 100%; } - /* line 235, ../../../../general/res/sass/controls/_menus.scss */ - .context-menu-holder.go-left .context-menu, .context-menu-holder.go-left .checkbox-menu, .context-menu-holder.go-left .menu, - .menu-holder.go-left .context-menu, - .menu-holder.go-left .checkbox-menu, - .menu-holder.go-left .menu { - right: 0; } - /* line 239, ../../../../general/res/sass/controls/_menus.scss */ - .context-menu-holder.go-up .context-menu, .context-menu-holder.go-up .checkbox-menu, .context-menu-holder.go-up .menu, - .menu-holder.go-up .context-menu, - .menu-holder.go-up .checkbox-menu, - .menu-holder.go-up .menu { - bottom: 0; } - -/* line 245, ../../../../general/res/sass/controls/_menus.scss */ -.context-menu-holder { - pointer-events: none; - height: 200px; - width: 170px; } - -/* line 251, ../../../../general/res/sass/controls/_menus.scss */ -.btn-bar.right .menu, -.menus-to-left .menu { - left: auto; - right: 0; - width: auto; } - -/* line 13, ../../../../general/res/sass/controls/_time-controller.scss */ -mct-include.l-time-controller { - overflow: hidden; - position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; - width: auto; - height: auto; - display: block; - top: auto; - height: 83px; - min-width: 500px; - font-size: 0.8rem; } - /* line 38, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder, - mct-include.l-time-controller .l-time-range-slider-holder, - mct-include.l-time-controller .l-time-range-ticks-holder { - overflow: visible; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - top: auto; } - /* line 47, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-slider, - mct-include.l-time-controller .l-time-range-ticks { - overflow: visible; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; - left: 150px; - right: 150px; } - /* line 54, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder { - height: 33px; - bottom: 46px; - padding-top: 5px; - border-top: 1px solid rgba(102, 102, 102, 0.2); } - /* line 59, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder .type-icon { - font-size: 120%; - vertical-align: middle; } - /* line 63, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input, - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem { - margin-right: 5px; } - /* line 66, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .lbl, - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .lbl { - color: #999999; } - /* line 69, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .ui-symbol.icon, - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.s-icon-btn, - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.mini-tab, - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .l-datetime-picker .l-month-year-pager .icon.pager, - .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.pager { - font-size: 11px; - width: 11px; } - /* line 76, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-slider-holder { - height: 20px; - bottom: 23px; } - /* line 79, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-slider-holder .range-holder { - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; - background: none; - border: none; } - /* line 84, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line { - -moz-transform: translateX(50%); - -ms-transform: translateX(50%); - -webkit-transform: translateX(50%); - transform: translateX(50%); - position: absolute; - top: 0; - right: 0; - bottom: 0px; - left: auto; - width: 8px; - height: auto; - z-index: 2; } - /* line 94, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line:before, mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line:after { - background-color: #666; - content: ""; - position: absolute; } - /* line 100, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line:before { - top: 0; - right: auto; - bottom: -10px; - left: 3px; - width: 2px; } - /* line 106, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line:after { - -moz-border-radius: 8px; - -webkit-border-radius: 8px; - border-radius: 8px; - -moz-transform: translateY(-50%); - -ms-transform: translateY(-50%); - -webkit-transform: translateY(-50%); - transform: translateY(-50%); - top: 50%; - right: 0; - bottom: auto; - left: 0; - width: auto; - height: 8px; } - /* line 3, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range:hover .toi-line:before, mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range:hover .toi-line:after { - background-color: #0052b5; } - /* line 122, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-slider-holder:not(:active) .knob, - mct-include.l-time-controller .l-time-range-slider-holder:not(:active) .range { - -moz-transition-property: left, right; - -o-transition-property: left, right; - -webkit-transition-property: left, right; - transition-property: left, right; - -moz-transition-duration: 500ms; - -o-transition-duration: 500ms; - -webkit-transition-duration: 500ms; - transition-duration: 500ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; } - /* line 131, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-ticks-holder { - height: 20px; } - /* line 133, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-ticks-holder .l-time-range-ticks { - border-top: 1px solid rgba(0, 0, 0, 0.2); } - /* line 135, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-ticks-holder .l-time-range-ticks .tick { - background-color: rgba(0, 0, 0, 0.2); - border: none; - height: 5px; - width: 1px; - margin-left: -1px; - position: absolute; } - /* line 142, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-ticks-holder .l-time-range-ticks .tick:first-child { - margin-left: 0; } - /* line 145, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-ticks-holder .l-time-range-ticks .tick .l-time-range-tick-label { - transform: translateX(-50%); - -webkit-transform: translateX(-50%); - color: #999999; - display: inline-block; - font-size: 0.9em; - position: absolute; - top: 8px; - white-space: nowrap; - z-index: 2; } - /* line 159, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .knob { - z-index: 2; } - /* line 161, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .knob .range-value { - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 0.25s; - -o-transition-duration: 0.25s; - -webkit-transition-duration: 0.25s; - transition-duration: 0.25s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - padding: 0 10px; - position: absolute; - height: 20px; - line-height: 20px; - white-space: nowrap; } - /* line 170, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .knob:hover .range-value { - color: rgba(0, 153, 204, 0.7); } - /* line 173, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .knob.knob-l { - margin-left: -10px; } - /* line 176, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .knob.knob-l .range-value { - text-align: right; - right: 10px; } - /* line 181, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .knob.knob-r { - margin-right: -10px; } - /* line 184, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .knob.knob-r .range-value { - left: 10px; } - /* line 3, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .knob.knob-r:hover + .range-holder .range .toi-line:before, mct-include.l-time-controller .knob.knob-r:hover + .range-holder .range .toi-line:after { - background-color: #0052b5; } - -/* line 198, ../../../../general/res/sass/controls/_time-controller.scss */ -.s-time-range-val { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - background-color: #fff; - padding: 1px 1px 0 5px; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 26, ../../../../general/res/sass/mobile/controls/_menus.scss */ - .menu-element .super-menu { - width: 250px; - height: 250px; } - /* line 32, ../../../../general/res/sass/mobile/controls/_menus.scss */ - .menu-element .super-menu .pane.left { - border-right: none; - padding-right: 0; - width: 100%; } - /* line 37, ../../../../general/res/sass/mobile/controls/_menus.scss */ - .menu-element .super-menu .pane.right { - display: none; } } -/********************************* FORMS */ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/forms/_elems.scss */ -.section-header { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - background: rgba(0, 0, 0, 0.05); - color: #999999; - font-size: 0.8em; - padding: 5px 5px; - text-transform: uppercase; } - -/* line 33, ../../../../general/res/sass/forms/_elems.scss */ -.form { - color: gray; } - /* line 36, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-section { - position: relative; - margin-bottom: 20px; } - /* line 41, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - *zoom: 1; - border-top: 1px solid rgba(0, 0, 0, 0.1); - margin-top: 5px; - padding: 5px 0; - position: relative; } - /* line 49, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row.first { - border-top: none; } - /* line 53, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row > .label, - .form .form-row > .controls { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - *zoom: 1; - font-size: 0.8rem; - line-height: 22px; - min-height: 22px; } - /* line 62, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row > .label { - float: left; - min-width: 120px; - position: relative; - white-space: nowrap; - width: 30%; } - /* line 72, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .value { - color: #666; } - /* line 76, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .controls { - float: left; - position: relative; - width: 69.9%; } - /* line 83, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .controls .l-composite-control.l-checkbox { - display: inline-block; - line-height: 14px; - margin-right: 5px; } - /* line 92, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .controls .l-med input[type="text"] { - width: 200px; } - /* line 96, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .controls .l-small input[type="text"] { - width: 50px; } - /* line 100, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .controls .l-numeric input[type="text"] { - text-align: right; } - /* line 104, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .controls .select { - margin-right: 5px; } - /* line 109, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .field-hints { - color: #333333; } - /* line 113, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .selector-list { - -moz-appearance: none; - -webkit-appearance: none; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - background: #fff; - border: none; - color: #666; - outline: none; - padding: 0 3px; - position: relative; - height: 150px; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ - .form .form-row .selector-list.error { - background: rgba(255, 0, 0, 0.5); } - /* line 124, ../../../../general/res/sass/forms/_elems.scss */ - .form .form-row .selector-list > .wrapper { - overflow: auto; - position: absolute; - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; } - -/* line 138, ../../../../general/res/sass/forms/_elems.scss */ -label.form-control.checkbox input { - margin-right: 5px; - vertical-align: top; } - -/* line 144, ../../../../general/res/sass/forms/_elems.scss */ -.hint, -.s-hint { - font-size: 0.9em; } - -/* line 149, ../../../../general/res/sass/forms/_elems.scss */ -.l-result { - display: inline-block; - min-width: 32px; - min-height: 32px; - position: relative; - vertical-align: top; } - /* line 156, ../../../../general/res/sass/forms/_elems.scss */ - .l-result div.s-hint { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - background: rgba(255, 34, 0, 0.8); - display: block; - color: #ffa799; - padding: 5px; } - -/* line 165, ../../../../general/res/sass/forms/_elems.scss */ -input[type="text"] { - -moz-appearance: none; - -webkit-appearance: none; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - background: #fff; - border: none; - color: #666; - outline: none; - padding: 0 3px; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ - input[type="text"].error { - background: rgba(255, 0, 0, 0.5); } - /* line 172, ../../../../general/res/sass/forms/_elems.scss */ - input[type="text"].numeric { - text-align: right; } - -/* line 177, ../../../../general/res/sass/forms/_elems.scss */ -textarea { - -moz-appearance: none; - -webkit-appearance: none; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - background: #fff; - border: none; - color: #666; - outline: none; - padding: 5px; - position: absolute; - height: 100%; - width: 100%; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ - textarea.error { - background: rgba(255, 0, 0, 0.5); } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/forms/_selects.scss */ -.select { - background-color: #ddd; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #fff; - display: inline-block; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - -moz-transition: background, 0.25s; - -o-transition: background, 0.25s; - -webkit-transition: background, 0.25s; - transition: background, 0.25s; - text-shadow: none; - padding: 0 5px; - overflow: hidden; - position: relative; - line-height: 22px; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ - .select .icon { - color: #eee; } - /* line 31, ../../../../general/res/sass/forms/_selects.scss */ - .select select { - -moz-appearance: none; - -webkit-appearance: none; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - background: none; - color: #666; - cursor: pointer; - border: none !important; - padding: 4px 25px 2px 0px; - width: 120%; } - /* line 40, ../../../../general/res/sass/forms/_selects.scss */ - .select select option { - margin: 5px 0; } - /* line 44, ../../../../general/res/sass/forms/_selects.scss */ - .select:after { - text-shadow: none; - content: '\76'; - display: inline-block; - font-family: 'symbolsfont'; - margin-left: 3px; - vertical-align: top; - pointer-events: none; - color: rgba(102, 102, 102, 0.4); - position: absolute; - right: 5px; - top: 0; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 23, ../../../../general/res/sass/forms/_channel-selector.scss */ -.channel-selector .line { - margin-bottom: 5px; - min-height: 22px; } -/* line 27, ../../../../general/res/sass/forms/_channel-selector.scss */ -.channel-selector .treeview { - -moz-appearance: none; - -webkit-appearance: none; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - background: #fcfcfc; - border: none; - color: #666; - outline: none; - padding: 0 3px; - background: white; - border-bottom: 1px solid white; - min-height: 300px; - max-height: 400px; - overflow: auto; - padding: 5px; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ - .channel-selector .treeview.error { - background: rgba(255, 0, 0, 0.5); } -/* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ -.channel-selector .btns-add-remove { - margin-top: 150px; } - /* line 39, ../../../../general/res/sass/forms/_channel-selector.scss */ - .channel-selector .btns-add-remove .s-btn, .channel-selector .btns-add-remove .s-menu-btn { - display: block; - margin-bottom: 5px; - text-align: center; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 29, ../../../../general/res/sass/forms/_datetime.scss */ -.complex.datetime { - /* - .field-hints, - .fields { - } - - - .field-hints { - - } - */ } - /* line 30, ../../../../general/res/sass/forms/_datetime.scss */ - .complex.datetime span { - display: inline-block; - margin-right: 5px; } - /* line 46, ../../../../general/res/sass/forms/_datetime.scss */ - .complex.datetime .fields { - margin-top: 3px 0; - padding: 3px 0; } - /* line 51, ../../../../general/res/sass/forms/_datetime.scss */ - .complex.datetime .date { - width: 85px; } - /* line 24, ../../../../general/res/sass/forms/_datetime.scss */ - .complex.datetime .date input[type="text"] { - width: 80px; } - /* line 55, ../../../../general/res/sass/forms/_datetime.scss */ - .complex.datetime .time.md { - width: 65px; } - /* line 24, ../../../../general/res/sass/forms/_datetime.scss */ - .complex.datetime .time.md input[type="text"] { - width: 60px; } - /* line 59, ../../../../general/res/sass/forms/_datetime.scss */ - .complex.datetime .time.sm { - width: 45px; } - /* line 24, ../../../../general/res/sass/forms/_datetime.scss */ - .complex.datetime .time.sm input[type="text"] { - width: 40px; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 23, ../../../../general/res/sass/forms/_validation.scss */ -.validates > .label { - padding-right: 10px; } - /* line 25, ../../../../general/res/sass/forms/_validation.scss */ - .validates > .label::after { - float: right; - font-family: symbolsfont; - font-size: 0.7em; } -/* line 33, ../../../../general/res/sass/forms/_validation.scss */ -.validates.invalid > .label::after, .validates.invalid.req > .label::after { - color: #ff2200; - content: "x"; } -/* line 40, ../../../../general/res/sass/forms/_validation.scss */ -.validates.valid > .label::after, .validates.valid.req > .label::after { - color: #33cc33; - content: "2"; } -/* line 46, ../../../../general/res/sass/forms/_validation.scss */ -.validates.req > .label::after { - color: #0099cc; - content: "*"; } - -/* line 52, ../../../../general/res/sass/forms/_validation.scss */ -.req { - font-size: 0.7em; } - -/* line 55, ../../../../general/res/sass/forms/_validation.scss */ -span.req { - color: #0099cc; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 24, ../../../../general/res/sass/forms/_filter.scss */ -.filter input.filter, -.filter input.t-filter-input, -.t-filter input.filter, -.t-filter input.t-filter-input { - -moz-appearance: none; - -webkit-appearance: none; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; - background: #fcfcfc; - border: none; - color: #666; - outline: none; - padding: 0 3px; - background: white; - border-bottom: 1px solid white; } - /* line 298, ../../../../general/res/sass/_mixins.scss */ - .filter input.filter.error, - .filter input.t-filter-input.error, - .t-filter input.filter.error, - .t-filter input.t-filter-input.error { - background: rgba(255, 0, 0, 0.5); } -/* line 28, ../../../../general/res/sass/forms/_filter.scss */ -.filter input.t-filter-input, -.t-filter input.t-filter-input { - height: 22px; - width: 200px; } - /* line 38, ../../../../general/res/sass/forms/_filter.scss */ - .filter input.t-filter-input:not(.ng-dirty) + .t-a-clear, - .t-filter input.t-filter-input:not(.ng-dirty) + .t-a-clear { - display: none; } -/* line 42, ../../../../general/res/sass/forms/_filter.scss */ -.filter .icon.ui-symbol, .filter .icon.s-icon-btn, .filter .icon.mini-tab, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, -.t-filter .icon.ui-symbol, -.t-filter .icon.s-icon-btn, -.t-filter .icon.mini-tab, -.t-filter .l-datetime-picker .l-month-year-pager .icon.pager, -.l-datetime-picker .l-month-year-pager .t-filter .icon.pager { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - display: inline-block; - font-size: 1.3em; - height: 22px; - line-height: 22px; - padding: 0px 5px; - vertical-align: middle; } - /* line 50, ../../../../general/res/sass/forms/_filter.scss */ - .filter .icon.ui-symbol:hover, .filter .icon.s-icon-btn:hover, .filter .icon.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, - .t-filter .icon.ui-symbol:hover, - .t-filter .icon.s-icon-btn:hover, - .t-filter .icon.mini-tab:hover, - .t-filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, - .l-datetime-picker .l-month-year-pager .t-filter .icon.pager:hover { - background: rgba(255, 255, 255, 0.1); } -/* line 54, ../../../../general/res/sass/forms/_filter.scss */ -.filter .s-a-clear.ui-symbol, .filter .s-a-clear.s-icon-btn, .filter .s-a-clear.mini-tab, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, -.t-filter .s-a-clear.ui-symbol, -.t-filter .s-a-clear.s-icon-btn, -.t-filter .s-a-clear.mini-tab, -.t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, -.l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); - opacity: 0.2; - background: #fff; - color: #333; - display: block; - position: absolute; - height: 13px; - width: 13px; - line-height: 13px; - margin-top: -6.5px; - overflow: hidden; - padding-top: 1px; - right: 4.5px; - top: 50%; - text-align: center; - z-index: 5; } - /* line 74, ../../../../general/res/sass/forms/_filter.scss */ - .filter .s-a-clear.ui-symbol:hover, .filter .s-a-clear.s-icon-btn:hover, .filter .s-a-clear.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, - .t-filter .s-a-clear.ui-symbol:hover, - .t-filter .s-a-clear.s-icon-btn:hover, - .t-filter .s-a-clear.mini-tab:hover, - .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, - .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager:hover { - filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); - opacity: 0.6; - background-color: #0099cc; } - -/* line 82, ../../../../general/res/sass/forms/_filter.scss */ -.l-filter { - display: inline-block; - position: relative; } - -/* line 89, ../../../../general/res/sass/forms/_filter.scss */ -.top-bar input.filter { - font-size: .9em; - height: 24px; - line-height: 24px; - margin-right: 5px; - padding-left: 10px; - padding-right: 10px; - vertical-align: top; } -/* line 100, ../../../../general/res/sass/forms/_filter.scss */ -.top-bar .icon-filter { - font-size: 1.4em; } - -/********************************* USER ENVIRON */ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 32, ../../../../general/res/sass/user-environ/_layout.scss */ -.holder-all { - top: 0; - right: 0; - bottom: 0; - left: 0; } - -/* line 40, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-area, -.edit-area, -.editor { - position: absolute; } - -/* line 46, ../../../../general/res/sass/user-environ/_layout.scss */ -.editor { - -moz-border-radius: 6px; - -webkit-border-radius: 6px; - border-radius: 6px; } - -/* line 50, ../../../../general/res/sass/user-environ/_layout.scss */ -.contents { - box-sizing: border-box; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; } - /* line 58, ../../../../general/res/sass/user-environ/_layout.scss */ - .contents.nomargin { - right: 0px; - bottom: 0px; - left: 0px; } - -/* line 67, ../../../../general/res/sass/user-environ/_layout.scss */ -.bar .icon.major { - margin-right: 5px; } -/* line 70, ../../../../general/res/sass/user-environ/_layout.scss */ -.bar.abs, .l-datetime-picker .l-month-year-pager .bar.pager, -.l-datetime-picker .l-month-year-pager .bar.val, .s-menu-btn span.bar.l-click-area { - text-wrap: none; - white-space: nowrap; } - /* line 73, ../../../../general/res/sass/user-environ/_layout.scss */ - .bar.abs.left, .l-datetime-picker .l-month-year-pager .bar.left.pager, - .l-datetime-picker .l-month-year-pager .bar.left.val, .s-menu-btn span.bar.left.l-click-area, - .bar.abs .left, - .l-datetime-picker .l-month-year-pager .bar.pager .left, - .l-datetime-picker .l-month-year-pager .bar.val .left, - .s-menu-btn span.bar.l-click-area .left { - width: 45%; - right: auto; } - /* line 78, ../../../../general/res/sass/user-environ/_layout.scss */ - .bar.abs.right, .l-datetime-picker .l-month-year-pager .bar.right.pager, - .l-datetime-picker .l-month-year-pager .bar.right.val, .s-menu-btn span.bar.right.l-click-area, - .bar.abs .right, - .l-datetime-picker .l-month-year-pager .bar.pager .right, - .l-datetime-picker .l-month-year-pager .bar.val .right, - .s-menu-btn span.bar.l-click-area .right { - width: 45%; - left: auto; - text-align: right; } - /* line 83, ../../../../general/res/sass/user-environ/_layout.scss */ - .bar.abs.right .icon.major, .l-datetime-picker .l-month-year-pager .bar.right.pager .icon.major, - .l-datetime-picker .l-month-year-pager .bar.right.val .icon.major, .s-menu-btn span.bar.right.l-click-area .icon.major, - .bar.abs .right .icon.major, - .l-datetime-picker .l-month-year-pager .bar.pager .right .icon.major, - .l-datetime-picker .l-month-year-pager .bar.val .right .icon.major, - .s-menu-btn span.bar.l-click-area .right .icon.major { - margin-left: 15px; } - /* line 89, ../../../../general/res/sass/user-environ/_layout.scss */ - .bar.abs .l-flex .left, .l-datetime-picker .l-month-year-pager .bar.pager .l-flex .left, - .l-datetime-picker .l-month-year-pager .bar.val .l-flex .left, .s-menu-btn span.bar.l-click-area .l-flex .left, - .bar.abs .l-flex .right, - .l-datetime-picker .l-month-year-pager .bar.pager .l-flex .right, - .l-datetime-picker .l-month-year-pager .bar.val .l-flex .right, - .s-menu-btn span.bar.l-click-area .l-flex .right, .bar.abs.l-flex .left, .l-datetime-picker .l-month-year-pager .bar.l-flex.pager .left, - .l-datetime-picker .l-month-year-pager .bar.l-flex.val .left, .s-menu-btn span.bar.l-flex.l-click-area .left, - .bar.abs.l-flex .right, - .l-datetime-picker .l-month-year-pager .bar.l-flex.pager .right, - .l-datetime-picker .l-month-year-pager .bar.l-flex.val .right, - .s-menu-btn span.bar.l-flex.l-click-area .right { - width: auto; } - -/* line 98, ../../../../general/res/sass/user-environ/_layout.scss */ -.user-environ .browse-area, -.user-environ .edit-area, -.user-environ .editor { - top: 39px; - right: 10px; - bottom: 35px; - left: 10px; } -/* line 109, ../../../../general/res/sass/user-environ/_layout.scss */ -.user-environ .browse-area > .contents, -.user-environ .edit-area > .contents { - left: 0; - right: 0; } -/* line 115, ../../../../general/res/sass/user-environ/_layout.scss */ -.user-environ .edit-area { - top: 45px; } - /* line 118, ../../../../general/res/sass/user-environ/_layout.scss */ - .user-environ .edit-area .tool-bar { - bottom: auto; - height: 30px; - line-height: 25px; } - /* line 123, ../../../../general/res/sass/user-environ/_layout.scss */ - .user-environ .edit-area .work-area { - top: 40px; } -/* line 128, ../../../../general/res/sass/user-environ/_layout.scss */ -.user-environ .ue-bottom-bar { - overflow: hidden; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; - top: auto; - height: 25px; } - /* line 133, ../../../../general/res/sass/user-environ/_layout.scss */ - .user-environ .ue-bottom-bar .status-holder { - z-index: 1; } - /* line 137, ../../../../general/res/sass/user-environ/_layout.scss */ - .user-environ .ue-bottom-bar .app-logo { - left: auto; - width: 105px; - z-index: 2; } - -/* line 145, ../../../../general/res/sass/user-environ/_layout.scss */ -.cols { - overflow: hidden; - *zoom: 1; } - /* line 147, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols .col { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - *zoom: 1; - float: left; - margin-left: 1.5%; - padding-left: 5px; - position: relative; } - /* line 155, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols .col:first-child { - margin-left: 0; - padding-left: 0; } - /* line 162, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols.cols-2 .col-1 { - min-width: 250px; - width: 48.5%; } - /* line 168, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols.cols-2-ff .col-100px { - width: 100px; } - /* line 175, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols.cols-6 .col-1 { - min-width: 83.33333px; - width: 15.16667%; } - /* line 181, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols.cols-16 .col-1 { - min-width: 31.25px; - width: 4.75%; } - /* line 184, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols.cols-16 .col-2 { - min-width: 62.5px; - width: 11%; } - /* line 187, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols.cols-16 .col-7 { - min-width: 218.75px; - width: 42.25%; } - /* line 193, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols.cols-32 .col-2 { - min-width: 31.25px; - width: 4.75%; } - /* line 196, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols.cols-32 .col-15 { - min-width: 234.375px; - width: 45.375%; } - /* line 200, ../../../../general/res/sass/user-environ/_layout.scss */ - .cols .l-row { - overflow: hidden; - *zoom: 1; - padding: 5px 0; } - -/* line 209, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-mode .split-layout .split-pane-component.pane.treeview.left { - min-width: 150px; - max-width: 800px; - width: 25%; } -/* line 214, ../../../../general/res/sass/user-environ/_layout.scss */ -.browse-mode .split-layout .split-pane-component.pane.t-inspect.right { - min-width: 150px; - max-width: 800px; - width: 10%; } - -/* line 225, ../../../../general/res/sass/user-environ/_layout.scss */ -.edit-mode .split-layout .split-pane-component.pane.right { - width: 15%; } - /* line 227, ../../../../general/res/sass/user-environ/_layout.scss */ - .edit-mode .split-layout .split-pane-component.pane.right .pane.bottom { - min-height: 50px; - height: 30%; } - -/* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane { - position: absolute; } - /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane.treeview.left .create-btn-holder { - bottom: auto; - top: 0; - height: 24px; } - /* line 242, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane.treeview.left .create-btn-holder .wrapper.menu-element { - position: absolute; - bottom: 5px; } - /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane.treeview.left .search-holder { - top: 34px; } - /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane.treeview.left .tree-holder { - overflow: auto; - top: 64px; } - /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane { - z-index: 2; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane { - top: 5px; } - /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { - left: -30px; } - /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { - content: 'F'; } - /* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { - left: -25px; } - /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { - right: -25px; } - /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { - content: '\e608'; } - /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { - right: -20px; } } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, - .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, - .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, - .pane.items .object-browse-bar .right.abs, - .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .right.pager, - .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .right.pager, - .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .right.val, - .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .right.val, - .pane.items .object-browse-bar .s-menu-btn span.right.l-click-area, - .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { - top: auto; } - -/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ -.split-layout.horizontal > .pane { - margin-top: 5px; } - /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ - .split-layout.horizontal > .pane:first-child { - margin-top: 0; } -/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ -.split-layout.vertical > .pane { - margin-left: 5px; } - /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ - .split-layout.vertical > .pane > .holder { - left: 0; - right: 0; } - /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ - .split-layout.vertical > .pane:first-child { - margin-left: 0; } - /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ - .split-layout.vertical > .pane:first-child .holder { - right: 3px; } - -/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ -.object-holder { - overflow: hidden; - top: 34px; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ - .object-holder > ng-include { - overflow: auto; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; } - /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ - .object-holder.l-controls-visible.l-time-controller-visible { - bottom: 88px; } - -/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ -.object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, -.top-bar .buttons-main .s-btn, -.top-bar .buttons-main .s-menu-btn, -.top-bar .s-menu-btn, -.tool-bar .s-btn, -.tool-bar .s-menu-btn, -.tool-bar .s-menu-btn { - height: 25px; - line-height: 25px; - vertical-align: top; } - -/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ -.object-browse-bar .view-switcher, -.top-bar .view-switcher { - margin-right: 20px; } - -/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ -.object-browse-bar { - overflow: visible; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - height: 24px; - line-height: 24px; - white-space: nowrap; } - /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ - .object-browse-bar .left { - padding-right: 20px; } - /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ - .object-browse-bar .left .l-back { - display: inline-block; - float: left; - margin-right: 10px; } - -/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ -.l-flex { - display: flex; - display: -webkit-flex; - flex-flow: row nowrap; - -webkit-flex-flow: row nowrap; } - /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ - .l-flex .left { - flex: 1 1 0; - -webkit-flex: 1 1 0; - padding-right: 10px; } - -/* line 390, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-tree-hidden .pane.left.treeview, -.pane-tree-hidden .splitter-treeview { - opacity: 0; } -/* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-tree-hidden .pane.right.items { - left: 15px !important; } - -/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-tree-showing .pane.left.treeview, -.pane-tree-showing .splitter-treeview { - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 250ms; - -o-transition-duration: 250ms; - -webkit-transition-duration: 250ms; - transition-duration: 250ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 250ms; - -o-transition-delay: 250ms; - -webkit-transition-delay: 250ms; - transition-delay: 250ms; - opacity: 1; } - -/* line 411, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-inspect-showing .l-object-and-inspector .pane.right, -.pane-inspect-showing .l-object-and-inspector .splitter-inspect { - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 250ms; - -o-transition-duration: 250ms; - -webkit-transition-duration: 250ms; - transition-duration: 250ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 250ms; - -o-transition-delay: 250ms; - -webkit-transition-delay: 250ms; - transition-delay: 250ms; - opacity: 1; } - -/* line 420, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-inspect-hidden .l-object-and-inspector .pane.right, -.pane-inspect-hidden .l-object-and-inspector .splitter-inspect { - opacity: 0; } -/* line 424, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane-inspect-hidden .l-object-and-inspector .pane.left { - right: 15px !important; } - -/* line 430, ../../../../general/res/sass/user-environ/_layout.scss */ -.pane.right.t-inspect { - background-color: rgba(255, 165, 0, 0.3) !important; } - -@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 435, ../../../../general/res/sass/user-environ/_layout.scss */ - .pane:not(.resizing) { - -moz-transition-property: width, left, right; - -o-transition-property: width, left, right; - -webkit-transition-property: width, left, right; - transition-property: width, left, right; - -moz-transition-duration: 250ms; - -o-transition-duration: 250ms; - -webkit-transition-duration: 250ms; - transition-duration: 250ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; } } -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 26, ../../../../general/res/sass/mobile/_layout.scss */ - .browse-wrapper, - .pane { - top: 0 !important; - right: 0; - bottom: 0; - left: 0; } - - /* line 31, ../../../../general/res/sass/mobile/_layout.scss */ - .pane.left.treeview { - background-color: #f7f7f7; } - - /* line 35, ../../../../general/res/sass/mobile/_layout.scss */ - .pane.right.items { - -moz-transition-duration: 0.35s; - -o-transition-duration: 0.35s; - -webkit-transition-duration: 0.35s; - transition-duration: 0.35s; - transition-timing-function: ease; - backface-visibility: hidden; - margin-left: 0 !important; } - /* line 39, ../../../../general/res/sass/mobile/_layout.scss */ - .pane.right.items #content-area { - -moz-transition-duration: 0.35s; - -o-transition-duration: 0.35s; - -webkit-transition-duration: 0.35s; - transition-duration: 0.35s; - transition-timing-function: ease; - backface-visibility: hidden; - opacity: 1; } - - /* line 45, ../../../../general/res/sass/mobile/_layout.scss */ - .user-environ .browse-area, - .user-environ .edit-area, - .user-environ .editor { - top: 0; - left: 0; - right: 0; - bottom: 25px; } - - /* line 51, ../../../../general/res/sass/mobile/_layout.scss */ - .holder.l-mobile { - top: 10px !important; - right: 10px !important; - bottom: 10px !important; - left: 10px !important; } - - /* line 65, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-hidden .pane.left.treeview { - right: 100% !important; - width: auto !important; - overflow-y: hidden; - overflow-x: hidden; } - - /* line 82, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.left.treeview { - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSI5OCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); - background-size: 100%; - background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); - background-image: -webkit-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); - background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); - right: auto !important; - width: 40% !important; } - /* line 88, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.right.items { - left: 40% !important; } - - /* line 93, ../../../../general/res/sass/mobile/_layout.scss */ - .toggle-tree { - color: #0099cc !important; - font-size: 110%; - position: absolute; - top: 12px; - left: 10px; } - /* line 99, ../../../../general/res/sass/mobile/_layout.scss */ - .toggle-tree:after { - content: 'm' !important; } - - /* line 104, ../../../../general/res/sass/mobile/_layout.scss */ - .object-browse-bar { - left: 30px !important; } - /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ - .object-browse-bar .context-available { - opacity: 1 !important; } - /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ - .object-browse-bar .view-switcher { - margin-right: 0 !important; } - /* line 112, ../../../../general/res/sass/mobile/_layout.scss */ - .object-browse-bar .view-switcher .title-label { - display: none; } - - /* line 119, ../../../../general/res/sass/mobile/_layout.scss */ - .tree-holder { - overflow-x: hidden !important; } - - /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ - .mobile-disable-select { - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; } - - /* line 128, ../../../../general/res/sass/mobile/_layout.scss */ - .mobile-hide, - .mobile-hide-important { - display: none !important; } - - /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ - .mobile-back-hide { - pointer-events: none; - -moz-transition-property: opacity; - -o-transition-property: opacity; - -webkit-transition-property: opacity; - transition-property: opacity; - -moz-transition-duration: 0.4s; - -o-transition-duration: 0.4s; - -webkit-transition-duration: 0.4s; - transition-duration: 0.4s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - opacity: 0; } - - /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ - .mobile-back-unhide { - pointer-events: all; - -moz-transition-property: opacity; - -o-transition-property: opacity; - -webkit-transition-property: opacity; - transition-property: opacity; - -moz-transition-duration: 0.4s; - -o-transition-duration: 0.4s; - -webkit-transition-duration: 0.4s; - transition-duration: 0.4s; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - opacity: 1; } } -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { - /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.left.treeview { - width: 90% !important; } - /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.right.items { - left: 0 !important; - -moz-transform: translateX(90%); - -ms-transform: translateX(90%); - -webkit-transform: translateX(90%); - transform: translateX(90%); } - /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ - .pane-tree-showing .pane.right.items #content-area { - opacity: 0; } } -@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ - .desktop-hide { - display: none; } } -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 26, ../../../../general/res/sass/edit/_editor.scss */ -.edit-main .edit-corner, -.edit-main .edit-handle { - position: absolute; - z-index: 2; } -/* line 32, ../../../../general/res/sass/edit/_editor.scss */ -.edit-main .edit-corner { - width: 15px; - height: 15px; } - /* line 35, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-corner:hover { - z-index: 11; } - /* line 38, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-corner.edit-resize-nw { - -moz-border-radius-bottomright: 5px; - -webkit-border-bottom-right-radius: 5px; - border-bottom-right-radius: 5px; - cursor: nw-resize; - top: 0; - left: 0; } - /* line 43, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-corner.edit-resize-ne { - -moz-border-radius-bottomleft: 5px; - -webkit-border-bottom-left-radius: 5px; - border-bottom-left-radius: 5px; - cursor: ne-resize; - top: 0; - right: 0; } - /* line 48, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-corner.edit-resize-se { - -moz-border-radius-topleft: 5px; - -webkit-border-top-left-radius: 5px; - border-top-left-radius: 5px; - cursor: se-resize; - bottom: 0; - right: 0; } - /* line 53, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-corner.edit-resize-sw { - -moz-border-radius-topright: 5px; - -webkit-border-top-right-radius: 5px; - border-top-right-radius: 5px; - cursor: sw-resize; - bottom: 0; - left: 0; } -/* line 61, ../../../../general/res/sass/edit/_editor.scss */ -.edit-main .edit-handle { - top: 15px; - right: 15px; - bottom: 15px; - left: 15px; } - /* line 63, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-handle.edit-move { - cursor: move; - left: 0; - right: 0; - top: 0; - bottom: 0; - z-index: 1; } - /* line 73, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-handle.edit-resize-n { - top: 0px; - bottom: auto; - height: 15px; - cursor: n-resize; } - /* line 78, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-handle.edit-resize-e { - right: 0px; - left: auto; - width: 15px; - cursor: e-resize; } - /* line 83, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-handle.edit-resize-s { - bottom: 0px; - top: auto; - height: 15px; - cursor: s-resize; } - /* line 88, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .edit-handle.edit-resize-w { - left: 0px; - right: auto; - width: 15px; - cursor: w-resize; } -/* line 97, ../../../../general/res/sass/edit/_editor.scss */ -.edit-main .frame.child-frame.panel:hover { - -moz-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; - -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; - box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; - border-color: #0099cc; } - /* line 101, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .frame.child-frame.panel:hover .view-switcher { - opacity: 1; } - /* line 104, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .frame.child-frame.panel:hover .edit-corner { - background-color: rgba(0, 153, 204, 0.8); } - /* line 106, ../../../../general/res/sass/edit/_editor.scss */ - .edit-main .frame.child-frame.panel:hover .edit-corner:hover { - background-color: #0099cc; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 23, ../../../../general/res/sass/search/_search.scss */ -.abs.search-holder, .l-datetime-picker .l-month-year-pager .search-holder.pager, -.l-datetime-picker .l-month-year-pager .search-holder.val, .s-menu-btn span.search-holder.l-click-area { - height: 25px; - bottom: 0; - top: 23px; - z-index: 5; } - /* line 27, ../../../../general/res/sass/search/_search.scss */ - .abs.search-holder.active, .l-datetime-picker .l-month-year-pager .search-holder.active.pager, - .l-datetime-picker .l-month-year-pager .search-holder.active.val, .s-menu-btn span.search-holder.active.l-click-area { - height: auto; - bottom: 0; } - -/* line 38, ../../../../general/res/sass/search/_search.scss */ -.search { - display: flex; - display: -webkit-flex; - flex-direction: column; - -webkit-flex-direction: column; - height: 100%; } - /* line 48, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar { - font-size: 0.8em; - max-width: 250px; - position: relative; - width: 100%; } - /* line 60, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .search-input { - height: 25px; - line-height: 25px; - padding-top: 0; - padding-bottom: 0; } - /* line 67, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .search-icon, - .search .search-bar .clear-icon, - .search .search-bar .menu-icon { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #a6a6a6; - height: 17px; - width: 17px; - line-height: 17px; - position: absolute; - text-align: center; - top: 4px; } - /* line 80, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .clear-icon, - .search .search-bar .menu-icon { - cursor: pointer; - -moz-transition: color, 0.25s; - -o-transition: color, 0.25s; - -webkit-transition: color, 0.25s; - transition: color, 0.25s; } - /* line 87, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .search-input { - position: relative; - width: 100%; - padding-left: 22px !important; - padding-right: 44px !important; } - /* line 94, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .search-input input { - width: 100%; } - /* line 99, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .search-icon { - left: 3px; - transition: visibility .15s, opacity .15s, color .2s; - pointer-events: none; } - /* line 119, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .search-input:hover + div.search-icon { - color: #8c8c8c; } - /* line 123, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .clear-icon { - right: 22px; - visibility: hidden; - opacity: 0; - transition: visibility .15s, opacity .15s, color .2s; } - /* line 132, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .clear-icon.content { - visibility: visible; - opacity: 1; } - /* line 137, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .clear-icon:hover { - color: #8c8c8c; } - /* line 142, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .menu-icon { - font-size: 0.8em; - padding-right: 4px; - right: 4px; - text-align: right; } - /* line 148, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .menu-icon:hover { - color: #8c8c8c; } - /* line 153, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .search-menu-holder { - float: right; - left: -20px; - z-index: 1; - transition: visibility .05s, opacity .05s; } - /* line 163, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .search-menu-holder.off { - visibility: hidden; - opacity: 0; } - /* line 170, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar .menu-icon:hover + div.search-menu-holder { - visibility: visible; } - /* line 173, ../../../../general/res/sass/search/_search.scss */ - .search .search-bar div.search-menu-holder:hover { - visibility: visible; } - /* line 178, ../../../../general/res/sass/search/_search.scss */ - .search .active-filter-display { - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - line-height: 130%; - padding: 5px 0; - padding-left: 1.4625em; - font-size: 0.65em; - margin-top: 3px; } - /* line 193, ../../../../general/res/sass/search/_search.scss */ - .search .active-filter-display .clear-filters-icon { - color: #a6a6a6; - opacity: 1; - font-size: 0.8em; - position: absolute; - left: 1px; - cursor: pointer; } - /* line 205, ../../../../general/res/sass/search/_search.scss */ - .search .active-filter-display.off { - visibility: hidden; - opacity: 0; - height: 0; - margin: 0; - padding: 0; - border: 0; } - /* line 215, ../../../../general/res/sass/search/_search.scss */ - .search .search-scroll { - order: 3; - margin-top: 4px; - overflow-y: auto; - top: auto; - height: auto; - max-height: 100%; - position: relative; } - /* line 226, ../../../../general/res/sass/search/_search.scss */ - .search .search-scroll .load-icon { - position: relative; } - /* line 230, ../../../../general/res/sass/search/_search.scss */ - .search .search-scroll .load-more-button { - margin-top: 5px 0; - font-size: 0.8em; - position: relative; - left: 50%; - margin-left: -45px; - text-align: center; - width: 90px; - white-space: nowrap; } - -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 5, ../../../../general/res/sass/mobile/search/_search.scss */ - .search .search-bar .menu-icon { - display: none; } - /* line 8, ../../../../general/res/sass/mobile/search/_search.scss */ - .search .search-bar .clear-icon { - right: 5px; } } -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 23, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .blocker { - background: rgba(0, 0, 0, 0.7); - z-index: 100; } -/* line 27, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .clk-icon.close { - font-size: 0.8rem; - position: absolute; - top: 10px; - right: 10px; - bottom: auto; - left: auto; - z-index: 100; } -/* line 33, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay > .holder { - background-color: #fcfcfc; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #666; - display: inline-block; - -moz-border-radius: 12px; - -webkit-border-radius: 12px; - border-radius: 12px; - color: #666; - top: 15%; - right: 15%; - bottom: 15%; - left: 15%; - z-index: 101; } - /* line 40, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay > .holder > .contents { - top: 25px; - right: 25px; - bottom: 25px; - left: 25px; } -/* line 45, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .title { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 1.2em; - margin-bottom: 5px; } -/* line 51, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .top-bar { - height: 60px; } -/* line 55, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .editor { - top: 70px; - bottom: 40px; - left: 0; - right: 0; } -/* line 61, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .bottom-bar { - top: auto; - right: 0; - bottom: 0; - left: 0; - overflow: visible; - height: 30px; - text-align: right; } - /* line 67, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu-btn { - font-size: 95%; - height: 30px; - line-height: 30px; - margin-left: 5px; - padding: 0 15px; } - /* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu-btn:not(.major) { - background-color: #969696; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #fff; - display: inline-block; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - -moz-transition: background, 0.25s; - -o-transition: background, 0.25s; - -webkit-transition: background, 0.25s; - transition: background, 0.25s; - text-shadow: none; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ - .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon { - color: #fff; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ - .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover { - background: #7d7d7d; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ - .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover > .icon { - color: white; } } -/* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ -.overlay .contents.l-dialog { - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - overflow: auto; } - /* line 93, ../../../../general/res/sass/overlay/_overlay.scss */ - .overlay .contents.l-dialog .field.l-med input[type='text'] { - width: 100%; } - -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 4, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay .clk-icon.close { - top: 10px; - right: 10px; } - /* line 8, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder { - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - top: 0; - right: 0; - bottom: 0; - left: 0; } - /* line 14, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents { - top: 10px; - right: 10px; - bottom: 10px; - left: 10px; } - /* line 21, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .top-bar > .title { - margin-right: 1.2em; } - /* line 26, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor { - border: none; } - /* line 29, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor .contents { - top: 0; - right: 0; - bottom: 0; - left: 0; } } -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 43, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor .contents .form-row > .label, - .overlay > .holder > .contents .form.editor .contents .form-row > .controls { - display: block; - float: none; - width: 100%; } - /* line 51, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ - .overlay > .holder > .contents .form.editor .contents .form-row > .label:after { - float: none; } } -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 23, ../../../../general/res/sass/tree/_tree.scss */ -ul.tree { - margin: 0; - padding: 0; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; } - /* line 331, ../../../../general/res/sass/_mixins.scss */ - ul.tree li { - list-style-type: none; - margin: 0; - padding: 0; } - /* line 26, ../../../../general/res/sass/tree/_tree.scss */ - ul.tree li { - display: block; - position: relative; } - /* line 30, ../../../../general/res/sass/tree/_tree.scss */ - ul.tree ul.tree { - margin-left: 15px; } - -/* line 35, ../../../../general/res/sass/tree/_tree.scss */ -.tree-item, -.search-result-item { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-transition: background-color 0.25s; - -o-transition: background-color 0.25s; - -webkit-transition: background-color 0.25s; - transition: background-color 0.25s; - display: block; - font-size: 0.8rem; - height: 1.5rem; - line-height: 1.5rem; - margin-bottom: 3px; - position: relative; } - /* line 48, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .view-control, - .search-result-item .view-control { - color: #666; - display: inline-block; - margin-left: 5px; - font-size: 0.75em; - width: 10px; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 57, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .view-control:hover, - .search-result-item .view-control:hover { - color: #0099cc !important; } } - /* line 63, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .label, - .search-result-item .label { - display: block; - overflow: hidden; - position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; - width: auto; - height: auto; - line-height: 1.5rem; } - /* line 71, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .label .type-icon, - .search-result-item .label .type-icon { - font-size: 16px; - color: #0099cc; - left: 5px; - position: absolute; - top: 4px; - bottom: auto; - height: 16px; - line-height: 100%; - right: auto; - width: 16px; } - /* line 84, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .label .type-icon .icon.l-icon-link, .tree-item .label .type-icon .icon.l-icon-alert, - .search-result-item .label .type-icon .icon.l-icon-link, - .search-result-item .label .type-icon .icon.l-icon-alert { - position: absolute; - z-index: 2; } - /* line 89, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .label .type-icon .icon.l-icon-alert, - .search-result-item .label .type-icon .icon.l-icon-alert { - color: #ff3c00; - font-size: 8px; - line-height: 8px; - height: 8px; - width: 8px; - top: 1px; - right: -2px; } - /* line 95, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .label .type-icon .icon.l-icon-link, - .search-result-item .label .type-icon .icon.l-icon-link { - color: #49dedb; - font-size: 8px; - line-height: 8px; - height: 8px; - width: 8px; - left: -3px; - bottom: 0px; } - /* line 103, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .label .title-label, - .search-result-item .label .title-label { - overflow: hidden; - position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; - width: auto; - height: auto; - display: block; - left: 30px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; } - /* line 113, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item.selected, - .search-result-item.selected { - background: #1ac6ff; - color: #fcfcfc; } - /* line 116, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item.selected .view-control, - .search-result-item.selected .view-control { - color: #fcfcfc; } - /* line 119, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item.selected .label .type-icon, - .search-result-item.selected .label .type-icon { - color: #fcfcfc; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 127, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item:not(.selected):hover, - .search-result-item:not(.selected):hover { - background: rgba(102, 102, 102, 0.1); - color: #333333; } - /* line 130, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item:not(.selected):hover .icon, - .search-result-item:not(.selected):hover .icon { - color: #0099cc; } } - /* line 137, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item:not(.loading), - .search-result-item:not(.loading) { - cursor: pointer; } - /* line 141, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .context-trigger, - .search-result-item .context-trigger { - top: -1px; - position: absolute; - right: 3px; } - /* line 146, ../../../../general/res/sass/tree/_tree.scss */ - .tree-item .context-trigger .invoke-menu, - .search-result-item .context-trigger .invoke-menu { - font-size: 0.75em; - height: 0.9rem; - line-height: 0.9rem; } - -/* line 155, ../../../../general/res/sass/tree/_tree.scss */ -.tree-item .label { - left: 15px; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 27, ../../../../general/res/sass/mobile/_tree.scss */ - ul.tree ul.tree { - margin-left: 20px; } - - /* line 31, ../../../../general/res/sass/mobile/_tree.scss */ - .tree-item, - .search-result-item { - height: 35px; - line-height: 35px; - margin-bottom: 0px; } - /* line 36, ../../../../general/res/sass/mobile/_tree.scss */ - .tree-item .view-control, - .search-result-item .view-control { - position: absolute; - font-size: 1.1em; - right: 0px; - width: 30px; - text-align: center; } - /* line 45, ../../../../general/res/sass/mobile/_tree.scss */ - .tree-item .label, - .search-result-item .label { - left: 0; - right: 35px; - line-height: 35px; } - /* line 50, ../../../../general/res/sass/mobile/_tree.scss */ - .tree-item .label .type-icon, - .search-result-item .label .type-icon { - top: 9px; - bottom: auto; - height: 16px; } } -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 25, ../../../../general/res/sass/user-environ/_frame.scss */ -.frame.child-frame.panel { - background: #fcfcfc; - border: 1px solid rgba(102, 102, 102, 0.2); } - /* line 28, ../../../../general/res/sass/user-environ/_frame.scss */ - .frame.child-frame.panel:hover { - border-color: rgba(128, 128, 128, 0.2); } -/* line 32, ../../../../general/res/sass/user-environ/_frame.scss */ -.frame > .object-header.abs, .l-datetime-picker .l-month-year-pager .frame > .object-header.pager, -.l-datetime-picker .l-month-year-pager .frame > .object-header.val, .s-menu-btn .frame > span.object-header.l-click-area { - font-size: 0.75em; - height: 16px; - line-height: 16px; } -/* line 38, ../../../../general/res/sass/user-environ/_frame.scss */ -.frame > .object-holder.abs, .l-datetime-picker .l-month-year-pager .frame > .object-holder.pager, -.l-datetime-picker .l-month-year-pager .frame > .object-holder.val, .s-menu-btn .frame > span.object-holder.l-click-area { - top: 21px; } -/* line 41, ../../../../general/res/sass/user-environ/_frame.scss */ -.frame .contents { - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; } -/* line 49, ../../../../general/res/sass/user-environ/_frame.scss */ -.frame.frame-template .s-btn, .frame.frame-template .s-menu-btn, -.frame.frame-template .s-menu-btn { - height: 16px; - line-height: 16px; - padding: 0 5px; } - /* line 54, ../../../../general/res/sass/user-environ/_frame.scss */ - .frame.frame-template .s-btn > span, .frame.frame-template .s-menu-btn > span, - .frame.frame-template .s-menu-btn > span { - font-size: 0.65rem; } -/* line 59, ../../../../general/res/sass/user-environ/_frame.scss */ -.frame.frame-template .s-menu-btn:after { - font-size: 8px; } -/* line 63, ../../../../general/res/sass/user-environ/_frame.scss */ -.frame.frame-template .view-switcher { - z-index: 10; } -@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 69, ../../../../general/res/sass/user-environ/_frame.scss */ - .frame.frame-template .view-switcher { - opacity: 0; } - /* line 72, ../../../../general/res/sass/user-environ/_frame.scss */ - .frame.frame-template:hover .view-switcher { - opacity: 1; } } -/* line 80, ../../../../general/res/sass/user-environ/_frame.scss */ -.frame .view-switcher .title-label { - display: none; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/user-environ/_top-bar.scss */ -.top-bar { - /* .title { - color: #fff; - }*/ } - /* line 23, ../../../../general/res/sass/user-environ/_top-bar.scss */ - .top-bar.browse, .top-bar.edit { - border-bottom: 1px solid rgba(102, 102, 102, 0.2); - top: 10px; - right: 10px; - bottom: auto; - left: 10px; - height: 30px; - line-height: 24px; } - /* line 35, ../../../../general/res/sass/user-environ/_top-bar.scss */ - .top-bar .buttons-main { - font-size: 0.8em; - left: auto; - text-align: right; } - -/* line 48, ../../../../general/res/sass/user-environ/_top-bar.scss */ -.edit-mode .top-bar .buttons-main { - white-space: nowrap; } - /* line 52, ../../../../general/res/sass/user-environ/_top-bar.scss */ - .edit-mode .top-bar .buttons-main.abs, .edit-mode .top-bar .l-datetime-picker .l-month-year-pager .buttons-main.pager, .l-datetime-picker .l-month-year-pager .edit-mode .top-bar .buttons-main.pager, - .edit-mode .top-bar .l-datetime-picker .l-month-year-pager .buttons-main.val, - .l-datetime-picker .l-month-year-pager .edit-mode .top-bar .buttons-main.val, .edit-mode .top-bar .s-menu-btn span.buttons-main.l-click-area, .s-menu-btn .edit-mode .top-bar span.buttons-main.l-click-area { - bottom: auto; - left: auto; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ -.ue-bottom-bar { - background: #000; - color: white; - font-size: .7rem; } - /* line 28, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .ue-bottom-bar .status-holder { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - position: absolute; - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - width: auto; - height: auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - line-height: 15px; - right: 120px; - text-transform: uppercase; } - /* line 39, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .ue-bottom-bar .app-logo { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - overflow: hidden; - position: absolute; - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; - width: auto; - height: auto; - left: auto; - cursor: pointer; } - /* line 48, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .ue-bottom-bar .app-logo.logo-openmctweb { - background: url("../../../../general/res/images/logo-openmctweb.svg") no-repeat center center; } - -/* line 54, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ -.status.block { - display: inline; - margin-right: 10px; } - /* line 58, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .status.block .status-indicator { - display: inline-block; - margin-right: 3px; - color: #0099cc; } - /* line 65, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .status.block .status-indicator.ok { - color: #009900; } - /* line 68, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ - .status.block .status-indicator.caution { - color: #ffaa00; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/user-environ/_tool-bar.scss */ -.tool-bar { - border-bottom: 1px solid rgba(102, 102, 102, 0.2); } - /* line 24, ../../../../general/res/sass/user-environ/_tool-bar.scss */ - .tool-bar .l-control-group { - height: 25px; } - /* line 27, ../../../../general/res/sass/user-environ/_tool-bar.scss */ - .tool-bar input[type="text"] { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - font-size: .9em; - height: 25px; - margin-bottom: 1px; - position: relative; } - /* line 33, ../../../../general/res/sass/user-environ/_tool-bar.scss */ - .tool-bar input[type="text"].sm { - width: 25px; } - /* line 37, ../../../../general/res/sass/user-environ/_tool-bar.scss */ - .tool-bar .input-labeled label { - font-size: 11.25px; } - -/********************************* VIEWS */ -/***************************************************************************** -* Open MCT Web, Copyright (c) 2014-2015, United States Government -* as represented by the Administrator of the National Aeronautics and Space -* Administration. All rights reserved. -* -* Open MCT Web 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 Web 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. -*****************************************************************************/ -/* line 23, ../../../../general/res/sass/_fixed-position.scss */ -.t-fixed-position.l-fixed-position { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; } - /* line 33, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position.l-fixed-position .l-grid-holder { - position: relative; - height: 100%; - width: 100%; } - /* line 37, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position.l-fixed-position .l-grid-holder .l-grid { - position: absolute; - height: 100%; - width: 100%; - pointer-events: none; - z-index: 0; } -/* line 48, ../../../../general/res/sass/_fixed-position.scss */ -.t-fixed-position .l-fixed-position-item { - position: absolute; - border: 1px solid transparent; } - /* line 52, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item.s-selected { - -moz-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; - -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; - box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; - border-color: #0099cc; - cursor: move; } - /* line 57, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item.s-not-selected { - opacity: 0.8; } - /* line 61, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item .l-fixed-position-box, - .t-fixed-position .l-fixed-position-item .l-fixed-position-image, - .t-fixed-position .l-fixed-position-item .l-fixed-position-text { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - height: 100%; - width: 100%; } - /* line 72, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item .l-fixed-position-image { - background-size: cover; - background-repeat: no-repeat; - background-position: center; } - /* line 78, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item .l-fixed-position-text { - border: 1px solid transparent; - font-size: 0.8rem; - line-height: 100%; } - /* line 84, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-static-text { - padding: 1px; } - /* line 89, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - display: block; - padding: 2px; } - /* line 96, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem.l-title { - float: none; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: auto; } - /* line 105, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem.l-value { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - float: right; - margin-left: 5px; - padding-left: 5px; - padding-right: 5px; - text-align: right; } - /* line 116, ../../../../general/res/sass/_fixed-position.scss */ - .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem.l-value.telem-only { - margin-left: 0; - width: 100%; } -/* line 126, ../../../../general/res/sass/_fixed-position.scss */ -.t-fixed-position .l-fixed-position-item-handle { - background: rgba(0, 153, 204, 0.5); - cursor: crosshair; - border: 1px solid #0099cc; - position: absolute; } - -/* line 140, ../../../../general/res/sass/_fixed-position.scss */ -.edit-mode .t-fixed-position.l-fixed-position .l-grid-holder .l-grid.l-grid-x { - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIxcHgiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wNSIvPjxzdG9wIG9mZnNldD0iMXB4IiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); - background-size: 100%; - background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); - background-image: -webkit-linear-gradient(0deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); - background-image: linear-gradient(90deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); - background-repeat: repeat-x; } -/* line 144, ../../../../general/res/sass/_fixed-position.scss */ -.edit-mode .t-fixed-position.l-fixed-position .l-grid-holder .l-grid.l-grid-y { - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjEuMCIgeDI9IjAuNSIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIxcHgiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wNSIvPjxzdG9wIG9mZnNldD0iMXB4IiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); - background-size: 100%; - background-image: -moz-linear-gradient(90deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); - background-image: -webkit-linear-gradient(90deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); - background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); - background-repeat: repeat-y; } -/* line 152, ../../../../general/res/sass/_fixed-position.scss */ -.edit-mode .t-fixed-position .l-fixed-position-item:not(.s-selected) { - border: 1px dotted rgba(0, 153, 204, 0.75); } - /* line 154, ../../../../general/res/sass/_fixed-position.scss */ - .edit-mode .t-fixed-position .l-fixed-position-item:not(.s-selected):hover { - border: 1px dotted #0099cc; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/lists/_tabular.scss */ -.w1, .w2 { - position: relative; - height: 100%; } - -/* line 27, ../../../../general/res/sass/lists/_tabular.scss */ -.tabular, -table { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - border-spacing: 0; - border-collapse: collapse; - display: table; - font-size: 0.75rem; - position: relative; - width: 100%; } - /* line 36, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular thead, .tabular .thead, - .tabular tbody tr, .tabular .tbody .tr, - table thead, - table .thead, - table tbody tr, - table .tbody .tr { - width: 100%; } - /* line 40, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular thead, .tabular .thead, - table thead, - table .thead { - border-bottom: 1px solid #fcfcfc; } - /* line 44, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular:not(.fixed-header) tr th, - table:not(.fixed-header) tr th { - background-color: #e3e3e3; } - /* line 48, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tbody, .tabular .tbody, - table tbody, - table .tbody { - display: table-row-group; } - /* line 51, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tbody tr:hover, .tabular tbody .tr:hover, .tabular .tbody tr:hover, .tabular .tbody .tr:hover, - table tbody tr:hover, - table tbody .tr:hover, - table .tbody tr:hover, - table .tbody .tr:hover { - background: rgba(51, 51, 51, 0.1); } - /* line 56, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr, .tabular .tr, - table tr, - table .tr { - display: table-row; } - /* line 58, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr:first-child .td, .tabular .tr:first-child .td, - table tr:first-child .td, - table .tr:first-child .td { - border-top: none; } - /* line 62, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr.group-header td, .tabular tr.group-header .td, .tabular .tr.group-header td, .tabular .tr.group-header .td, - table tr.group-header td, - table tr.group-header .td, - table .tr.group-header td, - table .tr.group-header .td { - background-color: #efefef; - color: #404040; } - /* line 68, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr th, .tabular tr .th, .tabular tr td, .tabular tr .td, .tabular .tr th, .tabular .tr .th, .tabular .tr td, .tabular .tr .td, - table tr th, - table tr .th, - table tr td, - table tr .td, - table .tr th, - table .tr .th, - table .tr td, - table .tr .td { - display: table-cell; } - /* line 71, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr th, .tabular tr .th, .tabular .tr th, .tabular .tr .th, - table tr th, - table tr .th, - table .tr th, - table .tr .th { - border-left: 1px solid #fcfcfc; - color: #333333; - padding: 5px 5px; - white-space: nowrap; - vertical-align: middle; } - /* line 77, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr th:first-child, .tabular tr .th:first-child, .tabular .tr th:first-child, .tabular .tr .th:first-child, - table tr th:first-child, - table tr .th:first-child, - table .tr th:first-child, - table .tr .th:first-child { - border-left: none; } - /* line 81, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr th.sort.sort:after, .tabular tr .th.sort.sort:after, .tabular .tr th.sort.sort:after, .tabular .tr .th.sort.sort:after, - table tr th.sort.sort:after, - table tr .th.sort.sort:after, - table .tr th.sort.sort:after, - table .tr .th.sort.sort:after { - color: #49dedb; - font-family: symbolsfont; - font-size: 8px; - content: "\ed"; - display: inline-block; - margin-left: 3px; } - /* line 89, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr th.sort.sort.desc:after, .tabular tr .th.sort.sort.desc:after, .tabular .tr th.sort.sort.desc:after, .tabular .tr .th.sort.sort.desc:after, - table tr th.sort.sort.desc:after, - table tr .th.sort.sort.desc:after, - table .tr th.sort.sort.desc:after, - table .tr .th.sort.sort.desc:after { - content: "\ec"; } - /* line 93, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr th.sortable, .tabular tr .th.sortable, .tabular .tr th.sortable, .tabular .tr .th.sortable, - table tr th.sortable, - table tr .th.sortable, - table .tr th.sortable, - table .tr .th.sortable { - cursor: pointer; } - /* line 97, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr td, .tabular tr .td, .tabular .tr td, .tabular .tr .td, - table tr td, - table tr .td, - table .tr td, - table .tr .td { - border-bottom: 1px solid #e3e3e3; - min-width: 20px; - color: #333333; - padding: 3px 5px; - word-wrap: break-word; - vertical-align: top; } - /* line 104, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr td.numeric, .tabular tr .td.numeric, .tabular .tr td.numeric, .tabular .tr .td.numeric, - table tr td.numeric, - table tr .td.numeric, - table .tr td.numeric, - table .tr .td.numeric { - text-align: right; } - /* line 107, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr td.s-cell-type-value, .tabular tr .td.s-cell-type-value, .tabular .tr td.s-cell-type-value, .tabular .tr .td.s-cell-type-value, - table tr td.s-cell-type-value, - table tr .td.s-cell-type-value, - table .tr td.s-cell-type-value, - table .tr .td.s-cell-type-value { - text-align: right; } - /* line 109, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular tr td.s-cell-type-value .l-cell-contents, .tabular tr .td.s-cell-type-value .l-cell-contents, .tabular .tr td.s-cell-type-value .l-cell-contents, .tabular .tr .td.s-cell-type-value .l-cell-contents, - table tr td.s-cell-type-value .l-cell-contents, - table tr .td.s-cell-type-value .l-cell-contents, - table .tr td.s-cell-type-value .l-cell-contents, - table .tr .td.s-cell-type-value .l-cell-contents { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - padding-left: 5px; - padding-right: 5px; } - /* line 125, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular.filterable tbody, .tabular.filterable .tbody, - table.filterable tbody, - table.filterable .tbody { - top: 44px; } - /* line 128, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular.filterable input[type="text"], - table.filterable input[type="text"] { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - width: 100%; } - /* line 134, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular.fixed-header, - table.fixed-header { - height: 100%; } - /* line 136, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular.fixed-header thead, .tabular.fixed-header .thead, - .tabular.fixed-header tbody tr, .tabular.fixed-header .tbody .tr, - table.fixed-header thead, - table.fixed-header .thead, - table.fixed-header tbody tr, - table.fixed-header .tbody .tr { - display: table; - table-layout: fixed; } - /* line 141, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular.fixed-header thead, .tabular.fixed-header .thead, - table.fixed-header thead, - table.fixed-header .thead { - width: calc(100% - 10px); } - /* line 143, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular.fixed-header thead:before, .tabular.fixed-header .thead:before, - table.fixed-header thead:before, - table.fixed-header .thead:before { - content: ""; - display: block; - z-index: 0; - position: absolute; - width: 100%; - height: 22px; - background-color: #e3e3e3; } - /* line 153, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular.fixed-header tbody, .tabular.fixed-header .tbody, - table.fixed-header tbody, - table.fixed-header .tbody { - overflow: hidden; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; - top: 22px; - display: block; - overflow-y: scroll; } - /* line 161, ../../../../general/res/sass/lists/_tabular.scss */ - .tabular.t-event-messages td, .tabular.t-event-messages .td, - table.t-event-messages td, - table.t-event-messages .td { - min-width: 150px; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 31, ../../../../general/res/sass/plots/_plots-main.scss */ -.gl-plot { - color: #666; - font-size: 0.7rem; - position: relative; - width: 100%; - height: 100%; - /****************************** Limits and Out-of-Bounds data */ } - /* line 38, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-axis-area { - position: absolute; } - /* line 41, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-axis-area.gl-plot-x { - top: auto; - right: 0; - bottom: 5px; - left: 60px; - height: 32px; - width: auto; - overflow: hidden; } - /* line 50, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-axis-area.gl-plot-y { - top: 25px; - right: auto; - bottom: 37px; - left: 0; - width: 60px; } - /* line 59, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-coords { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - background: black; - color: #b3b3b3; - padding: 2px 5px; - position: absolute; - top: 35px; - right: auto; - bottom: auto; - left: 70px; - z-index: 10; } - /* line 71, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-coords:empty { - display: none; } - /* line 76, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-display-area { - background-color: rgba(0, 0, 0, 0.05); - position: absolute; - top: 25px; - right: 0; - bottom: 37px; - left: 60px; - cursor: crosshair; - border: 1px solid rgba(102, 102, 102, 0.2); } - /* line 89, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-label, - .gl-plot .l-plot-label { - color: #999999; - position: absolute; - text-align: center; } - /* line 97, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-label.gl-plot-x-label, .gl-plot .gl-plot-label.l-plot-x-label, - .gl-plot .l-plot-label.gl-plot-x-label, - .gl-plot .l-plot-label.l-plot-x-label { - top: auto; - right: 0; - bottom: 0; - left: 0; - height: auto; } - /* line 106, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-label.gl-plot-y-label, .gl-plot .gl-plot-label.l-plot-y-label, - .gl-plot .l-plot-label.gl-plot-y-label, - .gl-plot .l-plot-label.l-plot-y-label { - -moz-transform-origin: 50% 0; - -ms-transform-origin: 50% 0; - -webkit-transform-origin: 50% 0; - transform-origin: 50% 0; - -moz-transform: translateX(-50%) rotate(-90deg); - -ms-transform: translateX(-50%) rotate(-90deg); - -webkit-transform: translateX(-50%) rotate(-90deg); - transform: translateX(-50%) rotate(-90deg); - display: inline-block; - margin-left: 5px; - left: 0; - top: 50%; - white-space: nowrap; } - /* line 120, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-y-options { - position: absolute; - top: 50%; - right: auto; - bottom: auto; - left: auto5px; - margin-top: -16px; - height: auto; - min-height: 32px; - width: 32px; } - /* line 134, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-hash { - position: absolute; - border: 0 rgba(0, 0, 0, 0.2) dashed; } - /* line 137, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-hash.hash-v { - border-right-width: 1px; - height: 100%; } - /* line 141, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-hash.hash-h { - border-bottom-width: 1px; - width: 100%; } - /* line 147, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .gl-plot-legend { - position: absolute; - top: 0; - right: 0; - bottom: auto; - left: 0; - height: 20px; - overflow-x: hidden; - overflow-y: auto; } - /* line 160, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .l-limit-bar, - .gl-plot .l-oob-data { - position: absolute; - left: 0; - right: 0; - width: auto; } - /* line 168, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .l-limit-bar { - height: auto; - z-index: 0; } - /* line 176, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .l-limit-bar.s-limit-yellow { - background: rgba(255, 170, 0, 0.2); } - /* line 177, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .l-limit-bar.s-limit-red { - background: rgba(255, 0, 0, 0.2); } - /* line 180, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .l-oob-data { - overflow: hidden; - position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; - width: auto; - height: auto; - pointer-events: none; - height: 10px; - z-index: 1; } - /* line 188, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .l-oob-data.l-oob-data-up { - top: 0; - bottom: auto; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjEuMCIgeDI9IjAuNSIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzc3NDhkNiIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM3NzQ4ZDYiIHN0b3Atb3BhY2l0eT0iMC41Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); - background-size: 100%; - background-image: -moz-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); - background-image: -webkit-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); - background-image: linear-gradient(0deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); } - /* line 193, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot .l-oob-data.l-oob-data-dwn { - bottom: 0; - top: auto; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzc3NDhkNiIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM3NzQ4ZDYiIHN0b3Atb3BhY2l0eT0iMC41Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); - background-size: 100%; - background-image: -moz-linear-gradient(270deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); - background-image: -webkit-linear-gradient(270deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); - background-image: linear-gradient(180deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); } - -/* line 203, ../../../../general/res/sass/plots/_plots-main.scss */ -.gl-plot-legend .plot-legend-item, -.gl-plot-legend .legend-item, -.legend .plot-legend-item, -.legend .legend-item { - display: inline-block; - margin-right: 10px; - margin-bottom: 3px; } - /* line 208, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot-legend .plot-legend-item span, - .gl-plot-legend .legend-item span, - .legend .plot-legend-item span, - .legend .legend-item span { - vertical-align: middle; } - /* line 211, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot-legend .plot-legend-item .plot-color-swatch, - .gl-plot-legend .plot-legend-item .color-swatch, - .gl-plot-legend .legend-item .plot-color-swatch, - .gl-plot-legend .legend-item .color-swatch, - .legend .plot-legend-item .plot-color-swatch, - .legend .plot-legend-item .color-swatch, - .legend .legend-item .plot-color-swatch, - .legend .legend-item .color-swatch { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; - display: inline-block; - height: 8px; - width: 8px; } - -/* line 228, ../../../../general/res/sass/plots/_plots-main.scss */ -.gl-plot-legend .plot-legend-item { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - line-height: 1.5em; - padding: 0px 5px; } - /* line 234, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot-legend .plot-legend-item .plot-color-swatch { - border: 1px solid #fcfcfc; - height: 9px; - width: 9px; } - -/* line 242, ../../../../general/res/sass/plots/_plots-main.scss */ -.tick { - position: absolute; - border: 0 rgba(0, 0, 0, 0.2) solid; } - /* line 245, ../../../../general/res/sass/plots/_plots-main.scss */ - .tick.tick-x { - border-right-width: 1px; - height: 100%; } - -/* line 251, ../../../../general/res/sass/plots/_plots-main.scss */ -.gl-plot-tick, -.tick-label { - font-size: 0.7rem; - position: absolute; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; } - /* line 259, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot-tick.gl-plot-x-tick-label, .gl-plot-tick.tick-label-x, - .tick-label.gl-plot-x-tick-label, - .tick-label.tick-label-x { - right: auto; - bottom: auto; - left: auto; - height: auto; - width: 20%; - margin-left: -10%; - text-align: center; } - /* line 269, ../../../../general/res/sass/plots/_plots-main.scss */ - .gl-plot-tick.gl-plot-y-tick-label, .gl-plot-tick.tick-label-y, - .tick-label.gl-plot-y-tick-label, - .tick-label.tick-label-y { - top: auto; - height: 1em; - width: auto; - margin-bottom: -0.5em; - text-align: right; } - -/* line 281, ../../../../general/res/sass/plots/_plots-main.scss */ -.gl-plot-tick.gl-plot-x-tick-label { - top: 5px; } -/* line 284, ../../../../general/res/sass/plots/_plots-main.scss */ -.gl-plot-tick.gl-plot-y-tick-label { - right: 5px; - left: 5px; } - -/* line 291, ../../../../general/res/sass/plots/_plots-main.scss */ -.tick-label.tick-label-x { - top: 0; } -/* line 294, ../../../../general/res/sass/plots/_plots-main.scss */ -.tick-label.tick-label-y { - right: 0; - left: 0; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* Styles for the iframe EmbeddedPageController element */ -/* line 25, ../../../../general/res/sass/_iframe.scss */ -.l-iframe iframe { - display: block; - height: 100%; - width: 100%; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/******************************** BROWSE */ -/* line 27, ../../../../general/res/sass/_hide-non-functional.scss */ -.browse-mode .browse.top-bar { - display: none; } -/* line 32, ../../../../general/res/sass/_hide-non-functional.scss */ -.browse-mode .browse-area.holder { - top: 10px; } - -/* Styles for sub-dividing views generically */ -/* line 3, ../../../../general/res/sass/_views.scss */ -.l-view-section { - overflow: hidden; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; - font-size: 0.8rem; } - /* line 6, ../../../../general/res/sass/_views.scss */ - .l-view-section h2 { - color: #fff; - margin-bottom: 5px; } - /* line 10, ../../../../general/res/sass/_views.scss */ - .l-view-section.fixed { - font-size: 0.8em; } - /* line 13, ../../../../general/res/sass/_views.scss */ - .l-view-section .controls, - .l-view-section label, - .l-view-section .inline-block { - display: inline-block; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/items/_item.scss */ -.items-holder { - overflow: hidden; - *zoom: 1; - overflow-y: auto; } - /* line 25, ../../../../general/res/sass/items/_item.scss */ - .items-holder .contents { - top: 0; } - /* line 29, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item { - background-color: #ddd; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #666; - display: inline-block; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - -moz-transition: background, 0.25s; - -o-transition: background, 0.25s; - -webkit-transition: background, 0.25s; - transition: background, 0.25s; - text-shadow: none; - box-sizing: border-box; - cursor: pointer; - float: left; - height: 200px; - width: 200px; - margin-bottom: 3px; - margin-right: 3px; - position: relative; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ - .items-holder .item.grid-item .icon { - color: #0099cc; } - @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 279, ../../../../general/res/sass/_mixins.scss */ - .items-holder .item.grid-item:not(.disabled):hover { - background: #d0d0d0; } - /* line 281, ../../../../general/res/sass/_mixins.scss */ - .items-holder .item.grid-item:not(.disabled):hover > .icon { - color: #33ccff; } } - /* line 45, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item:hover .item-main .item-type { - color: deepskyblue; } - /* line 47, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item:hover .item-main .item-type .l-icon-link { - color: #49dedb; } - /* line 51, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item:hover .item-main .item-open { - opacity: 1; } - /* line 55, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .contents { - top: 10px; - right: 10px; - bottom: 10px; - left: 10px; } - /* line 61, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .bar.top-bar { - bottom: auto; - color: #8c8c8c; - height: 20px; - line-height: 20px; - text-align: right; - z-index: 5; } - /* line 68, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .bar.top-bar .left, .items-holder .item.grid-item .bar.top-bar .right { - width: auto; } - /* line 70, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .bar.top-bar .left .icon, .items-holder .item.grid-item .bar.top-bar .right .icon { - margin-left: 3px; } - /* line 72, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .bar.top-bar .left .icon.l-icon-link, .items-holder .item.grid-item .bar.top-bar .right .icon.l-icon-link { - color: #49dedb; } - /* line 78, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .bar.bottom-bar { - top: auto; - line-height: 110%; } - /* line 83, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .item-main { - line-height: 160px; - z-index: 1; } - /* line 89, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .item-main .item-type { - overflow: false; - position: absolute; - top: 40px; - right: 40px; - bottom: 40px; - left: 40px; - width: auto; - height: auto; - text-align: center; - font-size: 96.9px; - line-height: 102px; - bottom: auto; - height: 102px; - top: 30px; } - /* line 100, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .item-main .item-type .l-icon-link { - color: #49dedb; - height: auto; - line-height: 100%; - position: absolute; - font-size: 0.3em; - left: 0px; - bottom: 10px; - z-index: 2; } - /* line 111, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .item-main .item-open { - -moz-transition-property: "opacity"; - -o-transition-property: "opacity"; - -webkit-transition-property: "opacity"; - transition-property: "opacity"; - -moz-transition-duration: 200ms; - -o-transition-duration: 200ms; - -webkit-transition-duration: 200ms; - transition-duration: 200ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - opacity: 0; - color: #8c8c8c; - font-size: 3em; - left: auto; - width: 50px; - pointer-events: none; - text-align: right; } - /* line 121, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .title { - text-shadow: none; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: #666; } - /* line 126, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item .details { - text-shadow: none; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: #8c8c8c; - font-size: 0.8em; } - /* line 132, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item.selected { - background-color: #0099cc; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #fff; - display: inline-block; - -moz-user-select: -moz-none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - -moz-transition: background, 0.25s; - -o-transition: background, 0.25s; - -webkit-transition: background, 0.25s; - transition: background, 0.25s; - text-shadow: none; - color: #80dfff; } - /* line 274, ../../../../general/res/sass/_mixins.scss */ - .items-holder .item.grid-item.selected .icon { - color: #eee; } - /* line 137, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item.selected .item-type, .items-holder .item.grid-item.selected .top-bar .icon:not(.alert) { - color: #80dfff; } - /* line 138, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item.selected .item-main .item-open { - color: #80dfff; } - /* line 139, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item.selected .title { - color: white; } - /* line 141, ../../../../general/res/sass/items/_item.scss */ - .items-holder .item.grid-item.selected:hover .item-main .item-type { - color: white !important; } - -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 29, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item { - width: 100%; } - /* line 33, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item > .contents { - top: 0px; - right: 10px; - bottom: 0px; - left: 10px; } - /* line 37, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .bar.top-bar { - bottom: 0 !important; - left: auto !important; - right: 20px !important; - width: 40px !important; - height: auto !important; - text-align: right; } - /* line 44, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .bar.bottom-bar { - left: 40px; - right: 60px; } - /* line 52, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .item-main .item-type { - font-size: 30px; - right: auto; - bottom: auto; - left: 0; - line-height: 100%; - text-align: left; - width: 30px; } - /* line 61, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .item-main .item-type .l-icon-link { - bottom: 0; } - /* line 65, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .item-main .item-open { - display: block; - opacity: 1; - font-size: 1em; - width: auto; } } -@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 29, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item { - height: 50px; } - /* line 78, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .bar.top-bar { - line-height: 50px !important; } - /* line 82, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .bar.bottom-bar { - top: 7px; - bottom: auto; - height: 35px; } - /* line 87, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .item-main .item-type { - top: 10px; - bottom: auto; - height: 30px; } - /* line 90, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .item-main .item-open { - line-height: 50px; } } -@media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { - /* line 29, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item { - height: 66px; } - /* line 100, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .bar.top-bar { - line-height: 66px !important; } - /* line 104, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .bar.bottom-bar { - top: 15px; - bottom: auto; - height: 35px; } - /* line 109, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .item-main .item-type { - top: 18px; - bottom: auto; - height: 30px; } - /* line 112, ../../../../general/res/sass/mobile/_item.scss */ - .items-holder .item.grid-item .item-main .item-open { - line-height: 66px; } } - -/********************************* TO BE MOVED */ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -/* line 22, ../../../../general/res/sass/_autoflow.scss */ -.autoflow { - font-size: 0.75rem; } - /* line 32, ../../../../general/res/sass/_autoflow.scss */ - .autoflow:hover .l-autoflow-header .s-btn.change-column-width, .autoflow:hover .l-autoflow-header .change-column-width.s-menu-btn { - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 50ms; - -o-transition-duration: 50ms; - -webkit-transition-duration: 50ms; - transition-duration: 50ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - opacity: 1; } - /* line 40, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-header { - bottom: auto; - height: 22px; - line-height: 22px; - min-width: 225px; } - /* line 45, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-header span { - vertical-align: middle; } - /* line 48, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-header .s-btn.change-column-width, .autoflow .l-autoflow-header .change-column-width.s-menu-btn { - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 500ms; - -o-transition-duration: 500ms; - -webkit-transition-duration: 500ms; - transition-duration: 500ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - opacity: 0; } - /* line 52, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-header .l-filter { - margin-left: 5px; } - /* line 54, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-header .l-filter input.t-filter-input { - width: 100px; } - /* line 60, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items { - overflow-x: scroll; - overflow-y: hidden; - top: 32px; - white-space: nowrap; } - /* line 66, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - border-left: 1px solid rgba(102, 102, 102, 0.2); - display: inline-block; - padding-left: 5px; - padding-right: 5px; - vertical-align: top; - width: 225px; } - /* line 76, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); - display: block; - height: 15px; - line-height: 15px; - margin-bottom: 1px; - margin-top: 1px; - position: relative; } - /* line 85, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row:first-child { - border-top: none; } - /* line 88, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row:hover { - background: rgba(255, 255, 255, 0.1); } - /* line 93, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row.s-stale .l-autoflow-item.l { - color: rgba(51, 51, 51, 0.3) !important; - font-style: italic; } - /* line 94, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row.s-stale .l-autoflow-item.r { - color: rgba(51, 51, 51, 0.5) !important; - font-style: italic; } - /* line 97, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row:not(.s-stale) .l-autoflow-item.r { - color: gray; } - /* line 101, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row.first-in-group { - border-top: 1px solid rgba(153, 153, 153, 0.2); } - /* line 104, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row .l-autoflow-item { - display: block; } - /* line 106, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row .l-autoflow-item.l { - float: none; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: auto; } - /* line 113, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row .l-autoflow-item.r { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border-radius: 3px; - float: right; - margin-left: 5px; - padding-left: 5px; - padding-right: 5px; - text-align: right; } - /* line 124, ../../../../general/res/sass/_autoflow.scss */ - .autoflow .l-autoflow-items .l-autoflow-col:first-child { - border-left: none; - padding-left: 0; } - -/* line 1, ../../../../general/res/sass/features/_imagery.scss */ -.l-image-main-wrapper, -.l-image-main, -.l-image-main-controlbar, -.l-image-thumbs-wrapper { - overflow: false; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - width: auto; - height: auto; } - -/*************************************** MAIN LAYOUT */ -/* line 9, ../../../../general/res/sass/features/_imagery.scss */ -.l-image-main-wrapper { - min-height: 100px; - min-width: 150px; } - /* line 16, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-wrapper .l-image-main { - background-color: rgba(0, 0, 0, 0.05); - bottom: 30px; } - /* line 20, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-wrapper .l-image-main-controlbar { - top: auto; - height: 25px; } - -/* line 26, ../../../../general/res/sass/features/_imagery.scss */ -.l-image-thumbs-wrapper { - top: auto; - height: 168px; } - -/* line 32, ../../../../general/res/sass/features/_imagery.scss */ -.l-date, -.l-time, -.l-timezone { - display: inline-block; } - -/*************************************** MAIN IMAGE */ -/* line 40, ../../../../general/res/sass/features/_imagery.scss */ -.l-image-main, -.l-image-thumb-item .l-thumb { - background-size: contain; - background-position: center; - background-repeat: no-repeat; } - -/* line 51, ../../../../general/res/sass/features/_imagery.scss */ -.l-image-main-controlbar { - font-size: 0.8em; - line-height: 25px; } - /* line 55, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-controlbar .left, .l-image-main-controlbar .right { - direction: rtl; - overflow: hidden; } - /* line 59, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-controlbar .left { - text-align: left; } - /* line 63, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-controlbar .right { - z-index: 2; } - /* line 67, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-controlbar .l-date, - .l-image-main-controlbar .l-time { - color: #333333; } - /* line 71, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-controlbar .l-mag { - direction: ltr; - display: inline-block; } - /* line 75, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-controlbar .l-mag:before { - content: "\000049"; } - /* line 79, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-controlbar .s-mag { - color: #999999; } - /* line 82, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-main-controlbar .l-btn.show-thumbs { - display: none; } - -/* line 87, ../../../../general/res/sass/features/_imagery.scss */ -.s-image-main { - border: 1px solid transparent; } - /* line 89, ../../../../general/res/sass/features/_imagery.scss */ - .s-image-main.paused { - border-color: #ff9900; } - -/*************************************** THUMBS */ -/* line 96, ../../../../general/res/sass/features/_imagery.scss */ -.l-image-thumbs-wrapper { - direction: rtl; - overflow-x: auto; - overflow-y: hidden; - padding-bottom: 5px; - white-space: nowrap; - z-index: 70; } - -/* line 106, ../../../../general/res/sass/features/_imagery.scss */ -.l-image-thumb-item { - -moz-transition: background-color 0.25s; - -o-transition: background-color 0.25s; - -webkit-transition: background-color 0.25s; - transition: background-color 0.25s; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - padding: 1px; - position: relative; - cursor: pointer; - direction: ltr; - display: inline-block; - font-size: 0.8em; - margin-left: 3px; - text-align: left; - width: 122px; - white-space: normal; } - /* line 111, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-thumb-item .l-thumb, - .l-image-thumb-item .l-date, - .l-image-thumb-item .l-time { - display: inline-block; } - /* line 116, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-thumb-item .l-date, - .l-image-thumb-item .l-time { - padding: 2px 3px; } - /* line 128, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-thumb-item:hover { - background: rgba(255, 255, 255, 0.2); } - /* line 130, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-thumb-item:hover .l-date, - .l-image-thumb-item:hover .l-time { - color: #fff; } - /* line 135, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-thumb-item.selected { - background: #0099cc; } - /* line 137, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-thumb-item.selected .l-date, - .l-image-thumb-item.selected .l-time { - color: #fff; } - /* line 142, ../../../../general/res/sass/features/_imagery.scss */ - .l-image-thumb-item .l-thumb { - background-color: rgba(255, 255, 255, 0.1); - height: 120px; - width: 120px; - margin-top: 0; } - -/*************************************** WHEN IN FRAME */ -/* line 152, ../../../../general/res/sass/features/_imagery.scss */ -.frame .t-imagery .l-image-main-wrapper { - bottom: 0; } - /* line 154, ../../../../general/res/sass/features/_imagery.scss */ - .frame .t-imagery .l-image-main-wrapper .l-image-main-controlbar { - font-size: 0.7em; } -/* line 163, ../../../../general/res/sass/features/_imagery.scss */ -.frame .t-imagery .l-image-thumbs-wrapper { - display: none; } - -/* line 5, ../../../../general/res/sass/features/_time-display.scss */ -.l-time-display:hover .l-btn.control { - opacity: 1; } -/* line 9, ../../../../general/res/sass/features/_time-display.scss */ -.l-time-display .l-elem-wrapper { - position: relative; } -/* line 12, ../../../../general/res/sass/features/_time-display.scss */ -.l-time-display .l-elem { - display: inline-block; } -/* line 17, ../../../../general/res/sass/features/_time-display.scss */ -.l-time-display.l-timer .l-elem.l-value { - -moz-transition-property: left; - -o-transition-property: left; - -webkit-transition-property: left; - transition-property: left; - -moz-transition-duration: 200ms; - -o-transition-duration: 200ms; - -webkit-transition-duration: 200ms; - transition-duration: 200ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - position: absolute; - left: 0; - z-index: 1; } - /* line 22, ../../../../general/res/sass/features/_time-display.scss */ - .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .direction.s-icon-btn, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { - font-size: 0.8em; } -/* line 26, ../../../../general/res/sass/features/_time-display.scss */ -.l-time-display.l-timer:hover .l-elem.l-value { - left: 20px; } -/* line 33, ../../../../general/res/sass/features/_time-display.scss */ -.l-time-display .l-elem .value.active, .l-time-display .l-elem.value.active { - color: #fff; } -/* line 38, ../../../../general/res/sass/features/_time-display.scss */ -.l-time-display .l-btn.control { - -moz-transition-property: opacity, background-color, border-color, color; - -o-transition-property: opacity, background-color, border-color, color; - -webkit-transition-property: opacity, background-color, border-color, color; - transition-property: opacity, background-color, border-color, color; - -moz-transition-duration: 200ms; - -o-transition-duration: 200ms; - -webkit-transition-duration: 200ms; - transition-duration: 200ms; - -moz-transition-timing-function: ease-in-out; - -o-transition-timing-function: ease-in-out; - -webkit-transition-timing-function: ease-in-out; - transition-timing-function: ease-in-out; - -moz-transition-delay: 0; - -o-transition-delay: 0; - -webkit-transition-delay: 0; - transition-delay: 0; - opacity: 0; - font-size: 0.65em; - vertical-align: top; } - -/* line 3, ../sass/_controls.scss */ -.s-btn.major .title-label, .major.s-menu-btn .title-label { - text-transform: uppercase; } +/* +Error: Undefined variable: "$colorInspectorSectionHeaderBg". + on line 45 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_inspector.scss + from line 33 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_main.scss + from line 36 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/themes/snow/res/sass/theme-snow.scss + +Backtrace: +/Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_inspector.scss:45 +/Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_main.scss:33 +/Users/iMac/dev/nasa/wtd-dev/platform/commonUI/themes/snow/res/sass/theme-snow.scss:36 +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/script/tree/variable.rb:49:in `_perform' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/script/tree/node.rb:50:in `perform' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:394:in `visit_prop' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `block (2 levels) in visit_rule' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `map' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `block in visit_rule' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:179:in `with_environment' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:428:in `visit_rule' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `block (2 levels) in visit_rule' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `map' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `block in visit_rule' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:179:in `with_environment' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:428:in `visit_rule' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `block (2 levels) in visit_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `map' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `block in visit_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:88:in `block in with_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:88:in `with_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:322:in `visit_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `block (2 levels) in visit_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `map' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `block in visit_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:88:in `block in with_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:88:in `with_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:322:in `visit_import' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:52:in `block in visit_children' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:52:in `map' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:52:in `visit_children' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:167:in `block in visit_children' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:179:in `with_environment' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:166:in `visit_children' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `block in visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:186:in `visit_root' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:157:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:8:in `visit' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/root_node.rb:36:in `css_tree' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/root_node.rb:20:in `render' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/engine.rb:268:in `render' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/plugin/compiler.rb:492:in `update_stylesheet' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/plugin/compiler.rb:215:in `block in update_stylesheets' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/plugin/compiler.rb:209:in `each' +/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/plugin/compiler.rb:209:in `update_stylesheets' +/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/sass_compiler.rb:40:in `compile!' +/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/commands/update_project.rb:49:in `perform' +/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/commands/base.rb:18:in `execute' +/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/commands/project_base.rb:19:in `execute' +/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/exec/sub_command_ui.rb:43:in `perform!' +/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/exec/sub_command_ui.rb:15:in `run!' +/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/bin/compass:30:in `block in ' +/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/bin/compass:44:in `call' +/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/bin/compass:44:in `' +/usr/bin/compass:23:in `load' +/usr/bin/compass:23:in `
    ' +*/ +body:before { + white-space: pre; + font-family: monospace; + content: "Error: Undefined variable: \"$colorInspectorSectionHeaderBg\".\A on line 45 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_inspector.scss\A from line 33 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_main.scss\A from line 36 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/themes/snow/res/sass/theme-snow.scss"; } From 29bdc9d57407c1aa7cb7a8549b0f4e70edf9dd2f Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 13:04:06 -0700 Subject: [PATCH 143/488] [Plot] Ignore empty lines Ignore empty lines (plot lines with no data) when determining domain extrema; avoids failure to draw multiple plot lines in a telemetry panel, nasa/openmctweb#150. --- .../features/plot/src/elements/PlotUpdater.js | 4 ++- .../plot/test/elements/PlotUpdaterSpec.js | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index d4b4ad3eec..53cfd1c4c4 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -159,7 +159,9 @@ define( // Update dimensions and origin based on extrema of plots PlotUpdater.prototype.updateBounds = function () { - var bufferArray = this.bufferArray, + var bufferArray = this.bufferArray.filter(function (lineBuffer) { + return lineBuffer.getLength() > 0; // Ignore empty lines + }), priorDomainOrigin = this.origin[0], priorDomainDimensions = this.dimensions[0]; diff --git a/platform/features/plot/test/elements/PlotUpdaterSpec.js b/platform/features/plot/test/elements/PlotUpdaterSpec.js index 9d7ab563de..c287dbfdf8 100644 --- a/platform/features/plot/test/elements/PlotUpdaterSpec.js +++ b/platform/features/plot/test/elements/PlotUpdaterSpec.js @@ -202,6 +202,38 @@ define( expect(updater.getDimensions()[1]).toBeGreaterThan(20); }); + describe("when no data is initially available", function () { + beforeEach(function () { + testDomainValues = {}; + testRangeValues = {}; + updater = new PlotUpdater( + mockSubscription, + testDomain, + testRange, + 1350 // Smaller max size for easier testing + ); + }); + + it("has no line data", function () { + // Either no lines, or empty lines are fine + expect(updater.getLineBuffers().map(function (lineBuffer) { + return lineBuffer.getLength(); + }).reduce(function (a, b) { + return a + b; + }, 0)).toEqual(0); + }); + + it("determines initial domain bounds from first available data", function () { + testDomainValues.a = 123; + testRangeValues.a = 456; + updater.update(); + expect(updater.getOrigin()[0]).toEqual(jasmine.any(Number)); + expect(updater.getOrigin()[1]).toEqual(jasmine.any(Number)); + expect(isNaN(updater.getOrigin()[0])).toBeFalsy(); + expect(isNaN(updater.getOrigin()[1])).toBeFalsy(); + }); + }); + }); } ); From 307047d3acf96ad66e2e30aa58f26ac391b071cb Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 13:44:39 -0700 Subject: [PATCH 144/488] [Clocks/Timers] Remove namespacing from specs WTD-1239 --- .../features/clock/test/actions/StartTimerActionSpec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/features/clock/test/actions/StartTimerActionSpec.js b/platform/features/clock/test/actions/StartTimerActionSpec.js index 10f60595d5..5d3c9b8e16 100644 --- a/platform/features/clock/test/actions/StartTimerActionSpec.js +++ b/platform/features/clock/test/actions/StartTimerActionSpec.js @@ -59,18 +59,18 @@ define( }); it("applies only to timers without a target time", function () { - testModel.type = 'warp.timer'; + testModel.type = 'timer'; testModel.timestamp = 12000; expect(StartTimerAction.appliesTo(testContext)).toBeFalsy(); - testModel.type = 'warp.timer'; + testModel.type = 'timer'; testModel.timestamp = undefined; expect(StartTimerAction.appliesTo(testContext)).toBeTruthy(); - testModel.type = 'warp.clock'; + testModel.type = 'clock'; testModel.timestamp = 12000; expect(StartTimerAction.appliesTo(testContext)).toBeFalsy(); }); }); } -); \ No newline at end of file +); From 8159c365b57c98af582ebdb2a3e56b5475e1b7fc Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 13:45:54 -0700 Subject: [PATCH 145/488] [Timeline] Remove namespacing from README WTD-1239 --- platform/features/timeline/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/features/timeline/README.md b/platform/features/timeline/README.md index 5dd9561c2c..38439df43e 100644 --- a/platform/features/timeline/README.md +++ b/platform/features/timeline/README.md @@ -16,7 +16,7 @@ A timeline's model looks like: ``` { - "type": "warp.timeline", + "type": "timeline", "start": { "timestamp": (milliseconds since epoch), "epoch": (currently, always "SET") @@ -35,7 +35,7 @@ An activity's model looks like: ``` { - "type": "warp.activity", + "type": "activity", "start": { "timestamp": (milliseconds since epoch), "epoch": (currently, always "SET") @@ -61,7 +61,7 @@ An activity mode's model looks like: ``` { - "type": "warp.mode", + "type": "mode", "resources": { "comms": (communications utilization, in Kbps) "power": (power utilization, in watts) From 19ad4c8174a7475102d7977fc34e7a3df5a80d98 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 13:48:07 -0700 Subject: [PATCH 146/488] [Timelines] Change namespacing for directives WTD-1239 --- platform/features/timeline/bundle.json | 4 ++-- .../{WARPSwimlaneDrag.js => MCTSwimlaneDrag.js} | 6 +++--- .../{WARPSwimlaneDrop.js => MCTSwimlaneDrop.js} | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) rename platform/features/timeline/src/directives/{WARPSwimlaneDrag.js => MCTSwimlaneDrag.js} (90%) rename platform/features/timeline/src/directives/{WARPSwimlaneDrop.js => MCTSwimlaneDrop.js} (92%) diff --git a/platform/features/timeline/bundle.json b/platform/features/timeline/bundle.json index 6cf2f9be8d..28080de247 100644 --- a/platform/features/timeline/bundle.json +++ b/platform/features/timeline/bundle.json @@ -335,12 +335,12 @@ "directives": [ { "key": "mctSwimlaneDrop", - "implementation": "directives/WARPSwimlaneDrop.js", + "implementation": "directives/MCTSwimlaneDrop.js", "depends": [ "dndService" ] }, { "key": "mctSwimlaneDrag", - "implementation": "directives/WARPSwimlaneDrag.js", + "implementation": "directives/MCTSwimlaneDrag.js", "depends": [ "dndService" ] } ], diff --git a/platform/features/timeline/src/directives/WARPSwimlaneDrag.js b/platform/features/timeline/src/directives/MCTSwimlaneDrag.js similarity index 90% rename from platform/features/timeline/src/directives/WARPSwimlaneDrag.js rename to platform/features/timeline/src/directives/MCTSwimlaneDrag.js index cd0aed252e..87bd829adf 100644 --- a/platform/features/timeline/src/directives/WARPSwimlaneDrag.js +++ b/platform/features/timeline/src/directives/MCTSwimlaneDrag.js @@ -6,14 +6,14 @@ define( "use strict"; /** - * Defines the `warp-swimlane-drag` directive. When a drag is initiated + * Defines the `mct-swimlane-drag` directive. When a drag is initiated * form an element with this attribute, the swimlane being dragged * (identified by the value of this attribute, as an Angular expression) * will be exported to the `dndService` as part of the active drag-drop * state. * @param {DndService} dndService drag-and-drop service */ - function WARPSwimlaneDrag(dndService) { + function MCTSwimlaneDrag(dndService) { function link(scope, element, attrs) { // Look up the swimlane from the provided expression function swimlane() { @@ -42,6 +42,6 @@ define( }; } - return WARPSwimlaneDrag; + return MCTSwimlaneDrag; } ); diff --git a/platform/features/timeline/src/directives/WARPSwimlaneDrop.js b/platform/features/timeline/src/directives/MCTSwimlaneDrop.js similarity index 92% rename from platform/features/timeline/src/directives/WARPSwimlaneDrop.js rename to platform/features/timeline/src/directives/MCTSwimlaneDrop.js index 916fb82bd7..980191bac8 100644 --- a/platform/features/timeline/src/directives/WARPSwimlaneDrop.js +++ b/platform/features/timeline/src/directives/MCTSwimlaneDrop.js @@ -6,14 +6,14 @@ define( "use strict"; /** - * Defines the `warp-swimlane-drop` directive. When a drop occurs + * Defines the `mct-swimlane-drop` directive. When a drop occurs * on an element with this attribute, the swimlane targeted by the drop * (identified by the value of this attribute, as an Angular expression) * will receive the dropped domain object (at which point it can handle * the drop, typically by inserting/reordering.) * @param {DndService} dndService drag-and-drop service */ - function WARPSwimlaneDrop(dndService) { + function MCTSwimlaneDrop(dndService) { // Handle dragover events function dragOver(e, element, swimlane) { @@ -60,7 +60,7 @@ define( SwimlaneDragConstants.MCT_DRAG_TYPE ), draggedSwimlane = dndService.getData( - SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE + SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE ); if (id) { @@ -76,7 +76,7 @@ define( function link(scope, element, attrs) { // Lookup swimlane by evaluating this attribute function swimlane() { - return scope.$eval(attrs.warpSwimlaneDrop); + return scope.$eval(attrs.mctSwimlaneDrop); } // Handle dragover element.on('dragover', function (e) { @@ -101,6 +101,6 @@ define( }; } - return WARPSwimlaneDrop; + return MCTSwimlaneDrop; } -); \ No newline at end of file +); From 3e0534c4c3fe329eb69648dfdd0a5072920a523c Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 13:49:46 -0700 Subject: [PATCH 147/488] [Timeline] Update namespacing in templates WTD-1239 --- .../res/templates/tabular-swimlane-cols-tree.html | 14 +++++++------- .../features/timeline/res/templates/timeline.html | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/platform/features/timeline/res/templates/tabular-swimlane-cols-tree.html b/platform/features/timeline/res/templates/tabular-swimlane-cols-tree.html index de071ed4f8..98adf7868e 100644 --- a/platform/features/timeline/res/templates/tabular-swimlane-cols-tree.html +++ b/platform/features/timeline/res/templates/tabular-swimlane-cols-tree.html @@ -1,12 +1,12 @@
    -
    +
    + > è - + mct-swimlane-drag="ngModel"> -
    -
    \ No newline at end of file +
    +
diff --git a/platform/features/timeline/res/templates/timeline.html b/platform/features/timeline/res/templates/timeline.html index f0e29c577f..7a00345fef 100644 --- a/platform/features/timeline/res/templates/timeline.html +++ b/platform/features/timeline/res/templates/timeline.html @@ -133,9 +133,9 @@ 'drop-after': swimlane.highlightBottom() }" ng-click="selection.select(swimlane)" - warp-swimlane-drop="swimlane"> + mct-swimlane-drop="swimlane"> - Date: Fri, 23 Oct 2015 13:56:34 -0700 Subject: [PATCH 150/488] [Timelines] De-namespace timeline's DateTimeController --- platform/features/timeline/bundle.json | 4 ++-- .../features/timeline/res/templates/controls/datetime.html | 4 ++-- ...ARPDateTimeController.js => TimelineDateTimeController.js} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename platform/features/timeline/src/controllers/{WARPDateTimeController.js => TimelineDateTimeController.js} (100%) diff --git a/platform/features/timeline/bundle.json b/platform/features/timeline/bundle.json index 28080de247..d5ceca3c9f 100644 --- a/platform/features/timeline/bundle.json +++ b/platform/features/timeline/bundle.json @@ -278,8 +278,8 @@ "depends": [ "$scope", "resources[]" ] }, { - "key": "WARPDateTimeController", - "implementation": "controllers/WARPDateTimeController.js", + "key": "TimelineDateTimeController", + "implementation": "controllers/TimelineDateTimeController.js", "depends": [ "$scope" ] }, { diff --git a/platform/features/timeline/res/templates/controls/datetime.html b/platform/features/timeline/res/templates/controls/datetime.html index 65386ed3ef..a017c7fa8a 100644 --- a/platform/features/timeline/res/templates/controls/datetime.html +++ b/platform/features/timeline/res/templates/controls/datetime.html @@ -9,7 +9,7 @@
-
+
-
\ No newline at end of file +
diff --git a/platform/features/timeline/src/controllers/WARPDateTimeController.js b/platform/features/timeline/src/controllers/TimelineDateTimeController.js similarity index 100% rename from platform/features/timeline/src/controllers/WARPDateTimeController.js rename to platform/features/timeline/src/controllers/TimelineDateTimeController.js From 26db524f0e386b87d4e0f550e548288e538a27f4 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 13:57:15 -0700 Subject: [PATCH 151/488] [Timelines] Rename bundle --- platform/features/timeline/bundle.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/features/timeline/bundle.json b/platform/features/timeline/bundle.json index d5ceca3c9f..4bd6c9dd7c 100644 --- a/platform/features/timeline/bundle.json +++ b/platform/features/timeline/bundle.json @@ -1,5 +1,5 @@ { - "name": "WARP Timeline", + "name": "Timelines", "description": "Resources, templates, CSS, and code for Timelines.", "resources": "res", "extensions": { From fffe07e7e631fda9e0bcbd234b68f00441047ecb Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 14:06:55 -0700 Subject: [PATCH 152/488] [Timeline] Remove namespacing from specs --- .../ActivityTimespanCapabilitySpec.js | 4 ++-- .../test/capabilities/CostCapabilitySpec.js | 8 ++++---- .../test/capabilities/GraphCapabilitySpec.js | 8 ++++---- .../TimelineTimespanCapabilitySpec.js | 4 ++-- .../capabilities/UtilizationCapabilitySpec.js | 6 +++--- ...Spec.js => TimelineDateTimeControllerSpec.js} | 6 +++--- .../drag/TimelineDragHandleFactorySpec.js | 4 ++-- .../swimlane/TimelineSwimlaneDecoratorSpec.js | 6 +++--- ...wimlaneDragSpec.js => MCTSwimlaneDragSpec.js} | 16 ++++++++-------- ...wimlaneDropSpec.js => MCTSwimlaneDropSpec.js} | 12 ++++++------ .../test/directives/SwimlaneDragConstantsSpec.js | 4 ++-- platform/features/timeline/test/suite.json | 6 +++--- 12 files changed, 42 insertions(+), 42 deletions(-) rename platform/features/timeline/test/controllers/{WARPDateTimeControllerSpec.js => TimelineDateTimeControllerSpec.js} (92%) rename platform/features/timeline/test/directives/{WARPSwimlaneDragSpec.js => MCTSwimlaneDragSpec.js} (82%) rename platform/features/timeline/test/directives/{WARPSwimlaneDropSpec.js => MCTSwimlaneDropSpec.js} (94%) diff --git a/platform/features/timeline/test/capabilities/ActivityTimespanCapabilitySpec.js b/platform/features/timeline/test/capabilities/ActivityTimespanCapabilitySpec.js index 9728fd2181..7874424975 100644 --- a/platform/features/timeline/test/capabilities/ActivityTimespanCapabilitySpec.js +++ b/platform/features/timeline/test/capabilities/ActivityTimespanCapabilitySpec.js @@ -44,7 +44,7 @@ define( it("applies only to activity objects", function () { expect(ActivityTimespanCapability.appliesTo({ - type: 'warp.activity' + type: 'activity' })).toBeTruthy(); expect(ActivityTimespanCapability.appliesTo({ type: 'folder' @@ -68,4 +68,4 @@ define( }); }); } -); \ No newline at end of file +); diff --git a/platform/features/timeline/test/capabilities/CostCapabilitySpec.js b/platform/features/timeline/test/capabilities/CostCapabilitySpec.js index 1d9ddbc0e7..15edbf5df5 100644 --- a/platform/features/timeline/test/capabilities/CostCapabilitySpec.js +++ b/platform/features/timeline/test/capabilities/CostCapabilitySpec.js @@ -45,16 +45,16 @@ define( it("applies to subsystem modes", function () { expect(CostCapability.appliesTo({ - type: "warp.mode" + type: "mode" })).toBeTruthy(); expect(CostCapability.appliesTo({ - type: "warp.activity" + type: "activity" })).toBeFalsy(); expect(CostCapability.appliesTo({ - type: "warp.other" + type: "other" })).toBeFalsy(); }); }); } -); \ No newline at end of file +); diff --git a/platform/features/timeline/test/capabilities/GraphCapabilitySpec.js b/platform/features/timeline/test/capabilities/GraphCapabilitySpec.js index 0ec3c7f58c..977a5ee766 100644 --- a/platform/features/timeline/test/capabilities/GraphCapabilitySpec.js +++ b/platform/features/timeline/test/capabilities/GraphCapabilitySpec.js @@ -27,7 +27,7 @@ define( ); testModel = { - type: "warp.activity", + type: "activity", resources: { abc: 100, xyz: 42 @@ -45,7 +45,7 @@ define( it("is applicable to timelines", function () { expect(GraphCapability.appliesTo({ - type: "warp.timeline" + type: "timeline" })).toBeTruthy(); }); @@ -82,7 +82,7 @@ define( it("provides a battery graph for timelines with capacity", function () { var mockCallback = jasmine.createSpy('callback'); testModel.capacity = 1000; - testModel.type = "warp.timeline"; + testModel.type = "timeline"; mockDomainObject.useCapability.andReturn(asPromise([ { key: "power", start: 0, end: 15 } ])); @@ -95,4 +95,4 @@ define( }); } -); \ No newline at end of file +); diff --git a/platform/features/timeline/test/capabilities/TimelineTimespanCapabilitySpec.js b/platform/features/timeline/test/capabilities/TimelineTimespanCapabilitySpec.js index 2208ec0002..564f15c532 100644 --- a/platform/features/timeline/test/capabilities/TimelineTimespanCapabilitySpec.js +++ b/platform/features/timeline/test/capabilities/TimelineTimespanCapabilitySpec.js @@ -84,7 +84,7 @@ define( it("applies only to timeline objects", function () { expect(TimelineTimespanCapability.appliesTo({ - type: 'warp.timeline' + type: 'timeline' })).toBeTruthy(); expect(TimelineTimespanCapability.appliesTo({ type: 'folder' @@ -112,4 +112,4 @@ define( }); }); } -); \ No newline at end of file +); diff --git a/platform/features/timeline/test/capabilities/UtilizationCapabilitySpec.js b/platform/features/timeline/test/capabilities/UtilizationCapabilitySpec.js index e9693d89db..2453910250 100644 --- a/platform/features/timeline/test/capabilities/UtilizationCapabilitySpec.js +++ b/platform/features/timeline/test/capabilities/UtilizationCapabilitySpec.js @@ -78,7 +78,7 @@ define( mockCallback = jasmine.createSpy('callback'); testModel = { - type: "warp.activity", + type: "activity", resources: { abc: 100, xyz: 42 @@ -107,7 +107,7 @@ define( it("is applicable to timelines", function () { expect(UtilizationCapability.appliesTo({ - type: "warp.timeline" + type: "timeline" })).toBeTruthy(); }); @@ -192,4 +192,4 @@ define( }); } -); \ No newline at end of file +); diff --git a/platform/features/timeline/test/controllers/WARPDateTimeControllerSpec.js b/platform/features/timeline/test/controllers/TimelineDateTimeControllerSpec.js similarity index 92% rename from platform/features/timeline/test/controllers/WARPDateTimeControllerSpec.js rename to platform/features/timeline/test/controllers/TimelineDateTimeControllerSpec.js index fadd6b5bf0..613e713c94 100644 --- a/platform/features/timeline/test/controllers/WARPDateTimeControllerSpec.js +++ b/platform/features/timeline/test/controllers/TimelineDateTimeControllerSpec.js @@ -2,8 +2,8 @@ /*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ define( - ["../../src/controllers/WARPDateTimeController"], - function (WARPDateTimeController) { + ["../../src/controllers/TimelineDateTimeController"], + function (TimelineDateTimeController) { "use strict"; describe("The date-time controller for timeline creation", function () { @@ -14,7 +14,7 @@ define( mockScope = jasmine.createSpyObj('$scope', ['$watchCollection']); mockScope.field = 'testField'; mockScope.ngModel = { testField: { timestamp: 0, epoch: "SET" } }; - controller = new WARPDateTimeController(mockScope); + controller = new TimelineDateTimeController(mockScope); }); diff --git a/platform/features/timeline/test/controllers/drag/TimelineDragHandleFactorySpec.js b/platform/features/timeline/test/controllers/drag/TimelineDragHandleFactorySpec.js index fc577ba782..e49a8c17c3 100644 --- a/platform/features/timeline/test/controllers/drag/TimelineDragHandleFactorySpec.js +++ b/platform/features/timeline/test/controllers/drag/TimelineDragHandleFactorySpec.js @@ -50,13 +50,13 @@ define( }); it("provides three handles for activities", function () { - testType = "warp.activity"; + testType = "activity"; expect(factory.handles(mockDomainObject).length) .toEqual(3); }); it("provides two handles for timelines", function () { - testType = "warp.timeline"; + testType = "timeline"; expect(factory.handles(mockDomainObject).length) .toEqual(2); }); diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js index ba4b72dca2..2d49ad0e31 100644 --- a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js +++ b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js @@ -61,13 +61,13 @@ define( it("adds a 'modes' getter-setter to activities", function () { expect(mockSwimlane.modes).toEqual(jasmine.any(Function)); expect(mockCapabilities.type.instanceOf) - .toHaveBeenCalledWith('warp.activity'); + .toHaveBeenCalledWith('activity'); }); it("adds a 'link' getter-setter to activities", function () { expect(mockSwimlane.link).toEqual(jasmine.any(Function)); expect(mockCapabilities.type.instanceOf) - .toHaveBeenCalledWith('warp.activity'); + .toHaveBeenCalledWith('activity'); }); it("gets modes from the domain object model", function () { @@ -157,4 +157,4 @@ define( }); } -); \ No newline at end of file +); diff --git a/platform/features/timeline/test/directives/WARPSwimlaneDragSpec.js b/platform/features/timeline/test/directives/MCTSwimlaneDragSpec.js similarity index 82% rename from platform/features/timeline/test/directives/WARPSwimlaneDragSpec.js rename to platform/features/timeline/test/directives/MCTSwimlaneDragSpec.js index 360fff4b69..28f060dc6c 100644 --- a/platform/features/timeline/test/directives/WARPSwimlaneDragSpec.js +++ b/platform/features/timeline/test/directives/MCTSwimlaneDragSpec.js @@ -1,11 +1,11 @@ /*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ define( - ['../../src/directives/WARPSwimlaneDrag', '../../src/directives/SwimlaneDragConstants'], - function (WARPSwimlaneDrag, SwimlaneDragConstants) { + ['../../src/directives/MCTSwimlaneDrag', '../../src/directives/SwimlaneDragConstants'], + function (MCTSwimlaneDrag, SwimlaneDragConstants) { "use strict"; - describe("The warp-swimlane-drag directive", function () { + describe("The mct-swimlane-drag directive", function () { var mockDndService, mockScope, mockElement, @@ -26,14 +26,14 @@ define( ); mockScope = jasmine.createSpyObj('$scope', ['$eval']); mockElement = jasmine.createSpyObj('element', ['on']); - testAttrs = { warpSwimlaneDrag: "someTestExpr" }; + testAttrs = { mctSwimlaneDrag: "someTestExpr" }; // Simulate evaluation of expressions in scope mockScope.$eval.andCallFake(function (expr) { return scopeExprs[expr]; }); - directive = new WARPSwimlaneDrag(mockDndService); + directive = new MCTSwimlaneDrag(mockDndService); // Run the link function, then capture the event handlers // for testing. @@ -56,7 +56,7 @@ define( handlers.dragstart(); // Should have exposed the swimlane expect(mockDndService.setData).toHaveBeenCalledWith( - SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE, + SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE, "some swimlane" ); }); @@ -68,9 +68,9 @@ define( handlers.dragend(); // Should have exposed the swimlane expect(mockDndService.removeData).toHaveBeenCalledWith( - SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE + SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE ); }); }); } -); \ No newline at end of file +); diff --git a/platform/features/timeline/test/directives/WARPSwimlaneDropSpec.js b/platform/features/timeline/test/directives/MCTSwimlaneDropSpec.js similarity index 94% rename from platform/features/timeline/test/directives/WARPSwimlaneDropSpec.js rename to platform/features/timeline/test/directives/MCTSwimlaneDropSpec.js index f1f0858503..b8a9c1620c 100644 --- a/platform/features/timeline/test/directives/WARPSwimlaneDropSpec.js +++ b/platform/features/timeline/test/directives/MCTSwimlaneDropSpec.js @@ -1,14 +1,14 @@ /*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/ define( - ['../../src/directives/WARPSwimlaneDrop'], - function (WARPSwimlaneDrop) { + ['../../src/directives/MCTSwimlaneDrop'], + function (MCTSwimlaneDrop) { "use strict"; var TEST_HEIGHT = 100, TEST_TOP = 600; - describe("The warp-swimlane-drop directive", function () { + describe("The mct-swimlane-drop directive", function () { var mockDndService, mockScope, mockElement, @@ -36,7 +36,7 @@ define( ); mockScope = jasmine.createSpyObj('$scope', ['$eval']); mockElement = jasmine.createSpyObj('element', ['on']); - testAttrs = { warpSwimlaneDrop: "mockSwimlane" }; + testAttrs = { mctSwimlaneDrop: "mockSwimlane" }; mockSwimlane = jasmine.createSpyObj( "swimlane", [ "allowDropIn", "allowDropAfter", "drop", "highlight", "highlightBottom" ] @@ -72,7 +72,7 @@ define( testEvent.dataTransfer.getData.andReturn('abc'); mockDndService.getData.andReturn({ domainObject: 'someDomainObject' }); - directive = new WARPSwimlaneDrop(mockDndService); + directive = new MCTSwimlaneDrop(mockDndService); // Run the link function, then capture the event handlers // for testing. @@ -144,4 +144,4 @@ define( }); }); } -); \ No newline at end of file +); diff --git a/platform/features/timeline/test/directives/SwimlaneDragConstantsSpec.js b/platform/features/timeline/test/directives/SwimlaneDragConstantsSpec.js index b9fe7caf50..f84a9600d1 100644 --- a/platform/features/timeline/test/directives/SwimlaneDragConstantsSpec.js +++ b/platform/features/timeline/test/directives/SwimlaneDragConstantsSpec.js @@ -7,9 +7,9 @@ define( describe("Timeline swimlane drag constants", function () { it("define a custom type for swimlane drag-drop", function () { - expect(SwimlaneDragConstants.WARP_SWIMLANE_DRAG_TYPE) + expect(SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE) .toEqual(jasmine.any(String)); }); }); } -); \ No newline at end of file +); diff --git a/platform/features/timeline/test/suite.json b/platform/features/timeline/test/suite.json index f04528a61c..e5028ef25d 100644 --- a/platform/features/timeline/test/suite.json +++ b/platform/features/timeline/test/suite.json @@ -21,7 +21,7 @@ "controllers/TimelineTableController", "controllers/TimelineTickController", "controllers/TimelineZoomController", - "controllers/WARPDateTimeController", + "controllers/TimelineDateTimeController", "controllers/drag/TimelineDragHandler", "controllers/drag/TimelineDragHandleFactory", @@ -43,8 +43,8 @@ "controllers/swimlane/TimelineSwimlanePopulator", "directives/SwimlaneDragConstants", - "directives/WARPSwimlaneDrag", - "directives/WARPSwimlaneDrop", + "directives/MCTSwimlaneDrag", + "directives/MCTSwimlaneDrop", "services/ObjectLoader" ] From a43c8f2ce8cc8da0edafdeb23bdb0f276ef28e79 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 14:14:24 -0700 Subject: [PATCH 153/488] [Timeline] Disable timelines due to missing styles WTD-1239. --- bundles.json | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles.json b/bundles.json index d0033ca754..2b5c5f862d 100644 --- a/bundles.json +++ b/bundles.json @@ -19,7 +19,6 @@ "platform/features/pages", "platform/features/plot", "platform/features/scrolling", - "platform/features/timeline", "platform/features/events", "platform/forms", "platform/identity", From efd209826dc541417fa687359a9276f165fa503a Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 23 Oct 2015 15:14:07 -0700 Subject: [PATCH 154/488] [Frontend] Inspector styling, in progress open #90 Significant styling in object-inspector.html; --- .../commonUI/browse/res/templates/browse.html | 3 +- .../commonUI/general/res/sass/_constants.scss | 2 +- .../commonUI/general/res/sass/_inspector.scss | 39 +- .../general/res/sass/controls/_controls.scss | 2 +- .../commonUI/general/res/sass/tree/_tree.scss | 4 +- .../res/sass/user-environ/_layout.scss | 11 +- .../commonUI/general/res/templates/label.html | 22 +- .../res/templates/object-inspector.html | 15 +- .../espresso/res/css/theme-espresso.css | 188 +- .../themes/espresso/res/sass/_constants.scss | 4 +- .../themes/snow/res/css/theme-snow.css | 6596 ++++++++++++++++- .../themes/snow/res/sass/_constants.scss | 9 + 12 files changed, 6689 insertions(+), 206 deletions(-) diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 0ea964780c..79644b5bb5 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -73,8 +73,7 @@
+ ng-model="treeModel">
diff --git a/platform/commonUI/general/res/sass/_constants.scss b/platform/commonUI/general/res/sass/_constants.scss index 6365119894..4f196b8f74 100644 --- a/platform/commonUI/general/res/sass/_constants.scss +++ b/platform/commonUI/general/res/sass/_constants.scss @@ -43,7 +43,7 @@ $ueColMargin: 1.5%; $ueAppLogoW: 105px; $ueEditToolBarH: 25px; $ueBrowseLeftPaneTreeW: 25%; -$ueBrowseRightPaneInspectW: 10%; +$ueBrowseRightPaneInspectW: 20%; $ueEditLeftPaneW: 75%; $treeSearchInputBarH: 25px; $ueTimeControlH: (33px, 20px, 20px); diff --git a/platform/commonUI/general/res/sass/_inspector.scss b/platform/commonUI/general/res/sass/_inspector.scss index 69e00dbab6..fcb8ebc959 100644 --- a/platform/commonUI/general/res/sass/_inspector.scss +++ b/platform/commonUI/general/res/sass/_inspector.scss @@ -23,12 +23,18 @@ //.pane.right.t-inspect { @include test(green, 0.3); } // TEMP! -.t-inspector, -.t-inspector table tr td { +.l-inspect, +.l-inspect table tr td { font-size: 0.7rem; } -.t-inspector { +.l-inspect { + color: $colorInspectorFg; + .pane-header { + color: pushBack($colorInspectorFg, 20%); + font-size: 0.8rem; + } + ul li, em { display: block; @@ -36,7 +42,6 @@ } ul li { - line-height: 130%; margin-bottom: $interiorMarginLg * 2; } @@ -61,7 +66,6 @@ white-space: nowrap; } &.value { - //word-wrap: break-word; // Doesn't work in ? word-break: break-all; } } @@ -70,4 +74,29 @@ border-top: none !important; } } + + .inspector-location { + line-height: 180%; + .location-item { + cursor: pointer; + display: inline; + position: relative; + padding: 2px 4px; + &:hover { + background: $colorItemTreeHoverBg; + color: $colorItemTreeHoverFg; + .icon { + color: $colorItemTreeIconHover; + } + } + } + &:not(.first):before { + color: pushBack($colorInspectorFg, 15%); + content: '\3e'; + display: inline-block; + font-family: symbolsfont; + font-size:7px; + width: 4px; + } + } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss index bc61fbc35e..429dc361fe 100644 --- a/platform/commonUI/general/res/sass/controls/_controls.scss +++ b/platform/commonUI/general/res/sass/controls/_controls.scss @@ -249,7 +249,7 @@ label.checkbox.custom { .l-elem-wrapper { //@include test(#66f, 0.2); - @include webkitProp(justify-content, flex-start); + @include justify-content(flex-start); mct-representation { // Holds the context-available item // Must have min-width to make flex work properly diff --git a/platform/commonUI/general/res/sass/tree/_tree.scss b/platform/commonUI/general/res/sass/tree/_tree.scss index d64f456b2e..b4ec062bbe 100644 --- a/platform/commonUI/general/res/sass/tree/_tree.scss +++ b/platform/commonUI/general/res/sass/tree/_tree.scss @@ -125,8 +125,8 @@ ul.tree { // NOTE: [Mobile] Removed Hover on Mobile @include desktop { &:hover { - background: rgba($colorBodyFg, 0.1); //lighten($colorBodyBg, 5%); - color: pullForward($colorBodyFg, 20%); + background: $colorItemTreeHoverBg; + color: $colorItemTreeHoverFg; .icon { color: $colorItemTreeIconHover; } diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 49944adc44..6a3d9ac2f5 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -212,7 +212,7 @@ width: $ueBrowseLeftPaneTreeW; } &.t-inspect.right { - min-width: 150px; + min-width: 100px; max-width: 800px; width: $ueBrowseRightPaneInspectW; } @@ -234,6 +234,14 @@ .pane { position: absolute; + + .pane-header { + text-transform: uppercase; + height: $ueTopBarH; + line-height: $ueTopBarH; + margin-bottom: $interiorMargin; + } + &.treeview.left { .create-btn-holder { bottom: auto; @@ -381,7 +389,6 @@ } } -// MOVED from mobile/_layout.scss // When the tree is hidden, these are the // classes used for the left menu and the // right representation. diff --git a/platform/commonUI/general/res/templates/label.html b/platform/commonUI/general/res/templates/label.html index 7ca73bb026..235678ad2d 100644 --- a/platform/commonUI/general/res/templates/label.html +++ b/platform/commonUI/general/res/templates/label.html @@ -20,15 +20,15 @@ at runtime from the About dialog for additional information. --> - - {{type.getGlyph()}} - - - - - {{model.name}} - + +{{type.getGlyph()}} + + + + +{{model.name}} + diff --git a/platform/commonUI/general/res/templates/object-inspector.html b/platform/commonUI/general/res/templates/object-inspector.html index f5f8e6921b..b635b4ccca 100644 --- a/platform/commonUI/general/res/templates/object-inspector.html +++ b/platform/commonUI/general/res/templates/object-inspector.html @@ -19,7 +19,8 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> - + +
Inspection
  • Properties @@ -33,22 +34,26 @@
  • Location + ng-repeat="parent in contextutalParents" + ng-class="{ first:$index === 0 }"> + ng-click="ngModel.selectedObject = parent" + class="location-item">
  • Original Location + ng-repeat="parent in primaryParents" + ng-class="{ first:$index === 0 }"> + ng-click="ngModel.selectedObject = parent" + class="location-item">
  • diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 3c1622bda3..08c6beb3d7 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -1259,46 +1259,76 @@ mct-container { *****************************************************************************/ /* Styles for the Inspector pane */ /* line 26, ../../../../general/res/sass/_inspector.scss */ -.t-inspector, -.t-inspector table tr td { +.l-inspect, +.l-inspect table tr td { font-size: 0.7rem; } -/* line 32, ../../../../general/res/sass/_inspector.scss */ -.t-inspector ul li, -.t-inspector em { - display: block; - position: relative; } -/* line 38, ../../../../general/res/sass/_inspector.scss */ -.t-inspector ul li { - line-height: 130%; - margin-bottom: 20px; } -/* line 43, ../../../../general/res/sass/_inspector.scss */ -.t-inspector em { - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border-radius: 2px; - background-color: #404040; - color: #999999; - margin-bottom: 5px; - padding: 5px 5px; - text-transform: uppercase; } -/* line 53, ../../../../general/res/sass/_inspector.scss */ -.t-inspector table tr td { - border: none; - border-top: 1px solid rgba(153, 153, 153, 0.1) !important; - padding: 2px 0; - vertical-align: top; } +/* line 31, ../../../../general/res/sass/_inspector.scss */ +.l-inspect { + color: #999; } + /* line 33, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .pane-header { + color: #666666; + font-size: 0.8rem; } + /* line 38, ../../../../general/res/sass/_inspector.scss */ + .l-inspect ul li, + .l-inspect em { + display: block; + position: relative; } + /* line 44, ../../../../general/res/sass/_inspector.scss */ + .l-inspect ul li { + margin-bottom: 20px; } + /* line 48, ../../../../general/res/sass/_inspector.scss */ + .l-inspect em { + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + background-color: #404040; + color: #999999; + margin-bottom: 5px; + padding: 5px 5px; + text-transform: uppercase; } /* line 58, ../../../../general/res/sass/_inspector.scss */ - .t-inspector table tr td.label { - color: #666666 !important; - padding-right: 5px !important; - white-space: nowrap; } - /* line 63, ../../../../general/res/sass/_inspector.scss */ - .t-inspector table tr td.value { - word-break: break-all; } -/* line 69, ../../../../general/res/sass/_inspector.scss */ -.t-inspector table tr:first-child td { - border-top: none !important; } + .l-inspect table tr td { + border: none; + border-top: 1px solid rgba(153, 153, 153, 0.1) !important; + padding: 2px 0; + vertical-align: top; } + /* line 63, ../../../../general/res/sass/_inspector.scss */ + .l-inspect table tr td.label { + color: #666666 !important; + padding-right: 5px !important; + white-space: nowrap; } + /* line 68, ../../../../general/res/sass/_inspector.scss */ + .l-inspect table tr td.value { + word-break: break-all; } + /* line 73, ../../../../general/res/sass/_inspector.scss */ + .l-inspect table tr:first-child td { + border-top: none !important; } + /* line 78, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location { + line-height: 180%; } + /* line 80, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location .location-item { + cursor: pointer; + display: inline; + position: relative; + padding: 2px 4px; } + /* line 85, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location .location-item:hover { + background: rgba(153, 153, 153, 0.1); + color: #cccccc; } + /* line 88, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location .location-item:hover .icon { + color: #33ccff; } + /* line 93, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location:not(.first):before { + color: #737373; + content: '\3e'; + display: inline-block; + font-family: symbolsfont; + font-size: 7px; + width: 4px; } /********************************* CONTROLS */ /* line 1, ../../../../general/res/sass/controls/_breadcrumb.scss */ @@ -2016,8 +2046,8 @@ label.checkbox.custom { margin-right: 5px; } /* line 250, ../../../../general/res/sass/controls/_controls.scss */ .object-header .l-elem-wrapper { - justify-content: flex-start; - -webkit-justify-content: flex-start; } + -webkit-justify-content: flex-start; + justify-content: flex-start; } /* line 253, ../../../../general/res/sass/controls/_controls.scss */ .object-header .l-elem-wrapper mct-representation { min-width: 0.7em; } @@ -3689,9 +3719,9 @@ span.req { width: 25%; } /* line 214, ../../../../general/res/sass/user-environ/_layout.scss */ .browse-mode .split-layout .split-pane-component.pane.t-inspect.right { - min-width: 150px; + min-width: 100px; max-width: 800px; - width: 10%; } + width: 20%; } /* line 225, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right { @@ -3705,47 +3735,53 @@ span.req { .pane { position: absolute; } /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .pane-header { + text-transform: uppercase; + height: 24px; + line-height: 24px; + margin-bottom: 5px; } + /* line 246, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 242, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 247, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 258, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { z-index: 2; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 256, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { top: 5px; } - /* line 262, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { left: -30px; } - /* line 265, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 273, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { content: 'F'; } - /* line 268, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 276, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { left: -25px; } - /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { right: -25px; } - /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 283, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { content: '\e608'; } - /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 286, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { right: -20px; } } - /* line 287, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 295, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3758,31 +3794,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 298, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 306, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 301, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 316, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 319, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 315, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 323, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 317, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 333, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 328, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 336, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3792,11 +3828,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 332, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 340, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 346, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3808,12 +3844,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 351, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 359, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 356, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3829,36 +3865,36 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 372, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 377, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 385, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 390, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 397, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.left.treeview, .pane-tree-hidden .splitter-treeview { opacity: 0; } -/* line 395, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.right.items { left: 15px !important; } -/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 409, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-showing .pane.left.treeview, .pane-tree-showing .splitter-treeview { -moz-transition-property: opacity, background-color, border-color, color; @@ -3879,7 +3915,7 @@ span.req { transition-delay: 250ms; opacity: 1; } -/* line 411, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 418, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-inspect-showing .l-object-and-inspector .pane.right, .pane-inspect-showing .l-object-and-inspector .splitter-inspect { -moz-transition-property: opacity, background-color, border-color, color; @@ -3900,16 +3936,16 @@ span.req { transition-delay: 250ms; opacity: 1; } -/* line 420, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 427, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-inspect-hidden .l-object-and-inspector .pane.right, .pane-inspect-hidden .l-object-and-inspector .splitter-inspect { opacity: 0; } -/* line 424, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 431, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-inspect-hidden .l-object-and-inspector .pane.left { right: 15px !important; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 431, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 438, ../../../../general/res/sass/user-environ/_layout.scss */ .pane:not(.resizing) { -moz-transition-property: width, left, right; -o-transition-property: width, left, right; diff --git a/platform/commonUI/themes/espresso/res/sass/_constants.scss b/platform/commonUI/themes/espresso/res/sass/_constants.scss index 8aa2bcbdf0..df85c6ecdb 100644 --- a/platform/commonUI/themes/espresso/res/sass/_constants.scss +++ b/platform/commonUI/themes/espresso/res/sass/_constants.scss @@ -73,7 +73,7 @@ $colorInputFg: pullForward($colorBodyFg, 20%); $colorFormText: rgba(#fff, 0.5); $colorInputIcon: pushBack($colorBodyFg, 15%); -// Inspector - TO-DO: Add to snow theme +// Inspector $colorInspectorFg: $colorBodyFg; $colorInspectorPropName: pushBack($colorBodyFg, 20%); $colorInspectorPropVal: $colorInspectorFg; @@ -134,6 +134,8 @@ $colorPlotAreaBorder: $colorInteriorBorder; $colorPlotLabelFg: pushBack($colorPlotFg, 20%); // Tree +$colorItemTreeHoverBg: rgba($colorBodyFg, 0.1); +$colorItemTreeHoverFg: pullForward($colorBodyFg, 20%); $colorItemTreeIcon: $colorKey; $colorItemTreeIconHover: lighten($colorItemTreeIcon, 20%); $colorItemTreeFg: $colorBodyFg; diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 869495b986..126ad40fa9 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -1,101 +1,6497 @@ -/* -Error: Undefined variable: "$colorInspectorSectionHeaderBg". - on line 45 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_inspector.scss - from line 33 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_main.scss - from line 36 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/themes/snow/res/sass/theme-snow.scss +@charset "UTF-8"; +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 5, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font: inherit; + font-size: 100%; + vertical-align: baseline; } -Backtrace: -/Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_inspector.scss:45 -/Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_main.scss:33 -/Users/iMac/dev/nasa/wtd-dev/platform/commonUI/themes/snow/res/sass/theme-snow.scss:36 -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/script/tree/variable.rb:49:in `_perform' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/script/tree/node.rb:50:in `perform' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:394:in `visit_prop' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `block (2 levels) in visit_rule' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `map' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `block in visit_rule' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:179:in `with_environment' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:428:in `visit_rule' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `block (2 levels) in visit_rule' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `map' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:430:in `block in visit_rule' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:179:in `with_environment' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:428:in `visit_rule' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `block (2 levels) in visit_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `map' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `block in visit_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:88:in `block in with_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:88:in `with_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:322:in `visit_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `block (2 levels) in visit_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `map' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:325:in `block in visit_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:88:in `block in with_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:88:in `with_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:322:in `visit_import' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `block in visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `block in with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:115:in `with_frame' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/stack.rb:79:in `with_base' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:158:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:52:in `block in visit_children' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:52:in `map' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:52:in `visit_children' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:167:in `block in visit_children' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:179:in `with_environment' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:166:in `visit_children' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `block in visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:186:in `visit_root' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/base.rb:36:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:157:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/visitors/perform.rb:8:in `visit' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/root_node.rb:36:in `css_tree' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/tree/root_node.rb:20:in `render' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/engine.rb:268:in `render' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/plugin/compiler.rb:492:in `update_stylesheet' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/plugin/compiler.rb:215:in `block in update_stylesheets' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/plugin/compiler.rb:209:in `each' -/Library/Ruby/Gems/2.0.0/gems/sass-3.4.13/lib/sass/plugin/compiler.rb:209:in `update_stylesheets' -/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/sass_compiler.rb:40:in `compile!' -/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/commands/update_project.rb:49:in `perform' -/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/commands/base.rb:18:in `execute' -/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/commands/project_base.rb:19:in `execute' -/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/exec/sub_command_ui.rb:43:in `perform!' -/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/lib/compass/exec/sub_command_ui.rb:15:in `run!' -/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/bin/compass:30:in `block in ' -/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/bin/compass:44:in `call' -/Library/Ruby/Gems/2.0.0/gems/compass-1.0.3/bin/compass:44:in `' -/usr/bin/compass:23:in `load' -/usr/bin/compass:23:in `
    ' -*/ -body:before { - white-space: pre; - font-family: monospace; - content: "Error: Undefined variable: \"$colorInspectorSectionHeaderBg\".\A on line 45 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_inspector.scss\A from line 33 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/general/res/sass/_main.scss\A from line 36 of /Users/iMac/dev/nasa/wtd-dev/platform/commonUI/themes/snow/res/sass/theme-snow.scss"; } +/* line 22, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +html { + line-height: 1; } + +/* line 24, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +ol, ul { + list-style: none; } + +/* line 26, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +table { + border-collapse: collapse; + border-spacing: 0; } + +/* line 28, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +caption, th, td { + text-align: left; + font-weight: normal; + vertical-align: middle; } + +/* line 30, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +q, blockquote { + quotes: none; } + /* line 103, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ + q:before, q:after, blockquote:before, blockquote:after { + content: ""; + content: none; } + +/* line 32, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +a img { + border: none; } + +/* line 116, ../../../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/reset/_utilities.scss */ +article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { + display: block; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*********************************************** CONTROLS, FORM ELEMENTS */ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/************************** FEATURES */ +/************************** VERY INFLUENTIAL GLOBAL DIMENSIONS */ +/************************** RATIOS */ +/************************** LAYOUT */ +/************************** CONTROLS */ +/************************** PATHS */ +/************************** TIMINGS */ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */ +/************************** MOBILE TREE MENU DIMENSIONS */ +/************************** WINDOW DIMENSIONS FOR RWD */ +/************************** MEDIA QUERIES: WINDOW CHECKS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */ +/************************** MEDIA QUERIES: WINDOWS FOR SPECIFIC ORIENTATIONS FOR EACH DEVICE */ +/************************** DEVICE PARAMETERS FOR MENUS/REPRESENTATIONS */ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/_effects.scss */ +.disabled, +a.disabled { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=30); + opacity: 0.3; + pointer-events: none !important; + cursor: default !important; } + +/* line 29, ../../../../general/res/sass/_effects.scss */ +.incised { + -moz-box-shadow: inset rgba(0, 0, 0, 0.8) 0 1px 5px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.8) 0 1px 5px; + box-shadow: inset rgba(0, 0, 0, 0.8) 0 1px 5px; + border-bottom: 1px solid rgba(255, 255, 255, 0.3); } + +/* line 34, ../../../../general/res/sass/_effects.scss */ +.outline { + border: 1px solid white; } + +/* line 38, ../../../../general/res/sass/_effects.scss */ +.test-stripes { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjEuMCIgeTE9IjEuMCIgeDI9IjAuMCIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIyNSUiIHN0b3AtY29sb3I9IiNmZmZmMDAiIHN0b3Atb3BhY2l0eT0iMC4xIi8+PHN0b3Agb2Zmc2V0PSIyNSUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSI1MCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSI1MCUiIHN0b3AtY29sb3I9IiNmZmZmMDAiIHN0b3Atb3BhY2l0eT0iMC4xIi8+PHN0b3Agb2Zmc2V0PSI3NSUiIHN0b3AtY29sb3I9IiNmZmZmMDAiIHN0b3Atb3BhY2l0eT0iMC4xIi8+PHN0b3Agb2Zmc2V0PSI3NSUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -moz-linear-gradient(135deg, rgba(255, 255, 0, 0.1) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 0, 0.1) 50%, rgba(255, 255, 0, 0.1) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0) 100%); + background-image: -webkit-linear-gradient(135deg, rgba(255, 255, 0, 0.1) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 0, 0.1) 50%, rgba(255, 255, 0, 0.1) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0) 100%); + background-image: linear-gradient(-45deg, rgba(255, 255, 0, 0.1) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(255, 255, 0, 0.1) 50%, rgba(255, 255, 0, 0.1) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0) 100%); + background-repeat: repeat; + background-size: 40px 40px; } + +/* line 42, ../../../../general/res/sass/_effects.scss */ +.test { + background-color: rgba(255, 204, 0, 0.2) !important; } + +@-moz-keyframes pulse { + 0% { + opacity: 0.5; } + 100% { + opacity: 1; } } +@-webkit-keyframes pulse { + 0% { + opacity: 0.5; } + 100% { + opacity: 1; } } +@keyframes pulse { + 0% { + opacity: 0.5; } + 100% { + opacity: 1; } } +/* line 69, ../../../../general/res/sass/_effects.scss */ +.pulse { + -moz-animation-name: pulse; + -webkit-animation-name: pulse; + animation-name: pulse; + -moz-animation-duration: 750ms; + -webkit-animation-duration: 750ms; + animation-duration: 750ms; + -moz-animation-direction: alternate; + -webkit-animation-direction: alternate; + animation-direction: alternate; + -moz-animation-iteration-count: infinite; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + -moz-animation-timing-function: ease-in-out; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/************************** FONTS */ +@font-face { + /* + * Use https://icomoon.io/app with /platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json + */ + font-family: 'symbolsfont'; + src: url("../../../../general/res/fonts/symbols/wtdsymbols.eot"); + src: url("../../../../general/res/fonts/symbols/wtdsymbols.eot?#iefix") format("embedded-opentype"), url("../../../../general/res/fonts/symbols/wtdsymbols.woff") format("woff"), url("../../../../general/res/fonts/symbols/wtdsymbols.ttf") format("truetype"), url("../../../../general/res/fonts/symbols/wtdsymbols.svg#armataregular") format("svg"); + font-weight: normal; + font-style: normal; } +/************************** HTML ENTITIES */ +/* line 38, ../../../../general/res/sass/_global.scss */ +a { + color: #999; + cursor: pointer; + text-decoration: none; } + /* line 42, ../../../../general/res/sass/_global.scss */ + a:hover { + color: #0099cc; } + +/* line 47, ../../../../general/res/sass/_global.scss */ +body, html { + -webkit-font-smoothing: subpixel-antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: #fcfcfc; + color: #666; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 100%; + height: 100%; + width: 100%; + overflow: hidden; } + +/* line 60, ../../../../general/res/sass/_global.scss */ +em { + font-style: normal; } + +/* line 64, ../../../../general/res/sass/_global.scss */ +input, textarea { + font-family: Helvetica, Arial, sans-serif; } + +/* line 68, ../../../../general/res/sass/_global.scss */ +input[type="text"] { + vertical-align: baseline; + padding: 3px 5px !important; } + +/* line 73, ../../../../general/res/sass/_global.scss */ +h1, h2, h3 { + margin: 0; } + +/* line 77, ../../../../general/res/sass/_global.scss */ +h1 { + font-size: 1.7em; + font-weight: normal !important; + line-height: 120%; + margin-bottom: 20px; + margin-top: 0; } + +/* line 85, ../../../../general/res/sass/_global.scss */ +p { + margin-bottom: 10px; } + +/* line 89, ../../../../general/res/sass/_global.scss */ +mct-container { + display: block; } + +/* line 93, ../../../../general/res/sass/_global.scss */ +.abs, .l-datetime-picker .l-month-year-pager .pager, +.l-datetime-picker .l-month-year-pager .val, .s-menu-btn span.l-click-area { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + height: auto; + width: auto; } + +/* line 103, ../../../../general/res/sass/_global.scss */ +.code, .codehilite { + font-family: "Lucida Console", monospace; + font-size: 0.7em; + line-height: 150%; + white-space: pre; } + +/* line 110, ../../../../general/res/sass/_global.scss */ +.codehilite { + background-color: rgba(102, 102, 102, 0.1); + padding: 1em; } + +/* line 116, ../../../../general/res/sass/_global.scss */ +.align-right { + text-align: right; } + +/* line 120, ../../../../general/res/sass/_global.scss */ +.centered { + text-align: center; } + +/* line 124, ../../../../general/res/sass/_global.scss */ +.scrolling { + overflow: auto; } + +/* line 128, ../../../../general/res/sass/_global.scss */ +.vscroll { + overflow-y: auto; } + +/* line 132, ../../../../general/res/sass/_global.scss */ +.no-margin { + margin: 0; } + +/* line 136, ../../../../general/res/sass/_global.scss */ +.ds { + -moz-box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; + -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; + box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; } + +/* line 140, ../../../../general/res/sass/_global.scss */ +.hide, +.hidden { + display: none !important; } + +/* line 145, ../../../../general/res/sass/_global.scss */ +.sep { + color: rgba(255, 255, 255, 0.2); } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 26, ../../../../general/res/sass/_about.scss */ +.l-about.abs, .l-datetime-picker .l-month-year-pager .l-about.pager, +.l-datetime-picker .l-month-year-pager .l-about.val, .s-menu-btn span.l-about.l-click-area { + overflow: auto; } +/* line 31, ../../../../general/res/sass/_about.scss */ +.l-about .l-logo-holder { + position: relative; + height: 45%; } + /* line 34, ../../../../general/res/sass/_about.scss */ + .l-about .l-logo-holder .l-logo { + position: absolute; } + /* line 37, ../../../../general/res/sass/_about.scss */ + .l-about .l-logo-holder .l-logo.l-logo-app { + top: 0; + right: 15%; + bottom: 0; + left: 15%; } + /* line 41, ../../../../general/res/sass/_about.scss */ + .l-about .l-logo-holder .l-logo.s-logo-nasa { + background-image: url("../../../../general/res/images/logo-nasa.svg"); + top: 10px; + right: auto; + bottom: auto; + left: 10px; + width: 10%; + height: auto; + padding-bottom: 5%; + padding-top: 5%; } +/* line 50, ../../../../general/res/sass/_about.scss */ +.l-about .l-content { + position: relative; + margin-top: 10px; } + +/* line 57, ../../../../general/res/sass/_about.scss */ +.s-about { + line-height: 120%; } + /* line 61, ../../../../general/res/sass/_about.scss */ + .s-about a { + color: #84b3ff; } + /* line 68, ../../../../general/res/sass/_about.scss */ + .s-about .s-logo-holder { + background: url("../../../../general/res/images/bg-about-openmctweb.jpg") no-repeat center; + background-size: cover; } + /* line 72, ../../../../general/res/sass/_about.scss */ + .s-about .s-logo { + background-position: center; + background-repeat: no-repeat; + background-size: contain; } + /* line 78, ../../../../general/res/sass/_about.scss */ + .s-about .s-logo-openmctweb { + background-image: url("../../../../general/res/images/logo-openmctweb-shdw.svg"); } + /* line 81, ../../../../general/res/sass/_about.scss */ + .s-about .s-btn, .s-about .s-menu-btn { + line-height: 2em; } + /* line 85, ../../../../general/res/sass/_about.scss */ + .s-about .l-licenses-software .l-license-software { + border-top: 1px solid rgba(102, 102, 102, 0.2); + padding: 0.5em 0; } + /* line 88, ../../../../general/res/sass/_about.scss */ + .s-about .l-licenses-software .l-license-software:first-child { + border-top: none; } + /* line 91, ../../../../general/res/sass/_about.scss */ + .s-about .l-licenses-software .l-license-software em { + color: #999999; } + /* line 98, ../../../../general/res/sass/_about.scss */ + .s-about .l-licenses-software .l-license-software h3 { + font-size: 1.25em; } + /* line 101, ../../../../general/res/sass/_about.scss */ + .s-about .l-licenses-software .l-license-software .s-license-text { + font-size: 0.9em; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 24, ../../../../general/res/sass/_text.scss */ +.abs.l-standalone, .l-datetime-picker .l-month-year-pager .l-standalone.pager, +.l-datetime-picker .l-month-year-pager .l-standalone.val, .s-menu-btn span.l-standalone.l-click-area { + padding: 5% 20%; } + +/* line 29, ../../../../general/res/sass/_text.scss */ +.s-text { + font-size: 0.8em; } + /* line 31, ../../../../general/res/sass/_text.scss */ + .s-text ol, .s-text ul { + list-style: square; + margin-left: 1.5em; } + /* line 39, ../../../../general/res/sass/_text.scss */ + .s-text h1, .s-text h2, .s-text h3 { + color: #333333; + font-weight: normal !important; + margin-bottom: 1em; } + /* line 45, ../../../../general/res/sass/_text.scss */ + .s-text h2 { + border-top: 1px solid rgba(102, 102, 102, 0.2); + font-size: 1.5em; + margin-top: 2em; + padding-top: 1em; } + /* line 52, ../../../../general/res/sass/_text.scss */ + .s-text h3 { + margin-top: 2em; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/_icons.scss */ +.triangle { + width: 0; + height: 0; + border-top: 5px solid transparent; + border-left: 5px solid #0099cc; + border-bottom: 5px solid transparent; } + /* line 26, ../../../../general/res/sass/_icons.scss */ + .triangle.triangle-down { + width: 0; + height: 0; + border-left: 5px solid transparent; + border-top: 5px solid #0099cc; + border-right: 5px solid transparent; } + +/* line 31, ../../../../general/res/sass/_icons.scss */ +.ui-symbol, .s-icon-btn, .mini-tab, .l-datetime-picker .l-month-year-pager .pager { + font-family: 'symbolsfont'; } + /* line 33, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.type-icon, .type-icon.s-icon-btn, .type-icon.mini-tab, .l-datetime-picker .l-month-year-pager .type-icon.pager { + color: #b3b3b3; } + /* line 36, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon, .icon.s-icon-btn, .icon.mini-tab, .l-datetime-picker .l-month-year-pager .icon.pager { + color: #0099cc; } + /* line 38, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon.alert, .icon.alert.s-icon-btn, .icon.alert.mini-tab, .l-datetime-picker .l-month-year-pager .icon.alert.pager { + color: #ff3c00; } + /* line 40, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon.alert:hover, .icon.alert.s-icon-btn:hover, .icon.alert.mini-tab:hover, .l-datetime-picker .l-month-year-pager .icon.alert.pager:hover { + color: #ff8a66; } + /* line 44, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon.major, .icon.major.s-icon-btn, .icon.major.mini-tab, .l-datetime-picker .l-month-year-pager .icon.major.pager { + font-size: 1.65em; } + /* line 48, ../../../../general/res/sass/_icons.scss */ + .ui-symbol.icon-calendar:after, .icon-calendar.s-icon-btn:after, .icon-calendar.mini-tab:after, .l-datetime-picker .l-month-year-pager .icon-calendar.pager:after { + content: "\e605"; } + +/* line 53, ../../../../general/res/sass/_icons.scss */ +.bar .ui-symbol, .bar .s-icon-btn, .bar .mini-tab, .bar .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .bar .pager { + display: inline-block; } + +/* line 57, ../../../../general/res/sass/_icons.scss */ +.invoke-menu { + text-shadow: none; + display: inline-block; } + +/* line 62, ../../../../general/res/sass/_icons.scss */ +.s-menu-btn .invoke-menu, +.icon.major .invoke-menu { + margin-left: 3px; } + +/* line 67, ../../../../general/res/sass/_icons.scss */ +.menu .type-icon, +.tree-item .type-icon, +.super-menu.menu .type-icon { + position: absolute; } + +/* line 77, ../../../../general/res/sass/_icons.scss */ +.l-icon-link:before { + content: "\f4"; } + +/* line 81, ../../../../general/res/sass/_icons.scss */ +.l-icon-alert { + display: none !important; } + /* line 83, ../../../../general/res/sass/_icons.scss */ + .l-icon-alert:before { + color: #ff3c00; + content: "!"; } + +/* line 13, ../../../../general/res/sass/_limits.scss */ +[class*="s-limit"]:before { + display: inline-block; + font-family: symbolsfont; + font-size: 0.75em; + font-style: normal !important; + margin-right: 3px; + vertical-align: middle; } + +/* line 23, ../../../../general/res/sass/_limits.scss */ +.s-limit-upr-red { + background: rgba(255, 0, 0, 0.3) !important; } + /* line 4, ../../../../general/res/sass/_limits.scss */ + .s-limit-upr-red:before { + color: red; + content: "ë"; } + +/* line 24, ../../../../general/res/sass/_limits.scss */ +.s-limit-upr-yellow { + background: rgba(255, 170, 0, 0.3) !important; } + /* line 4, ../../../../general/res/sass/_limits.scss */ + .s-limit-upr-yellow:before { + color: #ffaa00; + content: "í"; } + +/* line 25, ../../../../general/res/sass/_limits.scss */ +.s-limit-lwr-yellow { + background: rgba(255, 170, 0, 0.3) !important; } + /* line 4, ../../../../general/res/sass/_limits.scss */ + .s-limit-lwr-yellow:before { + color: #ffaa00; + content: "ì"; } + +/* line 26, ../../../../general/res/sass/_limits.scss */ +.s-limit-lwr-red { + background: rgba(255, 0, 0, 0.3) !important; } + /* line 4, ../../../../general/res/sass/_limits.scss */ + .s-limit-lwr-red:before { + color: red; + content: "î"; } + +/* line 1, ../../../../general/res/sass/_data-status.scss */ +.s-stale { + color: rgba(51, 51, 51, 0.5) !important; + font-style: italic; } + /* line 3, ../../../../general/res/sass/_data-status.scss */ + .s-stale .td { + color: rgba(51, 51, 51, 0.5) !important; + font-style: italic; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 24, ../../../../general/res/sass/helpers/_bubbles.scss */ +.bubble-container { + pointer-events: none; } + +/* line 31, ../../../../general/res/sass/helpers/_bubbles.scss */ +.l-infobubble-wrapper { + -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; + -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; + box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; + position: relative; + z-index: 50; } + /* line 36, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble { + display: inline-block; + min-width: 100px; + max-width: 300px; + padding: 5px 10px; } + /* line 41, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble:before { + content: ""; + position: absolute; + width: 0; + height: 0; } + /* line 47, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table { + width: 100%; } + /* line 50, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td { + padding: 2px 0; + vertical-align: top; } + /* line 53, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td.label { + padding-right: 10px; + white-space: nowrap; } + /* line 57, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td.value { + word-break: break-all; } + /* line 61, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td.align-wrap { + white-space: normal; } + /* line 67, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble .title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 5px; } + /* line 74, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-left { + margin-left: 20px; } + /* line 76, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-left .l-infobubble::before { + right: 100%; } + @media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 76, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-left .l-infobubble::before { + width: 0; + height: 0; + border-top: 6.66667px solid transparent; + border-bottom: 6.66667px solid transparent; + border-right: 10px solid white; } } + @media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 88, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-right { + margin-right: 20px; } } + /* line 95, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-right .l-infobubble::before { + left: 100%; } + @media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 95, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-right .l-infobubble::before { + width: 0; + height: 0; + border-top: 6.66667px solid transparent; + border-bottom: 6.66667px solid transparent; + border-left: 10px solid white; } } + /* line 108, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-top .l-infobubble::before { + top: 20px; } + /* line 114, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-btm .l-infobubble::before { + bottom: 20px; } + /* line 119, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-down { + margin-bottom: 10px; } + /* line 121, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-down .l-infobubble::before { + left: 50%; + top: 100%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 7.5px solid white; } + /* line 130, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .arw { + z-index: 2; } + /* line 133, ../../../../general/res/sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-up .arw.arw-down, .l-infobubble-wrapper.arw-down .arw.arw-up { + display: none; } + +/* line 142, ../../../../general/res/sass/helpers/_bubbles.scss */ +.l-thumbsbubble-wrapper .arw-up { + width: 0; + height: 0; + border-left: 6.66667px solid transparent; + border-right: 6.66667px solid transparent; + border-bottom: 10px solid #e3e3e3; } +/* line 145, ../../../../general/res/sass/helpers/_bubbles.scss */ +.l-thumbsbubble-wrapper .arw-down { + width: 0; + height: 0; + border-left: 6.66667px solid transparent; + border-right: 6.66667px solid transparent; + border-top: 10px solid #e3e3e3; } + +/* line 150, ../../../../general/res/sass/helpers/_bubbles.scss */ +.s-infobubble { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; + -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; + box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; + background: white; + color: #666; + font-size: 0.8rem; } + /* line 157, ../../../../general/res/sass/helpers/_bubbles.scss */ + .s-infobubble .title { + color: #333333; + font-weight: bold; } + /* line 163, ../../../../general/res/sass/helpers/_bubbles.scss */ + .s-infobubble table tr td { + border: none; + border-top: 1px solid #e6e6e6 !important; + font-size: 0.9em; } + /* line 169, ../../../../general/res/sass/helpers/_bubbles.scss */ + .s-infobubble table tr:first-child td { + border-top: none !important; } + /* line 174, ../../../../general/res/sass/helpers/_bubbles.scss */ + .s-infobubble:first-child td { + border-top: none; } + /* line 178, ../../../../general/res/sass/helpers/_bubbles.scss */ + .s-infobubble .label { + color: gray; } + /* line 182, ../../../../general/res/sass/helpers/_bubbles.scss */ + .s-infobubble .value { + color: #333333; } + +/* line 188, ../../../../general/res/sass/helpers/_bubbles.scss */ +.s-thumbsbubble { + background: #e3e3e3; + color: #4d4d4d; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 25, ../../../../general/res/sass/helpers/_splitter.scss */ +.split-layout .splitter { + background-color: #969696; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + position: absolute; + z-index: 1; } + /* line 32, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout .splitter:hover { + background-color: #0099cc; } + /* line 34, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout .splitter:hover:after { + border-color: #fcfcfc !important; } + /* line 39, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout .splitter:active { + background-color: #898989; } +/* line 43, ../../../../general/res/sass/helpers/_splitter.scss */ +.split-layout.horizontal { + overflow: hidden; } + /* line 46, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.horizontal .pane { + left: 0; + right: 0; } + /* line 49, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.horizontal .pane.top { + bottom: auto; } + /* line 52, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.horizontal .pane.bottom { + top: auto; } + /* line 56, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.horizontal > .splitter { + cursor: row-resize; + left: 0; + right: 0; + width: auto; + height: 5px; } + /* line 57, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.horizontal > .splitter:after { + -moz-transition-property: "border-color"; + -o-transition-property: "border-color"; + -webkit-transition-property: "border-color"; + transition-property: "border-color"; + -moz-transition-duration: 25ms; + -o-transition-duration: 25ms; + -webkit-transition-duration: 25ms; + transition-duration: 25ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + content: ''; + display: block; + pointer-events: none; + position: absolute; + z-index: 2; + border-top: 1px dotted #d6d6d6; + top: 2px; + left: 5px; + right: 5px; + height: 1px; } +/* line 68, ../../../../general/res/sass/helpers/_splitter.scss */ +.split-layout.vertical .pane { + top: 0; + bottom: 0; } + /* line 71, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.vertical .pane.left { + right: auto; } + /* line 74, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.vertical .pane.right { + left: auto; } +/* line 78, ../../../../general/res/sass/helpers/_splitter.scss */ +.split-layout.vertical > .splitter { + bottom: 0; + cursor: col-resize; + width: 5px; } + /* line 82, ../../../../general/res/sass/helpers/_splitter.scss */ + .split-layout.vertical > .splitter:after { + -moz-transition-property: "border-color"; + -o-transition-property: "border-color"; + -webkit-transition-property: "border-color"; + transition-property: "border-color"; + -moz-transition-duration: 25ms; + -o-transition-duration: 25ms; + -webkit-transition-duration: 25ms; + transition-duration: 25ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + content: ''; + display: block; + pointer-events: none; + position: absolute; + z-index: 2; + border-left: 1px dotted #d6d6d6; + left: 2px; + bottom: 5px; + top: 5px; + width: 1px; } + +/* line 89, ../../../../general/res/sass/helpers/_splitter.scss */ +.browse-area .splitter { + top: 0; } + +/* line 93, ../../../../general/res/sass/helpers/_splitter.scss */ +.edit-area .splitter { + top: 0; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +@-moz-keyframes rotation { + 0% { + transform: rotate(0deg); } + 100% { + transform: rotate(359deg); } } +@-webkit-keyframes rotation { + 0% { + transform: rotate(0deg); } + 100% { + transform: rotate(359deg); } } +@keyframes rotation { + 0% { + transform: rotate(0deg); } + 100% { + transform: rotate(359deg); } } +/* line 63, ../../../../general/res/sass/helpers/_wait-spinner.scss */ +.t-wait-spinner, +.wait-spinner { + display: block; + position: absolute; + -webkit-animation: rotation .6s infinite linear; + -moz-animation: rotation .6s infinite linear; + -o-animation: rotation .6s infinite linear; + animation: rotation .6s infinite linear; + border-color: rgba(0, 153, 204, 0.25); + border-top-color: #0099cc; + border-style: solid; + border-width: 0.5em; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + top: 50%; + left: 50%; + height: auto; + width: auto; + padding: 5%; + pointer-events: none; + margin-top: -5%; + margin-left: -5%; + z-index: 2; } + /* line 74, ../../../../general/res/sass/helpers/_wait-spinner.scss */ + .t-wait-spinner.inline, + .wait-spinner.inline { + display: inline-block !important; + margin-right: 5px; + position: relative !important; + vertical-align: middle; } + +/* line 82, ../../../../general/res/sass/helpers/_wait-spinner.scss */ +.l-wait-spinner-holder { + pointer-events: none; + position: absolute; } + /* line 86, ../../../../general/res/sass/helpers/_wait-spinner.scss */ + .l-wait-spinner-holder.align-left .t-wait-spinner { + left: 0; + margin-left: 0; } + /* line 91, ../../../../general/res/sass/helpers/_wait-spinner.scss */ + .l-wait-spinner-holder.full-size { + display: inline-block; + height: 100%; + width: 100%; } + /* line 94, ../../../../general/res/sass/helpers/_wait-spinner.scss */ + .l-wait-spinner-holder.full-size .t-wait-spinner { + top: 0; + margin-top: 0; + padding: 30%; } + +/* line 103, ../../../../general/res/sass/helpers/_wait-spinner.scss */ +.treeview .wait-spinner { + display: block; + position: absolute; + -webkit-animation: rotation .6s infinite linear; + -moz-animation: rotation .6s infinite linear; + -o-animation: rotation .6s infinite linear; + animation: rotation .6s infinite linear; + border-color: rgba(0, 153, 204, 0.25); + border-top-color: #0099cc; + border-style: solid; + border-width: 0.25em; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + height: 10px; + width: 10px; + margin: 0 !important; + padding: 0 !important; + top: 2px; + left: 0; } + +/* line 112, ../../../../general/res/sass/helpers/_wait-spinner.scss */ +.wait-spinner.sm { + display: block; + position: absolute; + -webkit-animation: rotation .6s infinite linear; + -moz-animation: rotation .6s infinite linear; + -o-animation: rotation .6s infinite linear; + animation: rotation .6s infinite linear; + border-color: rgba(0, 153, 204, 0.25); + border-top-color: #0099cc; + border-style: solid; + border-width: 0.25em; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + height: 13px; + width: 13px; + margin-left: 0 !important; + margin-top: 0 !important; + padding: 0 !important; + top: 0; + left: 0; } + +/* line 122, ../../../../general/res/sass/helpers/_wait-spinner.scss */ +.loading { + pointer-events: none; } + /* line 125, ../../../../general/res/sass/helpers/_wait-spinner.scss */ + .loading:before, .loading:after { + content: ''; } + /* line 129, ../../../../general/res/sass/helpers/_wait-spinner.scss */ + .loading:before { + -moz-animation-name: rotateCentered; + -webkit-animation-name: rotateCentered; + animation-name: rotateCentered; + -moz-animation-duration: 0.5s; + -webkit-animation-duration: 0.5s; + animation-duration: 0.5s; + -moz-animation-iteration-count: infinite; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + -moz-animation-timing-function: linear; + -webkit-animation-timing-function: linear; + animation-timing-function: linear; + border-color: rgba(119, 107, 162, 0.25); + border-top-color: #776ba2; + border-style: solid; + border-width: 5px; + -moz-border-radius: 100%; + -webkit-border-radius: 100%; + border-radius: 100%; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: block; + position: absolute; + height: 0; + width: 0; + padding: 7%; + left: 50%; + top: 50%; + z-index: 10; } +@-moz-keyframes rotateCentered { + 0% { + transform: translateX(-50%) translateY(-50%) rotate(0deg); } + 100% { + transform: translateX(-50%) translateY(-50%) rotate(359deg); } } +@-webkit-keyframes rotateCentered { + 0% { + transform: translateX(-50%) translateY(-50%) rotate(0deg); } + 100% { + transform: translateX(-50%) translateY(-50%) rotate(359deg); } } +@keyframes rotateCentered { + 0% { + transform: translateX(-50%) translateY(-50%) rotate(0deg); } + 100% { + transform: translateX(-50%) translateY(-50%) rotate(359deg); } } + /* line 133, ../../../../general/res/sass/helpers/_wait-spinner.scss */ + .loading:after { + overflow: hidden; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + width: auto; + height: auto; + background: rgba(119, 107, 162, 0.1); + display: block; + z-index: 9; } + /* line 139, ../../../../general/res/sass/helpers/_wait-spinner.scss */ + .loading.tree-item:before { + padding: 0.375rem; + border-width: 2px; } + +/* Styles for messages */ +/* line 4, ../../../../general/res/sass/_messages.scss */ +.message.block { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + padding: 10px; } +/* line 8, ../../../../general/res/sass/_messages.scss */ +.message.error { + background-color: rgba(255, 60, 0, 0.3); + color: #ff8a66; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* Styles for the Inspector pane */ +/* line 26, ../../../../general/res/sass/_inspector.scss */ +.l-inspect, +.l-inspect table tr td { + font-size: 0.7rem; } + +/* line 31, ../../../../general/res/sass/_inspector.scss */ +.l-inspect { + color: #666; } + /* line 33, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .pane-header { + color: #999999; + font-size: 0.8rem; } + /* line 38, ../../../../general/res/sass/_inspector.scss */ + .l-inspect ul li, + .l-inspect em { + display: block; + position: relative; } + /* line 44, ../../../../general/res/sass/_inspector.scss */ + .l-inspect ul li { + margin-bottom: 20px; } + /* line 48, ../../../../general/res/sass/_inspector.scss */ + .l-inspect em { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background-color: #efefef; + color: #969696; + margin-bottom: 5px; + padding: 5px 5px; + text-transform: uppercase; } + /* line 58, ../../../../general/res/sass/_inspector.scss */ + .l-inspect table tr td { + border: none; + border-top: 1px solid rgba(102, 102, 102, 0.2) !important; + padding: 2px 0; + vertical-align: top; } + /* line 63, ../../../../general/res/sass/_inspector.scss */ + .l-inspect table tr td.label { + color: #999999 !important; + padding-right: 5px !important; + white-space: nowrap; } + /* line 68, ../../../../general/res/sass/_inspector.scss */ + .l-inspect table tr td.value { + word-break: break-all; } + /* line 73, ../../../../general/res/sass/_inspector.scss */ + .l-inspect table tr:first-child td { + border-top: none !important; } + /* line 78, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location { + line-height: 180%; } + /* line 80, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location .location-item { + cursor: pointer; + display: inline; + position: relative; + padding: 2px 4px; } + /* line 85, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location .location-item:hover { + background: rgba(102, 102, 102, 0.1); + color: #333333; } + /* line 88, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location .location-item:hover .icon { + color: #0099cc; } + /* line 93, ../../../../general/res/sass/_inspector.scss */ + .l-inspect .inspector-location:not(.first):before { + color: #8c8c8c; + content: '\3e'; + display: inline-block; + font-family: symbolsfont; + font-size: 7px; + width: 4px; } + +/********************************* CONTROLS */ +/* line 1, ../../../../general/res/sass/controls/_breadcrumb.scss */ +.l-breadcrumb { + font-size: 0.7rem; + line-height: 1em; + margin-bottom: 5px; + margin-left: -4px; } + /* line 10, ../../../../general/res/sass/controls/_breadcrumb.scss */ + .l-breadcrumb .l-breadcrumb-item a { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-transition: background-color 0.25s; + -o-transition: background-color 0.25s; + -webkit-transition: background-color 0.25s; + transition: background-color 0.25s; + color: #404040; + display: inline-block; + padding: 2px 4px; } + /* line 18, ../../../../general/res/sass/controls/_breadcrumb.scss */ + .l-breadcrumb .l-breadcrumb-item a .icon { + color: #0099cc; + margin-right: 5px; } + /* line 22, ../../../../general/res/sass/controls/_breadcrumb.scss */ + .l-breadcrumb .l-breadcrumb-item a:hover { + background: white; + color: gray; } + /* line 25, ../../../../general/res/sass/controls/_breadcrumb.scss */ + .l-breadcrumb .l-breadcrumb-item a:hover .icon { + color: #0099cc; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 25, ../../../../general/res/sass/controls/_buttons.scss */ +.s-btn, .s-menu-btn, +.s-icon-btn { + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + cursor: pointer; + text-decoration: none; + height: 25px; + line-height: 25px; } + +/* line 34, ../../../../general/res/sass/controls/_buttons.scss */ +.s-btn, .s-menu-btn { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0 7.5px; + font-size: 0.7rem; } + /* line 39, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn .icon, .s-menu-btn .icon { + font-size: 0.8rem; + color: #0099cc; } + /* line 44, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn .title-label, .s-menu-btn .title-label { + vertical-align: top; } + /* line 48, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn.lg, .lg.s-menu-btn { + font-size: 1rem; } + /* line 52, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn.sm, .sm.s-menu-btn { + padding: 0 5px; } + /* line 56, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn.vsm, .vsm.s-menu-btn { + padding: 0 2.5px; } + /* line 60, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn.major, .major.s-menu-btn { + background-color: #0099cc; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + display: inline-block; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + -moz-transition: background, 0.25s; + -o-transition: background, 0.25s; + -webkit-transition: background, 0.25s; + transition: background, 0.25s; + text-shadow: none; } + /* line 274, ../../../../general/res/sass/_mixins.scss */ + .s-btn.major .icon, .major.s-menu-btn .icon { + color: #fff; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 279, ../../../../general/res/sass/_mixins.scss */ + .s-btn.major:not(.disabled):hover, .major.s-menu-btn:not(.disabled):hover { + background: deepskyblue; } + /* line 281, ../../../../general/res/sass/_mixins.scss */ + .s-btn.major:not(.disabled):hover > .icon, .major.s-menu-btn:not(.disabled):hover > .icon { + color: white; } } + /* line 66, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn:not(.major), .s-menu-btn:not(.major) { + background-color: #969696; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + display: inline-block; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + -moz-transition: background, 0.25s; + -o-transition: background, 0.25s; + -webkit-transition: background, 0.25s; + transition: background, 0.25s; + text-shadow: none; } + /* line 274, ../../../../general/res/sass/_mixins.scss */ + .s-btn:not(.major) .icon, .s-menu-btn:not(.major) .icon { + color: #eee; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 279, ../../../../general/res/sass/_mixins.scss */ + .s-btn:not(.major):not(.disabled):hover, .s-menu-btn:not(.major):not(.disabled):hover { + background: #0099cc; } + /* line 281, ../../../../general/res/sass/_mixins.scss */ + .s-btn:not(.major):not(.disabled):hover > .icon, .s-menu-btn:not(.major):not(.disabled):hover > .icon { + color: white; } } + /* line 75, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn.pause-play .icon:before, .pause-play.s-menu-btn .icon:before { + content: "\0000F1"; } + /* line 78, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn.pause-play.paused, .pause-play.paused.s-menu-btn { + background-color: #ff9900; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + display: inline-block; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + -moz-transition: background, 0.25s; + -o-transition: background, 0.25s; + -webkit-transition: background, 0.25s; + transition: background, 0.25s; + text-shadow: none; } + /* line 274, ../../../../general/res/sass/_mixins.scss */ + .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { + color: #fff; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 279, ../../../../general/res/sass/_mixins.scss */ + .s-btn.pause-play.paused:not(.disabled):hover, .pause-play.paused.s-menu-btn:not(.disabled):hover { + background: #ffad33; } + /* line 281, ../../../../general/res/sass/_mixins.scss */ + .s-btn.pause-play.paused:not(.disabled):hover > .icon, .pause-play.paused.s-menu-btn:not(.disabled):hover > .icon { + color: white; } } + /* line 80, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn.pause-play.paused .icon, .pause-play.paused.s-menu-btn .icon { + -moz-animation-name: pulse; + -webkit-animation-name: pulse; + animation-name: pulse; + -moz-animation-duration: 1000ms; + -webkit-animation-duration: 1000ms; + animation-duration: 1000ms; + -moz-animation-direction: alternate; + -webkit-animation-direction: alternate; + animation-direction: alternate; + -moz-animation-iteration-count: infinite; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + -moz-animation-timing-function: ease-in-out; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; } + /* line 82, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn.pause-play.paused .icon :before, .pause-play.paused.s-menu-btn .icon :before { + content: "\0000EF"; } + /* line 90, ../../../../general/res/sass/controls/_buttons.scss */ + .s-btn.show-thumbs .icon:before, .show-thumbs.s-menu-btn .icon:before { + content: "\000039"; } + +/* line 96, ../../../../general/res/sass/controls/_buttons.scss */ +.s-icon-btn { + color: #eee; } + /* line 99, ../../../../general/res/sass/controls/_buttons.scss */ + .s-icon-btn:hover { + color: white; } + +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 104, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + color: #d6d6d6; + cursor: pointer; + display: block; + position: absolute; + font-size: 12px; + line-height: 12px; + height: 12px; + width: 12px; } + /* line 127, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover { + color: #a3a3a3; } + /* line 129, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:hover:after { + color: #0099cc; } + /* line 134, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before, .mini-tab:after { + -moz-transition-property: color; + -o-transition-property: color; + -webkit-transition-property: color; + transition-property: color; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + display: block; + position: absolute; } + /* line 141, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:before { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + font-size: 7px; + height: 100%; + width: 7px; } + /* line 148, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab:after { + width: 100%; + height: 100%; } + /* line 158, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:before { + content: '\3c'; + left: -7px; } + /* line 162, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left:hover:before { + left: -9px; } + /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left.collapsed:before { + content: '\3e'; + left: 12px; } + /* line 169, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-left.collapsed:hover:before { + left: 14px; } + /* line 175, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:before { + content: '\3e'; + left: 12px; } + /* line 179, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right:hover:before { + left: 14px; } + /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right.collapsed:before { + content: '\3c'; + left: -7px; } + /* line 186, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab.anchor-right.collapsed:hover:before { + left: -9px; } } + +/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ +.l-btn-set { + font-size: 0; } + /* line 202, ../../../../general/res/sass/controls/_buttons.scss */ + .l-btn-set .s-btn, .l-btn-set .s-menu-btn { + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; + margin-left: 1px; } + /* line 208, ../../../../general/res/sass/controls/_buttons.scss */ + .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { + -moz-border-radius-topleft: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + margin-left: 0; } + /* line 215, ../../../../general/res/sass/controls/_buttons.scss */ + .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; } + +/* line 222, ../../../../general/res/sass/controls/_buttons.scss */ +.paused:not(.s-btn):not(.s-menu-btn) { + border-color: #ff9900 !important; + color: #ff9900 !important; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/controls/_color-palette.scss */ +.l-color-palette { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 5px !important; } + /* line 31, ../../../../general/res/sass/controls/_color-palette.scss */ + .l-color-palette .l-palette-row { + overflow: hidden; + *zoom: 1; + line-height: 16px; + width: 170px; } + /* line 36, ../../../../general/res/sass/controls/_color-palette.scss */ + .l-color-palette .l-palette-row .l-palette-item { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + text-shadow: rgba(0, 0, 0, 0.8) 0 1px 2px; + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + border: 1px solid transparent; + color: #fff; + display: block; + font-family: 'symbolsfont'; + float: left; + height: 16px; + width: 16px; + line-height: 16px; + margin: 0 1px 1px 0; + text-align: center; + vertical-align: middle; } + /* line 53, ../../../../general/res/sass/controls/_color-palette.scss */ + .l-color-palette .l-palette-row .s-palette-item:hover { + -moz-transition-property: none; + -o-transition-property: none; + -webkit-transition-property: none; + transition-property: none; + border-color: #fff !important; } + /* line 59, ../../../../general/res/sass/controls/_color-palette.scss */ + .l-color-palette .l-palette-row .l-palette-item-label { + margin-left: 5px; } + /* line 63, ../../../../general/res/sass/controls/_color-palette.scss */ + .l-color-palette .l-palette-row.l-option-row { + margin-bottom: 5px; } + /* line 65, ../../../../general/res/sass/controls/_color-palette.scss */ + .l-color-palette .l-palette-row.l-option-row .s-palette-item { + border-color: #666; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/*.control { + // UNUSED? + &.view-control { + .icon { + display: inline-block; + margin: -1px 5px 1px 2px; + vertical-align: middle; + &.triangle-down { + margin: 2px 2px -2px 0px; + } + } + + .label { + display: inline-block; + font-size: 11px; + vertical-align: middle; + } + + .toggle { + @include border-radius(3px); + display: inline-block; + padding: 1px 6px 4px 4px; + &:hover { + background: rgba(white, 0.1); + } + } + } +}*/ +/* line 51, ../../../../general/res/sass/controls/_controls.scss */ +.accordion { + margin-top: 5px; } + /* line 54, ../../../../general/res/sass/controls/_controls.scss */ + .accordion:first-child { + margin-top: 0; } + /* line 57, ../../../../general/res/sass/controls/_controls.scss */ + .accordion .accordion-head { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + background: rgba(102, 102, 102, 0.2); + cursor: pointer; + font-size: 0.75em; + line-height: 18px; + margin-bottom: 5px; + padding: 0 5px; + position: absolute; + top: 0; + right: 0; + bottom: auto; + left: 0; + width: auto; + height: 18px; + text-transform: uppercase; } + /* line 75, ../../../../general/res/sass/controls/_controls.scss */ + .accordion .accordion-head:hover { + background: rgba(102, 102, 102, 0.4); } + /* line 78, ../../../../general/res/sass/controls/_controls.scss */ + .accordion .accordion-head:after { + content: "^"; + display: block; + font-family: 'symbolsfont'; + font-size: 0.9em; + position: absolute; + right: 5px; + text-transform: none; + top: 0; } + /* line 88, ../../../../general/res/sass/controls/_controls.scss */ + .accordion .accordion-head:not(.expanded):after { + content: "v"; } + /* line 92, ../../../../general/res/sass/controls/_controls.scss */ + .accordion .accordion-contents { + position: absolute; + top: 23px; + right: 0; + bottom: 0; + left: 0; + overflow-y: auto; + overflow-x: hidden; } + +/* line 103, ../../../../general/res/sass/controls/_controls.scss */ +.l-composite-control { + vertical-align: middle; } + /* line 106, ../../../../general/res/sass/controls/_controls.scss */ + .l-composite-control.l-checkbox .composite-control-label { + line-height: 18px; } + +/* line 112, ../../../../general/res/sass/controls/_controls.scss */ +.l-control-group { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + border-left: 1px solid rgba(102, 102, 102, 0.2); + display: inline-block; + padding: 0 5px; + position: relative; } + /* line 120, ../../../../general/res/sass/controls/_controls.scss */ + .l-control-group:first-child { + border-left: none; + padding-left: 0; } + +/* line 126, ../../../../general/res/sass/controls/_controls.scss */ +.l-local-controls { + position: absolute; + top: 5px; + right: 5px; + z-index: 5; } + +/* line 136, ../../../../general/res/sass/controls/_controls.scss */ +.s-local-controls { + font-size: 0.7rem; } + +/* line 140, ../../../../general/res/sass/controls/_controls.scss */ +label.checkbox.custom { + cursor: pointer; + display: inline-block; + line-height: 14px; + margin-right: 20px; + padding-left: 19px; + position: relative; + vertical-align: middle; } + /* line 150, ../../../../general/res/sass/controls/_controls.scss */ + label.checkbox.custom em { + color: #666; + display: inline-block; + height: 14px; + min-width: 14px; } + /* line 155, ../../../../general/res/sass/controls/_controls.scss */ + label.checkbox.custom em:before { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + background: #e3e3e3; + -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 2px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 2px; + box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 2px; + box-sizing: border-box; + content: " "; + font-family: 'symbolsfont'; + font-size: 0.8em; + display: inline-block; + margin-right: 5px; + height: 14px; + width: 14px; + left: 0; + top: 0; + position: absolute; + text-align: center; } + /* line 174, ../../../../general/res/sass/controls/_controls.scss */ + label.checkbox.custom.no-text { + overflow: hidden; + margin-right: 0; + padding-left: 0; + height: 14px; + width: 14px; } + /* line 180, ../../../../general/res/sass/controls/_controls.scss */ + label.checkbox.custom.no-text em { + overflow: hidden; } + /* line 184, ../../../../general/res/sass/controls/_controls.scss */ + label.checkbox.custom input { + display: none; } + /* line 186, ../../../../general/res/sass/controls/_controls.scss */ + label.checkbox.custom input:checked ~ em:before { + background: #0099cc; + color: #ccf2ff; + content: "2"; } + +/* line 194, ../../../../general/res/sass/controls/_controls.scss */ +.input-labeled { + margin-left: 5px; } + /* line 196, ../../../../general/res/sass/controls/_controls.scss */ + .input-labeled label { + display: inline-block; + margin-right: 3px; } + /* line 200, ../../../../general/res/sass/controls/_controls.scss */ + .input-labeled.inline { + display: inline-block; } + /* line 203, ../../../../general/res/sass/controls/_controls.scss */ + .input-labeled:first-child { + margin-left: 0; } + +/* line 208, ../../../../general/res/sass/controls/_controls.scss */ +.s-menu-btn label.checkbox.custom { + margin-left: 5px; } + +/* line 213, ../../../../general/res/sass/controls/_controls.scss */ +.item .checkbox.checked label { + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; + border-bottom: none; } + +/* line 219, ../../../../general/res/sass/controls/_controls.scss */ +.context-available { + color: #0099cc; } + /* line 222, ../../../../general/res/sass/controls/_controls.scss */ + .context-available:hover { + color: deepskyblue; } + +/* line 227, ../../../../general/res/sass/controls/_controls.scss */ +.view-switcher { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 100ms; + -o-transition-duration: 100ms; + -webkit-transition-duration: 100ms; + transition-duration: 100ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; } + +/******************************************************** OBJECT-HEADER */ +/* line 232, ../../../../general/res/sass/controls/_controls.scss */ +.object-header { + font-size: 1em; } + /* line 243, ../../../../general/res/sass/controls/_controls.scss */ + .object-header > .type-icon { + color: #b3b3b3; + font-size: 120%; + float: left; + margin-right: 5px; } + /* line 250, ../../../../general/res/sass/controls/_controls.scss */ + .object-header .l-elem-wrapper { + -webkit-justify-content: flex-start; + justify-content: flex-start; } + /* line 253, ../../../../general/res/sass/controls/_controls.scss */ + .object-header .l-elem-wrapper mct-representation { + min-width: 0.7em; } + /* line 261, ../../../../general/res/sass/controls/_controls.scss */ + .object-header .action { + margin-right: 5px; } + /* line 265, ../../../../general/res/sass/controls/_controls.scss */ + .object-header .title-label { + color: #666; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 0 1 auto; + -webkit-flex: 0 1 auto; + padding-right: 0.35em; } + /* line 275, ../../../../general/res/sass/controls/_controls.scss */ + .object-header .context-available { + font-size: 0.7em; + flex: 0 0 1; + -webkit-flex: 0 0 1; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 282, ../../../../general/res/sass/controls/_controls.scss */ + .object-header .context-available { + -moz-transition-property: opacity; + -o-transition-property: opacity; + -webkit-transition-property: opacity; + transition-property: opacity; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + opacity: 0; } + /* line 287, ../../../../general/res/sass/controls/_controls.scss */ + .object-header:hover .context-available { + opacity: 1; } } + +/******************************************************** SLIDERS */ +/* line 298, ../../../../general/res/sass/controls/_controls.scss */ +.slider .slot { + width: auto; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; } +/* line 308, ../../../../general/res/sass/controls/_controls.scss */ +.slider .knob { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + background-color: rgba(0, 153, 204, 0.5); + position: absolute; + height: 100%; + width: 10px; + top: 0; + auto: 0; + bottom: auto; + left: auto; } + /* line 311, ../../../../general/res/sass/controls/_controls.scss */ + .slider .knob:hover { + background-color: rgba(0, 153, 204, 0.7); } +/* line 322, ../../../../general/res/sass/controls/_controls.scss */ +.slider .knob-l { + -moz-border-radius-topleft: 10px; + -webkit-border-top-left-radius: 10px; + border-top-left-radius: 10px; + -moz-border-radius-bottomleft: 10px; + -webkit-border-bottom-left-radius: 10px; + border-bottom-left-radius: 10px; + cursor: w-resize; } +/* line 326, ../../../../general/res/sass/controls/_controls.scss */ +.slider .knob-r { + -moz-border-radius-topright: 10px; + -webkit-border-top-right-radius: 10px; + border-top-right-radius: 10px; + -moz-border-radius-bottomright: 10px; + -webkit-border-bottom-right-radius: 10px; + border-bottom-right-radius: 10px; + cursor: e-resize; } +/* line 330, ../../../../general/res/sass/controls/_controls.scss */ +.slider .range { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + background-color: rgba(0, 153, 204, 0.2); + cursor: ew-resize; + position: absolute; + top: 0; + right: auto; + bottom: 0; + left: auto; + height: auto; + width: auto; } + /* line 341, ../../../../general/res/sass/controls/_controls.scss */ + .slider .range:hover { + background-color: rgba(0, 153, 204, 0.4); } + +/******************************************************** DATETIME PICKER */ +/* line 348, ../../../../general/res/sass/controls/_controls.scss */ +.l-datetime-picker { + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + font-size: 0.8rem; + padding: 10px !important; + width: 230px; } + /* line 354, ../../../../general/res/sass/controls/_controls.scss */ + .l-datetime-picker .l-month-year-pager { + height: 15px; + margin-bottom: 5px; + position: relative; } + /* line 366, ../../../../general/res/sass/controls/_controls.scss */ + .l-datetime-picker .l-month-year-pager .pager { + width: 20px; } + /* line 369, ../../../../general/res/sass/controls/_controls.scss */ + .l-datetime-picker .l-month-year-pager .pager.prev { + right: auto; } + /* line 371, ../../../../general/res/sass/controls/_controls.scss */ + .l-datetime-picker .l-month-year-pager .pager.prev:before { + content: "\3c"; } + /* line 375, ../../../../general/res/sass/controls/_controls.scss */ + .l-datetime-picker .l-month-year-pager .pager.next { + left: auto; + text-align: right; } + /* line 378, ../../../../general/res/sass/controls/_controls.scss */ + .l-datetime-picker .l-month-year-pager .pager.next:before { + content: "\3e"; } + /* line 383, ../../../../general/res/sass/controls/_controls.scss */ + .l-datetime-picker .l-month-year-pager .val { + text-align: center; + left: 25px; + right: 25px; } + /* line 389, ../../../../general/res/sass/controls/_controls.scss */ + .l-datetime-picker .l-calendar, + .l-datetime-picker .l-time-selects { + border-top: 1px solid rgba(102, 102, 102, 0.2); } + /* line 393, ../../../../general/res/sass/controls/_controls.scss */ + .l-datetime-picker .l-time-selects { + line-height: 22px; } + +/******************************************************** CALENDAR */ +/* line 401, ../../../../general/res/sass/controls/_controls.scss */ +.l-calendar ul.l-cal-row { + display: -webkit-flex; + display: flex; + -webkit-flex-flow: row nowrap; + flex-flow: row nowrap; + margin-top: 1px; } + /* line 405, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row:first-child { + margin-top: 0; } + /* line 408, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row li { + -webkit-flex: 1 0; + flex: 1 0; + margin-left: 1px; + padding: 5px; + text-align: center; } + /* line 414, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row li:first-child { + margin-left: 0; } + /* line 418, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row.l-header li { + color: #999999; } + /* line 421, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row.l-body li { + -moz-transition-property: background-color; + -o-transition-property: background-color; + -webkit-transition-property: background-color; + transition-property: background-color; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + cursor: pointer; } + /* line 424, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row.l-body li.in-month { + background-color: #f2f2f2; } + /* line 427, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row.l-body li .sub { + color: #999999; + font-size: 0.8em; } + /* line 431, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row.l-body li.selected { + background: #1ac6ff; + color: #fcfcfc; } + /* line 434, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row.l-body li.selected .sub { + color: inherit; } + /* line 438, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row.l-body li:hover { + background-color: #0099cc; + color: #fff; } + /* line 441, ../../../../general/res/sass/controls/_controls.scss */ + .l-calendar ul.l-cal-row.l-body li:hover .sub { + color: inherit; } + +/******************************************************** BROWSER ELEMENTS */ +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 452, ../../../../general/res/sass/controls/_controls.scss */ + ::-webkit-scrollbar { + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 2px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 2px; + box-shadow: inset rgba(0, 0, 0, 0.2) 0 1px 2px; + background-color: rgba(0, 0, 0, 0.1); + height: 10px; + width: 10px; } + + /* line 461, ../../../../general/res/sass/controls/_controls.scss */ + ::-webkit-scrollbar-thumb { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzg5ODk4OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzdkN2Q3ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 20, color-stop(0%, #898989), color-stop(100%, #7d7d7d)); + background-image: -moz-linear-gradient(#898989, #7d7d7d 20px); + background-image: -webkit-linear-gradient(#898989, #7d7d7d 20px); + background-image: linear-gradient(#898989, #7d7d7d 20px); + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; } + /* line 470, ../../../../general/res/sass/controls/_controls.scss */ + ::-webkit-scrollbar-thumb:hover { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwYWNlNiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwOTljYyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -webkit-gradient(linear, 50% 0%, 50% 20, color-stop(0%, #00ace6), color-stop(100%, #0099cc)); + background-image: -moz-linear-gradient(#00ace6, #0099cc 20px); + background-image: -webkit-linear-gradient(#00ace6, #0099cc 20px); + background-image: linear-gradient(#00ace6, #0099cc 20px); } + + /* line 475, ../../../../general/res/sass/controls/_controls.scss */ + ::-webkit-scrollbar-corner { + background: rgba(0, 0, 0, 0.1); } } +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 23, ../../../../general/res/sass/controls/_lists.scss */ +.checkbox-list label.checkbox.custom { + display: block; + margin-bottom: 5px; } +/* line 27, ../../../../general/res/sass/controls/_lists.scss */ +.checkbox-list li { + margin-bottom: 5px; } + +/* line 35, ../../../../general/res/sass/controls/_lists.scss */ +.l-tree-item-flat-list .tree-item .label { + left: 5px !important; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/******************************************************** MENU BUTTONS */ +/* line 31, ../../../../general/res/sass/controls/_menus.scss */ +.s-menu-btn .icon { + font-size: 120%; } +/* line 35, ../../../../general/res/sass/controls/_menus.scss */ +.s-menu-btn .title-label { + margin-left: 3px; } +/* line 39, ../../../../general/res/sass/controls/_menus.scss */ +.s-menu-btn:after { + text-shadow: none; + content: '\76'; + display: inline-block; + font-family: 'symbolsfont'; + margin-left: 3px; + vertical-align: top; + color: rgba(255, 255, 255, 0.4); } +/* line 46, ../../../../general/res/sass/controls/_menus.scss */ +.s-menu-btn.create-btn .title-label { + font-size: 1rem; } +/* line 54, ../../../../general/res/sass/controls/_menus.scss */ +.s-menu-btn .menu { + left: 0; + text-align: left; } + /* line 57, ../../../../general/res/sass/controls/_menus.scss */ + .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .icon.s-icon-btn, .s-menu-btn .menu .icon.mini-tab, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { + width: 12px; } + +/******************************************************** MENUS THEMSELVES */ +/* line 64, ../../../../general/res/sass/controls/_menus.scss */ +.menu-element { + cursor: pointer; + position: relative; } + +/* line 69, ../../../../general/res/sass/controls/_menus.scss */ +.s-menu, .menu { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background-color: white; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #4d4d4d; + display: inline-block; + -moz-box-shadow: rgba(0, 0, 0, 0.5) 0 1px 5px; + -webkit-box-shadow: rgba(0, 0, 0, 0.5) 0 1px 5px; + box-shadow: rgba(0, 0, 0, 0.5) 0 1px 5px; + text-shadow: none; + padding: 3px 0; } + +/* line 77, ../../../../general/res/sass/controls/_menus.scss */ +.menu { + display: block; + position: absolute; + z-index: 10; } + /* line 82, ../../../../general/res/sass/controls/_menus.scss */ + .menu ul { + margin: 0; + padding: 0; } + /* line 331, ../../../../general/res/sass/_mixins.scss */ + .menu ul li { + list-style-type: none; + margin: 0; + padding: 0; } + /* line 84, ../../../../general/res/sass/controls/_menus.scss */ + .menu ul li { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + border-top: 1px solid white; + color: #666666; + line-height: 1.5rem; + padding: 3px 10px 3px 30px; + position: relative; + white-space: nowrap; } + /* line 92, ../../../../general/res/sass/controls/_menus.scss */ + .menu ul li:first-child { + border: none; } + /* line 95, ../../../../general/res/sass/controls/_menus.scss */ + .menu ul li:hover { + background: #e6e6e6; + color: #4d4d4d; } + /* line 98, ../../../../general/res/sass/controls/_menus.scss */ + .menu ul li:hover .icon { + color: #0099cc; } + /* line 102, ../../../../general/res/sass/controls/_menus.scss */ + .menu ul li .type-icon { + left: 10px; } + +/* line 109, ../../../../general/res/sass/controls/_menus.scss */ +.menu, +.context-menu, +.checkbox-menu, +.super-menu { + pointer-events: auto; } + /* line 115, ../../../../general/res/sass/controls/_menus.scss */ + .menu ul li a, + .context-menu ul li a, + .checkbox-menu ul li a, + .super-menu ul li a { + color: #4d4d4d; } + /* line 118, ../../../../general/res/sass/controls/_menus.scss */ + .menu ul li .icon, + .context-menu ul li .icon, + .checkbox-menu ul li .icon, + .super-menu ul li .icon { + color: #0099cc; } + /* line 121, ../../../../general/res/sass/controls/_menus.scss */ + .menu ul li .type-icon, + .context-menu ul li .type-icon, + .checkbox-menu ul li .type-icon, + .super-menu ul li .type-icon { + left: 5px; } + +/* line 133, ../../../../general/res/sass/controls/_menus.scss */ +.checkbox-menu ul li { + padding-left: 50px; } + /* line 135, ../../../../general/res/sass/controls/_menus.scss */ + .checkbox-menu ul li .checkbox { + position: absolute; + left: 5px; + top: 0.53333rem; } + /* line 140, ../../../../general/res/sass/controls/_menus.scss */ + .checkbox-menu ul li .checkbox em { + height: 0.7rem; + width: 0.7rem; } + /* line 143, ../../../../general/res/sass/controls/_menus.scss */ + .checkbox-menu ul li .checkbox em:before { + font-size: 7px !important; + height: 0.7rem; + width: 0.7rem; + line-height: 0.7rem; } + /* line 151, ../../../../general/res/sass/controls/_menus.scss */ + .checkbox-menu ul li .type-icon { + left: 25px; } + +/* line 157, ../../../../general/res/sass/controls/_menus.scss */ +.super-menu { + display: block; + width: 500px; + height: 480px; } + /* line 165, ../../../../general/res/sass/controls/_menus.scss */ + .super-menu .contents { + overflow: hidden; + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; + width: auto; + height: auto; } + /* line 168, ../../../../general/res/sass/controls/_menus.scss */ + .super-menu .pane { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; } + /* line 170, ../../../../general/res/sass/controls/_menus.scss */ + .super-menu .pane.left { + border-right: 1px solid #e6e6e6; + left: 0; + padding-right: 5px; + right: auto; + width: 50%; + overflow-x: hidden; + overflow-y: auto; } + /* line 180, ../../../../general/res/sass/controls/_menus.scss */ + .super-menu .pane.left ul li { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + padding-left: 30px; + border-top: none; } + /* line 187, ../../../../general/res/sass/controls/_menus.scss */ + .super-menu .pane.right { + left: auto; + right: 0; + padding: 25px; + width: 50%; } + /* line 197, ../../../../general/res/sass/controls/_menus.scss */ + .super-menu .menu-item-description .desc-area.icon { + color: #0099cc; + position: relative; + font-size: 8em; + left: 0; + height: 150px; + line-height: 150px; + margin-bottom: 25px; + text-align: center; } + /* line 208, ../../../../general/res/sass/controls/_menus.scss */ + .super-menu .menu-item-description .desc-area.title { + color: #666; + font-size: 1.2em; + margin-bottom: 0.5em; } + /* line 213, ../../../../general/res/sass/controls/_menus.scss */ + .super-menu .menu-item-description .desc-area.description { + color: #666; + font-size: 0.8em; + line-height: 1.5em; } + +/* line 222, ../../../../general/res/sass/controls/_menus.scss */ +.context-menu, .checkbox-menu { + font-size: 0.80rem; } + +/* line 226, ../../../../general/res/sass/controls/_menus.scss */ +.context-menu-holder, +.menu-holder { + position: absolute; + z-index: 70; } + /* line 230, ../../../../general/res/sass/controls/_menus.scss */ + .context-menu-holder .context-menu-wrapper, + .menu-holder .context-menu-wrapper { + position: absolute; + height: 100%; + width: 100%; } + /* line 235, ../../../../general/res/sass/controls/_menus.scss */ + .context-menu-holder.go-left .context-menu, .context-menu-holder.go-left .checkbox-menu, .context-menu-holder.go-left .menu, + .menu-holder.go-left .context-menu, + .menu-holder.go-left .checkbox-menu, + .menu-holder.go-left .menu { + right: 0; } + /* line 239, ../../../../general/res/sass/controls/_menus.scss */ + .context-menu-holder.go-up .context-menu, .context-menu-holder.go-up .checkbox-menu, .context-menu-holder.go-up .menu, + .menu-holder.go-up .context-menu, + .menu-holder.go-up .checkbox-menu, + .menu-holder.go-up .menu { + bottom: 0; } + +/* line 245, ../../../../general/res/sass/controls/_menus.scss */ +.context-menu-holder { + pointer-events: none; + height: 200px; + width: 170px; } + +/* line 251, ../../../../general/res/sass/controls/_menus.scss */ +.btn-bar.right .menu, +.menus-to-left .menu { + left: auto; + right: 0; + width: auto; } + +/* line 13, ../../../../general/res/sass/controls/_time-controller.scss */ +mct-include.l-time-controller { + overflow: hidden; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + width: auto; + height: auto; + display: block; + top: auto; + height: 83px; + min-width: 500px; + font-size: 0.8rem; } + /* line 38, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-inputs-holder, + mct-include.l-time-controller .l-time-range-slider-holder, + mct-include.l-time-controller .l-time-range-ticks-holder { + overflow: visible; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + top: auto; } + /* line 47, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-slider, + mct-include.l-time-controller .l-time-range-ticks { + overflow: visible; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; + left: 150px; + right: 150px; } + /* line 54, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-inputs-holder { + height: 33px; + bottom: 46px; + padding-top: 5px; + border-top: 1px solid rgba(102, 102, 102, 0.2); } + /* line 59, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-inputs-holder .type-icon { + font-size: 120%; + vertical-align: middle; } + /* line 63, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem { + margin-right: 5px; } + /* line 66, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .lbl, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .lbl { + color: #999999; } + /* line 69, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .ui-symbol.icon, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.s-icon-btn, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.mini-tab, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .l-datetime-picker .l-month-year-pager .icon.pager, + .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.pager { + font-size: 11px; + width: 11px; } + /* line 76, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-slider-holder { + height: 20px; + bottom: 23px; } + /* line 79, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-slider-holder .range-holder { + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; + background: none; + border: none; } + /* line 84, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line { + -moz-transform: translateX(50%); + -ms-transform: translateX(50%); + -webkit-transform: translateX(50%); + transform: translateX(50%); + position: absolute; + top: 0; + right: 0; + bottom: 0px; + left: auto; + width: 8px; + height: auto; + z-index: 2; } + /* line 94, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line:before, mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line:after { + background-color: #666; + content: ""; + position: absolute; } + /* line 100, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line:before { + top: 0; + right: auto; + bottom: -10px; + left: 3px; + width: 2px; } + /* line 106, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range .toi-line:after { + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + border-radius: 8px; + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -webkit-transform: translateY(-50%); + transform: translateY(-50%); + top: 50%; + right: 0; + bottom: auto; + left: 0; + width: auto; + height: 8px; } + /* line 3, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range:hover .toi-line:before, mct-include.l-time-controller .l-time-range-slider-holder .range-holder .range:hover .toi-line:after { + background-color: #0052b5; } + /* line 122, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-slider-holder:not(:active) .knob, + mct-include.l-time-controller .l-time-range-slider-holder:not(:active) .range { + -moz-transition-property: left, right; + -o-transition-property: left, right; + -webkit-transition-property: left, right; + transition-property: left, right; + -moz-transition-duration: 500ms; + -o-transition-duration: 500ms; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } + /* line 131, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-ticks-holder { + height: 20px; } + /* line 133, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-ticks-holder .l-time-range-ticks { + border-top: 1px solid rgba(0, 0, 0, 0.2); } + /* line 135, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-ticks-holder .l-time-range-ticks .tick { + background-color: rgba(0, 0, 0, 0.2); + border: none; + height: 5px; + width: 1px; + margin-left: -1px; + position: absolute; } + /* line 142, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-ticks-holder .l-time-range-ticks .tick:first-child { + margin-left: 0; } + /* line 145, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .l-time-range-ticks-holder .l-time-range-ticks .tick .l-time-range-tick-label { + transform: translateX(-50%); + -webkit-transform: translateX(-50%); + color: #999999; + display: inline-block; + font-size: 0.9em; + position: absolute; + top: 8px; + white-space: nowrap; + z-index: 2; } + /* line 159, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .knob { + z-index: 2; } + /* line 161, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .knob .range-value { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 0.25s; + -o-transition-duration: 0.25s; + -webkit-transition-duration: 0.25s; + transition-duration: 0.25s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + padding: 0 10px; + position: absolute; + height: 20px; + line-height: 20px; + white-space: nowrap; } + /* line 170, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .knob:hover .range-value { + color: rgba(0, 153, 204, 0.7); } + /* line 173, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .knob.knob-l { + margin-left: -10px; } + /* line 176, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .knob.knob-l .range-value { + text-align: right; + right: 10px; } + /* line 181, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .knob.knob-r { + margin-right: -10px; } + /* line 184, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .knob.knob-r .range-value { + left: 10px; } + /* line 3, ../../../../general/res/sass/controls/_time-controller.scss */ + mct-include.l-time-controller .knob.knob-r:hover + .range-holder .range .toi-line:before, mct-include.l-time-controller .knob.knob-r:hover + .range-holder .range .toi-line:after { + background-color: #0052b5; } + +/* line 198, ../../../../general/res/sass/controls/_time-controller.scss */ +.s-time-range-val { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background-color: #fff; + padding: 1px 1px 0 5px; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 26, ../../../../general/res/sass/mobile/controls/_menus.scss */ + .menu-element .super-menu { + width: 250px; + height: 250px; } + /* line 32, ../../../../general/res/sass/mobile/controls/_menus.scss */ + .menu-element .super-menu .pane.left { + border-right: none; + padding-right: 0; + width: 100%; } + /* line 37, ../../../../general/res/sass/mobile/controls/_menus.scss */ + .menu-element .super-menu .pane.right { + display: none; } } +/********************************* FORMS */ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/forms/_elems.scss */ +.section-header { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background: rgba(0, 0, 0, 0.05); + color: #999999; + font-size: 0.8em; + padding: 5px 5px; + text-transform: uppercase; } + +/* line 33, ../../../../general/res/sass/forms/_elems.scss */ +.form { + color: gray; } + /* line 36, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-section { + position: relative; + margin-bottom: 20px; } + /* line 41, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + *zoom: 1; + border-top: 1px solid rgba(0, 0, 0, 0.1); + margin-top: 5px; + padding: 5px 0; + position: relative; } + /* line 49, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row.first { + border-top: none; } + /* line 53, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row > .label, + .form .form-row > .controls { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + *zoom: 1; + font-size: 0.8rem; + line-height: 22px; + min-height: 22px; } + /* line 62, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row > .label { + float: left; + min-width: 120px; + position: relative; + white-space: nowrap; + width: 30%; } + /* line 72, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .value { + color: #666; } + /* line 76, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .controls { + float: left; + position: relative; + width: 69.9%; } + /* line 83, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .controls .l-composite-control.l-checkbox { + display: inline-block; + line-height: 14px; + margin-right: 5px; } + /* line 92, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .controls .l-med input[type="text"] { + width: 200px; } + /* line 96, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .controls .l-small input[type="text"] { + width: 50px; } + /* line 100, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .controls .l-numeric input[type="text"] { + text-align: right; } + /* line 104, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .controls .select { + margin-right: 5px; } + /* line 109, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .field-hints { + color: #333333; } + /* line 113, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .selector-list { + -moz-appearance: none; + -webkit-appearance: none; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + background: #fff; + border: none; + color: #666; + outline: none; + padding: 0 3px; + position: relative; + height: 150px; } + /* line 298, ../../../../general/res/sass/_mixins.scss */ + .form .form-row .selector-list.error { + background: rgba(255, 0, 0, 0.5); } + /* line 124, ../../../../general/res/sass/forms/_elems.scss */ + .form .form-row .selector-list > .wrapper { + overflow: auto; + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; } + +/* line 138, ../../../../general/res/sass/forms/_elems.scss */ +label.form-control.checkbox input { + margin-right: 5px; + vertical-align: top; } + +/* line 144, ../../../../general/res/sass/forms/_elems.scss */ +.hint, +.s-hint { + font-size: 0.9em; } + +/* line 149, ../../../../general/res/sass/forms/_elems.scss */ +.l-result { + display: inline-block; + min-width: 32px; + min-height: 32px; + position: relative; + vertical-align: top; } + /* line 156, ../../../../general/res/sass/forms/_elems.scss */ + .l-result div.s-hint { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background: rgba(255, 34, 0, 0.8); + display: block; + color: #ffa799; + padding: 5px; } + +/* line 165, ../../../../general/res/sass/forms/_elems.scss */ +input[type="text"] { + -moz-appearance: none; + -webkit-appearance: none; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + background: #fff; + border: none; + color: #666; + outline: none; + padding: 0 3px; } + /* line 298, ../../../../general/res/sass/_mixins.scss */ + input[type="text"].error { + background: rgba(255, 0, 0, 0.5); } + /* line 172, ../../../../general/res/sass/forms/_elems.scss */ + input[type="text"].numeric { + text-align: right; } + +/* line 177, ../../../../general/res/sass/forms/_elems.scss */ +textarea { + -moz-appearance: none; + -webkit-appearance: none; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + background: #fff; + border: none; + color: #666; + outline: none; + padding: 5px; + position: absolute; + height: 100%; + width: 100%; } + /* line 298, ../../../../general/res/sass/_mixins.scss */ + textarea.error { + background: rgba(255, 0, 0, 0.5); } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/forms/_selects.scss */ +.select { + background-color: #ddd; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + display: inline-block; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + -moz-transition: background, 0.25s; + -o-transition: background, 0.25s; + -webkit-transition: background, 0.25s; + transition: background, 0.25s; + text-shadow: none; + padding: 0 5px; + overflow: hidden; + position: relative; + line-height: 22px; } + /* line 274, ../../../../general/res/sass/_mixins.scss */ + .select .icon { + color: #eee; } + /* line 31, ../../../../general/res/sass/forms/_selects.scss */ + .select select { + -moz-appearance: none; + -webkit-appearance: none; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + background: none; + color: #666; + cursor: pointer; + border: none !important; + padding: 4px 25px 2px 0px; + width: 120%; } + /* line 40, ../../../../general/res/sass/forms/_selects.scss */ + .select select option { + margin: 5px 0; } + /* line 44, ../../../../general/res/sass/forms/_selects.scss */ + .select:after { + text-shadow: none; + content: '\76'; + display: inline-block; + font-family: 'symbolsfont'; + margin-left: 3px; + vertical-align: top; + pointer-events: none; + color: rgba(102, 102, 102, 0.4); + position: absolute; + right: 5px; + top: 0; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 23, ../../../../general/res/sass/forms/_channel-selector.scss */ +.channel-selector .line { + margin-bottom: 5px; + min-height: 22px; } +/* line 27, ../../../../general/res/sass/forms/_channel-selector.scss */ +.channel-selector .treeview { + -moz-appearance: none; + -webkit-appearance: none; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + background: #fcfcfc; + border: none; + color: #666; + outline: none; + padding: 0 3px; + background: white; + border-bottom: 1px solid white; + min-height: 300px; + max-height: 400px; + overflow: auto; + padding: 5px; } + /* line 298, ../../../../general/res/sass/_mixins.scss */ + .channel-selector .treeview.error { + background: rgba(255, 0, 0, 0.5); } +/* line 36, ../../../../general/res/sass/forms/_channel-selector.scss */ +.channel-selector .btns-add-remove { + margin-top: 150px; } + /* line 39, ../../../../general/res/sass/forms/_channel-selector.scss */ + .channel-selector .btns-add-remove .s-btn, .channel-selector .btns-add-remove .s-menu-btn { + display: block; + margin-bottom: 5px; + text-align: center; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 29, ../../../../general/res/sass/forms/_datetime.scss */ +.complex.datetime { + /* + .field-hints, + .fields { + } + + + .field-hints { + + } + */ } + /* line 30, ../../../../general/res/sass/forms/_datetime.scss */ + .complex.datetime span { + display: inline-block; + margin-right: 5px; } + /* line 46, ../../../../general/res/sass/forms/_datetime.scss */ + .complex.datetime .fields { + margin-top: 3px 0; + padding: 3px 0; } + /* line 51, ../../../../general/res/sass/forms/_datetime.scss */ + .complex.datetime .date { + width: 85px; } + /* line 24, ../../../../general/res/sass/forms/_datetime.scss */ + .complex.datetime .date input[type="text"] { + width: 80px; } + /* line 55, ../../../../general/res/sass/forms/_datetime.scss */ + .complex.datetime .time.md { + width: 65px; } + /* line 24, ../../../../general/res/sass/forms/_datetime.scss */ + .complex.datetime .time.md input[type="text"] { + width: 60px; } + /* line 59, ../../../../general/res/sass/forms/_datetime.scss */ + .complex.datetime .time.sm { + width: 45px; } + /* line 24, ../../../../general/res/sass/forms/_datetime.scss */ + .complex.datetime .time.sm input[type="text"] { + width: 40px; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 23, ../../../../general/res/sass/forms/_validation.scss */ +.validates > .label { + padding-right: 10px; } + /* line 25, ../../../../general/res/sass/forms/_validation.scss */ + .validates > .label::after { + float: right; + font-family: symbolsfont; + font-size: 0.7em; } +/* line 33, ../../../../general/res/sass/forms/_validation.scss */ +.validates.invalid > .label::after, .validates.invalid.req > .label::after { + color: #ff2200; + content: "x"; } +/* line 40, ../../../../general/res/sass/forms/_validation.scss */ +.validates.valid > .label::after, .validates.valid.req > .label::after { + color: #33cc33; + content: "2"; } +/* line 46, ../../../../general/res/sass/forms/_validation.scss */ +.validates.req > .label::after { + color: #0099cc; + content: "*"; } + +/* line 52, ../../../../general/res/sass/forms/_validation.scss */ +.req { + font-size: 0.7em; } + +/* line 55, ../../../../general/res/sass/forms/_validation.scss */ +span.req { + color: #0099cc; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 24, ../../../../general/res/sass/forms/_filter.scss */ +.filter input.filter, +.filter input.t-filter-input, +.t-filter input.filter, +.t-filter input.t-filter-input { + -moz-appearance: none; + -webkit-appearance: none; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + -webkit-box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + box-shadow: inset rgba(0, 0, 0, 0.4) 0 1px 3px; + background: #fcfcfc; + border: none; + color: #666; + outline: none; + padding: 0 3px; + background: white; + border-bottom: 1px solid white; } + /* line 298, ../../../../general/res/sass/_mixins.scss */ + .filter input.filter.error, + .filter input.t-filter-input.error, + .t-filter input.filter.error, + .t-filter input.t-filter-input.error { + background: rgba(255, 0, 0, 0.5); } +/* line 28, ../../../../general/res/sass/forms/_filter.scss */ +.filter input.t-filter-input, +.t-filter input.t-filter-input { + height: 22px; + width: 200px; } + /* line 38, ../../../../general/res/sass/forms/_filter.scss */ + .filter input.t-filter-input:not(.ng-dirty) + .t-a-clear, + .t-filter input.t-filter-input:not(.ng-dirty) + .t-a-clear { + display: none; } +/* line 42, ../../../../general/res/sass/forms/_filter.scss */ +.filter .icon.ui-symbol, .filter .icon.s-icon-btn, .filter .icon.mini-tab, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, +.t-filter .icon.ui-symbol, +.t-filter .icon.s-icon-btn, +.t-filter .icon.mini-tab, +.t-filter .l-datetime-picker .l-month-year-pager .icon.pager, +.l-datetime-picker .l-month-year-pager .t-filter .icon.pager { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + display: inline-block; + font-size: 1.3em; + height: 22px; + line-height: 22px; + padding: 0px 5px; + vertical-align: middle; } + /* line 50, ../../../../general/res/sass/forms/_filter.scss */ + .filter .icon.ui-symbol:hover, .filter .icon.s-icon-btn:hover, .filter .icon.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, + .t-filter .icon.ui-symbol:hover, + .t-filter .icon.s-icon-btn:hover, + .t-filter .icon.mini-tab:hover, + .t-filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, + .l-datetime-picker .l-month-year-pager .t-filter .icon.pager:hover { + background: rgba(255, 255, 255, 0.1); } +/* line 54, ../../../../general/res/sass/forms/_filter.scss */ +.filter .s-a-clear.ui-symbol, .filter .s-a-clear.s-icon-btn, .filter .s-a-clear.mini-tab, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, +.t-filter .s-a-clear.ui-symbol, +.t-filter .s-a-clear.s-icon-btn, +.t-filter .s-a-clear.mini-tab, +.t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, +.l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); + opacity: 0.2; + background: #fff; + color: #333; + display: block; + position: absolute; + height: 13px; + width: 13px; + line-height: 13px; + margin-top: -6.5px; + overflow: hidden; + padding-top: 1px; + right: 4.5px; + top: 50%; + text-align: center; + z-index: 5; } + /* line 74, ../../../../general/res/sass/forms/_filter.scss */ + .filter .s-a-clear.ui-symbol:hover, .filter .s-a-clear.s-icon-btn:hover, .filter .s-a-clear.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, + .t-filter .s-a-clear.ui-symbol:hover, + .t-filter .s-a-clear.s-icon-btn:hover, + .t-filter .s-a-clear.mini-tab:hover, + .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, + .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager:hover { + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); + opacity: 0.6; + background-color: #0099cc; } + +/* line 82, ../../../../general/res/sass/forms/_filter.scss */ +.l-filter { + display: inline-block; + position: relative; } + +/* line 89, ../../../../general/res/sass/forms/_filter.scss */ +.top-bar input.filter { + font-size: .9em; + height: 24px; + line-height: 24px; + margin-right: 5px; + padding-left: 10px; + padding-right: 10px; + vertical-align: top; } +/* line 100, ../../../../general/res/sass/forms/_filter.scss */ +.top-bar .icon-filter { + font-size: 1.4em; } + +/********************************* USER ENVIRON */ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 32, ../../../../general/res/sass/user-environ/_layout.scss */ +.holder-all { + top: 0; + right: 0; + bottom: 0; + left: 0; } + +/* line 40, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-area, +.edit-area, +.editor { + position: absolute; } + +/* line 46, ../../../../general/res/sass/user-environ/_layout.scss */ +.editor { + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; } + +/* line 50, ../../../../general/res/sass/user-environ/_layout.scss */ +.contents { + box-sizing: border-box; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; } + /* line 58, ../../../../general/res/sass/user-environ/_layout.scss */ + .contents.nomargin { + right: 0px; + bottom: 0px; + left: 0px; } + +/* line 67, ../../../../general/res/sass/user-environ/_layout.scss */ +.bar .icon.major { + margin-right: 5px; } +/* line 70, ../../../../general/res/sass/user-environ/_layout.scss */ +.bar.abs, .l-datetime-picker .l-month-year-pager .bar.pager, +.l-datetime-picker .l-month-year-pager .bar.val, .s-menu-btn span.bar.l-click-area { + text-wrap: none; + white-space: nowrap; } + /* line 73, ../../../../general/res/sass/user-environ/_layout.scss */ + .bar.abs.left, .l-datetime-picker .l-month-year-pager .bar.left.pager, + .l-datetime-picker .l-month-year-pager .bar.left.val, .s-menu-btn span.bar.left.l-click-area, + .bar.abs .left, + .l-datetime-picker .l-month-year-pager .bar.pager .left, + .l-datetime-picker .l-month-year-pager .bar.val .left, + .s-menu-btn span.bar.l-click-area .left { + width: 45%; + right: auto; } + /* line 78, ../../../../general/res/sass/user-environ/_layout.scss */ + .bar.abs.right, .l-datetime-picker .l-month-year-pager .bar.right.pager, + .l-datetime-picker .l-month-year-pager .bar.right.val, .s-menu-btn span.bar.right.l-click-area, + .bar.abs .right, + .l-datetime-picker .l-month-year-pager .bar.pager .right, + .l-datetime-picker .l-month-year-pager .bar.val .right, + .s-menu-btn span.bar.l-click-area .right { + width: 45%; + left: auto; + text-align: right; } + /* line 83, ../../../../general/res/sass/user-environ/_layout.scss */ + .bar.abs.right .icon.major, .l-datetime-picker .l-month-year-pager .bar.right.pager .icon.major, + .l-datetime-picker .l-month-year-pager .bar.right.val .icon.major, .s-menu-btn span.bar.right.l-click-area .icon.major, + .bar.abs .right .icon.major, + .l-datetime-picker .l-month-year-pager .bar.pager .right .icon.major, + .l-datetime-picker .l-month-year-pager .bar.val .right .icon.major, + .s-menu-btn span.bar.l-click-area .right .icon.major { + margin-left: 15px; } + /* line 89, ../../../../general/res/sass/user-environ/_layout.scss */ + .bar.abs .l-flex .left, .l-datetime-picker .l-month-year-pager .bar.pager .l-flex .left, + .l-datetime-picker .l-month-year-pager .bar.val .l-flex .left, .s-menu-btn span.bar.l-click-area .l-flex .left, + .bar.abs .l-flex .right, + .l-datetime-picker .l-month-year-pager .bar.pager .l-flex .right, + .l-datetime-picker .l-month-year-pager .bar.val .l-flex .right, + .s-menu-btn span.bar.l-click-area .l-flex .right, .bar.abs.l-flex .left, .l-datetime-picker .l-month-year-pager .bar.l-flex.pager .left, + .l-datetime-picker .l-month-year-pager .bar.l-flex.val .left, .s-menu-btn span.bar.l-flex.l-click-area .left, + .bar.abs.l-flex .right, + .l-datetime-picker .l-month-year-pager .bar.l-flex.pager .right, + .l-datetime-picker .l-month-year-pager .bar.l-flex.val .right, + .s-menu-btn span.bar.l-flex.l-click-area .right { + width: auto; } + +/* line 98, ../../../../general/res/sass/user-environ/_layout.scss */ +.user-environ .browse-area, +.user-environ .edit-area, +.user-environ .editor { + top: 39px; + right: 10px; + bottom: 35px; + left: 10px; } +/* line 109, ../../../../general/res/sass/user-environ/_layout.scss */ +.user-environ .browse-area > .contents, +.user-environ .edit-area > .contents { + left: 0; + right: 0; } +/* line 115, ../../../../general/res/sass/user-environ/_layout.scss */ +.user-environ .edit-area { + top: 45px; } + /* line 118, ../../../../general/res/sass/user-environ/_layout.scss */ + .user-environ .edit-area .tool-bar { + bottom: auto; + height: 30px; + line-height: 25px; } + /* line 123, ../../../../general/res/sass/user-environ/_layout.scss */ + .user-environ .edit-area .work-area { + top: 40px; } +/* line 128, ../../../../general/res/sass/user-environ/_layout.scss */ +.user-environ .ue-bottom-bar { + overflow: hidden; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; + top: auto; + height: 25px; } + /* line 133, ../../../../general/res/sass/user-environ/_layout.scss */ + .user-environ .ue-bottom-bar .status-holder { + z-index: 1; } + /* line 137, ../../../../general/res/sass/user-environ/_layout.scss */ + .user-environ .ue-bottom-bar .app-logo { + left: auto; + width: 105px; + z-index: 2; } + +/* line 145, ../../../../general/res/sass/user-environ/_layout.scss */ +.cols { + overflow: hidden; + *zoom: 1; } + /* line 147, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols .col { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + *zoom: 1; + float: left; + margin-left: 1.5%; + padding-left: 5px; + position: relative; } + /* line 155, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols .col:first-child { + margin-left: 0; + padding-left: 0; } + /* line 162, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols.cols-2 .col-1 { + min-width: 250px; + width: 48.5%; } + /* line 168, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols.cols-2-ff .col-100px { + width: 100px; } + /* line 175, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols.cols-6 .col-1 { + min-width: 83.33333px; + width: 15.16667%; } + /* line 181, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols.cols-16 .col-1 { + min-width: 31.25px; + width: 4.75%; } + /* line 184, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols.cols-16 .col-2 { + min-width: 62.5px; + width: 11%; } + /* line 187, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols.cols-16 .col-7 { + min-width: 218.75px; + width: 42.25%; } + /* line 193, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols.cols-32 .col-2 { + min-width: 31.25px; + width: 4.75%; } + /* line 196, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols.cols-32 .col-15 { + min-width: 234.375px; + width: 45.375%; } + /* line 200, ../../../../general/res/sass/user-environ/_layout.scss */ + .cols .l-row { + overflow: hidden; + *zoom: 1; + padding: 5px 0; } + +/* line 209, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-mode .split-layout .split-pane-component.pane.treeview.left { + min-width: 150px; + max-width: 800px; + width: 25%; } +/* line 214, ../../../../general/res/sass/user-environ/_layout.scss */ +.browse-mode .split-layout .split-pane-component.pane.t-inspect.right { + min-width: 100px; + max-width: 800px; + width: 20%; } + +/* line 225, ../../../../general/res/sass/user-environ/_layout.scss */ +.edit-mode .split-layout .split-pane-component.pane.right { + width: 15%; } + /* line 227, ../../../../general/res/sass/user-environ/_layout.scss */ + .edit-mode .split-layout .split-pane-component.pane.right .pane.bottom { + min-height: 50px; + height: 30%; } + +/* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane { + position: absolute; } + /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .pane-header { + text-transform: uppercase; + height: 24px; + line-height: 24px; + margin-bottom: 5px; } + /* line 246, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane.treeview.left .create-btn-holder { + bottom: auto; + top: 0; + height: 24px; } + /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane.treeview.left .create-btn-holder .wrapper.menu-element { + position: absolute; + bottom: 5px; } + /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane.treeview.left .search-holder { + top: 34px; } + /* line 258, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane.treeview.left .tree-holder { + overflow: auto; + top: 64px; } + /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane { + z-index: 2; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane { + top: 5px; } + /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { + left: -30px; } + /* line 273, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { + content: 'F'; } + /* line 276, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { + left: -25px; } + /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { + right: -25px; } + /* line 283, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { + content: '\e608'; } + /* line 286, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { + right: -20px; } } + /* line 295, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, + .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, + .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, + .pane.items .object-browse-bar .right.abs, + .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .right.pager, + .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .right.pager, + .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .right.val, + .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .right.val, + .pane.items .object-browse-bar .s-menu-btn span.right.l-click-area, + .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { + top: auto; } + +/* line 306, ../../../../general/res/sass/user-environ/_layout.scss */ +.split-layout.horizontal > .pane { + margin-top: 5px; } + /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ + .split-layout.horizontal > .pane:first-child { + margin-top: 0; } +/* line 316, ../../../../general/res/sass/user-environ/_layout.scss */ +.split-layout.vertical > .pane { + margin-left: 5px; } + /* line 319, ../../../../general/res/sass/user-environ/_layout.scss */ + .split-layout.vertical > .pane > .holder { + left: 0; + right: 0; } + /* line 323, ../../../../general/res/sass/user-environ/_layout.scss */ + .split-layout.vertical > .pane:first-child { + margin-left: 0; } + /* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ + .split-layout.vertical > .pane:first-child .holder { + right: 3px; } + +/* line 333, ../../../../general/res/sass/user-environ/_layout.scss */ +.object-holder { + overflow: hidden; + top: 34px; } + /* line 336, ../../../../general/res/sass/user-environ/_layout.scss */ + .object-holder > ng-include { + overflow: auto; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; } + /* line 340, ../../../../general/res/sass/user-environ/_layout.scss */ + .object-holder.l-controls-visible.l-time-controller-visible { + bottom: 88px; } + +/* line 346, ../../../../general/res/sass/user-environ/_layout.scss */ +.object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, +.top-bar .buttons-main .s-btn, +.top-bar .buttons-main .s-menu-btn, +.top-bar .s-menu-btn, +.tool-bar .s-btn, +.tool-bar .s-menu-btn, +.tool-bar .s-menu-btn { + height: 25px; + line-height: 25px; + vertical-align: top; } + +/* line 359, ../../../../general/res/sass/user-environ/_layout.scss */ +.object-browse-bar .view-switcher, +.top-bar .view-switcher { + margin-right: 20px; } + +/* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ +.object-browse-bar { + overflow: visible; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 24px; + line-height: 24px; + white-space: nowrap; } + /* line 372, ../../../../general/res/sass/user-environ/_layout.scss */ + .object-browse-bar .left { + padding-right: 20px; } + /* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ + .object-browse-bar .left .l-back { + display: inline-block; + float: left; + margin-right: 10px; } + +/* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ +.l-flex { + display: flex; + display: -webkit-flex; + flex-flow: row nowrap; + -webkit-flex-flow: row nowrap; } + /* line 385, ../../../../general/res/sass/user-environ/_layout.scss */ + .l-flex .left { + flex: 1 1 0; + -webkit-flex: 1 1 0; + padding-right: 10px; } + +/* line 397, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-hidden .pane.left.treeview, +.pane-tree-hidden .splitter-treeview { + opacity: 0; } +/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-hidden .pane.right.items { + left: 15px !important; } + +/* line 409, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-tree-showing .pane.left.treeview, +.pane-tree-showing .splitter-treeview { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 250ms; + -o-transition-delay: 250ms; + -webkit-transition-delay: 250ms; + transition-delay: 250ms; + opacity: 1; } + +/* line 418, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-inspect-showing .l-object-and-inspector .pane.right, +.pane-inspect-showing .l-object-and-inspector .splitter-inspect { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 250ms; + -o-transition-delay: 250ms; + -webkit-transition-delay: 250ms; + transition-delay: 250ms; + opacity: 1; } + +/* line 427, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-inspect-hidden .l-object-and-inspector .pane.right, +.pane-inspect-hidden .l-object-and-inspector .splitter-inspect { + opacity: 0; } +/* line 431, ../../../../general/res/sass/user-environ/_layout.scss */ +.pane-inspect-hidden .l-object-and-inspector .pane.left { + right: 15px !important; } + +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 438, ../../../../general/res/sass/user-environ/_layout.scss */ + .pane:not(.resizing) { + -moz-transition-property: width, left, right; + -o-transition-property: width, left, right; + -webkit-transition-property: width, left, right; + transition-property: width, left, right; + -moz-transition-duration: 250ms; + -o-transition-duration: 250ms; + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; } } +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 26, ../../../../general/res/sass/mobile/_layout.scss */ + .browse-wrapper, + .pane { + top: 0 !important; + right: 0; + bottom: 0; + left: 0; } + + /* line 31, ../../../../general/res/sass/mobile/_layout.scss */ + .pane.left.treeview { + background-color: #f7f7f7; } + + /* line 35, ../../../../general/res/sass/mobile/_layout.scss */ + .pane.right.items { + -moz-transition-duration: 0.35s; + -o-transition-duration: 0.35s; + -webkit-transition-duration: 0.35s; + transition-duration: 0.35s; + transition-timing-function: ease; + backface-visibility: hidden; + margin-left: 0 !important; } + /* line 39, ../../../../general/res/sass/mobile/_layout.scss */ + .pane.right.items #content-area { + -moz-transition-duration: 0.35s; + -o-transition-duration: 0.35s; + -webkit-transition-duration: 0.35s; + transition-duration: 0.35s; + transition-timing-function: ease; + backface-visibility: hidden; + opacity: 1; } + + /* line 45, ../../../../general/res/sass/mobile/_layout.scss */ + .user-environ .browse-area, + .user-environ .edit-area, + .user-environ .editor { + top: 0; + left: 0; + right: 0; + bottom: 25px; } + + /* line 51, ../../../../general/res/sass/mobile/_layout.scss */ + .holder.l-mobile { + top: 10px !important; + right: 10px !important; + bottom: 10px !important; + left: 10px !important; } + + /* line 65, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-hidden .pane.left.treeview { + right: 100% !important; + width: auto !important; + overflow-y: hidden; + overflow-x: hidden; } + + /* line 82, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.left.treeview { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSI5OCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); + background-size: 100%; + background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); + background-image: -webkit-linear-gradient(0deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); + background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 98%, rgba(0, 0, 0, 0.3) 100%); + right: auto !important; + width: 40% !important; } + /* line 88, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.right.items { + left: 40% !important; } + + /* line 93, ../../../../general/res/sass/mobile/_layout.scss */ + .toggle-tree { + color: #0099cc !important; + font-size: 110%; + position: absolute; + top: 12px; + left: 10px; } + /* line 99, ../../../../general/res/sass/mobile/_layout.scss */ + .toggle-tree:after { + content: 'm' !important; } + + /* line 104, ../../../../general/res/sass/mobile/_layout.scss */ + .object-browse-bar { + left: 30px !important; } + /* line 107, ../../../../general/res/sass/mobile/_layout.scss */ + .object-browse-bar .context-available { + opacity: 1 !important; } + /* line 110, ../../../../general/res/sass/mobile/_layout.scss */ + .object-browse-bar .view-switcher { + margin-right: 0 !important; } + /* line 112, ../../../../general/res/sass/mobile/_layout.scss */ + .object-browse-bar .view-switcher .title-label { + display: none; } + + /* line 119, ../../../../general/res/sass/mobile/_layout.scss */ + .tree-holder { + overflow-x: hidden !important; } + + /* line 123, ../../../../general/res/sass/mobile/_layout.scss */ + .mobile-disable-select { + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; } + + /* line 128, ../../../../general/res/sass/mobile/_layout.scss */ + .mobile-hide, + .mobile-hide-important { + display: none !important; } + + /* line 133, ../../../../general/res/sass/mobile/_layout.scss */ + .mobile-back-hide { + pointer-events: none; + -moz-transition-property: opacity; + -o-transition-property: opacity; + -webkit-transition-property: opacity; + transition-property: opacity; + -moz-transition-duration: 0.4s; + -o-transition-duration: 0.4s; + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + opacity: 0; } + + /* line 138, ../../../../general/res/sass/mobile/_layout.scss */ + .mobile-back-unhide { + pointer-events: all; + -moz-transition-property: opacity; + -o-transition-property: opacity; + -webkit-transition-property: opacity; + transition-property: opacity; + -moz-transition-duration: 0.4s; + -o-transition-duration: 0.4s; + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + opacity: 1; } } +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px) { + /* line 147, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.left.treeview { + width: 90% !important; } + /* line 150, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.right.items { + left: 0 !important; + -moz-transform: translateX(90%); + -ms-transform: translateX(90%); + -webkit-transform: translateX(90%); + transform: translateX(90%); } + /* line 153, ../../../../general/res/sass/mobile/_layout.scss */ + .pane-tree-showing .pane.right.items #content-area { + opacity: 0; } } +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 161, ../../../../general/res/sass/mobile/_layout.scss */ + .desktop-hide { + display: none; } } +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 26, ../../../../general/res/sass/edit/_editor.scss */ +.edit-main .edit-corner, +.edit-main .edit-handle { + position: absolute; + z-index: 2; } +/* line 32, ../../../../general/res/sass/edit/_editor.scss */ +.edit-main .edit-corner { + width: 15px; + height: 15px; } + /* line 35, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-corner:hover { + z-index: 11; } + /* line 38, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-corner.edit-resize-nw { + -moz-border-radius-bottomright: 5px; + -webkit-border-bottom-right-radius: 5px; + border-bottom-right-radius: 5px; + cursor: nw-resize; + top: 0; + left: 0; } + /* line 43, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-corner.edit-resize-ne { + -moz-border-radius-bottomleft: 5px; + -webkit-border-bottom-left-radius: 5px; + border-bottom-left-radius: 5px; + cursor: ne-resize; + top: 0; + right: 0; } + /* line 48, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-corner.edit-resize-se { + -moz-border-radius-topleft: 5px; + -webkit-border-top-left-radius: 5px; + border-top-left-radius: 5px; + cursor: se-resize; + bottom: 0; + right: 0; } + /* line 53, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-corner.edit-resize-sw { + -moz-border-radius-topright: 5px; + -webkit-border-top-right-radius: 5px; + border-top-right-radius: 5px; + cursor: sw-resize; + bottom: 0; + left: 0; } +/* line 61, ../../../../general/res/sass/edit/_editor.scss */ +.edit-main .edit-handle { + top: 15px; + right: 15px; + bottom: 15px; + left: 15px; } + /* line 63, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-handle.edit-move { + cursor: move; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 1; } + /* line 73, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-handle.edit-resize-n { + top: 0px; + bottom: auto; + height: 15px; + cursor: n-resize; } + /* line 78, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-handle.edit-resize-e { + right: 0px; + left: auto; + width: 15px; + cursor: e-resize; } + /* line 83, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-handle.edit-resize-s { + bottom: 0px; + top: auto; + height: 15px; + cursor: s-resize; } + /* line 88, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .edit-handle.edit-resize-w { + left: 0px; + right: auto; + width: 15px; + cursor: w-resize; } +/* line 97, ../../../../general/res/sass/edit/_editor.scss */ +.edit-main .frame.child-frame.panel:hover { + -moz-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; + -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; + box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; + border-color: #0099cc; } + /* line 101, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .frame.child-frame.panel:hover .view-switcher { + opacity: 1; } + /* line 104, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .frame.child-frame.panel:hover .edit-corner { + background-color: rgba(0, 153, 204, 0.8); } + /* line 106, ../../../../general/res/sass/edit/_editor.scss */ + .edit-main .frame.child-frame.panel:hover .edit-corner:hover { + background-color: #0099cc; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 23, ../../../../general/res/sass/search/_search.scss */ +.abs.search-holder, .l-datetime-picker .l-month-year-pager .search-holder.pager, +.l-datetime-picker .l-month-year-pager .search-holder.val, .s-menu-btn span.search-holder.l-click-area { + height: 25px; + bottom: 0; + top: 23px; + z-index: 5; } + /* line 27, ../../../../general/res/sass/search/_search.scss */ + .abs.search-holder.active, .l-datetime-picker .l-month-year-pager .search-holder.active.pager, + .l-datetime-picker .l-month-year-pager .search-holder.active.val, .s-menu-btn span.search-holder.active.l-click-area { + height: auto; + bottom: 0; } + +/* line 38, ../../../../general/res/sass/search/_search.scss */ +.search { + display: flex; + display: -webkit-flex; + flex-direction: column; + -webkit-flex-direction: column; + height: 100%; } + /* line 48, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar { + font-size: 0.8em; + max-width: 250px; + position: relative; + width: 100%; } + /* line 60, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .search-input { + height: 25px; + line-height: 25px; + padding-top: 0; + padding-bottom: 0; } + /* line 67, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .search-icon, + .search .search-bar .clear-icon, + .search .search-bar .menu-icon { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #a6a6a6; + height: 17px; + width: 17px; + line-height: 17px; + position: absolute; + text-align: center; + top: 4px; } + /* line 80, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .clear-icon, + .search .search-bar .menu-icon { + cursor: pointer; + -moz-transition: color, 0.25s; + -o-transition: color, 0.25s; + -webkit-transition: color, 0.25s; + transition: color, 0.25s; } + /* line 87, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .search-input { + position: relative; + width: 100%; + padding-left: 22px !important; + padding-right: 44px !important; } + /* line 94, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .search-input input { + width: 100%; } + /* line 99, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .search-icon { + left: 3px; + transition: visibility .15s, opacity .15s, color .2s; + pointer-events: none; } + /* line 119, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .search-input:hover + div.search-icon { + color: #8c8c8c; } + /* line 123, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .clear-icon { + right: 22px; + visibility: hidden; + opacity: 0; + transition: visibility .15s, opacity .15s, color .2s; } + /* line 132, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .clear-icon.content { + visibility: visible; + opacity: 1; } + /* line 137, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .clear-icon:hover { + color: #8c8c8c; } + /* line 142, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .menu-icon { + font-size: 0.8em; + padding-right: 4px; + right: 4px; + text-align: right; } + /* line 148, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .menu-icon:hover { + color: #8c8c8c; } + /* line 153, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .search-menu-holder { + float: right; + left: -20px; + z-index: 1; + transition: visibility .05s, opacity .05s; } + /* line 163, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .search-menu-holder.off { + visibility: hidden; + opacity: 0; } + /* line 170, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar .menu-icon:hover + div.search-menu-holder { + visibility: visible; } + /* line 173, ../../../../general/res/sass/search/_search.scss */ + .search .search-bar div.search-menu-holder:hover { + visibility: visible; } + /* line 178, ../../../../general/res/sass/search/_search.scss */ + .search .active-filter-display { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + line-height: 130%; + padding: 5px 0; + padding-left: 1.4625em; + font-size: 0.65em; + margin-top: 3px; } + /* line 193, ../../../../general/res/sass/search/_search.scss */ + .search .active-filter-display .clear-filters-icon { + color: #a6a6a6; + opacity: 1; + font-size: 0.8em; + position: absolute; + left: 1px; + cursor: pointer; } + /* line 205, ../../../../general/res/sass/search/_search.scss */ + .search .active-filter-display.off { + visibility: hidden; + opacity: 0; + height: 0; + margin: 0; + padding: 0; + border: 0; } + /* line 215, ../../../../general/res/sass/search/_search.scss */ + .search .search-scroll { + order: 3; + margin-top: 4px; + overflow-y: auto; + top: auto; + height: auto; + max-height: 100%; + position: relative; } + /* line 226, ../../../../general/res/sass/search/_search.scss */ + .search .search-scroll .load-icon { + position: relative; } + /* line 230, ../../../../general/res/sass/search/_search.scss */ + .search .search-scroll .load-more-button { + margin-top: 5px 0; + font-size: 0.8em; + position: relative; + left: 50%; + margin-left: -45px; + text-align: center; + width: 90px; + white-space: nowrap; } + +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 5, ../../../../general/res/sass/mobile/search/_search.scss */ + .search .search-bar .menu-icon { + display: none; } + /* line 8, ../../../../general/res/sass/mobile/search/_search.scss */ + .search .search-bar .clear-icon { + right: 5px; } } +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 23, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .blocker { + background: rgba(0, 0, 0, 0.7); + z-index: 100; } +/* line 27, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .clk-icon.close { + font-size: 0.8rem; + position: absolute; + top: 10px; + right: 10px; + bottom: auto; + left: auto; + z-index: 100; } +/* line 33, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay > .holder { + background-color: #fcfcfc; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #666; + display: inline-block; + -moz-border-radius: 12px; + -webkit-border-radius: 12px; + border-radius: 12px; + color: #666; + top: 15%; + right: 15%; + bottom: 15%; + left: 15%; + z-index: 101; } + /* line 40, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay > .holder > .contents { + top: 25px; + right: 25px; + bottom: 25px; + left: 25px; } +/* line 45, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 1.2em; + margin-bottom: 5px; } +/* line 51, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .top-bar { + height: 60px; } +/* line 55, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .editor { + top: 70px; + bottom: 40px; + left: 0; + right: 0; } +/* line 61, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .bottom-bar { + top: auto; + right: 0; + bottom: 0; + left: 0; + overflow: visible; + height: 30px; + text-align: right; } + /* line 67, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar .s-btn, .overlay .bottom-bar .s-menu-btn { + font-size: 95%; + height: 30px; + line-height: 30px; + margin-left: 5px; + padding: 0 15px; } + /* line 69, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .bottom-bar .s-btn:not(.major), .overlay .bottom-bar .s-menu-btn:not(.major) { + background-color: #969696; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + display: inline-block; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + -moz-transition: background, 0.25s; + -o-transition: background, 0.25s; + -webkit-transition: background, 0.25s; + transition: background, 0.25s; + text-shadow: none; } + /* line 274, ../../../../general/res/sass/_mixins.scss */ + .overlay .bottom-bar .s-btn:not(.major) .icon, .overlay .bottom-bar .s-menu-btn:not(.major) .icon { + color: #fff; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 279, ../../../../general/res/sass/_mixins.scss */ + .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover { + background: #7d7d7d; } + /* line 281, ../../../../general/res/sass/_mixins.scss */ + .overlay .bottom-bar .s-btn:not(.major):not(.disabled):hover > .icon, .overlay .bottom-bar .s-menu-btn:not(.major):not(.disabled):hover > .icon { + color: white; } } +/* line 85, ../../../../general/res/sass/overlay/_overlay.scss */ +.overlay .contents.l-dialog { + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; + overflow: auto; } + /* line 93, ../../../../general/res/sass/overlay/_overlay.scss */ + .overlay .contents.l-dialog .field.l-med input[type='text'] { + width: 100%; } + +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 4, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay .clk-icon.close { + top: 10px; + right: 10px; } + /* line 8, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder { + -moz-border-radius: 0; + -webkit-border-radius: 0; + border-radius: 0; + top: 0; + right: 0; + bottom: 0; + left: 0; } + /* line 14, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents { + top: 10px; + right: 10px; + bottom: 10px; + left: 10px; } + /* line 21, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents .top-bar > .title { + margin-right: 1.2em; } + /* line 26, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents .form.editor { + border: none; } + /* line 29, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents .form.editor .contents { + top: 0; + right: 0; + bottom: 0; + left: 0; } } +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 43, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents .form.editor .contents .form-row > .label, + .overlay > .holder > .contents .form.editor .contents .form-row > .controls { + display: block; + float: none; + width: 100%; } + /* line 51, ../../../../general/res/sass/mobile/overlay/_overlay.scss */ + .overlay > .holder > .contents .form.editor .contents .form-row > .label:after { + float: none; } } +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 23, ../../../../general/res/sass/tree/_tree.scss */ +ul.tree { + margin: 0; + padding: 0; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; } + /* line 331, ../../../../general/res/sass/_mixins.scss */ + ul.tree li { + list-style-type: none; + margin: 0; + padding: 0; } + /* line 26, ../../../../general/res/sass/tree/_tree.scss */ + ul.tree li { + display: block; + position: relative; } + /* line 30, ../../../../general/res/sass/tree/_tree.scss */ + ul.tree ul.tree { + margin-left: 15px; } + +/* line 35, ../../../../general/res/sass/tree/_tree.scss */ +.tree-item, +.search-result-item { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-transition: background-color 0.25s; + -o-transition: background-color 0.25s; + -webkit-transition: background-color 0.25s; + transition: background-color 0.25s; + display: block; + font-size: 0.8rem; + height: 1.5rem; + line-height: 1.5rem; + margin-bottom: 3px; + position: relative; } + /* line 48, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .view-control, + .search-result-item .view-control { + color: #666; + display: inline-block; + margin-left: 5px; + font-size: 0.75em; + width: 10px; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 57, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .view-control:hover, + .search-result-item .view-control:hover { + color: #0099cc !important; } } + /* line 63, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .label, + .search-result-item .label { + display: block; + overflow: hidden; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + width: auto; + height: auto; + line-height: 1.5rem; } + /* line 71, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .label .type-icon, + .search-result-item .label .type-icon { + font-size: 16px; + color: #0099cc; + left: 5px; + position: absolute; + top: 4px; + bottom: auto; + height: 16px; + line-height: 100%; + right: auto; + width: 16px; } + /* line 84, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .label .type-icon .icon.l-icon-link, .tree-item .label .type-icon .icon.l-icon-alert, + .search-result-item .label .type-icon .icon.l-icon-link, + .search-result-item .label .type-icon .icon.l-icon-alert { + position: absolute; + z-index: 2; } + /* line 89, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .label .type-icon .icon.l-icon-alert, + .search-result-item .label .type-icon .icon.l-icon-alert { + color: #ff3c00; + font-size: 8px; + line-height: 8px; + height: 8px; + width: 8px; + top: 1px; + right: -2px; } + /* line 95, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .label .type-icon .icon.l-icon-link, + .search-result-item .label .type-icon .icon.l-icon-link { + color: #49dedb; + font-size: 8px; + line-height: 8px; + height: 8px; + width: 8px; + left: -3px; + bottom: 0px; } + /* line 103, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .label .title-label, + .search-result-item .label .title-label { + overflow: hidden; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + width: auto; + height: auto; + display: block; + left: 30px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } + /* line 113, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item.selected, + .search-result-item.selected { + background: #1ac6ff; + color: #fcfcfc; } + /* line 116, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item.selected .view-control, + .search-result-item.selected .view-control { + color: #fcfcfc; } + /* line 119, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item.selected .label .type-icon, + .search-result-item.selected .label .type-icon { + color: #fcfcfc; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 127, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item:not(.selected):hover, + .search-result-item:not(.selected):hover { + background: rgba(102, 102, 102, 0.1); + color: #333333; } + /* line 130, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item:not(.selected):hover .icon, + .search-result-item:not(.selected):hover .icon { + color: #0099cc; } } + /* line 137, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item:not(.loading), + .search-result-item:not(.loading) { + cursor: pointer; } + /* line 141, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .context-trigger, + .search-result-item .context-trigger { + top: -1px; + position: absolute; + right: 3px; } + /* line 146, ../../../../general/res/sass/tree/_tree.scss */ + .tree-item .context-trigger .invoke-menu, + .search-result-item .context-trigger .invoke-menu { + font-size: 0.75em; + height: 0.9rem; + line-height: 0.9rem; } + +/* line 155, ../../../../general/res/sass/tree/_tree.scss */ +.tree-item .label { + left: 15px; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 27, ../../../../general/res/sass/mobile/_tree.scss */ + ul.tree ul.tree { + margin-left: 20px; } + + /* line 31, ../../../../general/res/sass/mobile/_tree.scss */ + .tree-item, + .search-result-item { + height: 35px; + line-height: 35px; + margin-bottom: 0px; } + /* line 36, ../../../../general/res/sass/mobile/_tree.scss */ + .tree-item .view-control, + .search-result-item .view-control { + position: absolute; + font-size: 1.1em; + right: 0px; + width: 30px; + text-align: center; } + /* line 45, ../../../../general/res/sass/mobile/_tree.scss */ + .tree-item .label, + .search-result-item .label { + left: 0; + right: 35px; + line-height: 35px; } + /* line 50, ../../../../general/res/sass/mobile/_tree.scss */ + .tree-item .label .type-icon, + .search-result-item .label .type-icon { + top: 9px; + bottom: auto; + height: 16px; } } +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 25, ../../../../general/res/sass/user-environ/_frame.scss */ +.frame.child-frame.panel { + background: #fcfcfc; + border: 1px solid rgba(102, 102, 102, 0.2); } + /* line 28, ../../../../general/res/sass/user-environ/_frame.scss */ + .frame.child-frame.panel:hover { + border-color: rgba(128, 128, 128, 0.2); } +/* line 32, ../../../../general/res/sass/user-environ/_frame.scss */ +.frame > .object-header.abs, .l-datetime-picker .l-month-year-pager .frame > .object-header.pager, +.l-datetime-picker .l-month-year-pager .frame > .object-header.val, .s-menu-btn .frame > span.object-header.l-click-area { + font-size: 0.75em; + height: 16px; + line-height: 16px; } +/* line 38, ../../../../general/res/sass/user-environ/_frame.scss */ +.frame > .object-holder.abs, .l-datetime-picker .l-month-year-pager .frame > .object-holder.pager, +.l-datetime-picker .l-month-year-pager .frame > .object-holder.val, .s-menu-btn .frame > span.object-holder.l-click-area { + top: 21px; } +/* line 41, ../../../../general/res/sass/user-environ/_frame.scss */ +.frame .contents { + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; } +/* line 49, ../../../../general/res/sass/user-environ/_frame.scss */ +.frame.frame-template .s-btn, .frame.frame-template .s-menu-btn, +.frame.frame-template .s-menu-btn { + height: 16px; + line-height: 16px; + padding: 0 5px; } + /* line 54, ../../../../general/res/sass/user-environ/_frame.scss */ + .frame.frame-template .s-btn > span, .frame.frame-template .s-menu-btn > span, + .frame.frame-template .s-menu-btn > span { + font-size: 0.65rem; } +/* line 59, ../../../../general/res/sass/user-environ/_frame.scss */ +.frame.frame-template .s-menu-btn:after { + font-size: 8px; } +/* line 63, ../../../../general/res/sass/user-environ/_frame.scss */ +.frame.frame-template .view-switcher { + z-index: 10; } +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 69, ../../../../general/res/sass/user-environ/_frame.scss */ + .frame.frame-template .view-switcher { + opacity: 0; } + /* line 72, ../../../../general/res/sass/user-environ/_frame.scss */ + .frame.frame-template:hover .view-switcher { + opacity: 1; } } +/* line 80, ../../../../general/res/sass/user-environ/_frame.scss */ +.frame .view-switcher .title-label { + display: none; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/user-environ/_top-bar.scss */ +.top-bar { + /* .title { + color: #fff; + }*/ } + /* line 23, ../../../../general/res/sass/user-environ/_top-bar.scss */ + .top-bar.browse, .top-bar.edit { + border-bottom: 1px solid rgba(102, 102, 102, 0.2); + top: 10px; + right: 10px; + bottom: auto; + left: 10px; + height: 30px; + line-height: 24px; } + /* line 35, ../../../../general/res/sass/user-environ/_top-bar.scss */ + .top-bar .buttons-main { + font-size: 0.8em; + left: auto; + text-align: right; } + +/* line 48, ../../../../general/res/sass/user-environ/_top-bar.scss */ +.edit-mode .top-bar .buttons-main { + white-space: nowrap; } + /* line 52, ../../../../general/res/sass/user-environ/_top-bar.scss */ + .edit-mode .top-bar .buttons-main.abs, .edit-mode .top-bar .l-datetime-picker .l-month-year-pager .buttons-main.pager, .l-datetime-picker .l-month-year-pager .edit-mode .top-bar .buttons-main.pager, + .edit-mode .top-bar .l-datetime-picker .l-month-year-pager .buttons-main.val, + .l-datetime-picker .l-month-year-pager .edit-mode .top-bar .buttons-main.val, .edit-mode .top-bar .s-menu-btn span.buttons-main.l-click-area, .s-menu-btn .edit-mode .top-bar span.buttons-main.l-click-area { + bottom: auto; + left: auto; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ +.ue-bottom-bar { + background: #000; + color: white; + font-size: .7rem; } + /* line 28, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ + .ue-bottom-bar .status-holder { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; + width: auto; + height: auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 15px; + right: 120px; + text-transform: uppercase; } + /* line 39, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ + .ue-bottom-bar .app-logo { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; + width: auto; + height: auto; + left: auto; + cursor: pointer; } + /* line 48, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ + .ue-bottom-bar .app-logo.logo-openmctweb { + background: url("../../../../general/res/images/logo-openmctweb.svg") no-repeat center center; } + +/* line 54, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ +.status.block { + display: inline; + margin-right: 10px; } + /* line 58, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ + .status.block .status-indicator { + display: inline-block; + margin-right: 3px; + color: #0099cc; } + /* line 65, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ + .status.block .status-indicator.ok { + color: #009900; } + /* line 68, ../../../../general/res/sass/user-environ/_bottom-bar.scss */ + .status.block .status-indicator.caution { + color: #ffaa00; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/user-environ/_tool-bar.scss */ +.tool-bar { + border-bottom: 1px solid rgba(102, 102, 102, 0.2); } + /* line 24, ../../../../general/res/sass/user-environ/_tool-bar.scss */ + .tool-bar .l-control-group { + height: 25px; } + /* line 27, ../../../../general/res/sass/user-environ/_tool-bar.scss */ + .tool-bar input[type="text"] { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + font-size: .9em; + height: 25px; + margin-bottom: 1px; + position: relative; } + /* line 33, ../../../../general/res/sass/user-environ/_tool-bar.scss */ + .tool-bar input[type="text"].sm { + width: 25px; } + /* line 37, ../../../../general/res/sass/user-environ/_tool-bar.scss */ + .tool-bar .input-labeled label { + font-size: 11.25px; } + +/********************************* VIEWS */ +/***************************************************************************** +* Open MCT Web, Copyright (c) 2014-2015, United States Government +* as represented by the Administrator of the National Aeronautics and Space +* Administration. All rights reserved. +* +* Open MCT Web 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 Web 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. +*****************************************************************************/ +/* line 23, ../../../../general/res/sass/_fixed-position.scss */ +.t-fixed-position.l-fixed-position { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; } + /* line 33, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position.l-fixed-position .l-grid-holder { + position: relative; + height: 100%; + width: 100%; } + /* line 37, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position.l-fixed-position .l-grid-holder .l-grid { + position: absolute; + height: 100%; + width: 100%; + pointer-events: none; + z-index: 0; } +/* line 48, ../../../../general/res/sass/_fixed-position.scss */ +.t-fixed-position .l-fixed-position-item { + position: absolute; + border: 1px solid transparent; } + /* line 52, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item.s-selected { + -moz-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; + -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; + box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; + border-color: #0099cc; + cursor: move; } + /* line 57, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item.s-not-selected { + opacity: 0.8; } + /* line 61, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item .l-fixed-position-box, + .t-fixed-position .l-fixed-position-item .l-fixed-position-image, + .t-fixed-position .l-fixed-position-item .l-fixed-position-text { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 100%; + width: 100%; } + /* line 72, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item .l-fixed-position-image { + background-size: cover; + background-repeat: no-repeat; + background-position: center; } + /* line 78, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item .l-fixed-position-text { + border: 1px solid transparent; + font-size: 0.8rem; + line-height: 100%; } + /* line 84, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-static-text { + padding: 1px; } + /* line 89, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: block; + padding: 2px; } + /* line 96, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem.l-title { + float: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: auto; } + /* line 105, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem.l-value { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + float: right; + margin-left: 5px; + padding-left: 5px; + padding-right: 5px; + text-align: right; } + /* line 116, ../../../../general/res/sass/_fixed-position.scss */ + .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem.l-value.telem-only { + margin-left: 0; + width: 100%; } +/* line 126, ../../../../general/res/sass/_fixed-position.scss */ +.t-fixed-position .l-fixed-position-item-handle { + background: rgba(0, 153, 204, 0.5); + cursor: crosshair; + border: 1px solid #0099cc; + position: absolute; } + +/* line 140, ../../../../general/res/sass/_fixed-position.scss */ +.edit-mode .t-fixed-position.l-fixed-position .l-grid-holder .l-grid.l-grid-x { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuMCIgeTE9IjAuNSIgeDI9IjEuMCIgeTI9IjAuNSI+PHN0b3Agb2Zmc2V0PSIxcHgiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wNSIvPjxzdG9wIG9mZnNldD0iMXB4IiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); + background-size: 100%; + background-image: -moz-linear-gradient(0deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); + background-image: -webkit-linear-gradient(0deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); + background-image: linear-gradient(90deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); + background-repeat: repeat-x; } +/* line 144, ../../../../general/res/sass/_fixed-position.scss */ +.edit-mode .t-fixed-position.l-fixed-position .l-grid-holder .l-grid.l-grid-y { + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjEuMCIgeDI9IjAuNSIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIxcHgiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wNSIvPjxzdG9wIG9mZnNldD0iMXB4IiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); + background-size: 100%; + background-image: -moz-linear-gradient(90deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); + background-image: -webkit-linear-gradient(90deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); + background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.05) 1px, rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 100%); + background-repeat: repeat-y; } +/* line 152, ../../../../general/res/sass/_fixed-position.scss */ +.edit-mode .t-fixed-position .l-fixed-position-item:not(.s-selected) { + border: 1px dotted rgba(0, 153, 204, 0.75); } + /* line 154, ../../../../general/res/sass/_fixed-position.scss */ + .edit-mode .t-fixed-position .l-fixed-position-item:not(.s-selected):hover { + border: 1px dotted #0099cc; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/lists/_tabular.scss */ +.w1, .w2 { + position: relative; + height: 100%; } + +/* line 27, ../../../../general/res/sass/lists/_tabular.scss */ +.tabular, +table { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + border-spacing: 0; + border-collapse: collapse; + display: table; + font-size: 0.75rem; + position: relative; + width: 100%; } + /* line 36, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular thead, .tabular .thead, + .tabular tbody tr, .tabular .tbody .tr, + table thead, + table .thead, + table tbody tr, + table .tbody .tr { + width: 100%; } + /* line 40, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular thead, .tabular .thead, + table thead, + table .thead { + border-bottom: 1px solid #fcfcfc; } + /* line 44, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular:not(.fixed-header) tr th, + table:not(.fixed-header) tr th { + background-color: #e3e3e3; } + /* line 48, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tbody, .tabular .tbody, + table tbody, + table .tbody { + display: table-row-group; } + /* line 51, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tbody tr:hover, .tabular tbody .tr:hover, .tabular .tbody tr:hover, .tabular .tbody .tr:hover, + table tbody tr:hover, + table tbody .tr:hover, + table .tbody tr:hover, + table .tbody .tr:hover { + background: rgba(51, 51, 51, 0.1); } + /* line 56, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr, .tabular .tr, + table tr, + table .tr { + display: table-row; } + /* line 58, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr:first-child .td, .tabular .tr:first-child .td, + table tr:first-child .td, + table .tr:first-child .td { + border-top: none; } + /* line 62, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr.group-header td, .tabular tr.group-header .td, .tabular .tr.group-header td, .tabular .tr.group-header .td, + table tr.group-header td, + table tr.group-header .td, + table .tr.group-header td, + table .tr.group-header .td { + background-color: #efefef; + color: #404040; } + /* line 68, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr th, .tabular tr .th, .tabular tr td, .tabular tr .td, .tabular .tr th, .tabular .tr .th, .tabular .tr td, .tabular .tr .td, + table tr th, + table tr .th, + table tr td, + table tr .td, + table .tr th, + table .tr .th, + table .tr td, + table .tr .td { + display: table-cell; } + /* line 71, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr th, .tabular tr .th, .tabular .tr th, .tabular .tr .th, + table tr th, + table tr .th, + table .tr th, + table .tr .th { + border-left: 1px solid #fcfcfc; + color: #333333; + padding: 5px 5px; + white-space: nowrap; + vertical-align: middle; } + /* line 77, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr th:first-child, .tabular tr .th:first-child, .tabular .tr th:first-child, .tabular .tr .th:first-child, + table tr th:first-child, + table tr .th:first-child, + table .tr th:first-child, + table .tr .th:first-child { + border-left: none; } + /* line 81, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr th.sort.sort:after, .tabular tr .th.sort.sort:after, .tabular .tr th.sort.sort:after, .tabular .tr .th.sort.sort:after, + table tr th.sort.sort:after, + table tr .th.sort.sort:after, + table .tr th.sort.sort:after, + table .tr .th.sort.sort:after { + color: #49dedb; + font-family: symbolsfont; + font-size: 8px; + content: "\ed"; + display: inline-block; + margin-left: 3px; } + /* line 89, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr th.sort.sort.desc:after, .tabular tr .th.sort.sort.desc:after, .tabular .tr th.sort.sort.desc:after, .tabular .tr .th.sort.sort.desc:after, + table tr th.sort.sort.desc:after, + table tr .th.sort.sort.desc:after, + table .tr th.sort.sort.desc:after, + table .tr .th.sort.sort.desc:after { + content: "\ec"; } + /* line 93, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr th.sortable, .tabular tr .th.sortable, .tabular .tr th.sortable, .tabular .tr .th.sortable, + table tr th.sortable, + table tr .th.sortable, + table .tr th.sortable, + table .tr .th.sortable { + cursor: pointer; } + /* line 97, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr td, .tabular tr .td, .tabular .tr td, .tabular .tr .td, + table tr td, + table tr .td, + table .tr td, + table .tr .td { + border-bottom: 1px solid #e3e3e3; + min-width: 20px; + color: #333333; + padding: 3px 5px; + word-wrap: break-word; + vertical-align: top; } + /* line 104, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr td.numeric, .tabular tr .td.numeric, .tabular .tr td.numeric, .tabular .tr .td.numeric, + table tr td.numeric, + table tr .td.numeric, + table .tr td.numeric, + table .tr .td.numeric { + text-align: right; } + /* line 107, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr td.s-cell-type-value, .tabular tr .td.s-cell-type-value, .tabular .tr td.s-cell-type-value, .tabular .tr .td.s-cell-type-value, + table tr td.s-cell-type-value, + table tr .td.s-cell-type-value, + table .tr td.s-cell-type-value, + table .tr .td.s-cell-type-value { + text-align: right; } + /* line 109, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular tr td.s-cell-type-value .l-cell-contents, .tabular tr .td.s-cell-type-value .l-cell-contents, .tabular .tr td.s-cell-type-value .l-cell-contents, .tabular .tr .td.s-cell-type-value .l-cell-contents, + table tr td.s-cell-type-value .l-cell-contents, + table tr .td.s-cell-type-value .l-cell-contents, + table .tr td.s-cell-type-value .l-cell-contents, + table .tr .td.s-cell-type-value .l-cell-contents { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + padding-left: 5px; + padding-right: 5px; } + /* line 125, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular.filterable tbody, .tabular.filterable .tbody, + table.filterable tbody, + table.filterable .tbody { + top: 44px; } + /* line 128, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular.filterable input[type="text"], + table.filterable input[type="text"] { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + width: 100%; } + /* line 134, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular.fixed-header, + table.fixed-header { + height: 100%; } + /* line 136, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular.fixed-header thead, .tabular.fixed-header .thead, + .tabular.fixed-header tbody tr, .tabular.fixed-header .tbody .tr, + table.fixed-header thead, + table.fixed-header .thead, + table.fixed-header tbody tr, + table.fixed-header .tbody .tr { + display: table; + table-layout: fixed; } + /* line 141, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular.fixed-header thead, .tabular.fixed-header .thead, + table.fixed-header thead, + table.fixed-header .thead { + width: calc(100% - 10px); } + /* line 143, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular.fixed-header thead:before, .tabular.fixed-header .thead:before, + table.fixed-header thead:before, + table.fixed-header .thead:before { + content: ""; + display: block; + z-index: 0; + position: absolute; + width: 100%; + height: 22px; + background-color: #e3e3e3; } + /* line 153, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular.fixed-header tbody, .tabular.fixed-header .tbody, + table.fixed-header tbody, + table.fixed-header .tbody { + overflow: hidden; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; + top: 22px; + display: block; + overflow-y: scroll; } + /* line 161, ../../../../general/res/sass/lists/_tabular.scss */ + .tabular.t-event-messages td, .tabular.t-event-messages .td, + table.t-event-messages td, + table.t-event-messages .td { + min-width: 150px; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 31, ../../../../general/res/sass/plots/_plots-main.scss */ +.gl-plot { + color: #666; + font-size: 0.7rem; + position: relative; + width: 100%; + height: 100%; + /****************************** Limits and Out-of-Bounds data */ } + /* line 38, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-axis-area { + position: absolute; } + /* line 41, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-axis-area.gl-plot-x { + top: auto; + right: 0; + bottom: 5px; + left: 60px; + height: 32px; + width: auto; + overflow: hidden; } + /* line 50, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-axis-area.gl-plot-y { + top: 25px; + right: auto; + bottom: 37px; + left: 0; + width: 60px; } + /* line 59, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-coords { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background: black; + color: #b3b3b3; + padding: 2px 5px; + position: absolute; + top: 35px; + right: auto; + bottom: auto; + left: 70px; + z-index: 10; } + /* line 71, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-coords:empty { + display: none; } + /* line 76, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-display-area { + background-color: rgba(0, 0, 0, 0.05); + position: absolute; + top: 25px; + right: 0; + bottom: 37px; + left: 60px; + cursor: crosshair; + border: 1px solid rgba(102, 102, 102, 0.2); } + /* line 89, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-label, + .gl-plot .l-plot-label { + color: #999999; + position: absolute; + text-align: center; } + /* line 97, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-label.gl-plot-x-label, .gl-plot .gl-plot-label.l-plot-x-label, + .gl-plot .l-plot-label.gl-plot-x-label, + .gl-plot .l-plot-label.l-plot-x-label { + top: auto; + right: 0; + bottom: 0; + left: 0; + height: auto; } + /* line 106, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-label.gl-plot-y-label, .gl-plot .gl-plot-label.l-plot-y-label, + .gl-plot .l-plot-label.gl-plot-y-label, + .gl-plot .l-plot-label.l-plot-y-label { + -moz-transform-origin: 50% 0; + -ms-transform-origin: 50% 0; + -webkit-transform-origin: 50% 0; + transform-origin: 50% 0; + -moz-transform: translateX(-50%) rotate(-90deg); + -ms-transform: translateX(-50%) rotate(-90deg); + -webkit-transform: translateX(-50%) rotate(-90deg); + transform: translateX(-50%) rotate(-90deg); + display: inline-block; + margin-left: 5px; + left: 0; + top: 50%; + white-space: nowrap; } + /* line 120, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-y-options { + position: absolute; + top: 50%; + right: auto; + bottom: auto; + left: auto5px; + margin-top: -16px; + height: auto; + min-height: 32px; + width: 32px; } + /* line 134, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-hash { + position: absolute; + border: 0 rgba(0, 0, 0, 0.2) dashed; } + /* line 137, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-hash.hash-v { + border-right-width: 1px; + height: 100%; } + /* line 141, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-hash.hash-h { + border-bottom-width: 1px; + width: 100%; } + /* line 147, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .gl-plot-legend { + position: absolute; + top: 0; + right: 0; + bottom: auto; + left: 0; + height: 20px; + overflow-x: hidden; + overflow-y: auto; } + /* line 160, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .l-limit-bar, + .gl-plot .l-oob-data { + position: absolute; + left: 0; + right: 0; + width: auto; } + /* line 168, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .l-limit-bar { + height: auto; + z-index: 0; } + /* line 176, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .l-limit-bar.s-limit-yellow { + background: rgba(255, 170, 0, 0.2); } + /* line 177, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .l-limit-bar.s-limit-red { + background: rgba(255, 0, 0, 0.2); } + /* line 180, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .l-oob-data { + overflow: hidden; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + width: auto; + height: auto; + pointer-events: none; + height: 10px; + z-index: 1; } + /* line 188, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .l-oob-data.l-oob-data-up { + top: 0; + bottom: auto; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjEuMCIgeDI9IjAuNSIgeTI9IjAuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzc3NDhkNiIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM3NzQ4ZDYiIHN0b3Atb3BhY2l0eT0iMC41Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + background-size: 100%; + background-image: -moz-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); + background-image: -webkit-linear-gradient(90deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); + background-image: linear-gradient(0deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); } + /* line 193, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot .l-oob-data.l-oob-data-dwn { + bottom: 0; + top: auto; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzc3NDhkNiIgc3RvcC1vcGFjaXR5PSIwLjAiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM3NzQ4ZDYiIHN0b3Atb3BhY2l0eT0iMC41Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); + background-size: 100%; + background-image: -moz-linear-gradient(270deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); + background-image: -webkit-linear-gradient(270deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); + background-image: linear-gradient(180deg, rgba(119, 72, 214, 0), rgba(119, 72, 214, 0.5) 100%); } + +/* line 203, ../../../../general/res/sass/plots/_plots-main.scss */ +.gl-plot-legend .plot-legend-item, +.gl-plot-legend .legend-item, +.legend .plot-legend-item, +.legend .legend-item { + display: inline-block; + margin-right: 10px; + margin-bottom: 3px; } + /* line 208, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot-legend .plot-legend-item span, + .gl-plot-legend .legend-item span, + .legend .plot-legend-item span, + .legend .legend-item span { + vertical-align: middle; } + /* line 211, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot-legend .plot-legend-item .plot-color-swatch, + .gl-plot-legend .plot-legend-item .color-swatch, + .gl-plot-legend .legend-item .plot-color-swatch, + .gl-plot-legend .legend-item .color-swatch, + .legend .plot-legend-item .plot-color-swatch, + .legend .plot-legend-item .color-swatch, + .legend .legend-item .plot-color-swatch, + .legend .legend-item .color-swatch { + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; + display: inline-block; + height: 8px; + width: 8px; } + +/* line 228, ../../../../general/res/sass/plots/_plots-main.scss */ +.gl-plot-legend .plot-legend-item { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + line-height: 1.5em; + padding: 0px 5px; } + /* line 234, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot-legend .plot-legend-item .plot-color-swatch { + border: 1px solid #fcfcfc; + height: 9px; + width: 9px; } + +/* line 242, ../../../../general/res/sass/plots/_plots-main.scss */ +.tick { + position: absolute; + border: 0 rgba(0, 0, 0, 0.2) solid; } + /* line 245, ../../../../general/res/sass/plots/_plots-main.scss */ + .tick.tick-x { + border-right-width: 1px; + height: 100%; } + +/* line 251, ../../../../general/res/sass/plots/_plots-main.scss */ +.gl-plot-tick, +.tick-label { + font-size: 0.7rem; + position: absolute; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } + /* line 259, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot-tick.gl-plot-x-tick-label, .gl-plot-tick.tick-label-x, + .tick-label.gl-plot-x-tick-label, + .tick-label.tick-label-x { + right: auto; + bottom: auto; + left: auto; + height: auto; + width: 20%; + margin-left: -10%; + text-align: center; } + /* line 269, ../../../../general/res/sass/plots/_plots-main.scss */ + .gl-plot-tick.gl-plot-y-tick-label, .gl-plot-tick.tick-label-y, + .tick-label.gl-plot-y-tick-label, + .tick-label.tick-label-y { + top: auto; + height: 1em; + width: auto; + margin-bottom: -0.5em; + text-align: right; } + +/* line 281, ../../../../general/res/sass/plots/_plots-main.scss */ +.gl-plot-tick.gl-plot-x-tick-label { + top: 5px; } +/* line 284, ../../../../general/res/sass/plots/_plots-main.scss */ +.gl-plot-tick.gl-plot-y-tick-label { + right: 5px; + left: 5px; } + +/* line 291, ../../../../general/res/sass/plots/_plots-main.scss */ +.tick-label.tick-label-x { + top: 0; } +/* line 294, ../../../../general/res/sass/plots/_plots-main.scss */ +.tick-label.tick-label-y { + right: 0; + left: 0; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* Styles for the iframe EmbeddedPageController element */ +/* line 25, ../../../../general/res/sass/_iframe.scss */ +.l-iframe iframe { + display: block; + height: 100%; + width: 100%; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/******************************** BROWSE */ +/* line 27, ../../../../general/res/sass/_hide-non-functional.scss */ +.browse-mode .browse.top-bar { + display: none; } +/* line 32, ../../../../general/res/sass/_hide-non-functional.scss */ +.browse-mode .browse-area.holder { + top: 10px; } + +/* Styles for sub-dividing views generically */ +/* line 3, ../../../../general/res/sass/_views.scss */ +.l-view-section { + overflow: hidden; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; + font-size: 0.8rem; } + /* line 6, ../../../../general/res/sass/_views.scss */ + .l-view-section h2 { + color: #fff; + margin-bottom: 5px; } + /* line 10, ../../../../general/res/sass/_views.scss */ + .l-view-section.fixed { + font-size: 0.8em; } + /* line 13, ../../../../general/res/sass/_views.scss */ + .l-view-section .controls, + .l-view-section label, + .l-view-section .inline-block { + display: inline-block; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/items/_item.scss */ +.items-holder { + overflow: hidden; + *zoom: 1; + overflow-y: auto; } + /* line 25, ../../../../general/res/sass/items/_item.scss */ + .items-holder .contents { + top: 0; } + /* line 29, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item { + background-color: #ddd; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #666; + display: inline-block; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + -moz-transition: background, 0.25s; + -o-transition: background, 0.25s; + -webkit-transition: background, 0.25s; + transition: background, 0.25s; + text-shadow: none; + box-sizing: border-box; + cursor: pointer; + float: left; + height: 200px; + width: 200px; + margin-bottom: 3px; + margin-right: 3px; + position: relative; } + /* line 274, ../../../../general/res/sass/_mixins.scss */ + .items-holder .item.grid-item .icon { + color: #0099cc; } + @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 279, ../../../../general/res/sass/_mixins.scss */ + .items-holder .item.grid-item:not(.disabled):hover { + background: #d0d0d0; } + /* line 281, ../../../../general/res/sass/_mixins.scss */ + .items-holder .item.grid-item:not(.disabled):hover > .icon { + color: #33ccff; } } + /* line 45, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item:hover .item-main .item-type { + color: deepskyblue; } + /* line 47, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item:hover .item-main .item-type .l-icon-link { + color: #49dedb; } + /* line 51, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item:hover .item-main .item-open { + opacity: 1; } + /* line 55, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .contents { + top: 10px; + right: 10px; + bottom: 10px; + left: 10px; } + /* line 61, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .bar.top-bar { + bottom: auto; + color: #8c8c8c; + height: 20px; + line-height: 20px; + text-align: right; + z-index: 5; } + /* line 68, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .bar.top-bar .left, .items-holder .item.grid-item .bar.top-bar .right { + width: auto; } + /* line 70, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .bar.top-bar .left .icon, .items-holder .item.grid-item .bar.top-bar .right .icon { + margin-left: 3px; } + /* line 72, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .bar.top-bar .left .icon.l-icon-link, .items-holder .item.grid-item .bar.top-bar .right .icon.l-icon-link { + color: #49dedb; } + /* line 78, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .bar.bottom-bar { + top: auto; + line-height: 110%; } + /* line 83, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .item-main { + line-height: 160px; + z-index: 1; } + /* line 89, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .item-main .item-type { + overflow: false; + position: absolute; + top: 40px; + right: 40px; + bottom: 40px; + left: 40px; + width: auto; + height: auto; + text-align: center; + font-size: 96.9px; + line-height: 102px; + bottom: auto; + height: 102px; + top: 30px; } + /* line 100, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .item-main .item-type .l-icon-link { + color: #49dedb; + height: auto; + line-height: 100%; + position: absolute; + font-size: 0.3em; + left: 0px; + bottom: 10px; + z-index: 2; } + /* line 111, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .item-main .item-open { + -moz-transition-property: "opacity"; + -o-transition-property: "opacity"; + -webkit-transition-property: "opacity"; + transition-property: "opacity"; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + opacity: 0; + color: #8c8c8c; + font-size: 3em; + left: auto; + width: 50px; + pointer-events: none; + text-align: right; } + /* line 121, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .title { + text-shadow: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: #666; } + /* line 126, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item .details { + text-shadow: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: #8c8c8c; + font-size: 0.8em; } + /* line 132, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item.selected { + background-color: #0099cc; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + display: inline-block; + -moz-user-select: -moz-none; + -ms-user-select: none; + -webkit-user-select: none; + user-select: none; + -moz-transition: background, 0.25s; + -o-transition: background, 0.25s; + -webkit-transition: background, 0.25s; + transition: background, 0.25s; + text-shadow: none; + color: #80dfff; } + /* line 274, ../../../../general/res/sass/_mixins.scss */ + .items-holder .item.grid-item.selected .icon { + color: #eee; } + /* line 137, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item.selected .item-type, .items-holder .item.grid-item.selected .top-bar .icon:not(.alert) { + color: #80dfff; } + /* line 138, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item.selected .item-main .item-open { + color: #80dfff; } + /* line 139, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item.selected .title { + color: white; } + /* line 141, ../../../../general/res/sass/items/_item.scss */ + .items-holder .item.grid-item.selected:hover .item-main .item-type { + color: white !important; } + +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px), screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 29, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item { + width: 100%; } + /* line 33, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item > .contents { + top: 0px; + right: 10px; + bottom: 0px; + left: 10px; } + /* line 37, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .bar.top-bar { + bottom: 0 !important; + left: auto !important; + right: 20px !important; + width: 40px !important; + height: auto !important; + text-align: right; } + /* line 44, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .bar.bottom-bar { + left: 40px; + right: 60px; } + /* line 52, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .item-main .item-type { + font-size: 30px; + right: auto; + bottom: auto; + left: 0; + line-height: 100%; + text-align: left; + width: 30px; } + /* line 61, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .item-main .item-type .l-icon-link { + bottom: 0; } + /* line 65, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .item-main .item-open { + display: block; + opacity: 1; + font-size: 1em; + width: auto; } } +@media screen and (orientation: portrait) and (max-width: 514px) and (max-height: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (max-height: 514px) and (max-width: 740px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 29, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item { + height: 50px; } + /* line 78, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .bar.top-bar { + line-height: 50px !important; } + /* line 82, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .bar.bottom-bar { + top: 7px; + bottom: auto; + height: 35px; } + /* line 87, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .item-main .item-type { + top: 10px; + bottom: auto; + height: 30px; } + /* line 90, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .item-main .item-open { + line-height: 50px; } } +@media screen and (orientation: portrait) and (min-width: 515px) and (max-width: 799px) and (min-height: 741px) and (max-height: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 799px) and (max-device-height: 1024px), screen and (orientation: landscape) and (min-height: 515px) and (max-height: 799px) and (min-width: 741px) and (max-width: 1024px) and (max-device-width: 1024px) and (max-device-height: 799px) { + /* line 29, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item { + height: 66px; } + /* line 100, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .bar.top-bar { + line-height: 66px !important; } + /* line 104, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .bar.bottom-bar { + top: 15px; + bottom: auto; + height: 35px; } + /* line 109, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .item-main .item-type { + top: 18px; + bottom: auto; + height: 30px; } + /* line 112, ../../../../general/res/sass/mobile/_item.scss */ + .items-holder .item.grid-item .item-main .item-open { + line-height: 66px; } } + +/********************************* TO BE MOVED */ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* line 22, ../../../../general/res/sass/_autoflow.scss */ +.autoflow { + font-size: 0.75rem; } + /* line 32, ../../../../general/res/sass/_autoflow.scss */ + .autoflow:hover .l-autoflow-header .s-btn.change-column-width, .autoflow:hover .l-autoflow-header .change-column-width.s-menu-btn { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 50ms; + -o-transition-duration: 50ms; + -webkit-transition-duration: 50ms; + transition-duration: 50ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + opacity: 1; } + /* line 40, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-header { + bottom: auto; + height: 22px; + line-height: 22px; + min-width: 225px; } + /* line 45, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-header span { + vertical-align: middle; } + /* line 48, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-header .s-btn.change-column-width, .autoflow .l-autoflow-header .change-column-width.s-menu-btn { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 500ms; + -o-transition-duration: 500ms; + -webkit-transition-duration: 500ms; + transition-duration: 500ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + opacity: 0; } + /* line 52, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-header .l-filter { + margin-left: 5px; } + /* line 54, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-header .l-filter input.t-filter-input { + width: 100px; } + /* line 60, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items { + overflow-x: scroll; + overflow-y: hidden; + top: 32px; + white-space: nowrap; } + /* line 66, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + border-left: 1px solid rgba(102, 102, 102, 0.2); + display: inline-block; + padding-left: 5px; + padding-right: 5px; + vertical-align: top; + width: 225px; } + /* line 76, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); + display: block; + height: 15px; + line-height: 15px; + margin-bottom: 1px; + margin-top: 1px; + position: relative; } + /* line 85, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row:first-child { + border-top: none; } + /* line 88, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row:hover { + background: rgba(255, 255, 255, 0.1); } + /* line 93, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row.s-stale .l-autoflow-item.l { + color: rgba(51, 51, 51, 0.3) !important; + font-style: italic; } + /* line 94, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row.s-stale .l-autoflow-item.r { + color: rgba(51, 51, 51, 0.5) !important; + font-style: italic; } + /* line 97, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row:not(.s-stale) .l-autoflow-item.r { + color: gray; } + /* line 101, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row.first-in-group { + border-top: 1px solid rgba(153, 153, 153, 0.2); } + /* line 104, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row .l-autoflow-item { + display: block; } + /* line 106, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row .l-autoflow-item.l { + float: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: auto; } + /* line 113, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col .l-autoflow-row .l-autoflow-item.r { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + float: right; + margin-left: 5px; + padding-left: 5px; + padding-right: 5px; + text-align: right; } + /* line 124, ../../../../general/res/sass/_autoflow.scss */ + .autoflow .l-autoflow-items .l-autoflow-col:first-child { + border-left: none; + padding-left: 0; } + +/* line 1, ../../../../general/res/sass/features/_imagery.scss */ +.l-image-main-wrapper, +.l-image-main, +.l-image-main-controlbar, +.l-image-thumbs-wrapper { + overflow: false; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; } + +/*************************************** MAIN LAYOUT */ +/* line 9, ../../../../general/res/sass/features/_imagery.scss */ +.l-image-main-wrapper { + min-height: 100px; + min-width: 150px; } + /* line 16, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-wrapper .l-image-main { + background-color: rgba(0, 0, 0, 0.05); + bottom: 30px; } + /* line 20, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-wrapper .l-image-main-controlbar { + top: auto; + height: 25px; } + +/* line 26, ../../../../general/res/sass/features/_imagery.scss */ +.l-image-thumbs-wrapper { + top: auto; + height: 168px; } + +/* line 32, ../../../../general/res/sass/features/_imagery.scss */ +.l-date, +.l-time, +.l-timezone { + display: inline-block; } + +/*************************************** MAIN IMAGE */ +/* line 40, ../../../../general/res/sass/features/_imagery.scss */ +.l-image-main, +.l-image-thumb-item .l-thumb { + background-size: contain; + background-position: center; + background-repeat: no-repeat; } + +/* line 51, ../../../../general/res/sass/features/_imagery.scss */ +.l-image-main-controlbar { + font-size: 0.8em; + line-height: 25px; } + /* line 55, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-controlbar .left, .l-image-main-controlbar .right { + direction: rtl; + overflow: hidden; } + /* line 59, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-controlbar .left { + text-align: left; } + /* line 63, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-controlbar .right { + z-index: 2; } + /* line 67, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-controlbar .l-date, + .l-image-main-controlbar .l-time { + color: #333333; } + /* line 71, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-controlbar .l-mag { + direction: ltr; + display: inline-block; } + /* line 75, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-controlbar .l-mag:before { + content: "\000049"; } + /* line 79, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-controlbar .s-mag { + color: #999999; } + /* line 82, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-main-controlbar .l-btn.show-thumbs { + display: none; } + +/* line 87, ../../../../general/res/sass/features/_imagery.scss */ +.s-image-main { + border: 1px solid transparent; } + /* line 89, ../../../../general/res/sass/features/_imagery.scss */ + .s-image-main.paused { + border-color: #ff9900; } + +/*************************************** THUMBS */ +/* line 96, ../../../../general/res/sass/features/_imagery.scss */ +.l-image-thumbs-wrapper { + direction: rtl; + overflow-x: auto; + overflow-y: hidden; + padding-bottom: 5px; + white-space: nowrap; + z-index: 70; } + +/* line 106, ../../../../general/res/sass/features/_imagery.scss */ +.l-image-thumb-item { + -moz-transition: background-color 0.25s; + -o-transition: background-color 0.25s; + -webkit-transition: background-color 0.25s; + transition: background-color 0.25s; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 1px; + position: relative; + cursor: pointer; + direction: ltr; + display: inline-block; + font-size: 0.8em; + margin-left: 3px; + text-align: left; + width: 122px; + white-space: normal; } + /* line 111, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-thumb-item .l-thumb, + .l-image-thumb-item .l-date, + .l-image-thumb-item .l-time { + display: inline-block; } + /* line 116, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-thumb-item .l-date, + .l-image-thumb-item .l-time { + padding: 2px 3px; } + /* line 128, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-thumb-item:hover { + background: rgba(255, 255, 255, 0.2); } + /* line 130, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-thumb-item:hover .l-date, + .l-image-thumb-item:hover .l-time { + color: #fff; } + /* line 135, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-thumb-item.selected { + background: #0099cc; } + /* line 137, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-thumb-item.selected .l-date, + .l-image-thumb-item.selected .l-time { + color: #fff; } + /* line 142, ../../../../general/res/sass/features/_imagery.scss */ + .l-image-thumb-item .l-thumb { + background-color: rgba(255, 255, 255, 0.1); + height: 120px; + width: 120px; + margin-top: 0; } + +/*************************************** WHEN IN FRAME */ +/* line 152, ../../../../general/res/sass/features/_imagery.scss */ +.frame .t-imagery .l-image-main-wrapper { + bottom: 0; } + /* line 154, ../../../../general/res/sass/features/_imagery.scss */ + .frame .t-imagery .l-image-main-wrapper .l-image-main-controlbar { + font-size: 0.7em; } +/* line 163, ../../../../general/res/sass/features/_imagery.scss */ +.frame .t-imagery .l-image-thumbs-wrapper { + display: none; } + +/* line 5, ../../../../general/res/sass/features/_time-display.scss */ +.l-time-display:hover .l-btn.control { + opacity: 1; } +/* line 9, ../../../../general/res/sass/features/_time-display.scss */ +.l-time-display .l-elem-wrapper { + position: relative; } +/* line 12, ../../../../general/res/sass/features/_time-display.scss */ +.l-time-display .l-elem { + display: inline-block; } +/* line 17, ../../../../general/res/sass/features/_time-display.scss */ +.l-time-display.l-timer .l-elem.l-value { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + position: absolute; + left: 0; + z-index: 1; } + /* line 22, ../../../../general/res/sass/features/_time-display.scss */ + .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .direction.s-icon-btn, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { + font-size: 0.8em; } +/* line 26, ../../../../general/res/sass/features/_time-display.scss */ +.l-time-display.l-timer:hover .l-elem.l-value { + left: 20px; } +/* line 33, ../../../../general/res/sass/features/_time-display.scss */ +.l-time-display .l-elem .value.active, .l-time-display .l-elem.value.active { + color: #fff; } +/* line 38, ../../../../general/res/sass/features/_time-display.scss */ +.l-time-display .l-btn.control { + -moz-transition-property: opacity, background-color, border-color, color; + -o-transition-property: opacity, background-color, border-color, color; + -webkit-transition-property: opacity, background-color, border-color, color; + transition-property: opacity, background-color, border-color, color; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + opacity: 0; + font-size: 0.65em; + vertical-align: top; } + +/* line 3, ../sass/_controls.scss */ +.s-btn.major .title-label, .major.s-menu-btn .title-label { + text-transform: uppercase; } diff --git a/platform/commonUI/themes/snow/res/sass/_constants.scss b/platform/commonUI/themes/snow/res/sass/_constants.scss index f97f2a4df0..13390fa79e 100644 --- a/platform/commonUI/themes/snow/res/sass/_constants.scss +++ b/platform/commonUI/themes/snow/res/sass/_constants.scss @@ -75,6 +75,13 @@ $colorInputIcon: pushBack($colorBodyFg, 25%); $colorSelectBg: #ddd; $colorSelectFg: $colorBodyFg; +// Inspector +$colorInspectorFg: $colorBodyFg; +$colorInspectorPropName: pushBack($colorBodyFg, 20%); +$colorInspectorPropVal: $colorInspectorFg; +$colorInspectorSectionHeaderBg: pullForward($colorBodyBg, 5%); +$colorInspectorSectionHeaderFg: pullForward($colorBodyBg, 40%); + // Limits and staleness colors// $colorTelemFresh: pullForward($colorBodyFg, 20%); $colorTelemStale: pushBack($colorBodyFg, 20%); @@ -110,6 +117,7 @@ $colorItemBgSelected: $colorKey; // Tabular $colorTabBorder: pullForward($colorBodyBg, 10%); +$colorItemTreeHoverFg: pullForward($colorBodyFg, 20%); $colorTabBodyBg: $colorBodyBg; $colorTabBodyFg: pullForward($colorBodyFg, 20%); $colorTabHeaderBg: pullForward($colorBodyBg, 10%); @@ -125,6 +133,7 @@ $colorPlotAreaBorder: $colorInteriorBorder; $colorPlotLabelFg: pushBack($colorPlotFg, 20%); // Tree +$colorItemTreeHoverBg: rgba($colorBodyFg, 0.1); $colorItemTreeIcon: $colorKey; $colorItemTreeIconHover: $colorItemTreeIcon; //pushBack($colorItemTreeIcon, 20%); $colorItemTreeVCHover: $colorKey; From 17b3378655346a006c8ec2e1345fea08d1fcb6c5 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 23 Oct 2015 15:36:52 -0700 Subject: [PATCH 155/488] [Frontend] Expand/collapse styling open #90 Modding mini-tab look, just started; --- .../commonUI/general/res/sass/_inspector.scss | 2 - .../general/res/sass/controls/_buttons.scss | 91 ++++++ .../res/sass/user-environ/_layout.scss | 2 + .../espresso/res/css/theme-espresso.css | 282 ++++++++++++------ .../themes/snow/res/css/theme-snow.css | 282 ++++++++++++------ 5 files changed, 491 insertions(+), 168 deletions(-) diff --git a/platform/commonUI/general/res/sass/_inspector.scss b/platform/commonUI/general/res/sass/_inspector.scss index fcb8ebc959..bd818738a9 100644 --- a/platform/commonUI/general/res/sass/_inspector.scss +++ b/platform/commonUI/general/res/sass/_inspector.scss @@ -21,8 +21,6 @@ *****************************************************************************/ /* Styles for the Inspector pane */ -//.pane.right.t-inspect { @include test(green, 0.3); } // TEMP! - .l-inspect, .l-inspect table tr td { font-size: 0.7rem; diff --git a/platform/commonUI/general/res/sass/controls/_buttons.scss b/platform/commonUI/general/res/sass/controls/_buttons.scss index c33e2306ac..8efcc9af18 100644 --- a/platform/commonUI/general/res/sass/controls/_buttons.scss +++ b/platform/commonUI/general/res/sass/controls/_buttons.scss @@ -102,6 +102,97 @@ $pad: $interiorMargin * $baseRatio; } .mini-tab { + // Meant to be used as pane hide/show control elements in concert with mct-splitter + @extend .ui-symbol; + @include desktop { + @include trans-prop-nice(left, 150ms); + //@include test(green); + $iconD: 12px; + $arwD: 7px; + $arwOffsetX: 0px; + $arwAnimOffsetX: 2px; + $arwToLeftAnimX: -1 * $arwAnimOffsetX; + $arwToRightAnimX: $arwAnimOffsetX; + $arwToLeftX: ($arwOffsetX + $arwD) * -1; + $arwToRightX: $iconD + $arwOffsetX; + $c: pullForward($colorBodyBg, 15%); + color: $c; + cursor: pointer; + display: block; + position: absolute; + font-size: $iconD; + line-height: $iconD; + height: $iconD; width: $iconD; + + &:hover { + color: pullForward($c, 20%); + &:after { + color: $colorKey; + } + } + + &:before, + &:after { + @include trans-prop-nice(color, 200ms); + display: block; + position: absolute; + } + + &:before { + // Always the arrow icon + @include trans-prop-nice(left, 150ms); + //@include test(green); + font-size: $arwD; + height: 100%; width: $arwD; + } + &:after { + // Always vertical bar element + width: 100%; + height: 100%; + } + + &.anchor-left { + // < [] + $xpos: $arwToLeftX; + &:before { + content:'\3c'; + left: $xpos; + } + &:hover:before { left: $xpos + $arwToLeftAnimX; } + &.collapsed { + $xpos: $arwToRightX; + &:before { + content:'\3e'; + left: $xpos; + } + &:hover:before { left: $xpos + $arwToRightAnimX; } + } + } + &.anchor-right { + // [] > + $xpos: $arwToRightX; + &:before { + content:'\3e'; + left: $xpos; + } + &:hover:before { left: $xpos + $arwToRightAnimX; } + &.collapsed { + $xpos: $arwToLeftX; + &:before { + content:'\3c'; + left: $xpos; + } + &:hover:before { left: $xpos + $arwToLeftAnimX; } + } + } + + &.collapsed { + // State when the pane this element controls has been collapsed + } + } +} + +.mini-tab-icon { // Meant to be used as pane hide/show control elements in concert with mct-splitter @extend .ui-symbol; @include desktop { diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 6a3d9ac2f5..d32ac2cb93 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -214,6 +214,7 @@ &.t-inspect.right { min-width: 100px; max-width: 800px; + padding-left: 15px; // Allow room for mini-tab element width: $ueBrowseRightPaneInspectW; } } @@ -233,6 +234,7 @@ } .pane { + @include box-sizing(border-box); position: absolute; .pane-header { diff --git a/platform/commonUI/themes/espresso/res/css/theme-espresso.css b/platform/commonUI/themes/espresso/res/css/theme-espresso.css index 08c6beb3d7..602b4455ed 100644 --- a/platform/commonUI/themes/espresso/res/css/theme-espresso.css +++ b/platform/commonUI/themes/espresso/res/css/theme-espresso.css @@ -603,29 +603,29 @@ mct-container { border-right: 5px solid transparent; } /* line 31, ../../../../general/res/sass/_icons.scss */ -.ui-symbol, .s-icon-btn, .mini-tab, .l-datetime-picker .l-month-year-pager .pager { +.ui-symbol, .s-icon-btn, .mini-tab, .mini-tab-icon, .l-datetime-picker .l-month-year-pager .pager { font-family: 'symbolsfont'; } /* line 33, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.type-icon, .type-icon.s-icon-btn, .type-icon.mini-tab, .l-datetime-picker .l-month-year-pager .type-icon.pager { + .ui-symbol.type-icon, .type-icon.s-icon-btn, .type-icon.mini-tab, .type-icon.mini-tab-icon, .l-datetime-picker .l-month-year-pager .type-icon.pager { color: #cccccc; } /* line 36, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon, .icon.s-icon-btn, .icon.mini-tab, .l-datetime-picker .l-month-year-pager .icon.pager { + .ui-symbol.icon, .icon.s-icon-btn, .icon.mini-tab, .icon.mini-tab-icon, .l-datetime-picker .l-month-year-pager .icon.pager { color: #0099cc; } /* line 38, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.alert, .icon.alert.s-icon-btn, .icon.alert.mini-tab, .l-datetime-picker .l-month-year-pager .icon.alert.pager { + .ui-symbol.icon.alert, .icon.alert.s-icon-btn, .icon.alert.mini-tab, .icon.alert.mini-tab-icon, .l-datetime-picker .l-month-year-pager .icon.alert.pager { color: #ff3c00; } /* line 40, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.alert:hover, .icon.alert.s-icon-btn:hover, .icon.alert.mini-tab:hover, .l-datetime-picker .l-month-year-pager .icon.alert.pager:hover { + .ui-symbol.icon.alert:hover, .icon.alert.s-icon-btn:hover, .icon.alert.mini-tab:hover, .icon.alert.mini-tab-icon:hover, .l-datetime-picker .l-month-year-pager .icon.alert.pager:hover { color: #ff8a66; } /* line 44, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.major, .icon.major.s-icon-btn, .icon.major.mini-tab, .l-datetime-picker .l-month-year-pager .icon.major.pager { + .ui-symbol.icon.major, .icon.major.s-icon-btn, .icon.major.mini-tab, .icon.major.mini-tab-icon, .l-datetime-picker .l-month-year-pager .icon.major.pager { font-size: 1.65em; } /* line 48, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon-calendar:after, .icon-calendar.s-icon-btn:after, .icon-calendar.mini-tab:after, .l-datetime-picker .l-month-year-pager .icon-calendar.pager:after { + .ui-symbol.icon-calendar:after, .icon-calendar.s-icon-btn:after, .icon-calendar.mini-tab:after, .icon-calendar.mini-tab-icon:after, .l-datetime-picker .l-month-year-pager .icon-calendar.pager:after { content: "\e605"; } /* line 53, ../../../../general/res/sass/_icons.scss */ -.bar .ui-symbol, .bar .s-icon-btn, .bar .mini-tab, .bar .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .bar .pager { +.bar .ui-symbol, .bar .s-icon-btn, .bar .mini-tab, .bar .mini-tab-icon, .bar .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .bar .pager { display: inline-block; } /* line 57, ../../../../general/res/sass/_icons.scss */ @@ -1258,27 +1258,27 @@ mct-container { * at runtime from the About dialog for additional information. *****************************************************************************/ /* Styles for the Inspector pane */ -/* line 26, ../../../../general/res/sass/_inspector.scss */ +/* line 24, ../../../../general/res/sass/_inspector.scss */ .l-inspect, .l-inspect table tr td { font-size: 0.7rem; } -/* line 31, ../../../../general/res/sass/_inspector.scss */ +/* line 29, ../../../../general/res/sass/_inspector.scss */ .l-inspect { color: #999; } - /* line 33, ../../../../general/res/sass/_inspector.scss */ + /* line 31, ../../../../general/res/sass/_inspector.scss */ .l-inspect .pane-header { color: #666666; font-size: 0.8rem; } - /* line 38, ../../../../general/res/sass/_inspector.scss */ + /* line 36, ../../../../general/res/sass/_inspector.scss */ .l-inspect ul li, .l-inspect em { display: block; position: relative; } - /* line 44, ../../../../general/res/sass/_inspector.scss */ + /* line 42, ../../../../general/res/sass/_inspector.scss */ .l-inspect ul li { margin-bottom: 20px; } - /* line 48, ../../../../general/res/sass/_inspector.scss */ + /* line 46, ../../../../general/res/sass/_inspector.scss */ .l-inspect em { -moz-border-radius: 2px; -webkit-border-radius: 2px; @@ -1288,40 +1288,40 @@ mct-container { margin-bottom: 5px; padding: 5px 5px; text-transform: uppercase; } - /* line 58, ../../../../general/res/sass/_inspector.scss */ + /* line 56, ../../../../general/res/sass/_inspector.scss */ .l-inspect table tr td { border: none; border-top: 1px solid rgba(153, 153, 153, 0.1) !important; padding: 2px 0; vertical-align: top; } - /* line 63, ../../../../general/res/sass/_inspector.scss */ + /* line 61, ../../../../general/res/sass/_inspector.scss */ .l-inspect table tr td.label { color: #666666 !important; padding-right: 5px !important; white-space: nowrap; } - /* line 68, ../../../../general/res/sass/_inspector.scss */ + /* line 66, ../../../../general/res/sass/_inspector.scss */ .l-inspect table tr td.value { word-break: break-all; } - /* line 73, ../../../../general/res/sass/_inspector.scss */ + /* line 71, ../../../../general/res/sass/_inspector.scss */ .l-inspect table tr:first-child td { border-top: none !important; } - /* line 78, ../../../../general/res/sass/_inspector.scss */ + /* line 76, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location { line-height: 180%; } - /* line 80, ../../../../general/res/sass/_inspector.scss */ + /* line 78, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location .location-item { cursor: pointer; display: inline; position: relative; padding: 2px 4px; } - /* line 85, ../../../../general/res/sass/_inspector.scss */ + /* line 83, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location .location-item:hover { background: rgba(153, 153, 153, 0.1); color: #cccccc; } - /* line 88, ../../../../general/res/sass/_inspector.scss */ + /* line 86, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location .location-item:hover .icon { color: #33ccff; } - /* line 93, ../../../../general/res/sass/_inspector.scss */ + /* line 91, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location:not(.first):before { color: #737373; content: '\3e'; @@ -1649,45 +1649,152 @@ mct-container { .mini-tab:after { width: 100%; height: 100%; } - /* line 158, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 157, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:before { content: '\3c'; left: -7px; } - /* line 162, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 161, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:hover:before { left: -9px; } - /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 164, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left.collapsed:before { content: '\3e'; left: 12px; } - /* line 169, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 168, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left.collapsed:hover:before { left: 14px; } - /* line 175, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 174, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:before { content: '\3e'; left: 12px; } - /* line 179, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 178, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:hover:before { left: 14px; } - /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 181, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right.collapsed:before { content: '\3c'; left: -7px; } - /* line 186, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 185, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right.collapsed:hover:before { left: -9px; } } -/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 195, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + color: #595959; + cursor: pointer; + display: block; + position: absolute; + font-size: 12px; + line-height: 12px; + height: 12px; + width: 12px; } + /* line 218, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:hover { + color: #8c8c8c; } + /* line 220, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:hover:after { + color: #0099cc; } + /* line 225, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:before, .mini-tab-icon:after { + -moz-transition-property: color; + -o-transition-property: color; + -webkit-transition-property: color; + transition-property: color; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + display: block; + position: absolute; } + /* line 232, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:before { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + font-size: 7px; + height: 100%; + width: 7px; } + /* line 239, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:after { + width: 100%; + height: 100%; } + /* line 249, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-left:before { + content: '\3c'; + left: -7px; } + /* line 253, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-left:hover:before { + left: -9px; } + /* line 256, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-left.collapsed:before { + content: '\3e'; + left: 12px; } + /* line 260, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-left.collapsed:hover:before { + left: 14px; } + /* line 266, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-right:before { + content: '\3e'; + left: 12px; } + /* line 270, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-right:hover:before { + left: 14px; } + /* line 273, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-right.collapsed:before { + content: '\3c'; + left: -7px; } + /* line 277, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-right.collapsed:hover:before { + left: -9px; } } + +/* line 287, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { font-size: 0; } - /* line 202, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 293, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .s-btn, .l-btn-set .s-menu-btn { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; margin-left: 1px; } - /* line 208, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 299, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; @@ -1696,7 +1803,7 @@ mct-container { -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; margin-left: 0; } - /* line 215, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 306, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; @@ -1705,7 +1812,7 @@ mct-container { -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; } -/* line 222, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 313, ../../../../general/res/sass/controls/_buttons.scss */ .paused:not(.s-btn):not(.s-menu-btn) { border-color: #c56f01 !important; color: #c56f01 !important; } @@ -2409,7 +2516,7 @@ label.checkbox.custom { left: 0; text-align: left; } /* line 57, ../../../../general/res/sass/controls/_menus.scss */ - .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .icon.s-icon-btn, .s-menu-btn .menu .icon.mini-tab, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { + .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .icon.s-icon-btn, .s-menu-btn .menu .icon.mini-tab, .s-menu-btn .menu .icon.mini-tab-icon, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { width: 12px; } /******************************************************** MENUS THEMSELVES */ @@ -2697,10 +2804,11 @@ mct-include.l-time-controller { mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .lbl { color: #666666; } /* line 69, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab-icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.mini-tab, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.mini-tab-icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.pager { font-size: 11px; @@ -3426,10 +3534,11 @@ span.req { .t-filter input.t-filter-input:not(.ng-dirty) + .t-a-clear { display: none; } /* line 42, ../../../../general/res/sass/forms/_filter.scss */ -.filter .icon.ui-symbol, .filter .icon.s-icon-btn, .filter .icon.mini-tab, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, +.filter .icon.ui-symbol, .filter .icon.s-icon-btn, .filter .icon.mini-tab, .filter .icon.mini-tab-icon, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, .t-filter .icon.ui-symbol, .t-filter .icon.s-icon-btn, .t-filter .icon.mini-tab, +.t-filter .icon.mini-tab-icon, .t-filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .t-filter .icon.pager { -moz-border-radius: 3px; @@ -3442,18 +3551,20 @@ span.req { padding: 0px 5px; vertical-align: middle; } /* line 50, ../../../../general/res/sass/forms/_filter.scss */ - .filter .icon.ui-symbol:hover, .filter .icon.s-icon-btn:hover, .filter .icon.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, + .filter .icon.ui-symbol:hover, .filter .icon.s-icon-btn:hover, .filter .icon.mini-tab:hover, .filter .icon.mini-tab-icon:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, .t-filter .icon.ui-symbol:hover, .t-filter .icon.s-icon-btn:hover, .t-filter .icon.mini-tab:hover, + .t-filter .icon.mini-tab-icon:hover, .t-filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .t-filter .icon.pager:hover { background: rgba(255, 255, 255, 0.1); } /* line 54, ../../../../general/res/sass/forms/_filter.scss */ -.filter .s-a-clear.ui-symbol, .filter .s-a-clear.s-icon-btn, .filter .s-a-clear.mini-tab, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, +.filter .s-a-clear.ui-symbol, .filter .s-a-clear.s-icon-btn, .filter .s-a-clear.mini-tab, .filter .s-a-clear.mini-tab-icon, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, .t-filter .s-a-clear.ui-symbol, .t-filter .s-a-clear.s-icon-btn, .t-filter .s-a-clear.mini-tab, +.t-filter .s-a-clear.mini-tab-icon, .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager { -moz-border-radius: 3px; @@ -3479,10 +3590,11 @@ span.req { text-align: center; z-index: 5; } /* line 74, ../../../../general/res/sass/forms/_filter.scss */ - .filter .s-a-clear.ui-symbol:hover, .filter .s-a-clear.s-icon-btn:hover, .filter .s-a-clear.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, + .filter .s-a-clear.ui-symbol:hover, .filter .s-a-clear.s-icon-btn:hover, .filter .s-a-clear.mini-tab:hover, .filter .s-a-clear.mini-tab-icon:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, .t-filter .s-a-clear.ui-symbol:hover, .t-filter .s-a-clear.s-icon-btn:hover, .t-filter .s-a-clear.mini-tab:hover, + .t-filter .s-a-clear.mini-tab-icon:hover, .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager:hover { filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); @@ -3721,67 +3833,71 @@ span.req { .browse-mode .split-layout .split-pane-component.pane.t-inspect.right { min-width: 100px; max-width: 800px; + padding-left: 15px; width: 20%; } -/* line 225, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 226, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right { width: 15%; } - /* line 227, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 228, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right .pane.bottom { min-height: 50px; height: 30%; } -/* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 236, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; position: absolute; } - /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 240, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .pane-header { text-transform: uppercase; height: 24px; line-height: 24px; margin-bottom: 5px; } - /* line 246, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 248, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 252, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 257, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 258, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 260, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 266, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { z-index: 2; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 266, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { top: 5px; } - /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { left: -30px; } - /* line 273, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { content: 'F'; } - /* line 276, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { left: -25px; } - /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 282, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { right: -25px; } - /* line 283, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 285, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { content: '\e608'; } - /* line 286, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 288, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { right: -20px; } } - /* line 295, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 297, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3794,31 +3910,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 306, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 316, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 318, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 319, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 321, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 323, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 327, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 333, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 335, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 336, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3828,11 +3944,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 340, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 342, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 346, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 348, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3844,12 +3960,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 359, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 361, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3865,36 +3981,36 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 372, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 376, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 384, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 385, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 387, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 397, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 399, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.left.treeview, .pane-tree-hidden .splitter-treeview { opacity: 0; } -/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 404, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.right.items { left: 15px !important; } -/* line 409, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 411, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-showing .pane.left.treeview, .pane-tree-showing .splitter-treeview { -moz-transition-property: opacity, background-color, border-color, color; @@ -3915,7 +4031,7 @@ span.req { transition-delay: 250ms; opacity: 1; } -/* line 418, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 420, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-inspect-showing .l-object-and-inspector .pane.right, .pane-inspect-showing .l-object-and-inspector .splitter-inspect { -moz-transition-property: opacity, background-color, border-color, color; @@ -3936,16 +4052,16 @@ span.req { transition-delay: 250ms; opacity: 1; } -/* line 427, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 429, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-inspect-hidden .l-object-and-inspector .pane.right, .pane-inspect-hidden .l-object-and-inspector .splitter-inspect { opacity: 0; } -/* line 431, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 433, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-inspect-hidden .l-object-and-inspector .pane.left { right: 15px !important; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 438, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 440, ../../../../general/res/sass/user-environ/_layout.scss */ .pane:not(.resizing) { -moz-transition-property: width, left, right; -o-transition-property: width, left, right; @@ -6559,7 +6675,7 @@ table { left: 0; z-index: 1; } /* line 22, ../../../../general/res/sass/features/_time-display.scss */ - .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .direction.s-icon-btn, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { + .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .direction.s-icon-btn, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab-icon, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { font-size: 0.8em; } /* line 26, ../../../../general/res/sass/features/_time-display.scss */ .l-time-display.l-timer:hover .l-elem.l-value { diff --git a/platform/commonUI/themes/snow/res/css/theme-snow.css b/platform/commonUI/themes/snow/res/css/theme-snow.css index 126ad40fa9..575e954f6c 100644 --- a/platform/commonUI/themes/snow/res/css/theme-snow.css +++ b/platform/commonUI/themes/snow/res/css/theme-snow.css @@ -603,29 +603,29 @@ mct-container { border-right: 5px solid transparent; } /* line 31, ../../../../general/res/sass/_icons.scss */ -.ui-symbol, .s-icon-btn, .mini-tab, .l-datetime-picker .l-month-year-pager .pager { +.ui-symbol, .s-icon-btn, .mini-tab, .mini-tab-icon, .l-datetime-picker .l-month-year-pager .pager { font-family: 'symbolsfont'; } /* line 33, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.type-icon, .type-icon.s-icon-btn, .type-icon.mini-tab, .l-datetime-picker .l-month-year-pager .type-icon.pager { + .ui-symbol.type-icon, .type-icon.s-icon-btn, .type-icon.mini-tab, .type-icon.mini-tab-icon, .l-datetime-picker .l-month-year-pager .type-icon.pager { color: #b3b3b3; } /* line 36, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon, .icon.s-icon-btn, .icon.mini-tab, .l-datetime-picker .l-month-year-pager .icon.pager { + .ui-symbol.icon, .icon.s-icon-btn, .icon.mini-tab, .icon.mini-tab-icon, .l-datetime-picker .l-month-year-pager .icon.pager { color: #0099cc; } /* line 38, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.alert, .icon.alert.s-icon-btn, .icon.alert.mini-tab, .l-datetime-picker .l-month-year-pager .icon.alert.pager { + .ui-symbol.icon.alert, .icon.alert.s-icon-btn, .icon.alert.mini-tab, .icon.alert.mini-tab-icon, .l-datetime-picker .l-month-year-pager .icon.alert.pager { color: #ff3c00; } /* line 40, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.alert:hover, .icon.alert.s-icon-btn:hover, .icon.alert.mini-tab:hover, .l-datetime-picker .l-month-year-pager .icon.alert.pager:hover { + .ui-symbol.icon.alert:hover, .icon.alert.s-icon-btn:hover, .icon.alert.mini-tab:hover, .icon.alert.mini-tab-icon:hover, .l-datetime-picker .l-month-year-pager .icon.alert.pager:hover { color: #ff8a66; } /* line 44, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon.major, .icon.major.s-icon-btn, .icon.major.mini-tab, .l-datetime-picker .l-month-year-pager .icon.major.pager { + .ui-symbol.icon.major, .icon.major.s-icon-btn, .icon.major.mini-tab, .icon.major.mini-tab-icon, .l-datetime-picker .l-month-year-pager .icon.major.pager { font-size: 1.65em; } /* line 48, ../../../../general/res/sass/_icons.scss */ - .ui-symbol.icon-calendar:after, .icon-calendar.s-icon-btn:after, .icon-calendar.mini-tab:after, .l-datetime-picker .l-month-year-pager .icon-calendar.pager:after { + .ui-symbol.icon-calendar:after, .icon-calendar.s-icon-btn:after, .icon-calendar.mini-tab:after, .icon-calendar.mini-tab-icon:after, .l-datetime-picker .l-month-year-pager .icon-calendar.pager:after { content: "\e605"; } /* line 53, ../../../../general/res/sass/_icons.scss */ -.bar .ui-symbol, .bar .s-icon-btn, .bar .mini-tab, .bar .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .bar .pager { +.bar .ui-symbol, .bar .s-icon-btn, .bar .mini-tab, .bar .mini-tab-icon, .bar .l-datetime-picker .l-month-year-pager .pager, .l-datetime-picker .l-month-year-pager .bar .pager { display: inline-block; } /* line 57, ../../../../general/res/sass/_icons.scss */ @@ -1255,27 +1255,27 @@ mct-container { * at runtime from the About dialog for additional information. *****************************************************************************/ /* Styles for the Inspector pane */ -/* line 26, ../../../../general/res/sass/_inspector.scss */ +/* line 24, ../../../../general/res/sass/_inspector.scss */ .l-inspect, .l-inspect table tr td { font-size: 0.7rem; } -/* line 31, ../../../../general/res/sass/_inspector.scss */ +/* line 29, ../../../../general/res/sass/_inspector.scss */ .l-inspect { color: #666; } - /* line 33, ../../../../general/res/sass/_inspector.scss */ + /* line 31, ../../../../general/res/sass/_inspector.scss */ .l-inspect .pane-header { color: #999999; font-size: 0.8rem; } - /* line 38, ../../../../general/res/sass/_inspector.scss */ + /* line 36, ../../../../general/res/sass/_inspector.scss */ .l-inspect ul li, .l-inspect em { display: block; position: relative; } - /* line 44, ../../../../general/res/sass/_inspector.scss */ + /* line 42, ../../../../general/res/sass/_inspector.scss */ .l-inspect ul li { margin-bottom: 20px; } - /* line 48, ../../../../general/res/sass/_inspector.scss */ + /* line 46, ../../../../general/res/sass/_inspector.scss */ .l-inspect em { -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -1285,40 +1285,40 @@ mct-container { margin-bottom: 5px; padding: 5px 5px; text-transform: uppercase; } - /* line 58, ../../../../general/res/sass/_inspector.scss */ + /* line 56, ../../../../general/res/sass/_inspector.scss */ .l-inspect table tr td { border: none; border-top: 1px solid rgba(102, 102, 102, 0.2) !important; padding: 2px 0; vertical-align: top; } - /* line 63, ../../../../general/res/sass/_inspector.scss */ + /* line 61, ../../../../general/res/sass/_inspector.scss */ .l-inspect table tr td.label { color: #999999 !important; padding-right: 5px !important; white-space: nowrap; } - /* line 68, ../../../../general/res/sass/_inspector.scss */ + /* line 66, ../../../../general/res/sass/_inspector.scss */ .l-inspect table tr td.value { word-break: break-all; } - /* line 73, ../../../../general/res/sass/_inspector.scss */ + /* line 71, ../../../../general/res/sass/_inspector.scss */ .l-inspect table tr:first-child td { border-top: none !important; } - /* line 78, ../../../../general/res/sass/_inspector.scss */ + /* line 76, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location { line-height: 180%; } - /* line 80, ../../../../general/res/sass/_inspector.scss */ + /* line 78, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location .location-item { cursor: pointer; display: inline; position: relative; padding: 2px 4px; } - /* line 85, ../../../../general/res/sass/_inspector.scss */ + /* line 83, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location .location-item:hover { background: rgba(102, 102, 102, 0.1); color: #333333; } - /* line 88, ../../../../general/res/sass/_inspector.scss */ + /* line 86, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location .location-item:hover .icon { color: #0099cc; } - /* line 93, ../../../../general/res/sass/_inspector.scss */ + /* line 91, ../../../../general/res/sass/_inspector.scss */ .l-inspect .inspector-location:not(.first):before { color: #8c8c8c; content: '\3e'; @@ -1619,45 +1619,152 @@ mct-container { .mini-tab:after { width: 100%; height: 100%; } - /* line 158, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 157, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:before { content: '\3c'; left: -7px; } - /* line 162, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 161, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left:hover:before { left: -9px; } - /* line 165, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 164, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left.collapsed:before { content: '\3e'; left: 12px; } - /* line 169, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 168, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-left.collapsed:hover:before { left: 14px; } - /* line 175, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 174, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:before { content: '\3e'; left: 12px; } - /* line 179, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 178, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right:hover:before { left: 14px; } - /* line 182, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 181, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right.collapsed:before { content: '\3c'; left: -7px; } - /* line 186, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 185, ../../../../general/res/sass/controls/_buttons.scss */ .mini-tab.anchor-right.collapsed:hover:before { left: -9px; } } -/* line 196, ../../../../general/res/sass/controls/_buttons.scss */ +@media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { + /* line 195, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + color: #d6d6d6; + cursor: pointer; + display: block; + position: absolute; + font-size: 12px; + line-height: 12px; + height: 12px; + width: 12px; } + /* line 218, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:hover { + color: #a3a3a3; } + /* line 220, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:hover:after { + color: #0099cc; } + /* line 225, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:before, .mini-tab-icon:after { + -moz-transition-property: color; + -o-transition-property: color; + -webkit-transition-property: color; + transition-property: color; + -moz-transition-duration: 200ms; + -o-transition-duration: 200ms; + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + display: block; + position: absolute; } + /* line 232, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:before { + -moz-transition-property: left; + -o-transition-property: left; + -webkit-transition-property: left; + transition-property: left; + -moz-transition-duration: 150ms; + -o-transition-duration: 150ms; + -webkit-transition-duration: 150ms; + transition-duration: 150ms; + -moz-transition-timing-function: ease-in-out; + -o-transition-timing-function: ease-in-out; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -moz-transition-delay: 0; + -o-transition-delay: 0; + -webkit-transition-delay: 0; + transition-delay: 0; + font-size: 7px; + height: 100%; + width: 7px; } + /* line 239, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon:after { + width: 100%; + height: 100%; } + /* line 249, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-left:before { + content: '\3c'; + left: -7px; } + /* line 253, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-left:hover:before { + left: -9px; } + /* line 256, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-left.collapsed:before { + content: '\3e'; + left: 12px; } + /* line 260, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-left.collapsed:hover:before { + left: 14px; } + /* line 266, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-right:before { + content: '\3e'; + left: 12px; } + /* line 270, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-right:hover:before { + left: 14px; } + /* line 273, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-right.collapsed:before { + content: '\3c'; + left: -7px; } + /* line 277, ../../../../general/res/sass/controls/_buttons.scss */ + .mini-tab-icon.anchor-right.collapsed:hover:before { + left: -9px; } } + +/* line 287, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set { font-size: 0; } - /* line 202, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 293, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .s-btn, .l-btn-set .s-menu-btn { -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; margin-left: 1px; } - /* line 208, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 299, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .first .s-btn, .l-btn-set .first .s-menu-btn { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; @@ -1666,7 +1773,7 @@ mct-container { -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; margin-left: 0; } - /* line 215, ../../../../general/res/sass/controls/_buttons.scss */ + /* line 306, ../../../../general/res/sass/controls/_buttons.scss */ .l-btn-set .last .s-btn, .l-btn-set .last .s-menu-btn { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; @@ -1675,7 +1782,7 @@ mct-container { -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } -/* line 222, ../../../../general/res/sass/controls/_buttons.scss */ +/* line 313, ../../../../general/res/sass/controls/_buttons.scss */ .paused:not(.s-btn):not(.s-menu-btn) { border-color: #ff9900 !important; color: #ff9900 !important; } @@ -2379,7 +2486,7 @@ label.checkbox.custom { left: 0; text-align: left; } /* line 57, ../../../../general/res/sass/controls/_menus.scss */ - .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .icon.s-icon-btn, .s-menu-btn .menu .icon.mini-tab, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { + .s-menu-btn .menu .ui-symbol.icon, .s-menu-btn .menu .icon.s-icon-btn, .s-menu-btn .menu .icon.mini-tab, .s-menu-btn .menu .icon.mini-tab-icon, .s-menu-btn .menu .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .s-menu-btn .menu .icon.pager { width: 12px; } /******************************************************** MENUS THEMSELVES */ @@ -2661,10 +2768,11 @@ mct-include.l-time-controller { mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .lbl { color: #999999; } /* line 69, ../../../../general/res/sass/controls/_time-controller.scss */ - mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.mini-tab-icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-input .icon.pager, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .ui-symbol.icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.s-icon-btn, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.mini-tab, + mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.mini-tab-icon, mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager mct-include.l-time-controller .l-time-range-inputs-holder .l-time-range-inputs-elem .icon.pager { font-size: 11px; @@ -3373,10 +3481,11 @@ span.req { .t-filter input.t-filter-input:not(.ng-dirty) + .t-a-clear { display: none; } /* line 42, ../../../../general/res/sass/forms/_filter.scss */ -.filter .icon.ui-symbol, .filter .icon.s-icon-btn, .filter .icon.mini-tab, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, +.filter .icon.ui-symbol, .filter .icon.s-icon-btn, .filter .icon.mini-tab, .filter .icon.mini-tab-icon, .filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .filter .icon.pager, .t-filter .icon.ui-symbol, .t-filter .icon.s-icon-btn, .t-filter .icon.mini-tab, +.t-filter .icon.mini-tab-icon, .t-filter .l-datetime-picker .l-month-year-pager .icon.pager, .l-datetime-picker .l-month-year-pager .t-filter .icon.pager { -moz-border-radius: 4px; @@ -3389,18 +3498,20 @@ span.req { padding: 0px 5px; vertical-align: middle; } /* line 50, ../../../../general/res/sass/forms/_filter.scss */ - .filter .icon.ui-symbol:hover, .filter .icon.s-icon-btn:hover, .filter .icon.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, + .filter .icon.ui-symbol:hover, .filter .icon.s-icon-btn:hover, .filter .icon.mini-tab:hover, .filter .icon.mini-tab-icon:hover, .filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .filter .icon.pager:hover, .t-filter .icon.ui-symbol:hover, .t-filter .icon.s-icon-btn:hover, .t-filter .icon.mini-tab:hover, + .t-filter .icon.mini-tab-icon:hover, .t-filter .l-datetime-picker .l-month-year-pager .icon.pager:hover, .l-datetime-picker .l-month-year-pager .t-filter .icon.pager:hover { background: rgba(255, 255, 255, 0.1); } /* line 54, ../../../../general/res/sass/forms/_filter.scss */ -.filter .s-a-clear.ui-symbol, .filter .s-a-clear.s-icon-btn, .filter .s-a-clear.mini-tab, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, +.filter .s-a-clear.ui-symbol, .filter .s-a-clear.s-icon-btn, .filter .s-a-clear.mini-tab, .filter .s-a-clear.mini-tab-icon, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager, .t-filter .s-a-clear.ui-symbol, .t-filter .s-a-clear.s-icon-btn, .t-filter .s-a-clear.mini-tab, +.t-filter .s-a-clear.mini-tab-icon, .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager, .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager { -moz-border-radius: 4px; @@ -3426,10 +3537,11 @@ span.req { text-align: center; z-index: 5; } /* line 74, ../../../../general/res/sass/forms/_filter.scss */ - .filter .s-a-clear.ui-symbol:hover, .filter .s-a-clear.s-icon-btn:hover, .filter .s-a-clear.mini-tab:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, + .filter .s-a-clear.ui-symbol:hover, .filter .s-a-clear.s-icon-btn:hover, .filter .s-a-clear.mini-tab:hover, .filter .s-a-clear.mini-tab-icon:hover, .filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .filter .s-a-clear.pager:hover, .t-filter .s-a-clear.ui-symbol:hover, .t-filter .s-a-clear.s-icon-btn:hover, .t-filter .s-a-clear.mini-tab:hover, + .t-filter .s-a-clear.mini-tab-icon:hover, .t-filter .l-datetime-picker .l-month-year-pager .s-a-clear.pager:hover, .l-datetime-picker .l-month-year-pager .t-filter .s-a-clear.pager:hover { filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); @@ -3668,67 +3780,71 @@ span.req { .browse-mode .split-layout .split-pane-component.pane.t-inspect.right { min-width: 100px; max-width: 800px; + padding-left: 15px; width: 20%; } -/* line 225, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 226, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right { width: 15%; } - /* line 227, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 228, ../../../../general/res/sass/user-environ/_layout.scss */ .edit-mode .split-layout .split-pane-component.pane.right .pane.bottom { min-height: 50px; height: 30%; } -/* line 235, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 236, ../../../../general/res/sass/user-environ/_layout.scss */ .pane { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; position: absolute; } - /* line 238, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 240, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .pane-header { text-transform: uppercase; height: 24px; line-height: 24px; margin-bottom: 5px; } - /* line 246, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 248, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder { bottom: auto; top: 0; height: 24px; } - /* line 250, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 252, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .create-btn-holder .wrapper.menu-element { position: absolute; bottom: 5px; } - /* line 255, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 257, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .search-holder { top: 34px; } - /* line 258, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 260, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.treeview.left .tree-holder { overflow: auto; top: 64px; } - /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 266, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { z-index: 2; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 264, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 266, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane { top: 5px; } - /* line 270, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 272, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left { left: -30px; } - /* line 273, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 275, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left:after { content: 'F'; } - /* line 276, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 278, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-tree.anchor-left.collapsed { left: -25px; } - /* line 280, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 282, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right { right: -25px; } - /* line 283, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 285, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right:after { content: '\e608'; } - /* line 286, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 288, ../../../../general/res/sass/user-environ/_layout.scss */ .pane .mini-tab.toggle-pane.toggle-inspect.anchor-right.collapsed { right: -20px; } } - /* line 295, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 297, ../../../../general/res/sass/user-environ/_layout.scss */ .pane.items .object-browse-bar .left.abs, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.pager, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.pager, .pane.items .object-browse-bar .l-datetime-picker .l-month-year-pager .left.val, .l-datetime-picker .l-month-year-pager .pane.items .object-browse-bar .left.val, .pane.items .object-browse-bar .s-menu-btn span.left.l-click-area, .s-menu-btn .pane.items .object-browse-bar span.left.l-click-area, @@ -3741,31 +3857,31 @@ span.req { .s-menu-btn .pane.items .object-browse-bar span.right.l-click-area { top: auto; } -/* line 306, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 308, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 309, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 311, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 316, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 318, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 319, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 321, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 323, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 325, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 327, ../../../../general/res/sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 3px; } -/* line 333, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 335, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder { overflow: hidden; top: 34px; } - /* line 336, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 338, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder > ng-include { overflow: auto; position: absolute; @@ -3775,11 +3891,11 @@ span.req { left: 0; width: auto; height: auto; } - /* line 340, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 342, ../../../../general/res/sass/user-environ/_layout.scss */ .object-holder.l-controls-visible.l-time-controller-visible { bottom: 88px; } -/* line 346, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 348, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .s-btn, .object-browse-bar .s-menu-btn, .top-bar .buttons-main .s-btn, .top-bar .buttons-main .s-menu-btn, @@ -3791,12 +3907,12 @@ span.req { line-height: 25px; vertical-align: top; } -/* line 359, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 361, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .view-switcher, .top-bar .view-switcher { margin-right: 20px; } -/* line 364, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 366, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar { overflow: visible; position: absolute; @@ -3812,36 +3928,36 @@ span.req { height: 24px; line-height: 24px; white-space: nowrap; } - /* line 372, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left { padding-right: 20px; } - /* line 374, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 376, ../../../../general/res/sass/user-environ/_layout.scss */ .object-browse-bar .left .l-back { display: inline-block; float: left; margin-right: 10px; } -/* line 382, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 384, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex { display: flex; display: -webkit-flex; flex-flow: row nowrap; -webkit-flex-flow: row nowrap; } - /* line 385, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 387, ../../../../general/res/sass/user-environ/_layout.scss */ .l-flex .left { flex: 1 1 0; -webkit-flex: 1 1 0; padding-right: 10px; } -/* line 397, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 399, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.left.treeview, .pane-tree-hidden .splitter-treeview { opacity: 0; } -/* line 402, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 404, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-hidden .pane.right.items { left: 15px !important; } -/* line 409, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 411, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-tree-showing .pane.left.treeview, .pane-tree-showing .splitter-treeview { -moz-transition-property: opacity, background-color, border-color, color; @@ -3862,7 +3978,7 @@ span.req { transition-delay: 250ms; opacity: 1; } -/* line 418, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 420, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-inspect-showing .l-object-and-inspector .pane.right, .pane-inspect-showing .l-object-and-inspector .splitter-inspect { -moz-transition-property: opacity, background-color, border-color, color; @@ -3883,16 +3999,16 @@ span.req { transition-delay: 250ms; opacity: 1; } -/* line 427, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 429, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-inspect-hidden .l-object-and-inspector .pane.right, .pane-inspect-hidden .l-object-and-inspector .splitter-inspect { opacity: 0; } -/* line 431, ../../../../general/res/sass/user-environ/_layout.scss */ +/* line 433, ../../../../general/res/sass/user-environ/_layout.scss */ .pane-inspect-hidden .l-object-and-inspector .pane.left { right: 15px !important; } @media screen and (min-device-width: 800px) and (min-device-height: 1025px), screen and (min-device-width: 1025px) and (min-device-height: 800px) { - /* line 438, ../../../../general/res/sass/user-environ/_layout.scss */ + /* line 440, ../../../../general/res/sass/user-environ/_layout.scss */ .pane:not(.resizing) { -moz-transition-property: width, left, right; -o-transition-property: width, left, right; @@ -6462,7 +6578,7 @@ table { left: 0; z-index: 1; } /* line 22, ../../../../general/res/sass/features/_time-display.scss */ - .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .direction.s-icon-btn, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { + .l-time-display.l-timer .l-elem.l-value .ui-symbol.direction, .l-time-display.l-timer .l-elem.l-value .direction.s-icon-btn, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab, .l-time-display.l-timer .l-elem.l-value .direction.mini-tab-icon, .l-time-display.l-timer .l-elem.l-value .l-datetime-picker .l-month-year-pager .direction.pager, .l-datetime-picker .l-month-year-pager .l-time-display.l-timer .l-elem.l-value .direction.pager { font-size: 0.8em; } /* line 26, ../../../../general/res/sass/features/_time-display.scss */ .l-time-display.l-timer:hover .l-elem.l-value { From ddb567aeb5bb25b8a27f33092420f1838c41f8a1 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 15:41:59 -0700 Subject: [PATCH 156/488] [Clocks/Timers] Declare dependency of moment-duration-format ...to ensure that moment is loaded first, such that the plugin can correctly install itself. nasa/openmctweb#204 --- platform/features/clock/bundle.json | 5 +++++ .../features/clock/test/controllers/TimerFormatterSpec.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/features/clock/bundle.json b/platform/features/clock/bundle.json index 0d1ac5611a..5468c46207 100644 --- a/platform/features/clock/bundle.json +++ b/platform/features/clock/bundle.json @@ -4,6 +4,11 @@ "configuration": { "paths": { "moment-duration-format": "moment-duration-format" + }, + "shim": { + "moment-duration-format": { + "deps": [ "moment" ] + } } }, "extensions": { diff --git a/platform/features/clock/test/controllers/TimerFormatterSpec.js b/platform/features/clock/test/controllers/TimerFormatterSpec.js index f0f4d954c3..e485811988 100644 --- a/platform/features/clock/test/controllers/TimerFormatterSpec.js +++ b/platform/features/clock/test/controllers/TimerFormatterSpec.js @@ -93,4 +93,4 @@ define( }); } -); \ No newline at end of file +); From 8acf01ebdfc0056f0355a4cfaa982af9b7a2ee33 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 16:03:57 -0700 Subject: [PATCH 157/488] [Tests] Declare deps for moment-duration-format ...in karma test runners. --- karma.conf.js | 1 - test-main.js | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 47aa3f059b..16175556ae 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -34,7 +34,6 @@ module.exports = function(config) { // List of files / patterns to load in the browser. // By default, files are also included in a script tag. files: [ - '**/moment*', {pattern: 'example/**/*.js', included: false}, {pattern: 'platform/**/*.js', included: false}, {pattern: 'warp/**/*.js', included: false}, diff --git a/test-main.js b/test-main.js index 46740a93b2..d3bbb1fcc3 100644 --- a/test-main.js +++ b/test-main.js @@ -44,7 +44,14 @@ require.config({ paths: { 'es6-promise': 'platform/framework/lib/es6-promise-2.0.0.min', - 'moment-duration-format': 'warp/clock/lib/moment-duration-format' + 'moment': 'platform/telemetry/lib/moment.min', + 'moment-duration-format': 'platform/features/clock/lib/moment-duration-format' + }, + + shim: { + 'moment-duration-format': { + deps: [ 'moment' ] + } }, // dynamically load all test files From 789b640b9bb8979c9620d46c678573a510b9a52e Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 23 Oct 2015 16:28:42 -0700 Subject: [PATCH 158/488] Refactored dismiss and minimize out of NotificationService and on to returned Notification type --- .../src/NotificationLaunchController.js | 19 +- .../general/res/templates/message-banner.html | 18 +- .../src/controllers/BannerController.js | 9 +- .../src/NotificationIndicatorController.js | 6 +- .../notification/src/NotificationService.js | 233 +++++++++++------- .../test/NotificationServiceSpec.js | 47 +++- 6 files changed, 218 insertions(+), 114 deletions(-) diff --git a/example/notifications/src/NotificationLaunchController.js b/example/notifications/src/NotificationLaunchController.js index 7d0618ada4..851e0575f5 100644 --- a/example/notifications/src/NotificationLaunchController.js +++ b/example/notifications/src/NotificationLaunchController.js @@ -130,7 +130,7 @@ define( */ $scope.newProgress = function(){ - var notification = { + var notificationModel = { title: "Progress notification example", severity: "info", progress: 0, @@ -142,17 +142,18 @@ define( * Simulate an ongoing process and update the progress bar. * @param notification */ - function incrementProgress(notification) { - notification.progress = Math.min(100, Math.floor(notification.progress + Math.random() * 30)); - notification.progressText = ["Estimated time remaining:" + - " about ", 60 - Math.floor((notification.progress / 100) * 60), " seconds"].join(" "); - if (notification.progress < 100) { - $timeout(function(){incrementProgress(notification);}, 1000); + function incrementProgress(notificationModel) { + notificationModel.progress = Math.min(100, Math.floor(notificationModel.progress + Math.random() * 30)); + notificationModel.progressText = ["Estimated time" + + " remaining:" + + " about ", 60 - Math.floor((notificationModel.progress / 100) * 60), " seconds"].join(" "); + if (notificationModel.progress < 100) { + $timeout(function(){incrementProgress(notificationModel);}, 1000); } } - notificationService.notify(notification); - incrementProgress(notification); + notificationService.notify(notificationModel); + incrementProgress(notificationModel); }; /** diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html index 29b107126e..44c7f915b6 100644 --- a/platform/commonUI/general/res/templates/message-banner.html +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -1,20 +1,20 @@
    - + - diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js index 068b5cda81..4be9304cc6 100644 --- a/platform/commonUI/general/src/controllers/BannerController.js +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -52,14 +52,15 @@ define( }; $scope.dismiss = function(notification, $event) { $event.stopPropagation(); - notificationService.dismissOrMinimize(notification); + notification.dismissOrMinimize(); }; $scope.maximize = function(notification) { - if (notification.severity !== "info"){ - notification.cancel = function(){ + if (notification.model.severity !== "info"){ + + notification.model.cancel = function(){ dialogService.dismiss(); }; - dialogService.showBlockingMessage(notification); + dialogService.showBlockingMessage(notification.model); } }; } diff --git a/platform/commonUI/notification/src/NotificationIndicatorController.js b/platform/commonUI/notification/src/NotificationIndicatorController.js index 66ad825a32..e58b0a606b 100644 --- a/platform/commonUI/notification/src/NotificationIndicatorController.js +++ b/platform/commonUI/notification/src/NotificationIndicatorController.js @@ -48,7 +48,11 @@ define( dialogService.getDialogResponse('overlay-message-list', { dialog: { title: "Messages", - messages: notificationService.notifications + //Launch the message list dialog with the models + // from the notifications + messages: notificationService.notifications && notificationService.notifications.map(function(notification){ + return notification.model; + }) }, cancel: function(){ dialogService.dismiss(); diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js index 140c4537c8..cff2d57d7a 100644 --- a/platform/commonUI/notification/src/NotificationService.js +++ b/platform/commonUI/notification/src/NotificationService.js @@ -29,7 +29,7 @@ * dialogs so that the same information can be provided in a dialog * and then minimized to a banner notification if needed. * - * @namespace platform/commonUI/dialog + * @namespace platform/commonUI/notification */ define( [], @@ -54,11 +54,10 @@ define( * dialogs so that the same information can be provided in a dialog * and then minimized to a banner notification if needed. * - * @typedef {object} Notification + * @typedef {object} NotificationModel * @property {string} title The title of the message - * @property {string} severity The importance of the - * message (one of 'info', 'alert', or 'error' where info < alert < - * error) + * @property {string} severity The importance of the message (one of + * 'info', 'alert', or 'error' where info < alert