From 036722b275f239b8619a25a7cf59d18d73f1b0e9 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 10 Apr 2015 19:24:16 -0700 Subject: [PATCH 01/45] [Containment] Disallow composition in immutable objects Disallow composition in objects which cannot be created, under the rationale that creatable objects must also be immutable. WTD-1098. --- platform/containment/bundle.json | 5 ++++ .../src/CompositionMutabilityPolicy.js | 30 +++++++++++++++++++ .../test/CompositionMutabilityPolicySpec.js | 26 ++++++++++++++++ platform/containment/test/suite.json | 1 + 4 files changed, 62 insertions(+) create mode 100644 platform/containment/src/CompositionMutabilityPolicy.js create mode 100644 platform/containment/test/CompositionMutabilityPolicySpec.js diff --git a/platform/containment/bundle.json b/platform/containment/bundle.json index a31eb60c4a..e61085f66f 100644 --- a/platform/containment/bundle.json +++ b/platform/containment/bundle.json @@ -7,6 +7,11 @@ "depends": [ "$injector" ], "message": "Objects of this type cannot contain objects of that type." }, + { + "category": "composition", + "implementation": "CompositionMutabilityPolicy.js", + "message": "Objects of this type cannot be modified." + }, { "category": "action", "implementation": "ComposeActionPolicy.js", diff --git a/platform/containment/src/CompositionMutabilityPolicy.js b/platform/containment/src/CompositionMutabilityPolicy.js new file mode 100644 index 0000000000..21dc02fafc --- /dev/null +++ b/platform/containment/src/CompositionMutabilityPolicy.js @@ -0,0 +1,30 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Disallow composition changes to objects which are not mutable. + * @constructor + */ + function CompositionMutabilityPolicy() { + return { + /** + * Is the type identified by the candidate allowed to + * contain the type described by the context? + * @param {Type} candidate the type of domain object + */ + allow: function (candidate) { + // Equate creatability with mutability; that is, users + // can only modify objects of types they can create, and + // vice versa. + return candidate.hasFeature('creation'); + } + }; + } + + return CompositionMutabilityPolicy; + } +); \ No newline at end of file diff --git a/platform/containment/test/CompositionMutabilityPolicySpec.js b/platform/containment/test/CompositionMutabilityPolicySpec.js new file mode 100644 index 0000000000..460f8b6870 --- /dev/null +++ b/platform/containment/test/CompositionMutabilityPolicySpec.js @@ -0,0 +1,26 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../src/CompositionMutabilityPolicy"], + function (CompositionMutabilityPolicy) { + "use strict"; + + describe("The composition mutability policy", function () { + var mockType, + policy; + + beforeEach(function () { + mockType = jasmine.createSpyObj('type', ['hasFeature']); + policy = new CompositionMutabilityPolicy(); + }); + + it("only allows composition for types which will have a composition capability", function () { + expect(policy.allow(mockType)).toBeFalsy(); + mockType.hasFeature.andReturn(true); + expect(policy.allow(mockType)).toBeTruthy(); + expect(mockType.hasFeature).toHaveBeenCalledWith('creation'); + }); + }); + + } +); \ No newline at end of file diff --git a/platform/containment/test/suite.json b/platform/containment/test/suite.json index a82d203c53..987ef9a86c 100644 --- a/platform/containment/test/suite.json +++ b/platform/containment/test/suite.json @@ -1,6 +1,7 @@ [ "CapabilityTable", "ComposeActionPolicy", + "CompositionMutabilityPolicy", "CompositionPolicy", "ContainmentTable" ] \ No newline at end of file From dbe27d2842b11dced03f52bfa3788beb1ad56cf1 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 15 Apr 2015 14:40:38 -0700 Subject: [PATCH 02/45] [Frontend/Content] Copy and styling tweaks WTD-1036 Open source copyright information added to about-dialog.html; Styling for a and h2 elements tweaked; --- .../about/res/templates/about-dialog.html | 7 ++- .../general/res/css/theme-espresso.css | 57 +++++++++++-------- .../commonUI/general/res/sass/_about.scss | 14 +++++ 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/platform/commonUI/about/res/templates/about-dialog.html b/platform/commonUI/about/res/templates/about-dialog.html index d54ef2e9a9..5185e376f0 100644 --- a/platform/commonUI/about/res/templates/about-dialog.html +++ b/platform/commonUI/about/res/templates/about-dialog.html @@ -1,4 +1,4 @@ -
+
@@ -7,7 +7,10 @@

OpenMCT Web

-

OpenMCT Web is a project of NASA's Ames Research Center. Copyright ©2015 NASA Ames Research Center, all rights reserved. Licensing information

+

Open MCT Web, Copyright © 2009-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 included with this distribution or click here for licensing information.

Version Information

    diff --git a/platform/commonUI/general/res/css/theme-espresso.css b/platform/commonUI/general/res/css/theme-espresso.css index e810f9d517..0538a36c25 100644 --- a/platform/commonUI/general/res/css/theme-espresso.css +++ b/platform/commonUI/general/res/css/theme-espresso.css @@ -1,5 +1,5 @@ /* CONSTANTS */ -/* line 17, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 17, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/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, @@ -20,38 +20,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/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-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/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-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary { display: block; } @@ -508,26 +508,37 @@ span { .t-about .s-logo-openmctweb { background-image: url("../images/logo-openmctweb-shdw.svg"); } /* line 23, ../sass/_about.scss */ + .t-about .s-btn, .t-about .s-icon-btn { + line-height: 2em; } + /* line 27, ../sass/_about.scss */ + .t-about a { + color: #84b3ff; } + /* line 31, ../sass/_about.scss */ .t-about h1, .t-about h2 { color: #fff; font-weight: normal !important; } - /* line 29, ../sass/_about.scss */ + /* line 36, ../sass/_about.scss */ + .t-about h2 { + border-top: 1px solid #4d4d4d; + margin-top: 2em; + padding-top: 1em; } + /* line 43, ../sass/_about.scss */ .t-about.l-about.abs, .btn-menu span.t-about.l-about.l-click-area { overflow: auto; } - /* line 34, ../sass/_about.scss */ + /* line 48, ../sass/_about.scss */ .t-about .l-logo-holder { position: relative; height: 45%; } - /* line 37, ../sass/_about.scss */ + /* line 51, ../sass/_about.scss */ .t-about .l-logo-holder .l-logo { position: absolute; } - /* line 40, ../sass/_about.scss */ + /* line 54, ../sass/_about.scss */ .t-about .l-logo-holder .l-logo.l-logo-app { top: 0; right: 15%; bottom: 0; left: 15%; } - /* line 44, ../sass/_about.scss */ + /* line 58, ../sass/_about.scss */ .t-about .l-logo-holder .l-logo.s-logo-nasa { background-image: url("../images/logo-nasa.svg"); top: 10px; @@ -538,7 +549,7 @@ span { height: auto; padding-bottom: 5%; padding-top: 5%; } - /* line 53, ../sass/_about.scss */ + /* line 67, ../sass/_about.scss */ .t-about .l-content { position: relative; margin-top: 10px; } @@ -2833,10 +2844,10 @@ input[type="text"] { .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; + -webkit-animation: rotation 0.6s infinite linear; + -moz-animation: rotation 0.6s infinite linear; + -o-animation: rotation 0.6s infinite linear; + animation: rotation 0.6s infinite linear; border-color: rgba(0, 153, 204, 0.25); border-top-color: #0099cc; border-style: solid; @@ -2875,10 +2886,10 @@ input[type="text"] { .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; + -webkit-animation: rotation 0.6s infinite linear; + -moz-animation: rotation 0.6s infinite linear; + -o-animation: rotation 0.6s infinite linear; + animation: rotation 0.6s infinite linear; border-color: rgba(0, 153, 204, 0.25); border-top-color: #0099cc; border-style: solid; diff --git a/platform/commonUI/general/res/sass/_about.scss b/platform/commonUI/general/res/sass/_about.scss index 7a5a7bfa57..e4e88380ea 100644 --- a/platform/commonUI/general/res/sass/_about.scss +++ b/platform/commonUI/general/res/sass/_about.scss @@ -20,11 +20,25 @@ .s-logo-openmctweb { background-image: url($dirImgs + 'logo-openmctweb-shdw.svg'); } + .s-btn { + line-height: 2em; + } + + a { + color: #84b3ff; + } + h1, h2 { color: #fff; font-weight: normal !important; } + h2 { + border-top: 1px solid $colorInteriorBorder; + margin-top: 2em; + padding-top: 1em; + } + // Layout &.l-about.abs { // top: 20px; From b6503489b9d3f2708430ddfdc30c6f5e240e2c13 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 15 Apr 2015 17:40:12 -0700 Subject: [PATCH 03/45] [Frontend] Major reformatting of /licenses page WTD-1036 Major additions to /licenses page Apache license added; Full licenses for each software component added; Formatting and styles; --- platform/commonUI/about/bundle.json | 8 + .../about/res/templates/about-dialog.html | 2 +- .../about/res/templates/license-apache.html | 178 ++++++++++++++++++ .../about/res/templates/license-mit.html | 3 + .../about/res/templates/licenses.html | 52 +++-- platform/commonUI/browse/bundle.json | 12 +- platform/commonUI/general/bundle.json | 12 +- .../general/res/css/theme-espresso.css | 160 ++++++++++------ .../commonUI/general/res/sass/_about.scss | 93 +++++---- .../commonUI/general/res/sass/_global.scss | 12 +- platform/commonUI/general/res/sass/_main.scss | 1 + platform/commonUI/general/res/sass/_text.scss | 35 ++++ platform/framework/bundle.json | 36 ++-- platform/telemetry/bundle.json | 7 +- 14 files changed, 450 insertions(+), 161 deletions(-) create mode 100644 platform/commonUI/about/res/templates/license-apache.html create mode 100644 platform/commonUI/about/res/templates/license-mit.html create mode 100644 platform/commonUI/general/res/sass/_text.scss diff --git a/platform/commonUI/about/bundle.json b/platform/commonUI/about/bundle.json index 88a43b5eb3..34e65eb81e 100644 --- a/platform/commonUI/about/bundle.json +++ b/platform/commonUI/about/bundle.json @@ -19,6 +19,14 @@ { "key": "overlay-about", "templateUrl": "templates/overlay-about.html" + }, + { + "key": "license-apache", + "templateUrl": "templates/license-apache.html" + }, + { + "key": "license-mit", + "templateUrl": "templates/license-mit.html" } ], "controllers": [ diff --git a/platform/commonUI/about/res/templates/about-dialog.html b/platform/commonUI/about/res/templates/about-dialog.html index 5185e376f0..455e4e11e1 100644 --- a/platform/commonUI/about/res/templates/about-dialog.html +++ b/platform/commonUI/about/res/templates/about-dialog.html @@ -4,7 +4,7 @@
-
+

OpenMCT Web

Open MCT Web, Copyright © 2009-2015, United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.

diff --git a/platform/commonUI/about/res/templates/license-apache.html b/platform/commonUI/about/res/templates/license-apache.html new file mode 100644 index 0000000000..a7690c196e --- /dev/null +++ b/platform/commonUI/about/res/templates/license-apache.html @@ -0,0 +1,178 @@ + +
+

Version 2.0, January 2004

+

http://www.apache.org/licenses/

+

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

+

1. Definitions.

+

"License" shall mean the terms and conditions for use, reproduction, and + distribution as defined by Sections 1 through 9 of this document.

+

"Licensor" shall mean the copyright owner or entity authorized by the + copyright owner that is granting the License.

+

"Legal Entity" shall mean the union of the acting entity and all other + entities that control, are controlled by, or are under common control with + that entity. For the purposes of this definition, "control" means (i) the + power, direct or indirect, to cause the direction or management of such + entity, whether by contract or otherwise, or (ii) ownership of fifty + percent (50%) or more of the outstanding shares, or (iii) beneficial + ownership of such entity.

+

"You" (or "Your") shall mean an individual or Legal Entity exercising + permissions granted by this License.

+

"Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation source, + and configuration files.

+

"Object" form shall mean any form resulting from mechanical transformation + or translation of a Source form, including but not limited to compiled + object code, generated documentation, and conversions to other media types.

+

"Work" shall mean the work of authorship, whether in Source or Object form, + made available under the License, as indicated by a copyright notice that + is included in or attached to the work (an example is provided in the + Appendix below).

+

"Derivative Works" shall mean any work, whether in Source or Object form, + that is based on (or derived from) the Work and for which the editorial + revisions, annotations, elaborations, or other modifications represent, as + a whole, an original work of authorship. For the purposes of this License, + Derivative Works shall not include works that remain separable from, or + merely link (or bind by name) to the interfaces of, the Work and Derivative + Works thereof.

+

"Contribution" shall mean any work of authorship, including the original + version of the Work and any modifications or additions to that Work or + Derivative Works thereof, that is intentionally submitted to Licensor for + inclusion in the Work by the copyright owner or by an individual or Legal + Entity authorized to submit on behalf of the copyright owner. For the + purposes of this definition, "submitted" means any form of electronic, + verbal, or written communication sent to the Licensor or its + representatives, including but not limited to communication on electronic + mailing lists, source code control systems, and issue tracking systems that + are managed by, or on behalf of, the Licensor for the purpose of discussing + and improving the Work, but excluding communication that is conspicuously + marked or otherwise designated in writing by the copyright owner as "Not a + Contribution."

+

"Contributor" shall mean Licensor and any individual or Legal Entity on + behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work.

+

2. Grant of Copyright License. Subject to the + terms and conditions of this License, each Contributor hereby grants to You + a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, publicly + display, publicly perform, sublicense, and distribute the Work and such + Derivative Works in Source or Object form.

+

3. Grant of Patent License. Subject to the terms + and conditions of this License, each Contributor hereby grants to You a + perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, use, + offer to sell, sell, import, and otherwise transfer the Work, where such + license applies only to those patent claims licensable by such Contributor + that are necessarily infringed by their Contribution(s) alone or by + combination of their Contribution(s) with the Work to which such + Contribution(s) was submitted. If You institute patent litigation against + any entity (including a cross-claim or counterclaim in a lawsuit) alleging + that the Work or a Contribution incorporated within the Work constitutes + direct or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate as of the + date such litigation is filed.

+

4. Redistribution. You may reproduce and + distribute copies of the Work or Derivative Works thereof in any medium, + with or without modifications, and in Source or Object form, provided that + You meet the following conditions:

+
    +
  1. You must give any other recipients of the Work or Derivative Works a + copy of this License; and
  2. + +
  3. You must cause any modified files to carry prominent notices stating + that You changed the files; and
  4. + +
  5. You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices from + the Source form of the Work, excluding those notices that do not pertain to + any part of the Derivative Works; and
  6. + +
  7. If the Work includes a "NOTICE" text file as part of its distribution, + then any Derivative Works that You distribute must include a readable copy + of the attribution notices contained within such NOTICE file, excluding + those notices that do not pertain to any part of the Derivative Works, in + at least one of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or documentation, + if provided along with the Derivative Works; or, within a display generated + by the Derivative Works, if and wherever such third-party notices normally + appear. The contents of the NOTICE file are for informational purposes only + and do not modify the License. You may add Your own attribution notices + within Derivative Works that You distribute, alongside or as an addendum to + the NOTICE text from the Work, provided that such additional attribution + notices cannot be construed as modifying the License. +
    +
    + You may add Your own copyright statement to Your modifications and may + provide additional or different license terms and conditions for use, + reproduction, or distribution of Your modifications, or for any such + Derivative Works as a whole, provided Your use, reproduction, and + distribution of the Work otherwise complies with the conditions stated in + this License. +
  8. + +
+ +

5. Submission of Contributions. Unless You + explicitly state otherwise, any Contribution intentionally submitted for + inclusion in the Work by You to the Licensor shall be under the terms and + conditions of this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify the + terms of any separate license agreement you may have executed with Licensor + regarding such Contributions.

+

6. Trademarks. This License does not grant + permission to use the trade names, trademarks, service marks, or product + names of the Licensor, except as required for reasonable and customary use + in describing the origin of the Work and reproducing the content of the + NOTICE file.

+

7. Disclaimer of Warranty. Unless required by + applicable law or agreed to in writing, Licensor provides the Work (and + each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, + without limitation, any warranties or conditions of TITLE, + NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You + are solely responsible for determining the appropriateness of using or + redistributing the Work and assume any risks associated with Your exercise + of permissions under this License.

+

8. Limitation of Liability. In no event and + under no legal theory, whether in tort (including negligence), contract, or + otherwise, unless required by applicable law (such as deliberate and + grossly negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a result + of this License or out of the use or inability to use the Work (including + but not limited to damages for loss of goodwill, work stoppage, computer + failure or malfunction, or any and all other commercial damages or losses), + even if such Contributor has been advised of the possibility of such + damages.

+

9. Accepting Warranty or Additional Liability. + While redistributing the Work or Derivative Works thereof, You may choose + to offer, and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this License. + However, in accepting such obligations, You may act only on Your own behalf + and on Your sole responsibility, not on behalf of any other Contributor, + and only if You agree to indemnify, defend, and hold each Contributor + harmless for any liability incurred by, or claims asserted against, such + Contributor by reason of your accepting any such warranty or additional + liability.

+

END OF TERMS AND CONDITIONS

+

APPENDIX: How to apply the Apache License to your work

+

To apply the Apache License to your work, attach the following boilerplate + notice, with the fields enclosed by brackets "[]" replaced with your own + identifying information. (Don't include the brackets!) The text should be + enclosed in the appropriate comment syntax for the file format. We also + recommend that a file or class name and description of purpose be included + on the same "printed page" as the copyright notice for easier + identification within third-party archives.

+
Copyright [yyyy] [name of copyright owner]
+
+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.
+
\ No newline at end of file diff --git a/platform/commonUI/about/res/templates/license-mit.html b/platform/commonUI/about/res/templates/license-mit.html new file mode 100644 index 0000000000..79f5428eca --- /dev/null +++ b/platform/commonUI/about/res/templates/license-mit.html @@ -0,0 +1,3 @@ +

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.

\ No newline at end of file diff --git a/platform/commonUI/about/res/templates/licenses.html b/platform/commonUI/about/res/templates/licenses.html index 9aafaa22f1..472507a52d 100644 --- a/platform/commonUI/about/res/templates/licenses.html +++ b/platform/commonUI/about/res/templates/licenses.html @@ -1,33 +1,23 @@ - +
+

OpenMCT Web Licenses

+

Apache License

+ -
- -

- This software includes components released under the following licenses. -

- - - - - - - - - - - - - - - - - - - - -
NameVersionAuthorDescriptionLicense
{{license.name}}{{license.version}}{{license.author}}{{license.description}}{{license.license}}
+

Software Components Licenses

+

This software includes components released under the following licenses:

+
+
+

{{license.name}}

+

+ Version {{license.version}} +  | Author {{license.author}} +  | Description {{license.description}} +

+ License +
+

{{license.copyright}}

+ +
+
+
\ No newline at end of file diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json index 40b215fa2b..4da8efe424 100644 --- a/platform/commonUI/browse/bundle.json +++ b/platform/commonUI/browse/bundle.json @@ -85,7 +85,7 @@ { "key": "window", "implementation": "windowing/NewWindowAction.js", - "description": "Open this object in a new window.", + "description": "Open this object in a new window", "category": "view-control", "depends": [ "$window" ], "group": "windowing", @@ -104,7 +104,7 @@ "key": "items", "name": "Items", "glyph": "9", - "description": "Grid of available items.", + "description": "Grid of available items", "templateUrl": "templates/items/items.html", "uses": [ "composition" ], "gestures": [ "drop" ], @@ -125,10 +125,11 @@ { "name": "screenfull.js", "version": "1.2.0", - "description": "Wrapper for cross-browser usage of fullscreen API.", + "description": "Wrapper for cross-browser usage of fullscreen API", "author": "Sindre Sorhus", "website": "https://github.com/sindresorhus/screenfull.js/", - "license": "MIT", + "copyright": "Copyright (c) Sindre Sorhus (sindresorhus.com)", + "license": "license-mit", "link": "https://github.com/sindresorhus/screenfull.js/blob/gh-pages/license" }, { @@ -137,7 +138,8 @@ "description": "Unique identifer generation (code adapted.)", "author": "Robert Kieffer", "website": "https://github.com/broofa/node-uuid", - "license": "MIT", + "copyright": "", + "license": "license-mit", "link": "http://opensource.org/licenses/MIT" } ] diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index f765245c1e..70c80ad199 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -1,6 +1,6 @@ { "name": "General UI elements", - "description": "General UI elements, meant to be reused across modes.", + "description": "General UI elements, meant to be reused across modes", "resources": "res", "extensions": { "runs": [ @@ -211,19 +211,21 @@ { "name": "Modernizr", "version": "2.6.2", - "description": "Browser/device capability finding.", + "description": "Browser/device capability finding", "author": "Faruk Ateş", "website": "http://modernizr.com", - "license": "MIT", + "copyright": "Copyright (c) 2009–2015", + "license": "license-mit", "link": "http://modernizr.com/license/" }, { "name": "Normalize.css", "version": "1.1.2", - "description": "Browser style normalization.", + "description": "Browser style normalization", "author": "Nicolas Gallagher, Jonathan Neal", "website": "http://necolas.github.io/normalize.css/", - "license": "MIT", + "copyright": "Copyright (c) Nicolas Gallagher and Jonathan Neal", + "license": "license-mit", "link": "https://github.com/necolas/normalize.css/blob/master/LICENSE.md" } ] diff --git a/platform/commonUI/general/res/css/theme-espresso.css b/platform/commonUI/general/res/css/theme-espresso.css index 0538a36c25..741c0aee75 100644 --- a/platform/commonUI/general/res/css/theme-espresso.css +++ b/platform/commonUI/general/res/css/theme-espresso.css @@ -108,7 +108,6 @@ body, html { /* line 20, ../sass/_global.scss */ em { - color: rgba(255, 255, 255, 0.2); font-style: normal; } /* line 25, ../sass/_global.scss */ @@ -148,36 +147,45 @@ span { width: auto; } /* line 61, ../sass/_global.scss */ -.code { +.code, .codehilite { font-family: "Lucida Console", monospace; font-size: 0.7em; line-height: 150%; white-space: pre; } /* line 68, ../sass/_global.scss */ +.codehilite { + background-color: rgba(255, 255, 255, 0.1); + padding: 1em; } + +/* line 74, ../sass/_global.scss */ .align-right { text-align: right; } -/* line 72, ../sass/_global.scss */ +/* line 78, ../sass/_global.scss */ .centered { text-align: center; } -/* line 76, ../sass/_global.scss */ +/* line 82, ../sass/_global.scss */ +.no-margin { + margin: 0; } + +/* line 86, ../sass/_global.scss */ .colorKey { color: #0099cc; } -/* line 80, ../sass/_global.scss */ +/* line 90, ../sass/_global.scss */ .ds { -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 4px 10px 2px; -moz-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 85, ../sass/_global.scss */ +/* line 95, ../sass/_global.scss */ .hide, .hidden { display: none !important; } -/* line 89, ../sass/_global.scss */ +/* line 99, ../sass/_global.scss */ .sep { color: rgba(255, 255, 255, 0.2); } @@ -488,71 +496,101 @@ span { .edit-mode .t-fixed-position .l-fixed-position-item:not(.s-selected):hover { border: 1px dotted rgba(0, 153, 204, 0.5); } -/* line 3, ../sass/_about.scss */ -.t-about { +/* line 5, ../sass/_about.scss */ +.l-about.abs, .btn-menu span.l-about.l-click-area { + overflow: auto; } +/* line 10, ../sass/_about.scss */ +.l-about .l-logo-holder { + position: relative; + height: 45%; } + /* line 13, ../sass/_about.scss */ + .l-about .l-logo-holder .l-logo { + position: absolute; } + /* line 16, ../sass/_about.scss */ + .l-about .l-logo-holder .l-logo.l-logo-app { + top: 0; + right: 15%; + bottom: 0; + left: 15%; } + /* line 20, ../sass/_about.scss */ + .l-about .l-logo-holder .l-logo.s-logo-nasa { + background-image: url("../images/logo-nasa.svg"); + top: 10px; + right: auto; + bottom: auto; + left: 10px; + width: 10%; + height: auto; + padding-bottom: 5%; + padding-top: 5%; } +/* line 29, ../sass/_about.scss */ +.l-about .l-content { + position: relative; + margin-top: 10px; } + +/* line 36, ../sass/_about.scss */ +.s-about { line-height: 120%; } - /* line 7, ../sass/_about.scss */ - .t-about .s-description, - .t-about .s-info { - font-size: 0.8em; } - /* line 10, ../sass/_about.scss */ - .t-about .s-logo-holder { + /* line 40, ../sass/_about.scss */ + .s-about a { + color: #84b3ff; } + /* line 47, ../sass/_about.scss */ + .s-about .s-logo-holder { background: url("../images/bg-about-openmctweb.jpg") no-repeat center; background-size: cover; } - /* line 14, ../sass/_about.scss */ - .t-about .s-logo { + /* line 51, ../sass/_about.scss */ + .s-about .s-logo { background-position: center; background-repeat: no-repeat; background-size: contain; } - /* line 20, ../sass/_about.scss */ - .t-about .s-logo-openmctweb { + /* line 57, ../sass/_about.scss */ + .s-about .s-logo-openmctweb { background-image: url("../images/logo-openmctweb-shdw.svg"); } - /* line 23, ../sass/_about.scss */ - .t-about .s-btn, .t-about .s-icon-btn { + /* line 60, ../sass/_about.scss */ + .s-about .s-btn, .s-about .s-icon-btn { line-height: 2em; } - /* line 27, ../sass/_about.scss */ - .t-about a { - color: #84b3ff; } - /* line 31, ../sass/_about.scss */ - .t-about h1, .t-about h2 { - color: #fff; - font-weight: normal !important; } - /* line 36, ../sass/_about.scss */ - .t-about h2 { + /* line 64, ../sass/_about.scss */ + .s-about .l-licenses-software .l-license-software { border-top: 1px solid #4d4d4d; + padding: 0.5em 0; } + /* line 67, ../sass/_about.scss */ + .s-about .l-licenses-software .l-license-software:first-child { + border-top: none; } + /* line 70, ../sass/_about.scss */ + .s-about .l-licenses-software .l-license-software em { + color: #666666; } + /* line 77, ../sass/_about.scss */ + .s-about .l-licenses-software .l-license-software h3 { + font-size: 1.25em; } + /* line 80, ../sass/_about.scss */ + .s-about .l-licenses-software .l-license-software .s-license-text { + font-size: 0.9em; } + +/* line 3, ../sass/_text.scss */ +.abs.l-standalone, .btn-menu span.l-standalone.l-click-area { + padding: 5% 20%; } + +/* line 8, ../sass/_text.scss */ +.s-text { + font-size: 0.8em; } + /* line 10, ../sass/_text.scss */ + .s-text ol, .s-text ul { + list-style: square; + margin-left: 1.5em; } + /* line 18, ../sass/_text.scss */ + .s-text h1, .s-text h2, .s-text h3 { + color: #fff; + font-weight: normal !important; + margin-bottom: 1em; } + /* line 24, ../sass/_text.scss */ + .s-text h2 { + border-top: 1px solid #4d4d4d; + font-size: 1.5em; margin-top: 2em; padding-top: 1em; } - /* line 43, ../sass/_about.scss */ - .t-about.l-about.abs, .btn-menu span.t-about.l-about.l-click-area { - overflow: auto; } - /* line 48, ../sass/_about.scss */ - .t-about .l-logo-holder { - position: relative; - height: 45%; } - /* line 51, ../sass/_about.scss */ - .t-about .l-logo-holder .l-logo { - position: absolute; } - /* line 54, ../sass/_about.scss */ - .t-about .l-logo-holder .l-logo.l-logo-app { - top: 0; - right: 15%; - bottom: 0; - left: 15%; } - /* line 58, ../sass/_about.scss */ - .t-about .l-logo-holder .l-logo.s-logo-nasa { - background-image: url("../images/logo-nasa.svg"); - top: 10px; - right: auto; - bottom: auto; - left: 10px; - width: 10%; - height: auto; - padding-bottom: 5%; - padding-top: 5%; } - /* line 67, ../sass/_about.scss */ - .t-about .l-content { - position: relative; - margin-top: 10px; } + /* line 31, ../sass/_text.scss */ + .s-text h3 { + margin-top: 2em; } /* line 1, ../sass/_badges.scss */ .badge { diff --git a/platform/commonUI/general/res/sass/_about.scss b/platform/commonUI/general/res/sass/_about.scss index e4e88380ea..5135a370eb 100644 --- a/platform/commonUI/general/res/sass/_about.scss +++ b/platform/commonUI/general/res/sass/_about.scss @@ -1,46 +1,8 @@ // General About dialog styling -.t-about { - // Styling - line-height: 120%; - .s-description, - .s-info { - font-size: 0.8em; - } - .s-logo-holder { - background: url($dirImgs + "bg-about-openmctweb.jpg") no-repeat center; // For OpenMCT Web. - background-size: cover; - } - .s-logo { -// @include txtShdwLarge(); // text-shadow doesn't work for svg - background-position: center; - background-repeat: no-repeat; - background-size: contain; - } - .s-logo-openmctweb { - background-image: url($dirImgs + 'logo-openmctweb-shdw.svg'); - } - .s-btn { - line-height: 2em; - } - - a { - color: #84b3ff; - } - - h1, h2 { - color: #fff; - font-weight: normal !important; - } - - h2 { - border-top: 1px solid $colorInteriorBorder; - margin-top: 2em; - padding-top: 1em; - } - +.l-about { // Layout - &.l-about.abs { + &.abs { // top: 20px; overflow: auto; } @@ -71,3 +33,54 @@ } } +.s-about { + // Styling + line-height: 120%; + + a { + color: #84b3ff; + } + .s-description, + .s-info { +// font-size: 0.8em; + } + .s-logo-holder { + background: url($dirImgs + "bg-about-openmctweb.jpg") no-repeat center; // For OpenMCT Web. + background-size: cover; + } + .s-logo { + // @include txtShdwLarge(); // text-shadow doesn't work for svg + background-position: center; + background-repeat: no-repeat; + background-size: contain; + } + .s-logo-openmctweb { + background-image: url($dirImgs + 'logo-openmctweb-shdw.svg'); + } + .s-btn { + line-height: 2em; + } + .l-licenses-software { + .l-license-software { + border-top: 1px solid $colorInteriorBorder; + padding: 0.5em 0; + &:first-child { + border-top: none; + } + em { + color: darken($colorBodyFg, 20%); +// margin-left: 2em; + &:first-child { +// margin-left: 0; + } + } + h3 { + font-size: 1.25em; + } + .s-license-text { + font-size: 0.9em; + } + } + } +} + diff --git a/platform/commonUI/general/res/sass/_global.scss b/platform/commonUI/general/res/sass/_global.scss index 4036672870..3490b4a9cb 100644 --- a/platform/commonUI/general/res/sass/_global.scss +++ b/platform/commonUI/general/res/sass/_global.scss @@ -18,7 +18,7 @@ body, html { } em { - color: rgba(white, 0.2); +// color: rgba(white, 0.2); Removed this as a global setting font-style: normal; } @@ -65,6 +65,12 @@ span { white-space: pre; } +.codehilite { + @extend .code; + background-color: rgba(#fff, 0.1); + padding: 1em; +} + .align-right { text-align: right; } @@ -73,6 +79,10 @@ span { text-align: center; } +.no-margin { + margin: 0; +} + .colorKey { color: $colorKey; } diff --git a/platform/commonUI/general/res/sass/_main.scss b/platform/commonUI/general/res/sass/_main.scss index fb21912d56..10cc1789d2 100755 --- a/platform/commonUI/general/res/sass/_main.scss +++ b/platform/commonUI/general/res/sass/_main.scss @@ -11,6 +11,7 @@ @import "user-environ/layout"; @import "fixed-position"; @import "about"; +@import "text"; @import "badges"; @import "icons"; @import "lists/tabular"; diff --git a/platform/commonUI/general/res/sass/_text.scss b/platform/commonUI/general/res/sass/_text.scss new file mode 100644 index 0000000000..ae37c50db6 --- /dev/null +++ b/platform/commonUI/general/res/sass/_text.scss @@ -0,0 +1,35 @@ +// Styles for extended text copy + +.abs.l-standalone { + $d: 20%; + padding: $d/4 $d; +} + +.s-text { + font-size: 0.8em; + ol, ul { + list-style: square; + margin-left: 1.5em; + li { + + } + } + + h1, h2, h3 { + color: #fff; + font-weight: normal !important; + margin-bottom: 1em; + } + + h2 { + border-top: 1px solid $colorInteriorBorder; + font-size: 1.5em; + margin-top: 2em; + padding-top: 1em; + } + + h3 { + margin-top: 2em; + } + +} \ No newline at end of file diff --git a/platform/framework/bundle.json b/platform/framework/bundle.json index 2b7494b83d..854d3871b5 100644 --- a/platform/framework/bundle.json +++ b/platform/framework/bundle.json @@ -1,6 +1,6 @@ { "name": "Open MCT Web Framework Component", - "description": "Framework layer for Open MCT Web; interprets bundle definitions and serves as an intermediary between Require and Angular.", + "description": "Framework layer for Open MCT Web; interprets bundle definitions and serves as an intermediary between Require and Angular", "libraries": "lib", "configuration": { "paths": { @@ -17,57 +17,65 @@ { "name": "Blanket.js", "version": "1.1.5", - "description": "Code coverage measurement and reporting.", + "description": "Code coverage measurement and reporting", "author": "Alex Seville", "website": "http://blanketjs.org/", - "license": "MIT", + "copyright": "", + "license": "license-mit", "link": "http://opensource.org/licenses/MIT" }, { "name": "Jasmine", "version": "1.3.1", - "description": "Unit testing.", + "description": "Unit testing", "author": "Pivotal Labs", "website": "http://jasmine.github.io/", - "license": "MIT", + "copyright": "", + "license": "license-mit", "link": "http://opensource.org/licenses/MIT" }, { "name": "RequireJS", "version": "2.1.9", - "description": "Script loader.", + "description": "Script loader", "author": "The Dojo Foundation", "website": "http://requirejs.org/", - "license": "MIT", + "copyright": "Copyright (c) 2010-2015, The Dojo Foundation", + "license": "license-mit", "link": "https://github.com/jrburke/requirejs/blob/master/LICENSE" }, { "name": "AngularJS", "version": "1.2.26", - "description": "Client-side web application framework.", + "description": "Client-side web application framework", "author": "Google", "website": "http://angularjs.org/", - "license": "MIT", + "copyright": "Copyright (c) 2010-2015 Google, Inc. http://angularjs.org", + "license": "license-mit", "link": "https://github.com/angular/angular.js/blob/master/LICENSE" }, { "name": "Angular-Route", "version": "1.2.26", - "description": "Client-side view routing.", + "description": "Client-side view routing", "author": "Google", "website": "http://angularjs.org/", - "license": "MIT", + "copyright": "Copyright (c) 2010-2015 Google, Inc. http://angularjs.org", + "license": "license-mit", "link": "https://github.com/angular/angular.js/blob/master/LICENSE" }, { "name": "ES6-Promise", "version": "2.0.0", - "description": "Promise polyfill for pre-ECMAScript 6 browsers.", + "description": "Promise polyfill for pre-ECMAScript 6 browsers", "author": "Yehuda Katz, Tom Dale, Stefan Penner and contributors", "website": "https://github.com/jakearchibald/es6-promise", - "license": "MIT", + "copyright": "Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors", + "license": "license-mit", "link": "https://github.com/jakearchibald/es6-promise/blob/master/LICENSE" } ] } -} \ No newline at end of file +} + + diff --git a/platform/telemetry/bundle.json b/platform/telemetry/bundle.json index f1500a67bf..9b02e64aec 100644 --- a/platform/telemetry/bundle.json +++ b/platform/telemetry/bundle.json @@ -1,6 +1,6 @@ { "name": "Data bundle", - "description": "Interfaces and infrastructure for real-time and historical data.", + "description": "Interfaces and infrastructure for real-time and historical data", "configuration": { "paths": { "moment": "moment.min" @@ -50,9 +50,10 @@ "name": "Moment.js", "version": "2.7.0", "author": "Tim Wood, Iskren Chernev, Moment.js contributors", - "description": "Time/date parsing/formatting.", + "description": "Time/date parsing/formatting", "website": "http://momentjs.com", - "license": "MIT", + "copyright": "Copyright (c) 2011-2014 Tim Wood, Iskren Chernev, Moment.js contributors", + "license": "license-mit", "link": "https://raw.githubusercontent.com/moment/moment/develop/LICENSE" } ] From b4cabbb4af45146a14e90a302db56c2f5e8d1b18 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 15 Apr 2015 17:46:11 -0700 Subject: [PATCH 04/45] [Frontend] Added dynamic plurality to "author" field label WTD-1036 If licenses/author node in *bundle.json includes a comma, display authors instead of author as the label --- platform/commonUI/about/res/templates/licenses.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/commonUI/about/res/templates/licenses.html b/platform/commonUI/about/res/templates/licenses.html index 472507a52d..474432014a 100644 --- a/platform/commonUI/about/res/templates/licenses.html +++ b/platform/commonUI/about/res/templates/licenses.html @@ -10,7 +10,7 @@

{{license.name}}

Version {{license.version}} -  | Author {{license.author}} +  | Authors {{license.author}}  | Description {{license.description}}

License From 06a887a96e2953ee073b6299ad7f92496718ae3b Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Thu, 16 Apr 2015 15:16:00 -0700 Subject: [PATCH 05/45] [Content/Dev] Licenses generator and content WTD-1036 Added file to gen LICENSES.md content; Added LICENSES.md file to repo --- LICENSES.md | 335 ++++++++++++++++++ platform/commonUI/about/bundle.json | 4 + .../res/templates/licenses-export-md.html | 21 ++ 3 files changed, 360 insertions(+) create mode 100644 LICENSES.md create mode 100644 platform/commonUI/about/res/templates/licenses-export-md.html diff --git a/LICENSES.md b/LICENSES.md new file mode 100644 index 0000000000..9030203f1f --- /dev/null +++ b/LICENSES.md @@ -0,0 +1,335 @@ +# Open MCT Web Licenses + +## Apache License + + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +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. +## Software Components Licenses + +This software includes components released under the following licenses: + +### Blanket.js + +#### Info + +* Link: http://blanketjs.org/ + +* Version: 1.1.5 + +* Author: Alex Seville + +* Description: Code coverage measurement and reporting + +#### License + +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. + +--- + +### Jasmine + +#### Info + +* Link: http://jasmine.github.io/ + +* Version: 1.3.1 + +* Author: Pivotal Labs + +* Description: Unit testing + +#### License + +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. + +--- + +### RequireJS + +#### Info + +* Link: http://requirejs.org/ + +* Version: 2.1.9 + +* Author: The Dojo Foundation + +* Description: Script loader + +#### License + +Copyright (c) 2010-2015, The Dojo Foundation + +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. + +--- + +### AngularJS + +#### Info + +* Link: http://angularjs.org/ + +* Version: 1.2.26 + +* Author: Google + +* Description: Client-side web application framework + +#### License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +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. + +--- + +### Angular-Route + +#### Info + +* Link: http://angularjs.org/ + +* Version: 1.2.26 + +* Author: Google + +* Description: Client-side view routing + +#### License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +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. + +--- + +### ES6-Promise + +#### Info + +* Link: https://github.com/jakearchibald/es6-promise + +* Version: 2.0.0 + +* Authors: Yehuda Katz, Tom Dale, Stefan Penner and contributors + +* Description: Promise polyfill for pre-ECMAScript 6 browsers + +#### License + +Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors + +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. + +--- + +### screenfull.js + +#### Info + +* Link: https://github.com/sindresorhus/screenfull.js/ + +* Version: 1.2.0 + +* Author: Sindre Sorhus + +* Description: Wrapper for cross-browser usage of fullscreen API + +#### License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +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. + +--- + +### Math.uuid.js + +#### Info + +* Link: https://github.com/broofa/node-uuid + +* Version: 1.4 + +* Author: Robert Kieffer + +* Description: Unique identifer generation (code adapted.) + +#### License + +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. + +--- + +### Modernizr + +#### Info + +* Link: http://modernizr.com + +* Version: 2.6.2 + +* Author: Faruk Ateş + +* Description: Browser/device capability finding + +#### License + +Copyright (c) 2009–2015 + +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. + +--- + +### Normalize.css + +#### Info + +* Link: http://necolas.github.io/normalize.css/ + +* Version: 1.1.2 + +* Authors: Nicolas Gallagher, Jonathan Neal + +* Description: Browser style normalization + +#### License + +Copyright (c) Nicolas Gallagher and Jonathan Neal + +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. + +--- + +### Moment.js + +#### Info + +* Link: http://momentjs.com + +* Version: 2.7.0 + +* Authors: Tim Wood, Iskren Chernev, Moment.js contributors + +* Description: Time/date parsing/formatting + +#### License + +Copyright (c) 2011-2014 Tim Wood, Iskren Chernev, Moment.js contributors + +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. + +--- \ No newline at end of file diff --git a/platform/commonUI/about/bundle.json b/platform/commonUI/about/bundle.json index 34e65eb81e..462f6f696f 100644 --- a/platform/commonUI/about/bundle.json +++ b/platform/commonUI/about/bundle.json @@ -50,6 +50,10 @@ { "when": "/licenses", "templateUrl": "templates/licenses.html" + }, + { + "when": "/licenses-md", + "templateUrl": "templates/licenses-export-md.html" } ] } diff --git a/platform/commonUI/about/res/templates/licenses-export-md.html b/platform/commonUI/about/res/templates/licenses-export-md.html new file mode 100644 index 0000000000..1e97b0f236 --- /dev/null +++ b/platform/commonUI/about/res/templates/licenses-export-md.html @@ -0,0 +1,21 @@ +
+

# Open MCT Web Licenses

+

## Apache License

+ + +

## Software Components Licenses

+

This software includes components released under the following licenses:

+
+

### {{license.name}}

+

#### Info

+

* Link: {{license.website}}

+

* Version: {{license.version}}

+

* Authors: {{license.author}}

+

* Description: {{license.description}}

+

#### License

+

{{license.copyright}}

+ +

 

+

---

+
+
\ No newline at end of file From 774c4dec1d80072ff3a882cdc0f2bfc4b8658641 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 16 Apr 2015 16:01:51 -0700 Subject: [PATCH 06/45] [Telemetry] Separate out delegator Separate out handling of delegation related to telemetry, WTD-806. --- platform/telemetry/src/TelemetryDelegator.js | 45 +++++++++++++++++++ platform/telemetry/src/TelemetryRequester.js | 34 ++++++++++++++ .../telemetry/src/TelemetrySubscription.js | 25 +++-------- 3 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 platform/telemetry/src/TelemetryDelegator.js create mode 100644 platform/telemetry/src/TelemetryRequester.js diff --git a/platform/telemetry/src/TelemetryDelegator.js b/platform/telemetry/src/TelemetryDelegator.js new file mode 100644 index 0000000000..df66e84407 --- /dev/null +++ b/platform/telemetry/src/TelemetryDelegator.js @@ -0,0 +1,45 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * Used to handle telemetry delegation associated with a + * given domain object. + */ + function TelemetryDelegator($q) { + return { + /** + * Promise telemetry-providing objects associated with + * this domain object (either the domain object itself, + * or the objects it delegates) + * @returns {Promise.} domain objects with + * a telemetry capability + */ + promiseTelemetryObjects: function (domainObject) { + // If object has been cleared, there are no relevant + // telemetry-providing domain objects. + if (!domainObject) { + return $q.when([]); + } + + // Otherwise, try delegation first, and attach the + // object itself if it has a telemetry capability. + return $q.when(domainObject.useCapability( + "delegation", + "telemetry" + )).then(function (result) { + var head = domainObject.hasCapability("telemetry") ? + [ domainObject ] : [], + tail = result || []; + return head.concat(tail); + }); + } + }; + } + + return TelemetryDelegator; + } +); \ No newline at end of file diff --git a/platform/telemetry/src/TelemetryRequester.js b/platform/telemetry/src/TelemetryRequester.js new file mode 100644 index 0000000000..f400083b28 --- /dev/null +++ b/platform/telemetry/src/TelemetryRequester.js @@ -0,0 +1,34 @@ +/*global define*/ + +define( + ['./TelemetryDelegator'], + function (TelemetryDelegator) { + "use strict"; + + + /** + * A TelemetryRequester provides an easy interface to request + * telemetry associated with a set of domain objects. + * + * @constructor + * @param $q Angular's $q + */ + function TelemetryRequester($q) { + var delegator = new TelemetryDelegator($q); + + // Look up domain objects which have telemetry capabilities. + // This will either be the object in view, or object that + // this object delegates its telemetry capability to. + function promiseRelevantObjects(domainObject) { + return delegator.promiseTelemetryObjects(domainObject); + } + + return { + + }; + } + + return TelemetryRequester; + + } +); \ No newline at end of file diff --git a/platform/telemetry/src/TelemetrySubscription.js b/platform/telemetry/src/TelemetrySubscription.js index cce24d48dd..adbe08ba51 100644 --- a/platform/telemetry/src/TelemetrySubscription.js +++ b/platform/telemetry/src/TelemetrySubscription.js @@ -1,8 +1,8 @@ /*global define*/ define( - ['./TelemetryQueue', './TelemetryTable'], - function (TelemetryQueue, TelemetryTable) { + ['./TelemetryQueue', './TelemetryTable', './TelemetryDelegator'], + function (TelemetryQueue, TelemetryTable, TelemetryDelegator) { "use strict"; @@ -31,7 +31,8 @@ define( * the callback once, with access to the latest data */ function TelemetrySubscription($q, $timeout, domainObject, callback, lossless) { - var unsubscribePromise, + var delegator = new TelemetryDelegator($q), + unsubscribePromise, latestValues = {}, telemetryObjects = [], pool = lossless ? new TelemetryQueue() : new TelemetryTable(), @@ -42,23 +43,7 @@ define( // This will either be the object in view, or object that // this object delegates its telemetry capability to. function promiseRelevantObjects(domainObject) { - // If object has been cleared, there are no relevant - // telemetry-providing domain objects. - if (!domainObject) { - return $q.when([]); - } - - // Otherwise, try delegation first, and attach the - // object itself if it has a telemetry capability. - return $q.when(domainObject.useCapability( - "delegation", - "telemetry" - )).then(function (result) { - var head = domainObject.hasCapability("telemetry") ? - [ domainObject ] : [], - tail = result || []; - return head.concat(tail); - }); + return delegator.promiseTelemetryObjects(domainObject); } function updateValuesFromPool() { From 60e888e16ece2ad59384db3de6840cd999c8af84 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 16 Apr 2015 16:27:25 -0700 Subject: [PATCH 07/45] [Telemetry] Begin adding telemetry handler Begin adding a general purpose handler for telemetry which extends on the behavior associated with the telemetrySubscriber by supporting access to historical data as well. WTD-806. --- platform/telemetry/src/TelemetryHandle.js | 81 +++++++++++++++++++ platform/telemetry/src/TelemetryHandler.js | 33 ++++++++ platform/telemetry/src/TelemetryRequester.js | 34 -------- .../telemetry/src/TelemetrySubscription.js | 16 +++- 4 files changed, 128 insertions(+), 36 deletions(-) create mode 100644 platform/telemetry/src/TelemetryHandle.js create mode 100644 platform/telemetry/src/TelemetryHandler.js delete mode 100644 platform/telemetry/src/TelemetryRequester.js diff --git a/platform/telemetry/src/TelemetryHandle.js b/platform/telemetry/src/TelemetryHandle.js new file mode 100644 index 0000000000..eef57caba6 --- /dev/null +++ b/platform/telemetry/src/TelemetryHandle.js @@ -0,0 +1,81 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + function TelemetryHandle($q, subscription) { + var seriesMap = {}, + self = Object.create(subscription); + + function requestSeries(telemetryObject, request, callback) { + var id = telemetryObject.getId(), + telemetry = telemetryObject.getCapability('telemetry'); + + function receiveSeries(series) { + // Store it for subsequent lookup + seriesMap[id] = series; + // Notify callback of new series data, if there is one + if (callback) { + callback(telemetryObject, series); + } + // Pass it along for promise-chaining + return series; + } + + // Issue the request via the object's telemetry capability + return telemetry.requestData(request).then(receiveSeries); + } + + + /** + * Get the most recently obtained telemetry data series associated + * with this domain object. + * @param {DomainObject} the domain object which has telemetry + * data associated with it + * @return {TelemetrySeries} the most recent telemetry series + * (or undefined if there is not one) + */ + self.getSeries = function (domainObject) { + var id = domainObject.getId(); + return seriesMap[id]; + }; + + + /** + * Change the request duration. + * @param {object|number} request the duration of historical + * data to look at; or, the request to issue + * @param {Function} [callback] a callback that will be + * invoked as new data becomes available, with the + * domain object for which new data is available. + */ + self.request = function (request, callback) { + // Issue (and handle) the new request from this object + function issueRequest(telemetryObject) { + return requestSeries(telemetryObject, request, callback); + } + + // Map the request to all telemetry objects + function issueRequests(telemetryObjects) { + return $q.all(telemetryObjects.map(issueRequest)); + } + + // If the request is a simple number, treat it as a duration + request = (typeof request === 'number') ? + { duration: request } : request; + + // Look up telemetry-providing objects from the subscription, + // then issue new requests. + return subscription.promiseTelemetryObjects() + .then(issueRequests); + }; + + return self; + } + + return TelemetryHandle; + + } +); \ No newline at end of file diff --git a/platform/telemetry/src/TelemetryHandler.js b/platform/telemetry/src/TelemetryHandler.js new file mode 100644 index 0000000000..2b3aa954eb --- /dev/null +++ b/platform/telemetry/src/TelemetryHandler.js @@ -0,0 +1,33 @@ +/*global define*/ + +define( + ['./TelemetryHandle'], + function (TelemetryHandle) { + "use strict"; + + + /** + * A TelemetryRequester provides an easy interface to request + * telemetry associated with a set of domain objects. + * + * @constructor + * @param $q Angular's $q + */ + function TelemetryHandler($q, telemetrySubscriber) { + return { + handle: function (domainObject, callback, lossless) { + var subscription = telemetrySubscriber.subscribe( + domainObject, + callback, + lossless + ); + + return new TelemetryHandle($q, subscription); + } + }; + } + + return TelemetryHandler; + + } +); \ No newline at end of file diff --git a/platform/telemetry/src/TelemetryRequester.js b/platform/telemetry/src/TelemetryRequester.js deleted file mode 100644 index f400083b28..0000000000 --- a/platform/telemetry/src/TelemetryRequester.js +++ /dev/null @@ -1,34 +0,0 @@ -/*global define*/ - -define( - ['./TelemetryDelegator'], - function (TelemetryDelegator) { - "use strict"; - - - /** - * A TelemetryRequester provides an easy interface to request - * telemetry associated with a set of domain objects. - * - * @constructor - * @param $q Angular's $q - */ - function TelemetryRequester($q) { - var delegator = new TelemetryDelegator($q); - - // Look up domain objects which have telemetry capabilities. - // This will either be the object in view, or object that - // this object delegates its telemetry capability to. - function promiseRelevantObjects(domainObject) { - return delegator.promiseTelemetryObjects(domainObject); - } - - return { - - }; - } - - return TelemetryRequester; - - } -); \ No newline at end of file diff --git a/platform/telemetry/src/TelemetrySubscription.js b/platform/telemetry/src/TelemetrySubscription.js index adbe08ba51..2afae3495c 100644 --- a/platform/telemetry/src/TelemetrySubscription.js +++ b/platform/telemetry/src/TelemetrySubscription.js @@ -33,6 +33,7 @@ define( function TelemetrySubscription($q, $timeout, domainObject, callback, lossless) { var delegator = new TelemetryDelegator($q), unsubscribePromise, + telemetryObjectPromise, latestValues = {}, telemetryObjects = [], pool = lossless ? new TelemetryQueue() : new TelemetryTable(), @@ -137,8 +138,8 @@ define( // will be unsubscribe functions. (This must be a promise // because delegation is supported, and retrieving delegate // telemetry-capable objects may be an asynchronous operation.) - unsubscribePromise = - promiseRelevantObjects(domainObject) + telemetryObjectPromise = promiseRelevantObjects(domainObject); + unsubscribePromise = telemetryObjectPromise .then(cacheObjectReferences) .then(subscribeAll); @@ -224,6 +225,17 @@ define( */ getMetadata: function () { return metadatas; + }, + /** + * Get a promise for all telemetry-providing objects + * associated with this subscription. + * @returns {Promise.} a promise for + * telemetry-providing objects + */ + promiseTelemetryObjects: function () { + // Unsubscribe promise is available after objects + // are loaded. + return telemetryObjectPromise; } }; } From 03e1633a227aa7137d9d0e606916675bd2bfc890 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 16 Apr 2015 16:41:09 -0700 Subject: [PATCH 08/45] [Timeline] Track realtime index in updater Track realtime index in PlotUpdater to facilitate prepending of historical data portion on-demand, WTD-806. --- .../features/plot/src/elements/PlotUpdater.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index ed81035b6b..250a140cfc 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -29,6 +29,7 @@ define( domainOffset, buffers = {}, lengths = {}, + realtimeIndex = {}, lengthArray = [], bufferArray = []; @@ -52,6 +53,8 @@ define( } else { // Just shift the existing buffer buffer.set(buffer.subarray(2)); + // Update the realtime index accordingly + realtimeIndex[id] = Math.max(realtimeIndex[id] - 1, 0); } } @@ -91,6 +94,11 @@ define( // Observe max/min range values max[1] = Math.max(max[1], rangeValue); min[1] = Math.min(min[1], rangeValue); + // Update the cutoff point for when we started receiving + // realtime data, to aid in clearing historical data later + if (realtimeIndex[id] === undefined) { + realtimeIndex[id] = index; + } } return buffer; @@ -111,6 +119,11 @@ define( }); } + // Update historical data for this domain object + function setHistorical(domainObject) { + + } + // Handle new telemetry data function update() { var objects = subscription.getTelemetryObjects(); @@ -205,7 +218,11 @@ define( /** * Update with latest data. */ - update: update + update: update, + /** + * Fill in historical data. + */ + setHistorical: setHistorical }; } From 9215eb1427d98a6d6c30f04490cacc290444d8b5 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 16 Apr 2015 16:48:03 -0700 Subject: [PATCH 09/45] [Plot] Begin updating plot Begin updating plot to merge realtime and historical telemetry, WTD-806. --- .../features/plot/src/elements/PlotUpdater.js | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index 250a140cfc..0ca8684ad7 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -61,21 +61,7 @@ define( return buffer; } - // Add data to the plot. - function addData(obj) { - var id = obj.getId(), - index = lengths[id] || 0, - buffer = buffers[id], - domainValue = subscription.getDomainValue(obj, domain), - rangeValue = subscription.getRangeValue(obj, range); - - // If we don't already have a data buffer for that ID, - // make one. - if (!buffer) { - buffer = new Float32Array(INITIAL_SIZE); - buffers[id] = buffer; - } - + function setData(buffer, id, index, domainValue, rangeValue) { // Make sure there's data to add, and then add it if (domainValue !== undefined && rangeValue !== undefined && (index < 1 || domainValue !== buffer[index * 2 - 2])) { @@ -94,14 +80,39 @@ define( // Observe max/min range values max[1] = Math.max(max[1], rangeValue); min[1] = Math.min(min[1], rangeValue); - // Update the cutoff point for when we started receiving - // realtime data, to aid in clearing historical data later - if (realtimeIndex[id] === undefined) { - realtimeIndex[id] = index; - } + } + return buffer; + } + + // Add data to the plot. + function addData(obj) { + var id = obj.getId(), + index = lengths[id] || 0, + buffer = buffers[id], + domainValue = subscription.getDomainValue(obj, domain), + rangeValue = subscription.getRangeValue(obj, range); + + // If we don't already have a data buffer for that ID, + // make one. + if (!buffer) { + buffer = new Float32Array(INITIAL_SIZE); + buffers[id] = buffer; } - return buffer; + // Update the cutoff point for when we started receiving + // realtime data, to aid in clearing historical data later + if (realtimeIndex[id] === undefined) { + realtimeIndex[id] = index; + } + + // Put the data in the buffer + return setData( + buffer, + id, + index, + domainValue, + rangeValue + ); } // Update min/max domain values for these objects @@ -121,7 +132,15 @@ define( // Update historical data for this domain object function setHistorical(domainObject) { + var id = domainObject.getId(), + buffer = buffers[id], + endIndex = realtimeIndex[id] || 0; + // Make sure the buffer is big enough + + // Move the realtime data into the correct position + + // Insert the historical data before it } // Handle new telemetry data From 15b1c824e3491a2ac1f0c472fa4357bd8cfdcc27 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 11:35:24 -0700 Subject: [PATCH 10/45] [Plot] Begin separating out plot line handling Begin separating out plot line buffer from the rest of plot; managing this buffer separately will aid in merging realtime and hsitorical data, WTD-806. --- .../features/plot/src/elements/PlotLine.js | 102 +++++++++++++++++ .../plot/src/elements/PlotLineBuffer.js | 107 ++++++++++++++++++ .../plot/src/elements/PlotSeriesWindow.js | 45 ++++++++ .../features/plot/src/elements/PlotUpdater.js | 17 ++- .../plot/test/elements/PlotLineBufferSpec.js | 72 ++++++++++++ platform/features/plot/test/suite.json | 1 + 6 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 platform/features/plot/src/elements/PlotLine.js create mode 100644 platform/features/plot/src/elements/PlotLineBuffer.js create mode 100644 platform/features/plot/src/elements/PlotSeriesWindow.js create mode 100644 platform/features/plot/test/elements/PlotLineBufferSpec.js diff --git a/platform/features/plot/src/elements/PlotLine.js b/platform/features/plot/src/elements/PlotLine.js new file mode 100644 index 0000000000..3c2257c317 --- /dev/null +++ b/platform/features/plot/src/elements/PlotLine.js @@ -0,0 +1,102 @@ +/*global define,Float32Array*/ + +define( + ['./PlotSeriesWindow'], + function (PlotSeriesWindow) { + "use strict"; + + + function PlotLine(initialSize, maxPoints) { + var buffer, + length = 0, + timeWindow; + + // Binary search the buffer to find the index where + // a point with this timestamp should be inserted. + // After is a flag indicating whether it is preferred + // to insert after or before its nearest timestamp + function searchBuffer(timestamp, after) { + // Binary search for an appropriate insertion index. + function binSearch(min, max) { + var mid = Math.floor((min + max) / 2), + ts; + + if (max < min) { + return -1; + } + + ts = buffer[mid * 2]; + + // Check for an exact match... + if (ts === timestamp) { + // This is a case where we'll need to + // split up what we want to insert. + return mid + after ? -1 : 1; + } else { + // Found our index? + if (max === min) { + return max; + } + // Otherwise, search recursively + if (ts < timestamp) { + + } else { + + } + } + + } + + // Booleanize + after = !!after; + + return binSearch(0, length - 1); + } + + function insertSeriesWindow(seriesWindow) { + var startIndex = findStartIndex(), + endIndex = findEndIndex(); + + if (startIndex === endIndex) { + + } else { + // Split it up, and add the two halves + seriesWindow.split().forEach(insertSeriesWindow); + } + } + + function createWindow(series, domain, range) { + // TODO: Enforce time window, too! + return new PlotSeriesWindow( + series, + domain, + range, + 0, + series.getPointCount() + ); + } + + return { + addData: function (domainValue, rangeValue) { + // Should append to buffer + }, + addSeries: function (series, domain, range) { + // Should try to add via insertion if a + // clear insertion point is available; + // if not, should split and add each half. + // Insertion operation also needs to factor out + // redundant timestamps, for overlapping data + insertSeriesWindow(createWindow(series, domain, range)); + }, + setTimeWindow: function (start, end) { + timeWindow = [ start, end ]; + }, + clearTimeWindow: function () { + timeWindow = undefined; + } + }; + } + + return PlotLine; + } +); \ No newline at end of file diff --git a/platform/features/plot/src/elements/PlotLineBuffer.js b/platform/features/plot/src/elements/PlotLineBuffer.js new file mode 100644 index 0000000000..8a1f6a715f --- /dev/null +++ b/platform/features/plot/src/elements/PlotLineBuffer.js @@ -0,0 +1,107 @@ +/*global define,Float32Array*/ + +define( + [], + function () { + "use strict"; + + function PlotLineBuffer(domainOffset, initialSize, maxSize) { + var buffer = new Float32Array(initialSize * 2), + length = 0; + + // Binary search for an insertion index + function binSearch(value, min, max) { + var mid = Math.floor((min + max) / 2), + found = buffer[mid * 2]; + + // Collisions are not wanted + if (found === value) { + return -1; + } + + // Otherwise, if we're down to a single index, + // we've found our insertion point + if (min >= max) { + // Compare the found timestamp with the search + // value to decide if we'll insert after or before. + return min + ((found < value) ? 1 : 0); + } + + // Finally, do the recursive step + if (found < value) { + return binSearch(value, mid + 1, max); + } else { + return binSearch(value, min, mid - 1); + } + } + + // Increase the size of the buffer + function doubleBufferSize() { + var sz = Math.min(maxSize, buffer.length * 2), + canDouble = sz > buffer.length, + doubled = canDouble && new Float32Array(sz); + + if (canDouble) { + doubled.set(buffer); // Copy contents of original + buffer = doubled; + } + + return canDouble; + } + + return { + getBuffer: function () { + return buffer; + }, + insert: function (series, index) { + var sz = series.getPointCount(), + free = (buffer.length / 2) - length, + i; + + // Don't allow append after the end; that doesn't make sense + if (index > length) { + index = length; + } + + // Resize if necessary + if (sz > free) { + if (!doubleBufferSize()) { + // TODO: Figure out which data to discard + i = 0; + } + } + + // Insert data into the set + for (i = 0; i < series.getPointCount(); i += 1) { + buffer[(i + index) * 2] = + series.getDomainValue(i) - domainOffset; + buffer[(i + index) * 2 + 1] = + series.getRangeValue(i); + } + + // Increase the length + length += sz; + }, + /** + * Find an index for inserting data with this + * timestamp. The second argument indicates whether + * we are searching for insert-before or insert-after + * positions. + * Timestamps are meant to be unique, so if a collision + * occurs, this will return -1. + * @param {number} timestamp timestamp to insert + * @returns {number} the index for insertion (or -1) + */ + findInsertionIndex: function (timestamp) { + return binSearch( + timestamp - domainOffset, + 0, + length - 1 + ); + } + }; + } + + return PlotLineBuffer; + } +); \ No newline at end of file diff --git a/platform/features/plot/src/elements/PlotSeriesWindow.js b/platform/features/plot/src/elements/PlotSeriesWindow.js new file mode 100644 index 0000000000..614c09b525 --- /dev/null +++ b/platform/features/plot/src/elements/PlotSeriesWindow.js @@ -0,0 +1,45 @@ +/*global define*/ +define( + [], + function () { + "use strict"; + + /** + * Provides a window on a telemetry data series, to support + * insertion into a plot line. + */ + function PlotSeriesWindow(series, domain, range, start, end) { + return { + getPointCount: function () { + return end - start; + }, + getDomainValue: function (index) { + return series.getDomainValue(index - start, domain); + }, + getRangeValue: function (index) { + return series.getRangeValue(index - start, range); + }, + split: function () { + var mid = Math.floor((end + start) / 2); + return end > start ? + [ + new PlotSeriesWindow( + series, + domain, + range, + start, + mid + ), + new PlotSeriesWindow( + series, + domain, + range, + mid + 1, + end + ) + ] : []; + } + }; + } + } +); \ No newline at end of file diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index 0ca8684ad7..3b9be1d7a3 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -133,8 +133,23 @@ define( // Update historical data for this domain object function setHistorical(domainObject) { var id = domainObject.getId(), + // Buffer to expand buffer = buffers[id], - endIndex = realtimeIndex[id] || 0; + // Index where historical data ends (and realtime begins) + endIndex = realtimeIndex[id] || 0, + // Look up the data series + series = subscription.getSeries(domainObject), + // Get its length + seriesLength = series ? series.getPointCount() : 0, + // Get the current buffer length... + length = lengths[id] || 0, + // As well as the length of the realtime segment + realtimeLength = length - endIndex, + // Determine the new total length of the existing + // realtime + new historical segment. + totalLength = + Math.min(seriesLength + realtimeLength, maxPoints), + seriesFit = Math.max(0, totalLength - realtimeLength); // Make sure the buffer is big enough diff --git a/platform/features/plot/test/elements/PlotLineBufferSpec.js b/platform/features/plot/test/elements/PlotLineBufferSpec.js new file mode 100644 index 0000000000..7838d3bbd4 --- /dev/null +++ b/platform/features/plot/test/elements/PlotLineBufferSpec.js @@ -0,0 +1,72 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +/** + * MergeModelsSpec. Created by vwoeltje on 11/6/14. + */ +define( + ["../../src/elements/PlotLineBuffer"], + function (PlotLineBuffer) { + "use strict"; + + var TEST_INITIAL_SIZE = 10, + TEST_MAX_SIZE = 40, + TEST_DOMAIN_OFFSET = 42; + + describe("A plot line buffer", function () { + var mockSeries, + testDomainValues, + testRangeValues, + buffer; + + beforeEach(function () { + testDomainValues = [ 1, 3, 7, 9, 14, 15 ]; + testRangeValues = [ 8, 0, 3, 9, 8, 11 ]; + mockSeries = jasmine.createSpyObj( + "series", + ['getPointCount', 'getDomainValue', 'getRangeValue'] + ); + mockSeries.getPointCount.andCallFake(function () { + return testDomainValues.length; + }); + mockSeries.getDomainValue.andCallFake(function (i) { + return testDomainValues[i]; + }); + mockSeries.getRangeValue.andCallFake(function (i) { + return testRangeValues[i]; + }); + + buffer = new PlotLineBuffer( + TEST_DOMAIN_OFFSET, + TEST_INITIAL_SIZE, + TEST_MAX_SIZE + ); + + // Start with some data in there + buffer.insert(mockSeries, 0); + }); + + it("allows insertion of series data", function () { + // Convert to a regular array for checking. + // Verify that domain/ranges were interleaved and + // that domain offset was adjusted for. + expect( + Array.prototype.slice.call(buffer.getBuffer()).slice(0, 12) + ).toEqual([ -41, 8, -39, 0, -35, 3, -33, 9, -28, 8, -27, 11]); + }); + + it("finds insertion indexes", function () { + expect(buffer.findInsertionIndex(0)).toEqual(0); + expect(buffer.findInsertionIndex(2)).toEqual(1); + expect(buffer.findInsertionIndex(5)).toEqual(2); + expect(buffer.findInsertionIndex(10)).toEqual(4); + expect(buffer.findInsertionIndex(14.5)).toEqual(5); + expect(buffer.findInsertionIndex(20)).toEqual(6); + + // 9 is already in there, disallow insertion + expect(buffer.findInsertionIndex(9)).toEqual(-1); + }); + + + }); + } +); \ No newline at end of file diff --git a/platform/features/plot/test/suite.json b/platform/features/plot/test/suite.json index 92ee3b07c8..7d099f14da 100644 --- a/platform/features/plot/test/suite.json +++ b/platform/features/plot/test/suite.json @@ -6,6 +6,7 @@ "SubPlot", "SubPlotFactory", "elements/PlotAxis", + "elements/PlotLineBuffer", "elements/PlotPalette", "elements/PlotPanZoomStack", "elements/PlotPanZoomStackGroup", From b20643018a95dced760488a7a96de8379a2226df Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 12:06:44 -0700 Subject: [PATCH 11/45] [Plot] Complete PlotLineBuffer Complete implementation of PlotLineBuffer, which is responsible for managing the displayable buffer for an individual plot line. WTD-806. --- .../plot/src/elements/PlotLineBuffer.js | 74 ++++++++++++++++++- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/platform/features/plot/src/elements/PlotLineBuffer.js b/platform/features/plot/src/elements/PlotLineBuffer.js index 8a1f6a715f..d875c9bc06 100644 --- a/platform/features/plot/src/elements/PlotLineBuffer.js +++ b/platform/features/plot/src/elements/PlotLineBuffer.js @@ -5,6 +5,13 @@ define( function () { "use strict"; + /** + * Contains the buffer used to draw a plot. + * @param {number} domainOffset number to subtract from domain values + * @param {number} initialSize initial buffer size + * @param {number} maxSize maximum buffer size + * @constructor + */ function PlotLineBuffer(domainOffset, initialSize, maxSize) { var buffer = new Float32Array(initialSize * 2), length = 0; @@ -49,10 +56,60 @@ define( return canDouble; } + // Decrease the size of the buffer + function halveBufferSize() { + var sz = Math.max(initialSize, buffer.length / 2), + canHalve = sz < buffer.length; + + if (canHalve) { + buffer = new Float32Array(buffer.subarray(0, sz)); + } + + return canHalve; + } + + return { + /** + * Get the WebGL-displayable buffer of points to plot. + * @returns {Float32Array} displayable buffer for this line + */ getBuffer: function () { return buffer; }, + /** + * Remove values from this buffer. + * Normally, values are removed from the start + * of the buffer; a truthy value in the second argument + * will cause values to be removed from the end. + * @param {number} count number of values to remove + * @param {boolean} [fromEnd] true if the most recent + * values should be removed + */ + trim: function (count, fromEnd) { + // If we're removing values from the start... + if (!fromEnd) { + // ...do so by shifting buffer contents over + buffer.set(buffer.subarray(2 * count)); + } + // Reduce used buffer size accordingly + length -= count; + // Finally, if less than half of the buffer is being + // used, free up some memory. + if (length < buffer.length / 4) { + halveBufferSize(); + } + }, + /** + * Insert data from the provided series at the specified + * index. If this would exceed the buffer's maximum capacity, + * this operation fails and the buffer is unchanged. + * @param {TelemetrySeries} series the series to insert + * @param {number} index the index at which to insert this + * series + * @returns {boolean} true if insertion succeeded; otherwise + * false + */ insert: function (series, index) { var sz = series.getPointCount(), free = (buffer.length / 2) - length, @@ -66,13 +123,21 @@ define( // Resize if necessary if (sz > free) { if (!doubleBufferSize()) { - // TODO: Figure out which data to discard - i = 0; + // Can't make room for this, insertion fails + return false; } } + // Shift data over if necessary + if (index < length) { + buffer.set( + buffer.subarray(index, length - index), + index + sz + ); + } + // Insert data into the set - for (i = 0; i < series.getPointCount(); i += 1) { + for (i = 0; i < sz; i += 1) { buffer[(i + index) * 2] = series.getDomainValue(i) - domainOffset; buffer[(i + index) * 2 + 1] = @@ -81,6 +146,9 @@ define( // Increase the length length += sz; + + // Indicate that insertion was successful + return true; }, /** * Find an index for inserting data with this From 6551e9212db7408d65fb90b9624beddbfe12c06a Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 12:13:17 -0700 Subject: [PATCH 12/45] [Plot] Fix insertion in PlotLineBuffer WTD-806. --- platform/features/plot/src/elements/PlotLineBuffer.js | 4 ++-- .../features/plot/test/elements/PlotLineBufferSpec.js | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/platform/features/plot/src/elements/PlotLineBuffer.js b/platform/features/plot/src/elements/PlotLineBuffer.js index d875c9bc06..307abae666 100644 --- a/platform/features/plot/src/elements/PlotLineBuffer.js +++ b/platform/features/plot/src/elements/PlotLineBuffer.js @@ -131,8 +131,8 @@ define( // Shift data over if necessary if (index < length) { buffer.set( - buffer.subarray(index, length - index), - index + sz + buffer.subarray(index * 2, length * 2), + (index + sz) * 2 ); } diff --git a/platform/features/plot/test/elements/PlotLineBufferSpec.js b/platform/features/plot/test/elements/PlotLineBufferSpec.js index 7838d3bbd4..5d6b294da8 100644 --- a/platform/features/plot/test/elements/PlotLineBufferSpec.js +++ b/platform/features/plot/test/elements/PlotLineBufferSpec.js @@ -66,6 +66,14 @@ define( expect(buffer.findInsertionIndex(9)).toEqual(-1); }); + it("allows insertion in the middle", function () { + var head = [ -41, 8, -39, 0, -35, 3 ], + tail = [ -33, 9, -28, 8, -27, 11]; + buffer.insert(mockSeries, 3); + expect( + Array.prototype.slice.call(buffer.getBuffer()).slice(0, 24) + ).toEqual(head.concat(head).concat(tail).concat(tail)); + }); }); } From 4d34f19aa2842a7637fa3c58d986f4d9c1bd072f Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 12:26:44 -0700 Subject: [PATCH 13/45] [Plot] Complete coverage of PlotLineBuffer WTD-806. --- .../plot/src/elements/PlotLineBuffer.js | 15 ++++-- .../plot/test/elements/PlotLineBufferSpec.js | 46 +++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/platform/features/plot/src/elements/PlotLineBuffer.js b/platform/features/plot/src/elements/PlotLineBuffer.js index 307abae666..1504fe021c 100644 --- a/platform/features/plot/src/elements/PlotLineBuffer.js +++ b/platform/features/plot/src/elements/PlotLineBuffer.js @@ -44,7 +44,7 @@ define( // Increase the size of the buffer function doubleBufferSize() { - var sz = Math.min(maxSize, buffer.length * 2), + var sz = Math.min(maxSize * 2, buffer.length * 2), canDouble = sz > buffer.length, doubled = canDouble && new Float32Array(sz); @@ -58,7 +58,7 @@ define( // Decrease the size of the buffer function halveBufferSize() { - var sz = Math.max(initialSize, buffer.length / 2), + var sz = Math.max(initialSize * 2, buffer.length / 2), canHalve = sz < buffer.length; if (canHalve) { @@ -77,6 +77,13 @@ define( getBuffer: function () { return buffer; }, + /** + * Get the number of points stored in this buffer. + * @returns {number} the number of points stored + */ + getLength: function () { + return length; + }, /** * Remove values from this buffer. * Normally, values are removed from the start @@ -116,9 +123,7 @@ define( i; // Don't allow append after the end; that doesn't make sense - if (index > length) { - index = length; - } + index = Math.min(index, length); // Resize if necessary if (sz > free) { diff --git a/platform/features/plot/test/elements/PlotLineBufferSpec.js b/platform/features/plot/test/elements/PlotLineBufferSpec.js index 5d6b294da8..3fc7b06c6e 100644 --- a/platform/features/plot/test/elements/PlotLineBufferSpec.js +++ b/platform/features/plot/test/elements/PlotLineBufferSpec.js @@ -52,6 +52,7 @@ define( expect( Array.prototype.slice.call(buffer.getBuffer()).slice(0, 12) ).toEqual([ -41, 8, -39, 0, -35, 3, -33, 9, -28, 8, -27, 11]); + expect(buffer.getLength()).toEqual(6); }); it("finds insertion indexes", function () { @@ -73,6 +74,51 @@ define( expect( Array.prototype.slice.call(buffer.getBuffer()).slice(0, 24) ).toEqual(head.concat(head).concat(tail).concat(tail)); + expect(buffer.getLength()).toEqual(12); + }); + + it("allows values to be trimmed from the start", function () { + buffer.trim(2); + expect(buffer.getLength()).toEqual(4); + expect( + Array.prototype.slice.call(buffer.getBuffer()).slice(0, 8) + ).toEqual([ -35, 3, -33, 9, -28, 8, -27, 11]); + }); + + it("ensures a maximum size", function () { + var i; + + // Should be able to insert 6 series of 6 points each + // (After that, we'll hit the test max of 40) + for (i = 1; i < 6; i += 1) { + expect(buffer.getLength()).toEqual(6 * i); + expect(buffer.insert(mockSeries, Number.POSITIVE_INFINITY)) + .toBeTruthy(); + } + + // Should be maxed out now + expect(buffer.getLength()).toEqual(36); + expect(buffer.insert(mockSeries, Number.POSITIVE_INFINITY)) + .toBeFalsy(); + expect(buffer.getLength()).toEqual(36); + + }); + + it("reduces buffer size when space is no longer needed", function () { + // Check that actual buffer is sized to the initial size + // (double TEST_INITIAL_SIZE, since two elements are needed per + // point; one for domain, one for range) + expect(buffer.getBuffer().length).toEqual(20); + // Should have 6 elements now... grow to 24 + buffer.insert(mockSeries, Number.POSITIVE_INFINITY); + buffer.insert(mockSeries, Number.POSITIVE_INFINITY); + buffer.insert(mockSeries, Number.POSITIVE_INFINITY); + // This should have doubled the actual buffer size + expect(buffer.getBuffer().length).toEqual(80); + // Remove some values + buffer.trim(20); + // Actual buffer size should have been reduced accordingly + expect(buffer.getBuffer().length).toBeLessThan(80); }); }); From 7370b1a87ff59fc97b13a9b8618518ecc7208a62 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 14:00:00 -0700 Subject: [PATCH 14/45] [Plot] Use new API from PlotUpdater Use PlotLine and PlotLineBuffer from PlotUpdater, to enable merging of real-time and historical telemetry. WTD-806. --- .../features/plot/src/elements/PlotLine.js | 90 ++---- .../plot/src/elements/PlotLineBuffer.js | 84 ++++- .../features/plot/src/elements/PlotUpdater.js | 293 +++++++++--------- .../plot/src/modes/PlotOverlayMode.js | 6 +- .../features/plot/src/modes/PlotStackMode.js | 6 +- 5 files changed, 255 insertions(+), 224 deletions(-) diff --git a/platform/features/plot/src/elements/PlotLine.js b/platform/features/plot/src/elements/PlotLine.js index 3c2257c317..3a507868d9 100644 --- a/platform/features/plot/src/elements/PlotLine.js +++ b/platform/features/plot/src/elements/PlotLine.js @@ -6,67 +6,35 @@ define( "use strict"; - function PlotLine(initialSize, maxPoints) { - var buffer, - length = 0, - timeWindow; + function PlotLine(buffer) { - // Binary search the buffer to find the index where - // a point with this timestamp should be inserted. - // After is a flag indicating whether it is preferred - // to insert after or before its nearest timestamp - function searchBuffer(timestamp, after) { - // Binary search for an appropriate insertion index. - function binSearch(min, max) { - var mid = Math.floor((min + max) / 2), - ts; + // Insert a time-windowed data series into the buffer + function insertSeriesWindow(seriesWindow) { + var count = seriesWindow.getPointCount(); - if (max < min) { - return -1; - } + function doInsert() { + var firstTimestamp = buffer.getDomainValue(0), + lastTimestamp = buffer.getDomainValue(count - 1), + startIndex = buffer.findInsertionIndex(firstTimestamp), + endIndex = buffer.findInsertionIndex(lastTimestamp); - ts = buffer[mid * 2]; - - // Check for an exact match... - if (ts === timestamp) { - // This is a case where we'll need to - // split up what we want to insert. - return mid + after ? -1 : 1; + // Does the whole series fit in between two adjacent indexes? + if ((startIndex === endIndex) && startIndex > -1) { + // Insert it in between + buffer.insert(seriesWindow, startIndex); } else { - // Found our index? - if (max === min) { - return max; - } - // Otherwise, search recursively - if (ts < timestamp) { - - } else { - - } + // Split it up, and add the two halves + seriesWindow.split().forEach(insertSeriesWindow); } - } - // Booleanize - after = !!after; - - return binSearch(0, length - 1); - } - - function insertSeriesWindow(seriesWindow) { - var startIndex = findStartIndex(), - endIndex = findEndIndex(); - - if (startIndex === endIndex) { - - } else { - // Split it up, and add the two halves - seriesWindow.split().forEach(insertSeriesWindow); + // Only insert if there are points to insert + if (count > 0) { + doInsert(); } } function createWindow(series, domain, range) { - // TODO: Enforce time window, too! return new PlotSeriesWindow( series, domain, @@ -77,8 +45,20 @@ define( } return { - addData: function (domainValue, rangeValue) { - // Should append to buffer + getLineBuffer: function () { + return buffer; + }, + addPoint: function (domainValue, rangeValue) { + var index = buffer.findInsertionIndex(domainValue); + if (index > -1) { + // Insert the point + if (!buffer.insertPoint(domainValue, rangeValue, index)) { + // If insertion failed, trim from the beginning... + buffer.trim(1); + // ...and try again. + buffer.insertPoint(domainValue, rangeValue, index); + } + } }, addSeries: function (series, domain, range) { // Should try to add via insertion if a @@ -87,12 +67,6 @@ define( // Insertion operation also needs to factor out // redundant timestamps, for overlapping data insertSeriesWindow(createWindow(series, domain, range)); - }, - setTimeWindow: function (start, end) { - timeWindow = [ start, end ]; - }, - clearTimeWindow: function () { - timeWindow = undefined; } }; } diff --git a/platform/features/plot/src/elements/PlotLineBuffer.js b/platform/features/plot/src/elements/PlotLineBuffer.js index 1504fe021c..724347ed0c 100644 --- a/platform/features/plot/src/elements/PlotLineBuffer.js +++ b/platform/features/plot/src/elements/PlotLineBuffer.js @@ -14,6 +14,7 @@ define( */ function PlotLineBuffer(domainOffset, initialSize, maxSize) { var buffer = new Float32Array(initialSize * 2), + rangeExtrema = [ Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY ], length = 0; // Binary search for an insertion index @@ -68,6 +69,17 @@ define( return canHalve; } + // Set a value in the buffer + function setValue(index, domainValue, rangeValue) { + buffer[index * 2] = domainValue - domainOffset; + buffer[index * 2 + 1] = rangeValue; + // Track min/max of range values (min/max for + // domain values can be read directly from buffer) + rangeExtrema = [ + Math.min(rangeExtrema[0], rangeValue), + Math.max(rangeExtrema[1], rangeValue) + ]; + } return { /** @@ -84,6 +96,29 @@ define( getLength: function () { return length; }, + /** + * Get the min/max range values that are currently in this + * buffer. Unlike range extrema, these will change as the + * buffer gets trimmed. + * @returns {number[]} min, max domain values + */ + getDomainExtrema: function () { + // Since these are ordered in the buffer, assume + // these are the values at the first and last index + return [ + buffer[0] + domainOffset, + buffer[length * 2 - 2] + domainOffset + ]; + }, + /** + * Get the min/max range values that have been observed for this + * buffer. Note that these values may have been trimmed out at + * some point. + * @returns {number[]} min, max range values + */ + getRangeExtrema: function () { + return rangeExtrema; + }, /** * Remove values from this buffer. * Normally, values are removed from the start @@ -119,14 +154,13 @@ define( */ insert: function (series, index) { var sz = series.getPointCount(), - free = (buffer.length / 2) - length, i; // Don't allow append after the end; that doesn't make sense index = Math.min(index, length); // Resize if necessary - if (sz > free) { + while (sz > ((buffer.length / 2) - length)) { if (!doubleBufferSize()) { // Can't make room for this, insertion fails return false; @@ -143,10 +177,11 @@ define( // Insert data into the set for (i = 0; i < sz; i += 1) { - buffer[(i + index) * 2] = - series.getDomainValue(i) - domainOffset; - buffer[(i + index) * 2 + 1] = - series.getRangeValue(i); + setValue( + i + index, + series.getDomainValue(i), + series.getRangeValue(i) + ); } // Increase the length @@ -155,6 +190,29 @@ define( // Indicate that insertion was successful return true; }, + /** + * Append a single data point. + */ + insertPoint: function (domainValue, rangeValue, index) { + // Don't allow + index = Math.min(length, index); + + // Ensure there is space for this point + if (length >= (buffer.length / 2)) { + if (!doubleBufferSize()) { + return false; + } + } + + // Put the data in the buffer + setValue(length, domainValue, rangeValue); + + // Update length + length += 1; + + // Indicate that this was successful + return true; + }, /** * Find an index for inserting data with this * timestamp. The second argument indicates whether @@ -166,11 +224,15 @@ define( * @returns {number} the index for insertion (or -1) */ findInsertionIndex: function (timestamp) { - return binSearch( - timestamp - domainOffset, - 0, - length - 1 - ); + var value = timestamp - domainOffset; + + // Handle empty buffer case and check for an + // append opportunity (which is most common case for + // real-time data so is optimized-for) before falling + // back to a binary search for the insertion point. + return (length < 1) ? 0 : + (value > buffer[length * 2 - 2]) ? length : + binSearch(value, 0, length - 1); } }; } diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index 3b9be1d7a3..f39b77437b 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -5,7 +5,8 @@ * the conversion from data API to displayable buffers. */ define( - function () { + ['./PlotLine', './PlotLineBuffer'], + function (PlotLine, PlotLineBuffer) { 'use strict'; var MAX_POINTS = 86400, @@ -17,164 +18,171 @@ define( * Float32Array for each trace, and tracks the boundaries of the * data sets (since this is convenient to do during the same pass). * @constructor - * @param {Telemetry[]} datas telemetry data objects + * @param {TelemetryHandle} handle the handle to telemetry access * @param {string} domain the key to use when looking up domain values * @param {string} range the key to use when looking up range values */ - function PlotUpdater(subscription, domain, range, maxPoints) { - var max = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY], - min = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY], - x, - y, - domainOffset, - buffers = {}, - lengths = {}, - realtimeIndex = {}, - lengthArray = [], - bufferArray = []; + function PlotUpdater(handle, domain, range, maxPoints) { + var ids = [], + lines = {}, + dimensions = [0, 0], + origin = [0, 0], + domainExtrema, + rangeExtrema, + bufferArray = [], + domainOffset; - // Double the size of a Float32Array - function doubleSize(buffer) { - var doubled = new Float32Array(buffer.length * 2); - doubled.set(buffer); // Copy contents of original - return doubled; + // Look up a domain object's id (for mapping, below) + function getId(domainObject) { + return domainObject.getId(); } - // Make sure there is enough space in a buffer to accomodate a - // new point at the specified index. This will updates buffers[id] - // if necessary. - function ensureBufferSize(buffer, id, index) { - // Check if we don't have enough room - if (index > (buffer.length / 2 - 1)) { - // If we don't, can we expand? - if (index < maxPoints) { - // Double the buffer size - buffer = buffers[id] = doubleSize(buffer); - } else { - // Just shift the existing buffer - buffer.set(buffer.subarray(2)); - // Update the realtime index accordingly - realtimeIndex[id] = Math.max(realtimeIndex[id] - 1, 0); - } + // Check if this set of ids matches the current set of ids + // (used to detect if line preparation can be skipped) + function idsMatch(nextIds) { + return nextIds.map(function (id, index) { + return ids[index] === id; + }).reduce(function (a, b) { + return a && b; + }, true); + } + + // Prepare plot lines for this group of telemetry objects + function prepareLines(telemetryObjects) { + var nextIds = telemetryObjects.map(getId), + next = {}; + + // Detect if we already have everything we need prepared + if (ids.length === nextIds.length && idsMatch(nextIds)) { + // Nothing to prepare, move on + return; } - return buffer; - } + // Update list of ids in use + ids = nextIds; - function setData(buffer, id, index, domainValue, rangeValue) { - // Make sure there's data to add, and then add it - if (domainValue !== undefined && rangeValue !== undefined && - (index < 1 || domainValue !== buffer[index * 2 - 2])) { - // Use the first observed domain value as a domainOffset - domainOffset = domainOffset !== undefined ? - domainOffset : domainValue; - // Ensure there is space for the new buffer - buffer = ensureBufferSize(buffer, id, index); - // Account for shifting that may have occurred - index = Math.min(index, maxPoints - 1); - // Update the buffer - buffer[index * 2] = domainValue - domainOffset; - buffer[index * 2 + 1] = rangeValue; - // Update length - lengths[id] = Math.min(index + 1, maxPoints); - // Observe max/min range values - max[1] = Math.max(max[1], rangeValue); - min[1] = Math.min(min[1], rangeValue); - } - return buffer; - } - - // Add data to the plot. - function addData(obj) { - var id = obj.getId(), - index = lengths[id] || 0, - buffer = buffers[id], - domainValue = subscription.getDomainValue(obj, domain), - rangeValue = subscription.getRangeValue(obj, range); - - // If we don't already have a data buffer for that ID, - // make one. - if (!buffer) { - buffer = new Float32Array(INITIAL_SIZE); - buffers[id] = buffer; + // Built up a set of ids. Note that we can only + // create plot lines after our domain offset has + // been determined. + if (domainOffset !== undefined) { + bufferArray = ids.map(function (id) { + var buffer = new PlotLineBuffer( + domainOffset, + INITIAL_SIZE, + maxPoints + ); + next[id] = lines[id] || new PlotLine(buffer); + return buffer; + }); } - // Update the cutoff point for when we started receiving - // realtime data, to aid in clearing historical data later - if (realtimeIndex[id] === undefined) { - realtimeIndex[id] = index; + // If there are no more lines, clear the domain offset + if (Object.keys(lines).length < 1) { + domainOffset = undefined; } - // Put the data in the buffer - return setData( - buffer, - id, - index, - domainValue, - rangeValue - ); + // Update to the current set of lines + lines = next; } - // Update min/max domain values for these objects - function updateDomainExtrema(objects) { - max[0] = Number.NEGATIVE_INFINITY; - min[0] = Number.POSITIVE_INFINITY; - objects.forEach(function (obj) { - var id = obj.getId(), - buffer = buffers[id], - length = lengths[id], - low = buffer[0] + domainOffset, - high = buffer[length * 2 - 2] + domainOffset; - max[0] = Math.max(high, max[0]); - min[0] = Math.min(low, min[0]); - }); + // Initialize the domain offset, based on these observed values + function initializeDomainOffset(values) { + domainOffset = + ((domainOffset === undefined) && (values.length > 0)) ? + (values.reduce(function (a, b) { + return (a || 0) + (b || 0); + }, 0) / values.length) : + domainOffset; } - // Update historical data for this domain object - function setHistorical(domainObject) { - var id = domainObject.getId(), - // Buffer to expand - buffer = buffers[id], - // Index where historical data ends (and realtime begins) - endIndex = realtimeIndex[id] || 0, - // Look up the data series - series = subscription.getSeries(domainObject), - // Get its length - seriesLength = series ? series.getPointCount() : 0, - // Get the current buffer length... - length = lengths[id] || 0, - // As well as the length of the realtime segment - realtimeLength = length - endIndex, - // Determine the new total length of the existing - // realtime + new historical segment. - totalLength = - Math.min(seriesLength + realtimeLength, maxPoints), - seriesFit = Math.max(0, totalLength - realtimeLength); + // Used in the reduce step of updateExtrema + function reduceExtrema(a, b) { + return [ Math.min(a[0], b[0]), Math.max(a[1], b[1]) ]; + } - // Make sure the buffer is big enough + // Convert a domain/range extrema to plot dimensions + function dimensionsOf(extrema) { + return extrema[1] - extrema[0]; + } - // Move the realtime data into the correct position + // Convert a domain/range extrema to a plot origin + function originOf(extrema) { + return extrema[0]; + } - // Insert the historical data before it + // Update dimensions and origin based on extrema of plots + function updateExtrema() { + domainExtrema = bufferArray.map(function (lineBuffer) { + return lineBuffer.getDomainExtrema(); + }).reduce(reduceExtrema); + + rangeExtrema = bufferArray.map(function (lineBuffer) { + return lineBuffer.getRangeExtrema(); + }).reduce(reduceExtrema); + + dimensions = (rangeExtrema[0] === rangeExtrema[1]) ? + [dimensionsOf(domainExtrema), 2.0 ] : + [dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)]; + origin = [originOf(domainExtrema), originOf(rangeExtrema)]; } // Handle new telemetry data function update() { - var objects = subscription.getTelemetryObjects(); - bufferArray = objects.map(addData); - lengthArray = objects.map(function (obj) { - return lengths[obj.getId()]; + var objects = handle.getTelemetryObjects(); + + // Initialize domain offset if necessary + if (domainOffset === undefined) { + initializeDomainOffset(objects.map(function (obj) { + return handle.getDomainValue(obj, domain); + })); + } + + // Make sure lines are available + prepareLines(objects); + + // Add new data + objects.forEach(function (obj, index) { + lines[obj.getId()].addPoint( + handle.getDomainValue(obj, domain), + handle.getRangeValue(obj, range) + ); }); - updateDomainExtrema(objects); + + // Finally, update extrema + updateExtrema(); } - // Prepare buffers and related state for this object - function prepare(telemetryObject) { - var id = telemetryObject.getId(); - lengths[id] = 0; - buffers[id] = new Float32Array(INITIAL_SIZE); - lengthArray.push(lengths[id]); - bufferArray.push(buffers[id]); + // Add historical data for this domain object + function setHistorical(domainObject, series) { + var count = series.getPointCount(), + line; + + // Nothing to do if it's an empty series + if (count < 1) { + return; + } + + // Initialize domain offset if necessary + if (domainOffset === undefined && series) { + initializeDomainOffset([ + series.getDomainValue(0, domain), + series.getDomainValue(count - 1, domain) + ]); + } + + // Make sure lines are available + prepareLines(handle.getTelemetryObjects()); + + // Look up the line for this domain object + line = lines[domainObject.getId()]; + + // ...and put the data into it. + if (line) { + line.addSeries(series, domain, range); + } + + // Finally, update extrema + updateExtrema(); } // Use a default MAX_POINTS if none is provided @@ -183,7 +191,7 @@ define( // Initially prepare state for these objects. // Note that this may be an empty array at this time, // so we also need to check during update cycles. - subscription.getTelemetryObjects().forEach(prepare); + prepareLines(handle.getTelemetryObjects()); return { /** @@ -193,10 +201,7 @@ define( * @returns {number[]} the dimensions which bound this data set */ getDimensions: function () { - // Pad range if necessary - return (max[1] === min[1]) ? - [max[0] - min[0], 2.0 ] : - [max[0] - min[0], max[1] - min[1]]; + return dimensions; }, /** * Get the origin of this data set's boundary. @@ -207,7 +212,7 @@ define( */ getOrigin: function () { // Pad range if necessary - return (max[1] === min[1]) ? [ min[0], min[1] - 1.0 ] : min; + return origin; }, /** * Get the domain offset; this offset will have been subtracted @@ -236,19 +241,9 @@ define( * * @returns {Float32Array[]} the buffers for these traces */ - getBuffers: function () { + getLineBuffers: function () { return bufferArray; }, - /** - * Get the number of points in the buffer with the specified - * index. Buffers are padded to minimize memory allocations, - * so user code will need this information to know how much - * data to plot. - * @returns {number} the number of points in this buffer - */ - getLength: function (index) { - return lengthArray[index] || 0; - }, /** * Update with latest data. */ diff --git a/platform/features/plot/src/modes/PlotOverlayMode.js b/platform/features/plot/src/modes/PlotOverlayMode.js index 342bbcc1c3..093ae07b1b 100644 --- a/platform/features/plot/src/modes/PlotOverlayMode.js +++ b/platform/features/plot/src/modes/PlotOverlayMode.js @@ -34,11 +34,11 @@ define( subplot.setDomainOffset(prepared.getDomainOffset()); // Draw the buffers. Select color by index. - subplot.getDrawingObject().lines = prepared.getBuffers().map(function (buf, i) { + subplot.getDrawingObject().lines = prepared.getLineBuffers().map(function (buf, i) { return { - buffer: buf, + buffer: buf.getBuffer(), color: PlotPalette.getFloatColor(i), - points: prepared.getLength(i) + points: buf.getLength() }; }); diff --git a/platform/features/plot/src/modes/PlotStackMode.js b/platform/features/plot/src/modes/PlotStackMode.js index 5fe3895a61..a92179dcdb 100644 --- a/platform/features/plot/src/modes/PlotStackMode.js +++ b/platform/features/plot/src/modes/PlotStackMode.js @@ -23,7 +23,7 @@ define( }); function plotTelemetryTo(subplot, prepared, index) { - var buffer = prepared.getBuffers()[index]; + var buffer = prepared.getLineBuffers()[index]; // Track the domain offset, used to bias domain values // to minimize loss of precision when converted to 32-bit @@ -33,9 +33,9 @@ define( // Draw the buffers. Always use the 0th color, because there // is one line per plot. subplot.getDrawingObject().lines = [{ - buffer: buffer, + buffer: buffer.getBuffer(), color: PlotPalette.getFloatColor(0), - points: prepared.getLength(index) + points: buffer.getLength() }]; subplot.update(); From 9f390de21382a8ae13b680c003cb3b8d592a5ec0 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 14:04:46 -0700 Subject: [PATCH 15/45] [Plot] Add clarifying comments Add clarifying comments to PlotLine, added to simplify merging of real-time with historical data, WTD-806. --- platform/features/plot/src/elements/PlotLine.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/platform/features/plot/src/elements/PlotLine.js b/platform/features/plot/src/elements/PlotLine.js index 3a507868d9..d5bba0e320 100644 --- a/platform/features/plot/src/elements/PlotLine.js +++ b/platform/features/plot/src/elements/PlotLine.js @@ -45,9 +45,11 @@ define( } return { - getLineBuffer: function () { - return buffer; - }, + /** + * Add a point to this plot line. + * @param {number} domainValue the domain value + * @param {number} rangeValue the range value + */ addPoint: function (domainValue, rangeValue) { var index = buffer.findInsertionIndex(domainValue); if (index > -1) { @@ -60,6 +62,14 @@ define( } } }, + /** + * Add a series of telemetry data to this plot line. + * @param {TelemetrySeries} series the data series + * @param {string} [domain] the key indicating which domain + * to use when looking up data from this series + * @param {string} [range] the key indicating which range + * to use when looking up data from this series + */ addSeries: function (series, domain, range) { // Should try to add via insertion if a // clear insertion point is available; From 5cc89e798316bd9fa5b0f7be0e56a211bd2849ff Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 14:09:39 -0700 Subject: [PATCH 16/45] [Plot] Update PlotController Update plot controller to request historical telemetry, WTD-806. --- platform/features/plot/src/PlotController.js | 36 +++++++++++-------- .../features/plot/src/elements/PlotUpdater.js | 2 +- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/platform/features/plot/src/PlotController.js b/platform/features/plot/src/PlotController.js index 6449e0f3a1..a2f5695f42 100644 --- a/platform/features/plot/src/PlotController.js +++ b/platform/features/plot/src/PlotController.js @@ -30,13 +30,13 @@ define( * * @constructor */ - function PlotController($scope, telemetryFormatter, telemetrySubscriber) { + function PlotController($scope, telemetryFormatter, telemetryHandler) { var subPlotFactory = new SubPlotFactory(telemetryFormatter), modeOptions = new PlotModeOptions([], subPlotFactory), subplots = [], cachedObjects = [], updater, - subscription, + handle, domainOffset; // Populate the scope with axis information (specifically, options @@ -77,7 +77,7 @@ define( // new subscription.) This will clear the plot. function recreateUpdater() { updater = new PlotUpdater( - subscription, + handle, ($scope.axes[0].active || {}).key, ($scope.axes[1].active || {}).key ); @@ -85,8 +85,8 @@ define( // Handle new telemetry data in this plot function updateValues() { - if (subscription) { - setupModes(subscription.getTelemetryObjects()); + if (handle) { + setupModes(handle.getTelemetryObjects()); } if (updater) { updater.update(); @@ -95,29 +95,37 @@ define( update(); } + // Issue a new request for historical telemetry + function requestTelemetry() { + if (handle && updater) { + handle.request({}, updater.addHistorical); + } + } + // Create a new subscription; telemetrySubscriber gets // to do the meaningful work here. function subscribe(domainObject) { - if (subscription) { - subscription.unsubscribe(); + if (handle) { + handle.unsubscribe(); } - subscription = domainObject && telemetrySubscriber.subscribe( + handle = domainObject && telemetryHandler.handle( domainObject, updateValues, true // Lossless ); - if (subscription) { - setupModes(subscription.getTelemetryObjects()); - setupAxes(subscription.getMetadata()); + if (handle) { + setupModes(handle.getTelemetryObjects()); + setupAxes(handle.getMetadata()); recreateUpdater(); + requestTelemetry(); } } // Release the current subscription (called when scope is destroyed) function releaseSubscription() { - if (subscription) { - subscription.unsubscribe(); - subscription = undefined; + if (handle) { + handle.unsubscribe(); + handle = undefined; } } diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index f39b77437b..3e155131e4 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -251,7 +251,7 @@ define( /** * Fill in historical data. */ - setHistorical: setHistorical + addHistorical: setHistorical }; } From 8ba9c0553a745f90bf98c36e918fdde0478be482 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 14:53:21 -0700 Subject: [PATCH 17/45] [Plot] Begin integrating with telemetry handler Miscellaneous tweaks and fixes to begin showing merged real-time and historical telemetry, WTD-806. --- platform/features/plot/bundle.json | 2 +- .../features/plot/src/elements/PlotLine.js | 16 ++++-- .../plot/src/elements/PlotLineBuffer.js | 6 +-- .../plot/src/elements/PlotSeriesWindow.js | 2 + .../features/plot/src/elements/PlotUpdater.js | 53 +++++++++++-------- platform/telemetry/bundle.json | 5 ++ 6 files changed, 54 insertions(+), 30 deletions(-) diff --git a/platform/features/plot/bundle.json b/platform/features/plot/bundle.json index 67c69d9e4b..ab8cf89b7c 100644 --- a/platform/features/plot/bundle.json +++ b/platform/features/plot/bundle.json @@ -22,7 +22,7 @@ { "key": "PlotController", "implementation": "PlotController.js", - "depends": [ "$scope", "telemetryFormatter", "telemetrySubscriber" ] + "depends": [ "$scope", "telemetryFormatter", "telemetryHandler" ] } ] } diff --git a/platform/features/plot/src/elements/PlotLine.js b/platform/features/plot/src/elements/PlotLine.js index d5bba0e320..00a65cbfac 100644 --- a/platform/features/plot/src/elements/PlotLine.js +++ b/platform/features/plot/src/elements/PlotLine.js @@ -13,8 +13,8 @@ define( var count = seriesWindow.getPointCount(); function doInsert() { - var firstTimestamp = buffer.getDomainValue(0), - lastTimestamp = buffer.getDomainValue(count - 1), + var firstTimestamp = seriesWindow.getDomainValue(0), + lastTimestamp = seriesWindow.getDomainValue(count - 1), startIndex = buffer.findInsertionIndex(firstTimestamp), endIndex = buffer.findInsertionIndex(lastTimestamp); @@ -51,8 +51,16 @@ define( * @param {number} rangeValue the range value */ addPoint: function (domainValue, rangeValue) { - var index = buffer.findInsertionIndex(domainValue); - if (index > -1) { + var index; + // Make sure we got real/useful values here... + if (domainValue !== undefined && rangeValue !== undefined) { + index = buffer.findInsertionIndex(domainValue); + + // Already in the buffer? Skip insertion + if (index < 0) { + return; + } + // Insert the point if (!buffer.insertPoint(domainValue, rangeValue, index)) { // If insertion failed, trim from the beginning... diff --git a/platform/features/plot/src/elements/PlotLineBuffer.js b/platform/features/plot/src/elements/PlotLineBuffer.js index 724347ed0c..7511ab4870 100644 --- a/platform/features/plot/src/elements/PlotLineBuffer.js +++ b/platform/features/plot/src/elements/PlotLineBuffer.js @@ -75,10 +75,8 @@ define( buffer[index * 2 + 1] = rangeValue; // Track min/max of range values (min/max for // domain values can be read directly from buffer) - rangeExtrema = [ - Math.min(rangeExtrema[0], rangeValue), - Math.max(rangeExtrema[1], rangeValue) - ]; + rangeExtrema[0] = Math.min(rangeExtrema[0], rangeValue); + rangeExtrema[1] = Math.max(rangeExtrema[1], rangeValue); } return { diff --git a/platform/features/plot/src/elements/PlotSeriesWindow.js b/platform/features/plot/src/elements/PlotSeriesWindow.js index 614c09b525..d8308eb3eb 100644 --- a/platform/features/plot/src/elements/PlotSeriesWindow.js +++ b/platform/features/plot/src/elements/PlotSeriesWindow.js @@ -41,5 +41,7 @@ define( } }; } + + return PlotSeriesWindow; } ); \ No newline at end of file diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index 3e155131e4..815b91b4e5 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -58,13 +58,14 @@ define( return; } - // Update list of ids in use - ids = nextIds; - // Built up a set of ids. Note that we can only // create plot lines after our domain offset has // been determined. if (domainOffset !== undefined) { + // Update list of ids in use + ids = nextIds; + + // Create buffers for these objects bufferArray = ids.map(function (id) { var buffer = new PlotLineBuffer( domainOffset, @@ -112,18 +113,31 @@ define( // Update dimensions and origin based on extrema of plots function updateExtrema() { - domainExtrema = bufferArray.map(function (lineBuffer) { - return lineBuffer.getDomainExtrema(); - }).reduce(reduceExtrema); + if (bufferArray.length > 0) { + domainExtrema = bufferArray.map(function (lineBuffer) { + return lineBuffer.getDomainExtrema(); + }).reduce(reduceExtrema); - rangeExtrema = bufferArray.map(function (lineBuffer) { - return lineBuffer.getRangeExtrema(); - }).reduce(reduceExtrema); + rangeExtrema = bufferArray.map(function (lineBuffer) { + return lineBuffer.getRangeExtrema(); + }).reduce(reduceExtrema); - dimensions = (rangeExtrema[0] === rangeExtrema[1]) ? - [dimensionsOf(domainExtrema), 2.0 ] : - [dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)]; - origin = [originOf(domainExtrema), originOf(rangeExtrema)]; + dimensions = (rangeExtrema[0] === rangeExtrema[1]) ? + [dimensionsOf(domainExtrema), 2.0 ] : + [dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)]; + origin = [originOf(domainExtrema), originOf(rangeExtrema)]; + } + } + + // Add latest data for this domain object + function addPointFor(domainObject) { + var line = lines[domainObject.getId()]; + if (line) { + line.addPoint( + handle.getDomainValue(domainObject, domain), + handle.getRangeValue(domainObject, range) + ); + } } // Handle new telemetry data @@ -134,6 +148,8 @@ define( if (domainOffset === undefined) { initializeDomainOffset(objects.map(function (obj) { return handle.getDomainValue(obj, domain); + }).filter(function (value) { + return typeof value === 'number'; })); } @@ -141,12 +157,7 @@ define( prepareLines(objects); // Add new data - objects.forEach(function (obj, index) { - lines[obj.getId()].addPoint( - handle.getDomainValue(obj, domain), - handle.getRangeValue(obj, range) - ); - }); + objects.forEach(addPointFor); // Finally, update extrema updateExtrema(); @@ -154,7 +165,7 @@ define( // Add historical data for this domain object function setHistorical(domainObject, series) { - var count = series.getPointCount(), + var count = series ? series.getPointCount() : 0, line; // Nothing to do if it's an empty series @@ -163,7 +174,7 @@ define( } // Initialize domain offset if necessary - if (domainOffset === undefined && series) { + if (domainOffset === undefined) { initializeDomainOffset([ series.getDomainValue(0, domain), series.getDomainValue(count - 1, domain) diff --git a/platform/telemetry/bundle.json b/platform/telemetry/bundle.json index f1500a67bf..a93c61bb02 100644 --- a/platform/telemetry/bundle.json +++ b/platform/telemetry/bundle.json @@ -43,6 +43,11 @@ "key": "telemetrySubscriber", "implementation": "TelemetrySubscriber.js", "depends": [ "$q", "$timeout" ] + }, + { + "key": "telemetryHandler", + "implementation": "TelemetryHandler.js", + "depends": [ "$q", "telemetrySubscriber" ] } ], "licenses": [ From 62958280b7748263e3a9d7725ce0eb62df92edeb Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 15:05:56 -0700 Subject: [PATCH 18/45] [Plot] Avoid clearing domain offset Avoid clearing domain offset unnecessarily by fixing variable reference; WTD-806. --- platform/features/plot/src/elements/PlotUpdater.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index 815b91b4e5..d714e8f939 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -78,7 +78,7 @@ define( } // If there are no more lines, clear the domain offset - if (Object.keys(lines).length < 1) { + if (Object.keys(next).length < 1) { domainOffset = undefined; } From 955c4209f0345748657cf87bda142229de2af472 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 15:11:22 -0700 Subject: [PATCH 19/45] [Plot] Add placeholder specs Add placeholder specs for new scripts introduced to support merging real-time and historical telemetry, WTD-806. --- platform/features/plot/test/elements/PlotLineSpec.js | 12 ++++++++++++ .../plot/test/elements/PlotSeriesWindowSpec.js | 12 ++++++++++++ platform/features/plot/test/suite.json | 2 ++ platform/telemetry/test/TelemetryDelegatorSpec.js | 12 ++++++++++++ platform/telemetry/test/TelemetryHandleSpec.js | 12 ++++++++++++ platform/telemetry/test/TelemetryHandlerSpec.js | 12 ++++++++++++ platform/telemetry/test/suite.json | 3 +++ 7 files changed, 65 insertions(+) create mode 100644 platform/features/plot/test/elements/PlotLineSpec.js create mode 100644 platform/features/plot/test/elements/PlotSeriesWindowSpec.js create mode 100644 platform/telemetry/test/TelemetryDelegatorSpec.js create mode 100644 platform/telemetry/test/TelemetryHandleSpec.js create mode 100644 platform/telemetry/test/TelemetryHandlerSpec.js diff --git a/platform/features/plot/test/elements/PlotLineSpec.js b/platform/features/plot/test/elements/PlotLineSpec.js new file mode 100644 index 0000000000..d2cf845bc2 --- /dev/null +++ b/platform/features/plot/test/elements/PlotLineSpec.js @@ -0,0 +1,12 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../../src/elements/PlotLine"], + function (PlotLine) { + "use strict"; + + describe("A plot line", function () { + + }); + } +); \ No newline at end of file diff --git a/platform/features/plot/test/elements/PlotSeriesWindowSpec.js b/platform/features/plot/test/elements/PlotSeriesWindowSpec.js new file mode 100644 index 0000000000..448d031e5c --- /dev/null +++ b/platform/features/plot/test/elements/PlotSeriesWindowSpec.js @@ -0,0 +1,12 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../../src/elements/PlotSeriesWindow"], + function (PlotSeriesWindow) { + "use strict"; + + describe("A plot's window on a telemetry series", function () { + + }); + } +); \ No newline at end of file diff --git a/platform/features/plot/test/suite.json b/platform/features/plot/test/suite.json index 7d099f14da..e3fa3fb796 100644 --- a/platform/features/plot/test/suite.json +++ b/platform/features/plot/test/suite.json @@ -6,12 +6,14 @@ "SubPlot", "SubPlotFactory", "elements/PlotAxis", + "elements/PlotLine", "elements/PlotLineBuffer", "elements/PlotPalette", "elements/PlotPanZoomStack", "elements/PlotPanZoomStackGroup", "elements/PlotPosition", "elements/PlotPreparer", + "elements/PlotSeriesWindow", "elements/PlotTickGenerator", "elements/PlotUpdater", "modes/PlotModeOptions", diff --git a/platform/telemetry/test/TelemetryDelegatorSpec.js b/platform/telemetry/test/TelemetryDelegatorSpec.js new file mode 100644 index 0000000000..9ff80d3c34 --- /dev/null +++ b/platform/telemetry/test/TelemetryDelegatorSpec.js @@ -0,0 +1,12 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../src/TelemetryDelegator"], + function (TelemetryDelegator) { + "use strict"; + + describe("The telemetry delegator", function () { + + }); + } +); diff --git a/platform/telemetry/test/TelemetryHandleSpec.js b/platform/telemetry/test/TelemetryHandleSpec.js new file mode 100644 index 0000000000..b8fc361814 --- /dev/null +++ b/platform/telemetry/test/TelemetryHandleSpec.js @@ -0,0 +1,12 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../src/TelemetryHandle"], + function (TelemetryHandle) { + "use strict"; + + describe("A telemetry handle", function () { + + }); + } +); diff --git a/platform/telemetry/test/TelemetryHandlerSpec.js b/platform/telemetry/test/TelemetryHandlerSpec.js new file mode 100644 index 0000000000..b488f156b8 --- /dev/null +++ b/platform/telemetry/test/TelemetryHandlerSpec.js @@ -0,0 +1,12 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../src/TelemetryHandler"], + function (TelemetryHandler) { + "use strict"; + + describe("The telemetry handler", function () { + + }); + } +); diff --git a/platform/telemetry/test/suite.json b/platform/telemetry/test/suite.json index 0ed5887ac5..8881466572 100644 --- a/platform/telemetry/test/suite.json +++ b/platform/telemetry/test/suite.json @@ -2,7 +2,10 @@ "TelemetryAggregator", "TelemetryCapability", "TelemetryController", + "TelemetryDelegator", "TelemetryFormatter", + "TelemetryHandle", + "TelemetryHandler", "TelemetryQueue", "TelemetrySubscriber", "TelemetrySubscription", From 74b9d68dc8f417f84bc81ae5f90ea32cc5fc4de0 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 16:42:15 -0700 Subject: [PATCH 20/45] [Plot] Update failing specs Update failing specs after changes for WTD-806. --- .../features/plot/src/elements/PlotUpdater.js | 2 +- .../features/plot/test/PlotControllerSpec.js | 41 +++++------ .../plot/test/elements/PlotUpdaterSpec.js | 72 ++----------------- .../plot/test/modes/PlotOverlayModeSpec.js | 22 ++++-- .../plot/test/modes/PlotStackModeSpec.js | 17 +++-- 5 files changed, 55 insertions(+), 99 deletions(-) diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index d714e8f939..80074870cc 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -202,7 +202,7 @@ define( // Initially prepare state for these objects. // Note that this may be an empty array at this time, // so we also need to check during update cycles. - prepareLines(handle.getTelemetryObjects()); + update(); return { /** diff --git a/platform/features/plot/test/PlotControllerSpec.js b/platform/features/plot/test/PlotControllerSpec.js index 865ad2c335..301873ae16 100644 --- a/platform/features/plot/test/PlotControllerSpec.js +++ b/platform/features/plot/test/PlotControllerSpec.js @@ -11,8 +11,8 @@ define( describe("The plot controller", function () { var mockScope, mockFormatter, - mockSubscriber, - mockSubscription, + mockHandler, + mockHandle, mockDomainObject, controller; @@ -30,28 +30,29 @@ define( "domainObject", [ "getId", "getModel", "getCapability" ] ); - mockSubscriber = jasmine.createSpyObj( + mockHandler = jasmine.createSpyObj( "telemetrySubscriber", - ["subscribe"] + ["handle"] ); - mockSubscription = jasmine.createSpyObj( + mockHandle = jasmine.createSpyObj( "subscription", [ "unsubscribe", "getTelemetryObjects", "getMetadata", "getDomainValue", - "getRangeValue" + "getRangeValue", + "request" ] ); - mockSubscriber.subscribe.andReturn(mockSubscription); - mockSubscription.getTelemetryObjects.andReturn([mockDomainObject]); - mockSubscription.getMetadata.andReturn([{}]); - mockSubscription.getDomainValue.andReturn(123); - mockSubscription.getRangeValue.andReturn(42); + mockHandler.handle.andReturn(mockHandle); + mockHandle.getTelemetryObjects.andReturn([mockDomainObject]); + mockHandle.getMetadata.andReturn([{}]); + mockHandle.getDomainValue.andReturn(123); + mockHandle.getRangeValue.andReturn(42); - controller = new PlotController(mockScope, mockFormatter, mockSubscriber); + controller = new PlotController(mockScope, mockFormatter, mockHandler); }); it("provides plot colors", function () { @@ -71,7 +72,7 @@ define( // Make an object available mockScope.$watch.mostRecentCall.args[1](mockDomainObject); // Should have subscribed - expect(mockSubscriber.subscribe).toHaveBeenCalledWith( + expect(mockHandler.handle).toHaveBeenCalledWith( mockDomainObject, jasmine.any(Function), true // Lossless @@ -92,7 +93,7 @@ define( expect(controller.getSubPlots().length > 0).toBeTruthy(); // Broadcast data - mockSubscriber.subscribe.mostRecentCall.args[1](); + mockHandler.handle.mostRecentCall.args[1](); controller.getSubPlots().forEach(function (subplot) { expect(subplot.getDrawingObject().lines) @@ -104,17 +105,17 @@ define( // Make an object available mockScope.$watch.mostRecentCall.args[1](mockDomainObject); // Verify precondition - shouldn't unsubscribe yet - expect(mockSubscription.unsubscribe).not.toHaveBeenCalled(); + expect(mockHandle.unsubscribe).not.toHaveBeenCalled(); // Remove the domain object mockScope.$watch.mostRecentCall.args[1](undefined); // Should have unsubscribed - expect(mockSubscription.unsubscribe).toHaveBeenCalled(); + expect(mockHandle.unsubscribe).toHaveBeenCalled(); }); it("changes modes depending on number of objects", function () { // Act like one object is available - mockSubscription.getTelemetryObjects.andReturn([ + mockHandle.getTelemetryObjects.andReturn([ mockDomainObject ]); @@ -124,7 +125,7 @@ define( expect(controller.getModeOptions().length).toEqual(1); // Act like one object is available - mockSubscription.getTelemetryObjects.andReturn([ + mockHandle.getTelemetryObjects.andReturn([ mockDomainObject, mockDomainObject, mockDomainObject @@ -180,11 +181,11 @@ define( // Make sure $destroy is what's listened for expect(mockScope.$on.mostRecentCall.args[0]).toEqual('$destroy'); // Also verify precondition - expect(mockSubscription.unsubscribe).not.toHaveBeenCalled(); + expect(mockHandle.unsubscribe).not.toHaveBeenCalled(); // Destroy the scope mockScope.$on.mostRecentCall.args[1](); // Should have unsubscribed - expect(mockSubscription.unsubscribe).toHaveBeenCalled(); + expect(mockHandle.unsubscribe).toHaveBeenCalled(); }); }); } diff --git a/platform/features/plot/test/elements/PlotUpdaterSpec.js b/platform/features/plot/test/elements/PlotUpdaterSpec.js index 41be699578..250182922a 100644 --- a/platform/features/plot/test/elements/PlotUpdaterSpec.js +++ b/platform/features/plot/test/elements/PlotUpdaterSpec.js @@ -55,57 +55,14 @@ define( }); it("provides one buffer per telemetry object", function () { - expect(updater.getBuffers().length).toEqual(3); + expect(updater.getLineBuffers().length).toEqual(3); }); it("changes buffer count if telemetry object counts change", function () { mockSubscription.getTelemetryObjects .andReturn([makeMockDomainObject('a')]); updater.update(); - expect(updater.getBuffers().length).toEqual(1); - }); - - it("maintains a buffer of received telemetry", function () { - // Count should be large enough to trigger a buffer resize - var count = 750, - i; - - // Increment values exposed by subscription - function increment() { - Object.keys(testDomainValues).forEach(function (k) { - testDomainValues[k] += 1; - testRangeValues[k] += 1; - }); - } - - // Simulate a lot of telemetry updates - for (i = 0; i < count; i += 1) { - updater.update(); - expect(updater.getLength(0)).toEqual(i + 1); - expect(updater.getLength(1)).toEqual(i + 1); - expect(updater.getLength(2)).toEqual(i + 1); - increment(); - } - - // Domain offset should be lowest domain value - expect(updater.getDomainOffset()).toEqual(3); - - // Test against initial values, offset by count, - // as was the case during each update - for (i = 0; i < count; i += 1) { - expect(updater.getBuffers()[0][i * 2]) - .toEqual(3 + i - 3); - expect(updater.getBuffers()[0][i * 2 + 1]) - .toEqual(123 + i); - expect(updater.getBuffers()[1][i * 2]) - .toEqual(7 + i - 3); - expect(updater.getBuffers()[1][i * 2 + 1]) - .toEqual(456 + i); - expect(updater.getBuffers()[2][i * 2]) - .toEqual(13 + i - 3); - expect(updater.getBuffers()[2][i * 2 + 1]) - .toEqual(789 + i); - } + expect(updater.getLineBuffers().length).toEqual(1); }); it("can handle delayed telemetry object availability", function () { @@ -124,7 +81,7 @@ define( ); // Should have 0 buffers for 0 objects - expect(updater.getBuffers().length).toEqual(0); + expect(updater.getLineBuffers().length).toEqual(0); // Restore the three objects the test subscription would // normally have. @@ -132,30 +89,9 @@ define( updater.update(); // Should have 3 buffers for 3 objects - expect(updater.getBuffers().length).toEqual(3); + expect(updater.getLineBuffers().length).toEqual(3); }); - - it("shifts buffer upon expansion", function () { - // Count should be large enough to hit buffer's max size - var count = 1400, - i; - - // Initial update; should have 3 in first position - // (a's initial domain value) - updater.update(); - expect(updater.getBuffers()[0][1]).toEqual(123); - - // Simulate a lot of telemetry updates - for (i = 0; i < count; i += 1) { - testDomainValues.a += 1; - testRangeValues.a += 1; - updater.update(); - } - - // Value at front of the buffer should have been pushed out - expect(updater.getBuffers()[0][1]).not.toEqual(123); - }); }); } ); \ No newline at end of file diff --git a/platform/features/plot/test/modes/PlotOverlayModeSpec.js b/platform/features/plot/test/modes/PlotOverlayModeSpec.js index 8c2922cf22..ee1be40bfc 100644 --- a/platform/features/plot/test/modes/PlotOverlayModeSpec.js +++ b/platform/features/plot/test/modes/PlotOverlayModeSpec.js @@ -57,18 +57,30 @@ define( // Prepared telemetry data mockPrepared = jasmine.createSpyObj( "prepared", - [ "getDomainOffset", "getOrigin", "getDimensions", "getBuffers", "getLength" ] + [ + "getDomainOffset", + "getOrigin", + "getDimensions", + "getLineBuffers" + ] ); mockSubPlotFactory.createSubPlot.andCallFake(createMockSubPlot); // Act as if we have three buffers full of data - testBuffers = [["a"], ["b"], ["c"]]; - mockPrepared.getBuffers.andReturn(testBuffers); + testBuffers = ['a', 'b', 'c'].map(function (id) { + var mockBuffer = jasmine.createSpyObj( + 'buffer-' + id, + ['getBuffer', 'getLength'] + ); + mockBuffer.getBuffer.andReturn([id]); + mockBuffer.getLength.andReturn(3); + return mockBuffer; + }); + mockPrepared.getLineBuffers.andReturn(testBuffers); mockPrepared.getDomainOffset.andReturn(1234); mockPrepared.getOrigin.andReturn([10, 10]); mockPrepared.getDimensions.andReturn([500, 500]); - mockPrepared.getLength.andReturn(3); // Clear out drawing objects testDrawingObjects = []; @@ -104,7 +116,7 @@ define( // Make sure the right buffer was drawn to the // right subplot. testDrawingObject.lines.forEach(function (line, j) { - expect(line.buffer).toEqual(testBuffers[j]); + expect(line.buffer).toEqual(testBuffers[j].getBuffer()); }); }); }); diff --git a/platform/features/plot/test/modes/PlotStackModeSpec.js b/platform/features/plot/test/modes/PlotStackModeSpec.js index 4c2c11a308..6101d2ccc8 100644 --- a/platform/features/plot/test/modes/PlotStackModeSpec.js +++ b/platform/features/plot/test/modes/PlotStackModeSpec.js @@ -57,18 +57,25 @@ define( // Prepared telemetry data mockPrepared = jasmine.createSpyObj( "prepared", - [ "getDomainOffset", "getOrigin", "getDimensions", "getBuffers", "getLength" ] + [ "getDomainOffset", "getOrigin", "getDimensions", "getLineBuffers" ] ); mockSubPlotFactory.createSubPlot.andCallFake(createMockSubPlot); // Act as if we have three buffers full of data - testBuffers = [["a"], ["b"], ["c"]]; - mockPrepared.getBuffers.andReturn(testBuffers); + testBuffers = ['a', 'b', 'c'].map(function (id) { + var mockBuffer = jasmine.createSpyObj( + 'buffer-' + id, + ['getBuffer', 'getLength'] + ); + mockBuffer.getBuffer.andReturn([id]); + mockBuffer.getLength.andReturn(3); + return mockBuffer; + }); + mockPrepared.getLineBuffers.andReturn(testBuffers); mockPrepared.getDomainOffset.andReturn(1234); mockPrepared.getOrigin.andReturn([10, 10]); mockPrepared.getDimensions.andReturn([500, 500]); - mockPrepared.getLength.andReturn(3); // Objects that will be drawn to in sub-plots testDrawingObjects = []; @@ -104,7 +111,7 @@ define( // Make sure the right buffer was drawn to the // right subplot. expect(testDrawingObject.lines[0].buffer) - .toEqual(testBuffers[i]); + .toEqual(testBuffers[i].getBuffer()); }); }); From ffcd91f88d2db82544b2d246d56c0cd5c2764423 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 16:55:49 -0700 Subject: [PATCH 21/45] [Plot] Add test cases Add test cases to spec for PlotUpdater to reflect changes for merging streaming and historical data. WTD-806. --- .../plot/test/elements/PlotUpdaterSpec.js | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/platform/features/plot/test/elements/PlotUpdaterSpec.js b/platform/features/plot/test/elements/PlotUpdaterSpec.js index 250182922a..9b33f769c9 100644 --- a/platform/features/plot/test/elements/PlotUpdaterSpec.js +++ b/platform/features/plot/test/elements/PlotUpdaterSpec.js @@ -14,6 +14,7 @@ define( testRange, testDomainValues, testRangeValues, + mockSeries, updater; function makeMockDomainObject(id) { @@ -33,6 +34,10 @@ define( "subscription", [ "getDomainValue", "getRangeValue", "getTelemetryObjects" ] ); + mockSeries = jasmine.createSpyObj( + 'series', + ['getPointCount', 'getDomainValue', 'getRangeValue'] + ); testDomain = "testDomain"; testRange = "testRange"; testDomainValues = { a: 3, b: 7, c: 13 }; @@ -92,6 +97,76 @@ define( expect(updater.getLineBuffers().length).toEqual(3); }); + it("accepts historical telemetry updates", function () { + var mockObject = mockSubscription.getTelemetryObjects()[0]; + + mockSeries.getPointCount.andReturn(3); + mockSeries.getDomainValue.andCallFake(function (i) { + return 1000 + i * 1000; + }); + mockSeries.getRangeValue.andReturn(10); + + // PlotLine & PlotLineBuffer are tested for most of the + // details here, so just check for some expected side + // effect; in this case, should see more points in the buffer + expect(updater.getLineBuffers()[0].getLength()).toEqual(1); + updater.addHistorical(mockObject, mockSeries); + expect(updater.getLineBuffers()[0].getLength()).toEqual(4); + }); + + it("clears the domain offset if no objects are present", function () { + mockSubscription.getTelemetryObjects.andReturn([]); + updater.update(); + expect(updater.getDomainOffset()).toBeUndefined(); + }); + + it("handles empty historical telemetry updates", function () { + // General robustness check for when a series is empty + var mockObject = mockSubscription.getTelemetryObjects()[0]; + + mockSeries.getPointCount.andReturn(0); + mockSeries.getDomainValue.andCallFake(function (i) { + return 1000 + i * 1000; + }); + mockSeries.getRangeValue.andReturn(10); + + // PlotLine & PlotLineBuffer are tested for most of the + // details here, so just check for some expected side + // effect; in this case, should see more points in the buffer + expect(updater.getLineBuffers()[0].getLength()).toEqual(1); + updater.addHistorical(mockObject, mockSeries); + expect(updater.getLineBuffers()[0].getLength()).toEqual(1); + }); + + it("can initialize domain offset from historical telemetry", function () { + var tmp = mockSubscription.getTelemetryObjects(); + + mockSubscription.getTelemetryObjects.andReturn([]); + + // Reinstantiate with the empty subscription + updater = new PlotUpdater( + mockSubscription, + testDomain, + testRange + ); + + // Restore subscription, provide some historical data + mockSubscription.getTelemetryObjects.andReturn(tmp); + mockSeries.getPointCount.andReturn(3); + mockSeries.getDomainValue.andCallFake(function (i) { + return 1000 + i * 1000; + }); + mockSeries.getRangeValue.andReturn(10); + + // PlotLine & PlotLineBuffer are tested for most of the + // details here, so just check for some expected side + // effect; in this case, should see more points in the buffer + expect(updater.getDomainOffset()).toBeUndefined(); + updater.addHistorical(tmp[0], mockSeries); + expect(updater.getDomainOffset()).toBeDefined(); + }); + + }); } ); \ No newline at end of file From daabe4312d2fc39d7f46cb68f9fbf0568956f0a6 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 17 Apr 2015 17:04:59 -0700 Subject: [PATCH 22/45] [Licenses] Add copyright notice WTD-1123. --- LICENSES.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/LICENSES.md b/LICENSES.md index 9030203f1f..0772b0c583 100644 --- a/LICENSES.md +++ b/LICENSES.md @@ -1,7 +1,14 @@ # Open MCT Web Licenses -## Apache License +Open MCT Web, Copyright (c) 2009-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 as follows. + +## Apache License Version 2.0, January 2004 From e21cbbe2c6ecbf786ed4aceffe3c056964e4adec Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 20 Apr 2015 11:00:54 -0700 Subject: [PATCH 23/45] [Plot] Add test cases WTD-806 --- .../plot/test/elements/PlotLineSpec.js | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/platform/features/plot/test/elements/PlotLineSpec.js b/platform/features/plot/test/elements/PlotLineSpec.js index d2cf845bc2..41ce6e2dfc 100644 --- a/platform/features/plot/test/elements/PlotLineSpec.js +++ b/platform/features/plot/test/elements/PlotLineSpec.js @@ -6,6 +6,97 @@ define( "use strict"; describe("A plot line", function () { + var mockBuffer, + mockSeries, + testDomainBuffer, + testRangeBuffer, + testSeries, + line; + + beforeEach(function () { + testDomainBuffer = []; + testRangeBuffer = []; + testSeries = []; + + mockBuffer = jasmine.createSpyObj( + 'buffer', + ['findInsertionIndex', 'insert', 'insertPoint'] + ); + mockSeries = jasmine.createSpyObj( + 'series', + ['getPointCount', 'getDomainValue', 'getRangeValue'] + ); + + mockSeries.getPointCount.andCallFake(function () { + return testSeries.length; + }); + mockSeries.getDomainValue.andCallFake(function (i) { + return testSeries[i][0]; + }); + mockSeries.getRangeValue.andCallFake(function (i) { + return testSeries[i][1]; + }); + + // Function like PlotLineBuffer, to aid in testability + mockBuffer.findInsertionIndex.andCallFake(function (v) { + var index = 0; + if (testDomainBuffer.indexOf(v) !== -1) { + return -1; + } + while ((index < testDomainBuffer.length) && + (testDomainBuffer[index] < v)) { + index += 1; + } + return index; + }); + mockBuffer.insert.andCallFake(function (series, index) { + var domains = [], ranges = [], i; + for (i = 0; i < series.getPointCount(); i += 1) { + domains.push(series.getDomainValue(i)); + ranges.push(series.getRangeValue(i)); + } + testDomainBuffer = testDomainBuffer.slice(0, index) + .concat(domains) + .concat(testDomainBuffer.slice(index)); + testRangeBuffer = testRangeBuffer.slice(0, index) + .concat(ranges) + .concat(testRangeBuffer.slice(index)); + return true; + }); + mockBuffer.insertPoint.andCallFake(function (dv, rv, index) { + testDomainBuffer.splice(index, 0, dv); + testRangeBuffer.splice(index, 0, rv); + return true; + }); + + line = new PlotLine(mockBuffer); + }); + + it("allows single point insertion", function () { + line.addPoint(100, 200); + line.addPoint(50, 42); + line.addPoint(150, 12321); + // Should have managed insertion index choices to get to... + expect(testDomainBuffer).toEqual([50, 100, 150]); + expect(testRangeBuffer).toEqual([42, 200, 12321]); + }); + + it("allows series insertion", function () { + testSeries = [ [ 50, 42 ], [ 100, 200 ], [ 150, 12321 ] ]; + line.addSeries(mockSeries); + // Should have managed insertion index choices to get to... + expect(testDomainBuffer).toEqual([50, 100, 150]); + expect(testRangeBuffer).toEqual([42, 200, 12321]); + }); + + it("splits series insertion when necessary", function () { + testSeries = [ [ 50, 42 ], [ 100, 200 ], [ 150, 12321 ] ]; + line.addPoint(75, 1); + line.addSeries(mockSeries); + // Should have managed insertion index choices to get to... + expect(testDomainBuffer).toEqual([50, 75, 100, 150]); + expect(testRangeBuffer).toEqual([42, 1, 200, 12321]); + }); }); } From 4d288950fd0b671c6ddb354f64563d877ef11d0b Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 20 Apr 2015 14:31:29 -0700 Subject: [PATCH 24/45] [Plot] Add test cases Add test cases for plot line management, WTD-806. --- .../plot/src/elements/PlotSeriesWindow.js | 8 +-- .../plot/test/elements/PlotLineBufferSpec.js | 25 ++++++++ .../plot/test/elements/PlotLineSpec.js | 17 ++++- .../test/elements/PlotSeriesWindowSpec.js | 62 +++++++++++++++++++ 4 files changed, 105 insertions(+), 7 deletions(-) diff --git a/platform/features/plot/src/elements/PlotSeriesWindow.js b/platform/features/plot/src/elements/PlotSeriesWindow.js index d8308eb3eb..572c2c419c 100644 --- a/platform/features/plot/src/elements/PlotSeriesWindow.js +++ b/platform/features/plot/src/elements/PlotSeriesWindow.js @@ -14,14 +14,14 @@ define( return end - start; }, getDomainValue: function (index) { - return series.getDomainValue(index - start, domain); + return series.getDomainValue(index + start, domain); }, getRangeValue: function (index) { - return series.getRangeValue(index - start, range); + return series.getRangeValue(index + start, range); }, split: function () { var mid = Math.floor((end + start) / 2); - return end > start ? + return ((end - start) > 1) ? [ new PlotSeriesWindow( series, @@ -34,7 +34,7 @@ define( series, domain, range, - mid + 1, + mid, end ) ] : []; diff --git a/platform/features/plot/test/elements/PlotLineBufferSpec.js b/platform/features/plot/test/elements/PlotLineBufferSpec.js index 3fc7b06c6e..a152ba5a4c 100644 --- a/platform/features/plot/test/elements/PlotLineBufferSpec.js +++ b/platform/features/plot/test/elements/PlotLineBufferSpec.js @@ -85,6 +85,31 @@ define( ).toEqual([ -35, 3, -33, 9, -28, 8, -27, 11]); }); + it("expands buffer when needed to accommodate more data", function () { + var i; + + // Initial underlying buffer should be twice initial size... + // (Since each pair will take up two elements) + expect(buffer.getBuffer().length).toEqual(20); + + // Should be able to insert 6 series of 6 points each + // (After that, we'll hit the test max of 40) + for (i = 1; i < 15; i += 1) { + expect(buffer.insertPoint(i * 10, Math.sin(i), i)) + .toBeTruthy(); + } + + // Buffer should have expanded in the process + expect(buffer.getBuffer().length).toEqual(40); + + // Push to maximum size just to make sure... + for (i = 1; i < 150; i += 1) { + buffer.insertPoint(i * 10, Math.sin(i), i); + } + + expect(buffer.getBuffer().length).toEqual(80); + }); + it("ensures a maximum size", function () { var i; diff --git a/platform/features/plot/test/elements/PlotLineSpec.js b/platform/features/plot/test/elements/PlotLineSpec.js index 41ce6e2dfc..027e628bae 100644 --- a/platform/features/plot/test/elements/PlotLineSpec.js +++ b/platform/features/plot/test/elements/PlotLineSpec.js @@ -20,7 +20,7 @@ define( mockBuffer = jasmine.createSpyObj( 'buffer', - ['findInsertionIndex', 'insert', 'insertPoint'] + ['findInsertionIndex', 'insert', 'insertPoint', 'trim'] ); mockSeries = jasmine.createSpyObj( 'series', @@ -31,10 +31,10 @@ define( return testSeries.length; }); mockSeries.getDomainValue.andCallFake(function (i) { - return testSeries[i][0]; + return (testSeries[i] || [])[0]; }); mockSeries.getRangeValue.andCallFake(function (i) { - return testSeries[i][1]; + return (testSeries[i] || [])[1]; }); // Function like PlotLineBuffer, to aid in testability @@ -98,6 +98,17 @@ define( expect(testRangeBuffer).toEqual([42, 1, 200, 12321]); }); + it("attempts to remove points when insertion fails", function () { + // Verify precondition - normally doesn't try to trim + line.addPoint(1, 2); + expect(mockBuffer.trim).not.toHaveBeenCalled(); + + // But if insertPoint fails, it should trim + mockBuffer.insertPoint.andReturn(false); + line.addPoint(2, 3); + expect(mockBuffer.trim).toHaveBeenCalled(); + }); + }); } ); \ No newline at end of file diff --git a/platform/features/plot/test/elements/PlotSeriesWindowSpec.js b/platform/features/plot/test/elements/PlotSeriesWindowSpec.js index 448d031e5c..ad88874e9a 100644 --- a/platform/features/plot/test/elements/PlotSeriesWindowSpec.js +++ b/platform/features/plot/test/elements/PlotSeriesWindowSpec.js @@ -6,6 +6,68 @@ define( "use strict"; describe("A plot's window on a telemetry series", function () { + var mockSeries, + testSeries, + window; + + beforeEach(function () { + testSeries = [ + [ 0, 42 ], + [ 10, 1 ], + [ 20, 4 ], + [ 30, 9 ], + [ 40, 3 ] + ]; + + mockSeries = jasmine.createSpyObj( + 'series', + ['getPointCount', 'getDomainValue', 'getRangeValue'] + ); + + mockSeries.getPointCount.andCallFake(function () { + return testSeries.length; + }); + mockSeries.getDomainValue.andCallFake(function (i) { + return testSeries[i][0]; + }); + mockSeries.getRangeValue.andCallFake(function (i) { + return testSeries[i][1]; + }); + + window = new PlotSeriesWindow( + mockSeries, + "testDomain", + "testRange", + 1, + testSeries.length + ); + }); + + it("provides a window upon a data series", function () { + expect(window.getPointCount()).toEqual(4); + expect(window.getDomainValue(0)).toEqual(10); + expect(window.getRangeValue(0)).toEqual(1); + }); + + it("looks up using specific domain/range keys", function () { + window.getDomainValue(0); + window.getRangeValue(0); + expect(mockSeries.getDomainValue) + .toHaveBeenCalledWith(1, 'testDomain'); + expect(mockSeries.getRangeValue) + .toHaveBeenCalledWith(1, 'testRange'); + }); + + it("can be split into smaller windows", function () { + var windows = window.split(); + expect(windows.length).toEqual(2); + expect(windows[0].getPointCount()).toEqual(2); + expect(windows[1].getPointCount()).toEqual(2); + expect(windows[0].getDomainValue(0)).toEqual(10); + expect(windows[1].getDomainValue(0)).toEqual(30); + expect(windows[0].getRangeValue(0)).toEqual(1); + expect(windows[1].getRangeValue(0)).toEqual(9); + }); }); } From ffc122fb5c540242d683dc4a91e34723afd3be86 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 20 Apr 2015 16:51:08 -0700 Subject: [PATCH 25/45] [Telemetry] Add test casese Add test cases for general support for handling real-time telemetry, WTD-806. --- platform/telemetry/src/TelemetryHandle.js | 10 +++ .../telemetry/test/TelemetryHandleSpec.js | 76 +++++++++++++++++++ .../telemetry/test/TelemetryHandlerSpec.js | 52 +++++++++++++ .../test/TelemetrySubscriptionSpec.js | 8 ++ 4 files changed, 146 insertions(+) diff --git a/platform/telemetry/src/TelemetryHandle.js b/platform/telemetry/src/TelemetryHandle.js index eef57caba6..fd5d7582f9 100644 --- a/platform/telemetry/src/TelemetryHandle.js +++ b/platform/telemetry/src/TelemetryHandle.js @@ -5,10 +5,20 @@ define( function () { "use strict"; + /** + * A telemetry handle acts as a helper in issuing requests for + * new telemetry as well as subscribing to real-time updates + * for the same telemetry series. This is exposed through the + * `telemetryHandler` service. + * @param $q Angular's $q, for promises + * @param {TelemetrySubscription} subscription a subscription + * to supplied telemetry + */ function TelemetryHandle($q, subscription) { var seriesMap = {}, self = Object.create(subscription); + // Request a telemetry series for this specific object function requestSeries(telemetryObject, request, callback) { var id = telemetryObject.getId(), telemetry = telemetryObject.getCapability('telemetry'); diff --git a/platform/telemetry/test/TelemetryHandleSpec.js b/platform/telemetry/test/TelemetryHandleSpec.js index b8fc361814..c7db9ac835 100644 --- a/platform/telemetry/test/TelemetryHandleSpec.js +++ b/platform/telemetry/test/TelemetryHandleSpec.js @@ -6,7 +6,83 @@ define( "use strict"; describe("A telemetry handle", function () { + var mockQ, + mockSubscription, + mockDomainObject, + mockTelemetry, + mockSeries, + mockCallback, + handle; + function asPromise(v) { + return (v || {}).then ? v : { + then: function (callback) { + return asPromise(callback(v)); + } + }; + } + + beforeEach(function () { + mockQ = jasmine.createSpyObj('$q', ['when', 'all']); + mockSubscription = jasmine.createSpyObj( + 'subscription', + ['unsubscribe', 'getTelemetryObjects', 'promiseTelemetryObjects'] + ); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + ['getId', 'getCapability'] + ); + mockTelemetry = jasmine.createSpyObj( + 'telemetry', + ['requestData'] + ); + mockSeries = jasmine.createSpyObj( + 'series', + ['getPointCount', 'getDomainValue', 'getRangeValue'] + ); + mockCallback = jasmine.createSpy('callback'); + + // Simulate $q.all, at least for asPromise-provided promises + mockQ.all.andCallFake(function (values) { + return values.map(function (v) { + var r; + asPromise(v).then(function (value) { r = value; }); + return r; + }); + }); + mockQ.when.andCallFake(asPromise); + mockSubscription.getTelemetryObjects + .andReturn([mockDomainObject]); + mockSubscription.promiseTelemetryObjects + .andReturn(asPromise([mockDomainObject])); + mockDomainObject.getId.andReturn('testId'); + mockDomainObject.getCapability.andReturn(mockTelemetry); + mockTelemetry.requestData.andReturn(asPromise(mockSeries)); + + handle = new TelemetryHandle(mockQ, mockSubscription); + }); + + it("exposes subscription API", function () { + // Should still expose methods from the provided subscription + expect(handle.unsubscribe) + .toBe(mockSubscription.unsubscribe); + expect(handle.getTelemetryObjects) + .toBe(mockSubscription.getTelemetryObjects); + }); + + it("provides an interface for historical requests", function () { + handle.request({}, mockCallback); + expect(mockCallback).toHaveBeenCalledWith( + mockDomainObject, + mockSeries + ); + }); + + it("provides the latest series for domain objects", function () { + handle.request({}); + expect(handle.getSeries(mockDomainObject)) + .toEqual(mockSeries); + }); }); } ); diff --git a/platform/telemetry/test/TelemetryHandlerSpec.js b/platform/telemetry/test/TelemetryHandlerSpec.js index b488f156b8..be0588a0e9 100644 --- a/platform/telemetry/test/TelemetryHandlerSpec.js +++ b/platform/telemetry/test/TelemetryHandlerSpec.js @@ -6,6 +6,58 @@ define( "use strict"; describe("The telemetry handler", function () { + // TelemetryHandler just provides a factory + // for TelemetryHandle, so most real testing + // should happen there. + var mockQ, + mockSubscriber, + mockDomainObject, + mockCallback, + mockSubscription, + handler; + + beforeEach(function () { + mockQ = jasmine.createSpyObj("$q", ["when"]); + mockSubscriber = jasmine.createSpyObj( + 'telemetrySubscriber', + ['subscribe'] + ); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + ['getId', 'getCapability'] + ); + mockCallback = jasmine.createSpy('callback'); + mockSubscription = jasmine.createSpyObj( + 'subscription', + [ + 'unsubscribe', + 'getTelemetryObjects', + 'getRangeValue', + 'getDomainValue' + ] + ); + + mockSubscriber.subscribe.andReturn(mockSubscription); + + handler = new TelemetryHandler(mockQ, mockSubscriber); + }); + + it("acts as a factory for subscription objects", function () { + var handle = handler.handle( + mockDomainObject, + mockCallback + ); + // Just verify that this looks like a TelemetrySubscription + [ + "unsubscribe", + "getTelemetryObjects", + "getRangeValue", + "getDomainValue", + "request" + ].forEach(function (method) { + expect(handle[method]).toEqual(jasmine.any(Function)); + }); + }); }); } diff --git a/platform/telemetry/test/TelemetrySubscriptionSpec.js b/platform/telemetry/test/TelemetrySubscriptionSpec.js index 6dd27320c9..057aa97748 100644 --- a/platform/telemetry/test/TelemetrySubscriptionSpec.js +++ b/platform/telemetry/test/TelemetrySubscriptionSpec.js @@ -184,6 +184,14 @@ define( it("fires callback when telemetry objects are available", function () { expect(mockCallback.calls.length).toEqual(1); }); + + it("exposes a promise for telemetry objects", function () { + var mockCallback2 = jasmine.createSpy('callback'); + subscription.promiseTelemetryObjects().then(mockCallback2); + + expect(mockCallback2) + .toHaveBeenCalledWith([ mockDomainObject ]); + }); }); } ); \ No newline at end of file From 6400a670fab8e7015689276df5123355b482729b Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 20 Apr 2015 17:00:30 -0700 Subject: [PATCH 26/45] [Plot] Update plot with historical data Trigger update of displayed plot data when new historical data becomes available, WTD-806. --- platform/features/plot/src/PlotController.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/platform/features/plot/src/PlotController.js b/platform/features/plot/src/PlotController.js index a2f5695f42..046a9f4fb9 100644 --- a/platform/features/plot/src/PlotController.js +++ b/platform/features/plot/src/PlotController.js @@ -95,10 +95,17 @@ define( update(); } + // Display new historical data as it becomes available + function addHistoricalData(domainObject, series) { + updater.addHistorical(domainObject, series); + modeOptions.getModeHandler().plotTelemetry(updater); + update(); + } + // Issue a new request for historical telemetry function requestTelemetry() { if (handle && updater) { - handle.request({}, updater.addHistorical); + handle.request({}, addHistoricalData); } } From 186ae05686485aaa7f3b40d1059441cc87e687f9 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 20 Apr 2015 17:04:34 -0700 Subject: [PATCH 27/45] [Plot] Add test case Add test case which exercises handling of historical telemetry, WTD-806. --- platform/features/plot/test/PlotControllerSpec.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/platform/features/plot/test/PlotControllerSpec.js b/platform/features/plot/test/PlotControllerSpec.js index 301873ae16..846bac452f 100644 --- a/platform/features/plot/test/PlotControllerSpec.js +++ b/platform/features/plot/test/PlotControllerSpec.js @@ -14,6 +14,7 @@ define( mockHandler, mockHandle, mockDomainObject, + mockSeries, controller; @@ -45,6 +46,10 @@ define( "request" ] ); + mockSeries = jasmine.createSpyObj( + 'series', + ['getPointCount', 'getDomainValue', 'getRangeValue'] + ); mockHandler.handle.andReturn(mockHandle); mockHandle.getTelemetryObjects.andReturn([mockDomainObject]); @@ -175,6 +180,15 @@ define( expect(controller.isRequestPending()).toBeFalsy(); }); + it("requests historical telemetry", function () { + mockScope.$watch.mostRecentCall.args[1](mockDomainObject); + expect(mockHandle.request).toHaveBeenCalled(); + mockHandle.request.mostRecentCall.args[1]( + mockDomainObject, + mockSeries + ); + }); + it("unsubscribes when destroyed", function () { // Make an object available mockScope.$watch.mostRecentCall.args[1](mockDomainObject); From 6e24052a3d234ed33a300637eeca977c3cfebfe3 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 23 Apr 2015 14:04:03 -0700 Subject: [PATCH 28/45] [Example] Add iframe example plugin Add example plugin with iframe; relates to WTD-1030, generally. --- example/iframe/bundle.json | 33 ++++++++++++++++++++++++++ example/iframe/res/iframe.html | 4 ++++ example/iframe/src/IFrameController.js | 19 +++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 example/iframe/bundle.json create mode 100644 example/iframe/res/iframe.html create mode 100644 example/iframe/src/IFrameController.js diff --git a/example/iframe/bundle.json b/example/iframe/bundle.json new file mode 100644 index 0000000000..3b4d658146 --- /dev/null +++ b/example/iframe/bundle.json @@ -0,0 +1,33 @@ +{ + "extensions": { + "types": [ + { + "key": "example.page", + "name": "Page", + "features": [ "creation" ], + "properties": [ + { + "key": "url", + "name": "URL", + "control": "textfield" + } + ] + } + ], + "views": [ + { + "templateUrl": "iframe.html", + "name": "Page", + "type": "example.page", + "key": "example.page" + } + ], + "controllers": [ + { + "key": "IFrameController", + "implementation": "IFrameController.js", + "depends": ["$sce"] + } + ] + } +} \ No newline at end of file diff --git a/example/iframe/res/iframe.html b/example/iframe/res/iframe.html new file mode 100644 index 0000000000..ee2d985948 --- /dev/null +++ b/example/iframe/res/iframe.html @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/example/iframe/src/IFrameController.js b/example/iframe/src/IFrameController.js new file mode 100644 index 0000000000..94f78d47e7 --- /dev/null +++ b/example/iframe/src/IFrameController.js @@ -0,0 +1,19 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + function Controller($sce) { + return { + trust: function (url) { + return $sce.trustAsResourceUrl(url); + } + }; + } + + return Controller; + } + +); \ No newline at end of file From 01e2840aaefa941da3ff69539eb2657a3190a573 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 27 Apr 2015 14:46:33 -0700 Subject: [PATCH 29/45] [Pages] Move page plugin into platform Move Page plugin from prototype location into platform, WTD-1145. --- {example/iframe => platform/features/pages}/bundle.json | 0 {example/iframe => platform/features/pages}/res/iframe.html | 0 .../iframe => platform/features/pages}/src/IFrameController.js | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {example/iframe => platform/features/pages}/bundle.json (100%) rename {example/iframe => platform/features/pages}/res/iframe.html (100%) rename {example/iframe => platform/features/pages}/src/IFrameController.js (100%) diff --git a/example/iframe/bundle.json b/platform/features/pages/bundle.json similarity index 100% rename from example/iframe/bundle.json rename to platform/features/pages/bundle.json diff --git a/example/iframe/res/iframe.html b/platform/features/pages/res/iframe.html similarity index 100% rename from example/iframe/res/iframe.html rename to platform/features/pages/res/iframe.html diff --git a/example/iframe/src/IFrameController.js b/platform/features/pages/src/IFrameController.js similarity index 100% rename from example/iframe/src/IFrameController.js rename to platform/features/pages/src/IFrameController.js From 012fad6984de3765fcc136c947bbd39541d2f0b2 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 27 Apr 2015 14:50:14 -0700 Subject: [PATCH 30/45] [Pages] Validate input Valid URL input for Pages, WTD-1145. --- platform/features/pages/bundle.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/features/pages/bundle.json b/platform/features/pages/bundle.json index 3b4d658146..f1d559dde7 100644 --- a/platform/features/pages/bundle.json +++ b/platform/features/pages/bundle.json @@ -9,7 +9,9 @@ { "key": "url", "name": "URL", - "control": "textfield" + "control": "textfield", + "pattern": "^(ftp|https?)\\:\\/\\/\\w+(\\.\\w+)*(\\:\\d+)?(\\/\\S*)*$", + "required": true } ] } From cab259415d6cfb2c74225228233ba0064b0262ae Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 27 Apr 2015 14:58:32 -0700 Subject: [PATCH 31/45] [Pages] Test controller Test controller for WTD-1145. --- ...ontroller.js => EmbeddedPageController.js} | 4 +- .../pages/test/EmbeddedPageControllerSpec.js | 37 +++++++++++++++++++ platform/features/pages/test/suite.json | 3 ++ 3 files changed, 42 insertions(+), 2 deletions(-) rename platform/features/pages/src/{IFrameController.js => EmbeddedPageController.js} (73%) create mode 100644 platform/features/pages/test/EmbeddedPageControllerSpec.js create mode 100644 platform/features/pages/test/suite.json diff --git a/platform/features/pages/src/IFrameController.js b/platform/features/pages/src/EmbeddedPageController.js similarity index 73% rename from platform/features/pages/src/IFrameController.js rename to platform/features/pages/src/EmbeddedPageController.js index 94f78d47e7..31ce5c69e3 100644 --- a/platform/features/pages/src/IFrameController.js +++ b/platform/features/pages/src/EmbeddedPageController.js @@ -5,7 +5,7 @@ define( function () { "use strict"; - function Controller($sce) { + function EmbeddedPageController($sce) { return { trust: function (url) { return $sce.trustAsResourceUrl(url); @@ -13,7 +13,7 @@ define( }; } - return Controller; + return EmbeddedPageController; } ); \ No newline at end of file diff --git a/platform/features/pages/test/EmbeddedPageControllerSpec.js b/platform/features/pages/test/EmbeddedPageControllerSpec.js new file mode 100644 index 0000000000..ae2d572b30 --- /dev/null +++ b/platform/features/pages/test/EmbeddedPageControllerSpec.js @@ -0,0 +1,37 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../src/EmbeddedPageController"], + function (EmbeddedPageController) { + "use strict"; + + describe("The controller for embedded pages", function () { + var mockSCE, + controller; + + beforeEach(function () { + mockSCE = jasmine.createSpyObj( + '$sce', + ["trustAsResourceUrl"] + ); + + mockSCE.trustAsResourceUrl.andCallFake(function (v) { + return v; + }); + + controller = new EmbeddedPageController(mockSCE) + }); + + it("allows URLs to be marked as trusted", function () { + var testURL = "http://www.nasa.gov"; + + expect(controller.trust(testURL)) + .toEqual(testURL); + + expect(mockSCE.trustAsResourceUrl) + .toHaveBeenCalledWith(testURL); + }); + + }); + } +); diff --git a/platform/features/pages/test/suite.json b/platform/features/pages/test/suite.json new file mode 100644 index 0000000000..0d0efc8302 --- /dev/null +++ b/platform/features/pages/test/suite.json @@ -0,0 +1,3 @@ +[ + "EmbeddedPageController" +] \ No newline at end of file From 58c2bb701e4fda7b3d2e2132bf323b1ffa32cdb1 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 27 Apr 2015 15:17:44 -0700 Subject: [PATCH 32/45] [Pages] Add bundle to active set Add Pages to set of active bundles, WTD-1145. --- bundles.json | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles.json b/bundles.json index 4aba0715b1..04515b1897 100644 --- a/bundles.json +++ b/bundles.json @@ -10,6 +10,7 @@ "platform/containment", "platform/telemetry", "platform/features/layout", + "platform/features/pages", "platform/features/plot", "platform/features/scrolling", "platform/forms", From 582a236d8ac487b3dbe534394682a9fd38ce7d43 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Tue, 28 Apr 2015 14:28:17 -0700 Subject: [PATCH 33/45] [Frontend] Fixes, glyph and description updates WTD-1150 WTD-1145 Fixed incorrect references to iFrameController to EmbeddedPageController; Added new glyph for Page; Renamed Page to Web Page; --- bundles.json | 2 +- .../general/res/fonts/symbols/wtdsymbols.eot | Bin 9880 -> 9952 bytes .../general/res/fonts/symbols/wtdsymbols.svg | 12 +++++++----- .../general/res/fonts/symbols/wtdsymbols.ttf | Bin 9716 -> 9788 bytes .../general/res/fonts/symbols/wtdsymbols.woff | Bin 6892 -> 6948 bytes platform/features/layout/bundle.json | 4 ++-- platform/features/pages/bundle.json | 8 +++++--- platform/features/pages/res/iframe.html | 2 +- 8 files changed, 16 insertions(+), 12 deletions(-) diff --git a/bundles.json b/bundles.json index 04515b1897..26e684cdef 100644 --- a/bundles.json +++ b/bundles.json @@ -15,7 +15,7 @@ "platform/features/scrolling", "platform/forms", "platform/persistence/queue", - "platform/persistence/elastic", + "platform/persistence/couch", "platform/policy", "example/generator" diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot index f704abcc2a3b8a339305331787fd94fea9164c29..6caed6eec3d0a1b06ed8d53b74d0b8b90e5064cc 100644 GIT binary patch delta 650 zcmXw1Ur3Wt6hG(Q@0+vl+icF(B&mI?tp}sFX>&}2DJu&!Xl5=9a@BMX>V}Tem-MBg z@dOvo{Dd*W&uDk` zrY|Nup51W(=@Pcv zKDG+L)&Rvc@5HadEOnqwbV)DqwD8OIVpOtXJDmdJzI?U%hZ0aOC=<$*GOKQ*O#YdShs74J!a7whenz2R**GaC}%Ef0_i|}jN zCT4WX)+ovfG&rFHe){UIyp0E P=$4Py?-_bN8Cm}a1O%WF delta 552 zcmaFhJHwZ4h8hFI7uAVuW-K<|zp5uXlt?_E9?8YPpn8LWfkDRI#np|Wg@Fyo-!buw zM7`YJ=ll!|%pwd7jJMJgiwpk$XJ7`(Z2+p}NYANEd)!^1&cMLBfPq2!Lq=+13hTA= zvw?DVfY>YpD8TN{bQ7o-sGmV4Be$gDGxPt8K>iLOj>*YSP88Rb%VA((`2dvPkegUh zz%ZX71Sn?#(3JS^eN0t}3k4Ow+2Phyz@r0pj+uy8YmOfF>Ip#F{9j602c z4);3l13Y{{sKw*KlfW~9XC2QCo0yc>8Q@c!WavDt@hH9I5!<{!M@Sy+KV%JgXR z7oi3w5z)t7kOu>HTErx+`3|A5@j$iT$F#B_)0 z?q(|)Ru*2MPnq5U-KfZ5!4NRnN5Osa6a_yfE|tko6?`V^E0%43srZGNF?{kp)s=#K x7?~KESv~X&42-~};p91Lvg%<#BZ1ZdjfEHwBu{qc8-h4KciTV_2MoJpTL904lU)D+ diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg index 256d5b9276..47dc61951f 100644 --- a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg +++ b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg @@ -2,7 +2,7 @@ -Created by FontForge 20110222 at Thu Mar 19 18:28:43 2015 +Created by FontForge 20090622 at Tue Apr 28 20:52:23 2015 By deploy user Copyright 2015 Adobe Systems Incorporated. All rights reserved. @@ -21,7 +21,7 @@ Copyright 2015 Adobe Systems Incorporated. All rights reserved. bbox="-33 -19 1143 787" underline-thickness="50" underline-position="-50" - unicode-range="U+0020-2044" + unicode-range="U+0020-U+2044" /> @@ -129,7 +129,7 @@ v-356zM635 88v356h-83v-356h83z" /> +d="M806 0h-178l-228 265q-19 -6 -41 -6q-65 0 -111 47t-46 112q0 34 12 63l-214 251h178l139 -162q23 5 42 5q65 0 112 -46t47 -111q0 -30 -14 -65zM286 418q0 -31 21 -53t52 -22t53 22t22 53t-22 52t-53 21t-52 -21t-21 -52z" /> @@ -185,7 +185,7 @@ v-170z" /> +d="M394 202l-394 377h787z" /> +q11 12 28 12q18 0 29 -12q12 -12 12 -29q0 -14 -12 -28l-41 -41q3 0 7 -0.5t5 -0.5q29 0 50 22z" /> + Jwm6*go!|HS&iDO(oUOAJwUSkgd^UJ0&Wj zvC*;ZKQKf1HSO+!p&rfUDy3njE#hCrw6QqMK?iY-gq1-prY97(2%xTzi01gv$mr0C z!BrAaO#HX3IfPT?#oV}>-?R;)@PqQOtj|yXk8FPW0ebTy*~$pCaX;>1A8%r{sPX|; zg-$-pylCMcm^aVOHUU@~kXwW<+~B=Jr(h#AWvbyD!YsDaElBP=D0=vw>1OqB(IZ|H zABhX%l5~JprPLx_k!GX?X+_$VhvgUYN7=ACtPX(WocVb!_9a8|Uz1s-Iq!ID!6Ykm zBvS>C1#~8R9r@-GM%GZpyT)~)Fook}0>J_n_L;3FAC{O+vYTGMC7miX!X=(43t}Bl zmz@_xTLC|*g!p}Bw3%hxvNTm$fj$pyYLPwXuv%Vu%vRq}r&9khyb<;p)xNeiAD%&{ zvCcM)J3q7VlP_$nC-;>d0V8~;;#tu!EFnX|+Ns;DZr_Az?Wo>cM)kK~}58^=)!E-#NeUch2woHyv5)83P0W z4RXLC8Vbcutvt9;58zoO@80mqt`OV+2jLflU1xe*{nwMu?*Mq7a9<*ASk9L#Isk7Z z-^T%C$f{IG2|NYjw+y64`@f7^UI2L#ps8dgjVto|w~vS`(r!1+rQtX4Gx8 z4yfH~pE{vVsf+5mmemT{C#|fN?Y#V~4s|=G{gz~EGW#r|4LR1h%YN7JkfXy+?bhY} z5_J>Z4he5>Wckjc8-HHoXy&fUtiRY7F=W_4`T HxkTrGdE1lf diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff index 767f990e67a9bf0124d6ce0f28f022fb57ec4dd4..8a4b634718466824d978e61081578be443e76c7d 100644 GIT binary patch delta 6337 zcmZu#RZyHwuw8-%vN$2=La^Wo8r&BM7F>h7ThPDVN7^y%rTsd<_s?tC7=T?q;WA%PG^MhgP}7wHB6^Z%bgp=t^U9R{LC{vWoA z0S#$MDJTd;x`)Ui2vC83fY6jw**QQUqZlf*{K%fUy2Fgjdx@2e(bM;*@A&=w^S@^Y?ctOk{(9JG^wIA?9?IaM|LyU*$%^pz zFW5jxMp?FBiQ*B`;UawyM%r(IASfK7N922*&FB5rk-}81{mzm2ww_2Y_g90|n=1M@uzmvQpVM12Jsg{* z%gk5?k3oXvkrlD6`9$#!B{xTje}Kfy5CsRobDZK}HCLHQ9c8zgC3WL$(lH38bYfM9 z(b;^=yAjuq&hp4(+u&9@N+(~#EV>fTFLqbO_x>yRm^QjPB>OyV&ECM)fS-cwlIlB# zm!iWZ0Rnj}R7A_NOBfLbW$AKHYh8X`N`>ojD@~Wq=t&UO5Y*t>_KMSX17pAidikgt zZW{etd|>0&9*kAS);h$u>KV7IFy`(@*gotZRFdqh)2+*=UWN5VfhWE3!`TK+Cl{WE zmPv+!+x%{{$(YBc1Wssic%=$jCJGs?e7t&}eRUrtIyocHSj*6GS?eH7a41huM*d`5 z(S93Rx&4W4TYPL=(#=!=uzy}*m9?^ZMB@sxPVDfzue~0~XX4KYm)F&BQ?%D_Z_BO@ zF@Bd*SKgXCsjRLLaZ+AUR+LjylJYvK1^&zO=C_!Irn?fm&p+e$=oox$E3u#Tw$bJC+G#@#r>a&#%&?{02f#sSjJe%SfyC4uP_md z{*~k_y;tF{N?-kXbqjU|mxIT^PuQ=qU;lTjKeVwtRMqQ+ZL)Y6tfuO$obeavtu6%> zrg*F-x30vpR26RrTMp`-|gQwppW}fL=^7eZ+iA#lA}>U6tqSPPq}lOq0vW zzZsrgr>6$El6P$!Mh;u@xF5&mqh>TUFnDMA{9Um)hR zCY2@rH8f%B>>!6ll6#|%B!ZHI<(U5&)GD1ff53{ItKMDy4 zdd#Pzem8X_MJah@%BfYK65KzRxD7+oBa!}u(Ir^8qW1V2s&o?g?Yh6~1Na@kQd z`2#e1{G0O?>(_$ASbaYxF;l6so`^2)-0s&e%;IztS*%i8Sum|-@>VCSLKA%jJm)WD zS>=-6>%8=hFeRGY;4%z;H5dZOYxF%x_iNOC5QsFJ>|l=SITeb0kl$^L&gTty8e#7U zGyq)ySx}csc2v(ZUK5;-D9mNKz^iLDLeC=P+ROns6{PmNDWjO zPrU{WFP3e`I#Xi4TjD%Vad3)-60LAf(g!@;Ou642=bav}Djh%? zN^S(Rd`0v#(h*wr|d?!8-i)ZCgS+z81Cc@11e?7FILPlV>~aHAA)| zduCXp-rZCEvQluj-Y0BR5r;;7Y_tgy#RQveN4-0mhyCMfMrDtl^UbRYVuSpmnvQIE zLS~9^My8dWnJLji#x0n{Zu_uWoFCZq`NXI6jCHV2elJkGJsIEBK)1^w9JGr=tcD{c zWiPifKDCLf(25f*yaxgZdSB;C3IwxyE-7!cN8RG7))(H0B_t{Qg3DY zpjm~~Ae9-`D6=K<}L`=3L-V2p%;iGjn1#i;hHq1l`$v;GIb#4p^O<2Sp!! zbaEuf3**a~;;^3K2Ys&j_8K&f-G^aLO>;%IoIASh1zotR&sP5@2A`Og1*bVnIO*Y~ zLYz3;eKifan`kgI*IVF*nxj`X9dhF*JC|CV7xr9b7Qe^u2DDQZ8-6qAdHMAw|dW5SJTFZSYBon z??+$QIoxLV&2-r7bsNkAx-WJgh0Ji&x3q>SHL{sta{U)qIza1SjRz+)duIbzgj|c) z`N8g^ynfN#JGimfajlqx+aVq73zd-T#+v4MpM@as+Lv3jzl|KgTYv!Rl=o3_3~@FI zIbJr6X0@IqsQ+#@O7u_f$qlN{>_qJ5okusD;5~AEjsWLAZc00)1oO;S3kJ;C& z_F3E$uC2Mbtsh94Si!RqNUO}3`ivReha-@&RPOWZ$7sc5)QKK=U?fIzR9zrPZ;^`k9N(diLYd!eS;6@jK3?US)LYmrsDg5m&Nf>QpVm-V zbasIdol;j@Ll+Nb?85H%Tb#4~drNhSG-vDU!B?vnlX4ALX}V?B3k%kziA%7(f+Q&W z*IOX?D`)W>%4UZaneWuZ??03_RYr=awv)zw1?|<3<>`2{p z-f`FM{Yp>v*Pz(ev#9hSL!96kkF}D@OJIi#aH_!Xg=$+4U^+>?RH_xG*nUW2?SUj+@j+sh9PRKrG_8yqs3`)JW0%qJr1@lS2ho+O)2}4~ zDAs1VMNpqDJGuvrV9?Q2yXW{=$KDz}2YqT9@6U2M&JMb8(uDfVjR%=-qW^S z1y&z5b&vE|r>)-5ESC9%It%@H)OBYP^Y^sB z^#(ILJr&zS+$OlMXEuzP=N-_cq(8xy@%vzF(bV#y?!hLszzpNr;0_84`qTu`X(MfpFVvF++4Vh;5=NJMfKTdMY zU`ZLuVaE+M4mw56aNbK^C_(yu$U4L}WSQ4zeJ0ZLlD~ds`GZoSgsNtFGQSznhiOI* zxVjt(d+HA?hcaRnl_g9ts%qvtX4DFgr+k#gPxuEMPU6Vi8n7>%11!mje{S4AGF$8} z4L1>G^oY9zRU!X*^_=^bv>Mcn+3sOo5@9~~ZR%Tn6{f;9{o*W-!8cXiMHf4}YzLaJ zZLY!aKNbPpZ9G{?O+;1fl+qNyavIZZ+fn1{Y7Ej6`(@a7?VSMg_avx>x@cT}xc=~@ zVBvOJBEK=~f~;*~Z>&8FRMcoYYF;q-OOP(>mCJ~lTyB*iDVfXxK~E!E5Z;lZuo12) zqp3L*Nor3li%@K}*E{pus-bmqp3uK79#{@_p@6Q6fsPIisd{;r(Z3zQ@IDist$fB<8&5AmZ0xHZWB;oi|MAzQ41VOP6z5BjA$uv>7_yEc z;e`yDzPr=PoZoqu2~&-HuPH7Fg~XIvcPsqp{A1l!eVaqlp{Iaqadt>xX~9^trDEod z8syt&zBEux&)?r$wf>d990r{Yug`3gr_(AJBK?4@HT$2kZ{Wan? zW)`M`tjn@1N%emxJf|hE*sw5zFZUq5K11hZ@dWgciqVKW~opV_clp?-MB{iGNZILg<8dZe-+jr07?~u(X zl^E~01KZohR>WIOnkgZqzu69dBg^vmML|Iuni#m;IP9KGYS$yH&$Ucfw4R55)&q(Z zDFnYUwfpqs8h-uiI1v=zS`^1@sZOESBO>v075V2rUhfvbQAQqeTwQF9cUTh$>Tb?{ z6dT$aMddlCnfPQ=ap}`ON|X}kQ`f0k`Xz<01f_Y3E0vo~zar%w<_TM#p(3waQ)sr; zghgYsr2nX-)*XKLs04l5FRtcVy1iWVFFDaz#+7TRaJLE_8$Fx!cWzd7#z`U0U5)zqy`e!knWY9S$pMjc&hNLDje2bFvP2_ex;qtW3mt`G=v4^mYaiWBl z3z6|GtuxQ|v_wtfBsI+>kGQ{o39bTCLbk+br3uU@LeikxIkfnmv5wo0l_*Ndn7YMv zcv=naj<`MXUF!YxG*2rtr4Agz)Yja*DiHjUh{}jx-Y9W4}sfXYeV*}b2Tzp?JkfjqgRqWNX6QGQd8VW-Ai%z z5lCj*I&Smce?Z5s4i}r;wHor?5?XQ*?h*CNq7q;omL8?o{~G|q&bNIp*Q%@LNh`Dw zV@suk#?ihu#U@YxxJzGjZMH^{ipyAAb@u^4sZTG%Iof|yz|is1%X!-1%yjs{>XA5z zoMJf&<3msGchA2AR^uC-2Z>B7%9HUiMM}eKI9t}@`XOYK%lB6tGQm=gSj!4GYZROI z_e4$iFOGfIQ5!FtD13ZM8Y3B=^UlVoy`aZ0Ef{9n==i);56>Kc$?AdLma1j+=l7ZR=r>z62{AWVnH1Zw0 z&!==nwr&e&RL~DHpGopRH#tM;__|eSsXYpB8r?ScVeV#Ql@*L5`(t)MGF;lkJHVGM zLqx=7limB~xGZIB>|iVfiSNN$N6QA-QQ%$qkQ=awhx1-ur&~SuL@z)(!>bWH<^ogH zyihwdiYC-0q{79UC3c|M0?#uqyD_`QG%?Tz3Ip$t)h{qd`V&BzPq0Vfl$uGD^KAVw&8gE`RoeGWXa(JNf0orfw<2yvEd$1=TU#o~VC%I*K@wpNZ+p>$b7H=5rJ$!*XjGFf`F?GdwtP zFyGe)^DbMI`?OG&bg}@y+6@I#hF|o|xxEjy>ly=QhnA&h&+afABgRd*<+`K0jBOgG z5Bo~B&ReR^&?b9}n^K;O8qR|>4Ffjfrqff?=`E6j8Wo^ibJ(&0M4U0TXoAY3%fAiP zG3b#7trD!iGx27qc&R6ZDG-6$N~eq<^?vy#S&GtCm7RP9PSKxBRTKburB~*M)nO`B za|I)Xu5(dLe64yx@FgQU%TIL4$KO>Qs%dmkUe32qE|X_}@h>a{#k-5RAu0YUOxpGH z$NoBg?5nv?+4Fh8#*{5V`^NS?ZAaTK|F=saE|Xiz4eH8v(W#(YNORghAKm(^yR;Tr zO9@rb`LA!L-;GEEMO^;?7&9c1&yAO~VNw=oD{EWeU0%CXm=n!VuI8SIC~Ef^Icu7# z(V7IB?`Hpe8qa$6&!>PV6WU0YUI%=`*Xpu1m_44qoHtzew4QoPXnD6~IGU^^3KD3x zRw&ql`!qCI2Zg2E3G!qj^c8MjNd(d1NM9VSG#~i2b*?+tAO*GpJX(LJhPdmURrKB| z-R{pEEGaX=rOFMSI>FOF=w2`~#xtxm2*nC~Dz+ydVu!t9B+||Jo6aUoL z&vfrE%utj-ZI0odyqWVd;41e#jiD`$5fqvm}$6IjQ96Sfs@;e{Fo`t~WR|4gxUixBTSJe1AvX zbbjVnZ{Iv@o@<4l&Ar))XiFPT2ZDKH3Y4`M;08uV+!~ueDe=(uAA*;koLQboU(yf5 zYt(XC^GlSJSq3*nwaeQFAGK~vv*$7tLJtZ67wS^O^)xNR^0|f%_cD#^ zNimoVR~M6Bv0=<`YHzxwNT8G z_L95=`EOPw|3|k@j@vaH2nKu1mWc|=3}g^8Ct%|aY#TF*`!WwDrP7~JSX#vw@X@LW zqC=;Zd>kEB0$AW9D&Z9`G3{H!owD_ZW(_PZe@|8ff3@TOqdB#pgG@jj1x5Q~*08$xNCk1fC^e zihj4n^CX-gF@WvU1``AgT3JAs1~bo>CGqE*vES)nWQv7ACSmOy+P6FwegRw|@=a z8c#ZfMn>=s&TsPeMC>6llubBlo%^>HlDW(l!nIv?Shea*rSfrs zDker7so7v-0O M(*BDRW};{R1LausXaE2J delta 6293 zcmZvgbx<5lwDuPWt|2%Cw_u9|x8N4s-Q6|#KnSkEEx5ZAfYm(IU&huh=+xc*EE^te%2pgDV8+jDxc|%!jZHV|D+Dkd@rQ(bOB(7w#Jf z1c1n4kP?&&(r_|$v;={OBw-V<#t7nNl<=)C&h8$t8-!Uvr7*2K1vyiBubP`!n3#Ze z{u*!cTs-p_6aUOG21yaIk&ty_7DE3(p{d2$xt<`8yg&4 zyaRoKjp~fx>{7GY$4YsuSeE z5~g@zx%%7-8dnotEq8xWP}M`~hMOqJ1opvsT@EiCkf=#o=C#zMtTba2Fbfk~KHex}ZCO=?*ZjO% zso*gwiLITJZSlmu(;2&fu*b~lDde;eFmbV2=H0ROU|}bx{m8*TaE%SN6f8!I#3X~4&0*!-KK_^Mi2(G{_IuE)PdNz6udJhH`>|(-@ z#W2B0z^K7k#(2i`!mPvG!o0`4_g)RvMfWEi$oS*DxG3MkqMxc2x@6OYakK75`cpks5IO*D!9ed?U_C~Q_ zv&?PveNJ7M050dZN?{QfonD)Y5EDsGjol<0l^m0@dag}LW1GK6HjmUY%Pa?Fyy*Ns z1iPk%bTT@&l!pQ&5NaN@_>W$+|KiIVG3!|zCzF7>VUxW0vPM@yOK8%bQ0Y)O4mivG zQv!xk2&HC_1Jan+)w!fE;NSvBmZK^ggK;{Edf1S zogaC&>N0qkDZG?CyQ(5?UMyYHA^n+Y%3+d}^U!gM3pN5(_a+ul){)#5UZW7jYu{6@yl#Kd2xSi zSx?S4)&jF;z_tfVHzDOdqDl@UCa&7b+>30i1X?H**Kx;}!Yn}CQ>mcoBongV=yUx>#`L}?Cj$=%{!;ov>6DlfQGE^ERu ziZ>_rF|vuZaZa}Z9V-THv(USkmXYaTgzFzKPmp$Gtc1rI9>KRY;P0(?v;9*^shAP!+ef#@D%mmq$$>oymHIJAg3xF&ZZ=26uy^QJt0XrmS!hVcK7cQ! zZ532oP`7+sd#M-wylH1lT!l+vcSrbZBQ;7F`?ZAHg^}X6Nn6LD zMjUv`DU~o~n?QC8;$0s`4mrXo9lP=8%YXikaP735%+j&_8};!39X!poY&^dxJvEsv zH`UTyZ<*5EeoorZoKoA529dvJ2KM1!ux;|LjsWrZY5C)wG~8)39GpN=o;M@4o6O(9 z;G7_x%K$#kRjVf>QhLLVAXXvc5+u$WrS;UBkRKi==19w^f|#g+{|1T=YPK~;+I%s8 z-cSjj?HmOW{m5yLx*AHl`jkNk(&*eV%8FcLj6L2QvMq630TG z$jKjH)u5Bu;8PTq(_LwAKjNkFgQyyPe0)4=a?zv8A6O}6yjkL6KXa z9P{{I#)vLjWDW1TKT(jA0jup$vKvTLD8aA`@%44e3h_v@XGi>V=$xy$%V|W+V8a{5 z;z(8o=F`8jk&zudbZ5@jrTsIrvC7b=2Zig4c+s#H1(z}v8Rdm_4|PmQN8K*vJz}oC zzgbT5bK}tO*;1KO((YlpDcc@Diz1jsiNr`_@JP3v!#~a86dJ$x1(E~tB^o)eOthDJ z+1zfgw)5+=Hq$?%sz9Bg@;uTk;2y^SV1f$ z5WcbbmRf)th`<>R#`*{==m9A!m5rsTGkp#6r>CnB5jXedq^=&rVaZ-4fKO4t!>)}>fh((`5Bw^m}#Smm05cFhf)<%qK z4I48j2zgL#>SPQ?yE;YuJxR;Ci4piXbilVVGcP9^x;{ydYyVGndDr{Q=9T}m+;aZD zth&&M{=Dxc9Wd%auKl%%eUOt&r#fAP6oru{aR`E=@;RM0v8Ox~Jm9p+ekl0I_9?CX z@H5YkvWg0asL;`|{+r#NyUVX>V~67pJMHcVp98Cf@a!w9Dwv#YtI&#UrleO9*Wc2q zQgCG4q)_JJX5NsefOy#bif*Pt`=_eIB`zC}&%AlWm4S&h>S{jO5|mx|c|H7hc?|4I zd0JFjQfzzcB9>ZCvtbup!{NhnG}M^joNa;H4~+i3Eea4*lB|E1vTuKWDKS{uG}7iW zAFF=R=!oyh)IGIdYXKAIoec*|RiXlBhxfCz zgD%K4oKFcxvcYuij9L9uVR?9+;^kQ1O_PKRb&6ylPYc!63+(2eOKufxFDNd{n(62J<8Hinbjq~mpSppHUZqsr2yK%U74I;DxNoxziM@j|U z6yGF#ucb}f8BIohyB+Qb6T}ZSgzw?Wsm*NaI~SushprpYRvR+o z(*-(QRE09zLzbAe$#v@C0fW)Q=M?`Kzv%nU(WFvL2vJPp2~PwclUSdONCf=010|;( z5nzqL#bV`0*K@q>YSDcg`{ebai`<&vTL1i&V{a1UDyuJ@gZRr^byKe*u}@ zLQmwf5BC&ocnz0KSXrAB5eRCRz7o9+&;$B(?M|CheOWsR&dOpRzxnrB<|7_XKNZ*4 z2qLe$5Uwv6DU1~~Zm}T=h$)IZBsfJ*X5xpo@5scH$^AusNcN9%v)COaVsfL#atV{z zPC)rkjKR>Hi0WEm){)W8k-6nyO$IfQcn;2pZuMf*nQrscKZMV~QZ{b3du-CIOa@$^ zXC^L?_Yi-qC8`rlX)qVgmDI}b3RnU;T&{6=Oi$3;;A{FYqjFf<=+C1R>Y)V)8;WYQ}=svKRwNJX1EFP zC~aQ14)F}&$xUm+uLXbqM7)v42FUG+ZPJB!8yyfLHjX1tm~f)?#9`QgsNj#m*uZtZ z#v6gklt4Y5zpMqh1o&b#q3fJN{=`nL(^D*StRr0o#152n${4#OMACJF4@F`L#R(b2 zh!*+C0cvjo-ktee3KuT(Df=CaoLWhk8+@s$%J1la`&i*!*#CHwKkgID104DOm$wPl|l8u55s1ota4%5ZI7dP*#UM}|RfD@4)!d){tUNF6na8d#! zP^T&eBZ^fb<0R=w&?5gO0O=nogc|j=T*A1L{%CkyssVY$3>PzD1kj!qM}*f@%-_pg zn}51f#!qdr93aPQxHkXt$xl|-ms(duyBHiPj&Y`hLj%#8=RXK~ZXpPBp;Muff2+UV zn{)2ww9-0H)faCZukBFGDPVw%eCgDzV^O(PT2)R<;5rJ3!pHlL2Q)X;L?0w~l|F5e zP8D&={P$!jR;Rk;GYQdR## zCW%ewM_dn2xN_m z53d>p$)7wPj8o;31Ypx^s`VBH*gPu_DrNI+U*AXm6B#O^G$_xO{Bz^Y^c;*9UrP4k zre>Q>Kq|~$h;h8^`*2&~o5l`?i+6>!Ilf{(7q#Gv(D!G%SdN&;L#xn+;c;9}keq_;Z zWlUH>VK$ab_gS`R%ox;aMKZijV8(M3TgUGxTddKxk3Eh}8gPo$Iw4594F<~q4xa+r zMf|p9)^H@|aRF+Y`wRod@-oJJdj-9#cv6g^D}9BoC4$%%sS!j#Zl^6#UVGa*G$~qy z*<)_h##Z;E-htxETKo*Q6-^@g29aXA6b(6vPiVI6fn47LO&jh!JAR9b4UNlTS3VVn zde|aSR_wi>LS@&${kO7(b$_lLcn3Sy2OVO-`;UrqBf!WL{n5HZ?}@jtUaI>2bj1*9 zBQdd@aLWf*fRkh&Lv5KQx|mT z*~WJa^p@=*t-WV1teA~z4v1Ve(^>wF5*ZX`>gv9A7bpf?h!lrK8+)+fUcz_pg@&FE7z);MxoURyupOn5Q}Z$m0U*O;1lo!(siW z+nGBoatq>+N$4k!demRmdL*$8paHG|A=Sy=e)DlY*vs)@iwjzNH+x@%Mpw=2JHedir%aLXDDt2W@5EGZhEoF zWx{E;$9J8N-wGRMUHj5?n`+8ujm@x2AZSz+pKYq?YT#%H1$W`iv}Vd^+DyJ^0Jx$* zB?S5sn5h?WsFywq?qX3dxp*Q(1`FHJ41hX|nrlqIx{Q;bA77Q#yZO6{fXM#jn10~Y zW2s)_WJzU-Ek;qAuI~Hx7#7*bA)}dMgiWFO5&zirAX)zlHj7}YzalX|!4G{sfmZOU zs@CFL!!EzO%UpVECej#_wS2r5L_iR#$Z!WT93o`e;;9c>U_q4#ldl%mswJ#c8#wD4 zCza*e{YH9Hfw3%owuQ;zyKlLy0&qjJP-TX3%xseJT1O(n)~Iaj%u3W2gBXw)X-KM~ zdTBxYg=&gw`_^jmwdI%fHuy5isxXEd6>_&yPz|Uj1vMYpAdP zKD*q0EyJm4!7vxt&8VI~_vMvO`OG#~?ZLc`%FVGD(M!srj)fS>o%VcPtFonhikdWR z$v60nFz7w9;o{ug3z%^vz-jf((dCKu5{9H%=*o8t z+Nq^@I}ULX**^@Q+vL(R{8~nNe()9wM5QnG>6tV$AsCQnU+hO&pRCG?hzmD}6KxDh z-Bnw@e0qtL6jJhbh?ySTRJ5M%7Rwr*EoXTV$ium1s&rja0QTG-Bj3>R=Xj^QN!s__ zWO{|Jm3F#v<310XMRJdLdV6tNz8BQ)pD+MfGzA@Vm%*SeKVMv|nZ+NjOz zq6LM?Ht(6nTMhK*SxXnx*CF*x$En z)~lT~iy@6I00x-)R8>DNlI$X8Y_e$cU|rbNMeVf)vua-S5XAzlHTY$#xDii^Bmf~> z@=u+uTf1hym1z5U5=DBxFy2BAk-?A`y-uY=#JN}>Q<`XVOKT=L3oF@JMi)%+LgFJ? z;X0`ILjOfbYJPb4lZtl+Ekzo&%TjM&Z-D!)1~`Qp*yBxnHoe*l2&qU^e6av5A3GJKlB8HAy?F z!!PBA_Ad7oyaj2pwijUujQ`I{|@B_^2+oOzvdmR(icS4FC9a|<3_@3UBv}+wz!4L&I^k!EvZY_z8)a-*EC z-?__w5^JU4KLUZzh^7fdRrm#E^Iz(bYrBYSK}lOAWQ+)jEGFa#AQS>n^nVAOaQ_V> Vko|NrE3hTUBF{{bc-=ePg> diff --git a/platform/features/layout/bundle.json b/platform/features/layout/bundle.json index 0df6413ed0..6f8630c07f 100644 --- a/platform/features/layout/bundle.json +++ b/platform/features/layout/bundle.json @@ -5,7 +5,7 @@ "views": [ { "key": "layout", - "name": "Layout", + "name": "Display Layout", "glyph": "L", "type": "layout", "templateUrl": "templates/layout.html", @@ -197,7 +197,7 @@ "types": [ { "key": "layout", - "name": "Layout", + "name": "Display Layout", "glyph": "L", "description": "A layout in which multiple telemetry panels may be displayed.", "features": "creation", diff --git a/platform/features/pages/bundle.json b/platform/features/pages/bundle.json index f1d559dde7..099b96415b 100644 --- a/platform/features/pages/bundle.json +++ b/platform/features/pages/bundle.json @@ -3,7 +3,9 @@ "types": [ { "key": "example.page", - "name": "Page", + "name": "Web Page", + "glyph": "\u00EA", + "description": "A component to display a web page or image with a valid URL. Can be added to a Display Layout.", "features": [ "creation" ], "properties": [ { @@ -26,8 +28,8 @@ ], "controllers": [ { - "key": "IFrameController", - "implementation": "IFrameController.js", + "key": "EmbeddedPageController", + "implementation": "EmbeddedPageController.js", "depends": ["$sce"] } ] diff --git a/platform/features/pages/res/iframe.html b/platform/features/pages/res/iframe.html index ee2d985948..e23e073555 100644 --- a/platform/features/pages/res/iframe.html +++ b/platform/features/pages/res/iframe.html @@ -1,4 +1,4 @@ - \ No newline at end of file From babe420f268e19f8b9802e22a7638d348bbd62f0 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Tue, 28 Apr 2015 14:49:08 -0700 Subject: [PATCH 34/45] [Frontend] CSS and markup for iframe WTD-1150 WTD-1145 CSS and markup to cause iframes to layout properly without double scroll bars --- .../general/res/css/theme-espresso.css | 41 +++++++++++-------- .../commonUI/general/res/sass/_iframe.scss | 9 ++++ platform/commonUI/general/res/sass/_main.scss | 1 + platform/features/pages/res/iframe.html | 5 ++- 4 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 platform/commonUI/general/res/sass/_iframe.scss diff --git a/platform/commonUI/general/res/css/theme-espresso.css b/platform/commonUI/general/res/css/theme-espresso.css index 741c0aee75..e2c08cabac 100644 --- a/platform/commonUI/general/res/css/theme-espresso.css +++ b/platform/commonUI/general/res/css/theme-espresso.css @@ -1,5 +1,5 @@ /* CONSTANTS */ -/* line 17, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 17, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/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, @@ -20,38 +20,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } -/* line 30, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } -/* line 32, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary { display: block; } @@ -2882,10 +2882,10 @@ input[type="text"] { .wait-spinner { display: block; position: absolute; - -webkit-animation: rotation 0.6s infinite linear; - -moz-animation: rotation 0.6s infinite linear; - -o-animation: rotation 0.6s infinite linear; - animation: rotation 0.6s infinite linear; + -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; @@ -2924,10 +2924,10 @@ input[type="text"] { .treeview .wait-spinner { display: block; position: absolute; - -webkit-animation: rotation 0.6s infinite linear; - -moz-animation: rotation 0.6s infinite linear; - -o-animation: rotation 0.6s infinite linear; - animation: rotation 0.6s infinite linear; + -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; @@ -3039,3 +3039,10 @@ input[type="text"] { .autoflow .l-autoflow-items .l-autoflow-col:first-child { border-left: none; padding-left: 0; } + +/* Styles for the iframe EmbeddedPageController element */ +/* line 4, ../sass/iframe.scss */ +.l-iframe iframe { + display: block; + height: 100%; + width: 100%; } diff --git a/platform/commonUI/general/res/sass/_iframe.scss b/platform/commonUI/general/res/sass/_iframe.scss new file mode 100644 index 0000000000..3c8aacbe67 --- /dev/null +++ b/platform/commonUI/general/res/sass/_iframe.scss @@ -0,0 +1,9 @@ +/* Styles for the iframe EmbeddedPageController element */ + +.l-iframe { + iframe { + display: block; + height: 100%; + width: 100%; + } +} \ 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 10cc1789d2..99bf5b6322 100755 --- a/platform/commonUI/general/res/sass/_main.scss +++ b/platform/commonUI/general/res/sass/_main.scss @@ -40,3 +40,4 @@ @import "helpers/wait-spinner"; @import "properties"; @import "autoflow"; +@import "iframe"; diff --git a/platform/features/pages/res/iframe.html b/platform/features/pages/res/iframe.html index e23e073555..0ab4edea5c 100644 --- a/platform/features/pages/res/iframe.html +++ b/platform/features/pages/res/iframe.html @@ -1,4 +1,5 @@ +
\ No newline at end of file + +
\ No newline at end of file From 57c5b301778160ac2750955f9a3359b464eb6c6f Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Tue, 28 Apr 2015 15:05:23 -0700 Subject: [PATCH 35/45] Repair to bundles.json WTD-1150 Mistakenly checked in dev version of bundles that was using couch persistence --- bundles.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles.json b/bundles.json index 26e684cdef..04515b1897 100644 --- a/bundles.json +++ b/bundles.json @@ -15,7 +15,7 @@ "platform/features/scrolling", "platform/forms", "platform/persistence/queue", - "platform/persistence/couch", + "platform/persistence/elastic", "platform/policy", "example/generator" From ec2d5892a55aecd3befa05c51f4e21a6bdfb9248 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Tue, 28 Apr 2015 16:08:33 -0700 Subject: [PATCH 36/45] [Frontend] Markup for info bubble WTD-1054 Temp object to allow work on markup for info bubble --- bundles.json | 2 ++ platform/commonUI/inspect/bundle.json | 21 +++++++++++++++++++ platform/commonUI/inspect/res/infobubble.html | 5 +++++ 3 files changed, 28 insertions(+) create mode 100644 platform/commonUI/inspect/bundle.json create mode 100644 platform/commonUI/inspect/res/infobubble.html diff --git a/bundles.json b/bundles.json index 04515b1897..336aba67b8 100644 --- a/bundles.json +++ b/bundles.json @@ -6,11 +6,13 @@ "platform/commonUI/browse", "platform/commonUI/edit", "platform/commonUI/dialog", + "platform/commonUI/inspect", "platform/commonUI/general", "platform/containment", "platform/telemetry", "platform/features/layout", "platform/features/pages", + "platform/features/plot", "platform/features/scrolling", "platform/forms", diff --git a/platform/commonUI/inspect/bundle.json b/platform/commonUI/inspect/bundle.json new file mode 100644 index 0000000000..16f2bdf89c --- /dev/null +++ b/platform/commonUI/inspect/bundle.json @@ -0,0 +1,21 @@ +{ + "extensions": { + "types": [ + { + "key": "infobubble", + "name": "Info Bubble", + "glyph": "\u00EA", + "description": "Static markup for info bubbles", + "features": [ "creation" ] + } + ], + "views": [ + { + "templateUrl": "infobubble.html", + "name": "Info Bubble", + "type": "infobubble", + "key": "infobubble" + } + ] + } +} \ No newline at end of file diff --git a/platform/commonUI/inspect/res/infobubble.html b/platform/commonUI/inspect/res/infobubble.html new file mode 100644 index 0000000000..653ccedbcf --- /dev/null +++ b/platform/commonUI/inspect/res/infobubble.html @@ -0,0 +1,5 @@ +
+
+ Info Bubble, and it works! +
+
\ No newline at end of file From 2cba0234c539da2b66e3a2c48546b532695a75c9 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 29 Apr 2015 15:04:59 -0700 Subject: [PATCH 37/45] [Pages] Add comments Add comments to controller for Page objects in preparation for integration, WTD-1158. --- platform/features/pages/src/EmbeddedPageController.js | 7 +++++++ platform/features/pages/test/EmbeddedPageControllerSpec.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/platform/features/pages/src/EmbeddedPageController.js b/platform/features/pages/src/EmbeddedPageController.js index 31ce5c69e3..6c57578ec6 100644 --- a/platform/features/pages/src/EmbeddedPageController.js +++ b/platform/features/pages/src/EmbeddedPageController.js @@ -5,8 +5,15 @@ define( function () { "use strict"; + /** + * Controller for embedded web pages; serves simply as a + * wrapper for `$sce` to mark pages as trusted. + */ function EmbeddedPageController($sce) { return { + /** + * Alias of `$sce.trustAsResourceUrl`. + */ trust: function (url) { return $sce.trustAsResourceUrl(url); } diff --git a/platform/features/pages/test/EmbeddedPageControllerSpec.js b/platform/features/pages/test/EmbeddedPageControllerSpec.js index ae2d572b30..fd5b816a34 100644 --- a/platform/features/pages/test/EmbeddedPageControllerSpec.js +++ b/platform/features/pages/test/EmbeddedPageControllerSpec.js @@ -19,7 +19,7 @@ define( return v; }); - controller = new EmbeddedPageController(mockSCE) + controller = new EmbeddedPageController(mockSCE); }); it("allows URLs to be marked as trusted", function () { From eafc603e03b2140deee912e4b50dfe087dd0f964 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 29 Apr 2015 17:52:06 -0700 Subject: [PATCH 38/45] [Frontend] Infobubble markup and CSS WTD-1054 Markup and styling for infobubble; Includes fake data in infobubble.html --- .../general/res/css/theme-espresso.css | 151 ++++++++++++------ .../general/res/sass/helpers/_bubbles.scss | 146 ++++++++++++----- platform/commonUI/inspect/res/infobubble.html | 44 ++++- 3 files changed, 247 insertions(+), 94 deletions(-) diff --git a/platform/commonUI/general/res/css/theme-espresso.css b/platform/commonUI/general/res/css/theme-espresso.css index e2c08cabac..6d1e916c26 100644 --- a/platform/commonUI/general/res/css/theme-espresso.css +++ b/platform/commonUI/general/res/css/theme-espresso.css @@ -1,5 +1,5 @@ /* CONSTANTS */ -/* line 17, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 17, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/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, @@ -20,38 +20,38 @@ time, mark, audio, video { font-size: 100%; vertical-align: baseline; } -/* line 22, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 22, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ html { line-height: 1; } -/* line 24, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 24, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ ol, ul { list-style: none; } -/* line 26, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 26, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ table { border-collapse: collapse; border-spacing: 0; } -/* line 28, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 28, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/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-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 30, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ q, blockquote { quotes: none; } - /* line 103, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ + /* line 103, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/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-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 32, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ a img { border: none; } -/* line 116, ../../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ +/* line 116, ../../../../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/reset/_utilities.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary { display: block; } @@ -2716,49 +2716,96 @@ input[type="text"] { .tool-bar .input-labeled label { font-size: 12.6px; } -/* line 1, ../sass/helpers/_bubbles.scss */ -.bubble-wrapper { +/* line 6, ../sass/helpers/_bubbles.scss */ +.l-infobubble-wrapper { -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; -moz-box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; box-shadow: rgba(0, 0, 0, 0.4) 0 1px 5px; position: absolute; - z-index: 10; } - /* line 8, ../sass/helpers/_bubbles.scss */ - .bubble-wrapper .bubble { - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - -o-border-radius: 3px; - border-radius: 3px; + z-index: 70; } + /* line 11, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble { display: inline-block; - background: #990000; - color: #ff9999; - font-size: 0.8rem; - font-style: italic; - max-width: 200px; - padding: 4px 8px; } - /* line 17, ../sass/helpers/_bubbles.scss */ - .bubble-wrapper .bubble:before { + max-width: 400px; + padding: 5px 10px; } + /* line 15, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble:before { content: ""; position: absolute; width: 0; height: 0; } - /* line 24, ../sass/helpers/_bubbles.scss */ - .bubble-wrapper.arw-left .bubble:before { - right: 100%; - top: 50%; - margin-top: -7px; - border-top: 7px solid transparent; - border-bottom: 7px solid transparent; - border-right: 10.5px solid #990000; } - /* line 32, ../sass/helpers/_bubbles.scss */ - .bubble-wrapper.arw-down .bubble:before { - left: 50%; - top: 100%; - margin-left: -7px; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-top: 10.5px solid #990000; } + /* line 22, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td { + max-width: 150px; + padding: 2px 0; + vertical-align: top; } + /* line 29, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td.value { + padding-left: 10px; } + /* line 32, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td.align-wrap { + white-space: normal; } + /* line 39, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-left { + margin-left: 10px; } + /* line 41, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-left .l-infobubble::before { + right: 100%; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 7.5px solid #dddddd; } + /* line 49, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-right { + margin-right: 10px; } + /* line 51, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-right .l-infobubble::before { + left: 100%; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 7.5px solid #dddddd; } + /* line 60, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-top .l-infobubble::before { + top: 10px; } + /* line 66, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-btm .l-infobubble::before { + bottom: 10px; } + /* line 71, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper.arw-down { + margin-bottom: 10px; } + /* line 73, ../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 #dddddd; } + +/* line 86, ../sass/helpers/_bubbles.scss */ +.s-infobubble { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + background: #dddddd; + color: #666666; + font-size: 0.8rem; } + /* line 92, ../sass/helpers/_bubbles.scss */ + .s-infobubble .title { + color: #333333; + font-weight: bold; + margin-bottom: 5px; } + /* line 98, ../sass/helpers/_bubbles.scss */ + .s-infobubble tr td { + border-top: 1px solid #c4c4c4; + font-size: 0.9em; } + /* line 102, ../sass/helpers/_bubbles.scss */ + .s-infobubble tr:first-child td { + border-top: none; } + /* line 106, ../sass/helpers/_bubbles.scss */ + .s-infobubble .value { + color: #333333; } /* line 8, ../sass/helpers/_splitter.scss */ .split-layout .splitter { @@ -2882,10 +2929,10 @@ input[type="text"] { .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; + -webkit-animation: rotation 0.6s infinite linear; + -moz-animation: rotation 0.6s infinite linear; + -o-animation: rotation 0.6s infinite linear; + animation: rotation 0.6s infinite linear; border-color: rgba(0, 153, 204, 0.25); border-top-color: #0099cc; border-style: solid; @@ -2924,10 +2971,10 @@ input[type="text"] { .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; + -webkit-animation: rotation 0.6s infinite linear; + -moz-animation: rotation 0.6s infinite linear; + -o-animation: rotation 0.6s infinite linear; + animation: rotation 0.6s infinite linear; border-color: rgba(0, 153, 204, 0.25); border-top-color: #0099cc; border-style: solid; @@ -3041,7 +3088,7 @@ input[type="text"] { padding-left: 0; } /* Styles for the iframe EmbeddedPageController element */ -/* line 4, ../sass/iframe.scss */ +/* line 4, ../sass/_iframe.scss */ .l-iframe iframe { display: block; height: 100%; diff --git a/platform/commonUI/general/res/sass/helpers/_bubbles.scss b/platform/commonUI/general/res/sass/helpers/_bubbles.scss index 0f7548a9c9..849cea01d3 100644 --- a/platform/commonUI/general/res/sass/helpers/_bubbles.scss +++ b/platform/commonUI/general/res/sass/helpers/_bubbles.scss @@ -1,40 +1,110 @@ -.bubble-wrapper { - $arwSize: 7px; - $c: #990000; - @include box-shadow(rgba(black, 0.4) 0 1px 5px); - position: absolute; - //top: 200px; left: 200px; - z-index: 10; - .bubble { - @include border-radius($basicCr); - display: inline-block; - background: $c; - color: lighten($c, 50%); - font-size: 0.8rem; - font-style: italic; - max-width: 200px; - padding: 4px 8px; - &:before { - content:""; - position: absolute; - width: 0; - height: 0; - } +//************************************************* LAYOUT + +$infoBubbleFg: #666; +$infoBubbleBg: #ddd; + +.l-infobubble-wrapper { + $arwSize: 5px; + @include box-shadow(rgba(black, 0.4) 0 1px 5px); + position: absolute; + z-index: 70; + .l-infobubble { + display: inline-block; + max-width: 400px; + padding: 5px 10px; + &:before { + content:""; + position: absolute; + width: 0; + height: 0; + } + table tr { + td { + max-width: 150px; + padding: 2px 0; + vertical-align: top; + //white-space: nowrap; + //overflow: hidden; + //text-overflow: ellipsis; + &.value { + padding-left: $interiorMargin * 2; + } + &.align-wrap { + white-space: normal; + } + } + } } - &.arw-left .bubble:before { - right: 100%; - top: 50%; // 26px; - margin-top: -1 * $arwSize; - border-top: $arwSize solid transparent; - border-bottom: $arwSize solid transparent; - border-right: ($arwSize * 1.5) solid $c; - } - &.arw-down .bubble:before { - left: 50%; - top: 100%; - margin-left: -1 * $arwSize; - border-left: $arwSize solid transparent; - border-right: $arwSize solid transparent; - border-top: ($arwSize * 1.5) solid $c; - } + + &.arw-left { + margin-left: $arwSize*2; + .l-infobubble::before { + right: 100%; + border-top: $arwSize solid transparent; + border-bottom: $arwSize solid transparent; + border-right: ($arwSize * 1.5) solid $infoBubbleBg; + } + } + + &.arw-right { + margin-right: $arwSize*2; + .l-infobubble::before { + left: 100%; + border-top: $arwSize solid transparent; + border-bottom: $arwSize solid transparent; + border-left: ($arwSize * 1.5) solid $infoBubbleBg; + } + } + + &.arw-top { + .l-infobubble::before { + top: $arwSize * 2; + } + } + + &.arw-btm { + .l-infobubble::before { + bottom: $arwSize * 2; + } + } + + &.arw-down { + margin-bottom: $arwSize*2; + .l-infobubble::before { + left: 50%; + top: 100%; + margin-left: -1 * $arwSize; + border-left: $arwSize solid transparent; + border-right: $arwSize solid transparent; + border-top: ($arwSize * 1.5) solid $infoBubbleBg; + } + } +} + +//************************************************* LOOK AND FEEL + +.s-infobubble { + $emFg: darken($infoBubbleFg, 20%); + @include border-radius($basicCr); + background: $infoBubbleBg; + color: $infoBubbleFg; + font-size: 0.8rem; + .title { + color: $emFg; + font-weight: bold; + margin-bottom: $interiorMargin; + } + tr { + td { + border-top: 1px solid darken($infoBubbleBg, 10%); + font-size: 0.9em; + } + &:first-child td { + border-top: none; + } + } + .value { + color: $emFg; + } + } \ No newline at end of file diff --git a/platform/commonUI/inspect/res/infobubble.html b/platform/commonUI/inspect/res/infobubble.html index 653ccedbcf..97187a3465 100644 --- a/platform/commonUI/inspect/res/infobubble.html +++ b/platform/commonUI/inspect/res/infobubble.html @@ -1,5 +1,41 @@ -
-
- Info Bubble, and it works! -
+
+ +
+
+
{{title}} +
+ + + + + +
{{property.label}}{{property.value}}
+
\ No newline at end of file From 0bbe3ec99af2410acc29c2f81308aec3856a22ed Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Wed, 29 Apr 2015 18:13:35 -0700 Subject: [PATCH 39/45] [Frontend] CSS enhancements WTD-1054 Better handling of different content cases; --- .../general/res/css/theme-espresso.css | 65 +++++++++++-------- .../general/res/sass/helpers/_bubbles.scss | 42 +++++++----- platform/commonUI/inspect/res/infobubble.html | 45 ++++++++----- 3 files changed, 92 insertions(+), 60 deletions(-) diff --git a/platform/commonUI/general/res/css/theme-espresso.css b/platform/commonUI/general/res/css/theme-espresso.css index 6d1e916c26..b2668e99a7 100644 --- a/platform/commonUI/general/res/css/theme-espresso.css +++ b/platform/commonUI/general/res/css/theme-espresso.css @@ -2726,7 +2726,7 @@ input[type="text"] { /* line 11, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble { display: inline-block; - max-width: 400px; + max-width: 250px; padding: 5px 10px; } /* line 15, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper .l-infobubble:before { @@ -2734,45 +2734,57 @@ input[type="text"] { position: absolute; width: 0; height: 0; } - /* line 22, ../sass/helpers/_bubbles.scss */ - .l-infobubble-wrapper .l-infobubble table tr td { - max-width: 150px; - padding: 2px 0; - vertical-align: top; } - /* line 29, ../sass/helpers/_bubbles.scss */ - .l-infobubble-wrapper .l-infobubble table tr td.value { - padding-left: 10px; } - /* line 32, ../sass/helpers/_bubbles.scss */ - .l-infobubble-wrapper .l-infobubble table tr td.align-wrap { - white-space: normal; } - /* line 39, ../sass/helpers/_bubbles.scss */ + /* line 21, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table { + width: 100%; } + /* line 24, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td { + padding: 2px 0; + vertical-align: top; } + /* line 31, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td.label { + padding-right: 10px; + white-space: nowrap; } + /* line 35, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td.value { + white-space: nowrap; } + /* line 39, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble table tr td.align-wrap { + white-space: normal; } + /* line 45, ../sass/helpers/_bubbles.scss */ + .l-infobubble-wrapper .l-infobubble .title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-bottom: 5px; } + /* line 52, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-left { margin-left: 10px; } - /* line 41, ../sass/helpers/_bubbles.scss */ + /* line 54, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-left .l-infobubble::before { right: 100%; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-right: 7.5px solid #dddddd; } - /* line 49, ../sass/helpers/_bubbles.scss */ + /* line 62, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-right { margin-right: 10px; } - /* line 51, ../sass/helpers/_bubbles.scss */ + /* line 64, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-right .l-infobubble::before { left: 100%; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 7.5px solid #dddddd; } - /* line 60, ../sass/helpers/_bubbles.scss */ + /* line 73, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-top .l-infobubble::before { top: 10px; } - /* line 66, ../sass/helpers/_bubbles.scss */ + /* line 79, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-btm .l-infobubble::before { bottom: 10px; } - /* line 71, ../sass/helpers/_bubbles.scss */ + /* line 84, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-down { margin-bottom: 10px; } - /* line 73, ../sass/helpers/_bubbles.scss */ + /* line 86, ../sass/helpers/_bubbles.scss */ .l-infobubble-wrapper.arw-down .l-infobubble::before { left: 50%; top: 100%; @@ -2781,7 +2793,7 @@ input[type="text"] { border-right: 5px solid transparent; border-top: 7.5px solid #dddddd; } -/* line 86, ../sass/helpers/_bubbles.scss */ +/* line 99, ../sass/helpers/_bubbles.scss */ .s-infobubble { -webkit-border-radius: 3px; -moz-border-radius: 3px; @@ -2791,19 +2803,18 @@ input[type="text"] { background: #dddddd; color: #666666; font-size: 0.8rem; } - /* line 92, ../sass/helpers/_bubbles.scss */ + /* line 105, ../sass/helpers/_bubbles.scss */ .s-infobubble .title { color: #333333; - font-weight: bold; - margin-bottom: 5px; } - /* line 98, ../sass/helpers/_bubbles.scss */ + font-weight: bold; } + /* line 110, ../sass/helpers/_bubbles.scss */ .s-infobubble tr td { border-top: 1px solid #c4c4c4; font-size: 0.9em; } - /* line 102, ../sass/helpers/_bubbles.scss */ + /* line 114, ../sass/helpers/_bubbles.scss */ .s-infobubble tr:first-child td { border-top: none; } - /* line 106, ../sass/helpers/_bubbles.scss */ + /* line 118, ../sass/helpers/_bubbles.scss */ .s-infobubble .value { color: #333333; } diff --git a/platform/commonUI/general/res/sass/helpers/_bubbles.scss b/platform/commonUI/general/res/sass/helpers/_bubbles.scss index 849cea01d3..63187c9690 100644 --- a/platform/commonUI/general/res/sass/helpers/_bubbles.scss +++ b/platform/commonUI/general/res/sass/helpers/_bubbles.scss @@ -10,7 +10,7 @@ $infoBubbleBg: #ddd; z-index: 70; .l-infobubble { display: inline-block; - max-width: 400px; + max-width: 250px; padding: 5px 10px; &:before { content:""; @@ -18,22 +18,35 @@ $infoBubbleBg: #ddd; width: 0; height: 0; } - table tr { - td { - max-width: 150px; - padding: 2px 0; - vertical-align: top; - //white-space: nowrap; - //overflow: hidden; - //text-overflow: ellipsis; - &.value { - padding-left: $interiorMargin * 2; - } - &.align-wrap { - white-space: normal; + table { + width: 100%; + tr { + td { + //max-width: 150px; + padding: 2px 0; + vertical-align: top; + //white-space: nowrap; + //overflow: hidden; + //text-overflow: ellipsis; + &.label { + padding-right: $interiorMargin * 2; + white-space: nowrap; + } + &.value { + white-space: nowrap; + //width: 90%; + } + &.align-wrap { + white-space: normal; + } } } } + .title { + @include ellipsize(); + margin-bottom: $interiorMargin; + //max-width: 200px; + } } &.arw-left { @@ -92,7 +105,6 @@ $infoBubbleBg: #ddd; .title { color: $emFg; font-weight: bold; - margin-bottom: $interiorMargin; } tr { td { diff --git a/platform/commonUI/inspect/res/infobubble.html b/platform/commonUI/inspect/res/infobubble.html index 97187a3465..436ba83557 100644 --- a/platform/commonUI/inspect/res/infobubble.html +++ b/platform/commonUI/inspect/res/infobubble.html @@ -6,36 +6,45 @@ bubbles = [ {layout: 'arw-btm arw-right'} ]; +titlex='Egress Scenario scelerisque mauris pid montes nunc ut aliquam elementum tincidunt phasellus 1'; title='Egress Scenario 1'; +propertiesShort=[{label:'Type', value:'Timeline', align:'left'}, +{label:'Start', value:'2015-04-27 00:00:00 UTC', align:'left'}, +{label:'End', value:'2015-04-27 09:15:37 UTC', align:'left'}, +{label:'ID', value:'WRP-T-89', align:'left'}]; + properties=[{label:'Type', value:'Timeline', align:'left'}, {label:'Start', value:'2015-04-27 00:00:00 UTC', align:'left'}, -{label:'End', value:'2015-04-27 09:15:37 UTC', align:'left'}, -{label:'Duration', value:'09:15:37', align:'left'}, -{label:'Created', value:'2015-04-26 06:54:00 UTC', align:'left'}, -{label:'Modified', value:'2015-04-26 18:38:00 UTC', align:'left'}, +{label:'End', value:'2015-04-27 06:07:26 UTC', align:'left'}, +{label:'Duration', value:'06:07:26', align:'left'}, +{label:'Created', value:'2015-04-26 12:27:00 UTC', align:'left'}, +{label:'Modified', value:'2015-04-26 23:21:00 UTC', align:'left'}, {label:'Status', value:'Up to date', align:'left'}, -{label:'URL', value:'http://www.logitech.com/en-us/product/bluetooth-mouse-m557?crid=7', align:'left'}, +{label:'URL', value:'http://www.logitech.com/en-us/product/bluetooth-mouse-m557?crid=7', align:'wrap'}, {label:'Description', value:'Enjoy exceptional battery life for a Bluetooth mouse, and work for up to a full year between battery changes, An On/Off switch helps conserve power, smart sleep mode extends battery life, and an indicator light helps to ensure that you’ll never be caught off guard.', align:'wrap'}, {label:'ID', value:'WRP-T-89', align:'left'}]; ">
+
-
-
{{title}} +
+
+
{{title}} +
+ + + + + +
{{property.label}}{{property.value}}
- - - - - -
{{property.label}}{{property.value}}
\ No newline at end of file From aec1176dc4187462a87b0f3b9cbd81499094a972 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 1 May 2015 10:10:35 -0700 Subject: [PATCH 40/45] [Frontend] Hiding non-functional items; padding adjustments WTD-1163 WTD-1146 New CSS to hide non-functional elements, including browse.top-bar with search elements; Adjusted spacing, mainly in .contents CSS class; --- .../commonUI/browse/res/templates/browse.html | 2 +- .../edit/res/templates/edit-object.html | 2 +- platform/commonUI/general/res/css/items.css | 40 ++-- .../general/res/css/theme-espresso.css | 225 ++++++++++-------- .../res/sass/_hide-non-functional.scss | 25 ++ platform/commonUI/general/res/sass/_main.scss | 1 + .../general/res/sass/items/_item.scss | 7 +- .../general/res/sass/overlay/_overlay.scss | 5 + .../general/res/sass/user-environ/_frame.scss | 7 + .../res/sass/user-environ/_layout.scss | 99 ++++---- .../res/sass/user-environ/_top-bar.scss | 1 + .../res/templates/controls/action-button.html | 2 +- 12 files changed, 259 insertions(+), 157 deletions(-) create mode 100644 platform/commonUI/general/res/sass/_hide-non-functional.scss diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 54ccbae8a7..60b51ccfac 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -1,6 +1,6 @@
-
+
-
.contents, +.user-environ .edit-area > .contents { + left: 0; + right: 0; } +/* line 93, ../sass/user-environ/_layout.scss */ +.user-environ .edit-area .tool-bar { + bottom: auto; + height: 35px; + line-height: 28px; } +/* line 98, ../sass/user-environ/_layout.scss */ +.user-environ .edit-area .work-area { + top: 45px; } +/* line 103, ../sass/user-environ/_layout.scss */ +.user-environ .bottom-bar { + top: auto; + right: 5px; + bottom: 5px; + left: 5px; + height: 20px; } + /* line 109, ../sass/user-environ/_layout.scss */ + .user-environ .bottom-bar .status-holder { + right: 110px; } + /* line 112, ../sass/user-environ/_layout.scss */ + .user-environ .bottom-bar .app-logo { + left: auto; + width: 105px; } + +/* line 119, ../sass/user-environ/_layout.scss */ .cols { overflow: hidden; *zoom: 1; } - /* line 106, ../sass/user-environ/_layout.scss */ + /* line 121, ../sass/user-environ/_layout.scss */ .cols .col { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; @@ -301,89 +316,89 @@ span { margin-left: 1.5%; padding-left: 5px; position: relative; } - /* line 114, ../sass/user-environ/_layout.scss */ + /* line 129, ../sass/user-environ/_layout.scss */ .cols .col:first-child { margin-left: 0; padding-left: 0; } - /* line 121, ../sass/user-environ/_layout.scss */ + /* line 136, ../sass/user-environ/_layout.scss */ .cols.cols-2 .col-1 { min-width: 250px; width: 48.5%; } - /* line 127, ../sass/user-environ/_layout.scss */ + /* line 142, ../sass/user-environ/_layout.scss */ .cols.cols-2-ff .col-100px { width: 100px; } - /* line 134, ../sass/user-environ/_layout.scss */ + /* line 149, ../sass/user-environ/_layout.scss */ .cols.cols-6 .col-1 { min-width: 83.33333px; width: 15.16667%; } - /* line 140, ../sass/user-environ/_layout.scss */ + /* line 155, ../sass/user-environ/_layout.scss */ .cols.cols-16 .col-1 { min-width: 31.25px; width: 4.75%; } - /* line 143, ../sass/user-environ/_layout.scss */ + /* line 158, ../sass/user-environ/_layout.scss */ .cols.cols-16 .col-2 { min-width: 62.5px; width: 11%; } - /* line 146, ../sass/user-environ/_layout.scss */ + /* line 161, ../sass/user-environ/_layout.scss */ .cols.cols-16 .col-7 { min-width: 218.75px; width: 42.25%; } - /* line 152, ../sass/user-environ/_layout.scss */ + /* line 167, ../sass/user-environ/_layout.scss */ .cols.cols-32 .col-2 { min-width: 31.25px; width: 4.75%; } - /* line 155, ../sass/user-environ/_layout.scss */ + /* line 170, ../sass/user-environ/_layout.scss */ .cols.cols-32 .col-15 { min-width: 234.375px; width: 45.375%; } - /* line 159, ../sass/user-environ/_layout.scss */ + /* line 174, ../sass/user-environ/_layout.scss */ .cols .l-row { overflow: hidden; *zoom: 1; padding: 5px 0; } -/* line 165, ../sass/user-environ/_layout.scss */ +/* line 180, ../sass/user-environ/_layout.scss */ .pane { position: absolute; } - /* line 168, ../sass/user-environ/_layout.scss */ + /* line 183, ../sass/user-environ/_layout.scss */ .pane.treeview .create-btn-holder { bottom: auto; height: 35px; } - /* line 171, ../sass/user-environ/_layout.scss */ + /* line 186, ../sass/user-environ/_layout.scss */ .pane.treeview .tree-holder { overflow: auto; top: 40px; } - /* line 180, ../sass/user-environ/_layout.scss */ + /* line 195, ../sass/user-environ/_layout.scss */ .pane.items .object-holder { top: 40px; } - /* line 185, ../sass/user-environ/_layout.scss */ + /* line 200, ../sass/user-environ/_layout.scss */ .pane.edit-main .object-holder { top: 0; } - /* line 191, ../sass/user-environ/_layout.scss */ + /* line 206, ../sass/user-environ/_layout.scss */ .pane .object-holder { overflow: auto; } -/* line 199, ../sass/user-environ/_layout.scss */ +/* line 214, ../sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane { margin-top: 5px; } - /* line 202, ../sass/user-environ/_layout.scss */ + /* line 217, ../sass/user-environ/_layout.scss */ .split-layout.horizontal > .pane:first-child { margin-top: 0; } -/* line 209, ../sass/user-environ/_layout.scss */ +/* line 224, ../sass/user-environ/_layout.scss */ .split-layout.vertical > .pane { margin-left: 5px; } - /* line 211, ../sass/user-environ/_layout.scss */ + /* line 226, ../sass/user-environ/_layout.scss */ .split-layout.vertical > .pane > .holder { left: 0; right: 0; } - /* line 215, ../sass/user-environ/_layout.scss */ + /* line 230, ../sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child { margin-left: 0; } - /* line 217, ../sass/user-environ/_layout.scss */ + /* line 232, ../sass/user-environ/_layout.scss */ .split-layout.vertical > .pane:first-child .holder { right: 5px; } -/* line 226, ../sass/user-environ/_layout.scss */ +/* line 241, ../sass/user-environ/_layout.scss */ .vscroll { overflow-y: auto; } @@ -2573,6 +2588,10 @@ input[type="text"] { margin-left: 10px; } /* line 53, ../sass/overlay/_overlay.scss */ .overlay .contents.l-dialog { + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; overflow: auto; } /* line 4, ../sass/user-environ/_frame.scss */ @@ -2589,8 +2608,14 @@ input[type="text"] { /* line 18, ../sass/user-environ/_frame.scss */ .frame > .object-holder.abs, .btn-menu .frame > span.object-holder.l-click-area { top: 23px; } +/* line 21, ../sass/user-environ/_frame.scss */ +.frame .contents { + top: 5px; + right: 5px; + bottom: 5px; + left: 5px; } -/* line 24, ../sass/user-environ/_frame.scss */ +/* line 31, ../sass/user-environ/_frame.scss */ .edit-main .frame.child-frame.panel:hover { border-color: #0099cc; -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; @@ -2602,27 +2627,28 @@ input[type="text"] { line-height: 35px; } /* line 7, ../sass/user-environ/_top-bar.scss */ .top-bar.browse, .top-bar.edit { + border-bottom: 1px solid #4d4d4d; top: 5px; right: 5px; bottom: auto; left: 5px; height: 35px; } - /* line 15, ../sass/user-environ/_top-bar.scss */ + /* line 16, ../sass/user-environ/_top-bar.scss */ .top-bar .title { color: #fff; } - /* line 20, ../sass/user-environ/_top-bar.scss */ + /* line 21, ../sass/user-environ/_top-bar.scss */ .top-bar .buttons-main { font-size: 0.8em; left: auto; text-align: right; } - /* line 25, ../sass/user-environ/_top-bar.scss */ + /* line 26, ../sass/user-environ/_top-bar.scss */ .top-bar .buttons-main .btn { margin-left: 5px; } -/* line 33, ../sass/user-environ/_top-bar.scss */ +/* line 34, ../sass/user-environ/_top-bar.scss */ .edit-mode .top-bar .buttons-main { white-space: nowrap; } - /* line 37, ../sass/user-environ/_top-bar.scss */ + /* line 38, ../sass/user-environ/_top-bar.scss */ .edit-mode .top-bar .buttons-main.abs, .edit-mode .top-bar .btn-menu span.buttons-main.l-click-area, .btn-menu .edit-mode .top-bar span.buttons-main.l-click-area { bottom: auto; left: auto; } @@ -2882,10 +2908,10 @@ input[type="text"] { .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; + -webkit-animation: rotation 0.6s infinite linear; + -moz-animation: rotation 0.6s infinite linear; + -o-animation: rotation 0.6s infinite linear; + animation: rotation 0.6s infinite linear; border-color: rgba(0, 153, 204, 0.25); border-top-color: #0099cc; border-style: solid; @@ -2924,10 +2950,10 @@ input[type="text"] { .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; + -webkit-animation: rotation 0.6s infinite linear; + -moz-animation: rotation 0.6s infinite linear; + -o-animation: rotation 0.6s infinite linear; + animation: rotation 0.6s infinite linear; border-color: rgba(0, 153, 204, 0.25); border-top-color: #0099cc; border-style: solid; @@ -3041,8 +3067,19 @@ input[type="text"] { padding-left: 0; } /* Styles for the iframe EmbeddedPageController element */ -/* line 4, ../sass/iframe.scss */ +/* line 4, ../sass/_iframe.scss */ .l-iframe iframe { display: block; height: 100%; width: 100%; } + +/******************************** BROWSE */ +/* line 6, ../sass/_hide-non-functional.scss */ +.browse-mode .browse.top-bar { + display: none; } +/* line 11, ../sass/_hide-non-functional.scss */ +.browse-mode .browse-area.holder { + top: 5px; } + /* line 18, ../sass/_hide-non-functional.scss */ + .browse-mode .browse-area.holder > .contents.split-layout .object-browse-bar .t-btn.key-window { + display: none; } diff --git a/platform/commonUI/general/res/sass/_hide-non-functional.scss b/platform/commonUI/general/res/sass/_hide-non-functional.scss new file mode 100644 index 0000000000..2361480712 --- /dev/null +++ b/platform/commonUI/general/res/sass/_hide-non-functional.scss @@ -0,0 +1,25 @@ +// Styles to temporarily hide non-functional elements + +/******************************** BROWSE */ +.browse-mode { + .browse { + &.top-bar { + display: none; + } + } + + .browse-area.holder { + // When .browse.top-bar is hidden, set the top of the browse-area holder + top: $interiorMargin; + > .contents.split-layout { + // Don't pad in from top and bottom + //top: 0; bottom: 0; + .object-browse-bar { + .t-btn.key-window { + // Hide the Open in New Window button + display: none; + } + } + } + } +} \ 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 99bf5b6322..f4dac4f816 100755 --- a/platform/commonUI/general/res/sass/_main.scss +++ b/platform/commonUI/general/res/sass/_main.scss @@ -41,3 +41,4 @@ @import "properties"; @import "autoflow"; @import "iframe"; +@import "hide-non-functional"; diff --git a/platform/commonUI/general/res/sass/items/_item.scss b/platform/commonUI/general/res/sass/items/_item.scss index e585450e39..162e7fe496 100644 --- a/platform/commonUI/general/res/sass/items/_item.scss +++ b/platform/commonUI/general/res/sass/items/_item.scss @@ -1,7 +1,9 @@ .items-holder { @include clearfix; overflow-y: auto; - .contents { top: 0; } + .contents { + top: 0; + } .item { &.grid-item { $d: $ueBrowseGridItemLg; @@ -23,6 +25,9 @@ display: block; } } + .contents { + top: $interiorMargin; right: $interiorMargin; bottom: $interiorMargin; left: $interiorMargin; + } .bar { &.top-bar.abs { bottom: auto; diff --git a/platform/commonUI/general/res/sass/overlay/_overlay.scss b/platform/commonUI/general/res/sass/overlay/_overlay.scss index 1ea122d838..731923a8f0 100644 --- a/platform/commonUI/general/res/sass/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/overlay/_overlay.scss @@ -51,6 +51,11 @@ } } .contents.l-dialog { + $myM: $interiorMargin; + top: $myM; + right: $myM; + bottom: $myM; + left: $myM; overflow: auto; } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/user-environ/_frame.scss b/platform/commonUI/general/res/sass/user-environ/_frame.scss index 9f9a6409f4..eb370535eb 100644 --- a/platform/commonUI/general/res/sass/user-environ/_frame.scss +++ b/platform/commonUI/general/res/sass/user-environ/_frame.scss @@ -18,6 +18,13 @@ >.object-holder.abs { top: $ohH + $interiorMarginSm; } + .contents { + $myM: $interiorMargin; + top: $myM; + right: $myM; + bottom: $myM; + left: $myM; + } } .edit-main .frame.child-frame.panel { diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 810a0c412e..e689e0e522 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -8,57 +8,26 @@ } } +.holder-all { + $myM: $interiorMargin; + top: $myM; + right: $myM; + bottom: $myM; + left: $myM; +} + .browse-area, .edit-area, .editor { - @include border-radius($basicCr * 1.5); position: absolute; - .contents { -// overflow-y: auto; - } } -.user-environ { - .browse-area, - .edit-area, - .editor { - top: $bodyMargin + $ueTopBarH + ($interiorMargin); - right: $bodyMargin; - bottom: $bodyMargin + $ueFooterH + $interiorMargin; - left: $bodyMargin; - } - - .edit-area { - $tbH: $ueEditToolBarH; - .tool-bar { - bottom: auto; - height: $tbH; - line-height: $ueEditToolBarButtonH; - } - .work-area { - top: $tbH + $interiorMargin * 2; - } - } - - .bottom-bar { - top: auto; - right: $bodyMargin; - bottom: $bodyMargin; - left: $bodyMargin; - height: $ueFooterH; - .status-holder { - right: $ueAppLogoW + $bodyMargin; - } - .app-logo { - left: auto; - width: $ueAppLogoW; - } - } +.editor { + @include border-radius($basicCr * 1.5); } - .contents { - $myM: $interiorMargin; + $myM: 0; //$interiorMargin; box-sizing: border-box; position: absolute; top: $myM; @@ -101,6 +70,52 @@ } } +.user-environ { + .browse-area, + .edit-area, + .editor { + top: $bodyMargin + $ueTopBarH + ($interiorMargin); + right: $bodyMargin; + bottom: $bodyMargin + $ueFooterH + $interiorMargin; + left: $bodyMargin; + } + + .browse-area, + .edit-area { + > .contents { + left: 0; + right: 0; + } + } + + .edit-area { + $tbH: $ueEditToolBarH; + .tool-bar { + bottom: auto; + height: $tbH; + line-height: $ueEditToolBarButtonH; + } + .work-area { + top: $tbH + $interiorMargin * 2; + } + } + + .bottom-bar { + top: auto; + right: $bodyMargin; + bottom: $bodyMargin; + left: $bodyMargin; + height: $ueFooterH; + .status-holder { + right: $ueAppLogoW + $bodyMargin; + } + .app-logo { + left: auto; + width: $ueAppLogoW; + } + } +} + .cols { @include clearfix; .col { diff --git a/platform/commonUI/general/res/sass/user-environ/_top-bar.scss b/platform/commonUI/general/res/sass/user-environ/_top-bar.scss index 17292e34e6..d70fbf0533 100644 --- a/platform/commonUI/general/res/sass/user-environ/_top-bar.scss +++ b/platform/commonUI/general/res/sass/user-environ/_top-bar.scss @@ -5,6 +5,7 @@ &.browse, &.edit { + border-bottom: 1px solid $colorInteriorBorder; top: $bodyMargin; right: $bodyMargin; bottom: auto; left: $bodyMargin; height: $ueTopBarH; } diff --git a/platform/commonUI/general/res/templates/controls/action-button.html b/platform/commonUI/general/res/templates/controls/action-button.html index 169e015470..79c639eeed 100644 --- a/platform/commonUI/general/res/templates/controls/action-button.html +++ b/platform/commonUI/general/res/templates/controls/action-button.html @@ -1,4 +1,4 @@ - From 6aadf921e19f956342ec46c93362aec9733b66b2 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 1 May 2015 10:18:33 -0700 Subject: [PATCH 41/45] [Frontend] Tweaks to spacing WTD-1163 WTD-1146 --- platform/commonUI/general/res/css/theme-espresso.css | 8 ++++---- .../commonUI/general/res/sass/user-environ/_layout.scss | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/commonUI/general/res/css/theme-espresso.css b/platform/commonUI/general/res/css/theme-espresso.css index 51babfdad7..a6e9951bb9 100644 --- a/platform/commonUI/general/res/css/theme-espresso.css +++ b/platform/commonUI/general/res/css/theme-espresso.css @@ -205,10 +205,10 @@ span { /* line 11, ../sass/user-environ/_layout.scss */ .holder-all { - top: 5px; - right: 5px; - bottom: 5px; - left: 5px; } + top: 3px; + right: 3px; + bottom: 3px; + left: 3px; } /* line 21, ../sass/user-environ/_layout.scss */ .browse-area, diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index e689e0e522..ece5675ee0 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -9,7 +9,7 @@ } .holder-all { - $myM: $interiorMargin; + $myM: $interiorMarginSm; top: $myM; right: $myM; bottom: $myM; From ac81968f93fbe6a39b07d5f915342d5d19be0b6f Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 1 May 2015 10:43:15 -0700 Subject: [PATCH 42/45] [Frontend] CSS in Fixed Position view Edit mode WTD-1163 WTD-1114 Added CSS to give a border to all elements in Fixed Position view when in Edit mode. Only applied to outer element, does not affect object border styling; --- .../general/res/css/theme-espresso.css | 41 ++++++++++--------- .../general/res/sass/_fixed-position.scss | 38 ++++++++++------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/platform/commonUI/general/res/css/theme-espresso.css b/platform/commonUI/general/res/css/theme-espresso.css index a6e9951bb9..be65449fd4 100644 --- a/platform/commonUI/general/res/css/theme-espresso.css +++ b/platform/commonUI/general/res/css/theme-espresso.css @@ -411,33 +411,33 @@ span { left: 0; width: auto; height: auto; } - /* line 8, ../sass/_fixed-position.scss */ + /* line 12, ../sass/_fixed-position.scss */ .t-fixed-position.l-fixed-position .l-grid-holder { position: relative; height: 100%; width: 100%; } - /* line 11, ../sass/_fixed-position.scss */ + /* line 16, ../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 21, ../sass/_fixed-position.scss */ +/* line 27, ../sass/_fixed-position.scss */ .t-fixed-position .l-fixed-position-item { position: absolute; border: 1px solid transparent; } - /* line 25, ../sass/_fixed-position.scss */ + /* line 31, ../sass/_fixed-position.scss */ .t-fixed-position .l-fixed-position-item.s-selected { -webkit-box-shadow: rgba(0, 0, 0, 0.7) 0 3px 10px; -moz-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 30, ../sass/_fixed-position.scss */ + /* line 36, ../sass/_fixed-position.scss */ .t-fixed-position .l-fixed-position-item.s-not-selected { opacity: 0.8; } - /* line 36, ../sass/_fixed-position.scss */ + /* line 42, ../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 { @@ -446,20 +446,20 @@ span { box-sizing: border-box; height: 100%; width: 100%; } - /* line 44, ../sass/_fixed-position.scss */ + /* line 51, ../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 50, ../sass/_fixed-position.scss */ + /* line 57, ../sass/_fixed-position.scss */ .t-fixed-position .l-fixed-position-item .l-fixed-position-text { text-shadow: rgba(0, 0, 0, 0.1) 0 1px 2px; border: 1px solid transparent; font-size: 0.8rem; } - /* line 55, ../sass/_fixed-position.scss */ + /* line 62, ../sass/_fixed-position.scss */ .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-static-text { padding: 3px; } - /* line 60, ../sass/_fixed-position.scss */ + /* line 67, ../sass/_fixed-position.scss */ .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem { overflow: hidden; position: absolute; @@ -473,43 +473,46 @@ span { -moz-box-sizing: border-box; box-sizing: border-box; width: 50%; } - /* line 64, ../sass/_fixed-position.scss */ + /* line 71, ../sass/_fixed-position.scss */ .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem.l-title { right: auto; left: 3px; } - /* line 68, ../sass/_fixed-position.scss */ + /* line 75, ../sass/_fixed-position.scss */ .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem.l-value { right: 3px; left: auto; text-align: right; } - /* line 73, ../sass/_fixed-position.scss */ + /* line 80, ../sass/_fixed-position.scss */ .t-fixed-position .l-fixed-position-item .l-fixed-position-text.l-telemetry .l-elem.l-value.telem-only { left: 3px; width: auto; } -/* line 84, ../sass/_fixed-position.scss */ +/* line 91, ../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 98, ../sass/_fixed-position.scss */ +/* line 105, ../sass/_fixed-position.scss */ .edit-mode .t-fixed-position.l-fixed-position .l-grid-holder .l-grid.l-grid-x { background-image: -webkit-linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px, transparent 100%); background-image: -moz-linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px, transparent 100%); background-image: -o-linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px, transparent 100%); background-image: linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px, transparent 100%); background-repeat: repeat-x; } -/* line 102, ../sass/_fixed-position.scss */ +/* line 109, ../sass/_fixed-position.scss */ .edit-mode .t-fixed-position.l-fixed-position .l-grid-holder .l-grid.l-grid-y { background-image: -webkit-linear-gradient(0deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px, transparent 100%); background-image: -moz-linear-gradient(0deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px, transparent 100%); background-image: -o-linear-gradient(0deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px, transparent 100%); background-image: linear-gradient(0deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px, transparent 100%); background-repeat: repeat-y; } -/* line 110, ../sass/_fixed-position.scss */ -.edit-mode .t-fixed-position .l-fixed-position-item:not(.s-selected):hover { - border: 1px dotted rgba(0, 153, 204, 0.5); } +/* line 117, ../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 119, ../sass/_fixed-position.scss */ + .edit-mode .t-fixed-position .l-fixed-position-item:not(.s-selected):hover { + border: 1px dotted #0099cc; } /* line 5, ../sass/_about.scss */ .l-about.abs, .btn-menu span.l-about.l-click-area { diff --git a/platform/commonUI/general/res/sass/_fixed-position.scss b/platform/commonUI/general/res/sass/_fixed-position.scss index 577850ed61..ab81688b49 100644 --- a/platform/commonUI/general/res/sass/_fixed-position.scss +++ b/platform/commonUI/general/res/sass/_fixed-position.scss @@ -1,17 +1,23 @@ .t-fixed-position { &.l-fixed-position { -// @include test(red); + // @include test(red); position: absolute; - top: 0; right: 0; bottom: 0; left: 0; - width: auto; height: auto; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: auto; + height: auto; .l-grid-holder { position: relative; - height: 100%; width: 100%; + height: 100%; + width: 100%; .l-grid { -// @include test(orange); + // @include test(orange); position: absolute; - height: 100%; width: 100%; + height: 100%; + width: 100%; pointer-events: none; z-index: 0; } @@ -35,12 +41,13 @@ .l-fixed-position-image, .l-fixed-position-text { @include box-sizing(border-box); - height: 100%; width: 100%; + height: 100%; + width: 100%; } .l-fixed-position-box { } - + .l-fixed-position-image { background-size: cover; background-repeat: no-repeat; @@ -49,11 +56,11 @@ .l-fixed-position-text { @include txtShdwSubtle(); - border:1px solid transparent; + border: 1px solid transparent; font-size: 0.8rem; $p: $interiorMarginSm; &.l-static-text { -// overflow: auto; + // overflow: auto; padding: $p; } &.l-telemetry { @@ -66,12 +73,12 @@ left: $p; } &.l-value { -// @include test(blue); + // @include test(blue); right: $p; left: auto; text-align: right; &.telem-only { -// @include test(red); + // @include test(red); left: $p; width: auto; } @@ -107,8 +114,11 @@ } .l-fixed-position-item { - &:not(.s-selected):hover { - border: 1px dotted rgba($colorKey, 0.5); + &:not(.s-selected) { + border: 1px dotted rgba($colorKey, 0.75); + &:hover { + border: 1px dotted rgba($colorKey, 1.0); + } } } } \ No newline at end of file From ad9a0b4ee233617e22b95968323152740fd38400 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 1 May 2015 10:39:50 -0700 Subject: [PATCH 43/45] [Info] Remove inspect from active bundles Remove markup demonstration for info bubbles from list of active bundles, WTD-1162. --- bundles.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/bundles.json b/bundles.json index 336aba67b8..04515b1897 100644 --- a/bundles.json +++ b/bundles.json @@ -6,13 +6,11 @@ "platform/commonUI/browse", "platform/commonUI/edit", "platform/commonUI/dialog", - "platform/commonUI/inspect", "platform/commonUI/general", "platform/containment", "platform/telemetry", "platform/features/layout", "platform/features/pages", - "platform/features/plot", "platform/features/scrolling", "platform/forms", From bfb3a0b57b06c181fd9f29b6c96be87930e66cdc Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 4 May 2015 13:31:25 -0700 Subject: [PATCH 44/45] [Build] Remove snapshot status Remove SNAPSHOT from version number to close out sprint Skoll, WTD-820. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af26614f96..36d7705d54 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ gov.nasa.arc.wtd open-mct-web Open MCT Web - 0.6.2-SNAPSHOT + 0.6.2 war From 2be2e23847ef8f978c7db05d3c9f1f8a0ec08dc3 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 4 May 2015 13:36:49 -0700 Subject: [PATCH 45/45] [Build] Bump version number Bump version number, restore snapshot status to open sprint Surtur. WTD-820. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 36d7705d54..783cc8ce45 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ gov.nasa.arc.wtd open-mct-web Open MCT Web - 0.6.2 + 0.7.0-SNAPSHOT war