mirror of
https://github.com/nasa/openmct.git
synced 2024-12-21 14:07:50 +00:00
Merge branch 'master' into mct588comm
This commit is contained in:
commit
d8dc3c8445
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
||||
*.gzip
|
||||
*.tgz
|
||||
*.DS_Store
|
||||
*.swp
|
||||
|
||||
# Compiled CSS, unless directly added
|
||||
*.sass-cache
|
||||
|
@ -13,11 +13,13 @@
|
||||
"moment-duration-format": "^1.3.0",
|
||||
"requirejs": "~2.1.22",
|
||||
"text": "requirejs-text#^2.0.14",
|
||||
"es6-promise": "^3.0.2",
|
||||
"es6-promise": "^3.3.0",
|
||||
"screenfull": "^3.0.0",
|
||||
"node-uuid": "^1.4.7",
|
||||
"comma-separated-values": "^3.6.4",
|
||||
"FileSaver.js": "^0.0.2",
|
||||
"zepto": "^1.1.6"
|
||||
"zepto": "^1.1.6",
|
||||
"html2canvas": "^0.4.1",
|
||||
"jspdf": "^1.2.61"
|
||||
}
|
||||
}
|
||||
|
@ -933,7 +933,7 @@ Note that `templateUrl` is not supported for `containers`.
|
||||
|
||||
Controls provide options for the `mct-control` directive.
|
||||
|
||||
Six standard control types are included in the forms bundle:
|
||||
Ten standard control types are included in the forms bundle:
|
||||
|
||||
* `textfield`: An area to enter plain text.
|
||||
* `select`: A drop-down list of options.
|
||||
@ -941,7 +941,13 @@ Six standard control types are included in the forms bundle:
|
||||
* `color`: A color picker.
|
||||
* `button`: A button.
|
||||
* `datetime`: An input for UTC date/time entry; gives result as a UNIX
|
||||
timestamp, in milliseconds since start of 1970, UTC.
|
||||
timestamp, in milliseconds since start of 1970, UTC.
|
||||
* `composite`: A control parenting an array of other controls.
|
||||
* `menu-button`: A drop-down list of items supporting custom behavior
|
||||
on click.
|
||||
* `dialog-button`: A button which opens a dialog allowing a single property
|
||||
to be edited.
|
||||
* `radio`: A radio button.
|
||||
|
||||
New controls may be added as extensions of the controls category. Extensions of
|
||||
this category have two properties:
|
||||
|
21
index.html
21
index.html
@ -19,16 +19,15 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<title></title>
|
||||
<script type="text/javascript"
|
||||
src="bower_components/requirejs/require.js">
|
||||
<script src="bower_components/requirejs/require.js">
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
require(['main'], function (mct) {
|
||||
require([
|
||||
'./example/imagery/bundle',
|
||||
@ -39,10 +38,10 @@
|
||||
</script>
|
||||
<link rel="stylesheet" href="platform/commonUI/general/res/css/startup-base.css">
|
||||
<link rel="stylesheet" href="platform/commonUI/general/res/css/openmct.css">
|
||||
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="shortcut icon" href="platform/commonUI/general/res/images/favicons/favicon.ico">
|
||||
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="shortcut icon" href="platform/commonUI/general/res/images/favicons/favicon.ico">
|
||||
</head>
|
||||
<body class="user-environ">
|
||||
<div class="l-splash-holder s-splash-holder">
|
||||
|
10
main.js
10
main.js
@ -27,7 +27,9 @@ requirejs.config({
|
||||
"angular": "bower_components/angular/angular.min",
|
||||
"angular-route": "bower_components/angular-route/angular-route.min",
|
||||
"csv": "bower_components/comma-separated-values/csv.min",
|
||||
"es6-promise": "bower_components/es6-promise/promise.min",
|
||||
"es6-promise": "bower_components/es6-promise/es6-promise.min",
|
||||
"html2canvas": "bower_components/html2canvas/build/html2canvas.min",
|
||||
"jsPDF": "bower_components/jspdf/dist/jspdf.min",
|
||||
"moment": "bower_components/moment/moment",
|
||||
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
||||
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
||||
@ -43,6 +45,12 @@ requirejs.config({
|
||||
"angular-route": {
|
||||
"deps": ["angular"]
|
||||
},
|
||||
"html2canvas": {
|
||||
"exports": "html2canvas"
|
||||
},
|
||||
"jsPDF": {
|
||||
"exports": "jsPDF"
|
||||
},
|
||||
"moment-duration-format": {
|
||||
"deps": ["moment"]
|
||||
},
|
||||
|
@ -40,7 +40,7 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"moment": "^2.11.1",
|
||||
"node-bourbon": "^4.2.3",
|
||||
"phantomjs-prebuilt": "^2.1.0",
|
||||
"phantomjs-prebuilt": "2.1.11 || >2.1.12 <3.0.0",
|
||||
"requirejs": "2.1.x",
|
||||
"split": "^1.0.0"
|
||||
},
|
||||
|
@ -387,7 +387,7 @@ define([
|
||||
"constants": [
|
||||
{
|
||||
"key": "editModeBlacklist",
|
||||
"value": ["copy", "follow", "window", "link", "locate"]
|
||||
"value": ["copy", "follow", "link", "locate"]
|
||||
},
|
||||
{
|
||||
"key": "nonEditContextBlacklist",
|
||||
|
@ -126,6 +126,7 @@ $menuLineH: 1.5rem;
|
||||
$menuLineHPx: 24px;
|
||||
$btnStdH: 25px;
|
||||
$btnToolbarH: $btnStdH;
|
||||
$controlBarH: $btnStdH;
|
||||
$btnFrameH: 16px;
|
||||
|
||||
/************************** PATHS */
|
||||
|
@ -1,8 +1,11 @@
|
||||
/* Styles for sub-dividing views generically */
|
||||
.l-control-bar {
|
||||
// Element that can be placed above l-view-section, holds controls, buttons, etc.
|
||||
height: $controlBarH;
|
||||
}
|
||||
|
||||
.l-view-section {
|
||||
@include absPosDefault(0);
|
||||
font-size: 0.8rem;
|
||||
h2 {
|
||||
color: #fff;
|
||||
margin-bottom: $interiorMargin;
|
||||
@ -15,4 +18,36 @@
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.has-control-bar {
|
||||
.l-view-section {
|
||||
top: $controlBarH + $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.child-frame {
|
||||
.has-control-bar {
|
||||
$btnExportH: $btnFrameH;
|
||||
.l-control-bar {
|
||||
@include trans-prop-nice(opacity, $dur: 50ms);
|
||||
opacity: 0;
|
||||
}
|
||||
.l-view-section {
|
||||
@include trans-prop-nice(top, $dur: 150ms, $delay: 50ms);
|
||||
top: 0;
|
||||
}
|
||||
&:hover {
|
||||
.l-control-bar {
|
||||
@include trans-prop-nice(opacity, 150ms, 100ms);
|
||||
opacity: 1;
|
||||
}
|
||||
.l-view-section {
|
||||
@include trans-prop-nice(top, $dur: 150ms);
|
||||
top: $btnExportH + $interiorMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -160,39 +160,3 @@ table {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************** SPECIFIC TABULAR VIEWS */
|
||||
.tabular-holder {
|
||||
&.t-exportable {
|
||||
$btnExportH: 25px;
|
||||
.l-view-section {
|
||||
top: $btnExportH + $interiorMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.child-frame {
|
||||
.tabular-holder {
|
||||
&.t-exportable {
|
||||
$btnExportH: $btnFrameH;
|
||||
.s-button.t-export {
|
||||
@include trans-prop-nice(opacity, $dur: 50ms);
|
||||
opacity: 0;
|
||||
}
|
||||
.l-view-section {
|
||||
@include trans-prop-nice(top, $dur: 150ms, $delay: 50ms);
|
||||
top: 0;
|
||||
}
|
||||
&:hover {
|
||||
.s-button.t-export {
|
||||
@include trans-prop-nice(opacity, 150ms, 100ms);
|
||||
opacity: 1;
|
||||
}
|
||||
.l-view-section {
|
||||
@include trans-prop-nice(top, $dur: 150ms);
|
||||
top: $btnExportH + $interiorMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
@ -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,38 @@ 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"
|
||||
},
|
||||
{
|
||||
"name": "jsPDF",
|
||||
"version": "1.2.61",
|
||||
"author": "James Hall",
|
||||
"description": "JavaScript HTML renderer",
|
||||
"website": "https://github.com/MrRio/jsPDF",
|
||||
"copyright": "Copyright © 2010-2016 James Hall",
|
||||
"license": "license-mit",
|
||||
"link": "https://github.com/MrRio/jsPDF/blob/master/MIT-LICENSE.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
@ -20,120 +20,141 @@
|
||||
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 icon-download labeled first"
|
||||
ng-click="plot.exportPDF()"
|
||||
title="Export This View's Data as PDF">
|
||||
PDF
|
||||
</a>
|
||||
<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-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 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.
|
||||
@ -364,6 +368,39 @@ define(
|
||||
return this.pending;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the plot to PDF
|
||||
*/
|
||||
PlotController.prototype.exportPDF = function () {
|
||||
var self = this;
|
||||
self.hideExportButtons = true;
|
||||
self.exportImageService.exportPDF(self.$element[0], "plot.pdf").finally(function () {
|
||||
self.hideExportButtons = false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
);
|
||||
|
176
platform/features/plot/src/services/ExportImageService.js
Normal file
176
platform/features/plot/src/services/ExportImageService.js
Normal file
@ -0,0 +1,176 @@
|
||||
/*****************************************************************************
|
||||
* 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",
|
||||
"jsPDF",
|
||||
"saveAs"
|
||||
],
|
||||
function (
|
||||
html2canvas,
|
||||
jsPDF,
|
||||
saveAs
|
||||
) {
|
||||
var self = this;
|
||||
|
||||
/**
|
||||
* The export image service will export any HTML node to
|
||||
* PDF, 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, injJsPDF, injSaveAs, injFileReader) {
|
||||
self.$q = $q;
|
||||
self.$timeout = $timeout;
|
||||
self.$log = $log;
|
||||
self.EXPORT_IMAGE_TIMEOUT = EXPORT_IMAGE_TIMEOUT;
|
||||
self.html2canvas = injHtml2Canvas || html2canvas;
|
||||
self.jsPDF = injJsPDF || jsPDF;
|
||||
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 PDF.
|
||||
* @param {node} element to be exported
|
||||
* @param {string} filename the exported image
|
||||
* @returns {promise}
|
||||
*/
|
||||
ExportImageService.prototype.exportPDF = function (element, filename) {
|
||||
return renderElement(element, "jpeg").then(function (img) {
|
||||
self.reader.readAsDataURL(img);
|
||||
self.reader.onloadend = function () {
|
||||
var pdf = new self.jsPDF("l", "px", [element.offsetHeight, element.offsetWidth]);
|
||||
pdf.addImage(self.reader.result, "JPEG", 0, 0, element.offsetWidth, element.offsetHeight);
|
||||
pdf.save(filename);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
);
|
@ -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", "exportPDF"]
|
||||
);
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
"formatter",
|
||||
["formatDomainValue", "formatRangeValue"]
|
||||
@ -107,6 +116,8 @@ define(
|
||||
|
||||
controller = new PlotController(
|
||||
mockScope,
|
||||
mockElement,
|
||||
mockExportImageService,
|
||||
mockFormatter,
|
||||
mockHandler,
|
||||
mockThrottle
|
||||
|
141
platform/features/plot/test/services/ExportImageServiceSpec.js
Normal file
141
platform/features/plot/test/services/ExportImageServiceSpec.js
Normal file
@ -0,0 +1,141 @@
|
||||
/*****************************************************************************
|
||||
* 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,
|
||||
mockJsPDF,
|
||||
mockJsPDFSave,
|
||||
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"]
|
||||
);
|
||||
mockJsPDFSave = jasmine.createSpy("jsPDFSave");
|
||||
mockJsPDF = function () {
|
||||
return {
|
||||
"addImage": function () {},
|
||||
"save": mockJsPDFSave
|
||||
};
|
||||
};
|
||||
mockSaveAs = jasmine.createSpy("saveAs");
|
||||
mockFileReader = jasmine.createSpyObj(
|
||||
"FileReader",
|
||||
["readAsDataURL", "onloadend"]
|
||||
);
|
||||
mockExportTimeoutConstant = 0;
|
||||
testElement = {};
|
||||
|
||||
exportImageService = new ExportImageService(
|
||||
mockQ,
|
||||
mockTimeout,
|
||||
mockLog,
|
||||
mockExportTimeoutConstant,
|
||||
mockHtml2Canvas,
|
||||
mockJsPDF,
|
||||
mockSaveAs,
|
||||
mockFileReader
|
||||
);
|
||||
});
|
||||
|
||||
it("runs html2canvas and tries to save a pdf", function () {
|
||||
exportImageService.exportPDF(testElement, "plot.pdf");
|
||||
mockFileReader.onloadend();
|
||||
|
||||
expect(mockHtml2Canvas).toHaveBeenCalledWith(testElement, { onrendered: jasmine.any(Function) });
|
||||
expect(mockCanvas.toBlob).toHaveBeenCalledWith(mockDeferred.resolve, "image/jpeg");
|
||||
expect(mockDeferred.reject).not.toHaveBeenCalled();
|
||||
expect(mockJsPDFSave).toHaveBeenCalled();
|
||||
expect(mockPromise.finally).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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="s-button t-export icon-download labeled"
|
||||
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>
|
@ -36,18 +36,18 @@
|
||||
ng-controller="ColorController as colors"
|
||||
ng-show="toggle.isActive()">
|
||||
<div
|
||||
class="l-palette-row l-option-row"
|
||||
ng-if="!structure.mandatory">
|
||||
class="l-palette-row l-option-row"
|
||||
ng-if="!structure.mandatory">
|
||||
<div class="l-palette-item s-palette-item {{ngModel[field] === 'transparent' ? 'icon-check' : '' }}"
|
||||
ng-click="ngModel[field] = 'transparent'">
|
||||
</div>
|
||||
<span class="l-palette-item-label">None</span>
|
||||
</div>
|
||||
<div
|
||||
class="l-palette-row"
|
||||
class="l-palette-row"
|
||||
ng-repeat="group in colors.groups()">
|
||||
<div class="l-palette-item s-palette-item {{ngModel[field] === color ? 'icon-check' : '' }}"
|
||||
ng-repeat="color in group"
|
||||
ng-repeat="color in group"
|
||||
ng-style="{ background: color }"
|
||||
ng-click="ngModel[field] = color">
|
||||
</div>
|
||||
|
@ -21,18 +21,18 @@
|
||||
-->
|
||||
<span ng-controller="CompositeController as compositeCtrl">
|
||||
<ng-form name="mctFormItem" ng-repeat="item in structure.items">
|
||||
<div class="l-composite-control l-{{item.control}} {{item.cssclass}}">
|
||||
<mct-control key="item.control"
|
||||
ng-model="ngModel[field]"
|
||||
ng-required="ngRequired || compositeCtrl.isNonEmpty(ngModel[field])"
|
||||
ng-pattern="ngPattern"
|
||||
options="item.options"
|
||||
structure="row"
|
||||
field="$index">
|
||||
</mct-control>
|
||||
<span class="composite-control-label">
|
||||
{{item.name}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="l-composite-control l-{{item.control}} {{item.cssclass}}">
|
||||
<mct-control key="item.control"
|
||||
ng-model="ngModel[field]"
|
||||
ng-required="ngRequired || compositeCtrl.isNonEmpty(ngModel[field])"
|
||||
ng-pattern="ngPattern"
|
||||
options="item.options"
|
||||
structure="row"
|
||||
field="$index">
|
||||
</mct-control>
|
||||
<span class="composite-control-label">
|
||||
{{item.name}}
|
||||
</span>
|
||||
</div>
|
||||
</ng-form>
|
||||
</span>
|
||||
|
@ -20,31 +20,31 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<form novalidate>
|
||||
<div class="tool-bar btn-bar contents abs">
|
||||
<span ng-repeat="section in structure.sections"
|
||||
class="l-control-group"
|
||||
ng-if="!section.hidden"
|
||||
title="{{section.description}}">
|
||||
<ng-form ng-repeat="item in section.items"
|
||||
ng-class="{ 'input-labeled': item.name }"
|
||||
ng-hide="item.hidden"
|
||||
class="inline"
|
||||
title="{{item.description}}"
|
||||
name="mctFormInner">
|
||||
<div class="tool-bar btn-bar contents abs">
|
||||
<span ng-repeat="section in structure.sections"
|
||||
class="l-control-group"
|
||||
ng-if="!section.hidden"
|
||||
title="{{section.description}}">
|
||||
<ng-form ng-repeat="item in section.items"
|
||||
ng-class="{ 'input-labeled': item.name }"
|
||||
ng-hide="item.hidden"
|
||||
class="inline"
|
||||
title="{{item.description}}"
|
||||
name="mctFormInner">
|
||||
|
||||
<label ng-if="item.name">
|
||||
{{item.name}}:
|
||||
</label>
|
||||
<mct-control key="item.control"
|
||||
ng-class="{ disabled: item.disabled }"
|
||||
ng-model="ngModel"
|
||||
ng-required="item.required"
|
||||
ng-pattern="getRegExp(item.pattern)"
|
||||
options="item.options"
|
||||
structure="item"
|
||||
field="item.key">
|
||||
</mct-control>
|
||||
</ng-form>
|
||||
</span>
|
||||
</div>
|
||||
<label ng-if="item.name">
|
||||
{{item.name}}:
|
||||
</label>
|
||||
<mct-control key="item.control"
|
||||
ng-class="{ disabled: item.disabled }"
|
||||
ng-model="ngModel"
|
||||
ng-required="item.required"
|
||||
ng-pattern="getRegExp(item.pattern)"
|
||||
options="item.options"
|
||||
structure="item"
|
||||
field="item.key">
|
||||
</mct-control>
|
||||
</ng-form>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
@ -53,7 +53,9 @@ requirejs.config({
|
||||
"angular": "bower_components/angular/angular.min",
|
||||
"angular-route": "bower_components/angular-route/angular-route.min",
|
||||
"csv": "bower_components/comma-separated-values/csv.min",
|
||||
"es6-promise": "bower_components/es6-promise/promise.min",
|
||||
"es6-promise": "bower_components/es6-promise/es6-promise.min",
|
||||
"html2canvas": "bower_components/html2canvas/build/html2canvas.min",
|
||||
"jsPDF": "bower_components/jspdf/dist/jspdf.min",
|
||||
"moment": "bower_components/moment/moment",
|
||||
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
||||
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
||||
|
Loading…
Reference in New Issue
Block a user