Merge remote-tracking branch 'origin/master' into persist-on-mutation-825
This commit is contained in:
@@ -136,7 +136,7 @@ define([
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Start",
|
||||
"glyph": "ï",
|
||||
"cssclass": "icon-play",
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
@@ -147,7 +147,7 @@ define([
|
||||
],
|
||||
"category": "contextual",
|
||||
"name": "Restart at 0",
|
||||
"glyph": "r",
|
||||
"cssclass": "icon-refresh",
|
||||
"priority": "preferred"
|
||||
}
|
||||
],
|
||||
@@ -155,7 +155,7 @@ define([
|
||||
{
|
||||
"key": "clock",
|
||||
"name": "Clock",
|
||||
"glyph": "\u0043",
|
||||
"cssclass": "icon-clock",
|
||||
"description": "A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.",
|
||||
"priority": 101,
|
||||
"features": [
|
||||
@@ -212,7 +212,7 @@ define([
|
||||
{
|
||||
"key": "timer",
|
||||
"name": "Timer",
|
||||
"glyph": "\u00f5",
|
||||
"cssclass": "icon-timer",
|
||||
"description": "A timer that counts up or down to a datetime. Timers can be started, stopped and reset whenever needed, and support a variety of display formats. Each Timer displays the same value to all users. Timers can be added to Display Layouts.",
|
||||
"priority": 100,
|
||||
"features": [
|
||||
|
||||
@@ -21,20 +21,13 @@
|
||||
-->
|
||||
<div class="l-time-display l-digital l-timer s-timer" ng-controller="TimerController as timer">
|
||||
<div class="l-elem-wrapper l-flex-row">
|
||||
<a
|
||||
ng-click="timer.clickButton()"
|
||||
<a ng-click="timer.clickButton()"
|
||||
title="{{timer.buttonText()}}"
|
||||
class="flex-elem s-icon-btn control"
|
||||
>
|
||||
{{timer.buttonGlyph()}}
|
||||
</a>
|
||||
<span class="flex-elem l-value">
|
||||
<span class="ui-symbol direction">{{timer.sign()}}</span>
|
||||
<span
|
||||
class="value"
|
||||
ng-class="{ active:timer.text() }"
|
||||
>{{timer.text() || "--:--:--"}}
|
||||
</span>
|
||||
class="flex-elem control s-icon-button {{timer.buttonCssClass()}}"></a>
|
||||
<span class="flex-elem l-value {{timer.signClass()}}">
|
||||
<span class="value"
|
||||
ng-class="{ active:timer.text() }">{{timer.text() || "--:--:--"}}
|
||||
</span>
|
||||
</span>
|
||||
<span ng-controller="RefreshingController"></span>
|
||||
</div>
|
||||
|
||||
@@ -50,10 +50,13 @@ define(
|
||||
if (formatter && !isNaN(timeDelta)) {
|
||||
self.textValue = formatter(timeDelta);
|
||||
self.signValue = timeDelta < 0 ? "-" :
|
||||
timeDelta >= 1000 ? "+" : "";
|
||||
timeDelta >= 1000 ? "+" : "";
|
||||
self.signCssClass = timeDelta < 0 ? "icon-minus" :
|
||||
timeDelta >= 1000 ? "icon-plus" : "";
|
||||
} else {
|
||||
self.textValue = "";
|
||||
self.signValue = "";
|
||||
self.signCssClass = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,12 +129,13 @@ define(
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the glyph to display for the start/restart button.
|
||||
* @returns {string} glyph to display
|
||||
* Get the CSS class to display the right icon
|
||||
* for the start/restart button.
|
||||
* @returns {string} cssclass to display
|
||||
*/
|
||||
TimerController.prototype.buttonGlyph = function () {
|
||||
TimerController.prototype.buttonCssClass = function () {
|
||||
return this.relevantAction ?
|
||||
this.relevantAction.getMetadata().glyph : "";
|
||||
this.relevantAction.getMetadata().cssclass : "";
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -164,6 +168,15 @@ define(
|
||||
return this.signValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the sign (+ or -) of the current timer value, as
|
||||
* a CSS class.
|
||||
* @returns {string} sign of the current timer value
|
||||
*/
|
||||
TimerController.prototype.signClass = function () {
|
||||
return this.signCssClass;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text to display for the current timer value.
|
||||
* @returns {string} current timer value
|
||||
|
||||
@@ -44,12 +44,12 @@ define(
|
||||
});
|
||||
}
|
||||
|
||||
ClockIndicator.prototype.getGlyph = function () {
|
||||
return "C";
|
||||
ClockIndicator.prototype.getGlyphClass = function () {
|
||||
return "no-collapse float-right subdued";
|
||||
};
|
||||
|
||||
ClockIndicator.prototype.getGlyphClass = function () {
|
||||
return "no-icon no-collapse float-right subtle";
|
||||
ClockIndicator.prototype.getCssClass = function () {
|
||||
return "icon-clock";
|
||||
};
|
||||
|
||||
ClockIndicator.prototype.getText = function () {
|
||||
|
||||
@@ -85,8 +85,8 @@ define(
|
||||
'timer.restart': mockRestart
|
||||
}[k]];
|
||||
});
|
||||
mockStart.getMetadata.andReturn({ glyph: "S", name: "Start" });
|
||||
mockRestart.getMetadata.andReturn({ glyph: "R", name: "Restart" });
|
||||
mockStart.getMetadata.andReturn({ cssclass: "icon-play", name: "Start" });
|
||||
mockRestart.getMetadata.andReturn({ cssclass: "icon-refresh", name: "Restart" });
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
testModel = {};
|
||||
@@ -144,14 +144,14 @@ define(
|
||||
expect(controller.text()).toEqual("0D 00:00:00");
|
||||
});
|
||||
|
||||
it("shows glyph & name for the applicable start/restart action", function () {
|
||||
it("shows cssclass & name for the applicable start/restart action", function () {
|
||||
invokeWatch('domainObject', mockDomainObject);
|
||||
expect(controller.buttonGlyph()).toEqual("S");
|
||||
expect(controller.buttonCssClass()).toEqual("icon-play");
|
||||
expect(controller.buttonText()).toEqual("Start");
|
||||
|
||||
testModel.timestamp = 12321;
|
||||
invokeWatch('model.modified', 1);
|
||||
expect(controller.buttonGlyph()).toEqual("R");
|
||||
expect(controller.buttonCssClass()).toEqual("icon-refresh");
|
||||
expect(controller.buttonText()).toEqual("Restart");
|
||||
});
|
||||
|
||||
|
||||
@@ -48,8 +48,7 @@ define(
|
||||
});
|
||||
|
||||
it("implements the Indicator interface", function () {
|
||||
expect(indicator.getGlyph()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getGlyphClass()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getCssClass()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getText()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getDescription()).toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ define([
|
||||
{
|
||||
"key": "fixed-display",
|
||||
"name": "Fixed Position Display",
|
||||
"glyph": "3",
|
||||
"cssclass": "icon-box-with-dashed-lines",
|
||||
"type": "telemetry.fixed",
|
||||
"template": fixedTemplate,
|
||||
"uses": [
|
||||
@@ -49,28 +49,28 @@ define([
|
||||
"items": [
|
||||
{
|
||||
"method": "add",
|
||||
"glyph": "\u002b",
|
||||
"cssclass": "icon-plus",
|
||||
"control": "menu-button",
|
||||
"text": "Add",
|
||||
"options": [
|
||||
{
|
||||
"name": "Box",
|
||||
"glyph": "\u00e0",
|
||||
"cssclass": "icon-box",
|
||||
"key": "fixed.box"
|
||||
},
|
||||
{
|
||||
"name": "Line",
|
||||
"glyph": "\u00e2",
|
||||
"cssclass": "icon-line-horz",
|
||||
"key": "fixed.line"
|
||||
},
|
||||
{
|
||||
"name": "Text",
|
||||
"glyph": "\u00e4",
|
||||
"cssclass": "icon-T",
|
||||
"key": "fixed.text"
|
||||
},
|
||||
{
|
||||
"name": "Image",
|
||||
"glyph": "\u00e3",
|
||||
"cssclass": "icon-image",
|
||||
"key": "fixed.image"
|
||||
}
|
||||
]
|
||||
@@ -81,63 +81,74 @@ define([
|
||||
"items": [
|
||||
{
|
||||
"method": "order",
|
||||
"glyph": "á",
|
||||
"cssclass": "icon-layers",
|
||||
"control": "menu-button",
|
||||
"title": "Layering",
|
||||
"description": "Move the selected object above or below other objects",
|
||||
"options": [
|
||||
{
|
||||
"name": "Move to Top",
|
||||
"glyph": "\u00eb",
|
||||
"cssclass": "icon-arrow-double-up",
|
||||
"key": "top"
|
||||
},
|
||||
{
|
||||
"name": "Move Up",
|
||||
"glyph": "\u005e",
|
||||
"cssclass": "icon-arrow-up",
|
||||
"key": "up"
|
||||
},
|
||||
{
|
||||
"name": "Move Down",
|
||||
"glyph": "\u0076",
|
||||
"cssclass": "icon-arrow-down",
|
||||
"key": "down"
|
||||
},
|
||||
{
|
||||
"name": "Move to Bottom",
|
||||
"glyph": "\u00ee",
|
||||
"cssclass": "icon-arrow-double-down",
|
||||
"key": "bottom"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "fill",
|
||||
"glyph": "",
|
||||
"cssclass": "icon-paint-bucket",
|
||||
"title": "Fill color",
|
||||
"description": "Set fill color",
|
||||
"control": "color"
|
||||
},
|
||||
{
|
||||
"property": "stroke",
|
||||
"glyph": "â",
|
||||
"cssclass": "icon-line-horz",
|
||||
"title": "Border color",
|
||||
"description": "Set border color",
|
||||
"control": "color"
|
||||
},
|
||||
{
|
||||
"property": "color",
|
||||
"glyph": "ä",
|
||||
"cssclass": "icon-T",
|
||||
"title": "Text color",
|
||||
"description": "Set text color",
|
||||
"mandatory": true,
|
||||
"control": "color"
|
||||
},
|
||||
{
|
||||
"property": "url",
|
||||
"glyph": "ã",
|
||||
"cssclass": "icon-image",
|
||||
"control": "dialog-button",
|
||||
"title": "Image Properties",
|
||||
"description": "Edit image properties",
|
||||
"dialog": {
|
||||
"control": "textfield",
|
||||
"name": "Image URL",
|
||||
"cssclass": "l-input-lg",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"property": "text",
|
||||
"glyph": "G",
|
||||
"cssclass": "icon-gear",
|
||||
"control": "dialog-button",
|
||||
"title": "Text Properties",
|
||||
"description": "Edit text properties",
|
||||
"dialog": {
|
||||
"control": "textfield",
|
||||
"name": "Text",
|
||||
@@ -146,15 +157,17 @@ define([
|
||||
},
|
||||
{
|
||||
"method": "showTitle",
|
||||
"glyph": "ç",
|
||||
"cssclass": "icon-two-parts-both",
|
||||
"control": "button",
|
||||
"description": "Show telemetry element title."
|
||||
"title": "Show title",
|
||||
"description": "Show telemetry element title"
|
||||
},
|
||||
{
|
||||
"method": "hideTitle",
|
||||
"glyph": "å",
|
||||
"cssclass": "icon-two-parts-one-only",
|
||||
"control": "button",
|
||||
"description": "Hide telemetry element title."
|
||||
"title": "Hide title",
|
||||
"description": "Hide telemetry element title"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -163,7 +176,7 @@ define([
|
||||
{
|
||||
"method": "remove",
|
||||
"control": "button",
|
||||
"glyph": "Z"
|
||||
"cssclass": "icon-trash"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -175,9 +188,11 @@ define([
|
||||
{
|
||||
"key": "telemetry.fixed",
|
||||
"name": "Fixed Position Display",
|
||||
"glyph": "3",
|
||||
"description": "A panel for collecting telemetry" +
|
||||
" elements in a fixed position display.",
|
||||
"cssclass": "icon-box-with-dashed-lines",
|
||||
"description": "Collect and display telemetry elements in " +
|
||||
"alphanumeric format in a simple canvas workspace. " +
|
||||
"Elements can be positioned and sized. " +
|
||||
"Lines, boxes and images can be added as well.",
|
||||
"priority": 899,
|
||||
"delegates": [
|
||||
"telemetry"
|
||||
@@ -199,12 +214,12 @@ define([
|
||||
{
|
||||
"name": "Horizontal grid (px)",
|
||||
"control": "textfield",
|
||||
"cssclass": "l-small l-numeric"
|
||||
"cssclass": "l-input-sm l-numeric"
|
||||
},
|
||||
{
|
||||
"name": "Vertical grid (px)",
|
||||
"control": "textfield",
|
||||
"cssclass": "l-small l-numeric"
|
||||
"cssclass": "l-input-sm l-numeric"
|
||||
}
|
||||
],
|
||||
"pattern": "^(\\d*[1-9]\\d*)?$",
|
||||
|
||||
@@ -41,7 +41,7 @@ define([
|
||||
{
|
||||
"name": "Imagery",
|
||||
"key": "imagery",
|
||||
"glyph": "ã",
|
||||
"cssclass": "icon-image",
|
||||
"template": imageryTemplate,
|
||||
"priority": "preferred",
|
||||
"needs": [
|
||||
|
||||
@@ -5,18 +5,16 @@
|
||||
<div
|
||||
class="l-local-controls s-local-controls"
|
||||
ng-show="false && showLocalControls">
|
||||
<a class="s-btn"
|
||||
<a class="s-button icon-arrow-left"
|
||||
ng-click="plot.stepBackPanZoom()"
|
||||
ng-show="1"
|
||||
title="Restore previous pan/zoom">
|
||||
<span class="ui-symbol icon"><</span>
|
||||
</a>
|
||||
|
||||
<a class="s-btn"
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="plot.unzoom()"
|
||||
ng-show="1"
|
||||
title="Reset pan/zoom">
|
||||
<span class="ui-symbol icon">I</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -27,24 +25,24 @@
|
||||
|
||||
<div class="l-image-main-controlbar flex-elem l-flex-row">
|
||||
<div class="left flex-elem grows">
|
||||
<a class="s-btn show-thumbs sm hidden"
|
||||
ng-click="showThumbsBubble = (showThumbsBubble)? false:true"><span class="ui-symbol icon"></span></a>
|
||||
<a class="s-button show-thumbs sm hidden icon-thumbs-strip"
|
||||
ng-click="showThumbsBubble = (showThumbsBubble)? false:true"></a>
|
||||
<span class="l-timezone">{{imagery.getZone()}}</span>
|
||||
<span class="l-time">{{imagery.getTime()}}</span>
|
||||
<span class="l-date">{{imagery.getDate()}}</span>
|
||||
</div>
|
||||
<div class="right flex-elem">
|
||||
<a class="s-btn pause-play"
|
||||
<a class="s-button pause-play"
|
||||
ng-click="imagery.paused(!imagery.paused())"
|
||||
ng-class="{ paused: imagery.paused() }"><span class="ui-symbol icon"></span></a>
|
||||
ng-class="{ paused: imagery.paused() }"></a>
|
||||
<a href="{{imagery.getImageUrl()}}"
|
||||
ng-if="imagery.getImageUrl()"
|
||||
target="_blank"
|
||||
title="Open image in new tab."
|
||||
class="s-btn">
|
||||
<span class="ui-symbol icon">y</span></a>
|
||||
class="s-button icon-new-window">
|
||||
</a>
|
||||
<a href=""
|
||||
class="s-btn l-mag s-mag ui-symbol vsm"
|
||||
class="s-button l-mag s-mag ui-symbol vsm icon-arrows-out"
|
||||
ng-click="clipped = false"
|
||||
ng-show="clipped === true"
|
||||
title="Not all of image is visible; click to reset."></a>
|
||||
|
||||
@@ -56,7 +56,7 @@ define([
|
||||
{
|
||||
"key": "layout",
|
||||
"name": "Display Layout",
|
||||
"glyph": "\u004c",
|
||||
"cssclass": "icon-layout",
|
||||
"type": "layout",
|
||||
"template": layoutTemplate,
|
||||
"editable": true,
|
||||
@@ -65,7 +65,7 @@ define([
|
||||
{
|
||||
"key": "fixed",
|
||||
"name": "Fixed Position",
|
||||
"glyph": "3",
|
||||
"cssclass": "icon-box-with-dashed-lines",
|
||||
"type": "telemetry.panel",
|
||||
"template": fixedTemplate,
|
||||
"uses": [
|
||||
@@ -77,28 +77,30 @@ define([
|
||||
"items": [
|
||||
{
|
||||
"method": "add",
|
||||
"glyph": "\u002b",
|
||||
"cssclass": "icon-plus",
|
||||
"control": "menu-button",
|
||||
"text": "Add",
|
||||
"title": "Add",
|
||||
"description": "Add new items",
|
||||
"options": [
|
||||
{
|
||||
"name": "Box",
|
||||
"glyph": "\u00e0",
|
||||
"cssclass": "icon-box",
|
||||
"key": "fixed.box"
|
||||
},
|
||||
{
|
||||
"name": "Line",
|
||||
"glyph": "\u00e2",
|
||||
"cssclass": "icon-line-horz",
|
||||
"key": "fixed.line"
|
||||
},
|
||||
{
|
||||
"name": "Text",
|
||||
"glyph": "\u00e4",
|
||||
"cssclass": "icon-T",
|
||||
"key": "fixed.text"
|
||||
},
|
||||
{
|
||||
"name": "Image",
|
||||
"glyph": "\u00e3",
|
||||
"cssclass": "icon-image",
|
||||
"key": "fixed.image"
|
||||
}
|
||||
]
|
||||
@@ -109,63 +111,74 @@ define([
|
||||
"items": [
|
||||
{
|
||||
"method": "order",
|
||||
"glyph": "á",
|
||||
"cssclass": "icon-layers",
|
||||
"control": "menu-button",
|
||||
"title": "Layering",
|
||||
"description": "Move the selected object above or below other objects",
|
||||
"options": [
|
||||
{
|
||||
"name": "Move to Top",
|
||||
"glyph": "\u00eb",
|
||||
"cssclass": "icon-arrow-double-up",
|
||||
"key": "top"
|
||||
},
|
||||
{
|
||||
"name": "Move Up",
|
||||
"glyph": "\u005e",
|
||||
"cssclass": "icon-arrow-up",
|
||||
"key": "up"
|
||||
},
|
||||
{
|
||||
"name": "Move Down",
|
||||
"glyph": "\u0076",
|
||||
"cssclass": "icon-arrow-down",
|
||||
"key": "down"
|
||||
},
|
||||
{
|
||||
"name": "Move to Bottom",
|
||||
"glyph": "\u00ee",
|
||||
"cssclass": "icon-arrow-double-down",
|
||||
"key": "bottom"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"property": "fill",
|
||||
"glyph": "",
|
||||
"cssclass": "icon-paint-bucket",
|
||||
"title": "Fill color",
|
||||
"description": "Set fill color",
|
||||
"control": "color"
|
||||
},
|
||||
{
|
||||
"property": "stroke",
|
||||
"glyph": "â",
|
||||
"cssclass": "icon-line-horz",
|
||||
"title": "Border color",
|
||||
"description": "Set border color",
|
||||
"control": "color"
|
||||
},
|
||||
{
|
||||
"property": "color",
|
||||
"glyph": "ä",
|
||||
"cssclass": "icon-T",
|
||||
"title": "Text color",
|
||||
"description": "Set text color",
|
||||
"mandatory": true,
|
||||
"control": "color"
|
||||
},
|
||||
{
|
||||
"property": "url",
|
||||
"glyph": "ã",
|
||||
"cssclass": "icon-image",
|
||||
"control": "dialog-button",
|
||||
"title": "Image Properties",
|
||||
"description": "Edit image properties",
|
||||
"dialog": {
|
||||
"control": "textfield",
|
||||
"name": "Image URL",
|
||||
"cssclass": "l-input-lg",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"property": "text",
|
||||
"glyph": "G",
|
||||
"cssclass": "icon-gear",
|
||||
"control": "dialog-button",
|
||||
"title": "Text Properties",
|
||||
"description": "Edit text properties",
|
||||
"dialog": {
|
||||
"control": "textfield",
|
||||
"name": "Text",
|
||||
@@ -174,15 +187,17 @@ define([
|
||||
},
|
||||
{
|
||||
"method": "showTitle",
|
||||
"glyph": "ç",
|
||||
"cssclass": "icon-two-parts-both",
|
||||
"control": "button",
|
||||
"description": "Show telemetry element title."
|
||||
"title": "Show title",
|
||||
"description": "Show telemetry element title"
|
||||
},
|
||||
{
|
||||
"method": "hideTitle",
|
||||
"glyph": "å",
|
||||
"cssclass": "icon-two-parts-one-only",
|
||||
"control": "button",
|
||||
"description": "Hide telemetry element title."
|
||||
"title": "Hide title",
|
||||
"description": "Hide telemetry element title"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -191,7 +206,9 @@ define([
|
||||
{
|
||||
"method": "remove",
|
||||
"control": "button",
|
||||
"glyph": "Z"
|
||||
"cssclass": "icon-trash",
|
||||
"title": "Delete",
|
||||
"description": "Delete the selected item"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -258,7 +275,7 @@ define([
|
||||
{
|
||||
"key": "layout",
|
||||
"name": "Display Layout",
|
||||
"glyph": "\u004c",
|
||||
"cssclass": "icon-layout",
|
||||
"description": "Assemble other objects and components together into a reusable screen layout. Working in a simple canvas workspace, simply drag in the objects you want, position and size them. Save your design and view or edit it at any time.",
|
||||
"priority": 900,
|
||||
"features": "creation",
|
||||
@@ -290,7 +307,7 @@ define([
|
||||
{
|
||||
"key": "telemetry.panel",
|
||||
"name": "Telemetry Panel",
|
||||
"glyph": "t",
|
||||
"cssclass": "icon-telemetry-panel",
|
||||
"description": "A panel for collecting telemetry elements.",
|
||||
"priority": 899,
|
||||
"delegates": [
|
||||
@@ -313,12 +330,12 @@ define([
|
||||
{
|
||||
"name": "Horizontal grid (px)",
|
||||
"control": "textfield",
|
||||
"cssclass": "l-small l-numeric"
|
||||
"cssclass": "l-input-sm l-numeric"
|
||||
},
|
||||
{
|
||||
"name": "Vertical grid (px)",
|
||||
"control": "textfield",
|
||||
"cssclass": "l-small l-numeric"
|
||||
"cssclass": "l-input-sm l-numeric"
|
||||
}
|
||||
],
|
||||
"pattern": "^(\\d*[1-9]\\d*)?$",
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<!-- Selection highlight, handles -->
|
||||
<span ng-if="controller.selected()">
|
||||
<div class="l-fixed-position-item s-selected"
|
||||
<div class="l-fixed-position-item s-selectable s-selected s-moveable"
|
||||
mct-drag-down="controller.moveHandle().startDrag(controller.selected())"
|
||||
mct-drag="controller.moveHandle().continueDrag(delta)"
|
||||
mct-drag-up="controller.moveHandle().endDrag()"
|
||||
|
||||
@@ -33,61 +33,28 @@
|
||||
</div>
|
||||
<!-- Drag handles -->
|
||||
<span ng-show="domainObject.hasCapability('editor')">
|
||||
|
||||
<span
|
||||
class="edit-handle edit-move"
|
||||
<span class="edit-handle edit-move"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<!--
|
||||
<span
|
||||
class="edit-handle edit-resize-w"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,0], [-1,0])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span
|
||||
class="edit-handle edit-resize-e"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [1,0])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
|
||||
<span
|
||||
class="edit-handle edit-resize-n"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [0,1], [0,-1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span
|
||||
class="edit-handle edit-resize-s"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [0,1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
-->
|
||||
|
||||
<span
|
||||
class="edit-corner edit-resize-nw"
|
||||
<span class="edit-corner edit-resize-nw"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [-1,-1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span
|
||||
class="edit-corner edit-resize-ne"
|
||||
<span class="edit-corner edit-resize-ne"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [0,1], [1,-1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span
|
||||
class="edit-corner edit-resize-sw"
|
||||
<span class="edit-corner edit-resize-sw"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,0], [-1,1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span
|
||||
class="edit-corner edit-resize-se"
|
||||
<span class="edit-corner edit-resize-se"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [1,1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
|
||||
@@ -94,7 +94,7 @@ define(
|
||||
};
|
||||
|
||||
/**
|
||||
* End a drag gesture. This should be callled when a drag
|
||||
* End a drag gesture. This should be called when a drag
|
||||
* concludes to trigger commit of changes.
|
||||
*/
|
||||
FixedDragHandle.prototype.endDrag = function () {
|
||||
|
||||
@@ -152,7 +152,7 @@ define(
|
||||
}
|
||||
|
||||
// Convert from { positions: ..., dimensions: ... } to an
|
||||
// apropriate ng-style argument, to position frames.
|
||||
// appropriate ng-style argument, to position frames.
|
||||
LayoutController.prototype.convertPosition = function (raw) {
|
||||
var gridSize = this.gridSize;
|
||||
// Multiply position/dimensions by grid size
|
||||
|
||||
@@ -55,6 +55,7 @@ define(
|
||||
key: "url",
|
||||
control: "textfield",
|
||||
name: "Image URL",
|
||||
"cssclass": "l-input-lg",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -164,7 +164,7 @@ define(
|
||||
// Populate scope
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||
|
||||
// Verify precondtion
|
||||
// Verify precondition
|
||||
expect(testConfiguration.panels.b).not.toBeDefined();
|
||||
|
||||
// Do a drag
|
||||
|
||||
@@ -36,7 +36,7 @@ define([
|
||||
{
|
||||
"key": "example.page",
|
||||
"name": "Web Page",
|
||||
"glyph": "\u00ea",
|
||||
"cssclass": "icon-page",
|
||||
"description": "Embed a web page or web-based image in a resizeable window component. Can be added to Display Layouts. Note that the URL being embedded must allow iframing.",
|
||||
"priority": 50,
|
||||
"features": [
|
||||
|
||||
@@ -25,6 +25,7 @@ define([
|
||||
"./src/PlotController",
|
||||
"./src/policies/PlotViewPolicy",
|
||||
"./src/PlotOptionsController",
|
||||
"./src/services/ExportImageService",
|
||||
"text!./res/templates/plot.html",
|
||||
"text!./res/templates/plot-options-browse.html",
|
||||
'legacyRegistry'
|
||||
@@ -33,6 +34,7 @@ define([
|
||||
PlotController,
|
||||
PlotViewPolicy,
|
||||
PlotOptionsController,
|
||||
exportImageService,
|
||||
plotTemplate,
|
||||
plotOptionsBrowseTemplate,
|
||||
legacyRegistry
|
||||
@@ -45,7 +47,7 @@ define([
|
||||
{
|
||||
"name": "Plot",
|
||||
"key": "plot",
|
||||
"glyph": "6",
|
||||
"cssclass": "icon-sine",
|
||||
"template": plotTemplate,
|
||||
"needs": [
|
||||
"telemetry"
|
||||
@@ -70,6 +72,8 @@ define([
|
||||
"implementation": PlotController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$element",
|
||||
"exportImageService",
|
||||
"telemetryFormatter",
|
||||
"telemetryHandler",
|
||||
"throttle",
|
||||
@@ -84,12 +88,30 @@ define([
|
||||
]
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "exportImageService",
|
||||
"implementation": exportImageService,
|
||||
"depends": [
|
||||
"$q",
|
||||
"$timeout",
|
||||
"$log",
|
||||
"EXPORT_IMAGE_TIMEOUT"
|
||||
]
|
||||
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "PLOT_FIXED_DURATION",
|
||||
"value": 900000,
|
||||
"priority": "fallback",
|
||||
"comment": "Fifteen minutes."
|
||||
},
|
||||
{
|
||||
"key": "EXPORT_IMAGE_TIMEOUT",
|
||||
"value": 500,
|
||||
"priority": "fallback"
|
||||
}
|
||||
],
|
||||
"policies": [
|
||||
@@ -103,6 +125,28 @@ define([
|
||||
"key": "plot-options-browse",
|
||||
"template": plotOptionsBrowseTemplate
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
{
|
||||
"name": "FileSaver.js",
|
||||
"version": "0.0.2",
|
||||
"author": "Eli Grey",
|
||||
"description": "File download initiator (for file exports)",
|
||||
"website": "https://github.com/eligrey/FileSaver.js/",
|
||||
"copyright": "Copyright © 2015 Eli Grey.",
|
||||
"license": "license-mit",
|
||||
"link": "https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md"
|
||||
},
|
||||
{
|
||||
"name": "html2canvas",
|
||||
"version": "0.4.1",
|
||||
"author": "Niklas von Hertzen",
|
||||
"description": "JavaScript HTML renderer",
|
||||
"website": "https://github.com/niklasvh/html2canvas",
|
||||
"copyright": "Copyright © 2012 Niklas von Hertzen.",
|
||||
"license": "license-mit",
|
||||
"link": "https://github.com/niklasvh/html2canvas/blob/master/LICENSE"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -25,13 +25,13 @@
|
||||
ng-model="configuration.plot.xAxis"
|
||||
structure="xAxisForm"
|
||||
name="xAxisFormState"
|
||||
class="flex-elem l-flex-row no-validate no-margin">
|
||||
class="flex-elem l-flex-row no-margin">
|
||||
</mct-form>
|
||||
<mct-form
|
||||
ng-model="configuration.plot.yAxis"
|
||||
structure="yAxisForm"
|
||||
name="yAxisFormState"
|
||||
class="flex-elem l-flex-row no-validate no-margin">
|
||||
class="flex-elem l-flex-row no-margin">
|
||||
</mct-form>
|
||||
<div class="form">
|
||||
<div class="section-header ng-binding ng-scope">
|
||||
@@ -60,7 +60,7 @@
|
||||
ng-model="configuration.plot.series[$index]"
|
||||
structure="plotSeriesForm"
|
||||
name="plotOptionsState"
|
||||
class="flex-elem l-flex-row no-validate">
|
||||
class="flex-elem l-flex-row">
|
||||
</mct-form>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
@@ -20,126 +20,136 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<span ng-controller="PlotController as plot"
|
||||
class="abs holder holder-plot">
|
||||
<div class="gl-plot"
|
||||
ng-style="{ height: 100 / plot.getSubPlots().length + '%'}"
|
||||
ng-repeat="subplot in plot.getSubPlots()">
|
||||
<div class="gl-plot-legend">
|
||||
<!-- ng-class is temporarily hard-coded in next element -->
|
||||
class="abs holder holder-plot has-control-bar">
|
||||
<div class="l-control-bar" ng-show="!plot.hideExportButtons">
|
||||
<span class="l-btn-set">
|
||||
<a class="s-button t-export labeled"
|
||||
ng-click="plot.exportPNG()"
|
||||
title="Export This View's Data as PNG">
|
||||
PNG
|
||||
</a>
|
||||
<a class="s-button t-export labeled last"
|
||||
ng-click="plot.exportJPG()"
|
||||
title="Export This View's Data as JPG">
|
||||
JPG
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="l-view-section">
|
||||
<div class="gl-plot"
|
||||
ng-style="{ height: 100 / plot.getSubPlots().length + '%'}"
|
||||
ng-repeat="subplot in plot.getSubPlots()">
|
||||
<div class="gl-plot-legend">
|
||||
<!-- ng-class is temporarily hard-coded in next element -->
|
||||
<span
|
||||
class='plot-legend-item'
|
||||
ng-repeat="telemetryObject in subplot.getTelemetryObjects()"
|
||||
ng-class="plot.getLegendClass(telemetryObject)">
|
||||
class='plot-legend-item'
|
||||
ng-repeat="telemetryObject in subplot.getTelemetryObjects()"
|
||||
ng-class="plot.getLegendClass(telemetryObject)">
|
||||
<span class='plot-color-swatch'
|
||||
ng-style="{ 'background-color': plot.getColor($index) }">
|
||||
</span>
|
||||
<span class='title-label'>{{telemetryObject.getModel().name}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="gl-plot-coords"
|
||||
ng-if="subplot.isHovering() && subplot.getHoverCoordinates()">
|
||||
{{subplot.getHoverCoordinates()}}
|
||||
</div>
|
||||
<div class="gl-plot-axis-area gl-plot-y">
|
||||
<div class="gl-plot-label gl-plot-y-label">
|
||||
{{axes[1].active.name}}
|
||||
</div>
|
||||
<div ng-repeat="tick in subplot.getRangeTicks()"
|
||||
class="gl-plot-tick gl-plot-y-tick-label"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
<div class="gl-plot-coords"
|
||||
ng-if="subplot.isHovering() && subplot.getHoverCoordinates()">
|
||||
{{subplot.getHoverCoordinates()}}
|
||||
</div>
|
||||
<div class="gl-plot-y-options gl-plot-local-controls"
|
||||
ng-if="axes[1].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[1].active"
|
||||
ng-options="option.name for option in axes[1].options">
|
||||
</select>
|
||||
<div class="gl-plot-axis-area gl-plot-y">
|
||||
<div class="gl-plot-label gl-plot-y-label">
|
||||
{{axes[1].active.name}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gl-plot-display-area"
|
||||
ng-mouseenter="subplot.isHovering(true);"
|
||||
ng-mouseleave="subplot.isHovering(false)"
|
||||
ng-class="{ loading: plot.isRequestPending() }">
|
||||
<!-- Out-of-bounds data indicators -->
|
||||
<!-- ng-show is temporarily hard-coded in next element -->
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-up"></div>
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-dwn"></div>
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in subplot.getDomainTicks()"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%', height: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)">
|
||||
</div>
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in subplot.getRangeTicks()"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%', width: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getRangeTicks().length - 1)">
|
||||
</div>
|
||||
<mct-chart draw="subplot.getDrawingObject()"
|
||||
ng-if="subplot.getTelemetryObjects().length > 0"
|
||||
ng-mousemove="subplot.hover($event)"
|
||||
mct-drag="subplot.continueDrag($event)"
|
||||
mct-drag-down="subplot.startDrag($event)"
|
||||
mct-drag-up="subplot.endDrag($event); plot.update()">
|
||||
</mct-chart>
|
||||
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
||||
<div class="l-local-controls gl-plot-local-controls t-plot-display-controls"
|
||||
ng-if="$first">
|
||||
<a class="s-btn"
|
||||
ng-click="plot.stepBackPanZoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Restore previous pan/zoom">
|
||||
<span class="ui-symbol icon"><</span>
|
||||
</a>
|
||||
<a class="s-btn"
|
||||
ng-click="plot.unzoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Reset pan/zoom">
|
||||
<span class="ui-symbol icon">I</span>
|
||||
</a>
|
||||
<div class="menu-element s-menu-btn menus-to-left"
|
||||
ng-if="plot.getModeOptions().length > 1"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||
<span class="ui-symbol icon type-icon">{{plot.getMode().glyph}}</span>
|
||||
<span>{{plot.getMode().name}}</span>
|
||||
<div class="menu" ng-show="toggle.isActive()">
|
||||
<ul>
|
||||
<li ng-repeat="option in plot.getModeOptions()">
|
||||
<a href="" ng-click="plot.setMode(option); toggle.setState(false)">
|
||||
<span class="ui-symbol type-icon icon">
|
||||
{{option.glyph}}
|
||||
</span>
|
||||
{{option.name}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div ng-repeat="tick in subplot.getRangeTicks()"
|
||||
class="gl-plot-tick gl-plot-y-tick-label"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-y-options gl-plot-local-controls"
|
||||
ng-if="axes[1].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[1].active"
|
||||
ng-options="option.name for option in axes[1].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="$last" class="gl-plot-axis-area gl-plot-x">
|
||||
<div ng-repeat="tick in subplot.getDomainTicks()"
|
||||
class="gl-plot-tick gl-plot-x-tick-label"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-label gl-plot-x-label">
|
||||
{{axes[0].active.name}}
|
||||
</div>
|
||||
<div class="gl-plot-x-options gl-plot-local-controls"
|
||||
ng-if="axes[0].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[0].active"
|
||||
ng-options="option.name for option in axes[0].options">
|
||||
</select>
|
||||
<div class="gl-plot-display-area"
|
||||
ng-mouseenter="subplot.isHovering(true);"
|
||||
ng-mouseleave="subplot.isHovering(false)"
|
||||
ng-class="{ loading: plot.isRequestPending() }">
|
||||
<!-- Out-of-bounds data indicators -->
|
||||
<!-- ng-show is temporarily hard-coded in next element -->
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-up"></div>
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-dwn"></div>
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in subplot.getDomainTicks()"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%', height: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)">
|
||||
</div>
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in subplot.getRangeTicks()"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%', width: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getRangeTicks().length - 1)">
|
||||
</div>
|
||||
<mct-chart draw="subplot.getDrawingObject()"
|
||||
ng-if="subplot.getTelemetryObjects().length > 0"
|
||||
ng-mousemove="subplot.hover($event)"
|
||||
mct-drag="subplot.continueDrag($event)"
|
||||
mct-drag-down="subplot.startDrag($event)"
|
||||
mct-drag-up="subplot.endDrag($event); plot.update()">
|
||||
</mct-chart>
|
||||
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
||||
<div class="l-local-controls gl-plot-local-controls t-plot-display-controls"
|
||||
ng-if="$first">
|
||||
<a class="s-button icon-arrow-left"
|
||||
ng-click="plot.stepBackPanZoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Restore previous pan/zoom">
|
||||
</a>
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="plot.unzoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Reset pan/zoom">
|
||||
</a>
|
||||
<div class="menu-element s-menu-button menus-to-left {{plot.getMode().cssclass}}"
|
||||
ng-if="plot.getModeOptions().length > 1"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||
<span>{{plot.getMode().name}}</span>
|
||||
<div class="menu" ng-show="toggle.isActive()">
|
||||
<ul>
|
||||
<li ng-repeat="option in plot.getModeOptions()"
|
||||
ng-click="plot.setMode(option); toggle.setState(false)"
|
||||
class="{{option.cssclass}}">
|
||||
{{option.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="$last" class="gl-plot-axis-area gl-plot-x">
|
||||
<div ng-repeat="tick in subplot.getDomainTicks()"
|
||||
class="gl-plot-tick gl-plot-x-tick-label"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-label gl-plot-x-label">
|
||||
{{axes[0].active.name}}
|
||||
</div>
|
||||
<div class="gl-plot-x-options gl-plot-local-controls"
|
||||
ng-if="axes[0].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[0].active"
|
||||
ng-options="option.name for option in axes[0].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
@@ -54,7 +54,8 @@ define(
|
||||
* @throws {Error} an error is thrown if WebGL is unavailable.
|
||||
*/
|
||||
function GLChart(canvas) {
|
||||
var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"),
|
||||
var gl = canvas.getContext("webgl", { preserveDrawingBuffer: true }) ||
|
||||
canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true }),
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
program,
|
||||
|
||||
@@ -63,6 +63,8 @@ define(
|
||||
*/
|
||||
function PlotController(
|
||||
$scope,
|
||||
$element,
|
||||
exportImageService,
|
||||
telemetryFormatter,
|
||||
telemetryHandler,
|
||||
throttle,
|
||||
@@ -246,6 +248,8 @@ define(
|
||||
});
|
||||
|
||||
self.pending = true;
|
||||
self.$element = $element;
|
||||
self.exportImageService = exportImageService;
|
||||
|
||||
// Initialize axes; will get repopulated when telemetry
|
||||
// metadata becomes available.
|
||||
@@ -313,7 +317,7 @@ define(
|
||||
|
||||
/**
|
||||
* Get the current mode that is applicable to this plot. This
|
||||
* will include key, name, and glyph fields.
|
||||
* will include key, name, and cssclass fields.
|
||||
*/
|
||||
PlotController.prototype.getMode = function () {
|
||||
return this.modeOptions.getMode();
|
||||
@@ -364,6 +368,28 @@ define(
|
||||
return this.pending;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the plot to PNG
|
||||
*/
|
||||
PlotController.prototype.exportPNG = function () {
|
||||
var self = this;
|
||||
self.hideExportButtons = true;
|
||||
self.exportImageService.exportPNG(self.$element[0], "plot.png").finally(function () {
|
||||
self.hideExportButtons = false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the plot to JPG
|
||||
*/
|
||||
PlotController.prototype.exportJPG = function () {
|
||||
var self = this;
|
||||
self.hideExportButtons = true;
|
||||
self.exportImageService.exportJPG(self.$element[0], "plot.jpg").finally(function () {
|
||||
self.hideExportButtons = false;
|
||||
});
|
||||
};
|
||||
|
||||
return PlotController;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -27,13 +27,13 @@ define(
|
||||
var STACKED = {
|
||||
key: "stacked",
|
||||
name: "Stacked",
|
||||
glyph: "m",
|
||||
cssclass: "icon-plot-stacked",
|
||||
Constructor: PlotStackMode
|
||||
},
|
||||
OVERLAID = {
|
||||
key: "overlaid",
|
||||
name: "Overlaid",
|
||||
glyph: "6",
|
||||
cssclass: "icon-plot-overlay",
|
||||
Constructor: PlotOverlayMode
|
||||
};
|
||||
|
||||
@@ -115,7 +115,7 @@ define(
|
||||
|
||||
/**
|
||||
* Get all mode options available for each plot. Each
|
||||
* mode contains a `name` and `glyph` field suitable
|
||||
* mode contains a `name` and `cssclass` field suitable
|
||||
* for display in a template.
|
||||
* @return {Array} the available modes
|
||||
*/
|
||||
|
||||
156
platform/features/plot/src/services/ExportImageService.js
Normal file
156
platform/features/plot/src/services/ExportImageService.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Module defining ExportImageService. Created by hudsonfoo on 09/02/16
|
||||
*/
|
||||
define(
|
||||
[
|
||||
"html2canvas",
|
||||
"saveAs"
|
||||
],
|
||||
function (
|
||||
html2canvas,
|
||||
saveAs
|
||||
) {
|
||||
var self = this;
|
||||
|
||||
/**
|
||||
* The export image service will export any HTML node to
|
||||
* JPG, or PNG.
|
||||
* @param {object} $q
|
||||
* @param {object} $timeout
|
||||
* @param {object} $log
|
||||
* @param {constant} EXPORT_IMAGE_TIMEOUT time in milliseconds before a timeout error is returned
|
||||
* @constructor
|
||||
*/
|
||||
function ExportImageService($q, $timeout, $log, EXPORT_IMAGE_TIMEOUT, injHtml2Canvas, injSaveAs, injFileReader) {
|
||||
self.$q = $q;
|
||||
self.$timeout = $timeout;
|
||||
self.$log = $log;
|
||||
self.EXPORT_IMAGE_TIMEOUT = EXPORT_IMAGE_TIMEOUT;
|
||||
self.html2canvas = injHtml2Canvas || html2canvas;
|
||||
self.saveAs = injSaveAs || saveAs;
|
||||
self.reader = injFileReader || new FileReader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an HTML element into a base64 encoded image
|
||||
* as a BLOB, PNG, or JPG.
|
||||
* @param {node} element that will be converted to an image
|
||||
* @param {string} type of image to convert the element to
|
||||
* @returns {promise}
|
||||
*/
|
||||
function renderElement(element, type) {
|
||||
var defer = self.$q.defer(),
|
||||
validTypes = ["png", "jpg", "jpeg"],
|
||||
renderTimeout;
|
||||
|
||||
if (validTypes.indexOf(type) === -1) {
|
||||
self.$log.error("Invalid type requested. Try: (" + validTypes.join(",") + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
renderTimeout = self.$timeout(function () {
|
||||
defer.reject("html2canvas timed out");
|
||||
self.$log.warn("html2canvas timed out");
|
||||
}, self.EXPORT_IMAGE_TIMEOUT);
|
||||
|
||||
try {
|
||||
self.html2canvas(element, {
|
||||
onrendered: function (canvas) {
|
||||
switch (type.toLowerCase()) {
|
||||
case "png":
|
||||
canvas.toBlob(defer.resolve, "image/png");
|
||||
break;
|
||||
|
||||
default:
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
canvas.toBlob(defer.resolve, "image/jpeg");
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
defer.reject(e);
|
||||
self.$log.warn("html2canvas failed with error: " + e);
|
||||
}
|
||||
|
||||
defer.promise.finally(renderTimeout.cancel);
|
||||
|
||||
return defer.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* canvas.toBlob() not supported in IE < 10, Opera, and Safari. This polyfill
|
||||
* implements the method in browsers that would not otherwise support it.
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
|
||||
*/
|
||||
function polyfillToBlob() {
|
||||
if (!HTMLCanvasElement.prototype.toBlob) {
|
||||
Object.defineProperty(HTMLCanvasElement.prototype, "toBlob", {
|
||||
value: function (callback, type, quality) {
|
||||
|
||||
var binStr = atob(this.toDataURL(type, quality).split(',')[1]),
|
||||
len = binStr.length,
|
||||
arr = new Uint8Array(len);
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
arr[i] = binStr.charCodeAt(i);
|
||||
}
|
||||
|
||||
callback(new Blob([arr], {type: type || "image/png"}));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a screenshot of a DOM node and exports to JPG.
|
||||
* @param {node} element to be exported
|
||||
* @param {string} filename the exported image
|
||||
* @returns {promise}
|
||||
*/
|
||||
ExportImageService.prototype.exportJPG = function (element, filename) {
|
||||
return renderElement(element, "jpeg").then(function (img) {
|
||||
self.saveAs(img, filename);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a screenshot of a DOM node and exports to PNG.
|
||||
* @param {node} element to be exported
|
||||
* @param {string} filename the exported image
|
||||
* @returns {promise}
|
||||
*/
|
||||
ExportImageService.prototype.exportPNG = function (element, filename) {
|
||||
return renderElement(element, "png").then(function (img) {
|
||||
self.saveAs(img, filename);
|
||||
});
|
||||
};
|
||||
|
||||
polyfillToBlob();
|
||||
|
||||
return ExportImageService;
|
||||
}
|
||||
);
|
||||
@@ -59,7 +59,7 @@ define(
|
||||
expect(mock2d.clearRect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("doees not construct if 2D is unavailable", function () {
|
||||
it("does not construct if 2D is unavailable", function () {
|
||||
mockCanvas.getContext.andReturn(undefined);
|
||||
expect(function () {
|
||||
return new Canvas2DChart(mockCanvas);
|
||||
|
||||
@@ -82,7 +82,7 @@ define(
|
||||
expect(mockGL.clear).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("doees not construct if WebGL is unavailable", function () {
|
||||
it("does not construct if WebGL is unavailable", function () {
|
||||
mockCanvas.getContext.andReturn(undefined);
|
||||
expect(function () {
|
||||
return new GLChart(mockCanvas);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/*global angular*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
@@ -29,6 +31,8 @@ define(
|
||||
|
||||
describe("The plot controller", function () {
|
||||
var mockScope,
|
||||
mockElement,
|
||||
mockExportImageService,
|
||||
mockFormatter,
|
||||
mockHandler,
|
||||
mockThrottle,
|
||||
@@ -65,6 +69,11 @@ define(
|
||||
"$scope",
|
||||
["$watch", "$on", "$emit"]
|
||||
);
|
||||
mockElement = angular.element('<div />');
|
||||
mockExportImageService = jasmine.createSpyObj(
|
||||
"ExportImageService",
|
||||
["exportJPG", "exportPNG"]
|
||||
);
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
"formatter",
|
||||
["formatDomainValue", "formatRangeValue"]
|
||||
@@ -107,6 +116,8 @@ define(
|
||||
|
||||
controller = new PlotController(
|
||||
mockScope,
|
||||
mockElement,
|
||||
mockExportImageService,
|
||||
mockFormatter,
|
||||
mockHandler,
|
||||
mockThrottle
|
||||
|
||||
120
platform/features/plot/test/services/ExportImageServiceSpec.js
Normal file
120
platform/features/plot/test/services/ExportImageServiceSpec.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* ExportImageServiceSpec. Created by hudsonfoo on 09/03/16.
|
||||
*/
|
||||
define(
|
||||
["../../src/services/ExportImageService"],
|
||||
function (ExportImageService) {
|
||||
var mockQ,
|
||||
mockDeferred,
|
||||
mockPromise,
|
||||
mockTimeout,
|
||||
mockLog,
|
||||
mockHtml2Canvas,
|
||||
mockCanvas,
|
||||
mockSaveAs,
|
||||
mockFileReader,
|
||||
mockExportTimeoutConstant,
|
||||
testElement,
|
||||
exportImageService;
|
||||
|
||||
describe("ExportImageService", function () {
|
||||
beforeEach(function () {
|
||||
mockDeferred = jasmine.createSpyObj(
|
||||
"deferred",
|
||||
["reject", "resolve"]
|
||||
);
|
||||
mockPromise = jasmine.createSpyObj(
|
||||
"promise",
|
||||
["then", "finally"]
|
||||
);
|
||||
mockPromise.then = function (callback) {
|
||||
callback();
|
||||
};
|
||||
mockQ = {
|
||||
"defer": function () {
|
||||
return {
|
||||
"resolve": mockDeferred.resolve,
|
||||
"reject": mockDeferred.reject,
|
||||
"promise": mockPromise
|
||||
};
|
||||
}
|
||||
};
|
||||
mockTimeout = function (fn, time) {
|
||||
return {
|
||||
"cancel": function () {}
|
||||
};
|
||||
};
|
||||
mockLog = jasmine.createSpyObj(
|
||||
"$log",
|
||||
["warn"]
|
||||
);
|
||||
mockHtml2Canvas = jasmine.createSpy("html2canvas").andCallFake(function (element, opts) {
|
||||
opts.onrendered(mockCanvas);
|
||||
});
|
||||
mockCanvas = jasmine.createSpyObj(
|
||||
"canvas",
|
||||
["toBlob"]
|
||||
);
|
||||
mockSaveAs = jasmine.createSpy("saveAs");
|
||||
mockFileReader = jasmine.createSpyObj(
|
||||
"FileReader",
|
||||
["readAsDataURL", "onloadend"]
|
||||
);
|
||||
mockExportTimeoutConstant = 0;
|
||||
testElement = {};
|
||||
|
||||
exportImageService = new ExportImageService(
|
||||
mockQ,
|
||||
mockTimeout,
|
||||
mockLog,
|
||||
mockExportTimeoutConstant,
|
||||
mockHtml2Canvas,
|
||||
mockSaveAs,
|
||||
mockFileReader
|
||||
);
|
||||
});
|
||||
|
||||
it("runs html2canvas and tries to save a png", function () {
|
||||
exportImageService.exportPNG(testElement, "plot.png");
|
||||
|
||||
expect(mockHtml2Canvas).toHaveBeenCalledWith(testElement, { onrendered: jasmine.any(Function) });
|
||||
expect(mockCanvas.toBlob).toHaveBeenCalledWith(mockDeferred.resolve, "image/png");
|
||||
expect(mockDeferred.reject).not.toHaveBeenCalled();
|
||||
expect(mockSaveAs).toHaveBeenCalled();
|
||||
expect(mockPromise.finally).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("runs html2canvas and tries to save a jpg", function () {
|
||||
exportImageService.exportJPG(testElement, "plot.png");
|
||||
|
||||
expect(mockHtml2Canvas).toHaveBeenCalledWith(testElement, { onrendered: jasmine.any(Function) });
|
||||
expect(mockCanvas.toBlob).toHaveBeenCalledWith(mockDeferred.resolve, "image/jpeg");
|
||||
expect(mockDeferred.reject).not.toHaveBeenCalled();
|
||||
expect(mockSaveAs).toHaveBeenCalled();
|
||||
expect(mockPromise.finally).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -36,7 +36,7 @@ define([
|
||||
{
|
||||
"key": "static.markup",
|
||||
"name": "Static Markup",
|
||||
"glyph": "p",
|
||||
"cssclass": "icon-pencil",
|
||||
"description": "Static markup sandbox",
|
||||
"features": [
|
||||
"creation"
|
||||
|
||||
@@ -60,7 +60,7 @@ define([
|
||||
{
|
||||
"key": "table",
|
||||
"name": "Historical Telemetry Table",
|
||||
"glyph": "\ue604",
|
||||
"cssclass": "icon-tabular",
|
||||
"description": "A static table of all values over time for all included telemetry elements. Rows are timestamped data values for each telemetry element; columns are data fields. The number of rows is based on the range of your query. New incoming data must be manually re-queried for.",
|
||||
"priority": 861,
|
||||
"features": "creation",
|
||||
@@ -83,7 +83,7 @@ define([
|
||||
{
|
||||
"key": "rttable",
|
||||
"name": "Real-time Telemetry Table",
|
||||
"glyph": "\ue620",
|
||||
"cssclass": "icon-tabular-realtime",
|
||||
"description": "A scrolling table of latest values for all included telemetry elements. Rows are timestamped data values for each telemetry element; columns are data fields. New incoming data is automatically added to the view.",
|
||||
"priority": 860,
|
||||
"features": "creation",
|
||||
@@ -127,7 +127,7 @@ define([
|
||||
{
|
||||
"name": "Historical Table",
|
||||
"key": "table",
|
||||
"glyph": "\ue604",
|
||||
"cssclass": "icon-tabular",
|
||||
"templateUrl": "templates/historical-table.html",
|
||||
"needs": [
|
||||
"telemetry"
|
||||
@@ -138,7 +138,7 @@ define([
|
||||
{
|
||||
"name": "Real-time Table",
|
||||
"key": "rt-table",
|
||||
"glyph": "\ue620",
|
||||
"cssclass": "icon-tabular-realtime",
|
||||
"templateUrl": "templates/rt-table.html",
|
||||
"needs": [
|
||||
"telemetry"
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
rows="rows"
|
||||
enableFilter="true"
|
||||
enableSort="true"
|
||||
class="tabular-holder t-exportable">
|
||||
class="tabular-holder has-control-bar">
|
||||
</mct-table>
|
||||
</div>
|
||||
@@ -1,8 +1,10 @@
|
||||
<a class="t-btn l-btn s-btn t-export"
|
||||
ng-click="exportAsCSV()"
|
||||
title="Export This View's Data">
|
||||
Export
|
||||
</a>
|
||||
<div class="l-control-bar">
|
||||
<a class="s-button t-export icon-download labeled"
|
||||
ng-click="exportAsCSV()"
|
||||
title="Export This View's Data">
|
||||
Export
|
||||
</a>
|
||||
</div>
|
||||
<div class="l-view-section scrolling" style="overflow: auto;" mct-resize="resize()">
|
||||
<table class="sizing-table">
|
||||
<tbody>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
rows="rows"
|
||||
enableFilter="true"
|
||||
enableSort="true"
|
||||
class="tabular-holder t-exportable"
|
||||
class="tabular-holder has-control-bar"
|
||||
auto-scroll="true">
|
||||
</mct-table>
|
||||
</div>
|
||||
@@ -25,6 +25,6 @@
|
||||
ng-model="configuration.table.columns"
|
||||
structure="columnsForm"
|
||||
name="columnsFormState"
|
||||
class="flex-elem l-flex-row no-validate no-margin">
|
||||
class="flex-elem l-flex-row no-margin">
|
||||
</mct-form>
|
||||
</div>
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
['zepto'],
|
||||
function ($) {
|
||||
|
||||
/**
|
||||
* A controller for the MCTTable directive. Populates scope with
|
||||
@@ -16,13 +16,13 @@ define(
|
||||
var self = this;
|
||||
|
||||
this.$scope = $scope;
|
||||
this.element = element;
|
||||
this.element = $(element[0]);
|
||||
this.$timeout = $timeout;
|
||||
this.maxDisplayRows = 50;
|
||||
|
||||
this.scrollable = element.find('div');
|
||||
this.thead = element.find('thead');
|
||||
this.tbody = element.find('tbody');
|
||||
this.scrollable = this.element.find('.l-view-section.scrolling').first();
|
||||
this.resultsHeader = this.element.find('.mct-table>thead').first();
|
||||
this.sizingTableBody = this.element.find('.sizing-table>tbody').first();
|
||||
this.$scope.sizingRow = {};
|
||||
|
||||
this.scrollable.on('scroll', this.onScroll.bind(this));
|
||||
@@ -261,8 +261,8 @@ define(
|
||||
* for individual rows.
|
||||
*/
|
||||
MCTTableController.prototype.setElementSizes = function () {
|
||||
var thead = this.thead,
|
||||
tbody = this.tbody,
|
||||
var thead = this.resultsHeader,
|
||||
tbody = this.sizingTableBody,
|
||||
firstRow = tbody.find('tr'),
|
||||
column = firstRow.find('td'),
|
||||
headerHeight = thead.prop('offsetHeight'),
|
||||
|
||||
@@ -22,9 +22,16 @@
|
||||
|
||||
define(
|
||||
[
|
||||
"zepto",
|
||||
"../../src/controllers/MCTTableController"
|
||||
],
|
||||
function (MCTTableController) {
|
||||
function ($, MCTTableController) {
|
||||
|
||||
var MOCK_ELEMENT_TEMPLATE =
|
||||
'<div><div class="l-view-section scrolling">' +
|
||||
'<table class="sizing-table"><tbody></tbody></table>' +
|
||||
'<table class="mct-table"><thead></thead></table>' +
|
||||
'</div></div>';
|
||||
|
||||
describe('The MCTTable Controller', function () {
|
||||
|
||||
@@ -55,19 +62,7 @@ define(
|
||||
watches[event] = callback;
|
||||
});
|
||||
|
||||
mockElement = jasmine.createSpyObj('element', [
|
||||
'find',
|
||||
'prop',
|
||||
'on'
|
||||
]);
|
||||
mockElement.find.andReturn(mockElement);
|
||||
mockElement.prop.andReturn(0);
|
||||
mockElement[0] = {
|
||||
scrollTop: 0,
|
||||
scrollHeight: 500,
|
||||
offsetHeight: 1000
|
||||
};
|
||||
|
||||
mockElement = $(MOCK_ELEMENT_TEMPLATE);
|
||||
mockExportService = jasmine.createSpyObj('exportService', [
|
||||
'exportCSV'
|
||||
]);
|
||||
|
||||
@@ -151,7 +151,7 @@ define([
|
||||
{
|
||||
"key": "timeline",
|
||||
"name": "Timeline",
|
||||
"glyph": "\u0053",
|
||||
"cssclass": "icon-timeline",
|
||||
"description": "A time-oriented container that lets you enclose and organize other Timelines and Activities. The Timeline view provides both tabular and Gantt views as well as resource utilization graphing of Activities.",
|
||||
"priority": 502,
|
||||
"features": [
|
||||
@@ -182,6 +182,16 @@ define([
|
||||
"capacity"
|
||||
],
|
||||
"pattern": "^-?\\d+(\\.\\d*)?$"
|
||||
},
|
||||
{
|
||||
"name": "Battery starting SOC (%)",
|
||||
"control": "textfield",
|
||||
"required": false,
|
||||
"conversion": "number",
|
||||
"property": [
|
||||
"startingSOC"
|
||||
],
|
||||
"pattern": "^([0-9](\\.\\d*)?|[1-9][0-9](\\.\\d*)?|100)%?$"
|
||||
}
|
||||
],
|
||||
"model": {
|
||||
@@ -194,7 +204,7 @@ define([
|
||||
{
|
||||
"key": "activity",
|
||||
"name": "Activity",
|
||||
"glyph": "\u0061",
|
||||
"cssclass": "icon-activity",
|
||||
"features": [
|
||||
"creation"
|
||||
],
|
||||
@@ -240,7 +250,7 @@ define([
|
||||
{
|
||||
"key": "mode",
|
||||
"name": "Activity Mode",
|
||||
"glyph": "\u0041",
|
||||
"cssclass": "icon-activity-mode",
|
||||
"features": [
|
||||
"creation"
|
||||
],
|
||||
@@ -280,7 +290,7 @@ define([
|
||||
{
|
||||
"key": "values",
|
||||
"name": "Values",
|
||||
"glyph": "\u0041",
|
||||
"cssclass": "icon-activity-mode",
|
||||
"template": valuesTemplate,
|
||||
"type": "mode",
|
||||
"uses": [
|
||||
@@ -291,7 +301,7 @@ define([
|
||||
{
|
||||
"key": "timeline",
|
||||
"name": "Timeline",
|
||||
"glyph": "\u0053",
|
||||
"cssclass": "icon-timeline",
|
||||
"type": "timeline",
|
||||
"description": "A time-oriented container that lets you enclose and organize other Timelines and Activities. The Timeline view provides both tabular and Gantt views as well as resource utilization graphing of Activities.",
|
||||
"template": timelineTemplate,
|
||||
@@ -302,18 +312,17 @@ define([
|
||||
"items": [
|
||||
{
|
||||
"method": "add",
|
||||
"glyph": "\u002b",
|
||||
"control": "menu-button",
|
||||
"text": "Add",
|
||||
"options": [
|
||||
{
|
||||
"name": "Timeline",
|
||||
"glyph": "\u0053",
|
||||
"cssclass": "icon-timeline",
|
||||
"key": "timeline"
|
||||
},
|
||||
{
|
||||
"name": "Activity",
|
||||
"glyph": "\u0061",
|
||||
"cssclass": "icon-activity",
|
||||
"key": "activity"
|
||||
}
|
||||
]
|
||||
@@ -323,13 +332,13 @@ define([
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"glyph": "\u00e9",
|
||||
"cssclass": "icon-plot-resource",
|
||||
"description": "Graph Resource Utilization",
|
||||
"control": "button",
|
||||
"method": "toggleGraph"
|
||||
},
|
||||
{
|
||||
"glyph": "\u0041",
|
||||
"cssclass": "icon-activity-mode",
|
||||
"control": "dialog-button",
|
||||
"description": "Apply Activity Modes...",
|
||||
"title": "Apply Activity Modes",
|
||||
@@ -342,7 +351,7 @@ define([
|
||||
"property": "modes"
|
||||
},
|
||||
{
|
||||
"glyph": "\u00e8",
|
||||
"cssclass": "icon-chain-links",
|
||||
"description": "Edit Activity Link",
|
||||
"title": "Activity Link",
|
||||
"control": "dialog-button",
|
||||
@@ -355,7 +364,7 @@ define([
|
||||
"property": "link"
|
||||
},
|
||||
{
|
||||
"glyph": "\u0047",
|
||||
"cssclass": "icon-gear",
|
||||
"description": "Edit Properties...",
|
||||
"control": "button",
|
||||
"method": "properties"
|
||||
@@ -368,7 +377,7 @@ define([
|
||||
"method": "remove",
|
||||
"description": "Remove Item",
|
||||
"control": "button",
|
||||
"glyph": "\u005a"
|
||||
"cssclass": "icon-trash"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@
|
||||
.l-pane-l {
|
||||
// Left pane of the tabular area
|
||||
.l-cols {
|
||||
.s-label .ui-symbol.icon {
|
||||
color: $colorGanttBarTabularFgIcon;
|
||||
.t-object-label .t-item-icon {
|
||||
color: pullForward($colorGanttBarBg, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@
|
||||
}
|
||||
|
||||
.s-ticks {
|
||||
@include bgTicks($timelineColorAlt1);
|
||||
@include bgTicks( $timelineColorAlt1);
|
||||
}
|
||||
.s-hover-btns-holder {
|
||||
$bg: $timelineHeaderColorBg;
|
||||
@@ -112,7 +112,7 @@
|
||||
$l: 5%;
|
||||
@include user-select(none);
|
||||
@include background-image(linear-gradient(-90deg, rgba($bg, $bga), rgba($bg, $bga) 70%, rgba($bg, 0) 100%));
|
||||
.s-btn {
|
||||
.s-button {
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
.icon {
|
||||
|
||||
@@ -241,8 +241,8 @@
|
||||
@include trans-prop-nice-fade(500ms);
|
||||
opacity: 0;
|
||||
height: $timelineTopPaneHeaderH;
|
||||
width: 100px; left: auto;
|
||||
padding: $interiorMargin;
|
||||
left: auto;
|
||||
padding: $interiorMargin $interiorMargin $interiorMargin $interiorMargin * 10;
|
||||
text-align: right;
|
||||
z-index: 10;
|
||||
}
|
||||
@@ -266,9 +266,6 @@
|
||||
width: $timelineColIconW;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
.ui-symbol {
|
||||
color: $colorKey;
|
||||
}
|
||||
}
|
||||
|
||||
&.l-plot-resource {
|
||||
|
||||
@@ -29,9 +29,7 @@
|
||||
} : {}">
|
||||
|
||||
<div class="bar">
|
||||
<span class="s-activity-type ui-symbol">
|
||||
{{type.getGlyph()}}
|
||||
</span>
|
||||
<span class="s-activity-type {{type.getCssClass()}}"></span>
|
||||
<span class="s-title">
|
||||
{{model.name}}
|
||||
</span>
|
||||
|
||||
@@ -31,20 +31,17 @@
|
||||
<span class="l-col l-col-icon l-plot-resource"
|
||||
ng-click="ngModel.toggleGraph(); parameters.commit()"
|
||||
title="Click to enable or disable inclusion in Resource Graphing">
|
||||
<span class="ui-symbol"
|
||||
ng-show="ngModel.graph()"
|
||||
>
|
||||
é
|
||||
<span class="icon-plot-resource s-toggle-icon"
|
||||
ng-class="{ active: ngModel.graph() }">
|
||||
</span>
|
||||
</span>
|
||||
<span class="l-col l-col-icon l-link">
|
||||
<a class="ui-symbol"
|
||||
<a class="icon-chain-links"
|
||||
target="_blank"
|
||||
ng-href="{{ngModel.link()}}"
|
||||
ng-if="ngModel.link().length > 0"
|
||||
title="{{ngModel.link()}}"
|
||||
>
|
||||
è
|
||||
</a>
|
||||
</span>
|
||||
<span class="l-col l-title"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="s-timeline l-timeline-holder split-layout vertical"
|
||||
<div class="s-timeline l-timeline-holder split-layout vertical splitter-sm"
|
||||
ng-controller="TimelineController as timelineController">
|
||||
|
||||
<mct-split-pane anchor="left" class="abs" position="pane.x">
|
||||
@@ -27,7 +27,7 @@
|
||||
<mct-split-pane anchor="bottom"
|
||||
position="pane.y"
|
||||
class="abs horizontal split-pane-component l-timeline-pane l-pane-l t-pane-v">
|
||||
<!-- TOP PANE TABULAR AREA. ADD CLASS "hidden" FOR INTERIM NO-TABULAR DELIVERY -->
|
||||
<!-- TOP PANE TABULAR AREA -->
|
||||
<div class="split-pane-component s-timeline-tabular l-timeline-pane t-pane-h l-pane-top">
|
||||
<!-- TABULAR LEFT FIXED AREA -->
|
||||
<div class="t-pane-v l-pane-l l-tabular-l"
|
||||
@@ -35,8 +35,8 @@
|
||||
|
||||
<div class="t-header l-header s-header">
|
||||
<div class="l-cols">
|
||||
<span title="Resource Graphing: click a row to toggle" class="l-col l-col-icon l-plot-resource ui-symbol">é</span>
|
||||
<span title="Activity Links" class="l-col l-col-icon l-col-link ui-symbol">è</span>
|
||||
<span title="Resource Graphing: click a row to toggle" class="l-col l-col-icon l-plot-resource icon-plot-resource"></span>
|
||||
<span title="Activity Links" class="l-col l-col-icon l-col-link icon-chain-links"></span>
|
||||
<span class="l-col l-title">Title</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,26 +102,23 @@
|
||||
|
||||
<!-- TOP PANE GANTT BARS -->
|
||||
<div class="split-pane-component l-timeline-pane t-pane-h l-pane-top t-timeline-gantt l-timeline-gantt s-timeline-gantt">
|
||||
<div class="l-hover-btns-holder s-hover-btns-holder t-btns-zoom">
|
||||
<a class="t-btn l-btn s-btn"
|
||||
<div class="l-hover-btns-holder s-hover-btns-holder">
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="zoomController.fit()"
|
||||
ng-show="true"
|
||||
title="Zoom to fit">
|
||||
<span class="ui-symbol icon zoom-in">I</span>
|
||||
</a>
|
||||
|
||||
<a class="t-btn l-btn s-btn"
|
||||
<a class="s-button icon-magnify-in"
|
||||
ng-click="zoomController.zoom(-1)"
|
||||
ng-show="true"
|
||||
title="Zoom in">
|
||||
<span class="ui-symbol icon zoom-in">X</span>
|
||||
</a>
|
||||
|
||||
<a class="t-btn l-btn s-btn"
|
||||
<a class="s-button icon-magnify-out"
|
||||
ng-click="zoomController.zoom(1)"
|
||||
ng-show="true"
|
||||
title="Zoom out">
|
||||
<span class="ui-symbol icon zoom-out">Y</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ define(
|
||||
// Build graphs for this group of utilizations
|
||||
function buildGraphs(utilizations) {
|
||||
var utilizationMap = {},
|
||||
result = {};
|
||||
result = {},
|
||||
startingSOC;
|
||||
|
||||
// Bucket utilizations by type
|
||||
utilizations.forEach(function (u) {
|
||||
@@ -55,12 +56,14 @@ define(
|
||||
if (domainObject.getModel().type === 'timeline' &&
|
||||
result.power &&
|
||||
domainObject.getModel().capacity > 0) {
|
||||
startingSOC = isNaN(parseFloat(domainObject.getModel().startingSOC)) ?
|
||||
100 : parseFloat(domainObject.getModel().startingSOC);
|
||||
|
||||
result.battery = new CumulativeGraph(
|
||||
result.power,
|
||||
0,
|
||||
domainObject.getModel().capacity, // Watts
|
||||
domainObject.getModel().capacity,
|
||||
(startingSOC / 100) * domainObject.getModel().capacity,
|
||||
1 / 3600000 // millis-to-hour (since units are watt-hours)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ define(
|
||||
|
||||
/**
|
||||
* Control for Gantt bars in a timeline view.
|
||||
* Primarily reesponsible for supporting the positioning of Gantt
|
||||
* Primarily responsible for supporting the positioning of Gantt
|
||||
* bars; particularly, this ensures that the left and right edges
|
||||
* never go to far off screen, because in some environments this
|
||||
* will effect rendering performance without visible results.
|
||||
|
||||
@@ -28,7 +28,7 @@ define(
|
||||
|
||||
/**
|
||||
* Provides labels for the tick mark area of a timeline view.
|
||||
* Since the tick mark regin is potentially extremeley large,
|
||||
* Since the tick mark region is potentially extremely large,
|
||||
* only the subset of ticks which will actually be shown in
|
||||
* view are provided.
|
||||
* @constructor
|
||||
|
||||
@@ -140,7 +140,7 @@ define(
|
||||
var timespan = timespans[toId(id)];
|
||||
// Use as setter if argument is present
|
||||
if ((typeof value === 'number') && timespan) {
|
||||
// Set the end (ensuring it doesn't preceed start)
|
||||
// Set the end (ensuring it doesn't precede start)
|
||||
timespan.setEnd(
|
||||
Math.max(value, timespan.getStart())
|
||||
);
|
||||
|
||||
@@ -41,7 +41,7 @@ define(
|
||||
ids,
|
||||
candidates;
|
||||
|
||||
// Filter an id for inclustion
|
||||
// Filter an id for inclusion
|
||||
function include(id) {
|
||||
return id !== exclude;
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ define(
|
||||
it("provides a battery graph for timelines with capacity", function () {
|
||||
var mockCallback = jasmine.createSpy('callback');
|
||||
testModel.capacity = 1000;
|
||||
testModel.startingSOC = 100;
|
||||
testModel.type = "timeline";
|
||||
mockDomainObject.useCapability.andReturn(asPromise([
|
||||
{ key: "power", start: 0, end: 15 }
|
||||
|
||||
Reference in New Issue
Block a user