manual merge, still wip
This commit is contained in:
commit
368a6b2406
@ -11,6 +11,7 @@ www/common/hyperscript.js
|
|||||||
www/common/tippy.min.js
|
www/common/tippy.min.js
|
||||||
|
|
||||||
www/pad/wysiwygarea-plugin.js
|
www/pad/wysiwygarea-plugin.js
|
||||||
www/pad2/wysiwygarea-plugin.js
|
www/pad/mediatag-plugin.js
|
||||||
|
www/pad/mediatag-plugin-dialog.js
|
||||||
|
|
||||||
www/common/media-tag-nacl.min.js
|
www/common/media-tag-nacl.min.js
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
"jquery": "~2.1.3",
|
"jquery": "~2.1.3",
|
||||||
"tweetnacl": "0.12.2",
|
"tweetnacl": "0.12.2",
|
||||||
"components-font-awesome": "^4.6.3",
|
"components-font-awesome": "^4.6.3",
|
||||||
"ckeditor": "~4.7",
|
"ckeditor": "4.7.3",
|
||||||
"codemirror": "^5.19.0",
|
"codemirror": "^5.19.0",
|
||||||
"requirejs": "2.3.5",
|
"requirejs": "2.3.5",
|
||||||
"marked": "0.3.5",
|
"marked": "0.3.5",
|
||||||
|
|||||||
@ -10,7 +10,7 @@ CKEDITOR.editorConfig = function( config ) {
|
|||||||
// document itself and causes problems when it's sent across the wire and reflected back
|
// document itself and causes problems when it's sent across the wire and reflected back
|
||||||
config.removePlugins= 'resize,elementspath';
|
config.removePlugins= 'resize,elementspath';
|
||||||
config.resize_enabled= false; //bottom-bar
|
config.resize_enabled= false; //bottom-bar
|
||||||
config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify';
|
config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify,mediatag';
|
||||||
config.toolbarGroups= [
|
config.toolbarGroups= [
|
||||||
// {"name":"clipboard","groups":["clipboard","undo"]},
|
// {"name":"clipboard","groups":["clipboard","undo"]},
|
||||||
//{"name":"editing","groups":["find","selection"]},
|
//{"name":"editing","groups":["find","selection"]},
|
||||||
|
|||||||
@ -15,6 +15,7 @@ var map = {
|
|||||||
var getStoredLanguage = function () { return localStorage.getItem(LS_LANG); };
|
var getStoredLanguage = function () { return localStorage.getItem(LS_LANG); };
|
||||||
var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage; };
|
var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage; };
|
||||||
var getLanguage = function () {
|
var getLanguage = function () {
|
||||||
|
if (window.cryptpadLanguage) { return window.cryptpadLanguage; }
|
||||||
if (getStoredLanguage()) { return getStoredLanguage(); }
|
if (getStoredLanguage()) { return getStoredLanguage(); }
|
||||||
var l = getBrowserLanguage() || '';
|
var l = getBrowserLanguage() || '';
|
||||||
if (Object.keys(map).indexOf(l) !== -1) {
|
if (Object.keys(map).indexOf(l) !== -1) {
|
||||||
|
|||||||
@ -570,69 +570,66 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var appToolbar = function () {
|
var appToolbar = function () {
|
||||||
return h('div#toolbar.toolbar-container');
|
return h('div#cp-toolbar.cp-toolbar-container');
|
||||||
};
|
};
|
||||||
|
|
||||||
Pages['/whiteboard/'] = Pages['/whiteboard/index.html'] = function () {
|
Pages['/whiteboard/'] = Pages['/whiteboard/index.html'] = function () {
|
||||||
return [
|
return [
|
||||||
appToolbar(),
|
appToolbar(),
|
||||||
h('div#canvas-area', h('canvas#canvas', {
|
h('div#cp-app-whiteboard-canvas-area', h('canvas#cp-app-whiteboard-canvas', {
|
||||||
width: 600,
|
width: 600,
|
||||||
height: 600
|
height: 600
|
||||||
})),
|
})),
|
||||||
h('div#controls', {
|
h('div#cp-app-whiteboard-controls', {
|
||||||
style: {
|
style: {
|
||||||
display: 'block',
|
display: 'block',
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
h('button#clear.btn.btn-danger', Msg.canvas_clear), ' ',
|
h('button#cp-app-whiteboard-clear.btn.btn-danger', Msg.canvas_clear), ' ',
|
||||||
h('button#toggleDraw.btn.btn-secondary', Msg.canvas_disable),
|
h('button#cp-app-whiteboard-toggledraw.btn.btn-secondary', Msg.canvas_disable),
|
||||||
h('button#delete.btn.btn-secondary', {
|
h('button#cp-app-whiteboard-delete.btn.btn-secondary', {
|
||||||
style: {
|
style: {
|
||||||
display: 'none',
|
display: 'none',
|
||||||
}
|
}
|
||||||
}, Msg.canvas_delete),
|
}, Msg.canvas_delete),
|
||||||
h('div.range-group', [
|
h('div.cp-app-whiteboard-range-group', [
|
||||||
h('label', {
|
h('label', {
|
||||||
'for': 'width'
|
'for': 'cp-app-whiteboard-width'
|
||||||
}, Msg.canvas_width),
|
}, Msg.canvas_width),
|
||||||
h('input#width', {
|
h('input#cp-app-whiteboard-width', {
|
||||||
type: 'range',
|
type: 'range',
|
||||||
value: "5",
|
|
||||||
min: "1",
|
min: "1",
|
||||||
max: "100"
|
max: "100"
|
||||||
}),
|
}),
|
||||||
h('span#width-val', '5px')
|
h('span#cp-app-whiteboard-width-val', '5px')
|
||||||
]),
|
]),
|
||||||
h('div.range-group', [
|
h('div.cp-app-whiteboard-range-group', [
|
||||||
h('label', {
|
h('label', {
|
||||||
'for': 'opacity',
|
'for': 'cp-app-whiteboard-opacity',
|
||||||
}, Msg.canvas_opacity),
|
}, Msg.canvas_opacity),
|
||||||
h('input#opacity', {
|
h('input#cp-app-whiteboard-opacity', {
|
||||||
type: 'range',
|
type: 'range',
|
||||||
value: "1",
|
|
||||||
min: "0.1",
|
min: "0.1",
|
||||||
max: "1",
|
max: "1",
|
||||||
step: "0.1"
|
step: "0.1"
|
||||||
}),
|
}),
|
||||||
h('span#opacity-val', '100%')
|
h('span#cp-app-whiteboard-opacity-val', '100%')
|
||||||
]),
|
]),
|
||||||
h('span.selected', [
|
h('span.cp-app-whiteboard-selected.cp-app-whiteboard-unselectable', [
|
||||||
h('img', {
|
h('img', {
|
||||||
title: Msg.canvas_currentBrush
|
title: Msg.canvas_currentBrush
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
setHTML(h('div#colors'), ' '),
|
setHTML(h('div#cp-app-whiteboard-colors'), ' '),
|
||||||
loadingScreen(),
|
h('div#cp-app-whiteboard-cursors', {
|
||||||
h('div#cursors', {
|
|
||||||
style: {
|
style: {
|
||||||
display: 'none',
|
display: 'none',
|
||||||
background: 'white',
|
background: 'white',
|
||||||
'text-align': 'center',
|
'text-align': 'center',
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
h('div#pickers'),
|
h('div#cp-app-whiteboard-pickers'),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -683,8 +680,7 @@ define([
|
|||||||
])
|
])
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
]),
|
])
|
||||||
loadingScreen()
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,11 @@
|
|||||||
td {
|
td {
|
||||||
padding: @upload_pad_h @upload_pad_v;
|
padding: @upload_pad_h @upload_pad_v;
|
||||||
}
|
}
|
||||||
|
.cp-fileupload-table-link {
|
||||||
|
.fa {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
.cp-fileupload-table-progress {
|
.cp-fileupload-table-progress {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.font_neuropolitical () {
|
.font_neuropolitical () {
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Neuropolitical;
|
font-family: Neuropolitical;
|
||||||
src: url(./customize/fonts/neuropolitical.ttf)
|
src: url(/customize/fonts/neuropolitical.ttf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.font_open-sans () {
|
.font_open-sans () {
|
||||||
|
|||||||
@ -13,11 +13,12 @@
|
|||||||
height: auto;
|
height: auto;
|
||||||
min-height: 34px;
|
min-height: 34px;
|
||||||
padding-bottom: 0px;
|
padding-bottom: 0px;
|
||||||
&.focus {
|
background-color: unset;
|
||||||
border-color: #66afe9;
|
border: none;
|
||||||
outline: 0;
|
display: flex;
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);
|
flex-wrap: wrap;
|
||||||
}
|
align-items: center;
|
||||||
|
padding: 0 10px;
|
||||||
.token {
|
.token {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@ -25,8 +26,9 @@
|
|||||||
border: 1px solid #d9d9d9;
|
border: 1px solid #d9d9d9;
|
||||||
background-color: #ededed;
|
background-color: #ededed;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin: -1px 5px 5px 0;
|
margin: 10px 5px;
|
||||||
vertical-align: center;
|
height: 24px;
|
||||||
|
vertical-align: middle;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
color: #222;
|
color: #222;
|
||||||
@ -50,17 +52,17 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
vertical-align: center;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.close {
|
.close {
|
||||||
font-family: Arial;
|
font-family: Arial;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
line-height: 100%;
|
line-height: 24px;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
float: none;
|
float: none;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
vertical-align: center;
|
vertical-align: middle;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
&.active {
|
&.active {
|
||||||
@ -73,11 +75,10 @@
|
|||||||
}
|
}
|
||||||
.token-input {
|
.token-input {
|
||||||
background: none;
|
background: none;
|
||||||
width: 0%; //60px;
|
flex: 1;
|
||||||
min-width: 60px;
|
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-bottom: 6px;
|
margin: 0 !important; // Override alertify
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
&:focus {
|
&:focus {
|
||||||
@ -86,9 +87,5 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.disabled {
|
|
||||||
cursor: not-allowed;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
@import (once) "./colortheme.less";
|
@import (once) "./colortheme.less";
|
||||||
|
|
||||||
.history_main () {
|
.history_main () {
|
||||||
body .cp-toolbar-history {
|
.cp-toolbar-history {
|
||||||
display: none;
|
display: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
* {
|
* {
|
||||||
|
|||||||
@ -658,6 +658,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
p.cp-toolbar-account {
|
||||||
|
&> span {
|
||||||
|
font-weight: bold;
|
||||||
|
span {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.cp-toolbar-backup {
|
.cp-toolbar-backup {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|||||||
@ -27,4 +27,6 @@ body.cp-app-code { @import "../../../code/app-code.less"; }
|
|||||||
body.cp-app-slide { @import "../../../slide/app-slide.less"; }
|
body.cp-app-slide { @import "../../../slide/app-slide.less"; }
|
||||||
body.cp-app-file { @import "../../../file/app-file.less"; }
|
body.cp-app-file { @import "../../../file/app-file.less"; }
|
||||||
body.cp-app-filepicker { @import "../../../filepicker/app-filepicker.less"; }
|
body.cp-app-filepicker { @import "../../../filepicker/app-filepicker.less"; }
|
||||||
|
//body.cp-app-poll { @import "../../../poll/app-poll.less"; }
|
||||||
|
body.cp-app-whiteboard { @import "../../../whiteboard/app-whiteboard.less"; }
|
||||||
|
|
||||||
|
|||||||
@ -157,6 +157,10 @@ define(function () {
|
|||||||
out.filePicker_filter = "Filtrez les fichiers par leur nom";
|
out.filePicker_filter = "Filtrez les fichiers par leur nom";
|
||||||
out.or = 'ou';
|
out.or = 'ou';
|
||||||
|
|
||||||
|
out.tags_title = "Mots-clés du pad";
|
||||||
|
out.tags_add = "Modifier les mots-clés du pad";
|
||||||
|
out.tags_duplicate = "Mot-clé déjà présent : {0}";
|
||||||
|
|
||||||
out.slideOptionsText = "Options";
|
out.slideOptionsText = "Options";
|
||||||
out.slideOptionsTitle = "Personnaliser la présentation";
|
out.slideOptionsTitle = "Personnaliser la présentation";
|
||||||
out.slideOptionsButton = "Enregistrer (Entrée)";
|
out.slideOptionsButton = "Enregistrer (Entrée)";
|
||||||
@ -204,8 +208,11 @@ define(function () {
|
|||||||
out.history_restoreDone = "Document restauré";
|
out.history_restoreDone = "Document restauré";
|
||||||
out.history_version = "Version :";
|
out.history_version = "Version :";
|
||||||
|
|
||||||
// Ckeditor links
|
// Ckeditor
|
||||||
out.openLinkInNewTab = "Ouvrir le lien dans un nouvel onglet";
|
out.openLinkInNewTab = "Ouvrir le lien dans un nouvel onglet";
|
||||||
|
out.pad_mediatagTitle = "Options du Media-Tag";
|
||||||
|
out.pad_mediatagWidth = "Largeur (px)";
|
||||||
|
out.pad_mediatagHeight = "Hauteur (px)";
|
||||||
|
|
||||||
// Polls
|
// Polls
|
||||||
|
|
||||||
@ -363,6 +370,8 @@ define(function () {
|
|||||||
out.fm_error_cantPin = "Erreur interne du serveur. Veuillez recharger la page et essayer de nouveau.";
|
out.fm_error_cantPin = "Erreur interne du serveur. Veuillez recharger la page et essayer de nouveau.";
|
||||||
out.fm_viewListButton = "Liste";
|
out.fm_viewListButton = "Liste";
|
||||||
out.fm_viewGridButton = "Grille";
|
out.fm_viewGridButton = "Grille";
|
||||||
|
out.fm_renamedPad = "Vous avez renommé ce pad dans votre Drive. Son titre est:<br><b>{0}</b>";
|
||||||
|
out.fm_prop_tagsList = "Mots-clés";
|
||||||
// File - Context menu
|
// File - Context menu
|
||||||
out.fc_newfolder = "Nouveau dossier";
|
out.fc_newfolder = "Nouveau dossier";
|
||||||
out.fc_rename = "Renommer";
|
out.fc_rename = "Renommer";
|
||||||
|
|||||||
@ -159,6 +159,10 @@ define(function () {
|
|||||||
out.filePicker_filter = "Filter files by name";
|
out.filePicker_filter = "Filter files by name";
|
||||||
out.or = 'or';
|
out.or = 'or';
|
||||||
|
|
||||||
|
out.tags_title = "Tags";
|
||||||
|
out.tags_add = "Update this pad's tags";
|
||||||
|
out.tags_duplicate = "Duplicate tag: {0}";
|
||||||
|
|
||||||
out.slideOptionsText = "Options";
|
out.slideOptionsText = "Options";
|
||||||
out.slideOptionsTitle = "Customize your slides";
|
out.slideOptionsTitle = "Customize your slides";
|
||||||
out.slideOptionsButton = "Save (enter)";
|
out.slideOptionsButton = "Save (enter)";
|
||||||
@ -206,8 +210,11 @@ define(function () {
|
|||||||
out.history_restoreDone = "Document restored";
|
out.history_restoreDone = "Document restored";
|
||||||
out.history_version = "Version:";
|
out.history_version = "Version:";
|
||||||
|
|
||||||
// Ckeditor links
|
// Ckeditor
|
||||||
out.openLinkInNewTab = "Open Link in New Tab";
|
out.openLinkInNewTab = "Open Link in New Tab";
|
||||||
|
out.pad_mediatagTitle = "Media-Tag settings";
|
||||||
|
out.pad_mediatagWidth = "Width (px)";
|
||||||
|
out.pad_mediatagHeight = "Height (px)";
|
||||||
|
|
||||||
// Polls
|
// Polls
|
||||||
|
|
||||||
@ -364,6 +371,8 @@ define(function () {
|
|||||||
out.fm_error_cantPin = "Internal server error. Please reload the page and try again.";
|
out.fm_error_cantPin = "Internal server error. Please reload the page and try again.";
|
||||||
out.fm_viewListButton = "List view";
|
out.fm_viewListButton = "List view";
|
||||||
out.fm_viewGridButton = "Grid view";
|
out.fm_viewGridButton = "Grid view";
|
||||||
|
out.fm_renamedPad = "You've set a custom name for this pad. Its shared title is:<br><b>{0}</b>";
|
||||||
|
out.fm_prop_tagsList = "Tags";
|
||||||
// File - Context menu
|
// File - Context menu
|
||||||
out.fc_newfolder = "New folder";
|
out.fc_newfolder = "New folder";
|
||||||
out.fc_rename = "Rename";
|
out.fc_rename = "Rename";
|
||||||
|
|||||||
@ -3,10 +3,12 @@
|
|||||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||||
|
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||||
|
|
||||||
.toolbar_main();
|
.toolbar_main();
|
||||||
.fileupload_main();
|
.fileupload_main();
|
||||||
.alertify_main();
|
.alertify_main();
|
||||||
|
.tokenfield_main();
|
||||||
|
|
||||||
// body
|
// body
|
||||||
&.cp-app-code {
|
&.cp-app-code {
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<html class="cp-app-noscroll">
|
<html class="cp-app-noscroll">
|
||||||
<head>
|
<head>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
<script async data-bootload="/code/inner.js" data-main="/common/sframe-boot.js?ver=1.4" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
<script async data-bootload="/code/inner.js" data-main="/common/sframe-boot.js?ver=1.5" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||||
<style>
|
<style>
|
||||||
.loading-hidden { display: none; }
|
.loading-hidden { display: none; }
|
||||||
#editor1 { display: none; }
|
#editor1 { display: none; }
|
||||||
|
|||||||
@ -230,7 +230,7 @@ define([
|
|||||||
if (data !== false) {
|
if (data !== false) {
|
||||||
$previewContainer.show();
|
$previewContainer.show();
|
||||||
APP.$previewButton.addClass('active');
|
APP.$previewButton.addClass('active');
|
||||||
$codeMirror.removeClass('fullPage');
|
$codeMirror.removeClass('cp-app-code-fullpage');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -238,7 +238,7 @@ define([
|
|||||||
APP.$previewButton.hide();
|
APP.$previewButton.hide();
|
||||||
$previewContainer.hide();
|
$previewContainer.hide();
|
||||||
APP.$previewButton.removeClass('active');
|
APP.$previewButton.removeClass('active');
|
||||||
$codeMirror.addClass('fullPage');
|
$codeMirror.addClass('cp-app-code-fullpage');
|
||||||
};
|
};
|
||||||
|
|
||||||
config.onInit = function (info) {
|
config.onInit = function (info) {
|
||||||
@ -374,11 +374,13 @@ define([
|
|||||||
};
|
};
|
||||||
common.openFilePicker(pickerCfg);
|
common.openFilePicker(pickerCfg);
|
||||||
}).appendTo($rightside);
|
}).appendTo($rightside);
|
||||||
|
|
||||||
|
var $tags = common.createButton('hashtag', true);
|
||||||
|
$rightside.append($tags);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
config.onReady = function (info) {
|
config.onReady = function (info) {
|
||||||
console.log('onready');
|
|
||||||
if (APP.realtime !== info.realtime) {
|
if (APP.realtime !== info.realtime) {
|
||||||
var realtime = APP.realtime = info.realtime;
|
var realtime = APP.realtime = info.realtime;
|
||||||
APP.patchText = TextPatcher.create({
|
APP.patchText = TextPatcher.create({
|
||||||
@ -400,7 +402,8 @@ define([
|
|||||||
metadataMgr.updateMetadata(hjson.metadata);
|
metadataMgr.updateMetadata(hjson.metadata);
|
||||||
}
|
}
|
||||||
if (typeof (hjson) !== 'object' || Array.isArray(hjson) ||
|
if (typeof (hjson) !== 'object' || Array.isArray(hjson) ||
|
||||||
(typeof(hjson.type) !== 'undefined' && hjson.type !== 'code')) {
|
(hjson.metadata && typeof(hjson.metadata.type) !== 'undefined' &&
|
||||||
|
hjson.metadata.type !== 'code')) {
|
||||||
var errorText = Messages.typeError;
|
var errorText = Messages.typeError;
|
||||||
Cryptpad.errorLoadingScreen(errorText);
|
Cryptpad.errorLoadingScreen(errorText);
|
||||||
throw new Error(errorText);
|
throw new Error(errorText);
|
||||||
@ -437,35 +440,35 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
// add the splitter
|
// add the splitter
|
||||||
if (!$iframe.has('.cp-splitter').length) {
|
if (!$('.cp-splitter').length) {
|
||||||
var $preview = $iframe.find('#previewContainer');
|
|
||||||
var splitter = $('<div>', {
|
var splitter = $('<div>', {
|
||||||
'class': 'cp-splitter'
|
'class': 'cp-splitter'
|
||||||
}).appendTo($preview);
|
}).appendTo($previewContainer);
|
||||||
|
|
||||||
$preview.on('scroll', function() {
|
$preview.on('scroll', function() {
|
||||||
splitter.css('top', $preview.scrollTop() + 'px');
|
splitter.css('top', $preview.scrollTop() + 'px');
|
||||||
});
|
});
|
||||||
|
|
||||||
var $target = $iframe.find('.CodeMirror');
|
var $target = $('.CodeMirror');
|
||||||
|
|
||||||
splitter.on('mousedown', function (e) {
|
splitter.on('mousedown', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var x = e.pageX;
|
var x = e.pageX;
|
||||||
var w = $target.width();
|
var w = $target.width();
|
||||||
|
|
||||||
$iframe.on('mouseup mousemove', function handler(evt) {
|
$(window).on('mouseup mousemove', function handler(evt) {
|
||||||
if (evt.type === 'mouseup') {
|
if (evt.type === 'mouseup') {
|
||||||
$iframe.off('mouseup mousemove', handler);
|
$(window).off('mouseup mousemove', handler);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$target.css('width', (w - x + evt.pageX) + 'px');
|
$target.css('width', (w - x + evt.pageX) + 'px');
|
||||||
|
editor.refresh();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
Cryptpad.removeLoadingScreen();
|
Cryptpad.removeLoadingScreen();
|
||||||
setEditable(!readOnly);
|
setEditable(!readOnly);
|
||||||
@ -533,7 +536,7 @@ define([
|
|||||||
APP.patchText(shjson2);
|
APP.patchText(shjson2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oldDoc !== remoteDoc) { Cryptpad.notify(); }
|
if (oldDoc !== remoteDoc) { common.notify(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
config.onAbort = function () {
|
config.onAbort = function () {
|
||||||
@ -604,7 +607,7 @@ define([
|
|||||||
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
|
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
|
||||||
}).nThen(function (/*waitFor*/) {
|
}).nThen(function (/*waitFor*/) {
|
||||||
CodeMirror = common.initCodeMirrorApp(null, CM);
|
CodeMirror = common.initCodeMirrorApp(null, CM);
|
||||||
$('.CodeMirror').addClass('fullPage');
|
$('.CodeMirror').addClass('cp-app-code-fullpage');
|
||||||
editor = CodeMirror.editor;
|
editor = CodeMirror.editor;
|
||||||
Cryptpad.onError(function (info) {
|
Cryptpad.onError(function (info) {
|
||||||
if (info && info.type === "store") {
|
if (info && info.type === "store") {
|
||||||
|
|||||||
@ -120,6 +120,7 @@ define([
|
|||||||
}
|
}
|
||||||
console.log('CACHE MISS ' + url);
|
console.log('CACHE MISS ' + url);
|
||||||
((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) {
|
((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) {
|
||||||
|
if (err) { console.error(err); }
|
||||||
var output = fixAllURLs(css, url);
|
var output = fixAllURLs(css, url);
|
||||||
cachePut(url, output);
|
cachePut(url, output);
|
||||||
inject(output, url);
|
inject(output, url);
|
||||||
|
|||||||
@ -131,13 +131,19 @@ define([
|
|||||||
var $t = t.tokenfield = $(t.element).tokenfield();
|
var $t = t.tokenfield = $(t.element).tokenfield();
|
||||||
t.getTokens = function () {
|
t.getTokens = function () {
|
||||||
return $t.tokenfield('getTokens').map(function (token) {
|
return $t.tokenfield('getTokens').map(function (token) {
|
||||||
return token.value;
|
return token.value.toLowerCase();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var $root = $t.parent();
|
||||||
|
$t.on('tokenfield:removetoken', function () {
|
||||||
|
$root.find('.token-input').focus();
|
||||||
|
});
|
||||||
|
|
||||||
t.preventDuplicates = function (cb) {
|
t.preventDuplicates = function (cb) {
|
||||||
$t.on('tokenfield:createtoken', function (ev) {
|
$t.on('tokenfield:createtoken', function (ev) {
|
||||||
var val;
|
var val;
|
||||||
|
ev.attrs.value = ev.attrs.value.toLowerCase();
|
||||||
if (t.getTokens().some(function (t) {
|
if (t.getTokens().some(function (t) {
|
||||||
if (t === ev.attrs.value) { return ((val = t)); }
|
if (t === ev.attrs.value) { return ((val = t)); }
|
||||||
})) {
|
})) {
|
||||||
@ -152,8 +158,8 @@ define([
|
|||||||
$t.tokenfield('setTokens',
|
$t.tokenfield('setTokens',
|
||||||
tokens.map(function (token) {
|
tokens.map(function (token) {
|
||||||
return {
|
return {
|
||||||
value: token,
|
value: token.toLowerCase(),
|
||||||
label: token,
|
label: token.toLowerCase(),
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
@ -171,35 +177,38 @@ define([
|
|||||||
var input = dialog.textInput();
|
var input = dialog.textInput();
|
||||||
|
|
||||||
var tagger = dialog.frame([
|
var tagger = dialog.frame([
|
||||||
dialog.message('make some tags'), // TODO translate
|
dialog.message(Messages.tags_add),
|
||||||
input,
|
input,
|
||||||
dialog.nav(),
|
dialog.nav(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var field = UI.tokenField(input).preventDuplicates(function (val) {
|
var field = UI.tokenField(input).preventDuplicates(function (val) {
|
||||||
UI.warn('Duplicate tag: ' + val); // TODO translate
|
UI.warn(Messages._getKey('tags_duplicate', [val]));
|
||||||
});
|
});
|
||||||
|
|
||||||
var close = Util.once(function () {
|
var listener;
|
||||||
var $t = $(tagger).fadeOut(150, function () { $t.remove(); });
|
var close = Util.once(function (result, ev) {
|
||||||
|
var $frame = $(tagger).fadeOut(150, function () {
|
||||||
|
stopListening(listener);
|
||||||
|
$frame.remove();
|
||||||
|
cb(result, ev);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var listener = listenForKeys(function () {}, function () {
|
var $ok = findOKButton(tagger).click(function () {
|
||||||
close();
|
|
||||||
stopListening(listener);
|
|
||||||
});
|
|
||||||
|
|
||||||
var CB = Util.once(cb);
|
|
||||||
findOKButton(tagger).click(function () {
|
|
||||||
var tokens = field.getTokens();
|
var tokens = field.getTokens();
|
||||||
close();
|
close(tokens);
|
||||||
CB(tokens);
|
|
||||||
});
|
});
|
||||||
findCancelButton(tagger).click(function () {
|
var $cancel = findCancelButton(tagger).click(function () {
|
||||||
close();
|
close(null);
|
||||||
CB(null);
|
});
|
||||||
|
listenForKeys(function () {
|
||||||
|
$ok.click();
|
||||||
|
}, function () {
|
||||||
|
$cancel.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(tagger);
|
||||||
// :(
|
// :(
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
field.setTokens(tags);
|
field.setTokens(tags);
|
||||||
|
|||||||
@ -129,6 +129,10 @@ define([
|
|||||||
return messenger.range_requests[txid];
|
return messenger.range_requests[txid];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var deleteRangeRequest = function (txid) {
|
||||||
|
delete messenger.range_requests[txid];
|
||||||
|
};
|
||||||
|
|
||||||
messenger.getMoreHistory = function (curvePublic, hash, count, cb) {
|
messenger.getMoreHistory = function (curvePublic, hash, count, cb) {
|
||||||
if (typeof(cb) !== 'function') { return; }
|
if (typeof(cb) !== 'function') { return; }
|
||||||
|
|
||||||
@ -393,7 +397,8 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
orderMessages(curvePublic, decrypted, req.sig);
|
orderMessages(curvePublic, decrypted, req.sig);
|
||||||
return void req.cb(void 0, decrypted);
|
req.cb(void 0, decrypted);
|
||||||
|
return deleteRangeRequest(txid);
|
||||||
} else {
|
} else {
|
||||||
console.log(parsed);
|
console.log(parsed);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ define([], function () {
|
|||||||
handlers.splice(handlers.indexOf(cb), 1);
|
handlers.splice(handlers.indexOf(cb), 1);
|
||||||
},
|
},
|
||||||
fire: function () {
|
fire: function () {
|
||||||
if (fired) { return; }
|
if (once && fired) { return; }
|
||||||
fired = true;
|
fired = true;
|
||||||
var args = Array.prototype.slice.call(arguments);
|
var args = Array.prototype.slice.call(arguments);
|
||||||
handlers.forEach(function (h) { h.apply(null, args); });
|
handlers.forEach(function (h) { h.apply(null, args); });
|
||||||
@ -203,5 +203,9 @@ define([], function () {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Util.slice = function (A) {
|
||||||
|
return Array.prototype.slice.call(A);
|
||||||
|
};
|
||||||
|
|
||||||
return Util;
|
return Util;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -20,9 +20,10 @@ define([
|
|||||||
'/common/pinpad.js',
|
'/common/pinpad.js',
|
||||||
'/customize/application_config.js',
|
'/customize/application_config.js',
|
||||||
'/common/media-tag.js',
|
'/common/media-tag.js',
|
||||||
|
'/bower_components/nthen/index.js',
|
||||||
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata,
|
], function ($, Config, Messages, Store, Util, Hash, UI, History, UserList, Title, Metadata,
|
||||||
Messaging, CodeMirror, Files, FileCrypto, Realtime, Clipboard,
|
Messaging, CodeMirror, Files, FileCrypto, Realtime, Clipboard,
|
||||||
Pinpad, AppConfig, MediaTag) {
|
Pinpad, AppConfig, MediaTag, Nthen) {
|
||||||
|
|
||||||
// Configure MediaTags to use our local viewer
|
// Configure MediaTags to use our local viewer
|
||||||
if (MediaTag && MediaTag.PdfPlugin) {
|
if (MediaTag && MediaTag.PdfPlugin) {
|
||||||
@ -102,6 +103,7 @@ define([
|
|||||||
common.getAppType = Util.getAppType;
|
common.getAppType = Util.getAppType;
|
||||||
common.notAgainForAnother = Util.notAgainForAnother;
|
common.notAgainForAnother = Util.notAgainForAnother;
|
||||||
common.uid = Util.uid;
|
common.uid = Util.uid;
|
||||||
|
common.slice = Util.slice;
|
||||||
|
|
||||||
// import hash utilities for export
|
// import hash utilities for export
|
||||||
var createRandomHash = common.createRandomHash = Hash.createRandomHash;
|
var createRandomHash = common.createRandomHash = Hash.createRandomHash;
|
||||||
@ -182,6 +184,9 @@ define([
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
common.getLanguage = function () {
|
||||||
|
return Messages._languageUsed;
|
||||||
|
};
|
||||||
common.getUserlist = function () {
|
common.getUserlist = function () {
|
||||||
if (store) {
|
if (store) {
|
||||||
if (store.getProxy() && store.getProxy().info) {
|
if (store.getProxy() && store.getProxy().info) {
|
||||||
@ -531,6 +536,17 @@ define([
|
|||||||
cb(void 0, entry);
|
cb(void 0, entry);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
common.resetTags = function (href, tags, cb) {
|
||||||
|
cb = cb || $.noop;
|
||||||
|
if (!Array.isArray(tags)) { return void cb('INVALID_TAGS'); }
|
||||||
|
getFileEntry(href, function (e, entry) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
if (!entry) { cb('NO_ENTRY'); }
|
||||||
|
entry.tags = tags.slice();
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
common.tagPad = function (href, tag, cb) {
|
common.tagPad = function (href, tag, cb) {
|
||||||
if (typeof(cb) !== 'function') {
|
if (typeof(cb) !== 'function') {
|
||||||
return void console.error('EXPECTED_CALLBACK');
|
return void console.error('EXPECTED_CALLBACK');
|
||||||
@ -2071,23 +2087,8 @@ define([
|
|||||||
|
|
||||||
return function (f) {
|
return function (f) {
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
return void window.setTimeout(function () {
|
return void setTimeout(function () { f(void 0, env); });
|
||||||
f(void 0, env);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
var block = 0;
|
|
||||||
|
|
||||||
var cb = function () {
|
|
||||||
block--;
|
|
||||||
if (!block) {
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
updateLocalVersion();
|
|
||||||
common.addTooltips();
|
|
||||||
f(void 0, env);
|
|
||||||
if (typeof(window.onhashchange) === 'function') { window.onhashchange(); }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (sessionStorage[newPadNameKey]) {
|
if (sessionStorage[newPadNameKey]) {
|
||||||
common.initialName = sessionStorage[newPadNameKey];
|
common.initialName = sessionStorage[newPadNameKey];
|
||||||
@ -2097,7 +2098,6 @@ define([
|
|||||||
common.initialPath = sessionStorage[newPadPathKey];
|
common.initialPath = sessionStorage[newPadPathKey];
|
||||||
delete sessionStorage[newPadPathKey];
|
delete sessionStorage[newPadPathKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
common.onFriendRequest = function (confirmText, cb) {
|
common.onFriendRequest = function (confirmText, cb) {
|
||||||
common.confirm(confirmText, cb, null, true);
|
common.confirm(confirmText, cb, null, true);
|
||||||
};
|
};
|
||||||
@ -2105,20 +2105,9 @@ define([
|
|||||||
common.log(data.logText);
|
common.log(data.logText);
|
||||||
};
|
};
|
||||||
|
|
||||||
Store.ready(function (err, storeObj) {
|
var proxy;
|
||||||
store = common.store = env.store = storeObj;
|
var network;
|
||||||
common.addDirectMessageHandler(common);
|
var provideFeedback = function () {
|
||||||
|
|
||||||
var proxy = getProxy();
|
|
||||||
var network = getNetwork();
|
|
||||||
|
|
||||||
network.on('disconnect', function () {
|
|
||||||
Realtime.setConnectionState(false);
|
|
||||||
});
|
|
||||||
network.on('reconnect', function () {
|
|
||||||
Realtime.setConnectionState(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Object.keys(proxy).length === 1) {
|
if (Object.keys(proxy).length === 1) {
|
||||||
feedback("FIRST_APP_USE", true);
|
feedback("FIRST_APP_USE", true);
|
||||||
}
|
}
|
||||||
@ -2141,110 +2130,123 @@ define([
|
|||||||
}
|
}
|
||||||
common.reportScreenDimensions();
|
common.reportScreenDimensions();
|
||||||
common.reportLanguage();
|
common.reportLanguage();
|
||||||
|
};
|
||||||
|
|
||||||
$(function() {
|
Nthen(function (waitFor) {
|
||||||
// Race condition : if document.body is undefined when alertify.js is loaded, Alertify
|
Store.ready(waitFor(function (err, storeObj) {
|
||||||
// won't work. We have to reset it now to make sure it uses a correct "body"
|
store = common.store = env.store = storeObj;
|
||||||
UI.Alertify.reset();
|
common.addDirectMessageHandler(common);
|
||||||
// clear any tooltips that might get hung
|
proxy = getProxy();
|
||||||
setInterval(function () { common.clearTooltips(); }, 5000);
|
network = getNetwork();
|
||||||
|
network.on('disconnect', function () {
|
||||||
// Load the new pad when the hash has changed
|
Realtime.setConnectionState(false);
|
||||||
var oldHref = document.location.href;
|
|
||||||
window.onhashchange = function () {
|
|
||||||
var newHref = document.location.href;
|
|
||||||
var parsedOld = parsePadUrl(oldHref).hashData;
|
|
||||||
var parsedNew = parsePadUrl(newHref).hashData;
|
|
||||||
if (parsedOld && parsedNew && (
|
|
||||||
parsedOld.type !== parsedNew.type
|
|
||||||
|| parsedOld.channel !== parsedNew.channel
|
|
||||||
|| parsedOld.mode !== parsedNew.mode
|
|
||||||
|| parsedOld.key !== parsedNew.key)) {
|
|
||||||
if (!parsedOld.channel) { oldHref = newHref; return; }
|
|
||||||
document.location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (parsedNew) {
|
|
||||||
oldHref = newHref;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (PINNING_ENABLED && isLoggedIn()) {
|
|
||||||
console.log("logged in. pads will be pinned");
|
|
||||||
block++;
|
|
||||||
|
|
||||||
Pinpad.create(network, proxy, function (e, call) {
|
|
||||||
if (e) {
|
|
||||||
console.error(e);
|
|
||||||
return cb();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('RPC handshake complete');
|
|
||||||
rpc = common.rpc = env.rpc = call;
|
|
||||||
|
|
||||||
common.getPinLimit(function (e, limit, plan, note) {
|
|
||||||
if (e) { return void console.error(e); }
|
|
||||||
common.account.limit = limit;
|
|
||||||
localStorage.plan = common.account.plan = plan;
|
|
||||||
common.account.note = note;
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
|
|
||||||
common.arePinsSynced(function (err, yes) {
|
|
||||||
if (!yes) {
|
|
||||||
common.resetPins(function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.error("Pin Reset Error");
|
|
||||||
return console.error(err);
|
|
||||||
}
|
|
||||||
console.log('RESET DONE');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else if (PINNING_ENABLED) {
|
|
||||||
console.log('not logged in. pads will not be pinned');
|
|
||||||
} else {
|
|
||||||
console.log('pinning disabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
block++;
|
|
||||||
require([
|
|
||||||
'/common/rpc.js',
|
|
||||||
], function (Rpc) {
|
|
||||||
Rpc.createAnonymous(network, function (e, call) {
|
|
||||||
if (e) {
|
|
||||||
console.error(e);
|
|
||||||
return void cb();
|
|
||||||
}
|
|
||||||
anon_rpc = common.anon_rpc = env.anon_rpc = call;
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
network.on('reconnect', function () {
|
||||||
|
Realtime.setConnectionState(true);
|
||||||
|
});
|
||||||
|
provideFeedback();
|
||||||
|
}), common);
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
$(waitFor());
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
// Race condition : if document.body is undefined when alertify.js is loaded, Alertify
|
||||||
|
// won't work. We have to reset it now to make sure it uses a correct "body"
|
||||||
|
UI.Alertify.reset();
|
||||||
|
// clear any tooltips that might get hung
|
||||||
|
setInterval(function () { common.clearTooltips(); }, 5000);
|
||||||
|
|
||||||
|
// Load the new pad when the hash has changed
|
||||||
// Everything's ready, continue...
|
var oldHref = document.location.href;
|
||||||
if($('#pad-iframe').length) {
|
window.onhashchange = function () {
|
||||||
block++;
|
var newHref = document.location.href;
|
||||||
var $iframe = $('#pad-iframe');
|
var parsedOld = parsePadUrl(oldHref).hashData;
|
||||||
var iframe = $iframe[0];
|
var parsedNew = parsePadUrl(newHref).hashData;
|
||||||
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
if (parsedOld && parsedNew && (
|
||||||
if (iframeDoc.readyState === 'complete') {
|
parsedOld.type !== parsedNew.type
|
||||||
cb();
|
|| parsedOld.channel !== parsedNew.channel
|
||||||
return;
|
|| parsedOld.mode !== parsedNew.mode
|
||||||
}
|
|| parsedOld.key !== parsedNew.key)) {
|
||||||
$iframe.load(cb);
|
if (!parsedOld.channel) { oldHref = newHref; return; }
|
||||||
|
document.location.reload();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (parsedNew) { oldHref = newHref; }
|
||||||
|
};
|
||||||
|
|
||||||
block++;
|
if (PINNING_ENABLED && isLoggedIn()) {
|
||||||
cb();
|
console.log("logged in. pads will be pinned");
|
||||||
|
var w0 = waitFor();
|
||||||
|
Pinpad.create(network, proxy, function (e, call) {
|
||||||
|
if (e) {
|
||||||
|
console.error(e);
|
||||||
|
return w0();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('RPC handshake complete');
|
||||||
|
rpc = common.rpc = env.rpc = call;
|
||||||
|
|
||||||
|
common.getPinLimit(function (e, limit, plan, note) {
|
||||||
|
if (e) { return void console.error(e); }
|
||||||
|
common.account.limit = limit;
|
||||||
|
localStorage.plan = common.account.plan = plan;
|
||||||
|
common.account.note = note;
|
||||||
|
w0();
|
||||||
|
});
|
||||||
|
|
||||||
|
common.arePinsSynced(function (err, yes) {
|
||||||
|
if (!yes) {
|
||||||
|
common.resetPins(function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.error("Pin Reset Error");
|
||||||
|
return console.error(err);
|
||||||
|
}
|
||||||
|
console.log('RESET DONE');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (PINNING_ENABLED) {
|
||||||
|
console.log('not logged in. pads will not be pinned');
|
||||||
|
} else {
|
||||||
|
console.log('pinning disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
var w1 = waitFor();
|
||||||
|
require([
|
||||||
|
'/common/rpc.js',
|
||||||
|
], function (Rpc) {
|
||||||
|
Rpc.createAnonymous(network, function (e, call) {
|
||||||
|
if (e) {
|
||||||
|
console.error(e);
|
||||||
|
return void w1();
|
||||||
|
}
|
||||||
|
anon_rpc = common.anon_rpc = env.anon_rpc = call;
|
||||||
|
w1();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}, common);
|
|
||||||
|
// Everything's ready, continue...
|
||||||
|
if($('#pad-iframe').length) {
|
||||||
|
var w2 = waitFor();
|
||||||
|
var $iframe = $('#pad-iframe');
|
||||||
|
var iframe = $iframe[0];
|
||||||
|
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||||
|
if (iframeDoc.readyState === 'complete') {
|
||||||
|
return void w2();
|
||||||
|
}
|
||||||
|
$iframe.load(w2); //cb);
|
||||||
|
}
|
||||||
|
}).nThen(function () {
|
||||||
|
updateLocalVersion();
|
||||||
|
common.addTooltips();
|
||||||
|
f(void 0, env);
|
||||||
|
if (typeof(window.onhashchange) === 'function') { window.onhashchange(); }
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
// MAGIC that happens implicitly
|
||||||
$(function () {
|
$(function () {
|
||||||
Messages._applyTranslation();
|
Messages._applyTranslation();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -99,7 +99,7 @@ function context () {
|
|||||||
} else if (k.substr(0, 5) === "data-") {
|
} else if (k.substr(0, 5) === "data-") {
|
||||||
e.setAttribute(k, l[k])
|
e.setAttribute(k, l[k])
|
||||||
} else {
|
} else {
|
||||||
e[k] = l[k]
|
e.setAttribute(k, l[k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ('function' === typeof l) {
|
} else if ('function' === typeof l) {
|
||||||
|
|||||||
@ -77,5 +77,19 @@ define([], function () {
|
|||||||
Cryptpad.feedback('Migrate-2', true);
|
Cryptpad.feedback('Migrate-2', true);
|
||||||
userObject.version = version = 2;
|
userObject.version = version = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Migration 3: language from localStorage to settings
|
||||||
|
var migrateLanguage = function () {
|
||||||
|
if (!localStorage.CRYPTPAD_LANG) { return; }
|
||||||
|
var l = localStorage.CRYPTPAD_LANG;
|
||||||
|
userObject.settings.language = l;
|
||||||
|
};
|
||||||
|
if (version < 3) {
|
||||||
|
migrateLanguage();
|
||||||
|
Cryptpad.feedback('Migrate-3', true);
|
||||||
|
userObject.version = version = 3;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,15 +3,11 @@ define([
|
|||||||
'/bower_components/hyperjson/hyperjson.js',
|
'/bower_components/hyperjson/hyperjson.js',
|
||||||
'/common/toolbar3.js',
|
'/common/toolbar3.js',
|
||||||
'/bower_components/chainpad-json-validator/json-ot.js',
|
'/bower_components/chainpad-json-validator/json-ot.js',
|
||||||
'/common/TypingTests.js',
|
|
||||||
'json.sortify',
|
'json.sortify',
|
||||||
'/bower_components/textpatcher/TextPatcher.js',
|
'/bower_components/textpatcher/TextPatcher.js',
|
||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/cryptget.js',
|
|
||||||
'/pad/links.js',
|
|
||||||
'/bower_components/nthen/index.js',
|
'/bower_components/nthen/index.js',
|
||||||
'/common/sframe-common.js',
|
'/common/sframe-common.js',
|
||||||
'/api/config',
|
|
||||||
'/customize/messages.js',
|
'/customize/messages.js',
|
||||||
'/common/common-util.js',
|
'/common/common-util.js',
|
||||||
|
|
||||||
@ -23,15 +19,11 @@ define([
|
|||||||
Hyperjson,
|
Hyperjson,
|
||||||
Toolbar,
|
Toolbar,
|
||||||
JsonOT,
|
JsonOT,
|
||||||
TypingTest,
|
|
||||||
JSONSortify,
|
JSONSortify,
|
||||||
TextPatcher,
|
TextPatcher,
|
||||||
Cryptpad,
|
Cryptpad,
|
||||||
Cryptget,
|
|
||||||
Links,
|
|
||||||
nThen,
|
nThen,
|
||||||
SFCommon,
|
SFCommon,
|
||||||
ApiConfig,
|
|
||||||
Messages,
|
Messages,
|
||||||
Util)
|
Util)
|
||||||
{
|
{
|
||||||
@ -90,12 +82,8 @@ define([
|
|||||||
return meta;
|
return meta;
|
||||||
};
|
};
|
||||||
|
|
||||||
var isEditable = function () {
|
|
||||||
return (state === STATE.READY && !readOnly);
|
|
||||||
};
|
|
||||||
|
|
||||||
var stateChange = function (newState) {
|
var stateChange = function (newState) {
|
||||||
var wasEditable = isEditable();
|
var wasEditable = (state === STATE.READY);
|
||||||
if (state === STATE.INFINITE_SPINNER) { return; }
|
if (state === STATE.INFINITE_SPINNER) { return; }
|
||||||
if (newState === STATE.INFINITE_SPINNER) {
|
if (newState === STATE.INFINITE_SPINNER) {
|
||||||
state = newState;
|
state = newState;
|
||||||
@ -106,6 +94,7 @@ define([
|
|||||||
} else {
|
} else {
|
||||||
state = newState;
|
state = newState;
|
||||||
}
|
}
|
||||||
|
console.log(state + ' ' + wasEditable);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case STATE.DISCONNECTED:
|
case STATE.DISCONNECTED:
|
||||||
case STATE.INITIALIZING: {
|
case STATE.INITIALIZING: {
|
||||||
@ -118,7 +107,10 @@ define([
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
if (wasEditable !== isEditable()) { evEditableStateChange.fire(isEditable()); }
|
if (wasEditable !== (state === STATE.READY)) {
|
||||||
|
console.log("fire");
|
||||||
|
evEditableStateChange.fire(state === STATE.READY);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var onRemote = function () {
|
var onRemote = function () {
|
||||||
|
|||||||
@ -42,6 +42,7 @@ var afterLoaded = function (req) {
|
|||||||
updated: updated,
|
updated: updated,
|
||||||
cache: data.cache
|
cache: data.cache
|
||||||
};
|
};
|
||||||
|
window.cryptpadLanguage = data.language;
|
||||||
require(['/common/sframe-boot2.js'], function () { });
|
require(['/common/sframe-boot2.js'], function () { });
|
||||||
};
|
};
|
||||||
window.addEventListener('message', onReply);
|
window.addEventListener('message', onReply);
|
||||||
|
|||||||
748
www/common/sframe-chainpad-listmap.js
Normal file
748
www/common/sframe-chainpad-listmap.js
Normal file
@ -0,0 +1,748 @@
|
|||||||
|
require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
|
||||||
|
define([
|
||||||
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||||
|
'/bower_components/chainpad-json-validator/json-ot.js',
|
||||||
|
'json.sortify',
|
||||||
|
'/bower_components/textpatcher/TextPatcher.js',
|
||||||
|
], function (Realtime, JsonOT, Sortify, TextPatcher) {
|
||||||
|
var api = {};
|
||||||
|
// "Proxy" is undefined in Safari : we need to use an normal object and check if there are local
|
||||||
|
// changes regurlarly.
|
||||||
|
var isFakeProxy = typeof window.Proxy === "undefined";
|
||||||
|
|
||||||
|
var DeepProxy = api.DeepProxy = (function () {
|
||||||
|
var deepProxy = {};
|
||||||
|
|
||||||
|
var isArray = deepProxy.isArray = Array.isArray || function (obj) {
|
||||||
|
return Object.toString(obj) === '[object Array]';
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Arrays and nulls both register as 'object' when using native typeof
|
||||||
|
we need to distinguish them as their own types, so use this instead. */
|
||||||
|
var type = deepProxy.type = function (dat) {
|
||||||
|
return dat === null? 'null': isArray(dat)?'array': typeof(dat);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Check if an (sub-)element in an object or an array and should be a proxy.
|
||||||
|
If the browser doesn't support Proxy, return false */
|
||||||
|
var isProxyable = deepProxy.isProxyable = function (obj, forceCheck) {
|
||||||
|
if (typeof forceCheck === "undefined" && isFakeProxy) { return false; }
|
||||||
|
return ['object', 'array'].indexOf(type(obj)) !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Any time you set a value, check its type.
|
||||||
|
If that type is proxyable, make a new proxy. */
|
||||||
|
var setter = deepProxy.set = function (cb) {
|
||||||
|
return function (obj, prop, value) {
|
||||||
|
if (prop === 'on') {
|
||||||
|
throw new Error("'on' is a reserved attribute name for realtime lists and maps");
|
||||||
|
}
|
||||||
|
if (isProxyable(value)) {
|
||||||
|
obj[prop] = deepProxy.create(value, cb);
|
||||||
|
} else {
|
||||||
|
obj[prop] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
return obj[prop] || true; // always return truthey or you have problems
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var pathMatches = deepProxy.pathMatches = function (path, pattern) {
|
||||||
|
return !pattern.some(function (x, i) {
|
||||||
|
return x !== path[i];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var lengthDescending = function (a, b) { return b.pattern.length - a.pattern.length; };
|
||||||
|
|
||||||
|
/* TODO implement 'off' as well.
|
||||||
|
change 'setter' to warn users when they attempt to set 'off'
|
||||||
|
*/
|
||||||
|
var on = function(events) {
|
||||||
|
return function (evt, pattern, f) {
|
||||||
|
switch (evt) {
|
||||||
|
case 'change':
|
||||||
|
// pattern needs to be an array
|
||||||
|
pattern = type(pattern) === 'array'? pattern: [pattern];
|
||||||
|
|
||||||
|
events.change.push({
|
||||||
|
cb: function (oldval, newval, path, root) {
|
||||||
|
if (pathMatches(path, pattern)) {
|
||||||
|
return f(oldval, newval, path, root);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pattern: pattern,
|
||||||
|
});
|
||||||
|
// sort into descending order so we evaluate in order of specificity
|
||||||
|
events.change.sort(lengthDescending);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'remove':
|
||||||
|
pattern = type(pattern) === 'array'? pattern: [pattern];
|
||||||
|
|
||||||
|
events.remove.push({
|
||||||
|
cb: function (oldval, path, root) {
|
||||||
|
if (pathMatches(path, pattern)) { return f(oldval, path, root); }
|
||||||
|
},
|
||||||
|
pattern: pattern,
|
||||||
|
});
|
||||||
|
|
||||||
|
events.remove.sort(lengthDescending);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'ready':
|
||||||
|
events.ready.push({
|
||||||
|
// on('ready' has a different signature than
|
||||||
|
// change and delete, so use 'pattern', not 'f'
|
||||||
|
|
||||||
|
cb: function (info) {
|
||||||
|
pattern(info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'disconnect':
|
||||||
|
events.disconnect.push({
|
||||||
|
cb: function (info) {
|
||||||
|
// as above
|
||||||
|
pattern(info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'reconnect':
|
||||||
|
events.reconnect.push({
|
||||||
|
cb: function (info) {
|
||||||
|
// as above
|
||||||
|
pattern(info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'create':
|
||||||
|
events.create.push({
|
||||||
|
cb: function (info) {
|
||||||
|
pattern(info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var getter = deepProxy.get = function (/* cb */) {
|
||||||
|
var events = {
|
||||||
|
disconnect: [],
|
||||||
|
reconnect: [],
|
||||||
|
change: [],
|
||||||
|
ready: [],
|
||||||
|
remove: [],
|
||||||
|
create: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
return function (obj, prop) {
|
||||||
|
if (prop === 'on') {
|
||||||
|
return on(events);
|
||||||
|
} else if (prop === '_isProxy') {
|
||||||
|
return true;
|
||||||
|
} else if (prop === '_events') {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
return obj[prop];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var deleter = deepProxy.delete = function (cb) {
|
||||||
|
return function (obj, prop) {
|
||||||
|
if (typeof(obj[prop]) === 'undefined') { return true; }
|
||||||
|
delete obj[prop];
|
||||||
|
cb();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var handlers = deepProxy.handlers = function (cb, isRoot) {
|
||||||
|
if (!isRoot) {
|
||||||
|
return {
|
||||||
|
set: setter(cb),
|
||||||
|
get: function (obj, prop) {
|
||||||
|
if (prop === '_isProxy') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return obj[prop];
|
||||||
|
},
|
||||||
|
deleteProperty: deleter(cb),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
set: setter(cb),
|
||||||
|
get: getter(cb),
|
||||||
|
deleteProperty: deleter(cb),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var remoteChangeFlag = deepProxy.remoteChangeFlag = false;
|
||||||
|
|
||||||
|
var stringifyFakeProxy = deepProxy.stringifyFakeProxy = function (proxy) {
|
||||||
|
var copy = JSON.parse(Sortify(proxy));
|
||||||
|
delete copy._events;
|
||||||
|
delete copy._isProxy;
|
||||||
|
return Sortify(copy);
|
||||||
|
};
|
||||||
|
|
||||||
|
deepProxy.checkLocalChange = function (obj, cb) {
|
||||||
|
if (!isFakeProxy) { return; }
|
||||||
|
var oldObj = stringifyFakeProxy(obj);
|
||||||
|
window.setInterval(function() {
|
||||||
|
var newObj = stringifyFakeProxy(obj);
|
||||||
|
if (newObj !== oldObj) {
|
||||||
|
oldObj = newObj;
|
||||||
|
if (remoteChangeFlag) {
|
||||||
|
remoteChangeFlag = false;
|
||||||
|
} else {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},300);
|
||||||
|
};
|
||||||
|
|
||||||
|
var create = deepProxy.create = function (obj, opt, isRoot) {
|
||||||
|
/* recursively create proxies in case users do:
|
||||||
|
`x.a = {b: {c: 5}};
|
||||||
|
|
||||||
|
otherwise the inner object is not a proxy, which leads to incorrect
|
||||||
|
behaviour on the client that initiated the object (but not for
|
||||||
|
clients that receive the objects) */
|
||||||
|
|
||||||
|
// if the user supplied a callback, use it to create handlers
|
||||||
|
// this saves a bit of work in recursion
|
||||||
|
var methods = type(opt) === 'function'? handlers(opt, isRoot) : opt;
|
||||||
|
switch (type(obj)) {
|
||||||
|
case 'object':
|
||||||
|
var keys = Object.keys(obj);
|
||||||
|
keys.forEach(function (k) {
|
||||||
|
if (isProxyable(obj[k]) && !obj[k]._isProxy) {
|
||||||
|
obj[k] = create(obj[k], opt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
obj.forEach(function (o, i) {
|
||||||
|
if (isProxyable(o) && !o._isProxy) {
|
||||||
|
obj[i] = create(obj[i], opt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// if it's not an array or object, you don't need to proxy it
|
||||||
|
throw new Error('attempted to make a proxy of an unproxyable object');
|
||||||
|
}
|
||||||
|
if (!isFakeProxy) {
|
||||||
|
if (obj._isProxy) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return new window.Proxy(obj, methods);
|
||||||
|
}
|
||||||
|
|
||||||
|
var proxy = JSON.parse(JSON.stringify(obj));
|
||||||
|
|
||||||
|
if (isRoot) {
|
||||||
|
var events = {
|
||||||
|
disconnect: [],
|
||||||
|
reconnect: [],
|
||||||
|
change: [],
|
||||||
|
ready: [],
|
||||||
|
remove: [],
|
||||||
|
create: [],
|
||||||
|
};
|
||||||
|
proxy.on = on(events);
|
||||||
|
proxy._events = events;
|
||||||
|
}
|
||||||
|
return proxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
// onChange(path, key, root, oldval, newval)
|
||||||
|
var onChange = function (path, key, root, oldval, newval) {
|
||||||
|
var P = path.slice(0);
|
||||||
|
P.push(key);
|
||||||
|
|
||||||
|
/* returning false in your callback terminates 'bubbling up'
|
||||||
|
we can accomplish this with Array.some because we've presorted
|
||||||
|
listeners by the specificity of their path
|
||||||
|
*/
|
||||||
|
root._events.change.some(function (handler) {
|
||||||
|
return handler.cb(oldval, newval, P, root) === false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var find = deepProxy.find = function (map, path) {
|
||||||
|
/* safely search for nested values in an object via a path */
|
||||||
|
return (map && path.reduce(function (p, n) {
|
||||||
|
return typeof p[n] !== 'undefined' && p[n];
|
||||||
|
}, map)) || undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
var onRemove = function (path, key, root, old, top) {
|
||||||
|
var newpath = path.concat(key);
|
||||||
|
var X = find(root, newpath);
|
||||||
|
|
||||||
|
var t_X = type(X);
|
||||||
|
|
||||||
|
/* TODO 'find' is correct but unnecessarily expensive.
|
||||||
|
optimize it. */
|
||||||
|
|
||||||
|
switch (t_X) {
|
||||||
|
case 'array':
|
||||||
|
|
||||||
|
if (top) {
|
||||||
|
// the top of an onremove should emit an onchange instead
|
||||||
|
onChange(path, key, root, old, undefined);// no newval since it's a deletion
|
||||||
|
} else {
|
||||||
|
root._events.remove.forEach(function (handler) {
|
||||||
|
return handler.cb(X, newpath, root);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// remove all of the array's children
|
||||||
|
X.forEach(function (x, i) {
|
||||||
|
onRemove(newpath, i, root);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'object':
|
||||||
|
if (top) {
|
||||||
|
onChange(path, key, root, old, undefined);// no newval since it's a deletion
|
||||||
|
} else {
|
||||||
|
root._events.remove.forEach(function (handler) {
|
||||||
|
return handler.cb(X, newpath, root, old, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// remove all of the object's children
|
||||||
|
Object.keys(X).forEach(function (key) {
|
||||||
|
onRemove(newpath, key, root, X[key], false);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
root._events.remove.forEach(function (handler) {
|
||||||
|
return handler.cb(X, newpath, root);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* compare a new object 'B' against an existing proxy object 'A'
|
||||||
|
provide a unary function 'f' for the purpose of constructing new
|
||||||
|
deep proxies from regular objects and arrays.
|
||||||
|
|
||||||
|
Supply the path as you recurse, for the purpose of emitting events
|
||||||
|
attached to particular paths within the complete structure.
|
||||||
|
|
||||||
|
Operates entirely via side effects on 'A'
|
||||||
|
*/
|
||||||
|
var objects = deepProxy.objects = function (A, B, f, path, root) {
|
||||||
|
var Akeys = Object.keys(A);
|
||||||
|
var Bkeys = Object.keys(B);
|
||||||
|
|
||||||
|
/* iterating over the keys in B will tell you if a new key exists
|
||||||
|
it will not tell you if a key has been removed.
|
||||||
|
to accomplish that you will need to iterate over A's keys
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TODO return a truthy or falsey value (in 'objects' and 'arrays')
|
||||||
|
so that we have some measure of whether an object or array changed
|
||||||
|
(from the higher level in the tree, rather than doing everything
|
||||||
|
at the leaf level).
|
||||||
|
|
||||||
|
bonus points if you can defer events until the complete diff has
|
||||||
|
finished (collect them into an array or something, and simplify
|
||||||
|
the event if possible)
|
||||||
|
*/
|
||||||
|
|
||||||
|
Bkeys.forEach(function (b) {
|
||||||
|
var t_b = type(B[b]);
|
||||||
|
var old = A[b];
|
||||||
|
|
||||||
|
if (Akeys.indexOf(b) === -1) {
|
||||||
|
// there was an insertion
|
||||||
|
|
||||||
|
// mind the fallthrough behaviour
|
||||||
|
switch (t_b) {
|
||||||
|
case 'undefined':
|
||||||
|
// umm. this should never happen?
|
||||||
|
throw new Error("undefined type has key. this shouldn't happen?");
|
||||||
|
case 'array':
|
||||||
|
case 'object':
|
||||||
|
A[b] = f(B[b]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
A[b] = B[b];
|
||||||
|
}
|
||||||
|
|
||||||
|
// insertions are a change
|
||||||
|
|
||||||
|
// onChange(path, key, root, oldval, newval)
|
||||||
|
onChange(path, b, root, old, B[b]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else the key already existed
|
||||||
|
var t_a = type(A[b]);
|
||||||
|
if (t_a !== t_b) {
|
||||||
|
// its type changed!
|
||||||
|
console.log("type changed from [%s] to [%s]", t_a, t_b);
|
||||||
|
switch (t_b) {
|
||||||
|
case 'undefined':
|
||||||
|
throw new Error("first pass should never reveal undefined keys");
|
||||||
|
case 'array':
|
||||||
|
A[b] = f(B[b]);
|
||||||
|
// make a new proxy
|
||||||
|
break;
|
||||||
|
case 'object':
|
||||||
|
A[b] = f(B[b]);
|
||||||
|
// make a new proxy
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// all other datatypes just require assignment.
|
||||||
|
A[b] = B[b];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// type changes always mean a change happened
|
||||||
|
onChange(path, b, root, old, B[b]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// values might have changed, if not types
|
||||||
|
if (['array', 'object'].indexOf(t_a) === -1) {
|
||||||
|
// it's not an array or object, so we can do deep equality
|
||||||
|
if (A[b] !== B[b]) {
|
||||||
|
// not equal, so assign
|
||||||
|
A[b] = B[b];
|
||||||
|
|
||||||
|
onChange(path, b, root, old, B[b]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else it's an array or object
|
||||||
|
var nextPath = path.slice(0).concat(b);
|
||||||
|
if (t_a === 'object') {
|
||||||
|
// it's an object
|
||||||
|
objects.call(root, A[b], B[b], f, nextPath, root);
|
||||||
|
} else {
|
||||||
|
// it's an array
|
||||||
|
deepProxy.arrays.call(root, A[b], B[b], f, nextPath, root);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Akeys.forEach(function (a) {
|
||||||
|
var old = A[a];
|
||||||
|
|
||||||
|
if (a === "on" || a === "_events") { return; }
|
||||||
|
|
||||||
|
// the key was deleted
|
||||||
|
if (Bkeys.indexOf(a) === -1 || type(B[a]) === 'undefined') {
|
||||||
|
onRemove(path, a, root, old, true);
|
||||||
|
delete A[a];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
var arrays = deepProxy.arrays = function (A, B, f, path, root) {
|
||||||
|
var l_A = A.length;
|
||||||
|
var l_B = B.length;
|
||||||
|
|
||||||
|
if (l_A !== l_B) {
|
||||||
|
// B is longer than Aj
|
||||||
|
// there has been an insertion
|
||||||
|
|
||||||
|
// OR
|
||||||
|
|
||||||
|
// A is longer than B
|
||||||
|
// there has been a deletion
|
||||||
|
|
||||||
|
B.forEach(function (b, i) {
|
||||||
|
var t_a = type(A[i]);
|
||||||
|
var t_b = type(b);
|
||||||
|
|
||||||
|
var old = A[i];
|
||||||
|
|
||||||
|
if (t_a !== t_b) {
|
||||||
|
// type changes are always destructive
|
||||||
|
// that's good news because destructive is easy
|
||||||
|
switch (t_b) {
|
||||||
|
case 'undefined':
|
||||||
|
throw new Error('this should never happen');
|
||||||
|
case 'object':
|
||||||
|
A[i] = f(b);
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
A[i] = f(b);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
A[i] = b;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// path, key, root object, oldvalue, newvalue
|
||||||
|
onChange(path, i, root, old, b);
|
||||||
|
} else {
|
||||||
|
// same type
|
||||||
|
var nextPath = path.slice(0).concat(i);
|
||||||
|
|
||||||
|
switch (t_b) {
|
||||||
|
case 'object':
|
||||||
|
objects.call(root, A[i], b, f, nextPath, root);
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
if (arrays.call(root, A[i], b, f, nextPath, root)) {
|
||||||
|
onChange(path, i, root, old, b);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (b !== A[i]) {
|
||||||
|
A[i] = b;
|
||||||
|
onChange(path, i, root, old, b);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (l_A > l_B) {
|
||||||
|
// A was longer than B, so there have been deletions
|
||||||
|
var i = l_B;
|
||||||
|
//var t_a;
|
||||||
|
var old;
|
||||||
|
|
||||||
|
for (; i <= l_B; i++) {
|
||||||
|
// recursively delete
|
||||||
|
old = A[i];
|
||||||
|
|
||||||
|
onRemove(path, i, root, old, true);
|
||||||
|
}
|
||||||
|
// cool
|
||||||
|
}
|
||||||
|
|
||||||
|
A.length = l_B;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else they are the same length, iterate over their values
|
||||||
|
A.forEach(function (a, i) {
|
||||||
|
var t_a = type(a);
|
||||||
|
var t_b = type(B[i]);
|
||||||
|
|
||||||
|
var old = a;
|
||||||
|
|
||||||
|
// they have different types
|
||||||
|
if (t_a !== t_b) {
|
||||||
|
switch (t_b) {
|
||||||
|
case 'undefined':
|
||||||
|
onRemove(path, i, root, old, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// watch out for fallthrough behaviour
|
||||||
|
// if it's an object or array, create a proxy
|
||||||
|
case 'object':
|
||||||
|
case 'array':
|
||||||
|
A[i] = f(B[i]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
A[i] = B[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(path, i, root, old, B[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// they are the same type, clone the paths array and push to it
|
||||||
|
var nextPath = path.slice(0).concat(i);
|
||||||
|
|
||||||
|
// same type
|
||||||
|
switch (t_b) {
|
||||||
|
case 'undefined':
|
||||||
|
throw new Error('existing key had type `undefined`. this should never happen');
|
||||||
|
case 'object':
|
||||||
|
if (objects.call(root, A[i], B[i], f, nextPath, root)) {
|
||||||
|
onChange(path, i, root, old, B[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
if (arrays.call(root, A[i], B[i], f, nextPath, root)) {
|
||||||
|
onChange(path, i, root, old, B[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (A[i] !== B[i]) {
|
||||||
|
A[i] = B[i];
|
||||||
|
onChange(path, i, root, old, B[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
deepProxy.update = function (A, B, cb) {
|
||||||
|
var t_A = type(A);
|
||||||
|
var t_B = type(B);
|
||||||
|
|
||||||
|
if (t_A !== t_B) {
|
||||||
|
throw new Error("Proxy updates can't result in type changes");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (t_B) {
|
||||||
|
/* use .call so you can supply a different `this` value */
|
||||||
|
case 'array':
|
||||||
|
arrays.call(A, A, B, function (obj) {
|
||||||
|
return create(obj, cb);
|
||||||
|
}, [], A);
|
||||||
|
break;
|
||||||
|
case 'object':
|
||||||
|
// arrays.call(this, A , B , f, path , root)
|
||||||
|
objects.call(A, A, B, function (obj) {
|
||||||
|
return create(obj, cb);
|
||||||
|
}, [], A);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("unsupported realtime datatype:" + t_B);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return deepProxy;
|
||||||
|
}());
|
||||||
|
|
||||||
|
api.create = function (cfg) {
|
||||||
|
/* validate your inputs before proceeding */
|
||||||
|
|
||||||
|
if (!DeepProxy.isProxyable(cfg.data, true)) {
|
||||||
|
throw new Error('unsupported datatype: '+ DeepProxy.type(cfg.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
var realtimeOptions = {
|
||||||
|
userName: cfg.userName,
|
||||||
|
initialState: Sortify(cfg.data),
|
||||||
|
readOnly: cfg.readOnly,
|
||||||
|
transformFunction: JsonOT.transform || JsonOT.validate,
|
||||||
|
logLevel: typeof(cfg.logLevel) === 'undefined'? 0: cfg.logLevel,
|
||||||
|
validateContent: function (content) {
|
||||||
|
try {
|
||||||
|
JSON.parse(content);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to parse, rejecting patch");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var initializing = true;
|
||||||
|
|
||||||
|
var setterCb = function () {
|
||||||
|
if (!DeepProxy.remoteChangeFlag) { realtimeOptions.onLocal(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
var rt = {};
|
||||||
|
var realtime;
|
||||||
|
|
||||||
|
var proxy;
|
||||||
|
var patchText;
|
||||||
|
|
||||||
|
realtimeOptions.onRemote = function () {
|
||||||
|
if (initializing) { return; }
|
||||||
|
// TODO maybe implement history support here
|
||||||
|
|
||||||
|
var userDoc = realtime.getUserDoc();
|
||||||
|
var parsed = JSON.parse(userDoc);
|
||||||
|
|
||||||
|
DeepProxy.remoteChangeFlag = true;
|
||||||
|
DeepProxy.update(proxy, parsed, setterCb);
|
||||||
|
DeepProxy.remoteChangeFlag = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var onLocal = realtimeOptions.onLocal = function () {
|
||||||
|
if (initializing) { return; }
|
||||||
|
var strung = isFakeProxy? DeepProxy.stringifyFakeProxy(proxy): Sortify(proxy);
|
||||||
|
patchText(strung);
|
||||||
|
|
||||||
|
// try harder
|
||||||
|
if (realtime.getUserDoc() !== strung) { patchText(strung); }
|
||||||
|
|
||||||
|
// onLocal
|
||||||
|
if (cfg.onLocal) { cfg.onLocal(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
proxy = DeepProxy.create(cfg.data, setterCb, true);
|
||||||
|
console.log(proxy);
|
||||||
|
|
||||||
|
realtimeOptions.onInit = function (info) {
|
||||||
|
proxy._events.create.forEach(function (handler) {
|
||||||
|
handler.cb(info);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
realtimeOptions.onReady = function (info) {
|
||||||
|
// create your patcher
|
||||||
|
if (realtime !== info.realtime) {
|
||||||
|
realtime = rt.realtime = info.realtime;
|
||||||
|
patchText = TextPatcher.create({
|
||||||
|
realtime: realtime,
|
||||||
|
logging: cfg.logging || false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error(realtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
var userDoc = realtime.getUserDoc();
|
||||||
|
var parsed = JSON.parse(userDoc);
|
||||||
|
|
||||||
|
DeepProxy.update(proxy, parsed, setterCb);
|
||||||
|
|
||||||
|
proxy._events.ready.forEach(function (handler) {
|
||||||
|
handler.cb(info);
|
||||||
|
});
|
||||||
|
|
||||||
|
DeepProxy.checkLocalChange(proxy, onLocal);
|
||||||
|
|
||||||
|
initializing = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
realtimeOptions.onAbort = function (info) {
|
||||||
|
proxy._events.disconnect.forEach(function (handler) {
|
||||||
|
handler.cb(info);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
realtimeOptions.onConnectionChange = function (info) {
|
||||||
|
if (info.state) { // reconnect
|
||||||
|
initializing = true;
|
||||||
|
proxy._events.reconnect.forEach(function (handler) {
|
||||||
|
handler.cb(info);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// disconnected
|
||||||
|
proxy._events.disconnect.forEach(function (handler) {
|
||||||
|
handler.cb(info);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
realtimeOptions.onError = function (info) {
|
||||||
|
proxy._events.disconnect.forEach(function (handler) {
|
||||||
|
handler.cb(info);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
realtime = rt.cpCnInner = cfg.common.startRealtime(realtimeOptions);
|
||||||
|
rt.proxy = proxy;
|
||||||
|
rt.realtime = realtime;
|
||||||
|
|
||||||
|
return rt;
|
||||||
|
};
|
||||||
|
|
||||||
|
return api;
|
||||||
|
});
|
||||||
@ -51,9 +51,7 @@ define([
|
|||||||
$('<td>').text(Messages.cancel).appendTo($thead);
|
$('<td>').text(Messages.cancel).appendTo($thead);
|
||||||
|
|
||||||
var createTableContainer = function ($body) {
|
var createTableContainer = function ($body) {
|
||||||
console.log($body);
|
|
||||||
File.$container = $('<div>', { id: 'cp-fileupload' }).append($table).appendTo($body);
|
File.$container = $('<div>', { id: 'cp-fileupload' }).append($table).appendTo($body);
|
||||||
console.log('done');
|
|
||||||
return File.$container;
|
return File.$container;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -114,10 +112,13 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
onComplete = function (href) {
|
onComplete = function (href) {
|
||||||
|
var mdMgr = common.getMetadataMgr();
|
||||||
|
var origin = mdMgr.getPrivateData().origin;
|
||||||
|
$link.prepend($('<span>', {'class': 'fa fa-external-link'}));
|
||||||
$link.attr('href', href)
|
$link.attr('href', href)
|
||||||
.click(function (e) {
|
.click(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
window.open($link.attr('href'), '_blank');
|
window.open(origin + $link.attr('href'), '_blank');
|
||||||
});
|
});
|
||||||
var title = metadata.name;
|
var title = metadata.name;
|
||||||
Cryptpad.log(Messages._getKey('upload_success', [title]));
|
Cryptpad.log(Messages._getKey('upload_success', [title]));
|
||||||
@ -290,10 +291,22 @@ define([
|
|||||||
onFileDrop(dropped, e);
|
onFileDrop(dropped, e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
var createCkeditorDropHandler = function () {
|
||||||
|
var editor = config.ckeditor;
|
||||||
|
editor.document.on('drop', function (ev) {
|
||||||
|
var dropped = ev.data.$.dataTransfer.files;
|
||||||
|
onFileDrop(dropped, ev);
|
||||||
|
ev.data.preventDefault(true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var createUploader = function ($area, $hover, $body) {
|
var createUploader = function ($area, $hover, $body) {
|
||||||
if (!config.noHandlers) {
|
if (!config.noHandlers) {
|
||||||
createAreaHandlers($area, null);
|
if (config.ckeditor) {
|
||||||
|
createCkeditorDropHandler();
|
||||||
|
} else {
|
||||||
|
createAreaHandlers($area, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createTableContainer($body);
|
createTableContainer($body);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -21,6 +21,15 @@ define([
|
|||||||
var createRealtime = function () {
|
var createRealtime = function () {
|
||||||
return ChainPad.create({
|
return ChainPad.create({
|
||||||
userName: 'history',
|
userName: 'history',
|
||||||
|
validateContent: function (content) {
|
||||||
|
try {
|
||||||
|
JSON.parse(content);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Failed to parse, rejecting patch');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
initialState: '',
|
initialState: '',
|
||||||
transformFunction: JsonOT.validate,
|
transformFunction: JsonOT.validate,
|
||||||
logLevel: 0,
|
logLevel: 0,
|
||||||
@ -69,9 +78,9 @@ define([
|
|||||||
config.onLocal();
|
config.onLocal();
|
||||||
config.onRemote();
|
config.onRemote();
|
||||||
};
|
};
|
||||||
var onReady = function () {
|
|
||||||
config.setHistory(true);
|
config.setHistory(true);
|
||||||
};
|
var onReady = function () { };
|
||||||
|
|
||||||
var Messages = common.Messages;
|
var Messages = common.Messages;
|
||||||
var Cryptpad = common.getCryptpadCommon();
|
var Cryptpad = common.getCryptpadCommon();
|
||||||
|
|||||||
@ -182,11 +182,35 @@ define([
|
|||||||
break;
|
break;
|
||||||
case 'more':
|
case 'more':
|
||||||
button = $('<button>', {
|
button = $('<button>', {
|
||||||
title: Messages.moreActions || 'TODO',
|
title: Messages.moreActions,
|
||||||
'class': "cp-toolbar-drawer-button fa fa-ellipsis-h",
|
'class': "cp-toolbar-drawer-button fa fa-ellipsis-h",
|
||||||
style: 'font:'+size+' FontAwesome'
|
style: 'font:'+size+' FontAwesome'
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case 'savetodrive':
|
||||||
|
button = $('<button>', {
|
||||||
|
'class': 'fa fa-cloud-upload',
|
||||||
|
title: Messages.canvas_saveToDrive,
|
||||||
|
})
|
||||||
|
.click(common.prepareFeedback(type));
|
||||||
|
break;
|
||||||
|
case 'hashtag':
|
||||||
|
button = $('<button>', {
|
||||||
|
'class': 'fa fa-hashtag',
|
||||||
|
title: Messages.tags_title,
|
||||||
|
})
|
||||||
|
.click(common.prepareFeedback(type))
|
||||||
|
.click(function () {
|
||||||
|
sframeChan.query('Q_TAGS_GET', null, function (err, res) {
|
||||||
|
if (err || res.error) { return void console.error(err || res.error); }
|
||||||
|
Cryptpad.dialog.tagPrompt(res.data, function (tags) {
|
||||||
|
if (!Array.isArray(tags)) { return; }
|
||||||
|
console.error(tags);
|
||||||
|
sframeChan.event('EV_TAGS_SET', tags);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
button = $('<button>', {
|
button = $('<button>', {
|
||||||
'class': "fa fa-question",
|
'class': "fa fa-question",
|
||||||
@ -294,7 +318,7 @@ define([
|
|||||||
$userAdminContent.append($userName);
|
$userAdminContent.append($userName);
|
||||||
options.push({
|
options.push({
|
||||||
tag: 'p',
|
tag: 'p',
|
||||||
attributes: {'class': 'accountData'},
|
attributes: {'class': 'cp-toolbar-account'},
|
||||||
content: $userAdminContent.html()
|
content: $userAdminContent.html()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ define([
|
|||||||
|
|
||||||
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
|
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
|
||||||
sframeChan = sfc;
|
sframeChan = sfc;
|
||||||
}), false, { cache: cache });
|
}), false, { cache: cache, language: Cryptpad.getLanguage() });
|
||||||
Cryptpad.ready(waitFor());
|
Cryptpad.ready(waitFor());
|
||||||
}));
|
}));
|
||||||
}).nThen(function (waitFor) {
|
}).nThen(function (waitFor) {
|
||||||
@ -228,23 +228,22 @@ define([
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
var msgs = [];
|
||||||
var onMsg = function (msg) {
|
var onMsg = function (msg) {
|
||||||
var parsed = parse(msg);
|
var parsed = parse(msg);
|
||||||
if (parsed[0] === 'FULL_HISTORY_END') {
|
if (parsed[0] === 'FULL_HISTORY_END') {
|
||||||
console.log('END');
|
cb(msgs);
|
||||||
cb();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (parsed[0] !== 'FULL_HISTORY') { return; }
|
if (parsed[0] !== 'FULL_HISTORY') { return; }
|
||||||
if (parsed[1] && parsed[1].validateKey) { // First message
|
if (parsed[1] && parsed[1].validateKey) { // First message
|
||||||
secret.keys.validateKey = parsed[1].validateKey;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
msg = parsed[1][4];
|
msg = parsed[1][4];
|
||||||
if (msg) {
|
if (msg) {
|
||||||
msg = msg.replace(/^cp\|/, '');
|
msg = msg.replace(/^cp\|/, '');
|
||||||
var decryptedMsg = crypto.decrypt(msg, secret.keys.validateKey);
|
var decryptedMsg = crypto.decrypt(msg, secret.keys.validateKey);
|
||||||
sframeChan.event('EV_RT_HIST_MESSAGE', decryptedMsg);
|
msgs.push(decryptedMsg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
network.on('message', onMsg);
|
network.on('message', onMsg);
|
||||||
@ -370,6 +369,20 @@ define([
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sframeChan.on('Q_TAGS_GET', function (data, cb) {
|
||||||
|
Cryptpad.getPadTags(null, function (err, data) {
|
||||||
|
cb({
|
||||||
|
error: err,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sframeChan.on('EV_TAGS_SET', function (data) {
|
||||||
|
console.log(data);
|
||||||
|
Cryptpad.resetTags(null, data);
|
||||||
|
});
|
||||||
|
|
||||||
if (cfg.addRpc) {
|
if (cfg.addRpc) {
|
||||||
cfg.addRpc(sframeChan, Cryptpad);
|
cfg.addRpc(sframeChan, Cryptpad);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,10 +164,14 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
funcs.getFullHistory = function (realtime, cb) {
|
funcs.getFullHistory = function (realtime, cb) {
|
||||||
ctx.sframeChan.on('EV_RT_HIST_MESSAGE', function (content) {
|
ctx.sframeChan.query('Q_GET_FULL_HISTORY', null, function (err, messages) {
|
||||||
realtime.message(content);
|
if (err) { return void console.error(err); }
|
||||||
|
if (!Array.isArray(messages)) { return; }
|
||||||
|
messages.forEach(function (m) {
|
||||||
|
realtime.message(m);
|
||||||
|
});
|
||||||
|
cb();
|
||||||
});
|
});
|
||||||
ctx.sframeChan.query('Q_GET_FULL_HISTORY', null, cb);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
funcs.getPadAttribute = function (key, cb) {
|
funcs.getPadAttribute = function (key, cb) {
|
||||||
|
|||||||
@ -130,4 +130,9 @@ define({
|
|||||||
|
|
||||||
// Put one or more entries to the cache which will go in localStorage.
|
// Put one or more entries to the cache which will go in localStorage.
|
||||||
'EV_CACHE_PUT': true,
|
'EV_CACHE_PUT': true,
|
||||||
|
|
||||||
|
// Set and get the tags using the tag prompt button
|
||||||
|
'Q_TAGS_GET': true,
|
||||||
|
'EV_TAGS_SET': true,
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -688,7 +688,7 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var typing = -1;
|
var typing = -1;
|
||||||
var kickSpinner = function (toolbar, config, local) {
|
var kickSpinner = function (toolbar, config/*, local*/) {
|
||||||
if (!toolbar.spinner) { return; }
|
if (!toolbar.spinner) { return; }
|
||||||
var $spin = toolbar.spinner;
|
var $spin = toolbar.spinner;
|
||||||
|
|
||||||
@ -708,7 +708,7 @@ define([
|
|||||||
window.clearInterval($spin.interval);
|
window.clearInterval($spin.interval);
|
||||||
typing = -1;
|
typing = -1;
|
||||||
$spin.text(Messages.saved);
|
$spin.text(Messages.saved);
|
||||||
}, local ? 0 : SPINNER_DISAPPEAR_TIME);
|
}, /*local ? 0 :*/ SPINNER_DISAPPEAR_TIME);
|
||||||
};
|
};
|
||||||
config.sfCommon.whenRealtimeSyncs(onSynced);
|
config.sfCommon.whenRealtimeSyncs(onSynced);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -415,6 +415,7 @@ define([
|
|||||||
var containsSearchedTag = function (T) {
|
var containsSearchedTag = function (T) {
|
||||||
if (!tags) { return false; }
|
if (!tags) { return false; }
|
||||||
if (!T.length) { return false; }
|
if (!T.length) { return false; }
|
||||||
|
T = T.map(function (t) { return t.toLowerCase(); });
|
||||||
return tags.some(function (tag) {
|
return tags.some(function (tag) {
|
||||||
return T.some(function (t) {
|
return T.some(function (t) {
|
||||||
return t.indexOf(tag) !== -1;
|
return t.indexOf(tag) !== -1;
|
||||||
@ -446,6 +447,7 @@ define([
|
|||||||
res.forEach(function (l) {
|
res.forEach(function (l) {
|
||||||
//var paths = findFile(l);
|
//var paths = findFile(l);
|
||||||
ret.push({
|
ret.push({
|
||||||
|
id: l,
|
||||||
paths: findFile(l),
|
paths: findFile(l),
|
||||||
data: exp.getFileData(l)
|
data: exp.getFileData(l)
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,19 +6,21 @@ define([
|
|||||||
|
|
||||||
'/common/common-messenger.js',
|
'/common/common-messenger.js',
|
||||||
'/contacts/messenger-ui.js',
|
'/contacts/messenger-ui.js',
|
||||||
|
'/bower_components/nthen/index.js',
|
||||||
|
|
||||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||||
'less!/customize/src/less/cryptpad.less',
|
'less!/customize/src/less/cryptpad.less',
|
||||||
], function ($, Crypto, Toolbar, Cryptpad, Messenger, UI) {
|
], function ($, Crypto, Toolbar, Cryptpad, Messenger, UI, Nthen) {
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
var APP = window.APP = {
|
var APP = window.APP = {
|
||||||
Cryptpad: Cryptpad
|
Cryptpad: Cryptpad
|
||||||
};
|
};
|
||||||
|
|
||||||
$(function () {
|
Nthen(function (waitFor) {
|
||||||
|
$(waitFor());
|
||||||
var andThen = function () {
|
}).nThen(function (waitFor) {
|
||||||
|
Cryptpad.ready(waitFor(Cryptpad.reportAppUsage));
|
||||||
|
}).nThen(function () {
|
||||||
Cryptpad.addLoadingScreen();
|
Cryptpad.addLoadingScreen();
|
||||||
|
|
||||||
var ifrw = $('#pad-iframe')[0].contentWindow;
|
var ifrw = $('#pad-iframe')[0].contentWindow;
|
||||||
@ -56,12 +58,5 @@ define([
|
|||||||
|
|
||||||
var messenger = window.messenger = Messenger.messenger(Cryptpad);
|
var messenger = window.messenger = Messenger.messenger(Cryptpad);
|
||||||
UI.create(messenger, $list, $messages);
|
UI.create(messenger, $list, $messages);
|
||||||
};
|
|
||||||
|
|
||||||
Cryptpad.ready(function () {
|
|
||||||
andThen();
|
|
||||||
Cryptpad.reportAppUsage();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -129,7 +129,7 @@ define([
|
|||||||
console.error('No more messages to fetch');
|
console.error('No more messages to fetch');
|
||||||
channel.exhausted = true;
|
channel.exhausted = true;
|
||||||
console.log(channel);
|
console.log(channel);
|
||||||
return;
|
return void $moreHistory.addClass('faded');
|
||||||
} else {
|
} else {
|
||||||
channel.TAIL = msg.sig;
|
channel.TAIL = msg.sig;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,10 +164,11 @@ define([
|
|||||||
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
|
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
|
||||||
var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
|
var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
|
||||||
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
|
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
|
||||||
var $trashIcon = $('<span>', {"class": "fa fa-trash-o"});
|
var $trashIcon = $('<span>', {"class": "fa fa-trash"});
|
||||||
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
|
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
|
||||||
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o expcol"});
|
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o expcol"});
|
||||||
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o expcol"});
|
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o expcol"});
|
||||||
|
var $emptyTrashIcon = $('<button>', {"class": "fa fa-ban"});
|
||||||
var $listIcon = $('<button>', {"class": "fa fa-list"});
|
var $listIcon = $('<button>', {"class": "fa fa-list"});
|
||||||
var $gridIcon = $('<button>', {"class": "fa fa-th-large"});
|
var $gridIcon = $('<button>', {"class": "fa fa-th-large"});
|
||||||
var $sortAscIcon = $('<span>', {"class": "fa fa-angle-up sortasc"});
|
var $sortAscIcon = $('<span>', {"class": "fa fa-angle-up sortasc"});
|
||||||
@ -696,6 +697,11 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
var updateContextButton = function () {
|
var updateContextButton = function () {
|
||||||
|
if (filesOp.isPathIn(currentPath, [TRASH])) {
|
||||||
|
$driveToolbar.find('cp-app-drive-toolbar-emptytrash').show();
|
||||||
|
} else {
|
||||||
|
$driveToolbar.find('cp-app-drive-toolbar-emptytrash').hide();
|
||||||
|
}
|
||||||
var $li = $content.find('.selected');
|
var $li = $content.find('.selected');
|
||||||
if ($li.length === 0) {
|
if ($li.length === 0) {
|
||||||
$li = findDataHolder($tree.find('.active'));
|
$li = findDataHolder($tree.find('.active'));
|
||||||
@ -1129,7 +1135,7 @@ define([
|
|||||||
}
|
}
|
||||||
if (data.filename && data.filename !== data.title) {
|
if (data.filename && data.filename !== data.title) {
|
||||||
var $renamed = $renamedIcon.clone().appendTo($state);
|
var $renamed = $renamedIcon.clone().appendTo($state);
|
||||||
$renamed.attr('title', "TODO: you've set a custom name for this pad. Its shared title is:\n<b>{0}</b>");
|
$renamed.attr('title', Messages._getKey('fm_renamedPad', [data.title]));
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = filesOp.getTitle(element);
|
var name = filesOp.getTitle(element);
|
||||||
@ -1198,6 +1204,13 @@ define([
|
|||||||
draggable: true,
|
draggable: true,
|
||||||
'class': 'element-row'
|
'class': 'element-row'
|
||||||
});
|
});
|
||||||
|
if (!isFolder && Array.isArray(APP.selectedFiles)) {
|
||||||
|
var idx = APP.selectedFiles.indexOf(element);
|
||||||
|
if (idx !== -1) {
|
||||||
|
$element.addClass('selected');
|
||||||
|
APP.selectedFiles.splice(idx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (isFolder) {
|
if (isFolder) {
|
||||||
addFolderData(element, key, $element);
|
addFolderData(element, key, $element);
|
||||||
} else {
|
} else {
|
||||||
@ -1382,6 +1395,18 @@ define([
|
|||||||
$gridButton.attr('title', Messages.fm_viewGridButton);
|
$gridButton.attr('title', Messages.fm_viewGridButton);
|
||||||
$container.append($listButton).append($gridButton);
|
$container.append($listButton).append($gridButton);
|
||||||
};
|
};
|
||||||
|
var createEmptyTrashButton = function ($container) {
|
||||||
|
var $button = $emptyTrashIcon.clone().addClass('element');
|
||||||
|
$button.addClass('cp-app-drive-toolbar-emptytrash');
|
||||||
|
$button.attr('title', Messages.fc_empty);
|
||||||
|
$button.click(function () {
|
||||||
|
Cryptpad.confirm(Messages.fm_emptyTrashDialog, function(res) {
|
||||||
|
if (!res) { return; }
|
||||||
|
filesOp.emptyTrash(refresh);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$container.append($button);
|
||||||
|
};
|
||||||
|
|
||||||
var getNewPadTypes = function () {
|
var getNewPadTypes = function () {
|
||||||
var arr = [];
|
var arr = [];
|
||||||
@ -1751,6 +1776,13 @@ define([
|
|||||||
'class': 'file-element element element-row' + roClass,
|
'class': 'file-element element element-row' + roClass,
|
||||||
draggable: draggable
|
draggable: draggable
|
||||||
});
|
});
|
||||||
|
if (Array.isArray(APP.selectedFiles)) {
|
||||||
|
var sidx = APP.selectedFiles.indexOf(id);
|
||||||
|
if (sidx !== -1) {
|
||||||
|
$element.addClass('selected');
|
||||||
|
APP.selectedFiles.splice(sidx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
addFileData(id, $element);
|
addFileData(id, $element);
|
||||||
$element.prepend($icon).dblclick(function () {
|
$element.prepend($icon).dblclick(function () {
|
||||||
openFile(id);
|
openFile(id);
|
||||||
@ -1864,6 +1896,7 @@ define([
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (filesOp.isInTrashRoot(parentPath)) { parentPath = [TRASH]; }
|
if (filesOp.isInTrashRoot(parentPath)) { parentPath = [TRASH]; }
|
||||||
else { parentPath.pop(); }
|
else { parentPath.pop(); }
|
||||||
|
APP.selectedFiles = [r.id];
|
||||||
module.displayDirectory(parentPath);
|
module.displayDirectory(parentPath);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1946,6 +1979,7 @@ define([
|
|||||||
path = [ROOT];
|
path = [ROOT];
|
||||||
}
|
}
|
||||||
var isInRoot = filesOp.isPathIn(path, [ROOT]);
|
var isInRoot = filesOp.isPathIn(path, [ROOT]);
|
||||||
|
var inTrash = filesOp.isPathIn(path, [TRASH]);
|
||||||
var isTrashRoot = filesOp.comparePath(path, [TRASH]);
|
var isTrashRoot = filesOp.comparePath(path, [TRASH]);
|
||||||
var isTemplate = filesOp.comparePath(path, [TEMPLATE]);
|
var isTemplate = filesOp.comparePath(path, [TEMPLATE]);
|
||||||
var isAllFiles = filesOp.comparePath(path, [FILES_DATA]);
|
var isAllFiles = filesOp.comparePath(path, [FILES_DATA]);
|
||||||
@ -1992,6 +2026,10 @@ define([
|
|||||||
}
|
}
|
||||||
createViewModeButton($toolbar.find('.rightside'));
|
createViewModeButton($toolbar.find('.rightside'));
|
||||||
}
|
}
|
||||||
|
if (inTrash) {
|
||||||
|
createEmptyTrashButton($toolbar.find('.rightside'));
|
||||||
|
}
|
||||||
|
|
||||||
var $list = $('<ul>').appendTo($dirContent);
|
var $list = $('<ul>').appendTo($dirContent);
|
||||||
|
|
||||||
// NewButton can be undefined if we're in read only mode
|
// NewButton can be undefined if we're in read only mode
|
||||||
@ -2370,6 +2408,13 @@ define([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.tags && Array.isArray(data.tags)) {
|
||||||
|
$('<label>', {'for': 'cp-drive-tags'}).text(Messages.fm_prop_tagsList).appendTo($d);
|
||||||
|
$d.append(Cryptpad.dialog.selectable(data.tags.join(', '), {
|
||||||
|
id: 'cp-drive-tags',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
if (APP.loggedIn && AppConfig.enablePinning) {
|
if (APP.loggedIn && AppConfig.enablePinning) {
|
||||||
// check the size of this file...
|
// check the size of this file...
|
||||||
console.log(data.href);
|
console.log(data.href);
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<html class="cp-app-noscroll">
|
<html class="cp-app-noscroll">
|
||||||
<head>
|
<head>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
<script async data-bootload="/file/inner.js" data-main="/common/sframe-boot.js?ver=1.4" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
<script async data-bootload="/file/inner.js" data-main="/common/sframe-boot.js?ver=1.5" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||||
<style>
|
<style>
|
||||||
.loading-hidden { display: none; }
|
.loading-hidden { display: none; }
|
||||||
#editor1 { display: none; }
|
#editor1 { display: none; }
|
||||||
|
|||||||
@ -2,10 +2,10 @@
|
|||||||
<html style="height: 100%; background: transparent;">
|
<html style="height: 100%; background: transparent;">
|
||||||
<head>
|
<head>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
<script async data-bootload="/filepicker/inner.js" data-main="/common/sframe-boot.js?ver=1.4" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
<script async data-bootload="/filepicker/inner.js" data-main="/common/sframe-boot.js?ver=1.5" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||||
<style>
|
<style>
|
||||||
.loading-hidden { display: none; }
|
.loading-hidden { display: none; }
|
||||||
body #loading {
|
body #cp-loading {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 15vh;
|
top: 15vh;
|
||||||
bottom: 15vh;
|
bottom: 15vh;
|
||||||
@ -14,10 +14,10 @@
|
|||||||
z-index: 200000;
|
z-index: 200000;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
body #loading .loadingContainer {
|
body #cp-loading .cp-loading-container {
|
||||||
margin-top: 35vh;
|
margin-top: 35vh;
|
||||||
}
|
}
|
||||||
body #loading .cryptofist {
|
body #cp-loading .cp-loading-cryptofist {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Binary file not shown.
@ -1,47 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html class="cp pad">
|
|
||||||
<head>
|
|
||||||
<title>CryptPad</title>
|
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
|
|
||||||
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
|
|
||||||
<link rel="icon" type="image/png"
|
|
||||||
href="/customize/main-favicon.png"
|
|
||||||
data-main-favicon="/customize/main-favicon.png"
|
|
||||||
data-alt-favicon="/customize/alt-favicon.png"
|
|
||||||
id="favicon" />
|
|
||||||
<link rel="stylesheet" href="/customize/main.css" />
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
#pad-iframe {
|
|
||||||
position:fixed;
|
|
||||||
top:0px;
|
|
||||||
left:0px;
|
|
||||||
bottom:0px;
|
|
||||||
right:0px;
|
|
||||||
width:100%;
|
|
||||||
height:100%;
|
|
||||||
border:none;
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
|
||||||
overflow:hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<iframe id="pad-iframe"></iframe><script src="/common/noscriptfix.js"></script>
|
|
||||||
<div id="loading">
|
|
||||||
<div class="loadingContainer">
|
|
||||||
<img class="cryptofist" src="/customize/cryptofist_small.png" />
|
|
||||||
<div class="spinnerContainer">
|
|
||||||
<span class="fa fa-spinner fa-pulse fa-4x fa-fw"></span>
|
|
||||||
</div>
|
|
||||||
<p data-localization="loading"></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
|
||||||
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
|
|
||||||
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
|
|
||||||
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
margin: 0px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.cryptpad-toolbar {
|
|
||||||
margin-bottom: 1px;
|
|
||||||
padding: 0px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
media-tag * {
|
|
||||||
max-width: 100%;
|
|
||||||
margin: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
media-tag *:not(button) {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
media-tag video {
|
|
||||||
min-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="toolbar" class="toolbar-container"></div>
|
|
||||||
<media-tag id="encryptedFile"></media-tag>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@ -1,139 +0,0 @@
|
|||||||
define([
|
|
||||||
'jquery',
|
|
||||||
'/bower_components/chainpad-crypto/crypto.js',
|
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
|
||||||
'/common/toolbar.js',
|
|
||||||
'/common/cryptpad-common.js',
|
|
||||||
//'/common/visible.js',
|
|
||||||
//'/common/notify.js',
|
|
||||||
//'pdfjs-dist/build/pdf',
|
|
||||||
//'pdfjs-dist/build/pdf.worker',
|
|
||||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
|
||||||
'/bower_components/file-saver/FileSaver.min.js',
|
|
||||||
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad /*, Visible, Notify*/) {
|
|
||||||
//var Messages = Cryptpad.Messages;
|
|
||||||
//var saveAs = window.saveAs;
|
|
||||||
//window.Nacl = window.nacl;
|
|
||||||
$(function () {
|
|
||||||
|
|
||||||
var ifrw = $('#pad-iframe')[0].contentWindow;
|
|
||||||
var $iframe = $('#pad-iframe').contents();
|
|
||||||
|
|
||||||
Cryptpad.addLoadingScreen();
|
|
||||||
|
|
||||||
var andThen = function () {
|
|
||||||
var $bar = $iframe.find('.toolbar-container');
|
|
||||||
var secret = Cryptpad.getSecrets();
|
|
||||||
|
|
||||||
if (!secret.keys) { throw new Error("You need a hash"); } // TODO
|
|
||||||
|
|
||||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
|
||||||
var fileId = secret.channel;
|
|
||||||
var hexFileName = Cryptpad.base64ToHex(fileId);
|
|
||||||
// var type = "image/png";
|
|
||||||
|
|
||||||
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
var defaultName = Cryptpad.getDefaultName(parsed);
|
|
||||||
|
|
||||||
var getTitle = function () {
|
|
||||||
var pad = Cryptpad.getRelativeHref(window.location.href);
|
|
||||||
var fo = Cryptpad.getStore().getProxy().fo;
|
|
||||||
var data = fo.getFileData(pad);
|
|
||||||
return data ? data.title : undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateTitle = function (newTitle) {
|
|
||||||
var title = document.title = newTitle;
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('span.title').text(title);
|
|
||||||
$bar.find('.' + Toolbar.constants.title).find('input').val(title);
|
|
||||||
};
|
|
||||||
|
|
||||||
var suggestName = function () {
|
|
||||||
return document.title || getTitle() || '';
|
|
||||||
};
|
|
||||||
|
|
||||||
var renameCb = function (err, title) {
|
|
||||||
document.title = title;
|
|
||||||
};
|
|
||||||
|
|
||||||
var $mt = $iframe.find('#encryptedFile');
|
|
||||||
$mt.attr('src', '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName);
|
|
||||||
$mt.attr('data-crypto-key', 'cryptpad:'+cryptKey);
|
|
||||||
// $mt.attr('data-type', type);
|
|
||||||
|
|
||||||
$(window.document).on('decryption', function (e) {
|
|
||||||
var decrypted = e.originalEvent;
|
|
||||||
var metadata = decrypted.metadata;
|
|
||||||
|
|
||||||
if (decrypted.callback) { decrypted.callback(); }
|
|
||||||
//console.log(metadata);
|
|
||||||
//console.log(defaultName);
|
|
||||||
if (!metadata || metadata.name !== defaultName) { return; }
|
|
||||||
var title = document.title = metadata.name;
|
|
||||||
updateTitle(title || defaultName);
|
|
||||||
})
|
|
||||||
.on('decryptionError', function (e) {
|
|
||||||
var error = e.originalEvent;
|
|
||||||
Cryptpad.alert(error.message);
|
|
||||||
})
|
|
||||||
.on('decryptionProgress', function (e) {
|
|
||||||
var progress = e.originalEvent;
|
|
||||||
console.log(progress.percent);
|
|
||||||
});
|
|
||||||
|
|
||||||
require(['/common/media-tag.js'], function (MediaTag) {
|
|
||||||
var configTb = {
|
|
||||||
displayed: ['useradmin', 'share', 'newpad'],
|
|
||||||
ifrw: ifrw,
|
|
||||||
common: Cryptpad,
|
|
||||||
title: {
|
|
||||||
onRename: renameCb,
|
|
||||||
defaultName: defaultName,
|
|
||||||
suggestName: suggestName
|
|
||||||
},
|
|
||||||
share: {
|
|
||||||
secret: secret,
|
|
||||||
channel: hexFileName
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Toolbar.create($bar, null, null, null, null, configTb);
|
|
||||||
|
|
||||||
updateTitle(Cryptpad.initialName || getTitle() || defaultName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allowed mime types that have to be set for a rendering after a decryption.
|
|
||||||
*
|
|
||||||
* @type {Array}
|
|
||||||
*/
|
|
||||||
var allowedMediaTypes = [
|
|
||||||
'image/png',
|
|
||||||
'image/jpeg',
|
|
||||||
'image/jpg',
|
|
||||||
'image/gif',
|
|
||||||
'audio/mp3',
|
|
||||||
'audio/ogg',
|
|
||||||
'audio/wav',
|
|
||||||
'audio/webm',
|
|
||||||
'video/mp4',
|
|
||||||
'video/ogg',
|
|
||||||
'video/webm',
|
|
||||||
'application/pdf',
|
|
||||||
'application/dash+xml',
|
|
||||||
'download'
|
|
||||||
];
|
|
||||||
|
|
||||||
MediaTag.CryptoFilter.setAllowedMediaTypes(allowedMediaTypes);
|
|
||||||
|
|
||||||
MediaTag($mt[0]);
|
|
||||||
|
|
||||||
Cryptpad.removeLoadingScreen();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Cryptpad.ready(function () {
|
|
||||||
andThen();
|
|
||||||
Cryptpad.reportAppUsage();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
@import "/customize/src/less/variables.less";
|
|
||||||
@import "/customize/src/less/mixins.less";
|
|
||||||
@import "/common/markdown.less";
|
|
||||||
@import "/common/file-dialog.less";
|
|
||||||
|
|
||||||
html, body{
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
max-height: 100%;
|
|
||||||
min-height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@slideTime: 500ms;
|
|
||||||
.CodeMirror {
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
width: 50%;
|
|
||||||
&.transition {
|
|
||||||
transition: width @slideTime, min-width @slideTime, max-width @slideTime;
|
|
||||||
}
|
|
||||||
min-width: 20%;
|
|
||||||
max-width: 80%;
|
|
||||||
resize: horizontal;
|
|
||||||
font-size: initial;
|
|
||||||
}
|
|
||||||
.CodeMirror.fullPage {
|
|
||||||
//min-width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
resize: none;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.CodeMirror-focused .cm-matchhighlight {
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
|
|
||||||
background-position: bottom;
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
}
|
|
||||||
#editorContainer {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
#previewContainer {
|
|
||||||
flex: 1;
|
|
||||||
padding: 5px 20px;
|
|
||||||
overflow: auto;
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
border-left: 1px solid black;
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: Calibri,Ubuntu,sans-serif;
|
|
||||||
word-wrap: break-word;
|
|
||||||
position: relative;
|
|
||||||
media-tag {
|
|
||||||
* {
|
|
||||||
max-width:100%;
|
|
||||||
}
|
|
||||||
iframe[type="application/pdf"] {
|
|
||||||
max-height:50vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#preview {
|
|
||||||
max-width: 40vw;
|
|
||||||
margin: 1em auto;
|
|
||||||
|
|
||||||
.markdown_preformatted-code;
|
|
||||||
.markdown_gfm-table(black);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cp-splitter {
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
width: 8px;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
cursor: col-resize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: @media-medium-screen) {
|
|
||||||
.CodeMirror {
|
|
||||||
flex: 1;
|
|
||||||
max-width: 100%;
|
|
||||||
resize: none;
|
|
||||||
}
|
|
||||||
#previewContainer {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html class="cp code">
|
|
||||||
<head>
|
|
||||||
<title>CryptPad</title>
|
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta name="referrer" content="no-referrer" />
|
|
||||||
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
#iframe-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
right: 0px;
|
|
||||||
left: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
#pad-iframe {
|
|
||||||
width:100%;
|
|
||||||
height:100%;
|
|
||||||
border:none;
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
|
||||||
overflow:hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
/* We use !important here to override the 96% set to the element in DecorateToolbar.js
|
|
||||||
when we enter fullscreen mode. It allows us to avoid changing the iframe's size in JS */
|
|
||||||
#pad-iframe.fullscreen {
|
|
||||||
top: 0px;
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="iframe-container">
|
|
||||||
<iframe id="pad-iframe"></iframe><script src="/common/noscriptfix.js"></script>
|
|
||||||
</div>
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html style="height: 100%;">
|
|
||||||
<head>
|
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
|
||||||
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
|
|
||||||
<script async data-bootload="inner.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
|
||||||
<style> .loading-hidden { display: none; } </style>
|
|
||||||
</head>
|
|
||||||
<body class="loading-hidden">
|
|
||||||
<div id="cme_toolbox" class="toolbar-container"></div>
|
|
||||||
<div id="editorContainer">
|
|
||||||
<textarea id="editor1" name="editor1"></textarea>
|
|
||||||
<div id="previewContainer"><div id="preview"></div></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
define([
|
|
||||||
'jquery',
|
|
||||||
|
|
||||||
'cm/lib/codemirror',
|
|
||||||
|
|
||||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
|
||||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
|
||||||
'less!/code/code.less',
|
|
||||||
'less!/customize/src/less/toolbar.less',
|
|
||||||
'less!/customize/src/less/cryptpad.less',
|
|
||||||
|
|
||||||
'css!cm/lib/codemirror.css',
|
|
||||||
'css!cm/addon/dialog/dialog.css',
|
|
||||||
'css!cm/addon/fold/foldgutter.css',
|
|
||||||
|
|
||||||
'cm/mode/markdown/markdown',
|
|
||||||
'cm/addon/mode/loadmode',
|
|
||||||
'cm/mode/meta',
|
|
||||||
'cm/addon/mode/overlay',
|
|
||||||
'cm/addon/mode/multiplex',
|
|
||||||
'cm/addon/mode/simple',
|
|
||||||
'cm/addon/edit/closebrackets',
|
|
||||||
'cm/addon/edit/matchbrackets',
|
|
||||||
'cm/addon/edit/trailingspace',
|
|
||||||
'cm/addon/selection/active-line',
|
|
||||||
'cm/addon/search/search',
|
|
||||||
'cm/addon/search/match-highlighter',
|
|
||||||
'cm/addon/search/searchcursor',
|
|
||||||
'cm/addon/dialog/dialog',
|
|
||||||
'cm/addon/fold/foldcode',
|
|
||||||
'cm/addon/fold/foldgutter',
|
|
||||||
'cm/addon/fold/brace-fold',
|
|
||||||
'cm/addon/fold/xml-fold',
|
|
||||||
'cm/addon/fold/markdown-fold',
|
|
||||||
'cm/addon/fold/comment-fold',
|
|
||||||
'cm/addon/display/placeholder',
|
|
||||||
], function ($, CMeditor) {
|
|
||||||
window.CodeMirror = CMeditor;
|
|
||||||
$('.loading-hidden').removeClass('loading-hidden');
|
|
||||||
});
|
|
||||||
@ -1,559 +0,0 @@
|
|||||||
define([
|
|
||||||
'jquery',
|
|
||||||
'/bower_components/chainpad-crypto/crypto.js',
|
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
|
||||||
'/bower_components/textpatcher/TextPatcher.js',
|
|
||||||
'/common/toolbar2.js',
|
|
||||||
'json.sortify',
|
|
||||||
'/bower_components/chainpad-json-validator/json-ot.js',
|
|
||||||
'/common/cryptpad-common.js',
|
|
||||||
'/common/cryptget.js',
|
|
||||||
'/common/diffMarked.js',
|
|
||||||
|
|
||||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
|
||||||
'less!/customize/src/less/cryptpad.less'
|
|
||||||
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad,
|
|
||||||
Cryptget, DiffMd) {
|
|
||||||
var Messages = Cryptpad.Messages;
|
|
||||||
|
|
||||||
var APP = window.APP = {
|
|
||||||
Cryptpad: Cryptpad,
|
|
||||||
};
|
|
||||||
|
|
||||||
$(function () {
|
|
||||||
Cryptpad.addLoadingScreen();
|
|
||||||
|
|
||||||
var ifrw = APP.ifrw = $('#pad-iframe')[0].contentWindow;
|
|
||||||
var stringify = function (obj) {
|
|
||||||
return JSONSortify(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
var toolbar;
|
|
||||||
var editor;
|
|
||||||
|
|
||||||
var secret = Cryptpad.getSecrets();
|
|
||||||
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
|
||||||
if (!secret.keys) {
|
|
||||||
secret.keys = secret.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
var onConnectError = function () {
|
|
||||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
|
||||||
};
|
|
||||||
|
|
||||||
var andThen = function (CMeditor) {
|
|
||||||
var $iframe = $('#pad-iframe').contents();
|
|
||||||
var $contentContainer = $iframe.find('#editorContainer');
|
|
||||||
var $previewContainer = $iframe.find('#previewContainer');
|
|
||||||
var $preview = $iframe.find('#preview');
|
|
||||||
$preview.click(function (e) {
|
|
||||||
if (!e.target) { return; }
|
|
||||||
var $t = $(e.target);
|
|
||||||
if ($t.is('a') || $t.parents('a').length) {
|
|
||||||
e.preventDefault();
|
|
||||||
var $a = $t.is('a') ? $t : $t.parents('a').first();
|
|
||||||
var href = $a.attr('href');
|
|
||||||
window.open(href);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var CodeMirror = Cryptpad.createCodemirror(ifrw, Cryptpad, null, CMeditor);
|
|
||||||
$iframe.find('.CodeMirror').addClass('fullPage');
|
|
||||||
editor = CodeMirror.editor;
|
|
||||||
|
|
||||||
var setIndentation = APP.setIndentation = function (units, useTabs) {
|
|
||||||
if (typeof(units) !== 'number') { return; }
|
|
||||||
editor.setOption('indentUnit', units);
|
|
||||||
editor.setOption('tabSize', units);
|
|
||||||
editor.setOption('indentWithTabs', useTabs);
|
|
||||||
};
|
|
||||||
|
|
||||||
var indentKey = 'indentUnit';
|
|
||||||
var useTabsKey = 'indentWithTabs';
|
|
||||||
|
|
||||||
var proxy = Cryptpad.getProxy();
|
|
||||||
|
|
||||||
var updateIndentSettings = APP.updateIndentSettings = function () {
|
|
||||||
var indentUnit = proxy.settings[indentKey];
|
|
||||||
var useTabs = proxy.settings[useTabsKey];
|
|
||||||
setIndentation(
|
|
||||||
typeof(indentUnit) === 'number'? indentUnit: 2,
|
|
||||||
typeof(useTabs) === 'boolean'? useTabs: false);
|
|
||||||
};
|
|
||||||
|
|
||||||
proxy.on('change', ['settings', indentKey], updateIndentSettings);
|
|
||||||
proxy.on('change', ['settings', useTabsKey], updateIndentSettings);
|
|
||||||
|
|
||||||
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
|
|
||||||
|
|
||||||
var isHistoryMode = false;
|
|
||||||
|
|
||||||
var setEditable = APP.setEditable = function (bool) {
|
|
||||||
if (readOnly && bool) { return; }
|
|
||||||
editor.setOption('readOnly', !bool);
|
|
||||||
};
|
|
||||||
|
|
||||||
var Title;
|
|
||||||
var UserList;
|
|
||||||
var Metadata;
|
|
||||||
|
|
||||||
var config = {
|
|
||||||
initialState: '{}',
|
|
||||||
websocketURL: Cryptpad.getWebsocketURL(),
|
|
||||||
channel: secret.channel,
|
|
||||||
// our public key
|
|
||||||
validateKey: secret.keys.validateKey || undefined,
|
|
||||||
readOnly: readOnly,
|
|
||||||
crypto: Crypto.createEncryptor(secret.keys),
|
|
||||||
network: Cryptpad.getNetwork(),
|
|
||||||
transformFunction: JsonOT.validate,
|
|
||||||
};
|
|
||||||
|
|
||||||
var canonicalize = function (t) { return t.replace(/\r\n/g, '\n'); };
|
|
||||||
|
|
||||||
var setHistory = function (bool, update) {
|
|
||||||
isHistoryMode = bool;
|
|
||||||
setEditable(!bool);
|
|
||||||
if (!bool && update) {
|
|
||||||
config.onRemote();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var initializing = true;
|
|
||||||
|
|
||||||
var stringifyInner = function (textValue) {
|
|
||||||
var obj = {
|
|
||||||
content: textValue,
|
|
||||||
metadata: {
|
|
||||||
users: UserList.userData,
|
|
||||||
defaultTitle: Title.defaultTitle
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (!initializing) {
|
|
||||||
obj.metadata.title = Title.title;
|
|
||||||
}
|
|
||||||
// set mode too...
|
|
||||||
obj.highlightMode = CodeMirror.highlightMode;
|
|
||||||
|
|
||||||
// stringify the json and send it into chainpad
|
|
||||||
return stringify(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
var forceDrawPreview = function () {
|
|
||||||
try {
|
|
||||||
DiffMd.apply(DiffMd.render(editor.getValue()), $preview);
|
|
||||||
} catch (e) { console.error(e); }
|
|
||||||
};
|
|
||||||
|
|
||||||
var drawPreview = Cryptpad.throttle(function () {
|
|
||||||
if (CodeMirror.highlightMode !== 'markdown') { return; }
|
|
||||||
if (!$previewContainer.is(':visible')) { return; }
|
|
||||||
forceDrawPreview();
|
|
||||||
}, 150);
|
|
||||||
|
|
||||||
var onLocal = config.onLocal = function () {
|
|
||||||
if (initializing) { return; }
|
|
||||||
if (isHistoryMode) { return; }
|
|
||||||
if (readOnly) { return; }
|
|
||||||
|
|
||||||
editor.save();
|
|
||||||
|
|
||||||
drawPreview();
|
|
||||||
|
|
||||||
var textValue = canonicalize(CodeMirror.$textarea.val());
|
|
||||||
var shjson = stringifyInner(textValue);
|
|
||||||
|
|
||||||
APP.patchText(shjson);
|
|
||||||
|
|
||||||
if (APP.realtime.getUserDoc() !== shjson) {
|
|
||||||
console.error("realtime.getUserDoc() !== shjson");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var mediaTagModes = [
|
|
||||||
'markdown',
|
|
||||||
'html',
|
|
||||||
'htmlembedded',
|
|
||||||
'htmlmixed',
|
|
||||||
'index.html',
|
|
||||||
'php',
|
|
||||||
'velocity',
|
|
||||||
'xml',
|
|
||||||
];
|
|
||||||
|
|
||||||
var onModeChanged = function (mode) {
|
|
||||||
var $codeMirror = $iframe.find('.CodeMirror');
|
|
||||||
window.clearTimeout(APP.previewTo);
|
|
||||||
$codeMirror.addClass('transition');
|
|
||||||
APP.previewTo = window.setTimeout(function () {
|
|
||||||
$codeMirror.removeClass('transition');
|
|
||||||
}, 500);
|
|
||||||
if (mediaTagModes.indexOf(mode) !== -1) {
|
|
||||||
APP.$mediaTagButton.show();
|
|
||||||
} else { APP.$mediaTagButton.hide(); }
|
|
||||||
|
|
||||||
if (mode === "markdown") {
|
|
||||||
APP.$previewButton.show();
|
|
||||||
Cryptpad.getPadAttribute('previewMode', function (e, data) {
|
|
||||||
if (e) { return void console.error(e); }
|
|
||||||
if (data !== false) {
|
|
||||||
$previewContainer.show();
|
|
||||||
APP.$previewButton.addClass('active');
|
|
||||||
$codeMirror.removeClass('fullPage');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
APP.$previewButton.hide();
|
|
||||||
$previewContainer.hide();
|
|
||||||
APP.$previewButton.removeClass('active');
|
|
||||||
$codeMirror.addClass('fullPage');
|
|
||||||
if (typeof(APP.updateIndentSettings) === 'function') {
|
|
||||||
APP.updateIndentSettings();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onInit = function (info) {
|
|
||||||
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
|
|
||||||
|
|
||||||
var titleCfg = { getHeadingText: CodeMirror.getHeadingText };
|
|
||||||
Title = Cryptpad.createTitle(titleCfg, config.onLocal, Cryptpad);
|
|
||||||
|
|
||||||
Metadata = Cryptpad.createMetadata(UserList, Title, null, Cryptpad);
|
|
||||||
|
|
||||||
var configTb = {
|
|
||||||
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit', 'upgrade'],
|
|
||||||
userList: UserList.getToolbarConfig(),
|
|
||||||
share: {
|
|
||||||
secret: secret,
|
|
||||||
channel: info.channel
|
|
||||||
},
|
|
||||||
title: Title.getTitleConfig(),
|
|
||||||
common: Cryptpad,
|
|
||||||
readOnly: readOnly,
|
|
||||||
ifrw: ifrw,
|
|
||||||
realtime: info.realtime,
|
|
||||||
network: info.network,
|
|
||||||
$container: $bar,
|
|
||||||
$contentContainer: $contentContainer
|
|
||||||
};
|
|
||||||
toolbar = APP.toolbar = Toolbar.create(configTb);
|
|
||||||
|
|
||||||
Title.setToolbar(toolbar);
|
|
||||||
CodeMirror.init(config.onLocal, Title, toolbar);
|
|
||||||
|
|
||||||
var $rightside = toolbar.$rightside;
|
|
||||||
var $drawer = toolbar.$drawer;
|
|
||||||
|
|
||||||
var editHash;
|
|
||||||
if (!readOnly) {
|
|
||||||
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add a history button */
|
|
||||||
var histConfig = {
|
|
||||||
onLocal: config.onLocal,
|
|
||||||
onRemote: config.onRemote,
|
|
||||||
setHistory: setHistory,
|
|
||||||
applyVal: function (val) {
|
|
||||||
var remoteDoc = JSON.parse(val || '{}').content;
|
|
||||||
editor.setValue(remoteDoc || '');
|
|
||||||
editor.save();
|
|
||||||
},
|
|
||||||
$toolbar: $bar
|
|
||||||
};
|
|
||||||
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
|
|
||||||
$drawer.append($hist);
|
|
||||||
|
|
||||||
/* save as template */
|
|
||||||
if (!Cryptpad.isTemplate(window.location.href)) {
|
|
||||||
var templateObj = {
|
|
||||||
rt: info.realtime,
|
|
||||||
Crypt: Cryptget,
|
|
||||||
getTitle: Title.getTitle
|
|
||||||
};
|
|
||||||
var $templateButton = Cryptpad.createButton('template', true, templateObj);
|
|
||||||
$rightside.append($templateButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add an export button */
|
|
||||||
var $export = Cryptpad.createButton('export', true, {}, CodeMirror.exportText);
|
|
||||||
$drawer.append($export);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
|
||||||
/* add an import button */
|
|
||||||
var $import = Cryptpad.createButton('import', true, {}, CodeMirror.importText);
|
|
||||||
$drawer.append($import);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add a forget button */
|
|
||||||
var forgetCb = function (err) {
|
|
||||||
if (err) { return; }
|
|
||||||
setEditable(false);
|
|
||||||
};
|
|
||||||
var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
|
|
||||||
$rightside.append($forgetPad);
|
|
||||||
|
|
||||||
var fileDialogCfg = {
|
|
||||||
$body: $iframe.find('body'),
|
|
||||||
onSelect: function (href) {
|
|
||||||
var parsed = Cryptpad.parsePadUrl(href);
|
|
||||||
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
|
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
|
||||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
|
|
||||||
editor.replaceSelection(mt);
|
|
||||||
},
|
|
||||||
data: APP
|
|
||||||
};
|
|
||||||
APP.$mediaTagButton = $('<button>', {
|
|
||||||
title: Messages.filePickerButton,
|
|
||||||
'class': 'rightside-button fa fa-picture-o',
|
|
||||||
style: 'font-size: 17px'
|
|
||||||
}).click(function () {
|
|
||||||
Cryptpad.createFileDialog(fileDialogCfg);
|
|
||||||
}).appendTo($rightside);
|
|
||||||
|
|
||||||
var $previewButton = APP.$previewButton = Cryptpad.createButton(null, true);
|
|
||||||
$previewButton.removeClass('fa-question').addClass('fa-eye');
|
|
||||||
$previewButton.attr('title', Messages.previewButtonTitle);
|
|
||||||
$previewButton.click(function () {
|
|
||||||
var $codeMirror = $iframe.find('.CodeMirror');
|
|
||||||
window.clearTimeout(APP.previewTo);
|
|
||||||
$codeMirror.addClass('transition');
|
|
||||||
APP.previewTo = window.setTimeout(function () {
|
|
||||||
$codeMirror.removeClass('transition');
|
|
||||||
}, 500);
|
|
||||||
if (CodeMirror.highlightMode !== 'markdown') {
|
|
||||||
$previewContainer.show();
|
|
||||||
}
|
|
||||||
$previewContainer.toggle();
|
|
||||||
if ($previewContainer.is(':visible')) {
|
|
||||||
forceDrawPreview();
|
|
||||||
$codeMirror.removeClass('fullPage');
|
|
||||||
Cryptpad.setPadAttribute('previewMode', true, function (e) {
|
|
||||||
if (e) { return console.log(e); }
|
|
||||||
});
|
|
||||||
$previewButton.addClass('active');
|
|
||||||
} else {
|
|
||||||
$codeMirror.addClass('fullPage');
|
|
||||||
$previewButton.removeClass('active');
|
|
||||||
Cryptpad.setPadAttribute('previewMode', false, function (e) {
|
|
||||||
if (e) { return console.log(e); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$rightside.append($previewButton);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
|
||||||
CodeMirror.configureTheme(function () {
|
|
||||||
CodeMirror.configureLanguage(null, onModeChanged);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
CodeMirror.configureTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// set the hash
|
|
||||||
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onReady = function (info) {
|
|
||||||
if (APP.realtime !== info.realtime) {
|
|
||||||
var realtime = APP.realtime = info.realtime;
|
|
||||||
APP.patchText = TextPatcher.create({
|
|
||||||
realtime: realtime,
|
|
||||||
//logging: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var userDoc = APP.realtime.getUserDoc();
|
|
||||||
|
|
||||||
var isNew = false;
|
|
||||||
if (userDoc === "" || userDoc === "{}") { isNew = true; }
|
|
||||||
|
|
||||||
var newDoc = "";
|
|
||||||
if(userDoc !== "") {
|
|
||||||
var hjson = JSON.parse(userDoc);
|
|
||||||
|
|
||||||
if (typeof (hjson) !== 'object' || Array.isArray(hjson) ||
|
|
||||||
(typeof(hjson.type) !== 'undefined' && hjson.type !== 'code')) {
|
|
||||||
var errorText = Messages.typeError;
|
|
||||||
Cryptpad.errorLoadingScreen(errorText);
|
|
||||||
throw new Error(errorText);
|
|
||||||
}
|
|
||||||
|
|
||||||
newDoc = hjson.content;
|
|
||||||
|
|
||||||
if (hjson.highlightMode) {
|
|
||||||
CodeMirror.setMode(hjson.highlightMode, onModeChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CodeMirror.highlightMode) {
|
|
||||||
CodeMirror.setMode('markdown', onModeChanged);
|
|
||||||
console.log("%s => %s", CodeMirror.highlightMode, CodeMirror.$language.val());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
|
||||||
Metadata.update(userDoc);
|
|
||||||
|
|
||||||
if (newDoc) {
|
|
||||||
editor.setValue(newDoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Cryptpad.initialName && Title.isDefaultTitle()) {
|
|
||||||
Title.updateTitle(Cryptpad.initialName);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cryptpad.getPadAttribute('previewMode', function (e, data) {
|
|
||||||
if (e) { return void console.error(e); }
|
|
||||||
if (data === false && APP.$previewButton) {
|
|
||||||
APP.$previewButton.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
// add the splitter
|
|
||||||
if (!$iframe.has('.cp-splitter').length) {
|
|
||||||
var $preview = $iframe.find('#previewContainer');
|
|
||||||
var splitter = $('<div>', {
|
|
||||||
'class': 'cp-splitter'
|
|
||||||
}).appendTo($preview);
|
|
||||||
|
|
||||||
$preview.on('scroll', function() {
|
|
||||||
splitter.css('top', $preview.scrollTop() + 'px');
|
|
||||||
});
|
|
||||||
|
|
||||||
var $target = $iframe.find('.CodeMirror');
|
|
||||||
|
|
||||||
splitter.on('mousedown', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var x = e.pageX;
|
|
||||||
var w = $target.width();
|
|
||||||
|
|
||||||
$iframe.on('mouseup mousemove', function handler(evt) {
|
|
||||||
if (evt.type === 'mouseup') {
|
|
||||||
$iframe.off('mouseup mousemove', handler);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$target.css('width', (w - x + evt.pageX) + 'px');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Cryptpad.removeLoadingScreen();
|
|
||||||
setEditable(true);
|
|
||||||
initializing = false;
|
|
||||||
|
|
||||||
onLocal(); // push local state to avoid parse errors later.
|
|
||||||
|
|
||||||
if (readOnly) {
|
|
||||||
config.onRemote();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UserList.getLastName(toolbar.$userNameButton, isNew);
|
|
||||||
var fmConfig = {
|
|
||||||
dropArea: $iframe.find('.CodeMirror'),
|
|
||||||
body: $iframe.find('body'),
|
|
||||||
onUploaded: function (ev, data) {
|
|
||||||
//var cursor = editor.getCursor();
|
|
||||||
//var cleanName = data.name.replace(/[\[\]]/g, '');
|
|
||||||
//var text = '';
|
|
||||||
var parsed = Cryptpad.parsePadUrl(data.url);
|
|
||||||
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
|
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
|
||||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
|
|
||||||
editor.replaceSelection(mt);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
APP.FM = Cryptpad.createFileManager(fmConfig);
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onRemote = function () {
|
|
||||||
if (initializing) { return; }
|
|
||||||
if (isHistoryMode) { return; }
|
|
||||||
|
|
||||||
var oldDoc = canonicalize(CodeMirror.$textarea.val());
|
|
||||||
var shjson = APP.realtime.getUserDoc();
|
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
|
||||||
Metadata.update(shjson);
|
|
||||||
|
|
||||||
var hjson = JSON.parse(shjson);
|
|
||||||
var remoteDoc = hjson.content;
|
|
||||||
|
|
||||||
var highlightMode = hjson.highlightMode;
|
|
||||||
if (highlightMode && highlightMode !== APP.highlightMode) {
|
|
||||||
CodeMirror.setMode(highlightMode, onModeChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeMirror.setValueAndCursor(oldDoc, remoteDoc, TextPatcher);
|
|
||||||
drawPreview();
|
|
||||||
|
|
||||||
if (!readOnly) {
|
|
||||||
var textValue = canonicalize(CodeMirror.$textarea.val());
|
|
||||||
var shjson2 = stringifyInner(textValue);
|
|
||||||
if (shjson2 !== shjson) {
|
|
||||||
console.error("shjson2 !== shjson");
|
|
||||||
TextPatcher.log(shjson, TextPatcher.diff(shjson, shjson2));
|
|
||||||
APP.patchText(shjson2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldDoc !== remoteDoc) { Cryptpad.notify(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onAbort = function () {
|
|
||||||
// inform of network disconnect
|
|
||||||
setEditable(false);
|
|
||||||
toolbar.failed();
|
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onConnectionChange = function (info) {
|
|
||||||
setEditable(info.state);
|
|
||||||
toolbar.failed();
|
|
||||||
if (info.state) {
|
|
||||||
initializing = true;
|
|
||||||
toolbar.reconnecting(info.myId);
|
|
||||||
Cryptpad.findOKButton().click();
|
|
||||||
} else {
|
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onError = onConnectError;
|
|
||||||
|
|
||||||
APP.realtime = Realtime.start(config);
|
|
||||||
|
|
||||||
editor.on('change', onLocal);
|
|
||||||
|
|
||||||
Cryptpad.onLogout(function () { setEditable(false); });
|
|
||||||
};
|
|
||||||
|
|
||||||
var interval = 100;
|
|
||||||
var second = function (CM) {
|
|
||||||
Cryptpad.ready(function () {
|
|
||||||
andThen(CM);
|
|
||||||
Cryptpad.reportAppUsage();
|
|
||||||
});
|
|
||||||
Cryptpad.onError(function (info) {
|
|
||||||
if (info && info.type === "store") {
|
|
||||||
onConnectError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var first = function () {
|
|
||||||
if (ifrw.CodeMirror) {
|
|
||||||
second(ifrw.CodeMirror);
|
|
||||||
} else {
|
|
||||||
console.log("CodeMirror was not defined. Trying again in %sms", interval);
|
|
||||||
setTimeout(first, interval);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
first();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html class="cp slide">
|
|
||||||
<head>
|
|
||||||
<title>CryptPad</title>
|
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta name="referrer" content="no-referrer" />
|
|
||||||
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
#iframe-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
right: 0px;
|
|
||||||
left: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
#pad-iframe {
|
|
||||||
width:100%;
|
|
||||||
height:100%;
|
|
||||||
border:none;
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
|
||||||
overflow:hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
body #pad-iframe.fullscreen {
|
|
||||||
top: 0px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="iframe-container">
|
|
||||||
<iframe id="pad-iframe", name="pad-iframe"></iframe><script src="/common/noscriptfix.js"></script>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
|
|
||||||
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
|
|
||||||
<script async data-bootload="inner.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
|
||||||
<style>.loading-hidden { display: none; } </style>
|
|
||||||
</head>
|
|
||||||
<body class="loading-hidden">
|
|
||||||
<div id="bar"></div>
|
|
||||||
<!-- <textarea></textarea>-->
|
|
||||||
<div id="cme_toolbox" class="toolbar-container"></div>
|
|
||||||
|
|
||||||
<div id="editorContainer">
|
|
||||||
<textarea id="editor1" name="editor1"></textarea>
|
|
||||||
<div class="cp-app-slide-viewer slide" tabindex="2">
|
|
||||||
<div id="modal">
|
|
||||||
<div id="button_exit" class="button"><span class="fa fa-times"></span></div>
|
|
||||||
<div id="button_left" class="button"><span class="fa fa-chevron-left"></span></div>
|
|
||||||
<div id="button_right" class="button"><span class="fa fa-chevron-right"></span></div>
|
|
||||||
<div id="content"></div>
|
|
||||||
</div>
|
|
||||||
<div id="print"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="nope"></div>
|
|
||||||
<div id="colorPicker_check"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
define([
|
|
||||||
'jquery',
|
|
||||||
|
|
||||||
'cm/lib/codemirror',
|
|
||||||
|
|
||||||
|
|
||||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
|
||||||
'less!/customize/src/less/toolbar.less',
|
|
||||||
'less!/customize/src/less/cryptpad.less',
|
|
||||||
'less!/slide/slide.less',
|
|
||||||
|
|
||||||
'css!cm/lib/codemirror.css',
|
|
||||||
'css!cm/addon/dialog/dialog.css',
|
|
||||||
'css!cm/addon/fold/foldgutter.css',
|
|
||||||
|
|
||||||
'cm/mode/markdown/markdown',
|
|
||||||
'cm/addon/mode/loadmode',
|
|
||||||
'cm/mode/meta',
|
|
||||||
'cm/addon/mode/overlay',
|
|
||||||
'cm/addon/mode/multiplex',
|
|
||||||
'cm/addon/mode/simple',
|
|
||||||
'cm/addon/edit/closebrackets',
|
|
||||||
'cm/addon/edit/matchbrackets',
|
|
||||||
'cm/addon/edit/trailingspace',
|
|
||||||
'cm/addon/selection/active-line',
|
|
||||||
'cm/addon/search/search',
|
|
||||||
'cm/addon/search/match-highlighter',
|
|
||||||
'cm/addon/search/searchcursor',
|
|
||||||
'cm/addon/dialog/dialog',
|
|
||||||
'cm/addon/fold/foldcode',
|
|
||||||
'cm/addon/fold/foldgutter',
|
|
||||||
'cm/addon/fold/brace-fold',
|
|
||||||
'cm/addon/fold/xml-fold',
|
|
||||||
'cm/addon/fold/markdown-fold',
|
|
||||||
'cm/addon/fold/comment-fold',
|
|
||||||
'cm/addon/display/placeholder',
|
|
||||||
|
|
||||||
], function ($, CMeditor) {
|
|
||||||
window.CodeMirror = CMeditor;
|
|
||||||
$('.loading-hidden').removeClass('loading-hidden');
|
|
||||||
});
|
|
||||||
@ -1,680 +0,0 @@
|
|||||||
define([
|
|
||||||
'jquery',
|
|
||||||
'/bower_components/chainpad-crypto/crypto.js',
|
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
|
||||||
'/bower_components/textpatcher/TextPatcher.js',
|
|
||||||
'/common/toolbar2.js',
|
|
||||||
'json.sortify',
|
|
||||||
'/bower_components/chainpad-json-validator/json-ot.js',
|
|
||||||
'/common/cryptpad-common.js',
|
|
||||||
'/common/cryptget.js',
|
|
||||||
'/slide/slide.js',
|
|
||||||
|
|
||||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
|
||||||
'less!/customize/src/less/cryptpad.less',
|
|
||||||
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, Cryptget, Slide) {
|
|
||||||
var Messages = Cryptpad.Messages;
|
|
||||||
|
|
||||||
var module = window.APP = {
|
|
||||||
Cryptpad: Cryptpad,
|
|
||||||
TextPatcher: TextPatcher,
|
|
||||||
Slide: Slide,
|
|
||||||
};
|
|
||||||
var APP = window.APP;
|
|
||||||
|
|
||||||
var SLIDE_BACKCOLOR_ID = "cryptpad-backcolor";
|
|
||||||
var SLIDE_COLOR_ID = "cryptpad-color";
|
|
||||||
|
|
||||||
|
|
||||||
$(function () {
|
|
||||||
Cryptpad.addLoadingScreen();
|
|
||||||
|
|
||||||
var stringify = function (obj) {
|
|
||||||
return JSONSortify(obj);
|
|
||||||
};
|
|
||||||
var ifrw = module.ifrw = $('#pad-iframe')[0].contentWindow;
|
|
||||||
var toolbar;
|
|
||||||
var editor;
|
|
||||||
|
|
||||||
var secret = Cryptpad.getSecrets();
|
|
||||||
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
|
||||||
Slide.readOnly = readOnly;
|
|
||||||
if (!secret.keys) {
|
|
||||||
secret.keys = secret.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
var presentMode = Slide.isPresentURL();
|
|
||||||
|
|
||||||
var onConnectError = function () {
|
|
||||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
|
||||||
};
|
|
||||||
|
|
||||||
var andThen = function (CMeditor) {
|
|
||||||
var $iframe = $('#pad-iframe').contents();
|
|
||||||
var $contentContainer = $iframe.find('#editorContainer');
|
|
||||||
var CodeMirror = Cryptpad.createCodemirror(ifrw, Cryptpad, null, CMeditor);
|
|
||||||
editor = CodeMirror.editor;
|
|
||||||
|
|
||||||
var $bar = $('#pad-iframe')[0].contentWindow.$('#cme_toolbox');
|
|
||||||
var $pad = $('#pad-iframe');
|
|
||||||
|
|
||||||
var isHistoryMode = false;
|
|
||||||
|
|
||||||
var setEditable = module.setEditable = function (bool) {
|
|
||||||
if (readOnly && bool) { return; }
|
|
||||||
editor.setOption('readOnly', !bool);
|
|
||||||
};
|
|
||||||
|
|
||||||
var Title;
|
|
||||||
var UserList;
|
|
||||||
var Metadata;
|
|
||||||
|
|
||||||
var setTabTitle = function (title) {
|
|
||||||
var slideNumber = '';
|
|
||||||
if (Slide.shown) { //Slide.index && Slide.content.length) {
|
|
||||||
slideNumber = ' (' + Slide.index + '/' + Slide.content.length + ')';
|
|
||||||
}
|
|
||||||
document.title = title + slideNumber;
|
|
||||||
};
|
|
||||||
|
|
||||||
var initialState = Messages.slideInitialState;
|
|
||||||
|
|
||||||
var $modal = $pad.contents().find('#modal');
|
|
||||||
var $content = $pad.contents().find('#content');
|
|
||||||
var $print = $pad.contents().find('#print');
|
|
||||||
var slideOptions = {};
|
|
||||||
|
|
||||||
$content.click(function (e) {
|
|
||||||
if (!e.target) { return; }
|
|
||||||
var $t = $(e.target);
|
|
||||||
if ($t.is('a') || $t.parents('a').length) {
|
|
||||||
e.preventDefault();
|
|
||||||
var $a = $t.is('a') ? $t : $t.parents('a').first();
|
|
||||||
var href = $a.attr('href');
|
|
||||||
window.open(href);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Slide.setModal($modal, $content, $pad, ifrw, slideOptions, initialState);
|
|
||||||
|
|
||||||
var enterPresentationMode = function (shouldLog) {
|
|
||||||
Slide.show(true, editor.getValue());
|
|
||||||
if (shouldLog) {
|
|
||||||
Cryptpad.log(Messages.presentSuccess);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (presentMode) {
|
|
||||||
enterPresentationMode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var textColor;
|
|
||||||
var backColor;
|
|
||||||
|
|
||||||
var config = {
|
|
||||||
initialState: '{}',
|
|
||||||
websocketURL: Cryptpad.getWebsocketURL(),
|
|
||||||
channel: secret.channel,
|
|
||||||
// our public key
|
|
||||||
validateKey: secret.keys.validateKey || undefined,
|
|
||||||
readOnly: readOnly,
|
|
||||||
crypto: Crypto.createEncryptor(secret.keys),
|
|
||||||
transformFunction: JsonOT.validate,
|
|
||||||
network: Cryptpad.getNetwork()
|
|
||||||
};
|
|
||||||
|
|
||||||
var canonicalize = function (t) { return t.replace(/\r\n/g, '\n'); };
|
|
||||||
|
|
||||||
var setHistory = function (bool, update) {
|
|
||||||
isHistoryMode = bool;
|
|
||||||
setEditable(!bool);
|
|
||||||
if (!bool && update) {
|
|
||||||
config.onRemote();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var initializing = true;
|
|
||||||
|
|
||||||
var stringifyInner = function (textValue) {
|
|
||||||
var obj = {
|
|
||||||
content: textValue,
|
|
||||||
metadata: {
|
|
||||||
users: UserList.userData,
|
|
||||||
defaultTitle: Title.defaultTitle,
|
|
||||||
slideOptions: slideOptions
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (!initializing) {
|
|
||||||
obj.metadata.title = Title.title;
|
|
||||||
}
|
|
||||||
if (textColor) {
|
|
||||||
obj.metadata.color = textColor;
|
|
||||||
}
|
|
||||||
if (backColor) {
|
|
||||||
obj.metadata.backColor = backColor;
|
|
||||||
}
|
|
||||||
// stringify the json and send it into chainpad
|
|
||||||
return stringify(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
var onLocal = config.onLocal = function () {
|
|
||||||
if (initializing) { return; }
|
|
||||||
if (isHistoryMode) { return; }
|
|
||||||
if (readOnly) { return; }
|
|
||||||
|
|
||||||
editor.save();
|
|
||||||
|
|
||||||
var textValue = canonicalize(CodeMirror.$textarea.val());
|
|
||||||
var shjson = stringifyInner(textValue);
|
|
||||||
|
|
||||||
module.patchText(shjson);
|
|
||||||
Slide.update(textValue);
|
|
||||||
|
|
||||||
if (module.realtime.getUserDoc() !== shjson) {
|
|
||||||
console.error("realtime.getUserDoc() !== shjson");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var metadataCfg = {
|
|
||||||
slideOptions: function (newOpt) {
|
|
||||||
if (stringify(newOpt) !== stringify(slideOptions)) {
|
|
||||||
$.extend(slideOptions, newOpt);
|
|
||||||
// TODO: manage realtime + cursor in the "options" modal ??
|
|
||||||
Slide.updateOptions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var updateColors = metadataCfg.slideColors = function (text, back) {
|
|
||||||
if (text) {
|
|
||||||
textColor = text;
|
|
||||||
$modal.css('color', text);
|
|
||||||
$modal.css('border-color', text);
|
|
||||||
$pad.contents().find('#' + SLIDE_COLOR_ID).css('color', text);
|
|
||||||
}
|
|
||||||
if (back) {
|
|
||||||
backColor = back;
|
|
||||||
$modal.css('background-color', back);
|
|
||||||
//$pad.contents().find('#' + SLIDE_COLOR_ID).css('background', back);
|
|
||||||
$pad.contents().find('#' + SLIDE_BACKCOLOR_ID).css('color', back);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var createPrintDialog = function () {
|
|
||||||
var slideOptionsTmp = {
|
|
||||||
title: false,
|
|
||||||
slide: false,
|
|
||||||
date: false,
|
|
||||||
transition: true,
|
|
||||||
style: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
$.extend(slideOptionsTmp, slideOptions);
|
|
||||||
var $container = $('<div class="alertify">');
|
|
||||||
var $container2 = $('<div class="dialog">').appendTo($container);
|
|
||||||
var $div = $('<div id="printOptions">').appendTo($container2);
|
|
||||||
var $p = $('<p>', {'class': 'msg'}).appendTo($div);
|
|
||||||
$('<b>').text(Messages.printOptions).appendTo($p);
|
|
||||||
$p.append($('<br>'));
|
|
||||||
// Slide number
|
|
||||||
$('<input>', {type: 'checkbox', id: 'checkNumber', checked: slideOptionsTmp.slide}).on('change', function () {
|
|
||||||
var c = this.checked;
|
|
||||||
slideOptionsTmp.slide = c;
|
|
||||||
}).appendTo($p).css('width', 'auto');
|
|
||||||
$('<label>', {'for': 'checkNumber'}).text(Messages.printSlideNumber).appendTo($p);
|
|
||||||
$p.append($('<br>'));
|
|
||||||
// Date
|
|
||||||
$('<input>', {type: 'checkbox', id: 'checkDate', checked: slideOptionsTmp.date}).on('change', function () {
|
|
||||||
var c = this.checked;
|
|
||||||
slideOptionsTmp.date = c;
|
|
||||||
}).appendTo($p).css('width', 'auto');
|
|
||||||
$('<label>', {'for': 'checkDate'}).text(Messages.printDate).appendTo($p);
|
|
||||||
$p.append($('<br>'));
|
|
||||||
// Title
|
|
||||||
$('<input>', {type: 'checkbox', id: 'checkTitle', checked: slideOptionsTmp.title}).on('change', function () {
|
|
||||||
var c = this.checked;
|
|
||||||
slideOptionsTmp.title = c;
|
|
||||||
}).appendTo($p).css('width', 'auto');
|
|
||||||
$('<label>', {'for': 'checkTitle'}).text(Messages.printTitle).appendTo($p);
|
|
||||||
$p.append($('<br>'));
|
|
||||||
// Transition
|
|
||||||
$('<input>', {type: 'checkbox', id: 'checkTransition', checked: slideOptionsTmp.transition}).on('change', function () {
|
|
||||||
var c = this.checked;
|
|
||||||
slideOptionsTmp.transition = c;
|
|
||||||
}).appendTo($p).css('width', 'auto');
|
|
||||||
$('<label>', {'for': 'checkTransition'}).text(Messages.printTransition).appendTo($p);
|
|
||||||
$p.append($('<br>'));
|
|
||||||
// CSS
|
|
||||||
$('<label>', {'for': 'cssPrint'}).text(Messages.printCSS).appendTo($p);
|
|
||||||
$p.append($('<br>'));
|
|
||||||
var $textarea = $('<textarea>', {'id':'cssPrint'}).css({'width':'100%', 'height':'100px'}).appendTo($p)
|
|
||||||
.on('keydown keyup', function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
});
|
|
||||||
$textarea.val(slideOptionsTmp.style);
|
|
||||||
window.setTimeout(function () { $textarea.focus(); }, 0);
|
|
||||||
|
|
||||||
var h;
|
|
||||||
|
|
||||||
var todo = function () {
|
|
||||||
$.extend(slideOptions, slideOptionsTmp);
|
|
||||||
slideOptions.style = $textarea.val();
|
|
||||||
onLocal();
|
|
||||||
$container.remove();
|
|
||||||
Cryptpad.stopListening(h);
|
|
||||||
};
|
|
||||||
var todoCancel = function () {
|
|
||||||
$container.remove();
|
|
||||||
Cryptpad.stopListening(h);
|
|
||||||
};
|
|
||||||
|
|
||||||
h = Cryptpad.listenForKeys(todo, todoCancel);
|
|
||||||
|
|
||||||
var $nav = $('<nav>').appendTo($div);
|
|
||||||
$('<button>', {'class': 'cancel'}).text(Messages.cancelButton).appendTo($nav).click(todoCancel);
|
|
||||||
$('<button>', {'class': 'ok'}).text(Messages.settings_save).appendTo($nav).click(todo);
|
|
||||||
|
|
||||||
return $container;
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onInit = function (info) {
|
|
||||||
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
|
|
||||||
|
|
||||||
var titleCfg = {
|
|
||||||
updateLocalTitle: setTabTitle,
|
|
||||||
getHeadingText: CodeMirror.getHeadingText
|
|
||||||
};
|
|
||||||
Title = Cryptpad.createTitle(titleCfg, config.onLocal, Cryptpad);
|
|
||||||
|
|
||||||
Slide.setTitle(Title);
|
|
||||||
|
|
||||||
Metadata = Cryptpad.createMetadata(UserList, Title, metadataCfg, Cryptpad);
|
|
||||||
|
|
||||||
var configTb = {
|
|
||||||
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit', 'upgrade'],
|
|
||||||
userList: UserList.getToolbarConfig(),
|
|
||||||
share: {
|
|
||||||
secret: secret,
|
|
||||||
channel: info.channel
|
|
||||||
},
|
|
||||||
title: Title.getTitleConfig(),
|
|
||||||
common: Cryptpad,
|
|
||||||
readOnly: readOnly,
|
|
||||||
ifrw: ifrw,
|
|
||||||
realtime: info.realtime,
|
|
||||||
network: info.network,
|
|
||||||
$container: $bar,
|
|
||||||
$contentContainer: $contentContainer
|
|
||||||
};
|
|
||||||
toolbar = module.toolbar = Toolbar.create(configTb);
|
|
||||||
|
|
||||||
Title.setToolbar(toolbar);
|
|
||||||
CodeMirror.init(config.onLocal, Title, toolbar);
|
|
||||||
|
|
||||||
var $rightside = toolbar.$rightside;
|
|
||||||
var $drawer = toolbar.$drawer;
|
|
||||||
|
|
||||||
var editHash;
|
|
||||||
|
|
||||||
if (!readOnly) {
|
|
||||||
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add a history button */
|
|
||||||
var histConfig = {
|
|
||||||
onLocal: config.onLocal,
|
|
||||||
onRemote: config.onRemote,
|
|
||||||
setHistory: setHistory,
|
|
||||||
applyVal: function (val) {
|
|
||||||
var remoteDoc = JSON.parse(val || '{}').content;
|
|
||||||
editor.setValue(remoteDoc || '');
|
|
||||||
editor.save();
|
|
||||||
},
|
|
||||||
$toolbar: $bar
|
|
||||||
};
|
|
||||||
var $hist = Cryptpad.createButton('history', true, {histConfig: histConfig});
|
|
||||||
$drawer.append($hist);
|
|
||||||
|
|
||||||
/* save as template */
|
|
||||||
if (!Cryptpad.isTemplate(window.location.href)) {
|
|
||||||
var templateObj = {
|
|
||||||
rt: info.realtime,
|
|
||||||
Crypt: Cryptget,
|
|
||||||
getTitle: function () { return document.title; }
|
|
||||||
};
|
|
||||||
var $templateButton = Cryptpad.createButton('template', true, templateObj);
|
|
||||||
$rightside.append($templateButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add an export button */
|
|
||||||
var $export = Cryptpad.createButton('export', true, {}, CodeMirror.exportText);
|
|
||||||
$drawer.append($export);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
|
||||||
/* add an import button */
|
|
||||||
var $import = Cryptpad.createButton('import', true, {}, CodeMirror.importText);
|
|
||||||
$drawer.append($import);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add a forget button */
|
|
||||||
var forgetCb = function (err) {
|
|
||||||
if (err) { return; }
|
|
||||||
setEditable(false);
|
|
||||||
};
|
|
||||||
var $forgetPad = Cryptpad.createButton('forget', true, {}, forgetCb);
|
|
||||||
$rightside.append($forgetPad);
|
|
||||||
|
|
||||||
var fileDialogCfg = {
|
|
||||||
$body: $iframe.find('body'),
|
|
||||||
onSelect: function (href) {
|
|
||||||
var parsed = Cryptpad.parsePadUrl(href);
|
|
||||||
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
|
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
|
||||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
|
|
||||||
editor.replaceSelection(mt);
|
|
||||||
},
|
|
||||||
data: APP
|
|
||||||
};
|
|
||||||
$('<button>', {
|
|
||||||
title: Messages.filePickerButton,
|
|
||||||
'class': 'rightside-button fa fa-picture-o',
|
|
||||||
style: 'font-size: 17px'
|
|
||||||
}).click(function () {
|
|
||||||
Cryptpad.createFileDialog(fileDialogCfg);
|
|
||||||
}).appendTo($rightside);
|
|
||||||
|
|
||||||
var $previewButton = APP.$previewButton = Cryptpad.createButton(null, true);
|
|
||||||
$previewButton.removeClass('fa-question').addClass('fa-eye');
|
|
||||||
$previewButton.attr('title', Messages.previewButtonTitle);
|
|
||||||
$previewButton.click(function () {
|
|
||||||
var $c = $iframe.find('#editorContainer');
|
|
||||||
if ($c.hasClass('preview')) {
|
|
||||||
Cryptpad.setPadAttribute('previewMode', false, function (e) {
|
|
||||||
if (e) { return console.log(e); }
|
|
||||||
});
|
|
||||||
$previewButton.removeClass('active');
|
|
||||||
return void $c.removeClass('preview');
|
|
||||||
}
|
|
||||||
Cryptpad.setPadAttribute('previewMode', true, function (e) {
|
|
||||||
if (e) { return console.log(e); }
|
|
||||||
});
|
|
||||||
$c.addClass('preview');
|
|
||||||
$previewButton.addClass('active');
|
|
||||||
Slide.updateFontSize();
|
|
||||||
});
|
|
||||||
$rightside.append($previewButton);
|
|
||||||
|
|
||||||
var $printButton = $('<button>', {
|
|
||||||
title: Messages.printButtonTitle,
|
|
||||||
'class': 'rightside-button fa fa-print',
|
|
||||||
style: 'font-size: 17px'
|
|
||||||
}).click(function () {
|
|
||||||
Slide.update(editor.getValue(), true);
|
|
||||||
$print.html($content.html());
|
|
||||||
Cryptpad.confirm("Are you sure you want to print?", function (yes) {
|
|
||||||
if (yes) {
|
|
||||||
window.frames["pad-iframe"].focus();
|
|
||||||
window.frames["pad-iframe"].print();
|
|
||||||
}
|
|
||||||
}, {ok: Messages.printButton});
|
|
||||||
Cryptpad.feedback('PRINT_SLIDES');
|
|
||||||
//$('body').append(createPrintDialog());
|
|
||||||
}).append($('<span>', {'class': 'drawer'}).text(Messages.printText));
|
|
||||||
|
|
||||||
$drawer.append($printButton);
|
|
||||||
|
|
||||||
var $slideOptions = $('<button>', {
|
|
||||||
title: Messages.slideOptionsTitle,
|
|
||||||
'class': 'rightside-button fa fa-cog',
|
|
||||||
style: 'font-size: 17px'
|
|
||||||
}).click(function () {
|
|
||||||
$('body').append(createPrintDialog());
|
|
||||||
}).append($('<span>', {'class': 'drawer'}).text(Messages.slideOptionsText));
|
|
||||||
$drawer.append($slideOptions);
|
|
||||||
|
|
||||||
var $present = Cryptpad.createButton('present', true)
|
|
||||||
.click(function () {
|
|
||||||
enterPresentationMode(true);
|
|
||||||
});
|
|
||||||
if (presentMode) {
|
|
||||||
$present.hide();
|
|
||||||
}
|
|
||||||
$rightside.append($present);
|
|
||||||
|
|
||||||
var configureColors = function () {
|
|
||||||
var $back = $('<button>', {
|
|
||||||
id: SLIDE_BACKCOLOR_ID,
|
|
||||||
'class': 'fa fa-square rightside-button',
|
|
||||||
'style': 'font-family: FontAwesome; color: #000;',
|
|
||||||
title: Messages.backgroundButtonTitle
|
|
||||||
});
|
|
||||||
var $text = $('<button>', {
|
|
||||||
id: SLIDE_COLOR_ID,
|
|
||||||
'class': 'fa fa-i-cursor rightside-button',
|
|
||||||
'style': 'font-family: FontAwesome; font-weight: bold; color: #fff;',
|
|
||||||
title: Messages.colorButtonTitle
|
|
||||||
});
|
|
||||||
var $testColor = $('<input>', { type: 'color', value: '!' });
|
|
||||||
var $check = $pad.contents().find("#colorPicker_check");
|
|
||||||
if ($testColor.attr('type') !== "color" || $testColor.val() === '!') { return; }
|
|
||||||
$back.on('click', function() {
|
|
||||||
var $picker = $('<input>', { type: 'color', value: backColor })
|
|
||||||
.css({ display: 'none', })
|
|
||||||
.on('change', function() {
|
|
||||||
updateColors(undefined, this.value);
|
|
||||||
onLocal();
|
|
||||||
});
|
|
||||||
$check.append($picker);
|
|
||||||
setTimeout(function() {
|
|
||||||
$picker.click();
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
$text.on('click', function() {
|
|
||||||
var $picker = $('<input>', { type: 'color', value: textColor })
|
|
||||||
.css({ display: 'none', })
|
|
||||||
.on('change', function() {
|
|
||||||
updateColors(this.value, undefined);
|
|
||||||
onLocal();
|
|
||||||
$check.html('');
|
|
||||||
});
|
|
||||||
$check.append($picker);
|
|
||||||
setTimeout(function() {
|
|
||||||
$picker.click();
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
$rightside.append($back).append($text);
|
|
||||||
};
|
|
||||||
|
|
||||||
configureColors();
|
|
||||||
CodeMirror.configureTheme();
|
|
||||||
|
|
||||||
if (presentMode) {
|
|
||||||
$('#top-bar').hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the hash
|
|
||||||
if (!window.location.hash || window.location.hash === '#') {
|
|
||||||
Cryptpad.replaceHash(editHash);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onReady = function (info) {
|
|
||||||
if (module.realtime !== info.realtime) {
|
|
||||||
var realtime = module.realtime = info.realtime;
|
|
||||||
module.patchText = TextPatcher.create({
|
|
||||||
realtime: realtime,
|
|
||||||
//logging: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var userDoc = module.realtime.getUserDoc();
|
|
||||||
|
|
||||||
var isNew = false;
|
|
||||||
if (userDoc === "" || userDoc === "{}") { isNew = true; }
|
|
||||||
|
|
||||||
var newDoc = "";
|
|
||||||
if(userDoc !== "") {
|
|
||||||
var hjson = JSON.parse(userDoc);
|
|
||||||
newDoc = hjson.content;
|
|
||||||
|
|
||||||
if (typeof (hjson) !== 'object' || Array.isArray(hjson) ||
|
|
||||||
(typeof(hjson.type) !== 'undefined' && hjson.type !== 'slide')) {
|
|
||||||
var errorText = Messages.typeError;
|
|
||||||
Cryptpad.errorLoadingScreen(errorText);
|
|
||||||
throw new Error(errorText);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hjson.highlightMode) {
|
|
||||||
CodeMirror.setMode(hjson.highlightMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!CodeMirror.highlightMode) {
|
|
||||||
CodeMirror.setMode('markdown');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
|
||||||
Metadata.update(userDoc);
|
|
||||||
editor.setValue(newDoc || initialState);
|
|
||||||
|
|
||||||
if (Cryptpad.initialName && Title.isDefaultTitle()) {
|
|
||||||
Title.updateTitle(Cryptpad.initialName);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cryptpad.getPadAttribute('previewMode', function (e, data) {
|
|
||||||
if (e) { return void console.error(e); }
|
|
||||||
if ([true, undefined].indexOf(data) !== -1 && APP.$previewButton) {
|
|
||||||
APP.$previewButton.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Slide.onChange(function (o, n, l) {
|
|
||||||
var slideNumber = '';
|
|
||||||
if (n !== null) {
|
|
||||||
if (Slide.shown) { //Slide.index && Slide.content.length) {
|
|
||||||
slideNumber = ' (' + (++n) + '/' + l + ')';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.title = Title.title + slideNumber;
|
|
||||||
});
|
|
||||||
|
|
||||||
Cryptpad.removeLoadingScreen();
|
|
||||||
setEditable(true);
|
|
||||||
initializing = false;
|
|
||||||
|
|
||||||
onLocal(); // push local state to avoid parse errors later.
|
|
||||||
Slide.update(editor.getValue());
|
|
||||||
|
|
||||||
if (readOnly) { return; }
|
|
||||||
UserList.getLastName(toolbar.$userNameButton, isNew);
|
|
||||||
|
|
||||||
var fmConfig = {
|
|
||||||
dropArea: $iframe.find('.CodeMirror'),
|
|
||||||
body: $iframe.find('body'),
|
|
||||||
onUploaded: function (ev, data) {
|
|
||||||
//var cursor = editor.getCursor();
|
|
||||||
//var cleanName = data.name.replace(/[\[\]]/g, '');
|
|
||||||
//var text = '';
|
|
||||||
var parsed = Cryptpad.parsePadUrl(data.url);
|
|
||||||
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
|
|
||||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
|
||||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '"></media-tag>';
|
|
||||||
editor.replaceSelection(mt);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
APP.FM = Cryptpad.createFileManager(fmConfig);
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onRemote = function () {
|
|
||||||
if (initializing) { return; }
|
|
||||||
if (isHistoryMode) { return; }
|
|
||||||
|
|
||||||
var oldDoc = canonicalize(CodeMirror.$textarea.val());
|
|
||||||
var shjson = module.realtime.getUserDoc();
|
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
|
||||||
Metadata.update(shjson);
|
|
||||||
|
|
||||||
var hjson = JSON.parse(shjson);
|
|
||||||
var remoteDoc = hjson.content;
|
|
||||||
|
|
||||||
var highlightMode = hjson.highlightMode;
|
|
||||||
if (highlightMode && highlightMode !== CodeMirror.highlightMode) {
|
|
||||||
CodeMirror.setMode(highlightMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeMirror.setValueAndCursor(oldDoc, remoteDoc, TextPatcher);
|
|
||||||
|
|
||||||
if (!readOnly) {
|
|
||||||
var textValue = canonicalize(CodeMirror.$textarea.val());
|
|
||||||
var shjson2 = stringifyInner(textValue);
|
|
||||||
if (shjson2 !== shjson) {
|
|
||||||
console.error("shjson2 !== shjson");
|
|
||||||
TextPatcher.log(shjson, TextPatcher.diff(shjson, shjson2));
|
|
||||||
module.patchText(shjson2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Slide.update(remoteDoc);
|
|
||||||
|
|
||||||
if (oldDoc !== remoteDoc) {
|
|
||||||
Cryptpad.notify();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onAbort = function () {
|
|
||||||
// inform of network disconnect
|
|
||||||
setEditable(false);
|
|
||||||
toolbar.failed();
|
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onConnectionChange = function (info) {
|
|
||||||
setEditable(info.state);
|
|
||||||
toolbar.failed();
|
|
||||||
if (info.state) {
|
|
||||||
initializing = true;
|
|
||||||
toolbar.reconnecting(info.myId);
|
|
||||||
Cryptpad.findOKButton().click();
|
|
||||||
} else {
|
|
||||||
if (!Slide.isPresentURL()) {
|
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onError = onConnectError;
|
|
||||||
|
|
||||||
module.realtime = Realtime.start(config);
|
|
||||||
|
|
||||||
editor.on('change', onLocal);
|
|
||||||
|
|
||||||
Cryptpad.onLogout(function () { setEditable(false); });
|
|
||||||
};
|
|
||||||
|
|
||||||
var interval = 100;
|
|
||||||
|
|
||||||
var second = function (CM) {
|
|
||||||
Cryptpad.ready(function () {
|
|
||||||
andThen(CM);
|
|
||||||
Cryptpad.reportAppUsage();
|
|
||||||
});
|
|
||||||
Cryptpad.onError(function (info) {
|
|
||||||
if (info && info.type === "store") {
|
|
||||||
onConnectError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var first = function () {
|
|
||||||
if (ifrw.CodeMirror) {
|
|
||||||
// it exists, call your continuation
|
|
||||||
second(ifrw.CodeMirror);
|
|
||||||
} else {
|
|
||||||
console.log("CodeMirror was not defined. Trying again in %sms", interval);
|
|
||||||
// try again in 'interval' ms
|
|
||||||
setTimeout(first, interval);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
first();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,428 +0,0 @@
|
|||||||
html,
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 40px;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 37px;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 34px;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
font-size: 31px;
|
|
||||||
}
|
|
||||||
h5 {
|
|
||||||
font-size: 27px;
|
|
||||||
}
|
|
||||||
h6 {
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
body .CodeMirror {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
body .CodeMirror-focused .cm-matchhighlight {
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
|
|
||||||
background-position: bottom;
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
}
|
|
||||||
#colorPicker_check {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
@media print {
|
|
||||||
@page {
|
|
||||||
margin: 0;
|
|
||||||
size: landscape;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
body .CodeMirror,
|
|
||||||
body #cme_toolbox {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
body * {
|
|
||||||
visibility: hidden;
|
|
||||||
height: auto;
|
|
||||||
max-height: none;
|
|
||||||
}
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
max-height: none;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
html .cp #print {
|
|
||||||
display: block;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
html .cp #print * {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#cme_toolbox {
|
|
||||||
z-index: 10000;
|
|
||||||
}
|
|
||||||
#editorContainer {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
#editorContainer .CodeMirror {
|
|
||||||
resize: none;
|
|
||||||
width: 100vw;
|
|
||||||
}
|
|
||||||
#editorContainer.preview .CodeMirror {
|
|
||||||
width: 50vw;
|
|
||||||
}
|
|
||||||
.preview .cp {
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.preview .cp div#modal:not(.shown) {
|
|
||||||
position: relative;
|
|
||||||
top: auto;
|
|
||||||
left: auto;
|
|
||||||
width: auto;
|
|
||||||
display: block;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.preview .cp div#modal:not(.shown) #content .slide-container {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.preview .cp div#modal:not(.shown) #content .slide-frame {
|
|
||||||
width: 50vw;
|
|
||||||
height: 28.125vw;
|
|
||||||
max-height: 100vh;
|
|
||||||
max-width: 177.78vh;
|
|
||||||
}
|
|
||||||
.cp {
|
|
||||||
/* Slide position (print mode) */
|
|
||||||
/* Slide position (present mode) */
|
|
||||||
/* Slide content */
|
|
||||||
}
|
|
||||||
.cp #print {
|
|
||||||
position: relative;
|
|
||||||
display: none;
|
|
||||||
font-size: 10.125vw;
|
|
||||||
/*.slide-frame:first-child {
|
|
||||||
margin-top: ~"calc(((100vh - 56.25vw)/2))";
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
.cp #print .slide-frame {
|
|
||||||
display: block !important;
|
|
||||||
padding: 0.5em;
|
|
||||||
margin: auto;
|
|
||||||
border: 1px solid black;
|
|
||||||
height: 50.625vw;
|
|
||||||
width: 90vw;
|
|
||||||
page-break-after: always;
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.cp #print .slide-frame li {
|
|
||||||
min-width: 45vw;
|
|
||||||
}
|
|
||||||
.cp #print .slide-frame h1 {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
.cp #print .slide-container {
|
|
||||||
width: 90vw;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0vh 5vw;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.cp #print .slide-container:last-child {
|
|
||||||
height: calc(100vh - 2px);
|
|
||||||
}
|
|
||||||
.cp div.modal,
|
|
||||||
.cp div#modal {
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
/* Navigation buttons */
|
|
||||||
box-sizing: border-box;
|
|
||||||
z-index: 9001;
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100vh;
|
|
||||||
display: none;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
.cp div.modal .button,
|
|
||||||
.cp div#modal .button {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 30px;
|
|
||||||
opacity: 0.6;
|
|
||||||
display: none;
|
|
||||||
z-index: 9001;
|
|
||||||
}
|
|
||||||
.cp div.modal .button:hover,
|
|
||||||
.cp div#modal .button:hover {
|
|
||||||
opacity: 1;
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
.cp div.modal #button_exit,
|
|
||||||
.cp div#modal #button_exit {
|
|
||||||
left: 20px;
|
|
||||||
top: 20px;
|
|
||||||
}
|
|
||||||
.cp div.modal #button_left,
|
|
||||||
.cp div#modal #button_left {
|
|
||||||
left: 6vw;
|
|
||||||
bottom: 10vh;
|
|
||||||
}
|
|
||||||
.cp div.modal #button_right,
|
|
||||||
.cp div#modal #button_right {
|
|
||||||
right: 6vw;
|
|
||||||
bottom: 10vh;
|
|
||||||
}
|
|
||||||
.cp div.modal.shown,
|
|
||||||
.cp div#modal.shown {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
z-index: 100000;
|
|
||||||
height: 100vh;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.cp div.modal #content,
|
|
||||||
.cp div#modal #content {
|
|
||||||
font-size: 20vh;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
overflow: visible;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.cp div.modal #content .slide-frame,
|
|
||||||
.cp div#modal #content .slide-frame {
|
|
||||||
overflow: hidden;
|
|
||||||
display: inline-block;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid;
|
|
||||||
white-space: normal;
|
|
||||||
vertical-align: middle;
|
|
||||||
/* center things as much as possible
|
|
||||||
|
|
||||||
margin-top: 50vh;
|
|
||||||
margin-bottom: 50vh;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
|
|
||||||
*/
|
|
||||||
padding: 0.5em;
|
|
||||||
width: 100vw;
|
|
||||||
height: 56.25vw;
|
|
||||||
max-height: 100vh;
|
|
||||||
max-width: 177.78vh;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
.cp div.modal #content .slide-container,
|
|
||||||
.cp div#modal #content .slide-container {
|
|
||||||
display: inline-flex;
|
|
||||||
height: 100%;
|
|
||||||
width: 100vw;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
.cp div.modal #content.transition .slide-container,
|
|
||||||
.cp div#modal #content.transition .slide-container {
|
|
||||||
transition: margin-left 1s;
|
|
||||||
}
|
|
||||||
.cp div.modal .center,
|
|
||||||
.cp div#modal .center {
|
|
||||||
position: relative;
|
|
||||||
width: 80%;
|
|
||||||
height: 80%;
|
|
||||||
margin: auto;
|
|
||||||
border: 1px solid #ffffff;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.cp div.modal.shown,
|
|
||||||
.cp div#modal.shown {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame,
|
|
||||||
.cp #print .slide-frame {
|
|
||||||
text-align: left;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame *,
|
|
||||||
.cp #print .slide-frame * {
|
|
||||||
font-size: 27.5%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame * *,
|
|
||||||
.cp #print .slide-frame * * {
|
|
||||||
font-size: 100%;
|
|
||||||
line-height: 1em;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame ul ul,
|
|
||||||
.cp #print .slide-frame ul ul,
|
|
||||||
.cp div#modal #content .slide-frame ol ul,
|
|
||||||
.cp #print .slide-frame ol ul,
|
|
||||||
.cp div#modal #content .slide-frame ul ol,
|
|
||||||
.cp #print .slide-frame ul ol,
|
|
||||||
.cp div#modal #content .slide-frame ol ol,
|
|
||||||
.cp #print .slide-frame ol ol {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame h1,
|
|
||||||
.cp #print .slide-frame h1 {
|
|
||||||
font-size: 50%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame h2,
|
|
||||||
.cp #print .slide-frame h2 {
|
|
||||||
font-size: 42%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame h3,
|
|
||||||
.cp #print .slide-frame h3 {
|
|
||||||
font-size: 36%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame h4,
|
|
||||||
.cp #print .slide-frame h4 {
|
|
||||||
font-size: 30%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame h5,
|
|
||||||
.cp #print .slide-frame h5 {
|
|
||||||
font-size: 22%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame h6,
|
|
||||||
.cp #print .slide-frame h6 {
|
|
||||||
font-size: 16%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame h1,
|
|
||||||
.cp #print .slide-frame h1,
|
|
||||||
.cp div#modal #content .slide-frame h2,
|
|
||||||
.cp #print .slide-frame h2,
|
|
||||||
.cp div#modal #content .slide-frame h3,
|
|
||||||
.cp #print .slide-frame h3,
|
|
||||||
.cp div#modal #content .slide-frame h4,
|
|
||||||
.cp #print .slide-frame h4,
|
|
||||||
.cp div#modal #content .slide-frame h5,
|
|
||||||
.cp #print .slide-frame h5,
|
|
||||||
.cp div#modal #content .slide-frame h6,
|
|
||||||
.cp #print .slide-frame h6 {
|
|
||||||
color: inherit;
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame pre > code,
|
|
||||||
.cp #print .slide-frame pre > code {
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
border: 1px solid #333;
|
|
||||||
width: 90%;
|
|
||||||
margin: auto;
|
|
||||||
padding-left: .25vw;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame ul,
|
|
||||||
.cp #print .slide-frame ul,
|
|
||||||
.cp div#modal #content .slide-frame ol,
|
|
||||||
.cp #print .slide-frame ol {
|
|
||||||
min-width: 50%;
|
|
||||||
max-width: 100%;
|
|
||||||
display: table;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding-left: 0.3em;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame img,
|
|
||||||
.cp #print .slide-frame img {
|
|
||||||
position: relative;
|
|
||||||
min-width: 1%;
|
|
||||||
max-width: 90%;
|
|
||||||
max-height: 90%;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame .slideNumber,
|
|
||||||
.cp #print .slide-frame .slideNumber {
|
|
||||||
position: absolute;
|
|
||||||
right: 5vh;
|
|
||||||
bottom: 5vh;
|
|
||||||
font-size: 10%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame .slideDate,
|
|
||||||
.cp #print .slide-frame .slideDate {
|
|
||||||
position: absolute;
|
|
||||||
left: 5vh;
|
|
||||||
bottom: 5vh;
|
|
||||||
font-size: 10%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
.cp div#modal #content .slide-frame .slideTitle,
|
|
||||||
.cp #print .slide-frame .slideTitle {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 5vh;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 10%;
|
|
||||||
line-height: 110%;
|
|
||||||
}
|
|
||||||
#fileDialog {
|
|
||||||
position: absolute;
|
|
||||||
background-color: rgba(200, 200, 200, 0.8);
|
|
||||||
top: 15vh;
|
|
||||||
bottom: 15vh;
|
|
||||||
left: 10vw;
|
|
||||||
right: 10vw;
|
|
||||||
border: 1px solid black;
|
|
||||||
z-index: 10;
|
|
||||||
overflow: auto;
|
|
||||||
display: none;
|
|
||||||
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#fileDialog .close {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
#fileDialog .element {
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-flex;
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
margin: 5px;
|
|
||||||
overflow: hidden;
|
|
||||||
word-wrap: break-word;
|
|
||||||
background-color: white;
|
|
||||||
padding: 5px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
#fileDialog .element span {
|
|
||||||
width: 100px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
@ -1,321 +0,0 @@
|
|||||||
define([
|
|
||||||
'jquery',
|
|
||||||
'/common/diffMarked.js',
|
|
||||||
'/common/cryptpad-common.js',
|
|
||||||
],function ($, DiffMd, Cryptpad) {
|
|
||||||
|
|
||||||
var Slide = {
|
|
||||||
index: 0,
|
|
||||||
lastIndex: 0,
|
|
||||||
content: [],
|
|
||||||
changeHandlers: [],
|
|
||||||
};
|
|
||||||
var ifrw;
|
|
||||||
var $modal;
|
|
||||||
var $content;
|
|
||||||
var $pad;
|
|
||||||
var placeholder;
|
|
||||||
var options;
|
|
||||||
var separator = '<hr data-pewpew="pezpez">';
|
|
||||||
var separatorReg = /<hr data\-pewpew="pezpez">/g;
|
|
||||||
var slideClass = 'slide-frame';
|
|
||||||
var Title;
|
|
||||||
|
|
||||||
Slide.onChange = function (f) {
|
|
||||||
if (typeof(f) === 'function') {
|
|
||||||
Slide.changeHandlers.push(f);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var getNumberOfSlides = Slide.getNumberOfSlides = function () {
|
|
||||||
return $content.find('.' + slideClass).length;
|
|
||||||
};
|
|
||||||
|
|
||||||
var change = function (oldIndex, newIndex) {
|
|
||||||
if (Slide.changeHandlers.length) {
|
|
||||||
Slide.changeHandlers.some(function (f) {
|
|
||||||
f(oldIndex, newIndex, getNumberOfSlides());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateFontSize = Slide.updateFontSize = function () {
|
|
||||||
// 20vh
|
|
||||||
// 20 * 16 / 9vw
|
|
||||||
var wbase = 20;
|
|
||||||
var vh = 20;
|
|
||||||
var $elem = $(window);
|
|
||||||
if (!Slide.shown) {
|
|
||||||
wbase = 10;
|
|
||||||
vh *= $content.height()/$(window).height();
|
|
||||||
$elem = $content;
|
|
||||||
}
|
|
||||||
if ($elem.width() > 16/9*$elem.height()) {
|
|
||||||
$content.css('font-size', vh+'vh');
|
|
||||||
// $print.css('font-size', '20vh');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$content.css('font-size', (wbase*9/16)+'vw');
|
|
||||||
// $print.css('font-size', (20*9/16)+'vw');
|
|
||||||
};
|
|
||||||
|
|
||||||
var fixCSS = function (css) {
|
|
||||||
var append = '.cp #print .slide-frame ';
|
|
||||||
var append2 = '.cp div#modal #content .slide-frame ';
|
|
||||||
return css.replace(/(\n*)([^\n}]+)\s*\{/g, '$1' + append + '$2,' + append2 + '$2 {');
|
|
||||||
};
|
|
||||||
|
|
||||||
var goTo = Slide.goTo = function (i) {
|
|
||||||
i = i || 0;
|
|
||||||
Slide.index = i;
|
|
||||||
$content.find('.slide-container').first().css('margin-left', -(i*100)+'%');
|
|
||||||
updateFontSize();
|
|
||||||
change(Slide.lastIndex, Slide.index);
|
|
||||||
$modal.find('#button_left > span').css({
|
|
||||||
opacity: Slide.index === 0? 0: 1
|
|
||||||
});
|
|
||||||
$modal.find('#button_right > span').css({
|
|
||||||
opacity: Slide.index === (getNumberOfSlides() -1)? 0: 1
|
|
||||||
});
|
|
||||||
};
|
|
||||||
var draw = Slide.draw = function (i) {
|
|
||||||
if (typeof(Slide.content) !== 'string') { return; }
|
|
||||||
|
|
||||||
var c = Slide.content;
|
|
||||||
var m = '<span class="slide-container"><span class="'+slideClass+'">'+DiffMd.render(c).replace(separatorReg, '</span></span><span class="slide-container"><span class="'+slideClass+'">')+'</span></span>';
|
|
||||||
|
|
||||||
DiffMd.apply(m, $content);
|
|
||||||
|
|
||||||
var length = getNumberOfSlides();
|
|
||||||
$modal.find('style.slideStyle').remove();
|
|
||||||
if (options.style && Slide.shown) {
|
|
||||||
$modal.prepend($('<style>', {'class': 'slideStyle'}).text(fixCSS(options.style)));
|
|
||||||
}
|
|
||||||
$content.find('.slide-frame').each(function (i, el) {
|
|
||||||
if (options.slide) {
|
|
||||||
$('<div>', {'class': 'slideNumber'}).text((i+1)+'/'+length).appendTo($(el));
|
|
||||||
}
|
|
||||||
if (options.date) {
|
|
||||||
$('<div>', {'class': 'slideDate'}).text(new Date().toLocaleDateString()).appendTo($(el));
|
|
||||||
}
|
|
||||||
if (options.title) {
|
|
||||||
$('<div>', {'class': 'slideTitle'}).text(Title.title).appendTo($(el));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$content.removeClass('transition');
|
|
||||||
if (options.transition || typeof(options.transition) === "undefined") {
|
|
||||||
$content.addClass('transition');
|
|
||||||
}
|
|
||||||
//$content.find('.' + slideClass).hide();
|
|
||||||
//$content.find('.' + slideClass + ':eq( ' + i + ' )').show();
|
|
||||||
//$content.css('margin-left', -(i*100)+'vw');
|
|
||||||
goTo(Math.min(i, getNumberOfSlides() - 1));
|
|
||||||
};
|
|
||||||
|
|
||||||
Slide.updateOptions = function () {
|
|
||||||
draw(Slide.index);
|
|
||||||
};
|
|
||||||
|
|
||||||
var isPresentURL = Slide.isPresentURL = function () {
|
|
||||||
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
return parsed && parsed.hashData && parsed.hashData.present;
|
|
||||||
};
|
|
||||||
|
|
||||||
var show = Slide.show = function (bool, content) {
|
|
||||||
var parsed = Cryptpad.parsePadUrl(window.location.href);
|
|
||||||
var hashData = parsed.hashData || {};
|
|
||||||
Slide.shown = bool;
|
|
||||||
if (bool) {
|
|
||||||
Slide.update(content);
|
|
||||||
Slide.draw(Slide.index);
|
|
||||||
$modal.addClass('shown');
|
|
||||||
$(ifrw).focus();
|
|
||||||
change(null, Slide.index);
|
|
||||||
if (!isPresentURL()) {
|
|
||||||
window.location += parsed.getUrl({present: true, embed: hashData.embed});
|
|
||||||
}
|
|
||||||
$pad.contents().find('.cryptpad-present-button').hide();
|
|
||||||
$pad.contents().find('.cryptpad-source-button').show();
|
|
||||||
$pad.addClass('fullscreen');
|
|
||||||
$('#iframe-container').addClass('fullscreen');
|
|
||||||
$('.top-bar').hide();
|
|
||||||
updateFontSize();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.location = parsed.getUrl({embed: hashData.embed});
|
|
||||||
change(Slide.index, null);
|
|
||||||
$pad.contents().find('.cryptpad-present-button').show();
|
|
||||||
$pad.contents().find('.cryptpad-source-button').hide();
|
|
||||||
$pad.removeClass('fullscreen');
|
|
||||||
$('#iframe-container').removeClass('fullscreen');
|
|
||||||
$('.top-bar').show();
|
|
||||||
$modal.removeClass('shown');
|
|
||||||
updateFontSize();
|
|
||||||
};
|
|
||||||
|
|
||||||
Slide.update = function (content) {
|
|
||||||
updateFontSize();
|
|
||||||
//if (!init) { return; }
|
|
||||||
if (!content) { content = ''; }
|
|
||||||
var old = Slide.content;
|
|
||||||
Slide.content = content.replace(/\n\s*\-\-\-\s*\n/g, '\n\n'+separator+'\n\n');
|
|
||||||
if (old !== Slide.content) {
|
|
||||||
draw(Slide.index);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
change(Slide.lastIndex, Slide.index);
|
|
||||||
};
|
|
||||||
|
|
||||||
Slide.left = function () {
|
|
||||||
console.log('left');
|
|
||||||
Slide.lastIndex = Slide.index;
|
|
||||||
|
|
||||||
var i = Slide.index = Math.max(0, Slide.index - 1);
|
|
||||||
Slide.goTo(i);
|
|
||||||
};
|
|
||||||
|
|
||||||
Slide.right = function () {
|
|
||||||
console.log('right');
|
|
||||||
Slide.lastIndex = Slide.index;
|
|
||||||
|
|
||||||
var i = Slide.index = Math.min(getNumberOfSlides() -1, Slide.index + 1);
|
|
||||||
Slide.goTo(i);
|
|
||||||
};
|
|
||||||
|
|
||||||
Slide.first = function () {
|
|
||||||
console.log('first');
|
|
||||||
Slide.lastIndex = Slide.index;
|
|
||||||
|
|
||||||
var i = Slide.index = 0;
|
|
||||||
Slide.goTo(i);
|
|
||||||
};
|
|
||||||
|
|
||||||
Slide.last = function () {
|
|
||||||
console.log('end');
|
|
||||||
Slide.lastIndex = Slide.index;
|
|
||||||
|
|
||||||
var i = Slide.index = getNumberOfSlides() - 1;
|
|
||||||
Slide.goTo(i);
|
|
||||||
};
|
|
||||||
|
|
||||||
var addEvent = function () {
|
|
||||||
var icon_to;
|
|
||||||
$modal.mousemove(function () {
|
|
||||||
var $buttons = $modal.find('.button');
|
|
||||||
$buttons.show();
|
|
||||||
if (icon_to) { window.clearTimeout(icon_to); }
|
|
||||||
icon_to = window.setTimeout(function() {
|
|
||||||
$buttons.fadeOut();
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
$modal.find('#button_exit').click(function () {
|
|
||||||
var ev = $.Event("keyup");
|
|
||||||
ev.which = 27;
|
|
||||||
$modal.trigger(ev);
|
|
||||||
});
|
|
||||||
$modal.find('#button_left').click(function () {
|
|
||||||
var ev = $.Event("keyup");
|
|
||||||
ev.which = 37;
|
|
||||||
$modal.trigger(ev);
|
|
||||||
});
|
|
||||||
$modal.find('#button_right').click(function () {
|
|
||||||
console.log('right');
|
|
||||||
var ev = $.Event("keyup");
|
|
||||||
ev.which = 39;
|
|
||||||
$modal.trigger(ev);
|
|
||||||
});
|
|
||||||
|
|
||||||
$pad.contents().find('.CodeMirror').keyup(function (e) { e.stopPropagation(); });
|
|
||||||
$(ifrw).on('keyup', function (e) {
|
|
||||||
//if (!Slide.shown) { return; }
|
|
||||||
if (e.ctrlKey) { return; }
|
|
||||||
switch(e.which) {
|
|
||||||
case 33: // pageup
|
|
||||||
case 38: // up
|
|
||||||
case 37: // left
|
|
||||||
Slide.left();
|
|
||||||
break;
|
|
||||||
case 34: // pagedown
|
|
||||||
case 32: // space
|
|
||||||
case 40: // down
|
|
||||||
case 39: // right
|
|
||||||
Slide.right();
|
|
||||||
break;
|
|
||||||
case 36: // home
|
|
||||||
Slide.first();
|
|
||||||
break;
|
|
||||||
case 35: // end
|
|
||||||
Slide.last();
|
|
||||||
break;
|
|
||||||
case 27: // esc
|
|
||||||
show(false);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log(e.which);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$(window).resize(Slide.updateFontSize);
|
|
||||||
|
|
||||||
// Swipe
|
|
||||||
var addSwipeEvents = function () {
|
|
||||||
var touch = {
|
|
||||||
maxTime: 2000,
|
|
||||||
minXDist: 150,
|
|
||||||
maxYDist: 100
|
|
||||||
};
|
|
||||||
|
|
||||||
var resetSwipe = function () {
|
|
||||||
touch.x = 0;
|
|
||||||
touch.y = 0;
|
|
||||||
touch.time = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
$content.on('touchstart', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
resetSwipe();
|
|
||||||
var t = e.originalEvent.changedTouches[0];
|
|
||||||
touch.x = t.pageX;
|
|
||||||
touch.y = t.pageY;
|
|
||||||
touch.time = new Date().getTime();
|
|
||||||
});
|
|
||||||
|
|
||||||
$content.on('touchend', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var t = e.originalEvent.changedTouches[0];
|
|
||||||
var xDist = t.pageX - touch.x;
|
|
||||||
var yDist = t.pageY - touch.y;
|
|
||||||
var time = new Date().getTime() - touch.time;
|
|
||||||
if (time <= touch.maxTime && Math.abs(xDist) >= touch.minXDist && Math.abs(yDist) <= touch.maxYDist) {
|
|
||||||
if (xDist < 0) {
|
|
||||||
Slide.right();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Slide.left();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$content.on('touchmove', function (e){
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Slide.setModal = function ($m, $c, $p, iframe, opt, ph) {
|
|
||||||
$modal = Slide.$modal = $m;
|
|
||||||
$content = Slide.$content = $c;
|
|
||||||
$pad = Slide.$pad = $p;
|
|
||||||
ifrw = Slide.ifrw = iframe;
|
|
||||||
placeholder = Slide.placeholder = ph;
|
|
||||||
options = Slide.options = opt;
|
|
||||||
addEvent();
|
|
||||||
addSwipeEvents();
|
|
||||||
};
|
|
||||||
|
|
||||||
Slide.setTitle = function (titleObj) {
|
|
||||||
Title = titleObj;
|
|
||||||
};
|
|
||||||
|
|
||||||
return Slide;
|
|
||||||
});
|
|
||||||
@ -1,398 +0,0 @@
|
|||||||
@import "/customize/src/less/variables.less";
|
|
||||||
@import "/customize/src/less/mixins.less";
|
|
||||||
@import "/common/markdown.less";
|
|
||||||
|
|
||||||
// used for slides
|
|
||||||
.viewportRatio (@x, @y, @p: 100) {
|
|
||||||
width: @p * 100vw;
|
|
||||||
height: @y * (@p * 100vw) / @x;
|
|
||||||
max-width: @x / @y * (@p * 100vh);
|
|
||||||
max-height: (@p * 100vh);
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body{
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-size: unset;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
//.cp {
|
|
||||||
h1 { font-size: 40px; }
|
|
||||||
h2 { font-size: 37px; }
|
|
||||||
h3 { font-size: 34px; }
|
|
||||||
h4 { font-size: 31px; }
|
|
||||||
h5 { font-size: 27px; }
|
|
||||||
h6 { font-size: 24px; }
|
|
||||||
|
|
||||||
body {
|
|
||||||
.CodeMirror {
|
|
||||||
height: 100%;
|
|
||||||
font-size: initial;
|
|
||||||
}
|
|
||||||
.CodeMirror-focused .cm-matchhighlight {
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
|
|
||||||
background-position: bottom;
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#colorPicker_check {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
@page {
|
|
||||||
margin: 0;
|
|
||||||
size: landscape;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
.CodeMirror, #cme_toolbox {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
* {
|
|
||||||
visibility: hidden;
|
|
||||||
height: auto;
|
|
||||||
max-height: none;
|
|
||||||
}
|
|
||||||
display:block;
|
|
||||||
}
|
|
||||||
html, body {
|
|
||||||
height: auto;
|
|
||||||
max-height: none;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
html {
|
|
||||||
.cp #print {
|
|
||||||
display: block;
|
|
||||||
visibility: visible;
|
|
||||||
* {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.cp #modal {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.cp {
|
|
||||||
flex: 1 !important;
|
|
||||||
overflow: visible !important;
|
|
||||||
}
|
|
||||||
.userlist-drawer {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
#editorContainer {
|
|
||||||
height: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#cme_toolbox {
|
|
||||||
z-index: 10000;
|
|
||||||
}
|
|
||||||
#editorContainer {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
.CodeMirror {
|
|
||||||
resize: none;
|
|
||||||
width: 100vw;
|
|
||||||
}
|
|
||||||
&.preview {
|
|
||||||
.CodeMirror {
|
|
||||||
//resize: horizontal;
|
|
||||||
width: 50vw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.preview {
|
|
||||||
.cp {
|
|
||||||
width: 50vw;
|
|
||||||
overflow: hidden;
|
|
||||||
div#modal:not(.shown) {
|
|
||||||
position: relative;
|
|
||||||
top: auto;
|
|
||||||
left: auto;
|
|
||||||
width: auto;
|
|
||||||
display: block;
|
|
||||||
height: 100%;
|
|
||||||
#content {
|
|
||||||
.slide-container {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.slide-frame {
|
|
||||||
width: 50vw;
|
|
||||||
height: 28.125vw; // height:width ratio = 9/16 = .5625
|
|
||||||
max-height: ~"calc(100vh - 96px)";
|
|
||||||
max-width: ~"calc(16 / 9 * (100vh - 96px))";
|
|
||||||
//max-height: 100vh;
|
|
||||||
//max-width: 177.78vh; // 16/9 = 1.778
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#button_exit {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.CodeMirror {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.cp {
|
|
||||||
|
|
||||||
/* Slide position (print mode) */
|
|
||||||
@ratio:0.9;
|
|
||||||
#print {
|
|
||||||
position: relative;
|
|
||||||
display: none;
|
|
||||||
font-size: @ratio*11.25vw;
|
|
||||||
.slide-frame {
|
|
||||||
display: block !important;
|
|
||||||
padding: 0.5em;
|
|
||||||
margin: auto;
|
|
||||||
border: 1px solid black;
|
|
||||||
height: @ratio*56.25vw;
|
|
||||||
width: @ratio*100vw;
|
|
||||||
page-break-after: always;
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
li {
|
|
||||||
min-width: @ratio*50vw;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
}
|
|
||||||
.slide-container {
|
|
||||||
width: 90vw;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0vh 5vw !important;
|
|
||||||
display: flex;
|
|
||||||
&:last-child {
|
|
||||||
height: ~"calc(100vh - 2px)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Slide position (present mode) */
|
|
||||||
div.modal, div#modal {
|
|
||||||
display: none;
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
/* Navigation buttons */
|
|
||||||
.button {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 30px;
|
|
||||||
opacity: 0.6;
|
|
||||||
display: none;
|
|
||||||
z-index: 9001;
|
|
||||||
}
|
|
||||||
.button:hover {
|
|
||||||
opacity: 1;
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
#button_exit {
|
|
||||||
left: 20px;
|
|
||||||
top: 20px;
|
|
||||||
}
|
|
||||||
#button_left {
|
|
||||||
left: 6vw;
|
|
||||||
bottom: 10vh;
|
|
||||||
}
|
|
||||||
#button_right {
|
|
||||||
right: 6vw;
|
|
||||||
bottom: 10vh;
|
|
||||||
}
|
|
||||||
&.shown {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
z-index: 100000;
|
|
||||||
height: 100vh;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
#content {
|
|
||||||
font-size: 20vh;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
overflow: visible;
|
|
||||||
white-space: nowrap;
|
|
||||||
.slide-frame {
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column !important;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid;
|
|
||||||
white-space: normal;
|
|
||||||
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
padding: 0.5em;
|
|
||||||
width: 100vw;
|
|
||||||
height: 56.25vw; // height:width ratio = 9/16 = .5625
|
|
||||||
max-height: 100vh;
|
|
||||||
max-width: 177.78vh; // 16/9 = 1.778
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
.slide-container {
|
|
||||||
display: inline-flex;
|
|
||||||
height: 100%; width: 100vw;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
&.transition {
|
|
||||||
.slide-container {
|
|
||||||
transition: margin-left 1s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
media-tag button {
|
|
||||||
max-height: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
|
||||||
z-index: 9001;
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100vh;
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
background-color: @slide-default-bg;
|
|
||||||
|
|
||||||
.center {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 80%;
|
|
||||||
height: 80%;
|
|
||||||
margin: auto;
|
|
||||||
border: 1px solid @light-base;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.shown {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Slide content */
|
|
||||||
div#modal #content, #print {
|
|
||||||
.slide-frame {
|
|
||||||
* {
|
|
||||||
.size(2.75);
|
|
||||||
* {
|
|
||||||
font-size: 1em;
|
|
||||||
line-height: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ul, ol {
|
|
||||||
ul, ol {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 { .size(5); }
|
|
||||||
h2 { .size(4.2); }
|
|
||||||
h3 { .size(3.6); }
|
|
||||||
h4 { .size (3); }
|
|
||||||
h5 { .size(2.2); }
|
|
||||||
h6 { .size(1.6); }
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
color: inherit;
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown_preformatted-code;
|
|
||||||
|
|
||||||
ul, ol {
|
|
||||||
min-width: 50%;
|
|
||||||
max-width: 100%;
|
|
||||||
display: table;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding-left: 0.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixes image overflowing
|
|
||||||
media-tag {
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
& + * {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
position: relative;
|
|
||||||
min-width: 1%;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
.slideNumber {
|
|
||||||
position: absolute;
|
|
||||||
right: 5vh;
|
|
||||||
bottom: 5vh;
|
|
||||||
.size(1);
|
|
||||||
}
|
|
||||||
.slideDate {
|
|
||||||
position: absolute;
|
|
||||||
left: 5vh;
|
|
||||||
bottom: 5vh;
|
|
||||||
.size(1);
|
|
||||||
}
|
|
||||||
.slideTitle {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 5vh;
|
|
||||||
left: 0px; right: 0px;
|
|
||||||
text-align: center;
|
|
||||||
.size(1);
|
|
||||||
}
|
|
||||||
text-align: left;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@import "/common/file-dialog.less";
|
|
||||||
|
|
||||||
.slide-frame * {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
//display: flex;
|
|
||||||
//flex-flow: row wrap;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
min-height:0;
|
|
||||||
min-width:0;
|
|
||||||
//flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@import "../customize/src/less2/include/mediatag.less";
|
|
||||||
|
|
||||||
.mediatag_base();
|
|
||||||
52
www/oldwhiteboard/colors.js
Normal file
52
www/oldwhiteboard/colors.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
define(function () {
|
||||||
|
var padZero = function (str, len) {
|
||||||
|
len = len || 2;
|
||||||
|
var zeros = new Array(len).join('0');
|
||||||
|
return (zeros + str).slice(-len);
|
||||||
|
};
|
||||||
|
var invertColor = function (hex) {
|
||||||
|
if (hex.indexOf('#') === 0) {
|
||||||
|
hex = hex.slice(1);
|
||||||
|
}
|
||||||
|
// convert 3-digit hex to 6-digits.
|
||||||
|
if (hex.length === 3) {
|
||||||
|
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
||||||
|
}
|
||||||
|
if (hex.length !== 6) {
|
||||||
|
console.error(hex);
|
||||||
|
throw new Error('Invalid HEX color.');
|
||||||
|
}
|
||||||
|
// invert color components
|
||||||
|
var r = (255 - parseInt(hex.slice(0, 2), 16)).toString(16),
|
||||||
|
g = (255 - parseInt(hex.slice(2, 4), 16)).toString(16),
|
||||||
|
b = (255 - parseInt(hex.slice(4, 6), 16)).toString(16);
|
||||||
|
// pad each with zeros and return
|
||||||
|
return '#' + padZero(r) + padZero(g) + padZero(b);
|
||||||
|
};
|
||||||
|
var rgb2hex = function (rgb) {
|
||||||
|
if (rgb.indexOf('#') === 0) { return rgb; }
|
||||||
|
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
|
||||||
|
var hex = function (x) {
|
||||||
|
return ("0" + parseInt(x).toString(16)).slice(-2);
|
||||||
|
};
|
||||||
|
return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
|
||||||
|
};
|
||||||
|
var hex2rgba = function (hex, opacity) {
|
||||||
|
if (hex.indexOf('#') === 0) {
|
||||||
|
hex = hex.slice(1);
|
||||||
|
}
|
||||||
|
if (hex.length === 3) {
|
||||||
|
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
||||||
|
}
|
||||||
|
if (!opacity) { opacity = 1; }
|
||||||
|
var r = parseInt(hex.slice(0,2), 16);
|
||||||
|
var g = parseInt(hex.slice(2,4), 16);
|
||||||
|
var b = parseInt(hex.slice(4,6), 16);
|
||||||
|
return 'rgba('+r+', '+g+', '+b+', '+opacity+')';
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
invert: invertColor,
|
||||||
|
rgb2hex: rgb2hex,
|
||||||
|
hex2rgba: hex2rgba
|
||||||
|
};
|
||||||
|
});
|
||||||
9
www/oldwhiteboard/index.html
Normal file
9
www/oldwhiteboard/index.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="cp">
|
||||||
|
<head>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
511
www/oldwhiteboard/main.js
Normal file
511
www/oldwhiteboard/main.js
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'/api/config',
|
||||||
|
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||||
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
|
'/common/toolbar2.js',
|
||||||
|
'/bower_components/textpatcher/TextPatcher.amd.js',
|
||||||
|
'json.sortify',
|
||||||
|
'/bower_components/chainpad-json-validator/json-ot.js',
|
||||||
|
'/common/cryptpad-common.js',
|
||||||
|
'/common/cryptget.js',
|
||||||
|
'/whiteboard/colors.js',
|
||||||
|
'/customize/application_config.js',
|
||||||
|
'/common/common-thumbnail.js',
|
||||||
|
'/bower_components/secure-fabric.js/dist/fabric.min.js',
|
||||||
|
'/bower_components/file-saver/FileSaver.min.js',
|
||||||
|
|
||||||
|
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||||
|
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||||
|
'less!/customize/src/less/cryptpad.less',
|
||||||
|
'less!/whiteboard/whiteboard.less',
|
||||||
|
'less!/customize/src/less/toolbar.less',
|
||||||
|
], function ($, Config, Realtime, Crypto, Toolbar, TextPatcher, JSONSortify, JsonOT, Cryptpad, Cryptget, Colors, AppConfig, Thumb) {
|
||||||
|
var saveAs = window.saveAs;
|
||||||
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
|
var module = window.APP = { $:$ };
|
||||||
|
var Fabric = module.Fabric = window.fabric;
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
Cryptpad.addLoadingScreen();
|
||||||
|
var onConnectError = function () {
|
||||||
|
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
||||||
|
};
|
||||||
|
var toolbar;
|
||||||
|
|
||||||
|
var secret = Cryptpad.getSecrets();
|
||||||
|
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
||||||
|
if (!secret.keys) {
|
||||||
|
secret.keys = secret.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
var andThen = function () {
|
||||||
|
/* Initialize Fabric */
|
||||||
|
var canvas = module.canvas = new Fabric.Canvas('canvas');
|
||||||
|
var $canvas = $('canvas');
|
||||||
|
var $controls = $('#controls');
|
||||||
|
var $canvasContainer = $('canvas').parents('.canvas-container');
|
||||||
|
var $pickers = $('#pickers');
|
||||||
|
var $colors = $('#colors');
|
||||||
|
var $cursors = $('#cursors');
|
||||||
|
var $deleteButton = $('#delete');
|
||||||
|
|
||||||
|
var brush = {
|
||||||
|
color: '#000000',
|
||||||
|
opacity: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var $toggle = $('#toggleDraw');
|
||||||
|
var $width = $('#width');
|
||||||
|
var $widthLabel = $('label[for="width"]');
|
||||||
|
var $opacity = $('#opacity');
|
||||||
|
var $opacityLabel = $('label[for="opacity"]');
|
||||||
|
window.canvas = canvas;
|
||||||
|
var createCursor = function () {
|
||||||
|
var w = canvas.freeDrawingBrush.width;
|
||||||
|
var c = canvas.freeDrawingBrush.color;
|
||||||
|
var size = w > 30 ? w+2 : w+32;
|
||||||
|
$cursors.html('<canvas width="'+size+'" height="'+size+'"></canvas>');
|
||||||
|
var $ccanvas = $cursors.find('canvas');
|
||||||
|
var ccanvas = $ccanvas[0];
|
||||||
|
|
||||||
|
var ctx = ccanvas.getContext('2d');
|
||||||
|
var centerX = size / 2;
|
||||||
|
var centerY = size / 2;
|
||||||
|
var radius = w/2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
|
||||||
|
ctx.fillStyle = c;
|
||||||
|
ctx.fill();
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle = brush.color;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(size/2, 0); ctx.lineTo(size/2, 10);
|
||||||
|
ctx.moveTo(size/2, size); ctx.lineTo(size/2, size-10);
|
||||||
|
ctx.moveTo(0, size/2); ctx.lineTo(10, size/2);
|
||||||
|
ctx.moveTo(size, size/2); ctx.lineTo(size-10, size/2);
|
||||||
|
ctx.strokeStyle = '#000000';
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
var img = ccanvas.toDataURL("image/png");
|
||||||
|
$controls.find('.selected > img').attr('src', img);
|
||||||
|
canvas.freeDrawingCursor = 'url('+img+') '+size/2+' '+size/2+', crosshair';
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateBrushWidth = function () {
|
||||||
|
var val = $width.val();
|
||||||
|
canvas.freeDrawingBrush.width = Number(val);
|
||||||
|
$widthLabel.text(Cryptpad.Messages._getKey("canvas_widthLabel", [val]));
|
||||||
|
$('#width-val').text(val + 'px');
|
||||||
|
createCursor();
|
||||||
|
};
|
||||||
|
updateBrushWidth();
|
||||||
|
|
||||||
|
$width.on('change', updateBrushWidth);
|
||||||
|
|
||||||
|
var updateBrushOpacity = function () {
|
||||||
|
var val = $opacity.val();
|
||||||
|
brush.opacity = Number(val);
|
||||||
|
canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity);
|
||||||
|
$opacityLabel.text(Cryptpad.Messages._getKey("canvas_opacityLabel", [val]));
|
||||||
|
$('#opacity-val').text((Number(val) * 100) + '%');
|
||||||
|
createCursor();
|
||||||
|
};
|
||||||
|
updateBrushOpacity();
|
||||||
|
|
||||||
|
$opacity.on('change', updateBrushOpacity);
|
||||||
|
|
||||||
|
var pickColor = function (current, cb) {
|
||||||
|
var $picker = $('<input>', {
|
||||||
|
type: 'color',
|
||||||
|
value: '#FFFFFF',
|
||||||
|
})
|
||||||
|
// TODO confirm that this is safe to remove
|
||||||
|
//.css({ visibility: 'hidden' })
|
||||||
|
.on('change', function () {
|
||||||
|
var color = this.value;
|
||||||
|
cb(color);
|
||||||
|
}).appendTo($pickers);
|
||||||
|
setTimeout(function () {
|
||||||
|
$picker.val(current);
|
||||||
|
$picker.click();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var setColor = function (c) {
|
||||||
|
c = Colors.rgb2hex(c);
|
||||||
|
brush.color = c;
|
||||||
|
canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity);
|
||||||
|
module.$color.css({
|
||||||
|
'color': c,
|
||||||
|
});
|
||||||
|
createCursor();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var palette = AppConfig.whiteboardPalette || [
|
||||||
|
'red', 'blue', 'green', 'white', 'black', 'purple',
|
||||||
|
'gray', 'beige', 'brown', 'cyan', 'darkcyan', 'gold', 'yellow', 'pink'
|
||||||
|
];
|
||||||
|
|
||||||
|
$('.palette-color').on('click', function () {
|
||||||
|
var color = $(this).css('background-color');
|
||||||
|
setColor(color);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.draw = true;
|
||||||
|
var toggleDrawMode = function () {
|
||||||
|
module.draw = !module.draw;
|
||||||
|
canvas.isDrawingMode = module.draw;
|
||||||
|
$toggle.text(module.draw ? Messages.canvas_disable : Messages.canvas_enable);
|
||||||
|
if (module.draw) { $deleteButton.hide(); }
|
||||||
|
else { $deleteButton.show(); }
|
||||||
|
};
|
||||||
|
$toggle.click(toggleDrawMode);
|
||||||
|
|
||||||
|
var deleteSelection = function () {
|
||||||
|
if (canvas.getActiveObject()) {
|
||||||
|
canvas.getActiveObject().remove();
|
||||||
|
}
|
||||||
|
if (canvas.getActiveGroup()) {
|
||||||
|
canvas.getActiveGroup()._objects.forEach(function (el) {
|
||||||
|
el.remove();
|
||||||
|
});
|
||||||
|
canvas.discardActiveGroup();
|
||||||
|
}
|
||||||
|
canvas.renderAll();
|
||||||
|
module.onLocal();
|
||||||
|
};
|
||||||
|
$deleteButton.click(deleteSelection);
|
||||||
|
$(window).on('keyup', function (e) {
|
||||||
|
if (e.which === 46) { deleteSelection (); }
|
||||||
|
});
|
||||||
|
|
||||||
|
var setEditable = function (bool) {
|
||||||
|
if (readOnly && bool) { return; }
|
||||||
|
if (bool) { $controls.css('display', 'flex'); }
|
||||||
|
else { $controls.hide(); }
|
||||||
|
|
||||||
|
canvas.isDrawingMode = bool ? module.draw : false;
|
||||||
|
if (!bool) {
|
||||||
|
canvas.deactivateAll();
|
||||||
|
canvas.renderAll();
|
||||||
|
}
|
||||||
|
canvas.forEachObject(function (object) {
|
||||||
|
object.selectable = bool;
|
||||||
|
});
|
||||||
|
$canvasContainer.find('canvas').css('border-color', bool? 'black': 'red');
|
||||||
|
};
|
||||||
|
|
||||||
|
var saveImage = module.saveImage = function () {
|
||||||
|
var defaultName = "pretty-picture.png";
|
||||||
|
Cryptpad.prompt(Messages.exportPrompt, defaultName, function (filename) {
|
||||||
|
if (!(typeof(filename) === 'string' && filename)) { return; }
|
||||||
|
$canvas[0].toBlob(function (blob) {
|
||||||
|
saveAs(blob, filename);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.FM = Cryptpad.createFileManager({});
|
||||||
|
module.upload = function (title) {
|
||||||
|
var canvas = $canvas[0];
|
||||||
|
var finish = function (thumb) {
|
||||||
|
canvas.toBlob(function (blob) {
|
||||||
|
blob.name = title;
|
||||||
|
module.FM.handleFile(blob, void 0, thumb);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Thumb.fromCanvas(canvas, function (e, blob) {
|
||||||
|
// carry on even if you can't get a thumbnail
|
||||||
|
if (e) { console.error(e); }
|
||||||
|
finish(blob);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var initializing = true;
|
||||||
|
|
||||||
|
var $bar = $('#toolbar');
|
||||||
|
|
||||||
|
var Title;
|
||||||
|
var UserList;
|
||||||
|
var Metadata;
|
||||||
|
|
||||||
|
var config = module.config = {
|
||||||
|
initialState: '{}',
|
||||||
|
websocketURL: Cryptpad.getWebsocketURL(),
|
||||||
|
validateKey: secret.keys.validateKey,
|
||||||
|
readOnly: readOnly,
|
||||||
|
channel: secret.channel,
|
||||||
|
crypto: Crypto.createEncryptor(secret.keys),
|
||||||
|
transformFunction: JsonOT.transform,
|
||||||
|
};
|
||||||
|
|
||||||
|
var addColorToPalette = function (color, i) {
|
||||||
|
if (readOnly) { return; }
|
||||||
|
var $color = $('<span>', {
|
||||||
|
'class': 'palette-color',
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
'background-color': color,
|
||||||
|
})
|
||||||
|
.click(function () {
|
||||||
|
var c = Colors.rgb2hex($color.css('background-color'));
|
||||||
|
setColor(c);
|
||||||
|
})
|
||||||
|
.on('dblclick', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
pickColor(Colors.rgb2hex($color.css('background-color')), function (c) {
|
||||||
|
$color.css({
|
||||||
|
'background-color': c,
|
||||||
|
});
|
||||||
|
palette.splice(i, 1, c);
|
||||||
|
config.onLocal();
|
||||||
|
setColor(c);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$colors.append($color);
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataCfg = {};
|
||||||
|
var updatePalette = metadataCfg.updatePalette = function (newPalette) {
|
||||||
|
palette = newPalette;
|
||||||
|
$colors.html('<div class="hidden"> </div>');
|
||||||
|
palette.forEach(addColorToPalette);
|
||||||
|
};
|
||||||
|
updatePalette(palette);
|
||||||
|
|
||||||
|
var makeColorButton = function ($container) {
|
||||||
|
var $testColor = $('<input>', { type: 'color', value: '!' });
|
||||||
|
|
||||||
|
// if colors aren't supported, bail out
|
||||||
|
if ($testColor.attr('type') !== 'color' ||
|
||||||
|
$testColor.val() === '!') {
|
||||||
|
console.log("Colors aren't supported. Aborting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $color = module.$color = $('<button>', {
|
||||||
|
id: "color-picker",
|
||||||
|
title: Messages.canvas_chooseColor,
|
||||||
|
'class': "fa fa-square rightside-button",
|
||||||
|
})
|
||||||
|
.on('click', function () {
|
||||||
|
pickColor($color.css('background-color'), function (color) {
|
||||||
|
setColor(color);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setColor('#000');
|
||||||
|
|
||||||
|
$container.append($color);
|
||||||
|
|
||||||
|
return $color;
|
||||||
|
};
|
||||||
|
|
||||||
|
config.onInit = function (info) {
|
||||||
|
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
|
||||||
|
|
||||||
|
Title = Cryptpad.createTitle({}, config.onLocal, Cryptpad);
|
||||||
|
|
||||||
|
Metadata = Cryptpad.createMetadata(UserList, Title, metadataCfg, Cryptpad);
|
||||||
|
|
||||||
|
var configTb = {
|
||||||
|
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit', 'upgrade'],
|
||||||
|
userList: UserList.getToolbarConfig(),
|
||||||
|
share: {
|
||||||
|
secret: secret,
|
||||||
|
channel: info.channel
|
||||||
|
},
|
||||||
|
title: Title.getTitleConfig(),
|
||||||
|
common: Cryptpad,
|
||||||
|
readOnly: readOnly,
|
||||||
|
ifrw: window,
|
||||||
|
realtime: info.realtime,
|
||||||
|
network: info.network,
|
||||||
|
$container: $bar,
|
||||||
|
$contentContainer: $('#canvas-area')
|
||||||
|
};
|
||||||
|
|
||||||
|
toolbar = module.toolbar = Toolbar.create(configTb);
|
||||||
|
|
||||||
|
Title.setToolbar(toolbar);
|
||||||
|
|
||||||
|
var $rightside = toolbar.$rightside;
|
||||||
|
|
||||||
|
/* save as template */
|
||||||
|
if (!Cryptpad.isTemplate(window.location.href)) {
|
||||||
|
var templateObj = {
|
||||||
|
rt: info.realtime,
|
||||||
|
Crypt: Cryptget,
|
||||||
|
getTitle: function () { return document.title; }
|
||||||
|
};
|
||||||
|
var $templateButton = Cryptpad.createButton('template', true, templateObj);
|
||||||
|
$rightside.append($templateButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
var $export = Cryptpad.createButton('export', true, {}, saveImage);
|
||||||
|
$rightside.append($export);
|
||||||
|
|
||||||
|
Cryptpad.createButton('savetodrive', true, {}, function () {})
|
||||||
|
.click(function () {
|
||||||
|
Cryptpad.prompt(Messages.exportPrompt, document.title + '.png',
|
||||||
|
function (name) {
|
||||||
|
if (name === null || !name.trim()) { return; }
|
||||||
|
module.upload(name);
|
||||||
|
});
|
||||||
|
}).appendTo($rightside);
|
||||||
|
|
||||||
|
var $forget = Cryptpad.createButton('forget', true, {}, function (err) {
|
||||||
|
if (err) { return; }
|
||||||
|
setEditable(false);
|
||||||
|
toolbar.failed();
|
||||||
|
});
|
||||||
|
$rightside.append($forget);
|
||||||
|
|
||||||
|
var editHash;
|
||||||
|
|
||||||
|
if (!readOnly) {
|
||||||
|
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
||||||
|
makeColorButton($rightside);
|
||||||
|
}
|
||||||
|
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// used for debugging, feel free to remove
|
||||||
|
var Catch = function (f) {
|
||||||
|
return function () {
|
||||||
|
try {
|
||||||
|
f();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var onRemote = config.onRemote = Catch(function () {
|
||||||
|
if (initializing) { return; }
|
||||||
|
var userDoc = module.realtime.getUserDoc();
|
||||||
|
|
||||||
|
Metadata.update(userDoc);
|
||||||
|
var json = JSON.parse(userDoc);
|
||||||
|
var remoteDoc = json.content;
|
||||||
|
|
||||||
|
// TODO update palette if it has changed
|
||||||
|
|
||||||
|
canvas.loadFromJSON(remoteDoc);
|
||||||
|
canvas.renderAll();
|
||||||
|
|
||||||
|
var content = canvas.toDatalessJSON();
|
||||||
|
if (content !== remoteDoc) { Cryptpad.notify(); }
|
||||||
|
if (readOnly) { setEditable(false); }
|
||||||
|
});
|
||||||
|
setEditable(false);
|
||||||
|
|
||||||
|
var stringifyInner = function (textValue) {
|
||||||
|
var obj = {
|
||||||
|
content: textValue,
|
||||||
|
metadata: {
|
||||||
|
users: UserList.userData,
|
||||||
|
palette: palette,
|
||||||
|
defaultTitle: Title.defaultTitle,
|
||||||
|
type: 'whiteboard',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!initializing) {
|
||||||
|
obj.metadata.title = Title.title;
|
||||||
|
}
|
||||||
|
// stringify the json and send it into chainpad
|
||||||
|
return JSONSortify(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var onLocal = module.onLocal = config.onLocal = Catch(function () {
|
||||||
|
if (initializing) { return; }
|
||||||
|
if (readOnly) { return; }
|
||||||
|
|
||||||
|
var content = stringifyInner(canvas.toDatalessJSON());
|
||||||
|
|
||||||
|
module.patchText(content);
|
||||||
|
});
|
||||||
|
|
||||||
|
config.onReady = function (info) {
|
||||||
|
var realtime = module.realtime = info.realtime;
|
||||||
|
module.patchText = TextPatcher.create({
|
||||||
|
realtime: realtime
|
||||||
|
});
|
||||||
|
|
||||||
|
var isNew = false;
|
||||||
|
var userDoc = module.realtime.getUserDoc();
|
||||||
|
if (userDoc === "" || userDoc === "{}") { isNew = true; }
|
||||||
|
else {
|
||||||
|
var hjson = JSON.parse(userDoc);
|
||||||
|
if (typeof(hjson) !== 'object' || Array.isArray(hjson) ||
|
||||||
|
(typeof(hjson.type) !== 'undefined' && hjson.type !== 'whiteboard')) {
|
||||||
|
Cryptpad.errorLoadingScreen(Messages.typeError);
|
||||||
|
throw new Error(Messages.typeError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cryptpad.removeLoadingScreen();
|
||||||
|
setEditable(true);
|
||||||
|
initializing = false;
|
||||||
|
onRemote();
|
||||||
|
|
||||||
|
/* TODO: restore palette from metadata.palette */
|
||||||
|
|
||||||
|
if (readOnly) { return; }
|
||||||
|
UserList.getLastName(toolbar.$userNameButton, isNew);
|
||||||
|
};
|
||||||
|
|
||||||
|
config.onAbort = function () {
|
||||||
|
setEditable(false);
|
||||||
|
toolbar.failed();
|
||||||
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO onConnectionStateChange
|
||||||
|
config.onConnectionChange = function (info) {
|
||||||
|
setEditable(info.state);
|
||||||
|
toolbar.failed();
|
||||||
|
if (info.state) {
|
||||||
|
initializing = true;
|
||||||
|
toolbar.reconnecting(info.myId);
|
||||||
|
Cryptpad.findOKButton().click();
|
||||||
|
} else {
|
||||||
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.rt = Realtime.start(config);
|
||||||
|
|
||||||
|
canvas.on('mouse:up', onLocal);
|
||||||
|
|
||||||
|
$('#clear').on('click', function () {
|
||||||
|
canvas.clear();
|
||||||
|
onLocal();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#save').on('click', function () {
|
||||||
|
saveImage();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Cryptpad.ready(function () {
|
||||||
|
andThen();
|
||||||
|
Cryptpad.reportAppUsage();
|
||||||
|
});
|
||||||
|
Cryptpad.onError(function (info) {
|
||||||
|
if (info) {
|
||||||
|
onConnectError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,11 +1,13 @@
|
|||||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||||
|
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||||
|
|
||||||
.toolbar_main();
|
.toolbar_main();
|
||||||
.alertify_main();
|
.alertify_main();
|
||||||
|
|
||||||
// body
|
// body
|
||||||
&.cp-app-pad {
|
&.cp-app-pad {
|
||||||
|
.tokenfield_main();
|
||||||
#cke_1_top {
|
#cke_1_top {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
@ -38,6 +40,6 @@
|
|||||||
display:none !important;
|
display:none !important;
|
||||||
}
|
}
|
||||||
&.cp-app-pad .cp-toolbar-userlist-drawer {
|
&.cp-app-pad .cp-toolbar-userlist-drawer {
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<html class="cp-app-noscroll">
|
<html class="cp-app-noscroll">
|
||||||
<head>
|
<head>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
<script async data-bootload="/pad/inner.js" data-main="/common/sframe-boot.js?ver=1.4" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
<script async data-bootload="/pad/inner.js" data-main="/common/sframe-boot.js?ver=1.5" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="cp-app-pad">
|
<body class="cp-app-pad">
|
||||||
<textarea style="display:none" id="editor1" name="editor1"></textarea>
|
<textarea style="display:none" id="editor1" name="editor1"></textarea>
|
||||||
|
|||||||
125
www/pad/inner.js
125
www/pad/inner.js
@ -26,7 +26,9 @@ define([
|
|||||||
'/customize/messages.js',
|
'/customize/messages.js',
|
||||||
'/pad/links.js',
|
'/pad/links.js',
|
||||||
'/bower_components/nthen/index.js',
|
'/bower_components/nthen/index.js',
|
||||||
|
'/common/media-tag.js',
|
||||||
'/api/config',
|
'/api/config',
|
||||||
|
'/common/cryptpad-common.js',
|
||||||
|
|
||||||
'/bower_components/diff-dom/diffDOM.js',
|
'/bower_components/diff-dom/diffDOM.js',
|
||||||
|
|
||||||
@ -42,7 +44,9 @@ define([
|
|||||||
Messages,
|
Messages,
|
||||||
Links,
|
Links,
|
||||||
nThen,
|
nThen,
|
||||||
ApiConfig)
|
MediaTag,
|
||||||
|
ApiConfig,
|
||||||
|
Cryptpad)
|
||||||
{
|
{
|
||||||
var DiffDom = window.diffDOM;
|
var DiffDom = window.diffDOM;
|
||||||
|
|
||||||
@ -78,9 +82,18 @@ define([
|
|||||||
el.getAttribute('class').split(' ').indexOf('non-realtime') !== -1);
|
el.getAttribute('class').split(' ').indexOf('non-realtime') !== -1);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* catch `type="_moz"` before it goes over the wire */
|
var hjsonFilters = function (hj) {
|
||||||
var brFilter = function (hj) {
|
/* catch `type="_moz"` before it goes over the wire */
|
||||||
if (hj[1].type === '_moz') { hj[1].type = undefined; }
|
var brFilter = function (hj) {
|
||||||
|
if (hj[1].type === '_moz') { hj[1].type = undefined; }
|
||||||
|
return hj;
|
||||||
|
};
|
||||||
|
var mediatagContentFilter = function (hj) {
|
||||||
|
if (hj[0] === 'MEDIA-TAG') { hj[2] = []; }
|
||||||
|
return hj;
|
||||||
|
};
|
||||||
|
brFilter(hj);
|
||||||
|
mediatagContentFilter(hj);
|
||||||
return hj;
|
return hj;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,11 +103,11 @@ define([
|
|||||||
|
|
||||||
var forbiddenTags = [
|
var forbiddenTags = [
|
||||||
'SCRIPT',
|
'SCRIPT',
|
||||||
'IFRAME',
|
//'IFRAME',
|
||||||
'OBJECT',
|
'OBJECT',
|
||||||
'APPLET',
|
'APPLET',
|
||||||
'VIDEO',
|
//'VIDEO',
|
||||||
'AUDIO'
|
//'AUDIO'
|
||||||
];
|
];
|
||||||
|
|
||||||
var getHTML = function (inner) {
|
var getHTML = function (inner) {
|
||||||
@ -308,11 +321,38 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!framework.isReadOnly()) {
|
if (!framework.isReadOnly()) {
|
||||||
|
console.log('\n\n\n\n\nREGISTER\n\n\n\n\n');
|
||||||
framework.onEditableChange(function () {
|
framework.onEditableChange(function () {
|
||||||
|
console.log("Editable change");
|
||||||
var locked = framework.isLocked();
|
var locked = framework.isLocked();
|
||||||
$(inner).css({ 'background-color': ((locked) ? '#aaa' : '') });
|
$(inner).css({ 'background-color': ((locked) ? '#aaa' : '') });
|
||||||
inner.setAttribute('contenteditable', !locked);
|
inner.setAttribute('contenteditable', !locked);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var fileDialogCfg = {
|
||||||
|
onSelect: function (data) {
|
||||||
|
if (data.type === 'file') {
|
||||||
|
var mt = '<media-tag contenteditable="false" src="' + data.src + '" data-crypto-key="cryptpad:' + data.key + '" tabindex="1"></media-tag>';
|
||||||
|
editor.insertElement(window.CKEDITOR.dom.element.createFromHtml(mt));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
framework._.sfCommon.initFilePicker(fileDialogCfg);
|
||||||
|
window.APP.$mediaTagButton = $('<button>', {
|
||||||
|
title: Messages.filePickerButton,
|
||||||
|
'class': 'cp-toolbar-rightside-button fa fa-picture-o',
|
||||||
|
style: 'font-size: 17px'
|
||||||
|
}).click(function () {
|
||||||
|
var pickerCfg = {
|
||||||
|
types: ['file'],
|
||||||
|
where: ['root']
|
||||||
|
};
|
||||||
|
framework._.sfCommon.openFilePicker(pickerCfg);
|
||||||
|
}).appendTo(framework._.toolbar.$rightside);
|
||||||
|
|
||||||
|
var $tags = framework._.sfCommon.createButton('hashtag', true);
|
||||||
|
framework._.toolbar.$rightside.append($tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
framework.setTitleRecommender(function () {
|
framework.setTitleRecommender(function () {
|
||||||
@ -328,15 +368,65 @@ define([
|
|||||||
|
|
||||||
var DD = new DiffDom(mkDiffOptions(cursor, framework.isReadOnly()));
|
var DD = new DiffDom(mkDiffOptions(cursor, framework.isReadOnly()));
|
||||||
|
|
||||||
|
var mediaMap = {};
|
||||||
|
var restoreMediaTags = function (tempDom) {
|
||||||
|
var pattern = /(<media-tag contenteditable="false" data-crypto-key="([^"]*)" src="([^"]*)" tabindex="1">)<\/media-tag>/i;
|
||||||
|
var tags = tempDom.querySelectorAll('media-tag:empty');
|
||||||
|
Array.prototype.slice.call(tags).forEach(function (tag) {
|
||||||
|
if (pattern.length !== 4) { return; }
|
||||||
|
var src = pattern[3];
|
||||||
|
if (mediaMap[src]) {
|
||||||
|
mediaMap[src].forEach(function (n) {
|
||||||
|
tag.appendChild(n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var displayMediaTags = function (dom) {
|
||||||
|
setTimeout(function () { // Just in case
|
||||||
|
var tags = dom.querySelectorAll('media-tag:empty');
|
||||||
|
console.log(Array.prototype.slice.call(tags));
|
||||||
|
Array.prototype.slice.call(tags).forEach(function (el) {
|
||||||
|
MediaTag(el);
|
||||||
|
$(el).on('keydown', function (e) {
|
||||||
|
if ([8,46].indexOf(e.which) !== -1) {
|
||||||
|
$(el).remove();
|
||||||
|
framework.localChange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var observer = new MutationObserver(function(mutations) {
|
||||||
|
mutations.forEach(function(mutation) {
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
var list_values = [].slice.call(el.children);
|
||||||
|
mediaMap[el.getAttribute('src')] = list_values;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
observer.observe(el, {
|
||||||
|
attributes: false,
|
||||||
|
childList: true,
|
||||||
|
characterData: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// apply patches, and try not to lose the cursor in the process!
|
// apply patches, and try not to lose the cursor in the process!
|
||||||
framework.onContentUpdate(function (hjson) {
|
framework.onContentUpdate(function (hjson) {
|
||||||
|
if (!Array.isArray(hjson)) {
|
||||||
|
var errorText = Messages.typeError;
|
||||||
|
Cryptpad.errorLoadingScreen(errorText);
|
||||||
|
throw new Error(errorText);
|
||||||
|
}
|
||||||
var userDocStateDom = hjsonToDom(hjson);
|
var userDocStateDom = hjsonToDom(hjson);
|
||||||
|
|
||||||
userDocStateDom.setAttribute("contenteditable",
|
userDocStateDom.setAttribute("contenteditable",
|
||||||
inner.getAttribute('contenteditable'));
|
inner.getAttribute('contenteditable'));
|
||||||
|
|
||||||
|
restoreMediaTags(userDocStateDom);
|
||||||
var patch = (DD).diff(inner, userDocStateDom);
|
var patch = (DD).diff(inner, userDocStateDom);
|
||||||
(DD).apply(inner, patch);
|
(DD).apply(inner, patch);
|
||||||
|
displayMediaTags(inner);
|
||||||
if (framework.isReadOnly()) {
|
if (framework.isReadOnly()) {
|
||||||
var $links = $(inner).find('a');
|
var $links = $(inner).find('a');
|
||||||
// off so that we don't end up with multiple identical handlers
|
// off so that we don't end up with multiple identical handlers
|
||||||
@ -345,7 +435,7 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
framework.setContentGetter(function () {
|
framework.setContentGetter(function () {
|
||||||
return Hyperjson.fromDOM(inner, isNotMagicLine, brFilter);
|
return Hyperjson.fromDOM(inner, isNotMagicLine, hjsonFilters);
|
||||||
});
|
});
|
||||||
|
|
||||||
$bar.find('#cke_1_toolbar_collapser').hide();
|
$bar.find('#cke_1_toolbar_collapser').hide();
|
||||||
@ -365,11 +455,22 @@ define([
|
|||||||
|
|
||||||
editor.focus();
|
editor.focus();
|
||||||
if (newPad) {
|
if (newPad) {
|
||||||
documentBody.innerHTML = Messages.initialState;
|
|
||||||
cursor.setToEnd();
|
cursor.setToEnd();
|
||||||
} else if (framework.isReadOnly()) {
|
} else if (framework.isReadOnly()) {
|
||||||
cursor.setToStart();
|
cursor.setToStart();
|
||||||
}
|
}
|
||||||
|
var fmConfig = {
|
||||||
|
ckeditor: editor,
|
||||||
|
body: $('body'),
|
||||||
|
onUploaded: function (ev, data) {
|
||||||
|
var parsed = Cryptpad.parsePadUrl(data.url);
|
||||||
|
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
|
||||||
|
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||||
|
var mt = '<media-tag contenteditable="false" src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '" tabindex="1"></media-tag>';
|
||||||
|
editor.insertElement(window.CKEDITOR.dom.element.createFromHtml(mt));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.APP.FM = framework._.sfCommon.createFileManager(fmConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
framework.onDefaultContentNeeded(function () {
|
framework.onDefaultContentNeeded(function () {
|
||||||
@ -457,12 +558,18 @@ define([
|
|||||||
}
|
}
|
||||||
// Used in ckeditor-config.js
|
// Used in ckeditor-config.js
|
||||||
Ckeditor.CRYPTPAD_URLARGS = ApiConfig.requireConf.urlArgs;
|
Ckeditor.CRYPTPAD_URLARGS = ApiConfig.requireConf.urlArgs;
|
||||||
|
Ckeditor.plugins.addExternal('mediatag','/pad/', 'mediatag-plugin.js');
|
||||||
module.ckeditor = editor = Ckeditor.replace('editor1', {
|
module.ckeditor = editor = Ckeditor.replace('editor1', {
|
||||||
customConfig: '/customize/ckeditor-config.js',
|
customConfig: '/customize/ckeditor-config.js',
|
||||||
});
|
});
|
||||||
editor.on('instanceReady', waitFor());
|
editor.on('instanceReady', waitFor());
|
||||||
}).nThen(function (waitFor) {
|
}).nThen(function (waitFor) {
|
||||||
Framework.create({}, waitFor(function (fw) { window.APP.framework = framework = fw; }));
|
Framework.create({}, waitFor(function (fw) { window.APP.framework = framework = fw; }));
|
||||||
|
editor.plugins.mediatag.translations = {
|
||||||
|
title: Messages.pad_mediatagTitle,
|
||||||
|
width: Messages.pad_mediatagWidth,
|
||||||
|
height: Messages.pad_mediatagHeight
|
||||||
|
};
|
||||||
Links.addSupportForOpeningLinksInNewTab(Ckeditor)({editor: editor});
|
Links.addSupportForOpeningLinksInNewTab(Ckeditor)({editor: editor});
|
||||||
}).nThen(function (/*waitFor*/) {
|
}).nThen(function (/*waitFor*/) {
|
||||||
andThen2(editor, Ckeditor, framework);
|
andThen2(editor, Ckeditor, framework);
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
define(['/common/cryptpad-common.js'], function (Cryptpad) {
|
define(['/customize/messages.js'], function (Messages) {
|
||||||
// Adds a context menu entry to open the selected link in a new tab.
|
// Adds a context menu entry to open the selected link in a new tab.
|
||||||
// See https://github.com/xwiki-contrib/application-ckeditor/commit/755d193497bf23ed874d874b4ae92fbee887fc10
|
// See https://github.com/xwiki-contrib/application-ckeditor/commit/755d193497bf23ed874d874b4ae92fbee887fc10
|
||||||
var Messages = Cryptpad.Messages;
|
|
||||||
return {
|
return {
|
||||||
addSupportForOpeningLinksInNewTab : function (Ckeditor) {
|
addSupportForOpeningLinksInNewTab : function (Ckeditor) {
|
||||||
// Returns the DOM element of the active (currently focused) link. It has also support for linked image widgets.
|
// Returns the DOM element of the active (currently focused) link. It has also support for linked image widgets.
|
||||||
|
|||||||
60
www/pad/mediatag-plugin-dialog.js
Normal file
60
www/pad/mediatag-plugin-dialog.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
CKEDITOR.dialog.add('mediatag', function (editor) {
|
||||||
|
var Messages = editor.plugins.mediatag.translations;
|
||||||
|
return {
|
||||||
|
title: Messages.title,
|
||||||
|
minWidth: 400,
|
||||||
|
minHeight: 200,
|
||||||
|
contents: [
|
||||||
|
{
|
||||||
|
id: 'tab-basic',
|
||||||
|
label: Messages.title,
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
id: 'width',
|
||||||
|
label: Messages.width,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
id: 'height',
|
||||||
|
label: Messages.height,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onShow: function () {
|
||||||
|
var el = editor.plugins.mediatag.clicked;
|
||||||
|
var rect = el.getClientRect();
|
||||||
|
var dialog = this.parts.contents.$;
|
||||||
|
var inputs = dialog.querySelectorAll('input');
|
||||||
|
var wInput = inputs[0];
|
||||||
|
var hInput = inputs[1];
|
||||||
|
wInput.value = Math.round(rect.width);
|
||||||
|
hInput.value = Math.round(rect.height);
|
||||||
|
},
|
||||||
|
onOk: function() {
|
||||||
|
var dialog = this;
|
||||||
|
var el = editor.plugins.mediatag.clicked;
|
||||||
|
var dialog = this.parts.contents.$;
|
||||||
|
var inputs = dialog.querySelectorAll('input');
|
||||||
|
var wInput = inputs[0];
|
||||||
|
var hInput = inputs[1];
|
||||||
|
|
||||||
|
window.setTimeout(function () {
|
||||||
|
if (wInput.value === "") {
|
||||||
|
el.removeAttribute('width');
|
||||||
|
el.removeStyle('width');
|
||||||
|
} else {
|
||||||
|
el.setSize('width', parseInt(wInput.value));
|
||||||
|
}
|
||||||
|
if (hInput.value === "") {
|
||||||
|
el.removeAttribute('height');
|
||||||
|
el.removeStyle('height');
|
||||||
|
} else {
|
||||||
|
el.setSize('height', parseInt(hInput.value));
|
||||||
|
}
|
||||||
|
editor.fire( 'change' );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
181
www/pad/mediatag-plugin.js
Normal file
181
www/pad/mediatag-plugin.js
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/**
|
||||||
|
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
|
||||||
|
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileOverview The Image plugin.
|
||||||
|
*/
|
||||||
|
|
||||||
|
( function() {
|
||||||
|
|
||||||
|
CKEDITOR.plugins.add( 'mediatag', {
|
||||||
|
requires: 'dialog',
|
||||||
|
//icons: 'image',
|
||||||
|
//hidpi: true,
|
||||||
|
onLoad: function () {
|
||||||
|
|
||||||
|
CKEDITOR.addCss(
|
||||||
|
'media-tag{' +
|
||||||
|
'display:inline-block;' +
|
||||||
|
'}' +
|
||||||
|
'media-tag.selected{' +
|
||||||
|
'border: 1px solid black;' +
|
||||||
|
'}' +
|
||||||
|
'media-tag iframe{' +
|
||||||
|
'border: 6px solid #eee;' +
|
||||||
|
'}' +
|
||||||
|
'media-tag img{' +
|
||||||
|
'vertical-align: top;' +
|
||||||
|
'}' +
|
||||||
|
'media-tag *{' +
|
||||||
|
'width:100%; height:100%;' +
|
||||||
|
'}');
|
||||||
|
},
|
||||||
|
init: function( editor ) {
|
||||||
|
var pluginName = 'mediatag';
|
||||||
|
|
||||||
|
// Register the dialog.
|
||||||
|
CKEDITOR.dialog.add( pluginName, this.path + 'mediatag-plugin-dialog.js' );
|
||||||
|
|
||||||
|
var allowed = 'media-tag[!data-crypto-key,!src,contenteditable,width,height]{border-style,border-width,float,height,margin,margin-bottom,margin-left,margin-right,margin-top,width}',
|
||||||
|
required = 'media-tag[data-crypto-key,src]';
|
||||||
|
|
||||||
|
// Register the command.
|
||||||
|
editor.addCommand( pluginName, new CKEDITOR.dialogCommand( pluginName, {
|
||||||
|
allowedContent: allowed,
|
||||||
|
requiredContent: required,
|
||||||
|
contentTransformations: [
|
||||||
|
[ 'media-tag{width}: sizeToStyle', 'media-tag[width]: sizeToAttribute' ],
|
||||||
|
[ 'media-tag{float}: alignmentToStyle', 'media-tag[align]: alignmentToAttribute' ]
|
||||||
|
]
|
||||||
|
} ) );
|
||||||
|
|
||||||
|
var isMediaTag = function (el) {
|
||||||
|
if (el.is('media-tag')) { return el; }
|
||||||
|
var mt = el.getParents().slice().filter(function (p) {
|
||||||
|
return p.is('media-tag');
|
||||||
|
});
|
||||||
|
if (mt.length !== 1) { return; }
|
||||||
|
return mt[0];
|
||||||
|
};
|
||||||
|
editor.on('doubleclick', function (evt) {
|
||||||
|
var element = evt.data.element;
|
||||||
|
var mt = isMediaTag(element);
|
||||||
|
if (mt && !element.data('cke-realelement')) {
|
||||||
|
editor.plugins.mediatag.clicked = mt;
|
||||||
|
evt.data.dialog = 'mediatag';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the "contextmenu" plugin is loaded, register the listeners.
|
||||||
|
if (editor.contextMenu) {
|
||||||
|
editor.contextMenu.addListener(function (element) {
|
||||||
|
if (getSelectedMediatag(editor, element)) {
|
||||||
|
return { mediatag: CKEDITOR.TRISTATE_OFF };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
afterInit: function( editor ) {
|
||||||
|
// Customize the behavior of the alignment commands. (http://dev.ckeditor.com/ticket/7430)
|
||||||
|
setupAlignCommand('left');
|
||||||
|
setupAlignCommand('right');
|
||||||
|
setupAlignCommand('center');
|
||||||
|
setupAlignCommand('block');
|
||||||
|
|
||||||
|
function setupAlignCommand (value) {
|
||||||
|
var command = editor.getCommand('justify' + value);
|
||||||
|
if (command) {
|
||||||
|
if (value === 'left' || value === 'right') {
|
||||||
|
command.on('exec', function (evt) {
|
||||||
|
var img = getSelectedMediatag(editor), align;
|
||||||
|
if (img) {
|
||||||
|
align = getMediatagAlignment(img);
|
||||||
|
if (align === value) {
|
||||||
|
img.removeStyle('float');
|
||||||
|
|
||||||
|
// Remove "align" attribute when necessary.
|
||||||
|
if (value === getMediatagAlignment(img))
|
||||||
|
img.removeAttribute( 'align' );
|
||||||
|
} else {
|
||||||
|
img.setStyle( 'float', value );
|
||||||
|
}
|
||||||
|
|
||||||
|
evt.cancel();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
command.on('refresh', function (evt) {
|
||||||
|
var img = getSelectedMediatag(editor), align;
|
||||||
|
if (img) {
|
||||||
|
align = getMediatagAlignment(img);
|
||||||
|
|
||||||
|
this.setState(
|
||||||
|
(align === value) ? CKEDITOR.TRISTATE_ON : ( value === 'right' || value === 'left' ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
|
||||||
|
|
||||||
|
evt.cancel();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
function getSelectedMediatag (editor, element) {
|
||||||
|
if (!element) {
|
||||||
|
var sel = editor.getSelection();
|
||||||
|
element = sel.getSelectedElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element && element.is('media-tag') && !element.data('cke-realelement')
|
||||||
|
&& !element.isReadOnly()) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMediatagAlignment (element) {
|
||||||
|
var align = element.getStyle('float');
|
||||||
|
|
||||||
|
if (align === 'inherit' || align === 'none') {
|
||||||
|
align = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!align) {
|
||||||
|
align = element.getAttribute('align');
|
||||||
|
}
|
||||||
|
|
||||||
|
return align;
|
||||||
|
}
|
||||||
|
} )();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether dimension inputs should be automatically filled when the image URL changes in the Image plugin dialog window.
|
||||||
|
*
|
||||||
|
* config.image_prefillDimensions = false;
|
||||||
|
*
|
||||||
|
* @since 4.5
|
||||||
|
* @cfg {Boolean} [image_prefillDimensions=true]
|
||||||
|
* @member CKEDITOR.config
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to remove links when emptying the link URL field in the Image dialog window.
|
||||||
|
*
|
||||||
|
* config.image_removeLinkByEmptyURL = false;
|
||||||
|
*
|
||||||
|
* @cfg {Boolean} [image_removeLinkByEmptyURL=true]
|
||||||
|
* @member CKEDITOR.config
|
||||||
|
*/
|
||||||
|
CKEDITOR.config.mediatag_removeLinkByEmptyURL = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Padding text to set off the image in the preview area.
|
||||||
|
*
|
||||||
|
* config.image_previewText = CKEDITOR.tools.repeat( '___ ', 100 );
|
||||||
|
*
|
||||||
|
* @cfg {String} [image_previewText='Lorem ipsum dolor...' (placeholder text)]
|
||||||
|
* @member CKEDITOR.config
|
||||||
|
*/
|
||||||
|
|
||||||
@ -4,11 +4,13 @@
|
|||||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||||
@import (once) "../../customize/src/less2/include/mediatag.less";
|
@import (once) "../../customize/src/less2/include/mediatag.less";
|
||||||
|
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||||
|
|
||||||
.mediatag_base();
|
.mediatag_base();
|
||||||
.toolbar_main();
|
.toolbar_main();
|
||||||
.fileupload_main();
|
.fileupload_main();
|
||||||
.alertify_main();
|
.alertify_main();
|
||||||
|
.tokenfield_main();
|
||||||
|
|
||||||
// body
|
// body
|
||||||
font-size: unset;
|
font-size: unset;
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<html class="cp-app-noscroll cp-app-print">
|
<html class="cp-app-noscroll cp-app-print">
|
||||||
<head>
|
<head>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
<script async data-bootload="/slide/inner.js" data-main="/common/sframe-boot.js?ver=1.4" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
<script async data-bootload="/slide/inner.js" data-main="/common/sframe-boot.js?ver=1.5" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||||
<style>
|
<style>
|
||||||
.loading-hidden { display: none; }
|
.loading-hidden { display: none; }
|
||||||
#editor1 { display: none; }
|
#editor1 { display: none; }
|
||||||
|
|||||||
@ -516,6 +516,9 @@ define([
|
|||||||
};
|
};
|
||||||
common.openFilePicker(pickerCfg);
|
common.openFilePicker(pickerCfg);
|
||||||
}).appendTo($rightside);
|
}).appendTo($rightside);
|
||||||
|
|
||||||
|
var $tags = common.createButton('hashtag', true);
|
||||||
|
$rightside.append($tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
metadataMgr.onChange(function () {
|
metadataMgr.onChange(function () {
|
||||||
@ -552,7 +555,8 @@ define([
|
|||||||
metadataMgr.updateMetadata(hjson.metadata);
|
metadataMgr.updateMetadata(hjson.metadata);
|
||||||
}
|
}
|
||||||
if (typeof (hjson) !== 'object' || Array.isArray(hjson) ||
|
if (typeof (hjson) !== 'object' || Array.isArray(hjson) ||
|
||||||
(typeof(hjson.type) !== 'undefined' && hjson.type !== 'code')) {
|
(hjson.metadata && typeof(hjson.metadata.type) !== 'undefined' &&
|
||||||
|
hjson.metadata.type !== 'slide')) {
|
||||||
var errorText = Messages.typeError;
|
var errorText = Messages.typeError;
|
||||||
Cryptpad.errorLoadingScreen(errorText);
|
Cryptpad.errorLoadingScreen(errorText);
|
||||||
throw new Error(errorText);
|
throw new Error(errorText);
|
||||||
@ -655,7 +659,7 @@ define([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Slide.update(remoteDoc);
|
Slide.update(remoteDoc);
|
||||||
if (oldDoc !== remoteDoc) { Cryptpad.notify(); }
|
if (oldDoc !== remoteDoc) { common.notify(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
config.onAbort = function () {
|
config.onAbort = function () {
|
||||||
|
|||||||
148
www/whiteboard/app-whiteboard.less
Normal file
148
www/whiteboard/app-whiteboard.less
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
@import (once) "../../customize/src/less2/include/browser.less";
|
||||||
|
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||||
|
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||||
|
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||||
|
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||||
|
|
||||||
|
.toolbar_main();
|
||||||
|
.fileupload_main();
|
||||||
|
.alertify_main();
|
||||||
|
|
||||||
|
// body
|
||||||
|
&.cp-app-whiteboard {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.middle () {
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// created in the html
|
||||||
|
#cp-app-whiteboard-canvas-area {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
// created by fabricjs. styled so defaults don't break anything
|
||||||
|
.cp-app-whiteboard-canvas-container {
|
||||||
|
margin: auto;
|
||||||
|
background: white;
|
||||||
|
& > canvas {
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cp-app-whiteboard-unselectable {
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains user tools
|
||||||
|
#cp-app-whiteboard-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
border-top: 1px solid black;
|
||||||
|
background: white;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
& > * + * {
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cp-app-whiteboard-width, #cp-app-whiteboard-opacity {
|
||||||
|
.middle;
|
||||||
|
}
|
||||||
|
#cp-app-whiteboard-clear, #cp-app-whiteboard-delete, #cp-app-whiteboard-toggledraw {
|
||||||
|
display: inline;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.cp-app-whiteboard-selected {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9001;
|
||||||
|
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cp-app-whiteboard-range-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input[type="range"] {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
cursor: default;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cp-app-whiteboard-range-group:first-of-type {
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
.cp-app-whiteboard-range-group:last-of-type {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colors */
|
||||||
|
#cp-app-whiteboard-colors {
|
||||||
|
.middle;
|
||||||
|
z-index: 100;
|
||||||
|
background: white;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
span.cp-app-whiteboard-palette-color {
|
||||||
|
height: 4vw;
|
||||||
|
width: 4vw;
|
||||||
|
display: block;
|
||||||
|
margin: 5px;
|
||||||
|
border: 1px solid black;
|
||||||
|
vertical-align: top;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: transform 0.1s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// used in the toolbar if supported
|
||||||
|
#cp-app-whiteboard-color-picker {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// input[type=color] must exist in the dom to work correctly
|
||||||
|
// styled so that they don't break layouts
|
||||||
|
|
||||||
|
#cp-app-whiteboard-pickers {
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
z-index: -5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,9 +1,38 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html class="cp">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<title>CryptPad</title>
|
||||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
<meta name="referrer" content="no-referrer" />
|
||||||
|
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
#sbox-iframe {
|
||||||
|
position:fixed;
|
||||||
|
top:0px;
|
||||||
|
left:0px;
|
||||||
|
bottom:0px;
|
||||||
|
right:0px;
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
border:none;
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
#sbox-filePicker-iframe {
|
||||||
|
position: fixed;
|
||||||
|
top:0; left:0;
|
||||||
|
bottom:0; right:0;
|
||||||
|
width:100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<iframe id="sbox-iframe">
|
||||||
|
|||||||
12
www/whiteboard/inner.html
Normal file
12
www/whiteboard/inner.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||||
|
<script async data-bootload="/whiteboard/inner.js" data-main="/common/sframe-boot.js?ver=1.5" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||||
|
<style>
|
||||||
|
.loading-hidden { display: none; }
|
||||||
|
#editor1 { display: none; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="cp-app-whiteboard">
|
||||||
|
|
||||||
551
www/whiteboard/inner.js
Normal file
551
www/whiteboard/inner.js
Normal file
@ -0,0 +1,551 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'/bower_components/chainpad-crypto/crypto.js',
|
||||||
|
'/bower_components/textpatcher/TextPatcher.js',
|
||||||
|
'/common/toolbar3.js',
|
||||||
|
'json.sortify',
|
||||||
|
'/bower_components/chainpad-json-validator/json-ot.js',
|
||||||
|
'/common/cryptpad-common.js',
|
||||||
|
'/common/cryptget.js',
|
||||||
|
'/bower_components/nthen/index.js',
|
||||||
|
'/common/sframe-common.js',
|
||||||
|
'/api/config',
|
||||||
|
'/common/common-realtime.js',
|
||||||
|
'/customize/pages.js',
|
||||||
|
|
||||||
|
'/customize/application_config.js',
|
||||||
|
'/common/common-thumbnail.js',
|
||||||
|
'/whiteboard/colors.js',
|
||||||
|
|
||||||
|
'/bower_components/secure-fabric.js/dist/fabric.min.js',
|
||||||
|
'/bower_components/file-saver/FileSaver.min.js',
|
||||||
|
|
||||||
|
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||||
|
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||||
|
'less!/customize/src/less2/main.less',
|
||||||
|
], function (
|
||||||
|
$,
|
||||||
|
Crypto,
|
||||||
|
TextPatcher,
|
||||||
|
Toolbar,
|
||||||
|
JSONSortify,
|
||||||
|
JsonOT,
|
||||||
|
Cryptpad,
|
||||||
|
Cryptget,
|
||||||
|
nThen,
|
||||||
|
SFCommon,
|
||||||
|
ApiConfig,
|
||||||
|
CommonRealtime,
|
||||||
|
Pages,
|
||||||
|
AppConfig,
|
||||||
|
Thumb,
|
||||||
|
Colors)
|
||||||
|
{
|
||||||
|
var saveAs = window.saveAs;
|
||||||
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
|
var APP = window.APP = {
|
||||||
|
Cryptpad: Cryptpad,
|
||||||
|
$: $
|
||||||
|
};
|
||||||
|
var Fabric = APP.Fabric = window.fabric;
|
||||||
|
|
||||||
|
var stringify = function (obj) {
|
||||||
|
return JSONSortify(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
var toolbar;
|
||||||
|
|
||||||
|
var onConnectError = function () {
|
||||||
|
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
||||||
|
};
|
||||||
|
|
||||||
|
var andThen = function (common) {
|
||||||
|
var config = {};
|
||||||
|
/* Initialize Fabric */
|
||||||
|
var canvas = APP.canvas = new Fabric.Canvas('cp-app-whiteboard-canvas', {
|
||||||
|
containerClass: 'cp-app-whiteboard-canvas-container'
|
||||||
|
});
|
||||||
|
var $canvas = $('canvas');
|
||||||
|
var $controls = $('#cp-app-whiteboard-controls');
|
||||||
|
var $canvasContainer = $('canvas').parents('.cp-app-whiteboard-canvas-container');
|
||||||
|
var $pickers = $('#cp-app-whiteboard-pickers');
|
||||||
|
var $colors = $('#cp-app-whiteboard-colors');
|
||||||
|
var $cursors = $('#cp-app-whiteboard-cursors');
|
||||||
|
var $deleteButton = $('#cp-app-whiteboard-delete');
|
||||||
|
var $toggle = $('#cp-app-whiteboard-toggledraw');
|
||||||
|
var $width = $('#cp-app-whiteboard-width');
|
||||||
|
var $widthLabel = $('label[for="cp-app-whiteboard-width"]');
|
||||||
|
var $opacity = $('#cp-app-whiteboard-opacity');
|
||||||
|
var $opacityLabel = $('label[for="cp-app-whiteboard-opacity"]');
|
||||||
|
|
||||||
|
|
||||||
|
// Brush
|
||||||
|
|
||||||
|
var readOnly = false;
|
||||||
|
var brush = {
|
||||||
|
color: '#000000',
|
||||||
|
opacity: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var createCursor = function () {
|
||||||
|
var w = canvas.freeDrawingBrush.width;
|
||||||
|
var c = canvas.freeDrawingBrush.color;
|
||||||
|
var size = w > 30 ? w+2 : w+32;
|
||||||
|
$cursors.html('<canvas width="'+size+'" height="'+size+'"></canvas>');
|
||||||
|
var $ccanvas = $cursors.find('canvas');
|
||||||
|
var ccanvas = $ccanvas[0];
|
||||||
|
|
||||||
|
var ctx = ccanvas.getContext('2d');
|
||||||
|
var centerX = size / 2;
|
||||||
|
var centerY = size / 2;
|
||||||
|
var radius = w/2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
|
||||||
|
ctx.fillStyle = c;
|
||||||
|
ctx.fill();
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle = brush.color;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(size/2, 0); ctx.lineTo(size/2, 10);
|
||||||
|
ctx.moveTo(size/2, size); ctx.lineTo(size/2, size-10);
|
||||||
|
ctx.moveTo(0, size/2); ctx.lineTo(10, size/2);
|
||||||
|
ctx.moveTo(size, size/2); ctx.lineTo(size-10, size/2);
|
||||||
|
ctx.strokeStyle = '#000000';
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
var img = ccanvas.toDataURL("image/png");
|
||||||
|
$controls.find('.cp-app-whiteboard-selected > img').attr('src', img);
|
||||||
|
canvas.freeDrawingCursor = 'url('+img+') '+size/2+' '+size/2+', crosshair';
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateBrushWidth = function () {
|
||||||
|
var val = $width.val();
|
||||||
|
canvas.freeDrawingBrush.width = Number(val);
|
||||||
|
$widthLabel.text(Cryptpad.Messages._getKey("canvas_widthLabel", [val]));
|
||||||
|
$('#cp-app-whiteboard-width-val').text(val + 'px');
|
||||||
|
createCursor();
|
||||||
|
};
|
||||||
|
updateBrushWidth();
|
||||||
|
$width.on('change', updateBrushWidth);
|
||||||
|
|
||||||
|
var updateBrushOpacity = function () {
|
||||||
|
var val = $opacity.val();
|
||||||
|
brush.opacity = Number(val);
|
||||||
|
canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity);
|
||||||
|
$opacityLabel.text(Cryptpad.Messages._getKey("canvas_opacityLabel", [val]));
|
||||||
|
$('#cp-app-whiteboard-opacity-val').text((Number(val) * 100) + '%');
|
||||||
|
createCursor();
|
||||||
|
};
|
||||||
|
updateBrushOpacity();
|
||||||
|
$opacity.on('change', updateBrushOpacity);
|
||||||
|
|
||||||
|
var pickColor = function (current, cb) {
|
||||||
|
var $picker = $('<input>', {
|
||||||
|
type: 'color',
|
||||||
|
value: '#FFFFFF',
|
||||||
|
})
|
||||||
|
.on('change', function () {
|
||||||
|
var color = this.value;
|
||||||
|
cb(color);
|
||||||
|
}).appendTo($pickers);
|
||||||
|
setTimeout(function () {
|
||||||
|
$picker.val(current);
|
||||||
|
$picker.click();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var setColor = function (c) {
|
||||||
|
c = Colors.rgb2hex(c);
|
||||||
|
brush.color = c;
|
||||||
|
canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity);
|
||||||
|
APP.$color.css({
|
||||||
|
'color': c,
|
||||||
|
});
|
||||||
|
createCursor();
|
||||||
|
};
|
||||||
|
|
||||||
|
var palette = AppConfig.whiteboardPalette || [
|
||||||
|
'red', 'blue', 'green', 'white', 'black', 'purple',
|
||||||
|
'gray', 'beige', 'brown', 'cyan', 'darkcyan', 'gold', 'yellow', 'pink'
|
||||||
|
];
|
||||||
|
$('.cp-app-whiteboard-palette-color').on('click', function () {
|
||||||
|
var color = $(this).css('background-color');
|
||||||
|
setColor(color);
|
||||||
|
});
|
||||||
|
|
||||||
|
APP.draw = true;
|
||||||
|
var toggleDrawMode = function () {
|
||||||
|
APP.draw = !APP.draw;
|
||||||
|
canvas.isDrawingMode = APP.draw;
|
||||||
|
$toggle.text(APP.draw ? Messages.canvas_disable : Messages.canvas_enable);
|
||||||
|
if (APP.draw) { $deleteButton.hide(); }
|
||||||
|
else { $deleteButton.show(); }
|
||||||
|
};
|
||||||
|
$toggle.click(toggleDrawMode);
|
||||||
|
|
||||||
|
var deleteSelection = function () {
|
||||||
|
if (canvas.getActiveObject()) {
|
||||||
|
canvas.getActiveObject().remove();
|
||||||
|
}
|
||||||
|
if (canvas.getActiveGroup()) {
|
||||||
|
canvas.getActiveGroup()._objects.forEach(function (el) {
|
||||||
|
el.remove();
|
||||||
|
});
|
||||||
|
canvas.discardActiveGroup();
|
||||||
|
}
|
||||||
|
canvas.renderAll();
|
||||||
|
config.onLocal();
|
||||||
|
};
|
||||||
|
$deleteButton.click(deleteSelection);
|
||||||
|
$(window).on('keyup', function (e) {
|
||||||
|
if (e.which === 46) { deleteSelection (); }
|
||||||
|
});
|
||||||
|
|
||||||
|
var setEditable = function (bool) {
|
||||||
|
if (readOnly && bool) { return; }
|
||||||
|
if (bool) { $controls.css('display', 'flex'); }
|
||||||
|
else { $controls.hide(); }
|
||||||
|
|
||||||
|
canvas.isDrawingMode = bool ? APP.draw : false;
|
||||||
|
if (!bool) {
|
||||||
|
canvas.deactivateAll();
|
||||||
|
canvas.renderAll();
|
||||||
|
}
|
||||||
|
canvas.forEachObject(function (object) {
|
||||||
|
object.selectable = bool;
|
||||||
|
});
|
||||||
|
$canvasContainer.find('canvas').css('border-color', bool? 'black': 'red');
|
||||||
|
};
|
||||||
|
|
||||||
|
var saveImage = APP.saveImage = function () {
|
||||||
|
var defaultName = "pretty-picture.png";
|
||||||
|
Cryptpad.prompt(Messages.exportPrompt, defaultName, function (filename) {
|
||||||
|
if (!(typeof(filename) === 'string' && filename)) { return; }
|
||||||
|
$canvas[0].toBlob(function (blob) {
|
||||||
|
saveAs(blob, filename);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
APP.FM = common.createFileManager({});
|
||||||
|
APP.upload = function (title) {
|
||||||
|
var canvas = $canvas[0];
|
||||||
|
var finish = function (thumb) {
|
||||||
|
canvas.toBlob(function (blob) {
|
||||||
|
blob.name = title;
|
||||||
|
APP.FM.handleFile(blob, void 0, thumb);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Thumb.fromCanvas(canvas, function (e, blob) {
|
||||||
|
// carry on even if you can't get a thumbnail
|
||||||
|
if (e) { console.error(e); }
|
||||||
|
finish(blob);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var initializing = true;
|
||||||
|
var $bar = $('#cp-toolbar');
|
||||||
|
var Title;
|
||||||
|
var cpNfInner;
|
||||||
|
var metadataMgr;
|
||||||
|
|
||||||
|
config = {
|
||||||
|
readOnly: readOnly,
|
||||||
|
transformFunction: JsonOT.validate,
|
||||||
|
// cryptpad debug logging (default is 1)
|
||||||
|
// logLevel: 0,
|
||||||
|
validateContent: function (content) {
|
||||||
|
try {
|
||||||
|
JSON.parse(content);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Failed to parse, rejecting patch");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var addColorToPalette = function (color, i) {
|
||||||
|
if (readOnly) { return; }
|
||||||
|
var $color = $('<span>', {
|
||||||
|
'class': 'cp-app-whiteboard-palette-color',
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
'background-color': color,
|
||||||
|
})
|
||||||
|
.click(function () {
|
||||||
|
var c = Colors.rgb2hex($color.css('background-color'));
|
||||||
|
setColor(c);
|
||||||
|
})
|
||||||
|
.on('dblclick', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
pickColor(Colors.rgb2hex($color.css('background-color')), function (c) {
|
||||||
|
$color.css({
|
||||||
|
'background-color': c,
|
||||||
|
});
|
||||||
|
palette.splice(i, 1, c);
|
||||||
|
APP.updateLocalPalette(palette);
|
||||||
|
setColor(c);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$colors.append($color);
|
||||||
|
};
|
||||||
|
|
||||||
|
var first = true;
|
||||||
|
var updatePalette = function (newPalette) {
|
||||||
|
if (first || stringify(palette) !== stringify(newPalette)) {
|
||||||
|
palette = newPalette;
|
||||||
|
$colors.html('<div class="hidden"> </div>');
|
||||||
|
palette.forEach(addColorToPalette);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var updateLocalPalette = APP.updateLocalPalette = function (newPalette) {
|
||||||
|
updatePalette(newPalette);
|
||||||
|
var metadata = JSON.parse(JSON.stringify(metadataMgr.getMetadata()));
|
||||||
|
metadata.palette = newPalette;
|
||||||
|
metadataMgr.updateMetadata(metadata);
|
||||||
|
config.onLocal();
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeColorButton = function ($container) {
|
||||||
|
var $testColor = $('<input>', { type: 'color', value: '!' });
|
||||||
|
|
||||||
|
// if colors aren't supported, bail out
|
||||||
|
if ($testColor.attr('type') !== 'color' ||
|
||||||
|
$testColor.val() === '!') {
|
||||||
|
console.log("Colors aren't supported. Aborting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $color = APP.$color = $('<button>', {
|
||||||
|
id: "cp-app-whiteboard-color-picker",
|
||||||
|
title: Messages.canvas_chooseColor,
|
||||||
|
'class': "fa fa-square cp-toolbar-rightside-button",
|
||||||
|
})
|
||||||
|
.on('click', function () {
|
||||||
|
pickColor($color.css('background-color'), function (color) {
|
||||||
|
setColor(color);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setColor('#000');
|
||||||
|
|
||||||
|
$container.append($color);
|
||||||
|
|
||||||
|
return $color;
|
||||||
|
};
|
||||||
|
|
||||||
|
var stringifyInner = function (textValue) {
|
||||||
|
var obj = {
|
||||||
|
content: textValue,
|
||||||
|
metadata: metadataMgr.getMetadataLazy()
|
||||||
|
};
|
||||||
|
// stringify the json and send it into chainpad
|
||||||
|
return stringify(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
var onLocal = config.onLocal = function () {
|
||||||
|
if (initializing) { return; }
|
||||||
|
if (readOnly) { return; }
|
||||||
|
|
||||||
|
var content = stringifyInner(canvas.toDatalessJSON());
|
||||||
|
|
||||||
|
APP.patchText(content);
|
||||||
|
};
|
||||||
|
|
||||||
|
config.onInit = function (info) {
|
||||||
|
updateLocalPalette(palette);
|
||||||
|
readOnly = metadataMgr.getPrivateData().readOnly;
|
||||||
|
|
||||||
|
Title = common.createTitle({}, config.onLocal);
|
||||||
|
|
||||||
|
var configTb = {
|
||||||
|
displayed: ['title', 'useradmin', 'spinner', 'share', 'userlist', 'newpad', 'limit'],
|
||||||
|
title: Title.getTitleConfig(),
|
||||||
|
metadataMgr: metadataMgr,
|
||||||
|
readOnly: readOnly,
|
||||||
|
realtime: info.realtime,
|
||||||
|
common: Cryptpad,
|
||||||
|
sfCommon: common,
|
||||||
|
$container: $bar,
|
||||||
|
$contentContainer: $('#cp-app-whiteboard-canvas-area')
|
||||||
|
};
|
||||||
|
toolbar = APP.toolbar = Toolbar.create(configTb);
|
||||||
|
Title.setToolbar(toolbar);
|
||||||
|
|
||||||
|
var $rightside = toolbar.$rightside;
|
||||||
|
|
||||||
|
/* save as template */
|
||||||
|
if (!metadataMgr.getPrivateData().isTemplate) {
|
||||||
|
var templateObj = {
|
||||||
|
rt: info.realtime,
|
||||||
|
getTitle: function () { return metadataMgr.getMetadata().title; }
|
||||||
|
};
|
||||||
|
var $templateButton = common.createButton('template', true, templateObj);
|
||||||
|
$rightside.append($templateButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add an export button */
|
||||||
|
var $export = common.createButton('export', true, {}, saveImage);
|
||||||
|
$rightside.append($export);
|
||||||
|
|
||||||
|
common.createButton('savetodrive', true, {}, function () {})
|
||||||
|
.click(function () {
|
||||||
|
Cryptpad.prompt(Messages.exportPrompt, document.title + '.png',
|
||||||
|
function (name) {
|
||||||
|
if (name === null || !name.trim()) { return; }
|
||||||
|
APP.upload(name);
|
||||||
|
});
|
||||||
|
}).appendTo($rightside);
|
||||||
|
|
||||||
|
var $forget = common.createButton('forget', true, {}, function (err) {
|
||||||
|
if (err) { return; }
|
||||||
|
setEditable(false);
|
||||||
|
});
|
||||||
|
$rightside.append($forget);
|
||||||
|
|
||||||
|
if (!readOnly) {
|
||||||
|
makeColorButton($rightside);
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataMgr.onChange(function () {
|
||||||
|
var md = metadataMgr.getMetadata();
|
||||||
|
if (md.palette) {
|
||||||
|
updateLocalPalette(md.palette);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
config.onReady = function (info) {
|
||||||
|
if (APP.realtime !== info.realtime) {
|
||||||
|
var realtime = APP.realtime = info.realtime;
|
||||||
|
APP.patchText = TextPatcher.create({
|
||||||
|
realtime: realtime,
|
||||||
|
//logging: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var userDoc = APP.realtime.getUserDoc();
|
||||||
|
var isNew = false;
|
||||||
|
if (userDoc === "" || userDoc === "{}") { isNew = true; }
|
||||||
|
|
||||||
|
if (userDoc !== "") {
|
||||||
|
var hjson = JSON.parse(userDoc);
|
||||||
|
|
||||||
|
if (hjson && hjson.metadata) {
|
||||||
|
metadataMgr.updateMetadata(hjson.metadata);
|
||||||
|
}
|
||||||
|
if (typeof (hjson) !== 'object' || Array.isArray(hjson) ||
|
||||||
|
(hjson.metadata && typeof(hjson.metadata.type) !== 'undefined' &&
|
||||||
|
hjson.metadata.type !== 'whiteboard')) {
|
||||||
|
var errorText = Messages.typeError;
|
||||||
|
Cryptpad.errorLoadingScreen(errorText);
|
||||||
|
throw new Error(errorText);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Title.updateTitle(Cryptpad.initialName || Title.defaultTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cryptpad.removeLoadingScreen();
|
||||||
|
setEditable(!readOnly);
|
||||||
|
initializing = false;
|
||||||
|
config.onLocal();
|
||||||
|
|
||||||
|
if (readOnly) { return; }
|
||||||
|
if (isNew) {
|
||||||
|
common.openTemplatePicker();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
config.onRemote = function () {
|
||||||
|
if (initializing) { return; }
|
||||||
|
var userDoc = APP.realtime.getUserDoc();
|
||||||
|
|
||||||
|
var json = JSON.parse(userDoc);
|
||||||
|
var remoteDoc = json.content;
|
||||||
|
|
||||||
|
if (json.metadata) {
|
||||||
|
metadataMgr.updateMetadata(json.metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO update palette if it has changed
|
||||||
|
|
||||||
|
canvas.loadFromJSON(remoteDoc);
|
||||||
|
canvas.renderAll();
|
||||||
|
|
||||||
|
var content = canvas.toDatalessJSON();
|
||||||
|
if (content !== remoteDoc) { common.notify(); }
|
||||||
|
if (readOnly) { setEditable(false); }
|
||||||
|
};
|
||||||
|
|
||||||
|
config.onAbort = function () {
|
||||||
|
// inform of network disconnect
|
||||||
|
setEditable(false);
|
||||||
|
toolbar.failed();
|
||||||
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
config.onConnectionChange = function (info) {
|
||||||
|
setEditable(info.state);
|
||||||
|
if (info.state) {
|
||||||
|
initializing = true;
|
||||||
|
Cryptpad.findOKButton().click();
|
||||||
|
} else {
|
||||||
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
config.onError = onConnectError;
|
||||||
|
|
||||||
|
cpNfInner = common.startRealtime(config);
|
||||||
|
metadataMgr = cpNfInner.metadataMgr;
|
||||||
|
|
||||||
|
cpNfInner.onInfiniteSpinner(function () {
|
||||||
|
setEditable(false);
|
||||||
|
Cryptpad.confirm(Messages.realtime_unrecoverableError, function (yes) {
|
||||||
|
if (!yes) { return; }
|
||||||
|
common.gotoURL();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.on('mouse:up', onLocal);
|
||||||
|
|
||||||
|
$('#clear').on('click', function () {
|
||||||
|
canvas.clear();
|
||||||
|
onLocal();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#save').on('click', function () {
|
||||||
|
saveImage();
|
||||||
|
});
|
||||||
|
|
||||||
|
Cryptpad.onLogout(function () { setEditable(false); });
|
||||||
|
};
|
||||||
|
|
||||||
|
var main = function () {
|
||||||
|
var common;
|
||||||
|
|
||||||
|
nThen(function (waitFor) {
|
||||||
|
$(waitFor(function () {
|
||||||
|
Cryptpad.addLoadingScreen();
|
||||||
|
var $div = $('<div>').append(Pages['/whiteboard/']());
|
||||||
|
$('body').append($div.html());
|
||||||
|
}));
|
||||||
|
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
|
||||||
|
}).nThen(function (/*waitFor*/) {
|
||||||
|
Cryptpad.onError(function (info) {
|
||||||
|
if (info && info.type === "store") {
|
||||||
|
onConnectError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
andThen(common);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
main();
|
||||||
|
});
|
||||||
@ -1,511 +1,41 @@
|
|||||||
|
// Load #1, load as little as possible because we are in a race to get the loading screen up.
|
||||||
define([
|
define([
|
||||||
'jquery',
|
'/bower_components/nthen/index.js',
|
||||||
'/api/config',
|
'/api/config',
|
||||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
'jquery',
|
||||||
'/bower_components/chainpad-crypto/crypto.js',
|
'/common/requireconfig.js',
|
||||||
'/common/toolbar2.js',
|
'/common/sframe-common-outer.js'
|
||||||
'/bower_components/textpatcher/TextPatcher.amd.js',
|
], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) {
|
||||||
'json.sortify',
|
var requireConfig = RequireConfig();
|
||||||
'/bower_components/chainpad-json-validator/json-ot.js',
|
|
||||||
'/common/cryptpad-common.js',
|
|
||||||
'/common/cryptget.js',
|
|
||||||
'/whiteboard/colors.js',
|
|
||||||
'/customize/application_config.js',
|
|
||||||
'/common/common-thumbnail.js',
|
|
||||||
'/bower_components/secure-fabric.js/dist/fabric.min.js',
|
|
||||||
'/bower_components/file-saver/FileSaver.min.js',
|
|
||||||
|
|
||||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
// Loaded in load #2
|
||||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
nThen(function (waitFor) {
|
||||||
'less!/customize/src/less/cryptpad.less',
|
$(waitFor());
|
||||||
'less!/whiteboard/whiteboard.less',
|
}).nThen(function (waitFor) {
|
||||||
'less!/customize/src/less/toolbar.less',
|
var req = {
|
||||||
], function ($, Config, Realtime, Crypto, Toolbar, TextPatcher, JSONSortify, JsonOT, Cryptpad, Cryptget, Colors, AppConfig, Thumb) {
|
cfg: requireConfig,
|
||||||
var saveAs = window.saveAs;
|
req: [ '/common/loading.js' ],
|
||||||
var Messages = Cryptpad.Messages;
|
pfx: window.location.origin
|
||||||
|
|
||||||
var module = window.APP = { $:$ };
|
|
||||||
var Fabric = module.Fabric = window.fabric;
|
|
||||||
|
|
||||||
$(function () {
|
|
||||||
Cryptpad.addLoadingScreen();
|
|
||||||
var onConnectError = function () {
|
|
||||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
|
||||||
};
|
|
||||||
var toolbar;
|
|
||||||
|
|
||||||
var secret = Cryptpad.getSecrets();
|
|
||||||
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
|
||||||
if (!secret.keys) {
|
|
||||||
secret.keys = secret.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
var andThen = function () {
|
|
||||||
/* Initialize Fabric */
|
|
||||||
var canvas = module.canvas = new Fabric.Canvas('canvas');
|
|
||||||
var $canvas = $('canvas');
|
|
||||||
var $controls = $('#controls');
|
|
||||||
var $canvasContainer = $('canvas').parents('.canvas-container');
|
|
||||||
var $pickers = $('#pickers');
|
|
||||||
var $colors = $('#colors');
|
|
||||||
var $cursors = $('#cursors');
|
|
||||||
var $deleteButton = $('#delete');
|
|
||||||
|
|
||||||
var brush = {
|
|
||||||
color: '#000000',
|
|
||||||
opacity: 1
|
|
||||||
};
|
};
|
||||||
|
window.rc = requireConfig;
|
||||||
|
window.apiconf = ApiConfig;
|
||||||
|
$('#sbox-iframe').attr('src',
|
||||||
|
ApiConfig.httpSafeOrigin + '/whiteboard/inner.html?' + requireConfig.urlArgs +
|
||||||
|
'#' + encodeURIComponent(JSON.stringify(req)));
|
||||||
|
|
||||||
var $toggle = $('#toggleDraw');
|
// This is a cheap trick to avoid loading sframe-channel in parallel with the
|
||||||
var $width = $('#width');
|
// loading screen setup.
|
||||||
var $widthLabel = $('label[for="width"]');
|
var done = waitFor();
|
||||||
var $opacity = $('#opacity');
|
var onMsg = function (msg) {
|
||||||
var $opacityLabel = $('label[for="opacity"]');
|
var data = JSON.parse(msg.data);
|
||||||
window.canvas = canvas;
|
if (data.q !== 'READY') { return; }
|
||||||
var createCursor = function () {
|
window.removeEventListener('message', onMsg);
|
||||||
var w = canvas.freeDrawingBrush.width;
|
var _done = done;
|
||||||
var c = canvas.freeDrawingBrush.color;
|
done = function () { };
|
||||||
var size = w > 30 ? w+2 : w+32;
|
_done();
|
||||||
$cursors.html('<canvas width="'+size+'" height="'+size+'"></canvas>');
|
|
||||||
var $ccanvas = $cursors.find('canvas');
|
|
||||||
var ccanvas = $ccanvas[0];
|
|
||||||
|
|
||||||
var ctx = ccanvas.getContext('2d');
|
|
||||||
var centerX = size / 2;
|
|
||||||
var centerY = size / 2;
|
|
||||||
var radius = w/2;
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
|
|
||||||
ctx.fillStyle = c;
|
|
||||||
ctx.fill();
|
|
||||||
ctx.lineWidth = 1;
|
|
||||||
ctx.strokeStyle = brush.color;
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(size/2, 0); ctx.lineTo(size/2, 10);
|
|
||||||
ctx.moveTo(size/2, size); ctx.lineTo(size/2, size-10);
|
|
||||||
ctx.moveTo(0, size/2); ctx.lineTo(10, size/2);
|
|
||||||
ctx.moveTo(size, size/2); ctx.lineTo(size-10, size/2);
|
|
||||||
ctx.strokeStyle = '#000000';
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
var img = ccanvas.toDataURL("image/png");
|
|
||||||
$controls.find('.selected > img').attr('src', img);
|
|
||||||
canvas.freeDrawingCursor = 'url('+img+') '+size/2+' '+size/2+', crosshair';
|
|
||||||
};
|
};
|
||||||
|
window.addEventListener('message', onMsg);
|
||||||
var updateBrushWidth = function () {
|
}).nThen(function (/*waitFor*/) {
|
||||||
var val = $width.val();
|
SFCommonO.start();
|
||||||
canvas.freeDrawingBrush.width = Number(val);
|
|
||||||
$widthLabel.text(Cryptpad.Messages._getKey("canvas_widthLabel", [val]));
|
|
||||||
$('#width-val').text(val + 'px');
|
|
||||||
createCursor();
|
|
||||||
};
|
|
||||||
updateBrushWidth();
|
|
||||||
|
|
||||||
$width.on('change', updateBrushWidth);
|
|
||||||
|
|
||||||
var updateBrushOpacity = function () {
|
|
||||||
var val = $opacity.val();
|
|
||||||
brush.opacity = Number(val);
|
|
||||||
canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity);
|
|
||||||
$opacityLabel.text(Cryptpad.Messages._getKey("canvas_opacityLabel", [val]));
|
|
||||||
$('#opacity-val').text((Number(val) * 100) + '%');
|
|
||||||
createCursor();
|
|
||||||
};
|
|
||||||
updateBrushOpacity();
|
|
||||||
|
|
||||||
$opacity.on('change', updateBrushOpacity);
|
|
||||||
|
|
||||||
var pickColor = function (current, cb) {
|
|
||||||
var $picker = $('<input>', {
|
|
||||||
type: 'color',
|
|
||||||
value: '#FFFFFF',
|
|
||||||
})
|
|
||||||
// TODO confirm that this is safe to remove
|
|
||||||
//.css({ visibility: 'hidden' })
|
|
||||||
.on('change', function () {
|
|
||||||
var color = this.value;
|
|
||||||
cb(color);
|
|
||||||
}).appendTo($pickers);
|
|
||||||
setTimeout(function () {
|
|
||||||
$picker.val(current);
|
|
||||||
$picker.click();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var setColor = function (c) {
|
|
||||||
c = Colors.rgb2hex(c);
|
|
||||||
brush.color = c;
|
|
||||||
canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity);
|
|
||||||
module.$color.css({
|
|
||||||
'color': c,
|
|
||||||
});
|
|
||||||
createCursor();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var palette = AppConfig.whiteboardPalette || [
|
|
||||||
'red', 'blue', 'green', 'white', 'black', 'purple',
|
|
||||||
'gray', 'beige', 'brown', 'cyan', 'darkcyan', 'gold', 'yellow', 'pink'
|
|
||||||
];
|
|
||||||
|
|
||||||
$('.palette-color').on('click', function () {
|
|
||||||
var color = $(this).css('background-color');
|
|
||||||
setColor(color);
|
|
||||||
});
|
|
||||||
|
|
||||||
module.draw = true;
|
|
||||||
var toggleDrawMode = function () {
|
|
||||||
module.draw = !module.draw;
|
|
||||||
canvas.isDrawingMode = module.draw;
|
|
||||||
$toggle.text(module.draw ? Messages.canvas_disable : Messages.canvas_enable);
|
|
||||||
if (module.draw) { $deleteButton.hide(); }
|
|
||||||
else { $deleteButton.show(); }
|
|
||||||
};
|
|
||||||
$toggle.click(toggleDrawMode);
|
|
||||||
|
|
||||||
var deleteSelection = function () {
|
|
||||||
if (canvas.getActiveObject()) {
|
|
||||||
canvas.getActiveObject().remove();
|
|
||||||
}
|
|
||||||
if (canvas.getActiveGroup()) {
|
|
||||||
canvas.getActiveGroup()._objects.forEach(function (el) {
|
|
||||||
el.remove();
|
|
||||||
});
|
|
||||||
canvas.discardActiveGroup();
|
|
||||||
}
|
|
||||||
canvas.renderAll();
|
|
||||||
module.onLocal();
|
|
||||||
};
|
|
||||||
$deleteButton.click(deleteSelection);
|
|
||||||
$(window).on('keyup', function (e) {
|
|
||||||
if (e.which === 46) { deleteSelection (); }
|
|
||||||
});
|
|
||||||
|
|
||||||
var setEditable = function (bool) {
|
|
||||||
if (readOnly && bool) { return; }
|
|
||||||
if (bool) { $controls.css('display', 'flex'); }
|
|
||||||
else { $controls.hide(); }
|
|
||||||
|
|
||||||
canvas.isDrawingMode = bool ? module.draw : false;
|
|
||||||
if (!bool) {
|
|
||||||
canvas.deactivateAll();
|
|
||||||
canvas.renderAll();
|
|
||||||
}
|
|
||||||
canvas.forEachObject(function (object) {
|
|
||||||
object.selectable = bool;
|
|
||||||
});
|
|
||||||
$canvasContainer.find('canvas').css('border-color', bool? 'black': 'red');
|
|
||||||
};
|
|
||||||
|
|
||||||
var saveImage = module.saveImage = function () {
|
|
||||||
var defaultName = "pretty-picture.png";
|
|
||||||
Cryptpad.prompt(Messages.exportPrompt, defaultName, function (filename) {
|
|
||||||
if (!(typeof(filename) === 'string' && filename)) { return; }
|
|
||||||
$canvas[0].toBlob(function (blob) {
|
|
||||||
saveAs(blob, filename);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.FM = Cryptpad.createFileManager({});
|
|
||||||
module.upload = function (title) {
|
|
||||||
var canvas = $canvas[0];
|
|
||||||
var finish = function (thumb) {
|
|
||||||
canvas.toBlob(function (blob) {
|
|
||||||
blob.name = title;
|
|
||||||
module.FM.handleFile(blob, void 0, thumb);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Thumb.fromCanvas(canvas, function (e, blob) {
|
|
||||||
// carry on even if you can't get a thumbnail
|
|
||||||
if (e) { console.error(e); }
|
|
||||||
finish(blob);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var initializing = true;
|
|
||||||
|
|
||||||
var $bar = $('#toolbar');
|
|
||||||
|
|
||||||
var Title;
|
|
||||||
var UserList;
|
|
||||||
var Metadata;
|
|
||||||
|
|
||||||
var config = module.config = {
|
|
||||||
initialState: '{}',
|
|
||||||
websocketURL: Cryptpad.getWebsocketURL(),
|
|
||||||
validateKey: secret.keys.validateKey,
|
|
||||||
readOnly: readOnly,
|
|
||||||
channel: secret.channel,
|
|
||||||
crypto: Crypto.createEncryptor(secret.keys),
|
|
||||||
transformFunction: JsonOT.transform,
|
|
||||||
};
|
|
||||||
|
|
||||||
var addColorToPalette = function (color, i) {
|
|
||||||
if (readOnly) { return; }
|
|
||||||
var $color = $('<span>', {
|
|
||||||
'class': 'palette-color',
|
|
||||||
})
|
|
||||||
.css({
|
|
||||||
'background-color': color,
|
|
||||||
})
|
|
||||||
.click(function () {
|
|
||||||
var c = Colors.rgb2hex($color.css('background-color'));
|
|
||||||
setColor(c);
|
|
||||||
})
|
|
||||||
.on('dblclick', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
pickColor(Colors.rgb2hex($color.css('background-color')), function (c) {
|
|
||||||
$color.css({
|
|
||||||
'background-color': c,
|
|
||||||
});
|
|
||||||
palette.splice(i, 1, c);
|
|
||||||
config.onLocal();
|
|
||||||
setColor(c);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$colors.append($color);
|
|
||||||
};
|
|
||||||
|
|
||||||
var metadataCfg = {};
|
|
||||||
var updatePalette = metadataCfg.updatePalette = function (newPalette) {
|
|
||||||
palette = newPalette;
|
|
||||||
$colors.html('<div class="hidden"> </div>');
|
|
||||||
palette.forEach(addColorToPalette);
|
|
||||||
};
|
|
||||||
updatePalette(palette);
|
|
||||||
|
|
||||||
var makeColorButton = function ($container) {
|
|
||||||
var $testColor = $('<input>', { type: 'color', value: '!' });
|
|
||||||
|
|
||||||
// if colors aren't supported, bail out
|
|
||||||
if ($testColor.attr('type') !== 'color' ||
|
|
||||||
$testColor.val() === '!') {
|
|
||||||
console.log("Colors aren't supported. Aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var $color = module.$color = $('<button>', {
|
|
||||||
id: "color-picker",
|
|
||||||
title: Messages.canvas_chooseColor,
|
|
||||||
'class': "fa fa-square rightside-button",
|
|
||||||
})
|
|
||||||
.on('click', function () {
|
|
||||||
pickColor($color.css('background-color'), function (color) {
|
|
||||||
setColor(color);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
setColor('#000');
|
|
||||||
|
|
||||||
$container.append($color);
|
|
||||||
|
|
||||||
return $color;
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onInit = function (info) {
|
|
||||||
UserList = Cryptpad.createUserList(info, config.onLocal, Cryptget, Cryptpad);
|
|
||||||
|
|
||||||
Title = Cryptpad.createTitle({}, config.onLocal, Cryptpad);
|
|
||||||
|
|
||||||
Metadata = Cryptpad.createMetadata(UserList, Title, metadataCfg, Cryptpad);
|
|
||||||
|
|
||||||
var configTb = {
|
|
||||||
displayed: ['title', 'useradmin', 'spinner', 'lag', 'state', 'share', 'userlist', 'newpad', 'limit', 'upgrade'],
|
|
||||||
userList: UserList.getToolbarConfig(),
|
|
||||||
share: {
|
|
||||||
secret: secret,
|
|
||||||
channel: info.channel
|
|
||||||
},
|
|
||||||
title: Title.getTitleConfig(),
|
|
||||||
common: Cryptpad,
|
|
||||||
readOnly: readOnly,
|
|
||||||
ifrw: window,
|
|
||||||
realtime: info.realtime,
|
|
||||||
network: info.network,
|
|
||||||
$container: $bar,
|
|
||||||
$contentContainer: $('#canvas-area')
|
|
||||||
};
|
|
||||||
|
|
||||||
toolbar = module.toolbar = Toolbar.create(configTb);
|
|
||||||
|
|
||||||
Title.setToolbar(toolbar);
|
|
||||||
|
|
||||||
var $rightside = toolbar.$rightside;
|
|
||||||
|
|
||||||
/* save as template */
|
|
||||||
if (!Cryptpad.isTemplate(window.location.href)) {
|
|
||||||
var templateObj = {
|
|
||||||
rt: info.realtime,
|
|
||||||
Crypt: Cryptget,
|
|
||||||
getTitle: function () { return document.title; }
|
|
||||||
};
|
|
||||||
var $templateButton = Cryptpad.createButton('template', true, templateObj);
|
|
||||||
$rightside.append($templateButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
var $export = Cryptpad.createButton('export', true, {}, saveImage);
|
|
||||||
$rightside.append($export);
|
|
||||||
|
|
||||||
Cryptpad.createButton('savetodrive', true, {}, function () {})
|
|
||||||
.click(function () {
|
|
||||||
Cryptpad.prompt(Messages.exportPrompt, document.title + '.png',
|
|
||||||
function (name) {
|
|
||||||
if (name === null || !name.trim()) { return; }
|
|
||||||
module.upload(name);
|
|
||||||
});
|
|
||||||
}).appendTo($rightside);
|
|
||||||
|
|
||||||
var $forget = Cryptpad.createButton('forget', true, {}, function (err) {
|
|
||||||
if (err) { return; }
|
|
||||||
setEditable(false);
|
|
||||||
toolbar.failed();
|
|
||||||
});
|
|
||||||
$rightside.append($forget);
|
|
||||||
|
|
||||||
var editHash;
|
|
||||||
|
|
||||||
if (!readOnly) {
|
|
||||||
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
|
|
||||||
makeColorButton($rightside);
|
|
||||||
}
|
|
||||||
if (!readOnly) { Cryptpad.replaceHash(editHash); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// used for debugging, feel free to remove
|
|
||||||
var Catch = function (f) {
|
|
||||||
return function () {
|
|
||||||
try {
|
|
||||||
f();
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var onRemote = config.onRemote = Catch(function () {
|
|
||||||
if (initializing) { return; }
|
|
||||||
var userDoc = module.realtime.getUserDoc();
|
|
||||||
|
|
||||||
Metadata.update(userDoc);
|
|
||||||
var json = JSON.parse(userDoc);
|
|
||||||
var remoteDoc = json.content;
|
|
||||||
|
|
||||||
// TODO update palette if it has changed
|
|
||||||
|
|
||||||
canvas.loadFromJSON(remoteDoc);
|
|
||||||
canvas.renderAll();
|
|
||||||
|
|
||||||
var content = canvas.toDatalessJSON();
|
|
||||||
if (content !== remoteDoc) { Cryptpad.notify(); }
|
|
||||||
if (readOnly) { setEditable(false); }
|
|
||||||
});
|
|
||||||
setEditable(false);
|
|
||||||
|
|
||||||
var stringifyInner = function (textValue) {
|
|
||||||
var obj = {
|
|
||||||
content: textValue,
|
|
||||||
metadata: {
|
|
||||||
users: UserList.userData,
|
|
||||||
palette: palette,
|
|
||||||
defaultTitle: Title.defaultTitle,
|
|
||||||
type: 'whiteboard',
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (!initializing) {
|
|
||||||
obj.metadata.title = Title.title;
|
|
||||||
}
|
|
||||||
// stringify the json and send it into chainpad
|
|
||||||
return JSONSortify(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var onLocal = module.onLocal = config.onLocal = Catch(function () {
|
|
||||||
if (initializing) { return; }
|
|
||||||
if (readOnly) { return; }
|
|
||||||
|
|
||||||
var content = stringifyInner(canvas.toDatalessJSON());
|
|
||||||
|
|
||||||
module.patchText(content);
|
|
||||||
});
|
|
||||||
|
|
||||||
config.onReady = function (info) {
|
|
||||||
var realtime = module.realtime = info.realtime;
|
|
||||||
module.patchText = TextPatcher.create({
|
|
||||||
realtime: realtime
|
|
||||||
});
|
|
||||||
|
|
||||||
var isNew = false;
|
|
||||||
var userDoc = module.realtime.getUserDoc();
|
|
||||||
if (userDoc === "" || userDoc === "{}") { isNew = true; }
|
|
||||||
else {
|
|
||||||
var hjson = JSON.parse(userDoc);
|
|
||||||
if (typeof(hjson) !== 'object' || Array.isArray(hjson) ||
|
|
||||||
(typeof(hjson.type) !== 'undefined' && hjson.type !== 'whiteboard')) {
|
|
||||||
Cryptpad.errorLoadingScreen(Messages.typeError);
|
|
||||||
throw new Error(Messages.typeError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Cryptpad.removeLoadingScreen();
|
|
||||||
setEditable(true);
|
|
||||||
initializing = false;
|
|
||||||
onRemote();
|
|
||||||
|
|
||||||
/* TODO: restore palette from metadata.palette */
|
|
||||||
|
|
||||||
if (readOnly) { return; }
|
|
||||||
UserList.getLastName(toolbar.$userNameButton, isNew);
|
|
||||||
};
|
|
||||||
|
|
||||||
config.onAbort = function () {
|
|
||||||
setEditable(false);
|
|
||||||
toolbar.failed();
|
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO onConnectionStateChange
|
|
||||||
config.onConnectionChange = function (info) {
|
|
||||||
setEditable(info.state);
|
|
||||||
toolbar.failed();
|
|
||||||
if (info.state) {
|
|
||||||
initializing = true;
|
|
||||||
toolbar.reconnecting(info.myId);
|
|
||||||
Cryptpad.findOKButton().click();
|
|
||||||
} else {
|
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.rt = Realtime.start(config);
|
|
||||||
|
|
||||||
canvas.on('mouse:up', onLocal);
|
|
||||||
|
|
||||||
$('#clear').on('click', function () {
|
|
||||||
canvas.clear();
|
|
||||||
onLocal();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#save').on('click', function () {
|
|
||||||
saveImage();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Cryptpad.ready(function () {
|
|
||||||
andThen();
|
|
||||||
Cryptpad.reportAppUsage();
|
|
||||||
});
|
|
||||||
Cryptpad.onError(function (info) {
|
|
||||||
if (info) {
|
|
||||||
onConnectError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user