Compare commits

...

2 Commits

@ -1,4 +1,5 @@
'use strict'; 'use strict';
const React = require('react'); const React = require('react');
const create = require('create-react-class'); const create = require('create-react-class');
const Promise = require('bluebird'); const Promise = require('bluebird');

@ -1,7 +1,6 @@
'use strict'; 'use strict';
const React = require('react'); const React = require('react');
const create = require('create-react-class'); const create = require('create-react-class');
const jdenticon = require('jdenticon');
const sanitize = require('sanitize-html'); const sanitize = require('sanitize-html');
const Event = require('./events/Event.js'); const Event = require('./events/Event.js');
@ -10,17 +9,7 @@ const Input = require('./input.js');
const User = require('./events/user.js'); const User = require('./events/user.js');
const Loading = require('./loading.js'); const Loading = require('./loading.js');
jdenticon.config = { const generateJdenticon = require("../lib/generate-jdenticon");
lightness: {
color: [0.58, 0.66],
grayscale: [0.30, 0.90]
},
saturation: {
color: 0.66,
grayscale: 0.00
},
backColor: "#00000000"
};
let eventFunctions = { let eventFunctions = {
plaintext: function() { plaintext: function() {
@ -202,7 +191,7 @@ let EventGroup = create({
}, },
avatarRef: function(ref) { avatarRef: function(ref) {
jdenticon.update(ref, this.state.user.userId); generateJdenticon(this.state.user.userId).update(ref);
}, },
render: function() { render: function() {

@ -1,39 +1,17 @@
'use strict'; "use strict";
const React = require('react');
const create = require('create-react-class');
const jdenticon = require('jdenticon');
jdenticon.config = { const React = require("react");
lightness: { const create = require("create-react-class");
color: [0.58, 0.66],
grayscale: [0.30, 0.90] const generateJdenticon = require("../../lib/generate-jdenticon");
},
saturation: {
color: 0.66,
grayscale: 0.00
},
backColor: "#00000000"
};
let User = create({ let User = create({
displayName: "user", displayName: "user",
getInitialState: function() { getInitialState: function() {
let icon = jdenticon.toSvg(this.props.user.userId, 200);
let match = icon.match(/#([a-f0-9]{6})/g);
let color = '#ff0000';
/* FIXME: Replace with a .find */
for(let i=match.length-1; i>= 0; i--) {
color = match[i];
let r = color.substr(1, 2);
let g = color.substr(3, 2);
let b = color.substr(5, 2);
if (r != g && g != b) { // not greyscale
break;
}
}
return { return {
color: color /* FIXME: Cache this to speed it up */
color: generateJdenticon(this.props.user.userId).primaryColor()
}; };
}, },

@ -23,5 +23,5 @@ module.exports = function createApiRequester(apiUrl) {
} }
}); });
}); });
} };
}; };

@ -0,0 +1,49 @@
"use strict";
const jdenticon = require("jdenticon");
const defaultValue = require("default-value");
module.exports = function generateJdenticon(name) {
function setConfig() {
/* NOTE: This is a hack to ensure that any other code using the `jdenticon` library can't mess with our config, since it's set globally. This function gets called prior to *every* jdenticon-generating operation. */
jdenticon.config = {
lightness: {
color: [0.58, 0.66],
grayscale: [0.30, 0.90]
},
saturation: {
color: 0.66,
grayscale: 0.00
},
backColor: "#00000000"
};
}
return {
toSvg: function (size) {
setConfig();
return jdenticon.toSvg(name, size);
},
primaryColor: function () {
let svg = this.toSvg();
let color = svg.match(/#([a-f0-9]{6})/g).find((candidate) => {
let r = candidate.substr(1, 2);
let g = candidate.substr(3, 2);
let b = candidate.substr(5, 2);
let isGrayScale = (r === g && g === b);
return !isGrayScale;
});
return defaultValue(color, "#ff0000");
},
update: function (element) {
setConfig();
jdenticon.update(element, name);
}
}
};

@ -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 = Object.assign(document.createElement("canvas"), {
width: targetWidth,
height: targetHeight
});
const canvas = document.createElement("canvas");
canvas.width = targetWidth;
canvas.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];
} }

Loading…
Cancel
Save