cryptpad/www/common/common-thumbnail.js

347 lines
12 KiB
JavaScript
Raw Normal View History

define([
2017-11-06 14:49:40 +00:00
'jquery',
'/common/common-util.js',
'/common/visible.js',
'/common/common-hash.js',
'/common/media-tag.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, Util, Visible, Hash, MediaTag) {
var Nacl = window.nacl;
var Thumb = {
2017-09-05 09:25:35 +00:00
dimension: 100,
padDimension: 200,
2017-11-02 18:11:27 +00:00
UPDATE_INTERVAL: 60000,
UPDATE_FIRST: 5000
2017-09-05 08:46:00 +00:00
};
var supportedTypes = [
'text/plain',
2017-09-05 08:46:00 +00:00
'image/png',
'image/jpeg',
'image/jpg',
2017-10-24 10:26:04 +00:00
'image/gif',
2017-10-24 12:31:42 +00:00
'video/',
'application/pdf'
2017-09-05 08:46:00 +00:00
];
Thumb.isSupportedType = function (file) {
2019-08-13 16:05:03 +00:00
if (!file) { return false; }
var type = file.type;
if (Util.isPlainTextFile(file.type, file.name)) {
type = "text/plain";
}
2017-10-24 10:26:04 +00:00
return supportedTypes.some(function (t) {
return type.indexOf(t) !== -1;
});
};
// create thumbnail image from metadata
// return an img tag, or undefined if anything goes wrong
Thumb.fromMetadata = function (metadata) {
if (!metadata || typeof(metadata) !== 'object' || !metadata.thumbnail) { return; }
try {
var u8 = Nacl.util.decodeBase64(metadata.thumbnail);
var blob = new Blob([u8], {
type: 'image/png'
});
var url = URL.createObjectURL(blob);
var img = new Image();
img.src = url;
img.width = Thumb.dimension;
img.height = Thumb.dimension;
return img;
} catch (e) {
console.error(e);
return;
}
};
var getResizedDimensions = Thumb.getResizedDimensions = function (img, type) {
2017-10-24 10:26:04 +00:00
var h = type === 'video' ? img.videoHeight : img.height;
var w = type === 'video' ? img.videoWidth : img.width;
2017-09-05 08:46:00 +00:00
2017-10-26 10:31:16 +00:00
var dim = type === 'pad' ? Thumb.padDimension : Thumb.dimension;
2017-09-05 08:46:00 +00:00
// if the image is too small, don't bother making a thumbnail
2017-10-26 10:31:16 +00:00
/*if (h <= dim && w <= dim) {
2017-10-24 16:49:58 +00:00
return {
x: Math.floor((dim - w) / 2),
w: w,
y: Math.floor((dim - h) / 2),
h : h
};
2017-10-26 10:31:16 +00:00
}*/
2017-09-05 08:46:00 +00:00
// the image is taller than it is wide, so scale to that.
var r = dim / (h > w? h: w); // ratio
2017-10-26 10:31:16 +00:00
if (h <= dim && w <= dim) { r = 1; }
2017-09-05 08:46:00 +00:00
var d;
if (h > w) {
var newW = Math.floor(w*r);
d = Math.floor((dim - newW) / 2);
2017-09-05 08:46:00 +00:00
return {
2017-10-26 10:31:16 +00:00
dim: dim,
x: d,
w: newW,
y: 0,
h: dim,
2017-09-05 08:46:00 +00:00
};
} else {
var newH = Math.floor(h*r);
d = Math.floor((dim - newH) / 2);
2017-09-05 08:46:00 +00:00
return {
2017-10-26 10:31:16 +00:00
dim: dim,
x: 0,
w: dim,
y: d,
h: newH
2017-09-05 08:46:00 +00:00
};
}
};
// assumes that your canvas is square
// nodeback returning blob
2017-10-24 16:49:58 +00:00
Thumb.fromCanvas = function (canvas, D, cb) {
var c2 = document.createElement('canvas');
2017-10-24 16:49:58 +00:00
if (!D) { return void cb('ERROR'); }
2017-09-05 08:46:00 +00:00
2017-10-26 10:31:16 +00:00
c2.width = D.dim;
c2.height = D.dim;
var ctx = c2.getContext('2d');
2020-04-30 09:07:55 +00:00
try {
ctx.drawImage(canvas, D.x, D.y, D.w, D.h);
} catch (e) {
return void cb('ERROR');
}
2017-10-24 16:49:58 +00:00
cb(void 0, c2.toDataURL());
};
2017-09-05 08:46:00 +00:00
Thumb.fromImageBlob = function (blob, cb) {
var url = URL.createObjectURL(blob);
var img = new Image();
img.onload = function () {
2017-10-24 10:26:04 +00:00
var D = getResizedDimensions(img, 'image');
2017-10-24 16:49:58 +00:00
Thumb.fromCanvas(img, D, cb);
2017-09-05 08:46:00 +00:00
};
img.onerror = function () {
cb('ERROR');
};
img.src = url;
};
2017-10-24 10:26:04 +00:00
Thumb.fromVideoBlob = function (blob, cb) {
var url = URL.createObjectURL(blob);
var video = document.createElement("VIDEO");
2017-11-17 17:20:02 +00:00
video.addEventListener('loadeddata', function() {
var D = getResizedDimensions(video, 'video');
Thumb.fromCanvas(video, D, cb);
}, false);
video.addEventListener('error', function (e) {
2017-10-24 12:02:29 +00:00
console.error(e);
cb('ERROR');
});
video.src = url;
2017-10-24 10:26:04 +00:00
};
2017-10-24 12:31:42 +00:00
Thumb.fromPdfBlob = function (blob, cb) {
require.config({paths: {'pdfjs-dist': '/lib/pdfjs'}});
2017-10-24 12:31:42 +00:00
require(['pdfjs-dist/build/pdf'], function (PDFJS) {
var url = URL.createObjectURL(blob);
var makeThumb = function (page) {
var vp = page.getViewport(1);
var canvas = document.createElement("canvas");
canvas.width = canvas.height = Thumb.dimension;
var scale = Math.min(canvas.width / vp.width, canvas.height / vp.height);
canvas.width = Math.floor(vp.width * scale);
canvas.height = Math.floor(vp.height * scale);
return page.render({
canvasContext: canvas.getContext("2d"),
viewport: page.getViewport(scale)
}).promise.then(function () {
return canvas;
2020-04-30 09:07:55 +00:00
}).catch(function () {
cb('ERROR');
2017-10-24 12:31:42 +00:00
});
};
PDFJS.getDocument(url).promise
.then(function (doc) {
return doc.getPage(1).then(makeThumb).then(function (canvas) {
2017-10-26 10:31:16 +00:00
var D = getResizedDimensions(canvas, 'pdf');
Thumb.fromCanvas(canvas, D, cb);
2017-10-24 12:31:42 +00:00
});
2017-10-24 12:32:47 +00:00
}).catch(function () {
2017-10-24 12:31:42 +00:00
cb('ERROR');
});
});
};
Thumb.fromPlainTextBlob = function (blob, cb) {
var canvas = document.createElement("canvas");
canvas.width = canvas.height = Thumb.dimension;
var reader = new FileReader();
reader.addEventListener('loadend', function (e) {
var content = e.srcElement.result;
var lines = content.split("\n");
var canvasContext = canvas.getContext("2d");
var fontSize = 4;
canvas.height = (lines.length) * (fontSize + 1);
canvasContext.font = fontSize + 'px monospace';
lines.forEach(function (text, i) {
canvasContext.fillText(text, 5, i * (fontSize + 1));
});
var D = getResizedDimensions(canvas, "txt");
Thumb.fromCanvas(canvas, D, cb);
});
reader.readAsText(blob);
};
2020-04-30 09:07:55 +00:00
Thumb.fromBlob = function (blob, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
// The blob is already in memory, it should be super-fast to make a thumbnail
// ==> 1s timeout
setTimeout(function () {
cb('TIMEOUT');
}, 1000);
try {
if (blob.type.indexOf('video/') !== -1) {
return void Thumb.fromVideoBlob(blob, cb);
}
if (blob.type.indexOf('application/pdf') !== -1) {
return void Thumb.fromPdfBlob(blob, cb);
}
if (Util.isPlainTextFile(blob.type, blob.name)) {
return void Thumb.fromPlainTextBlob(blob, cb);
}
if (blob.type.indexOf('image/') !== -1) {
return void Thumb.fromImageBlob(blob, cb);
}
} catch (e) {
return void cb('THUMBNAIL_ERROR');
2020-04-30 09:07:55 +00:00
}
return void cb('NO_THUMBNAIL');
2017-10-24 10:26:04 +00:00
};
2017-09-05 08:46:00 +00:00
2017-10-24 16:49:58 +00:00
window.html2canvas = undefined;
2017-10-26 10:31:16 +00:00
Thumb.fromDOM = function (opts, cb) {
var element = opts.getContainer();
if (!element) { return; }
2017-10-24 16:49:58 +00:00
var todo = function () {
2017-10-26 10:31:16 +00:00
if (opts.filter) { opts.filter(element, true); }
window.html2canvas(element, {
2017-10-24 16:49:58 +00:00
allowTaint: true,
onrendered: function (canvas) {
2017-10-26 10:31:16 +00:00
if (opts.filter) { opts.filter(element, false); }
setTimeout(function () {
var D = getResizedDimensions(canvas, 'pad');
Thumb.fromCanvas(canvas, D, cb);
}, 10);
2017-10-24 16:49:58 +00:00
}
});
};
2017-10-26 10:31:16 +00:00
if (window.html2canvas) { return void todo(); }
2017-10-24 16:49:58 +00:00
require(['/bower_components/html2canvas/build/html2canvas.min.js'], todo);
};
2017-11-07 13:51:53 +00:00
Thumb.initPadThumbnails = function (common, opts) {
2019-06-20 14:11:12 +00:00
if (!opts.type || !opts.getContent) {
throw new Error("type and getContent are needed for thumbnails");
}
var oldThumbnailState;
var mkThumbnail = function () {
var content = opts.getContent();
if (content === oldThumbnailState) { return; }
oldThumbnailState = content;
Thumb.fromDOM(opts, function (err, b64) {
2019-05-29 17:00:20 +00:00
Thumb.setPadThumbnail(common, opts.type, null, b64);
});
};
var nafa = Util.notAgainForAnother(mkThumbnail, Thumb.UPDATE_INTERVAL);
var to;
var tUntil;
var interval = function () {
tUntil = nafa();
if (tUntil) {
window.clearTimeout(to);
to = window.setTimeout(interval, tUntil+1);
return;
}
to = window.setTimeout(interval, Thumb.UPDATE_INTERVAL+1);
};
Visible.onChange(function (v) {
if (v) {
window.clearTimeout(to);
return;
}
interval();
});
if (!Visible.currently()) { to = window.setTimeout(interval, Thumb.UPDATE_FIRST); }
};
var addThumbnail = function (err, thumb, $span, cb) {
2019-09-13 13:44:35 +00:00
var split = thumb.split(',');
var u8 = Nacl.util.decodeBase64(split[1] || split[0]);
var blob = new Blob([u8], {
type: 'image/png'
});
var url = URL.createObjectURL(blob);
var img = new Image();
img.src = url;
$span.find('.cp-icon').hide();
$span.prepend(img);
cb($(img));
};
2018-03-13 10:31:08 +00:00
Thumb.addThumbnail = function(thumb, $span, cb) {
return addThumbnail(null, thumb, $span, cb);
};
2018-04-27 15:23:23 +00:00
var getKey = function (type, channel) {
return 'thumbnail-' + type + '-' + channel;
2017-11-07 13:51:53 +00:00
};
2019-05-29 17:00:20 +00:00
Thumb.setPadThumbnail = function (common, type, channel, b64, cb) {
cb = cb || function () {};
2018-04-27 16:04:21 +00:00
channel = channel || common.getMetadataMgr().getPrivateData().channel;
2019-05-29 17:00:20 +00:00
var k = getKey(type, channel);
2017-11-07 13:51:53 +00:00
common.setThumbnail(k, b64, cb);
};
2018-05-25 16:00:10 +00:00
Thumb.displayThumbnail = function (common, href, channel, password, $container, cb) {
cb = cb || function () {};
var parsed = Hash.parsePadUrl(href);
2018-04-27 15:23:23 +00:00
var k = getKey(parsed.type, channel);
var whenNewThumb = function () {
var privateData = common.getMetadataMgr().getPrivateData();
var fileHost = privateData.fileHost || privateData.origin;
2018-05-25 16:00:10 +00:00
var secret = Hash.getSecrets('file', parsed.hash, password);
var hexFileName = secret.channel;
var src = fileHost + Hash.getBlobPathFromHex(hexFileName);
2018-05-25 16:00:10 +00:00
var key = secret.keys && secret.keys.cryptKey;
MediaTag.fetchDecryptedMetadata(src, key, function (e, metadata) {
if (e) {
if (e === 'XHR_ERROR') { return; }
return console.error(e);
}
if (!metadata) { return console.error("NO_METADATA"); }
2017-11-07 13:51:53 +00:00
var v = metadata.thumbnail;
if (!v) {
v = 'EMPTY';
}
2019-05-29 17:00:20 +00:00
Thumb.setPadThumbnail(common, parsed.type, hexFileName, v, function (err) {
2017-11-07 13:51:53 +00:00
if (!metadata.thumbnail) { return; }
addThumbnail(err, metadata.thumbnail, $container, cb);
});
});
};
2017-11-07 13:51:53 +00:00
common.getThumbnail(k, function (err, v) {
if (!v && parsed.type === 'file') {
// We can only create thumbnails for files here since we can't easily decrypt pads
return void whenNewThumb();
}
if (!v) { return; }
if (v === 'EMPTY') { return; }
addThumbnail(err, v, $container, cb);
});
};
return Thumb;
});