New apps for text, slide and spreadsheet
@ -93,6 +93,18 @@
|
|||||||
@colortheme_todo-color: #000;
|
@colortheme_todo-color: #000;
|
||||||
@colortheme_todo-warn: #cd2532;
|
@colortheme_todo-warn: #cd2532;
|
||||||
|
|
||||||
|
@colortheme_oodoc-bg: #5170B5;
|
||||||
|
@colortheme_oodoc-color: #FFF;
|
||||||
|
@colortheme_oodoc-warn: #cd2532;
|
||||||
|
|
||||||
|
@colortheme_ooslide-bg: #c65d27;
|
||||||
|
@colortheme_ooslide-color: #FFF;
|
||||||
|
@colortheme_ooslide-warn: #cd2532;
|
||||||
|
|
||||||
|
@colortheme_oocell-bg: #7e983f;
|
||||||
|
@colortheme_oocell-color: #FFF;
|
||||||
|
@colortheme_oocell-warn: #cd2532;
|
||||||
|
|
||||||
// Sidebar layout (profile / settings)
|
// Sidebar layout (profile / settings)
|
||||||
@colortheme_sidebar-active: #fff;
|
@colortheme_sidebar-active: #fff;
|
||||||
@colortheme_sidebar-left-bg: #eee;
|
@colortheme_sidebar-left-bg: #eee;
|
||||||
|
|||||||
@ -13,6 +13,9 @@
|
|||||||
.cp-icon-color-profile { color: @colortheme_settings-bg; }
|
.cp-icon-color-profile { color: @colortheme_settings-bg; }
|
||||||
.cp-icon-color-default { color: @colortheme_default-bg; }
|
.cp-icon-color-default { color: @colortheme_default-bg; }
|
||||||
.cp-icon-color-todo { color: @colortheme_todo-bg; }
|
.cp-icon-color-todo { color: @colortheme_todo-bg; }
|
||||||
|
.cp-icon-color-oodoc { color: @colortheme_oodoc-bg; }
|
||||||
|
.cp-icon-color-ooslide { color: @colortheme_ooslide-bg; }
|
||||||
|
.cp-icon-color-oocell { color: @colortheme_oocell-bg; }
|
||||||
|
|
||||||
.cp-border-color-pad { border-color: @colortheme_pad-bg !important; }
|
.cp-border-color-pad { border-color: @colortheme_pad-bg !important; }
|
||||||
.cp-border-color-code { border-color: @colortheme_code-bg !important; }
|
.cp-border-color-code { border-color: @colortheme_code-bg !important; }
|
||||||
@ -26,5 +29,8 @@
|
|||||||
.cp-border-color-profile { border-color: @colortheme_settings-bg !important; }
|
.cp-border-color-profile { border-color: @colortheme_settings-bg !important; }
|
||||||
.cp-border-color-default { border-color: @colortheme_default-bg !important; }
|
.cp-border-color-default { border-color: @colortheme_default-bg !important; }
|
||||||
.cp-border-color-todo { border-color: @colortheme_todo-bg !important; }
|
.cp-border-color-todo { border-color: @colortheme_todo-bg !important; }
|
||||||
|
.cp-border-color-oodoc { border-color: @colortheme_oodoc-bg !important; }
|
||||||
|
.cp-border-color-ooslide { border-color: @colortheme_ooslide-bg !important; }
|
||||||
|
.cp-border-color-oocell { border-color: @colortheme_oocell-bg !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,5 +40,7 @@ body.cp-app-profile { @import "../../../profile/app-profile.less"; }
|
|||||||
body.cp-app-settings { @import "../../../settings/app-settings.less"; }
|
body.cp-app-settings { @import "../../../settings/app-settings.less"; }
|
||||||
body.cp-app-debug { @import "../../../debug/app-debug.less"; }
|
body.cp-app-debug { @import "../../../debug/app-debug.less"; }
|
||||||
body.cp-app-worker { @import "../../../worker/app-worker.less"; }
|
body.cp-app-worker { @import "../../../worker/app-worker.less"; }
|
||||||
body.cp-app-oo { @import "../../../onlyoffice/document/app-oo.less"; }
|
body.cp-app-oodoc { @import "../../../oodoc/app-oodoc.less"; }
|
||||||
|
body.cp-app-ooslide { @import "../../../ooslide/app-ooslide.less"; }
|
||||||
|
body.cp-app-oocell { @import "../../../oocell/app-oocell.less"; }
|
||||||
|
|
||||||
|
|||||||
@ -15,12 +15,18 @@ define(function () {
|
|||||||
out.type.media = "Média";
|
out.type.media = "Média";
|
||||||
out.type.todo = "Todo";
|
out.type.todo = "Todo";
|
||||||
out.type.contacts = "Contacts";
|
out.type.contacts = "Contacts";
|
||||||
|
out.type.oodoc = 'OnlyOffice Texte';
|
||||||
|
out.type.ooslide = 'OnlyOffice Présentation';
|
||||||
|
out.type.oocell = 'OnlyOffice Tableur';
|
||||||
|
|
||||||
out.button_newpad = 'Nouveau document texte';
|
out.button_newpad = 'Nouveau document texte';
|
||||||
out.button_newcode = 'Nouvelle page de code';
|
out.button_newcode = 'Nouvelle page de code';
|
||||||
out.button_newpoll = 'Nouveau sondage';
|
out.button_newpoll = 'Nouveau sondage';
|
||||||
out.button_newslide = 'Nouvelle présentation';
|
out.button_newslide = 'Nouvelle présentation';
|
||||||
out.button_newwhiteboard = 'Nouveau tableau blanc';
|
out.button_newwhiteboard = 'Nouveau tableau blanc';
|
||||||
|
out.button_newoodoc = 'Nouveau texte OnlyOffice';
|
||||||
|
out.button_newooslide = 'Nouvelle présentation OnlyOffice';
|
||||||
|
out.button_newoocell = 'Nouveau tableur OnlyOffice';
|
||||||
|
|
||||||
out.updated_0_common_connectionLost = "<b>Connexion au serveur perdue</b><br>Vous êtes désormais en mode lecture seule jusqu'au retour de la connexion.";
|
out.updated_0_common_connectionLost = "<b>Connexion au serveur perdue</b><br>Vous êtes désormais en mode lecture seule jusqu'au retour de la connexion.";
|
||||||
out.common_connectionLost = out.updated_0_common_connectionLost;
|
out.common_connectionLost = out.updated_0_common_connectionLost;
|
||||||
|
|||||||
@ -15,12 +15,18 @@ define(function () {
|
|||||||
out.type.media = 'Media';
|
out.type.media = 'Media';
|
||||||
out.type.todo = "Todo";
|
out.type.todo = "Todo";
|
||||||
out.type.contacts = 'Contacts';
|
out.type.contacts = 'Contacts';
|
||||||
|
out.type.oodoc = 'OnlyOffice Text';
|
||||||
|
out.type.ooslide = 'OnlyOffice Slide';
|
||||||
|
out.type.oocell = 'OnlyOffice Spreadsheet';
|
||||||
|
|
||||||
out.button_newpad = 'New Rich Text pad';
|
out.button_newpad = 'New Rich Text pad';
|
||||||
out.button_newcode = 'New Code pad';
|
out.button_newcode = 'New Code pad';
|
||||||
out.button_newpoll = 'New Poll';
|
out.button_newpoll = 'New Poll';
|
||||||
out.button_newslide = 'New Presentation';
|
out.button_newslide = 'New Presentation';
|
||||||
out.button_newwhiteboard = 'New Whiteboard';
|
out.button_newwhiteboard = 'New Whiteboard';
|
||||||
|
out.button_newoodoc = 'New OnlyOffice document';
|
||||||
|
out.button_newooslide = 'New OnlyOffice presentation';
|
||||||
|
out.button_newoocell = 'New OnlyOffice spreadsheet';
|
||||||
|
|
||||||
// NOTE: Remove updated_0_ if we need an updated_1_
|
// NOTE: Remove updated_0_ if we need an updated_1_
|
||||||
out.updated_0_common_connectionLost = "<b>Server Connection Lost</b><br>You're now in read-only mode until the connection is back.";
|
out.updated_0_common_connectionLost = "<b>Server Connection Lost</b><br>You're now in read-only mode until the connection is back.";
|
||||||
|
|||||||
10
ooserver.js
@ -12,7 +12,7 @@ var origin = config.httpUnsafeOrigin || 'http://localhost:3000/';
|
|||||||
exports.install = function(server, callbackFunction) {
|
exports.install = function(server, callbackFunction) {
|
||||||
var sockjs_opts = {sockjs_url: ""},
|
var sockjs_opts = {sockjs_url: ""},
|
||||||
sockjs_echo = sockjs.createServer(sockjs_opts),
|
sockjs_echo = sockjs.createServer(sockjs_opts),
|
||||||
urlParse = new RegExp("^/onlyoffice/doc/([0-9-.a-zA-Z_=]*)/c.+", 'i');
|
urlParse = new RegExp("^/common/onlyoffice/doc/([0-9-.a-zA-Z_=]*)/c.+", 'i');
|
||||||
|
|
||||||
console.log("Start ooserver");
|
console.log("Start ooserver");
|
||||||
console.log("Port " + sockjs_echo.port);
|
console.log("Port " + sockjs_echo.port);
|
||||||
@ -70,11 +70,11 @@ sockjs_echo.on('connection', function(conn) {
|
|||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case 'auth':
|
case 'auth':
|
||||||
console.log("Response auth");
|
console.log("Response auth");
|
||||||
var fileUrl = origin + "onlyoffice/document/test.bin";
|
var fileUrl = origin + "oodoc/test.bin";
|
||||||
if (data.openCmd.format=="xlsx")
|
if (data.openCmd.format=="xlsx")
|
||||||
fileUrl = origin + "onlyoffice/spreadsheet/test.bin"
|
fileUrl = origin + "oocell/test.bin"
|
||||||
else if (data.openCmd.format=="pptx")
|
else if (data.openCmd.format=="pptx")
|
||||||
fileUrl = origin + "onlyoffice/presentation/test.bin"
|
fileUrl = origin + "ooslide/test.bin"
|
||||||
sendData(conn, {"type":"auth","result":1,"sessionId":"08e77705-dc5c-477d-b73a-b1a7cbca1e9b","sessionTimeConnect":1494226099270,"participants":[]});
|
sendData(conn, {"type":"auth","result":1,"sessionId":"08e77705-dc5c-477d-b73a-b1a7cbca1e9b","sessionTimeConnect":1494226099270,"participants":[]});
|
||||||
sendData(conn, {"type":"documentOpen","data":{"type":"open","status":"ok","data":{"Editor.bin":fileUrl}}});
|
sendData(conn, {"type":"documentOpen","data":{"type":"open","status":"ok","data":{"Editor.bin":fileUrl}}});
|
||||||
break;
|
break;
|
||||||
@ -109,7 +109,7 @@ sockjs_echo.on('connection', function(conn) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
sockjs_echo.installHandlers(server, {prefix: '/onlyoffice/doc/[0-9-.a-zA-Z_=]*/c', log: function(severity, message) {
|
sockjs_echo.installHandlers(server, {prefix: '/common/onlyoffice/doc/[0-9-.a-zA-Z_=]*/c', log: function(severity, message) {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
|||||||
14
server.js
@ -202,7 +202,7 @@ var FONT_OBFUSCATION_MAGIC = new Buffer([
|
|||||||
|
|
||||||
|
|
||||||
var FONT_NAME_MAP = {};
|
var FONT_NAME_MAP = {};
|
||||||
[ './www/onlyoffice/fonts/' ].forEach(function (path) {
|
[ './www/common/onlyoffice/fonts/' ].forEach(function (path) {
|
||||||
Fs.readdir(path, function (err, list) {
|
Fs.readdir(path, function (err, list) {
|
||||||
if (err) { throw err; }
|
if (err) { throw err; }
|
||||||
list.forEach(function (fontName) {
|
list.forEach(function (fontName) {
|
||||||
@ -213,7 +213,7 @@ var FONT_NAME_MAP = {};
|
|||||||
|
|
||||||
/* Code to automatically transform font to js */
|
/* Code to automatically transform font to js */
|
||||||
/* Currently not active, but might be necessary */
|
/* Currently not active, but might be necessary */
|
||||||
app.use("/onlyoffice/fonts/odttf/:name", function (req, res) {
|
app.use("/common/onlyoffice/fonts/odttf/:name", function (req, res) {
|
||||||
var name = req.params.name.replace(/\.js$/, '').toLowerCase();
|
var name = req.params.name.replace(/\.js$/, '').toLowerCase();
|
||||||
console.log(name);
|
console.log(name);
|
||||||
if (!FONT_NAME_MAP[name]) {
|
if (!FONT_NAME_MAP[name]) {
|
||||||
@ -233,12 +233,14 @@ app.use("/onlyoffice/fonts/odttf/:name", function (req, res) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* All fonts file replaced by the list of fonts in ttf */
|
/* All fonts file replaced by the list of fonts in ttf */
|
||||||
app.use("/onlyoffice/sdkjs/common/AllFonts.js",
|
app.use("/common/onlyoffice/sdkjs/common/AllFonts.js",
|
||||||
Express.static("./www/onlyoffice/allfonts-noobf.js"));
|
Express.static("./www/common/onlyoffice/allfonts-noobf.js"));
|
||||||
|
|
||||||
/* Replace fonts thumbnail call */
|
/* Replace fonts thumbnail call */
|
||||||
app.use("/onlyoffice/sdkjs/common/Images/fonts_thumbnail@2x.png",
|
app.use("/common/onlyoffice/sdkjs/common/Images/fonts_thumbnail@2x.png",
|
||||||
Express.static("./www/onlyoffice/fonts_thumbnail.png"));
|
Express.static("./www/common/onlyoffice/fonts_thumbnail@2x.png"));
|
||||||
|
app.use("/common/onlyoffice/sdkjs/common/Images/fonts_thumbnail.png",
|
||||||
|
Express.static("./www/common/onlyoffice/fonts_thumbnail.png"));
|
||||||
|
|
||||||
|
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
|
|||||||
@ -9,7 +9,8 @@ define(function() {
|
|||||||
/* Select the buttons displayed on the main page to create new collaborative sessions
|
/* Select the buttons displayed on the main page to create new collaborative sessions
|
||||||
* Existing types : pad, code, poll, slide
|
* Existing types : pad, code, poll, slide
|
||||||
*/
|
*/
|
||||||
config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'whiteboard', 'file', 'todo', 'contacts'];
|
config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'whiteboard',
|
||||||
|
'oodoc', 'ooslide', 'oocell', 'file', 'todo', 'contacts'];
|
||||||
config.registeredOnlyTypes = ['file', 'contacts'];
|
config.registeredOnlyTypes = ['file', 'contacts'];
|
||||||
|
|
||||||
/* Cryptpad apps use a common API to display notifications to users
|
/* Cryptpad apps use a common API to display notifications to users
|
||||||
@ -81,6 +82,9 @@ define(function() {
|
|||||||
whiteboard: 'fa-paint-brush',
|
whiteboard: 'fa-paint-brush',
|
||||||
todo: 'fa-tasks',
|
todo: 'fa-tasks',
|
||||||
contacts: 'fa-users',
|
contacts: 'fa-users',
|
||||||
|
oodoc: 'fa-file-word-o',
|
||||||
|
ooslide: 'fa-file-powerpoint-o',
|
||||||
|
oocell: 'fa-file-excel-o',
|
||||||
};
|
};
|
||||||
|
|
||||||
// EXPERIMENTAL: Enabling "displayCreationScreen" may cause UI issues and possible loss of data
|
// EXPERIMENTAL: Enabling "displayCreationScreen" may cause UI issues and possible loss of data
|
||||||
|
|||||||
@ -672,12 +672,24 @@ define([
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case 'save': // OnlyOffice save
|
||||||
|
button = $('<button>', {
|
||||||
|
'class': 'fa fa-save',
|
||||||
|
title: 'TODO OnlyOffice save',
|
||||||
|
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'})
|
||||||
|
.text('TODO: OnlyOffice save'))
|
||||||
|
.click(common.prepareFeedback(type));
|
||||||
|
if (callback) { button.click(callback); }
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
button = $('<button>', {
|
button = $('<button>', {
|
||||||
'class': "fa fa-question",
|
'class': "fa fa-question",
|
||||||
style: 'font:'+size+' FontAwesome'
|
style: 'font:'+size+' FontAwesome'
|
||||||
})
|
})
|
||||||
.click(common.prepareFeedback(type));
|
.click(common.prepareFeedback(type));
|
||||||
|
if (callback) {
|
||||||
|
button.click(callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (rightside) {
|
if (rightside) {
|
||||||
button.addClass('cp-toolbar-rightside-button');
|
button.addClass('cp-toolbar-rightside-button');
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 477 KiB After Width: | Height: | Size: 477 KiB |
BIN
www/common/onlyoffice/fonts_thumbnail2.png
Normal file
|
After Width: | Height: | Size: 222 KiB |
@ -29,10 +29,13 @@ define([
|
|||||||
{
|
{
|
||||||
var saveAs = window.saveAs;
|
var saveAs = window.saveAs;
|
||||||
|
|
||||||
|
/*
|
||||||
var ooReady = window.frames[0] && window.frames[0].frames[0] && window.frames[0].frames[0].editor;
|
var ooReady = window.frames[0] && window.frames[0].frames[0] && window.frames[0].frames[0].editor;
|
||||||
window.onOOReady = function () {
|
window.onOOReady = function () {
|
||||||
|
console.log('ready!');
|
||||||
ooReady = true;
|
ooReady = true;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
var APP = window.APP = {
|
var APP = window.APP = {
|
||||||
$: $
|
$: $
|
||||||
@ -77,9 +80,12 @@ define([
|
|||||||
var loadDocument = APP.loadDocument = function (content, file) {
|
var loadDocument = APP.loadDocument = function (content, file) {
|
||||||
console.log("Read " + content);
|
console.log("Read " + content);
|
||||||
return;
|
return;
|
||||||
|
/*
|
||||||
|
// TODO: load a document from server here
|
||||||
window.frames[0].frames[0].editor.asc_CloseFile();
|
window.frames[0].frames[0].editor.asc_CloseFile();
|
||||||
var openResult = {bSerFormat: true, data: content, url: "http://localhost:3000/onlyoffice/", changes: null};
|
var openResult = {bSerFormat: true, data: content, url: "http://localhost:3000/onlyoffice/", changes: null};
|
||||||
window.frames[0].frames[0].editor.openDocument(openResult);
|
window.frames[0].frames[0].editor.openDocument(openResult);
|
||||||
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
var readOnly = false;
|
var readOnly = false;
|
||||||
@ -119,7 +125,6 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
APP.onLocal = config.onLocal = function () {
|
APP.onLocal = config.onLocal = function () {
|
||||||
console.log(initializing, readOnly);
|
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
if (readOnly) { return; }
|
if (readOnly) { return; }
|
||||||
|
|
||||||
@ -127,8 +132,9 @@ define([
|
|||||||
console.log("Cannot access editor");
|
console.log("Cannot access editor");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('ok');
|
|
||||||
var data = window.frames[0].frames[0].editor.asc_nativeGetFile();
|
var data = window.frames[0].frames[0].editor.asc_nativeGetFile();
|
||||||
|
console.log('onLocal, data avalable');
|
||||||
|
data = '';
|
||||||
var content = stringifyInner(data);
|
var content = stringifyInner(data);
|
||||||
APP.realtime.contentUpdate(content);
|
APP.realtime.contentUpdate(content);
|
||||||
};
|
};
|
||||||
@ -169,14 +175,9 @@ define([
|
|||||||
var $import = common.createButton('import', true, {}, loadDocument);
|
var $import = common.createButton('import', true, {}, loadDocument);
|
||||||
$rightside.append($import);
|
$rightside.append($import);
|
||||||
var $save = common.createButton('save', true, {}, saveToServer);
|
var $save = common.createButton('save', true, {}, saveToServer);
|
||||||
$save.click(function () {
|
|
||||||
saveToServer();
|
|
||||||
});
|
|
||||||
$rightside.append($save);
|
$rightside.append($save);
|
||||||
var $remote = common.createButton('remote', true, {}, callRemote);
|
var $remote = common.createButton('remote', true, {}, callRemote);
|
||||||
$remote.click(function () {
|
$remote.attr('title', 'call onRemote');
|
||||||
callRemote();
|
|
||||||
});
|
|
||||||
$rightside.append($remote);
|
$rightside.append($remote);
|
||||||
|
|
||||||
if (common.isLoggedIn()) {
|
if (common.isLoggedIn()) {
|
||||||
@ -293,9 +294,11 @@ define([
|
|||||||
var common;
|
var common;
|
||||||
|
|
||||||
nThen(function (waitFor) {
|
nThen(function (waitFor) {
|
||||||
|
/*
|
||||||
if (!ooReady) {
|
if (!ooReady) {
|
||||||
window.onOOReady = waitFor();
|
window.onOOReady = waitFor();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
$(waitFor(function () {
|
$(waitFor(function () {
|
||||||
UI.addLoadingScreen();
|
UI.addLoadingScreen();
|
||||||
}));
|
}));
|
||||||
@ -20,8 +20,8 @@ define([
|
|||||||
window.rc = requireConfig;
|
window.rc = requireConfig;
|
||||||
window.apiconf = ApiConfig;
|
window.apiconf = ApiConfig;
|
||||||
document.getElementById('sbox-iframe').setAttribute('src',
|
document.getElementById('sbox-iframe').setAttribute('src',
|
||||||
ApiConfig.httpSafeOrigin + '/onlyoffice/document/inner.html?' + requireConfig.urlArgs +
|
ApiConfig.httpSafeOrigin + window.location.pathname + 'inner.html?' +
|
||||||
'#' + encodeURIComponent(JSON.stringify(req)));
|
requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req)));
|
||||||
|
|
||||||
// This is a cheap trick to avoid loading sframe-channel in parallel with the
|
// This is a cheap trick to avoid loading sframe-channel in parallel with the
|
||||||
// loading screen setup.
|
// loading screen setup.
|
||||||
1
www/common/onlyoffice/plugins.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 902 B After Width: | Height: | Size: 902 B |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 163 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 540 KiB After Width: | Height: | Size: 540 KiB |