support fixing up dynamically loaded SVGs

This commit is contained in:
Matthew Hodgson 2016-01-05 03:34:52 +00:00
parent 555abdae30
commit 001dc8612b
7 changed files with 74 additions and 25 deletions

View file

@ -45,6 +45,7 @@ var commands = {
tint: function(room_id, args) { tint: function(room_id, args) {
Tinter.tint(args); Tinter.tint(args);
return success();
}, },
encrypt: function(room_id, args) { encrypt: function(room_id, args) {

View file

@ -14,6 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
var dis = require('./dispatcher');
var registered = false;
if (!registered) {
dis.register(_onAction);
}
var keyRgb = [ var keyRgb = [
"rgb(118, 207, 166)", "rgb(118, 207, 166)",
"rgb(234, 245, 240)", "rgb(234, 245, 240)",
@ -30,6 +37,15 @@ var keyRgb = [
var keyHex = [ var keyHex = [
"#76CFA6", "#76CFA6",
"#EAF5F0", "#EAF5F0",
"#D3EFE1",
];
// cache of our replacement colours
// defaults to our keys.
var colors = [
keyHex[0],
keyHex[1],
keyHex[2],
]; ];
var cssFixups = [ var cssFixups = [
@ -60,6 +76,8 @@ var svgAttrs = [
"stroke", "stroke",
]; ];
var svgNodes = {};
var cached = false; var cached = false;
function calcCssFixups() { function calcCssFixups() {
@ -83,9 +101,11 @@ function calcCssFixups() {
} }
} }
function calcSvgFixups() { function calcSvgFixups(nodes) {
var svgs = document.getElementsByClassName("mx_Svg"); var svgs = nodes || document.getElementsByClassName("mx_Svg");
var fixups = [];
for (var i = 0; i < svgs.length; i++) { for (var i = 0; i < svgs.length; i++) {
var svgDoc = svgs[i].contentDocument; var svgDoc = svgs[i].contentDocument;
if (!svgDoc) continue; if (!svgDoc) continue;
var tags = svgDoc.getElementsByTagName("*"); var tags = svgDoc.getElementsByTagName("*");
@ -95,7 +115,7 @@ function calcSvgFixups() {
var attr = svgAttrs[k]; var attr = svgAttrs[k];
for (var l = 0; l < keyHex.length; l++) { for (var l = 0; l < keyHex.length; l++) {
if (tag.getAttribute(attr) && tag.getAttribute(attr).toUpperCase() === keyHex[l]) { if (tag.getAttribute(attr) && tag.getAttribute(attr).toUpperCase() === keyHex[l]) {
svgFixups.push({ fixups.push({
node: tag, node: tag,
attr: attr, attr: attr,
index: l, index: l,
@ -105,26 +125,35 @@ function calcSvgFixups() {
} }
} }
} }
return fixups;
} }
function applyCssFixups(primaryColor, secondaryColor, tertiaryColor) { function applyCssFixups() {
var colors = [primaryColor, secondaryColor, tertiaryColor];
for (var i = 0; i < cssFixups.length; i++) { for (var i = 0; i < cssFixups.length; i++) {
var cssFixup = cssFixups[i]; var cssFixup = cssFixups[i];
cssFixup.style[cssFixup.attr] = colors[cssFixup.index]; cssFixup.style[cssFixup.attr] = colors[cssFixup.index];
} }
} }
function applySvgFixups(primaryColor, secondaryColor, tertiaryColor) { function applySvgFixups(fixups) {
var colors = [primaryColor, secondaryColor, tertiaryColor]; for (var i = 0; i < fixups.length; i++) {
var svgFixup = fixups[i];
for (var i = 0; i < svgFixups.length; i++) {
var svgFixup = svgFixups[i];
svgFixup.node.setAttribute(svgFixup.attr, colors[svgFixup.index]); svgFixup.node.setAttribute(svgFixup.attr, colors[svgFixup.index]);
} }
} }
function _onAction(payload) {
if (payload.action !== "svg_onload") return;
// XXX: we should probably faff around with toggling the visibility of the node to avoid flashing the wrong colour.
// (although this would result in an even worse flicker as the element redraws)
var fixups = calcSvgFixups([ payload.svg ]);
if (fixups.length) {
svgFixups = svgFixups.concat(fixups); // XXX: this leaks fixups
applySvgFixups(fixups);
}
}
function hexToRgb(color) { function hexToRgb(color) {
if (color[0] === '#') color = color.slice(1); if (color[0] === '#') color = color.slice(1);
if (color.length === 3) { if (color.length === 3) {
@ -148,7 +177,7 @@ module.exports = {
tint: function(primaryColor, secondaryColor, tertiaryColor) { tint: function(primaryColor, secondaryColor, tertiaryColor) {
if (!cached) { if (!cached) {
calcCssFixups(); calcCssFixups();
calcSvgFixups(); svgFixups = calcSvgFixups();
cached = true; cached = true;
} }
@ -171,13 +200,15 @@ module.exports = {
tertiaryColor = rgbToHex(rgb1); tertiaryColor = rgbToHex(rgb1);
} }
colors = [primaryColor, secondaryColor, tertiaryColor];
// go through manually fixing up the stylesheets. // go through manually fixing up the stylesheets.
applyCssFixups(primaryColor, secondaryColor, tertiaryColor); applyCssFixups();
// go through manually fixing up SVG colours. // go through manually fixing up SVG colours.
// we could do this by stylesheets, but keeping the stylesheets // we could do this by stylesheets, but keeping the stylesheets
// updated would be a PITA, so just brute-force search for the // updated would be a PITA, so just brute-force search for the
// key colour; cache the element and apply. // key colour; cache the element and apply.
applySvgFixups(primaryColor, secondaryColor, tertiaryColor); applySvgFixups(svgFixups);
} }
}; };

View file

@ -1093,6 +1093,10 @@ module.exports = React.createClass({
}); });
}, },
onSvgLoad: function(event) {
dis.dispatch({ action: "svg_onload", svg: event.target });
},
render: function() { render: function() {
var RoomHeader = sdk.getComponent('rooms.RoomHeader'); var RoomHeader = sdk.getComponent('rooms.RoomHeader');
var MessageComposer = sdk.getComponent('rooms.MessageComposer'); var MessageComposer = sdk.getComponent('rooms.MessageComposer');
@ -1194,7 +1198,7 @@ module.exports = React.createClass({
<div className="mx_RoomView_tabCompleteWrapper"> <div className="mx_RoomView_tabCompleteWrapper">
<TabCompleteBar entries={this.tabComplete.peek(6)} /> <TabCompleteBar entries={this.tabComplete.peek(6)} />
<div className="mx_RoomView_tabCompleteEol" title="->|"> <div className="mx_RoomView_tabCompleteEol" title="->|">
<object className="mx_Svg" type="image/svg+xml" data="img/eol.svg" width="22" height="16"/> <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/eol.svg" width="22" height="16"/>
Auto-complete Auto-complete
</div> </div>
</div> </div>
@ -1268,7 +1272,7 @@ module.exports = React.createClass({
if (this.state.draggingFile) { if (this.state.draggingFile) {
fileDropTarget = <div className="mx_RoomView_fileDropTarget"> fileDropTarget = <div className="mx_RoomView_fileDropTarget">
<div className="mx_RoomView_fileDropTargetLabel" title="Drop File Here"> <div className="mx_RoomView_fileDropTargetLabel" title="Drop File Here">
<object className="mx_Svg" type="image/svg+xml" data="img/upload-big.svg" width="45" height="59"/><br/> <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/upload-big.svg" width="45" height="59"/><br/>
Drop File Here Drop File Here
</div> </div>
</div>; </div>;
@ -1306,7 +1310,7 @@ module.exports = React.createClass({
if (call.type === "video") { if (call.type === "video") {
zoomButton = ( zoomButton = (
<div className="mx_RoomView_voipButton" onClick={this.onFullscreenClick} title="Fill screen"> <div className="mx_RoomView_voipButton" onClick={this.onFullscreenClick} title="Fill screen">
<object className="mx_Svg" type="image/svg+xml" data="img/fullscreen.svg" width="29" height="22" style={{ marginTop: 1, marginRight: 4 }}/> <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/fullscreen.svg" width="29" height="22" style={{ marginTop: 1, marginRight: 4 }}/>
</div> </div>
); );
@ -1338,7 +1342,7 @@ module.exports = React.createClass({
{ videoMuteButton } { videoMuteButton }
{ zoomButton } { zoomButton }
{ statusBar } { statusBar }
<object className="mx_Svg" type="image/svg+xml" className="mx_RoomView_voipChevron" data="img/voip-chevron.svg" width="22" height="17"/> <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" className="mx_RoomView_voipChevron" data="img/voip-chevron.svg" width="22" height="17"/>
</div> </div>
} }

View file

@ -19,6 +19,7 @@ limitations under the License.
var React = require('react'); var React = require('react');
var filesize = require('filesize'); var filesize = require('filesize');
var MatrixClientPeg = require('../../../MatrixClientPeg'); var MatrixClientPeg = require('../../../MatrixClientPeg');
var dis = require("../../../dispatcher");
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'MFileBody', displayName: 'MFileBody',
@ -45,6 +46,10 @@ module.exports = React.createClass({
return linkText; return linkText;
}, },
onSvgLoad: function(event) {
dis.dispatch({ action: "svg_onload", svg: event.target });
},
render: function() { render: function() {
var content = this.props.mxEvent.getContent(); var content = this.props.mxEvent.getContent();
var cli = MatrixClientPeg.get(); var cli = MatrixClientPeg.get();
@ -57,7 +62,7 @@ module.exports = React.createClass({
<span className="mx_MFileBody"> <span className="mx_MFileBody">
<div className="mx_MImageBody_download"> <div className="mx_MImageBody_download">
<a href={cli.mxcUrlToHttp(content.url)} target="_blank"> <a href={cli.mxcUrlToHttp(content.url)} target="_blank">
<img src="img/download.png" width="10" height="12"/> <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/download.svg" width="12" height="14"/>
Download {text} Download {text}
</a> </a>
</div> </div>

View file

@ -118,7 +118,7 @@ module.exports = React.createClass({
</a> </a>
<div className="mx_MImageBody_download"> <div className="mx_MImageBody_download">
<a href={cli.mxcUrlToHttp(content.url)} target="_blank"> <a href={cli.mxcUrlToHttp(content.url)} target="_blank">
<img src="img/download.png" width="10" height="12"/> <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/download.svg" width="12" height="14"/>
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" }) Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
</a> </a>
</div> </div>

View file

@ -457,6 +457,10 @@ module.exports = React.createClass({
}); });
}, },
onSvgLoad: function(event) {
dis.dispatch({ action: "svg_onload", svg: event.target });
},
render: function() { render: function() {
var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId); var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
var uploadInputStyle = {display: 'none'}; var uploadInputStyle = {display: 'none'};
@ -474,11 +478,11 @@ module.exports = React.createClass({
else { else {
callButton = callButton =
<div className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick} title="Voice call"> <div className="mx_MessageComposer_voicecall" onClick={this.onVoiceCallClick} title="Voice call">
<object className="mx_Svg" type="image/svg+xml" data="img/voice.svg" width="16" height="26"/> <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/voice.svg" width="16" height="26"/>
</div> </div>
videoCallButton = videoCallButton =
<div className="mx_MessageComposer_videocall" onClick={this.onCallClick} title="Video call"> <div className="mx_MessageComposer_videocall" onClick={this.onCallClick} title="Video call">
<object className="mx_Svg" type="image/svg+xml" data="img/call.svg" width="30" height="22"/> <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/call.svg" width="30" height="22"/>
</div> </div>
} }
@ -493,7 +497,7 @@ module.exports = React.createClass({
<textarea ref="textarea" rows="1" onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} placeholder="Type a message..." /> <textarea ref="textarea" rows="1" onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} placeholder="Type a message..." />
</div> </div>
<div className="mx_MessageComposer_upload" onClick={this.onUploadClick} title="Upload file"> <div className="mx_MessageComposer_upload" onClick={this.onUploadClick} title="Upload file">
<object className="mx_Svg" type="image/svg+xml" data="img/upload.svg" width="19" height="24"/> <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/upload.svg" width="19" height="24"/>
<input type="file" style={uploadInputStyle} ref="uploadInput" onChange={this.onUploadFileSelected} /> <input type="file" style={uploadInputStyle} ref="uploadInput" onChange={this.onUploadFileSelected} />
</div> </div>
{ hangupButton } { hangupButton }

View file

@ -66,6 +66,10 @@ module.exports = React.createClass({
getRoomName: function() { getRoomName: function() {
return this.refs.name_edit.value; return this.refs.name_edit.value;
}, },
onSvgLoad: function(event) {
dis.dispatch({ action: "svg_onload", svg: event.target });
},
render: function() { render: function() {
var EditableText = sdk.getComponent("elements.EditableText"); var EditableText = sdk.getComponent("elements.EditableText");
@ -136,7 +140,7 @@ module.exports = React.createClass({
if (this.props.onLeaveClick) { if (this.props.onLeaveClick) {
leave_button = leave_button =
<div className="mx_RoomHeader_button mx_RoomHeader_leaveButton" onClick={this.props.onLeaveClick} title="Leave room"> <div className="mx_RoomHeader_button mx_RoomHeader_leaveButton" onClick={this.props.onLeaveClick} title="Leave room">
<object className="mx_Svg" type="image/svg+xml" data="img/leave.svg" <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/leave.svg"
width="26" height="20"/> width="26" height="20"/>
</div>; </div>;
} }
@ -145,7 +149,7 @@ module.exports = React.createClass({
if (this.props.onForgetClick) { if (this.props.onForgetClick) {
forget_button = forget_button =
<div className="mx_RoomHeader_button mx_RoomHeader_leaveButton" onClick={this.props.onForgetClick} title="Forget room"> <div className="mx_RoomHeader_button mx_RoomHeader_leaveButton" onClick={this.props.onForgetClick} title="Forget room">
<object className="mx_Svg" type="image/svg+xml" data="img/leave.svg" <object onLoad={ this.onSvgLoad } className="mx_Svg" type="image/svg+xml" data="img/leave.svg"
width="26" height="20"/> width="26" height="20"/>
</div>; </div>;
} }