mirror of
https://github.com/nasa/openmct.git
synced 2025-06-13 12:48:14 +00:00
[Notebooks] Don't save images on the object (#3792)
* Create and store image data into new domain object of type 'notebookSnapshotImage' * Reduced thumbnail size to 30px * Image migration script for old notebooks. * Saves thumbnail image on notebook instead of new object.
This commit is contained in:
@ -41,10 +41,10 @@
|
|||||||
"jsdoc": "^3.3.2",
|
"jsdoc": "^3.3.2",
|
||||||
"karma": "5.1.1",
|
"karma": "5.1.1",
|
||||||
"karma-chrome-launcher": "3.1.0",
|
"karma-chrome-launcher": "3.1.0",
|
||||||
"karma-firefox-launcher": "1.3.0",
|
|
||||||
"karma-cli": "2.0.0",
|
"karma-cli": "2.0.0",
|
||||||
"karma-coverage": "2.0.3",
|
"karma-coverage": "2.0.3",
|
||||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||||
|
"karma-firefox-launcher": "1.3.0",
|
||||||
"karma-html-reporter": "0.2.7",
|
"karma-html-reporter": "0.2.7",
|
||||||
"karma-jasmine": "3.3.1",
|
"karma-jasmine": "3.3.1",
|
||||||
"karma-sourcemap-loader": "0.3.7",
|
"karma-sourcemap-loader": "0.3.7",
|
||||||
@ -60,7 +60,7 @@
|
|||||||
"moment-timezone": "0.5.28",
|
"moment-timezone": "0.5.28",
|
||||||
"node-bourbon": "^4.2.3",
|
"node-bourbon": "^4.2.3",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
"painterro": "^1.0.35",
|
"painterro": "^1.2.56",
|
||||||
"printj": "^1.2.1",
|
"printj": "^1.2.1",
|
||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
"request": "^2.69.0",
|
"request": "^2.69.0",
|
||||||
|
@ -48,12 +48,12 @@ define(
|
|||||||
* Converts an HTML element into a PNG or JPG Blob.
|
* Converts an HTML element into a PNG or JPG Blob.
|
||||||
* @private
|
* @private
|
||||||
* @param {node} element that will be converted to an image
|
* @param {node} element that will be converted to an image
|
||||||
* @param {string} type of image to convert the element to.
|
* @param {object} options Image options.
|
||||||
* @returns {promise}
|
* @returns {promise}
|
||||||
*/
|
*/
|
||||||
ExportImageService.prototype.renderElement = function (element, imageType, className) {
|
ExportImageService.prototype.renderElement = function (element, {imageType, className, thumbnailSize}) {
|
||||||
|
const self = this;
|
||||||
const dialogService = this.dialogService;
|
const dialogService = this.dialogService;
|
||||||
|
|
||||||
const dialog = dialogService.showBlockingMessage({
|
const dialog = dialogService.showBlockingMessage({
|
||||||
title: "Capturing...",
|
title: "Capturing...",
|
||||||
hint: "Capturing an image",
|
hint: "Capturing an image",
|
||||||
@ -90,7 +90,16 @@ define(
|
|||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
return canvas.toBlob(resolve, mimeType);
|
if (thumbnailSize) {
|
||||||
|
const thumbnail = self.getThumbnail(canvas, mimeType, thumbnailSize);
|
||||||
|
|
||||||
|
return canvas.toBlob(blob => resolve({
|
||||||
|
blob,
|
||||||
|
thumbnail
|
||||||
|
}), mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas.toBlob(blob => resolve({ blob }), mimeType);
|
||||||
});
|
});
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
console.log('error capturing image', error);
|
console.log('error capturing image', error);
|
||||||
@ -109,6 +118,17 @@ define(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ExportImageService.prototype.getThumbnail = function (canvas, mimeType, size) {
|
||||||
|
const thumbnailCanvas = document.createElement('canvas');
|
||||||
|
thumbnailCanvas.setAttribute('width', size.width);
|
||||||
|
thumbnailCanvas.setAttribute('height', size.height);
|
||||||
|
const ctx = thumbnailCanvas.getContext('2d');
|
||||||
|
ctx.globalCompositeOperation = "copy";
|
||||||
|
ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, size.width, size.height);
|
||||||
|
|
||||||
|
return thumbnailCanvas.toDataURL(mimeType);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a screenshot of a DOM node and exports to JPG.
|
* Takes a screenshot of a DOM node and exports to JPG.
|
||||||
* @param {node} element to be exported
|
* @param {node} element to be exported
|
||||||
@ -119,9 +139,13 @@ define(
|
|||||||
ExportImageService.prototype.exportJPG = function (element, filename, className) {
|
ExportImageService.prototype.exportJPG = function (element, filename, className) {
|
||||||
const processedFilename = replaceDotsWithUnderscores(filename);
|
const processedFilename = replaceDotsWithUnderscores(filename);
|
||||||
|
|
||||||
return this.renderElement(element, "jpg", className).then(function (img) {
|
return this.renderElement(element, {
|
||||||
saveAs(img, processedFilename);
|
imageType: 'jpg',
|
||||||
});
|
className
|
||||||
|
})
|
||||||
|
.then(function (img) {
|
||||||
|
saveAs(img.blob, processedFilename);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,9 +158,13 @@ define(
|
|||||||
ExportImageService.prototype.exportPNG = function (element, filename, className) {
|
ExportImageService.prototype.exportPNG = function (element, filename, className) {
|
||||||
const processedFilename = replaceDotsWithUnderscores(filename);
|
const processedFilename = replaceDotsWithUnderscores(filename);
|
||||||
|
|
||||||
return this.renderElement(element, "png", className).then(function (img) {
|
return this.renderElement(element, {
|
||||||
saveAs(img, processedFilename);
|
imageType: 'png',
|
||||||
});
|
className
|
||||||
|
})
|
||||||
|
.then(function (img) {
|
||||||
|
saveAs(img.blob, processedFilename);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,8 +174,12 @@ define(
|
|||||||
* @returns {promise}
|
* @returns {promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ExportImageService.prototype.exportPNGtoSRC = function (element, className) {
|
ExportImageService.prototype.exportPNGtoSRC = function (element, options) {
|
||||||
return this.renderElement(element, "png", className);
|
|
||||||
|
return this.renderElement(element, {
|
||||||
|
imageType: 'png',
|
||||||
|
...options
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function replaceDotsWithUnderscores(filename) {
|
function replaceDotsWithUnderscores(filename) {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
class="c-ne__embed__snap-thumb"
|
class="c-ne__embed__snap-thumb"
|
||||||
@click="openSnapshot()"
|
@click="openSnapshot()"
|
||||||
>
|
>
|
||||||
<img :src="embed.snapshot.src">
|
<img :src="thumbnailImage">
|
||||||
</div>
|
</div>
|
||||||
<div class="c-ne__embed__info">
|
<div class="c-ne__embed__info">
|
||||||
<div class="c-ne__embed__name">
|
<div class="c-ne__embed__name">
|
||||||
@ -25,11 +25,14 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Moment from 'moment';
|
import Moment from 'moment';
|
||||||
import PopupMenu from './PopupMenu.vue';
|
|
||||||
import PreviewAction from '../../../ui/preview/PreviewAction';
|
import PreviewAction from '../../../ui/preview/PreviewAction';
|
||||||
import RemoveDialog from '../utils/removeDialog';
|
import RemoveDialog from '../utils/removeDialog';
|
||||||
import PainterroInstance from '../utils/painterroInstance';
|
import PainterroInstance from '../utils/painterroInstance';
|
||||||
import SnapshotTemplate from './snapshot-template.html';
|
import SnapshotTemplate from './snapshot-template.html';
|
||||||
|
|
||||||
|
import { updateNotebookImageDomainObject } from '../utils/notebook-image';
|
||||||
|
|
||||||
|
import PopupMenu from './PopupMenu.vue';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -59,6 +62,11 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
createdOn() {
|
createdOn() {
|
||||||
return this.formatTime(this.embed.createdOn, 'YYYY-MM-DD HH:mm:ss');
|
return this.formatTime(this.embed.createdOn, 'YYYY-MM-DD HH:mm:ss');
|
||||||
|
},
|
||||||
|
thumbnailImage() {
|
||||||
|
return this.embed.snapshot.thumbnailImage
|
||||||
|
? this.embed.snapshot.thumbnailImage.src
|
||||||
|
: this.embed.snapshot.src;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -85,7 +93,7 @@ export default {
|
|||||||
template: '<div id="snap-annotation"></div>'
|
template: '<div id="snap-annotation"></div>'
|
||||||
}).$mount();
|
}).$mount();
|
||||||
|
|
||||||
const painterroInstance = new PainterroInstance(annotateVue.$el, this.updateSnapshot);
|
const painterroInstance = new PainterroInstance(annotateVue.$el);
|
||||||
const annotateOverlay = this.openmct.overlays.overlay({
|
const annotateOverlay = this.openmct.overlays.overlay({
|
||||||
element: annotateVue.$el,
|
element: annotateVue.$el,
|
||||||
size: 'large',
|
size: 'large',
|
||||||
@ -102,10 +110,12 @@ export default {
|
|||||||
{
|
{
|
||||||
label: 'Save',
|
label: 'Save',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
painterroInstance.save();
|
painterroInstance.save((snapshotObject) => {
|
||||||
annotateOverlay.dismiss();
|
annotateOverlay.dismiss();
|
||||||
this.snapshotOverlay.dismiss();
|
this.snapshotOverlay.dismiss();
|
||||||
this.openSnapshot();
|
this.updateSnapshot(snapshotObject);
|
||||||
|
this.openSnapshotOverlay(snapshotObject.fullSizeImage.src);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -115,7 +125,19 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
painterroInstance.intialize();
|
painterroInstance.intialize();
|
||||||
painterroInstance.show(this.embed.snapshot.src);
|
|
||||||
|
const fullSizeImageObjectIdentifier = this.embed.snapshot.fullSizeImageObjectIdentifier;
|
||||||
|
if (!fullSizeImageObjectIdentifier) {
|
||||||
|
// legacy image data stored in embed
|
||||||
|
painterroInstance.show(this.embed.snapshot.src);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openmct.objects.get(fullSizeImageObjectIdentifier)
|
||||||
|
.then(object => {
|
||||||
|
painterroInstance.show(object.configuration.fullSizeImageURL);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
changeLocation() {
|
changeLocation() {
|
||||||
const hash = this.embed.historicLink;
|
const hash = this.embed.historicLink;
|
||||||
@ -159,12 +181,29 @@ export default {
|
|||||||
removeDialog.show();
|
removeDialog.show();
|
||||||
},
|
},
|
||||||
openSnapshot() {
|
openSnapshot() {
|
||||||
|
const fullSizeImageObjectIdentifier = this.embed.snapshot.fullSizeImageObjectIdentifier;
|
||||||
|
if (!fullSizeImageObjectIdentifier) {
|
||||||
|
// legacy image data stored in embed
|
||||||
|
this.openSnapshotOverlay(this.embed.snapshot.src);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openmct.objects.get(fullSizeImageObjectIdentifier)
|
||||||
|
.then(object => {
|
||||||
|
this.openSnapshotOverlay(object.configuration.fullSizeImageURL);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openSnapshotOverlay(src) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
this.snapshot = new Vue({
|
this.snapshot = new Vue({
|
||||||
data: () => {
|
data: () => {
|
||||||
return {
|
return {
|
||||||
createdOn: this.createdOn,
|
createdOn: this.createdOn,
|
||||||
embed: this.embed
|
name: this.embed.name,
|
||||||
|
cssClass: this.embed.cssClass,
|
||||||
|
src
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -217,7 +256,9 @@ export default {
|
|||||||
this.$emit('updateEmbed', embed);
|
this.$emit('updateEmbed', embed);
|
||||||
},
|
},
|
||||||
updateSnapshot(snapshotObject) {
|
updateSnapshot(snapshotObject) {
|
||||||
this.embed.snapshot = snapshotObject;
|
this.embed.snapshot.thumbnailImage = snapshotObject.thumbnailImage;
|
||||||
|
|
||||||
|
updateNotebookImageDomainObject(this.openmct, this.embed.snapshot.fullSizeImageObjectIdentifier, snapshotObject.fullSizeImage);
|
||||||
this.updateEmbed(this.embed);
|
this.updateEmbed(this.embed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,6 @@
|
|||||||
<NotebookEmbed v-for="embed in entry.embeds"
|
<NotebookEmbed v-for="embed in entry.embeds"
|
||||||
:key="embed.id"
|
:key="embed.id"
|
||||||
:embed="embed"
|
:embed="embed"
|
||||||
:entry="entry"
|
|
||||||
@removeEmbed="removeEmbed"
|
@removeEmbed="removeEmbed"
|
||||||
@updateEmbed="updateEmbed"
|
@updateEmbed="updateEmbed"
|
||||||
/>
|
/>
|
||||||
@ -254,6 +253,7 @@ export default {
|
|||||||
},
|
},
|
||||||
removeEmbed(id) {
|
removeEmbed(id) {
|
||||||
const embedPosition = this.findPositionInArray(this.entry.embeds, id);
|
const embedPosition = this.findPositionInArray(this.entry.embeds, id);
|
||||||
|
// TODO: remove notebook snapshot object using object remove API
|
||||||
this.entry.embeds.splice(embedPosition, 1);
|
this.entry.embeds.splice(embedPosition, 1);
|
||||||
|
|
||||||
this.$emit('updateEntry', this.entry);
|
this.$emit('updateEntry', this.entry);
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
<div class="l-browse-bar__start">
|
<div class="l-browse-bar__start">
|
||||||
<div class="l-browse-bar__object-name--w">
|
<div class="l-browse-bar__object-name--w">
|
||||||
<span class="c-object-label l-browse-bar__object-name"
|
<span class="c-object-label l-browse-bar__object-name"
|
||||||
v-bind:class="embed.cssClass"
|
v-bind:class="cssClass"
|
||||||
>
|
>
|
||||||
<span class="c-object-label__name">{{ embed.name }}</span>
|
<span class="c-object-label__name">{{ name }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -40,7 +40,7 @@
|
|||||||
<div
|
<div
|
||||||
ref="snapshot-image"
|
ref="snapshot-image"
|
||||||
class="c-notebook-snapshot__image"
|
class="c-notebook-snapshot__image"
|
||||||
:style="{ backgroundImage: 'url(' + embed.snapshot.src + ')' }"
|
:style="{ backgroundImage: 'url(' + src + ')' }"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,10 @@ import CopyToNotebookAction from './actions/CopyToNotebookAction';
|
|||||||
import Notebook from './components/Notebook.vue';
|
import Notebook from './components/Notebook.vue';
|
||||||
import NotebookSnapshotIndicator from './components/NotebookSnapshotIndicator.vue';
|
import NotebookSnapshotIndicator from './components/NotebookSnapshotIndicator.vue';
|
||||||
import SnapshotContainer from './snapshot-container';
|
import SnapshotContainer from './snapshot-container';
|
||||||
import {NOTEBOOK_TYPE} from './notebook-constants';
|
|
||||||
|
import { notebookImageMigration } from '../notebook/utils/notebook-migration';
|
||||||
|
import { NOTEBOOK_TYPE } from './notebook-constants';
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default function NotebookPlugin() {
|
export default function NotebookPlugin() {
|
||||||
@ -85,6 +88,19 @@ export default function NotebookPlugin() {
|
|||||||
};
|
};
|
||||||
openmct.types.addType(NOTEBOOK_TYPE, notebookType);
|
openmct.types.addType(NOTEBOOK_TYPE, notebookType);
|
||||||
|
|
||||||
|
const notebookSnapshotImageType = {
|
||||||
|
name: 'Notebook Snapshot Image Storage',
|
||||||
|
description: 'Notebook Snapshot Image Storage object',
|
||||||
|
creatable: false,
|
||||||
|
initialize: domainObject => {
|
||||||
|
domainObject.configuration = {
|
||||||
|
fullSizeImageURL: undefined,
|
||||||
|
thumbnailImageURL: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
openmct.types.addType('notebookSnapshotImage', notebookSnapshotImageType);
|
||||||
|
|
||||||
const snapshotContainer = new SnapshotContainer(openmct);
|
const snapshotContainer = new SnapshotContainer(openmct);
|
||||||
const notebookSnapshotIndicator = new Vue ({
|
const notebookSnapshotIndicator = new Vue ({
|
||||||
components: {
|
components: {
|
||||||
@ -138,5 +154,16 @@ export default function NotebookPlugin() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
openmct.objects.addGetInterceptor({
|
||||||
|
appliesTo: (identifier, domainObject) => {
|
||||||
|
return domainObject && domainObject.type === 'notebook';
|
||||||
|
},
|
||||||
|
invoke: (identifier, domainObject) => {
|
||||||
|
notebookImageMigration(openmct, domainObject);
|
||||||
|
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { addNotebookEntry, createNewEmbed } from './utils/notebook-entries';
|
import { addNotebookEntry, createNewEmbed } from './utils/notebook-entries';
|
||||||
import { getDefaultNotebook, getDefaultNotebookLink, setDefaultNotebook } from './utils/notebook-storage';
|
import { getDefaultNotebook, getDefaultNotebookLink, setDefaultNotebook } from './utils/notebook-storage';
|
||||||
import { NOTEBOOK_DEFAULT } from '@/plugins/notebook/notebook-constants';
|
import { NOTEBOOK_DEFAULT } from '@/plugins/notebook/notebook-constants';
|
||||||
|
import { createNotebookImageDomainObject, DEFAULT_SIZE } from './utils/notebook-image';
|
||||||
|
|
||||||
import SnapshotContainer from './snapshot-container';
|
import SnapshotContainer from './snapshot-container';
|
||||||
|
|
||||||
export default class Snapshot {
|
export default class Snapshot {
|
||||||
@ -14,12 +16,17 @@ export default class Snapshot {
|
|||||||
|
|
||||||
capture(snapshotMeta, notebookType, domElement) {
|
capture(snapshotMeta, notebookType, domElement) {
|
||||||
const exportImageService = this.openmct.$injector.get('exportImageService');
|
const exportImageService = this.openmct.$injector.get('exportImageService');
|
||||||
exportImageService.exportPNGtoSRC(domElement, 's-status-taking-snapshot')
|
|
||||||
.then(function (blob) {
|
const options = {
|
||||||
|
className: 's-status-taking-snapshot',
|
||||||
|
thumbnailSize: DEFAULT_SIZE
|
||||||
|
};
|
||||||
|
exportImageService.exportPNGtoSRC(domElement, options)
|
||||||
|
.then(function ({blob, thumbnail}) {
|
||||||
const reader = new window.FileReader();
|
const reader = new window.FileReader();
|
||||||
reader.readAsDataURL(blob);
|
reader.readAsDataURL(blob);
|
||||||
reader.onloadend = function () {
|
reader.onloadend = function () {
|
||||||
this._saveSnapShot(notebookType, reader.result, snapshotMeta);
|
this._saveSnapShot(notebookType, reader.result, thumbnail, snapshotMeta);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
@ -27,16 +34,23 @@ export default class Snapshot {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_saveSnapShot(notebookType, imageUrl, snapshotMeta) {
|
_saveSnapShot(notebookType, fullSizeImageURL, thumbnailImageURL, snapshotMeta) {
|
||||||
const snapshot = imageUrl ? { src: imageUrl } : '';
|
createNotebookImageDomainObject(this.openmct, fullSizeImageURL)
|
||||||
const embed = createNewEmbed(snapshotMeta, snapshot);
|
.then(object => {
|
||||||
if (notebookType === NOTEBOOK_DEFAULT) {
|
const thumbnailImage = { src: thumbnailImageURL || '' };
|
||||||
this._saveToDefaultNoteBook(embed);
|
const snapshot = {
|
||||||
|
fullSizeImageObjectIdentifier: object.identifier,
|
||||||
|
thumbnailImage
|
||||||
|
};
|
||||||
|
const embed = createNewEmbed(snapshotMeta, snapshot);
|
||||||
|
if (notebookType === NOTEBOOK_DEFAULT) {
|
||||||
|
this._saveToDefaultNoteBook(embed);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._saveToNotebookSnapshots(embed);
|
this._saveToNotebookSnapshots(embed);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
78
src/plugins/notebook/utils/notebook-image.js
Normal file
78
src/plugins/notebook/utils/notebook-image.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import uuid from 'uuid';
|
||||||
|
|
||||||
|
export const DEFAULT_SIZE = {
|
||||||
|
width: 30,
|
||||||
|
height: 30
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createNotebookImageDomainObject(openmct, fullSizeImageURL) {
|
||||||
|
const identifier = {
|
||||||
|
key: uuid(),
|
||||||
|
namespace: ''
|
||||||
|
};
|
||||||
|
const viewType = 'notebookSnapshotImage';
|
||||||
|
|
||||||
|
const object = {
|
||||||
|
name: 'Notebook Snapshot Image',
|
||||||
|
type: viewType,
|
||||||
|
identifier,
|
||||||
|
configuration: {
|
||||||
|
fullSizeImageURL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
openmct.objects.save(object)
|
||||||
|
.then(result => {
|
||||||
|
if (result) {
|
||||||
|
resolve(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
reject();
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.error(e);
|
||||||
|
reject();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getThumbnailURLFromCanvas(canvas, size = DEFAULT_SIZE) {
|
||||||
|
const thumbnailCanvas = document.createElement('canvas');
|
||||||
|
thumbnailCanvas.setAttribute('width', size.width);
|
||||||
|
thumbnailCanvas.setAttribute('height', size.height);
|
||||||
|
const ctx = thumbnailCanvas.getContext('2d');
|
||||||
|
ctx.globalCompositeOperation = "copy";
|
||||||
|
ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, size.width, size.height);
|
||||||
|
|
||||||
|
return thumbnailCanvas.toDataURL('image/png');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getThumbnailURLFromimageUrl(imageUrl, size = DEFAULT_SIZE) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const image = new Image();
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = size.width;
|
||||||
|
canvas.height = size.height;
|
||||||
|
|
||||||
|
image.onload = function () {
|
||||||
|
canvas.getContext('2d')
|
||||||
|
.drawImage(image, 0, 0, size.width, size.height);
|
||||||
|
|
||||||
|
resolve(canvas.toDataURL('image/png'));
|
||||||
|
};
|
||||||
|
|
||||||
|
image.src = imageUrl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateNotebookImageDomainObject(openmct, identifier, fullSizeImage) {
|
||||||
|
openmct.objects.get(identifier)
|
||||||
|
.then(domainObject => {
|
||||||
|
const configuration = domainObject.configuration;
|
||||||
|
configuration.fullSizeImageURL = fullSizeImage.src;
|
||||||
|
|
||||||
|
openmct.objects.mutate(domainObject, 'configuration', configuration);
|
||||||
|
});
|
||||||
|
}
|
43
src/plugins/notebook/utils/notebook-migration.js
Normal file
43
src/plugins/notebook/utils/notebook-migration.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { createNotebookImageDomainObject, getThumbnailURLFromimageUrl } from './notebook-image';
|
||||||
|
import { mutateObject } from './notebook-entries';
|
||||||
|
|
||||||
|
export function notebookImageMigration(openmct, domainObject) {
|
||||||
|
const configuration = domainObject.configuration;
|
||||||
|
const notebookEntries = configuration.entries;
|
||||||
|
|
||||||
|
const imageMigrationVer = configuration.imageMigrationVer;
|
||||||
|
if (imageMigrationVer && imageMigrationVer === 'v1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.imageMigrationVer = 'v1';
|
||||||
|
|
||||||
|
// to avoid muliple notebookImageMigration calls updating images.
|
||||||
|
mutateObject(openmct, domainObject, 'configuration', configuration);
|
||||||
|
|
||||||
|
configuration.sections.forEach(section => {
|
||||||
|
const sectionId = section.id;
|
||||||
|
section.pages.forEach(page => {
|
||||||
|
const pageId = page.id;
|
||||||
|
const notebookSection = notebookEntries && notebookEntries[sectionId] || {};
|
||||||
|
const pageEntries = notebookSection && notebookSection[pageId] || [];
|
||||||
|
pageEntries.forEach(entry => {
|
||||||
|
entry.embeds.forEach(async (embed) => {
|
||||||
|
const snapshot = embed.snapshot;
|
||||||
|
const fullSizeImageURL = snapshot.src;
|
||||||
|
if (fullSizeImageURL) {
|
||||||
|
const thumbnailImageURL = await getThumbnailURLFromimageUrl(fullSizeImageURL);
|
||||||
|
const notebookImageDomainObject = await createNotebookImageDomainObject(openmct, fullSizeImageURL);
|
||||||
|
|
||||||
|
embed.snapshot = {
|
||||||
|
fullSizeImageObjectIdentifier: notebookImageDomainObject.identifier,
|
||||||
|
thumbnailImage: { src: thumbnailImageURL || '' }
|
||||||
|
};
|
||||||
|
|
||||||
|
mutateObject(openmct, domainObject, 'configuration.entries', notebookEntries);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import Painterro from 'painterro';
|
import Painterro from 'painterro';
|
||||||
|
import { getThumbnailURLFromimageUrl } from './notebook-image';
|
||||||
|
|
||||||
const DEFAULT_CONFIG = {
|
const DEFAULT_CONFIG = {
|
||||||
activeColor: '#ff0000',
|
activeColor: '#ff0000',
|
||||||
@ -25,11 +26,11 @@ const DEFAULT_CONFIG = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default class PainterroInstance {
|
export default class PainterroInstance {
|
||||||
constructor(element, saveCallback) {
|
constructor(element) {
|
||||||
this.elementId = element.id;
|
this.elementId = element.id;
|
||||||
this.isSave = false;
|
this.isSave = false;
|
||||||
this.painterroInstance = null;
|
this.painterroInstance = undefined;
|
||||||
this.saveCallback = saveCallback;
|
this.saveCallback = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
dismiss() {
|
dismiss() {
|
||||||
@ -46,31 +47,41 @@ export default class PainterroInstance {
|
|||||||
this.painterro = Painterro(this.config);
|
this.painterro = Painterro(this.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save(callback) {
|
||||||
|
this.saveCallback = callback;
|
||||||
this.isSave = true;
|
this.isSave = true;
|
||||||
this.painterroInstance.save();
|
this.painterroInstance.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
saveHandler(image, done) {
|
saveHandler(image, done) {
|
||||||
if (this.isSave) {
|
if (this.isSave) {
|
||||||
const self = this;
|
|
||||||
const url = image.asBlob();
|
const url = image.asBlob();
|
||||||
|
|
||||||
const reader = new window.FileReader();
|
const reader = new window.FileReader();
|
||||||
reader.readAsDataURL(url);
|
reader.readAsDataURL(url);
|
||||||
reader.onloadend = () => {
|
reader.onloadend = async () => {
|
||||||
const snapshot = reader.result;
|
const fullSizeImageURL = reader.result;
|
||||||
|
const thumbnailURL = await getThumbnailURLFromimageUrl(fullSizeImageURL);
|
||||||
const snapshotObject = {
|
const snapshotObject = {
|
||||||
src: snapshot,
|
fullSizeImage: {
|
||||||
type: url.type,
|
src: fullSizeImageURL,
|
||||||
size: url.size,
|
type: url.type,
|
||||||
modified: Date.now()
|
size: url.size,
|
||||||
|
modified: Date.now()
|
||||||
|
},
|
||||||
|
thumbnailImage: {
|
||||||
|
src: thumbnailURL,
|
||||||
|
modified: Date.now()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.saveCallback(snapshotObject);
|
this.saveCallback(snapshotObject);
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
done(true);
|
done(true);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
done(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
show(src) {
|
show(src) {
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
import CouchDocument from "./CouchDocument";
|
import CouchDocument from "./CouchDocument";
|
||||||
import CouchObjectQueue from "./CouchObjectQueue";
|
import CouchObjectQueue from "./CouchObjectQueue";
|
||||||
import NOTEBOOK_TYPE from '../../notebook/notebook-constants.js';
|
import { NOTEBOOK_TYPE } from '../../notebook/notebook-constants.js';
|
||||||
|
|
||||||
const REV = "_rev";
|
const REV = "_rev";
|
||||||
const ID = "_id";
|
const ID = "_id";
|
||||||
|
Reference in New Issue
Block a user