Merge branch 'code2' into staging

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

@ -51,5 +51,16 @@ define(function() {
config.loginSalt = '';
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;
});

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

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

@ -458,7 +458,7 @@ noscript {
padding: 0;
&.buttons {
margin-bottom: 10px;
.dropdown-bar {
.cp-dropdown-container {
button {
white-space: normal;
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-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;
}
}
}
}
.dropdown_main();

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

@ -1,8 +1,16 @@
@import "./variables.less";
@import "./mixins.less";
@import "./dropdown.less";
@import (once) "../less2/include/dropdown.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 {
-webkit-touch-callout: none;
@ -13,51 +21,9 @@
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 {
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 {
font: @main-font-size @colortheme_font;
@ -201,36 +167,9 @@ body {
background: darken(@bgcolor, 10%);
color: @color;
}
.dropdown-bar-content.left a {
.cp-dropdown-content.cp-dropdown-left a {
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%;
z-index: 9001;
.dropdown-bar {
.cp-dropdown-container {
//height: 100%;
//display: inline-block;
button {
@ -865,7 +804,7 @@ body .cryptpad-toolbar {
&:hover {
background-color: rgba(0,0,0,0.4);
}
.dropdown-bar-content {
.cp-dropdown-content {
margin: 0;
}
button {
@ -929,7 +868,7 @@ body .cryptpad-toolbar {
border-radius: 0;
height: 100%;
}
.dropdown-bar-content {
.cp-dropdown-content {
margin-top: -1px;
}

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

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

@ -1,5 +1,6 @@
@import (once) "./colortheme.less";
@import (once) "./browser.less";
@import (once) "./modal-theme.less";
.alertify_main () {
@alertify-fore: @colortheme_modal-fg;
@ -19,13 +20,14 @@
@alertify-input-bg: @colortheme_modal-input;
@alertify-input-fg: @colortheme_modal-fg;
@alertify_padding-base: @colortheme_modal-padding;
@alertify_box-shadow: @colortheme_modal-shadow;
@alertify_padding-base: @modal_padding;
@alertify_box-shadow: @modal_shadow;
// Logs to show that something has happened
// These show only once
.alertify-logs {
z-index:10000;
@media print {
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_app-font-size: 16px;
@colortheme_app-font: @colortheme_app-font-size @colortheme_font;
@colortheme_link-color: #0275D8;
@colortheme_link-color-visited: #005999;
@ -18,8 +20,6 @@
@colortheme_modal-link: #eee;
@colortheme_modal-link-visited: lighten(@colortheme_modal-link, 10%);
@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;
@ -29,6 +29,11 @@
@colortheme_notification-log: rgba(0, 0, 0, 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
@colortheme_pad-bg: #1c4fa0;
@ -64,6 +69,9 @@
@colortheme_profile-bg: #0087ff;
@colortheme_profile-color: #fff;
@colortheme_todo-bg: #7bccd1;
@colortheme_todo-color: #000;
@cryptpad_color_blue: #4591C4;
@cryptpad_color_grey: #999999;
@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) "./modal-theme.less";
.modal_base() {
font-family: @colortheme_font;
background-color: @colortheme_modal-bg;
color: @colortheme_modal-fg;
box-shadow: @colortheme_modal-shadow;
box-shadow: @modal_shadow;
a {
color: @colortheme_modal-link;
@ -30,9 +31,9 @@
.cp-modal {
background-color: @colortheme_modal-bg;
color: @colortheme_modal-fg;
box-shadow: @colortheme_modal-shadow;
box-shadow: @modal_shadow;
padding: @colortheme_modal-padding;
padding: @modal_padding;
position: absolute;
top: 15vh; bottom: 15vh;
@ -70,7 +71,7 @@
position: absolute;
top: 0;
right: 0;
margin: @colortheme_modal-padding;
margin: @modal_padding;
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-privacy { @import "./pages/page-privacy.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 {
border: 1px solid red;
}
.thumb {
max-height: 150px;
width: auto;
border: 3px solid black;
}
</style>
</head>
@ -36,6 +41,7 @@
<!-- -->
<div id="quot"><p>"pewpewpew"</p></div>
<hr>
<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>
<hr>
<img id="thumb-orig" src="/customize/alt-favicon.png" />

@ -5,8 +5,9 @@ define([
'json.sortify',
'/common/cryptpad-common.js',
'/drive/tests.js',
'/common/test.js'
], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad, Drive, Test) {
'/common/test.js',
'/common/common-thumbnail.js',
], function ($, Hyperjson, TextPatcher, Sortify, Cryptpad, Drive, Test, Thumb) {
window.Hyperjson = Hyperjson;
window.TextPatcher = TextPatcher;
window.Sortify = Sortify;
@ -207,6 +208,31 @@ define([
return cb(true);
}, "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);

@ -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>
<html class="cp code">
<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="/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>
html, body {
overflow-y: hidden;
}
#iframe-container {
position: fixed;
top: 0px;
bottom: 0px;
right: 0px;
left: 0px;
margin: 0px;
padding: 0px;
}
#pad-iframe {
#sbox-iframe {
position:fixed;
top:0px;
left:0px;
bottom:0px;
right:0px;
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;
#sbox-filePicker-iframe {
position: fixed;
top:0; left:0;
bottom:0; right:0;
width:100%;
height: 100%;
border: 0;
}
</style>
</head>
<body>
<div id="iframe-container">
<iframe id="pad-iframe"></iframe><script src="/common/noscriptfix.js"></script>
</div>
<iframe id="sbox-iframe">

@ -1,16 +1,20 @@
<!DOCTYPE html>
<html style="height: 100%;">
<html class="cp-app-noscroll">
<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>
<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>
<style>
.loading-hidden { display: none; }
#editor1 { display: none; }
</style>
</head>
<body class="loading-hidden">
<div id="cme_toolbox" class="toolbar-container"></div>
<div id="editorContainer">
<body class="cp-app-code">
<div id="cme_toolbox" class="cp-toolbar-container"></div>
<div id="cp-app-code-editor">
<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>
</body>
</html>

@ -1,13 +1,23 @@
define([
'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.js',
'/common/toolbar3.js',
'json.sortify',
'/bower_components/chainpad-json-validator/json-ot.js',
'/common/cryptpad-common.js',
'/common/cryptget.js',
'/common/diffMarked.js',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'/api/config',
'/common/common-realtime.js',
'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!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'css!cm/lib/codemirror.css',
'css!cm/addon/dialog/dialog.css',
@ -34,7 +44,599 @@ define([
'cm/addon/fold/markdown-fold',
'cm/addon/fold/comment-fold',
'cm/addon/display/placeholder',
], function ($, CMeditor) {
], function (
$,
Crypto,
TextPatcher,
Toolbar,
JSONSortify,
JsonOT,
Cryptpad,
Cryptget,
DiffMd,
nThen,
SFCommon,
ApiConfig,
CommonRealtime,
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([
'/bower_components/nthen/index.js',
'/api/config',
'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 = '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();
'/common/requireconfig.js',
'/common/sframe-common-outer.js'
], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) {
var requireConfig = RequireConfig();
// Loaded in load #2
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-iframe').attr('src',
ApiConfig.httpSafeOrigin + '/code/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*/) {
SFCommonO.start();
});
});

@ -16,7 +16,8 @@ define([
CodeMirror.modeURL = "cm/mode/%N/%N";
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 onLocal = function () {};
@ -228,6 +229,7 @@ define([
var ext = /.+\.([^.]+)$/.exec(file.name);
if (ext[1]) {
mode = CMeditor.findModeByExtension(ext[1]);
mode = mode && mode.mode || null;
}
} else {
mode = mime && mime.mode || null;

@ -1,12 +1,13 @@
define([
'jquery',
'/file/file-crypto.js',
'/common/common-thumbnail.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function ($, FileCrypto) {
], function ($, FileCrypto, Thumb) {
var Nacl = window.nacl;
var module = {};
var blobToArrayBuffer = function (blob, cb) {
var blobToArrayBuffer = module.blobToArrayBuffer = function (blob, cb) {
var reader = new FileReader();
reader.onloadend = function () {
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) {
var File = {};
@ -62,7 +142,8 @@ define([
};
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 id = file.id;
if (queue.inProgress) { return; }
@ -83,58 +164,13 @@ define([
});
};
var u8 = new Uint8Array(blob);
var key = Nacl.randomBytes(32);
var next = FileCrypto.encrypt(u8, metadata, key);
var estimate = FileCrypto.computeEncryptedSize(blob.byteLength, metadata);
var sendChunk = function (box, cb) {
var enc = Nacl.util.encodeBase64(box);
common.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) {
console.log(box);
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 onComplete = function (href) {
$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')();
@ -147,19 +183,7 @@ define([
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) {
if (e) {
var onError = function (e) {
queue.inProgress = false;
queue.next();
if (e === 'TOO_LARGE') {
@ -172,25 +196,19 @@ define([
}
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) {
var onPending = function (cb) {
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);
cb();
});
};
file.blob = u8;
module.upload(file, config.noStore, common, updateProgress, onComplete, onError, onPending);
};
var prettySize = function (bytes) {
var kB = common.bytesToKilobytes(bytes);
if (kB < 1024) { return kB + Messages.KB; }
@ -246,30 +264,46 @@ define([
var handleFile = File.handleFile = function (file, e, thumbnail) {
var thumb;
var finish = function (arrayBuffer) {
var file_arraybuffer;
var finish = function () {
var metadata = {
name: file.name,
type: file.type,
};
if (thumb) { metadata.thumbnail = thumb; }
queue.push({
blob: arrayBuffer,
blob: file_arraybuffer,
metadata: metadata,
dropEvent: e
});
};
var processFile = function () {
blobToArrayBuffer(file, function (e, buffer) {
finish(buffer);
if (e) { console.error(e); }
file_arraybuffer = buffer;
if (thumbnail) { // there is already a thumbnail
return blobToArrayBuffer(thumbnail, function (e, buffer) {
if (e) { console.error(e); }
thumb = arrayBufferToString(buffer);
finish();
});
};
}
if (!thumbnail) { return void processFile(); }
blobToArrayBuffer(thumbnail, function (e, buffer) {
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);
processFile();
finish();
});
});
});
};

@ -280,28 +280,13 @@ define([
};
};
var $fileIcon = $('<span>', {"class": "fa fa-file-text-o file icon"});
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"});
var $defaultIcon = $('<span>', {"class": "fa fa-file-text-o"});
UI.getIcon = function (type) {
var $icon;
switch(type) {
case 'pad': $icon = $padIcon.clone(); break;
case 'file': $icon = $fileAppIcon.clone(); break;
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();
var $icon = $defaultIcon.clone();
if (AppConfig.applicationsIcon && AppConfig.applicationsIcon[type]) {
var appClass = ' cp-icon-color-'+type;
$icon = $('<span>', {'class': 'fa ' + AppConfig.applicationsIcon[type] + appClass});
}
return $icon;

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

@ -3,7 +3,18 @@ define([
], function () {
var Nacl = window.nacl;
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
@ -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
// nodeback returning blob
Thumb.fromCanvas = function (canvas, cb) {
canvas = canvas;
Thumb.fromCanvas = Thumb.fromImage = function (canvas, cb) {
var c2 = document.createElement('canvas');
var d = Thumb.dimension;
c2.width = d;
c2.height = 2;
var D = getResizedDimensions(canvas);
if (!D) { return void cb('TOO_SMALL'); }
c2.width = Thumb.dimension;
c2.height = Thumb.dimension;
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) {
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) {
cb = cb; // WIP
};

@ -80,6 +80,7 @@ define([
common.getIcon = UI.getIcon;
common.addTooltips = UI.addTooltips;
common.clearTooltips = UI.clearTooltips;
common.importContent = UI.importContent;
// import common utilities for export
common.find = Util.find;
@ -617,6 +618,16 @@ define([
});
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
/* fetch and migrate your pad history from the store */
@ -784,7 +795,7 @@ define([
var proxy = store.getProxy();
var fo = proxy.fo;
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 parsed = parsePadUrl(href);
if ((parsed.type === 'file' || parsed.type === 'media')
@ -795,6 +806,27 @@ define([
});
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 store = common.getStore();
@ -1014,6 +1046,9 @@ define([
rpc.uploadCancel(cb);
};
common.uploadFileSecure = Files.upload;
/* 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,
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
var getFileIcon = function (data) {
var getFileIcon = common.getFileIcon = function (data) {
var $icon = common.getIcon();
if (!data) { return $icon; }
@ -1483,12 +1518,8 @@ define([
var href = data.href;
if (!href) { return $icon; }
if (href.indexOf('/pad/') !== -1) { $icon = common.getIcon('pad'); }
else if (href.indexOf('/code/') !== -1) { $icon = common.getIcon('code'); }
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'); }
var type = common.parsePadUrl(href).type;
$icon = common.getIcon(type);
return $icon;
};
@ -1502,16 +1533,24 @@ define([
'id': cfg.id
});
}
var hide = function () {
if (cfg.onClose) { return void cfg.onClose(); }
$blockContainer.hide();
};
$blockContainer.html('').appendTo($body);
var $block = $('<div>', {'class': 'cp-modal'}).appendTo($blockContainer);
$('<span>', {
'class': 'cp-modal-close fa fa-times',
'title': Messages.filePicker_close
}).click(function () {
$blockContainer.hide();
}).appendTo($block);
}).click(hide).appendTo($block);
$body.click(hide);
$block.click(function (e) {
e.stopPropagation();
});
$body.keydown(function (e) {
if (e.which === 27) { $blockContainer.hide(); }
if (e.which === 27) {
hide();
}
});
return $blockContainer;
};
@ -1589,7 +1628,7 @@ define([
// Container
var $container = $(config.container);
var containerConfig = {
'class': 'dropdown-bar'
'class': 'cp-dropdown-container'
};
if (config.buttonTitle) {
containerConfig.title = config.buttonTitle;
@ -1602,14 +1641,14 @@ define([
// Button
var $button = $('<button>', {
'class': ''
}).append($('<span>', {'class': 'buttonTitle'}).html(config.text || ""));
}).append($('<span>', {'class': 'cp-dropdown-button-title'}).html(config.text || ""));
/*$('<span>', {
'class': 'fa fa-caret-down',
}).appendTo($button);*/
// Menu
var $innerblock = $('<div>', {'class': 'cryptpad-dropdown dropdown-bar-content'});
if (config.left) { $innerblock.addClass('left'); }
var $innerblock = $('<div>', {'class': 'cp-dropdown-content'});
if (config.left) { $innerblock.addClass('cp-dropdown-left'); }
config.options.forEach(function (o) {
if (!isValidOption(o)) { return; }
@ -1622,8 +1661,8 @@ define([
var setActive = function ($el) {
if ($el.length !== 1) { return; }
$innerblock.find('.active').removeClass('active');
$el.addClass('active');
$innerblock.find('.cp-dropdown-element-active').removeClass('cp-dropdown-element(active');
$el.addClass('cp-dropdown-element-active');
var scroll = $el.position().top + $innerblock.scrollTop();
if (scroll < $innerblock.scrollTop()) {
$innerblock.scrollTop(scroll);
@ -1638,7 +1677,7 @@ define([
var show = function () {
$innerblock.show();
$innerblock.find('.active').removeClass('active');
$innerblock.find('.cp-dropdown-element-active').removeClass('cp-dropdown-element-active');
if (config.isSelect && value) {
var $val = $innerblock.find('[data-value="'+value+'"]');
setActive($val);
@ -1650,10 +1689,10 @@ define([
$container.click(function (e) {
e.stopPropagation();
var state = $innerblock.is(':visible');
$('.dropdown-bar-content').hide();
$('.cp-dropdown-content').hide();
try {
$('iframe').each(function (idx, ifrw) {
$(ifrw).contents().find('.dropdown-bar-content').hide();
$(ifrw).contents().find('.cp-dropdown-content').hide();
});
} catch (er) {
// empty try catch in case this iframe is problematic (cross-origin)
@ -1669,7 +1708,7 @@ define([
var pressed = '';
var to;
$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 ($value.length) {
var $prev = $value.prev();
@ -1710,7 +1749,7 @@ define([
value = val;
var $val = $innerblock.find('[data-value="'+val+'"]');
var textValue = name || $val.html() || val;
$button.find('.buttonTitle').html(textValue);
$button.find('.cp-dropdown-button-title').html(textValue);
};
$container.getValue = function () {
return value || '';
@ -1857,7 +1896,7 @@ define([
var oldUrl;
if (account && !config.static && store) {
var $avatar = $userAdmin.find('.buttonTitle');
var $avatar = $userAdmin.find('.cp-dropdown-button-title');
var updateButton = function (newName) {
var profile = store.getProfile();
var url = profile && profile.avatar;

@ -1,7 +1,9 @@
@import (once) '../customize/src/less2/include/colortheme.less';
@import '../customize/src/less2/include/modal.less';
#fileDialog {
.fileDialog_main () {
#fileDialog {
display: none;
.cp-modal {
.fileContainer {
display: flex;
@ -45,4 +47,5 @@
}
}
}
}
}

@ -4,7 +4,8 @@ define([
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
'/bower_components/textpatcher/TextPatcher.amd.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
asyncronous API. This is so that we can substitute other storage
@ -155,6 +156,8 @@ define([
var todo = function () {
fo.fixFiles();
Migrate(proxy, Cryptpad);
//storeObj = proxy;
store = initStore(fo, proxy, exp);
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) {
meta = ev;
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) {
var data = JSON.parse(msg.data);
if (ow !== msg.source) {
console.log("DROP Message from unexpected source");
console.log(msg);
return;
//console.log("DROP Message from unexpected source");
//console.log(msg);
} else if (!otherWindow) {
otherWindow = ow;
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 c = states.length - 1;
var $hist = $toolbar.find('.cryptpad-toolbar-history');
var $left = $toolbar.find('.cryptpad-toolbar-leftside');
var $right = $toolbar.find('.cryptpad-toolbar-rightside');
var $hist = $toolbar.find('.cp-toolbar-history');
var $left = $toolbar.find('.cp-toolbar-leftside');
var $right = $toolbar.find('.cp-toolbar-rightside');
var $cke = $toolbar.find('.cke_toolbox_main');
$hist.html('').show();
@ -111,9 +111,9 @@ define([
var val = states[i].getContent().doc;
c = i;
if (typeof onUpdate === "function") { onUpdate(); }
$hist.find('.next, .previous').css('visibility', '');
if (c === states.length - 1) { $hist.find('.next').css('visibility', 'hidden'); }
if (c === 0) { $hist.find('.previous').css('visibility', 'hidden'); }
$hist.find('.cp-toolbar-history-next, .cp-toolbar-history-previous').css('visibility', '');
if (c === states.length - 1) { $hist.find('.cp-toolbar-history-next').css('visibility', 'hidden'); }
if (c === 0) { $hist.find('.cp-toolbar-history-previous').css('visibility', 'hidden'); }
return val || '';
};
@ -128,18 +128,18 @@ define([
var display = function () {
$hist.html('');
var $prev =$('<button>', {
'class': 'previous fa fa-step-backward buttonPrimary',
'class': 'cp-toolbar-history-previous fa fa-step-backward buttonPrimary',
title: Messages.history_prev
}).appendTo($hist);
var $nav = $('<div>', {'class': 'goto'}).appendTo($hist);
var $nav = $('<div>', {'class': 'cp-toolbar-history-goto'}).appendTo($hist);
var $next = $('<button>', {
'class': 'next fa fa-step-forward buttonPrimary',
'class': 'cp-toolbar-history-next fa fa-step-forward buttonPrimary',
title: Messages.history_next
}).appendTo($hist);
$('<label>').text(Messages.history_version).appendTo($nav);
var $cur = $('<input>', {
'class' : 'gotoInput',
'class' : 'cp-toolbar-history-goto-input',
'type' : 'number',
'min' : '1',
'max' : states.length
@ -150,11 +150,11 @@ define([
var $label2 = $('<label>').text(' / '+ states.length).appendTo($nav);
$('<br>').appendTo($nav);
var $close = $('<button>', {
'class':'closeHistory',
'class':'cp-toolbar-history-close',
title: Messages.history_closeTitle
}).text(Messages.history_closeTitle).appendTo($nav);
var $rev = $('<button>', {
'class':'revertHistory buttonSuccess',
'class':'cp-toolbar-history-revert buttonSuccess',
title: Messages.history_restoreTitle
}).text(Messages.history_restore).appendTo($nav);
if (History.readOnly) { $rev.hide(); }
@ -170,6 +170,7 @@ define([
$left.show();
$right.show();
$cke.show();
$(window).trigger('resize');
};
// Buttons actions
@ -203,6 +204,7 @@ define([
// Display the latest content
render(get(c));
$(window).trigger('resize');
};
// Load all the history messages into a new chainpad object

@ -39,7 +39,7 @@ define([
var MutationObserver = window.MutationObserver;
var displayDefault = function () {
var text = Cryptpad.getFirstEmojiOrCharacter(name);
var $avatar = $('<span>', {'class': 'default'}).text(text);
var $avatar = $('<span>', {'class': 'cp-avatar-default'}).text(text);
$container.append($avatar);
if (cb) { cb(); }
};
@ -207,7 +207,7 @@ define([
var $displayName = $userAdmin.find('.'+displayNameCls);
var $avatar = $userAdmin.find('.buttonTitle');
var $avatar = $userAdmin.find('.cp-dropdown-button-title');
var oldUrl;
var updateButton = function () {
var myData = metadataMgr.getUserData();
@ -220,7 +220,7 @@ define([
UI.displayAvatar(Common, $avatar, url, newName, function ($img) {
oldUrl = url;
if ($img) {
$userAdmin.find('button').addClass('avatar');
$userAdmin.find('button').addClass('cp-avatar');
}
});
}
@ -261,5 +261,43 @@ define([
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;
});

@ -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 () {
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);
exp.title = md.title;
//exp.updateTitle(md.title || md.defaultTitle);

@ -7,12 +7,13 @@ define([
'/common/sframe-common-title.js',
'/common/sframe-common-interface.js',
'/common/sframe-common-history.js',
'/common/sframe-common-file.js',
'/common/metadata-manager.js',
'/customize/application_config.js',
'/common/cryptpad-common.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) {
// Chainpad Netflux Inner
@ -36,6 +37,9 @@ define([
funcs.getCryptpadCommon = function () {
return Cryptpad;
};
funcs.getSframeChannel = function () {
return ctx.sframeChan;
};
var isLoggedIn = funcs.isLoggedIn = function () {
if (!ctx.cpNfInner) { throw new Error("cpNfInner is not ready!"); }
@ -51,6 +55,9 @@ define([
// UI
funcs.createUserAdminMenu = UI.createUserAdminMenu;
funcs.displayAvatar = UI.displayAvatar;
funcs.initFilePicker = UI.initFilePicker;
funcs.openFilePicker = UI.openFilePicker;
funcs.openTemplatePicker = UI.openTemplatePicker;
// History
funcs.getHistory = function (config) { return History.create(funcs, config); };
@ -107,6 +114,26 @@ define([
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
var pendingFriends = [];
funcs.getPendingFriends = function () {
@ -133,7 +160,7 @@ define([
url: href,
});
};
var prepareFeedback = function (key) {
var prepareFeedback = funcs.prepareFeedback = function (key) {
if (typeof(key) !== 'string') { return $.noop; }
var type = ctx.metadataMgr.getMetadata().type;
@ -155,7 +182,7 @@ define([
button = $('<button>', {
'class': 'fa fa-download',
title: Messages.exportButtonTitle,
}).append($('<span>', {'class': 'drawer'}).text(Messages.exportButton));
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.exportButton));
button.click(prepareFeedback(type));
if (callback) {
@ -166,7 +193,7 @@ define([
button = $('<button>', {
'class': 'fa fa-upload',
title: Messages.importButtonTitle,
}).append($('<span>', {'class': 'drawer'}).text(Messages.importButton));
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.importButton));
if (callback) {
button
.click(prepareFeedback(type))
@ -175,6 +202,30 @@ define([
}, {accept: data ? data.accept : undefined}));
}
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':
if (!AppConfig.enableTemplates) { return; }
button = $('<button>', {
@ -230,7 +281,7 @@ define([
style: 'font:'+size+' FontAwesome'
});
if (!isStrongestStored()) {
button.addClass('hidden');
button.addClass('cp-toolbar-hidden');
}
if (callback) {
button
@ -259,7 +310,7 @@ define([
button = $('<button>', {
title: Messages.historyButton,
'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) {
button
.click(prepareFeedback(type))
@ -271,7 +322,7 @@ define([
case 'more':
button = $('<button>', {
title: Messages.moreActions || 'TODO',
'class': "drawer-button fa fa-ellipsis-h",
'class': "cp-toolbar-drawer-button fa fa-ellipsis-h",
style: 'font:'+size+' FontAwesome'
});
break;
@ -283,11 +334,19 @@ define([
.click(prepareFeedback(type));
}
if (rightside) {
button.addClass('rightside-button');
button.addClass('cp-toolbar-rightside-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) {
ctx.sframeChan.query('Q_STORE_LINK_TO_CLIPBOARD', readOnly, function (err) {
if (cb) { cb(err); }

@ -82,4 +82,29 @@ define({
// Set the tab notification when the content of the pad changes
'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
}).appendTo($topContainer);
$('<span>', {'class': LIMIT_CLS}).hide().appendTo($userContainer);
$('<span>', {'class': NEWPAD_CLS + ' dropdown-bar'}).hide().appendTo($userContainer);
$('<span>', {'class': USERADMIN_CLS + ' dropdown-bar'}).hide().appendTo($userContainer);
$('<span>', {'class': NEWPAD_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);
$('<span>', {'class': USERADMIN_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);
$toolbar.append($topContainer)
.append($('<div>', {'class': LEFTSIDE_CLS}))
@ -75,7 +75,7 @@ define([
var $rightside = $toolbar.find('.'+RIGHTSIDE_CLS);
if (!config.hideDrawer) {
var $drawerContent = $('<div>', {
'class': DRAWER_CLS,// + ' dropdown-bar-content cryptpad-dropdown'
'class': DRAWER_CLS,
'tabindex': 1
}).appendTo($rightside).hide();
var $drawer = Cryptpad.createButton('more', true).appendTo($rightside);
@ -270,7 +270,7 @@ define([
var fa_editusers = '<span class="fa fa-users"></span>';
var fa_viewusers = '<span class="fa fa-eye"></span>';
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);
};
@ -311,7 +311,7 @@ define([
var $container = $('<span>', {id: 'userButtons', title: Messages.userListButton});
var $button = $('<button>').appendTo($container);
$('<span>',{'class': 'buttonTitle'}).appendTo($button);
$('<span>',{'class': 'cp-dropdown-button-title'}).appendTo($button);
toolbar.$leftside.prepend($container);
@ -444,7 +444,7 @@ define([
};
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
//$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.find('button').attr('title', Messages.shareButton);
@ -731,9 +731,6 @@ define([
};
var createNewPad = function (toolbar) {
/*var $newPad = $('<span>', {
'class': NEWPAD_CLS + " dropdown-bar"
}).appendTo(toolbar.$top);*/
var $newPad = toolbar.$top.find('.'+NEWPAD_CLS).show();
var pads_options = [];
@ -822,7 +819,7 @@ define([
// Events
var initClickEvents = function (toolbar, config) {
var removeDropdowns = function () {
toolbar.$toolbar.find('.cryptpad-dropdown').hide();
toolbar.$toolbar.find('.cp-dropdown-content').hide();
};
var cancelEditTitle = function (e) {
// Now we want to apply the title even if we click somewhere else

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

@ -24,6 +24,11 @@ define([
var NEW_FOLDER_NAME = Messages.fm_newFolder;
var NEW_FILE_NAME = Messages.fm_newFile;
exp.ROOT = ROOT;
exp.UNSORTED = UNSORTED;
exp.TRASH = TRASH;
exp.TEMPLATE = TEMPLATE;
// Logging
var logging = function () {
console.log.apply(console, arguments);
@ -151,6 +156,7 @@ define([
if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); }
var data = getFileData(id);
data[attr] = clone(value);
cb(null);
};
// PATHS
@ -849,6 +855,7 @@ define([
}
try {
debug("Migrating file system...");
Cryptpad.feedback('Migrate-oldFilesData', true);
files.migrate = 1;
var next = function () {
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 {
margin-right: 5px;
}
.buttonTitle {
.cp-dropdown-button-title {
display: inline-flex;
height: @toolbar-line-height;
align-items: center;
@ -730,13 +730,13 @@ span {
}
}
/* The container <div> - needed to position the dropdown content */
.dropdown-bar {
.cp-dropdown-container {
margin: 2px 2px;
line-height: 1em;
position: relative;
display: inline-block;
}
.dropdown-bar-content {
.cp-dropdown-content {
margin-right: 2px;
}

@ -1154,23 +1154,8 @@ define([
// This is duplicated in cryptpad-common, it should be unified
var getFileIcon = function (id) {
var $icon = Cryptpad.getIcon();
var data = filesOp.getFileData(id);
if (!data) { return $icon; }
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;
return Cryptpad.getFileIcon(data);
};
var getIcon = Cryptpad.getIcon;
@ -1356,10 +1341,6 @@ define([
// Create the button allowing the user to switch from list to icons modes
var createViewModeButton = function ($container) {
/*var $block = $('<div>', {
'class': 'dropdown-bar right changeViewModeContainer'
});*/
var $listButton = $listIcon.clone().addClass('element');
var $gridButton = $gridIcon.clone().addClass('element');
@ -1495,7 +1476,7 @@ define([
};
var hideNewButton = function () {
$iframe.find('.dropdown-bar-content').hide();
$iframe.find('.cp-dropdown-content').hide();
};
var SORT_FOLDER_DESC = 'sortFoldersDesc';
@ -2004,7 +1985,7 @@ define([
createTitle($toolbar.find('.path'), path);
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.appendTo($toolbar.find('.rightside'));
$context.click(function (e) {
@ -2301,7 +2282,7 @@ define([
$trashContextMenu.hide();
$contentContextMenu.hide();
$defaultContextMenu.hide();
$iframe.find('.cryptpad-dropdown').hide();
$iframe.find('.cp-dropdown-content').hide();
};
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
$(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) {
e.stopPropagation();
return false;
@ -2919,6 +2900,8 @@ define([
};
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');
$backupButton.append($backupIcon.clone().css('marginRight', '0px'));
$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 (i * cypherChunkLength < u8.length) { // not done
if ((2 + metadataLength + i * cypherChunkLength) < u8.length) { // not done
chunks.push(plaintext);
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');
$(window).click(function () {
$('.cryptpad-dropdown').hide();
$('.cp-dropdown-content').hide();
});
// main block is hidden in case javascript is disabled

@ -22,7 +22,7 @@ define([
$userAdmin.find('button').addClass('btn').addClass('btn-secondary');
$(window).click(function () {
$('.cryptpad-dropdown').hide();
$('.cp-dropdown-content').hide();
});
// 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>
<html class="cp pad">
<html class="cp-app-noscroll">
<head>
<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>
<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>
<body class="app-pad">
<body class="cp-app-pad">
<textarea style="display:none" id="editor1" name="editor1"></textarea>
</body>
</html>

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

@ -3,20 +3,12 @@ define([
'/bower_components/nthen/index.js',
'/api/config',
'jquery',
'/common/requireconfig.js'
], function (nThen, ApiConfig, $, RequireConfig) {
'/common/requireconfig.js',
'/common/sframe-common-outer.js'
], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) {
var requireConfig = RequireConfig();
// Loaded in load #2
var CpNfOuter;
var Cryptpad;
var Crypto;
var Cryptget;
var sframeChan;
var secret;
var hashes;
nThen(function (waitFor) {
$(waitFor());
}).nThen(function (waitFor) {
@ -43,201 +35,7 @@ define([
_done();
};
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*/) {
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 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();
SFCommonO.start();
});
});

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

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

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

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

Loading…
Cancel
Save