Kanban application using the cryptpad framework

pull/1/head
Ludovic Dubost 7 years ago
parent 4b83f2991a
commit 035a56fdab

@ -886,12 +886,5 @@ define([
]; ];
}; };
Pages['/kanban/'] = Pages['/kanban/index.html'] = function () {
return [
appToolbar(),
h('div#cp-app-kanban-content', [])
];
};
return Pages; return Pages;
}); });

@ -41,4 +41,5 @@ body.cp-app-profile { @import "../../../profile/app-profile.less"; }
body.cp-app-settings { @import "../../../settings/app-settings.less"; } body.cp-app-settings { @import "../../../settings/app-settings.less"; }
body.cp-app-debug { @import "../../../debug/app-debug.less"; } body.cp-app-debug { @import "../../../debug/app-debug.less"; }
body.cp-app-worker { @import "../../../worker/app-worker.less"; } body.cp-app-worker { @import "../../../worker/app-worker.less"; }
body.cp-app-miniapp { @import "../../../miniapp/app-miniapp.less"; }

@ -1,671 +1,39 @@
@import (once) "../../customize/src/less2/include/browser.less"; @import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less"; @import (once) "../../customize/src/less2/include/framework.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';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@import (once) '../../customize/src/less2/include/tools.less';
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/creation.less';
.toolbar_main( .framework_main( @bg-color: @colortheme_code-bg,
@bg-color: @colortheme_kanban-bg, @warn-color: @colortheme_code-warn,
@warn-color: @colortheme_kanban-warn, @color: @colortheme_code-color);
@color: @colortheme_kanban-color
);
.fileupload_main();
.alertify_main();
.tokenfield_main();
.creation_main();
@kanban-fore: #555; // body
&.cp-app-kanban {
@kanban-th-bg: @colortheme_kanban-th-bg;
@kanban-th-fg: @colortheme_kanban-th-fg;
@kanban-th-user-bg: darken(@kanban-th-bg, 10%);
@kanban-editing: lighten(@kanban-th-bg, 10%);
@kanban-winner: darken(@kanban-th-bg, 15%);
@kanban-td-bg: @kanban-th-bg;
@kanban-td-fg: @kanban-th-fg;
@kanban-help-bg: @colortheme_kanban-help-bg;
@kanban-uncommitted-cell: #eee;
@kanban-uncommitted-bg: #ddd; //lighten(@kanban-th-bg, 50%);
@kanban-uncommitted-text: black;
@kanban-placeholder: #fff;
@kanban-border-color: #555;
@kanban-cover-color: #000;
@kanban-fg: #000;
@kanban-option-yellow: #ff5;
@kanban-option-gray: #ccc;
@kanban-add-color: #fff;
@kanban-add-bg: #777;
@kanban-add-bg-alt: #444;
.bottom-left(@s: 5px) { border-bottom-left-radius: @s; }
.top-left(@s: 5px) { border-top-left-radius: @s; }
display: flex;
flex-flow: column;
overflow-x: hidden;
#cp-app-kanban-content {
display: flex; display: flex;
flex: 1; flex-flow: column;
min-height: 0; max-height: 100%;
#cp-app-kanban-form { min-height: auto;
flex: 1;
overflow-y: auto;
&.cp-app-kanban-readonly {
#cp-app-kanban-table-scroll {
max-width: ~"calc(75% - 30px - 100px)";
}
table {
width: 100%;
}
table tr td:last-child {
margin-left: 0; // uncommitted is hidden
}
td.cp-app-kanban-table-uncommitted {
display: none;
}
}
&.cp-app-kanban-published {
#cp-app-kanban-create-option {
display: none;
}
.cp-app-kanban-table-remove[data-rt-id^="y"], .cp-app-kanban-table-edit[data-rt-id^="y"] {
display: none;
}
tr.cp-app-kanban-table-uncommitted {
display: none;
}
}
}
}
input[type="text"], textarea {
background-color: white;
color: black;
border: 0;
}
input[type="text"][disabled], textarea[disabled] {
background-color: transparent;
border: 0px;
}
// The placeholder color only seems to effect Safari when not set
input[type="text"][disabled]::placeholder {
color: @kanban-placeholder;
opacity: 1;
}
table#cp-app-kanban-table { #cp-app-kanban-container {
margin: 0px;
overflow: hidden;
}
#cp-app-kanban-table-container {
position: relative;
margin: 20px;
}
#cp-app-kanban-table-container button {
border-radius: 0;
border: 0;
}
#cp-app-kanban-create-user {
display: inline-flex; display: inline-flex;
height: 20px; flex-flow: column;
padding: 0 5px; height: 100%;
margin: 2px auto; min-height: 100%;
width: auto; width: 200%;
resize: horizontal;
overflow: hidden; overflow: hidden;
color: @kanban-add-color;
background: @kanban-add-bg;
&:hover {
background: @kanban-add-bg-alt;
}
}
#cp-app-kanban-create-option {
order: 3;
display: inline-flex;
width: 46px;
height: 20px;
margin: 2px;
padding: 0;
color: @kanban-add-color;
background: @kanban-add-bg;
&:hover {
background: @kanban-add-bg-alt;
}
}
#cp-app-kanban-table-scroll {
overflow-y: hidden;
overflow-x: auto;
margin-left: 25%;
max-width: ~"calc(75% - 100px - 100px)";
width: auto;
display: inline-block;
}
.cp-markdown-toolbar {
margin: auto;
min-width: 80%;
width: 80%;
}
#cp-app-kanban-description {
&~ .CodeMirror {
margin: auto;
min-width: 80%;
width: 80%;
min-height: 200px;
height: 200px;
border: 1px solid black;
.CodeMirror-placeholder {
color: #777;
}
}
}
#cp-app-kanban-description-published {
display: none;
padding: 15px;
margin: auto;
min-width: 80%;
width: 80%;
min-height: 7em;
color: #000;
border: 1px solid transparent;
background-color: #eeeeee;
font: @colortheme_app-font;
text-align: left;
media-tag > * {
max-width: 100%;
max-height: 20em;
}
}
div.cp-app-kanban-published {
div.cp-app-kanban-realtime {
#cp-app-kanban-description {
display: none;
&~ .CodeMirror {
display: none;
}
}
#cp-app-kanban-description-published {
display: block;
&:empty {
display: none;
}
} }
#cp-app-kanban-nocomments { #cp-app-kanban-editor {
display: none;
}
#cp-app-kanban-comments {
display: block;
}
}
}
#cp-app-kanban-help {
width: 100%;
margin: auto;
padding: 20px 10%;
background: @kanban-help-bg;
}
// from cryptpad.less
table {
border-collapse: collapse;
border-spacing: 0;
margin: 20px;
}
tbody {
* {
box-sizing: border-box;
}
tr {
text-align: center;
}
td {
.tools_unselectable();
border-right: 1px solid @kanban-border-color;
padding: 12px;
padding-top: 0px;
padding-bottom: 0px;
&:last-child {
border-right: none;
}
}
}
div.cp-app-kanban-realtime {
display: block;
max-height: 100%;
max-width: 100%;
input {
&[type="text"] {
height: 1em;
margin: 0px;
}
}
> textarea {
width: 50%;
height: 15vh;
}
padding: 0px;
margin: 0px;
.cp-app-kanban-table-scrolled {
tr td:last-child {
right: 0;
}
tr td:nth-last-child(2) {
right: 100px;
}
}
table {
border-collapse: collapse;
width: ~"calc(100% - 1px)";
.cp-app-kanban-table-editing {
background-color: @kanban-editing;
}
.cp-app-kanban-table-uncommitted {
.cp-app-kanban-table-cover {
background-color: @kanban-uncommitted-cell !important;
}
div.cp-app-kanban-table-text-cell {
background-color: @kanban-uncommitted-bg !important;
color: @kanban-uncommitted-text !important;
}
text-align: center;
background-color: @kanban-uncommitted-bg !important;
color: @kanban-uncommitted-text !important;
}
tr {
height: 28px;
/* Options */
td:first-child {
position:absolute;
left: 0;
top: auto;
width: 25%;
}
/* Uncommitted column */
td:nth-last-child(2) {
position: absolute;
top: auto;
width: 100px;
min-width: unset !important;
height: auto !important;
}
/* Results */
td:last-child {
color: @kanban-th-fg;
position:absolute;
top: auto;
margin-left: 100px;
width: 100px;
min-width: unset !important;
background-color: @kanban-th-bg;
}
td {
padding: 0px;
margin: 0px;
div.cp-app-kanban-table-text-cell {
height: 28px;
padding: 0px;
margin: 0px;
display: flex;
align-items: center;
.cp-app-kanban-table-remove {
order: 1;
}
.cp-app-kanban-table-edit {
order: 3;
}
input {
min-width: 0;
order: 2;
flex: 1; flex: 1;
height: 24px;
border: 0px;
margin: 2px;
&[disabled] {
background-color: transparent;
color: @kanban-td-fg;
//font-weight: bold;
}
}
}
&.cp-app-kanban-table-checkbox-cell {
margin: 0px;
padding: 0px;
height: 100%;
min-width: 100px;
div.cp-app-kanban-table-checkbox-contain {
display: inline-block;
height: 100%;
width: 100%;
position: relative;
label {
background-color: transparent;
display: block;
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
}
input {
&[type="number"] {
&:not(.editable) {
display: none;
~ .cp-app-kanban-table-cover {
line-height: 28px;
display: block;
font-weight: bold;
height: 100%;
display: block;
color: @kanban-cover-color;
&:after {
height: 100%;
}
}
}
}
}
input[type="number"][value="0"] {
~ .cp-app-kanban-table-cover {
background-color: @colortheme_cp-red;
&:after { content: "✖"; }
}
}
input[type="number"][value="1"] {
~ .cp-app-kanban-table-cover {
background-color: @colortheme_cp-green;
&:after { content: "✔"; }
}
}
input[type="number"][value="2"] {
~ .cp-app-kanban-table-cover {
background-color: @kanban-option-yellow;
&:after { content: "~"; }
}
}
input[type="number"][value="3"] {
~ .cp-app-kanban-table-cover {
background-color: @kanban-option-gray;
&:after { content: "?"; }
}
}
}
}
}
}
input {
&[type="text"] {
height: auto;
width: 80%;
}
}
span {
.tools_unselectable();
}
thead {
height: 52px;
tr {
height: 52px;
}
td {
padding: 0px 5px;
background: @kanban-th-bg;
color: @kanban-th-fg;
&:not(:last-child) {
border-right: 1px solid rgba(255,255,255,0.2);
}
&:last-child {
height: 52px;
line-height: 52px;
text-align: center;
}
&:nth-last-child(2) {
border-right: 1px solid @kanban-border-color;
}
&.cp-app-kanban-table-own {
background: @kanban-th-user-bg;
.cp-app-kanban-table-lock {
cursor: default;
}
}
.cp-app-kanban-table-buttons {
display: flex; display: flex;
justify-content: space-between; flex-flow: row;
flex-wrap: wrap; height: 100%;
align-items: center;
span {
cursor: pointer;
width: 1em;
text-align: center;
}
.cp-app-kanban-table-bookmark {
color: darken(@kanban-th-fg, 30%);
&.cp-app-kanban-table-bookmark-full {
color: @kanban-th-fg;
}
}
}
input {
&[type="text"] {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
break-after: always;
width: ~"calc(100% - 2px)"; // borders...
box-sizing: border-box;
padding: 1px 5px;
margin: 1px;
&[disabled] {
color: @kanban-th-fg;
}
}
}
}
}
tbody {
td:first-child {
background: @kanban-td-bg;
color: @kanban-td-fg;
}
td.cp-app-kanban-table-winner {
background-color: @kanban-winner;
&:last-child { font-weight: bold; }
}
.cp-app-kanban-table-text-cell {
input[type="text"] {
width: ~"calc(100% - 50px)";
padding: 0 0.5em;
}
.cp-app-kanban-table-edit {
float:right;
margin: 2px 10px 0 0;
}
.cp-app-kanban-table-remove {
float: left;
margin: 2px 0 0 10px;
}
}
tr:not(:first-child) {
td:not(:first-child) {
label {
border-top: 1px solid @kanban-border-color;
}
}
}
}
.cp-app-kanban-table-edit {
//color: @kanban-cover-color;
cursor: pointer;
float: left;
margin-left: 10px;
} }
thead { @media (max-width: @browser_media-medium-screen) {
tr { #cp-app-kanban-container {
th {
input[type="text"][disabled] {
background-color: transparent;
color: @kanban-fore;
font-weight: bold;
}
.cp-app-kanban-table-remove {
cursor: pointer;
font-size: 20px;
}
}
}
}
tbody {
tr {
td {
}
}
}
tfoot {
display: none;
}
}
#cp-app-kanban-nocomments {
color: #999;
text-align: center;
margin: 20px;
font: @colortheme_app-font;
}
#cp-app-kanban-comments {
width: 50%;
margin: 20px auto;
min-width: 400px;
padding-bottom: 5px;
display: none;
button {
border-radius: 0;
}
#cp-app-kanban-comments-add {
input, textarea {
border: 1px solid black;
width: 90%;
margin: 5px 5%;
}
input {
padding: 5px;
height: 26px;
&[disabled] {
background: #eee;
}
}
textarea {
padding: 5px;
height: 8em;
line-height: 1.5em;
}
button {
padding: 10px;
}
text-align: center;
}
#cp-app-kanban-comments-list {
.cp-app-kanban-comments-list-el {
width: 90%;
margin: 5px 5%;
}
.cp-app-kanban-comments-list-msg {
display: flex;
background: #eee;
padding: 5px 10px;
.cp-app-kanban-comments-list-msg-text {
flex: 1;
white-space: pre-wrap;
}
.cp-app-kanban-comments-list-msg-actions {
button {
padding: 0;
width: 25px;
line-height: 20px;
}
}
}
.cp-app-kanban-comments-list-data {
background: #ddd;
padding: 5px 10px;
display: flex;
align-items: center;
.cp-app-kanban-comments-list-data-name {
margin-left: 10px;
flex: 1; flex: 1;
}
.cp-app-kanban-comments-list-data-avatar { .avatar_main(30px); }
}
}
}
@media screen and (max-width: 500px) {
#cp-app-kanban-table-scroll {
max-width: 100%; max-width: 100%;
padding: 0; resize: none;
margin: 0;
table {
tr {
td {
&:first-child {
position: unset;
min-width: 100px;
&:hover:not(:empty) {
position: absolute;
min-width: 100px;
width: auto;
z-index: 100;
}
} }
&:nth-last-child(2) {
position: unset;
} }
&:last-child {
position: unset;
}
}
}
}
}
#cp-app-kanban-comments {
min-width: 90%;
}
}
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
} }

@ -1,38 +1,37 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>CryptPad</title> <title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" /> <meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script> <script async data-bootload="/common/sframe-app-outer.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<style> <style>
html, body { html,
body {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
} }
#sbox-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;
}
#sbox-filePicker-iframe {
position: fixed; position: fixed;
top:0; left:0; top: 0px;
bottom:0; right:0; left: 0px;
width:100%; bottom: 0px;
right: 0px;
width: 100%;
height: 100%; height: 100%;
border: 0; border: none;
margin: 0;
padding: 0;
overflow: hidden;
} }
</style> </style>
</head> </head>
<body> <body>
<iframe id="sbox-iframe"> <iframe id="sbox-iframe">
</iframe>
</body>
</html>

@ -1,43 +1,23 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="cp-app-noscroll"> <html class="cp-app-noscroll">
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta content="text/html; charset=utf-8" http-equiv="content-type" />
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script async data-bootload="/kanban/inner.js" data-main="/common/sframe-boot.js?ver=1.6" src="/bower_components/requirejs/require.js?ver=2.3.5"></script> <script async data-bootload="/kanban/inner.js" data-main="/common/sframe-boot.js?ver=1.6" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<style> <style>
.loading-hidden { display: none; } .loading-hidden {
#editor1 { display: none; } display: none;
html, body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
border: 0px;
}
margin: 0;
padding: 0;
}
#myKanban {
overflow-x: auto;
padding: 20px 0;
}
.success {
background: #00B961;
}
.info {
background: #2A92BF;
}
.warning {
background: #F4CE46;
}
.error {
background: #FB7D44;
} }
</style>
</head> </head>
<body class="cp-app-kanban"> <body class="cp-app-kanban">
<div id="cme_toolbox" class="cp-toolbar-container"></div>
<div id="cp-app-kanban-editor">
<div id="cp-app-kanban-container">
<div id="cp-app-kanban-content"></div>
</div>
</div>
</body> </body>
</html>

@ -1,335 +1,26 @@
define([ define([
'jquery', 'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/common/toolbar3.js',
'json.sortify',
'/common/common-util.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/common-interface.js', '/common/sframe-app-framework.js',
'/api/config', '/common/common-util.js',
'/common/common-realtime.js', '/common/common-hash.js',
'/customize/pages.js', '/common/modes.js',
'/customize/messages.js', '/customize/messages.js',
'/customize/application_config.js',
'/common/common-thumbnail.js',
'/bower_components/chainpad/chainpad.dist.js',
'/bower_components/secure-fabric.js/dist/fabric.min.js',
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'css!/kanban/jkanban.css',
'/kanban/jkanban.js', '/kanban/jkanban.js',
'css!/kanban/jkanban.css',
], function ( ], function (
$, $,
Crypto,
Toolbar,
JSONSortify,
Util,
nThen, nThen,
SFCommon, SFCommon,
UI, Framework,
ApiConfig, Util,
CommonRealtime, Hash,
Pages, Modes,
Messages, Messages) {
AppConfig,
Thumb,
ChainPad)
{
var saveAs = window.saveAs;
var APP = window.APP = {
$: $
};
var Fabric = APP.Fabric = window.fabric;
var stringify = function (obj) {
return JSONSortify(obj);
};
var toolbar;
var andThen = function (common) {
var config = {};
/* Initialize Fabric */
var canvas = APP.canvas = null;
var $canvas = $('canvas');
var readOnly = false;
var setEditable = function (bool) {
APP.editable = bool;
if (readOnly && bool) { return; }
};
var saveImage = APP.saveImage = function () {
};
APP.FM = common.createFileManager({});
APP.upload = function (title) {
};
var initializing = true;
var $bar = $('#cp-toolbar');
var Title;
var cpNfInner;
var metadataMgr;
config = {
readOnly: readOnly,
patchTransformer: ChainPad.NaiveJSONTransformer,
// 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 true;
}
}
};
var stringifyInner = function (textValue) {
var obj = {
content: textValue,
metadata: metadataMgr.getMetadataLazy()
};
// stringify the json and send it into chainpad
return stringify(obj);
};
var onLocal = config.onLocal = function () {
if (initializing) { return; }
if (readOnly) { return; }
var content = stringifyInner(Kanban.getBoardsJSON());
console.log("Updating content " + content);
try {
APP.realtime.contentUpdate(content);
} catch (e) {
APP.unrecoverable = true;
setEditable(false);
APP.toolbar.errorState(true, e.message);
var msg = Messages.chainpadError;
UI.errorLoadingScreen(msg, true, true);
console.error(e);
}
};
var initThumbnails = function () {
var oldThumbnailState;
var privateDat = metadataMgr.getPrivateData();
if (!privateDat.thumbnails) { return; }
var hash = privateDat.availableHashes.editHash ||
privateDat.availableHashes.viewHash;
var href = privateDat.pathname + '#' + hash;
var mkThumbnail = function () {
if (!hash) { return; }
if (initializing) { return; }
if (!APP.realtime) { return; }
var content = APP.realtime.getUserDoc();
if (content === oldThumbnailState) { return; }
/*
var D = Thumb.getResizedDimensions($canvas[0], 'pad');
Thumb.fromCanvas($canvas[0], D, function (err, b64) {
oldThumbnailState = content;
Thumb.setPadThumbnail(common, href, b64);
});
*/
};
window.setInterval(mkThumbnail, Thumb.UPDATE_INTERVAL);
window.setTimeout(mkThumbnail, Thumb.UPDATE_FIRST);
};
config.onInit = function (info) {
readOnly = metadataMgr.getPrivateData().readOnly;
Title = common.createTitle({});
var configTb = {
displayed: [
'userlist',
'title',
'useradmin',
'spinner',
'newpad',
'share',
'limit',
'unpinnedWarning'
],
title: Title.getTitleConfig(),
metadataMgr: metadataMgr,
readOnly: readOnly,
realtime: info.realtime,
sfCommon: common,
$container: $bar,
$contentContainer: $('#cp-app-kanban-content')
};
toolbar = APP.toolbar = Toolbar.create(configTb);
Title.setToolbar(toolbar);
var $rightside = toolbar.$rightside;
var $drawer = toolbar.$drawer;
/* save as template */
if (!metadataMgr.getPrivateData().isTemplate) {
var templateObj = {
rt: info.realtime,
getTitle: function () { return metadataMgr.getMetadata().title; }
};
var $templateButton = common.createButton('template', true, templateObj);
$rightside.append($templateButton);
}
/* add an export button */
var $export = common.createButton('export', true, {}, saveImage);
$drawer.append($export);
if (common.isLoggedIn()) {
common.createButton('savetodrive', true, {}, function () {})
.click(function () {
UI.prompt(Messages.exportPrompt, document.title + '.png',
function (name) {
if (name === null || !name.trim()) { return; }
APP.upload(name);
});
}).appendTo($rightside);
common.createButton('hashtag', true).appendTo($rightside);
}
var $forget = common.createButton('forget', true, {}, function (err) {
if (err) { return; }
setEditable(false);
});
$rightside.append($forget);
var $properties = common.createButton('properties', true);
toolbar.$drawer.append($properties);
var $appContainer = $('#cp-app-kanban-container');
var helpMenu = common.createHelpMenu(['kanban']);
$appContainer.prepend(helpMenu.menu);
toolbar.$drawer.append(helpMenu.button);
metadataMgr.onChange(function () {
var md = metadataMgr.getMetadata();
});
};
config.onReady = function (info) {
if (APP.realtime !== info.realtime) {
APP.realtime = info.realtime;
}
var userDoc = APP.realtime.getUserDoc();
var isNew = false;
var newDoc = '';
if (userDoc === "" || userDoc === "{}") { isNew = true; }
if (userDoc !== "") {
var hjson = JSON.parse(userDoc);
if (hjson && hjson.metadata) {
metadataMgr.updateMetadata(hjson.metadata);
}
if (typeof (hjson) !== 'object' || Array.isArray(hjson) ||
(hjson.metadata && typeof(hjson.metadata.type) !== 'undefined' &&
hjson.metadata.type !== 'kanban')) {
var errorText = Messages.typeError;
UI.errorLoadingScreen(errorText);
throw new Error(errorText);
}
newDoc = hjson.content;
// launch the kanban code
config.initKanban(newDoc);
} else {
Title.updateTitle(Title.defaultTitle);
config.initKanban();
}
nThen(function (waitFor) {
if (newDoc) {
}
}).nThen(function () {
setEditable(!readOnly);
initializing = false;
config.onLocal();
UI.removeLoadingScreen();
initThumbnails();
if (readOnly) { return; }
var privateDat = metadataMgr.getPrivateData();
var skipTemp = Util.find(privateDat,
['settings', 'general', 'creation', 'noTemplate']);
var skipCreation = Util.find(privateDat, ['settings', 'general', 'creation', 'skip']);
if (isNew && (!AppConfig.displayCreationScreen || (!skipTemp && skipCreation))) {
common.openTemplatePicker();
}
});
};
config.onRemote = function () {
if (initializing) { return; }
var userDoc = APP.realtime.getUserDoc();
console.log("Received content: " + userDoc);
var json = JSON.parse(userDoc);
var remoteDoc = json.content;
var currentContent = stringify(Kanban.getBoardsJSON());
var remoteContent = stringify(json.content);
if (currentContent !== remoteContent) {
// reinit kanban (TODO: optimize to diff only)
console.log("Content is different.. Applying content");
config.Kanban.setBoards(remoteDoc);
common.notify();
}
if (readOnly) { setEditable(false); }
};
config.onAbort = function () {
if (APP.unrecoverable) { return; }
// inform of network disconnect
setEditable(false);
toolbar.failed();
UI.alert(Messages.common_connectionLost, undefined, true);
};
config.onConnectionChange = function (info) {
if (APP.unrecoverable) { return; }
setEditable(info.state);
if (info.state) {
initializing = true;
//UI.findOKButton().click();
} else {
//UI.alert(Messages.common_connectionLost, undefined, true);
}
};
config.onError = function (err) {
common.onServerError(err, toolbar, function () {
APP.unrecoverable = true;
setEditable(false);
});
};
config.initKanban = function(boards) { // Kanban code
var initKanban = function (framework, boards) {
var defaultBoards = [ var defaultBoards = [
{ {
"id": "todo", "id": "todo",
@ -371,7 +62,7 @@ define([
] ]
}]; }];
if (boards==null) { if (boards == null) {
console.log("Initializing with default boards content"); console.log("Initializing with default boards content");
boards = defaultBoards; boards = defaultBoards;
} else { } else {
@ -381,20 +72,20 @@ define([
// Remove any existing elements // Remove any existing elements
$(".kanban-container-outer").remove(); $(".kanban-container-outer").remove();
window.Kanban = config.Kanban = new jKanban({ var kanban = new jKanban({
element: '#cp-app-kanban-content', element: '#cp-app-kanban-content',
gutter: '15px', gutter: '15px',
widthBoard: '300px', widthBoard: '300px',
onChange: function() { onChange: function () {
console.log("Board object has changed"); console.log("Board object has changed");
config.onLocal(); framework.localChange();
} },
,click: function (el) { click: function (el) {
if (Kanban.inEditMode) { if (kanban.inEditMode) {
console.log("An edit is already active"); console.log("An edit is already active");
return; return;
} }
Kanban.inEditMode = true; kanban.inEditMode = true;
var name = $(el).text(); var name = $(el).text();
$(el).html(''); $(el).html('');
$('<input></input>') $('<input></input>')
@ -407,25 +98,25 @@ config.onLocal();
}) })
.appendTo(el); .appendTo(el);
$('#kanban_edit').focus(); $('#kanban_edit').focus();
$('#kanban_edit').blur(function() { $('#kanban_edit').blur(function () {
var name = $('#kanban_edit').val(); var name = $('#kanban_edit').val();
$(el).text(name); $(el).text(name);
var board = $(el.parentNode.parentNode).attr("data-id"); var board = $(el.parentNode.parentNode).attr("data-id");
var pos = Kanban.findElementPosition(el); var pos = kanban.findElementPosition(el);
console.log(pos); console.log(pos);
console.log(board); console.log(board);
Kanban.getBoardJSON(board).item[pos].title = name; kanban.getBoardJSON(board).item[pos].title = name;
Kanban.onChange(); kanban.onChange();
Kanban.inEditMode = false; kanban.inEditMode = false;
}); });
}, },
boardTitleClick: function (el) { boardTitleClick: function (el) {
if (Kanban.inEditMode) { if (kanban.inEditMode) {
console.log("An edit is already active"); console.log("An edit is already active");
return; return;
} }
Kanban.inEditMode = true; kanban.inEditMode = true;
var name = $(el).text(); var name = $(el).text();
$(el).html(''); $(el).html('');
$('<input></input>') $('<input></input>')
@ -438,80 +129,82 @@ boardTitleClick: function (el) {
}) })
.appendTo(el); .appendTo(el);
$('#kanban_edit').focus(); $('#kanban_edit').focus();
$('#kanban_edit').blur(function() { $('#kanban_edit').blur(function () {
var name = $('#kanban_edit').val(); var name = $('#kanban_edit').val();
$(el).text(name); $(el).text(name);
var board = $(el.parentNode.parentNode).attr("data-id"); var board = $(el.parentNode.parentNode).attr("data-id");
Kanban.getBoardJSON(board).title = name; kanban.getBoardJSON(board).title = name;
Kanban.onChange(); kanban.onChange();
Kanban.inEditMode = false; kanban.inEditMode = false;
}); });
}, },
colorClick: function (el, boardId) { colorClick: function (el, boardId) {
console.log("in color click"); console.log("in color click");
var board = $(el.parentNode).attr("data-id"); var board = $(el.parentNode).attr("data-id");
var boardJSON = Kanban.getBoardJSON(board); var boardJSON = kanban.getBoardJSON(board);
var currentColor = boardJSON.color; var currentColor = boardJSON.color;
console.log("Current color " + currentColor); console.log("Current color " + currentColor);
var index = Kanban.options.colors.findIndex(function(element) { return (element==currentColor) }) + 1; var index = kanban.options.colors.findIndex(function (element) {
return (element == currentColor)
}) + 1;
console.log("Next index " + index); console.log("Next index " + index);
if (index>=Kanban.options.colors.length) if (index >= kanban.options.colors.length)
index = 0; index = 0;
var nextColor = Kanban.options.colors[index]; var nextColor = kanban.options.colors[index];
console.log("Next color " + nextColor); console.log("Next color " + nextColor);
boardJSON.color = nextColor; boardJSON.color = nextColor;
$(el).removeClass("kanban-header-" + currentColor); $(el).removeClass("kanban-header-" + currentColor);
$(el).addClass("kanban-header-" + nextColor); $(el).addClass("kanban-header-" + nextColor);
Kanban.onChange(); kanban.onChange();
}, },
removeClick: function(el, boardId) { removeClick: function (el, boardId) {
if (confirm("Do you want to delete this board?")) { if (confirm("Do you want to delete this board?")) {
console.log("Delete board"); console.log("Delete board");
var boardName = $(el.parentNode.parentNode).attr("data-id"); var boardName = $(el.parentNode.parentNode).attr("data-id");
for (index in Kanban.options.boards) { for (index in kanban.options.boards) {
if (Kanban.options.boards[index].id == boardName) { if (kanban.options.boards[index].id == boardName) {
break; break;
} }
index++; index++;
} }
Kanban.options.boards.splice(index, 1); kanban.options.boards.splice(index, 1);
Kanban.removeBoard(boardName); kanban.removeBoard(boardName);
Kanban.onChange(); kanban.onChange();
} }
}, },
buttonClick: function (el, boardId) { buttonClick: function (el, boardId) {
console.log(el); console.log(el);
console.log(boardId); console.log(boardId);
// create a form to enter element // create a form to enter element
var formItem = document.createElement('form'); var formItem = document.createElement('form');
formItem.setAttribute("class", "itemform"); formItem.setAttribute("class", "itemform");
formItem.innerHTML = '<div class="form-group"><textarea class="form-control" rows="2" autofocus></textarea></div><div class="form-group"><button type="submit" class="btn btn-primary btn-xs">Submit</button><button type="button" id="CancelBtn" class="btn btn-default btn-xs pull-right">Cancel</button></div>' formItem.innerHTML = '<div class="form-group"><textarea class="form-control" rows="2" autofocus></textarea></div><div class="form-group"><button type="submit" class="btn btn-primary btn-xs">Submit</button><button type="button" id="CancelBtn" class="btn btn-default btn-xs pull-right">Cancel</button></div>'
Kanban.addForm(boardId, formItem); kanban.addForm(boardId, formItem);
formItem.addEventListener("submit", function (e) { formItem.addEventListener("submit", function (e) {
e.preventDefault(); e.preventDefault();
var text = e.target[0].value var text = e.target[0].value
Kanban.addElement(boardId, { kanban.addElement(boardId, {
"title": text, "title": text,
}) })
formItem.parentNode.removeChild(formItem); formItem.parentNode.removeChild(formItem);
}); });
document.getElementById('CancelBtn').onclick = function () { document.getElementById('CancelBtn').onclick = function () {
formItem.parentNode.removeChild(formItem) formItem.parentNode.removeChild(formItem)
} }
}, },
addItemButton: true, addItemButton: true,
boards: boards boards: boards
}); });
var addBoardDefault = document.getElementById('kanban-addboard'); var addBoardDefault = document.getElementById('kanban-addboard');
addBoardDefault.addEventListener('click', function () { addBoardDefault.addEventListener('click', function () {
var counter = 1; var counter = 1;
found = false; found = false;
while (found) { while (found) {
for (var board in Kanban.options.boards) { for (var board in kanban.options.boards) {
if (board.id == "board" + counter) { if (board.id == "board" + counter) {
counter++; counter++;
break; break;
@ -520,9 +213,9 @@ addBoardDefault.addEventListener('click', function () {
found = true; found = true;
} }
Kanban.addBoards( kanban.addBoards(
[{ [{
"id" : "board" + counter, "id": "board" + counter,
"title": "New Board", "title": "New Board",
"color": "yellow", "color": "yellow",
"item": [ "item": [
@ -532,96 +225,58 @@ addBoardDefault.addEventListener('click', function () {
] ]
}] }]
) )
Kanban.onChange(); kanban.onChange();
}); });
/* return kanban;
var toDoButton = document.getElementById('addToDo'); };
toDoButton.addEventListener('click', function () {
Kanban.addElement(
"_todo",
{
"title": "Test Add",
}
);
});
var addBoardDefault = document.getElementById('addDefault'); // Start of the main loop
addBoardDefault.addEventListener('click', function () { var andThen2 = function (framework) {
Kanban.addBoards(
[{
"id": "_default",
"title": "Kanban Default",
"item": [
{
"title": "Default Item",
},
{
"title": "Default Item 2",
},
{
"title": "Default Item 3",
}
]
}]
)
});
var removeBoard = document.getElementById('removeBoard'); var kanban = initKanban(framework);
removeBoard.addEventListener('click', function () {
Kanban.removeBoard('_done');
});
var removeElement = document.getElementById('removeElement'); framework.onContentUpdate(function (newContent) {
removeElement.addEventListener('click', function () { // Need to update the content
Kanban.removeElement('_test_delete'); console.log("Content should be updated to " + newContent);
}); var currentContent = kanban.getBoardsJSON();
var remoteContent = newContent.content;
var allEle = Kanban.getBoardElements('_todo'); if (currentContent !== remoteContent) {
allEle.forEach(function (item, index) { // reinit kanban (TODO: optimize to diff only)
//console.log(item); console.log("Content is different.. Applying content");
kanban.setBoards(remoteContent);
}
}); });
*/
};
cpNfInner = common.startRealtime(config); framework.setContentGetter(function () {
metadataMgr = cpNfInner.metadataMgr; // var content = $("#cp-app-kanban-content").val();
var content = kanban.getBoardsJSON();
cpNfInner.onInfiniteSpinner(function () { console.log("Content current value is " + content);
if (APP.unrecoverable) { return; } return {
setEditable(false); content: content
UI.confirm(Messages.realtime_unrecoverableError, function (yes) { };
if (!yes) { return; }
common.gotoURL();
});
}); });
$('#save').on('click', function () { framework.onReady(function (newPad) {
$("#cp-app-kanban-content").focus();
}); });
framework.start();
common.onLogout(function () { setEditable(false); });
}; };
var main = function () { var main = function () {
var common; // var framework;
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(function () {
UI.addLoadingScreen(); // Framework initialization
var $div = $('<div>').append(Pages['/kanban/']()); Framework.create({
$('body').append($div.html()); toolbarContainer: '#cme_toolbox',
$('body').addClass("cp-app-kanban"); contentContainer: '#cp-app-kanban-editor',
}, waitFor(function (fw) {
framework = fw;
andThen2(framework);
})); }));
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (waitFor) {
common.getSframeChannel().onReady(waitFor());
}).nThen(function (waitFor) {
common.handleNewFile(waitFor);
}).nThen(function (/*waitFor*/) {
andThen(common);
}); });
}; };
main(); main();

@ -1,4 +1,3 @@
.kanban-container-outer { .kanban-container-outer {
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
@ -30,7 +29,7 @@
vertical-align: top; vertical-align: top;
} }
.kanban-board.disabled-board{ .kanban-board.disabled-board {
opacity: .3; opacity: .3;
} }
@ -92,11 +91,25 @@
height: auto !important; height: auto !important;
} }
.kanban-header-yellow { background: #FC3; } .kanban-header-yellow {
.kanban-header-orange { background: #F91; } background: #FC3;
.kanban-header-blue { background: #0AC; } }
.kanban-header-red { background: #E43; }
.kanban-header-green { background: #8C4; } .kanban-header-orange {
background: #F91;
}
.kanban-header-blue {
background: #0AC;
}
.kanban-header-red {
background: #E43;
}
.kanban-header-green {
background: #8C4;
}
#kanban-addboard { #kanban-addboard {
float: left; float: left;
@ -123,6 +136,7 @@
} }
/* Dragula CSS */ /* Dragula CSS */
.gu-mirror { .gu-mirror {
position: fixed !important; position: fixed !important;
margin: 0 !important; margin: 0 !important;

File diff suppressed because it is too large Load Diff

@ -1,25 +0,0 @@
html, body {
margin: 0px;
padding: 0px;
}
#sbox-iframe {
position:fixed;
top:0px;
left:0px;
bottom:0px;
right:0px;
width:100%;
height:100%;
border:none;
margin:0;
padding:0;
overflow:hidden;
}
#sbox-filePicker-iframe {
position: fixed;
top:0; left:0;
bottom:0; right:0;
width:100%;
height: 100%;
border: 0;
}

@ -1,43 +0,0 @@
// 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',
'/common/dom-ready.js',
'/common/requireconfig.js',
'/common/sframe-common-outer.js',
], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) {
var requireConfig = RequireConfig();
// Loaded in load #2
nThen(function (waitFor) {
DomReady.onReady(waitFor());
}).nThen(function (waitFor) {
var req = {
cfg: requireConfig,
req: [ '/common/loading.js' ],
pfx: window.location.origin
};
window.rc = requireConfig;
window.apiconf = ApiConfig;
document.getElementById('sbox-iframe').setAttribute('src',
ApiConfig.httpSafeOrigin + '/kanban/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({
useCreationScreen: true
});
});
});
Loading…
Cancel
Save