|
|
@ -1,6 +1,9 @@
|
|
|
|
'use strict';
|
|
|
|
'use strict';
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
|
|
|
|
|
|
|
With modifications, by f0x
|
|
|
|
|
|
|
|
With modifications, by Sven Slootweg <admin@cryto.net>
|
|
|
|
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
You may obtain a copy of the License at
|
|
|
@ -14,7 +17,6 @@ limitations under the License.
|
|
|
|
|
|
|
|
|
|
|
|
const Promise = require('bluebird');
|
|
|
|
const Promise = require('bluebird');
|
|
|
|
const sanitize = require('sanitize-html');
|
|
|
|
const sanitize = require('sanitize-html');
|
|
|
|
//require("blueimp-canvas-to-blob");
|
|
|
|
|
|
|
|
const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/;
|
|
|
|
const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -36,29 +38,86 @@ const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/;
|
|
|
|
* and a thumbnail key.
|
|
|
|
* and a thumbnail key.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function canvasToBlob(canvas, mimeType) {
|
|
|
|
|
|
|
|
return new Promise((resolve, _reject) => {
|
|
|
|
|
|
|
|
canvas.toBlob(function(blob) {
|
|
|
|
|
|
|
|
resolve(blob);
|
|
|
|
|
|
|
|
}, mimeType);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function awaitImageLoad(image) {
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
image.onload = function() {
|
|
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
image.onerror = function(e) {
|
|
|
|
|
|
|
|
reject(e);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function awaitFileReader(dataUrl) {
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
reader.onload = function(event) {
|
|
|
|
|
|
|
|
resolve(event.target.result);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
reader.onerror = function(error) {
|
|
|
|
|
|
|
|
reject(error);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
reader.readAsDataURL(dataUrl);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function awaitVideoLoad(video) {
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
video.onloadeddata = function() {
|
|
|
|
|
|
|
|
resolve({
|
|
|
|
|
|
|
|
width: video.videoWidth,
|
|
|
|
|
|
|
|
height: video.videoHeight
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video.onerror = function(error) {
|
|
|
|
|
|
|
|
reject(error);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
module.exports = {
|
|
|
|
createThumbnail: function(element, inputWidth, inputHeight, mimeType) {
|
|
|
|
createThumbnail: function(element, inputWidth, inputHeight, mimeType) {
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
return Promise.try(() => {
|
|
|
|
const MAX_WIDTH = 800;
|
|
|
|
const MAX_WIDTH = 800;
|
|
|
|
const MAX_HEIGHT = 600;
|
|
|
|
const MAX_HEIGHT = 600;
|
|
|
|
|
|
|
|
|
|
|
|
let targetWidth = inputWidth;
|
|
|
|
let targetWidth = inputWidth;
|
|
|
|
let targetHeight = inputHeight;
|
|
|
|
let targetHeight = inputHeight;
|
|
|
|
|
|
|
|
|
|
|
|
if (targetHeight > MAX_HEIGHT) {
|
|
|
|
if (targetHeight > MAX_HEIGHT) {
|
|
|
|
targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight));
|
|
|
|
targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight));
|
|
|
|
targetHeight = MAX_HEIGHT;
|
|
|
|
targetHeight = MAX_HEIGHT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (targetWidth > MAX_WIDTH) {
|
|
|
|
if (targetWidth > MAX_WIDTH) {
|
|
|
|
targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth));
|
|
|
|
targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth));
|
|
|
|
targetWidth = MAX_WIDTH;
|
|
|
|
targetWidth = MAX_WIDTH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const canvas = document.createElement("canvas");
|
|
|
|
const canvas = Object.assign(document.createElement("canvas"), {
|
|
|
|
canvas.width = targetWidth;
|
|
|
|
width: targetWidth,
|
|
|
|
canvas.height = targetHeight;
|
|
|
|
height: targetHeight
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
canvas.getContext("2d").drawImage(element, 0, 0, targetWidth, targetHeight);
|
|
|
|
canvas.getContext("2d").drawImage(element, 0, 0, targetWidth, targetHeight);
|
|
|
|
canvas.toBlob(function(thumbnail) {
|
|
|
|
|
|
|
|
resolve({
|
|
|
|
return Promise.try(() => {
|
|
|
|
|
|
|
|
return canvasToBlob(canvas, mimeType);
|
|
|
|
|
|
|
|
}).then((thumbnail) => {
|
|
|
|
|
|
|
|
return {
|
|
|
|
info: {
|
|
|
|
info: {
|
|
|
|
thumbnail_info: {
|
|
|
|
thumbnail_info: {
|
|
|
|
w: targetWidth,
|
|
|
|
w: targetWidth,
|
|
|
@ -70,8 +129,8 @@ module.exports = {
|
|
|
|
h: inputHeight,
|
|
|
|
h: inputHeight,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
thumbnail: thumbnail,
|
|
|
|
thumbnail: thumbnail,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}, mimeType);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
@ -82,20 +141,16 @@ module.exports = {
|
|
|
|
* @return {Promise} A promise that resolves with the html image element.
|
|
|
|
* @return {Promise} A promise that resolves with the html image element.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
loadImageElement: function(imageFile) {
|
|
|
|
loadImageElement: function(imageFile) {
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
return Promise.try(() => {
|
|
|
|
// Load the file into an html element
|
|
|
|
|
|
|
|
const img = document.createElement("img");
|
|
|
|
const img = document.createElement("img");
|
|
|
|
const objectUrl = URL.createObjectURL(imageFile);
|
|
|
|
const objectUrl = URL.createObjectURL(imageFile);
|
|
|
|
img.src = objectUrl;
|
|
|
|
img.src = objectUrl;
|
|
|
|
|
|
|
|
|
|
|
|
// Once ready, create a thumbnail
|
|
|
|
return Promise.try(() => {
|
|
|
|
img.onload = function() {
|
|
|
|
return awaitImageLoad(img);
|
|
|
|
|
|
|
|
}).then(() => {
|
|
|
|
URL.revokeObjectURL(objectUrl);
|
|
|
|
URL.revokeObjectURL(objectUrl);
|
|
|
|
resolve(img);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
img.onerror = function(e) {
|
|
|
|
|
|
|
|
reject(e);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
@ -106,29 +161,19 @@ module.exports = {
|
|
|
|
* @return {Promise} A promise that resolves with the video image element.
|
|
|
|
* @return {Promise} A promise that resolves with the video image element.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
loadVideoElement: function(videoFile) {
|
|
|
|
loadVideoElement: function(videoFile) {
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
return Promise.try(() => {
|
|
|
|
// Load the file into an html element
|
|
|
|
return awaitFileReader(videoFile);
|
|
|
|
const video = document.createElement("video");
|
|
|
|
}).then((url) => {
|
|
|
|
|
|
|
|
const video = Object.assign(document.createElement("video"), {
|
|
|
|
const reader = new FileReader();
|
|
|
|
src: url
|
|
|
|
reader.onload = function(e) {
|
|
|
|
});
|
|
|
|
video.src = e.target.result;
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.try(() => {
|
|
|
|
// Once ready, returns its size
|
|
|
|
return awaitVideoLoad(video);
|
|
|
|
// Wait until we have enough data to thumbnail the first frame.
|
|
|
|
}).then((dimensions) => {
|
|
|
|
video.onloadeddata = function() {
|
|
|
|
/* FIXME: Check whether this can be improved, it's a bit dirty to shoehorn the dimensions onto the video object like this */
|
|
|
|
video.width = video.videoWidth;
|
|
|
|
return Object.assign(video, dimensions);
|
|
|
|
video.height = video.videoHeight;
|
|
|
|
});
|
|
|
|
resolve(video);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
video.onerror = function(e) {
|
|
|
|
|
|
|
|
reject(e);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
reader.onerror = function(e) {
|
|
|
|
|
|
|
|
reject(e);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
reader.readAsDataURL(videoFile);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
@ -163,7 +208,7 @@ module.exports = {
|
|
|
|
|
|
|
|
|
|
|
|
transformTags: { // custom to matrix
|
|
|
|
transformTags: { // custom to matrix
|
|
|
|
// add blank targets to all hyperlinks except vector URLs
|
|
|
|
// add blank targets to all hyperlinks except vector URLs
|
|
|
|
'img': function(tagName, attribs) {
|
|
|
|
'img': function(tagName, _attribs) {
|
|
|
|
// Strip out imgs that aren't `mxc` here instead of using allowedSchemesByTag
|
|
|
|
// Strip out imgs that aren't `mxc` here instead of using allowedSchemesByTag
|
|
|
|
// because transformTags is used _before_ we filter by allowedSchemesByTag and
|
|
|
|
// because transformTags is used _before_ we filter by allowedSchemesByTag and
|
|
|
|
// we don't want to allow images with `https?` `src`s.
|
|
|
|
// we don't want to allow images with `https?` `src`s.
|
|
|
@ -209,10 +254,7 @@ module.exports = {
|
|
|
|
Object.keys(customCSSMapper).forEach((customAttributeKey) => {
|
|
|
|
Object.keys(customCSSMapper).forEach((customAttributeKey) => {
|
|
|
|
const cssAttributeKey = customCSSMapper[customAttributeKey];
|
|
|
|
const cssAttributeKey = customCSSMapper[customAttributeKey];
|
|
|
|
const customAttributeValue = attribs[customAttributeKey];
|
|
|
|
const customAttributeValue = attribs[customAttributeKey];
|
|
|
|
if (customAttributeValue &&
|
|
|
|
if (customAttributeValue && typeof customAttributeValue === 'string' && COLOR_REGEX.test(customAttributeValue)) {
|
|
|
|
typeof customAttributeValue === 'string' &&
|
|
|
|
|
|
|
|
COLOR_REGEX.test(customAttributeValue)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
style += cssAttributeKey + ":" + customAttributeValue + ";";
|
|
|
|
style += cssAttributeKey + ":" + customAttributeValue + ";";
|
|
|
|
delete attribs[customAttributeKey];
|
|
|
|
delete attribs[customAttributeKey];
|
|
|
|
}
|
|
|
|
}
|
|
|
|