Merge branch 'kanban' into staging
This commit is contained in:
commit
2170fcb9ad
@ -2,6 +2,7 @@ node_modules/
|
|||||||
www/bower_components/
|
www/bower_components/
|
||||||
www/common/pdfjs/
|
www/common/pdfjs/
|
||||||
www/common/tippy/
|
www/common/tippy/
|
||||||
|
www/common/jquery-ui/
|
||||||
|
|
||||||
server.js
|
server.js
|
||||||
www/common/media-tag.js
|
www/common/media-tag.js
|
||||||
@ -14,6 +15,8 @@ www/pad/wysiwygarea-plugin.js
|
|||||||
www/pad/mediatag-plugin.js
|
www/pad/mediatag-plugin.js
|
||||||
www/pad/mediatag-plugin-dialog.js
|
www/pad/mediatag-plugin-dialog.js
|
||||||
|
|
||||||
|
www/kanban/jkanban.js
|
||||||
|
|
||||||
www/common/media-tag-nacl.min.js
|
www/common/media-tag-nacl.min.js
|
||||||
|
|
||||||
customize/
|
customize/
|
||||||
|
|||||||
@ -56,6 +56,7 @@ define([
|
|||||||
footLink('/code/', 'main_code'),
|
footLink('/code/', 'main_code'),
|
||||||
footLink('/slide/', 'main_slide'),
|
footLink('/slide/', 'main_slide'),
|
||||||
footLink('/poll/', 'main_poll'),
|
footLink('/poll/', 'main_poll'),
|
||||||
|
footLink('/kanban/', 'main_kanban'),
|
||||||
footLink('/whiteboard/', null, Msg.type.whiteboard)
|
footLink('/whiteboard/', null, Msg.type.whiteboard)
|
||||||
]),
|
]),
|
||||||
footerCol('footer_aboutUs', [
|
footerCol('footer_aboutUs', [
|
||||||
@ -566,6 +567,7 @@ define([
|
|||||||
[ 'code', '/code/', Msg.main_codePad, 'fa-file-code-o' ],
|
[ 'code', '/code/', Msg.main_codePad, 'fa-file-code-o' ],
|
||||||
[ 'slide', '/slide/', Msg.main_slidePad, 'fa-file-powerpoint-o' ],
|
[ 'slide', '/slide/', Msg.main_slidePad, 'fa-file-powerpoint-o' ],
|
||||||
[ 'poll', '/poll/', Msg.main_pollPad, 'fa-calendar' ],
|
[ 'poll', '/poll/', Msg.main_pollPad, 'fa-calendar' ],
|
||||||
|
[ 'kanban', '/kanban/', Msg.main_kanbanPad, 'fa-calendar' ],
|
||||||
[ 'whiteboard', '/whiteboard/', Msg.main_whiteboardPad, 'fa-paint-brush' ],
|
[ 'whiteboard', '/whiteboard/', Msg.main_whiteboardPad, 'fa-paint-brush' ],
|
||||||
[ 'recent', '/drive/', Msg.main_localPads, 'fa-hdd-o' ]
|
[ 'recent', '/drive/', Msg.main_localPads, 'fa-hdd-o' ]
|
||||||
].filter(function (x) {
|
].filter(function (x) {
|
||||||
|
|||||||
@ -115,6 +115,10 @@
|
|||||||
@colortheme_todo-color: #000;
|
@colortheme_todo-color: #000;
|
||||||
@colortheme_todo-warn: #cd2532;
|
@colortheme_todo-warn: #cd2532;
|
||||||
|
|
||||||
|
@colortheme_kanban-bg: #8C4;
|
||||||
|
@colortheme_kanban-color: #fff;
|
||||||
|
@colortheme_kanban-warn: #e6385d;
|
||||||
|
|
||||||
// Sidebar layout (profile / settings)
|
// Sidebar layout (profile / settings)
|
||||||
@colortheme_sidebar-active: #fff;
|
@colortheme_sidebar-active: #fff;
|
||||||
@colortheme_sidebar-left-bg: #eee;
|
@colortheme_sidebar-left-bg: #eee;
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
.cp-icon-color-profile { color: @colortheme_settings-bg; }
|
.cp-icon-color-profile { color: @colortheme_settings-bg; }
|
||||||
.cp-icon-color-default { color: @colortheme_default-bg; }
|
.cp-icon-color-default { color: @colortheme_default-bg; }
|
||||||
.cp-icon-color-todo { color: @colortheme_todo-bg; }
|
.cp-icon-color-todo { color: @colortheme_todo-bg; }
|
||||||
|
.cp-icon-color-kanban { color: @colortheme_kanban-bg; }
|
||||||
|
|
||||||
.cp-border-color-pad { border-color: @colortheme_pad-bg !important; }
|
.cp-border-color-pad { border-color: @colortheme_pad-bg !important; }
|
||||||
.cp-border-color-code { border-color: @colortheme_code-bg !important; }
|
.cp-border-color-code { border-color: @colortheme_code-bg !important; }
|
||||||
@ -26,5 +27,6 @@
|
|||||||
.cp-border-color-profile { border-color: @colortheme_settings-bg !important; }
|
.cp-border-color-profile { border-color: @colortheme_settings-bg !important; }
|
||||||
.cp-border-color-default { border-color: @colortheme_default-bg !important; }
|
.cp-border-color-default { border-color: @colortheme_default-bg !important; }
|
||||||
.cp-border-color-todo { border-color: @colortheme_todo-bg !important; }
|
.cp-border-color-todo { border-color: @colortheme_todo-bg !important; }
|
||||||
|
.cp-border-color-kanban { border-color: @colortheme_kanban-bg !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +1,31 @@
|
|||||||
@import (once) "./tools.less";
|
@import (once) "./tools.less";
|
||||||
|
|
||||||
.tokenfield_main () {
|
.tokenfield_main () {
|
||||||
|
.ui-autocomplete {
|
||||||
|
z-index: 100001; // alertify + 1
|
||||||
|
}
|
||||||
.tokenfield {
|
.tokenfield {
|
||||||
.tools_unselectable();
|
.tools_unselectable();
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-evenly;
|
||||||
height: auto;
|
height: auto;
|
||||||
min-height: 34px;
|
min-height: 34px;
|
||||||
padding-bottom: 0px;
|
padding-bottom: 0px;
|
||||||
background-color: unset;
|
background-color: unset;
|
||||||
border: none;
|
border: none;
|
||||||
display: flex;
|
margin: 0 10px;
|
||||||
flex-wrap: wrap;
|
padding: 0;
|
||||||
align-items: center;
|
width: ~"calc(100% - 20px)";
|
||||||
padding: 0 10px;
|
|
||||||
.token {
|
.token {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
border: 1px solid #d9d9d9;
|
border: 1px solid #d9d9d9;
|
||||||
background-color: #ededed;
|
background-color: #ededed;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin: 10px 5px;
|
margin: 2px 0;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
@ -50,7 +56,7 @@
|
|||||||
.close {
|
.close {
|
||||||
font-family: Arial;
|
font-family: Arial;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
line-height: 24px;
|
line-height: 1.49em;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
float: none;
|
float: none;
|
||||||
@ -73,6 +79,8 @@
|
|||||||
margin: 0 !important; // Override alertify
|
margin: 0 !important; // Override alertify
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 100% !important;
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tools_unselectable () {
|
.tools_unselectable () {
|
||||||
|
user-select: none;
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-khtml-user-select: none;
|
-khtml-user-select: none;
|
||||||
|
|||||||
@ -40,4 +40,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-kanban { @import "../../../kanban/app-kanban.less"; }
|
||||||
|
|
||||||
|
|||||||
@ -154,6 +154,7 @@ h4 {
|
|||||||
.cp-callout-code .fa { background-color: @colortheme_code-bg; }
|
.cp-callout-code .fa { background-color: @colortheme_code-bg; }
|
||||||
.cp-callout-slide .fa { background-color: @colortheme_slide-bg; }
|
.cp-callout-slide .fa { background-color: @colortheme_slide-bg; }
|
||||||
.cp-callout-poll .fa { background-color: @colortheme_poll-bg; }
|
.cp-callout-poll .fa { background-color: @colortheme_poll-bg; }
|
||||||
|
.cp-callout-kanban .fa { background-color: @colortheme_kanban-bg; }
|
||||||
.cp-callout-whiteboard .fa { background-color: @colortheme_whiteboard-bg; }
|
.cp-callout-whiteboard .fa { background-color: @colortheme_whiteboard-bg; }
|
||||||
.cp-callout-recent .fa { background-color: @colortheme_drive-bg; }
|
.cp-callout-recent .fa { background-color: @colortheme_drive-bg; }
|
||||||
.cp-hidden { display: none !important; }
|
.cp-hidden { display: none !important; }
|
||||||
|
|||||||
@ -8,6 +8,7 @@ define(function () {
|
|||||||
out.type.pad = 'Texte';
|
out.type.pad = 'Texte';
|
||||||
out.type.code = 'Code';
|
out.type.code = 'Code';
|
||||||
out.type.poll = 'Sondage';
|
out.type.poll = 'Sondage';
|
||||||
|
out.type.kanban = 'Kanban';
|
||||||
out.type.slide = 'Présentation';
|
out.type.slide = 'Présentation';
|
||||||
out.type.drive = 'CryptDrive';
|
out.type.drive = 'CryptDrive';
|
||||||
out.type.whiteboard = "Tableau Blanc";
|
out.type.whiteboard = "Tableau Blanc";
|
||||||
@ -21,6 +22,7 @@ define(function () {
|
|||||||
out.button_newpoll = 'Nouveau sondage';
|
out.button_newpoll = 'Nouveau sondage';
|
||||||
out.button_newslide = 'Nouvelle présentation';
|
out.button_newslide = 'Nouvelle présentation';
|
||||||
out.button_newwhiteboard = 'Nouveau tableau blanc';
|
out.button_newwhiteboard = 'Nouveau tableau blanc';
|
||||||
|
out.button_newkanban = 'Nouveau kanban';
|
||||||
|
|
||||||
out.updated_0_common_connectionLost = "<b>Connexion au serveur perdue</b><br>Vous êtes désormais en mode lecture seule jusqu'au retour de la connexion.";
|
out.updated_0_common_connectionLost = "<b>Connexion au serveur perdue</b><br>Vous êtes désormais en mode lecture seule jusqu'au retour de la connexion.";
|
||||||
out.common_connectionLost = out.updated_0_common_connectionLost;
|
out.common_connectionLost = out.updated_0_common_connectionLost;
|
||||||
@ -246,6 +248,15 @@ define(function () {
|
|||||||
out.pad_mediatagWidth = "Largeur (px)";
|
out.pad_mediatagWidth = "Largeur (px)";
|
||||||
out.pad_mediatagHeight = "Hauteur (px)";
|
out.pad_mediatagHeight = "Hauteur (px)";
|
||||||
|
|
||||||
|
// Kanban
|
||||||
|
out.kanban_newBoard = "Nouveau tableau";
|
||||||
|
out.kanban_item = "Élément {0}"; // Item number for initial content
|
||||||
|
out.kanban_todo = "À faire";
|
||||||
|
out.kanban_done = "Terminé";
|
||||||
|
out.kanban_working = "En cours";
|
||||||
|
out.kanban_deleteBoard = "Êtes-vous sûr de vouloir supprimer ce tableau ?";
|
||||||
|
out.kanban_deleteItem = "Êtes-vous sûr de vouloir supprimer cet élément?";
|
||||||
|
|
||||||
// Polls
|
// Polls
|
||||||
|
|
||||||
out.poll_title = "Sélecteur de date Zero Knowledge";
|
out.poll_title = "Sélecteur de date Zero Knowledge";
|
||||||
@ -367,6 +378,7 @@ define(function () {
|
|||||||
out.fm_searchName = "Recherche";
|
out.fm_searchName = "Recherche";
|
||||||
out.fm_recentPadsName = "Pads récents";
|
out.fm_recentPadsName = "Pads récents";
|
||||||
out.fm_ownedPadsName = "Pads en votre possession";
|
out.fm_ownedPadsName = "Pads en votre possession";
|
||||||
|
out.fm_tagsName = "Mots-clés";
|
||||||
out.fm_searchPlaceholder = "Rechercher...";
|
out.fm_searchPlaceholder = "Rechercher...";
|
||||||
out.fm_newButton = "Nouveau";
|
out.fm_newButton = "Nouveau";
|
||||||
out.fm_newButtonTitle = "Créer un nouveau pad ou un dossier, importer un fichier dans le dossier courant";
|
out.fm_newButtonTitle = "Créer un nouveau pad ou un dossier, importer un fichier dans le dossier courant";
|
||||||
@ -429,6 +441,8 @@ define(function () {
|
|||||||
out.fm_padIsOwned = "Vous êtes le propriétaire de ce pad";
|
out.fm_padIsOwned = "Vous êtes le propriétaire de ce pad";
|
||||||
out.fm_padIsOwnedOther = "Ce pad est la propriété d'un autre utilisateur";
|
out.fm_padIsOwnedOther = "Ce pad est la propriété d'un autre utilisateur";
|
||||||
out.fm_deletedPads = "Ces pads n'existent plus sur le serveur, ils ont été supprimés de votre CryptDrive: {0}";
|
out.fm_deletedPads = "Ces pads n'existent plus sur le serveur, ils ont été supprimés de votre CryptDrive: {0}";
|
||||||
|
out.fm_tags_name = "Mot-clé";
|
||||||
|
out.fm_tags_used = "Nombre d'utilisations";
|
||||||
// File - Context menu
|
// File - Context menu
|
||||||
out.fc_newfolder = "Nouveau dossier";
|
out.fc_newfolder = "Nouveau dossier";
|
||||||
out.fc_rename = "Renommer";
|
out.fc_rename = "Renommer";
|
||||||
|
|||||||
@ -8,6 +8,7 @@ define(function () {
|
|||||||
out.type.pad = 'Rich text';
|
out.type.pad = 'Rich text';
|
||||||
out.type.code = 'Code';
|
out.type.code = 'Code';
|
||||||
out.type.poll = 'Poll';
|
out.type.poll = 'Poll';
|
||||||
|
out.type.kanban = 'Kanban';
|
||||||
out.type.slide = 'Presentation';
|
out.type.slide = 'Presentation';
|
||||||
out.type.drive = 'CryptDrive';
|
out.type.drive = 'CryptDrive';
|
||||||
out.type.whiteboard = 'Whiteboard';
|
out.type.whiteboard = 'Whiteboard';
|
||||||
@ -21,6 +22,7 @@ define(function () {
|
|||||||
out.button_newpoll = 'New Poll';
|
out.button_newpoll = 'New Poll';
|
||||||
out.button_newslide = 'New Presentation';
|
out.button_newslide = 'New Presentation';
|
||||||
out.button_newwhiteboard = 'New Whiteboard';
|
out.button_newwhiteboard = 'New Whiteboard';
|
||||||
|
out.button_newkanban = 'New Kanban';
|
||||||
|
|
||||||
// NOTE: Remove updated_0_ if we need an updated_1_
|
// NOTE: Remove updated_0_ if we need an updated_1_
|
||||||
out.updated_0_common_connectionLost = "<b>Server Connection Lost</b><br>You're now in read-only mode until the connection is back.";
|
out.updated_0_common_connectionLost = "<b>Server Connection Lost</b><br>You're now in read-only mode until the connection is back.";
|
||||||
@ -248,6 +250,15 @@ define(function () {
|
|||||||
out.pad_mediatagWidth = "Width (px)";
|
out.pad_mediatagWidth = "Width (px)";
|
||||||
out.pad_mediatagHeight = "Height (px)";
|
out.pad_mediatagHeight = "Height (px)";
|
||||||
|
|
||||||
|
// Kanban
|
||||||
|
out.kanban_newBoard = "New board";
|
||||||
|
out.kanban_item = "Item {0}"; // Item number for initial content
|
||||||
|
out.kanban_todo = "To Do";
|
||||||
|
out.kanban_done = "Done";
|
||||||
|
out.kanban_working = "Working";
|
||||||
|
out.kanban_deleteBoard = "Are you sure you want to delete this board?";
|
||||||
|
out.kanban_deleteItem = "Are you sure you want to delete this item?";
|
||||||
|
|
||||||
// Polls
|
// Polls
|
||||||
|
|
||||||
out.poll_title = "Zero Knowledge Date Picker";
|
out.poll_title = "Zero Knowledge Date Picker";
|
||||||
@ -368,6 +379,7 @@ define(function () {
|
|||||||
out.fm_searchName = "Search";
|
out.fm_searchName = "Search";
|
||||||
out.fm_recentPadsName = "Recent pads";
|
out.fm_recentPadsName = "Recent pads";
|
||||||
out.fm_ownedPadsName = "Owned";
|
out.fm_ownedPadsName = "Owned";
|
||||||
|
out.fm_tagsName = "Tags";
|
||||||
out.fm_searchPlaceholder = "Search...";
|
out.fm_searchPlaceholder = "Search...";
|
||||||
out.fm_newButton = "New";
|
out.fm_newButton = "New";
|
||||||
out.fm_newButtonTitle = "Create a new pad or folder, import a file in the current folder";
|
out.fm_newButtonTitle = "Create a new pad or folder, import a file in the current folder";
|
||||||
@ -430,6 +442,8 @@ define(function () {
|
|||||||
out.fm_padIsOwned = "You are the owner of this pad";
|
out.fm_padIsOwned = "You are the owner of this pad";
|
||||||
out.fm_padIsOwnedOther = "This pad is owned by another user";
|
out.fm_padIsOwnedOther = "This pad is owned by another user";
|
||||||
out.fm_deletedPads = "These pads no longer exist on the server, they've been removed from your CryptDrive: {0}";
|
out.fm_deletedPads = "These pads no longer exist on the server, they've been removed from your CryptDrive: {0}";
|
||||||
|
out.fm_tags_name = "Tag name";
|
||||||
|
out.fm_tags_used = "Number of uses";
|
||||||
// File - Context menu
|
// File - Context menu
|
||||||
out.fc_newfolder = "New folder";
|
out.fc_newfolder = "New folder";
|
||||||
out.fc_rename = "Rename";
|
out.fc_rename = "Rename";
|
||||||
|
|||||||
@ -9,7 +9,7 @@ define(function() {
|
|||||||
/* Select the buttons displayed on the main page to create new collaborative sessions
|
/* Select the buttons displayed on the main page to create new collaborative sessions
|
||||||
* Existing types : pad, code, poll, slide
|
* Existing types : pad, code, poll, slide
|
||||||
*/
|
*/
|
||||||
config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'whiteboard', 'file', 'todo', 'contacts'];
|
config.availablePadTypes = ['drive', 'pad', 'code', 'slide', 'poll', 'kanban', 'whiteboard', 'file', 'todo', 'contacts'];
|
||||||
config.registeredOnlyTypes = ['file', 'contacts'];
|
config.registeredOnlyTypes = ['file', 'contacts'];
|
||||||
|
|
||||||
/* Cryptpad apps use a common API to display notifications to users
|
/* Cryptpad apps use a common API to display notifications to users
|
||||||
@ -81,6 +81,7 @@ define(function() {
|
|||||||
whiteboard: 'fa-paint-brush',
|
whiteboard: 'fa-paint-brush',
|
||||||
todo: 'fa-tasks',
|
todo: 'fa-tasks',
|
||||||
contacts: 'fa-users',
|
contacts: 'fa-users',
|
||||||
|
kanban: 'fa-list-alt',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ability to create owned pads and expiring pads through a new pad creation screen.
|
// Ability to create owned pads and expiring pads through a new pad creation screen.
|
||||||
|
|||||||
@ -12,8 +12,10 @@ define([
|
|||||||
'/customize/loading.js',
|
'/customize/loading.js',
|
||||||
'/common/test.js',
|
'/common/test.js',
|
||||||
|
|
||||||
|
'/common/jquery-ui/jquery-ui.min.js',
|
||||||
'/bower_components/bootstrap-tokenfield/dist/bootstrap-tokenfield.js',
|
'/bower_components/bootstrap-tokenfield/dist/bootstrap-tokenfield.js',
|
||||||
'css!/common/tippy/tippy.css',
|
'css!/common/tippy/tippy.css',
|
||||||
|
'css!/common/jquery-ui/jquery-ui.min.css'
|
||||||
], function ($, Messages, Util, Hash, Notifier, AppConfig,
|
], function ($, Messages, Util, Hash, Notifier, AppConfig,
|
||||||
Alertify, Tippy, Pages, h, Loading, Test) {
|
Alertify, Tippy, Pages, h, Loading, Test) {
|
||||||
var UI = {};
|
var UI = {};
|
||||||
@ -183,11 +185,17 @@ define([
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
UI.tokenField = function (target) {
|
UI.tokenField = function (target, autocomplete) {
|
||||||
var t = {
|
var t = {
|
||||||
element: target || h('input'),
|
element: target || h('input'),
|
||||||
};
|
};
|
||||||
var $t = t.tokenfield = $(t.element).tokenfield();
|
var $t = t.tokenfield = $(t.element).tokenfield({
|
||||||
|
autocomplete: {
|
||||||
|
source: autocomplete,
|
||||||
|
delay: 100
|
||||||
|
},
|
||||||
|
showAutocompleteOnFocus: false
|
||||||
|
});
|
||||||
|
|
||||||
t.getTokens = function (ignorePending) {
|
t.getTokens = function (ignorePending) {
|
||||||
var tokens = $t.tokenfield('getTokens').map(function (token) {
|
var tokens = $t.tokenfield('getTokens').map(function (token) {
|
||||||
@ -210,10 +218,17 @@ define([
|
|||||||
|
|
||||||
t.preventDuplicates = function (cb) {
|
t.preventDuplicates = function (cb) {
|
||||||
$t.on('tokenfield:createtoken', function (ev) {
|
$t.on('tokenfield:createtoken', function (ev) {
|
||||||
|
// Close the suggest list when a token is added because we're going to wipe the input
|
||||||
|
var $input = $t.closest('.tokenfield').find('.token-input');
|
||||||
|
$input.autocomplete('close');
|
||||||
|
|
||||||
var val;
|
var val;
|
||||||
ev.attrs.value = ev.attrs.value.toLowerCase();
|
ev.attrs.value = ev.attrs.value.toLowerCase();
|
||||||
if (t.getTokens(true).some(function (t) {
|
if (t.getTokens(true).some(function (t) {
|
||||||
if (t === ev.attrs.value) { return ((val = t)); }
|
if (t === ev.attrs.value) {
|
||||||
|
ev.preventDefault();
|
||||||
|
return ((val = t));
|
||||||
|
}
|
||||||
})) {
|
})) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (typeof(cb) === 'function') { cb(val); }
|
if (typeof(cb) === 'function') { cb(val); }
|
||||||
@ -241,7 +256,7 @@ define([
|
|||||||
return t;
|
return t;
|
||||||
};
|
};
|
||||||
|
|
||||||
dialog.tagPrompt = function (tags, cb) {
|
dialog.tagPrompt = function (tags, existing, cb) {
|
||||||
var input = dialog.textInput();
|
var input = dialog.textInput();
|
||||||
|
|
||||||
var tagger = dialog.frame([
|
var tagger = dialog.frame([
|
||||||
@ -255,7 +270,7 @@ define([
|
|||||||
dialog.nav(),
|
dialog.nav(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var field = UI.tokenField(input).preventDuplicates(function (val) {
|
var field = UI.tokenField(input, existing).preventDuplicates(function (val) {
|
||||||
UI.warn(Messages._getKey('tags_duplicate', [val]));
|
UI.warn(Messages._getKey('tags_duplicate', [val]));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -396,7 +411,7 @@ define([
|
|||||||
stopListening(listener);
|
stopListening(listener);
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
listener = listenForKeys(close, close, ok);
|
listener = listenForKeys(close, close);
|
||||||
var $ok = $(ok).click(close);
|
var $ok = $(ok).click(close);
|
||||||
|
|
||||||
document.body.appendChild(frame);
|
document.body.appendChild(frame);
|
||||||
|
|||||||
@ -23,20 +23,27 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
UIElements.updateTags = function (common, href) {
|
UIElements.updateTags = function (common, href) {
|
||||||
var sframeChan = common.getSframeChannel();
|
var existing, tags;
|
||||||
sframeChan.query('Q_TAGS_GET', href || null, function (err, res) {
|
NThen(function(waitFor) {
|
||||||
if (err || res.error) {
|
common.getSframeChannel().query("Q_GET_ALL_TAGS", null, waitFor(function(err, res) {
|
||||||
if (res.error === 'NO_ENTRY') {
|
if (err || res.error) { return void console.error(err || res.error); }
|
||||||
UI.alert(Messages.tags_noentry);
|
existing = Object.keys(res.tags).sort();
|
||||||
|
}));
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
common.getPadAttribute('tags', waitFor(function (err, res) {
|
||||||
|
if (err) {
|
||||||
|
if (err === 'NO_ENTRY') {
|
||||||
|
UI.alert(Messages.tags_noentry);
|
||||||
|
}
|
||||||
|
waitFor.abort();
|
||||||
|
return void console.error(err);
|
||||||
}
|
}
|
||||||
return void console.error(err || res.error);
|
tags = res || [];
|
||||||
}
|
}), href);
|
||||||
UI.dialog.tagPrompt(res.data, function (tags) {
|
}).nThen(function () {
|
||||||
if (!Array.isArray(tags)) { return; }
|
UI.dialog.tagPrompt(tags, existing, function (newTags) {
|
||||||
sframeChan.event('EV_TAGS_SET', {
|
if (!Array.isArray(newTags)) { return; }
|
||||||
tags: tags,
|
common.setPadAttribute('tags', newTags, null, href);
|
||||||
href: href,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
7
www/common/jquery-ui/jquery-ui.min.css
vendored
Normal file
7
www/common/jquery-ui/jquery-ui.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
6
www/common/jquery-ui/jquery-ui.min.js
vendored
Normal file
6
www/common/jquery-ui/jquery-ui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -636,18 +636,7 @@ define([
|
|||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
Store.listAllTags = function (data, cb) {
|
Store.listAllTags = function (data, cb) {
|
||||||
var all = [];
|
cb(store.userObject.getTagsList());
|
||||||
var files = Util.find(store.proxy, ['drive', 'filesData']);
|
|
||||||
|
|
||||||
if (typeof(files) !== 'object') { return cb({error: 'invalid_drive'}); }
|
|
||||||
Object.keys(files).forEach(function (k) {
|
|
||||||
var file = files[k];
|
|
||||||
if (!Array.isArray(file.tags)) { return; }
|
|
||||||
file.tags.forEach(function (tag) {
|
|
||||||
if (all.indexOf(tag) === -1) { all.push(tag); }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
cb(all);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Templates
|
// Templates
|
||||||
|
|||||||
@ -393,6 +393,7 @@ define([
|
|||||||
// If we have a stronger hash, use it for pad attributes
|
// If we have a stronger hash, use it for pad attributes
|
||||||
href = window.location.pathname + '#' + hashes.editHash;
|
href = window.location.pathname + '#' + hashes.editHash;
|
||||||
}
|
}
|
||||||
|
if (data.href) { href = data.href; }
|
||||||
Cryptpad.getPadAttribute(data.key, function (e, data) {
|
Cryptpad.getPadAttribute(data.key, function (e, data) {
|
||||||
cb({
|
cb({
|
||||||
error: e,
|
error: e,
|
||||||
@ -406,6 +407,7 @@ define([
|
|||||||
// If we have a stronger hash, use it for pad attributes
|
// If we have a stronger hash, use it for pad attributes
|
||||||
href = window.location.pathname + '#' + hashes.editHash;
|
href = window.location.pathname + '#' + hashes.editHash;
|
||||||
}
|
}
|
||||||
|
if (data.href) { href = data.href; }
|
||||||
Cryptpad.setPadAttribute(data.key, data.value, function (e) {
|
Cryptpad.setPadAttribute(data.key, data.value, function (e) {
|
||||||
cb({error:e});
|
cb({error:e});
|
||||||
}, href);
|
}, href);
|
||||||
@ -573,19 +575,6 @@ define([
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
sframeChan.on('Q_TAGS_GET', function (data, cb) {
|
|
||||||
Cryptpad.getPadTags(data, function (err, data) {
|
|
||||||
cb({
|
|
||||||
error: err,
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
sframeChan.on('EV_TAGS_SET', function (data) {
|
|
||||||
Cryptpad.resetTags(data.href, data.tags);
|
|
||||||
});
|
|
||||||
|
|
||||||
sframeChan.on('Q_PIN_GET_USAGE', function (data, cb) {
|
sframeChan.on('Q_PIN_GET_USAGE', function (data, cb) {
|
||||||
Cryptpad.isOverPinLimit(function (err, overLimit, data) {
|
Cryptpad.isOverPinLimit(function (err, overLimit, data) {
|
||||||
cb({
|
cb({
|
||||||
@ -606,6 +595,15 @@ define([
|
|||||||
Cryptpad.removeOwnedChannel(channel, cb);
|
Cryptpad.removeOwnedChannel(channel, cb);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sframeChan.on('Q_GET_ALL_TAGS', function (data, cb) {
|
||||||
|
Cryptpad.listAllTags(function (err, tags) {
|
||||||
|
cb({
|
||||||
|
error: err,
|
||||||
|
tags: tags
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (cfg.addRpc) {
|
if (cfg.addRpc) {
|
||||||
cfg.addRpc(sframeChan, Cryptpad, Utils);
|
cfg.addRpc(sframeChan, Cryptpad, Utils);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -235,17 +235,20 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
funcs.getPadAttribute = function (key, cb) {
|
// href is optional here: if not provided, we use the href of the current tab
|
||||||
|
funcs.getPadAttribute = function (key, cb, href) {
|
||||||
ctx.sframeChan.query('Q_GET_PAD_ATTRIBUTE', {
|
ctx.sframeChan.query('Q_GET_PAD_ATTRIBUTE', {
|
||||||
key: key
|
key: key,
|
||||||
|
href: href
|
||||||
}, function (err, res) {
|
}, function (err, res) {
|
||||||
cb (err || res.error, res.data);
|
cb (err || res.error, res.data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
funcs.setPadAttribute = function (key, value, cb) {
|
funcs.setPadAttribute = function (key, value, cb, href) {
|
||||||
cb = cb || $.noop;
|
cb = cb || $.noop;
|
||||||
ctx.sframeChan.query('Q_SET_PAD_ATTRIBUTE', {
|
ctx.sframeChan.query('Q_SET_PAD_ATTRIBUTE', {
|
||||||
key: key,
|
key: key,
|
||||||
|
href: href,
|
||||||
value: value
|
value: value
|
||||||
}, cb);
|
}, cb);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -165,10 +165,6 @@ define({
|
|||||||
// Put one entry in the parent sessionStorage
|
// Put one entry in the parent sessionStorage
|
||||||
'Q_SESSIONSTORAGE_PUT': true,
|
'Q_SESSIONSTORAGE_PUT': true,
|
||||||
|
|
||||||
// Set and get the tags using the tag prompt button
|
|
||||||
'Q_TAGS_GET': true,
|
|
||||||
'EV_TAGS_SET': true,
|
|
||||||
|
|
||||||
// Merge the anonymous drive (FS_hash) into the current logged in user's drive, to keep the pads
|
// Merge the anonymous drive (FS_hash) into the current logged in user's drive, to keep the pads
|
||||||
// in the drive at registration.
|
// in the drive at registration.
|
||||||
'Q_MERGE_ANON_DRIVE': true,
|
'Q_MERGE_ANON_DRIVE': true,
|
||||||
@ -237,4 +233,7 @@ define({
|
|||||||
|
|
||||||
// Loading events to display in the loading screen
|
// Loading events to display in the loading screen
|
||||||
'EV_LOADING_INFO': true,
|
'EV_LOADING_INFO': true,
|
||||||
|
|
||||||
|
// Get all existing tags
|
||||||
|
'Q_GET_ALL_TAGS': true,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -627,6 +627,21 @@ define([
|
|||||||
if (typeof cb === "function") { cb(); }
|
if (typeof cb === "function") { cb(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Tags
|
||||||
|
exp.getTagsList = function () {
|
||||||
|
var tags = {};
|
||||||
|
var data;
|
||||||
|
var pushTag = function (tag) {
|
||||||
|
tags[tag] = tags[tag] ? ++tags[tag] : 1;
|
||||||
|
};
|
||||||
|
for (var id in files[FILES_DATA]) {
|
||||||
|
data = files[FILES_DATA][id];
|
||||||
|
if (!data.tags || !Array.isArray(data.tags)) { continue; }
|
||||||
|
data.tags.forEach(pushTag);
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
};
|
||||||
|
|
||||||
return exp;
|
return exp;
|
||||||
};
|
};
|
||||||
return module;
|
return module;
|
||||||
|
|||||||
@ -462,6 +462,8 @@ span {
|
|||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
}
|
}
|
||||||
.cp-app-drive-search-opendir {
|
.cp-app-drive-search-opendir {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
a {
|
a {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #41b7d8;
|
color: #41b7d8;
|
||||||
@ -495,6 +497,19 @@ span {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.cp-app-drive-tags-list {
|
||||||
|
width: 100%;
|
||||||
|
table {
|
||||||
|
margin: 10px 50px;
|
||||||
|
width: ~"calc(100% - 100px)";
|
||||||
|
table-layout: fixed;
|
||||||
|
td, th {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.cp-app-drive-element {
|
.cp-app-drive-element {
|
||||||
|
|||||||
@ -51,20 +51,65 @@ define([
|
|||||||
|
|
||||||
var E_OVER_LIMIT = 'E_OVER_LIMIT';
|
var E_OVER_LIMIT = 'E_OVER_LIMIT';
|
||||||
|
|
||||||
var SEARCH = "search";
|
|
||||||
var SEARCH_NAME = Messages.fm_searchName;
|
|
||||||
var ROOT = "root";
|
var ROOT = "root";
|
||||||
var ROOT_NAME = Messages.fm_rootName;
|
var ROOT_NAME = Messages.fm_rootName;
|
||||||
|
var SEARCH = "search";
|
||||||
|
var SEARCH_NAME = Messages.fm_searchName;
|
||||||
|
var TRASH = "trash";
|
||||||
|
var TRASH_NAME = Messages.fm_trashName;
|
||||||
var FILES_DATA = Constants.storageKey;
|
var FILES_DATA = Constants.storageKey;
|
||||||
var FILES_DATA_NAME = Messages.fm_filesDataName;
|
var FILES_DATA_NAME = Messages.fm_filesDataName;
|
||||||
var TEMPLATE = "template";
|
var TEMPLATE = "template";
|
||||||
var TEMPLATE_NAME = Messages.fm_templateName;
|
var TEMPLATE_NAME = Messages.fm_templateName;
|
||||||
var TRASH = "trash";
|
|
||||||
var TRASH_NAME = Messages.fm_trashName;
|
|
||||||
var RECENT = "recent";
|
var RECENT = "recent";
|
||||||
var RECENT_NAME = Messages.fm_recentPadsName;
|
var RECENT_NAME = Messages.fm_recentPadsName;
|
||||||
var OWNED = "owned";
|
var OWNED = "owned";
|
||||||
var OWNED_NAME = Messages.fm_ownedPadsName;
|
var OWNED_NAME = Messages.fm_ownedPadsName;
|
||||||
|
var TAGS = "tags";
|
||||||
|
var TAGS_NAME = Messages.fm_tagsName;
|
||||||
|
|
||||||
|
// Icons
|
||||||
|
var faFolder = 'fa-folder';
|
||||||
|
var faFolderOpen = 'fa-folder-open';
|
||||||
|
var faReadOnly = 'fa-eye';
|
||||||
|
var faRename = 'fa-pencil';
|
||||||
|
var faTrash = 'fa-trash';
|
||||||
|
var faDelete = 'fa-eraser';
|
||||||
|
var faProperties = 'fa-database';
|
||||||
|
var faTags = 'fa-hashtag';
|
||||||
|
var faEmpty = 'fa-trash-o';
|
||||||
|
var faRestore = 'fa-repeat';
|
||||||
|
var faShowParent = 'fa-location-arrow';
|
||||||
|
var $folderIcon = $('<span>', {
|
||||||
|
"class": faFolder + " fa cp-app-drive-icon-folder cp-app-drive-content-icon"
|
||||||
|
});
|
||||||
|
//var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
|
||||||
|
var $folderEmptyIcon = $folderIcon.clone();
|
||||||
|
var $folderOpenedIcon = $('<span>', {"class": faFolderOpen + " fa cp-app-drive-icon-folder"});
|
||||||
|
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
|
||||||
|
var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
|
||||||
|
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
|
||||||
|
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
|
||||||
|
var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
|
||||||
|
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
|
||||||
|
var $trashIcon = $('<span>', {"class": "fa " + faTrash});
|
||||||
|
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
|
||||||
|
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
|
||||||
|
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
|
||||||
|
var $emptyTrashIcon = $('<button>', {"class": "fa fa-ban"});
|
||||||
|
var $listIcon = $('<button>', {"class": "fa fa-list"});
|
||||||
|
var $gridIcon = $('<button>', {"class": "fa fa-th-large"});
|
||||||
|
var $sortAscIcon = $('<span>', {"class": "fa fa-angle-up sortasc"});
|
||||||
|
var $sortDescIcon = $('<span>', {"class": "fa fa-angle-down sortdesc"});
|
||||||
|
var $closeIcon = $('<span>', {"class": "fa fa-window-close"});
|
||||||
|
//var $backupIcon = $('<span>', {"class": "fa fa-life-ring"});
|
||||||
|
var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-con"});
|
||||||
|
var $addIcon = $('<span>', {"class": "fa fa-plus"});
|
||||||
|
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
|
||||||
|
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
|
||||||
|
var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"});
|
||||||
|
var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
|
||||||
|
var $tagsIcon = $('<span>', {"class": "fa " + faTags});
|
||||||
|
|
||||||
var LS_LAST = "app-drive-lastOpened";
|
var LS_LAST = "app-drive-lastOpened";
|
||||||
var LS_OPENED = "app-drive-openedFolders";
|
var LS_OPENED = "app-drive-openedFolders";
|
||||||
@ -157,48 +202,6 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Icons
|
|
||||||
var faFolder = 'fa-folder';
|
|
||||||
var faFolderOpen = 'fa-folder-open';
|
|
||||||
var faReadOnly = 'fa-eye';
|
|
||||||
var faRename = 'fa-pencil';
|
|
||||||
var faTrash = 'fa-trash';
|
|
||||||
var faDelete = 'fa-eraser';
|
|
||||||
var faProperties = 'fa-database';
|
|
||||||
var faTags = 'fa-hashtag';
|
|
||||||
var faEmpty = 'fa-trash-o';
|
|
||||||
var faRestore = 'fa-repeat';
|
|
||||||
var faShowParent = 'fa-location-arrow';
|
|
||||||
var $folderIcon = $('<span>', {
|
|
||||||
"class": faFolder + " fa cp-app-drive-icon-folder cp-app-drive-content-icon"
|
|
||||||
});
|
|
||||||
//var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
|
|
||||||
var $folderEmptyIcon = $folderIcon.clone();
|
|
||||||
var $folderOpenedIcon = $('<span>', {"class": faFolderOpen + " fa cp-app-drive-icon-folder"});
|
|
||||||
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
|
|
||||||
var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
|
|
||||||
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
|
|
||||||
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
|
|
||||||
var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
|
|
||||||
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
|
|
||||||
var $trashIcon = $('<span>', {"class": "fa " + faTrash});
|
|
||||||
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
|
|
||||||
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
|
|
||||||
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
|
|
||||||
var $emptyTrashIcon = $('<button>', {"class": "fa fa-ban"});
|
|
||||||
var $listIcon = $('<button>', {"class": "fa fa-list"});
|
|
||||||
var $gridIcon = $('<button>', {"class": "fa fa-th-large"});
|
|
||||||
var $sortAscIcon = $('<span>', {"class": "fa fa-angle-up sortasc"});
|
|
||||||
var $sortDescIcon = $('<span>', {"class": "fa fa-angle-down sortdesc"});
|
|
||||||
var $closeIcon = $('<span>', {"class": "fa fa-window-close"});
|
|
||||||
//var $backupIcon = $('<span>', {"class": "fa fa-life-ring"});
|
|
||||||
var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-con"});
|
|
||||||
var $addIcon = $('<span>', {"class": "fa fa-plus"});
|
|
||||||
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
|
|
||||||
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
|
|
||||||
var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"});
|
|
||||||
var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
|
|
||||||
|
|
||||||
var history = {
|
var history = {
|
||||||
isHistoryMode: false,
|
isHistoryMode: false,
|
||||||
};
|
};
|
||||||
@ -360,10 +363,16 @@ define([
|
|||||||
// Categories dislayed in the menu
|
// Categories dislayed in the menu
|
||||||
// _WORKGROUP_ : do not display unsorted
|
// _WORKGROUP_ : do not display unsorted
|
||||||
var displayedCategories = [ROOT, TRASH, SEARCH, RECENT];
|
var displayedCategories = [ROOT, TRASH, SEARCH, RECENT];
|
||||||
|
|
||||||
|
// PCS enabled: display owned pads
|
||||||
if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
|
if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
|
||||||
|
// Templates enabled: display template category
|
||||||
if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); }
|
if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); }
|
||||||
|
// Tags used: display Tags category
|
||||||
|
if (Object.keys(filesOp.getTagsList()).length) { displayedCategories.push(TAGS); }
|
||||||
|
|
||||||
if (isWorkgroup()) { displayedCategories = [ROOT, TRASH, SEARCH]; }
|
if (isWorkgroup()) { displayedCategories = [ROOT, TRASH, SEARCH]; }
|
||||||
var virtualCategories = [SEARCH, RECENT, OWNED];
|
var virtualCategories = [SEARCH, RECENT, OWNED, TAGS];
|
||||||
|
|
||||||
if (!APP.loggedIn) {
|
if (!APP.loggedIn) {
|
||||||
displayedCategories = [FILES_DATA];
|
displayedCategories = [FILES_DATA];
|
||||||
@ -1444,6 +1453,7 @@ define([
|
|||||||
case SEARCH: pName = SEARCH_NAME; break;
|
case SEARCH: pName = SEARCH_NAME; break;
|
||||||
case RECENT: pName = RECENT_NAME; break;
|
case RECENT: pName = RECENT_NAME; break;
|
||||||
case OWNED: pName = OWNED_NAME; break;
|
case OWNED: pName = OWNED_NAME; break;
|
||||||
|
case TAGS: pName = TAGS_NAME; break;
|
||||||
default: pName = name;
|
default: pName = name;
|
||||||
}
|
}
|
||||||
return pName;
|
return pName;
|
||||||
@ -1512,6 +1522,8 @@ define([
|
|||||||
case OWNED:
|
case OWNED:
|
||||||
msg = Messages.fm_info_owned;
|
msg = Messages.fm_info_owned;
|
||||||
break;
|
break;
|
||||||
|
case TAGS:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
msg = undefined;
|
msg = undefined;
|
||||||
}
|
}
|
||||||
@ -2136,6 +2148,13 @@ define([
|
|||||||
}
|
}
|
||||||
var $openDir = $('<td>', {'class': 'cp-app-drive-search-opendir'}).append($a);
|
var $openDir = $('<td>', {'class': 'cp-app-drive-search-opendir'}).append($a);
|
||||||
|
|
||||||
|
$('<a>').text(Messages.fc_prop).click(function () {
|
||||||
|
APP.getProperties(r.id, function (e, $prop) {
|
||||||
|
if (e) { return void logError(e); }
|
||||||
|
UI.alert($prop[0], undefined, true);
|
||||||
|
});
|
||||||
|
}).appendTo($openDir);
|
||||||
|
|
||||||
// rows 1-3
|
// rows 1-3
|
||||||
$('<tr>').append($icon).append($title).append($typeName).append($type).appendTo($table);
|
$('<tr>').append($icon).append($title).append($typeName).append($type).appendTo($table);
|
||||||
$('<tr>').append($path).append($atimeName).append($atime).appendTo($table);
|
$('<tr>').append($path).append($atimeName).append($atime).appendTo($table);
|
||||||
@ -2226,6 +2245,35 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Tags category
|
||||||
|
var displayTags = function ($container) {
|
||||||
|
var list = filesOp.getTagsList();
|
||||||
|
if (Object.keys(list).length === 0) { return; }
|
||||||
|
var sortedTags = Object.keys(list);
|
||||||
|
sortedTags.sort(function (a, b) {
|
||||||
|
return list[b] - list[a];
|
||||||
|
});
|
||||||
|
var lines = [
|
||||||
|
h('tr', [
|
||||||
|
h('th', Messages.fm_tags_name),
|
||||||
|
h('th', Messages.fm_tags_used)
|
||||||
|
])
|
||||||
|
];
|
||||||
|
sortedTags.forEach(function (tag) {
|
||||||
|
var tagLink = h('a', { href: '#' }, '#' + tag);
|
||||||
|
$(tagLink).click(function () {
|
||||||
|
if (displayedCategories.indexOf(SEARCH) !== -1) {
|
||||||
|
APP.Search.$input.val('#' + tag).keyup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lines.push(h('tr', [
|
||||||
|
h('td', tagLink),
|
||||||
|
h('td.cp-app-drive-tags-used', list[tag])
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
$(h('li.cp-app-drive-tags-list', h('table', lines))).appendTo($container);
|
||||||
|
};
|
||||||
|
|
||||||
// Display the selected directory into the content part (rightside)
|
// Display the selected directory into the content part (rightside)
|
||||||
// NOTE: Elements in the trash are not using the same storage structure as the others
|
// NOTE: Elements in the trash are not using the same storage structure as the others
|
||||||
// _WORKGROUP_ : do not change the lastOpenedFolder value in localStorage
|
// _WORKGROUP_ : do not change the lastOpenedFolder value in localStorage
|
||||||
@ -2255,10 +2303,9 @@ define([
|
|||||||
var isTrashRoot = filesOp.comparePath(path, [TRASH]);
|
var isTrashRoot = filesOp.comparePath(path, [TRASH]);
|
||||||
var isTemplate = filesOp.comparePath(path, [TEMPLATE]);
|
var isTemplate = filesOp.comparePath(path, [TEMPLATE]);
|
||||||
var isAllFiles = filesOp.comparePath(path, [FILES_DATA]);
|
var isAllFiles = filesOp.comparePath(path, [FILES_DATA]);
|
||||||
var isSearch = path[0] === SEARCH;
|
|
||||||
var isRecent = path[0] === RECENT;
|
|
||||||
var isOwned = path[0] === OWNED;
|
|
||||||
var isVirtual = virtualCategories.indexOf(path[0]) !== -1;
|
var isVirtual = virtualCategories.indexOf(path[0]) !== -1;
|
||||||
|
var isSearch = path[0] === SEARCH;
|
||||||
|
var isTags = path[0] === TAGS;
|
||||||
|
|
||||||
var root = isVirtual ? undefined : filesOp.find(path);
|
var root = isVirtual ? undefined : filesOp.find(path);
|
||||||
if (!isVirtual && typeof(root) === "undefined") {
|
if (!isVirtual && typeof(root) === "undefined") {
|
||||||
@ -2292,7 +2339,7 @@ define([
|
|||||||
|
|
||||||
var $dirContent = $('<div>', {id: FOLDER_CONTENT_ID});
|
var $dirContent = $('<div>', {id: FOLDER_CONTENT_ID});
|
||||||
$dirContent.data('path', path);
|
$dirContent.data('path', path);
|
||||||
if (!isSearch) {
|
if (!isSearch && !isTags) {
|
||||||
var mode = getViewMode();
|
var mode = getViewMode();
|
||||||
if (mode) {
|
if (mode) {
|
||||||
$dirContent.addClass(getViewModeClass());
|
$dirContent.addClass(getViewModeClass());
|
||||||
@ -2354,10 +2401,12 @@ define([
|
|||||||
displayTrashRoot($list, $folderHeader, $fileHeader);
|
displayTrashRoot($list, $folderHeader, $fileHeader);
|
||||||
} else if (isSearch) {
|
} else if (isSearch) {
|
||||||
displaySearch($list, path[1]);
|
displaySearch($list, path[1]);
|
||||||
} else if (isRecent) {
|
} else if (path[0] === RECENT) {
|
||||||
displayRecent($list);
|
displayRecent($list);
|
||||||
} else if (isOwned) {
|
} else if (path[0] === OWNED) {
|
||||||
displayOwned($list);
|
displayOwned($list);
|
||||||
|
} else if (isTags) {
|
||||||
|
displayTags($list);
|
||||||
} else {
|
} else {
|
||||||
$dirContent.contextmenu(openContextMenu('content'));
|
$dirContent.contextmenu(openContextMenu('content'));
|
||||||
if (filesOp.hasSubfolder(root)) { $list.append($folderHeader); }
|
if (filesOp.hasSubfolder(root)) { $list.append($folderHeader); }
|
||||||
@ -2499,25 +2548,6 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var createTemplate = function ($container, path) {
|
|
||||||
var $icon = $templateIcon.clone();
|
|
||||||
var isOpened = filesOp.comparePath(path, currentPath);
|
|
||||||
var $element = createTreeElement(TEMPLATE_NAME, $icon, [TEMPLATE], false, true, false, isOpened);
|
|
||||||
$element.addClass('cp-app-drive-tree-root');
|
|
||||||
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
|
|
||||||
$container.append($list);
|
|
||||||
};
|
|
||||||
|
|
||||||
var createAllFiles = function ($container, path) {
|
|
||||||
var $icon = $unsortedIcon.clone();
|
|
||||||
var isOpened = filesOp.comparePath(path, currentPath);
|
|
||||||
var $allfilesElement = createTreeElement(FILES_DATA_NAME, $icon, [FILES_DATA], false, false, false, isOpened);
|
|
||||||
$allfilesElement.addClass('root');
|
|
||||||
var $allfilesList = $('<ul>', { 'class': 'cp-app-drive-tree-category' })
|
|
||||||
.append($allfilesElement);
|
|
||||||
$container.append($allfilesList);
|
|
||||||
};
|
|
||||||
|
|
||||||
var createTrash = function ($container, path) {
|
var createTrash = function ($container, path) {
|
||||||
var $icon = filesOp.isFolderEmpty(files[TRASH]) ? $trashEmptyIcon.clone() : $trashIcon.clone();
|
var $icon = filesOp.isFolderEmpty(files[TRASH]) ? $trashEmptyIcon.clone() : $trashIcon.clone();
|
||||||
var isOpened = filesOp.comparePath(path, currentPath);
|
var isOpened = filesOp.comparePath(path, currentPath);
|
||||||
@ -2530,29 +2560,11 @@ define([
|
|||||||
$container.append($trashList);
|
$container.append($trashList);
|
||||||
};
|
};
|
||||||
|
|
||||||
var createRecent = function ($container, path) {
|
|
||||||
var $icon = $recentIcon.clone();
|
|
||||||
var isOpened = filesOp.comparePath(path, currentPath);
|
|
||||||
var $element = createTreeElement(RECENT_NAME, $icon, [RECENT], false, false, false, isOpened);
|
|
||||||
$element.addClass('root');
|
|
||||||
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
|
|
||||||
$container.append($list);
|
|
||||||
};
|
|
||||||
|
|
||||||
var createOwned = function ($container, path) {
|
|
||||||
var $icon = $ownedIcon.clone(); // TODO
|
|
||||||
var isOpened = filesOp.comparePath(path, currentPath);
|
|
||||||
var $element = createTreeElement(OWNED_NAME, $icon, [OWNED], false, false, false, isOpened);
|
|
||||||
$element.addClass('root');
|
|
||||||
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
|
|
||||||
$container.append($list);
|
|
||||||
};
|
|
||||||
|
|
||||||
var search = APP.Search = {};
|
var search = APP.Search = {};
|
||||||
var createSearch = function ($container) {
|
var createSearch = function ($container) {
|
||||||
var isInSearch = currentPath[0] === SEARCH;
|
var isInSearch = currentPath[0] === SEARCH;
|
||||||
var $div = $('<div>', {'id': 'cp-app-drive-tree-search', 'class': 'cp-unselectable'});
|
var $div = $('<div>', {'id': 'cp-app-drive-tree-search', 'class': 'cp-unselectable'});
|
||||||
var $input = $('<input>', {
|
var $input = APP.Search.$input = $('<input>', {
|
||||||
id: 'cp-app-drive-tree-search-input',
|
id: 'cp-app-drive-tree-search-input',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
draggable: false,
|
draggable: false,
|
||||||
@ -2602,6 +2614,38 @@ define([
|
|||||||
$container.append($div);
|
$container.append($div);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var categories = {};
|
||||||
|
categories[FILES_DATA] = {
|
||||||
|
name: FILES_DATA_NAME,
|
||||||
|
$icon: $unsortedIcon
|
||||||
|
};
|
||||||
|
categories[TEMPLATE] = {
|
||||||
|
name: TEMPLATE_NAME,
|
||||||
|
droppable: true,
|
||||||
|
$icon: $templateIcon
|
||||||
|
};
|
||||||
|
categories[RECENT] = {
|
||||||
|
name: RECENT_NAME,
|
||||||
|
$icon: $recentIcon
|
||||||
|
};
|
||||||
|
categories[OWNED] = {
|
||||||
|
name: OWNED_NAME,
|
||||||
|
$icon: $ownedIcon
|
||||||
|
};
|
||||||
|
categories[TAGS] = {
|
||||||
|
name: TAGS_NAME,
|
||||||
|
$icon: $tagsIcon
|
||||||
|
};
|
||||||
|
var createCategory = function ($container, cat) {
|
||||||
|
var options = categories[cat];
|
||||||
|
var $icon = options.$icon.clone();
|
||||||
|
var isOpened = filesOp.comparePath([cat], currentPath);
|
||||||
|
var $element = createTreeElement(options.name, $icon, [cat], options.draggable, options.droppable, false, isOpened);
|
||||||
|
$element.addClass('cp-app-drive-tree-root');
|
||||||
|
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
|
||||||
|
$container.append($list);
|
||||||
|
};
|
||||||
|
|
||||||
APP.resetTree = function () {
|
APP.resetTree = function () {
|
||||||
var $categories = $tree.find('.cp-app-drive-tree-categories-container');
|
var $categories = $tree.find('.cp-app-drive-tree-categories-container');
|
||||||
var s = $categories.scrollTop() || 0;
|
var s = $categories.scrollTop() || 0;
|
||||||
@ -2610,11 +2654,12 @@ define([
|
|||||||
if (displayedCategories.indexOf(SEARCH) !== -1) { createSearch($tree); }
|
if (displayedCategories.indexOf(SEARCH) !== -1) { createSearch($tree); }
|
||||||
var $div = $('<div>', {'class': 'cp-app-drive-tree-categories-container'})
|
var $div = $('<div>', {'class': 'cp-app-drive-tree-categories-container'})
|
||||||
.appendTo($tree);
|
.appendTo($tree);
|
||||||
if (displayedCategories.indexOf(RECENT) !== -1) { createRecent($div, [RECENT]); }
|
if (displayedCategories.indexOf(TAGS) !== -1) { createCategory($div, TAGS); }
|
||||||
if (displayedCategories.indexOf(OWNED) !== -1) { createOwned($div, [OWNED]); }
|
if (displayedCategories.indexOf(RECENT) !== -1) { createCategory($div, RECENT); }
|
||||||
|
if (displayedCategories.indexOf(OWNED) !== -1) { createCategory($div, OWNED); }
|
||||||
if (displayedCategories.indexOf(ROOT) !== -1) { createTree($div, [ROOT]); }
|
if (displayedCategories.indexOf(ROOT) !== -1) { createTree($div, [ROOT]); }
|
||||||
if (displayedCategories.indexOf(TEMPLATE) !== -1) { createTemplate($div, [TEMPLATE]); }
|
if (displayedCategories.indexOf(TEMPLATE) !== -1) { createCategory($div, TEMPLATE); }
|
||||||
if (displayedCategories.indexOf(FILES_DATA) !== -1) { createAllFiles($div, [FILES_DATA]); }
|
if (displayedCategories.indexOf(FILES_DATA) !== -1) { createCategory($div, FILES_DATA); }
|
||||||
if (displayedCategories.indexOf(TRASH) !== -1) { createTrash($div, [TRASH]); }
|
if (displayedCategories.indexOf(TRASH) !== -1) { createTrash($div, [TRASH]); }
|
||||||
|
|
||||||
$tree.append(APP.$limit);
|
$tree.append(APP.$limit);
|
||||||
@ -2668,7 +2713,7 @@ define([
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var getProperties = function (el, cb) {
|
var getProperties = APP.getProperties = function (el, cb) {
|
||||||
if (!filesOp.isFile(el)) {
|
if (!filesOp.isFile(el)) {
|
||||||
return void cb('NOT_FILE');
|
return void cb('NOT_FILE');
|
||||||
}
|
}
|
||||||
|
|||||||
126
www/kanban/app-kanban.less
Normal file
126
www/kanban/app-kanban.less
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
@import (once) "../../customize/src/less2/include/browser.less";
|
||||||
|
@import (once) "../../customize/src/less2/include/framework.less";
|
||||||
|
@import (once) "../../customize/src/less2/include/tools.less";
|
||||||
|
|
||||||
|
.framework_main( @bg-color: @colortheme_kanban-bg,
|
||||||
|
@warn-color: @colortheme_kanban-warn,
|
||||||
|
@color: @colortheme_kanban-color);
|
||||||
|
|
||||||
|
// body
|
||||||
|
&.cp-app-kanban {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
max-height: 100%;
|
||||||
|
min-height: auto;
|
||||||
|
|
||||||
|
#cp-app-kanban-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
#cp-app-kanban-editor {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#cp-app-kanban-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
.kanban-container-outer {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.kanban-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-board {
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.kanban-title-board {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 10px;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
#kanban-edit {
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#kanban-edit {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@button-size: 50px;
|
||||||
|
#kanban-addboard {
|
||||||
|
margin: 30px;
|
||||||
|
border: 1px solid;
|
||||||
|
width: @button-size;
|
||||||
|
height: @button-size;
|
||||||
|
line-height: @button-size;
|
||||||
|
text-align: center;
|
||||||
|
background: @colortheme_kanban-bg;
|
||||||
|
font-weight: bold;
|
||||||
|
align-self: flex-start;
|
||||||
|
font-size: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
.tools_unselectable();
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-removeboard {
|
||||||
|
float: right;
|
||||||
|
margin: 10px;
|
||||||
|
padding: 3px;
|
||||||
|
width: 30px;
|
||||||
|
text-align: center;
|
||||||
|
background: #eee;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
.tools_unselectable();
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-header-yellow {
|
||||||
|
background: #FC3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-header-orange {
|
||||||
|
background: #F91;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-header-blue {
|
||||||
|
background: #0AC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-header-red {
|
||||||
|
background: #E43;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-header-green {
|
||||||
|
background: #8C4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: @browser_media-medium-screen) {
|
||||||
|
#cp-app-kanban-container {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 100%;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
www/kanban/index.html
Normal file
37
www/kanban/index.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<!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="/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>
|
||||||
|
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">
|
||||||
|
</iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
23
www/kanban/inner.html
Normal file
23
www/kanban/inner.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="cp-app-noscroll">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<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>
|
||||||
|
<style>
|
||||||
|
.loading-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
</html>
|
||||||
328
www/kanban/inner.js
Normal file
328
www/kanban/inner.js
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
define([
|
||||||
|
'jquery',
|
||||||
|
'/bower_components/nthen/index.js',
|
||||||
|
'/common/sframe-common.js',
|
||||||
|
'/common/sframe-app-framework.js',
|
||||||
|
'/common/common-util.js',
|
||||||
|
'/common/common-hash.js',
|
||||||
|
'/common/common-interface.js',
|
||||||
|
'/common/modes.js',
|
||||||
|
'/customize/messages.js',
|
||||||
|
'/kanban/jkanban.js',
|
||||||
|
'css!/kanban/jkanban.css',
|
||||||
|
], function (
|
||||||
|
$,
|
||||||
|
nThen,
|
||||||
|
SFCommon,
|
||||||
|
Framework,
|
||||||
|
Util,
|
||||||
|
Hash,
|
||||||
|
UI,
|
||||||
|
Modes,
|
||||||
|
Messages)
|
||||||
|
{
|
||||||
|
|
||||||
|
var verbose = function (x) { console.log(x); };
|
||||||
|
verbose = function () {}; // comment out to enable verbose logging
|
||||||
|
|
||||||
|
// Kanban code
|
||||||
|
var initKanban = function (framework, boards) {
|
||||||
|
var defaultBoards = [{
|
||||||
|
"id": "todo",
|
||||||
|
"title": Messages.kanban_todo,
|
||||||
|
"color": "blue",
|
||||||
|
"item": [{
|
||||||
|
"title": Messages._getKey('kanban_item', [1])
|
||||||
|
}, {
|
||||||
|
"title": Messages._getKey('kanban_item', [2])
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"id": "working",
|
||||||
|
"title": Messages.kanban_working,
|
||||||
|
"color": "orange",
|
||||||
|
"item": [{
|
||||||
|
"title": Messages._getKey('kanban_item', [3])
|
||||||
|
}, {
|
||||||
|
"title": Messages._getKey('kanban_item', [4])
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"id": "done",
|
||||||
|
"title": Messages.kanban_done,
|
||||||
|
"color": "green",
|
||||||
|
"item": [{
|
||||||
|
"title": Messages._getKey('kanban_item', [5])
|
||||||
|
}, {
|
||||||
|
"title": Messages._getKey('kanban_item', [6])
|
||||||
|
}]
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (!boards) {
|
||||||
|
verbose("Initializing with default boards content");
|
||||||
|
boards = defaultBoards;
|
||||||
|
} else {
|
||||||
|
verbose("Initializing with boards content " + boards);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any existing elements
|
||||||
|
$(".kanban-container-outer").remove();
|
||||||
|
|
||||||
|
var getInput = function () {
|
||||||
|
return $('<input>', {
|
||||||
|
'type': 'text',
|
||||||
|
'id': 'kanban-edit',
|
||||||
|
'size': '30'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var kanban = new window.jKanban({
|
||||||
|
element: '#cp-app-kanban-content',
|
||||||
|
gutter: '15px',
|
||||||
|
widthBoard: '300px',
|
||||||
|
onChange: function () {
|
||||||
|
verbose("Board object has changed");
|
||||||
|
framework.localChange();
|
||||||
|
},
|
||||||
|
click: function (el) {
|
||||||
|
if (kanban.inEditMode) {
|
||||||
|
verbose("An edit is already active");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kanban.inEditMode = true;
|
||||||
|
var name = $(el).text();
|
||||||
|
$(el).html('');
|
||||||
|
var $input = getInput().val(name).appendTo(el).focus();
|
||||||
|
$input[0].select();
|
||||||
|
var save = function () {
|
||||||
|
// Store the value
|
||||||
|
var name = $input.val();
|
||||||
|
// Remove the input
|
||||||
|
$(el).text(name);
|
||||||
|
// Save the value for the correct board
|
||||||
|
var board = $(el.parentNode.parentNode).attr("data-id");
|
||||||
|
var pos = kanban.findElementPosition(el);
|
||||||
|
kanban.getBoardJSON(board).item[pos].title = name;
|
||||||
|
kanban.onChange();
|
||||||
|
// Unlock edit mode
|
||||||
|
kanban.inEditMode = false;
|
||||||
|
};
|
||||||
|
$input.blur(save);
|
||||||
|
$input.keydown(function (e) {
|
||||||
|
if (e.which === 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.which === 27) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
$(el).text(name);
|
||||||
|
kanban.inEditMode = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
boardTitleClick: function (el, e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (kanban.inEditMode) {
|
||||||
|
verbose("An edit is already active");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kanban.inEditMode = true;
|
||||||
|
var name = $(el).text();
|
||||||
|
$(el).html('');
|
||||||
|
var $input = getInput().val(name).appendTo(el).focus();
|
||||||
|
$input[0].select();
|
||||||
|
var save = function () {
|
||||||
|
// Store the value
|
||||||
|
var name = $input.val();
|
||||||
|
// Remove the input
|
||||||
|
$(el).text(name);
|
||||||
|
// Save the value for the correct board
|
||||||
|
var board = $(el.parentNode.parentNode).attr("data-id");
|
||||||
|
kanban.getBoardJSON(board).title = name;
|
||||||
|
kanban.onChange();
|
||||||
|
// Unlock edit mode
|
||||||
|
kanban.inEditMode = false;
|
||||||
|
};
|
||||||
|
$input.blur(save);
|
||||||
|
$input.keydown(function (e) {
|
||||||
|
if (e.which === 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.which === 27) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
$(el).text(name);
|
||||||
|
kanban.inEditMode = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
colorClick: function (el) {
|
||||||
|
verbose("in color click");
|
||||||
|
var board = $(el.parentNode).attr("data-id");
|
||||||
|
var boardJSON = kanban.getBoardJSON(board);
|
||||||
|
var currentColor = boardJSON.color;
|
||||||
|
verbose("Current color " + currentColor);
|
||||||
|
var index = kanban.options.colors.findIndex(function (element) {
|
||||||
|
return (element === currentColor);
|
||||||
|
}) + 1;
|
||||||
|
verbose("Next index " + index);
|
||||||
|
if (index >= kanban.options.colors.length) { index = 0; }
|
||||||
|
var nextColor = kanban.options.colors[index];
|
||||||
|
verbose("Next color " + nextColor);
|
||||||
|
boardJSON.color = nextColor;
|
||||||
|
$(el).removeClass("kanban-header-" + currentColor);
|
||||||
|
$(el).addClass("kanban-header-" + nextColor);
|
||||||
|
kanban.onChange();
|
||||||
|
|
||||||
|
},
|
||||||
|
removeClick: function (el) {
|
||||||
|
UI.confirm(Messages.kanban_deleteBoard, function (yes) {
|
||||||
|
if (!yes) { return; }
|
||||||
|
verbose("Delete board");
|
||||||
|
var boardName = $(el.parentNode.parentNode).attr("data-id");
|
||||||
|
for (var index in kanban.options.boards) {
|
||||||
|
if (kanban.options.boards[index].id === boardName) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
kanban.options.boards.splice(index, 1);
|
||||||
|
kanban.removeBoard(boardName);
|
||||||
|
kanban.onChange();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
buttonClick: function (el, boardId, e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (kanban.inEditMode) {
|
||||||
|
verbose("An edit is already active");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kanban.inEditMode = true;
|
||||||
|
// create a form to enter element
|
||||||
|
var $item = $('<div>', {'class': 'kanban-item'});
|
||||||
|
var $input = getInput().val(name).appendTo($item);
|
||||||
|
kanban.addForm(boardId, $item[0]);
|
||||||
|
$input.focus();
|
||||||
|
var save = function () {
|
||||||
|
$item.remove();
|
||||||
|
kanban.addElement(boardId, {
|
||||||
|
"title": $input.val(),
|
||||||
|
});
|
||||||
|
kanban.inEditMode = false;
|
||||||
|
};
|
||||||
|
$input.blur(save);
|
||||||
|
$input.keydown(function (e) {
|
||||||
|
if (e.which === 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.which === 27) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
$(el).text(name);
|
||||||
|
kanban.inEditMode = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addItemButton: true,
|
||||||
|
boards: boards,
|
||||||
|
dragcancelEl: function (el, boardId) {
|
||||||
|
var pos = kanban.findElementPosition(el);
|
||||||
|
UI.confirm(Messages.kanban_deleteItem, function (yes) {
|
||||||
|
if (!yes) { return; }
|
||||||
|
var board;
|
||||||
|
kanban.options.boards.some(function (b) {
|
||||||
|
if (b.id === boardId) {
|
||||||
|
return (board = b);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!board) { return; }
|
||||||
|
board.item.splice(pos, 1);
|
||||||
|
$(el).remove();
|
||||||
|
kanban.onChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var addBoardDefault = document.getElementById('kanban-addboard');
|
||||||
|
addBoardDefault.addEventListener('click', function () {
|
||||||
|
var counter = 1;
|
||||||
|
|
||||||
|
// Get the new board id
|
||||||
|
while (kanban.options.boards.indexOf("board" + counter) !== -1) {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
kanban.addBoards([{
|
||||||
|
"id": "board" + counter,
|
||||||
|
"title": Messages.kanban_newBoard,
|
||||||
|
"color": "yellow",
|
||||||
|
"item": [{
|
||||||
|
"title": Messages._getKey('kanban_item', [1]),
|
||||||
|
}]
|
||||||
|
}]);
|
||||||
|
kanban.onChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
return kanban;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start of the main loop
|
||||||
|
var andThen2 = function (framework) {
|
||||||
|
|
||||||
|
var kanban = initKanban(framework);
|
||||||
|
|
||||||
|
framework.onContentUpdate(function (newContent) {
|
||||||
|
// Need to update the content
|
||||||
|
verbose("Content should be updated to " + newContent);
|
||||||
|
var currentContent = kanban.getBoardsJSON();
|
||||||
|
var remoteContent = newContent.content;
|
||||||
|
|
||||||
|
if (currentContent !== remoteContent) {
|
||||||
|
// reinit kanban (TODO: optimize to diff only)
|
||||||
|
verbose("Content is different.. Applying content");
|
||||||
|
kanban.setBoards(remoteContent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
framework.setContentGetter(function () {
|
||||||
|
// var content = $("#cp-app-kanban-content").val();
|
||||||
|
var content = kanban.getBoardsJSON();
|
||||||
|
verbose("Content current value is " + content);
|
||||||
|
return {
|
||||||
|
content: content
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
framework.onReady(function () {
|
||||||
|
$("#cp-app-kanban-content").focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
framework.start();
|
||||||
|
};
|
||||||
|
|
||||||
|
var main = function () {
|
||||||
|
// var framework;
|
||||||
|
nThen(function (waitFor) {
|
||||||
|
|
||||||
|
// Framework initialization
|
||||||
|
Framework.create({
|
||||||
|
toolbarContainer: '#cme_toolbox',
|
||||||
|
contentContainer: '#cp-app-kanban-editor',
|
||||||
|
}, waitFor(function (framework) {
|
||||||
|
andThen2(framework);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
main();
|
||||||
|
});
|
||||||
100
www/kanban/jkanban.css
Normal file
100
www/kanban/jkanban.css
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
.kanban-container * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-board {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
background: #E2E4E6;
|
||||||
|
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
margin: 10px;
|
||||||
|
vertical-align: top;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-board.disabled-board {
|
||||||
|
opacity: .3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-board.is-moving.gu-mirror {
|
||||||
|
transform: rotate(3deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-board.is-moving.gu-mirror .kanban-drag {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-board header {
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-board header .kanban-title-board {
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-board header .kanban-title-button {
|
||||||
|
float: right;
|
||||||
|
line-height: 1;
|
||||||
|
padding: .25rem .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-board .kanban-drag {
|
||||||
|
min-height: 200px;
|
||||||
|
padding: 20px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-item {
|
||||||
|
background: #fff;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-item:hover {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-item:last-child {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-item.is-moving.gu-mirror {
|
||||||
|
transform: rotate(3deg);
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dragula CSS */
|
||||||
|
|
||||||
|
.gu-mirror {
|
||||||
|
position: fixed !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
z-index: 9999 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gu-hide {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gu-unselectable {
|
||||||
|
-webkit-user-select: none !important;
|
||||||
|
-moz-user-select: none !important;
|
||||||
|
-ms-user-select: none !important;
|
||||||
|
user-select: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gu-transit {
|
||||||
|
opacity: 0.2 !important;
|
||||||
|
transform: rotate(0deg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
text-align: right;
|
||||||
|
margin-button: 5px;
|
||||||
|
}
|
||||||
1547
www/kanban/jkanban.js
Normal file
1547
www/kanban/jkanban.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user