Merge branch 'code2' into staging

pull/1/head
yflory 7 years ago
commit 78b338c379

@ -51,5 +51,16 @@ define(function() {
config.loginSalt = ''; config.loginSalt = '';
config.badStateTimeout = 30000; config.badStateTimeout = 30000;
config.applicationsIcon = {
file: 'fa-file-text-o',
pad: 'fa-file-word-o',
code: 'fa-file-code-o',
slide: 'fa-file-powerpoint-o',
poll: 'fa-calendar',
whiteboard: 'fa-paint-brush',
todo: 'fa-tasks',
contacts: 'fa-users',
};
return config; return config;
}); });

@ -41,7 +41,7 @@ define([
$userAdmin.find('button').addClass('btn').addClass('btn-secondary'); $userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () { $(window).click(function () {
$('.cryptpad-dropdown').hide(); $('.cp-dropdown-content').hide();
}); });
if (Cryptpad.isLoggedIn() && ApiConfig.allowSubscriptions) { if (Cryptpad.isLoggedIn() && ApiConfig.allowSubscriptions) {

@ -15,7 +15,7 @@ define([
var $main = $('#mainBlock'); var $main = $('#mainBlock');
$(window).click(function () { $(window).click(function () {
$('.cryptpad-dropdown').hide(); $('.cp-dropdown-content').hide();
}); });
// main block is hidden in case javascript is disabled // main block is hidden in case javascript is disabled
@ -61,7 +61,7 @@ define([
var displayCreateButtons = function () { var displayCreateButtons = function () {
var $parent = $('#buttons'); var $parent = $('#buttons');
var options = []; var options = [];
var $container = $('<div>', {'class': 'dropdown-bar'}).appendTo($parent); var $container = $('<div>', {'class': 'cp-dropdown-container'}).appendTo($parent);
Config.availablePadTypes.forEach(function (el) { Config.availablePadTypes.forEach(function (el) {
if (el === 'drive') { return; } if (el === 'drive') { return; }
if (!Cryptpad.isLoggedIn() && Config.registeredOnlyTypes && if (!Cryptpad.isLoggedIn() && Config.registeredOnlyTypes &&

@ -458,7 +458,7 @@ noscript {
padding: 0; padding: 0;
&.buttons { &.buttons {
margin-bottom: 10px; margin-bottom: 10px;
.dropdown-bar { .cp-dropdown-container {
button { button {
white-space: normal; white-space: normal;
text-align: left; text-align: left;

@ -1,115 +1,4 @@
@import (once) "../less2/include/colortheme.less"; @import (once) "../less2/include/dropdown.less";
/* The container <div> - needed to position the dropdown content */ .dropdown_main();
.dropdown-bar {
position: relative;
display: inline-block;
.dropbtn {
}
&:hover {
.dropbtn {
}
}
.fa {
font-family: FontAwesome;
}
button {
.fa-caret-down{
margin-right: 0px;
margin-left: 5px;
}
* {
.unselectable();
cursor: default;
}
}
.dropdown-bar-content {
display: none;
position: absolute;
background-color: @dropdown-bg;
min-width: 250px;
box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
font-family: @colortheme_font;
font-size: 16px;
line-height: 1em;
&.left {
right: 0;
}
&:hover {
display: block;
}
a {
color: @dropdown-color;
padding: 5px 16px;
text-decoration: none;
display: flex;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
float: none;
text-align: left;
font: @dropdown-font;
line-height: 1em;
.fa {
width: 20px;
text-align: center;
margin-right: 5px !important;
}
&:hover {
background-color: @dropdown-bg-hover;
color: @dropdown-color;
}
&.active {
background-color: @dropdown-bg-active;
color: @dropdown-color;
}
}
hr {
margin: 5px 0px;
height: 1px;
background: #bbb;
}
p {
min-width: 160px;
padding: 5px;
margin: 0;
white-space: normal;
text-align: left;
color: black;
font-size: 14px;
* {
font-size: 14px;
}
h2 {
color: black;
font-weight: bold;
text-align: center;
background-color: #EEEEEE;
padding: 5px 0px;
margin: 5px 0px;
font-size: 16px;
white-space: normal;
}
}
}
}

@ -1,7 +1,7 @@
@import "./variables.less"; @import "./variables.less";
@import (once) "../less2/include/colortheme.less"; @import (once) "../less2/include/colortheme.less";
.cp #loading { #loading {
position: fixed; position: fixed;
z-index: 9999; z-index: 9999;
top: 0px; top: 0px;
@ -33,7 +33,7 @@
} }
} }
} }
.cp #loadingTip { #loadingTip {
position: fixed; position: fixed;
z-index: 99999; z-index: 99999;
top: 80%; top: 80%;

@ -1,8 +1,16 @@
@import "./variables.less"; @import "./variables.less";
@import "./mixins.less"; @import "./mixins.less";
@import "./dropdown.less"; @import (once) "../less2/include/dropdown.less";
@import (once) "../less2/include/colortheme.less"; @import (once) "../less2/include/colortheme.less";
@import (once) "../less2/include/ckeditor-fix.less";
@import (once) "../less2/include/icon-colors.less";
.dropdown_main();
.ckeditor_fix();
.iconColors_main();
.unselectable { .unselectable {
-webkit-touch-callout: none; -webkit-touch-callout: none;
@ -13,51 +21,9 @@
user-select: none; user-select: none;
} }
.cke_reset_all * {
color: inherit;
}
// Classes used in common-interface.js
.padColor { color: @toolbar-pad-bg; }
.codeColor { color: @toolbar-code-bg; }
.slideColor { color: @toolbar-slide-bg; }
.pollColor { color: @toolbar-poll-bg; }
.fileColor { color: @toolbar-file-bg; }
.friendsColor { color: @toolbar-friends-bg; }
.whiteboardColor { color: @toolbar-whiteboard-bg; }
.driveColor { color: @toolbar-drive-bg; }
.settingsColor { color: @toolbar-settings-bg; }
.profileColor { color: @toolbar-settings-bg; }
.defaultColor { color: @toolbar-default-bg; }
.todoColor { color:@toolbar-todo-bg; }
.toolbar-container { .toolbar-container {
display: flex; display: flex;
} }
#cke_editor1 .cke_inner {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
flex-flow: column;
}
.cke_toolbox_main {
display: inline-block;
margin-bottom: -3px;
}
#cke_1_contents {
flex: 1;
margin-top: -1px;
display: flex;
overflow: visible;
iframe {
min-height: 100%;
width: 100%;
}
}
body .userlist-drawer { body .userlist-drawer {
font: @main-font-size @colortheme_font; font: @main-font-size @colortheme_font;
@ -201,36 +167,9 @@ body {
background: darken(@bgcolor, 10%); background: darken(@bgcolor, 10%);
color: @color; color: @color;
} }
.dropdown-bar-content.left a { .cp-dropdown-content.cp-dropdown-left a {
color: black; color: black;
} }
/*.dropdown-bar-content {
background: darken(@bgcolor, 5%);
border: 1px solid @color;
color: @color;
a {
color: @color;
&.active {
background-color: darken(@bgcolor, 10%);
color: @color;
}
&:hover {
background-color: @bgcolor;
color: @color;
}
}
hr {
background-color: darken(@bgcolor, 15%);
}
p {
h2 {
background-color: darken(@bgcolor, 10%);
}
.accountData {
background-color: @bgcolor;
}
}
}*/
} }
} }
@ -322,7 +261,7 @@ body .cryptpad-toolbar {
width: 100%; width: 100%;
z-index: 9001; z-index: 9001;
.dropdown-bar { .cp-dropdown-container {
//height: 100%; //height: 100%;
//display: inline-block; //display: inline-block;
button { button {
@ -865,7 +804,7 @@ body .cryptpad-toolbar {
&:hover { &:hover {
background-color: rgba(0,0,0,0.4); background-color: rgba(0,0,0,0.4);
} }
.dropdown-bar-content { .cp-dropdown-content {
margin: 0; margin: 0;
} }
button { button {
@ -929,7 +868,7 @@ body .cryptpad-toolbar {
border-radius: 0; border-radius: 0;
height: 100%; height: 100%;
} }
.dropdown-bar-content { .cp-dropdown-content {
margin-top: -1px; margin-top: -1px;
} }

@ -67,7 +67,7 @@
} }
button { button {
.buttonTitle { .cp-dropdown-button-title {
.fa-user { .fa-user {
margin-right: 5px; margin-right: 5px;
} }

@ -81,8 +81,8 @@
@toolbar-settings-color: @colortheme_settings-color; @toolbar-settings-color: @colortheme_settings-color;
@toolbar-profile-bg: @colortheme_profile-bg; @toolbar-profile-bg: @colortheme_profile-bg;
@toolbar-profile-color: @colortheme_profile-color; @toolbar-profile-color: @colortheme_profile-color;
@toolbar-todo-bg: #7bccd1; @toolbar-todo-bg: @colortheme_todo-bg;
@toolbar-todo-color: #000; @toolbar-todo-color: @colortheme_todo-color;
@topbar-back: #fff; @topbar-back: #fff;
@topbar-color: #000; @topbar-color: #000;

@ -1,5 +1,6 @@
@import (once) "./colortheme.less"; @import (once) "./colortheme.less";
@import (once) "./browser.less"; @import (once) "./browser.less";
@import (once) "./modal-theme.less";
.alertify_main () { .alertify_main () {
@alertify-fore: @colortheme_modal-fg; @alertify-fore: @colortheme_modal-fg;
@ -19,13 +20,14 @@
@alertify-input-bg: @colortheme_modal-input; @alertify-input-bg: @colortheme_modal-input;
@alertify-input-fg: @colortheme_modal-fg; @alertify-input-fg: @colortheme_modal-fg;
@alertify_padding-base: @colortheme_modal-padding; @alertify_padding-base: @modal_padding;
@alertify_box-shadow: @colortheme_modal-shadow; @alertify_box-shadow: @modal_shadow;
// Logs to show that something has happened // Logs to show that something has happened
// These show only once // These show only once
.alertify-logs { .alertify-logs {
z-index:10000;
@media print { @media print {
visibility: hidden; visibility: hidden;
} }

@ -0,0 +1,20 @@
// html
.noscroll_main () {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
overflow: hidden;
box-sizing: border-box;
position: relative;
body {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
overflow: hidden;
box-sizing: border-box;
position: relative;
}
}

@ -0,0 +1,38 @@
.avatar_main (@width) {
&.cp-avatar {
overflow: hidden;
text-overflow: ellipsis;
font-size: 16px;
display: flex;
align-items: center;
.cp-avatar-default, media-tag {
display: inline-flex;
width: @width;
height: @width;
justify-content: center;
align-items: center;
border-radius: 4px;
overflow: hidden;
box-sizing: content-box;
}
.cp-avatar-default {
.unselectable();
background: white;
color: black;
font-size: @width/1.2;
}
media-tag {
min-height: @width;
min-width: @width;
max-height: @width;
max-width: @width;
img {
min-width: 100%;
min-height: 100%;
max-width: none;
max-height: none; // To override 'media-tag img' in slide.less
}
}
}
}

@ -0,0 +1,35 @@
.ckeditor_fix () {
.cke_reset_all * {
color: inherit;
}
#cke_editor1 .cke_inner {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
flex-flow: column;
}
.cke_toolbox_main {
display: inline-block;
margin-bottom: -3px;
}
#cke_1_contents {
flex: 1;
margin-top: -1px;
display: flex;
overflow: visible;
iframe {
min-height: 100%;
width: 100%;
}
}
.cke_toolbox .cp-toolbar-history {
input.gotoInput { // TODO
padding: 3px 3px;
}
}
}

@ -1,4 +1,6 @@
@colortheme_font: 'Open Sans', 'Helvetica Neue', sans-serif; @colortheme_font: 'Open Sans', 'Helvetica Neue', sans-serif;
@colortheme_app-font-size: 16px;
@colortheme_app-font: @colortheme_app-font-size @colortheme_font;
@colortheme_link-color: #0275D8; @colortheme_link-color: #0275D8;
@colortheme_link-color-visited: #005999; @colortheme_link-color-visited: #005999;
@ -18,8 +20,6 @@
@colortheme_modal-link: #eee; @colortheme_modal-link: #eee;
@colortheme_modal-link-visited: lighten(@colortheme_modal-link, 10%); @colortheme_modal-link-visited: lighten(@colortheme_modal-link, 10%);
@colortheme_modal-dim: rgba(0, 0, 0, 0.4); @colortheme_modal-dim: rgba(0, 0, 0, 0.4);
@colortheme_modal-padding: 12px;
@colortheme_modal-shadow: 0 8px 32px 0 rgba(0,0,0,.4);
@colortheme_modal-input: #111; @colortheme_modal-input: #111;
@ -29,6 +29,11 @@
@colortheme_notification-log: rgba(0, 0, 0, 0.8); @colortheme_notification-log: rgba(0, 0, 0, 0.8);
@colortheme_notification-warn: rgba(205, 37, 50, 0.8); @colortheme_notification-warn: rgba(205, 37, 50, 0.8);
@colortheme_dropdown-bg: #f9f9f9;
@colortheme_dropdown-color: black;
@colortheme_dropdown-bg-hover: #f1f1f1;
@colortheme_dropdown-bg-active: #e8e8e8;
// Apps // Apps
@colortheme_pad-bg: #1c4fa0; @colortheme_pad-bg: #1c4fa0;
@ -64,6 +69,9 @@
@colortheme_profile-bg: #0087ff; @colortheme_profile-bg: #0087ff;
@colortheme_profile-color: #fff; @colortheme_profile-color: #fff;
@colortheme_todo-bg: #7bccd1;
@colortheme_todo-color: #000;
@cryptpad_color_blue: #4591C4; @cryptpad_color_blue: #4591C4;
@cryptpad_color_grey: #999999; @cryptpad_color_grey: #999999;
@cryptpad_header_col: #1E1F1F; @cryptpad_header_col: #1E1F1F;

@ -0,0 +1,109 @@
@import (once) "./colortheme.less";
/* The container <div> - needed to position the dropdown content */
.dropdown_main () {
.cp-dropdown-container {
@dropdown_font: @colortheme_app-font-size @colortheme_font;
position: relative;
display: inline-block;
.fa {
font-family: FontAwesome;
}
button {
.fa-caret-down{
margin-right: 0px;
margin-left: 5px;
}
* {
.unselectable();
cursor: default;
}
}
.cp-dropdown-content {
display: none;
position: absolute;
background-color: @colortheme_dropdown-bg;
min-width: 250px;
box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
font: @dropdown_font;
line-height: 1em;
&.cp-dropdown-left {
right: 0;
}
&:hover {
display: block;
}
a {
color: @colortheme_dropdown-color;
padding: 5px 16px;
text-decoration: none;
display: flex;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
float: none;
text-align: left;
font: @dropdown_font;
line-height: 1em;
.fa {
width: 20px;
text-align: center;
margin-right: 5px !important;
}
&:hover {
background-color: @colortheme_dropdown-bg-hover;
color: @colortheme_dropdown-color;
}
&.cp-dropdown-element-active {
background-color: @colortheme_dropdown-bg-active;
color: @colortheme_dropdown-color;
}
}
hr {
margin: 5px 0px;
height: 1px;
background: #bbb;
}
p {
min-width: 160px;
padding: 5px;
margin: 0;
white-space: normal;
text-align: left;
color: black;
font-size: 14px;
* {
font-size: 14px;
}
h2 {
color: black;
font-weight: bold;
text-align: center;
background-color: #EEEEEE;
padding: 5px 0px;
margin: 5px 0px;
font-size: 16px;
white-space: normal;
}
}
}
}
}

@ -0,0 +1,52 @@
@import (once) './colortheme.less';
@import (once) './modal.less';
.fileupload_main () {
/* Upload status table */
#cp-fileupload {
.modal_base();
position: absolute;
left: 10vw; right: 10vw;
bottom: 10vh;
opacity: 0.9;
box-sizing: border-box;
z-index: 1000000;
display: none;
#cp-fileupload-table {
width: 80vw;
tr:nth-child(1) {
background-color: darken(@colortheme_modal-bg, 20%);
td {
text-align: center;
font-weight: bold;
padding: 0.25em;
}
}
@upload_pad_h: 0.25em;
@upload_pad_v: 0.5em;
td {
padding: @upload_pad_h @upload_pad_v;
}
.cp-fileupload-table-progress {
width: 200px;
position: relative;
text-align: center;
box-sizing: border-box;
}
.cp-fileupload-table-progress-container {
position: absolute;
width: 0px;
left: @upload_pad_v;
top: @upload_pad_h; bottom: @upload_pad_h;
background-color: rgba(0,0,255,0.3);
z-index: -1;
}
.cp-fileupload-table-cancel { text-align: center; }
.fa.cancel {
color: rgb(255, 0, 115);
}
}
}
}

@ -0,0 +1,17 @@
@import (once) "./colortheme.less";
.iconColors_main () {
// Classes used in common-interface.js
.cp-icon-color-pad { color: @colortheme_pad-bg; }
.cp-icon-color-code { color: @colortheme_code-bg; }
.cp-icon-color-slide { color: @colortheme_slide-bg; }
.cp-icon-color-poll { color: @colortheme_poll-bg; }
.cp-icon-color-file { color: @colortheme_file-bg; }
.cp-icon-color-friends { color: @colortheme_friends-bg; }
.cp-icon-color-whiteboard { color: @colortheme_whiteboard-bg; }
.cp-icon-color-drive { color: @colortheme_drive-bg; }
.cp-icon-color-settings { color: @colortheme_settings-bg; }
.cp-icon-color-profile { color: @colortheme_settings-bg; }
.cp-icon-color-default { color: @colortheme_default-bg; }
.cp-icon-color-todo { color:@colortheme_todo-bg; }
}

@ -0,0 +1,27 @@
.markdown_preformatted-code (@color: #333) {
pre > code {
display: block;
position: relative;
border: 1px solid @color;
width: 90%;
margin: auto;
padding-left: .25vw;
overflow-x: auto;
overflow-y: hidden;
}
}
.markdown_gfm-table (@color: black) {
table {
border-collapse: collapse;
tr {
th {
border: 3px solid @color;
padding: 15px;
}
}
}
}
// todo ul, ol

@ -0,0 +1,4 @@
// Used in modal.less and alertify.less
@modal_padding: 12px;
@modal_shadow: 0 8px 32px 0 rgba(0,0,0,.4);

@ -1,11 +1,12 @@
@import (once) "./colortheme.less"; @import (once) "./colortheme.less";
@import (once) "./modal-theme.less";
.modal_base() { .modal_base() {
font-family: @colortheme_font; font-family: @colortheme_font;
background-color: @colortheme_modal-bg; background-color: @colortheme_modal-bg;
color: @colortheme_modal-fg; color: @colortheme_modal-fg;
box-shadow: @colortheme_modal-shadow; box-shadow: @modal_shadow;
a { a {
color: @colortheme_modal-link; color: @colortheme_modal-link;
@ -30,9 +31,9 @@
.cp-modal { .cp-modal {
background-color: @colortheme_modal-bg; background-color: @colortheme_modal-bg;
color: @colortheme_modal-fg; color: @colortheme_modal-fg;
box-shadow: @colortheme_modal-shadow; box-shadow: @modal_shadow;
padding: @colortheme_modal-padding; padding: @modal_padding;
position: absolute; position: absolute;
top: 15vh; bottom: 15vh; top: 15vh; bottom: 15vh;
@ -70,7 +71,7 @@
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
margin: @colortheme_modal-padding; margin: @modal_padding;
cursor: pointer; cursor: pointer;
} }
} }

@ -0,0 +1,51 @@
@import (once) "./colortheme.less";
.history_main () {
body .cp-toolbar-history {
display: none;
text-align: center;
* {
font: @colortheme_app-font;
}
.cp-toolbar-history-next {
display: inline-block;
vertical-align: middle;
margin: 20px;
}
.cp-toolbar-history-previous {
display: inline-block;
vertical-align: middle;
margin: 20px;
}
.cp-toolbar-history-goto {
display: inline-block;
vertical-align: middle;
text-align: center;
input { width: 75px; }
}
.cp-toolbar-history-goto-input {
padding-left: 5px;
margin-left: 5px;
vertical-align: middle;
}
button {
color: inherit;
background-color: rgba(0,0,0,0.2);
&:hover {
background-color: rgba(0,0,0,0.4);
}
}
.cp-toolbar-history-close {
background: white;
color: black;
margin-top: 5px;
&:hover {
background-color: #e6e6e6;
}
}
.fa-spinner {
font-size: 66px;
}
}
}

@ -0,0 +1,796 @@
@import (once) "./dropdown.less";
@import (once) "./colortheme.less";
@import (once) "./browser.less";
@import (once) "./ckeditor-fix.less";
@import (once) "./avatar.less";
@import (once) "./toolbar-history.less";
@import (once) "./icon-colors.less";
.toolbar_main () {
@toolbar_line-height: 32px;
@toolbar_top-height: 64px;
@toolbar_button-font: @colortheme_app-font;
.unselectable () {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.dropdown_main();
.ckeditor_fix();
.history_main();
.iconColors_main();
.cp-toolbar-container {
display: flex;
}
.cp-toolbar-userlist-drawer {
font: @colortheme_app-font-size @colortheme_font;
min-width: 175px;
width: 175px;
display: block;
overflow-y: auto;
overflow-x: hidden;
padding: 10px;
box-sizing: border-box;
.cp-toolbar-userlist-drawer-close {
position: absolute;
margin-top: -10px;
margin-left: 149px;
font-size: 15px;
opacity: 0.5;
cursor: pointer;
text-shadow: unset;
&:hover {
opacity: 1;
}
}
h2 {
color: inherit;
text-align: center;
padding: 5px 0px;
margin: 5px 0px;
font: inherit;
font-weight: bold;
white-space: normal;
line-height: auto;
}
text-align:baseline;
.cp-toolbar-userlist-viewer {
font-style: italic;
padding: 5px;
background: rgba(0,0,0,0.1);
margin: 2px 0;
}
& > p {
font: @colortheme_app-font-size @colortheme_font;
margin: 0;
padding: 0;
display: block;
}
.cp-toolbar-userlist-others {
display: flex;
flex-flow: column;
margin: 10px 0;
margin-bottom: 20px;
&>span {
padding: 5px;
margin: 2px 0;
background: rgba(0,0,0,0.1);
.avatar_main(30px);
.cp-avatar-default, media-tag {
margin-right: 5px;
}
}
}
.cp-toolbar-userlist-friend {
display: inline-block;
width: 20px;
}
}
.addToolbarColors (@color, @bg-color) {
.cp-toolbar-userlist-drawer {
background-color: @bgcolor;
color: @color;
.cp-toolbar-userlist-drawer-close {
color: @color;
}
h2 {
background-color: darken(@bgcolor, 10%);
color: @color;
}
.cp-toolbar-userlist-friend {
&:hover {
color: darken(@color, 15%);
}
}
}
.cp-toolbar {
background-color: @bgcolor;
color: @color;
.cp-toolbar-spinner {
font-size: @colortheme_app-font-size;
color: @color;
}
.cp-toolbar-limit {
text-shadow: -1px 0 @color, 0 1px @color, 1px 0 @color, 0 -1px @color;
}
.cp-toolbar-leftside, .cp-toolbar-rightside {
background-color: lighten(@bgcolor, 8%);
button:hover, button.cp-toolbar-button-active {
background-color: @bgcolor;
}
}
.cp-toolbar-title-hoverable:hover {
.cp-toolbar-title-editable, .cp-toolbar-title-edit {
cursor: text;
border: 1px solid darken(@bgcolor, 15%);
background: darken(@bgcolor, 10%);
transition: all 0.15s;
color: @color;
}
.cp-toolbar-title-editable {
cursor: text;
}
}
.cp-toolbar-title-save {
border: 1px solid darken(@bgcolor, 15%);
background: darken(@bgcolor, 10%);
color: @color;
&:hover {
background: darken(@bgcolor, 5%);
}
}
input {
border: 1px solid darken(@bgcolor, 15%);
background: darken(@bgcolor, 10%);
color: @color;
}
.cp-dropdown-content.cp-dropdown-left a {
color: black;
}
}
}
&.cp-app-pad {
@bgcolor: @colortheme_pad-bg;
@color: @colortheme_pad-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-code {
@bgcolor: @colortheme_code-bg;
@color: @colortheme_code-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-slide {
@bgcolor: @colortheme_slide-bg;
@color: @colortheme_slide-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-poll {
@bgcolor: @colortheme_poll-bg;
@color: @colortheme_poll-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-whiteboard {
@bgcolor: @colortheme_whiteboard-bg;
@color: @colortheme_whiteboard-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-drive {
@bgcolor: @colortheme_drive-bg;
@color: @colortheme_drive-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-file {
@bgcolor: @colortheme_file-bg;
@color: @colortheme_file-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-contacts {
@bgcolor: @colortheme_friends-bg;
@color: @colortheme_friends-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-settings {
@bgcolor: @colortheme_settings-bg;
@color: @colortheme_settings-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-profile {
@bgcolor: @colortheme_profile-bg;
@color: @colortheme_profile-color;
.addToolbarColors(@color, @bgcolor);
}
&.cp-app-todo {
@bgcolor: @colortheme_todo-bg;
@color: @colortheme_todo-color;
.addToolbarColors(@color, @bgcolor);
}
/* TODO: move to the slide LESS page */
.cp-app-slide {
@media screen and (max-width: @browser_media-medium-screen) {
.cp-toolbar-leftside {
flex-flow: row wrap;
width: 175px;
height: auto;
.cp-toolbar-spinner { order: 0; }
}
.cp-toolbar-rightside {
height: 2*@toolbar_line-height;
}
}
@media screen and (max-width: 320px) {
.cp-toolbar-leftside {
flex-flow: row wrap;
width: 175px;
height: auto;
padding-top: @toolbar_line-height;
.cp-toolbar-spinner { order: 0; }
}
.cp-toolbar-rightside {
height: auto;
}
}
}
.cp-toolbar {
* {
outline-width: 0;
&:focus {
outline-width: 0;
}
}
@toolbar-green: #5cb85c;
box-sizing: border-box;
padding: 0px;
//background-color: #BBBBFF;
background-color: @colortheme_default-bg;
color: @colortheme_default-color;
.fa {
font: normal normal normal 14px/1 FontAwesome;
font-family: FontAwesome;
}
.unselectable();
font: @toolbar_button-font;
width: 100%;
z-index: 9001;
.cp-dropdown-container {
//height: 100%;
//display: inline-block;
button {
height: 100%;
border-radius: 0;
margin: 0;
background: transparent;
}
}
button {
transition: all 0.15s;
.unselectable();
&.cp-toolbar-hidden {
display: none;
}
.cp-toolbar-drawer {
display: none;
}
// Bootstrap 4 colors (btn-secondary)
border: 1px solid transparent;
color: inherit;
font: @toolbar_button-font;
* {
color: inherit;
font: @toolbar_button-font;
}
}
.cp-toolbar-rightside button, .cp-toolbar-leftside button {
background: transparent;
&:hover {
background-color: rgba(50,50,50,0.3);
}
}
.cp-toolbar-limit {
box-sizing: border-box;
height: 26px;
width: 26px;
display: inline-block;
padding: 3px;
margin: 0px 3px 0 6px;
vertical-align: middle;
line-height: @toolbar_top-height;
span {
color: red;
cursor: pointer;
margin: auto;
font-size: 20px;
}
}
div {
white-space: normal;
}
button, select {
height: @toolbar_line-height;
box-sizing: border-box;
padding: 3px 10px;
margin: 0;
}
.cp-toolbar-rightside-button {
float: right;
cursor: pointer;
}
select {
border: 0px;
margin-left: 5px;
margin-right: 5px;
padding-left: 5px;
border: 1px solid #A6A6A6;
border-bottom-color: #979797;
vertical-align: top;
box-sizing: content-box;
option {
height: 24px;
}
}
&.cp-toolbar-notitle {
.cp-toolbar-top-filler {
flex: 1;
}
}
&:not(.cp-toolbar-notitle) {
.cp-toolbar-top {
@media screen and (max-width: @browser_media-medium-screen) {
flex-wrap: wrap;
height: auto;
.cp-toolbar-top-filler {
flex: 1;
}
.cp-toolbar-title {
flex: auto;
width: 100%;
order: 10;
height: @toolbar_line-height;
line-height: initial;
margin: 0;
.cp-toolbar-title-hoverable {
width: 100%;
}
.cp-toolbar-title-editable {
max-width: ~"calc(100vw - 26px)";
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
font-size: @colortheme_app-font-size;
height: @toolbar_line-height;
box-sizing: border-box;
line-height: 20px;
}
.cp-toolbar-title-edit, .cp-toolbar-title-save {
box-sizing: border-box;
height: @toolbar_line-height;
line-height: @colortheme_app-font-size;
display: inline-block;
.fa {
font-size: @colortheme_app-font-size;
}
}
input {
height: @toolbar_line-height;
font-size: @colortheme_app-font-size;
flex: 1;
max-width: none;
}
}
}
}
}
}
.cp-toolbar-top {
display: flex;
flex-flow: row;
height: @toolbar_top-height;
position: relative;
.cp-toolbar-top-filler {
height: 100%;
display: inline-block;
order: 4;
//flex: 1;
}
.cp-toolbar-title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
order: 3;
height: 100%;
display: inline-flex;
align-items: center;
line-height: @toolbar_top-height;
margin: 0 10px;
.cp-toolbar-title-value {
font-size: 25px;
vertical-align: middle;
line-height: 25px;
white-space: nowrap;
}
.cp-toolbar-title-value-page {
padding: 0 5px;
}
.cp-toolbar-title-edit, .cp-toolbar-title-save {
display: flex;
align-items: center;
font-size: 20px;
vertical-align: middle;
line-height: 20px;
.fa {
font-size: 20px;
}
}
.cp-toolbar-title-readonly {
margin-left: 10px;
font-size: 25px;
font-style: italic;
white-space: nowrap;
}
.cp-toolbar-title-hoverable {
display: inline-flex;
overflow: hidden;
}
.cp-toolbar-title-edit {
cursor: pointer;
border: 1px solid transparent;
padding: 5px;
border-collapse: collapse;
span {
cursor: pointer;
}
}
.cp-toolbar-title-save {
cursor: pointer;
padding: 5px;
border-collapse: collapse;
span {
cursor: pointer;
}
}
.cp-toolbar-title-editable {
overflow: hidden;
text-overflow: ellipsis;
border: 1px solid transparent;
padding: 5px;
border-collapse: collapse;
}
input {
max-width: ~"calc(100% - 40px)";
flex: 1;
font-size: 1.5em;
vertical-align: middle;
box-sizing: border-box;
cursor: auto;
width: 300px;
font-size: 20px;
padding: 5px 5px;
height: 40px;
}
}
.cp-toolbar-link, .cp-toolbar-new {
font-size: 48px;
line-height: 64px;
width: @toolbar_top-height;
height: @toolbar_top-height;
padding: 0;
box-sizing: border-box;
display: inline-block;
color: white;
a {
color: white;
}
transition: all 0.15s;
}
.cp-toolbar-new {
background-color: rgba(0,0,0,0.2);
&:hover {
background-color: rgba(0,0,0,0.3);
}
text-align: center;
font-size: 32px;
margin-left: 10px;
&> button {
display: flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px !important; // Allows us to have a nice square outline when focused
font-size: 1em;
color: inherit;
height: auto;
padding: 0px;
margin: 0;
&::before {
width: 100%;
text-align: center;
padding-top: 4px;
}
&:hover {
background-color: initial;
border-color: transparent;
}
span {
vertical-align: top;
font-size: 1em;
text-decoration: none;
color: inherit;
}
}
}
.cp-toolbar-link {
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
background-color: rgba(0,0,0,0.4);
&:hover {
background-color: rgba(0,0,0,0.5);
}
order: 1;
.fa {
margin: 0;
}
a.cp-toolbar-link-logo {
cursor: pointer;
display: inline-flex;
text-decoration: none;
height: auto;
padding: 10px;
img {
cursor: pointer;
height: 100%;
width: 100%;
}
}
}
.cp-toolbar-user {
height: 100%;
display: inline-flex;
order: 5;
line-height: @toolbar_top-height;
color: white;
.cp-toolbar-new { order: 2; }
.cp-toolbar-user-dropdown { order: 3; }
.cp-toolbar-backup { order: 4; } // TODO drive migration to secure iframe
&> * {
display: inline-block;
height: 100%;
vertical-align: top;
}
.cp-toolbar-user-dropdown {
z-index: 10000;
//margin-left: 20px;
height: 64px;
width: 64px;
padding: 0px;
box-sizing: border-box;
text-align: center;
background-color: rgba(0,0,0,0.3);
transition: all 0.15s;
&:hover {
background-color: rgba(0,0,0,0.4);
}
.cp-dropdown-content {
margin: 0;
}
button {
display: flex;
justify-content: center;
align-items: center;
height: 64px;
width: 64px;
padding: 0;
span {
text-align: center;
width: 100%;
cursor: default;
font-size: 32px;
}
&.cp-avatar {
.avatar_main(48px);
&.cp-userlist-clickable {
cursor: pointer;
&:hover {
background-color: rgba(0,0,0,0.3);
}
}
.cp-toolbar-userlist-rightcol {
order: 10;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: flex;
flex-flow: column;
.cp-toolbar-userlist-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cp-toolbar-userlist-friend {
padding: 0;
}
}
media-tag {
margin: 8px;
}
border: 0;
}
}
}
.cp-toolbar-backup {
margin: 0;
border-radius: 0;
background: transparent;
&:hover {
background-color: rgba(0,0,0,0.2);
}
}
}
}
.cp-toolbar-leftside {
//height: @toolbar_line-height;
&:empty {
height: 0;
}
float: left;
display: inline-flex;
align-items: center;
//margin-bottom: -1px;
.cp-toolbar-users {
pre {
/* needed for ckeditor */
white-space: pre;
margin: 5px 0px;
}
}
button {
margin: 0px;
border-radius: 0;
height: 100%;
}
.cp-dropdown-content {
margin-top: -1px;
}
& > span {
height: @toolbar_line-height;
}
#cp-toolbar-userlist-drawer-open { order: 1; }
.cp-toolbar-share-button { order: 2; }
.cp-toolbar-spinner { order: 3; }
#cp-toolbar-userlist-drawer-open button {
width: 125px;
text-align: center;
}
.cp-toolbar-share-button button {
width: 50px;
text-align: center;
}
}
.cp-toolbar-rightside {
min-height: @toolbar_line-height;
overflow: hidden;
&:empty {
min-height: 0;
height: 0;
}
text-align: right;
/*&> button {
height: 100%;
margin: 0;
border-radius: 0;
padding: 0 10px;
}*/
.cp-toolbar-drawer-content:empty ~ .cp-toolbar-drawer-button {
display: none;
}
.cp-toolbar-drawer-content {
box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2);
position: absolute;
right:0px;
margin-top: @toolbar_line-height;
min-width: 50px;
background: @colortheme_dropdown-bg;
display: flex;
flex-flow: column;
z-index:10000;
color: black;
.fa {
font-size: 17px;
}
&> span {
box-sizing: border-box;
min-width: 150px;
height: @toolbar_line-height;
border-radius: 0;
border: 0;
}
button {
padding: 5px 16px;
text-align: left;
margin: 0;
border-radius: 0;
border: 0;
width: 100%;
line-height: 1em;
.cp-toolbar-drawer-element {
margin-left: 10px;
display: inline;
vertical-align: top;
}
&:hover {
background-color: @colortheme_dropdown-bg-hover !important;
color: @colortheme_dropdown-color;
}
}
}
}
.cp-toolbar-spinner {
line-height: @toolbar_line-height;
padding: 0 20px;
&> span.fa {
height: 20px;
width: 20px;
//margin: 8px;
line-height: 20px;
font-size: 20px;
text-align: center;
}
}
.cp-toolbar-readonly {
margin-right: 5px;
font-weight: bold;
text-transform: uppercase;
}
.cp-toolbar-share {
a {
.fa {
margin-right: 5px;
}
}
}
}

@ -10,3 +10,14 @@ body.cp-page-what-is-cryptpad { @import "./pages/page-what-is-cryptpad.less"; }
body.cp-page-about { @import "./pages/page-about.less"; } body.cp-page-about { @import "./pages/page-about.less"; }
body.cp-page-privacy { @import "./pages/page-privacy.less"; } body.cp-page-privacy { @import "./pages/page-privacy.less"; }
body.cp-page-terms { @import "./pages/page-terms.less"; } body.cp-page-terms { @import "./pages/page-terms.less"; }
// Set the HTML style for the apps which shouldn't have a body scrollbar
html.cp-app-noscroll {
@import "./include/app-noscroll.less";
.noscroll_main();
}
body.cp-app-pad { @import "../../../pad/app-pad.less"; }
body.cp-app-code { @import "../../../code/app-code.less"; }
body.cp-app-filepicker { @import "../../../filepicker/app-filepicker.less"; }

@ -19,6 +19,11 @@
.error { .error {
border: 1px solid red; border: 1px solid red;
} }
.thumb {
max-height: 150px;
width: auto;
border: 3px solid black;
}
</style> </style>
</head> </head>
@ -36,6 +41,7 @@
<!-- --> <!-- -->
<div id="quot"><p>"pewpewpew"</p></div> <div id="quot"><p>"pewpewpew"</p></div>
<hr> <hr>
<h2>Test 2</h2> <h2>Test 2</h2>
@ -45,3 +51,6 @@
<div id="widget"><div data-cke-widget-id="0" tabindex="-1" data-cke-widget-wrapper="1" data-cke-filter="off" class="cke_widget_wrapper cke_widget_block" data-cke-display-name="macro:velocity" contenteditable="false"><div class="macro cke_widget_element" data-macro="startmacro:velocity|-||-|Here is a macro" data-cke-widget-data="%7B%22classes%22%3A%7B%22macro%22%3A1%7D%7D" data-cke-widget-upcasted="1" data-cke-widget-keep-attr="0" data-widget="xwiki-macro"><p>Here is a macro</p></div><span style='background: rgba(220, 220, 220, 0.5) url("/customize/cryptofist_small.png") repeat scroll 0% 0%; top: -15px; left: 0px; display: block;' class="cke_reset cke_widget_drag_handler_container"><img title="Click and drag to move" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" data-cke-widget-drag-handler="1" class="cke_reset cke_widget_drag_handler" height="15" width="15"></span></div></div> <div id="widget"><div data-cke-widget-id="0" tabindex="-1" data-cke-widget-wrapper="1" data-cke-filter="off" class="cke_widget_wrapper cke_widget_block" data-cke-display-name="macro:velocity" contenteditable="false"><div class="macro cke_widget_element" data-macro="startmacro:velocity|-||-|Here is a macro" data-cke-widget-data="%7B%22classes%22%3A%7B%22macro%22%3A1%7D%7D" data-cke-widget-upcasted="1" data-cke-widget-keep-attr="0" data-widget="xwiki-macro"><p>Here is a macro</p></div><span style='background: rgba(220, 220, 220, 0.5) url("/customize/cryptofist_small.png") repeat scroll 0% 0%; top: -15px; left: 0px; display: block;' class="cke_reset cke_widget_drag_handler_container"><img title="Click and drag to move" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" data-cke-widget-drag-handler="1" class="cke_reset cke_widget_drag_handler" height="15" width="15"></span></div></div>
<hr> <hr>
<img id="thumb-orig" src="/customize/alt-favicon.png" />

@ -5,8 +5,9 @@ define([
'json.sortify', 'json.sortify',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/drive/tests.js', '/drive/tests.js',
'/common/test.js' '/common/test.js',
], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad, Drive, Test) { '/common/common-thumbnail.js',
], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad, Drive, Test, Thumb) {
window.Hyperjson = Hyperjson; window.Hyperjson = Hyperjson;
window.TextPatcher = TextPatcher; window.TextPatcher = TextPatcher;
window.Sortify = Sortify; window.Sortify = Sortify;
@ -207,6 +208,31 @@ define([
return cb(true); return cb(true);
}, "version 2 hash failed to parse correctly"); }, "version 2 hash failed to parse correctly");
assert(function (cb) {
var getBlob = function (url, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "blob";
xhr.onload = function () {
cb(void 0, this.response);
};
xhr.send();
};
var $img = $('img#thumb-orig');
getBlob($img.attr('src'), function (e, blob) {
console.log(e, blob);
Thumb.fromImageBlob(blob, function (e, thumb) {
console.log(thumb);
var th = new Image();
th.src = URL.createObjectURL(thumb);
th.onload = function () {
$(document.body).append($(th).addClass('thumb'));
cb(th.width === Thumb.dimension && th.height === Thumb.dimension);
};
});
});
});
Drive.test(assert); Drive.test(assert);

@ -0,0 +1,95 @@
@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-code {
display: flex;
flex-flow: column;
max-height: 100%;
min-height: auto;
.CodeMirror {
display: inline-block;
height: 100%;
width: 50%;
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;
}
#cp-app-code-editor {
flex: 1;
display: flex;
flex-flow: row;
height: 100%;
overflow: hidden;
}
#cp-app-code-preview {
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;
}
}
}
#cp-app-code-preview-content {
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: @browser_media-medium-screen) {
.CodeMirror {
flex: 1;
max-width: 100%;
resize: none;
}
#cp-app-code-preview {
display: none !important;
}
}
}

@ -1,41 +1,39 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="cp code"> <html>
<head> <head>
<title>CryptPad</title> <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">
<meta name="referrer" content="no-referrer" /> <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> <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> <style>
html, body { html, body {
overflow-y: hidden; margin: 0px;
}
#iframe-container {
position: fixed;
top: 0px;
bottom: 0px;
right: 0px;
left: 0px;
padding: 0px; padding: 0px;
} }
#pad-iframe { #sbox-iframe {
position:fixed;
top:0px;
left:0px;
bottom:0px;
right:0px;
width:100%; width:100%;
height:100%; height:100%;
border:none; border:none;
margin:0; margin:0;
padding:0; padding:0;
overflow:hidden; overflow:hidden;
box-sizing: border-box;
} }
/* We use !important here to override the 96% set to the element in DecorateToolbar.js #sbox-filePicker-iframe {
when we enter fullscreen mode. It allows us to avoid changing the iframe's size in JS */ position: fixed;
#pad-iframe.fullscreen { top:0; left:0;
top: 0px; bottom:0; right:0;
height: 100% !important; width:100%;
height: 100%;
border: 0;
} }
</style> </style>
</head> </head>
<body> <body>
<div id="iframe-container"> <iframe id="sbox-iframe">
<iframe id="pad-iframe"></iframe><script src="/common/noscriptfix.js"></script>
</div>

@ -1,16 +1,20 @@
<!DOCTYPE html> <!DOCTYPE html>
<html style="height: 100%;"> <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 src="/bower_components/jquery/dist/jquery.min.js"></script> <script async data-bootload="/code/inner.js" data-main="/common/sframe-boot.js?ver=1.1" src="/bower_components/requirejs/require.js?ver=2.3.5"></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>
<style> .loading-hidden { display: none; } </style> .loading-hidden { display: none; }
#editor1 { display: none; }
</style>
</head> </head>
<body class="loading-hidden"> <body class="cp-app-code">
<div id="cme_toolbox" class="toolbar-container"></div> <div id="cme_toolbox" class="cp-toolbar-container"></div>
<div id="editorContainer"> <div id="cp-app-code-editor">
<textarea id="editor1" name="editor1"></textarea> <textarea id="editor1" name="editor1"></textarea>
<div id="previewContainer"><div id="preview"></div></div> <div id="cp-app-code-preview">
<div id="cp-app-code-preview-content"></div>
</div>
</div> </div>
</body> </body>
</html> </html>

@ -1,13 +1,23 @@
define([ define([
'jquery', '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',
'/common/diffMarked.js',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'/api/config',
'/common/common-realtime.js',
'cm/lib/codemirror', 'cm/lib/codemirror',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/code/code.less', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less/toolbar.less', 'less!/customize/src/less2/main.less',
'less!/customize/src/less/cryptpad.less',
'css!cm/lib/codemirror.css', 'css!cm/lib/codemirror.css',
'css!cm/addon/dialog/dialog.css', 'css!cm/addon/dialog/dialog.css',
@ -34,7 +44,599 @@ define([
'cm/addon/fold/markdown-fold', 'cm/addon/fold/markdown-fold',
'cm/addon/fold/comment-fold', 'cm/addon/fold/comment-fold',
'cm/addon/display/placeholder', 'cm/addon/display/placeholder',
], function ($, CMeditor) {
], function (
$,
Crypto,
TextPatcher,
Toolbar,
JSONSortify,
JsonOT,
Cryptpad,
Cryptget,
DiffMd,
nThen,
SFCommon,
ApiConfig,
CommonRealtime,
CMeditor)
{
window.CodeMirror = CMeditor; window.CodeMirror = CMeditor;
$('.loading-hidden').removeClass('loading-hidden'); var Messages = Cryptpad.Messages;
var APP = window.APP = {
Cryptpad: Cryptpad,
};
var stringify = function (obj) {
return JSONSortify(obj);
};
var toolbar;
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var andThen = function (editor, CodeMirror, common) {
var readOnly = false;
var cpNfInner;
var metadataMgr;
var $bar = $('#cme_toolbox');
var isHistoryMode = false;
var $contentContainer = $('#cp-app-code-editor');
var $previewContainer = $('#cp-app-code-preview');
var $preview = $('#cp-app-code-preview-content');
$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 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 updateIndentSettings = function () {
if (!metadataMgr) { return; }
var data = metadataMgr.getPrivateData().settings;
var indentUnit = data[indentKey];
var useTabs = data[useTabsKey];
setIndentation(
typeof(indentUnit) === 'number'? indentUnit: 2,
typeof(useTabs) === 'boolean'? useTabs: false);
};
var setEditable = APP.setEditable = function (bool) {
if (readOnly && bool) { return; }
editor.setOption('readOnly', !bool);
};
var Title;
var 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 canonicalize = function (t) { return t.replace(/\r\n/g, '\n'); };
var setHistory = function (bool, update) {
isHistoryMode = bool;
setEditable(!bool);
if (!bool && update) {
config.onRemote();
}
};
CommonRealtime.onInfiniteSpinner(function () { setEditable(false); });
setEditable(false);
var initializing = true;
var stringifyInner = function (textValue) {
var obj = {
content: textValue,
metadata: metadataMgr.getMetadataLazy()
};
/* 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 = $('.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();
common.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');
};
config.onInit = function (info) {
metadataMgr.onChangeLazy(updateIndentSettings);
updateIndentSettings();
readOnly = metadataMgr.getPrivateData().readOnly;
var titleCfg = { getHeadingText: CodeMirror.getHeadingText };
Title = common.createTitle(titleCfg, config.onLocal, common, metadataMgr);
var configTb = {
displayed: ['title', 'useradmin', 'spinner', 'share', 'userlist', 'newpad', 'limit'],
title: Title.getTitleConfig(),
metadataMgr: metadataMgr,
readOnly: readOnly,
ifrw: window,
realtime: info.realtime,
common: Cryptpad,
sfCommon: common,
$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;
/* 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 = common.createButton('history', true, {histConfig: histConfig});
$drawer.append($hist);
/* 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, {}, CodeMirror.exportText);
$drawer.append($export);
if (!readOnly) {
/* add an import button */
var $import = common.createButton('import', true, {}, CodeMirror.importText);
$drawer.append($import);
}
/* add a forget button */
var forgetCb = function (err) {
if (err) { return; }
setEditable(false);
};
var $forgetPad = common.createButton('forget', true, {}, forgetCb);
$rightside.append($forgetPad);
var $previewButton = APP.$previewButton = common.createButton(null, true);
$previewButton.removeClass('fa-question').addClass('fa-eye');
$previewButton.attr('title', Messages.previewButtonTitle);
$previewButton.click(function () {
var $codeMirror = $('.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');
$previewButton.addClass('active');
common.setPadAttribute('previewMode', true, function (e) {
if (e) { return console.log(e); }
});
} else {
$codeMirror.addClass('fullPage');
$previewButton.removeClass('active');
common.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();
}
var fileDialogCfg = {
onSelect: function (data) {
if (data.type === 'file') {
var mt = '<media-tag src="' + data.src + '" data-crypto-key="cryptpad:' + data.key + '"></media-tag>';
editor.replaceSelection(mt);
return;
}
}
};
common.initFilePicker(common, fileDialogCfg);
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']
};
common.openFilePicker(common, pickerCfg);
}).appendTo($rightside);
};
config.onReady = function (info) {
console.log('onready');
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 (hjson && hjson.metadata) {
metadataMgr.updateMetadata(hjson.metadata);
}
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);
}
} else {
Title.updateTitle(Cryptpad.initialName || Title.defaultTitle);
}
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);
}
common.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(!readOnly);
initializing = false;
onLocal(); // push local state to avoid parse errors later.
if (readOnly) {
config.onRemote();
return;
}
if (isNew) {
common.openTemplatePicker(common);
}
var fmConfig = {
dropArea: $('.CodeMirror'),
body: $('body'),
onUploaded: function (ev, data) {
//var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')';
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 = common.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;
if (hjson.metadata) {
metadataMgr.updateMetadata(hjson.metadata);
}
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;
cpNfInner = common.startRealtime(config);
metadataMgr = cpNfInner.metadataMgr;
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();*/
var CMEDITOR_CHECK_INTERVAL = 100;
var cmEditorAvailable = function (cb) {
var intr;
var check = function () {
if (window.CodeMirror) {
clearTimeout(intr);
cb(window.CodeMirror);
}
};
intr = setInterval(function () {
console.log("CodeMirror was not defined. Trying again in %sms", CMEDITOR_CHECK_INTERVAL);
check();
}, CMEDITOR_CHECK_INTERVAL);
check();
};
var main = function () {
var CM;
var CodeMirror;
var editor;
var common;
nThen(function (waitFor) {
cmEditorAvailable(waitFor(function (cm) {
CM = cm;
}));
$(waitFor(function () {
Cryptpad.addLoadingScreen();
}));
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (/*waitFor*/) {
CodeMirror = Cryptpad.createCodemirror(window, Cryptpad, null, CM);
$('.CodeMirror').addClass('fullPage');
editor = CodeMirror.editor;
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
andThen(editor, CodeMirror, common);
});
};
main();
}); });

@ -1,559 +1,41 @@
// Load #1, load as little as possible because we are in a race to get the loading screen up.
define([ define([
'/bower_components/nthen/index.js',
'/api/config',
'jquery', 'jquery',
'/bower_components/chainpad-crypto/crypto.js', '/common/requireconfig.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js', '/common/sframe-common-outer.js'
'/bower_components/textpatcher/TextPatcher.js', ], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) {
'/common/toolbar2.js', var requireConfig = RequireConfig();
'json.sortify',
'/bower_components/chainpad-json-validator/json-ot.js', // Loaded in load #2
'/common/cryptpad-common.js', nThen(function (waitFor) {
'/common/cryptget.js', $(waitFor());
'/common/diffMarked.js', }).nThen(function (waitFor) {
var req = {
'css!/bower_components/components-font-awesome/css/font-awesome.min.css', cfg: requireConfig,
'less!/customize/src/less/cryptpad.less' req: [ '/common/loading.js' ],
], function ($, Crypto, Realtime, TextPatcher, Toolbar, JSONSortify, JsonOT, Cryptpad, pfx: window.location.origin
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);
}; };
window.rc = requireConfig;
var toolbar; window.apiconf = ApiConfig;
var editor; $('#sbox-iframe').attr('src',
ApiConfig.httpSafeOrigin + '/code/inner.html?' + requireConfig.urlArgs +
var secret = Cryptpad.getSecrets(); '#' + encodeURIComponent(JSON.stringify(req)));
var readOnly = secret.keys && !secret.keys.editKeyStr;
if (!secret.keys) { // This is a cheap trick to avoid loading sframe-channel in parallel with the
secret.keys = secret.key; // loading screen setup.
} var done = waitFor();
var onMsg = function (msg) {
var onConnectError = function () { var data = JSON.parse(msg.data);
Cryptpad.errorLoadingScreen(Messages.websocketError); if (data.q !== 'READY') { return; }
window.removeEventListener('message', onMsg);
var _done = done;
done = function () { };
_done();
}; };
window.addEventListener('message', onMsg);
var andThen = function (CMeditor) { }).nThen(function (/*waitFor*/) {
var $iframe = $('#pad-iframe').contents(); SFCommonO.start();
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 = 'cryptpad.indentUnit';
var useTabsKey = 'cryptpad.indentWithTabs';
var proxy = Cryptpad.getProxy();
var updateIndentSettings = APP.updateIndentSettings = function () {
var indentUnit = proxy[indentKey];
var useTabs = proxy[useTabsKey];
setIndentation(
typeof(indentUnit) === 'number'? indentUnit: 2,
typeof(useTabs) === 'boolean'? useTabs: false);
};
proxy.on('change', [indentKey], updateIndentSettings);
proxy.on('change', [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 = '!['+cleanName+']('+data.url+')';
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();
}); });
}); });

@ -16,7 +16,8 @@ define([
CodeMirror.modeURL = "cm/mode/%N/%N"; CodeMirror.modeURL = "cm/mode/%N/%N";
var $pad = $('#pad-iframe'); var $pad = $('#pad-iframe');
var $textarea = exp.$textarea = $pad.contents().find('#editor1'); var $textarea = exp.$textarea = $('#editor1');
if (!$textarea.length) { $textarea = exp.$textarea = $pad.contents().find('#editor1'); }
var Title; var Title;
var onLocal = function () {}; var onLocal = function () {};
@ -228,6 +229,7 @@ define([
var ext = /.+\.([^.]+)$/.exec(file.name); var ext = /.+\.([^.]+)$/.exec(file.name);
if (ext[1]) { if (ext[1]) {
mode = CMeditor.findModeByExtension(ext[1]); mode = CMeditor.findModeByExtension(ext[1]);
mode = mode && mode.mode || null;
} }
} else { } else {
mode = mime && mime.mode || null; mode = mime && mime.mode || null;

@ -1,12 +1,13 @@
define([ define([
'jquery', 'jquery',
'/file/file-crypto.js', '/file/file-crypto.js',
'/common/common-thumbnail.js',
'/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, FileCrypto) { ], function ($, FileCrypto, Thumb) {
var Nacl = window.nacl; var Nacl = window.nacl;
var module = {}; var module = {};
var blobToArrayBuffer = function (blob, cb) { var blobToArrayBuffer = module.blobToArrayBuffer = function (blob, cb) {
var reader = new FileReader(); var reader = new FileReader();
reader.onloadend = function () { reader.onloadend = function () {
cb(void 0, this.result); cb(void 0, this.result);
@ -23,6 +24,85 @@ define([
} }
}; };
module.upload = function (file, noStore, common, updateProgress, onComplete, onError, onPending) {
var u8 = file.blob; // This is not a blob but a uint8array
var metadata = file.metadata;
var key = Nacl.randomBytes(32);
var next = FileCrypto.encrypt(u8, metadata, key);
var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata);
var sendChunk = function (box, cb) {
var enc = Nacl.util.encodeBase64(box);
common.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) {
cb(e, msg);
});
};
var actual = 0;
var again = function (err, box) {
if (err) { throw new Error(err); }
if (box) {
actual += box.length;
var progressValue = (actual / estimate * 100);
updateProgress(progressValue);
return void sendChunk(box, function (e) {
if (e) { return console.error(e); }
next(again);
});
}
if (actual !== estimate) {
console.error('Estimated size does not match actual size');
}
// if not box then done
common.uploadComplete(function (e, id) {
if (e) { return void console.error(e); }
var uri = ['', 'blob', id.slice(0,2), id].join('/');
console.log("encrypted blob is now available as %s", uri);
var b64Key = Nacl.util.encodeBase64(key);
var hash = common.getFileHashFromKeys(id, b64Key);
var href = '/file/#' + hash;
var title = metadata.name;
if (noStore) { return void onComplete(href); }
common.renamePad(title || "", href, function (err) {
if (err) { return void console.error(err); }
onComplete(href);
});
});
};
common.uploadStatus(estimate, function (e, pending) {
if (e) {
console.error(e);
onError(e);
return;
}
if (pending) {
return void onPending(function () {
// if the user wants to cancel the pending upload to execute that one
common.uploadCancel(function (e, res) {
if (e) {
return void console.error(e);
}
console.log(res);
next(again);
});
});
}
next(again);
});
};
module.create = function (common, config) { module.create = function (common, config) {
var File = {}; var File = {};
@ -62,7 +142,8 @@ define([
}; };
var upload = function (file) { var upload = function (file) {
var blob = file.blob; var blob = file.blob; // This is not a blob but an array buffer
var u8 = new Uint8Array(blob);
var metadata = file.metadata; var metadata = file.metadata;
var id = file.id; var id = file.id;
if (queue.inProgress) { return; } if (queue.inProgress) { return; }
@ -83,112 +164,49 @@ define([
}); });
}; };
var u8 = new Uint8Array(blob); var onComplete = function (href) {
$link.attr('href', href)
var key = Nacl.randomBytes(32); .click(function (e) {
var next = FileCrypto.encrypt(u8, metadata, key); e.preventDefault();
window.open($link.attr('href'), '_blank');
});
var title = metadata.name;
common.log(Messages._getKey('upload_success', [title]));
common.prepareFeedback('upload')();
var estimate = FileCrypto.computeEncryptedSize(blob.byteLength, metadata); if (config.onUploaded) {
var data = getData(file, href);
config.onUploaded(file.dropEvent, data);
}
var sendChunk = function (box, cb) { queue.inProgress = false;
var enc = Nacl.util.encodeBase64(box); queue.next();
common.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) {
console.log(box);
cb(e, msg);
});
}; };
var actual = 0; var onError = function (e) {
var again = function (err, box) { queue.inProgress = false;
if (err) { throw new Error(err); } queue.next();
if (box) { if (e === 'TOO_LARGE') {
actual += box.length; // TODO update table to say too big?
var progressValue = (actual / estimate * 100); return void common.alert(Messages.upload_tooLarge);
updateProgress(progressValue);
return void sendChunk(box, function (e) {
if (e) { return console.error(e); }
next(again);
});
} }
if (e === 'NOT_ENOUGH_SPACE') {
if (actual !== estimate) { // TODO update table to say not enough space?
console.error('Estimated size does not match actual size'); return void common.alert(Messages.upload_notEnoughSpace);
} }
console.error(e);
return void common.alert(Messages.upload_serverError);
};
// if not box then done var onPending = function (cb) {
common.uploadComplete(function (e, id) { common.confirm(Messages.upload_uploadPending, function (yes) {
if (e) { return void console.error(e); } if (!yes) { return; }
var uri = ['', 'blob', id.slice(0,2), id].join('/'); cb();
console.log("encrypted blob is now available as %s", uri);
var b64Key = Nacl.util.encodeBase64(key);
var hash = common.getFileHashFromKeys(id, b64Key);
var href = '/file/#' + hash;
$link.attr('href', href)
.click(function (e) {
e.preventDefault();
window.open($link.attr('href'), '_blank');
});
var title = metadata.name;
var onComplete = function () {
common.log(Messages._getKey('upload_success', [title]));
common.prepareFeedback('upload')();
if (config.onUploaded) {
var data = getData(file, href);
config.onUploaded(file.dropEvent, data);
}
queue.inProgress = false;
queue.next();
};
if (config.noStore) { return void onComplete(); }
common.renamePad(title || "", href, function (err) {
if (err) { return void console.error(err); } // TODO
onComplete();
});
//Title.updateTitle(title || "", href);
//APP.toolbar.title.show();
}); });
}; };
common.uploadStatus(estimate, function (e, pending) { file.blob = u8;
if (e) { module.upload(file, config.noStore, common, updateProgress, onComplete, onError, onPending);
queue.inProgress = false;
queue.next();
if (e === 'TOO_LARGE') {
// TODO update table to say too big?
return void common.alert(Messages.upload_tooLarge);
}
if (e === 'NOT_ENOUGH_SPACE') {
// TODO update table to say not enough space?
return void common.alert(Messages.upload_notEnoughSpace);
}
console.error(e);
return void common.alert(Messages.upload_serverError);
}
if (pending) {
// TODO keep this message in case of pending files in another window?
return void common.confirm(Messages.upload_uploadPending, function (yes) {
if (!yes) { return; }
common.uploadCancel(function (e, res) {
if (e) {
return void console.error(e);
}
console.log(res);
next(again);
});
});
}
next(again);
});
}; };
var prettySize = function (bytes) { var prettySize = function (bytes) {
@ -246,30 +264,46 @@ define([
var handleFile = File.handleFile = function (file, e, thumbnail) { var handleFile = File.handleFile = function (file, e, thumbnail) {
var thumb; var thumb;
var finish = function (arrayBuffer) { var file_arraybuffer;
var finish = function () {
var metadata = { var metadata = {
name: file.name, name: file.name,
type: file.type, type: file.type,
}; };
if (thumb) { metadata.thumbnail = thumb; } if (thumb) { metadata.thumbnail = thumb; }
queue.push({ queue.push({
blob: arrayBuffer, blob: file_arraybuffer,
metadata: metadata, metadata: metadata,
dropEvent: e dropEvent: e
}); });
}; };
var processFile = function () { blobToArrayBuffer(file, function (e, buffer) {
blobToArrayBuffer(file, function (e, buffer) {
finish(buffer);
});
};
if (!thumbnail) { return void processFile(); }
blobToArrayBuffer(thumbnail, function (e, buffer) {
if (e) { console.error(e); } if (e) { console.error(e); }
thumb = arrayBufferToString(buffer); file_arraybuffer = buffer;
processFile(); if (thumbnail) { // there is already a thumbnail
return blobToArrayBuffer(thumbnail, function (e, buffer) {
if (e) { console.error(e); }
thumb = arrayBufferToString(buffer);
finish();
});
}
if (!Thumb.isSupportedType(file.type)) { return finish(); }
// make a resized thumbnail from the image..
Thumb.fromImageBlob(file, function (e, thumb_blob) {
if (e) { console.error(e); }
if (!thumb_blob) { return finish(); }
blobToArrayBuffer(thumb_blob, function (e, buffer) {
if (e) {
console.error(e);
return finish();
}
thumb = arrayBufferToString(buffer);
finish();
});
});
}); });
}; };

@ -280,28 +280,13 @@ define([
}; };
}; };
var $fileIcon = $('<span>', {"class": "fa fa-file-text-o file icon"}); var $defaultIcon = $('<span>', {"class": "fa fa-file-text-o"});
var $fileAppIcon = $('<span>', {"class": "fa fa-file-text-o file icon fileColor"});
var $padIcon = $('<span>', {"class": "fa fa-file-word-o file icon padColor"});
var $codeIcon = $('<span>', {"class": "fa fa-file-code-o file icon codeColor"});
var $slideIcon = $('<span>', {"class": "fa fa-file-powerpoint-o file icon slideColor"});
var $pollIcon = $('<span>', {"class": "fa fa-calendar file icon pollColor"});
var $whiteboardIcon = $('<span>', {"class": "fa fa-paint-brush whiteboardColor"});
var $todoIcon = $('<span>', {"class": "fa fa-tasks todoColor"});
var $contactsIcon = $('<span>', {"class": "fa fa-users friendsColor"});
UI.getIcon = function (type) { UI.getIcon = function (type) {
var $icon; var $icon = $defaultIcon.clone();
switch(type) { if (AppConfig.applicationsIcon && AppConfig.applicationsIcon[type]) {
case 'pad': $icon = $padIcon.clone(); break; var appClass = ' cp-icon-color-'+type;
case 'file': $icon = $fileAppIcon.clone(); break; $icon = $('<span>', {'class': 'fa ' + AppConfig.applicationsIcon[type] + appClass});
case 'code': $icon = $codeIcon.clone(); break;
case 'slide': $icon = $slideIcon.clone(); break;
case 'poll': $icon = $pollIcon.clone(); break;
case 'whiteboard': $icon = $whiteboardIcon.clone(); break;
case 'todo': $icon = $todoIcon.clone(); break;
case 'contacts': $icon = $contactsIcon.clone(); break;
default: $icon = $fileIcon.clone();
} }
return $icon; return $icon;

@ -48,9 +48,10 @@ define([
}); });
}; };
Msg.getFriendChannelsList = function (proxy) { Msg.getFriendChannelsList = function (common) {
var list = []; var list = [];
eachFriend(proxy, function (friend) { var proxy = common.getProxy();
eachFriend(proxy.friends, function (friend) {
list.push(friend.channel); list.push(friend.channel);
}); });
return list; return list;

@ -3,7 +3,18 @@ define([
], function () { ], function () {
var Nacl = window.nacl; var Nacl = window.nacl;
var Thumb = { var Thumb = {
dimension: 150, // thumbnails are all 150px dimension: 100,
};
var supportedTypes = [
'image/png',
'image/jpeg',
'image/jpg',
'image/gif', // TODO confirm this is true
];
Thumb.isSupportedType = function (type) {
return supportedTypes.indexOf(type) !== -1;
}; };
// create thumbnail image from metadata // create thumbnail image from metadata
@ -27,22 +38,67 @@ define([
} }
}; };
var getResizedDimensions = function (img) {
var h = img.height;
var w = img.width;
var dim = Thumb.dimension;
// if the image is too small, don't bother making a thumbnail
if (h <= dim || w <= dim) { return null; }
// the image is taller than it is wide, so scale to that.
var r = dim / (h > w? h: w); // ratio
var d;
if (h > w) {
d = Math.floor(((h * r) - dim) / 2);
return {
x1: 0,
x2: dim,
y1: d,
y2: dim + d,
};
} else {
d = Math.floor(((w * r) - dim) / 2);
return {
x1: d,
x2: dim + d,
y1: 0,
y2: dim,
};
}
};
// assumes that your canvas is square // assumes that your canvas is square
// nodeback returning blob // nodeback returning blob
Thumb.fromCanvas = function (canvas, cb) { Thumb.fromCanvas = Thumb.fromImage = function (canvas, cb) {
canvas = canvas;
var c2 = document.createElement('canvas'); var c2 = document.createElement('canvas');
var d = Thumb.dimension; var D = getResizedDimensions(canvas);
c2.width = d; if (!D) { return void cb('TOO_SMALL'); }
c2.height = 2;
c2.width = Thumb.dimension;
c2.height = Thumb.dimension;
var ctx = c2.getContext('2d'); var ctx = c2.getContext('2d');
ctx.drawImage(canvas, 0, 0, d, d); ctx.drawImage(canvas, D.x1, D.y1, D.x2, D.y2);
c2.toBlob(function (blob) { c2.toBlob(function (blob) {
cb(void 0, blob); cb(void 0, blob);
}); });
}; };
Thumb.fromImageBlob = function (blob, cb) {
var url = URL.createObjectURL(blob);
var img = new Image();
img.onload = function () {
Thumb.fromImage(img, cb);
};
img.onerror = function () {
cb('ERROR');
};
img.src = url;
};
Thumb.fromVideo = function (video, cb) { Thumb.fromVideo = function (video, cb) {
cb = cb; // WIP cb = cb; // WIP
}; };

@ -80,6 +80,7 @@ define([
common.getIcon = UI.getIcon; common.getIcon = UI.getIcon;
common.addTooltips = UI.addTooltips; common.addTooltips = UI.addTooltips;
common.clearTooltips = UI.clearTooltips; common.clearTooltips = UI.clearTooltips;
common.importContent = UI.importContent;
// import common utilities for export // import common utilities for export
common.find = Util.find; common.find = Util.find;
@ -617,6 +618,16 @@ define([
}); });
common.findOKButton().text(Messages.cancelButton); common.findOKButton().text(Messages.cancelButton);
}; };
// Secure iframes
common.useTemplate = function (href, Crypt, cb) {
var parsed = parsePadUrl(href);
if(!parsed) { throw new Error("Cannot get template hash"); }
Crypt.get(parsed.hash, function (err, val) {
if (err) { throw new Error(err); }
var p = parsePadUrl(window.location.href);
Crypt.put(p.hash, val, cb);
});
};
// STORAGE // STORAGE
/* fetch and migrate your pad history from the store */ /* fetch and migrate your pad history from the store */
@ -784,7 +795,7 @@ define([
var proxy = store.getProxy(); var proxy = store.getProxy();
var fo = proxy.fo; var fo = proxy.fo;
var hashes = []; var hashes = [];
var list = fo.getFiles().filter(function (id) { var list = fo.getFiles([fo.ROOT]).filter(function (id) {
var href = fo.getFileData(id).href; var href = fo.getFileData(id).href;
var parsed = parsePadUrl(href); var parsed = parsePadUrl(href);
if ((parsed.type === 'file' || parsed.type === 'media') if ((parsed.type === 'file' || parsed.type === 'media')
@ -795,6 +806,27 @@ define([
}); });
return list; return list;
}; };
// Needed for the secure filepicker app
common.getSecureFilesList = function (filter, cb) {
var store = common.getStore();
if (!store) { return void cb("Store is not ready"); }
var proxy = store.getProxy();
var fo = proxy.fo;
var list = {};
var hashes = [];
var types = filter.types;
var where = filter.where;
fo.getFiles(where).forEach(function (id) {
var data = fo.getFileData(id);
var parsed = parsePadUrl(data.href);
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1)
&& hashes.indexOf(parsed.hash) === -1) {
hashes.push(parsed.hash);
list[id] = data;
}
});
cb (null, list);
};
var getUserChannelList = common.getUserChannelList = function () { var getUserChannelList = common.getUserChannelList = function () {
var store = common.getStore(); var store = common.getStore();
@ -1014,6 +1046,9 @@ define([
rpc.uploadCancel(cb); rpc.uploadCancel(cb);
}; };
common.uploadFileSecure = Files.upload;
/* Create a usage bar which keeps track of how much storage space is used /* Create a usage bar which keeps track of how much storage space is used
by your CryptDrive. The getPinnedUsage RPC is one of the heavier calls, by your CryptDrive. The getPinnedUsage RPC is one of the heavier calls,
so we throttle its usage. Clients will not update more than once per so we throttle its usage. Clients will not update more than once per
@ -1475,7 +1510,7 @@ define([
}; };
// This is duplicated in drive/main.js, it should be unified // This is duplicated in drive/main.js, it should be unified
var getFileIcon = function (data) { var getFileIcon = common.getFileIcon = function (data) {
var $icon = common.getIcon(); var $icon = common.getIcon();
if (!data) { return $icon; } if (!data) { return $icon; }
@ -1483,12 +1518,8 @@ define([
var href = data.href; var href = data.href;
if (!href) { return $icon; } if (!href) { return $icon; }
if (href.indexOf('/pad/') !== -1) { $icon = common.getIcon('pad'); } var type = common.parsePadUrl(href).type;
else if (href.indexOf('/code/') !== -1) { $icon = common.getIcon('code'); } $icon = common.getIcon(type);
else if (href.indexOf('/slide/') !== -1) { $icon = common.getIcon('slide'); }
else if (href.indexOf('/poll/') !== -1) { $icon = common.getIcon('poll'); }
else if (href.indexOf('/whiteboard/') !== -1) { $icon = common.getIcon('whiteboard'); }
else if (href.indexOf('/file/') !== -1) { $icon = common.getIcon('file'); }
return $icon; return $icon;
}; };
@ -1502,16 +1533,24 @@ define([
'id': cfg.id 'id': cfg.id
}); });
} }
var hide = function () {
if (cfg.onClose) { return void cfg.onClose(); }
$blockContainer.hide();
};
$blockContainer.html('').appendTo($body); $blockContainer.html('').appendTo($body);
var $block = $('<div>', {'class': 'cp-modal'}).appendTo($blockContainer); var $block = $('<div>', {'class': 'cp-modal'}).appendTo($blockContainer);
$('<span>', { $('<span>', {
'class': 'cp-modal-close fa fa-times', 'class': 'cp-modal-close fa fa-times',
'title': Messages.filePicker_close 'title': Messages.filePicker_close
}).click(function () { }).click(hide).appendTo($block);
$blockContainer.hide(); $body.click(hide);
}).appendTo($block); $block.click(function (e) {
e.stopPropagation();
});
$body.keydown(function (e) { $body.keydown(function (e) {
if (e.which === 27) { $blockContainer.hide(); } if (e.which === 27) {
hide();
}
}); });
return $blockContainer; return $blockContainer;
}; };
@ -1589,7 +1628,7 @@ define([
// Container // Container
var $container = $(config.container); var $container = $(config.container);
var containerConfig = { var containerConfig = {
'class': 'dropdown-bar' 'class': 'cp-dropdown-container'
}; };
if (config.buttonTitle) { if (config.buttonTitle) {
containerConfig.title = config.buttonTitle; containerConfig.title = config.buttonTitle;
@ -1602,14 +1641,14 @@ define([
// Button // Button
var $button = $('<button>', { var $button = $('<button>', {
'class': '' 'class': ''
}).append($('<span>', {'class': 'buttonTitle'}).html(config.text || "")); }).append($('<span>', {'class': 'cp-dropdown-button-title'}).html(config.text || ""));
/*$('<span>', { /*$('<span>', {
'class': 'fa fa-caret-down', 'class': 'fa fa-caret-down',
}).appendTo($button);*/ }).appendTo($button);*/
// Menu // Menu
var $innerblock = $('<div>', {'class': 'cryptpad-dropdown dropdown-bar-content'}); var $innerblock = $('<div>', {'class': 'cp-dropdown-content'});
if (config.left) { $innerblock.addClass('left'); } if (config.left) { $innerblock.addClass('cp-dropdown-left'); }
config.options.forEach(function (o) { config.options.forEach(function (o) {
if (!isValidOption(o)) { return; } if (!isValidOption(o)) { return; }
@ -1622,8 +1661,8 @@ define([
var setActive = function ($el) { var setActive = function ($el) {
if ($el.length !== 1) { return; } if ($el.length !== 1) { return; }
$innerblock.find('.active').removeClass('active'); $innerblock.find('.cp-dropdown-element-active').removeClass('cp-dropdown-element(active');
$el.addClass('active'); $el.addClass('cp-dropdown-element-active');
var scroll = $el.position().top + $innerblock.scrollTop(); var scroll = $el.position().top + $innerblock.scrollTop();
if (scroll < $innerblock.scrollTop()) { if (scroll < $innerblock.scrollTop()) {
$innerblock.scrollTop(scroll); $innerblock.scrollTop(scroll);
@ -1638,7 +1677,7 @@ define([
var show = function () { var show = function () {
$innerblock.show(); $innerblock.show();
$innerblock.find('.active').removeClass('active'); $innerblock.find('.cp-dropdown-element-active').removeClass('cp-dropdown-element-active');
if (config.isSelect && value) { if (config.isSelect && value) {
var $val = $innerblock.find('[data-value="'+value+'"]'); var $val = $innerblock.find('[data-value="'+value+'"]');
setActive($val); setActive($val);
@ -1650,10 +1689,10 @@ define([
$container.click(function (e) { $container.click(function (e) {
e.stopPropagation(); e.stopPropagation();
var state = $innerblock.is(':visible'); var state = $innerblock.is(':visible');
$('.dropdown-bar-content').hide(); $('.cp-dropdown-content').hide();
try { try {
$('iframe').each(function (idx, ifrw) { $('iframe').each(function (idx, ifrw) {
$(ifrw).contents().find('.dropdown-bar-content').hide(); $(ifrw).contents().find('.cp-dropdown-content').hide();
}); });
} catch (er) { } catch (er) {
// empty try catch in case this iframe is problematic (cross-origin) // empty try catch in case this iframe is problematic (cross-origin)
@ -1669,7 +1708,7 @@ define([
var pressed = ''; var pressed = '';
var to; var to;
$container.keydown(function (e) { $container.keydown(function (e) {
var $value = $innerblock.find('[data-value].active'); var $value = $innerblock.find('[data-value].cp-dropdown-element-active');
if (e.which === 38) { // Up if (e.which === 38) { // Up
if ($value.length) { if ($value.length) {
var $prev = $value.prev(); var $prev = $value.prev();
@ -1710,7 +1749,7 @@ define([
value = val; value = val;
var $val = $innerblock.find('[data-value="'+val+'"]'); var $val = $innerblock.find('[data-value="'+val+'"]');
var textValue = name || $val.html() || val; var textValue = name || $val.html() || val;
$button.find('.buttonTitle').html(textValue); $button.find('.cp-dropdown-button-title').html(textValue);
}; };
$container.getValue = function () { $container.getValue = function () {
return value || ''; return value || '';
@ -1857,7 +1896,7 @@ define([
var oldUrl; var oldUrl;
if (account && !config.static && store) { if (account && !config.static && store) {
var $avatar = $userAdmin.find('.buttonTitle'); var $avatar = $userAdmin.find('.cp-dropdown-button-title');
var updateButton = function (newName) { var updateButton = function (newName) {
var profile = store.getProfile(); var profile = store.getProfile();
var url = profile && profile.avatar; var url = profile && profile.avatar;

@ -1,47 +1,50 @@
@import (once) '../customize/src/less2/include/colortheme.less'; @import (once) '../customize/src/less2/include/colortheme.less';
@import '../customize/src/less2/include/modal.less'; @import '../customize/src/less2/include/modal.less';
#fileDialog { .fileDialog_main () {
.cp-modal { #fileDialog {
.fileContainer { display: none;
display: flex; .cp-modal {
flex-wrap: wrap; .fileContainer {
justify-content: center; display: flex;
overflow-y: auto; flex-wrap: wrap;
} justify-content: center;
overflow-y: auto;
}
.element { .element {
@darker: darken(@colortheme_modal-fg, 30%); @darker: darken(@colortheme_modal-fg, 30%);
width: 200px; width: 200px;
min-width: 200px; min-width: 200px;
height: 1em; height: 1em;
padding: 0.5em; padding: 0.5em;
margin: 5px; margin: 5px;
box-sizing: content-box; box-sizing: content-box;
text-align: left; text-align: left;
line-height: 1em; line-height: 1em;
cursor: pointer; cursor: pointer;
background-color: #111; background-color: #111;
color: @darker; color: @darker;
transition: all 0.1s; transition: all 0.1s;
&:hover { &:hover {
color: @colortheme_modal-fg; color: @colortheme_modal-fg;
} }
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
align-items: center; align-items: center;
.fa { .fa {
cursor: pointer; cursor: pointer;
margin-right: 0.5em; margin-right: 0.5em;
}
} }
} }
} }

@ -4,7 +4,8 @@ define([
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5', '/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
'/bower_components/textpatcher/TextPatcher.amd.js', '/bower_components/textpatcher/TextPatcher.amd.js',
'/common/userObject.js', '/common/userObject.js',
], function ($, Listmap, Crypto, TextPatcher, FO) { '/common/migrate-user-object.js'
], function ($, Listmap, Crypto, TextPatcher, FO, Migrate) {
/* /*
This module uses localStorage, which is synchronous, but exposes an This module uses localStorage, which is synchronous, but exposes an
asyncronous API. This is so that we can substitute other storage asyncronous API. This is so that we can substitute other storage
@ -155,6 +156,8 @@ define([
var todo = function () { var todo = function () {
fo.fixFiles(); fo.fixFiles();
Migrate(proxy, Cryptpad);
//storeObj = proxy; //storeObj = proxy;
store = initStore(fo, proxy, exp); store = initStore(fo, proxy, exp);
if (typeof(f) === 'function') { if (typeof(f) === 'function') {

File diff suppressed because one or more lines are too long

@ -73,6 +73,7 @@ define(['json.sortify'], function (Sortify) {
}); });
}; };
console.log('here register');
sframeChan.on('EV_METADATA_UPDATE', function (ev) { sframeChan.on('EV_METADATA_UPDATE', function (ev) {
meta = ev; meta = ev;
if (ev.priv) { if (ev.priv) {

@ -0,0 +1,68 @@
define([], function () {
// Start migration check
// Versions:
// 1: migrate pad attributes
// 2: migrate indent settings (codemirror)
return function (userObject, Cryptpad) {
var version = userObject.version || 0;
// Migration 1: pad attributes moved to filesData
var migrateAttributes = function () {
var files = userObject && userObject.drive;
if (!files) { return; }
var migratePadAttributes = function (el, id, parsed) {
// Migrate old pad attributes
['userid', 'previewMode'].forEach(function (attr) {
var key = parsed.hash + '.' + attr;
var key2 = parsed.hash.slice(0,-1) + '.' + attr;// old pads not ending with /
if (typeof(files[key]) !== "undefined" || typeof(files[key2]) !== "undefined") {
console.log("Migrating pad attribute", attr, "for pad", id);
el[attr] = files[key] || files[key2];
delete files[key];
delete files[key2];
}
});
};
var filesData = files.filesData;
if (!filesData) { return; }
var el, parsed;
for (var id in filesData) {
id = Number(id);
el = filesData[id];
parsed = el.href && Cryptpad.parsePadUrl(el.href);
if (!parsed) { continue; }
migratePadAttributes(el, id, parsed);
}
// Migration done
};
if (version < 1) {
migrateAttributes();
Cryptpad.feedback('Migrate-1', true);
userObject.version = version = 1;
}
// Migration 2: indentation settings for CodeMirror moved from root to 'settings'
var migrateIndent = function () {
var indentKey = 'cryptpad.indentUnit';
var useTabsKey = 'cryptpad.indentWithTabs';
userObject.settings = userObject.settings || {};
if (userObject[indentKey]) {
userObject.settings.indentUnit = userObject[indentKey];
delete userObject[indentKey];
}
if (userObject[useTabsKey]) {
userObject.settings.indentWithTabs = userObject[useTabsKey];
delete userObject[useTabsKey];
}
};
if (version < 2) {
migrateIndent();
Cryptpad.feedback('Migrate-2', true);
userObject.version = version = 2;
}
};
});

@ -109,8 +109,9 @@ define([
window.addEventListener('message', function (msg) { window.addEventListener('message', function (msg) {
var data = JSON.parse(msg.data); var data = JSON.parse(msg.data);
if (ow !== msg.source) { if (ow !== msg.source) {
console.log("DROP Message from unexpected source"); return;
console.log(msg); //console.log("DROP Message from unexpected source");
//console.log(msg);
} else if (!otherWindow) { } else if (!otherWindow) {
otherWindow = ow; otherWindow = ow;
ow.postMessage(JSON.stringify({ txid: data.txid }), '*'); ow.postMessage(JSON.stringify({ txid: data.txid }), '*');

@ -0,0 +1,302 @@
define([
'jquery',
'/file/file-crypto.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, FileCrypto) {
var Nacl = window.nacl;
var module = {};
var blobToArrayBuffer = function (blob, cb) {
var reader = new FileReader();
reader.onloadend = function () {
cb(void 0, this.result);
};
reader.readAsArrayBuffer(blob);
};
var arrayBufferToString = function (AB) {
try {
return Nacl.util.encodeBase64(new Uint8Array(AB));
} catch (e) {
console.error(e);
return null;
}
};
module.create = function (common, config) {
var File = {};
var Cryptpad = common.getCryptpadCommon();
var Messages = Cryptpad.Messages;
var queue = File.queue = {
queue: [],
inProgress: false
};
var uid = function () {
return 'cp-fileupload-element-' + String(Math.random()).substring(2);
};
var $table = File.$table = $('<table>', { id: 'cp-fileupload-table' });
var $thead = $('<tr>').appendTo($table);
$('<td>').text(Messages.upload_name).appendTo($thead);
$('<td>').text(Messages.upload_size).appendTo($thead);
$('<td>').text(Messages.upload_progress).appendTo($thead);
$('<td>').text(Messages.cancel).appendTo($thead);
var createTableContainer = function ($body) {
console.log($body);
File.$container = $('<div>', { id: 'cp-fileupload' }).append($table).appendTo($body);
console.log('done');
return File.$container;
};
var getData = function (file, href) {
var data = {};
data.name = file.metadata.name;
data.url = href;
if (file.metadata.type.slice(0,6) === 'image/') {
data.mediatag = true;
}
return data;
};
var sframeChan = common.getSframeChannel();
var onError = $.noop,
onComplete = $.noop,
updateProgress = $.noop,
onPending = $.noop;
sframeChan.on('EV_FILE_UPLOAD_STATE', function (data) {
if (data.error) {
return void onError(data.error);
}
if (data.complete && data.href) {
return void onComplete(data.href);
}
if (typeof data.progress !== "undefined") {
return void updateProgress(data.progress);
}
});
sframeChan.on('Q_CANCEL_PENDING_FILE_UPLOAD', function (data, cb) {
onPending(cb);
});
var upload = function (file) {
var blob = file.blob; // This is not a blob but an array buffer
var u8 = new Uint8Array(blob);
var metadata = file.metadata;
var id = file.id;
var dropEvent = file.dropEvent;
delete file.dropEvent;
if (queue.inProgress) { return; }
queue.inProgress = true;
var $row = $table.find('tr[id="'+id+'"]');
$row.find('.cp-fileupload-table-cancel').html('-');
var $pv = $row.find('.cp-fileupload-table-progress-value');
var $pb = $row.find('.cp-fileupload-table-progress-container');
var $pc = $row.find('.cp-fileupload-table-progress');
var $link = $row.find('.cp-fileupload-table-link');
updateProgress = function (progressValue) {
$pv.text(Math.round(progressValue*100)/100 + '%');
$pb.css({
width: (progressValue/100)*$pc.width()+'px'
});
};
onComplete = function (href) {
$link.attr('href', href)
.click(function (e) {
e.preventDefault();
window.open($link.attr('href'), '_blank');
});
var title = metadata.name;
Cryptpad.log(Messages._getKey('upload_success', [title]));
common.prepareFeedback('upload')();
if (config.onUploaded) {
var data = getData(file, href);
config.onUploaded(dropEvent, data);
}
queue.inProgress = false;
queue.next();
};
onError = function (e) {
queue.inProgress = false;
queue.next();
if (e === 'TOO_LARGE') {
// TODO update table to say too big?
return void Cryptpad.alert(Messages.upload_tooLarge);
}
if (e === 'NOT_ENOUGH_SPACE') {
// TODO update table to say not enough space?
return void Cryptpad.alert(Messages.upload_notEnoughSpace);
}
console.error(e);
return void Cryptpad.alert(Messages.upload_serverError);
};
onPending = function (cb) {
Cryptpad.confirm(Messages.upload_uploadPending, cb);
};
file.noStore = config.noStore;
try {
file.blob = Nacl.util.encodeBase64(u8);
common.uploadFile(file, function () {
console.log('Upload started...');
});
} catch (e) {
Cryptpad.alert(Messages.upload_serverError);
}
};
var prettySize = function (bytes) {
var kB = Cryptpad.bytesToKilobytes(bytes);
if (kB < 1024) { return kB + Messages.KB; }
var mB = Cryptpad.bytesToMegabytes(bytes);
return mB + Messages.MB;
};
queue.next = function () {
if (queue.queue.length === 0) {
queue.to = window.setTimeout(function () {
if (config.keepTable) { return; }
File.$container.fadeOut();
}, 3000);
return;
}
if (queue.inProgress) { return; }
// setTimeout to fix a firefox error 'NS_ERROR_NOT_AVAILABLE'
window.setTimeout(function () { File.$container.show(); });
var file = queue.queue.shift();
upload(file);
};
queue.push = function (obj) {
var id = uid();
obj.id = id;
queue.queue.push(obj);
// setTimeout to fix a firefox error 'NS_ERROR_NOT_AVAILABLE'
window.setTimeout(function () { $table.show(); });
var estimate = FileCrypto.computeEncryptedSize(obj.blob.byteLength, obj.metadata);
var $progressBar = $('<div>', {'class':'cp-fileupload-table-progress-container'});
var $progressValue = $('<span>', {'class':'cp-fileupload-table-progress-value'}).text(Messages.upload_pending);
var $tr = $('<tr>', {id: id}).appendTo($table);
var $cancel = $('<span>', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () {
queue.queue = queue.queue.filter(function (el) { return el.id !== id; });
$cancel.remove();
$tr.find('.cp-fileupload-table-cancel').text('-');
$tr.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled);
});
var $link = $('<a>', {
'class': 'cp-fileupload-table-link',
'rel': 'noopener noreferrer'
}).text(obj.metadata.name);
$('<td>').append($link).appendTo($tr);
$('<td>').text(prettySize(estimate)).appendTo($tr);
$('<td>', {'class': 'cp-fileupload-table-progress'}).append($progressBar).append($progressValue).appendTo($tr);
$('<td>', {'class': 'cp-fileupload-table-cancel'}).append($cancel).appendTo($tr);
queue.next();
};
var handleFile = File.handleFile = function (file, e, thumbnail) {
var thumb;
var finish = function (arrayBuffer) {
var metadata = {
name: file.name,
type: file.type,
};
if (thumb) { metadata.thumbnail = thumb; }
queue.push({
blob: arrayBuffer,
metadata: metadata,
dropEvent: e
});
};
var processFile = function () {
blobToArrayBuffer(file, function (e, buffer) {
finish(buffer);
});
};
if (!thumbnail) { return void processFile(); }
blobToArrayBuffer(thumbnail, function (e, buffer) {
if (e) { console.error(e); }
thumb = arrayBufferToString(buffer);
processFile();
});
};
var onFileDrop = File.onFileDrop = function (file, e) {
if (!common.isLoggedIn()) {
return Cryptpad.alert(common.Messages.upload_mustLogin);
}
Array.prototype.slice.call(file).forEach(function (d) {
handleFile(d, e);
});
};
var createAreaHandlers = File.createDropArea = function ($area, $hoverArea) {
var counter = 0;
if (!$hoverArea) { $hoverArea = $area; }
if (!$area) { return; }
$hoverArea
.on('dragenter', function (e) {
e.preventDefault();
e.stopPropagation();
counter++;
$hoverArea.addClass('cp-fileupload-hovering');
})
.on('dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
counter--;
if (counter <= 0) {
$hoverArea.removeClass('cp-fileupload-hovering');
}
});
$area
.on('drag dragstart dragend dragover drop dragenter dragleave', function (e) {
e.preventDefault();
e.stopPropagation();
})
.on('drop', function (e) {
e.stopPropagation();
var dropped = e.originalEvent.dataTransfer.files;
counter = 0;
$hoverArea.removeClass('cp-fileupload-hovering');
onFileDrop(dropped, e);
});
};
var createUploader = function ($area, $hover, $body) {
if (!config.noHandlers) {
createAreaHandlers($area, null);
}
createTableContainer($body);
};
createUploader(config.dropArea, config.hoverArea, config.body);
return File;
};
return module;
});

@ -81,9 +81,9 @@ define([
var states = []; var states = [];
var c = states.length - 1; var c = states.length - 1;
var $hist = $toolbar.find('.cryptpad-toolbar-history'); var $hist = $toolbar.find('.cp-toolbar-history');
var $left = $toolbar.find('.cryptpad-toolbar-leftside'); var $left = $toolbar.find('.cp-toolbar-leftside');
var $right = $toolbar.find('.cryptpad-toolbar-rightside'); var $right = $toolbar.find('.cp-toolbar-rightside');
var $cke = $toolbar.find('.cke_toolbox_main'); var $cke = $toolbar.find('.cke_toolbox_main');
$hist.html('').show(); $hist.html('').show();
@ -111,9 +111,9 @@ define([
var val = states[i].getContent().doc; var val = states[i].getContent().doc;
c = i; c = i;
if (typeof onUpdate === "function") { onUpdate(); } if (typeof onUpdate === "function") { onUpdate(); }
$hist.find('.next, .previous').css('visibility', ''); $hist.find('.cp-toolbar-history-next, .cp-toolbar-history-previous').css('visibility', '');
if (c === states.length - 1) { $hist.find('.next').css('visibility', 'hidden'); } if (c === states.length - 1) { $hist.find('.cp-toolbar-history-next').css('visibility', 'hidden'); }
if (c === 0) { $hist.find('.previous').css('visibility', 'hidden'); } if (c === 0) { $hist.find('.cp-toolbar-history-previous').css('visibility', 'hidden'); }
return val || ''; return val || '';
}; };
@ -128,18 +128,18 @@ define([
var display = function () { var display = function () {
$hist.html(''); $hist.html('');
var $prev =$('<button>', { var $prev =$('<button>', {
'class': 'previous fa fa-step-backward buttonPrimary', 'class': 'cp-toolbar-history-previous fa fa-step-backward buttonPrimary',
title: Messages.history_prev title: Messages.history_prev
}).appendTo($hist); }).appendTo($hist);
var $nav = $('<div>', {'class': 'goto'}).appendTo($hist); var $nav = $('<div>', {'class': 'cp-toolbar-history-goto'}).appendTo($hist);
var $next = $('<button>', { var $next = $('<button>', {
'class': 'next fa fa-step-forward buttonPrimary', 'class': 'cp-toolbar-history-next fa fa-step-forward buttonPrimary',
title: Messages.history_next title: Messages.history_next
}).appendTo($hist); }).appendTo($hist);
$('<label>').text(Messages.history_version).appendTo($nav); $('<label>').text(Messages.history_version).appendTo($nav);
var $cur = $('<input>', { var $cur = $('<input>', {
'class' : 'gotoInput', 'class' : 'cp-toolbar-history-goto-input',
'type' : 'number', 'type' : 'number',
'min' : '1', 'min' : '1',
'max' : states.length 'max' : states.length
@ -150,11 +150,11 @@ define([
var $label2 = $('<label>').text(' / '+ states.length).appendTo($nav); var $label2 = $('<label>').text(' / '+ states.length).appendTo($nav);
$('<br>').appendTo($nav); $('<br>').appendTo($nav);
var $close = $('<button>', { var $close = $('<button>', {
'class':'closeHistory', 'class':'cp-toolbar-history-close',
title: Messages.history_closeTitle title: Messages.history_closeTitle
}).text(Messages.history_closeTitle).appendTo($nav); }).text(Messages.history_closeTitle).appendTo($nav);
var $rev = $('<button>', { var $rev = $('<button>', {
'class':'revertHistory buttonSuccess', 'class':'cp-toolbar-history-revert buttonSuccess',
title: Messages.history_restoreTitle title: Messages.history_restoreTitle
}).text(Messages.history_restore).appendTo($nav); }).text(Messages.history_restore).appendTo($nav);
if (History.readOnly) { $rev.hide(); } if (History.readOnly) { $rev.hide(); }
@ -170,6 +170,7 @@ define([
$left.show(); $left.show();
$right.show(); $right.show();
$cke.show(); $cke.show();
$(window).trigger('resize');
}; };
// Buttons actions // Buttons actions
@ -203,6 +204,7 @@ define([
// Display the latest content // Display the latest content
render(get(c)); render(get(c));
$(window).trigger('resize');
}; };
// Load all the history messages into a new chainpad object // Load all the history messages into a new chainpad object

@ -39,7 +39,7 @@ define([
var MutationObserver = window.MutationObserver; var MutationObserver = window.MutationObserver;
var displayDefault = function () { var displayDefault = function () {
var text = Cryptpad.getFirstEmojiOrCharacter(name); var text = Cryptpad.getFirstEmojiOrCharacter(name);
var $avatar = $('<span>', {'class': 'default'}).text(text); var $avatar = $('<span>', {'class': 'cp-avatar-default'}).text(text);
$container.append($avatar); $container.append($avatar);
if (cb) { cb(); } if (cb) { cb(); }
}; };
@ -207,7 +207,7 @@ define([
var $displayName = $userAdmin.find('.'+displayNameCls); var $displayName = $userAdmin.find('.'+displayNameCls);
var $avatar = $userAdmin.find('.buttonTitle'); var $avatar = $userAdmin.find('.cp-dropdown-button-title');
var oldUrl; var oldUrl;
var updateButton = function () { var updateButton = function () {
var myData = metadataMgr.getUserData(); var myData = metadataMgr.getUserData();
@ -220,7 +220,7 @@ define([
UI.displayAvatar(Common, $avatar, url, newName, function ($img) { UI.displayAvatar(Common, $avatar, url, newName, function ($img) {
oldUrl = url; oldUrl = url;
if ($img) { if ($img) {
$userAdmin.find('button').addClass('avatar'); $userAdmin.find('button').addClass('cp-avatar');
} }
}); });
} }
@ -261,5 +261,43 @@ define([
return $userAdmin; return $userAdmin;
}; };
UI.initFilePicker = function (common, cfg) {
var onSelect = cfg.onSelect || $.noop;
var sframeChan = common.getSframeChannel();
sframeChan.on("EV_FILE_PICKED", function (data) {
onSelect(data);
});
};
UI.openFilePicker = function (common, types) {
var sframeChan = common.getSframeChannel();
sframeChan.event("EV_FILE_PICKER_OPEN", types);
};
UI.openTemplatePicker = function (common) {
var metadataMgr = common.getMetadataMgr();
var type = metadataMgr.getMetadataLazy().type;
var first = true; // We can only pick a template once (for a new document)
var fileDialogCfg = {
onSelect: function (data) {
if (data.type === type && first) {
Cryptpad.addLoadingScreen(null, true);
var sframeChan = common.getSframeChannel();
sframeChan.query('Q_TEMPLATE_USE', data.href, function () {
first = false;
Cryptpad.removeLoadingScreen();
common.feedback('TEMPLATE_USED');
});
return;
}
}
};
common.initFilePicker(common, fileDialogCfg);
var pickerCfg = {
types: [type],
where: ['template']
};
common.openFilePicker(common, pickerCfg);
};
return UI; return UI;
}); });

@ -0,0 +1,302 @@
// Load #1, load as little as possible because we are in a race to get the loading screen up.
define([
'/bower_components/nthen/index.js',
'/api/config',
'jquery',
], function (nThen, ApiConfig, $) {
var common = {};
common.start = function () {
var secret;
var hashes;
var CpNfOuter;
var Cryptpad;
var Crypto;
var Cryptget;
var sframeChan;
var FilePicker;
nThen(function (waitFor) {
// Load #2, the loading screen is up so grab whatever you need...
require([
'/common/sframe-chainpad-netflux-outer.js',
'/common/cryptpad-common.js',
'/bower_components/chainpad-crypto/crypto.js',
'/common/cryptget.js',
'/common/sframe-channel.js',
'/filepicker/main.js',
], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, SFrameChannel,
_FilePicker) {
CpNfOuter = _CpNfOuter;
Cryptpad = _Cryptpad;
Crypto = _Crypto;
Cryptget = _Cryptget;
FilePicker = _FilePicker;
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
sframeChan = sfc;
}));
Cryptpad.ready(waitFor());
}));
}).nThen(function (waitFor) {
secret = Cryptpad.getSecrets();
if (!secret.channel) {
// New pad: create a new random channel id
secret.channel = Cryptpad.createChannelId();
}
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
}).nThen(function () {
var readOnly = secret.keys && !secret.keys.editKeyStr;
if (!secret.keys) { secret.keys = secret.key; }
var parsed = Cryptpad.parsePadUrl(window.location.href);
if (!parsed.type) { throw new Error(); }
var defaultTitle = Cryptpad.getDefaultName(parsed);
var proxy = Cryptpad.getProxy();
var updateMeta = function () {
//console.log('EV_METADATA_UPDATE');
var name;
nThen(function (waitFor) {
Cryptpad.getLastName(waitFor(function (err, n) {
if (err) { console.log(err); }
name = n;
}));
}).nThen(function (/*waitFor*/) {
sframeChan.event('EV_METADATA_UPDATE', {
doc: {
defaultTitle: defaultTitle,
type: parsed.type
},
user: {
name: name,
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: proxy.curvePublic,
netfluxId: Cryptpad.getNetwork().webChannels[0].myID,
},
priv: {
accountName: Cryptpad.getAccountName(),
origin: window.location.origin,
pathname: window.location.pathname,
readOnly: readOnly,
availableHashes: hashes,
isTemplate: Cryptpad.isTemplate(window.location.href),
feedbackAllowed: Cryptpad.isFeedbackAllowed(),
friends: proxy.friends || {},
settings: proxy.settings || {}
}
});
});
};
Cryptpad.onDisplayNameChanged(updateMeta);
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
proxy.on('change', 'settings', updateMeta);
Cryptpad.onError(function (info) {
console.log('error');
console.log(info);
if (info && info.type === "store") {
//onConnectError();
}
});
sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) {
Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) {
cb({error: err, response: response});
});
});
sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) {
document.title = newTitle;
Cryptpad.renamePad(newTitle, undefined, function (err) {
if (err) { cb('ERROR'); } else { cb(); }
});
});
sframeChan.on('Q_SETTINGS_SET_DISPLAY_NAME', function (newName, cb) {
Cryptpad.setAttribute('username', newName, function (err) {
if (err) {
console.log("Couldn't set username");
console.error(err);
cb('ERROR');
return;
}
Cryptpad.changeDisplayName(newName, true);
cb();
});
});
sframeChan.on('Q_LOGOUT', function (data, cb) {
Cryptpad.logout(cb);
});
sframeChan.on('EV_NOTIFY', function () {
Cryptpad.notify();
});
sframeChan.on('Q_SET_LOGIN_REDIRECT', function (data, cb) {
sessionStorage.redirectTo = window.location.href;
cb();
});
sframeChan.on('Q_GET_PIN_LIMIT_STATUS', function (data, cb) {
Cryptpad.isOverPinLimit(function (e, overLimit, limits) {
cb({
error: e,
overLimit: overLimit,
limits: limits
});
});
});
sframeChan.on('Q_MOVE_TO_TRASH', function (data, cb) {
Cryptpad.moveToTrash(cb);
});
sframeChan.on('Q_SAVE_AS_TEMPLATE', function (data, cb) {
Cryptpad.saveAsTemplate(Cryptget.put, data, cb);
});
sframeChan.on('Q_SEND_FRIEND_REQUEST', function (netfluxId, cb) {
Cryptpad.inviteFromUserlist(Cryptpad, netfluxId);
cb();
});
Cryptpad.onFriendRequest = function (confirmText, cb) {
sframeChan.query('Q_INCOMING_FRIEND_REQUEST', confirmText, function (err, data) {
cb(data);
});
};
Cryptpad.onFriendComplete = function (data) {
sframeChan.event('EV_FRIEND_REQUEST', data);
};
sframeChan.on('Q_GET_FULL_HISTORY', function (data, cb) {
var network = Cryptpad.getNetwork();
var hkn = network.historyKeeper;
var crypto = Crypto.createEncryptor(secret.keys);
// Get the history messages and send them to the iframe
var parse = function (msg) {
try {
return JSON.parse(msg);
} catch (e) {
return null;
}
};
var onMsg = function (msg) {
var parsed = parse(msg);
if (parsed[0] === 'FULL_HISTORY_END') {
console.log('END');
cb();
return;
}
if (parsed[0] !== 'FULL_HISTORY') { return; }
if (parsed[1] && parsed[1].validateKey) { // First message
secret.keys.validateKey = parsed[1].validateKey;
return;
}
msg = parsed[1][4];
if (msg) {
msg = msg.replace(/^cp\|/, '');
var decryptedMsg = crypto.decrypt(msg, secret.keys.validateKey);
sframeChan.event('EV_RT_HIST_MESSAGE', decryptedMsg);
}
};
network.on('message', onMsg);
network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', secret.channel, secret.keys.validateKey]));
});
sframeChan.on('Q_GET_PAD_ATTRIBUTE', function (data, cb) {
Cryptpad.getPadAttribute(data.key, function (e, data) {
cb({
error: e,
data: data
});
});
});
sframeChan.on('Q_SET_PAD_ATTRIBUTE', function (data, cb) {
Cryptpad.setPadAttribute(data.key, data.value, function (e) {
cb({error:e});
});
});
var onFileUpload = function (sframeChan, data, cb) {
var sendEvent = function (data) {
sframeChan.event("EV_FILE_UPLOAD_STATE", data);
};
var updateProgress = function (progressValue) {
sendEvent({
progress: progressValue
});
};
var onComplete = function (href) {
sendEvent({
complete: true,
href: href
});
};
var onError = function (e) {
sendEvent({
error: e
});
};
var onPending = function (cb) {
sframeChan.query('Q_CANCEL_PENDING_FILE_UPLOAD', null, function (err, data) {
if (data) {
cb();
}
});
};
data.blob = Crypto.Nacl.util.decodeBase64(data.blob);
Cryptpad.uploadFileSecure(data, data.noStore, Cryptpad, updateProgress, onComplete, onError, onPending);
cb();
};
sframeChan.on('Q_UPLOAD_FILE', function (data, cb) {
onFileUpload(sframeChan, data, cb);
});
var FP = {};
var initFilePicker = function (types) {
var config = {};
config.onFilePicked = function (data) {
sframeChan.event('EV_FILE_PICKED', data);
};
config.onClose = function () {
FP.$iframe.hide();
};
config.onFileUpload = onFileUpload;
config.types = types;
if (!FP.$iframe) {
FP.$iframe = $('<iframe>', {id: 'sbox-filePicker-iframe'}).appendTo($('body'));
FP.picker = FilePicker.create(config);
} else {
FP.$iframe.show();
FP.picker.refresh(types);
}
};
sframeChan.on('EV_FILE_PICKER_OPEN', function (data) {
initFilePicker(data);
});
sframeChan.on('Q_TEMPLATE_USE', function (href, cb) {
Cryptpad.useTemplate(href, Cryptget, cb);
});
CpNfOuter.start({
sframeChan: sframeChan,
channel: secret.channel,
network: Cryptpad.getNetwork(),
validateKey: secret.keys.validateKey || undefined,
readOnly: readOnly,
crypto: Crypto.createEncryptor(secret.keys),
onConnect: function (wc) {
if (readOnly) { return; }
Cryptpad.replaceHash(Cryptpad.getEditHashFromKeys(wc.id, secret.keys));
}
});
Cryptpad.reportAppUsage();
});
};
return common;
});

@ -62,7 +62,7 @@ define(['jquery'], function ($) {
metadataMgr.onChange(function () { metadataMgr.onChange(function () {
var md = metadataMgr.getMetadata(); var md = metadataMgr.getMetadata();
$title.find('span.title').text(md.title || md.defaultTitle); $title.find('span.cp-toolbar-title-value').text(md.title || md.defaultTitle);
$title.find('input').val(md.title || md.defaultTitle); $title.find('input').val(md.title || md.defaultTitle);
exp.title = md.title; exp.title = md.title;
//exp.updateTitle(md.title || md.defaultTitle); //exp.updateTitle(md.title || md.defaultTitle);

@ -7,12 +7,13 @@ define([
'/common/sframe-common-title.js', '/common/sframe-common-title.js',
'/common/sframe-common-interface.js', '/common/sframe-common-interface.js',
'/common/sframe-common-history.js', '/common/sframe-common-history.js',
'/common/sframe-common-file.js',
'/common/metadata-manager.js', '/common/metadata-manager.js',
'/customize/application_config.js', '/customize/application_config.js',
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/common-realtime.js' '/common/common-realtime.js'
], function ($, nThen, Messages, CpNfInner, SFrameChannel, Title, UI, History, MetadataMgr, ], function ($, nThen, Messages, CpNfInner, SFrameChannel, Title, UI, History, File, MetadataMgr,
AppConfig, Cryptpad, CommonRealtime) { AppConfig, Cryptpad, CommonRealtime) {
// Chainpad Netflux Inner // Chainpad Netflux Inner
@ -36,6 +37,9 @@ define([
funcs.getCryptpadCommon = function () { funcs.getCryptpadCommon = function () {
return Cryptpad; return Cryptpad;
}; };
funcs.getSframeChannel = function () {
return ctx.sframeChan;
};
var isLoggedIn = funcs.isLoggedIn = function () { var isLoggedIn = funcs.isLoggedIn = function () {
if (!ctx.cpNfInner) { throw new Error("cpNfInner is not ready!"); } if (!ctx.cpNfInner) { throw new Error("cpNfInner is not ready!"); }
@ -51,6 +55,9 @@ define([
// UI // UI
funcs.createUserAdminMenu = UI.createUserAdminMenu; funcs.createUserAdminMenu = UI.createUserAdminMenu;
funcs.displayAvatar = UI.displayAvatar; funcs.displayAvatar = UI.displayAvatar;
funcs.initFilePicker = UI.initFilePicker;
funcs.openFilePicker = UI.openFilePicker;
funcs.openTemplatePicker = UI.openTemplatePicker;
// History // History
funcs.getHistory = function (config) { return History.create(funcs, config); }; funcs.getHistory = function (config) { return History.create(funcs, config); };
@ -107,6 +114,26 @@ define([
ctx.sframeChan.query('Q_GET_FULL_HISTORY', null, cb); ctx.sframeChan.query('Q_GET_FULL_HISTORY', null, cb);
}; };
funcs.getPadAttribute = function (key, cb) {
ctx.sframeChan.query('Q_GET_PAD_ATTRIBUTE', {
key: key
}, function (err, res) {
cb (err || res.error, res.data);
});
};
funcs.setPadAttribute = function (key, value, cb) {
ctx.sframeChan.query('Q_SET_PAD_ATTRIBUTE', {
key: key,
value: value
}, cb);
};
// Files
funcs.uploadFile = function (data, cb) {
ctx.sframeChan.query('Q_UPLOAD_FILE', data, cb);
};
funcs.createFileManager = function (config) { return File.create(funcs, config); };
// Friends // Friends
var pendingFriends = []; var pendingFriends = [];
funcs.getPendingFriends = function () { funcs.getPendingFriends = function () {
@ -133,7 +160,7 @@ define([
url: href, url: href,
}); });
}; };
var prepareFeedback = function (key) { var prepareFeedback = funcs.prepareFeedback = function (key) {
if (typeof(key) !== 'string') { return $.noop; } if (typeof(key) !== 'string') { return $.noop; }
var type = ctx.metadataMgr.getMetadata().type; var type = ctx.metadataMgr.getMetadata().type;
@ -155,7 +182,7 @@ define([
button = $('<button>', { button = $('<button>', {
'class': 'fa fa-download', 'class': 'fa fa-download',
title: Messages.exportButtonTitle, title: Messages.exportButtonTitle,
}).append($('<span>', {'class': 'drawer'}).text(Messages.exportButton)); }).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.exportButton));
button.click(prepareFeedback(type)); button.click(prepareFeedback(type));
if (callback) { if (callback) {
@ -166,7 +193,7 @@ define([
button = $('<button>', { button = $('<button>', {
'class': 'fa fa-upload', 'class': 'fa fa-upload',
title: Messages.importButtonTitle, title: Messages.importButtonTitle,
}).append($('<span>', {'class': 'drawer'}).text(Messages.importButton)); }).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.importButton));
if (callback) { if (callback) {
button button
.click(prepareFeedback(type)) .click(prepareFeedback(type))
@ -175,6 +202,30 @@ define([
}, {accept: data ? data.accept : undefined})); }, {accept: data ? data.accept : undefined}));
} }
break; break;
case 'upload':
button = $('<button>', {
'class': 'btn btn-primary new',
title: Messages.uploadButtonTitle,
}).append($('<span>', {'class':'fa fa-upload'})).append(' '+Messages.uploadButton);
if (!data.FM) { return; }
var $input = $('<input>', {
'type': 'file',
'style': 'display: none;'
}).on('change', function (e) {
var file = e.target.files[0];
var ev = {
target: data.target
};
if (data.filter && !data.filter(file)) {
Cryptpad.log('TODO: invalid avatar (type or size)');
return;
}
data.FM.handleFile(file, ev);
if (callback) { callback(); }
});
if (data.accept) { $input.attr('accept', data.accept); }
button.click(function () { $input.click(); });
break;
case 'template': case 'template':
if (!AppConfig.enableTemplates) { return; } if (!AppConfig.enableTemplates) { return; }
button = $('<button>', { button = $('<button>', {
@ -230,7 +281,7 @@ define([
style: 'font:'+size+' FontAwesome' style: 'font:'+size+' FontAwesome'
}); });
if (!isStrongestStored()) { if (!isStrongestStored()) {
button.addClass('hidden'); button.addClass('cp-toolbar-hidden');
} }
if (callback) { if (callback) {
button button
@ -259,7 +310,7 @@ define([
button = $('<button>', { button = $('<button>', {
title: Messages.historyButton, title: Messages.historyButton,
'class': "fa fa-history history", 'class': "fa fa-history history",
}).append($('<span>', {'class': 'drawer'}).text(Messages.historyText)); }).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.historyText));
if (data.histConfig) { if (data.histConfig) {
button button
.click(prepareFeedback(type)) .click(prepareFeedback(type))
@ -271,7 +322,7 @@ define([
case 'more': case 'more':
button = $('<button>', { button = $('<button>', {
title: Messages.moreActions || 'TODO', title: Messages.moreActions || 'TODO',
'class': "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;
@ -283,11 +334,19 @@ define([
.click(prepareFeedback(type)); .click(prepareFeedback(type));
} }
if (rightside) { if (rightside) {
button.addClass('rightside-button'); button.addClass('cp-toolbar-rightside-button');
} }
return button; return button;
};
// Can, only be called by the filepicker app
funcs.getFilesList = function (types, cb) {
ctx.sframeChan.query('Q_GET_FILES_LIST', types, function (err, data) {
cb(err || data.error, data.data);
});
}; };
/* funcs.storeLinkToClipboard = function (readOnly, cb) { /* funcs.storeLinkToClipboard = function (readOnly, cb) {
ctx.sframeChan.query('Q_STORE_LINK_TO_CLIPBOARD', readOnly, function (err) { ctx.sframeChan.query('Q_STORE_LINK_TO_CLIPBOARD', readOnly, function (err) {
if (cb) { cb(err); } if (cb) { cb(err); }

@ -82,4 +82,29 @@ define({
// Set the tab notification when the content of the pad changes // Set the tab notification when the content of the pad changes
'EV_NOTIFY': true, 'EV_NOTIFY': true,
// Send the new settings to the inner iframe when they are changed in the proxy
'EV_SETTINGS_UPDATE': true,
// Get and set pad attributes stored in the drive from the inner iframe
'Q_GET_PAD_ATTRIBUTE': true,
'Q_SET_PAD_ATTRIBUTE': true,
// Open/close the File picker (sent from the iframe to the outside)
'EV_FILE_PICKER_OPEN': true,
'EV_FILE_PICKER_CLOSE': true,
'EV_FILE_PICKER_REFRESH': true,
// File selected in the file picker: sent from the filepicker iframe to the outside
// and then send to the inner iframe
'EV_FILE_PICKED': true,
// Get all the files from the drive to display them in a file picker secure app
'Q_GET_FILES_LIST': true,
// Template picked, replace the content of the pad
'Q_TEMPLATE_USE': true,
// File upload queries and events
'Q_UPLOAD_FILE': true,
'EV_FILE_UPLOAD_STATE': true,
'Q_CANCEL_PENDING_FILE_UPLOAD': true,
}); });

@ -64,8 +64,8 @@ define([
'class': USER_CLS 'class': USER_CLS
}).appendTo($topContainer); }).appendTo($topContainer);
$('<span>', {'class': LIMIT_CLS}).hide().appendTo($userContainer); $('<span>', {'class': LIMIT_CLS}).hide().appendTo($userContainer);
$('<span>', {'class': NEWPAD_CLS + ' dropdown-bar'}).hide().appendTo($userContainer); $('<span>', {'class': NEWPAD_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);
$('<span>', {'class': USERADMIN_CLS + ' dropdown-bar'}).hide().appendTo($userContainer); $('<span>', {'class': USERADMIN_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);
$toolbar.append($topContainer) $toolbar.append($topContainer)
.append($('<div>', {'class': LEFTSIDE_CLS})) .append($('<div>', {'class': LEFTSIDE_CLS}))
@ -75,7 +75,7 @@ define([
var $rightside = $toolbar.find('.'+RIGHTSIDE_CLS); var $rightside = $toolbar.find('.'+RIGHTSIDE_CLS);
if (!config.hideDrawer) { if (!config.hideDrawer) {
var $drawerContent = $('<div>', { var $drawerContent = $('<div>', {
'class': DRAWER_CLS,// + ' dropdown-bar-content cryptpad-dropdown' 'class': DRAWER_CLS,
'tabindex': 1 'tabindex': 1
}).appendTo($rightside).hide(); }).appendTo($rightside).hide();
var $drawer = Cryptpad.createButton('more', true).appendTo($rightside); var $drawer = Cryptpad.createButton('more', true).appendTo($rightside);
@ -270,7 +270,7 @@ define([
var fa_editusers = '<span class="fa fa-users"></span>'; var fa_editusers = '<span class="fa fa-users"></span>';
var fa_viewusers = '<span class="fa fa-eye"></span>'; var fa_viewusers = '<span class="fa fa-eye"></span>';
var $spansmall = $('<span>').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers); var $spansmall = $('<span>').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers);
$userButtons.find('.buttonTitle').html('').append($spansmall); $userButtons.find('.cp-dropdown-button-title').html('').append($spansmall);
updateDisplayName(toolbar, config); updateDisplayName(toolbar, config);
}; };
@ -311,7 +311,7 @@ define([
var $container = $('<span>', {id: 'userButtons', title: Messages.userListButton}); var $container = $('<span>', {id: 'userButtons', title: Messages.userListButton});
var $button = $('<button>').appendTo($container); var $button = $('<button>').appendTo($container);
$('<span>',{'class': 'buttonTitle'}).appendTo($button); $('<span>',{'class': 'cp-dropdown-button-title'}).appendTo($button);
toolbar.$leftside.prepend($container); toolbar.$leftside.prepend($container);
@ -444,7 +444,7 @@ define([
}; };
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare); var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
//$shareBlock.find('button').attr('id', 'shareButton'); //$shareBlock.find('button').attr('id', 'shareButton');
$shareBlock.find('.dropdown-bar-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS); $shareBlock.find('.cp-dropdown-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
$shareBlock.addClass('shareButton'); $shareBlock.addClass('shareButton');
$shareBlock.find('button').attr('title', Messages.shareButton); $shareBlock.find('button').attr('title', Messages.shareButton);
@ -731,9 +731,6 @@ define([
}; };
var createNewPad = function (toolbar) { var createNewPad = function (toolbar) {
/*var $newPad = $('<span>', {
'class': NEWPAD_CLS + " dropdown-bar"
}).appendTo(toolbar.$top);*/
var $newPad = toolbar.$top.find('.'+NEWPAD_CLS).show(); var $newPad = toolbar.$top.find('.'+NEWPAD_CLS).show();
var pads_options = []; var pads_options = [];
@ -822,7 +819,7 @@ define([
// Events // Events
var initClickEvents = function (toolbar, config) { var initClickEvents = function (toolbar, config) {
var removeDropdowns = function () { var removeDropdowns = function () {
toolbar.$toolbar.find('.cryptpad-dropdown').hide(); toolbar.$toolbar.find('.cp-dropdown-content').hide();
}; };
var cancelEditTitle = function (e) { var cancelEditTitle = function (e) {
// Now we want to apply the title even if we click somewhere else // Now we want to apply the title even if we click somewhere else

@ -14,36 +14,37 @@ define([
var SPINNER_DISAPPEAR_TIME = 1000; var SPINNER_DISAPPEAR_TIME = 1000;
// Toolbar parts // Toolbar parts
var TOOLBAR_CLS = Bar.constants.toolbar = 'cryptpad-toolbar'; var TOOLBAR_CLS = Bar.constants.toolbar = 'cp-toolbar';
var TOP_CLS = Bar.constants.top = 'cryptpad-toolbar-top'; var TOP_CLS = Bar.constants.top = 'cp-toolbar-top';
var LEFTSIDE_CLS = Bar.constants.leftside = 'cryptpad-toolbar-leftside'; var LEFTSIDE_CLS = Bar.constants.leftside = 'cp-toolbar-leftside';
var RIGHTSIDE_CLS = Bar.constants.rightside = 'cryptpad-toolbar-rightside'; var RIGHTSIDE_CLS = Bar.constants.rightside = 'cp-toolbar-rightside';
var DRAWER_CLS = Bar.constants.drawer = 'drawer-content'; var DRAWER_CLS = Bar.constants.drawer = 'cp-toolbar-drawer-content';
var HISTORY_CLS = Bar.constants.history = 'cryptpad-toolbar-history'; var HISTORY_CLS = Bar.constants.history = 'cp-toolbar-history';
// Userlist // Userlist
var USERLIST_CLS = Bar.constants.userlist = "cryptpad-dropdown-users"; var USERLIST_CLS = Bar.constants.userlist = "cp-toolbar-users";
var EDITSHARE_CLS = Bar.constants.editShare = "cryptpad-dropdown-editShare"; var EDITSHARE_CLS = Bar.constants.editShare = "cp-toolbar-share-edit";
var VIEWSHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-viewShare"; var VIEWSHARE_CLS = Bar.constants.viewShare = "cp-toolbar-share-view";
var SHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-share"; var SHARE_CLS = Bar.constants.viewShare = "cp-toolbar-share";
// Top parts // Top parts
var USER_CLS = Bar.constants.userAdmin = "cryptpad-user"; var USER_CLS = Bar.constants.userAdmin = "cp-toolbar-user";
var SPINNER_CLS = Bar.constants.spinner = 'cryptpad-spinner'; var SPINNER_CLS = Bar.constants.spinner = 'cp-toolbar-spinner';
var LIMIT_CLS = Bar.constants.lag = 'cryptpad-limit'; var LIMIT_CLS = Bar.constants.limit = 'cp-toolbar-limit';
var TITLE_CLS = Bar.constants.title = "cryptpad-title"; var TITLE_CLS = Bar.constants.title = "cp-toolbar-title";
var NEWPAD_CLS = Bar.constants.newpad = "cryptpad-new"; var NEWPAD_CLS = Bar.constants.newpad = "cp-toolbar-new";
var LINK_CLS = Bar.constants.link = "cp-toolbar-link";
// User admin menu // User admin menu
var USERADMIN_CLS = Bar.constants.user = 'cryptpad-user-dropdown'; var USERADMIN_CLS = Bar.constants.user = 'cp-toolbar-user-dropdown';
var USERNAME_CLS = Bar.constants.username = 'cryptpad-toolbar-username'; var USERNAME_CLS = Bar.constants.username = 'cp-toolbar-user-name';
/*var READONLY_CLS = */Bar.constants.readonly = 'cryptpad-readonly'; /*var READONLY_CLS = */Bar.constants.readonly = 'cp-toolbar-readonly';
var USERBUTTON_CLS = Bar.constants.changeUsername = "cryptpad-change-username"; var USERBUTTON_CLS = Bar.constants.changeUsername = "cp-toolbar-user-rename";
// Create the toolbar element // Create the toolbar element
var uid = function () { var uid = function () {
return 'cryptpad-uid-' + String(Math.random()).substring(2); return 'cp-toolbar-uid-' + String(Math.random()).substring(2);
}; };
var createRealtimeToolbar = function (config) { var createRealtimeToolbar = function (config) {
@ -54,21 +55,14 @@ define([
id: uid(), id: uid(),
}); });
// TODO iframe
// var parsed = Cryptpad.parsePadUrl(window.location.href);
var parsed = { type:'pad' };
if (typeof parsed.type === "string") {
config.$container.parents('body').addClass('app-' + parsed.type);
}
var $topContainer = $('<div>', {'class': TOP_CLS}); var $topContainer = $('<div>', {'class': TOP_CLS});
$('<span>', {'class': 'filler'}).appendTo($topContainer); $('<span>', {'class': 'cp-toolbar-top-filler'}).appendTo($topContainer);
var $userContainer = $('<span>', { var $userContainer = $('<span>', {
'class': USER_CLS 'class': USER_CLS
}).appendTo($topContainer); }).appendTo($topContainer);
$('<span>', {'class': LIMIT_CLS}).hide().appendTo($userContainer); $('<span>', {'class': LIMIT_CLS}).hide().appendTo($userContainer);
$('<span>', {'class': NEWPAD_CLS + ' dropdown-bar'}).hide().appendTo($userContainer); $('<span>', {'class': NEWPAD_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);
$('<span>', {'class': USERADMIN_CLS + ' dropdown-bar'}).hide().appendTo($userContainer); $('<span>', {'class': USERADMIN_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);
$toolbar.append($topContainer) $toolbar.append($topContainer)
.append($('<div>', {'class': LEFTSIDE_CLS})) .append($('<div>', {'class': LEFTSIDE_CLS}))
@ -78,27 +72,27 @@ define([
var $rightside = $toolbar.find('.'+RIGHTSIDE_CLS); var $rightside = $toolbar.find('.'+RIGHTSIDE_CLS);
if (!config.hideDrawer) { if (!config.hideDrawer) {
var $drawerContent = $('<div>', { var $drawerContent = $('<div>', {
'class': DRAWER_CLS,// + ' dropdown-bar-content cryptpad-dropdown' 'class': DRAWER_CLS,
'tabindex': 1 'tabindex': 1
}).appendTo($rightside).hide(); }).appendTo($rightside).hide();
var $drawer = Cryptpad.createButton('more', true).appendTo($rightside); var $drawer = Common.createButton('more', true).appendTo($rightside);
$drawer.click(function () { $drawer.click(function () {
$drawerContent.toggle(); $drawerContent.toggle();
$drawer.removeClass('active'); $drawer.removeClass('cp-toolbar-button-active');
if ($drawerContent.is(':visible')) { if ($drawerContent.is(':visible')) {
$drawer.addClass('active'); $drawer.addClass('cp-toolbar-button-active');
$drawerContent.focus(); $drawerContent.focus();
} }
}); });
var onBlur = function (e) { var onBlur = function (e) {
if (e.relatedTarget) { if (e.relatedTarget) {
if ($(e.relatedTarget).is('.drawer-button')) { return; } if ($(e.relatedTarget).is('.cp-toolbar-drawer-button')) { return; }
if ($(e.relatedTarget).parents('.'+DRAWER_CLS).length) { if ($(e.relatedTarget).parents('.'+DRAWER_CLS).length) {
$(e.relatedTarget).blur(onBlur); $(e.relatedTarget).blur(onBlur);
return; return;
} }
} }
$drawer.removeClass('active'); $drawer.removeClass('cp-toolbar-button-active');
$drawerContent.hide(); $drawerContent.hide();
}; };
$drawerContent.blur(onBlur); $drawerContent.blur(onBlur);
@ -106,7 +100,7 @@ define([
// The 'notitle' class removes the line added for the title with a small screen // The 'notitle' class removes the line added for the title with a small screen
if (!config.title || typeof config.title !== "object") { if (!config.title || typeof config.title !== "object") {
$toolbar.addClass('notitle'); $toolbar.addClass('cp-toolbar-notitle');
} }
$container.prepend($toolbar); $container.prepend($toolbar);
@ -185,15 +179,15 @@ define([
var $editUsers = $userlistContent.find('.' + USERLIST_CLS).html(''); var $editUsers = $userlistContent.find('.' + USERLIST_CLS).html('');
Cryptpad.clearTooltips(); Cryptpad.clearTooltips();
var $editUsersList = $('<div>', {'class': 'userlist-others'}); var $editUsersList = $('<div>', {'class': 'cp-toolbar-userlist-others'});
// Editors // Editors
var pendingFriends = Common.getPendingFriends(); var pendingFriends = Common.getPendingFriends();
editUsersNames.forEach(function (data) { editUsersNames.forEach(function (data) {
var name = data.name || Messages.anonymous; var name = data.name || Messages.anonymous;
var $span = $('<span>', {'class': 'avatar'}); var $span = $('<span>', {'class': 'cp-avatar'});
var $rightCol = $('<span>', {'class': 'right-col'}); var $rightCol = $('<span>', {'class': 'cp-toolbar-userlist-rightcol'});
var $nameSpan = $('<span>', {'class': 'name'}).text(name).appendTo($rightCol); var $nameSpan = $('<span>', {'class': 'cp-toolbar-userlist-name'}).text(name).appendTo($rightCol);
var isMe = data.curvePublic === user.curvePublic; var isMe = data.curvePublic === user.curvePublic;
if (Common.isLoggedIn() && data.curvePublic) { if (Common.isLoggedIn() && data.curvePublic) {
if (isMe) { if (isMe) {
@ -203,11 +197,11 @@ define([
$nameSpan.text(name); $nameSpan.text(name);
} else if (!friends[data.curvePublic]) { } else if (!friends[data.curvePublic]) {
if (pendingFriends.indexOf(data.netfluxId) !== -1) { if (pendingFriends.indexOf(data.netfluxId) !== -1) {
$('<span>', {'class': 'friend'}).text(Messages.userlist_pending) $('<span>', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending)
.appendTo($rightCol); .appendTo($rightCol);
} else { } else {
$('<span>', { $('<span>', {
'class': 'fa fa-user-plus friend', 'class': 'fa fa-user-plus cp-toolbar-userlist-friend',
'title': Messages._getKey('userlist_addAsFriendTitle', [ 'title': Messages._getKey('userlist_addAsFriendTitle', [
name name
]) ])
@ -219,7 +213,7 @@ define([
} }
} }
if (data.profile) { if (data.profile) {
$span.addClass('clickable'); $span.addClass('cp-userlist-clickable');
$span.click(function () { $span.click(function () {
window.open(origin+'/profile/#' + data.profile); window.open(origin+'/profile/#' + data.profile);
}); });
@ -242,7 +236,7 @@ define([
// Viewers // Viewers
if (numberOfViewUsers > 0) { if (numberOfViewUsers > 0) {
var viewText = '<div class="viewer">'; var viewText = '<div class="cp-toolbar-userlist-viewer">';
var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer; var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
viewText += numberOfViewUsers + ' ' + viewerText + '</div>'; viewText += numberOfViewUsers + ' ' + viewerText + '</div>';
$editUsers.append(viewText); $editUsers.append(viewText);
@ -252,7 +246,7 @@ define([
var fa_editusers = '<span class="fa fa-users"></span>'; var fa_editusers = '<span class="fa fa-users"></span>';
var fa_viewusers = '<span class="fa fa-eye"></span>'; var fa_viewusers = '<span class="fa fa-eye"></span>';
var $spansmall = $('<span>').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers); var $spansmall = $('<span>').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers);
$userButtons.find('.buttonTitle').html('').append($spansmall); $userButtons.find('.cp-dropdown-button-title').html('').append($spansmall);
}; };
var initUserList = function (toolbar, config) { var initUserList = function (toolbar, config) {
@ -278,21 +272,21 @@ define([
if (!config.metadataMgr) { if (!config.metadataMgr) {
throw new Error("You must provide a `metadataMgr` to display the userlist"); throw new Error("You must provide a `metadataMgr` to display the userlist");
} }
var $content = $('<div>', {'class': 'userlist-drawer'}); var $content = $('<div>', {'class': 'cp-toolbar-userlist-drawer'});
$content.on('drop dragover', function (e) { $content.on('drop dragover', function (e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
}); });
var $closeIcon = $('<span>', {"class": "fa fa-window-close close"}).appendTo($content); var $closeIcon = $('<span>', {"class": "fa fa-window-close cp-toolbar-userlist-drawer-close"}).appendTo($content);
$('<h2>').text(Messages.users).appendTo($content); $('<h2>').text(Messages.users).appendTo($content);
$('<p>', {'class': USERLIST_CLS}).appendTo($content); $('<p>', {'class': USERLIST_CLS}).appendTo($content);
toolbar.userlistContent = $content; toolbar.userlistContent = $content;
var $container = $('<span>', {id: 'userButtons', title: Messages.userListButton}); var $container = $('<span>', {id: 'cp-toolbar-userlist-drawer-open', title: Messages.userListButton});
var $button = $('<button>').appendTo($container); var $button = $('<button>').appendTo($container);
$('<span>',{'class': 'buttonTitle'}).appendTo($button); $('<span>',{'class': 'cp-dropdown-button-title'}).appendTo($button);
toolbar.$leftside.prepend($container); toolbar.$leftside.prepend($container);
@ -304,7 +298,7 @@ define([
var mobile = $('body').width() <= 600; var mobile = $('body').width() <= 600;
var hide = function () { var hide = function () {
$content.hide(); $content.hide();
$button.removeClass('active'); $button.removeClass('cp-toolbar-button-active');
$ck.css({ $ck.css({
'padding-left': '', 'padding-left': '',
}); });
@ -314,7 +308,7 @@ define([
if (mobile) { if (mobile) {
$ck.hide(); $ck.hide();
} }
$button.addClass('active'); $button.addClass('cp-toolbar-button-active');
$ck.css({ $ck.css({
'padding-left': '175px', 'padding-left': '175px',
}); });
@ -370,7 +364,7 @@ define([
if (hashes.editHash) { if (hashes.editHash) {
options.push({ options.push({
tag: 'a', tag: 'a',
attributes: {title: Messages.editShareTitle, 'class': 'editShare'}, attributes: {title: Messages.editShareTitle, 'class': 'cp-toolbar-share-edit-copy'},
content: '<span class="fa fa-users"></span> ' + Messages.editShare content: '<span class="fa fa-users"></span> ' + Messages.editShare
}); });
if (readOnly) { if (readOnly) {
@ -379,7 +373,7 @@ define([
tag: 'a', tag: 'a',
attributes: { attributes: {
title: Messages.editOpenTitle, title: Messages.editOpenTitle,
'class': 'editOpen', 'class': 'cp-toolbar-share-edit-open',
href: origin + pathname + '#' + hashes.editHash, href: origin + pathname + '#' + hashes.editHash,
target: '_blank' target: '_blank'
}, },
@ -391,7 +385,7 @@ define([
if (hashes.viewHash) { if (hashes.viewHash) {
options.push({ options.push({
tag: 'a', tag: 'a',
attributes: {title: Messages.viewShareTitle, 'class': 'viewShare'}, attributes: {title: Messages.viewShareTitle, 'class': 'cp-toolbar-share-view-copy'},
content: '<span class="fa fa-eye"></span> ' + Messages.viewShare content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
}); });
if (!readOnly) { if (!readOnly) {
@ -400,7 +394,7 @@ define([
tag: 'a', tag: 'a',
attributes: { attributes: {
title: Messages.viewOpenTitle, title: Messages.viewOpenTitle,
'class': 'viewOpen', 'class': 'cp-toolbar-share-view-open',
href: origin + pathname + '#' + hashes.viewHash, href: origin + pathname + '#' + hashes.viewHash,
target: '_blank' target: '_blank'
}, },
@ -414,12 +408,12 @@ define([
feedback: 'SHARE_MENU', feedback: 'SHARE_MENU',
}; };
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare); var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
$shareBlock.find('.dropdown-bar-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS); $shareBlock.find('.cp-dropdown-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
$shareBlock.addClass('shareButton'); $shareBlock.addClass('cp-toolbar-share-button');
$shareBlock.find('button').attr('title', Messages.shareButton); $shareBlock.find('button').attr('title', Messages.shareButton);
if (hashes.editHash) { if (hashes.editHash) {
$shareBlock.find('a.editShare').click(function () { $shareBlock.find('a.cp-toolbar-share-edit-copy').click(function () {
/*Common.storeLinkToClipboard(false, function (err) { /*Common.storeLinkToClipboard(false, function (err) {
if (!err) { Cryptpad.log(Messages.shareSuccess); } if (!err) { Cryptpad.log(Messages.shareSuccess); }
});*/ });*/
@ -429,7 +423,7 @@ define([
}); });
} }
if (hashes.viewHash) { if (hashes.viewHash) {
$shareBlock.find('a.viewShare').click(function () { $shareBlock.find('a.cp-toolbar-share-view-copy').click(function () {
/*Common.storeLinkToClipboard(true, function (err) { /*Common.storeLinkToClipboard(true, function (err) {
if (!err) { Cryptpad.log(Messages.shareSuccess); } if (!err) { Cryptpad.log(Messages.shareSuccess); }
});*/ });*/
@ -451,7 +445,7 @@ define([
} }
var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'}); var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
var $button = $('<button>', {'title': Messages.shareButton}).append($shareIcon); var $button = $('<button>', {'title': Messages.shareButton}).append($shareIcon);
$button.addClass('shareButton'); $button.addClass('cp-toolbar-share-button');
$button.click(function () { $button.click(function () {
var url = window.location.href; var url = window.location.href;
var success = Cryptpad.Clipboard.copy(url); var success = Cryptpad.Clipboard.copy(url);
@ -464,11 +458,10 @@ define([
var createTitle = function (toolbar, config) { var createTitle = function (toolbar, config) {
var $titleContainer = $('<span>', { var $titleContainer = $('<span>', {
id: 'toolbarTitle',
'class': TITLE_CLS 'class': TITLE_CLS
}).appendTo(toolbar.$top); }).appendTo(toolbar.$top);
var $hoverable = $('<span>', {'class': 'hoverable'}).appendTo($titleContainer); var $hoverable = $('<span>', {'class': 'cp-toolbar-title-hoverable'}).appendTo($titleContainer);
if (typeof config.title !== "object") { if (typeof config.title !== "object") {
console.error("config.title", config); console.error("config.title", config);
@ -480,18 +473,18 @@ define([
// Buttons // Buttons
var $text = $('<span>', { var $text = $('<span>', {
'class': 'title' 'class': 'cp-toolbar-title-value'
}).appendTo($hoverable); }).appendTo($hoverable);
var $pencilIcon = $('<span>', { var $pencilIcon = $('<span>', {
'class': 'pencilIcon', 'class': 'cp-toolbar-title-edit',
'title': Messages.clickToEdit 'title': Messages.clickToEdit
}); });
var $saveIcon = $('<span>', { var $saveIcon = $('<span>', {
'class': 'saveIcon', 'class': 'cp-toolbar-title-save',
'title': Messages.saveTitle 'title': Messages.saveTitle
}).hide(); }).hide();
if (config.readOnly === 1) { if (config.readOnly === 1) {
$titleContainer.append($('<span>', {'class': 'readOnly'}) $titleContainer.append($('<span>', {'class': 'cp-toolbar-title-readonly'})
.text('('+Messages.readonly+')')); .text('('+Messages.readonly+')'));
} }
if (config.readOnly === 1 || typeof(Cryptpad) === "undefined") { return $titleContainer; } if (config.readOnly === 1 || typeof(Cryptpad) === "undefined") { return $titleContainer; }
@ -501,14 +494,14 @@ define([
}).appendTo($hoverable).hide(); }).appendTo($hoverable).hide();
if (config.readOnly !== 1) { if (config.readOnly !== 1) {
$text.attr("title", Messages.clickToEdit); $text.attr("title", Messages.clickToEdit);
$text.addClass("editable"); $text.addClass("cp-toolbar-title-editable");
var $icon = $('<span>', { var $icon = $('<span>', {
'class': 'fa fa-pencil readonly', 'class': 'fa fa-pencil cp-toolbar-title-icon-readonly',
style: 'font-family: FontAwesome;' style: 'font-family: FontAwesome;'
}); });
$pencilIcon.append($icon).appendTo($hoverable); $pencilIcon.append($icon).appendTo($hoverable);
var $icon2 = $('<span>', { var $icon2 = $('<span>', {
'class': 'fa fa-check readonly', 'class': 'fa fa-check cp-toolbar-title-icon-readonly',
style: 'font-family: FontAwesome;' style: 'font-family: FontAwesome;'
}); });
$saveIcon.append($icon2).appendTo($hoverable); $saveIcon.append($icon2).appendTo($hoverable);
@ -571,23 +564,22 @@ define([
var createPageTitle = function (toolbar, config) { var createPageTitle = function (toolbar, config) {
if (config.title || !config.pageTitle) { return; } if (config.title || !config.pageTitle) { return; }
var $titleContainer = $('<span>', { var $titleContainer = $('<span>', {
id: 'toolbarTitle',
'class': TITLE_CLS 'class': TITLE_CLS
}).appendTo(toolbar.$top); }).appendTo(toolbar.$top);
toolbar.$top.find('.filler').hide(); toolbar.$top.find('.filler').hide();
var $hoverable = $('<span>', {'class': 'hoverable'}).appendTo($titleContainer); var $hoverable = $('<span>', {'class': 'cp-toolbar-title-hoverable'}).appendTo($titleContainer);
// Buttons // Buttons
$('<span>', { $('<span>', {
'class': 'title pageTitle' 'class': 'cp-toolbar-title-value cp-toolbar-title-value-page'
}).appendTo($hoverable).text(config.pageTitle); }).appendTo($hoverable).text(config.pageTitle);
}; };
var createLinkToMain = function (toolbar, config) { var createLinkToMain = function (toolbar, config) {
var $linkContainer = $('<span>', { var $linkContainer = $('<span>', {
'class': "cryptpad-link" 'class': LINK_CLS
}).appendTo(toolbar.$top); }).appendTo(toolbar.$top);
// We need to override the "a" tag action here because it is inside the iframe! // We need to override the "a" tag action here because it is inside the iframe!
@ -601,7 +593,7 @@ define([
var $aTag = $('<a>', { var $aTag = $('<a>', {
href: href, href: href,
title: buttonTitle, title: buttonTitle,
'class': "cryptpad-logo" 'class': "cp-toolbar-link-logo"
}).append($('<img>', { }).append($('<img>', {
src: '/customize/images/logo_white.png?' + ApiConfig.requireConf.urlArgs src: '/customize/images/logo_white.png?' + ApiConfig.requireConf.urlArgs
})); }));
@ -784,7 +776,7 @@ define([
var initClickEvents = function (toolbar, config) { var initClickEvents = function (toolbar, config) {
var removeDropdowns = function () { var removeDropdowns = function () {
window.setTimeout(function () { window.setTimeout(function () {
toolbar.$toolbar.find('.cryptpad-dropdown').hide(); toolbar.$toolbar.find('.cp-dropdown-content').hide();
}); });
}; };
var cancelEditTitle = function (e) { var cancelEditTitle = function (e) {

@ -24,6 +24,11 @@ define([
var NEW_FOLDER_NAME = Messages.fm_newFolder; var NEW_FOLDER_NAME = Messages.fm_newFolder;
var NEW_FILE_NAME = Messages.fm_newFile; var NEW_FILE_NAME = Messages.fm_newFile;
exp.ROOT = ROOT;
exp.UNSORTED = UNSORTED;
exp.TRASH = TRASH;
exp.TEMPLATE = TEMPLATE;
// Logging // Logging
var logging = function () { var logging = function () {
console.log.apply(console, arguments); console.log.apply(console, arguments);
@ -151,6 +156,7 @@ define([
if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); } if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); }
var data = getFileData(id); var data = getFileData(id);
data[attr] = clone(value); data[attr] = clone(value);
cb(null);
}; };
// PATHS // PATHS
@ -849,6 +855,7 @@ define([
} }
try { try {
debug("Migrating file system..."); debug("Migrating file system...");
Cryptpad.feedback('Migrate-oldFilesData', true);
files.migrate = 1; files.migrate = 1;
var next = function () { var next = function () {
var oldData = files[OLD_FILES_DATA].slice(); var oldData = files[OLD_FILES_DATA].slice();

@ -1,595 +0,0 @@
/* PAGE */
html,
body {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
position: relative;
font-size: 16px;
overflow: auto;
}
body {
display: flex;
flex-flow: column;
}
img.icon {
max-width: 20px;
}
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.app-container {
flex: 1;
overflow: auto;
width: 100%;
display: flex;
flex-flow: row;
}
@media screen and (max-width: 600px) {
.app-container {
display: block;
}
.app-container #tree {
resize: none;
width: 100%;
max-width: unset;
border-bottom: 1px solid #ccc;
}
.app-container #tree .category2 {
margin-top: 0.5em;
}
}
.padColor {
color: #1c4fa0;
}
.codeColor {
color: #ffae00;
}
.slideColor {
color: #e57614;
}
.pollColor {
color: #006304;
}
.fileColor {
color: #cd2532;
}
.whiteboardColor {
color: #800080;
}
.driveColor {
color: #0087ff;
}
.defaultColor {
color: #ddd;
}
div:focus {
outline: none;
}
.fa {
/*min-width: 17px;*/
margin-right: 3px;
font-family: FontAwesome;
}
ul {
list-style: none;
padding-left: 0px;
}
li {
padding: 0px 5px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.folder,
.file {
margin-right: 5px;
}
.contextMenu {
display: none;
position: absolute;
z-index: 500;
}
.contextMenu li {
padding: 0;
font-size: 16px;
}
.contextMenu li a {
cursor: pointer;
}
.droppable {
background-color: #FE9A2E;
color: #222;
}
.selected {
border: 1px dotted #bbb;
background: #666;
color: #eee;
margin: -1px;
}
.selected .fa-minus-square-o,
.selected .fa-plus-square-o {
color: #000;
}
.selectedTmp {
border: 1px dotted #bbb;
background: #AAA;
color: #ddd;
margin: -1px;
}
.selectedTmp .fa-minus-square-o,
.selectedTmp .fa-plus-square-o {
color: #000;
}
span.fa-folder,
span.fa-folder-open {
color: #FEDE8B;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;
}
/* TREE */
#tree {
border-right: 1px solid #ccc;
box-sizing: border-box;
background: #fff;
overflow: auto;
resize: horizontal;
width: auto;
white-space: nowrap;
max-width: 500px;
min-width: 200px;
padding: 10px 0px;
color: #000;
}
#tree img.icon {
margin-bottom: 3px;
margin-left: -2px;
}
#tree li {
padding: 0 0 0 5px;
cursor: auto;
}
#tree li.collapsed ul {
display: none;
}
#tree li input {
width: calc(100% - 30px);
}
#tree li > span.element-row {
min-width: calc(100% + 5px);
display: inline-block;
cursor: pointer;
margin-left: -5px;
padding-left: 5px;
}
#tree li > span.element-row:not(.selected):not(.active):hover {
background-color: #eee;
}
#tree span.element {
cursor: pointer;
}
#tree .active:not(.selected):not(.droppable) {
background-color: #c8c8c8;
}
#tree .category2 {
margin-top: 2em;
}
#tree .category2 .root > .fa {
min-width: 30px;
cursor: pointer;
}
#tree #allfilesTree {
margin-top: 0;
}
#tree #searchContainer {
text-align: center;
padding: 5px 0;
}
#tree #searchContainer input {
width: 80%;
}
#tree .fa.expcol {
margin-left: -10px;
font-size: 14px;
position: absolute;
left: -20px;
top: 9px;
width: auto;
height: 11px;
padding: 0;
margin: 0;
background: white;
z-index: 10;
cursor: default;
}
#tree .fa.expcol:before {
position: relative;
top: -1px;
}
#tree ul {
margin: 0px 0px 0px 10px;
list-style: none;
padding-left: 10px;
}
#tree ul li {
position: relative;
}
#tree ul li:before {
position: absolute;
left: -15px;
top: -0.25em;
content: '';
display: block;
border-left: 1px solid #888;
height: 1em;
border-bottom: 1px solid #888;
width: 17.5px;
}
#tree ul li:after {
position: absolute;
left: -15px;
bottom: -7px;
content: '';
display: block;
border-left: 1px solid #888;
height: 100%;
}
#tree ul li.root {
margin: 0px 0px 0px -10px;
}
#tree ul li.root:before {
display: none;
}
#tree ul li.root:after {
display: none;
}
#tree ul li:last-child:after {
display: none;
}
/* CONTENT */
#rightCol {
display: flex;
flex-flow: column;
flex: 1;
min-width: 0;
}
#content {
box-sizing: border-box;
background: #fff;
color: #000;
overflow: auto;
flex: 1;
display: flex;
flex-flow: column;
position: relative;
}
#content .selectBox {
display: none;
background-color: rgba(100, 100, 100, 0.7);
position: absolute;
z-index: 50;
}
#content.readonly {
background: #e6e6e6;
}
#content h1 {
padding-left: 10px;
margin-top: 10px;
}
#content .info-box {
line-height: 40px;
padding-left: 10px;
margin: 10px auto;
background: #ddddff;
border: 1px solid #bbb;
border-radius: 5px;
}
#content .info-box span {
cursor: pointer;
margin-left: 10px;
float: right;
}
#content .info-box.noclose {
padding-right: 10px;
}
#content li {
cursor: default;
}
#content li:not(.header) *:not(input) {
/*pointer-events: none;*/
}
#content li:not(.header):hover:not(.selected, .selectedTmp) {
background-color: #eee;
}
#content li:not(.header):hover .name {
/*text-decoration: underline;*/
}
#content #folderContent li.searchResult {
border-bottom: 1px solid #bbb;
display: block;
}
#content #folderContent li.searchResult:hover {
background-color: initial;
}
#content #folderContent li.searchResult table {
width: 100%;
}
#content #folderContent li.searchResult table .label2 {
width: 150px;
font-size: 15px;
text-align: right;
padding-right: 15px;
}
#content #folderContent li.searchResult table .openDir a {
cursor: pointer;
color: #41b7d8;
}
#content #folderContent li.searchResult table .openDir a:hover {
color: #014c8c;
text-decoration: underline;
}
#content #folderContent li.searchResult table .path {
font-style: italic;
}
#content #folderContent li.searchResult table .title {
font-weight: bold;
cursor: pointer;
}
#content #folderContent li.searchResult table .title:hover {
background-color: #eee;
}
#content #folderContent li.searchResult table .col2 {
width: 250px;
}
#content #folderContent li.searchResult table td.icon {
width: 50px;
font-size: 40px;
}
#content .element .truncated {
display: none;
}
#content div.grid {
padding: 20px;
}
#content div.grid li {
display: inline-block;
margin: 10px 10px;
width: 140px;
height: 140px;
text-align: center;
vertical-align: top;
overflow: hidden;
text-overflow: ellipsis;
padding-top: 5px;
padding-bottom: 5px;
}
#content div.grid li:not(.selected):not(.selectedTmp) {
border: 1px solid #CCC;
}
#content div.grid li .name {
width: 100%;
height: 48px;
margin: 8px 0;
display: inline-block;
overflow: hidden;
word-wrap: break-word;
}
#content div.grid li.element {
position: relative;
}
#content div.grid li .truncated {
display: block;
position: absolute;
bottom: 0px;
left: 0;
right: 0;
text-align: center;
}
#content div.grid li input {
width: 100%;
margin-top: 5px;
}
#content div.grid li img.icon {
height: 48px;
max-width: none;
margin: 8px 0;
}
#content div.grid li .fa {
display: block;
margin: auto;
font-size: 48px;
margin: 8px 0;
text-align: center;
}
#content div.grid li .fa.listonly {
display: none;
}
#content div.grid .listElement {
display: none;
}
#content .list {
padding-left: 20px;
}
#content .list ul {
display: table;
width: 100%;
padding: 0px 10px;
}
#content .list li {
display: table-row;
}
#content .list li > span {
padding: 0 5px;
display: table-cell;
}
#content .list li.header {
cursor: default;
color: #555;
}
#content .list li.header span:not(.fa) {
text-align: left;
}
#content .list li.header span.sortasc,
#content .list li.header span.sortdesc {
float: right;
}
#content .list li.header > span {
padding: 15px 5px;
}
#content .list li.header > span.active {
font-weight: bold;
}
#content .list li.header > span.clickable {
cursor: pointer;
}
#content .list li.header > span.clickable:hover {
background: #e8e8e8;
}
#content .list .element span {
overflow: hidden;
white-space: nowrap;
box-sizing: border-box;
}
#content .list .element span.icon {
width: 30px;
}
#content .list .element span.type,
#content .list .element span.atime,
#content .list .element span.ctime {
width: 175px;
}
#content .list .element span.title {
width: 250px;
}
@media screen and (max-width: 1200px) {
#content .list .element span.title {
display: none;
}
}
#content .list .element span.folders,
#content .list .element span.files {
width: 150px;
}
.parentFolder {
cursor: pointer;
margin-left: 10px;
}
.parentFolder:hover {
text-decoration: underline;
}
#folderContent {
padding-right: 10px;
flex: 1;
}
/* Toolbar */
#driveToolbar {
background: #ddd;
color: #555;
border-top: 1px solid #ccc;
border-bottom: ;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
z-index: 100;
box-sizing: content-box;
padding: 0 6px;
display: flex;
flex-flow: row;
/* The container <div> - needed to position the dropdown content */
}
#driveToolbar .newPadContainer {
display: inline-block;
height: 100%;
}
#driveToolbar button {
height: 24px;
font: 12px Ubuntu, Arial, sans-serif;
}
#driveToolbar button span {
font: 12px Ubuntu, Arial, sans-serif;
}
#driveToolbar button .fa,
#driveToolbar button.fa {
font-family: FontAwesome;
}
#driveToolbar button.element {
height: 26px;
width: 26px;
padding: 0;
box-sizing: border-box;
}
#driveToolbar button.new {
padding: 0 5px;
}
#driveToolbar .dropdown-bar {
margin: 2px 2px;
line-height: 1em;
position: relative;
display: inline-block;
}
#driveToolbar .right {
float: right;
/* Right-side buttons */
}
#driveToolbar .right button {
display: inline-block;
}
#driveToolbar .right button.active {
display: none;
}
#driveToolbar .right button .fa {
margin-right: 0px;
}
#driveToolbar .dropdown-bar-content {
margin-right: 2px;
}
#driveToolbar .leftside {
width: auto;
margin: 0;
padding: 0;
display: inline-block;
}
#driveToolbar .rightside {
margin: 0;
padding: 0;
display: inline-block;
float: right;
}
#driveToolbar .path {
flex: 1;
width: 100%;
height: 30px;
line-height: 30px;
cursor: default;
width: auto;
overflow: hidden;
white-space: nowrap;
direction: rtl;
max-width: 100%;
text-align: left;
}
#driveToolbar .path .element {
padding: 5px;
border: 1px solid #ddd;
border-radius: 2px;
box-sizing: border-box;
}
#driveToolbar .path .element.clickable {
cursor: pointer;
}
#driveToolbar .path .element.clickable:hover {
background: #fff;
border: 1px solid #888;
}
#driveToolbar #contextButtonsContainer {
float: right;
margin: 2px;
}
#driveToolbar #contextButtonsContainer button {
vertical-align: top;
}

@ -709,7 +709,7 @@ span {
.fa { .fa {
margin-right: 5px; margin-right: 5px;
} }
.buttonTitle { .cp-dropdown-button-title {
display: inline-flex; display: inline-flex;
height: @toolbar-line-height; height: @toolbar-line-height;
align-items: center; align-items: center;
@ -730,13 +730,13 @@ span {
} }
} }
/* The container <div> - needed to position the dropdown content */ /* The container <div> - needed to position the dropdown content */
.dropdown-bar { .cp-dropdown-container {
margin: 2px 2px; margin: 2px 2px;
line-height: 1em; line-height: 1em;
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
.dropdown-bar-content { .cp-dropdown-content {
margin-right: 2px; margin-right: 2px;
} }

@ -1154,23 +1154,8 @@ define([
// This is duplicated in cryptpad-common, it should be unified // This is duplicated in cryptpad-common, it should be unified
var getFileIcon = function (id) { var getFileIcon = function (id) {
var $icon = Cryptpad.getIcon();
var data = filesOp.getFileData(id); var data = filesOp.getFileData(id);
if (!data) { return $icon; } return Cryptpad.getFileIcon(data);
var href = data.href;
if (!href) { return $icon; }
if (href.indexOf('/pad/') !== -1) { $icon = Cryptpad.getIcon('pad'); }
else if (href.indexOf('/pad2/') !== -1) { $icon = Cryptpad.getIcon('pad'); } // SFRAME
else if (href.indexOf('/code/') !== -1) { $icon = Cryptpad.getIcon('code'); }
else if (href.indexOf('/slide/') !== -1) { $icon = Cryptpad.getIcon('slide'); }
else if (href.indexOf('/poll/') !== -1) { $icon = Cryptpad.getIcon('poll'); }
else if (href.indexOf('/whiteboard/') !== -1) { $icon = Cryptpad.getIcon('whiteboard'); }
else if (href.indexOf('/file/') !== -1) { $icon = Cryptpad.getIcon('file'); }
return $icon;
}; };
var getIcon = Cryptpad.getIcon; var getIcon = Cryptpad.getIcon;
@ -1356,10 +1341,6 @@ define([
// Create the button allowing the user to switch from list to icons modes // Create the button allowing the user to switch from list to icons modes
var createViewModeButton = function ($container) { var createViewModeButton = function ($container) {
/*var $block = $('<div>', {
'class': 'dropdown-bar right changeViewModeContainer'
});*/
var $listButton = $listIcon.clone().addClass('element'); var $listButton = $listIcon.clone().addClass('element');
var $gridButton = $gridIcon.clone().addClass('element'); var $gridButton = $gridIcon.clone().addClass('element');
@ -1495,7 +1476,7 @@ define([
}; };
var hideNewButton = function () { var hideNewButton = function () {
$iframe.find('.dropdown-bar-content').hide(); $iframe.find('.cp-dropdown-content').hide();
}; };
var SORT_FOLDER_DESC = 'sortFoldersDesc'; var SORT_FOLDER_DESC = 'sortFoldersDesc';
@ -2004,7 +1985,7 @@ define([
createTitle($toolbar.find('.path'), path); createTitle($toolbar.find('.path'), path);
if (APP.mobile()) { if (APP.mobile()) {
var $context = $('<button>', {'class': 'element right dropdown-bar', id: 'contextButton'}); var $context = $('<button>', {'class': 'element right cp-dropdown-container', id: 'contextButton'});
$context.append($('<span>', {'class': 'fa fa-caret-down'})); $context.append($('<span>', {'class': 'fa fa-caret-down'}));
$context.appendTo($toolbar.find('.rightside')); $context.appendTo($toolbar.find('.rightside'));
$context.click(function (e) { $context.click(function (e) {
@ -2301,7 +2282,7 @@ define([
$trashContextMenu.hide(); $trashContextMenu.hide();
$contentContextMenu.hide(); $contentContextMenu.hide();
$defaultContextMenu.hide(); $defaultContextMenu.hide();
$iframe.find('.cryptpad-dropdown').hide(); $iframe.find('.cp-dropdown-content').hide();
}; };
var stringifyPath = function (path) { var stringifyPath = function (path) {
@ -2335,7 +2316,7 @@ define([
// Disable middle click in the context menu to avoid opening /drive/inner.html# in new tabs // Disable middle click in the context menu to avoid opening /drive/inner.html# in new tabs
$(ifrw).click(function (e) { $(ifrw).click(function (e) {
if (!e.target || !$(e.target).parents('.cryptpad-dropdown').length) { return; } if (!e.target || !$(e.target).parents('.cp-dropdown-content').length) { return; }
if (e.which !== 1) { if (e.which !== 1) {
e.stopPropagation(); e.stopPropagation();
return false; return false;
@ -2919,6 +2900,8 @@ define([
}; };
if (!readOnly && !APP.loggedIn) { if (!readOnly && !APP.loggedIn) {
// TODO secure drive
// cryptpad-backup --> cp-toolbar-backup
var $backupButton = Cryptpad.createButton('', true).removeClass('fa').removeClass('fa-question').addClass('cryptpad-backup'); var $backupButton = Cryptpad.createButton('', true).removeClass('fa').removeClass('fa-question').addClass('cryptpad-backup');
$backupButton.append($backupIcon.clone().css('marginRight', '0px')); $backupButton.append($backupIcon.clone().css('marginRight', '0px'));
$backupButton.attr('title', Messages.fm_backup_title); $backupButton.attr('title', Messages.fm_backup_title);

@ -1,378 +0,0 @@
/* PAGE */
html, body {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
position: relative;
font-size: 20px;
overflow: auto;
}
body {
display: flex;
flex-flow: column;
}
.app-container {
flex: 1;
overflow: auto;
width: 100%;
display: flex;
flex-flow: row;
}
.fa {
/*min-width: 17px;*/
margin-right: 3px;
font-family: FontAwesome;
}
ul {
list-style: none;
padding-left: 10px;
}
li {
padding: 0px 5px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.folder, .file {
margin-right: 5px;
}
.contextMenu {
display: none;
position: absolute;
}
.droppable {
background-color: #FE9A2E;
color: #222;
}
.selected {
border: 1px dotted #bbb;
background: #666;
color: #eee;
margin: -1px;
}
/* TREE */
#tree {
border-right: 1px solid #ccc;
box-sizing: border-box;
background: #eee;
overflow: auto;
resize: horizontal;
width: 350px;
white-space: nowrap;
max-width: 500px;
min-width: 200px;
padding: 10px 0px;
}
#tree li {
cursor: auto;
}
#tree span.element {
cursor: pointer;
}
#tree li > span.element:hover {
text-decoration: underline;
}
#tree .active {
text-decoration: underline;
}
#tree .category2 {
margin-top: 2em;
}
#tree .fa.expcol {
margin-left: -10px;
font-size: 14px;
position: absolute;
left: -20px;
top: 9px;
width: auto;
height: 11px;
padding: 0;
margin: 0;
background: white;
z-index: 10;
cursor: default;
}
#tree .fa.expcol:before {
position:relative;
top: -1px;
}
#tree li.collapsed ul {
display: none;
}
#tree li input {
width: calc(100% - 30px);
}
/* Tree lines */
#tree ul {
margin: 0px 0px 0px 10px;
list-style: none;
}
#tree ul li {
position: relative;
}
#tree ul li:before {
position: absolute;
left: -15px;
top: -0.25em;
content: '';
display: block;
border-left: 1px solid #888;
height: 1em;
border-bottom: 1px solid #888;
width: 17.5px;
}
#tree ul li:after {
position: absolute;
left: -15px;
bottom: -7px;
content: '';
display: block;
border-left: 1px solid #888;
height: 100%;
}
#tree ul li.root {
margin: 0px 0px 0px -10px;
}
#tree ul li.root:before {
display: none;
}
#tree ul li.root:after {
display: none;
}
#tree ul li:last-child:after {
display: none;
}
/* CONTENT */
#content {
box-sizing: border-box;
background: #eee;
overflow: auto;
flex: 1;
display: flex;
flex-flow: column;
}
#content h1 {
padding-left: 10px;
margin-top: 10px;
}
#content .info-box {
margin: 0px auto;
padding: 5px;
background: #ddddff;
border: 1px solid #bbb;
border-radius: 5px;
margin-bottom: 10px;
}
#content .info-box span {
cursor: pointer;
margin-left: 10px;
float: right;
}
.parentFolder {
cursor: pointer;
margin-left: 10px;
}
.parentFolder:hover {
text-decoration: underline;
}
#folderContent {
padding-right: 10px;
flex: 1;
}
#content li:not(.header) * {
pointer-events: none;
}
#content li:hover:not(.header) .name {
text-decoration: underline;
}
#content .grid li {
display: inline-block;
margin: 10px 10px;
width: 140px;
text-align: center;
vertical-align: top;
}
#content .grid li .name {
width: 100%;
}
#content .grid li input {
width: 100%;
}
#content .grid li .fa {
display: block;
margin: auto;
font-size: 40px;
text-align: center;
}
#content .grid li .fa.listonly {
display: none;
}
#content .list li {
display: flex;
flex-flow: row;
align-items: center;
padding-right: 0px;
}
#content .list li .element {
display: inline-flex;
flex: 1;
}
#content .list li.header {
cursor: default;
color: #008;
margin-top: 10px;
}
#content .list li.header .element span:not(.fa) {
border-right: 1px solid #CCC;
text-align: left;
}
#content .list li.header .element span.fa {
float: right;
}
#content .list li.header span.name {
padding-left: 0;
}
#content .list .element span {
padding: 0px 10px;
display: inline-block;
overflow: hidden;
white-space: nowrap;
box-sizing: border-box;
padding-right: 0px;
border-right: 10px solid rgba(0, 0, 0, 0);
}
#content .list .element span.name {
width: 478px;
}
.iframe #content .list .element span.name {
width: 278px;
}
#content .list .header span.name {
width: 500px;
}
.iframe #content .list .header span.name {
width: 300px;
}
#content .list .element span.type, #content .list .element span.atime, #content .list .element span.ctime {
width: 175px;
}
#content .list .element span.title {
width: 250px;
}
#content .list .element span.folders {
width: 150px;
}
#content .list .element span.files {
width: 150px;
}
#content div.grid .listElement {
display: none;
}
@media screen and (max-width: 1200px) {
#content .list .element span.title {
display: none;
}
}
@media screen and (min-width: 1201px) {
#content .list .element span.title {
display: inline;
}
}
/* Toolbar */
#driveToolbar {
background: #ddd;
height: 40px;
}
.newPadContainer {
display: inline-block;
height: 100%;
}
button.newElement {
border-radius: 2px;
height: 30px;
background: #888;
color: #eee;
font-size: 16px;
border: none;
font-weight: bold;
}
button.newElement:hover {
box-shadow: 0px 0px 2px #000;
}
/* The container <div> - needed to position the dropdown content */
#driveToolbar .dropdown-bar {
margin: 4px 5px;
position: relative;
display: inline-block;
}
#driveToolbar .dropdown-bar.right {
float: right;
}
/* Right-side buttons */
#driveToolbar .dropdown-bar.right button {
display: inline-block;
}
#driveToolbar .dropdown-bar.right button.active {
display: none;
}
#driveToolbar .dropdown-bar.right button .fa {
margin-right: 0px;
}
.dropdown-bar-content {
margin-top: -3px;
margin-right: 2px;
}

@ -185,7 +185,7 @@ define([
}); });
} }
if (plaintext) { if (plaintext) {
if (i * cypherChunkLength < u8.length) { // not done if ((2 + metadataLength + i * cypherChunkLength) < u8.length) { // not done
chunks.push(plaintext); chunks.push(plaintext);
return setTimeout(again); return setTimeout(again);
} }

@ -0,0 +1,55 @@
@import (once) '../../customize/src/less2/include/colortheme.less';
@import (once) '../../customize/src/less2/include/modal.less';
@import (once) '../../customize/src/less2/include/icon-colors.less';
@import (once) '../../customize/src/less2/include/fileupload.less';
.iconColors_main();
.fileupload_main();
#cp-filepicker-dialog {
display: none;
.cp-modal {
.cp-filepicker-content {
display: flex;
flex-wrap: wrap;
justify-content: center;
overflow-y: auto;
}
.cp-filepicker-content-element {
@darker: darken(@colortheme_modal-fg, 30%);
width: 200px;
min-width: 200px;
height: 1em;
padding: 0.5em;
margin: 5px;
box-sizing: content-box;
text-align: left;
line-height: 1em;
cursor: pointer;
background-color: #111;
color: @darker;
transition: all 0.1s;
&:hover {
color: @colortheme_modal-fg;
}
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
align-items: center;
.fa {
cursor: pointer;
margin-right: 0.5em;
}
}
}
}

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<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="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;
}
</style>
</head>
<body>
<iframe id="sbox-iframe">

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html style="height: 100%; background: transparent;">
<head>
<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.1" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<style>
.loading-hidden { display: none; }
body #loading {
position: absolute;
top: 15vh;
bottom: 15vh;
left: 10vw;
right: 10vw;
z-index: 200000;
overflow: hidden;
}
body #loading .loadingContainer {
margin-top: 35vh;
}
</style>
</head>
<body class="cp-app-filepicker" style="background: transparent;">
</body>
</html>

@ -0,0 +1,184 @@
define([
'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.js',
'/bower_components/chainpad-json-validator/json-ot.js',
'/common/cryptpad-common.js',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'json.sortify',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
Crypto,
TextPatcher,
JsonOT,
Cryptpad,
nThen,
SFCommon,
Sortify)
{
var Messages = Cryptpad.Messages;
var APP = window.APP = {
Cryptpad: Cryptpad,
};
var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError);
};
var andThen = function (common) {
var metadataMgr = common.getMetadataMgr();
var $body = $('body');
var sframeChan = common.getSframeChannel();
var filters = metadataMgr.getPrivateData().types;
var hideFileDialog = function () {
sframeChan.event('EV_FILE_PICKER_CLOSE');
};
var onFilePicked = function (data) {
var parsed = Cryptpad.parsePadUrl(data.url);
hideFileDialog();
if (parsed.type === 'file') {
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
sframeChan.event("EV_FILE_PICKED", {
type: parsed.type,
src: src,
key: parsed.hashData.key
});
return;
}
sframeChan.event("EV_FILE_PICKED", {
type: parsed.type,
href: data.url,
});
};
// File uploader
var fmConfig = {
body: $('body'),
noHandlers: true,
onUploaded: function (ev, data) {
onFilePicked(data);
}
};
APP.FM = common.createFileManager(fmConfig);
// Create file picker
var onSelect = function (url) {
onFilePicked({url: url});
};
var data = {
FM: APP.FM
};
var updateContainer;
var createFileDialog = function () {
var types = filters.types;
// Create modal
var $blockContainer = Cryptpad.createModal({
id: 'cp-filepicker-dialog',
$body: $body,
onClose: hideFileDialog
}).show();
// Set the fixed content
var $block = $blockContainer.find('.cp-modal');
// Description
var text = Messages.filePicker_description;
if (types.length === 1 && types[0] !== 'file') {
// Should be Templates
text = Messages.selectTemplate;
}
var $description = $('<p>').text(text);
$block.append($description);
var $filter = $('<p>', {'class': 'cp-modal-form'}).appendTo($block);
var to;
$('<input>', {
type: 'text',
'class': 'cp-filepicker-filter',
'placeholder': Messages.filePicker_filter
}).appendTo($filter).on('keypress', function () {
if (to) { window.clearTimeout(to); }
to = window.setTimeout(updateContainer, 300);
});
//If file, display the upload button
if (types.indexOf('file') !== -1) {
$filter.append(common.createButton('upload', false, data));
}
var $container = $('<span>', {'class': 'cp-filepicker-content'}).appendTo($block);
// Update the files list when needed
updateContainer = function () {
$container.html('');
var filter = $filter.find('.cp-filepicker-filter').val().trim();
var todo = function (err, list) {
if (err) { return void console.error(err); }
Object.keys(list).forEach(function (id) {
var data = list[id];
var name = data.title || '?';
if (filter && name.toLowerCase().indexOf(filter.toLowerCase()) === -1) {
return;
}
var $span = $('<span>', {
'class': 'cp-filepicker-content-element',
'title': name,
}).appendTo($container);
$span.append(Cryptpad.getFileIcon(data));
$span.append(name);
$span.click(function () {
if (typeof onSelect === "function") { onSelect(data.href); }
});
});
};
common.getFilesList(filters, todo);
};
updateContainer();
};
sframeChan.on('EV_FILE_PICKER_REFRESH', function (newFilters) {
console.log(Sortify(filters));
console.log(Sortify(newFilters));
if (Sortify(filters) !== Sortify(newFilters)) {
$body.html('');
filters = newFilters;
return void createFileDialog();
}
updateContainer();
});
createFileDialog();
Cryptpad.removeLoadingScreen();
};
var main = function () {
var common;
nThen(function (waitFor) {
$(waitFor(function () {
Cryptpad.addLoadingScreen();
}));
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (/*waitFor*/) {
var metadataMgr = common.getMetadataMgr();
Cryptpad.onError(function (info) {
if (info && info.type === "store") {
onConnectError();
}
});
if (metadataMgr.getMetadataLazy() !== 'uninitialized') {
andThen(common);
return;
}
metadataMgr.onChange(function () {
andThen(common);
});
});
};
main();
});

@ -0,0 +1,153 @@
// Load #1, load as little as possible because we are in a race to get the loading screen up.
define([
'/bower_components/nthen/index.js',
'/api/config',
'jquery',
'/common/requireconfig.js',
], function (nThen, ApiConfig, $, RequireConfig) {
var requireConfig = RequireConfig();
var create = function (config) {
// Loaded in load #2
var sframeChan;
nThen(function (waitFor) {
$(waitFor());
}).nThen(function (waitFor) {
var req = {
cfg: requireConfig,
req: [ '/common/loading.js' ],
pfx: window.location.origin
};
window.rc = requireConfig;
window.apiconf = ApiConfig;
$('#sbox-filePicker-iframe').attr('src',
ApiConfig.httpSafeOrigin + '/filepicker/inner.html?' + requireConfig.urlArgs +
'#' + encodeURIComponent(JSON.stringify(req)));
// This is a cheap trick to avoid loading sframe-channel in parallel with the
// loading screen setup.
var done = waitFor();
var onMsg = function (msg) {
var data = JSON.parse(msg.data);
if (data.q !== 'READY') { return; }
window.removeEventListener('message', onMsg);
var _done = done;
done = function () { };
_done();
};
window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) {
var Cryptpad;
var Crypto;
var Cryptget;
nThen(function (waitFor) {
// Load #2, the loading screen is up so grab whatever you need...
require([
'/common/cryptpad-common.js',
'/bower_components/chainpad-crypto/crypto.js',
'/common/cryptget.js',
'/common/sframe-channel.js',
], waitFor(function (_Cryptpad, _Crypto, _Cryptget, SFrameChannel) {
Cryptpad = _Cryptpad;
Crypto = _Crypto;
Cryptget = _Cryptget;
SFrameChannel.create($('#sbox-filePicker-iframe')[0].contentWindow, waitFor(function (sfc) {
sframeChan = sfc;
}));
Cryptpad.ready(waitFor());
}));
}).nThen(function () {
var proxy = Cryptpad.getProxy();
var updateMeta = function () {
//console.log('EV_METADATA_UPDATE');
var name;
nThen(function (waitFor) {
Cryptpad.getLastName(waitFor(function (err, n) {
if (err) { console.log(err); }
name = n;
}));
}).nThen(function (/*waitFor*/) {
sframeChan.event('EV_METADATA_UPDATE', {
doc: {},
user: {
name: name,
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: proxy.curvePublic,
netfluxId: Cryptpad.getNetwork().webChannels[0].myID,
},
priv: {
accountName: Cryptpad.getAccountName(),
origin: window.location.origin,
pathname: window.location.pathname,
feedbackAllowed: Cryptpad.isFeedbackAllowed(),
friends: proxy.friends || {},
settings: proxy.settings || {},
types: config.types
}
});
});
};
Cryptpad.onDisplayNameChanged(updateMeta);
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
proxy.on('change', 'settings', updateMeta);
Cryptpad.onError(function (info) {
console.log('error');
console.log(info);
if (info && info.type === "store") {
//onConnectError();
}
});
sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) {
Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) {
cb({error: err, response: response});
});
});
sframeChan.on('Q_GET_PIN_LIMIT_STATUS', function (data, cb) {
Cryptpad.isOverPinLimit(function (e, overLimit, limits) {
cb({
error: e,
overLimit: overLimit,
limits: limits
});
});
});
sframeChan.on('Q_GET_FILES_LIST', function (types, cb) {
console.error("TODO: make sure Q_GET_FILES_LIST is only available from filepicker");
Cryptpad.getSecureFilesList(types, function (err, data) {
cb({
error: err,
data: data
});
});
});
sframeChan.on('EV_FILE_PICKER_CLOSE', function () {
config.onClose();
});
sframeChan.on('EV_FILE_PICKED', function (data) {
config.onFilePicked(data);
});
sframeChan.on('Q_UPLOAD_FILE', function (data, cb) {
config.onFileUpload(sframeChan, data, cb);
});
});
});
var refresh = function (types) {
if (!sframeChan) { return; }
sframeChan.event('EV_FILE_PICKER_REFRESH', types);
};
return {
refresh: refresh
};
};
return {
create: create
};
});

@ -87,7 +87,7 @@ define([
$userAdmin.find('button').addClass('btn').addClass('btn-secondary'); $userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () { $(window).click(function () {
$('.cryptpad-dropdown').hide(); $('.cp-dropdown-content').hide();
}); });
// main block is hidden in case javascript is disabled // main block is hidden in case javascript is disabled

@ -22,7 +22,7 @@ define([
$userAdmin.find('button').addClass('btn').addClass('btn-secondary'); $userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () { $(window).click(function () {
$('.cryptpad-dropdown').hide(); $('.cp-dropdown-content').hide();
}); });
// main block is hidden in case javascript is disabled // main block is hidden in case javascript is disabled

@ -0,0 +1,41 @@
<!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>

@ -0,0 +1,17 @@
<!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>

@ -0,0 +1,40 @@
define([
'jquery',
'cm/lib/codemirror',
'css!/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');
});

@ -0,0 +1,559 @@
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',
'css!/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 = '!['+cleanName+']('+data.url+')';
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();
});
});

@ -0,0 +1,30 @@
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) '../../customize/src/less2/include/alertify.less';
.toolbar_main();
.alertify_main();
// body
&.cp-app-pad {
#cke_1_top {
overflow: visible;
padding: 0px;
display: flex;
}
#cke_1_toolbox {
display: inline-block;
width: 100%;
background-color: #c1e7ff;
}
#cke_1_toolbox .cke_toolbar {
height: 28px;
padding: 2px 0;
}
#cke_1_top .cryptpad-toolbar {
padding: 0;
display: block;
}
.cke_wysiwyg_frame {
min-width: 60%;
}
}

@ -1,36 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="cp pad"> <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.2" 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.2" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<style>
html, body {
margin: 0px;
}
#cke_1_top {
overflow: visible;
padding: 0px;
display: flex;
}
#cke_1_toolbox {
display: inline-block;
width: 100%;
background-color: #c1e7ff;
}
#cke_1_toolbox .cke_toolbar {
height: 28px;
padding: 2px 0;
}
#cke_1_top .cryptpad-toolbar {
padding: 0;
display: block;
}
.cke_wysiwyg_frame {
min-width: 60%;
}
</style>
</head> </head>
<body class="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>
</body> </body>
</html> </html>

@ -40,8 +40,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less/cryptpad.less', 'less!/customize/src/less2/main.less',
'less!/customize/src/less/toolbar.less'
], function ( ], function (
$, $,
Crypto, Crypto,
@ -545,7 +544,7 @@ define([
$bar.find('#cke_1_toolbar_collapser').hide(); $bar.find('#cke_1_toolbar_collapser').hide();
if (!readOnly) { if (!readOnly) {
// Expand / collapse the toolbar // Expand / collapse the toolbar
var $collapse = Cryptpad.createButton(null, true); var $collapse = common.createButton(null, true);
$collapse.removeClass('fa-question'); $collapse.removeClass('fa-question');
var updateIcon = function () { var updateIcon = function () {
$collapse.removeClass('fa-caret-down').removeClass('fa-caret-up'); $collapse.removeClass('fa-caret-down').removeClass('fa-caret-up');
@ -592,12 +591,12 @@ define([
} }
/* add an export button */ /* add an export button */
var $export = Cryptpad.createButton('export', true, {}, exportFile); var $export = common.createButton('export', true, {}, exportFile);
$drawer.append($export); $drawer.append($export);
if (!readOnly) { if (!readOnly) {
/* add an import button */ /* add an import button */
var $import = Cryptpad.createButton('import', true, { var $import = common.createButton('import', true, {
accept: 'text/html' accept: 'text/html'
}, importFile); }, importFile);
$drawer.append($import); $drawer.append($import);

@ -3,20 +3,12 @@ define([
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/api/config', '/api/config',
'jquery', 'jquery',
'/common/requireconfig.js' '/common/requireconfig.js',
], function (nThen, ApiConfig, $, RequireConfig) { '/common/sframe-common-outer.js'
], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) {
var requireConfig = RequireConfig(); var requireConfig = RequireConfig();
// Loaded in load #2 // Loaded in load #2
var CpNfOuter;
var Cryptpad;
var Crypto;
var Cryptget;
var sframeChan;
var secret;
var hashes;
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor()); $(waitFor());
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
@ -43,201 +35,7 @@ define([
_done(); _done();
}; };
window.addEventListener('message', onMsg); window.addEventListener('message', onMsg);
}).nThen(function (waitFor) {
// Load #2, the loading screen is up so grab whatever you need...
require([
'/common/sframe-chainpad-netflux-outer.js',
'/common/cryptpad-common.js',
'/bower_components/chainpad-crypto/crypto.js',
'/common/cryptget.js',
'/common/sframe-channel.js',
], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, SFrameChannel) {
CpNfOuter = _CpNfOuter;
Cryptpad = _Cryptpad;
Crypto = _Crypto;
Cryptget = _Cryptget;
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
sframeChan = sfc;
}));
Cryptpad.ready(waitFor());
}));
}).nThen(function (waitFor) {
secret = Cryptpad.getSecrets();
if (!secret.channel) {
// New pad: create a new random channel id
secret.channel = Cryptpad.createChannelId();
}
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var readOnly = secret.keys && !secret.keys.editKeyStr; SFCommonO.start();
if (!secret.keys) { secret.keys = secret.key; }
var parsed = Cryptpad.parsePadUrl(window.location.href);
if (!parsed.type) { throw new Error(); }
var defaultTitle = Cryptpad.getDefaultName(parsed);
var updateMeta = function () {
//console.log('EV_METADATA_UPDATE');
var name;
nThen(function (waitFor) {
Cryptpad.getLastName(waitFor(function (err, n) {
if (err) { console.log(err); }
name = n;
}));
}).nThen(function (/*waitFor*/) {
sframeChan.event('EV_METADATA_UPDATE', {
doc: {
defaultTitle: defaultTitle,
type: parsed.type
},
user: {
name: name,
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: Cryptpad.getProxy().curvePublic,
netfluxId: Cryptpad.getNetwork().webChannels[0].myID,
},
priv: {
accountName: Cryptpad.getAccountName(),
origin: window.location.origin,
pathname: window.location.pathname,
readOnly: readOnly,
availableHashes: hashes,
isTemplate: Cryptpad.isTemplate(window.location.href),
feedbackAllowed: Cryptpad.isFeedbackAllowed(),
friends: Cryptpad.getProxy().friends || {}
}
});
});
};
Cryptpad.onDisplayNameChanged(updateMeta);
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
Cryptpad.onError(function (info) {
console.log('error');
console.log(info);
if (info && info.type === "store") {
//onConnectError();
}
});
sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) {
Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) {
cb({error: err, response: response});
});
});
sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) {
document.title = newTitle;
Cryptpad.renamePad(newTitle, undefined, function (err) {
if (err) { cb('ERROR'); } else { cb(); }
});
});
sframeChan.on('Q_SETTINGS_SET_DISPLAY_NAME', function (newName, cb) {
Cryptpad.setAttribute('username', newName, function (err) {
if (err) {
console.log("Couldn't set username");
console.error(err);
cb('ERROR');
return;
}
Cryptpad.changeDisplayName(newName, true);
cb();
});
});
sframeChan.on('Q_LOGOUT', function (data, cb) {
Cryptpad.logout(cb);
});
sframeChan.on('EV_NOTIFY', function () {
Cryptpad.notify();
});
sframeChan.on('Q_SET_LOGIN_REDIRECT', function (data, cb) {
sessionStorage.redirectTo = window.location.href;
cb();
});
sframeChan.on('Q_GET_PIN_LIMIT_STATUS', function (data, cb) {
Cryptpad.isOverPinLimit(function (e, overLimit, limits) {
cb({
error: e,
overLimit: overLimit,
limits: limits
});
});
});
sframeChan.on('Q_MOVE_TO_TRASH', function (data, cb) {
Cryptpad.moveToTrash(cb);
});
sframeChan.on('Q_SAVE_AS_TEMPLATE', function (data, cb) {
Cryptpad.saveAsTemplate(Cryptget.put, data, cb);
});
sframeChan.on('Q_SEND_FRIEND_REQUEST', function (netfluxId, cb) {
Cryptpad.inviteFromUserlist(Cryptpad, netfluxId);
cb();
});
Cryptpad.onFriendRequest = function (confirmText, cb) {
sframeChan.query('Q_INCOMING_FRIEND_REQUEST', confirmText, function (err, data) {
cb(data);
});
};
Cryptpad.onFriendComplete = function (data) {
sframeChan.event('EV_FRIEND_REQUEST', data);
};
sframeChan.on('Q_GET_FULL_HISTORY', function (data, cb) {
var network = Cryptpad.getNetwork();
var hkn = network.historyKeeper;
var crypto = Crypto.createEncryptor(secret.keys);
// Get the history messages and send them to the iframe
var parse = function (msg) {
try {
return JSON.parse(msg);
} catch (e) {
return null;
}
};
var onMsg = function (msg) {
var parsed = parse(msg);
if (parsed[0] === 'FULL_HISTORY_END') {
console.log('END');
cb();
return;
}
if (parsed[0] !== 'FULL_HISTORY') { return; }
if (parsed[1] && parsed[1].validateKey) { // First message
secret.keys.validateKey = parsed[1].validateKey;
return;
}
msg = parsed[1][4];
if (msg) {
msg = msg.replace(/^cp\|/, '');
var decryptedMsg = crypto.decrypt(msg, secret.keys.validateKey);
sframeChan.event('EV_RT_HIST_MESSAGE', decryptedMsg);
}
};
network.on('message', onMsg);
network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', secret.channel, secret.keys.validateKey]));
});
CpNfOuter.start({
sframeChan: sframeChan,
channel: secret.channel,
network: Cryptpad.getNetwork(),
validateKey: secret.keys.validateKey || undefined,
readOnly: readOnly,
crypto: Crypto.createEncryptor(secret.keys),
onConnect: function (wc) {
if (readOnly) { return; }
Cryptpad.replaceHash(Cryptpad.getEditHashFromKeys(wc.id, secret.keys));
}
});
Cryptpad.reportAppUsage();
}); });
}); });

@ -544,7 +544,7 @@ define([
$(function () { $(function () {
$(window).click(function () { $(window).click(function () {
$('.cryptpad-dropdown').hide(); $('.cp-dropdown-content').hide();
}); });
APP.$container = $('#container'); APP.$container = $('#container');

@ -25,7 +25,7 @@ define([
$userAdmin.find('button').addClass('btn').addClass('btn-secondary'); $userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () { $(window).click(function () {
$('.cryptpad-dropdown').hide(); $('.cp-dropdown-content').hide();
}); });
// main block is hidden in case javascript is disabled // main block is hidden in case javascript is disabled

@ -131,6 +131,7 @@ define([
}; };
var createIndentUnitSelector = function (obj) { var createIndentUnitSelector = function (obj) {
var proxy = obj.proxy; var proxy = obj.proxy;
proxy.settings = proxy.settings || {};
var $div = $('<div>', { var $div = $('<div>', {
'class': 'indentUnit element' 'class': 'indentUnit element'
@ -148,26 +149,28 @@ define([
}).on('change', function () { }).on('change', function () {
var val = parseInt($input.val()); var val = parseInt($input.val());
if (typeof(val) !== 'number') { return; } if (typeof(val) !== 'number') { return; }
proxy['cryptpad.indentUnit'] = val; proxy.settings.indentUnit = val;
}).appendTo($inputBlock); }).appendTo($inputBlock);
proxy.on('change', [ 'cryptpad.indentUnit', ], function (o, n) { $input.val(n); }); proxy.on('change', [ 'settings', 'indentUnit', ], function (o, n) { $input.val(n); });
Cryptpad.getAttribute('indentUnit', function (e, val) { //Cryptpad.getAttribute('indentUnit', function (e, val) {
if (e) { return void console.error(e); } //if (e) { return void console.error(e); }
var val = proxy.settings.indentUnit;
if (typeof(val) !== 'number') { if (typeof(val) !== 'number') {
$input.val(2); $input.val(2);
} else { } else {
$input.val(val); $input.val(val);
} }
}); //});
return $div; return $div;
}; };
var createIndentTypeSelector = function (obj) { var createIndentTypeSelector = function (obj) {
var proxy = obj.proxy; var proxy = obj.proxy;
proxy.settings = proxy.settings || {};
var key = 'cryptpad.indentWithTabs'; var key = 'indentWithTabs';
var $div = $('<div>', { var $div = $('<div>', {
'class': 'indentType element' 'class': 'indentType element'
@ -184,11 +187,11 @@ define([
}).on('change', function () { }).on('change', function () {
var val = $input.is(':checked'); var val = $input.is(':checked');
if (typeof(val) !== 'boolean') { return; } if (typeof(val) !== 'boolean') { return; }
proxy[key] = val; proxy.settings[key] = val;
}).appendTo($inputBlock); }).appendTo($inputBlock);
$input[0].checked = !!proxy[key]; $input[0].checked = !!proxy.settings[key];
proxy.on('change', [key], function (o, n) { $input[0].checked = !!n; }); proxy.on('change', ['settings', key], function (o, n) { $input[0].checked = !!n; });
return $div; return $div;
}; };
@ -476,7 +479,7 @@ define([
$(function () { $(function () {
$(window).click(function () { $(window).click(function () {
$('.cryptpad-dropdown').hide(); $('.cp-dropdown-content').hide();
}); });
APP.$container = $('#container'); APP.$container = $('#container');

@ -41,7 +41,7 @@ define([
$userAdmin.find('button').addClass('btn').addClass('btn-secondary'); $userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () { $(window).click(function () {
$('.cryptpad-dropdown').hide(); $('.cp-dropdown-content').hide();
}); });
// main block is hidden in case javascript is disabled // main block is hidden in case javascript is disabled

Loading…
Cancel
Save