This commit is contained in:
steven mirecki 2016-09-16 17:16:35 -04:00
commit f9597fe799
27 changed files with 672 additions and 220 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
*.gzip *.gzip
*.tgz *.tgz
*.DS_Store *.DS_Store
*.swp
# Compiled CSS, unless directly added # Compiled CSS, unless directly added
*.sass-cache *.sass-cache

View File

@ -17,6 +17,7 @@ Differences between the two APIs include a move away from a declarative system o
## Building and Running Open MCT Locally ## Building and Running Open MCT Locally
Building and running Open MCT in your local dev environment is very easy. Be sure you have [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/) installed, then follow the directions below. Need additional information? Check out the [Getting Started](https://nasa.github.io/openmct/getting-started/) page on our website. Building and running Open MCT in your local dev environment is very easy. Be sure you have [Git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/) installed, then follow the directions below. Need additional information? Check out the [Getting Started](https://nasa.github.io/openmct/getting-started/) page on our website.
(These instructions assume you are installing as a non-root user; developers have [reported issues](https://github.com/nasa/openmct/issues/1151) running these steps with root privileges.)
1. Clone the source code 1. Clone the source code

10
app.js
View File

@ -67,14 +67,10 @@
}); });
app.use('/proxyUrl', function proxyRequest(req, res, next) { app.use('/proxyUrl', function proxyRequest(req, res, next) {
var targetUrl = req.query.url; console.log('Proxying request to: ', req.query.url);
var queryParameters = req.query;
console.log('Proxying request to: ', targetUrl);
delete queryParameters['url'];
req.pipe(request({ req.pipe(request({
url: targetUrl, url: req.query.url,
strictSSL: false, strictSSL: false
qs: queryParameters
}).on('error', next)).pipe(res); }).on('error', next)).pipe(res);
}); });

View File

@ -13,11 +13,13 @@
"moment-duration-format": "^1.3.0", "moment-duration-format": "^1.3.0",
"requirejs": "~2.1.22", "requirejs": "~2.1.22",
"text": "requirejs-text#^2.0.14", "text": "requirejs-text#^2.0.14",
"es6-promise": "^3.0.2", "es6-promise": "^3.3.0",
"screenfull": "^3.0.0", "screenfull": "^3.0.0",
"node-uuid": "^1.4.7", "node-uuid": "^1.4.7",
"comma-separated-values": "^3.6.4", "comma-separated-values": "^3.6.4",
"FileSaver.js": "^0.0.2", "FileSaver.js": "^0.0.2",
"zepto": "^1.1.6" "zepto": "^1.1.6",
"html2canvas": "^0.4.1",
"jspdf": "^1.2.61"
} }
} }

View File

@ -933,7 +933,7 @@ Note that `templateUrl` is not supported for `containers`.
Controls provide options for the `mct-control` directive. 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. * `textfield`: An area to enter plain text.
* `select`: A drop-down list of options. * `select`: A drop-down list of options.
@ -942,6 +942,12 @@ Six standard control types are included in the forms bundle:
* `button`: A button. * `button`: A button.
* `datetime`: An input for UTC date/time entry; gives result as a UNIX * `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 New controls may be added as extensions of the controls category. Extensions of
this category have two properties: this category have two properties:

View File

@ -103,11 +103,6 @@ gulp.task('stylesheets', function () {
.pipe(gulp.dest(__dirname)); .pipe(gulp.dest(__dirname));
}); });
gulp.task('nsp', function (done) {
var nsp = require('gulp-nsp');
nsp({package: __dirname + '/package.json'}, done);
});
gulp.task('lint', function () { gulp.task('lint', function () {
var nonspecs = paths.specs.map(function (glob) { var nonspecs = paths.specs.map(function (glob) {
return "!" + glob; return "!" + glob;
@ -157,6 +152,6 @@ gulp.task('develop', ['serve', 'stylesheets', 'watch']);
gulp.task('install', [ 'static', 'scripts' ]); gulp.task('install', [ 'static', 'scripts' ]);
gulp.task('verify', [ 'lint', 'test', 'checkstyle', 'nsp' ]); gulp.task('verify', [ 'lint', 'test', 'checkstyle' ]);
gulp.task('build', [ 'verify', 'install' ]); gulp.task('build', [ 'verify', 'install' ]);

View File

@ -19,16 +19,15 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<!DOCTYPE html> <!doctype html>
<html> <html lang="en">
<head lang="en"> <head>
<meta charset="UTF-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title></title> <title></title>
<script type="text/javascript" <script src="bower_components/requirejs/require.js">
src="bower_components/requirejs/require.js">
</script> </script>
<script type="text/javascript"> <script>
require(['main'], function (mct) { require(['main'], function (mct) {
require([ require([
'./example/imagery/bundle', './example/imagery/bundle',

10
main.js
View File

@ -27,7 +27,9 @@ requirejs.config({
"angular": "bower_components/angular/angular.min", "angular": "bower_components/angular/angular.min",
"angular-route": "bower_components/angular-route/angular-route.min", "angular-route": "bower_components/angular-route/angular-route.min",
"csv": "bower_components/comma-separated-values/csv.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": "bower_components/moment/moment",
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format", "moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
"saveAs": "bower_components/FileSaver.js/FileSaver.min", "saveAs": "bower_components/FileSaver.js/FileSaver.min",
@ -43,6 +45,12 @@ requirejs.config({
"angular-route": { "angular-route": {
"deps": ["angular"] "deps": ["angular"]
}, },
"html2canvas": {
"exports": "html2canvas"
},
"jsPDF": {
"exports": "jsPDF"
},
"moment-duration-format": { "moment-duration-format": {
"deps": ["moment"] "deps": ["moment"]
}, },

View File

@ -15,7 +15,6 @@
"gulp-jscs": "^3.0.2", "gulp-jscs": "^3.0.2",
"gulp-jshint": "^2.0.0", "gulp-jshint": "^2.0.0",
"gulp-jshint-html-reporter": "^0.1.3", "gulp-jshint-html-reporter": "^0.1.3",
"gulp-nsp": "^2.4.2",
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-replace-task": "^0.11.0", "gulp-replace-task": "^0.11.0",
"gulp-requirejs-optimize": "^0.3.1", "gulp-requirejs-optimize": "^0.3.1",
@ -40,7 +39,7 @@
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"moment": "^2.11.1", "moment": "^2.11.1",
"node-bourbon": "^4.2.3", "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", "requirejs": "2.1.x",
"split": "^1.0.0" "split": "^1.0.0"
}, },

View File

@ -387,7 +387,7 @@ define([
"constants": [ "constants": [
{ {
"key": "editModeBlacklist", "key": "editModeBlacklist",
"value": ["copy", "follow", "window", "link", "locate"] "value": ["copy", "follow", "link", "locate"]
}, },
{ {
"key": "nonEditContextBlacklist", "key": "nonEditContextBlacklist",

View File

@ -126,6 +126,7 @@ $menuLineH: 1.5rem;
$menuLineHPx: 24px; $menuLineHPx: 24px;
$btnStdH: 25px; $btnStdH: 25px;
$btnToolbarH: $btnStdH; $btnToolbarH: $btnStdH;
$controlBarH: $btnStdH;
$btnFrameH: 16px; $btnFrameH: 16px;
/************************** PATHS */ /************************** PATHS */

View File

@ -1,8 +1,11 @@
/* Styles for sub-dividing views generically */ /* 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 { .l-view-section {
@include absPosDefault(0); @include absPosDefault(0);
font-size: 0.8rem;
h2 { h2 {
color: #fff; color: #fff;
margin-bottom: $interiorMargin; margin-bottom: $interiorMargin;
@ -16,3 +19,35 @@
display: 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;
}
}
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -25,6 +25,7 @@ define([
"./src/PlotController", "./src/PlotController",
"./src/policies/PlotViewPolicy", "./src/policies/PlotViewPolicy",
"./src/PlotOptionsController", "./src/PlotOptionsController",
"./src/services/ExportImageService",
"text!./res/templates/plot.html", "text!./res/templates/plot.html",
"text!./res/templates/plot-options-browse.html", "text!./res/templates/plot-options-browse.html",
'legacyRegistry' 'legacyRegistry'
@ -33,6 +34,7 @@ define([
PlotController, PlotController,
PlotViewPolicy, PlotViewPolicy,
PlotOptionsController, PlotOptionsController,
exportImageService,
plotTemplate, plotTemplate,
plotOptionsBrowseTemplate, plotOptionsBrowseTemplate,
legacyRegistry legacyRegistry
@ -70,6 +72,8 @@ define([
"implementation": PlotController, "implementation": PlotController,
"depends": [ "depends": [
"$scope", "$scope",
"$element",
"exportImageService",
"telemetryFormatter", "telemetryFormatter",
"telemetryHandler", "telemetryHandler",
"throttle", "throttle",
@ -84,12 +88,30 @@ define([
] ]
} }
], ],
"services": [
{
"key": "exportImageService",
"implementation": exportImageService,
"depends": [
"$q",
"$timeout",
"$log",
"EXPORT_IMAGE_TIMEOUT"
]
}
],
"constants": [ "constants": [
{ {
"key": "PLOT_FIXED_DURATION", "key": "PLOT_FIXED_DURATION",
"value": 900000, "value": 900000,
"priority": "fallback", "priority": "fallback",
"comment": "Fifteen minutes." "comment": "Fifteen minutes."
},
{
"key": "EXPORT_IMAGE_TIMEOUT",
"value": 500,
"priority": "fallback"
} }
], ],
"policies": [ "policies": [
@ -103,6 +125,38 @@ define([
"key": "plot-options-browse", "key": "plot-options-browse",
"template": plotOptionsBrowseTemplate "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"
}
] ]
} }
}); });

View File

@ -20,7 +20,27 @@
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<span ng-controller="PlotController as plot" <span ng-controller="PlotController as plot"
class="abs holder holder-plot"> 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" <div class="gl-plot"
ng-style="{ height: 100 / plot.getSubPlots().length + '%'}" ng-style="{ height: 100 / plot.getSubPlots().length + '%'}"
ng-repeat="subplot in plot.getSubPlots()"> ng-repeat="subplot in plot.getSubPlots()">
@ -136,4 +156,5 @@
</div> </div>
</div> </div>
</div>
</span> </span>

View File

@ -54,7 +54,8 @@ define(
* @throws {Error} an error is thrown if WebGL is unavailable. * @throws {Error} an error is thrown if WebGL is unavailable.
*/ */
function GLChart(canvas) { 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, vertexShader,
fragmentShader, fragmentShader,
program, program,

View File

@ -63,6 +63,8 @@ define(
*/ */
function PlotController( function PlotController(
$scope, $scope,
$element,
exportImageService,
telemetryFormatter, telemetryFormatter,
telemetryHandler, telemetryHandler,
throttle, throttle,
@ -246,6 +248,8 @@ define(
}); });
self.pending = true; self.pending = true;
self.$element = $element;
self.exportImageService = exportImageService;
// Initialize axes; will get repopulated when telemetry // Initialize axes; will get repopulated when telemetry
// metadata becomes available. // metadata becomes available.
@ -364,6 +368,39 @@ define(
return this.pending; 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; return PlotController;
} }
); );

View 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;
}
);

View File

@ -1,3 +1,5 @@
/*global angular*/
/***************************************************************************** /*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government * Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space * as represented by the Administrator of the National Aeronautics and Space
@ -29,6 +31,8 @@ define(
describe("The plot controller", function () { describe("The plot controller", function () {
var mockScope, var mockScope,
mockElement,
mockExportImageService,
mockFormatter, mockFormatter,
mockHandler, mockHandler,
mockThrottle, mockThrottle,
@ -65,6 +69,11 @@ define(
"$scope", "$scope",
["$watch", "$on", "$emit"] ["$watch", "$on", "$emit"]
); );
mockElement = angular.element('<div />');
mockExportImageService = jasmine.createSpyObj(
"ExportImageService",
["exportJPG", "exportPNG", "exportPDF"]
);
mockFormatter = jasmine.createSpyObj( mockFormatter = jasmine.createSpyObj(
"formatter", "formatter",
["formatDomainValue", "formatRangeValue"] ["formatDomainValue", "formatRangeValue"]
@ -107,6 +116,8 @@ define(
controller = new PlotController( controller = new PlotController(
mockScope, mockScope,
mockElement,
mockExportImageService,
mockFormatter, mockFormatter,
mockHandler, mockHandler,
mockThrottle mockThrottle

View 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();
});
});
}
);

View File

@ -4,6 +4,6 @@
rows="rows" rows="rows"
enableFilter="true" enableFilter="true"
enableSort="true" enableSort="true"
class="tabular-holder t-exportable"> class="tabular-holder has-control-bar">
</mct-table> </mct-table>
</div> </div>

View File

@ -1,8 +1,10 @@
<div class="l-control-bar">
<a class="s-button t-export icon-download labeled" <a class="s-button t-export icon-download labeled"
ng-click="exportAsCSV()" ng-click="exportAsCSV()"
title="Export This View's Data"> title="Export This View's Data">
Export Export
</a> </a>
</div>
<div class="l-view-section scrolling" style="overflow: auto;" mct-resize="resize()"> <div class="l-view-section scrolling" style="overflow: auto;" mct-resize="resize()">
<table class="sizing-table"> <table class="sizing-table">
<tbody> <tbody>

View File

@ -4,7 +4,7 @@
rows="rows" rows="rows"
enableFilter="true" enableFilter="true"
enableSort="true" enableSort="true"
class="tabular-holder t-exportable" class="tabular-holder has-control-bar"
auto-scroll="true"> auto-scroll="true">
</mct-table> </mct-table>
</div> </div>

View File

@ -53,7 +53,9 @@ requirejs.config({
"angular": "bower_components/angular/angular.min", "angular": "bower_components/angular/angular.min",
"angular-route": "bower_components/angular-route/angular-route.min", "angular-route": "bower_components/angular-route/angular-route.min",
"csv": "bower_components/comma-separated-values/csv.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": "bower_components/moment/moment",
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format", "moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
"saveAs": "bower_components/FileSaver.js/FileSaver.min", "saveAs": "bower_components/FileSaver.js/FileSaver.min",