Compare commits
	
		
			167 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d926110b4e | ||
| 
						 | 
					485e6a9de1 | ||
| 
						 | 
					1e6731e6f2 | ||
| 
						 | 
					ec56fe7bfd | ||
| 
						 | 
					4abb48abd8 | ||
| 
						 | 
					a38d4829eb | ||
| 
						 | 
					d5f1d45759 | ||
| 
						 | 
					5ed34c1c30 | ||
| 
						 | 
					bc82a5bf7e | ||
| 
						 | 
					5af3d575a2 | ||
| 
						 | 
					58198636ed | ||
| 
						 | 
					86bf6c6dff | ||
| 
						 | 
					0e07fb860e | ||
| 
						 | 
					b3d4f48e2e | ||
| 
						 | 
					df3b0bd6fb | ||
| 
						 | 
					5b475c9f64 | ||
| 
						 | 
					a48370abd3 | ||
| 
						 | 
					54d608adb2 | ||
| 
						 | 
					5fba6f5ead | ||
| 
						 | 
					b177c38656 | ||
| 
						 | 
					59cd346911 | ||
| 
						 | 
					e7e66bff4b | ||
| 
						 | 
					ca62cc9066 | ||
| 
						 | 
					01b6fda1f2 | ||
| 
						 | 
					ae0cb63a92 | ||
| 
						 | 
					5604bf6d69 | ||
| 
						 | 
					c5fcc5a558 | ||
| 
						 | 
					ea9f607bba | ||
| 
						 | 
					0404303042 | ||
| 
						 | 
					5677548298 | ||
| 
						 | 
					bcc42d705e | ||
| 
						 | 
					ab008ae497 | ||
| 
						 | 
					3d59f6df0b | ||
| 
						 | 
					4708ac0ec1 | ||
| 
						 | 
					bd984abc0f | ||
| 
						 | 
					b4a44dee8f | ||
| 
						 | 
					942f617bd8 | ||
| 
						 | 
					757cb0f015 | ||
| 
						 | 
					a2d06583ca | ||
| 
						 | 
					5d5425db04 | ||
| 
						 | 
					a8856c0612 | ||
| 
						 | 
					74f289cb34 | ||
| 
						 | 
					4ec243c6fb | ||
| 
						 | 
					407d9881ff | ||
| 
						 | 
					6ee622b3f5 | ||
| 
						 | 
					95ea33b441 | ||
| 
						 | 
					099d70b8d9 | ||
| 
						 | 
					3d996ac466 | ||
| 
						 | 
					896dd8d2c7 | ||
| 
						 | 
					789b640b9b | ||
| 
						 | 
					90828ef63d | ||
| 
						 | 
					29bdc9d574 | ||
| 
						 | 
					bf24ac7c93 | ||
| 
						 | 
					e05858e26b | ||
| 
						 | 
					89046ecad5 | ||
| 
						 | 
					fa7431d68b | ||
| 
						 | 
					78b528b4a5 | ||
| 
						 | 
					59f094763b | ||
| 
						 | 
					e3ef68bc6f | ||
| 
						 | 
					83276d70d3 | ||
| 
						 | 
					109ae3323d | ||
| 
						 | 
					0053989de8 | ||
| 
						 | 
					0628398b01 | ||
| 
						 | 
					dc5feb8b1a | ||
| 
						 | 
					3080861764 | ||
| 
						 | 
					dbebf08500 | ||
| 
						 | 
					847c356063 | ||
| 
						 | 
					06bcd28558 | ||
| 
						 | 
					f88e8ebb51 | ||
| 
						 | 
					6d2b2fd81e | ||
| 
						 | 
					608800ae63 | ||
| 
						 | 
					07818b0a6d | ||
| 
						 | 
					496cf85b7e | ||
| 
						 | 
					833f57e284 | ||
| 
						 | 
					cc97408433 | ||
| 
						 | 
					93f8e61c40 | ||
| 
						 | 
					9a63e99710 | ||
| 
						 | 
					21739fffd9 | ||
| 
						 | 
					77d81f899b | ||
| 
						 | 
					fe3263fdfe | ||
| 
						 | 
					ce42429fbd | ||
| 
						 | 
					76151d09a0 | ||
| 
						 | 
					ec7e6cc5b4 | ||
| 
						 | 
					1ddce48f7e | ||
| 
						 | 
					98b5ff3c77 | ||
| 
						 | 
					14094a48fc | ||
| 
						 | 
					8e2a2eeba5 | ||
| 
						 | 
					0f63e4dde9 | ||
| 
						 | 
					12efb47be7 | ||
| 
						 | 
					a2fce8e56c | ||
| 
						 | 
					78e5c0143b | ||
| 
						 | 
					099591ad2e | ||
| 
						 | 
					b5505f372f | ||
| 
						 | 
					9ad860babd | ||
| 
						 | 
					87e317a6f5 | ||
| 
						 | 
					9d17768327 | ||
| 
						 | 
					688718cad0 | ||
| 
						 | 
					efb7611f6e | ||
| 
						 | 
					d3ff0a258e | ||
| 
						 | 
					4f18663c71 | ||
| 
						 | 
					8c2a29e895 | ||
| 
						 | 
					f08725b6a2 | ||
| 
						 | 
					301b73c6c6 | ||
| 
						 | 
					912e70d219 | ||
| 
						 | 
					1d41939418 | ||
| 
						 | 
					ee382be38d | ||
| 
						 | 
					34ea3ad9bb | ||
| 
						 | 
					2ba6f18c59 | ||
| 
						 | 
					b2a09599a0 | ||
| 
						 | 
					11264759ec | ||
| 
						 | 
					c0ff6de27b | ||
| 
						 | 
					60dda8a7a4 | ||
| 
						 | 
					5b617295e9 | ||
| 
						 | 
					53a3a2f459 | ||
| 
						 | 
					64fae21d16 | ||
| 
						 | 
					87f48aac35 | ||
| 
						 | 
					e43a788a6d | ||
| 
						 | 
					fa487e026e | ||
| 
						 | 
					3701fd75dd | ||
| 
						 | 
					d787e84fd4 | ||
| 
						 | 
					1922e1e241 | ||
| 
						 | 
					e52f53b7ff | ||
| 
						 | 
					d1be256691 | ||
| 
						 | 
					82ae9e72c1 | ||
| 
						 | 
					85300d3743 | ||
| 
						 | 
					fe3e3325e1 | ||
| 
						 | 
					2b97d61d6c | ||
| 
						 | 
					954fdd5906 | ||
| 
						 | 
					8267058487 | ||
| 
						 | 
					4a913376ac | ||
| 
						 | 
					5ff3c71523 | ||
| 
						 | 
					ba614fe2d6 | ||
| 
						 | 
					5e713f279b | ||
| 
						 | 
					df590107cb | ||
| 
						 | 
					fc8630dbc6 | ||
| 
						 | 
					12a94f828a | ||
| 
						 | 
					0e840ae003 | ||
| 
						 | 
					e1e5919f68 | ||
| 
						 | 
					b2cd66bd5d | ||
| 
						 | 
					af1fa6e77a | ||
| 
						 | 
					5ff90f7254 | ||
| 
						 | 
					0ca9e5c952 | ||
| 
						 | 
					dbcad51325 | ||
| 
						 | 
					8ee93d9603 | ||
| 
						 | 
					ac59df9595 | ||
| 
						 | 
					182eff977c | ||
| 
						 | 
					3af23b7bc5 | ||
| 
						 | 
					7f529eec68 | ||
| 
						 | 
					ad4292f1e9 | ||
| 
						 | 
					6840e596a5 | ||
| 
						 | 
					bf41d82a78 | ||
| 
						 | 
					a4944717a1 | ||
| 
						 | 
					70bbd3cf97 | ||
| 
						 | 
					e3afaf0842 | ||
| 
						 | 
					60f2f9fb6c | ||
| 
						 | 
					30fd8c451e | ||
| 
						 | 
					bf0014f1b9 | ||
| 
						 | 
					2aeebff652 | ||
| 
						 | 
					471a25a625 | ||
| 
						 | 
					d606ee421f | ||
| 
						 | 
					926aed72c3 | ||
| 
						 | 
					7014808c13 | ||
| 
						 | 
					37dede568c | ||
| 
						 | 
					b3fb06ba3f | ||
| 
						 | 
					b7a612127d | ||
| 
						 | 
					2f4cf44229 | ||
| 
						 | 
					73e959f95a | 
							
								
								
									
										2
									
								
								Procfile
									
									
									
									
									
								
							
							
						
						@@ -1 +1 @@
 | 
			
		||||
web: node app.js --port $PORT --include example/localstorage
 | 
			
		||||
web: node app.js --port $PORT
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
    "platform/commonUI/inspect",
 | 
			
		||||
    "platform/commonUI/mobile",
 | 
			
		||||
    "platform/commonUI/themes/espresso",
 | 
			
		||||
    "platform/commonUI/notification",
 | 
			
		||||
    "platform/containment",
 | 
			
		||||
    "platform/execution",
 | 
			
		||||
    "platform/telemetry",
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,8 @@
 | 
			
		||||
var CONSTANTS = {
 | 
			
		||||
        DIAGRAM_WIDTH: 800,
 | 
			
		||||
        DIAGRAM_HEIGHT: 500
 | 
			
		||||
    };
 | 
			
		||||
    },
 | 
			
		||||
    TOC_HEAD = "# Table of Contents";
 | 
			
		||||
 | 
			
		||||
GLOBAL.window = GLOBAL.window ||  GLOBAL; // nomnoml expects window to be defined
 | 
			
		||||
(function () {
 | 
			
		||||
@@ -44,6 +45,7 @@ GLOBAL.window = GLOBAL.window ||  GLOBAL; // nomnoml expects window to be define
 | 
			
		||||
        split = require("split"),
 | 
			
		||||
        stream = require("stream"),
 | 
			
		||||
        nomnoml = require('nomnoml'),
 | 
			
		||||
        toc = require("markdown-toc"),
 | 
			
		||||
        Canvas = require('canvas'),
 | 
			
		||||
        options = require("minimist")(process.argv.slice(2));
 | 
			
		||||
 | 
			
		||||
@@ -110,6 +112,9 @@ GLOBAL.window = GLOBAL.window ||  GLOBAL; // nomnoml expects window to be define
 | 
			
		||||
            done();
 | 
			
		||||
        };
 | 
			
		||||
        transform._flush = function (done) {
 | 
			
		||||
            // Prepend table of contents
 | 
			
		||||
            markdown =
 | 
			
		||||
                [ TOC_HEAD, toc(markdown).content, "", markdown ].join("\n");
 | 
			
		||||
            this.push("<html><body>\n");
 | 
			
		||||
            this.push(marked(markdown));
 | 
			
		||||
            this.push("\n</body></html>\n");
 | 
			
		||||
@@ -133,8 +138,8 @@ GLOBAL.window = GLOBAL.window ||  GLOBAL; // nomnoml expects window to be define
 | 
			
		||||
        customRenderer.link = function (href, title, text) {
 | 
			
		||||
            // ...but only if they look like relative paths
 | 
			
		||||
            return (href || "").indexOf(":") === -1 && href[0] !== "/" ?
 | 
			
		||||
                renderer.link(href.replace(/\.md/, ".html"), title, text) :
 | 
			
		||||
                renderer.link.apply(renderer, arguments);
 | 
			
		||||
                    renderer.link(href.replace(/\.md/, ".html"), title, text) :
 | 
			
		||||
                    renderer.link.apply(renderer, arguments);
 | 
			
		||||
        };
 | 
			
		||||
        return customRenderer;
 | 
			
		||||
    }
 | 
			
		||||
@@ -179,13 +184,17 @@ GLOBAL.window = GLOBAL.window ||  GLOBAL; // nomnoml expects window to be define
 | 
			
		||||
    glob(options['in'] + "/**/*.@(html|css|png)", {}, function (err, files) {
 | 
			
		||||
        files.forEach(function (file) {
 | 
			
		||||
            var destination = file.replace(options['in'], options.out),
 | 
			
		||||
                destPath = path.dirname(destination);
 | 
			
		||||
 | 
			
		||||
                destPath = path.dirname(destination),
 | 
			
		||||
                streamOptions = {};
 | 
			
		||||
            if (file.match(/png$/)){
 | 
			
		||||
                streamOptions.encoding = null;
 | 
			
		||||
            } else {
 | 
			
		||||
                streamOptions.encoding = 'utf8';
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            mkdirp(destPath, function (err) {
 | 
			
		||||
                fs.createReadStream(file, { encoding: 'utf8' })
 | 
			
		||||
                    .pipe(fs.createWriteStream(destination, {
 | 
			
		||||
                        encoding: 'utf8'
 | 
			
		||||
                    }));
 | 
			
		||||
                fs.createReadStream(file, streamOptions)
 | 
			
		||||
                    .pipe(fs.createWriteStream(destination, streamOptions));
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -35,16 +35,26 @@ in __any of these tiers__.
 | 
			
		||||
* _DOM_: The rendered HTML document, composed from HTML templates which
 | 
			
		||||
  have been processed by AngularJS and will be updated by AngularJS
 | 
			
		||||
  to reflect changes from the presentation layer. User interactions
 | 
			
		||||
  are initiated from here and invoke behavior in the presentation layer.
 | 
			
		||||
  are initiated from here and invoke behavior in the presentation layer. HTML 
 | 
			
		||||
  templates are written in Angular’s template syntax; see the [Angular documentation on templates](https://docs.angularjs.org/guide/templates). 
 | 
			
		||||
  These describe the page as actually seen by the user. Conceptually, 
 | 
			
		||||
  stylesheets (controlling the lookandfeel of the rendered templates) belong 
 | 
			
		||||
  in this grouping as well. 
 | 
			
		||||
* [_Presentation layer_](#presentation-layer): The presentation layer
 | 
			
		||||
  is responsible for updating (and providing information to update)
 | 
			
		||||
  the displayed state of the application. The presentation layer consists
 | 
			
		||||
  primarily of _controllers_ and _directives_. The presentation layer is
 | 
			
		||||
  concerned with inspecting the information model and preparing it for
 | 
			
		||||
  display.
 | 
			
		||||
* [_Information model_](#information-model): The information model
 | 
			
		||||
  describes the state and behavior of the objects with which the user
 | 
			
		||||
  interacts.
 | 
			
		||||
* [_Information model_](#information-model): Provides a common (within Open MCT 
 | 
			
		||||
  Web) set of interfaces for dealing with “things”  domain objects  within the 
 | 
			
		||||
  system. Userfacing concerns in a Open MCT Web application are expressed as 
 | 
			
		||||
  domain objects; examples include folders (used to organize other domain 
 | 
			
		||||
  objects), layouts (used to build displays), or telemetry points (used as 
 | 
			
		||||
  handles for streams of remote measurements.) These domain objects expose a 
 | 
			
		||||
  common set of interfaces to allow reusable user interfaces to be built in the 
 | 
			
		||||
  presentation and template tiers; the specifics of these behaviors are then 
 | 
			
		||||
  mapped to interactions with underlying services. 
 | 
			
		||||
* [_Service infrastructure_](#service-infrastructure): The service
 | 
			
		||||
  infrastructure is responsible for providing the underlying general
 | 
			
		||||
  functionality needed to support the information model. This includes
 | 
			
		||||
@@ -52,7 +62,9 @@ in __any of these tiers__.
 | 
			
		||||
  back-end.
 | 
			
		||||
* _Back-end_: The back-end is out of the scope of Open MCT Web, except
 | 
			
		||||
  for the interfaces which are utilized by adapters participating in the
 | 
			
		||||
  service infrastructure.
 | 
			
		||||
  service infrastructure. Includes the underlying persistence stores, telemetry 
 | 
			
		||||
  streams, and so forth which the Open MCT Web client is being used to interact 
 | 
			
		||||
  with.
 | 
			
		||||
 | 
			
		||||
## Application Start-up
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,9 @@
 | 
			
		||||
    Sections:
 | 
			
		||||
    <ul>
 | 
			
		||||
        <li><a href="api/">API</a></li>
 | 
			
		||||
        <li><a href="guide/">Developer Guide</a></li>
 | 
			
		||||
        <li><a href="architecture/">Architecture Overview</a></li>
 | 
			
		||||
        <li><a href="guide/">Developer Guide</a></li>
 | 
			
		||||
        <li><a href="tutorials/">Tutorials</a></li>
 | 
			
		||||
    </ul>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/add-task.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 23 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/bar-plot-2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 39 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/bar-plot-3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 42 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/bar-plot-4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 30 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/bar-plot.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 34 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/chrome.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 140 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/remove-task.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 11 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/telemetry-1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/telemetry-2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 39 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/telemetry-3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 51 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/todo-edit.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 25 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/todo-list.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 25 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/todo-restyled.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 25 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/todo-selection.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 31 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/src/tutorials/images/todo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 43 KiB  | 
							
								
								
									
										3055
									
								
								docs/src/tutorials/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -30,25 +30,25 @@ define(
 | 
			
		||||
            YELLOW = 0.5,
 | 
			
		||||
            LIMITS = {
 | 
			
		||||
                rh: {
 | 
			
		||||
                    cssClass: "s-limit-upr-red",
 | 
			
		||||
                    cssClass: "s-limit-upr s-limit-red",
 | 
			
		||||
                    low: RED,
 | 
			
		||||
                    high: Number.POSITIVE_INFINITY,
 | 
			
		||||
                    name: "Red High"
 | 
			
		||||
                },
 | 
			
		||||
                rl: {
 | 
			
		||||
                    cssClass: "s-limit-lwr-red",
 | 
			
		||||
                    cssClass: "s-limit-lwr s-limit-red",
 | 
			
		||||
                    high: -RED,
 | 
			
		||||
                    low: Number.NEGATIVE_INFINITY,
 | 
			
		||||
                    name: "Red Low"
 | 
			
		||||
                },
 | 
			
		||||
                yh: {
 | 
			
		||||
                    cssClass: "s-limit-upr-yellow",
 | 
			
		||||
                    cssClass: "s-limit-upr s-limit-yellow",
 | 
			
		||||
                    low: YELLOW,
 | 
			
		||||
                    high: RED,
 | 
			
		||||
                    name: "Yellow High"
 | 
			
		||||
                },
 | 
			
		||||
                yl: {
 | 
			
		||||
                    cssClass: "s-limit-lwr-yellow",
 | 
			
		||||
                    cssClass: "s-limit-lwr s-limit-yellow",
 | 
			
		||||
                    low: -RED,
 | 
			
		||||
                    high: -YELLOW,
 | 
			
		||||
                    name: "Yellow Low"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								example/notifications/bundle.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,47 @@
 | 
			
		||||
{
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "templates": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "dialogLaunchTemplate",
 | 
			
		||||
                "templateUrl": "dialog-launch.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "notificationLaunchTemplate",
 | 
			
		||||
                "templateUrl": "notification-launch.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "controllers": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "DialogLaunchController",
 | 
			
		||||
                "implementation": "DialogLaunchController.js",
 | 
			
		||||
                "depends": [
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    "$timeout",
 | 
			
		||||
                    "$log",
 | 
			
		||||
                    "dialogService",
 | 
			
		||||
                    "notificationService"
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "NotificationLaunchController",
 | 
			
		||||
                "implementation": "NotificationLaunchController.js",
 | 
			
		||||
                "depends": [
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    "$timeout",
 | 
			
		||||
                    "$log",
 | 
			
		||||
                    "notificationService"
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "indicators": [
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "DialogLaunchIndicator.js",
 | 
			
		||||
                "priority": "fallback"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "NotificationLaunchIndicator.js",
 | 
			
		||||
                "priority": "fallback"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								example/notifications/res/dialog-launch.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
			
		||||
<span class="status block ok" ng-controller="DialogLaunchController">
 | 
			
		||||
    <span class="ui-symbol status-indicator"></span>
 | 
			
		||||
    <span class="label">
 | 
			
		||||
        <a ng-click="launchProgress(true)">Known</a> |
 | 
			
		||||
        <a ng-click="launchProgress(false)">Unknown</a> |
 | 
			
		||||
        <a ng-click="launchError()">Error</a> |
 | 
			
		||||
        <a ng-click="launchInfo()">Info</a>
 | 
			
		||||
    </span>
 | 
			
		||||
    <span class="count">Dialogs</span>
 | 
			
		||||
</span>
 | 
			
		||||
							
								
								
									
										10
									
								
								example/notifications/res/notification-launch.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
			
		||||
<span class="status block ok" ng-controller="NotificationLaunchController">
 | 
			
		||||
    <span class="ui-symbol status-indicator"></span>
 | 
			
		||||
    <span class="label">
 | 
			
		||||
        <a ng-click="newInfo()">Success</a> |
 | 
			
		||||
        <a ng-click="newError()">Error</a> |
 | 
			
		||||
        <a ng-click="newAlert()">Alert</a> |
 | 
			
		||||
        <a ng-click="newProgress()">Progress</a>
 | 
			
		||||
    </span>
 | 
			
		||||
    <span class="count">Notifications</span>
 | 
			
		||||
</span>
 | 
			
		||||
							
								
								
									
										150
									
								
								example/notifications/src/DialogLaunchController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,150 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A controller for the dialog launch view. This view allows manual
 | 
			
		||||
         * launching of dialogs for demonstration and testing purposes. It
 | 
			
		||||
         * also demonstrates the use of the DialogService.
 | 
			
		||||
         * @param $scope
 | 
			
		||||
         * @param $timeout
 | 
			
		||||
         * @param $log
 | 
			
		||||
         * @param dialogService
 | 
			
		||||
         * @param notificationService
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function DialogLaunchController($scope, $timeout, $log, dialogService, notificationService) {
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            Demonstrates launching a progress dialog and updating it
 | 
			
		||||
             periodically with the progress of an ongoing process.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.launchProgress = function (knownProgress) {
 | 
			
		||||
                var model = {
 | 
			
		||||
                    title: "Progress Dialog Example",
 | 
			
		||||
                    progress: 0,
 | 
			
		||||
                    hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.",
 | 
			
		||||
                    actionText: "Calculating...",
 | 
			
		||||
                    unknownProgress: !knownProgress,
 | 
			
		||||
                    unknownDuration: false,
 | 
			
		||||
                    severity: "info",
 | 
			
		||||
                    options: [
 | 
			
		||||
                        {
 | 
			
		||||
                            label: "Cancel Operation",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("Operation cancelled");
 | 
			
		||||
                                dialogService.dismiss();
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: "Do something else...",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("Something else pressed");
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                function incrementProgress() {
 | 
			
		||||
                    model.progress = Math.min(100, Math.floor(model.progress + Math.random() * 30));
 | 
			
		||||
                    model.progressText = ["Estimated time remaining: about ", 60 - Math.floor((model.progress / 100) * 60), " seconds"].join(" ");
 | 
			
		||||
                    if (model.progress < 100) {
 | 
			
		||||
                        $timeout(incrementProgress, 1000);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (dialogService.showBlockingMessage(model)) {
 | 
			
		||||
                    //Do processing here
 | 
			
		||||
                    model.actionText = "Processing 100 objects...";
 | 
			
		||||
                    if (knownProgress) {
 | 
			
		||||
                        $timeout(incrementProgress, 1000);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $log.error("Could not display modal dialog");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             Demonstrates launching an error dialog
 | 
			
		||||
             */
 | 
			
		||||
            $scope.launchError = function () {
 | 
			
		||||
                var model = {
 | 
			
		||||
                    title: "Error Dialog Example",
 | 
			
		||||
                    actionText: "Something happened, and it was not good.",
 | 
			
		||||
                    severity: "error",
 | 
			
		||||
                    options: [
 | 
			
		||||
                        {
 | 
			
		||||
                            label: "Try Again",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("Try Again Pressed");
 | 
			
		||||
                                dialogService.dismiss();
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: "Cancel",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("Cancel Pressed");
 | 
			
		||||
                                dialogService.dismiss();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if (!dialogService.showBlockingMessage(model)) {
 | 
			
		||||
                    $log.error("Could not display modal dialog");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             Demonstrates launching an error dialog
 | 
			
		||||
             */
 | 
			
		||||
            $scope.launchInfo = function () {
 | 
			
		||||
                var model = {
 | 
			
		||||
                    title: "Info Dialog Example",
 | 
			
		||||
                    actionText: "This is an example of a blocking info" +
 | 
			
		||||
                    " dialog. This dialog can be used to draw the user's" +
 | 
			
		||||
                    " attention to an event.",
 | 
			
		||||
                    severity: "info",
 | 
			
		||||
                    primaryOption: {
 | 
			
		||||
                            label: "OK",
 | 
			
		||||
                            callback: function () {
 | 
			
		||||
                                $log.debug("OK Pressed");
 | 
			
		||||
                                dialogService.dismiss();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if (!dialogService.showBlockingMessage(model)) {
 | 
			
		||||
                    $log.error("Could not display modal dialog");
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return DialogLaunchController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										56
									
								
								example/notifications/src/DialogLaunchIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,56 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,window*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A tool for manually invoking dialogs. When included this
 | 
			
		||||
         * indicator will allow for dialogs of different types to be
 | 
			
		||||
         * launched for demonstration and testing purposes.
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function DialogLaunchIndicator() {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        DialogLaunchIndicator.template = 'dialogLaunchTemplate';
 | 
			
		||||
 | 
			
		||||
        DialogLaunchIndicator.prototype.getGlyph = function () {
 | 
			
		||||
            return "i";
 | 
			
		||||
        };
 | 
			
		||||
        DialogLaunchIndicator.prototype.getGlyphClass = function () {
 | 
			
		||||
            return 'caution';
 | 
			
		||||
        };
 | 
			
		||||
        DialogLaunchIndicator.prototype.getText = function () {
 | 
			
		||||
            return "Launch test dialog";
 | 
			
		||||
        };
 | 
			
		||||
        DialogLaunchIndicator.prototype.getDescription = function () {
 | 
			
		||||
            return "Launch test dialog";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return DialogLaunchIndicator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										172
									
								
								example/notifications/src/NotificationLaunchController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,172 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Allows launching of notification messages for the purposes of
 | 
			
		||||
         * demonstration and testing. Also demonstrates use of
 | 
			
		||||
         * the NotificationService. Notifications are non-blocking messages that
 | 
			
		||||
         * appear at the bottom of the screen to inform the user of events
 | 
			
		||||
         * in a non-intrusive way. For more information see the
 | 
			
		||||
         * {@link NotificationService}
 | 
			
		||||
         * @param $scope
 | 
			
		||||
         * @param $timeout
 | 
			
		||||
         * @param $log
 | 
			
		||||
         * @param notificationService
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function NotificationLaunchController($scope, $timeout, $log, notificationService) {
 | 
			
		||||
            var messageCounter = 1;
 | 
			
		||||
 | 
			
		||||
            function getExampleActionText() {
 | 
			
		||||
                var actionTexts = [
 | 
			
		||||
                    "Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.",
 | 
			
		||||
                    "Eros turpis, pulvinar turpis eros eu",
 | 
			
		||||
                    "Lundium nascetur a, lectus montes ac, parturient in natoque, duis risus risus pulvinar pid rhoncus, habitasse auctor natoque!"
 | 
			
		||||
                ];
 | 
			
		||||
                return actionTexts[Math.floor(Math.random()*3)];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function getExampleActions() {
 | 
			
		||||
                var actions = [
 | 
			
		||||
                    {
 | 
			
		||||
                        label: "Try Again",
 | 
			
		||||
                        callback: function () {
 | 
			
		||||
                            $log.debug("Try Again pressed");
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        label: "Remove",
 | 
			
		||||
                        callback: function () {
 | 
			
		||||
                            $log.debug("Remove pressed");
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        label: "Cancel",
 | 
			
		||||
                        callback: function () {
 | 
			
		||||
                            $log.debug("Cancel pressed");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ];
 | 
			
		||||
 | 
			
		||||
                // Randomly remove some actions off the top; leave at least one
 | 
			
		||||
                actions.splice(0,Math.floor(Math.random() * actions.length));
 | 
			
		||||
 | 
			
		||||
                return actions;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function getExampleSeverity() {
 | 
			
		||||
                var severities = [
 | 
			
		||||
                    "info",
 | 
			
		||||
                    "alert",
 | 
			
		||||
                    "error"
 | 
			
		||||
                ];
 | 
			
		||||
                return severities[Math.floor(Math.random() * severities.length)];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a new notification with a severity level of 'Error'.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.newError = function(){
 | 
			
		||||
 | 
			
		||||
                notificationService.notify({
 | 
			
		||||
                    title: "Example error notification " + messageCounter++,
 | 
			
		||||
                    hint: "An error has occurred",
 | 
			
		||||
                    severity: "error",
 | 
			
		||||
                    primaryOption: {
 | 
			
		||||
                        label: 'Retry',
 | 
			
		||||
                        callback: function() {
 | 
			
		||||
                            $log.info('Retry clicked');
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    options: getExampleActions()});
 | 
			
		||||
            };
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a new notification with a severity of 'Alert'.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.newAlert = function(){
 | 
			
		||||
 | 
			
		||||
                notificationService.notify({
 | 
			
		||||
                    title: "Alert notification " + (messageCounter++),
 | 
			
		||||
                    hint: "This is an alert message",
 | 
			
		||||
                    severity: "alert",
 | 
			
		||||
                    primaryOption: {
 | 
			
		||||
                        label: 'Retry',
 | 
			
		||||
                        callback: function() {
 | 
			
		||||
                            $log.info('Retry clicked');
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    options: getExampleActions()});
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a new notification with a progress bar that is updated
 | 
			
		||||
             * periodically, tracking an ongoing process.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.newProgress = function(){
 | 
			
		||||
 | 
			
		||||
                var notificationModel = {
 | 
			
		||||
                    title: "Progress notification example",
 | 
			
		||||
                    severity: "info",
 | 
			
		||||
                    progress: 0,
 | 
			
		||||
                    actionText: getExampleActionText(),
 | 
			
		||||
                    unknownProgress: false
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                /**
 | 
			
		||||
                 * Simulate an ongoing process and update the progress bar.
 | 
			
		||||
                 * @param notification
 | 
			
		||||
                 */
 | 
			
		||||
                function incrementProgress(notificationModel) {
 | 
			
		||||
                    notificationModel.progress = Math.min(100, Math.floor(notificationModel.progress + Math.random() * 30));
 | 
			
		||||
                    notificationModel.progressText = ["Estimated time" +
 | 
			
		||||
                    " remaining:" +
 | 
			
		||||
                    " about ", 60 - Math.floor((notificationModel.progress / 100) * 60), " seconds"].join(" ");
 | 
			
		||||
                    if (notificationModel.progress < 100) {
 | 
			
		||||
                        $timeout(function(){incrementProgress(notificationModel);}, 1000);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                notificationService.notify(notificationModel);
 | 
			
		||||
                incrementProgress(notificationModel);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a new notification with severity level of INFO.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.newInfo = function(){
 | 
			
		||||
 | 
			
		||||
                notificationService.info({
 | 
			
		||||
                    title: "Example Info notification " + messageCounter++
 | 
			
		||||
                });
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return NotificationLaunchController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										50
									
								
								example/notifications/src/NotificationLaunchIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,50 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,window*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        function NotificationLaunchIndicator() {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        NotificationLaunchIndicator.template = 'notificationLaunchTemplate';
 | 
			
		||||
 | 
			
		||||
        NotificationLaunchIndicator.prototype.getGlyph = function () {
 | 
			
		||||
            return "i";
 | 
			
		||||
        };
 | 
			
		||||
        NotificationLaunchIndicator.prototype.getGlyphClass = function () {
 | 
			
		||||
            return 'caution';
 | 
			
		||||
        };
 | 
			
		||||
        NotificationLaunchIndicator.prototype.getText = function () {
 | 
			
		||||
            return "Launch notification";
 | 
			
		||||
        };
 | 
			
		||||
        NotificationLaunchIndicator.prototype.getDescription = function () {
 | 
			
		||||
            return "Launch notification";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return NotificationLaunchIndicator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -22,7 +22,8 @@
 | 
			
		||||
    "split": "^1.0.0",
 | 
			
		||||
    "mkdirp": "^0.5.1",
 | 
			
		||||
    "nomnoml": "^0.0.3",
 | 
			
		||||
    "canvas": "^1.2.7"
 | 
			
		||||
    "canvas": "^1.2.7",
 | 
			
		||||
    "markdown-toc": "^0.11.7"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "node app.js",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
    "configuration": {
 | 
			
		||||
        "paths": {
 | 
			
		||||
            "uuid": "uuid"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "routes": [
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
 * Module defining CreateService. Created by vwoeltje on 11/10/14.
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    ["../../lib/uuid"],
 | 
			
		||||
    ["uuid"],
 | 
			
		||||
    function (uuid) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,18 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "form-dialog",
 | 
			
		||||
                "templateUrl": "templates/dialog.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "overlay-blocking-message",
 | 
			
		||||
                "templateUrl": "templates/overlay-blocking-message.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "message",
 | 
			
		||||
                "templateUrl": "templates/message.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "overlay-message-list",
 | 
			
		||||
                "templateUrl": "templates/overlay-message-list.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "containers": [
 | 
			
		||||
 
 | 
			
		||||
@@ -21,17 +21,13 @@
 | 
			
		||||
-->
 | 
			
		||||
<div class="abs top-bar">
 | 
			
		||||
    <div class="title">{{ngModel.title}}</div>
 | 
			
		||||
    <div class="hint">
 | 
			
		||||
        All fields marked <span class="ui-symbol req">*</span> are required.
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="hint">All fields marked <span class="ui-symbol req">*</span> are required.</div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="abs form editor">
 | 
			
		||||
    <div class='abs contents l-dialog'>
 | 
			
		||||
        <mct-form ng-model="ngModel.value"
 | 
			
		||||
                  structure="ngModel.structure"
 | 
			
		||||
                  name="createForm">
 | 
			
		||||
        </mct-form>
 | 
			
		||||
    </div>
 | 
			
		||||
<div class='abs editor'>
 | 
			
		||||
    <mct-form ng-model="ngModel.value"
 | 
			
		||||
              structure="ngModel.structure"
 | 
			
		||||
              name="createForm">
 | 
			
		||||
    </mct-form>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="abs bottom-bar">
 | 
			
		||||
    <a class='s-btn major'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								platform/commonUI/dialog/res/templates/message.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,32 @@
 | 
			
		||||
<div class="l-message"
 | 
			
		||||
     ng-class="'message-severity-' + ngModel.severity">
 | 
			
		||||
    <div class="ui-symbol type-icon message-type"></div>
 | 
			
		||||
    <div class="message-contents">
 | 
			
		||||
        <div class="top-bar">
 | 
			
		||||
            <div class="title">{{ngModel.title}}</div>
 | 
			
		||||
            <div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="message-body">
 | 
			
		||||
            <div class="message-action">
 | 
			
		||||
                {{ngModel.actionText}}
 | 
			
		||||
            </div>
 | 
			
		||||
            <mct-include key="'progress-bar'"
 | 
			
		||||
                         ng-model="ngModel"
 | 
			
		||||
                         ng-show="ngModel.progress !== undefined || ngModel.unknownProgress"></mct-include>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="bottom-bar">
 | 
			
		||||
            <a ng-repeat="dialogOption in ngModel.options"
 | 
			
		||||
               class="s-btn major"
 | 
			
		||||
               ng-click="dialogOption.callback()">
 | 
			
		||||
                {{dialogOption.label}}
 | 
			
		||||
            </a>
 | 
			
		||||
            <a class="s-btn major"
 | 
			
		||||
               ng-if="ngModel.primaryOption"
 | 
			
		||||
               ng-click="ngModel.primaryOption.callback()">
 | 
			
		||||
                {{ngModel.primaryOption.label}}
 | 
			
		||||
            </a>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
<!--
 | 
			
		||||
 Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 Administration. All rights reserved.
 | 
			
		||||
 | 
			
		||||
 Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 You may obtain a copy of the License at
 | 
			
		||||
 http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 | 
			
		||||
 Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 License for the specific language governing permissions and limitations
 | 
			
		||||
 under the License.
 | 
			
		||||
 | 
			
		||||
 Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<mct-container key="overlay" class="t-message-single">
 | 
			
		||||
    <mct-include key="'message'" ng-model="ngModel">
 | 
			
		||||
    </mct-include>
 | 
			
		||||
</mct-container>
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
<mct-container key="overlay" class="t-message-list">
 | 
			
		||||
    <div class="message-contents">
 | 
			
		||||
        <div class="abs top-bar">
 | 
			
		||||
            <div class="title">{{ngModel.dialog.title}}</div>
 | 
			
		||||
            <div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1">s</span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="abs message-body">
 | 
			
		||||
            <mct-include ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'" key="'message'" ng-model="msg"></mct-include>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="abs bottom-bar">
 | 
			
		||||
            <a ng-repeat="dialogAction in ngModel.dialog.actions"
 | 
			
		||||
               class="s-btn major"
 | 
			
		||||
               ng-click="dialogAction.action()">
 | 
			
		||||
                {{dialogAction.label}}
 | 
			
		||||
            </a>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</mct-container>
 | 
			
		||||
@@ -24,13 +24,11 @@
 | 
			
		||||
        <div class="title">{{ngModel.dialog.title}}</div>
 | 
			
		||||
        <div class="hint">{{ngModel.dialog.hint}}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="abs form outline editor">
 | 
			
		||||
        <div class='abs contents l-dialog'>
 | 
			
		||||
            <mct-include key="ngModel.dialog.template"
 | 
			
		||||
                         parameters="ngModel.dialog.parameters"
 | 
			
		||||
                         ng-model="ngModel.dialog.model">
 | 
			
		||||
            </mct-include>
 | 
			
		||||
        </div>
 | 
			
		||||
    <div class='abs editor'>
 | 
			
		||||
        <mct-include key="ngModel.dialog.template"
 | 
			
		||||
                     parameters="ngModel.dialog.parameters"
 | 
			
		||||
                     ng-model="ngModel.dialog.model">
 | 
			
		||||
        </mct-include>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="abs bottom-bar">
 | 
			
		||||
        <a ng-repeat="option in ngModel.dialog.options"
 | 
			
		||||
 
 | 
			
		||||
@@ -22,14 +22,9 @@
 | 
			
		||||
<div class="abs overlay">
 | 
			
		||||
    <div class="abs blocker"></div>
 | 
			
		||||
    <div class="abs holder">
 | 
			
		||||
        <a href=""
 | 
			
		||||
           ng-click="ngModel.cancel()"
 | 
			
		||||
        <a ng-click="ngModel.cancel()"
 | 
			
		||||
           ng-if="ngModel.cancel"
 | 
			
		||||
           class="clk-icon icon ui-symbol close">
 | 
			
		||||
            x
 | 
			
		||||
        </a>
 | 
			
		||||
        <div class="abs contents" ng-transclude>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
           class="clk-icon icon ui-symbol close">x</a>
 | 
			
		||||
        <div class="abs contents" ng-transclude></div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -55,7 +55,7 @@ define(
 | 
			
		||||
            this.dialogVisible = false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        DialogService.prototype.getDialogResponse = function (key, model, resultGetter) {
 | 
			
		||||
        DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) {
 | 
			
		||||
            // We will return this result as a promise, because user
 | 
			
		||||
            // input is asynchronous.
 | 
			
		||||
            var deferred = this.$q.defer(),
 | 
			
		||||
@@ -84,27 +84,20 @@ define(
 | 
			
		||||
            model.confirm = confirm;
 | 
			
		||||
            model.cancel = cancel;
 | 
			
		||||
 | 
			
		||||
            if (this.dialogVisible) {
 | 
			
		||||
                // Only one dialog should be shown at a time.
 | 
			
		||||
                // The application design should be such that
 | 
			
		||||
                // we never even try to do this.
 | 
			
		||||
                this.$log.warn([
 | 
			
		||||
                    "Dialog already showing; ",
 | 
			
		||||
                    "unable to show ",
 | 
			
		||||
                    model.name
 | 
			
		||||
                ].join(""));
 | 
			
		||||
                deferred.reject();
 | 
			
		||||
            } else {
 | 
			
		||||
            if (this.canShowDialog(model)) {
 | 
			
		||||
                // Add the overlay using the OverlayService, which
 | 
			
		||||
                // will handle actual insertion into the DOM
 | 
			
		||||
                this.overlay = this.overlayService.createOverlay(
 | 
			
		||||
                    key,
 | 
			
		||||
                    model
 | 
			
		||||
                    model,
 | 
			
		||||
                    typeClass || "t-dialog"
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                // Track that a dialog is already visible, to
 | 
			
		||||
                // avoid spawning multiple dialogs at once.
 | 
			
		||||
                this.dialogVisible = true;
 | 
			
		||||
            } else {
 | 
			
		||||
                deferred.reject();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return deferred.promise;
 | 
			
		||||
@@ -157,6 +150,99 @@ define(
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Tests if a dialog can be displayed. A modal dialog may only be
 | 
			
		||||
         * displayed if one is not already visible.
 | 
			
		||||
         * Will log a warning message if it can't display a dialog.
 | 
			
		||||
         * @returns {boolean} true if dialog is currently visible, false
 | 
			
		||||
         * otherwise
 | 
			
		||||
         */
 | 
			
		||||
        DialogService.prototype.canShowDialog = function(dialogModel){
 | 
			
		||||
            if (this.dialogVisible){
 | 
			
		||||
                // Only one dialog should be shown at a time.
 | 
			
		||||
                // The application design should be such that
 | 
			
		||||
                // we never even try to do this.
 | 
			
		||||
                this.$log.warn([
 | 
			
		||||
                    "Dialog already showing; ",
 | 
			
		||||
                    "unable to show ",
 | 
			
		||||
                    dialogModel.title
 | 
			
		||||
                ].join(""));
 | 
			
		||||
 | 
			
		||||
                return false;
 | 
			
		||||
            } else {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A user action that can be performed from a blocking dialog. These
 | 
			
		||||
         * actions will be rendered as buttons within a blocking dialog.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef DialogOption
 | 
			
		||||
         * @property {string} label a label to be displayed as the button
 | 
			
		||||
         * text for this action
 | 
			
		||||
         * @property {function} action a function to be called when the
 | 
			
		||||
         * button is clicked
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A description of the model options that may be passed to the
 | 
			
		||||
         * showBlockingMessage method. Note that the DialogModel desribed
 | 
			
		||||
         * here is shared with the Notifications framework.
 | 
			
		||||
         * @see NotificationService
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef DialogModel
 | 
			
		||||
         * @property {string} title the title to use for the dialog
 | 
			
		||||
         * @property {string} severity the severity level of this message.
 | 
			
		||||
         * These are defined in a bundle constant with key 'dialogSeverity'
 | 
			
		||||
         * @property {string} hint the 'hint' message to show below the title
 | 
			
		||||
         * @property {string} actionText text that indicates a current action,
 | 
			
		||||
         * shown above a progress bar to indicate what's happening.
 | 
			
		||||
         * @property {number} progress a percentage value (1-100)
 | 
			
		||||
         * indicating the completion of the blocking task
 | 
			
		||||
         * @property {string} progressText the message to show below a
 | 
			
		||||
         * progress bar to indicate progress. For example, this might be
 | 
			
		||||
         * used to indicate time remaining, or items still to process.
 | 
			
		||||
         * @property {boolean} unknownProgress some tasks may be
 | 
			
		||||
         * impossible to provide an estimate for. Providing a true value for
 | 
			
		||||
         * this attribute will indicate to the user that the progress and
 | 
			
		||||
         * duration cannot be estimated.
 | 
			
		||||
         * @property {DialogOption} primaryOption an action that will
 | 
			
		||||
         * be added to the dialog as a button. The primary action can be
 | 
			
		||||
         * used as the suggested course of action for the user. Making it
 | 
			
		||||
         * distinct from other actions allows it to be styled differently,
 | 
			
		||||
         * and treated preferentially in banner mode.
 | 
			
		||||
         * @property {DialogOption[]} options a list of actions that will
 | 
			
		||||
         * be added to the dialog as buttons.
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Displays a blocking (modal) dialog. This dialog can be used for
 | 
			
		||||
         * displaying messages that require the user's
 | 
			
		||||
         * immediate attention. The message may include an indication of
 | 
			
		||||
         * progress, as well as a series of actions that
 | 
			
		||||
         * the user can take if necessary
 | 
			
		||||
         * @param {DialogModel} dialogModel defines options for the dialog
 | 
			
		||||
         * @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class
 | 
			
		||||
         * @returns {boolean}
 | 
			
		||||
         */
 | 
			
		||||
        DialogService.prototype.showBlockingMessage = function(dialogModel) {
 | 
			
		||||
            if (this.canShowDialog(dialogModel)) {
 | 
			
		||||
                // Add the overlay using the OverlayService, which
 | 
			
		||||
                // will handle actual insertion into the DOM
 | 
			
		||||
                this.overlay = this.overlayService.createOverlay(
 | 
			
		||||
                    "overlay-blocking-message",
 | 
			
		||||
                    dialogModel,
 | 
			
		||||
                    "t-dialog-sm"
 | 
			
		||||
                );
 | 
			
		||||
                // Track that a dialog is already visible, to
 | 
			
		||||
                // avoid spawning multiple dialogs at once.
 | 
			
		||||
                this.dialogVisible = true;
 | 
			
		||||
                return true;
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return DialogService;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ define(
 | 
			
		||||
 | 
			
		||||
        // Template to inject into the DOM to show the dialog; really just points to
 | 
			
		||||
        // the a specific template that can be included via mct-include
 | 
			
		||||
        var TEMPLATE = '<mct-include ng-model="overlay" key="key"></mct-include>';
 | 
			
		||||
        var TEMPLATE = '<mct-include ng-model="overlay" key="key" ng-class="typeClass"></mct-include>';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -71,8 +71,11 @@ define(
 | 
			
		||||
         * @param {object} overlayModel the model to pass to the
 | 
			
		||||
         *        included overlay template (this will be passed
 | 
			
		||||
         *        in via ng-model)
 | 
			
		||||
         * @param {string} typeClass the element class to use in rendering
 | 
			
		||||
         *        the overlay. Can be specified to provide custom styling of
 | 
			
		||||
         *        overlays
 | 
			
		||||
         */
 | 
			
		||||
        OverlayService.prototype.createOverlay = function (key, overlayModel) {
 | 
			
		||||
        OverlayService.prototype.createOverlay = function (key, overlayModel, typeClass) {
 | 
			
		||||
            // Create a new scope for this overlay
 | 
			
		||||
            var scope = this.newScope(),
 | 
			
		||||
                element;
 | 
			
		||||
@@ -90,6 +93,7 @@ define(
 | 
			
		||||
            // Populate the scope; will be passed directly to the template
 | 
			
		||||
            scope.overlay = overlayModel;
 | 
			
		||||
            scope.key = key;
 | 
			
		||||
            scope.typeClass = typeClass || 't-dialog';
 | 
			
		||||
 | 
			
		||||
            // Create the overlay element and add it to the document's body
 | 
			
		||||
            element = this.$compile(TEMPLATE)(scope);
 | 
			
		||||
 
 | 
			
		||||
@@ -116,10 +116,22 @@ define(
 | 
			
		||||
                        dialog: dialogModel,
 | 
			
		||||
                        confirm: jasmine.any(Function),
 | 
			
		||||
                        cancel: jasmine.any(Function)
 | 
			
		||||
                    }
 | 
			
		||||
                    },
 | 
			
		||||
                    't-dialog'
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("invokes the overlay service with the correct parameters when" +
 | 
			
		||||
                " a blocking dialog is requested", function() {
 | 
			
		||||
                var dialogModel = {};
 | 
			
		||||
                expect(dialogService.showBlockingMessage(dialogModel)).toBe(true);
 | 
			
		||||
                expect(mockOverlayService.createOverlay).toHaveBeenCalledWith(
 | 
			
		||||
                    "overlay-blocking-message",
 | 
			
		||||
                    dialogModel,
 | 
			
		||||
                    "t-dialog-sm"
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,14 @@
 | 
			
		||||
                "key": "indicator",
 | 
			
		||||
                "templateUrl": "templates/indicator.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "message-banner",
 | 
			
		||||
                "templateUrl": "templates/message-banner.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "progress-bar",
 | 
			
		||||
                "templateUrl": "templates/progress-bar.html"
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              "key": "time-controller",
 | 
			
		||||
              "templateUrl": "templates/controls/time-controller.html"
 | 
			
		||||
@@ -107,6 +115,11 @@
 | 
			
		||||
                "key": "SelectorController",
 | 
			
		||||
                "implementation": "controllers/SelectorController.js",
 | 
			
		||||
                "depends": [ "objectService", "$scope" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "BannerController",
 | 
			
		||||
                "implementation": "controllers/BannerController.js",
 | 
			
		||||
                "depends": ["$scope", "notificationService", "dialogService"]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "directives": [
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,30 @@
 | 
			
		||||
{
 | 
			
		||||
	"metadata": {
 | 
			
		||||
		"name": "WTD Symbols v24",
 | 
			
		||||
		"lastOpened": 1441992412958,
 | 
			
		||||
		"created": 1441992410384
 | 
			
		||||
		"name": "WTD Symbols v2.3",
 | 
			
		||||
		"lastOpened": 1444267493342,
 | 
			
		||||
		"created": 1444266013303
 | 
			
		||||
	},
 | 
			
		||||
	"iconSets": [
 | 
			
		||||
		{
 | 
			
		||||
			"selection": [
 | 
			
		||||
				{
 | 
			
		||||
					"order": 86,
 | 
			
		||||
					"order": 90,
 | 
			
		||||
					"id": 87,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58896,
 | 
			
		||||
					"name": "icon-bell",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 91,
 | 
			
		||||
					"id": 86,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58889,
 | 
			
		||||
					"name": "icon-hourglass",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 92,
 | 
			
		||||
					"id": 85,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58888,
 | 
			
		||||
@@ -18,119 +34,119 @@
 | 
			
		||||
						58890
 | 
			
		||||
					],
 | 
			
		||||
					"name": "icon-info-v15",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 82,
 | 
			
		||||
					"order": 93,
 | 
			
		||||
					"id": 84,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58887,
 | 
			
		||||
					"name": "icon-x-in-circle",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 77,
 | 
			
		||||
					"order": 94,
 | 
			
		||||
					"id": 83,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58881,
 | 
			
		||||
					"name": "icon-datatable",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 78,
 | 
			
		||||
					"order": 95,
 | 
			
		||||
					"id": 82,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58882,
 | 
			
		||||
					"name": "icon-tabular-scrolling",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 79,
 | 
			
		||||
					"order": 96,
 | 
			
		||||
					"id": 81,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58884,
 | 
			
		||||
					"name": "icon-tabular",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 80,
 | 
			
		||||
					"order": 97,
 | 
			
		||||
					"id": 80,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58885,
 | 
			
		||||
					"name": "icon-calendar",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 83,
 | 
			
		||||
					"order": 98,
 | 
			
		||||
					"id": 78,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58886,
 | 
			
		||||
					"name": "icon-paint-bucket",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 1,
 | 
			
		||||
					"order": 99,
 | 
			
		||||
					"id": 75,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 123,
 | 
			
		||||
					"name": "icon-pointer-left",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 3,
 | 
			
		||||
					"order": 100,
 | 
			
		||||
					"id": 74,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 125,
 | 
			
		||||
					"name": "icon-pointer-right",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 4,
 | 
			
		||||
					"order": 101,
 | 
			
		||||
					"id": 73,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 80,
 | 
			
		||||
					"name": "icon-person",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 5,
 | 
			
		||||
					"order": 102,
 | 
			
		||||
					"id": 72,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 232,
 | 
			
		||||
					"name": "icon-chain-links",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 6,
 | 
			
		||||
					"order": 103,
 | 
			
		||||
					"id": 71,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 115,
 | 
			
		||||
					"name": "icon-database-in-brackets",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 7,
 | 
			
		||||
					"order": 104,
 | 
			
		||||
					"id": 70,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 114,
 | 
			
		||||
					"name": "icon-refresh",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 8,
 | 
			
		||||
					"order": 105,
 | 
			
		||||
					"id": 69,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 108,
 | 
			
		||||
					"name": "icon-lock",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 9,
 | 
			
		||||
					"order": 106,
 | 
			
		||||
					"id": 68,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 51,
 | 
			
		||||
					"name": "icon-box-with-dashed-lines",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 10,
 | 
			
		||||
@@ -138,7 +154,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58880,
 | 
			
		||||
					"name": "icon-box-with-arrow-cursor",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 11,
 | 
			
		||||
@@ -146,7 +162,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 65,
 | 
			
		||||
					"name": "icon-activity-mode",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 12,
 | 
			
		||||
@@ -154,15 +170,15 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 97,
 | 
			
		||||
					"name": "icon-activity",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 13,
 | 
			
		||||
					"order": 87,
 | 
			
		||||
					"id": 64,
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 33,
 | 
			
		||||
					"name": "icon-alert-rect",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 14,
 | 
			
		||||
@@ -170,7 +186,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58883,
 | 
			
		||||
					"name": "icon-alert-triangle",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 15,
 | 
			
		||||
@@ -178,7 +194,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 238,
 | 
			
		||||
					"name": "icon-arrow-double-down",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 16,
 | 
			
		||||
@@ -186,7 +202,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 235,
 | 
			
		||||
					"name": "icon-arrow-double-up",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 2,
 | 
			
		||||
@@ -194,7 +210,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 118,
 | 
			
		||||
					"name": "icon-arrow-down",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 19,
 | 
			
		||||
@@ -202,7 +218,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 60,
 | 
			
		||||
					"name": "icon-arrow-left",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 20,
 | 
			
		||||
@@ -210,7 +226,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 62,
 | 
			
		||||
					"name": "icon-arrow-right",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 21,
 | 
			
		||||
@@ -218,7 +234,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 236,
 | 
			
		||||
					"name": "icon-arrow-tall-down",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 22,
 | 
			
		||||
@@ -226,7 +242,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 237,
 | 
			
		||||
					"name": "icon-arrow-tall-up",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 23,
 | 
			
		||||
@@ -234,7 +250,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 94,
 | 
			
		||||
					"name": "icon-arrow-up",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 24,
 | 
			
		||||
@@ -242,7 +258,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 73,
 | 
			
		||||
					"name": "icon-arrows-out",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 25,
 | 
			
		||||
@@ -250,7 +266,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58893,
 | 
			
		||||
					"name": "icon-arrows-right-left",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 33,
 | 
			
		||||
@@ -258,7 +274,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 53,
 | 
			
		||||
					"name": "icon-arrows-up-down",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 26,
 | 
			
		||||
@@ -266,7 +282,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 42,
 | 
			
		||||
					"name": "icon-asterisk",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 27,
 | 
			
		||||
@@ -274,7 +290,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 72,
 | 
			
		||||
					"name": "icon-autoflow-tabular",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 28,
 | 
			
		||||
@@ -282,7 +298,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 224,
 | 
			
		||||
					"name": "icon-box",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 29,
 | 
			
		||||
@@ -290,7 +306,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 50,
 | 
			
		||||
					"name": "icon-check",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 30,
 | 
			
		||||
@@ -298,7 +314,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 67,
 | 
			
		||||
					"name": "icon-clock",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 31,
 | 
			
		||||
@@ -306,7 +322,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 46,
 | 
			
		||||
					"name": "icon-connectivity",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 32,
 | 
			
		||||
@@ -314,7 +330,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 100,
 | 
			
		||||
					"name": "icon-database-query",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 17,
 | 
			
		||||
@@ -322,7 +338,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 68,
 | 
			
		||||
					"name": "icon-database",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 35,
 | 
			
		||||
@@ -330,7 +346,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 81,
 | 
			
		||||
					"name": "icon-dictionary",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 36,
 | 
			
		||||
@@ -338,7 +354,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 242,
 | 
			
		||||
					"name": "icon-duplicate",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 37,
 | 
			
		||||
@@ -346,7 +362,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 102,
 | 
			
		||||
					"name": "icon-folder-new",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 38,
 | 
			
		||||
@@ -354,7 +370,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 70,
 | 
			
		||||
					"name": "icon-folder",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 39,
 | 
			
		||||
@@ -362,7 +378,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 95,
 | 
			
		||||
					"name": "icon-fullscreen-collapse",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 40,
 | 
			
		||||
@@ -370,7 +386,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 122,
 | 
			
		||||
					"name": "icon-fullscreen-expand",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 41,
 | 
			
		||||
@@ -378,7 +394,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 71,
 | 
			
		||||
					"name": "icon-gear",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 49,
 | 
			
		||||
@@ -386,7 +402,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 227,
 | 
			
		||||
					"name": "icon-image",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 42,
 | 
			
		||||
@@ -394,7 +410,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 225,
 | 
			
		||||
					"name": "icon-layers",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 43,
 | 
			
		||||
@@ -402,7 +418,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 76,
 | 
			
		||||
					"name": "icon-layout",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 44,
 | 
			
		||||
@@ -410,7 +426,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 226,
 | 
			
		||||
					"name": "icon-line-horz",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 75,
 | 
			
		||||
@@ -418,7 +434,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 244,
 | 
			
		||||
					"name": "icon-link",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 46,
 | 
			
		||||
@@ -426,7 +442,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 88,
 | 
			
		||||
					"name": "icon-magnify-in",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 47,
 | 
			
		||||
@@ -434,7 +450,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 89,
 | 
			
		||||
					"name": "icon-magnify-out",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 48,
 | 
			
		||||
@@ -442,7 +458,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 77,
 | 
			
		||||
					"name": "icon-magnify",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 34,
 | 
			
		||||
@@ -450,7 +466,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 109,
 | 
			
		||||
					"name": "icon-menu",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 50,
 | 
			
		||||
@@ -458,7 +474,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 243,
 | 
			
		||||
					"name": "icon-move",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 51,
 | 
			
		||||
@@ -466,7 +482,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 121,
 | 
			
		||||
					"name": "icon-new-window",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 52,
 | 
			
		||||
@@ -474,7 +490,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 111,
 | 
			
		||||
					"name": "icon-object",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 73,
 | 
			
		||||
@@ -482,7 +498,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 63,
 | 
			
		||||
					"name": "icon-object-unknown",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 53,
 | 
			
		||||
@@ -490,7 +506,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 86,
 | 
			
		||||
					"name": "icon-packet",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 54,
 | 
			
		||||
@@ -498,7 +514,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 234,
 | 
			
		||||
					"name": "icon-page",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 55,
 | 
			
		||||
@@ -506,7 +522,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 241,
 | 
			
		||||
					"name": "icon-pause",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 56,
 | 
			
		||||
@@ -514,7 +530,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 112,
 | 
			
		||||
					"name": "icon-pencil",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 65,
 | 
			
		||||
@@ -522,7 +538,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 79,
 | 
			
		||||
					"name": "icon-people",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 57,
 | 
			
		||||
@@ -530,7 +546,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 239,
 | 
			
		||||
					"name": "icon-play",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 58,
 | 
			
		||||
@@ -538,7 +554,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 233,
 | 
			
		||||
					"name": "icon-plot-resource",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 59,
 | 
			
		||||
@@ -546,7 +562,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 43,
 | 
			
		||||
					"name": "icon-plus",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 60,
 | 
			
		||||
@@ -554,7 +570,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 45,
 | 
			
		||||
					"name": "icon-minus",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 61,
 | 
			
		||||
@@ -562,7 +578,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 54,
 | 
			
		||||
					"name": "icon-sine",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 62,
 | 
			
		||||
@@ -570,7 +586,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 228,
 | 
			
		||||
					"name": "icon-T",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 63,
 | 
			
		||||
@@ -578,7 +594,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 116,
 | 
			
		||||
					"name": "icon-telemetry-panel",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 64,
 | 
			
		||||
@@ -586,7 +602,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 84,
 | 
			
		||||
					"name": "icon-telemetry",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 18,
 | 
			
		||||
@@ -594,7 +610,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 246,
 | 
			
		||||
					"name": "icon-thumbs-strip",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 67,
 | 
			
		||||
@@ -602,7 +618,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 83,
 | 
			
		||||
					"name": "icon-timeline",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 68,
 | 
			
		||||
@@ -610,7 +626,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 245,
 | 
			
		||||
					"name": "icon-timer",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 69,
 | 
			
		||||
@@ -618,7 +634,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 90,
 | 
			
		||||
					"name": "icon-trash",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 70,
 | 
			
		||||
@@ -626,7 +642,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 229,
 | 
			
		||||
					"name": "icon-two-parts-both",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 71,
 | 
			
		||||
@@ -634,7 +650,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 231,
 | 
			
		||||
					"name": "icon-two-parts-one-only",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 72,
 | 
			
		||||
@@ -642,7 +658,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 120,
 | 
			
		||||
					"name": "icon-x-heavy",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"order": 66,
 | 
			
		||||
@@ -650,7 +666,7 @@
 | 
			
		||||
					"prevSize": 32,
 | 
			
		||||
					"code": 58946,
 | 
			
		||||
					"name": "icon-x",
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
					"tempChar": ""
 | 
			
		||||
				}
 | 
			
		||||
			],
 | 
			
		||||
			"id": 2,
 | 
			
		||||
@@ -665,6 +681,58 @@
 | 
			
		||||
			"height": 1024,
 | 
			
		||||
			"prevSize": 32,
 | 
			
		||||
			"icons": [
 | 
			
		||||
				{
 | 
			
		||||
					"id": 87,
 | 
			
		||||
					"paths": [
 | 
			
		||||
						"M512 1024c106 0 192-86 192-192h-384c0 106 86 192 192 192z",
 | 
			
		||||
						"M896 448v-64c0-212-172-384-384-384s-384 172-384 384v64c0 70.6-57.4 128-128 128v128h1024v-128c-70.6 0-128-57.4-128-128z"
 | 
			
		||||
					],
 | 
			
		||||
					"attrs": [
 | 
			
		||||
						{
 | 
			
		||||
							"fill": "rgb(6, 161, 75)"
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							"fill": "rgb(6, 161, 75)"
 | 
			
		||||
						}
 | 
			
		||||
					],
 | 
			
		||||
					"isMulticolor": false,
 | 
			
		||||
					"grid": 0,
 | 
			
		||||
					"tags": [
 | 
			
		||||
						"icon-bell"
 | 
			
		||||
					],
 | 
			
		||||
					"colorPermutations": {
 | 
			
		||||
						"125525525516161751": [
 | 
			
		||||
							1,
 | 
			
		||||
							1
 | 
			
		||||
						]
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"id": 86,
 | 
			
		||||
					"paths": [
 | 
			
		||||
						"M1024 0h-1024c0 282.8 229.2 512 512 512s512-229.2 512-512zM512 384c-102.6 0-199-40-271.6-112.4-41.2-41.2-72-90.2-90.8-143.6h724.6c-18.8 53.4-49.6 102.4-90.8 143.6-72.4 72.4-168.8 112.4-271.4 112.4z",
 | 
			
		||||
						"M512 512c-282.8 0-512 229.2-512 512h1024c0-282.8-229.2-512-512-512z"
 | 
			
		||||
					],
 | 
			
		||||
					"attrs": [
 | 
			
		||||
						{
 | 
			
		||||
							"fill": "rgb(6, 161, 75)"
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							"fill": "rgb(6, 161, 75)"
 | 
			
		||||
						}
 | 
			
		||||
					],
 | 
			
		||||
					"isMulticolor": false,
 | 
			
		||||
					"grid": 0,
 | 
			
		||||
					"tags": [
 | 
			
		||||
						"icon-hourglass"
 | 
			
		||||
					],
 | 
			
		||||
					"colorPermutations": {
 | 
			
		||||
						"125525525516161751": [
 | 
			
		||||
							1,
 | 
			
		||||
							1
 | 
			
		||||
						]
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"id": 85,
 | 
			
		||||
					"paths": [
 | 
			
		||||
@@ -698,7 +766,8 @@
 | 
			
		||||
						"icon-x-in-circle"
 | 
			
		||||
					],
 | 
			
		||||
					"colorPermutations": {
 | 
			
		||||
						"16161751": []
 | 
			
		||||
						"16161751": [],
 | 
			
		||||
						"125525525516161751": []
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
@@ -899,6 +968,11 @@
 | 
			
		||||
							1,
 | 
			
		||||
							1,
 | 
			
		||||
							1
 | 
			
		||||
						],
 | 
			
		||||
						"125525525516161751": [
 | 
			
		||||
							1,
 | 
			
		||||
							1,
 | 
			
		||||
							1
 | 
			
		||||
						]
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
@@ -1051,18 +1125,28 @@
 | 
			
		||||
				{
 | 
			
		||||
					"id": 67,
 | 
			
		||||
					"paths": [
 | 
			
		||||
						"M832 512.4c0-0.2 0-0.2 0-0.4v-320c0-105.6-86.4-192-192-192h-448c-105.6 0-192 86.4-192 192v320c0 105.6 86.4 192 192 192h263.6l-197.2-445.6 573.6 254z",
 | 
			
		||||
						"M766.8 659.8l193.8-20.4-576.6-255.4 255.4 576.6 20.4-193.8 257 257.2 107.2-107.2z"
 | 
			
		||||
						"M894-2h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h400c-2.2-3.8-4-7.6-5.8-11.4l-255.2-576.8c-21.4-48.4-10.8-105 26.6-142.4 24.4-24.4 57.2-37.4 90.4-37.4 17.4 0 35.2 3.6 51.8 11l576.6 255.4c4 1.8 7.8 3.8 11.4 5.8v-400.2c0.2-70.4-57.4-128-127.8-128z",
 | 
			
		||||
						"M958.6 637.4l-576.6-255.4 255.4 576.6 64.6-128.6 192 192 128-128-192-192z"
 | 
			
		||||
					],
 | 
			
		||||
					"attrs": [
 | 
			
		||||
						{},
 | 
			
		||||
						{}
 | 
			
		||||
						{
 | 
			
		||||
							"fill": "rgb(0, 0, 0)"
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							"fill": "rgb(0, 0, 0)"
 | 
			
		||||
						}
 | 
			
		||||
					],
 | 
			
		||||
					"isMulticolor": false,
 | 
			
		||||
					"grid": 0,
 | 
			
		||||
					"tags": [
 | 
			
		||||
						"icon-box-with-arrow-cursor"
 | 
			
		||||
					]
 | 
			
		||||
					],
 | 
			
		||||
					"colorPermutations": {
 | 
			
		||||
						"125525525516161751": [
 | 
			
		||||
							0,
 | 
			
		||||
							0
 | 
			
		||||
						]
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"id": 66,
 | 
			
		||||
@@ -1338,6 +1422,9 @@
 | 
			
		||||
					"colorPermutations": {
 | 
			
		||||
						"16161751": [
 | 
			
		||||
							0
 | 
			
		||||
						],
 | 
			
		||||
						"125525525516161751": [
 | 
			
		||||
							0
 | 
			
		||||
						]
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
@@ -14853,6 +14940,5 @@
 | 
			
		||||
		"gridSize": 16,
 | 
			
		||||
		"showLiga": false
 | 
			
		||||
	},
 | 
			
		||||
	"uid": -1,
 | 
			
		||||
	"time": 1441993324496
 | 
			
		||||
	"uid": -1
 | 
			
		||||
}
 | 
			
		||||
@@ -76,7 +76,7 @@
 | 
			
		||||
<glyph unicode="ô" glyph-name="icon-link" d="M1024 448l-512 512v-307.2l-512-204.8v-256h512v-256z" />
 | 
			
		||||
<glyph unicode="õ" glyph-name="icon-timer" d="M638 898c0 35.4-28.6 64-64 64h-128c-35.4 0-64-28.6-64-64s28.6-64 64-64h128c35.4 0 64 28.6 64 64zM510 834c-247.4 0-448-200.6-448-448s200.6-448 448-448 448 200.6 448 448-200.6 448-448 448zM510 386h-336c0 185.2 150.8 336 336 336v-336z" />
 | 
			
		||||
<glyph unicode="ö" glyph-name="icon-thumbs-strip" d="M448 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-box-with-arrow-cursor" d="M832 447.6c0 0.2 0 0.2 0 0.4v320c0 105.6-86.4 192-192 192h-448c-105.6 0-192-86.4-192-192v-320c0-105.6 86.4-192 192-192h263.6l-197.2 445.6 573.6-254zM766.8 300.2l193.8 20.4-576.6 255.4 255.4-576.6 20.4 193.8 257-257.2 107.2 107.2z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-box-with-arrow-cursor" d="M894 962h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h400c-2.2 3.8-4 7.6-5.8 11.4l-255.2 576.8c-21.4 48.4-10.8 105 26.6 142.4 24.4 24.4 57.2 37.4 90.4 37.4 17.4 0 35.2-3.6 51.8-11l576.6-255.4c4-1.8 7.8-3.8 11.4-5.8v400.2c0.2 70.4-57.4 128-127.8 128zM958.6 322.6l-576.6 255.4 255.4-576.6 64.6 128.6 192-192 128 128-192 192z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-datatable" d="M1024 768c0-106.039-229.23-192-512-192s-512 85.961-512 192c0 106.039 229.23 192 512 192s512-85.961 512-192zM512 448c-282.8 0-512 86-512 192v-512c0-106 229.2-192 512-192s512 86 512 192v512c0-106-229.2-192-512-192zM896 385v-256c-36.6-15.6-79.8-28.8-128-39.4v256c48.2 10.6 91.4 23.8 128 39.4zM256 345.6v-256c-48.2 10.4-91.4 23.8-128 39.4v256c36.6-15.6 79.8-28.8 128-39.4zM384 70v256c41-4 83.8-6 128-6s87 2.2 128 6v-256c-41-4-83.8-6-128-6s-87 2.2-128 6z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-tabular-scrolling" d="M64 960c-35.2 0-64-28.8-64-64v-192h448v256h-384zM1024 704v192c0 35.2-28.8 64-64 64h-384v-256h448zM0 576v-192c0-35.2 28.8-64 64-64h384v256h-448zM960 320c35.2 0 64 28.8 64 64v192h-448v-256h384zM512-64l-256 256h512z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-alert-triangle" d="M998.208 111.136l-422.702 739.728c-34.928 61.124-92.084 61.124-127.012 0l-422.702-739.728c-34.928-61.126-5.906-111.136 64.494-111.136h843.428c70.4 0 99.422 50.010 64.494 111.136zM512 128c-35.2 0-64 28.8-64 64s28.8 64 64 64 64-28.8 64-64c0-35.2-28.8-64-64-64zM627.448 577.242l-38.898-194.486c-6.902-34.516-41.35-62.756-76.55-62.756s-69.648 28.24-76.552 62.758l-38.898 194.486c-6.902 34.516 16.25 62.756 51.45 62.756h128c35.2 0 58.352-28.24 51.448-62.758z" />
 | 
			
		||||
@@ -85,6 +85,8 @@
 | 
			
		||||
<glyph unicode="" glyph-name="icon-paint-bucket" d="M544 736v-224c0-88.4-71.6-160-160-160s-160 71.6-160 160v97.2l-197.4-196.4c-50-50-12.4-215.2 112.4-340s290-162.4 340-112.4l417 423.6-352 352zM896-64c70.6 0 128 57.4 128 128 0 108.6-128 192-128 192s-128-83.4-128-192c0-70.6 57.4-128 128-128zM384 448c-35.4 0-64 28.6-64 64v384c0 35.4 28.6 64 64 64s64-28.6 64-64v-384c0-35.4-28.6-64-64-64z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-x-in-circle" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM832 256l-128-128-192 192-192-192-128 128 192 192-192 192 128 128 192-192 192 192 128-128-192-192 192-192z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-info-v15" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM512 832c70.6 0 128-57.4 128-128s-57.4-128-128-128c-70.6 0-128 57.4-128 128s57.4 128 128 128zM704 128h-384v128h64v256h256v-256h64v-128z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-hourglass" d="M1024 960h-1024c0-282.8 229.2-512 512-512s512 229.2 512 512zM512 576c-102.6 0-199 40-271.6 112.4-41.2 41.2-72 90.2-90.8 143.6h724.6c-18.8-53.4-49.6-102.4-90.8-143.6-72.4-72.4-168.8-112.4-271.4-112.4zM512 448c-282.8 0-512-229.2-512-512h1024c0 282.8-229.2 512-512 512z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-arrows-right-left" d="M1024 448l-448-512v1024zM448 960l-448-512 448-512z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-bell" d="M512-64c106 0 192 86 192 192h-384c0-106 86-192 192-192zM896 512v64c0 212-172 384-384 384s-384-172-384-384v-64c0-70.6-57.4-128-128-128v-128h1024v128c-70.6 0-128 57.4-128 128z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-x" d="M384 448l-365.332-365.332c-24.89-24.89-24.89-65.62 0-90.51l37.49-37.49c24.89-24.89 65.62-24.89 90.51 0 0 0 365.332 365.332 365.332 365.332l365.332-365.332c24.89-24.89 65.62-24.89 90.51 0l37.49 37.49c24.89 24.89 24.89 65.62 0 90.51l-365.332 365.332c0 0 365.332 365.332 365.332 365.332 24.89 24.89 24.89 65.62 0 90.51l-37.49 37.49c-24.89 24.89-65.62 24.89-90.51 0 0 0-365.332-365.332-365.332-365.332l-365.332 365.332c-24.89 24.89-65.62 24.89-90.51 0l-37.49-37.49c-24.89-24.89-24.89-65.62 0-90.51 0 0 365.332-365.332 365.332-365.332z" />
 | 
			
		||||
</font></defs></svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB  | 
@@ -47,8 +47,8 @@ $ueEditLeftPaneW: 75%;
 | 
			
		||||
$treeSearchInputBarH: 25px;
 | 
			
		||||
$ueTimeControlH: (33px, 20px, 20px);
 | 
			
		||||
// Overlay
 | 
			
		||||
$ovrTopBarH: 60px;
 | 
			
		||||
$ovrFooterH: 30px;
 | 
			
		||||
$ovrTopBarH: 45px;
 | 
			
		||||
$ovrFooterH: 24px;
 | 
			
		||||
$overlayMargin: 25px;
 | 
			
		||||
// Items
 | 
			
		||||
$ueBrowseGridItemLg: 200px;
 | 
			
		||||
@@ -106,3 +106,8 @@ $dirImgs: $dirCommonRes + 'images/';
 | 
			
		||||
 | 
			
		||||
/************************** TIMINGS */
 | 
			
		||||
$controlFadeMs: 100ms;
 | 
			
		||||
 | 
			
		||||
/************************** LIMITS */
 | 
			
		||||
$glyphLimit: '\e603';
 | 
			
		||||
$glyphLimitUpr: '\0000eb';
 | 
			
		||||
$glyphLimitLwr: '\0000ee';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,39 @@
 | 
			
		||||
@mixin limit($bg, $ic, $glyph) {
 | 
			
		||||
	background: $bg !important;
 | 
			
		||||
	//color: $fg !important;
 | 
			
		||||
	&:before {
 | 
			
		||||
		//@include pulse(1000ms);
 | 
			
		||||
		color: $ic;
 | 
			
		||||
		content: $glyph;
 | 
			
		||||
	}
 | 
			
		||||
@mixin limitGlyph($iconColor, $glyph: $glyphLimit) {
 | 
			
		||||
    &:before {
 | 
			
		||||
        color: $iconColor;
 | 
			
		||||
        content: $glyph;
 | 
			
		||||
        font-family: symbolsfont;
 | 
			
		||||
        font-size: 0.8em;
 | 
			
		||||
        display: inline;
 | 
			
		||||
        margin-right: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[class*="s-limit"] {
 | 
			
		||||
	//white-space: nowrap;
 | 
			
		||||
	&:before {
 | 
			
		||||
		display: inline-block;
 | 
			
		||||
		font-family: symbolsfont;
 | 
			
		||||
		font-size: 0.75em;
 | 
			
		||||
		font-style: normal !important;
 | 
			
		||||
		margin-right: $interiorMarginSm;
 | 
			
		||||
		vertical-align: middle;
 | 
			
		||||
	}
 | 
			
		||||
.s-limit-red { background: $colorLimitRedBg !important; }
 | 
			
		||||
.s-limit-yellow { background: $colorLimitYellowBg !important; }
 | 
			
		||||
 | 
			
		||||
// Handle limit when applied to a tr
 | 
			
		||||
tr[class*="s-limit"] {
 | 
			
		||||
    &.s-limit-red td:first-child {
 | 
			
		||||
        @include limitGlyph($colorLimitRedIc);
 | 
			
		||||
    }
 | 
			
		||||
    &.s-limit-yellow td:first-child {
 | 
			
		||||
        @include limitGlyph($colorLimitYellowIc);
 | 
			
		||||
    }
 | 
			
		||||
    &.s-limit-upr td:first-child:before { content:$glyphLimitUpr; }
 | 
			
		||||
    &.s-limit-lwr td:first-child:before { content:$glyphLimitLwr; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-limit-upr-red { @include limit($colorLimitRedBg, $colorLimitRedIc, "\0000eb"); };
 | 
			
		||||
.s-limit-upr-yellow { @include limit($colorLimitYellowBg, $colorLimitYellowIc, "\0000ed"); };
 | 
			
		||||
.s-limit-lwr-yellow { @include limit($colorLimitYellowBg, $colorLimitYellowIc, "\0000ec"); };
 | 
			
		||||
.s-limit-lwr-red { @include limit($colorLimitRedBg, $colorLimitRedIc, "\0000ee"); };
 | 
			
		||||
// Handle limit when applied directly to a non-tr element
 | 
			
		||||
// Assume this is applied to the element that displays the limit value
 | 
			
		||||
:not(tr)[class*="s-limit"] {
 | 
			
		||||
    &.s-limit-red {
 | 
			
		||||
        @include limitGlyph($colorLimitRedIc);
 | 
			
		||||
    }
 | 
			
		||||
    &.s-limit-yellow {
 | 
			
		||||
        @include limitGlyph($colorLimitYellowIc);
 | 
			
		||||
    }
 | 
			
		||||
    &.s-limit-upr:before { content:$glyphLimitUpr; }
 | 
			
		||||
    &.s-limit-lwr:before { content:$glyphLimitLwr; }
 | 
			
		||||
}
 | 
			
		||||
@@ -29,7 +29,6 @@
 | 
			
		||||
@import "helpers/bubbles";
 | 
			
		||||
@import "helpers/splitter";
 | 
			
		||||
@import "helpers/wait-spinner";
 | 
			
		||||
@import "messages";
 | 
			
		||||
@import "properties";
 | 
			
		||||
 | 
			
		||||
/********************************* CONTROLS */
 | 
			
		||||
@@ -39,6 +38,7 @@
 | 
			
		||||
@import "controls/controls";
 | 
			
		||||
@import "controls/lists";
 | 
			
		||||
@import "controls/menus";
 | 
			
		||||
@import "controls/messages";
 | 
			
		||||
@import "controls/time-controller";
 | 
			
		||||
@import "mobile/controls/menus";
 | 
			
		||||
 | 
			
		||||
@@ -62,7 +62,6 @@
 | 
			
		||||
@import "mobile/tree";
 | 
			
		||||
@import "user-environ/frame";
 | 
			
		||||
@import "user-environ/top-bar";
 | 
			
		||||
@import "user-environ/bottom-bar";
 | 
			
		||||
@import "user-environ/tool-bar";
 | 
			
		||||
 | 
			
		||||
/********************************* VIEWS */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
/* Styles for messages */
 | 
			
		||||
 | 
			
		||||
.message {
 | 
			
		||||
	&.block {
 | 
			
		||||
		@include border-radius($basicCr);
 | 
			
		||||
		padding: $interiorMarginLg;
 | 
			
		||||
	}
 | 
			
		||||
	&.error {
 | 
			
		||||
		background-color: rgba($colorAlert,0.3);
 | 
			
		||||
		color: lighten($colorAlert, 20%);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -94,7 +94,6 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin triangle($dir: "left", $size: 5px, $ratio: 1, $color: red) {
 | 
			
		||||
    //$size: $size*2;
 | 
			
		||||
    width: 0;
 | 
			
		||||
    height: 0;
 | 
			
		||||
    $slopedB: $size/$ratio solid transparent;
 | 
			
		||||
@@ -129,6 +128,24 @@
 | 
			
		||||
    background-size: $d $d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin bgVertStripes($c: yellow, $a: 0.1, $d: 40px) {
 | 
			
		||||
    @include background-image(linear-gradient(-90deg,
 | 
			
		||||
        rgba($c, $a) 0%, rgba($c, $a) 50%,
 | 
			
		||||
        transparent 50%, transparent 100%
 | 
			
		||||
    ));
 | 
			
		||||
    background-repeat: repeat;
 | 
			
		||||
    background-size: $d $d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin bgVertFuzzyStripes($c: yellow, $a: 0.1, $d: 40px) {
 | 
			
		||||
    @include background-image(linear-gradient(-90deg,
 | 
			
		||||
        rgba($c, $a) 0%, transparent 50%,
 | 
			
		||||
        transparent 50%, rgba($c, $a) 100%
 | 
			
		||||
    ));
 | 
			
		||||
    background-repeat: repeat;
 | 
			
		||||
    background-size: $d $d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin bgTicks($c: $colorBodyFg, $repeatDir: 'x') {
 | 
			
		||||
    $deg: 90deg;
 | 
			
		||||
    @if ($repeatDir != 'x') {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,34 +19,6 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*.control {
 | 
			
		||||
	// UNUSED?
 | 
			
		||||
	&.view-control {
 | 
			
		||||
		.icon {
 | 
			
		||||
			display: inline-block;
 | 
			
		||||
			margin: -1px 5px 1px 2px;
 | 
			
		||||
			vertical-align: middle;
 | 
			
		||||
			&.triangle-down {
 | 
			
		||||
				margin: 2px 2px -2px 0px;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.label {
 | 
			
		||||
			display: inline-block;
 | 
			
		||||
			font-size: 11px;
 | 
			
		||||
			vertical-align: middle;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		.toggle {
 | 
			
		||||
			@include border-radius(3px);
 | 
			
		||||
			display: inline-block;
 | 
			
		||||
			padding: 1px 6px 4px 4px;
 | 
			
		||||
			&:hover {
 | 
			
		||||
				background: rgba(white, 0.1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
.accordion {
 | 
			
		||||
	$accordionHeadH: 18px;
 | 
			
		||||
@@ -291,6 +263,88 @@ label.checkbox.custom {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** PROGRESS BAR */
 | 
			
		||||
@include keyframes(progress) {
 | 
			
		||||
    100% { background-position: $progressBarStripeW center; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin bgProgressAnim($c: yellow, $a: 0.1, $d: 20px) {
 | 
			
		||||
    @include background-image(linear-gradient(-90deg,
 | 
			
		||||
        rgba($c, $a) 0%, transparent 50%,
 | 
			
		||||
        transparent 50%, rgba($c, $a) 100%
 | 
			
		||||
    ));
 | 
			
		||||
    background-position: 0 center;
 | 
			
		||||
    background-repeat: repeat-x;
 | 
			
		||||
    background-size: $d 40%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-progress-bar {
 | 
			
		||||
    // Assume will be determinate by default
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
	position: relative;
 | 
			
		||||
 | 
			
		||||
	.progress-amt-holder {
 | 
			
		||||
        @include absPosDefault(1px);
 | 
			
		||||
    }
 | 
			
		||||
    .progress-amt,
 | 
			
		||||
    .progress-amt:before,
 | 
			
		||||
    .progress-amt:after {
 | 
			
		||||
		@include absPosDefault();
 | 
			
		||||
        display: block;
 | 
			
		||||
        content: '';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    .progress-amt {
 | 
			
		||||
        right: auto; // Allow inline width to control }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.indeterminate {
 | 
			
		||||
        .progress-amt {
 | 
			
		||||
            width: 100% !important;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-progress-bar {
 | 
			
		||||
    @include border-radius($basicCr);
 | 
			
		||||
    @include boxIncised(0.3, 4px);
 | 
			
		||||
    background: $colorProgressBarOuter;
 | 
			
		||||
    //border:1px solid $colorProgressBarOuter;
 | 
			
		||||
	.progress-amt {
 | 
			
		||||
		@include border-radius($basicCr);
 | 
			
		||||
		@include boxShdw();
 | 
			
		||||
		@include border-radius($basicCr - 1);
 | 
			
		||||
        @include trans-prop-nice(width);
 | 
			
		||||
        &:before {
 | 
			
		||||
            background-color: $colorProgressBarAmt;
 | 
			
		||||
        }
 | 
			
		||||
        &:after {
 | 
			
		||||
            // Sheen
 | 
			
		||||
            @include background-image(linear-gradient(
 | 
			
		||||
                    transparent 5%, rgba(#fff,0.25) 30%, transparent 100%
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    &:not(.indeterminate) {
 | 
			
		||||
        .progress-amt:before {
 | 
			
		||||
            // More subtle anim for determinate progress
 | 
			
		||||
            @include animation(progress .4s linear infinite);
 | 
			
		||||
            @include bgProgressAnim(#fff, 0.1, $progressBarStripeW);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.indeterminate .progress-amt {
 | 
			
		||||
        &:before {
 | 
			
		||||
            // More visible std diag stripe anim for indeterminate progress
 | 
			
		||||
            @include animation(progress .6s linear infinite);
 | 
			
		||||
            @include bgDiagonalStripes(#fff, 0.2, $progressBarStripeW);
 | 
			
		||||
        }
 | 
			
		||||
        &:after { display: none; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** SLIDERS */
 | 
			
		||||
 | 
			
		||||
.slider {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										306
									
								
								platform/commonUI/general/res/sass/controls/_messages.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,306 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
@mixin statusBannerColors($bg, $fg: $colorStatusFg) {
 | 
			
		||||
	$bgPb: 30%;
 | 
			
		||||
	$bgPbD: 10%;
 | 
			
		||||
	background-color: darken($bg, $bgPb);
 | 
			
		||||
	color: $fg;
 | 
			
		||||
	&:hover {
 | 
			
		||||
		background-color: darken($bg, $bgPb - $bgPbD);
 | 
			
		||||
	}
 | 
			
		||||
	.s-action {
 | 
			
		||||
		background-color: darken($bg, $bgPb + $bgPbD);
 | 
			
		||||
		&:hover {
 | 
			
		||||
			background-color: darken($bg, $bgPb);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.status.block {
 | 
			
		||||
	color: $colorStatusDefault;
 | 
			
		||||
	cursor: default;
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	margin-right: $interiorMargin;
 | 
			
		||||
	.status-indicator,
 | 
			
		||||
	.label,
 | 
			
		||||
	.count {
 | 
			
		||||
		//@include test(#00ff00);
 | 
			
		||||
		display: inline-block;
 | 
			
		||||
		vertical-align: top;
 | 
			
		||||
	}
 | 
			
		||||
	.status-indicator {
 | 
			
		||||
		margin-right: $interiorMarginSm;
 | 
			
		||||
	}
 | 
			
		||||
	&.ok .status-indicator,
 | 
			
		||||
    &.info .status-indicator {
 | 
			
		||||
		color: $colorStatusInfo;
 | 
			
		||||
	}
 | 
			
		||||
    &.alert .status-indicator,
 | 
			
		||||
	&.warning .status-indicator,
 | 
			
		||||
	&.caution .status-indicator {
 | 
			
		||||
		color: $colorStatusAlert;
 | 
			
		||||
	}
 | 
			
		||||
	&.error .status-indicator {
 | 
			
		||||
		color: $colorStatusError;
 | 
			
		||||
	}
 | 
			
		||||
	.label {
 | 
			
		||||
		// Max-width silliness is necessary for width transition
 | 
			
		||||
		@include trans-prop-nice(max-width, .25s);
 | 
			
		||||
		overflow: hidden;
 | 
			
		||||
		max-width: 0px;
 | 
			
		||||
	}
 | 
			
		||||
	.count {
 | 
			
		||||
		@include trans-prop-nice(opacity, .25s);
 | 
			
		||||
		font-weight: bold;
 | 
			
		||||
		opacity: 1;
 | 
			
		||||
	}
 | 
			
		||||
	&:hover {
 | 
			
		||||
		.label {
 | 
			
		||||
			max-width: 450px;
 | 
			
		||||
			width: auto;
 | 
			
		||||
		}
 | 
			
		||||
		.count {
 | 
			
		||||
			opacity: 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Styles for messages and message banners */
 | 
			
		||||
.message {
 | 
			
		||||
	&.block {
 | 
			
		||||
		@include border-radius($basicCr);
 | 
			
		||||
		padding: $interiorMarginLg;
 | 
			
		||||
	}
 | 
			
		||||
	&.error {
 | 
			
		||||
		background-color: rgba($colorAlert,0.3);
 | 
			
		||||
		color: lighten($colorAlert, 20%);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-message-banner {
 | 
			
		||||
	$m: $interiorMarginSm;
 | 
			
		||||
	$lh: $ueFooterH - ($m*2) - 1;
 | 
			
		||||
	@include box-sizing(border-box);
 | 
			
		||||
	@include ellipsize();
 | 
			
		||||
	@include display-flex;
 | 
			
		||||
	@include flex-direction(row);
 | 
			
		||||
	@include align-items(center);
 | 
			
		||||
	position: absolute;
 | 
			
		||||
	top: $m; right: auto; bottom: $m; left: 50%;
 | 
			
		||||
	height: auto; width: auto;
 | 
			
		||||
	line-height: $lh;
 | 
			
		||||
	max-width: 300px;
 | 
			
		||||
	padding: 0 $interiorMargin 0 $interiorMargin;
 | 
			
		||||
	@include transform(translateX(-50%));
 | 
			
		||||
 | 
			
		||||
	&.minimized {
 | 
			
		||||
        @include transition-property(left, opacity);
 | 
			
		||||
        @include transition-duration(0.3s);
 | 
			
		||||
        @include transition-timing-function(ease-in-out);
 | 
			
		||||
		left: 0;
 | 
			
		||||
        opacity: 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    &.new {
 | 
			
		||||
        left: 50%;
 | 
			
		||||
        opacity: 1;
 | 
			
		||||
        &:not(.info) {
 | 
			
		||||
            @include pulse(100ms, 10);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	.banner-elem {
 | 
			
		||||
		@include flex(0 1 auto);
 | 
			
		||||
		margin-left: $interiorMargin;
 | 
			
		||||
	}
 | 
			
		||||
	a {
 | 
			
		||||
		display: inline-block;
 | 
			
		||||
	}
 | 
			
		||||
	.l-action {
 | 
			
		||||
		line-height: $lh - 3;
 | 
			
		||||
		padding: 0 $interiorMargin;
 | 
			
		||||
	}
 | 
			
		||||
    .close {
 | 
			
		||||
		//@include test(red, 0.7);
 | 
			
		||||
		cursor: pointer;
 | 
			
		||||
        font-size: 7px;
 | 
			
		||||
		width: 8px;
 | 
			
		||||
    }
 | 
			
		||||
	.l-progress-bar {
 | 
			
		||||
		$h: $lh - 10;
 | 
			
		||||
		height: $h;
 | 
			
		||||
		line-height: $h;
 | 
			
		||||
		width: 100px;
 | 
			
		||||
	}
 | 
			
		||||
    .progress-info { display: none; }
 | 
			
		||||
	z-index: 10;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-message-banner {
 | 
			
		||||
    //@include transition-property(left, opacity);
 | 
			
		||||
    //@include transition-duration(0.35s);
 | 
			
		||||
    //@include transition-timing-function(ease-in-out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.s-message-banner {
 | 
			
		||||
	@include border-radius($controlCr);
 | 
			
		||||
	@include statusBannerColors($colorStatusDefault, $colorStatusFg);
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	a { color: inherit; }
 | 
			
		||||
	.s-action {
 | 
			
		||||
		@include border-radius($basicCr);
 | 
			
		||||
        @include trans-prop-nice(background-color);
 | 
			
		||||
	}
 | 
			
		||||
	.close {
 | 
			
		||||
		opacity: 0.5;
 | 
			
		||||
		&:hover {
 | 
			
		||||
			opacity: 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	&.ok,
 | 
			
		||||
    &.info {
 | 
			
		||||
		@include statusBannerColors($colorStatusInfo);
 | 
			
		||||
	}
 | 
			
		||||
	&.caution,
 | 
			
		||||
    &.warning,
 | 
			
		||||
    &.alert {
 | 
			
		||||
		@include statusBannerColors($colorStatusAlert);
 | 
			
		||||
	}
 | 
			
		||||
    &.error {
 | 
			
		||||
        @include statusBannerColors($colorStatusError);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin messageBlock($iconW: 32px) {
 | 
			
		||||
    .type-icon.message-type {
 | 
			
		||||
        @include txtShdw($shdwStatusIc);
 | 
			
		||||
        &:before { content:"\e608"; }
 | 
			
		||||
        color: $colorStatusDefault;
 | 
			
		||||
        font-size: $iconW;
 | 
			
		||||
        padding: 1px;
 | 
			
		||||
        width: $iconW + 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .message-severity-info .type-icon.message-type {
 | 
			
		||||
        &:before { content:"\e608"; }
 | 
			
		||||
        color: $colorStatusInfo;
 | 
			
		||||
    }
 | 
			
		||||
    .message-severity-alert .type-icon.message-type {
 | 
			
		||||
        &:before { content:"\e610"; }
 | 
			
		||||
        color: $colorStatusAlert;
 | 
			
		||||
    }
 | 
			
		||||
    .message-severity-error .type-icon.message-type {
 | 
			
		||||
        &:before { content:"\21"; }
 | 
			
		||||
        color: $colorStatusError;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/* Paths:
 | 
			
		||||
    t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message >
 | 
			
		||||
        message-type > (icon)
 | 
			
		||||
        message-contents >
 | 
			
		||||
        top-bar >
 | 
			
		||||
            title
 | 
			
		||||
            hint
 | 
			
		||||
        editor >
 | 
			
		||||
            (if displaying list of messages)
 | 
			
		||||
            ul > li > l-message >
 | 
			
		||||
                ... same as above
 | 
			
		||||
        bottom-bar
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
.l-message {
 | 
			
		||||
    @include display-flex;
 | 
			
		||||
    @include flex-direction(row);
 | 
			
		||||
    @include align-items(stretch);
 | 
			
		||||
    .type-icon.message-type {
 | 
			
		||||
        //@include test(red);
 | 
			
		||||
        @include flex(0 1 auto);
 | 
			
		||||
        position: relative;
 | 
			
		||||
    }
 | 
			
		||||
    .message-contents {
 | 
			
		||||
        //@include test(blue);
 | 
			
		||||
        @include flex(1 1 auto);
 | 
			
		||||
        margin-left: $overlayMargin;
 | 
			
		||||
        position: relative;
 | 
			
		||||
 | 
			
		||||
        .top-bar,
 | 
			
		||||
        .message-body {
 | 
			
		||||
            margin-bottom: $interiorMarginLg * 2;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Message as singleton
 | 
			
		||||
.t-message-single {
 | 
			
		||||
    @include messageBlock(80px);
 | 
			
		||||
 | 
			
		||||
    @include desktop {
 | 
			
		||||
        .l-message,
 | 
			
		||||
        .bottom-bar {
 | 
			
		||||
            @include absPosDefault();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .bottom-bar {
 | 
			
		||||
            top: auto;
 | 
			
		||||
            height: $ovrFooterH;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Messages in list
 | 
			
		||||
.t-message-list {
 | 
			
		||||
    @include messageBlock(32px);
 | 
			
		||||
 | 
			
		||||
    .message-contents {
 | 
			
		||||
        .l-message {
 | 
			
		||||
            //border-bottom: 1px solid pullForward($colorOvrBg, 20%);
 | 
			
		||||
            @include border-radius($controlCr);
 | 
			
		||||
            background: rgba($colorOvrFg, 0.1);
 | 
			
		||||
            margin-bottom: $interiorMargin;
 | 
			
		||||
            padding: $interiorMarginLg;
 | 
			
		||||
 | 
			
		||||
            .message-contents,
 | 
			
		||||
            .bottom-bar {
 | 
			
		||||
                //@include test(green);
 | 
			
		||||
                position: relative;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .message-contents {
 | 
			
		||||
                font-size: 0.9em;
 | 
			
		||||
                margin-left: $interiorMarginLg;
 | 
			
		||||
                .message-action { color: pushBack($colorOvrFg, 20%); }
 | 
			
		||||
                .bottom-bar { text-align: left; }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .top-bar,
 | 
			
		||||
            .message-body {
 | 
			
		||||
                margin-bottom: $interiorMarginLg;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @include desktop {
 | 
			
		||||
        .message-contents .l-message { margin-right: $interiorMarginLg; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
/************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */
 | 
			
		||||
$mobileListIconSize: 30px;
 | 
			
		||||
$mobileTitleDescH: 35px;
 | 
			
		||||
$mobileOverlayMargin: 10px;
 | 
			
		||||
$mobileOverlayMargin: 20px;
 | 
			
		||||
$phoneItemH: floor($ueBrowseGridItemLg/4);
 | 
			
		||||
$tabletItemH: floor($ueBrowseGridItemLg/3);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,12 @@
 | 
			
		||||
@include phoneandtablet {
 | 
			
		||||
    .overlay {
 | 
			
		||||
        $m: 0;
 | 
			
		||||
        .clk-icon.close {
 | 
			
		||||
            top: $mobileOverlayMargin; right: $mobileOverlayMargin;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        > .holder {
 | 
			
		||||
            @include border-radius($m);
 | 
			
		||||
            top: $m;
 | 
			
		||||
            right: $m;
 | 
			
		||||
            bottom: $m;
 | 
			
		||||
            left: $m;
 | 
			
		||||
            height: 90%; width: 90%;
 | 
			
		||||
 | 
			
		||||
            > .contents {
 | 
			
		||||
                top: $mobileOverlayMargin;
 | 
			
		||||
                right: $mobileOverlayMargin;
 | 
			
		||||
@@ -22,35 +18,64 @@
 | 
			
		||||
                        margin-right: 1.2em;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                .form.editor {
 | 
			
		||||
                    border: none;
 | 
			
		||||
 | 
			
		||||
                    .contents {
 | 
			
		||||
                        top: 0;
 | 
			
		||||
                        right: 0;
 | 
			
		||||
                        bottom: 0;
 | 
			
		||||
                        left: 0;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@include phone {
 | 
			
		||||
    .overlay > .holder > .contents .form.editor .contents .form-row {
 | 
			
		||||
        > .label,
 | 
			
		||||
        > .controls {
 | 
			
		||||
            //@include test(blue);
 | 
			
		||||
            display: block;
 | 
			
		||||
            float: none;
 | 
			
		||||
            width: 100%;
 | 
			
		||||
    .overlay > .holder {
 | 
			
		||||
        //@include test(orange); // This works!
 | 
			
		||||
        $m: 0;
 | 
			
		||||
        @include border-radius($m);
 | 
			
		||||
        top: $m;
 | 
			
		||||
        right: $m;
 | 
			
		||||
        bottom: $m;
 | 
			
		||||
        left: $m;
 | 
			
		||||
        height: auto; width: auto;
 | 
			
		||||
        min-width: 200px; min-height: 200px;
 | 
			
		||||
        max-height: 100%; max-width: 100%;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        @include transform(none);
 | 
			
		||||
 | 
			
		||||
        .editor .form .form-row {
 | 
			
		||||
            > .label,
 | 
			
		||||
            > .controls {
 | 
			
		||||
                //@include test(blue);
 | 
			
		||||
                display: block;
 | 
			
		||||
                float: none;
 | 
			
		||||
                width: 100%;
 | 
			
		||||
            }
 | 
			
		||||
            > .label {
 | 
			
		||||
                &:after {
 | 
			
		||||
                    float: none;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .contents {
 | 
			
		||||
            .abs.top-bar,
 | 
			
		||||
            .abs.editor,
 | 
			
		||||
            .abs.message-body,
 | 
			
		||||
            .abs.bottom-bar {
 | 
			
		||||
                //@include test(orange);
 | 
			
		||||
                top: auto; right: auto; bottom: auto; left: auto;
 | 
			
		||||
                height: auto; width: auto;
 | 
			
		||||
                margin-bottom: $interiorMarginLg * 2;
 | 
			
		||||
                position: relative;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .t-dialog-sm .overlay > .holder {
 | 
			
		||||
        //@include test(blue);
 | 
			
		||||
        height: auto; max-height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@include phonePortrait {
 | 
			
		||||
    .overlay > .holder {
 | 
			
		||||
        .contents .bottom-bar {
 | 
			
		||||
            text-align: center;
 | 
			
		||||
        }
 | 
			
		||||
	    > .label {
 | 
			
		||||
		    &:after {
 | 
			
		||||
			    float: none;
 | 
			
		||||
		    }
 | 
			
		||||
	    }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -20,79 +20,124 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
.overlay {
 | 
			
		||||
	.blocker {
 | 
			
		||||
		background: $colorOvrBlocker;
 | 
			
		||||
		z-index: 100;
 | 
			
		||||
	}
 | 
			
		||||
    font-size: 90%;
 | 
			
		||||
    .blocker {
 | 
			
		||||
        background: $colorOvrBlocker;
 | 
			
		||||
        z-index: 100;
 | 
			
		||||
    }
 | 
			
		||||
    .clk-icon.close {
 | 
			
		||||
        font-size: 0.8rem;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: $interiorMarginLg; right: $interiorMarginLg; bottom: auto; left: auto;
 | 
			
		||||
	    z-index: 100;
 | 
			
		||||
        top: $interiorMarginLg;
 | 
			
		||||
        right: $interiorMarginLg;
 | 
			
		||||
        bottom: auto;
 | 
			
		||||
        left: auto;
 | 
			
		||||
        z-index: 100;
 | 
			
		||||
    }
 | 
			
		||||
	>.holder {
 | 
			
		||||
		$i: 15%;
 | 
			
		||||
		@include containerSubtle($colorOvrBg, $colorOvrFg);
 | 
			
		||||
		@include border-radius($basicCr * 3);
 | 
			
		||||
		color: $colorOvrFg;
 | 
			
		||||
		top: $i; right: $i; bottom: $i; left: $i;
 | 
			
		||||
		z-index: 101;
 | 
			
		||||
        >.contents {
 | 
			
		||||
    > .holder {
 | 
			
		||||
        //$i: 15%;
 | 
			
		||||
        @include containerSubtle($colorOvrBg, $colorOvrFg);
 | 
			
		||||
        @include border-radius($basicCr * 3);
 | 
			
		||||
        color: $colorOvrFg;
 | 
			
		||||
        top: 50%;
 | 
			
		||||
        right: auto;
 | 
			
		||||
        bottom: auto;
 | 
			
		||||
        left: 50%;
 | 
			
		||||
        @include transform(translateX(-50%) translateY(-50%));
 | 
			
		||||
        height: 70%;
 | 
			
		||||
        width: 50%;
 | 
			
		||||
        min-height: 300px;
 | 
			
		||||
        max-height: 800px;
 | 
			
		||||
        min-width: 600px;
 | 
			
		||||
        max-width: 1000px;
 | 
			
		||||
        z-index: 101;
 | 
			
		||||
        > .contents {
 | 
			
		||||
            $m: $overlayMargin;
 | 
			
		||||
            top: $m; right: $m; bottom: $m; left: $m;
 | 
			
		||||
            top: $m;
 | 
			
		||||
            right: $m;
 | 
			
		||||
            bottom: $m;
 | 
			
		||||
            left: $m;
 | 
			
		||||
 | 
			
		||||
            //.top-bar,
 | 
			
		||||
            //.editor,
 | 
			
		||||
            //.bottom-bar {
 | 
			
		||||
            //    @include absPosDefault();
 | 
			
		||||
            //}
 | 
			
		||||
        }
 | 
			
		||||
	}
 | 
			
		||||
    .title {
 | 
			
		||||
	    @include ellipsize();
 | 
			
		||||
        font-size: 1.2em;
 | 
			
		||||
	    margin-bottom: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .top-bar {
 | 
			
		||||
 | 
			
		||||
    .title {
 | 
			
		||||
        @include ellipsize();
 | 
			
		||||
        font-size: 1.2em;
 | 
			
		||||
        line-height: 120%;
 | 
			
		||||
        margin-bottom: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .hint {
 | 
			
		||||
        color: pushBack($colorOvrFg, 20%);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .abs.top-bar {
 | 
			
		||||
        height: $ovrTopBarH;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .editor {
 | 
			
		||||
        top: $ovrTopBarH + ($interiorMargin * 2);
 | 
			
		||||
        bottom: $ovrFooterH + $interiorMargin * 2;
 | 
			
		||||
        left: 0; right: 0;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .bottom-bar {
 | 
			
		||||
        top: auto; right: 0; bottom: 0; left: 0;
 | 
			
		||||
	    overflow: visible;
 | 
			
		||||
        //font-size: 1em;
 | 
			
		||||
        height: $ovrFooterH;
 | 
			
		||||
        text-align: right;
 | 
			
		||||
        .s-btn {
 | 
			
		||||
	        $bg: $colorOvrBtnBg;
 | 
			
		||||
	        &:not(.major) {
 | 
			
		||||
	            @include btnSubtle($bg, pullForward($bg, 10%), $colorOvrBtnFg, $colorOvrBtnFg);
 | 
			
		||||
	        }
 | 
			
		||||
	        font-size: 95%;
 | 
			
		||||
	        height: $ovrFooterH;
 | 
			
		||||
	        line-height: $ovrFooterH;
 | 
			
		||||
            margin-left: $interiorMargin;
 | 
			
		||||
	        padding: 0 $interiorMargin * 3;
 | 
			
		||||
	        //&.major {
 | 
			
		||||
		     //   @extend .s-btn.major;
 | 
			
		||||
		     //   &:hover {
 | 
			
		||||
			 //       @extend .s-btn.major:hover;
 | 
			
		||||
		     //   }
 | 
			
		||||
	        //}
 | 
			
		||||
 | 
			
		||||
    .abs.editor,
 | 
			
		||||
    .abs.message-body {
 | 
			
		||||
        top: $ovrTopBarH + $interiorMarginLg;
 | 
			
		||||
        bottom: $ovrFooterH + $interiorMarginLg;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        .field.l-med {
 | 
			
		||||
            input[type='text'] {
 | 
			
		||||
                width: 100%;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .contents.l-dialog {
 | 
			
		||||
	    $myM: $interiorMargin;
 | 
			
		||||
	    top: $myM;
 | 
			
		||||
	    right: $myM;
 | 
			
		||||
	    bottom: $myM;
 | 
			
		||||
	    left: $myM;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
	    .field.l-med {
 | 
			
		||||
		    input[type='text'] {
 | 
			
		||||
			    width: 100%;
 | 
			
		||||
		    }
 | 
			
		||||
	    }
 | 
			
		||||
 | 
			
		||||
    .bottom-bar {
 | 
			
		||||
        text-align: right;
 | 
			
		||||
        .s-btn {
 | 
			
		||||
            $bg: $colorOvrBtnBg;
 | 
			
		||||
            &:not(.major) {
 | 
			
		||||
                @include btnSubtle($bg, pullForward($bg, 10%), $colorOvrBtnFg, $colorOvrBtnFg);
 | 
			
		||||
            }
 | 
			
		||||
            font-size: 95%;
 | 
			
		||||
            height: $ovrFooterH;
 | 
			
		||||
            line-height: $ovrFooterH;
 | 
			
		||||
            margin-left: $interiorMargin;
 | 
			
		||||
            padding: 0 $interiorMargin * 3;
 | 
			
		||||
            &:first-child {
 | 
			
		||||
                margin-left: 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .abs.bottom-bar {
 | 
			
		||||
        top: auto;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        overflow: visible;
 | 
			
		||||
        //font-size: 1em;
 | 
			
		||||
        height: $ovrFooterH;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .l-progress-bar {
 | 
			
		||||
        $h: $progressBarHOverlay;
 | 
			
		||||
        display: block;
 | 
			
		||||
        height: $h;
 | 
			
		||||
        line-height: $h;
 | 
			
		||||
        margin: .5em 0;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.t-dialog-sm .overlay > .holder {
 | 
			
		||||
    // Used for blocker and in-progress dialogs, modal alerts, etc.
 | 
			
		||||
    //@include test(red);
 | 
			
		||||
    $h: 225px;
 | 
			
		||||
    min-height: $h;
 | 
			
		||||
    height: $h;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
.ue-bottom-bar {
 | 
			
		||||
	background: $colorFooterBg;
 | 
			
		||||
	color: lighten($colorBodyBg, 30%);
 | 
			
		||||
	font-size: .7rem;
 | 
			
		||||
	//line-height: $ueFooterH - 4px;
 | 
			
		||||
	//line-height: $ueFooterH;   // New status bar design
 | 
			
		||||
	.status-holder {
 | 
			
		||||
		//@include border-radius($basicCr * 1.75);   // New status bar design
 | 
			
		||||
		@include box-sizing(border-box);
 | 
			
		||||
		//background: $colorFooterBg;
 | 
			
		||||
        //border-bottom: 1px solid lighten($colorBodyBg, 10%);  // New status bar design
 | 
			
		||||
		@include absPosDefault($interiorMargin);
 | 
			
		||||
		@include ellipsize();
 | 
			
		||||
		line-height: $ueFooterH - ($interiorMargin * 2);
 | 
			
		||||
		right: 120px;
 | 
			
		||||
		text-transform: uppercase;
 | 
			
		||||
	}
 | 
			
		||||
	.app-logo {
 | 
			
		||||
		@include box-sizing(border-box);
 | 
			
		||||
		@include absPosDefault($interiorMargin);
 | 
			
		||||
		left: auto;
 | 
			
		||||
		cursor: pointer;
 | 
			
		||||
		//font-size: 0.8em;
 | 
			
		||||
		//line-height: $ueFooterH - 10px;
 | 
			
		||||
		//padding-top: 1px;
 | 
			
		||||
		//text-transform: uppercase;
 | 
			
		||||
		&.logo-openmctweb {
 | 
			
		||||
			background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.status.block {
 | 
			
		||||
	//display: inline-block;
 | 
			
		||||
	display: inline;  // New status bar design. Inline to support ellipsis overflow
 | 
			
		||||
	margin-right: $interiorMarginLg;
 | 
			
		||||
	.status-indicator {
 | 
			
		||||
		//@include border-radius($controlCr * 0.9);
 | 
			
		||||
		//@include box-shadow(inset rgba(black, 0.5) 0 0 3px);
 | 
			
		||||
		//@include text-shadow(rgba(black, 0.3) 0 0 2px);
 | 
			
		||||
		display: inline-block;
 | 
			
		||||
		margin-right: $interiorMarginSm;
 | 
			
		||||
		color: $colorKey;
 | 
			
		||||
		&.ok {
 | 
			
		||||
			color: #009900;
 | 
			
		||||
		}
 | 
			
		||||
		&.caution {
 | 
			
		||||
			color: #ffaa00;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -43,9 +43,9 @@
 | 
			
		||||
    position: absolute;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.editor {
 | 
			
		||||
    @include border-radius($basicCr * 1.5);
 | 
			
		||||
}
 | 
			
		||||
//.editor {
 | 
			
		||||
//	@include border-radius($basicCr * 1.5);
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
.contents {
 | 
			
		||||
    $myM: 0; //$interiorMargin;
 | 
			
		||||
@@ -125,21 +125,37 @@
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .ue-bottom-bar {
 | 
			
		||||
        //@include absPosDefault($bodyMargin);
 | 
			
		||||
        @include absPosDefault(0); // New status bar design
 | 
			
		||||
        top: auto;
 | 
			
		||||
        height: $ueFooterH;
 | 
			
		||||
        .status-holder {
 | 
			
		||||
            //right: $ueAppLogoW + $bodyMargin; New status bar design
 | 
			
		||||
            z-index: 1;
 | 
			
		||||
        }
 | 
			
		||||
        .app-logo {
 | 
			
		||||
            left: auto;
 | 
			
		||||
            width: $ueAppLogoW;
 | 
			
		||||
            z-index: 2;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	// from _bottom-bar.scss
 | 
			
		||||
	.ue-bottom-bar {
 | 
			
		||||
		@include absPosDefault(0);// New status bar design
 | 
			
		||||
		top: auto;
 | 
			
		||||
		height: $ueFooterH;
 | 
			
		||||
		line-height: $ueFooterH - ($interiorMargin * 2);
 | 
			
		||||
		background: $colorFooterBg;
 | 
			
		||||
		color: lighten($colorBodyBg, 30%);
 | 
			
		||||
		font-size: .7rem;
 | 
			
		||||
 | 
			
		||||
		.status-holder {
 | 
			
		||||
			@include box-sizing(border-box);
 | 
			
		||||
			@include absPosDefault($interiorMargin);
 | 
			
		||||
			@include ellipsize();
 | 
			
		||||
			//line-height: $ueFooterH - ($interiorMargin * 2);
 | 
			
		||||
			right: 120px;
 | 
			
		||||
			text-transform: uppercase;
 | 
			
		||||
			z-index: 1;
 | 
			
		||||
		}
 | 
			
		||||
		.app-logo {
 | 
			
		||||
			@include box-sizing(border-box);
 | 
			
		||||
			@include absPosDefault($interiorMargin);
 | 
			
		||||
			cursor: pointer;
 | 
			
		||||
			left: auto;
 | 
			
		||||
			width: $ueAppLogoW;
 | 
			
		||||
			z-index: 2;
 | 
			
		||||
			&.logo-openmctweb {
 | 
			
		||||
				background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.cols {
 | 
			
		||||
@@ -343,4 +359,4 @@
 | 
			
		||||
        @include webkitProp(flex, '1 1 0');
 | 
			
		||||
        padding-right: $interiorMarginLg;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,5 +26,6 @@
 | 
			
		||||
                     key="indicator.template">
 | 
			
		||||
        </mct-include>
 | 
			
		||||
    </div>
 | 
			
		||||
    <mct-include key="'message-banner'"></mct-include>
 | 
			
		||||
    <mct-include key="'about-logo'"></mct-include>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -19,84 +19,90 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<!-- MINE -->
 | 
			
		||||
<div ng-controller="TimeRangeController">
 | 
			
		||||
	<div class="l-time-range-inputs-holder">
 | 
			
		||||
		<span class="l-time-range-inputs-elem ui-symbol type-icon">C</span>
 | 
			
		||||
	    <span class="l-time-range-input" ng-controller="ToggleController as t1">
 | 
			
		||||
	        <!--<span class="lbl">Start</span>-->
 | 
			
		||||
		    <span class="s-btn time-range-start" ng-click="t1.toggle()">
 | 
			
		||||
		        <span class="val">{{startOuterText}}</span>
 | 
			
		||||
	            <a class="ui-symbol icon icon-calendar"></a>
 | 
			
		||||
	            <mct-popup ng-if="t1.isActive()">
 | 
			
		||||
		            <div mct-click-elsewhere="t1.setState(false)">
 | 
			
		||||
			            <mct-control key="'datetime-picker'"
 | 
			
		||||
			                         ng-model="ngModel.outer"
 | 
			
		||||
			                         field="'start'"
 | 
			
		||||
			                         options="{ hours: true }">
 | 
			
		||||
			            </mct-control>
 | 
			
		||||
		            </div>
 | 
			
		||||
	            </mct-popup>
 | 
			
		||||
		    </span>
 | 
			
		||||
	    </span>
 | 
			
		||||
    <div class="l-time-range-inputs-holder">
 | 
			
		||||
        <span class="l-time-range-inputs-elem ui-symbol type-icon">C</span>
 | 
			
		||||
        <span class="l-time-range-input" ng-controller="ToggleController as t1">
 | 
			
		||||
            <!--<span class="lbl">Start</span>-->
 | 
			
		||||
            <span class="s-btn time-range-start">
 | 
			
		||||
                <input type="text"
 | 
			
		||||
                       ng-model="boundsModel.start"
 | 
			
		||||
                       ng-class="{ error: !boundsModel.startValid }">
 | 
			
		||||
                </input>
 | 
			
		||||
                <a class="ui-symbol icon icon-calendar" ng-click="t1.toggle()"></a>
 | 
			
		||||
                <mct-popup ng-if="t1.isActive()">
 | 
			
		||||
                    <div mct-click-elsewhere="t1.setState(false)">
 | 
			
		||||
                        <mct-control key="'datetime-picker'"
 | 
			
		||||
                                     ng-model="ngModel.outer"
 | 
			
		||||
                                     field="'start'"
 | 
			
		||||
                                     options="{ hours: true }">
 | 
			
		||||
                        </mct-control>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </mct-popup>
 | 
			
		||||
            </span>
 | 
			
		||||
        </span>
 | 
			
		||||
 | 
			
		||||
		<span class="l-time-range-inputs-elem lbl">to</span>
 | 
			
		||||
        <span class="l-time-range-inputs-elem lbl">to</span>
 | 
			
		||||
 | 
			
		||||
	    <span class="l-time-range-input" ng-controller="ToggleController as t2">
 | 
			
		||||
	        <!--<span class="lbl">End</span>-->
 | 
			
		||||
		    <span class="s-btn l-time-range-input" ng-click="t2.toggle()">
 | 
			
		||||
			    <span class="val">{{endOuterText}}</span>
 | 
			
		||||
	            <a class="ui-symbol icon icon-calendar"></a>
 | 
			
		||||
	            <mct-popup ng-if="t2.isActive()">
 | 
			
		||||
		            <div mct-click-elsewhere="t2.setState(false)">
 | 
			
		||||
			            <mct-control key="'datetime-picker'"
 | 
			
		||||
			                         ng-model="ngModel.outer"
 | 
			
		||||
			                         field="'end'"
 | 
			
		||||
			                         options="{ hours: true }">
 | 
			
		||||
			            </mct-control>
 | 
			
		||||
		            </div>
 | 
			
		||||
	            </mct-popup>
 | 
			
		||||
		    </span> 
 | 
			
		||||
	    </span>
 | 
			
		||||
	</div>
 | 
			
		||||
        <span class="l-time-range-input" ng-controller="ToggleController as t2">
 | 
			
		||||
            <!--<span class="lbl">End</span>-->
 | 
			
		||||
            <span class="s-btn l-time-range-input">
 | 
			
		||||
                <input type="text"
 | 
			
		||||
                       ng-model="boundsModel.end"
 | 
			
		||||
                       ng-class="{ error: !boundsModel.endValid }">
 | 
			
		||||
                </input>
 | 
			
		||||
                <a class="ui-symbol icon icon-calendar" ng-click="t2.toggle()">
 | 
			
		||||
                </a>
 | 
			
		||||
                <mct-popup ng-if="t2.isActive()">
 | 
			
		||||
                    <div mct-click-elsewhere="t2.setState(false)">
 | 
			
		||||
                        <mct-control key="'datetime-picker'"
 | 
			
		||||
                                     ng-model="ngModel.outer"
 | 
			
		||||
                                     field="'end'"
 | 
			
		||||
                                     options="{ hours: true }">
 | 
			
		||||
                        </mct-control>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </mct-popup>
 | 
			
		||||
            </span> 
 | 
			
		||||
        </span>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
	<div class="l-time-range-slider-holder">
 | 
			
		||||
		<div class="l-time-range-slider">
 | 
			
		||||
			<div class="slider"
 | 
			
		||||
			     mct-resize="spanWidth = bounds.width">
 | 
			
		||||
				<div class="knob knob-l"
 | 
			
		||||
				     mct-drag-down="startLeftDrag()"
 | 
			
		||||
				     mct-drag="leftDrag(delta[0])"
 | 
			
		||||
				     ng-style="{ left: startInnerPct }">
 | 
			
		||||
					<div class="range-value">{{startInnerText}}</div>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="knob knob-r"
 | 
			
		||||
				     mct-drag-down="startRightDrag()"
 | 
			
		||||
				     mct-drag="rightDrag(delta[0])"
 | 
			
		||||
				     ng-style="{ right: endInnerPct }">
 | 
			
		||||
					<div class="range-value">{{endInnerText}}</div>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="slot range-holder">
 | 
			
		||||
					<div class="range"
 | 
			
		||||
					     mct-drag-down="startMiddleDrag()"
 | 
			
		||||
					     mct-drag="middleDrag(delta[0])"
 | 
			
		||||
					     ng-style="{ left: startInnerPct, right: endInnerPct}">
 | 
			
		||||
						<div class="toi-line"></div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
    <div class="l-time-range-slider-holder">
 | 
			
		||||
        <div class="l-time-range-slider">
 | 
			
		||||
            <div class="slider"
 | 
			
		||||
                 mct-resize="spanWidth = bounds.width">
 | 
			
		||||
                <div class="knob knob-l"
 | 
			
		||||
                     mct-drag-down="startLeftDrag()"
 | 
			
		||||
                     mct-drag="leftDrag(delta[0])"
 | 
			
		||||
                     ng-style="{ left: startInnerPct }">
 | 
			
		||||
                    <div class="range-value">{{startInnerText}}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="knob knob-r"
 | 
			
		||||
                     mct-drag-down="startRightDrag()"
 | 
			
		||||
                     mct-drag="rightDrag(delta[0])"
 | 
			
		||||
                     ng-style="{ right: endInnerPct }">
 | 
			
		||||
                    <div class="range-value">{{endInnerText}}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="slot range-holder">
 | 
			
		||||
                    <div class="range"
 | 
			
		||||
                         mct-drag-down="startMiddleDrag()"
 | 
			
		||||
                         mct-drag="middleDrag(delta[0])"
 | 
			
		||||
                         ng-style="{ left: startInnerPct, right: endInnerPct}">
 | 
			
		||||
                        <div class="toi-line"></div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
	<div class="l-time-range-ticks-holder">
 | 
			
		||||
		<div class="l-time-range-ticks">
 | 
			
		||||
			<div
 | 
			
		||||
				ng-repeat="tick in ticks"
 | 
			
		||||
				ng-style="{ left: $index * (100 / (ticks.length - 1)) + '%' }"
 | 
			
		||||
				class="tick tick-x"
 | 
			
		||||
				>
 | 
			
		||||
				<span class="l-time-range-tick-label">{{tick}}</span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
    <div class="l-time-range-ticks-holder">
 | 
			
		||||
        <div class="l-time-range-ticks">
 | 
			
		||||
            <div
 | 
			
		||||
                ng-repeat="tick in ticks"
 | 
			
		||||
                ng-style="{ left: $index * (100 / (ticks.length - 1)) + '%' }"
 | 
			
		||||
                class="tick tick-x"
 | 
			
		||||
                >
 | 
			
		||||
                <span class="l-time-range-tick-label">{{tick}}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,20 +19,21 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
 | 
			
		||||
<!--<div ng-init="reps = [1,2,3]"></div>-->
 | 
			
		||||
<div class='status block'
 | 
			
		||||
     title="{{ngModel.getDescription()}}"
 | 
			
		||||
     ng-click='ngModel.configure()'
 | 
			
		||||
     ng-class='ngModel.getClass()'
 | 
			
		||||
     ng-show="ngModel.getText().length > 0">
 | 
			
		||||
    <span class="ui-symbol status-indicator"
 | 
			
		||||
          ng-class='ngModel.getGlyphClass()'>
 | 
			
		||||
        {{ngModel.getGlyph()}}
 | 
			
		||||
    </span>
 | 
			
		||||
    <span class="label"
 | 
			
		||||
          ng-class='ngModel.getTextClass()'>
 | 
			
		||||
        {{ngModel.getText()}}
 | 
			
		||||
    </span>
 | 
			
		||||
	<a href=''
 | 
			
		||||
	 title="{{ngModel.getDescription()}}"
 | 
			
		||||
	 ng-click='ngModel.configure()'
 | 
			
		||||
	 ng-class='ngModel.getGlyphClass()'
 | 
			
		||||
	 ng-show="ngModel.getText().length > 0">
 | 
			
		||||
	<span class="ui-symbol status-indicator">
 | 
			
		||||
		{{ngModel.getGlyph()}}
 | 
			
		||||
	</span><span class="label"
 | 
			
		||||
		  ng-class='ngModel.getTextClass()'>
 | 
			
		||||
		{{ngModel.getText()}}
 | 
			
		||||
	</span><span class="count">
 | 
			
		||||
		<!-- Add int count value here if this type of indicator has one or more messages associated with it -->
 | 
			
		||||
	</span><a href=''
 | 
			
		||||
	   class="ui-symbol"
 | 
			
		||||
	   ng-if="ngModel.configure">
 | 
			
		||||
		G
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								platform/commonUI/general/res/templates/message-banner.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,21 @@
 | 
			
		||||
<div ng-controller="BannerController" ng-show="active.notification"
 | 
			
		||||
     class="l-message-banner s-message-banner {{active.notification.model.severity}}" ng-class="{
 | 
			
		||||
     'minimized': active.notification.model.minimized,
 | 
			
		||||
     'new': !active.notification.model.minimized}"
 | 
			
		||||
     ng-click="maximize(active.notification)">
 | 
			
		||||
    <span class="banner-elem label">
 | 
			
		||||
        {{active.notification.model.title}}
 | 
			
		||||
    </span>
 | 
			
		||||
    <span ng-show="active.notification.model.progress !== undefined || active.notification.model.unknownProgress">
 | 
			
		||||
        <mct-include key="'progress-bar'" class="banner-elem"
 | 
			
		||||
                     ng-model="active.notification.model">
 | 
			
		||||
        </mct-include>
 | 
			
		||||
    </span>
 | 
			
		||||
    <a ng-hide="active.notification.model.primaryOption === undefined"
 | 
			
		||||
       class="banner-elem l-action s-action"
 | 
			
		||||
       ng-click="action(active.notification.model.primaryOption.callback, $event)">
 | 
			
		||||
        {{active.notification.model.primaryOption.label}}
 | 
			
		||||
    </a>
 | 
			
		||||
    <a class="banner-elem ui-symbol close" ng-click="dismiss(active.notification, $event)">
 | 
			
		||||
        x</a>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										10
									
								
								platform/commonUI/general/res/templates/progress-bar.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,10 @@
 | 
			
		||||
<span class="l-progress-bar s-progress-bar"
 | 
			
		||||
      ng-class="{ indeterminate:ngModel.unknownProgress }">
 | 
			
		||||
    <span class="progress-amt-holder">
 | 
			
		||||
        <span class="progress-amt" style="width: {{ngModel.progress}}%"></span>
 | 
			
		||||
    </span>
 | 
			
		||||
</span>
 | 
			
		||||
<div class="progress-info hint" ng-hide="ngModel.progressText === undefined">
 | 
			
		||||
    <span class="progress-amt-text" ng-show="ngModel.progress > 0">{{ngModel.progress}}% complete. </span>
 | 
			
		||||
    {{ngModel.progressText}}
 | 
			
		||||
</div>
 | 
			
		||||
@@ -0,0 +1,68 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A controller for banner notifications. Banner notifications are a
 | 
			
		||||
         * non-blocking way of drawing the user's attention to an event such
 | 
			
		||||
         * as system errors, or the progress or successful completion of an
 | 
			
		||||
         * ongoing task. This controller provides scoped functions for
 | 
			
		||||
         * dismissing and 'maximizing' notifications. See {@link NotificationService}
 | 
			
		||||
         * for more details on Notifications.
 | 
			
		||||
         *
 | 
			
		||||
         * @param $scope
 | 
			
		||||
         * @param notificationService
 | 
			
		||||
         * @param dialogService
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function BannerController($scope, notificationService, dialogService) {
 | 
			
		||||
            $scope.active = notificationService.active;
 | 
			
		||||
 | 
			
		||||
            $scope.action = function (action, $event){
 | 
			
		||||
                /*
 | 
			
		||||
                 Prevents default 'maximize' behaviour when clicking on
 | 
			
		||||
                  notification button
 | 
			
		||||
                 */
 | 
			
		||||
                $event.stopPropagation();
 | 
			
		||||
                return action();
 | 
			
		||||
            };
 | 
			
		||||
            $scope.dismiss = function(notification, $event) {
 | 
			
		||||
                $event.stopPropagation();
 | 
			
		||||
                notification.dismissOrMinimize();
 | 
			
		||||
            };
 | 
			
		||||
            $scope.maximize = function(notification) {
 | 
			
		||||
                if (notification.model.severity !== "info"){
 | 
			
		||||
 | 
			
		||||
                    notification.model.cancel = function(){
 | 
			
		||||
                        dialogService.dismiss();
 | 
			
		||||
                    };
 | 
			
		||||
                    dialogService.showBlockingMessage(notification.model);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        return BannerController;
 | 
			
		||||
    });
 | 
			
		||||
@@ -26,9 +26,8 @@ define(
 | 
			
		||||
    function (moment) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var
 | 
			
		||||
				DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
 | 
			
		||||
				TICK_SPACING_PX = 150;
 | 
			
		||||
        var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
 | 
			
		||||
            TICK_SPACING_PX = 150;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @memberof platform/commonUI/general
 | 
			
		||||
@@ -44,6 +43,15 @@ define(
 | 
			
		||||
                return moment.utc(ts).format(DATE_FORMAT);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function parseTimestamp(text) {
 | 
			
		||||
                var m = moment.utc(text, DATE_FORMAT);
 | 
			
		||||
                if (m.isValid()) {
 | 
			
		||||
                    return m.valueOf();
 | 
			
		||||
                } else {
 | 
			
		||||
                    throw new Error("Could not parse " + text);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // From 0.0-1.0 to "0%"-"1%"
 | 
			
		||||
            function toPercent(p) {
 | 
			
		||||
                return (100 * p) + "%";
 | 
			
		||||
@@ -93,6 +101,25 @@ define(
 | 
			
		||||
                return { start: bounds.start, end: bounds.end };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateBoundsTextForProperty(ngModel, property) {
 | 
			
		||||
                try {
 | 
			
		||||
                    if (!$scope.boundsModel[property] ||
 | 
			
		||||
                            parseTimestamp($scope.boundsModel[property]) !==
 | 
			
		||||
                                ngModel.outer[property]) {
 | 
			
		||||
                        $scope.boundsModel[property] =
 | 
			
		||||
                            formatTimestamp(ngModel.outer[property]);
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
                    // User-entered text is invalid, so leave it be
 | 
			
		||||
                    // until they fix it.
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateBoundsText(ngModel) {
 | 
			
		||||
                updateBoundsTextForProperty(ngModel, 'start');
 | 
			
		||||
                updateBoundsTextForProperty(ngModel, 'end');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateViewFromModel(ngModel) {
 | 
			
		||||
                var t = now();
 | 
			
		||||
 | 
			
		||||
@@ -101,8 +128,7 @@ define(
 | 
			
		||||
                ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
 | 
			
		||||
 | 
			
		||||
                // First, dates for the date pickers for outer bounds
 | 
			
		||||
                $scope.startOuterDate = new Date(ngModel.outer.start);
 | 
			
		||||
                $scope.endOuterDate = new Date(ngModel.outer.end);
 | 
			
		||||
                updateBoundsText(ngModel);
 | 
			
		||||
 | 
			
		||||
                // Then various updates for the inner span
 | 
			
		||||
                updateViewForInnerSpanFromModel(ngModel);
 | 
			
		||||
@@ -178,6 +204,8 @@ define(
 | 
			
		||||
            function updateOuterStart(t) {
 | 
			
		||||
                var ngModel = $scope.ngModel;
 | 
			
		||||
 | 
			
		||||
                ngModel.outer.start = t;
 | 
			
		||||
 | 
			
		||||
                ngModel.outer.end = Math.max(
 | 
			
		||||
                    ngModel.outer.start + outerMinimumSpan,
 | 
			
		||||
                    ngModel.outer.end
 | 
			
		||||
@@ -190,14 +218,15 @@ define(
 | 
			
		||||
                    ngModel.inner.end
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                $scope.startOuterText = formatTimestamp(t);
 | 
			
		||||
 | 
			
		||||
                updateViewForInnerSpanFromModel(ngModel);
 | 
			
		||||
                updateTicks();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateOuterEnd(t) {
 | 
			
		||||
                var ngModel = $scope.ngModel;
 | 
			
		||||
 | 
			
		||||
                ngModel.outer.end = t;
 | 
			
		||||
 | 
			
		||||
                ngModel.outer.start = Math.min(
 | 
			
		||||
                    ngModel.outer.end - outerMinimumSpan,
 | 
			
		||||
                    ngModel.outer.start
 | 
			
		||||
@@ -210,9 +239,40 @@ define(
 | 
			
		||||
                    ngModel.inner.start
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                $scope.endOuterText = formatTimestamp(t);
 | 
			
		||||
 | 
			
		||||
                updateViewForInnerSpanFromModel(ngModel);
 | 
			
		||||
                updateTicks();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateStartFromText(value) {
 | 
			
		||||
                try {
 | 
			
		||||
                    updateOuterStart(parseTimestamp(value));
 | 
			
		||||
                    updateBoundsTextForProperty($scope.ngModel, 'end');
 | 
			
		||||
                    $scope.boundsModel.startValid = true;
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
                    $scope.boundsModel.startValid = false;
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateEndFromText(value) {
 | 
			
		||||
                try {
 | 
			
		||||
                    updateOuterEnd(parseTimestamp(value));
 | 
			
		||||
                    updateBoundsTextForProperty($scope.ngModel, 'start');
 | 
			
		||||
                    $scope.boundsModel.endValid = true;
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
                    $scope.boundsModel.endValid = false;
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateStartFromPicker(value) {
 | 
			
		||||
                updateOuterStart(value);
 | 
			
		||||
                updateBoundsText($scope.ngModel);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function updateEndFromPicker(value) {
 | 
			
		||||
                updateOuterEnd(value);
 | 
			
		||||
                updateBoundsText($scope.ngModel);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $scope.startLeftDrag = startLeftDrag;
 | 
			
		||||
@@ -224,14 +284,17 @@ define(
 | 
			
		||||
 | 
			
		||||
            $scope.state = false;
 | 
			
		||||
            $scope.ticks = [];
 | 
			
		||||
            $scope.boundsModel = {};
 | 
			
		||||
 | 
			
		||||
            // Initialize scope to defaults
 | 
			
		||||
            updateViewFromModel($scope.ngModel);
 | 
			
		||||
 | 
			
		||||
            $scope.$watchCollection("ngModel", updateViewFromModel);
 | 
			
		||||
            $scope.$watch("spanWidth", updateSpanWidth);
 | 
			
		||||
            $scope.$watch("ngModel.outer.start", updateOuterStart);
 | 
			
		||||
            $scope.$watch("ngModel.outer.end", updateOuterEnd);
 | 
			
		||||
            $scope.$watch("ngModel.outer.start", updateStartFromPicker);
 | 
			
		||||
            $scope.$watch("ngModel.outer.end", updateEndFromPicker);
 | 
			
		||||
            $scope.$watch("boundsModel.start", updateStartFromText);
 | 
			
		||||
            $scope.$watch("boundsModel.end", updateEndFromText);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TimeConductorController;
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,8 @@
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/controllers/TimeRangeController"],
 | 
			
		||||
    function (TimeRangeController) {
 | 
			
		||||
    ["../../src/controllers/TimeRangeController", "moment"],
 | 
			
		||||
    function (TimeRangeController, moment) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        var SEC = 1000,
 | 
			
		||||
@@ -166,8 +166,72 @@ define(
 | 
			
		||||
                    expect(mockScope.ngModel.inner.end)
 | 
			
		||||
                        .toBeGreaterThan(mockScope.ngModel.inner.start);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("by typing", function () {
 | 
			
		||||
                    it("updates models", function () {
 | 
			
		||||
                        var newStart = "1977-05-25 17:30:00",
 | 
			
		||||
                            newEnd = "2015-12-18 03:30:00";
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.start = newStart;
 | 
			
		||||
                        fireWatch("boundsModel.start", newStart);
 | 
			
		||||
                        expect(mockScope.ngModel.outer.start)
 | 
			
		||||
                            .toEqual(moment.utc(newStart).valueOf());
 | 
			
		||||
                        expect(mockScope.boundsModel.startValid)
 | 
			
		||||
                            .toBeTruthy();
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.end = newEnd;
 | 
			
		||||
                        fireWatch("boundsModel.end", newEnd);
 | 
			
		||||
                        expect(mockScope.ngModel.outer.end)
 | 
			
		||||
                            .toEqual(moment.utc(newEnd).valueOf());
 | 
			
		||||
                        expect(mockScope.boundsModel.endValid)
 | 
			
		||||
                            .toBeTruthy();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("displays error state", function () {
 | 
			
		||||
                        var newStart = "Not a date",
 | 
			
		||||
                            newEnd = "Definitely not a date",
 | 
			
		||||
                            oldStart = mockScope.ngModel.outer.start,
 | 
			
		||||
                            oldEnd = mockScope.ngModel.outer.end;
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.start = newStart;
 | 
			
		||||
                        fireWatch("boundsModel.start", newStart);
 | 
			
		||||
                        expect(mockScope.ngModel.outer.start)
 | 
			
		||||
                            .toEqual(oldStart);
 | 
			
		||||
                        expect(mockScope.boundsModel.startValid)
 | 
			
		||||
                            .toBeFalsy();
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.end = newEnd;
 | 
			
		||||
                        fireWatch("boundsModel.end", newEnd);
 | 
			
		||||
                        expect(mockScope.ngModel.outer.end)
 | 
			
		||||
                            .toEqual(oldEnd);
 | 
			
		||||
                        expect(mockScope.boundsModel.endValid)
 | 
			
		||||
                            .toBeFalsy();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("does not modify user input", function () {
 | 
			
		||||
                        // Don't want the controller "fixing" bad or
 | 
			
		||||
                        // irregularly-formatted input out from under
 | 
			
		||||
                        // the user's fingertips.
 | 
			
		||||
                        var newStart = "Not a date",
 | 
			
		||||
                            newEnd = "2015-3-3 01:02:04",
 | 
			
		||||
                            oldStart = mockScope.ngModel.outer.start,
 | 
			
		||||
                            oldEnd = mockScope.ngModel.outer.end;
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.start = newStart;
 | 
			
		||||
                        fireWatch("boundsModel.start", newStart);
 | 
			
		||||
                        expect(mockScope.boundsModel.start)
 | 
			
		||||
                            .toEqual(newStart);
 | 
			
		||||
 | 
			
		||||
                        mockScope.boundsModel.end = newEnd;
 | 
			
		||||
                        fireWatch("boundsModel.end", newEnd);
 | 
			
		||||
                        expect(mockScope.boundsModel.end)
 | 
			
		||||
                            .toEqual(newEnd);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -55,11 +55,6 @@ define(
 | 
			
		||||
                self.trackPosition(event);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Also make sure we dismiss bubble if representation is destroyed
 | 
			
		||||
            // before the mouse actually leaves it
 | 
			
		||||
            this.scopeOff =
 | 
			
		||||
                element.scope().$on('$destroy', this.hideBubbleCallback);
 | 
			
		||||
 | 
			
		||||
            this.element = element;
 | 
			
		||||
            this.$timeout = $timeout;
 | 
			
		||||
            this.infoService = infoService;
 | 
			
		||||
@@ -143,7 +138,6 @@ define(
 | 
			
		||||
            this.hideBubble();
 | 
			
		||||
            // ...and detach listeners
 | 
			
		||||
            this.element.off('mouseenter', this.showBubbleCallback);
 | 
			
		||||
            this.scopeOff();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return InfoGesture;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								platform/commonUI/notification/bundle.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,45 @@
 | 
			
		||||
{
 | 
			
		||||
    "extensions": {
 | 
			
		||||
        "constants": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "DEFAULT_AUTO_DISMISS",
 | 
			
		||||
                "value": 3000
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "FORCE_AUTO_DISMISS",
 | 
			
		||||
                "value": 1000
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "MINIMIZE_TIMEOUT",
 | 
			
		||||
                "value": 300
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "templates": [
 | 
			
		||||
            {
 | 
			
		||||
                "key":"notificationIndicatorTemplate",
 | 
			
		||||
                "templateUrl": "notification-indicator.html"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "controllers": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "NotificationIndicatorController",
 | 
			
		||||
                "implementation": "NotificationIndicatorController.js",
 | 
			
		||||
                "depends": ["$scope", "notificationService", "dialogService"]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "indicators": [
 | 
			
		||||
            {
 | 
			
		||||
                "implementation": "NotificationIndicator.js",
 | 
			
		||||
                "priority": "fallback"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "services": [
 | 
			
		||||
            {
 | 
			
		||||
                "key": "notificationService",
 | 
			
		||||
                "implementation": "NotificationService.js",
 | 
			
		||||
                "depends": [ "$timeout", "DEFAULT_AUTO_DISMISS",
 | 
			
		||||
                    "MINIMIZE_TIMEOUT" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,10 @@
 | 
			
		||||
<span ng-show="notifications.length > 0" class="status block"
 | 
			
		||||
      ng-class="highest.severity"
 | 
			
		||||
      ng-controller="NotificationIndicatorController">
 | 
			
		||||
    <span class="ui-symbol status-indicator"></span>
 | 
			
		||||
    <span class="label">
 | 
			
		||||
        <a ng-click="showNotificationsList()">{{notifications.length}}
 | 
			
		||||
            Notifications</a>
 | 
			
		||||
    </span>
 | 
			
		||||
    <span class="count">{{notifications.length}}</span>
 | 
			
		||||
</span>
 | 
			
		||||
							
								
								
									
										35
									
								
								platform/commonUI/notification/src/NotificationIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,35 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,window*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        function NotificationIndicator() {}
 | 
			
		||||
 | 
			
		||||
        NotificationIndicator.template = 'notificationIndicatorTemplate';
 | 
			
		||||
 | 
			
		||||
        return NotificationIndicator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -0,0 +1,67 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Provides an indicator that is visible when there are
 | 
			
		||||
         * banner notifications that have been minimized. Will also indicate
 | 
			
		||||
         * the number of notifications. Notifications can be viewed by
 | 
			
		||||
         * clicking on the indicator to launch a dialog showing a list of
 | 
			
		||||
         * notifications.
 | 
			
		||||
         * @param $scope
 | 
			
		||||
         * @param notificationService
 | 
			
		||||
         * @param dialogService
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function NotificationIndicatorController($scope, notificationService, dialogService) {
 | 
			
		||||
            $scope.notifications = notificationService.notifications;
 | 
			
		||||
            $scope.highest = notificationService.highest;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Launch a dialog showing a list of current notifications.
 | 
			
		||||
             */
 | 
			
		||||
            $scope.showNotificationsList = function(){
 | 
			
		||||
                dialogService.getDialogResponse('overlay-message-list', {
 | 
			
		||||
                    dialog: {
 | 
			
		||||
                        title: "Messages",
 | 
			
		||||
                        //Launch the message list dialog with the models
 | 
			
		||||
                        // from the notifications
 | 
			
		||||
                        messages: notificationService.notifications && notificationService.notifications.map(function(notification){
 | 
			
		||||
                            return notification.model;
 | 
			
		||||
                        })
 | 
			
		||||
                    },
 | 
			
		||||
                    cancel: function(){
 | 
			
		||||
                        dialogService.dismiss();
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        return NotificationIndicatorController;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										387
									
								
								platform/commonUI/notification/src/NotificationService.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,387 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This bundle implements the notification service, which can be used to
 | 
			
		||||
 * show banner notifications to the user. Banner notifications
 | 
			
		||||
 * are used to inform users of events in a non-intrusive way. As
 | 
			
		||||
 * much as possible, notifications share a model with blocking
 | 
			
		||||
 * dialogs so that the same information can be provided in a dialog
 | 
			
		||||
 * and then minimized to a banner notification if needed.
 | 
			
		||||
 *
 | 
			
		||||
 * @namespace platform/commonUI/notification
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A representation of a user action. Options are provided to
 | 
			
		||||
         * dialogs and notifications and are shown as buttons.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef {object} NotificationOption
 | 
			
		||||
         * @property {string} label the label to appear on the button for
 | 
			
		||||
         * this action
 | 
			
		||||
         * @property {function} callback a callback function to be invoked
 | 
			
		||||
         * when the button is clicked
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A representation of a banner notification. Banner notifications
 | 
			
		||||
         * are used to inform users of events in a non-intrusive way. As
 | 
			
		||||
         * much as possible, notifications share a model with blocking
 | 
			
		||||
         * dialogs so that the same information can be provided in a dialog
 | 
			
		||||
         * and then minimized to a banner notification if needed, or vice-versa.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef {object} NotificationModel
 | 
			
		||||
         * @property {string} title The title of the message
 | 
			
		||||
         * @property {string} severity The importance of the message (one of
 | 
			
		||||
         * 'info', 'alert', or 'error' where info < alert <error)
 | 
			
		||||
         * @property {number} progress The completion status of a task
 | 
			
		||||
         * represented numerically
 | 
			
		||||
         * @property {boolean} unknownProgress a boolean indicating that the
 | 
			
		||||
         * progress of the underlying task is unknown. This will result in a
 | 
			
		||||
         * visually distinct progress bar.
 | 
			
		||||
         * @property {boolean | number} autoDismiss If truthy, dialog will
 | 
			
		||||
         * be automatically minimized or dismissed (depending on severity).
 | 
			
		||||
         * Additionally, if the provided value is a number, it will be used
 | 
			
		||||
         * as the delay period before being dismissed.
 | 
			
		||||
         * @property {NotificationOption} primaryOption the default user
 | 
			
		||||
         * response to
 | 
			
		||||
         * this message. Will be represented as a button with the provided
 | 
			
		||||
         * label and action. May be used by banner notifications to display
 | 
			
		||||
         * only the most important option to users.
 | 
			
		||||
         * @property {NotificationOption[]} options any additional
 | 
			
		||||
         * actions the user can take. Will be represented as additional buttons
 | 
			
		||||
         * that may or may not be available from a banner.
 | 
			
		||||
         * @see DialogModel
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A wrapper object that is returned as a handle to a newly created
 | 
			
		||||
         * notification. Wraps the notifications model and decorates with
 | 
			
		||||
         * functions to dismiss or minimize the notification.
 | 
			
		||||
         *
 | 
			
		||||
         * @typedef {object} Notification
 | 
			
		||||
         * @property {function} dismiss This method is added to the object
 | 
			
		||||
         * returned by {@link NotificationService#notify} and can be used to
 | 
			
		||||
         * dismiss this notification. Dismissing a notification will remove
 | 
			
		||||
         * it completely and it will not appear in the notification indicator
 | 
			
		||||
         * @property {function} minimize This method is added to the object
 | 
			
		||||
         * returned by {@link NotificationService#notify} and can be used to
 | 
			
		||||
         * minimize this notification. Minimizing a notification will send
 | 
			
		||||
         * it to the notification indicator
 | 
			
		||||
         * @property {function} dismissOrMinimize This method is added to the
 | 
			
		||||
         * object returned by {@link NotificationService#notify}. It will
 | 
			
		||||
         * hide the notification by either dismissing or minimizing it,
 | 
			
		||||
         * depending on severity. Typically this is the method that should
 | 
			
		||||
         * be used for dismissing a notification. If more control is
 | 
			
		||||
         * required, then the minimize or dismiss functions can be called
 | 
			
		||||
         * individually.
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The notification service is responsible for informing the user of
 | 
			
		||||
         * events via the use of banner notifications.
 | 
			
		||||
         * @memberof platform/commonUI/notification
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param $timeout the Angular $timeout service
 | 
			
		||||
         * @param DEFAULT_AUTO_DISMISS The period of time that an
 | 
			
		||||
         * auto-dismissed message will be displayed for.
 | 
			
		||||
         * @param MINIMIZE_TIMEOUT When notifications are minimized, a brief
 | 
			
		||||
         * animation is shown. This animation requires some time to execute,
 | 
			
		||||
         * so a timeout is required before the notification is hidden
 | 
			
		||||
         */
 | 
			
		||||
        function NotificationService($timeout, DEFAULT_AUTO_DISMISS, MINIMIZE_TIMEOUT) {
 | 
			
		||||
            this.notifications = [];
 | 
			
		||||
            this.$timeout = $timeout;
 | 
			
		||||
            this.highest ={ severity: "info" };
 | 
			
		||||
            this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS;
 | 
			
		||||
            this.MINIMIZE_TIMEOUT = MINIMIZE_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * A context in which to hold the active notification and a
 | 
			
		||||
             * handle to its timeout.
 | 
			
		||||
             */
 | 
			
		||||
            this.active = {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Minimize a notification. The notification will still be available
 | 
			
		||||
         * from the notification list. Typically notifications with a
 | 
			
		||||
         * severity of 'info' should not be minimized, but rather
 | 
			
		||||
         * dismissed. If you're not sure which is appropriate,
 | 
			
		||||
         * use {@link Notification#dismissOrMinimize}
 | 
			
		||||
         */
 | 
			
		||||
        function minimize (service, notification) {
 | 
			
		||||
            //Check this is a known notification
 | 
			
		||||
            var index = service.notifications.indexOf(notification);
 | 
			
		||||
 | 
			
		||||
            if (service.active.timeout){
 | 
			
		||||
                /*
 | 
			
		||||
                 Method can be called manually (clicking dismiss) or
 | 
			
		||||
                 automatically from an auto-timeout. this.active.timeout
 | 
			
		||||
                 acts as a semaphore to prevent race conditions. Cancel any
 | 
			
		||||
                 timeout in progress (for the case where a manual dismiss
 | 
			
		||||
                 has shortcut an active auto-dismiss), and clear the
 | 
			
		||||
                 semaphore.
 | 
			
		||||
                 */
 | 
			
		||||
                service.$timeout.cancel(service.active.timeout);
 | 
			
		||||
                delete service.active.timeout;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (index >= 0) {
 | 
			
		||||
                notification.model.minimized=true;
 | 
			
		||||
                //Add a brief timeout before showing the next notification
 | 
			
		||||
                // in order to allow the minimize animation to run through.
 | 
			
		||||
                service.$timeout(function() {
 | 
			
		||||
                    service.setActiveNotification(service.selectNextNotification());
 | 
			
		||||
                }, service.MINIMIZE_TIMEOUT);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Completely removes a notification. This will dismiss it from the
 | 
			
		||||
         * message banner and remove it from the list of notifications.
 | 
			
		||||
         * Typically only notifications with a severity of info should be
 | 
			
		||||
         * dismissed. If you're not sure whether to dismiss or minimize a
 | 
			
		||||
         * notification, use {@link Notification#dismissOrMinimize}.
 | 
			
		||||
         * dismiss
 | 
			
		||||
         */
 | 
			
		||||
        function dismiss (service, notification) {
 | 
			
		||||
            //Check this is a known notification
 | 
			
		||||
            var index = service.notifications.indexOf(notification);
 | 
			
		||||
 | 
			
		||||
            if (service.active.timeout){
 | 
			
		||||
                /* Method can be called manually (clicking dismiss) or
 | 
			
		||||
                 * automatically from an auto-timeout. this.active.timeout
 | 
			
		||||
                 * acts as a semaphore to prevent race conditions. Cancel any
 | 
			
		||||
                 * timeout in progress (for the case where a manual dismiss
 | 
			
		||||
                 * has shortcut an active auto-dismiss), and clear the
 | 
			
		||||
                 * semaphore.
 | 
			
		||||
                 */
 | 
			
		||||
 | 
			
		||||
                service.$timeout.cancel(service.active.timeout);
 | 
			
		||||
                delete service.active.timeout;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (index >= 0) {
 | 
			
		||||
                service.notifications.splice(index, 1);
 | 
			
		||||
            }
 | 
			
		||||
            service.setActiveNotification(service.selectNextNotification());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Depending on the severity of the notification will selectively
 | 
			
		||||
         * dismiss or minimize where appropriate.
 | 
			
		||||
         */
 | 
			
		||||
        function dismissOrMinimize (notification){
 | 
			
		||||
 | 
			
		||||
            //For now minimize everything, and have discussion around which
 | 
			
		||||
            //kind of messages should or should not be in the minimized
 | 
			
		||||
            //notifications list
 | 
			
		||||
            notification.minimize();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Returns the notification that is currently visible in the banner area
 | 
			
		||||
         * @returns {Notification}
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.getActiveNotification = function (){
 | 
			
		||||
            return this.active.notification;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A convenience method for info notifications. Notifications
 | 
			
		||||
         * created via this method will be auto-dismissed after a default
 | 
			
		||||
         * wait period
 | 
			
		||||
         * @param {NotificationModel | string} message either a string for
 | 
			
		||||
         * the title of the notification message, or a {@link NotificationModel}
 | 
			
		||||
         * defining the options notification to display
 | 
			
		||||
         * @returns {Notification} the provided notification decorated with
 | 
			
		||||
         * functions to dismiss or minimize
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.info = function (message) {
 | 
			
		||||
            var notificationModel = typeof message === "string" ? {title: message} : message;
 | 
			
		||||
            notificationModel.autoDismiss = notificationModel.autoDismiss || true;
 | 
			
		||||
            notificationModel.severity = "info";
 | 
			
		||||
            return this.notify(notificationModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A convenience method for alert notifications. Notifications
 | 
			
		||||
         * created via this method will will have severity of "alert" enforced
 | 
			
		||||
         * @param {NotificationModel | string} message either a string for
 | 
			
		||||
         * the title of the alert message with default options, or a
 | 
			
		||||
         * {@link NotificationModel} defining the options notification to
 | 
			
		||||
         * display
 | 
			
		||||
         * @returns {Notification} the provided notification decorated with
 | 
			
		||||
         * functions to dismiss or minimize
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.alert = function (message) {
 | 
			
		||||
            var notificationModel = typeof message === "string" ? {title: message} : message;
 | 
			
		||||
            notificationModel.severity = "alert";
 | 
			
		||||
            return this.notify(notificationModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A convenience method for error notifications. Notifications
 | 
			
		||||
         * created via this method will will have severity of "error" enforced
 | 
			
		||||
         * @param {NotificationModel | string} message either a string for
 | 
			
		||||
         * the title of the error message with default options, or a
 | 
			
		||||
         * {@link NotificationModel} defining the options notification to
 | 
			
		||||
         * display
 | 
			
		||||
         * @returns {Notification} the provided notification decorated with
 | 
			
		||||
         * functions to dismiss or minimize
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.error = function (message) {
 | 
			
		||||
            var notificationModel = typeof message === "string" ? {title: message} : message;
 | 
			
		||||
            notificationModel.severity = "error";
 | 
			
		||||
            return this.notify(notificationModel);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Notifies the user of an event. If there is a banner notification
 | 
			
		||||
         * already active, then it will be dismissed or minimized automatically,
 | 
			
		||||
         * and the provided notification displayed in its place.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {NotificationModel} notificationModel The notification to
 | 
			
		||||
         * display
 | 
			
		||||
         * @returns {Notification} the provided notification decorated with
 | 
			
		||||
         * functions to {@link Notification#dismiss} or {@link Notification#minimize}
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.notify = function (notificationModel) {
 | 
			
		||||
            var self = this,
 | 
			
		||||
                notification,
 | 
			
		||||
                ordinality = {
 | 
			
		||||
                    "info": 1,
 | 
			
		||||
                    "alert": 2,
 | 
			
		||||
                    "error": 3
 | 
			
		||||
                },
 | 
			
		||||
                activeNotification = self.active.notification;
 | 
			
		||||
 | 
			
		||||
            notification = {
 | 
			
		||||
                model: notificationModel,
 | 
			
		||||
                minimize: function() {
 | 
			
		||||
                    minimize(self, notification);
 | 
			
		||||
                },
 | 
			
		||||
                dismiss: function(){
 | 
			
		||||
                    dismiss(self, notification);
 | 
			
		||||
                },
 | 
			
		||||
                dismissOrMinimize: function(){
 | 
			
		||||
                    dismissOrMinimize(notification);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            notificationModel.severity = notificationModel.severity || "info";
 | 
			
		||||
            if (notificationModel.autoDismiss === true){
 | 
			
		||||
                notificationModel.autoDismiss = this.DEFAULT_AUTO_DISMISS;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (ordinality[notificationModel.severity.toLowerCase()] > ordinality[this.highest.severity.toLowerCase()]){
 | 
			
		||||
                this.highest.severity = notificationModel.severity;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.notifications.push(notification);
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            Check if there is already an active (ie. visible) notification
 | 
			
		||||
             */
 | 
			
		||||
            if (!this.active.notification){
 | 
			
		||||
                this.setActiveNotification(notification);
 | 
			
		||||
 | 
			
		||||
            } else if (!this.active.timeout){
 | 
			
		||||
                /*
 | 
			
		||||
                 If there is already an active notification, time it out. If it's
 | 
			
		||||
                 already got a timeout in progress (either because it has had
 | 
			
		||||
                 timeout forced because of a queue of messages, or it had an
 | 
			
		||||
                 autodismiss specified), leave it to run. Otherwise force a
 | 
			
		||||
                  timeout.
 | 
			
		||||
 | 
			
		||||
                 This notifcation has been added to queue and will be
 | 
			
		||||
                  serviced as soon as possible.
 | 
			
		||||
                 */
 | 
			
		||||
                this.active.timeout = this.$timeout(function () {
 | 
			
		||||
                    activeNotification.dismissOrMinimize();
 | 
			
		||||
                }, this.DEFAULT_AUTO_DISMISS);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return notification;
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Used internally by the NotificationService
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.setActiveNotification =
 | 
			
		||||
            function (notification) {
 | 
			
		||||
 | 
			
		||||
                var self = this,
 | 
			
		||||
                    timeout;
 | 
			
		||||
                this.active.notification = notification;
 | 
			
		||||
                /*
 | 
			
		||||
                If autoDismiss has been specified, OR there are other
 | 
			
		||||
                 notifications queued for display, setup a timeout to
 | 
			
		||||
                  dismiss the dialog.
 | 
			
		||||
                 */
 | 
			
		||||
                if (notification && (notification.model.autoDismiss
 | 
			
		||||
                    || this.selectNextNotification())) {
 | 
			
		||||
 | 
			
		||||
                    timeout = notification.model.autoDismiss || this.DEFAULT_AUTO_DISMISS;
 | 
			
		||||
                    this.active.timeout = this.$timeout(function () {
 | 
			
		||||
                        notification.dismissOrMinimize();
 | 
			
		||||
                    }, timeout);
 | 
			
		||||
                } else {
 | 
			
		||||
                    delete this.active.timeout;
 | 
			
		||||
                }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Used internally by the NotificationService
 | 
			
		||||
         *
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        NotificationService.prototype.selectNextNotification = function () {
 | 
			
		||||
            var notification,
 | 
			
		||||
                i=0;
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
            Loop through the notifications queue and find the first one that
 | 
			
		||||
            has not already been minimized (manually or otherwise).
 | 
			
		||||
             */
 | 
			
		||||
            for (; i< this.notifications.length; i++) {
 | 
			
		||||
                notification = this.notifications[i];
 | 
			
		||||
 | 
			
		||||
                if (!notification.model.minimized
 | 
			
		||||
                    && notification!== this.active.notification) {
 | 
			
		||||
 | 
			
		||||
                    return notification;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return NotificationService;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -0,0 +1,78 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['../src/NotificationIndicatorController'],
 | 
			
		||||
    function (NotificationIndicatorController) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        describe("The notification indicator controller ", function () {
 | 
			
		||||
            var mockNotificationService,
 | 
			
		||||
                mockScope,
 | 
			
		||||
                mockDialogService;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function(){
 | 
			
		||||
                mockNotificationService = jasmine.createSpy("notificationService");
 | 
			
		||||
                mockScope = jasmine.createSpy("$scope");
 | 
			
		||||
                mockDialogService = jasmine.createSpyObj(
 | 
			
		||||
                    "dialogService",
 | 
			
		||||
                    ["getDialogResponse","dismiss"]
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exposes the highest notification severity to the template", function() {
 | 
			
		||||
                mockNotificationService.highest = {
 | 
			
		||||
                    severity: "error"
 | 
			
		||||
                };
 | 
			
		||||
                var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
 | 
			
		||||
                expect(mockScope.highest).toBeTruthy();
 | 
			
		||||
                expect(mockScope.highest.severity).toBe("error");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("invokes the dialog service to show list of messages", function() {
 | 
			
		||||
                var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
 | 
			
		||||
                expect(mockScope.showNotificationsList).toBeDefined();
 | 
			
		||||
                mockScope.showNotificationsList();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse.mostRecentCall.args[0]).toBe('overlay-message-list');
 | 
			
		||||
                expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].dialog).toBeDefined();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined();
 | 
			
		||||
                //Invoke the cancel callback
 | 
			
		||||
                mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel();
 | 
			
		||||
                expect(mockDialogService.dismiss).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("provides a means of dismissing the message list", function() {
 | 
			
		||||
                var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
 | 
			
		||||
                expect(mockScope.showNotificationsList).toBeDefined();
 | 
			
		||||
                mockScope.showNotificationsList();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
 | 
			
		||||
                expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined();
 | 
			
		||||
                //Invoke the cancel callback
 | 
			
		||||
                mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel();
 | 
			
		||||
                expect(mockDialogService.dismiss).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										219
									
								
								platform/commonUI/notification/test/NotificationServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,219 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['../src/NotificationService'],
 | 
			
		||||
    function (NotificationService) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        describe("The notification service ", function () {
 | 
			
		||||
            var notificationService,
 | 
			
		||||
                mockTimeout,
 | 
			
		||||
                mockAutoDismiss,
 | 
			
		||||
                mockMinimizeTimeout,
 | 
			
		||||
                successModel,
 | 
			
		||||
                errorModel;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function(){
 | 
			
		||||
                mockTimeout = jasmine.createSpy("$timeout");
 | 
			
		||||
                mockAutoDismiss = mockMinimizeTimeout = 1000;
 | 
			
		||||
                notificationService = new NotificationService(
 | 
			
		||||
                    mockTimeout, mockAutoDismiss, mockMinimizeTimeout);
 | 
			
		||||
                successModel = {
 | 
			
		||||
                    title: "Mock Success Notification",
 | 
			
		||||
                    severity: "info"
 | 
			
		||||
                };
 | 
			
		||||
                errorModel = {
 | 
			
		||||
                    title: "Mock Error Notification",
 | 
			
		||||
                    severity: "error"
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("gets a new success notification, making" +
 | 
			
		||||
                " the notification active", function() {
 | 
			
		||||
                var activeNotification;
 | 
			
		||||
                notificationService.notify(successModel);
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows specification of an info notification given just a" +
 | 
			
		||||
                " title, making the notification active", function() {
 | 
			
		||||
                var activeNotification,
 | 
			
		||||
                    notificationTitle = "Test info notification";
 | 
			
		||||
                notificationService.info(notificationTitle);
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model.title).toBe(notificationTitle);
 | 
			
		||||
                expect(activeNotification.model.severity).toBe("info");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("gets a new success notification with" +
 | 
			
		||||
                " numerical auto-dismiss specified. ", function() {
 | 
			
		||||
                var activeNotification;
 | 
			
		||||
                successModel.autoDismiss = 1000;
 | 
			
		||||
                notificationService.notify(successModel);
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                expect(mockTimeout.calls.length).toBe(2);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification).toBeUndefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("gets a new notification with" +
 | 
			
		||||
                " boolean auto-dismiss specified. ", function() {
 | 
			
		||||
                var activeNotification;
 | 
			
		||||
                successModel.autoDismiss = true;
 | 
			
		||||
                notificationService.notify(successModel);
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                expect(mockTimeout.calls.length).toBe(2);
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification).toBeUndefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows minimization of notifications", function() {
 | 
			
		||||
                var notification,
 | 
			
		||||
                    activeNotification;
 | 
			
		||||
 | 
			
		||||
                successModel.autoDismiss = false;
 | 
			
		||||
                notification = notificationService.notify(successModel);
 | 
			
		||||
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                notification.minimize();
 | 
			
		||||
                mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification).toBeUndefined();
 | 
			
		||||
                expect(notificationService.notifications.length).toBe(1);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows dismissal of notifications", function() {
 | 
			
		||||
                var notification,
 | 
			
		||||
                    activeNotification;
 | 
			
		||||
 | 
			
		||||
                successModel.autoDismiss = false;
 | 
			
		||||
                notification = notificationService.notify(successModel);
 | 
			
		||||
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                notification.dismiss();
 | 
			
		||||
                activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                expect(activeNotification).toBeUndefined();
 | 
			
		||||
                expect(notificationService.notifications.length).toBe(0);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe(" gets called with multiple notifications", function(){
 | 
			
		||||
                it("auto-dismisses the previously active notification, making" +
 | 
			
		||||
                    " the new notification active", function() {
 | 
			
		||||
                    var activeNotification;
 | 
			
		||||
                    //First pre-load with a info message
 | 
			
		||||
                    notificationService.notify(successModel);
 | 
			
		||||
                    activeNotification =
 | 
			
		||||
                        notificationService.getActiveNotification();
 | 
			
		||||
                    //Initially expect the active notification to be info
 | 
			
		||||
                    expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                    //Then notify of an error
 | 
			
		||||
                    notificationService.notify(errorModel);
 | 
			
		||||
                    //But it should be auto-dismissed and replaced with the
 | 
			
		||||
                    // error notification
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Two timeouts, one is to force minimization after
 | 
			
		||||
                    // displaying the message for a minimum period, the
 | 
			
		||||
                    // second is to allow minimization animation to take place.
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    activeNotification = notificationService.getActiveNotification();
 | 
			
		||||
                    expect(activeNotification.model).toBe(errorModel);
 | 
			
		||||
                });
 | 
			
		||||
                it("auto-minimizes an active error notification", function() {
 | 
			
		||||
                    var activeNotification;
 | 
			
		||||
                    //First pre-load with an error message
 | 
			
		||||
                    notificationService.notify(errorModel);
 | 
			
		||||
                    //Then notify of info
 | 
			
		||||
                    notificationService.notify(successModel);
 | 
			
		||||
                    expect(notificationService.notifications.length).toEqual(2);
 | 
			
		||||
                    //Mock the auto-minimize
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Two timeouts, one is to force minimization after
 | 
			
		||||
                    // displaying the message for a minimum period, the
 | 
			
		||||
                    // second is to allow minimization animation to take place.
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Previous error message should be minimized, not
 | 
			
		||||
                    // dismissed
 | 
			
		||||
                    expect(notificationService.notifications.length).toEqual(2);
 | 
			
		||||
                    activeNotification =
 | 
			
		||||
                        notificationService.getActiveNotification();
 | 
			
		||||
                    expect(activeNotification.model).toBe(successModel);
 | 
			
		||||
                    expect(errorModel.minimized).toEqual(true);
 | 
			
		||||
                });
 | 
			
		||||
                it("auto-minimizes errors when a number of them arrive in" +
 | 
			
		||||
                    " short succession ", function() {
 | 
			
		||||
                    var activeNotification,
 | 
			
		||||
                        error2 = {
 | 
			
		||||
                            title: "Second Mock Error Notification",
 | 
			
		||||
                            severity: "error"
 | 
			
		||||
                        },
 | 
			
		||||
                        error3 = {
 | 
			
		||||
                            title: "Third Mock Error Notification",
 | 
			
		||||
                            severity: "error"
 | 
			
		||||
                        };
 | 
			
		||||
 | 
			
		||||
                    //First pre-load with a info message
 | 
			
		||||
                    notificationService.notify(errorModel);
 | 
			
		||||
                    //Then notify of a third error
 | 
			
		||||
                    notificationService.notify(error2);
 | 
			
		||||
                    notificationService.notify(error3);
 | 
			
		||||
                    expect(notificationService.notifications.length).toEqual(3);
 | 
			
		||||
                    //Mock the auto-minimize
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Two timeouts, one is to force minimization after
 | 
			
		||||
                    // displaying the message for a minimum period, the
 | 
			
		||||
                    // second is to allow minimization animation to take place.
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Previous error message should be minimized, not
 | 
			
		||||
                    // dismissed
 | 
			
		||||
                    expect(notificationService.notifications.length).toEqual(3);
 | 
			
		||||
                    activeNotification =
 | 
			
		||||
                        notificationService.getActiveNotification();
 | 
			
		||||
                    expect(activeNotification.model).toBe(error2);
 | 
			
		||||
                    expect(errorModel.minimized).toEqual(true);
 | 
			
		||||
 | 
			
		||||
                    //Mock the second auto-minimize
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    //Two timeouts, one is to force minimization after
 | 
			
		||||
                    // displaying the message for a minimum period, the
 | 
			
		||||
                    // second is to allow minimization animation to take place.
 | 
			
		||||
                    mockTimeout.mostRecentCall.args[0]();
 | 
			
		||||
                    activeNotification =
 | 
			
		||||
                        notificationService.getActiveNotification();
 | 
			
		||||
                    expect(activeNotification.model).toBe(error3);
 | 
			
		||||
                    expect(error2.minimized).toEqual(true);
 | 
			
		||||
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										4
									
								
								platform/commonUI/notification/test/suite.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,4 @@
 | 
			
		||||
[
 | 
			
		||||
    "NotificationService",
 | 
			
		||||
    "NotificationIndicatorController"
 | 
			
		||||
]
 | 
			
		||||
@@ -10,7 +10,7 @@ $colorInteriorBorder: rgba($colorBodyFg, 0.1);
 | 
			
		||||
$colorA: #ccc;
 | 
			
		||||
$colorAHov: #fff;
 | 
			
		||||
$contrastRatioPercent: 7%;
 | 
			
		||||
$basicCr: 2px;
 | 
			
		||||
$basicCr: 3px;
 | 
			
		||||
$controlCr: 3px;
 | 
			
		||||
$smallCr: 2px;
 | 
			
		||||
 | 
			
		||||
@@ -37,14 +37,12 @@ $timeControllerToiLineColorHov: #fff;
 | 
			
		||||
 | 
			
		||||
// General Colors
 | 
			
		||||
$colorAlt1: #ffc700;
 | 
			
		||||
$colorAlert: #ff3c00;
 | 
			
		||||
$colorAlert: #ff533a;
 | 
			
		||||
$colorIconLink: #49dedb;
 | 
			
		||||
$colorPausedBg: #c56f01;
 | 
			
		||||
$colorPausedFg: #fff;
 | 
			
		||||
$colorCreateBtn: $colorKey;
 | 
			
		||||
$colorGridLines: rgba(#fff, 0.05);
 | 
			
		||||
$colorFormLines: rgba(#fff, 0.1);
 | 
			
		||||
$colorFormSectionHeader: rgba(#000, 0.2);
 | 
			
		||||
$colorInvokeMenu: #fff;
 | 
			
		||||
$colorObjHdrTxt: $colorBodyFg;
 | 
			
		||||
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
 | 
			
		||||
@@ -57,10 +55,10 @@ $colorMenuIc: pullForward($colorKey, 17%);
 | 
			
		||||
$colorMenuHovBg: pullForward($colorMenuBg, 10%);
 | 
			
		||||
$colorMenuHovFg: #fff;
 | 
			
		||||
$colorMenuHovIc: $colorMenuHovFg;
 | 
			
		||||
$shdwMenu: none;
 | 
			
		||||
$shdwMenuText: rgba(black, 0.1) 0 1px 2px;
 | 
			
		||||
$colorCreateMenuLgIcon: $colorMenuFg;
 | 
			
		||||
$colorCreateMenuText: $colorMenuFg;
 | 
			
		||||
$shdwMenu: none;
 | 
			
		||||
$shdwMenuText: rgba(black, 0.1) 0 1px 2px;
 | 
			
		||||
 | 
			
		||||
// Form colors
 | 
			
		||||
$colorCheck: $colorKey;
 | 
			
		||||
@@ -68,11 +66,25 @@ $colorFormRequired: $colorAlt1;
 | 
			
		||||
$colorFormValid: #33cc33;
 | 
			
		||||
$colorFormError: #cc0000;
 | 
			
		||||
$colorFormInvalid: #ff3300;
 | 
			
		||||
$colorFormLines: rgba(#fff, 0.1);
 | 
			
		||||
$colorFormSectionHeader: rgba(#000, 0.2);
 | 
			
		||||
$colorInputBg: rgba(#fff, 0.1);
 | 
			
		||||
$colorInputFg: pullForward($colorBodyFg, 20%);
 | 
			
		||||
$colorFormText: rgba(#fff, 0.5);
 | 
			
		||||
$colorInputIcon: pushBack($colorBodyFg, 15%);
 | 
			
		||||
 | 
			
		||||
// Status colors, mainly used for messaging and item ancillary symbols
 | 
			
		||||
$colorStatusFg: #ccc;
 | 
			
		||||
$colorStatusDefault: #ccc;
 | 
			
		||||
$colorStatusInfo: #62ba72;
 | 
			
		||||
$colorStatusAlert: #ffa66d;
 | 
			
		||||
$colorStatusError: #d4585c;
 | 
			
		||||
$colorProgressBarOuter: rgba(#000, 0.1);
 | 
			
		||||
$colorProgressBarAmt: $colorKey;
 | 
			
		||||
$progressBarHOverlay: 15px;
 | 
			
		||||
$progressBarStripeW: 20px;
 | 
			
		||||
$shdwStatusIc: rgba(black, 0.4) 0 1px 2px;
 | 
			
		||||
 | 
			
		||||
// Selects
 | 
			
		||||
$colorSelectBg: $colorBtnBg;
 | 
			
		||||
$colorSelectFg: $colorBtnFg;
 | 
			
		||||
@@ -167,4 +179,4 @@ $colorAboutLink: #84b3ff;
 | 
			
		||||
 | 
			
		||||
// Loading
 | 
			
		||||
$colorLoadingBg: rgba($colorBodyFg, 0.2);
 | 
			
		||||
$colorLoadingFg: $colorAlt1;
 | 
			
		||||
$colorLoadingFg: $colorAlt1;
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,20 @@ $colorInputBg: $colorGenBg;
 | 
			
		||||
$colorInputFg: $colorBodyFg;
 | 
			
		||||
$colorFormText: pushBack($colorBodyFg, 10%);
 | 
			
		||||
$colorInputIcon: pushBack($colorBodyFg, 25%);
 | 
			
		||||
 | 
			
		||||
// Status colors, mainly used for messaging and item ancillary symbols
 | 
			
		||||
$colorStatusFg: #fff;
 | 
			
		||||
$colorStatusDefault: #ccc;
 | 
			
		||||
$colorStatusInfo: #60ba7b;
 | 
			
		||||
$colorStatusAlert: #ffb66c;
 | 
			
		||||
$colorStatusError: #c96b68;
 | 
			
		||||
$colorProgressBarOuter: rgba(#000, 0.1);
 | 
			
		||||
$colorProgressBarAmt: #0a0;
 | 
			
		||||
$progressBarHOverlay: 15px;
 | 
			
		||||
$progressBarStripeW: 20px;
 | 
			
		||||
$shdwStatusIc: rgba(white, 0.8) 0 0px 5px;
 | 
			
		||||
 | 
			
		||||
// Selects
 | 
			
		||||
$colorSelectBg: #ddd;
 | 
			
		||||
$colorSelectFg: $colorBodyFg;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,14 @@
 | 
			
		||||
                "category": "contextual",
 | 
			
		||||
                "implementation": "actions/LinkAction.js",
 | 
			
		||||
                "depends": ["locationService", "linkService"]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "follow",
 | 
			
		||||
                "name": "Go To Original",
 | 
			
		||||
                "description": "Go to the original, un-linked instance of this object.",
 | 
			
		||||
                "glyph": "\u00F4",
 | 
			
		||||
                "category": "contextual",
 | 
			
		||||
                "implementation": "actions/GoToOriginalAction.js"
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "components": [
 | 
			
		||||
@@ -52,7 +60,8 @@
 | 
			
		||||
                "key": "location",
 | 
			
		||||
                "name": "Location Capability",
 | 
			
		||||
                "description": "Provides a capability for retrieving the location of an object based upon it's context.",
 | 
			
		||||
                "implementation": "capabilities/LocationCapability"
 | 
			
		||||
                "implementation": "capabilities/LocationCapability",
 | 
			
		||||
                "depends": [ "$q", "$injector" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "services": [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								platform/entanglement/src/actions/GoToOriginalAction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,62 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define */
 | 
			
		||||
define(
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Implements the "Go To Original" action, which follows a link back
 | 
			
		||||
         * to an original instance of an object.
 | 
			
		||||
         *
 | 
			
		||||
         * @implements {Action}
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @private
 | 
			
		||||
         * @memberof platform/entanglement
 | 
			
		||||
         * @param {ActionContext} context the context in which the action
 | 
			
		||||
         *        will be performed
 | 
			
		||||
         */
 | 
			
		||||
        function GoToOriginalAction(context) {
 | 
			
		||||
            this.domainObject = context.domainObject;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        GoToOriginalAction.prototype.perform = function () {
 | 
			
		||||
            return this.domainObject.getCapability("location").getOriginal()
 | 
			
		||||
                .then(function (originalObject) {
 | 
			
		||||
                    var actionCapability =
 | 
			
		||||
                        originalObject.getCapability("action");
 | 
			
		||||
                    return actionCapability &&
 | 
			
		||||
                            actionCapability.perform("navigate");
 | 
			
		||||
                });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        GoToOriginalAction.appliesTo = function (context) {
 | 
			
		||||
            var domainObject = context.domainObject;
 | 
			
		||||
            return domainObject && domainObject.hasCapability("location")
 | 
			
		||||
                && domainObject.getCapability("location").isLink();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return GoToOriginalAction;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@@ -1,3 +1,25 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define */
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
@@ -12,11 +34,41 @@ define(
 | 
			
		||||
         *
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function LocationCapability(domainObject) {
 | 
			
		||||
        function LocationCapability($q, $injector, domainObject) {
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.$injector = $injector;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get an instance of this domain object in its original location.
 | 
			
		||||
         *
 | 
			
		||||
         * @returns {Promise.<DomainObject>} a promise for the original
 | 
			
		||||
         *          instance of this domain object
 | 
			
		||||
         */
 | 
			
		||||
        LocationCapability.prototype.getOriginal = function () {
 | 
			
		||||
            var id;
 | 
			
		||||
 | 
			
		||||
            if (this.isOriginal()) {
 | 
			
		||||
                return this.$q.when(this.domainObject);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            id = this.domainObject.getId();
 | 
			
		||||
 | 
			
		||||
            this.objectService =
 | 
			
		||||
                this.objectService || this.$injector.get("objectService");
 | 
			
		||||
 | 
			
		||||
            // Assume that an object will be correctly contextualized when
 | 
			
		||||
            // loaded directly from the object service; this is true
 | 
			
		||||
            // so long as LocatingObjectDecorator is present, and that
 | 
			
		||||
            // decorator is also contained in this bundle.
 | 
			
		||||
            return this.objectService.getObjects([id])
 | 
			
		||||
                .then(function (objects) {
 | 
			
		||||
                    return objects[id];
 | 
			
		||||
                });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Set the primary location (the parent id) of the current domain
 | 
			
		||||
         * object.
 | 
			
		||||
@@ -78,10 +130,6 @@ define(
 | 
			
		||||
            return !this.isLink();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        function createLocationCapability(domainObject) {
 | 
			
		||||
            return new LocationCapability(domainObject);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return createLocationCapability;
 | 
			
		||||
        return LocationCapability;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										95
									
								
								platform/entanglement/test/actions/GoToOriginalActionSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,95 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define,describe,beforeEach,it,jasmine,expect */
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [
 | 
			
		||||
        '../../src/actions/GoToOriginalAction',
 | 
			
		||||
        '../DomainObjectFactory',
 | 
			
		||||
        '../ControlledPromise'
 | 
			
		||||
    ],
 | 
			
		||||
    function (GoToOriginalAction, domainObjectFactory, ControlledPromise) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        describe("The 'go to original' action", function () {
 | 
			
		||||
            var testContext,
 | 
			
		||||
                originalDomainObject,
 | 
			
		||||
                mockLocationCapability,
 | 
			
		||||
                mockOriginalActionCapability,
 | 
			
		||||
                originalPromise,
 | 
			
		||||
                action;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockLocationCapability = jasmine.createSpyObj(
 | 
			
		||||
                    'location',
 | 
			
		||||
                    [ 'isLink', 'isOriginal', 'getOriginal' ]
 | 
			
		||||
                );
 | 
			
		||||
                mockOriginalActionCapability = jasmine.createSpyObj(
 | 
			
		||||
                    'action',
 | 
			
		||||
                    [ 'perform', 'getActions' ]
 | 
			
		||||
                );
 | 
			
		||||
                originalPromise = new ControlledPromise();
 | 
			
		||||
                mockLocationCapability.getOriginal.andReturn(originalPromise);
 | 
			
		||||
                mockLocationCapability.isLink.andReturn(true);
 | 
			
		||||
                mockLocationCapability.isOriginal.andCallFake(function () {
 | 
			
		||||
                    return !mockLocationCapability.isLink();
 | 
			
		||||
                });
 | 
			
		||||
                testContext = {
 | 
			
		||||
                    domainObject: domainObjectFactory({
 | 
			
		||||
                        capabilities: {
 | 
			
		||||
                            location: mockLocationCapability
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                };
 | 
			
		||||
                originalDomainObject = domainObjectFactory({
 | 
			
		||||
                    capabilities: {
 | 
			
		||||
                        action: mockOriginalActionCapability
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                action = new GoToOriginalAction(testContext);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is applicable to links", function () {
 | 
			
		||||
                expect(GoToOriginalAction.appliesTo(testContext))
 | 
			
		||||
                    .toBeTruthy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is not applicable to originals", function () {
 | 
			
		||||
                mockLocationCapability.isLink.andReturn(false);
 | 
			
		||||
                expect(GoToOriginalAction.appliesTo(testContext))
 | 
			
		||||
                    .toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("navigates to original objects when performed", function () {
 | 
			
		||||
                expect(mockOriginalActionCapability.perform)
 | 
			
		||||
                    .not.toHaveBeenCalled();
 | 
			
		||||
                action.perform();
 | 
			
		||||
                originalPromise.resolve(originalDomainObject);
 | 
			
		||||
                expect(mockOriginalActionCapability.perform)
 | 
			
		||||
                    .toHaveBeenCalledWith('navigate');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,3 +1,25 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/*global define,describe,it,expect,beforeEach,jasmine */
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
@@ -7,6 +29,7 @@ define(
 | 
			
		||||
        '../ControlledPromise'
 | 
			
		||||
    ],
 | 
			
		||||
    function (LocationCapability, domainObjectFactory, ControlledPromise) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        describe("LocationCapability", function () {
 | 
			
		||||
 | 
			
		||||
@@ -14,13 +37,17 @@ define(
 | 
			
		||||
                var locationCapability,
 | 
			
		||||
                    persistencePromise,
 | 
			
		||||
                    mutationPromise,
 | 
			
		||||
                    mockQ,
 | 
			
		||||
                    mockInjector,
 | 
			
		||||
                    mockObjectService,
 | 
			
		||||
                    domainObject;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    domainObject = domainObjectFactory({
 | 
			
		||||
                        id: "testObject",
 | 
			
		||||
                        capabilities: {
 | 
			
		||||
                            context: {
 | 
			
		||||
                                getParent: function() {
 | 
			
		||||
                                getParent: function () {
 | 
			
		||||
                                    return domainObjectFactory({id: 'root'});
 | 
			
		||||
                                }
 | 
			
		||||
                            },
 | 
			
		||||
@@ -35,6 +62,11 @@ define(
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    mockQ = jasmine.createSpyObj("$q", ["when"]);
 | 
			
		||||
                    mockInjector = jasmine.createSpyObj("$injector", ["get"]);
 | 
			
		||||
                    mockObjectService =
 | 
			
		||||
                        jasmine.createSpyObj("objectService", ["getObjects"]);
 | 
			
		||||
 | 
			
		||||
                    persistencePromise = new ControlledPromise();
 | 
			
		||||
                    domainObject.capabilities.persistence.persist.andReturn(
 | 
			
		||||
                        persistencePromise
 | 
			
		||||
@@ -49,7 +81,11 @@ define(
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    locationCapability = new LocationCapability(domainObject);
 | 
			
		||||
                    locationCapability = new LocationCapability(
 | 
			
		||||
                        mockQ,
 | 
			
		||||
                        mockInjector,
 | 
			
		||||
                        domainObject
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("returns contextual location", function () {
 | 
			
		||||
@@ -88,6 +124,57 @@ define(
 | 
			
		||||
                    expect(whenComplete).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("when used to load an original instance", function () {
 | 
			
		||||
                    var objectPromise,
 | 
			
		||||
                        qPromise,
 | 
			
		||||
                        originalObjects,
 | 
			
		||||
                        mockCallback;
 | 
			
		||||
 | 
			
		||||
                    function resolvePromises() {
 | 
			
		||||
                        if (mockQ.when.calls.length > 0) {
 | 
			
		||||
                            qPromise.resolve(mockQ.when.mostRecentCall.args[0]);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (mockObjectService.getObjects.calls.length > 0) {
 | 
			
		||||
                            objectPromise.resolve(originalObjects);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        objectPromise = new ControlledPromise();
 | 
			
		||||
                        qPromise = new ControlledPromise();
 | 
			
		||||
                        originalObjects = {
 | 
			
		||||
                            testObject: domainObjectFactory()
 | 
			
		||||
                        };
 | 
			
		||||
 | 
			
		||||
                        mockInjector.get.andCallFake(function (key) {
 | 
			
		||||
                            return key === 'objectService' && mockObjectService;
 | 
			
		||||
                        });
 | 
			
		||||
                        mockObjectService.getObjects.andReturn(objectPromise);
 | 
			
		||||
                        mockQ.when.andReturn(qPromise);
 | 
			
		||||
 | 
			
		||||
                        mockCallback = jasmine.createSpy('callback');
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("provides originals directly", function () {
 | 
			
		||||
                        domainObject.model.location = 'root';
 | 
			
		||||
                        locationCapability.getOriginal().then(mockCallback);
 | 
			
		||||
                        expect(mockCallback).not.toHaveBeenCalled();
 | 
			
		||||
                        resolvePromises();
 | 
			
		||||
                        expect(mockCallback)
 | 
			
		||||
                            .toHaveBeenCalledWith(domainObject);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("loads from the object service for links", function () {
 | 
			
		||||
                        domainObject.model.location = 'some-other-root';
 | 
			
		||||
                        locationCapability.getOriginal().then(mockCallback);
 | 
			
		||||
                        expect(mockCallback).not.toHaveBeenCalled();
 | 
			
		||||
                        resolvePromises();
 | 
			
		||||
                        expect(mockCallback)
 | 
			
		||||
                            .toHaveBeenCalledWith(originalObjects.testObject);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
[
 | 
			
		||||
    "actions/AbstractComposeAction",
 | 
			
		||||
    "actions/CopyAction",
 | 
			
		||||
    "actions/GoToOriginalAction",
 | 
			
		||||
    "actions/LinkAction",
 | 
			
		||||
    "actions/MoveAction",
 | 
			
		||||
    "services/CopyService",
 | 
			
		||||
    "services/LinkService",
 | 
			
		||||
    "services/MoveService",
 | 
			
		||||
 
 | 
			
		||||
@@ -159,7 +159,9 @@ define(
 | 
			
		||||
 | 
			
		||||
        // Update dimensions and origin based on extrema of plots
 | 
			
		||||
        PlotUpdater.prototype.updateBounds = function () {
 | 
			
		||||
            var bufferArray = this.bufferArray,
 | 
			
		||||
            var bufferArray = this.bufferArray.filter(function (lineBuffer) {
 | 
			
		||||
                    return lineBuffer.getLength() > 0; // Ignore empty lines
 | 
			
		||||
                }),
 | 
			
		||||
                priorDomainOrigin = this.origin[0],
 | 
			
		||||
                priorDomainDimensions = this.dimensions[0];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -202,6 +202,38 @@ define(
 | 
			
		||||
                expect(updater.getDimensions()[1]).toBeGreaterThan(20);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when no data is initially available", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    testDomainValues = {};
 | 
			
		||||
                    testRangeValues = {};
 | 
			
		||||
                    updater = new PlotUpdater(
 | 
			
		||||
                        mockSubscription,
 | 
			
		||||
                        testDomain,
 | 
			
		||||
                        testRange,
 | 
			
		||||
                        1350 // Smaller max size for easier testing
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("has no line data", function () {
 | 
			
		||||
                    // Either no lines, or empty lines are fine
 | 
			
		||||
                    expect(updater.getLineBuffers().map(function (lineBuffer) {
 | 
			
		||||
                        return lineBuffer.getLength();
 | 
			
		||||
                    }).reduce(function (a, b) {
 | 
			
		||||
                        return a + b;
 | 
			
		||||
                    }, 0)).toEqual(0);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("determines initial domain bounds from first available data", function () {
 | 
			
		||||
                    testDomainValues.a = 123;
 | 
			
		||||
                    testRangeValues.a = 456;
 | 
			
		||||
                    updater.update();
 | 
			
		||||
                    expect(updater.getOrigin()[0]).toEqual(jasmine.any(Number));
 | 
			
		||||
                    expect(updater.getOrigin()[1]).toEqual(jasmine.any(Number));
 | 
			
		||||
                    expect(isNaN(updater.getOrigin()[0])).toBeFalsy();
 | 
			
		||||
                    expect(isNaN(updater.getOrigin()[1])).toBeFalsy();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@
 | 
			
		||||
                "provides": "searchService",
 | 
			
		||||
                "type": "provider",
 | 
			
		||||
                "implementation": "ElasticSearchProvider.js",
 | 
			
		||||
                "depends": [ "$http", "objectService", "ELASTIC_ROOT" ]
 | 
			
		||||
                "depends": [ "$http", "ELASTIC_ROOT" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "constants": [
 | 
			
		||||
 
 | 
			
		||||
@@ -24,190 +24,122 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Module defining ElasticSearchProvider. Created by shale on 07/16/2015.
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
define([
 | 
			
		||||
 | 
			
		||||
        // JSLint doesn't like underscore-prefixed properties,
 | 
			
		||||
        // so hide them here.
 | 
			
		||||
        var ID = "_id",
 | 
			
		||||
            SCORE = "_score",
 | 
			
		||||
            DEFAULT_MAX_RESULTS = 100;
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * A search service which searches through domain objects in 
 | 
			
		||||
         * the filetree using ElasticSearch.
 | 
			
		||||
         *
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param $http Angular's $http service, for working with urls.
 | 
			
		||||
         * @param {ObjectService} objectService the service from which
 | 
			
		||||
         *        domain objects can be gotten.
 | 
			
		||||
         * @param ROOT the constant `ELASTIC_ROOT` which allows us to
 | 
			
		||||
         *        interact with ElasticSearch.
 | 
			
		||||
         */
 | 
			
		||||
        function ElasticSearchProvider($http, objectService, ROOT) {
 | 
			
		||||
            this.$http = $http;
 | 
			
		||||
            this.objectService = objectService;
 | 
			
		||||
            this.root = ROOT;
 | 
			
		||||
        }
 | 
			
		||||
], function (
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Searches through the filetree for domain objects using a search
 | 
			
		||||
         *   term. This is done through querying elasticsearch. Returns a
 | 
			
		||||
         *   promise for a result object that has the format
 | 
			
		||||
         *   {hits: searchResult[], total: number, timedOut: boolean}
 | 
			
		||||
         *   where a searchResult has the format
 | 
			
		||||
         *   {id: string, object: domainObject, score: number}
 | 
			
		||||
         *
 | 
			
		||||
         * Notes:
 | 
			
		||||
         *   * The order of the results is from highest to lowest score,
 | 
			
		||||
         *     as elsaticsearch determines them to be.
 | 
			
		||||
         *   * Uses the fuzziness operator to get more results.
 | 
			
		||||
         *   * More about this search's behavior at
 | 
			
		||||
         *     https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html
 | 
			
		||||
         *
 | 
			
		||||
         * @param searchTerm The text input that is the query.
 | 
			
		||||
         * @param timestamp The time at which this function was called.
 | 
			
		||||
         *   This timestamp is used as a unique identifier for this
 | 
			
		||||
         *   query and the corresponding results.
 | 
			
		||||
         * @param maxResults (optional) The maximum number of results
 | 
			
		||||
         *   that this function should return.
 | 
			
		||||
         * @param timeout (optional) The time after which the search should
 | 
			
		||||
         *   stop calculations and return partial results. Elasticsearch
 | 
			
		||||
         *   does not guarentee that this timeout will be strictly followed.
 | 
			
		||||
         */
 | 
			
		||||
        ElasticSearchProvider.prototype.query = function query(searchTerm, timestamp, maxResults, timeout) {
 | 
			
		||||
            var $http = this.$http,
 | 
			
		||||
                objectService = this.objectService,
 | 
			
		||||
                root = this.root,
 | 
			
		||||
                esQuery;
 | 
			
		||||
            
 | 
			
		||||
            function addFuzziness(searchTerm, editDistance) {
 | 
			
		||||
                if (!editDistance) {
 | 
			
		||||
                    editDistance = '';
 | 
			
		||||
                }
 | 
			
		||||
) {
 | 
			
		||||
    "use strict";
 | 
			
		||||
 | 
			
		||||
                return searchTerm.split(' ').map(function (s) {
 | 
			
		||||
                    // Don't add fuzziness for quoted strings
 | 
			
		||||
                    if (s.indexOf('"') !== -1) {
 | 
			
		||||
                        return s;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return s + '~' + editDistance;
 | 
			
		||||
                    }
 | 
			
		||||
                }).join(' ');
 | 
			
		||||
            }
 | 
			
		||||
    var ID_PROPERTY = '_id',
 | 
			
		||||
        SOURCE_PROPERTY = '_source',
 | 
			
		||||
        SCORE_PROPERTY = '_score';
 | 
			
		||||
 | 
			
		||||
            // Currently specific to elasticsearch
 | 
			
		||||
            function processSearchTerm(searchTerm) {
 | 
			
		||||
                var spaceIndex;
 | 
			
		||||
 | 
			
		||||
                // Cut out any extra spaces
 | 
			
		||||
                while (searchTerm.substr(0, 1) === ' ') {
 | 
			
		||||
                    searchTerm = searchTerm.substring(1, searchTerm.length);
 | 
			
		||||
                }
 | 
			
		||||
                while (searchTerm.substr(searchTerm.length - 1, 1) === ' ') {
 | 
			
		||||
                    searchTerm = searchTerm.substring(0, searchTerm.length - 1);
 | 
			
		||||
                }
 | 
			
		||||
                spaceIndex = searchTerm.indexOf('  ');
 | 
			
		||||
                while (spaceIndex !== -1) {
 | 
			
		||||
                    searchTerm = searchTerm.substring(0, spaceIndex) +
 | 
			
		||||
                    searchTerm.substring(spaceIndex + 1, searchTerm.length);
 | 
			
		||||
                    spaceIndex = searchTerm.indexOf('  ');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Add fuzziness for completeness
 | 
			
		||||
                searchTerm = addFuzziness(searchTerm);
 | 
			
		||||
 | 
			
		||||
                return searchTerm;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Processes results from the format that elasticsearch returns to
 | 
			
		||||
            // a list of searchResult objects, then returns a result object
 | 
			
		||||
            // (See documentation for query for object descriptions)
 | 
			
		||||
            function processResults(rawResults, timestamp) {
 | 
			
		||||
                var results = rawResults.data.hits.hits,
 | 
			
		||||
                    resultsLength = results.length,
 | 
			
		||||
                    ids = [],
 | 
			
		||||
                    scores = {},
 | 
			
		||||
                    searchResults = [],
 | 
			
		||||
                    i;
 | 
			
		||||
 | 
			
		||||
                // Get the result objects' IDs
 | 
			
		||||
                for (i = 0; i < resultsLength; i += 1) {
 | 
			
		||||
                    ids.push(results[i][ID]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Get the result objects' scores
 | 
			
		||||
                for (i = 0; i < resultsLength; i += 1) {
 | 
			
		||||
                    scores[ids[i]] = results[i][SCORE];
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Get the domain objects from their IDs
 | 
			
		||||
                return objectService.getObjects(ids).then(function (objects) {
 | 
			
		||||
                    var j,
 | 
			
		||||
                        id;
 | 
			
		||||
 | 
			
		||||
                    for (j = 0; j < resultsLength; j += 1) {
 | 
			
		||||
                        id = ids[j];
 | 
			
		||||
 | 
			
		||||
                        // Include items we can get models for
 | 
			
		||||
                        if (objects[id].getModel) {
 | 
			
		||||
                            // Format the results as searchResult objects
 | 
			
		||||
                            searchResults.push({
 | 
			
		||||
                                id: id,
 | 
			
		||||
                                object: objects[id],
 | 
			
		||||
                                score: scores[id]
 | 
			
		||||
                            });
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return {
 | 
			
		||||
                        hits: searchResults,
 | 
			
		||||
                        total: rawResults.data.hits.total,
 | 
			
		||||
                        timedOut: rawResults.data.timed_out
 | 
			
		||||
                    };
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // Check to see if the user provided a maximum
 | 
			
		||||
            // number of results to display
 | 
			
		||||
            if (!maxResults) {
 | 
			
		||||
                // Else, we provide a default value.
 | 
			
		||||
                maxResults = DEFAULT_MAX_RESULTS;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // If the user input is empty, we want to have no search results.
 | 
			
		||||
            if (searchTerm !== '' && searchTerm !== undefined) {
 | 
			
		||||
                // Process the search term
 | 
			
		||||
                searchTerm = processSearchTerm(searchTerm);
 | 
			
		||||
 | 
			
		||||
                // Create the query to elasticsearch
 | 
			
		||||
                esQuery = root + "/_search/?q=" + searchTerm +
 | 
			
		||||
                "&size=" + maxResults;
 | 
			
		||||
                if (timeout) {
 | 
			
		||||
                    esQuery += "&timeout=" + timeout;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Get the data...
 | 
			
		||||
                return this.$http({
 | 
			
		||||
                    method: "GET",
 | 
			
		||||
                    url: esQuery
 | 
			
		||||
                }).then(function (rawResults) {
 | 
			
		||||
                    // ...then process the data
 | 
			
		||||
                    return processResults(rawResults, timestamp);
 | 
			
		||||
                }, function (err) {
 | 
			
		||||
                    // In case of error, return nothing. (To prevent
 | 
			
		||||
                    //   infinite loading time.)
 | 
			
		||||
                    return {hits: [], total: 0};
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                return {hits: [], total: 0};
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return ElasticSearchProvider;
 | 
			
		||||
    /**
 | 
			
		||||
     * A search service which searches through domain objects in
 | 
			
		||||
     * the filetree using ElasticSearch.
 | 
			
		||||
     *
 | 
			
		||||
     * @constructor
 | 
			
		||||
     * @param $http Angular's $http service, for working with urls.
 | 
			
		||||
     * @param ROOT the constant `ELASTIC_ROOT` which allows us to
 | 
			
		||||
     *        interact with ElasticSearch.
 | 
			
		||||
     */
 | 
			
		||||
    function ElasticSearchProvider($http, ROOT) {
 | 
			
		||||
        this.$http = $http;
 | 
			
		||||
        this.root = ROOT;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Search for domain objects using elasticsearch as a search provider.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {String} searchTerm the term to search by.
 | 
			
		||||
     * @param {Number} [maxResults] the max numer of results to return.
 | 
			
		||||
     * @returns {Promise} promise for a modelResults object.
 | 
			
		||||
     */
 | 
			
		||||
    ElasticSearchProvider.prototype.query = function (searchTerm, maxResults) {
 | 
			
		||||
        var searchUrl = this.root + '/_search/',
 | 
			
		||||
            params = {},
 | 
			
		||||
            provider = this;
 | 
			
		||||
 | 
			
		||||
        searchTerm = this.cleanTerm(searchTerm);
 | 
			
		||||
        searchTerm = this.fuzzyMatchUnquotedTerms(searchTerm);
 | 
			
		||||
 | 
			
		||||
        params.q = searchTerm;
 | 
			
		||||
        params.size = maxResults;
 | 
			
		||||
 | 
			
		||||
        return this
 | 
			
		||||
            .$http({
 | 
			
		||||
                method: "GET",
 | 
			
		||||
                url: searchUrl,
 | 
			
		||||
                params: params
 | 
			
		||||
            })
 | 
			
		||||
            .then(function success(succesResponse) {
 | 
			
		||||
                return provider.parseResponse(succesResponse);
 | 
			
		||||
            }, function error(errorResponse) {
 | 
			
		||||
                // Gracefully fail.
 | 
			
		||||
                return {
 | 
			
		||||
                    hits: [],
 | 
			
		||||
                    total: 0
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clean excess whitespace from a search term and return the cleaned
 | 
			
		||||
     * version.
 | 
			
		||||
     *
 | 
			
		||||
     * @private
 | 
			
		||||
     * @param {string} the search term to clean.
 | 
			
		||||
     * @returns {string} search terms cleaned of excess whitespace.
 | 
			
		||||
     */
 | 
			
		||||
    ElasticSearchProvider.prototype.cleanTerm = function (term) {
 | 
			
		||||
        return term.trim().replace(/ +/g, ' ');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add fuzzy matching markup to search terms that are not quoted.
 | 
			
		||||
     *
 | 
			
		||||
     * The following:
 | 
			
		||||
     *     hello welcome "to quoted village" have fun
 | 
			
		||||
     * will become
 | 
			
		||||
     *     hello~ welcome~ "to quoted village" have~ fun~
 | 
			
		||||
     *
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    ElasticSearchProvider.prototype.fuzzyMatchUnquotedTerms = function (query) {
 | 
			
		||||
        var matchUnquotedSpaces = '\\s+(?=([^"]*"[^"]*")*[^"]*$)',
 | 
			
		||||
            matcher = new RegExp(matchUnquotedSpaces, 'g');
 | 
			
		||||
 | 
			
		||||
        return query
 | 
			
		||||
            .replace(matcher, '~ ')
 | 
			
		||||
            .replace(/$/, '~')
 | 
			
		||||
            .replace(/"~+/, '"');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse the response from ElasticSearch and convert it to a
 | 
			
		||||
     * modelResults object.
 | 
			
		||||
     *
 | 
			
		||||
     * @private
 | 
			
		||||
     * @param response a ES response object from $http
 | 
			
		||||
     * @returns modelResults
 | 
			
		||||
     */
 | 
			
		||||
    ElasticSearchProvider.prototype.parseResponse = function (response) {
 | 
			
		||||
        var results = response.data.hits.hits,
 | 
			
		||||
            searchResults = results.map(function (result) {
 | 
			
		||||
                return {
 | 
			
		||||
                    id: result[ID_PROPERTY],
 | 
			
		||||
                    model: result[SOURCE_PROPERTY],
 | 
			
		||||
                    score: result[SCORE_PROPERTY]
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            hits: searchResults,
 | 
			
		||||
            total: response.data.hits.total
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return ElasticSearchProvider;
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -19,97 +19,151 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,describe,it,expect,beforeEach,jasmine*/
 | 
			
		||||
/*global define,describe,it,expect,beforeEach,jasmine,spyOn,Promise,waitsFor*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *  SearchSpec. Created by shale on 07/31/2015.
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/ElasticSearchProvider"],
 | 
			
		||||
    function (ElasticSearchProvider) {
 | 
			
		||||
        "use strict";
 | 
			
		||||
define([
 | 
			
		||||
    '../src/ElasticSearchProvider'
 | 
			
		||||
], function (
 | 
			
		||||
    ElasticSearchProvider
 | 
			
		||||
) {
 | 
			
		||||
    'use strict';
 | 
			
		||||
 | 
			
		||||
        // JSLint doesn't like underscore-prefixed properties,
 | 
			
		||||
        // so hide them here.
 | 
			
		||||
        var ID = "_id",
 | 
			
		||||
            SCORE = "_score";
 | 
			
		||||
        
 | 
			
		||||
        describe("The ElasticSearch search provider ", function () {
 | 
			
		||||
            var mockHttp,
 | 
			
		||||
                mockHttpPromise,
 | 
			
		||||
                mockObjectPromise,
 | 
			
		||||
                mockObjectService,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                provider,
 | 
			
		||||
                mockProviderResults;
 | 
			
		||||
    describe('ElasticSearchProvider', function () {
 | 
			
		||||
        var $http,
 | 
			
		||||
            ROOT,
 | 
			
		||||
            provider;
 | 
			
		||||
 | 
			
		||||
        beforeEach(function () {
 | 
			
		||||
            $http = jasmine.createSpy('$http');
 | 
			
		||||
            ROOT = 'http://localhost:9200';
 | 
			
		||||
 | 
			
		||||
            provider = new ElasticSearchProvider($http, ROOT);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe('query', function () {
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockHttp = jasmine.createSpy("$http");
 | 
			
		||||
                mockHttpPromise = jasmine.createSpyObj(
 | 
			
		||||
                    "promise",
 | 
			
		||||
                    [ "then" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockHttp.andReturn(mockHttpPromise);
 | 
			
		||||
                // allow chaining of promise.then().catch();
 | 
			
		||||
                mockHttpPromise.then.andReturn(mockHttpPromise);
 | 
			
		||||
                
 | 
			
		||||
                mockObjectService = jasmine.createSpyObj(
 | 
			
		||||
                    "objectService",
 | 
			
		||||
                    [ "getObjects" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockObjectPromise = jasmine.createSpyObj(
 | 
			
		||||
                    "promise",
 | 
			
		||||
                    [ "then" ]
 | 
			
		||||
                );
 | 
			
		||||
                mockObjectService.getObjects.andReturn(mockObjectPromise);
 | 
			
		||||
                
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    [ "getId", "getModel" ]
 | 
			
		||||
                );
 | 
			
		||||
                
 | 
			
		||||
                provider = new ElasticSearchProvider(mockHttp, mockObjectService, "");
 | 
			
		||||
                provider.query(' test  "query" ', 0, undefined, 1000);
 | 
			
		||||
                spyOn(provider, 'cleanTerm').andReturn('cleanedTerm');
 | 
			
		||||
                spyOn(provider, 'fuzzyMatchUnquotedTerms').andReturn('fuzzy');
 | 
			
		||||
                spyOn(provider, 'parseResponse').andReturn('parsedResponse');
 | 
			
		||||
                $http.andReturn(Promise.resolve({}));
 | 
			
		||||
            });
 | 
			
		||||
            
 | 
			
		||||
            it("sends a query to ElasticSearch", function () {
 | 
			
		||||
                expect(mockHttp).toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
            it('cleans terms and adds fuzzyness', function () {
 | 
			
		||||
                provider.query('hello', 10);
 | 
			
		||||
                expect(provider.cleanTerm).toHaveBeenCalledWith('hello');
 | 
			
		||||
                expect(provider.fuzzyMatchUnquotedTerms)
 | 
			
		||||
                    .toHaveBeenCalledWith('cleanedTerm');
 | 
			
		||||
            });
 | 
			
		||||
            
 | 
			
		||||
            it("gets data from ElasticSearch", function () {
 | 
			
		||||
                var data = {
 | 
			
		||||
                    hits: {
 | 
			
		||||
                        hits: [
 | 
			
		||||
                            {},
 | 
			
		||||
                            {}
 | 
			
		||||
                        ],
 | 
			
		||||
                        total: 0
 | 
			
		||||
 | 
			
		||||
            it('calls through to $http', function () {
 | 
			
		||||
                provider.query('hello', 10);
 | 
			
		||||
                expect($http).toHaveBeenCalledWith({
 | 
			
		||||
                    method: 'GET',
 | 
			
		||||
                    params: {
 | 
			
		||||
                        q: 'fuzzy',
 | 
			
		||||
                        size: 10
 | 
			
		||||
                    },
 | 
			
		||||
                    timed_out: false
 | 
			
		||||
                };
 | 
			
		||||
                data.hits.hits[0][ID] = 1;
 | 
			
		||||
                data.hits.hits[0][SCORE] = 1;
 | 
			
		||||
                data.hits.hits[1][ID] = 2;
 | 
			
		||||
                data.hits.hits[1][SCORE] = 2;
 | 
			
		||||
                
 | 
			
		||||
                mockProviderResults = mockHttpPromise.then.mostRecentCall.args[0]({data: data});
 | 
			
		||||
                
 | 
			
		||||
                expect(
 | 
			
		||||
                    mockObjectPromise.then.mostRecentCall.args[0]({
 | 
			
		||||
                        1: mockDomainObject,
 | 
			
		||||
                        2: mockDomainObject
 | 
			
		||||
                    }).hits.length
 | 
			
		||||
                ).toEqual(2);
 | 
			
		||||
                    url: 'http://localhost:9200/_search/'
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            
 | 
			
		||||
            it("returns nothing for an empty string query", function () {
 | 
			
		||||
                expect(provider.query("").hits).toEqual([]);
 | 
			
		||||
 | 
			
		||||
            it('gracefully fails when http fails', function () {
 | 
			
		||||
                var promiseChainResolved = false;
 | 
			
		||||
                $http.andReturn(Promise.reject());
 | 
			
		||||
 | 
			
		||||
                provider
 | 
			
		||||
                    .query('hello', 10)
 | 
			
		||||
                    .then(function (results) {
 | 
			
		||||
                        expect(results).toEqual({
 | 
			
		||||
                            hits: [],
 | 
			
		||||
                            total: 0
 | 
			
		||||
                        });
 | 
			
		||||
                        promiseChainResolved = true;
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                waitsFor(function () {
 | 
			
		||||
                    return promiseChainResolved;
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            
 | 
			
		||||
            it("returns something when there is an ElasticSearch error", function () {
 | 
			
		||||
                mockProviderResults = mockHttpPromise.then.mostRecentCall.args[1]();
 | 
			
		||||
                expect(mockProviderResults).toBeDefined();
 | 
			
		||||
 | 
			
		||||
            it('parses and returns when http succeeds', function () {
 | 
			
		||||
                var promiseChainResolved = false;
 | 
			
		||||
                $http.andReturn(Promise.resolve('successResponse'));
 | 
			
		||||
 | 
			
		||||
                provider
 | 
			
		||||
                    .query('hello', 10)
 | 
			
		||||
                    .then(function (results) {
 | 
			
		||||
                        expect(provider.parseResponse)
 | 
			
		||||
                            .toHaveBeenCalledWith('successResponse');
 | 
			
		||||
                        expect(results).toBe('parsedResponse');
 | 
			
		||||
                        promiseChainResolved = true;
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                waitsFor(function () {
 | 
			
		||||
                    return promiseChainResolved;
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
        it('can clean terms', function () {
 | 
			
		||||
            expect(provider.cleanTerm('   asdhs  ')).toBe('asdhs');
 | 
			
		||||
            expect(provider.cleanTerm('  and some    words'))
 | 
			
		||||
                .toBe('and some words');
 | 
			
		||||
            expect(provider.cleanTerm('Nice input')).toBe('Nice input');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can create fuzzy term matchers', function () {
 | 
			
		||||
            expect(provider.fuzzyMatchUnquotedTerms('pwr dvc 43'))
 | 
			
		||||
                .toBe('pwr~ dvc~ 43~');
 | 
			
		||||
 | 
			
		||||
            expect(provider.fuzzyMatchUnquotedTerms(
 | 
			
		||||
                    'hello welcome "to quoted village" have fun'
 | 
			
		||||
                )).toBe(
 | 
			
		||||
                    'hello~ welcome~ "to quoted village" have~ fun~'
 | 
			
		||||
                );
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can parse responses', function () {
 | 
			
		||||
            var elasticSearchResponse = {
 | 
			
		||||
                    data: {
 | 
			
		||||
                        hits: {
 | 
			
		||||
                            total: 2,
 | 
			
		||||
                            hits: [
 | 
			
		||||
                                {
 | 
			
		||||
                                    '_id': 'hit1Id',
 | 
			
		||||
                                    '_source': 'hit1Model',
 | 
			
		||||
                                    '_score': 0.56
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    '_id': 'hit2Id',
 | 
			
		||||
                                    '_source': 'hit2Model',
 | 
			
		||||
                                    '_score': 0.34
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
            expect(provider.parseResponse(elasticSearchResponse))
 | 
			
		||||
                .toEqual({
 | 
			
		||||
                    hits: [
 | 
			
		||||
                        {
 | 
			
		||||
                            id: 'hit1Id',
 | 
			
		||||
                            model: 'hit1Model',
 | 
			
		||||
                            score: 0.56
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            id: 'hit2Id',
 | 
			
		||||
                            model: 'hit2Model',
 | 
			
		||||
                            score: 0.34
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    total: 2
 | 
			
		||||
                });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,12 @@
 | 
			
		||||
            {
 | 
			
		||||
                "key": "mctInclude",
 | 
			
		||||
                "implementation": "MCTInclude.js",
 | 
			
		||||
                "depends": [ "templates[]", "$sce" ]
 | 
			
		||||
                "depends": [ "templates[]", "templateLinker" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "mctRepresentation",
 | 
			
		||||
                "implementation": "MCTRepresentation.js",
 | 
			
		||||
                "depends": [ "representations[]", "views[]", "representers[]", "$q", "$sce", "$log" ]
 | 
			
		||||
                "depends": [ "representations[]", "views[]", "representers[]", "$q", "templateLinker", "$log" ]
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "gestures": [
 | 
			
		||||
@@ -48,6 +48,12 @@
 | 
			
		||||
                "key": "dndService",
 | 
			
		||||
                "implementation": "services/DndService.js",
 | 
			
		||||
                "depends": [ "$log" ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "key": "templateLinker",
 | 
			
		||||
                "implementation": "TemplateLinker.js",
 | 
			
		||||
                "depends": [ "$templateRequest", "$sce", "$compile", "$log" ],
 | 
			
		||||
                "comment": "For internal use by mct-include and mct-representation."
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "actions": [
 | 
			
		||||
 
 | 
			
		||||
@@ -54,36 +54,38 @@ define(
 | 
			
		||||
         * @param {TemplateDefinition[]} templates an array of
 | 
			
		||||
         *        template extensions
 | 
			
		||||
         */
 | 
			
		||||
        function MCTInclude(templates, $sce) {
 | 
			
		||||
        function MCTInclude(templates, templateLinker) {
 | 
			
		||||
            var templateMap = {};
 | 
			
		||||
 | 
			
		||||
            function link(scope, element) {
 | 
			
		||||
                var changeTemplate = templateLinker.link(
 | 
			
		||||
                    scope,
 | 
			
		||||
                    element,
 | 
			
		||||
                    scope.key && templateMap[scope.key]
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                scope.$watch('key', function (key) {
 | 
			
		||||
                    changeTemplate(key && templateMap[key]);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Prepopulate templateMap for easy look up by key
 | 
			
		||||
            templates.forEach(function (template) {
 | 
			
		||||
                var key = template.key,
 | 
			
		||||
                    path = $sce.trustAsResourceUrl([
 | 
			
		||||
                        template.bundle.path,
 | 
			
		||||
                        template.bundle.resources,
 | 
			
		||||
                        template.templateUrl
 | 
			
		||||
                    ].join("/"));
 | 
			
		||||
                var key = template.key;
 | 
			
		||||
                // First found should win (priority ordering)
 | 
			
		||||
                templateMap[key] = templateMap[key] || path;
 | 
			
		||||
                templateMap[key] =
 | 
			
		||||
                    templateMap[key] || templateLinker.getPath(template);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            function controller($scope) {
 | 
			
		||||
                // Pass the template URL to ng-include via scope.
 | 
			
		||||
                $scope.inclusion = templateMap[$scope.key];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                // Only show at the element level
 | 
			
		||||
                restrict: "E",
 | 
			
		||||
 | 
			
		||||
                // Use the included controller to populate scope
 | 
			
		||||
                controller: controller,
 | 
			
		||||
                link: link,
 | 
			
		||||
 | 
			
		||||
                // Use ng-include as a template; "inclusion" will be the real
 | 
			
		||||
                // template path
 | 
			
		||||
                template: '<ng-include src="inclusion"></ng-include>',
 | 
			
		||||
                // May hide the element, so let other directives act first
 | 
			
		||||
                priority: -1000,
 | 
			
		||||
 | 
			
		||||
                // Two-way bind key, ngModel, and parameters
 | 
			
		||||
                scope: { key: "=", ngModel: "=", parameters: "=" }
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ define(
 | 
			
		||||
         *        representation extensions
 | 
			
		||||
         * @param {ViewDefinition[]} views an array of view extensions
 | 
			
		||||
         */
 | 
			
		||||
        function MCTRepresentation(representations, views, representers, $q, $sce, $log) {
 | 
			
		||||
        function MCTRepresentation(representations, views, representers, $q, templateLinker, $log) {
 | 
			
		||||
            var representationMap = {},
 | 
			
		||||
                gestureMap = {};
 | 
			
		||||
 | 
			
		||||
@@ -72,11 +72,7 @@ define(
 | 
			
		||||
 | 
			
		||||
            // Get a path to a representation
 | 
			
		||||
            function getPath(representation) {
 | 
			
		||||
                return $sce.trustAsResourceUrl([
 | 
			
		||||
                    representation.bundle.path,
 | 
			
		||||
                    representation.bundle.resources,
 | 
			
		||||
                    representation.templateUrl
 | 
			
		||||
                ].join("/"));
 | 
			
		||||
                return templateLinker.getPath(representation);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Look up a matching representation for this domain object
 | 
			
		||||
@@ -94,12 +90,13 @@ define(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function link($scope, element, attrs) {
 | 
			
		||||
            function link($scope, element, attrs, ctrl, transclude) {
 | 
			
		||||
                var activeRepresenters = representers.map(function (Representer) {
 | 
			
		||||
                        return new Representer($scope, element, attrs);
 | 
			
		||||
                    }),
 | 
			
		||||
                    toClear = [], // Properties to clear out of scope on change
 | 
			
		||||
                    counter = 0;
 | 
			
		||||
                    counter = 0,
 | 
			
		||||
                    changeTemplate = templateLinker.link($scope, element);
 | 
			
		||||
 | 
			
		||||
                // Populate scope with any capabilities indicated by the
 | 
			
		||||
                // representation's extension definition
 | 
			
		||||
@@ -150,15 +147,17 @@ define(
 | 
			
		||||
                function refresh() {
 | 
			
		||||
                    var domainObject = $scope.domainObject,
 | 
			
		||||
                        representation = lookup($scope.key, domainObject),
 | 
			
		||||
                        uses = ((representation || {}).uses || []);
 | 
			
		||||
                        path = representation && getPath(representation),
 | 
			
		||||
                        uses = ((representation || {}).uses || []),
 | 
			
		||||
                        canRepresent = !!(path && domainObject);
 | 
			
		||||
 | 
			
		||||
                    // Create an empty object named "representation", for this
 | 
			
		||||
                    // representation to store local variables into.
 | 
			
		||||
                    $scope.representation = {};
 | 
			
		||||
 | 
			
		||||
                    // Look up the actual template path, pass it to ng-include
 | 
			
		||||
                    // via the "inclusion" field
 | 
			
		||||
                    $scope.inclusion = representation && getPath(representation);
 | 
			
		||||
                    // Change templates (passing in undefined to clear
 | 
			
		||||
                    // if we don't have enough info to show a template.)
 | 
			
		||||
                    changeTemplate(canRepresent ? path : undefined);
 | 
			
		||||
 | 
			
		||||
                    // Any existing representers are no longer valid; release them.
 | 
			
		||||
                    destroyRepresenters();
 | 
			
		||||
@@ -176,7 +175,7 @@ define(
 | 
			
		||||
 | 
			
		||||
                    // Populate scope with fields associated with the current
 | 
			
		||||
                    // domain object (if one has been passed in)
 | 
			
		||||
                    if (domainObject) {
 | 
			
		||||
                    if (canRepresent) {
 | 
			
		||||
                        // Track how many representations we've made in this scope,
 | 
			
		||||
                        // to ensure that the correct representations are matched to
 | 
			
		||||
                        // the correct object/key pairs.
 | 
			
		||||
@@ -233,9 +232,8 @@ define(
 | 
			
		||||
                // Handle Angular's linking step
 | 
			
		||||
                link: link,
 | 
			
		||||
 | 
			
		||||
                // Use ng-include as a template; "inclusion" will be the real
 | 
			
		||||
                // template path
 | 
			
		||||
                template: '<ng-include src="inclusion"></ng-include>',
 | 
			
		||||
                // May hide the element, so let other directives act first
 | 
			
		||||
                priority: -1000,
 | 
			
		||||
 | 
			
		||||
                // Two-way bind key and parameters, get the represented domain
 | 
			
		||||
                // object as "mct-object"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										153
									
								
								platform/representation/src/TemplateLinker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,153 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise*/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        "use strict";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The `templateLinker` service is intended for internal use by
 | 
			
		||||
         * the `mct-include` and `mct-representation` directives. It is
 | 
			
		||||
         * used to support common behavior of directives; specifically,
 | 
			
		||||
         * loading templates and inserting them into a specified element,
 | 
			
		||||
         * and/or removing that element from the DOM when there is no
 | 
			
		||||
         * template to populate it with.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {Function} $templateRequest Angular's `$templateRequest`
 | 
			
		||||
         *        service
 | 
			
		||||
         * @param $sce Angular's `$sce` service
 | 
			
		||||
         * @param {Function} $compile Angular's `$compile` service
 | 
			
		||||
         * @param $log Angular's `$log` service
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        function TemplateLinker($templateRequest, $sce, $compile, $log) {
 | 
			
		||||
            this.$templateRequest = $templateRequest;
 | 
			
		||||
            this.$sce = $sce;
 | 
			
		||||
            this.$compile = $compile;
 | 
			
		||||
            this.$log = $log;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Load a template from the given URL. This request will be handled
 | 
			
		||||
         * via `$templateRequest` to ensure caching et cetera.
 | 
			
		||||
         * @param {string} the URL for the template
 | 
			
		||||
         * @returns {Promise.<string>} a promise for the HTML content of
 | 
			
		||||
         *          the template
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        TemplateLinker.prototype.load = function (templateUrl) {
 | 
			
		||||
            return this.$templateRequest(
 | 
			
		||||
                this.$sce.trustAsResourceUrl(templateUrl),
 | 
			
		||||
                false
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get a path to a template from an extension definition fo
 | 
			
		||||
         * a template, representation, or view.
 | 
			
		||||
         * @param {TemplateDefinition} extensionDefinition the definition
 | 
			
		||||
         *        of the template/representation/view to resolve
 | 
			
		||||
         */
 | 
			
		||||
        TemplateLinker.prototype.getPath = function (extensionDefinition) {
 | 
			
		||||
            return [
 | 
			
		||||
                extensionDefinition.bundle.path,
 | 
			
		||||
                extensionDefinition.bundle.resources,
 | 
			
		||||
                extensionDefinition.templateUrl
 | 
			
		||||
            ].join('/');
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Populate the given element with templates, within the given scope;
 | 
			
		||||
         * intended to support the `link` function of the supported directives.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {Scope} scope the Angular scope to use when rendering
 | 
			
		||||
         *        templates
 | 
			
		||||
         * @param element the jqLite-wrapped element into which templates
 | 
			
		||||
         *        should be inserted
 | 
			
		||||
         * @returns {Function} a function which can be called with a template
 | 
			
		||||
         *          URL to switch templates, or `undefined` to remove.
 | 
			
		||||
         */
 | 
			
		||||
        TemplateLinker.prototype.link = function (scope, element, templateUrl) {
 | 
			
		||||
            var activeElement = element,
 | 
			
		||||
                activeTemplateUrl,
 | 
			
		||||
                comment = this.$compile('<!-- hidden mct element -->')(scope),
 | 
			
		||||
                self = this;
 | 
			
		||||
 | 
			
		||||
            function removeElement() {
 | 
			
		||||
                if (activeElement !== comment) {
 | 
			
		||||
                    activeElement.replaceWith(comment);
 | 
			
		||||
                    activeElement = comment;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function addElement() {
 | 
			
		||||
                if (activeElement !== element) {
 | 
			
		||||
                    activeElement.replaceWith(element);
 | 
			
		||||
                    activeElement = element;
 | 
			
		||||
                    activeElement.empty();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function populateElement(template) {
 | 
			
		||||
                element.empty();
 | 
			
		||||
                element.append(self.$compile(template)(scope));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function badTemplate(templateUrl) {
 | 
			
		||||
                self.$log.warn("Couldn't load template at " + templateUrl);
 | 
			
		||||
                removeElement();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function changeTemplate(templateUrl) {
 | 
			
		||||
                if (templateUrl !== activeTemplateUrl) {
 | 
			
		||||
                    if (templateUrl) {
 | 
			
		||||
                        addElement();
 | 
			
		||||
                        self.load(templateUrl).then(function (template) {
 | 
			
		||||
                            // Avoid race conditions
 | 
			
		||||
                            if (templateUrl === activeTemplateUrl) {
 | 
			
		||||
                                populateElement(template);
 | 
			
		||||
                            }
 | 
			
		||||
                        }, function () {
 | 
			
		||||
                            badTemplate(templateUrl);
 | 
			
		||||
                        });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        removeElement();
 | 
			
		||||
                    }
 | 
			
		||||
                    activeTemplateUrl = templateUrl;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (templateUrl) {
 | 
			
		||||
                changeTemplate(templateUrl);
 | 
			
		||||
            } else {
 | 
			
		||||
                removeElement();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return changeTemplate;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return TemplateLinker;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@@ -31,9 +31,21 @@ define(
 | 
			
		||||
 | 
			
		||||
        describe("The mct-include directive", function () {
 | 
			
		||||
            var testTemplates,
 | 
			
		||||
                mockSce,
 | 
			
		||||
                testUrls,
 | 
			
		||||
                mockLinker,
 | 
			
		||||
                mockScope,
 | 
			
		||||
                mockElement,
 | 
			
		||||
                mockChangeTemplate,
 | 
			
		||||
                mctInclude;
 | 
			
		||||
 | 
			
		||||
            function fireWatch(expr, value) {
 | 
			
		||||
                mockScope.$watch.calls.forEach(function (call) {
 | 
			
		||||
                    if (call.args[0] === expr) {
 | 
			
		||||
                        call.args[1](value);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testTemplates = [
 | 
			
		||||
                    {
 | 
			
		||||
@@ -47,40 +59,44 @@ define(
 | 
			
		||||
                        templateUrl: "z/template.html"
 | 
			
		||||
                    }
 | 
			
		||||
                ];
 | 
			
		||||
                mockSce = jasmine.createSpyObj(
 | 
			
		||||
                    '$sce',
 | 
			
		||||
                    ['trustAsResourceUrl']
 | 
			
		||||
                );
 | 
			
		||||
                mockSce.trustAsResourceUrl.andCallFake(function (url) {
 | 
			
		||||
                    return url;
 | 
			
		||||
                testUrls = {};
 | 
			
		||||
                testTemplates.forEach(function (t, i) {
 | 
			
		||||
                    testUrls[t.key] = "some URL " + String(i);
 | 
			
		||||
                });
 | 
			
		||||
                mctInclude = new MCTInclude(testTemplates, mockSce);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("has a built-in template, with ng-include src=inclusion", function () {
 | 
			
		||||
                // Not rigorous, but should detect many cases when template is broken.
 | 
			
		||||
                expect(mctInclude.template.indexOf("ng-include")).not.toEqual(-1);
 | 
			
		||||
                expect(mctInclude.template.indexOf("inclusion")).not.toEqual(-1);
 | 
			
		||||
                mockLinker = jasmine.createSpyObj(
 | 
			
		||||
                    'templateLinker',
 | 
			
		||||
                    ['link', 'getPath']
 | 
			
		||||
                );
 | 
			
		||||
                mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']);
 | 
			
		||||
                mockElement = jasmine.createSpyObj('element', ['empty']);
 | 
			
		||||
                mockChangeTemplate = jasmine.createSpy('changeTemplate');
 | 
			
		||||
                mockLinker.link.andReturn(mockChangeTemplate);
 | 
			
		||||
                mockLinker.getPath.andCallFake(function (template) {
 | 
			
		||||
                    return testUrls[template.key];
 | 
			
		||||
                });
 | 
			
		||||
                mctInclude = new MCTInclude(testTemplates, mockLinker);
 | 
			
		||||
                mctInclude.link(mockScope, mockElement, {});
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is restricted to elements", function () {
 | 
			
		||||
                expect(mctInclude.restrict).toEqual("E");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("reads a template location from a scope's key variable", function () {
 | 
			
		||||
                var scope = { key: "abc" };
 | 
			
		||||
                mctInclude.controller(scope);
 | 
			
		||||
                expect(scope.inclusion).toEqual("a/b/c/template.html");
 | 
			
		||||
 | 
			
		||||
                scope = { key: "xyz" };
 | 
			
		||||
                mctInclude.controller(scope);
 | 
			
		||||
                expect(scope.inclusion).toEqual("x/y/z/template.html");
 | 
			
		||||
            it("exposes templates via the templateLinker", function () {
 | 
			
		||||
                expect(mockLinker.link)
 | 
			
		||||
                    .toHaveBeenCalledWith(mockScope, mockElement, undefined);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("trusts template URLs", function () {
 | 
			
		||||
                mctInclude.controller({ key: "xyz" });
 | 
			
		||||
                expect(mockSce.trustAsResourceUrl)
 | 
			
		||||
                    .toHaveBeenCalledWith("x/y/z/template.html");
 | 
			
		||||
            it("reads a template location from a scope's key variable", function () {
 | 
			
		||||
                mockScope.key = 'abc';
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(testUrls.abc);
 | 
			
		||||
 | 
			
		||||
                mockScope.key = 'xyz';
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(testUrls.xyz);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -36,10 +36,12 @@ define(
 | 
			
		||||
        describe("The mct-representation directive", function () {
 | 
			
		||||
            var testRepresentations,
 | 
			
		||||
                testViews,
 | 
			
		||||
                testUrls,
 | 
			
		||||
                mockRepresenters,
 | 
			
		||||
                mockQ,
 | 
			
		||||
                mockSce,
 | 
			
		||||
                mockLinker,
 | 
			
		||||
                mockLog,
 | 
			
		||||
                mockChangeTemplate,
 | 
			
		||||
                mockScope,
 | 
			
		||||
                mockElement,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
@@ -54,7 +56,17 @@ define(
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function fireWatch(expr, value) {
 | 
			
		||||
                mockScope.$watch.calls.forEach(function (call) {
 | 
			
		||||
                    if (call.args[0] === expr) {
 | 
			
		||||
                        call.args[1](value);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testUrls = {};
 | 
			
		||||
 | 
			
		||||
                testRepresentations = [
 | 
			
		||||
                    {
 | 
			
		||||
                        key: "abc",
 | 
			
		||||
@@ -85,6 +97,11 @@ define(
 | 
			
		||||
 | 
			
		||||
                testModel = { someKey: "some value" };
 | 
			
		||||
 | 
			
		||||
                testUrls = {};
 | 
			
		||||
                testViews.concat(testRepresentations).forEach(function (t, i) {
 | 
			
		||||
                    testUrls[t.key] = "some URL " + String(i);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockRepresenters = ["A", "B"].map(function (name) {
 | 
			
		||||
                    var constructor = jasmine.createSpy("Representer" + name),
 | 
			
		||||
                        representer = jasmine.createSpyObj(
 | 
			
		||||
@@ -96,45 +113,44 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockQ = { when: mockPromise };
 | 
			
		||||
                mockSce = jasmine.createSpyObj(
 | 
			
		||||
                    '$sce',
 | 
			
		||||
                    ['trustAsResourceUrl']
 | 
			
		||||
                mockLinker = jasmine.createSpyObj(
 | 
			
		||||
                    'templateLinker',
 | 
			
		||||
                    ['link', 'getPath']
 | 
			
		||||
                );
 | 
			
		||||
                mockChangeTemplate = jasmine.createSpy('changeTemplate');
 | 
			
		||||
                mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                mockSce.trustAsResourceUrl.andCallFake(function (url) {
 | 
			
		||||
                    return url;
 | 
			
		||||
                });
 | 
			
		||||
                mockScope = jasmine.createSpyObj("scope", [ "$watch", "$on" ]);
 | 
			
		||||
                mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
 | 
			
		||||
 | 
			
		||||
                mockDomainObject.getModel.andReturn(testModel);
 | 
			
		||||
                mockLinker.link.andReturn(mockChangeTemplate);
 | 
			
		||||
                mockLinker.getPath.andCallFake(function (ext) {
 | 
			
		||||
                    return testUrls[ext.key];
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mctRepresentation = new MCTRepresentation(
 | 
			
		||||
                    testRepresentations,
 | 
			
		||||
                    testViews,
 | 
			
		||||
                    mockRepresenters,
 | 
			
		||||
                    mockQ,
 | 
			
		||||
                    mockSce,
 | 
			
		||||
                    mockLinker,
 | 
			
		||||
                    mockLog
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            it("has a built-in template, with ng-include src=inclusion", function () {
 | 
			
		||||
                // Not rigorous, but should detect many cases when template is broken.
 | 
			
		||||
                expect(mctRepresentation.template.indexOf("ng-include")).not.toEqual(-1);
 | 
			
		||||
                expect(mctRepresentation.template.indexOf("inclusion")).not.toEqual(-1);
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("is restricted to elements", function () {
 | 
			
		||||
                expect(mctRepresentation.restrict).toEqual("E");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exposes templates via the templateLinker", function () {
 | 
			
		||||
                expect(mockLinker.link)
 | 
			
		||||
                    .toHaveBeenCalledWith(mockScope, mockElement);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("watches scope when linked", function () {
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement);
 | 
			
		||||
                expect(mockScope.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    "key",
 | 
			
		||||
                    jasmine.any(Function)
 | 
			
		||||
@@ -150,42 +166,46 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("recognizes keys for representations", function () {
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement);
 | 
			
		||||
 | 
			
		||||
                mockScope.key = "abc";
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
 | 
			
		||||
                // Trigger the watch
 | 
			
		||||
                mockScope.$watch.calls[0].args[1]();
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
                fireWatch('domainObject', mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockScope.inclusion).toEqual("a/b/c/template.html");
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(testUrls.abc);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("recognizes keys for views", function () {
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement);
 | 
			
		||||
 | 
			
		||||
                mockScope.key = "xyz";
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
 | 
			
		||||
                // Trigger the watch
 | 
			
		||||
                mockScope.$watch.calls[0].args[1]();
 | 
			
		||||
                // Trigger the watches
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
                fireWatch('domainObject', mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockScope.inclusion).toEqual("x/y/z/template.html");
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(testUrls.xyz);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("trusts template URLs", function () {
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement);
 | 
			
		||||
 | 
			
		||||
            it("does not load templates until there is an object", function () {
 | 
			
		||||
                mockScope.key = "xyz";
 | 
			
		||||
 | 
			
		||||
                // Trigger the watch
 | 
			
		||||
                mockScope.$watch.calls[0].args[1]();
 | 
			
		||||
                fireWatch('key', mockScope.key);
 | 
			
		||||
 | 
			
		||||
                expect(mockSce.trustAsResourceUrl)
 | 
			
		||||
                    .toHaveBeenCalledWith("x/y/z/template.html");
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .not.toHaveBeenCalledWith(jasmine.any(String));
 | 
			
		||||
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
                fireWatch('domainObject', mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockChangeTemplate)
 | 
			
		||||
                    .toHaveBeenCalledWith(jasmine.any(String));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("loads declared capabilities", function () {
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement);
 | 
			
		||||
 | 
			
		||||
                mockScope.key = "def";
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
 | 
			
		||||
@@ -199,8 +219,6 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("logs when no representation is available for a key", function () {
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement);
 | 
			
		||||
 | 
			
		||||
                mockScope.key = "someUnknownThing";
 | 
			
		||||
 | 
			
		||||
                // Verify precondition
 | 
			
		||||
@@ -214,8 +232,6 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("clears out obsolete peroperties from scope", function () {
 | 
			
		||||
                mctRepresentation.link(mockScope, mockElement);
 | 
			
		||||
 | 
			
		||||
                mockScope.key = "def";
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
                mockDomainObject.useCapability.andReturn("some value");
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										211
									
								
								platform/representation/test/TemplateLinkerSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,211 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/TemplateLinker"],
 | 
			
		||||
    function (TemplateLinker) {
 | 
			
		||||
        'use strict';
 | 
			
		||||
 | 
			
		||||
        var JQLITE_METHODS = [ 'replaceWith', 'empty', 'append' ];
 | 
			
		||||
 | 
			
		||||
        describe("TemplateLinker", function () {
 | 
			
		||||
            var mockTemplateRequest,
 | 
			
		||||
                mockSce,
 | 
			
		||||
                mockCompile,
 | 
			
		||||
                mockLog,
 | 
			
		||||
                mockScope,
 | 
			
		||||
                mockElement,
 | 
			
		||||
                mockTemplates,
 | 
			
		||||
                mockElements,
 | 
			
		||||
                mockPromise,
 | 
			
		||||
                linker;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockTemplateRequest = jasmine.createSpy('$templateRequest');
 | 
			
		||||
                mockSce = jasmine.createSpyObj('$sce', ['trustAsResourceUrl']);
 | 
			
		||||
                mockCompile = jasmine.createSpy('$compile');
 | 
			
		||||
                mockLog = jasmine.createSpyObj('$log', ['error', 'warn']);
 | 
			
		||||
                mockScope = jasmine.createSpyObj('$scope', ['$on']);
 | 
			
		||||
                mockElement = jasmine.createSpyObj('element', JQLITE_METHODS);
 | 
			
		||||
                mockPromise = jasmine.createSpyObj('promise', ['then']);
 | 
			
		||||
                mockTemplates = {};
 | 
			
		||||
                mockElements = {};
 | 
			
		||||
 | 
			
		||||
                mockTemplateRequest.andReturn(mockPromise);
 | 
			
		||||
                mockCompile.andCallFake(function (html) {
 | 
			
		||||
                    mockTemplates[html] = jasmine.createSpy('template');
 | 
			
		||||
                    mockElements[html] =
 | 
			
		||||
                        jasmine.createSpyObj('templateEl', JQLITE_METHODS);
 | 
			
		||||
                    mockTemplates[html].andReturn(mockElements[html]);
 | 
			
		||||
                    return mockTemplates[html];
 | 
			
		||||
                });
 | 
			
		||||
                mockSce.trustAsResourceUrl.andCallFake(function (url) {
 | 
			
		||||
                    return { trusted: url };
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                linker = new TemplateLinker(
 | 
			
		||||
                    mockTemplateRequest,
 | 
			
		||||
                    mockSce,
 | 
			
		||||
                    mockCompile,
 | 
			
		||||
                    mockLog
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("resolves extension paths", function () {
 | 
			
		||||
                expect(linker.getPath({
 | 
			
		||||
                    bundle: {
 | 
			
		||||
                        path: 'a',
 | 
			
		||||
                        resources: 'b'
 | 
			
		||||
                    },
 | 
			
		||||
                    templateUrl: 'c/d.html'
 | 
			
		||||
                })).toEqual('a/b/c/d.html');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when linking elements", function () {
 | 
			
		||||
                var changeTemplate,
 | 
			
		||||
                    commentElement;
 | 
			
		||||
 | 
			
		||||
                function findCommentElement() {
 | 
			
		||||
                    mockCompile.calls.forEach(function (call) {
 | 
			
		||||
                        var html = call.args[0];
 | 
			
		||||
                        if (html.indexOf("<!--") > -1) {
 | 
			
		||||
                            commentElement = mockElements[html];
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    changeTemplate = linker.link(mockScope, mockElement);
 | 
			
		||||
                    findCommentElement();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("compiles a comment to use to replace element", function () {
 | 
			
		||||
                    expect(commentElement).toBeDefined();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("initially replaces elements with comments", function () {
 | 
			
		||||
                    expect(mockElement.replaceWith)
 | 
			
		||||
                        .toHaveBeenCalledWith(commentElement);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("provides a function to change templates", function () {
 | 
			
		||||
                    expect(changeTemplate).toEqual(jasmine.any(Function));
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("and then changing templates", function () {
 | 
			
		||||
                    var testUrl,
 | 
			
		||||
                        testTemplate;
 | 
			
		||||
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        testUrl = "some/url/template.html";
 | 
			
		||||
                        testTemplate = "<div>Some template!</div>";
 | 
			
		||||
                        changeTemplate(testUrl);
 | 
			
		||||
                        mockPromise.then.mostRecentCall
 | 
			
		||||
                            .args[0](testTemplate);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("loads templates using $templateRequest", function () {
 | 
			
		||||
                        expect(mockTemplateRequest).toHaveBeenCalledWith({
 | 
			
		||||
                            trusted: testUrl
 | 
			
		||||
                        }, false);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("compiles loaded templates with linked scope", function () {
 | 
			
		||||
                        expect(mockCompile).toHaveBeenCalledWith(testTemplate);
 | 
			
		||||
                        expect(mockTemplates[testTemplate])
 | 
			
		||||
                            .toHaveBeenCalledWith(mockScope);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("replaces comments with specified element", function () {
 | 
			
		||||
                        expect(commentElement.replaceWith)
 | 
			
		||||
                            .toHaveBeenCalledWith(mockElement);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("appends rendered content to the specified element", function () {
 | 
			
		||||
                        expect(mockElement.append)
 | 
			
		||||
                            .toHaveBeenCalledWith(mockElements[testTemplate]);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("clears templates when called with undefined", function () {
 | 
			
		||||
                        expect(mockElement.replaceWith.callCount)
 | 
			
		||||
                            .toEqual(1);
 | 
			
		||||
                        changeTemplate(undefined);
 | 
			
		||||
                        expect(mockElement.replaceWith.callCount)
 | 
			
		||||
                            .toEqual(2);
 | 
			
		||||
                        expect(mockElement.replaceWith.mostRecentCall.args[0])
 | 
			
		||||
                            .toEqual(commentElement);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("logs no warnings for nominal changes", function () {
 | 
			
		||||
                        expect(mockLog.warn).not.toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    describe("which cannot be found", function () {
 | 
			
		||||
                        beforeEach(function () {
 | 
			
		||||
                            changeTemplate("some/bad/url");
 | 
			
		||||
                            // Reject the template promise
 | 
			
		||||
                            mockPromise.then.mostRecentCall.args[1]();
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        it("removes the element from the DOM", function () {
 | 
			
		||||
                            expect(mockElement.replaceWith.callCount)
 | 
			
		||||
                                .toEqual(2);
 | 
			
		||||
                            expect(
 | 
			
		||||
                                mockElement.replaceWith.mostRecentCall.args[0]
 | 
			
		||||
                            ).toEqual(commentElement);
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        it("logs a warning", function () {
 | 
			
		||||
                            expect(mockLog.warn)
 | 
			
		||||
                                .toHaveBeenCalledWith(jasmine.any(String));
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when an initial template URL is provided", function () {
 | 
			
		||||
                var testUrl;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    testUrl = "some/test/url.html";
 | 
			
		||||
                    linker.link(mockScope, mockElement, testUrl);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not remove the element initially", function () {
 | 
			
		||||
                    expect(mockElement.replaceWith)
 | 
			
		||||
                        .not.toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("loads the specified template", function () {
 | 
			
		||||
                    expect(mockTemplateRequest).toHaveBeenCalledWith({
 | 
			
		||||
                        trusted: testUrl
 | 
			
		||||
                    }, false);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||