diff --git a/.jshintignore b/.jshintignore
index b7a1c570e..45b4087f5 100644
--- a/.jshintignore
+++ b/.jshintignore
@@ -2,6 +2,7 @@ node_modules/
www/bower_components/
www/common/pdfjs/
www/common/tippy/
+www/common/jquery-ui/
server.js
www/common/media-tag.js
@@ -14,6 +15,8 @@ www/pad/wysiwygarea-plugin.js
www/pad/mediatag-plugin.js
www/pad/mediatag-plugin-dialog.js
+www/kanban/jkanban.js
+
www/common/media-tag-nacl.min.js
customize/
diff --git a/customize.dist/pages.js b/customize.dist/pages.js
index 281bdc585..a7557b84d 100644
--- a/customize.dist/pages.js
+++ b/customize.dist/pages.js
@@ -56,6 +56,7 @@ define([
footLink('/code/', 'main_code'),
footLink('/slide/', 'main_slide'),
footLink('/poll/', 'main_poll'),
+ footLink('/kanban/', 'main_kanban'),
footLink('/whiteboard/', null, Msg.type.whiteboard)
]),
footerCol('footer_aboutUs', [
@@ -566,6 +567,7 @@ define([
[ 'code', '/code/', Msg.main_codePad, 'fa-file-code-o' ],
[ 'slide', '/slide/', Msg.main_slidePad, 'fa-file-powerpoint-o' ],
[ 'poll', '/poll/', Msg.main_pollPad, 'fa-calendar' ],
+ [ 'kanban', '/kanban/', Msg.main_kanbanPad, 'fa-calendar' ],
[ 'whiteboard', '/whiteboard/', Msg.main_whiteboardPad, 'fa-paint-brush' ],
[ 'recent', '/drive/', Msg.main_localPads, 'fa-hdd-o' ]
].filter(function (x) {
diff --git a/customize.dist/src/less2/include/colortheme.less b/customize.dist/src/less2/include/colortheme.less
index 6a0293ae8..f613924f8 100644
--- a/customize.dist/src/less2/include/colortheme.less
+++ b/customize.dist/src/less2/include/colortheme.less
@@ -115,6 +115,10 @@
@colortheme_todo-color: #000;
@colortheme_todo-warn: #cd2532;
+@colortheme_kanban-bg: #8C4;
+@colortheme_kanban-color: #fff;
+@colortheme_kanban-warn: #e6385d;
+
// Sidebar layout (profile / settings)
@colortheme_sidebar-active: #fff;
@colortheme_sidebar-left-bg: #eee;
diff --git a/customize.dist/src/less2/include/icon-colors.less b/customize.dist/src/less2/include/icon-colors.less
index b787fee1c..283626cfd 100644
--- a/customize.dist/src/less2/include/icon-colors.less
+++ b/customize.dist/src/less2/include/icon-colors.less
@@ -13,6 +13,7 @@
.cp-icon-color-profile { color: @colortheme_settings-bg; }
.cp-icon-color-default { color: @colortheme_default-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-code { border-color: @colortheme_code-bg !important; }
@@ -26,5 +27,6 @@
.cp-border-color-profile { border-color: @colortheme_settings-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-kanban { border-color: @colortheme_kanban-bg !important; }
}
diff --git a/customize.dist/src/less2/include/tokenfield.less b/customize.dist/src/less2/include/tokenfield.less
index 6604b4481..46ba3af09 100644
--- a/customize.dist/src/less2/include/tokenfield.less
+++ b/customize.dist/src/less2/include/tokenfield.less
@@ -1,25 +1,31 @@
@import (once) "./tools.less";
.tokenfield_main () {
+ .ui-autocomplete {
+ z-index: 100001; // alertify + 1
+ }
.tokenfield {
.tools_unselectable();
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-evenly;
height: auto;
min-height: 34px;
padding-bottom: 0px;
background-color: unset;
border: none;
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- padding: 0 10px;
+ margin: 0 10px;
+ padding: 0;
+ width: ~"calc(100% - 20px)";
.token {
box-sizing: border-box;
border-radius: 3px;
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
border: 1px solid #d9d9d9;
background-color: #ededed;
white-space: nowrap;
- margin: 10px 5px;
+ margin: 2px 0;
height: 24px;
vertical-align: middle;
cursor: default;
@@ -50,7 +56,7 @@
.close {
font-family: Arial;
display: inline-block;
- line-height: 24px;
+ line-height: 1.49em;
font-size: 1.1em;
margin-left: 5px;
float: none;
@@ -73,6 +79,8 @@
margin: 0 !important; // Override alertify
box-shadow: none;
max-width: 100%;
+ width: 100%;
+ min-width: 100% !important;
&:focus {
border-color: transparent;
outline: 0;
diff --git a/customize.dist/src/less2/include/tools.less b/customize.dist/src/less2/include/tools.less
index 9fd2df5bc..b8c36cbcd 100644
--- a/customize.dist/src/less2/include/tools.less
+++ b/customize.dist/src/less2/include/tools.less
@@ -19,6 +19,7 @@
}
.tools_unselectable () {
+ user-select: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
diff --git a/customize.dist/src/less2/main.less b/customize.dist/src/less2/main.less
index dadbef539..1186465da 100644
--- a/customize.dist/src/less2/main.less
+++ b/customize.dist/src/less2/main.less
@@ -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-debug { @import "../../../debug/app-debug.less"; }
body.cp-app-worker { @import "../../../worker/app-worker.less"; }
+body.cp-app-kanban { @import "../../../kanban/app-kanban.less"; }
diff --git a/customize.dist/src/less2/pages/page-index.less b/customize.dist/src/less2/pages/page-index.less
index 55671f913..6ddee6349 100644
--- a/customize.dist/src/less2/pages/page-index.less
+++ b/customize.dist/src/less2/pages/page-index.less
@@ -154,6 +154,7 @@ h4 {
.cp-callout-code .fa { background-color: @colortheme_code-bg; }
.cp-callout-slide .fa { background-color: @colortheme_slide-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-recent .fa { background-color: @colortheme_drive-bg; }
.cp-hidden { display: none !important; }
diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js
index cf7ddcf8c..f2f3feb21 100644
--- a/customize.dist/translations/messages.fr.js
+++ b/customize.dist/translations/messages.fr.js
@@ -8,6 +8,7 @@ define(function () {
out.type.pad = 'Texte';
out.type.code = 'Code';
out.type.poll = 'Sondage';
+ out.type.kanban = 'Kanban';
out.type.slide = 'Présentation';
out.type.drive = 'CryptDrive';
out.type.whiteboard = "Tableau Blanc";
@@ -21,6 +22,7 @@ define(function () {
out.button_newpoll = 'Nouveau sondage';
out.button_newslide = 'Nouvelle présentation';
out.button_newwhiteboard = 'Nouveau tableau blanc';
+ out.button_newkanban = 'Nouveau kanban';
out.updated_0_common_connectionLost = "Connexion au serveur perdue Vous êtes désormais en mode lecture seule jusqu'au retour de la connexion.";
out.common_connectionLost = out.updated_0_common_connectionLost;
@@ -246,6 +248,15 @@ define(function () {
out.pad_mediatagWidth = "Largeur (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
out.poll_title = "Sélecteur de date Zero Knowledge";
@@ -367,6 +378,7 @@ define(function () {
out.fm_searchName = "Recherche";
out.fm_recentPadsName = "Pads récents";
out.fm_ownedPadsName = "Pads en votre possession";
+ out.fm_tagsName = "Mots-clés";
out.fm_searchPlaceholder = "Rechercher...";
out.fm_newButton = "Nouveau";
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_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_tags_name = "Mot-clé";
+ out.fm_tags_used = "Nombre d'utilisations";
// File - Context menu
out.fc_newfolder = "Nouveau dossier";
out.fc_rename = "Renommer";
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index 18e295a4c..114fb6249 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -8,6 +8,7 @@ define(function () {
out.type.pad = 'Rich text';
out.type.code = 'Code';
out.type.poll = 'Poll';
+ out.type.kanban = 'Kanban';
out.type.slide = 'Presentation';
out.type.drive = 'CryptDrive';
out.type.whiteboard = 'Whiteboard';
@@ -21,6 +22,7 @@ define(function () {
out.button_newpoll = 'New Poll';
out.button_newslide = 'New Presentation';
out.button_newwhiteboard = 'New Whiteboard';
+ out.button_newkanban = 'New Kanban';
// NOTE: Remove updated_0_ if we need an updated_1_
out.updated_0_common_connectionLost = "Server Connection Lost 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_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
out.poll_title = "Zero Knowledge Date Picker";
@@ -368,6 +379,7 @@ define(function () {
out.fm_searchName = "Search";
out.fm_recentPadsName = "Recent pads";
out.fm_ownedPadsName = "Owned";
+ out.fm_tagsName = "Tags";
out.fm_searchPlaceholder = "Search...";
out.fm_newButton = "New";
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_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_tags_name = "Tag name";
+ out.fm_tags_used = "Number of uses";
// File - Context menu
out.fc_newfolder = "New folder";
out.fc_rename = "Rename";
diff --git a/www/common/application_config_internal.js b/www/common/application_config_internal.js
index f6760f897..0ab300721 100644
--- a/www/common/application_config_internal.js
+++ b/www/common/application_config_internal.js
@@ -9,7 +9,7 @@ define(function() {
/* Select the buttons displayed on the main page to create new collaborative sessions
* 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'];
/* Cryptpad apps use a common API to display notifications to users
@@ -81,6 +81,7 @@ define(function() {
whiteboard: 'fa-paint-brush',
todo: 'fa-tasks',
contacts: 'fa-users',
+ kanban: 'fa-list-alt',
};
// Ability to create owned pads and expiring pads through a new pad creation screen.
diff --git a/www/common/common-interface.js b/www/common/common-interface.js
index 7f318dfae..0592d859a 100644
--- a/www/common/common-interface.js
+++ b/www/common/common-interface.js
@@ -12,8 +12,10 @@ define([
'/customize/loading.js',
'/common/test.js',
+ '/common/jquery-ui/jquery-ui.min.js',
'/bower_components/bootstrap-tokenfield/dist/bootstrap-tokenfield.js',
'css!/common/tippy/tippy.css',
+ 'css!/common/jquery-ui/jquery-ui.min.css'
], function ($, Messages, Util, Hash, Notifier, AppConfig,
Alertify, Tippy, Pages, h, Loading, Test) {
var UI = {};
@@ -183,11 +185,17 @@ define([
]);
};
- UI.tokenField = function (target) {
+ UI.tokenField = function (target, autocomplete) {
var t = {
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) {
var tokens = $t.tokenfield('getTokens').map(function (token) {
@@ -210,10 +218,17 @@ define([
t.preventDuplicates = function (cb) {
$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;
ev.attrs.value = ev.attrs.value.toLowerCase();
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();
if (typeof(cb) === 'function') { cb(val); }
@@ -241,7 +256,7 @@ define([
return t;
};
- dialog.tagPrompt = function (tags, cb) {
+ dialog.tagPrompt = function (tags, existing, cb) {
var input = dialog.textInput();
var tagger = dialog.frame([
@@ -255,7 +270,7 @@ define([
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]));
});
@@ -396,7 +411,7 @@ define([
stopListening(listener);
cb();
});
- listener = listenForKeys(close, close, ok);
+ listener = listenForKeys(close, close);
var $ok = $(ok).click(close);
document.body.appendChild(frame);
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 2e4dfacbc..f2e7acc0c 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -23,20 +23,27 @@ define([
}
UIElements.updateTags = function (common, href) {
- var sframeChan = common.getSframeChannel();
- sframeChan.query('Q_TAGS_GET', href || null, function (err, res) {
- if (err || res.error) {
- if (res.error === 'NO_ENTRY') {
- UI.alert(Messages.tags_noentry);
+ var existing, tags;
+ NThen(function(waitFor) {
+ common.getSframeChannel().query("Q_GET_ALL_TAGS", null, waitFor(function(err, res) {
+ if (err || res.error) { return void console.error(err || res.error); }
+ 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);
- }
- UI.dialog.tagPrompt(res.data, function (tags) {
- if (!Array.isArray(tags)) { return; }
- sframeChan.event('EV_TAGS_SET', {
- tags: tags,
- href: href,
- });
+ tags = res || [];
+ }), href);
+ }).nThen(function () {
+ UI.dialog.tagPrompt(tags, existing, function (newTags) {
+ if (!Array.isArray(newTags)) { return; }
+ common.setPadAttribute('tags', newTags, null, href);
});
});
};
diff --git a/www/common/jquery-ui/jquery-ui.min.css b/www/common/jquery-ui/jquery-ui.min.css
new file mode 100644
index 000000000..1ff50449e
--- /dev/null
+++ b/www/common/jquery-ui/jquery-ui.min.css
@@ -0,0 +1,7 @@
+/*! jQuery UI - v1.12.1 - 2018-05-15
+* http://jqueryui.com
+* Includes: core.css, autocomplete.css, menu.css, theme.css
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif
+* Copyright jQuery Foundation and other contributors; Licensed MIT */
+
+.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666}
\ No newline at end of file
diff --git a/www/common/jquery-ui/jquery-ui.min.js b/www/common/jquery-ui/jquery-ui.min.js
new file mode 100644
index 000000000..d17a46a44
--- /dev/null
+++ b/www/common/jquery-ui/jquery-ui.min.js
@@ -0,0 +1,6 @@
+/*! jQuery UI - v1.12.1 - 2018-05-15
+* http://jqueryui.com
+* Includes: widget.js, position.js, keycode.js, unique-id.js, widgets/autocomplete.js, widgets/menu.js
+* Copyright jQuery Foundation and other contributors; Licensed MIT */
+
+(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.1";var e=0,i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},l=e.split(".")[0];e=e.split(".")[1];var h=l+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][h.toLowerCase()]=function(e){return!!t.data(e,h)},t[l]=t[l]||{},n=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:l,widgetName:e,widgetFullName:h}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var s,n,o=i.call(arguments,1),a=0,r=o.length;r>a;a++)for(s in o[a])n=o[a][s],o[a].hasOwnProperty(s)&&void 0!==n&&(e[s]=t.isPlainObject(n)?t.isPlainObject(e[s])?t.widget.extend({},e[s],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,s){var n=s.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=i.call(arguments,1),l=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(l=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):l=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new s(o,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,l=/top|center|bottom/,h=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
"),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.width
i?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};h>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),l.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-r-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-r-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.widget("ui.menu",{version:"1.12.1",defaultElement:"",delay:300,options:{icons:{submenu:"ui-icon-caret-1-e"},items:"> *",menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().attr({role:this.options.role,tabIndex:0}),this._addClass("ui-menu","ui-widget ui-widget-content"),this._on({"mousedown .ui-menu-item":function(t){t.preventDefault()},"click .ui-menu-item":function(e){var i=t(e.target),s=t(t.ui.safeActiveElement(this.document[0]));!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(e),e.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(e):!this.element.is(":focus")&&s.closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){if(!this.previousFilter){var i=t(e.target).closest(".ui-menu-item"),s=t(e.currentTarget);i[0]===s[0]&&(this._removeClass(s.siblings().children(".ui-state-active"),null,"ui-state-active"),this.focus(e,s))}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.find(this.options.items).eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){var i=!t.contains(this.element[0],t.ui.safeActiveElement(this.document[0]));i&&this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t),this.mouseHandled=!1}})},_destroy:function(){var e=this.element.find(".ui-menu-item").removeAttr("role aria-disabled"),i=e.children(".ui-menu-item-wrapper").removeUniqueId().removeAttr("tabIndex role aria-haspopup");this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeAttr("role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex").removeUniqueId().show(),i.children().each(function(){var e=t(this);e.data("ui-menu-submenu-caret")&&e.remove()})},_keydown:function(e){var i,s,n,o,a=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:a=!1,s=this.previousFilter||"",o=!1,n=e.keyCode>=96&&105>=e.keyCode?""+(e.keyCode-96):String.fromCharCode(e.keyCode),clearTimeout(this.filterTimer),n===s?o=!0:n=s+n,i=this._filterMenuItems(n),i=o&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(e.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(e,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}a&&e.preventDefault()},_activate:function(t){this.active&&!this.active.is(".ui-state-disabled")&&(this.active.children("[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i,s,n,o,a=this,r=this.options.icons.submenu,l=this.element.find(this.options.menus);this._toggleClass("ui-menu-icons",null,!!this.element.find(".ui-icon").length),s=l.filter(":not(.ui-menu)").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),i=e.prev(),s=t("").data("ui-menu-submenu-caret",!0);a._addClass(s,"ui-menu-icon","ui-icon "+r),i.attr("aria-haspopup","true").prepend(s),e.attr("aria-labelledby",i.attr("id"))}),this._addClass(s,"ui-menu","ui-widget ui-widget-content ui-front"),e=l.add(this.element),i=e.find(this.options.items),i.not(".ui-menu-item").each(function(){var e=t(this);a._isDivider(e)&&a._addClass(e,"ui-menu-divider","ui-widget-content")}),n=i.not(".ui-menu-item, .ui-menu-divider"),o=n.children().not(".ui-menu").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),this._addClass(n,"ui-menu-item")._addClass(o,"ui-menu-item-wrapper"),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){if("icons"===t){var i=this.element.find(".ui-menu-icon");this._removeClass(i,null,this.options.icons.submenu)._addClass(i,null,e.submenu)}this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t+""),this._toggleClass(null,"ui-state-disabled",!!t)},focus:function(t,e){var i,s,n;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children(".ui-menu-item-wrapper"),this._addClass(s,null,"ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),n=this.active.parent().closest(".ui-menu-item").children(".ui-menu-item-wrapper"),this._addClass(n,null,"ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,o,a,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,o=this.activeMenu.scrollTop(),a=this.activeMenu.height(),r=e.outerHeight(),0>n?this.activeMenu.scrollTop(o+n):n+r>a&&this.activeMenu.scrollTop(o+n-a+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this._removeClass(this.active.children(".ui-menu-item-wrapper"),null,"ui-state-active"),this._trigger("blur",t,{item:this.active}),this.active=null)},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this._removeClass(s.find(".ui-state-active"),null,"ui-state-active"),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false")},_closeOnDocumentClick:function(e){return!t(e.target).closest(".ui-menu").length},_isDivider:function(t){return!/[^\-\u2014\u2013\s]/.test(t.text())},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(e),void 0)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items).first())),void 0):(this.next(e),void 0)},_hasScroll:function(){return this.element.outerHeight()",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var e,i,s,n=this.element[0].nodeName.toLowerCase(),o="textarea"===n,a="input"===n;this.isMultiLine=o||!a&&this._isContentEditable(this.element),this.valueMethod=this.element[o||a?"val":"text"],this.isNewMenu=!0,this._addClass("ui-autocomplete-input"),this.element.attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return e=!0,s=!0,i=!0,void 0;e=!1,s=!1,i=!1;var o=t.ui.keyCode;switch(n.keyCode){case o.PAGE_UP:e=!0,this._move("previousPage",n);break;case o.PAGE_DOWN:e=!0,this._move("nextPage",n);break;case o.UP:e=!0,this._keyEvent("previous",n);break;case o.DOWN:e=!0,this._keyEvent("next",n);break;case o.ENTER:this.menu.active&&(e=!0,n.preventDefault(),this.menu.select(n));break;case o.TAB:this.menu.active&&this.menu.select(n);break;case o.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(e)return e=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=t.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(t){return s?(s=!1,t.preventDefault(),void 0):(this._searchTimeout(t),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(t),this._change(t),void 0)}}),this._initSource(),this.menu=t("").appendTo(this._appendTo()).menu({role:null}).hide().menu("instance"),this._addClass(this.menu.element,"ui-autocomplete","ui-front"),this._on(this.menu.element,{mousedown:function(e){e.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,this.element[0]!==t.ui.safeActiveElement(this.document[0])&&this.element.trigger("focus")})},menufocus:function(e,i){var s,n;return this.isNewMenu&&(this.isNewMenu=!1,e.originalEvent&&/^mouse/.test(e.originalEvent.type))?(this.menu.blur(),this.document.one("mousemove",function(){t(e.target).trigger(e.originalEvent)}),void 0):(n=i.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",e,{item:n})&&e.originalEvent&&/^key/.test(e.originalEvent.type)&&this._value(n.value),s=i.item.attr("aria-label")||n.value,s&&t.trim(s).length&&(this.liveRegion.children().hide(),t("").text(s).appendTo(this.liveRegion)),void 0)},menuselect:function(e,i){var s=i.item.data("ui-autocomplete-item"),n=this.previous;this.element[0]!==t.ui.safeActiveElement(this.document[0])&&(this.element.trigger("focus"),this.previous=n,this._delay(function(){this.previous=n,this.selectedItem=s})),!1!==this._trigger("select",e,{item:s})&&this._value(s.value),this.term=this._value(),this.close(e),this.selectedItem=s}}),this.liveRegion=t("
",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(t,e){this._super(t,e),"source"===t&&this._initSource(),"appendTo"===t&&this.menu.element.appendTo(this._appendTo()),"disabled"===t&&e&&this.xhr&&this.xhr.abort()},_isEventTargetInWidget:function(e){var i=this.menu.element[0];return e.target===this.element[0]||e.target===i||t.contains(i,e.target)},_closeOnClickOutside:function(t){this._isEventTargetInWidget(t)||this.close()},_appendTo:function(){var e=this.options.appendTo;return e&&(e=e.jquery||e.nodeType?t(e):this.document.find(e).eq(0)),e&&e[0]||(e=this.element.closest(".ui-front, dialog")),e.length||(e=this.document[0].body),e},_initSource:function(){var e,i,s=this;t.isArray(this.options.source)?(e=this.options.source,this.source=function(i,s){s(t.ui.autocomplete.filter(e,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(e,n){s.xhr&&s.xhr.abort(),s.xhr=t.ajax({url:i,data:e,dataType:"json",success:function(t){n(t)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(t){clearTimeout(this.searching),this.searching=this._delay(function(){var e=this.term===this._value(),i=this.menu.element.is(":visible"),s=t.altKey||t.ctrlKey||t.metaKey||t.shiftKey;(!e||e&&!i&&!s)&&(this.selectedItem=null,this.search(null,t))},this.options.delay)},search:function(t,e){return t=null!=t?t:this._value(),this.term=this._value(),t.length
").append(t("").text(i.label)).appendTo(e)},_move:function(t,e){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(t)||this.menu.isLastItem()&&/^next/.test(t)?(this.isMultiLine||this._value(this.term),this.menu.blur(),void 0):(this.menu[t](e),void 0):(this.search(null,e),void 0)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(t,e){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(t,e),e.preventDefault())},_isContentEditable:function(t){if(!t.length)return!1;var e=t.prop("contentEditable");return"inherit"===e?this._isContentEditable(t.parent()):"true"===e}}),t.extend(t.ui.autocomplete,{escapeRegex:function(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(e,i){var s=RegExp(t.ui.autocomplete.escapeRegex(i),"i");return t.grep(e,function(t){return s.test(t.label||t.value||t)})}}),t.widget("ui.autocomplete",t.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(t){return t+(t>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(e){var i;this._superApply(arguments),this.options.disabled||this.cancelSearch||(i=e&&e.length?this.options.messages.results(e.length):this.options.messages.noResults,this.liveRegion.children().hide(),t("
").text(i).appendTo(this.liveRegion))}}),t.ui.autocomplete});
\ No newline at end of file
diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index 6b1a5b4a5..e02a527ae 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -636,18 +636,7 @@ define([
// Tags
Store.listAllTags = function (data, cb) {
- var all = [];
- 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);
+ cb(store.userObject.getTagsList());
};
// Templates
diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js
index 0fb4a7e39..fcebc354f 100644
--- a/www/common/sframe-common-outer.js
+++ b/www/common/sframe-common-outer.js
@@ -393,6 +393,7 @@ define([
// If we have a stronger hash, use it for pad attributes
href = window.location.pathname + '#' + hashes.editHash;
}
+ if (data.href) { href = data.href; }
Cryptpad.getPadAttribute(data.key, function (e, data) {
cb({
error: e,
@@ -406,6 +407,7 @@ define([
// If we have a stronger hash, use it for pad attributes
href = window.location.pathname + '#' + hashes.editHash;
}
+ if (data.href) { href = data.href; }
Cryptpad.setPadAttribute(data.key, data.value, function (e) {
cb({error:e});
}, 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) {
Cryptpad.isOverPinLimit(function (err, overLimit, data) {
cb({
@@ -606,6 +595,15 @@ define([
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) {
cfg.addRpc(sframeChan, Cryptpad, Utils);
}
diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js
index dca14afb9..4fc5a6ac6 100644
--- a/www/common/sframe-common.js
+++ b/www/common/sframe-common.js
@@ -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', {
- key: key
+ key: key,
+ href: href
}, function (err, res) {
cb (err || res.error, res.data);
});
};
- funcs.setPadAttribute = function (key, value, cb) {
+ funcs.setPadAttribute = function (key, value, cb, href) {
cb = cb || $.noop;
ctx.sframeChan.query('Q_SET_PAD_ATTRIBUTE', {
key: key,
+ href: href,
value: value
}, cb);
};
diff --git a/www/common/sframe-protocol.js b/www/common/sframe-protocol.js
index a0e9b82c2..fef8b1027 100644
--- a/www/common/sframe-protocol.js
+++ b/www/common/sframe-protocol.js
@@ -165,10 +165,6 @@ define({
// Put one entry in the parent sessionStorage
'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
// in the drive at registration.
'Q_MERGE_ANON_DRIVE': true,
@@ -237,4 +233,7 @@ define({
// Loading events to display in the loading screen
'EV_LOADING_INFO': true,
+
+ // Get all existing tags
+ 'Q_GET_ALL_TAGS': true,
});
diff --git a/www/common/userObject.js b/www/common/userObject.js
index 6192c4236..b05798946 100644
--- a/www/common/userObject.js
+++ b/www/common/userObject.js
@@ -627,6 +627,21 @@ define([
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 module;
diff --git a/www/drive/app-drive.less b/www/drive/app-drive.less
index 0348cf89e..8c1ea1fa4 100644
--- a/www/drive/app-drive.less
+++ b/www/drive/app-drive.less
@@ -462,6 +462,8 @@ span {
padding-right: 15px;
}
.cp-app-drive-search-opendir {
+ display: flex;
+ justify-content: space-between;
a {
cursor: pointer;
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 {
diff --git a/www/drive/inner.js b/www/drive/inner.js
index 5975d1e26..b668429cd 100644
--- a/www/drive/inner.js
+++ b/www/drive/inner.js
@@ -51,20 +51,65 @@ define([
var E_OVER_LIMIT = 'E_OVER_LIMIT';
- var SEARCH = "search";
- var SEARCH_NAME = Messages.fm_searchName;
var ROOT = "root";
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_NAME = Messages.fm_filesDataName;
var TEMPLATE = "template";
var TEMPLATE_NAME = Messages.fm_templateName;
- var TRASH = "trash";
- var TRASH_NAME = Messages.fm_trashName;
var RECENT = "recent";
var RECENT_NAME = Messages.fm_recentPadsName;
var OWNED = "owned";
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 = $('
', {
+ "class": faFolder + " fa cp-app-drive-icon-folder cp-app-drive-content-icon"
+ });
+ //var $folderIcon = $(' ', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
+ var $folderEmptyIcon = $folderIcon.clone();
+ var $folderOpenedIcon = $('', {"class": faFolderOpen + " fa cp-app-drive-icon-folder"});
+ //var $folderOpenedIcon = $(' ', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
+ var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
+ //var $upIcon = $('', {"class": "fa fa-arrow-circle-up"});
+ var $unsortedIcon = $('', {"class": "fa fa-files-o"});
+ var $templateIcon = $('', {"class": "fa fa-cubes"});
+ var $recentIcon = $('', {"class": "fa fa-clock-o"});
+ var $trashIcon = $('', {"class": "fa " + faTrash});
+ var $trashEmptyIcon = $('', {"class": "fa fa-trash-o"});
+ //var $collapseIcon = $('', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
+ var $expandIcon = $('', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
+ var $emptyTrashIcon = $('', {"class": "fa fa-ban"});
+ var $listIcon = $('', {"class": "fa fa-list"});
+ var $gridIcon = $('', {"class": "fa fa-th-large"});
+ var $sortAscIcon = $('', {"class": "fa fa-angle-up sortasc"});
+ var $sortDescIcon = $('', {"class": "fa fa-angle-down sortdesc"});
+ var $closeIcon = $('', {"class": "fa fa-window-close"});
+ //var $backupIcon = $('', {"class": "fa fa-life-ring"});
+ var $searchIcon = $('', {"class": "fa fa-search cp-app-drive-tree-search-con"});
+ var $addIcon = $('', {"class": "fa fa-plus"});
+ var $renamedIcon = $('', {"class": "fa fa-flag"});
+ var $readonlyIcon = $('', {"class": "fa " + faReadOnly});
+ var $ownedIcon = $('', {"class": "fa fa-id-card-o"});
+ var $ownerIcon = $('', {"class": "fa fa-id-card"});
+ var $tagsIcon = $('', {"class": "fa " + faTags});
var LS_LAST = "app-drive-lastOpened";
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 = $('', {
- "class": faFolder + " fa cp-app-drive-icon-folder cp-app-drive-content-icon"
- });
- //var $folderIcon = $(' ', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
- var $folderEmptyIcon = $folderIcon.clone();
- var $folderOpenedIcon = $('', {"class": faFolderOpen + " fa cp-app-drive-icon-folder"});
- //var $folderOpenedIcon = $(' ', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
- var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
- //var $upIcon = $('', {"class": "fa fa-arrow-circle-up"});
- var $unsortedIcon = $('', {"class": "fa fa-files-o"});
- var $templateIcon = $('', {"class": "fa fa-cubes"});
- var $recentIcon = $('', {"class": "fa fa-clock-o"});
- var $trashIcon = $('', {"class": "fa " + faTrash});
- var $trashEmptyIcon = $('', {"class": "fa fa-trash-o"});
- //var $collapseIcon = $('', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
- var $expandIcon = $('', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
- var $emptyTrashIcon = $('', {"class": "fa fa-ban"});
- var $listIcon = $('', {"class": "fa fa-list"});
- var $gridIcon = $('', {"class": "fa fa-th-large"});
- var $sortAscIcon = $('', {"class": "fa fa-angle-up sortasc"});
- var $sortDescIcon = $('', {"class": "fa fa-angle-down sortdesc"});
- var $closeIcon = $('', {"class": "fa fa-window-close"});
- //var $backupIcon = $('', {"class": "fa fa-life-ring"});
- var $searchIcon = $('', {"class": "fa fa-search cp-app-drive-tree-search-con"});
- var $addIcon = $('', {"class": "fa fa-plus"});
- var $renamedIcon = $('', {"class": "fa fa-flag"});
- var $readonlyIcon = $('', {"class": "fa " + faReadOnly});
- var $ownedIcon = $('', {"class": "fa fa-id-card-o"});
- var $ownerIcon = $('', {"class": "fa fa-id-card"});
-
var history = {
isHistoryMode: false,
};
@@ -360,10 +363,16 @@ define([
// Categories dislayed in the menu
// _WORKGROUP_ : do not display unsorted
var displayedCategories = [ROOT, TRASH, SEARCH, RECENT];
+
+ // PCS enabled: display owned pads
if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
+ // Templates enabled: display template category
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]; }
- var virtualCategories = [SEARCH, RECENT, OWNED];
+ var virtualCategories = [SEARCH, RECENT, OWNED, TAGS];
if (!APP.loggedIn) {
displayedCategories = [FILES_DATA];
@@ -1444,6 +1453,7 @@ define([
case SEARCH: pName = SEARCH_NAME; break;
case RECENT: pName = RECENT_NAME; break;
case OWNED: pName = OWNED_NAME; break;
+ case TAGS: pName = TAGS_NAME; break;
default: pName = name;
}
return pName;
@@ -1512,6 +1522,8 @@ define([
case OWNED:
msg = Messages.fm_info_owned;
break;
+ case TAGS:
+ break;
default:
msg = undefined;
}
@@ -2136,6 +2148,13 @@ define([
}
var $openDir = $(' ', {'class': 'cp-app-drive-search-opendir'}).append($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
$('').append($icon).append($title).append($typeName).append($type).appendTo($table);
$(' ').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)
// NOTE: Elements in the trash are not using the same storage structure as the others
// _WORKGROUP_ : do not change the lastOpenedFolder value in localStorage
@@ -2255,10 +2303,9 @@ define([
var isTrashRoot = filesOp.comparePath(path, [TRASH]);
var isTemplate = filesOp.comparePath(path, [TEMPLATE]);
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 isSearch = path[0] === SEARCH;
+ var isTags = path[0] === TAGS;
var root = isVirtual ? undefined : filesOp.find(path);
if (!isVirtual && typeof(root) === "undefined") {
@@ -2292,7 +2339,7 @@ define([
var $dirContent = $('', {id: FOLDER_CONTENT_ID});
$dirContent.data('path', path);
- if (!isSearch) {
+ if (!isSearch && !isTags) {
var mode = getViewMode();
if (mode) {
$dirContent.addClass(getViewModeClass());
@@ -2354,10 +2401,12 @@ define([
displayTrashRoot($list, $folderHeader, $fileHeader);
} else if (isSearch) {
displaySearch($list, path[1]);
- } else if (isRecent) {
+ } else if (path[0] === RECENT) {
displayRecent($list);
- } else if (isOwned) {
+ } else if (path[0] === OWNED) {
displayOwned($list);
+ } else if (isTags) {
+ displayTags($list);
} else {
$dirContent.contextmenu(openContextMenu('content'));
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 = $('
', { '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 = $('', { 'class': 'cp-app-drive-tree-category' })
- .append($allfilesElement);
- $container.append($allfilesList);
- };
-
var createTrash = function ($container, path) {
var $icon = filesOp.isFolderEmpty(files[TRASH]) ? $trashEmptyIcon.clone() : $trashIcon.clone();
var isOpened = filesOp.comparePath(path, currentPath);
@@ -2530,29 +2560,11 @@ define([
$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 = $('', { '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 = $('', { 'class': 'cp-app-drive-tree-category' }).append($element);
- $container.append($list);
- };
-
var search = APP.Search = {};
var createSearch = function ($container) {
var isInSearch = currentPath[0] === SEARCH;
var $div = $('', {'id': 'cp-app-drive-tree-search', 'class': 'cp-unselectable'});
- var $input = $('
', {
+ var $input = APP.Search.$input = $('
', {
id: 'cp-app-drive-tree-search-input',
type: 'text',
draggable: false,
@@ -2602,6 +2614,38 @@ define([
$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 = $('
', { 'class': 'cp-app-drive-tree-category' }).append($element);
+ $container.append($list);
+ };
+
APP.resetTree = function () {
var $categories = $tree.find('.cp-app-drive-tree-categories-container');
var s = $categories.scrollTop() || 0;
@@ -2610,11 +2654,12 @@ define([
if (displayedCategories.indexOf(SEARCH) !== -1) { createSearch($tree); }
var $div = $('', {'class': 'cp-app-drive-tree-categories-container'})
.appendTo($tree);
- if (displayedCategories.indexOf(RECENT) !== -1) { createRecent($div, [RECENT]); }
- if (displayedCategories.indexOf(OWNED) !== -1) { createOwned($div, [OWNED]); }
+ if (displayedCategories.indexOf(TAGS) !== -1) { createCategory($div, TAGS); }
+ 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(TEMPLATE) !== -1) { createTemplate($div, [TEMPLATE]); }
- if (displayedCategories.indexOf(FILES_DATA) !== -1) { createAllFiles($div, [FILES_DATA]); }
+ if (displayedCategories.indexOf(TEMPLATE) !== -1) { createCategory($div, TEMPLATE); }
+ if (displayedCategories.indexOf(FILES_DATA) !== -1) { createCategory($div, FILES_DATA); }
if (displayedCategories.indexOf(TRASH) !== -1) { createTrash($div, [TRASH]); }
$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)) {
return void cb('NOT_FILE');
}
diff --git a/www/kanban/app-kanban.less b/www/kanban/app-kanban.less
new file mode 100644
index 000000000..3e2b26bcb
--- /dev/null
+++ b/www/kanban/app-kanban.less
@@ -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;
+ }
+ }
+}
diff --git a/www/kanban/index.html b/www/kanban/index.html
new file mode 100644
index 000000000..e1f312ebd
--- /dev/null
+++ b/www/kanban/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
CryptPad
+
+
+
+
+
+
+
+
+
+
+
diff --git a/www/kanban/inner.html b/www/kanban/inner.html
new file mode 100644
index 000000000..0cc1ecdb2
--- /dev/null
+++ b/www/kanban/inner.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/www/kanban/inner.js b/www/kanban/inner.js
new file mode 100644
index 000000000..96c8317c0
--- /dev/null
+++ b/www/kanban/inner.js
@@ -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 $('
', {
+ '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 = $('
', {'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();
+});
diff --git a/www/kanban/jkanban.css b/www/kanban/jkanban.css
new file mode 100644
index 000000000..4bbb28128
--- /dev/null
+++ b/www/kanban/jkanban.css
@@ -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;
+}
diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js
new file mode 100644
index 000000000..bc9648ccb
--- /dev/null
+++ b/www/kanban/jkanban.js
@@ -0,0 +1,1547 @@
+(function e(t, n, r) {
+ function s(o, u) {
+ if (!n[o]) {
+ if (!t[o]) {
+ var a = typeof require == "function" && require;
+ if (!u && a) return a(o, !0);
+ if (i) return i(o, !0);
+ var f = new Error("Cannot find module '" + o + "'");
+ throw f.code = "MODULE_NOT_FOUND", f
+ }
+ var l = n[o] = {
+ exports: {}
+ };
+ t[o][0].call(l.exports, function (e) {
+ var n = t[o][1][e];
+ return s(n ? n : e)
+ }, l, l.exports, e, t, n, r)
+ }
+ return n[o].exports
+ }
+ var i = typeof require == "function" && require;
+ for (var o = 0; o < r.length; o++) s(r[o]);
+ return s
+})({
+ 1: [function (require, module, exports) {
+ /**
+ * jKanban
+ * Vanilla Javascript plugin for manage kanban boards
+ *
+ * @site: http://www.riccardotartaglia.it/jkanban/
+ * @author: Riccardo Tartaglia
+ */
+
+ //Require dragula
+ var dragula = require('dragula');
+
+ (function () {
+
+ this.jKanban = function () {
+ var self = this;
+ this.element = '';
+ this.container = '';
+ this.boardContainer = [];
+ this.dragula = dragula;
+ this.drake = '';
+ this.drakeBoard = '';
+ this.addItemButton = false;
+ this.buttonContent = '+';
+ defaults = {
+ element: '',
+ gutter: '15px',
+ widthBoard: '250px',
+ responsive: '700',
+ colors: ["yellow", "green", "blue", "red", "orange"],
+ boards: [],
+ dragBoards: true,
+ addItemButton: false,
+ buttonContent: '+',
+ dragEl: function (el, source) {},
+ dragendEl: function (el) {},
+ dropEl: function (el, target, source, sibling) {},
+ dragcancelEl: function (el, boardId) {},
+ dragBoard: function (el, source) {},
+ dragendBoard: function (el) {},
+ dropBoard: function (el, target, source, sibling) {},
+ click: function (el) {},
+ boardTitleclick: function (el, boardId) {},
+ buttonClick: function (el, boardId) {},
+ colorClick: function (el, boardId) {},
+ removeClick: function (el, boardId) {},
+ onChange: function () {}
+ };
+
+ if (arguments[0] && typeof arguments[0] === "object") {
+ this.options = __extendDefaults(defaults, arguments[0]);
+ }
+
+ this.init = function () {
+ // set initial boards
+ __setBoard();
+ //set drag with dragula
+ if (window.innerWidth > self.options.responsive) {
+
+ //Init Drag Board
+ self.drakeBoard = self.dragula([self.container], {
+ moves: function (el, source, handle, sibling) {
+ if (!self.options.dragBoards) return false;
+ return (handle.classList.contains('kanban-board-header') || handle.classList.contains('kanban-title-board'));
+ },
+ accepts: function (el, target, source, sibling) {
+ return target.classList.contains('kanban-container');
+ },
+ revertOnSpill: true,
+ direction: 'horizontal',
+ })
+ .on('drag', function (el, source) {
+ el.classList.add('is-moving');
+ self.options.dragBoard(el, source);
+ if (typeof (el.dragfn) === 'function')
+ el.dragfn(el, source);
+ })
+ .on('dragend', function (el) {
+ el.classList.remove('is-moving');
+ self.options.dragendBoard(el);
+ if (typeof (el.dragendfn) === 'function')
+ el.dragendfn(el);
+ })
+ .on('drop', function (el, target, source, sibling) {
+ el.classList.remove('is-moving');
+ self.options.dropBoard(el, target, source, sibling);
+ if (typeof (el.dropfn) === 'function')
+ el.dropfn(el, target, source, sibling);
+
+ // TODO: update board object board order
+ console.log("Drop " + $(el).attr("data-id") + " just before " + (sibling ? $(sibling).attr("data-id") : " end "));
+ var index1 = self.options.boards.findIndex(function (element) {
+ return element.id == $(el).attr("data-id");
+ });
+ var index2 = sibling ? self.options.boards.findIndex(function (element) {
+ return element.id == $(sibling).attr("data-id");
+ }) : self.options.boards.length;
+ console.log("Switch " + index1 + " and " + index2);
+ if (index1 < index2)
+ index2 = index2 - 1;
+ self.options.boards.splice(index2, 0, self.options.boards.splice(index1, 1)[0]);
+ // send event that board has changed
+ self.onChange();
+
+ });
+
+ //Init Drag Item
+ self.drake = self.dragula(self.boardContainer, function () {
+ revertOnSpill: true
+ })
+ .on('drag', function (el, source) {
+ // we need to calculate the position before starting to drag
+ self.dragItemPos = self.findElementPosition(el);
+
+ el.classList.add('is-moving');
+ var boardJSON = __findBoardJSON(source.parentNode.dataset.id);
+ if (boardJSON.dragTo !== undefined) {
+ self.options.boards.map(function (board) {
+ if (boardJSON.dragTo.indexOf(board.id) === -1 && board.id !== source.parentNode.dataset.id) {
+ self.findBoard(board.id).classList.add('disabled-board');
+ }
+ })
+ }
+
+ self.options.dragEl(el, source);
+ if (el !== null && typeof (el.dragfn) === 'function')
+ el.dragfn(el, source);
+ })
+ .on('dragend', function (el) {
+ console.log("In dragend");
+ self.options.dragendEl(el);
+ if (el !== null && typeof (el.dragendfn) === 'function')
+ el.dragendfn(el);
+ })
+ .on('cancel', function (el, container, source) {
+ console.log("In cancel");
+ // FIXME custom code
+ var boardId = source.parentNode.dataset.id;
+ self.options.dragcancelEl(el, boardId);
+ })
+ .on('drop', function (el, target, source, sibling) {
+ console.log("In drop");
+
+ // TODO: update board object board order
+ var board1 = self.options.boards.find(function (element) {
+ return element.id == $(source.parentNode).attr("data-id");
+ });
+ var board2 = self.options.boards.find(function (element) {
+ return element.id == $(target.parentNode).attr("data-id");
+ });
+ var pos1 = self.dragItemPos;
+ var pos2 = (sibling) ? self.findElementPosition(sibling) : (board2.item.length + 1);
+ console.log("Drop element " + pos1 + " before " + pos2);
+
+ // TODO: update board object item order
+
+ var allB = document.querySelectorAll('.kanban-board');
+ if (allB.length > 0 && allB !== undefined) {
+ for (var i = 0; i < allB.length; i++) {
+ allB[i].classList.remove('disabled-board');
+ }
+ }
+ var boardJSON = __findBoardJSON(source.parentNode.dataset.id);
+ if (boardJSON.dragTo !== undefined) {
+ if (boardJSON.dragTo.indexOf(target.parentNode.dataset.id) === -1 && target.parentNode.dataset.id !== source.parentNode.dataset.id) {
+ self.drake.cancel(true)
+ }
+ }
+ if (el !== null) {
+ self.options.dropEl(el, target, source, sibling);
+ el.classList.remove('is-moving');
+ if (typeof (el.dropfn) === 'function')
+ el.dropfn(el, target, source, sibling);
+ }
+
+ var item = board1.item[pos1];
+ // if (board1==board2 && pos2
'
+ headerBoard.appendChild(btn);
+ __onButtonClickHandler(btn, board.id);
+ }
+ //content board
+ var contentBoard = document.createElement('main');
+ contentBoard.classList.add('kanban-drag');
+ //add drag to array for dragula
+ self.boardContainer.push(contentBoard);
+ for (var itemkey in board.item) {
+ //create item
+ var itemKanban = board.item[itemkey];
+ var nodeItem = document.createElement('div');
+ nodeItem.classList.add('kanban-item');
+ nodeItem.dataset.eid = itemKanban.id;
+ nodeItem.innerHTML = itemKanban.title;
+ //add function
+ nodeItem.clickfn = itemKanban.click;
+ nodeItem.dragfn = itemKanban.drag;
+ nodeItem.dragendfn = itemKanban.dragend;
+ nodeItem.dropfn = itemKanban.drop;
+ //add click handler of item
+ __onclickHandler(nodeItem);
+ contentBoard.appendChild(nodeItem);
+ }
+ //footer board
+ var footerBoard = document.createElement('footer');
+ //remove button
+ var removeBoard = document.createElement('div');
+ $(removeBoard).text("-")
+ $(removeBoard).addClass("kanban-removeboard");
+ footerBoard.appendChild(removeBoard);
+ __onRemoveClickHandler(removeBoard);
+
+ //board assembly
+ boardNode.appendChild(headerBoard);
+ boardNode.appendChild(contentBoard);
+ boardNode.appendChild(footerBoard);
+ //board add
+ self.container.appendChild(boardNode);
+ }
+
+ // send event that board has changed
+ self.onChange();
+
+ return self;
+ }
+
+ this.setBoards = function (boards) {
+ for (var boardkey in boards) {
+ // single board
+ var board = boards[boardkey];
+ this.removeBoard(board.id);
+ }
+ this.options.boards = [];
+ this.addBoards(boards);
+ }
+
+ this.findBoard = function (id) {
+ var el = self.element.querySelector('[data-id="' + id + '"]');
+ return el;
+ }
+
+ this.findElement = function (id) {
+ var el = self.element.querySelector('[data-eid="' + id + '"]');
+ return el;
+ }
+
+ this.findElementPosition = function (el) {
+ // we are looking at the element position in the child array
+ return $(el.parentNode.children).index(el);
+ }
+
+ this.getBoardElements = function (id) {
+ var board = self.element.querySelector('[data-id="' + id + '"] .kanban-drag');
+ return (board.childNodes);
+ }
+
+ this.removeElement = function (el) {
+ if (typeof (el) === 'string')
+ el = self.element.querySelector('[data-eid="' + el + '"]');
+ el.remove();
+
+ // send event that board has changed
+ self.onChange();
+
+ return self;
+ };
+
+ this.removeBoard = function (board) {
+ if (typeof (board) === 'string')
+ board = self.element.querySelector('[data-id="' + board + '"]');
+ if (board) {
+ board.remove();
+
+ // send event that board has changed
+ self.onChange();
+ }
+
+ return self;
+ }
+
+ // board button on click function
+ this.onButtonClick = function (el) {
+
+ }
+
+ this.onChange = function () {
+ self.options.onChange();
+ }
+
+ this.getBoardsJSON = function (id) {
+ return self.options.boards;
+ }
+
+ this.getBoardJSON = function (id) {
+ return __findBoardJSON(id);
+ }
+
+ //PRIVATE FUNCTION
+ function __extendDefaults(source, properties) {
+ var property;
+ for (property in properties) {
+ if (properties.hasOwnProperty(property)) {
+ source[property] = properties[property];
+ }
+ }
+ return source;
+ }
+
+ function __setBoard() {
+ self.element = document.querySelector(self.options.element);
+ //create container
+ var boardContainerOuter = document.createElement('div');
+ boardContainerOuter.classList.add('kanban-container-outer');
+ var boardContainer = document.createElement('div');
+ boardContainer.classList.add('kanban-container');
+ boardContainerOuter.appendChild(boardContainer);
+ var addBoard = document.createElement('div');
+ addBoard.id = 'kanban-addboard';
+ $(addBoard).text("+");
+ boardContainerOuter.appendChild(addBoard);
+
+ self.container = boardContainer;
+ //add boards
+ self.addBoards(self.options.boards);
+ //appends to container
+ self.element.appendChild(boardContainerOuter);
+
+ // send event that board has changed
+ self.onChange();
+ };
+
+ function __onclickHandler(nodeItem, clickfn) {
+ nodeItem.addEventListener('click', function (e) {
+ e.preventDefault;
+ self.options.click(this);
+ if (typeof (this.clickfn) === 'function')
+ this.clickfn(this);
+ });
+ }
+
+ function __onboardTitleClickHandler(nodeItem, clickfn) {
+ nodeItem.addEventListener('click', function (e) {
+ e.preventDefault;
+ self.options.boardTitleClick(this, e);
+ if (typeof (this.clickfn) === 'function')
+ this.clickfn(this);
+ });
+ }
+
+ function __onColorClickHandler(nodeItem, clickfn) {
+ nodeItem.addEventListener('click', function (e) {
+ e.preventDefault;
+ self.options.colorClick(this);
+ if (typeof (this.clickfn) === 'function')
+ this.clickfn(this);
+ });
+ }
+
+ function __onRemoveClickHandler(nodeItem, clickfn) {
+ nodeItem.addEventListener('click', function (e) {
+ e.preventDefault;
+ self.options.removeClick(this);
+ if (typeof (this.clickfn) === 'function')
+ this.clickfn(this);
+ });
+ }
+
+ function __onButtonClickHandler(nodeItem, boardId) {
+ nodeItem.addEventListener('click', function (e) {
+ e.preventDefault;
+ self.options.buttonClick(this, boardId, e);
+ // if(typeof(this.clickfn) === 'function')
+ // this.clickfn(this);
+ });
+ }
+
+ function __findBoardJSON(id) {
+ var el = []
+ self.options.boards.map(function (board) {
+ if (board.id === id) {
+ return el.push(board)
+ }
+ })
+ return el[0]
+ }
+
+
+ //init plugin
+ this.init();
+ };
+ }());
+
+
+}, {
+ "dragula": 9
+ }],
+ 2: [function (require, module, exports) {
+ module.exports = function atoa(a, n) {
+ return Array.prototype.slice.call(a, n);
+ }
+
+}, {}],
+ 3: [function (require, module, exports) {
+ 'use strict';
+
+ var ticky = require('ticky');
+
+ module.exports = function debounce(fn, args, ctx) {
+ if (!fn) {
+ return;
+ }
+ ticky(function run() {
+ fn.apply(ctx || null, args || []);
+ });
+ };
+
+}, {
+ "ticky": 10
+ }],
+ 4: [function (require, module, exports) {
+ 'use strict';
+
+ var atoa = require('atoa');
+ var debounce = require('./debounce');
+
+ module.exports = function emitter(thing, options) {
+ var opts = options || {};
+ var evt = {};
+ if (thing === undefined) {
+ thing = {};
+ }
+ thing.on = function (type, fn) {
+ if (!evt[type]) {
+ evt[type] = [fn];
+ } else {
+ evt[type].push(fn);
+ }
+ return thing;
+ };
+ thing.once = function (type, fn) {
+ fn._once = true; // thing.off(fn) still works!
+ thing.on(type, fn);
+ return thing;
+ };
+ thing.off = function (type, fn) {
+ var c = arguments.length;
+ if (c === 1) {
+ delete evt[type];
+ } else if (c === 0) {
+ evt = {};
+ } else {
+ var et = evt[type];
+ if (!et) {
+ return thing;
+ }
+ et.splice(et.indexOf(fn), 1);
+ }
+ return thing;
+ };
+ thing.emit = function () {
+ var args = atoa(arguments);
+ return thing.emitterSnapshot(args.shift()).apply(this, args);
+ };
+ thing.emitterSnapshot = function (type) {
+ var et = (evt[type] || []).slice(0);
+ return function () {
+ var args = atoa(arguments);
+ var ctx = this || thing;
+ if (type === 'error' && opts.throws !== false && !et.length) {
+ throw args.length === 1 ? args[0] : args;
+ }
+ et.forEach(function emitter(listen) {
+ if (opts.async) {
+ debounce(listen, args, ctx);
+ } else {
+ listen.apply(ctx, args);
+ }
+ if (listen._once) {
+ thing.off(type, listen);
+ }
+ });
+ return thing;
+ };
+ };
+ return thing;
+ };
+
+}, {
+ "./debounce": 3,
+ "atoa": 2
+ }],
+ 5: [function (require, module, exports) {
+ (function (global) {
+ 'use strict';
+
+ var customEvent = require('custom-event');
+ var eventmap = require('./eventmap');
+ var doc = global.document;
+ var addEvent = addEventEasy;
+ var removeEvent = removeEventEasy;
+ var hardCache = [];
+
+ if (!global.addEventListener) {
+ addEvent = addEventHard;
+ removeEvent = removeEventHard;
+ }
+
+ module.exports = {
+ add: addEvent,
+ remove: removeEvent,
+ fabricate: fabricateEvent
+ };
+
+ function addEventEasy(el, type, fn, capturing) {
+ return el.addEventListener(type, fn, capturing);
+ }
+
+ function addEventHard(el, type, fn) {
+ return el.attachEvent('on' + type, wrap(el, type, fn));
+ }
+
+ function removeEventEasy(el, type, fn, capturing) {
+ return el.removeEventListener(type, fn, capturing);
+ }
+
+ function removeEventHard(el, type, fn) {
+ var listener = unwrap(el, type, fn);
+ if (listener) {
+ return el.detachEvent('on' + type, listener);
+ }
+ }
+
+ function fabricateEvent(el, type, model) {
+ var e = eventmap.indexOf(type) === -1 ? makeCustomEvent() : makeClassicEvent();
+ if (el.dispatchEvent) {
+ el.dispatchEvent(e);
+ } else {
+ el.fireEvent('on' + type, e);
+ }
+
+ function makeClassicEvent() {
+ var e;
+ if (doc.createEvent) {
+ e = doc.createEvent('Event');
+ e.initEvent(type, true, true);
+ } else if (doc.createEventObject) {
+ e = doc.createEventObject();
+ }
+ return e;
+ }
+
+ function makeCustomEvent() {
+ return new customEvent(type, {
+ detail: model
+ });
+ }
+ }
+
+ function wrapperFactory(el, type, fn) {
+ return function wrapper(originalEvent) {
+ var e = originalEvent || global.event;
+ e.target = e.target || e.srcElement;
+ e.preventDefault = e.preventDefault || function preventDefault() {
+ e.returnValue = false;
+ };
+ e.stopPropagation = e.stopPropagation || function stopPropagation() {
+ e.cancelBubble = true;
+ };
+ e.which = e.which || e.keyCode;
+ fn.call(el, e);
+ };
+ }
+
+ function wrap(el, type, fn) {
+ var wrapper = unwrap(el, type, fn) || wrapperFactory(el, type, fn);
+ hardCache.push({
+ wrapper: wrapper,
+ element: el,
+ type: type,
+ fn: fn
+ });
+ return wrapper;
+ }
+
+ function unwrap(el, type, fn) {
+ var i = find(el, type, fn);
+ if (i) {
+ var wrapper = hardCache[i].wrapper;
+ hardCache.splice(i, 1); // free up a tad of memory
+ return wrapper;
+ }
+ }
+
+ function find(el, type, fn) {
+ var i, item;
+ for (i = 0; i < hardCache.length; i++) {
+ item = hardCache[i];
+ if (item.element === el && item.type === type && item.fn === fn) {
+ return i;
+ }
+ }
+ }
+
+ }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+}, {
+ "./eventmap": 6,
+ "custom-event": 7
+ }],
+ 6: [function (require, module, exports) {
+ (function (global) {
+ 'use strict';
+
+ var eventmap = [];
+ var eventname = '';
+ var ron = /^on/;
+
+ for (eventname in global) {
+ if (ron.test(eventname)) {
+ eventmap.push(eventname.slice(2));
+ }
+ }
+
+ module.exports = eventmap;
+
+ }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+}, {}],
+ 7: [function (require, module, exports) {
+ (function (global) {
+
+ var NativeCustomEvent = global.CustomEvent;
+
+ function useNative() {
+ try {
+ var p = new NativeCustomEvent('cat', {
+ detail: {
+ foo: 'bar'
+ }
+ });
+ return 'cat' === p.type && 'bar' === p.detail.foo;
+ } catch (e) {}
+ return false;
+ }
+
+ /**
+ * Cross-browser `CustomEvent` constructor.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent.CustomEvent
+ *
+ * @public
+ */
+
+ module.exports = useNative() ? NativeCustomEvent :
+
+ // IE >= 9
+ 'function' === typeof document.createEvent ? function CustomEvent(type, params) {
+ var e = document.createEvent('CustomEvent');
+ if (params) {
+ e.initCustomEvent(type, params.bubbles, params.cancelable, params.detail);
+ } else {
+ e.initCustomEvent(type, false, false, void 0);
+ }
+ return e;
+ } :
+
+ // IE <= 8
+ function CustomEvent(type, params) {
+ var e = document.createEventObject();
+ e.type = type;
+ if (params) {
+ e.bubbles = Boolean(params.bubbles);
+ e.cancelable = Boolean(params.cancelable);
+ e.detail = params.detail;
+ } else {
+ e.bubbles = false;
+ e.cancelable = false;
+ e.detail = void 0;
+ }
+ return e;
+ }
+
+ }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+}, {}],
+ 8: [function (require, module, exports) {
+ 'use strict';
+
+ var cache = {};
+ var start = '(?:^|\\s)';
+ var end = '(?:\\s|$)';
+
+ function lookupClass(className) {
+ var cached = cache[className];
+ if (cached) {
+ cached.lastIndex = 0;
+ } else {
+ cache[className] = cached = new RegExp(start + className + end, 'g');
+ }
+ return cached;
+ }
+
+ function addClass(el, className) {
+ var current = el.className;
+ if (!current.length) {
+ el.className = className;
+ } else if (!lookupClass(className).test(current)) {
+ el.className += ' ' + className;
+ }
+ }
+
+ function rmClass(el, className) {
+ el.className = el.className.replace(lookupClass(className), ' ').trim();
+ }
+
+ module.exports = {
+ add: addClass,
+ rm: rmClass
+ };
+
+}, {}],
+ 9: [function (require, module, exports) {
+ (function (global) {
+ 'use strict';
+
+ var emitter = require('contra/emitter');
+ var crossvent = require('crossvent');
+ var classes = require('./classes');
+ var doc = document;
+ var documentElement = doc.documentElement;
+
+ function dragula(initialContainers, options) {
+ var len = arguments.length;
+ if (len === 1 && Array.isArray(initialContainers) === false) {
+ options = initialContainers;
+ initialContainers = [];
+ }
+ var _mirror; // mirror image
+ var _source; // source container
+ var _item; // item being dragged
+ var _offsetX; // reference x
+ var _offsetY; // reference y
+ var _moveX; // reference move x
+ var _moveY; // reference move y
+ var _initialSibling; // reference sibling when grabbed
+ var _currentSibling; // reference sibling now
+ var _copy; // item used for copying
+ var _renderTimer; // timer for setTimeout renderMirrorImage
+ var _lastDropTarget = null; // last container item was over
+ var _grabbed; // holds mousedown context until first mousemove
+
+ var o = options || {};
+ if (o.moves === void 0) {
+ o.moves = always;
+ }
+ if (o.accepts === void 0) {
+ o.accepts = always;
+ }
+ if (o.invalid === void 0) {
+ o.invalid = invalidTarget;
+ }
+ if (o.containers === void 0) {
+ o.containers = initialContainers || [];
+ }
+ if (o.isContainer === void 0) {
+ o.isContainer = never;
+ }
+ if (o.copy === void 0) {
+ o.copy = false;
+ }
+ if (o.copySortSource === void 0) {
+ o.copySortSource = false;
+ }
+ if (o.revertOnSpill === void 0) {
+ o.revertOnSpill = false;
+ }
+ if (o.removeOnSpill === void 0) {
+ o.removeOnSpill = false;
+ }
+ if (o.direction === void 0) {
+ o.direction = 'vertical';
+ }
+ if (o.ignoreInputTextSelection === void 0) {
+ o.ignoreInputTextSelection = true;
+ }
+ if (o.mirrorContainer === void 0) {
+ o.mirrorContainer = doc.body;
+ }
+
+ var drake = emitter({
+ containers: o.containers,
+ start: manualStart,
+ end: end,
+ cancel: cancel,
+ remove: remove,
+ destroy: destroy,
+ canMove: canMove,
+ dragging: false
+ });
+
+ if (o.removeOnSpill === true) {
+ drake.on('over', spillOver).on('out', spillOut);
+ }
+
+ events();
+
+ return drake;
+
+ function isContainer(el) {
+ return drake.containers.indexOf(el) !== -1 || o.isContainer(el);
+ }
+
+ function events(remove) {
+ var op = remove ? 'remove' : 'add';
+ touchy(documentElement, op, 'mousedown', grab);
+ touchy(documentElement, op, 'mouseup', release);
+ }
+
+ function eventualMovements(remove) {
+ var op = remove ? 'remove' : 'add';
+ touchy(documentElement, op, 'mousemove', startBecauseMouseMoved);
+ }
+
+ function movements(remove) {
+ var op = remove ? 'remove' : 'add';
+ crossvent[op](documentElement, 'selectstart', preventGrabbed); // IE8
+ crossvent[op](documentElement, 'click', preventGrabbed);
+ }
+
+ function destroy() {
+ events(true);
+ release({});
+ }
+
+ function preventGrabbed(e) {
+ if (_grabbed) {
+ e.preventDefault();
+ }
+ }
+
+ function grab(e) {
+ _moveX = e.clientX;
+ _moveY = e.clientY;
+
+ var ignore = whichMouseButton(e) !== 1 || e.metaKey || e.ctrlKey;
+ if (ignore) {
+ return; // we only care about honest-to-god left clicks and touch events
+ }
+ var item = e.target;
+ var context = canStart(item);
+ if (!context) {
+ return;
+ }
+ _grabbed = context;
+ eventualMovements();
+ if (e.type === 'mousedown') {
+ if (isInput(item)) { // see also: https://github.com/bevacqua/dragula/issues/208
+ item.focus(); // fixes https://github.com/bevacqua/dragula/issues/176
+ } else {
+ e.preventDefault(); // fixes https://github.com/bevacqua/dragula/issues/155
+ }
+ }
+ }
+
+ function startBecauseMouseMoved(e) {
+ if (!_grabbed) {
+ return;
+ }
+ if (whichMouseButton(e) === 0) {
+ release({});
+ return; // when text is selected on an input and then dragged, mouseup doesn't fire. this is our only hope
+ }
+ // truthy check fixes #239, equality fixes #207
+ if (e.clientX !== void 0 && e.clientX === _moveX && e.clientY !== void 0 && e.clientY === _moveY) {
+ return;
+ }
+ if (o.ignoreInputTextSelection) {
+ var clientX = getCoord('clientX', e);
+ var clientY = getCoord('clientY', e);
+ var elementBehindCursor = doc.elementFromPoint(clientX, clientY);
+ if (isInput(elementBehindCursor)) {
+ return;
+ }
+ }
+
+ var grabbed = _grabbed; // call to end() unsets _grabbed
+ eventualMovements(true);
+ movements();
+ end();
+ start(grabbed);
+
+ var offset = getOffset(_item);
+ _offsetX = getCoord('pageX', e) - offset.left;
+ _offsetY = getCoord('pageY', e) - offset.top;
+
+ classes.add(_copy || _item, 'gu-transit');
+ renderMirrorImage();
+ drag(e);
+ }
+
+ function canStart(item) {
+ if (drake.dragging && _mirror) {
+ return;
+ }
+ if (isContainer(item)) {
+ return; // don't drag container itself
+ }
+ var handle = item;
+ while (getParent(item) && isContainer(getParent(item)) === false) {
+ if (o.invalid(item, handle)) {
+ return;
+ }
+ item = getParent(item); // drag target should be a top element
+ if (!item) {
+ return;
+ }
+ }
+ var source = getParent(item);
+ if (!source) {
+ return;
+ }
+ if (o.invalid(item, handle)) {
+ return;
+ }
+
+ var movable = o.moves(item, source, handle, nextEl(item));
+ if (!movable) {
+ return;
+ }
+
+ return {
+ item: item,
+ source: source
+ };
+ }
+
+ function canMove(item) {
+ return !!canStart(item);
+ }
+
+ function manualStart(item) {
+ var context = canStart(item);
+ if (context) {
+ start(context);
+ }
+ }
+
+ function start(context) {
+ if (isCopy(context.item, context.source)) {
+ _copy = context.item.cloneNode(true);
+ drake.emit('cloned', _copy, context.item, 'copy');
+ }
+
+ _source = context.source;
+ _item = context.item;
+ _initialSibling = _currentSibling = nextEl(context.item);
+
+ drake.dragging = true;
+ drake.emit('drag', _item, _source);
+ }
+
+ function invalidTarget() {
+ return false;
+ }
+
+ function end() {
+ if (!drake.dragging) {
+ return;
+ }
+ var item = _copy || _item;
+ drop(item, getParent(item));
+ }
+
+ function ungrab() {
+ _grabbed = false;
+ eventualMovements(true);
+ movements(true);
+ }
+
+ function release(e) {
+ ungrab();
+
+ if (!drake.dragging) {
+ return;
+ }
+ var item = _copy || _item;
+ var clientX = getCoord('clientX', e);
+ var clientY = getCoord('clientY', e);
+ var elementBehindCursor = getElementBehindPoint(_mirror, clientX, clientY);
+ var dropTarget = findDropTarget(elementBehindCursor, clientX, clientY);
+ if (dropTarget && ((_copy && o.copySortSource) || (!_copy || dropTarget !== _source))) {
+ drop(item, dropTarget);
+ } else if (o.removeOnSpill) {
+ remove();
+ } else {
+ cancel();
+ }
+ }
+
+ function drop(item, target) {
+ var parent = getParent(item);
+ if (_copy && o.copySortSource && target === _source) {
+ parent.removeChild(_item);
+ }
+ if (isInitialPlacement(target)) {
+ drake.emit('cancel', item, _source, _source);
+ } else {
+ drake.emit('drop', item, target, _source, _currentSibling);
+ }
+ cleanup();
+ }
+
+ function remove() {
+ if (!drake.dragging) {
+ return;
+ }
+ var item = _copy || _item;
+ var parent = getParent(item);
+ if (parent) {
+ parent.removeChild(item);
+ }
+ drake.emit(_copy ? 'cancel' : 'remove', item, parent, _source);
+ cleanup();
+ }
+
+ function cancel(revert) {
+ if (!drake.dragging) {
+ return;
+ }
+ var reverts = arguments.length > 0 ? revert : o.revertOnSpill;
+ var item = _copy || _item;
+ var parent = getParent(item);
+ var initial = isInitialPlacement(parent);
+ if (initial === false && reverts) {
+ if (_copy) {
+ if (parent) {
+ parent.removeChild(_copy);
+ }
+ } else {
+ _source.insertBefore(item, _initialSibling);
+ }
+ }
+ if (initial || reverts) {
+ drake.emit('cancel', item, _source, _source);
+ } else {
+ drake.emit('drop', item, parent, _source, _currentSibling);
+ }
+ cleanup();
+ }
+
+ function cleanup() {
+ var item = _copy || _item;
+ ungrab();
+ removeMirrorImage();
+ if (item) {
+ classes.rm(item, 'gu-transit');
+ }
+ if (_renderTimer) {
+ clearTimeout(_renderTimer);
+ }
+ drake.dragging = false;
+ if (_lastDropTarget) {
+ drake.emit('out', item, _lastDropTarget, _source);
+ }
+ drake.emit('dragend', item);
+ _source = _item = _copy = _initialSibling = _currentSibling = _renderTimer = _lastDropTarget = null;
+ }
+
+ function isInitialPlacement(target, s) {
+ var sibling;
+ if (s !== void 0) {
+ sibling = s;
+ } else if (_mirror) {
+ sibling = _currentSibling;
+ } else {
+ sibling = nextEl(_copy || _item);
+ }
+ return target === _source && sibling === _initialSibling;
+ }
+
+ function findDropTarget(elementBehindCursor, clientX, clientY) {
+ var target = elementBehindCursor;
+ while (target && !accepted()) {
+ target = getParent(target);
+ }
+ return target;
+
+ function accepted() {
+ var droppable = isContainer(target);
+ if (droppable === false) {
+ return false;
+ }
+
+ var immediate = getImmediateChild(target, elementBehindCursor);
+ var reference = getReference(target, immediate, clientX, clientY);
+ var initial = isInitialPlacement(target, reference);
+ if (initial) {
+ return true; // should always be able to drop it right back where it was
+ }
+ return o.accepts(_item, target, _source, reference);
+ }
+ }
+
+ function drag(e) {
+ if (!_mirror) {
+ return;
+ }
+ e.preventDefault();
+
+ var clientX = getCoord('clientX', e);
+ var clientY = getCoord('clientY', e);
+ var x = clientX - _offsetX;
+ var y = clientY - _offsetY;
+
+ _mirror.style.left = x + 'px';
+ _mirror.style.top = y + 'px';
+
+ var item = _copy || _item;
+ var elementBehindCursor = getElementBehindPoint(_mirror, clientX, clientY);
+ var dropTarget = findDropTarget(elementBehindCursor, clientX, clientY);
+ var changed = dropTarget !== null && dropTarget !== _lastDropTarget;
+ if (changed || dropTarget === null) {
+ out();
+ _lastDropTarget = dropTarget;
+ over();
+ }
+ var parent = getParent(item);
+ if (dropTarget === _source && _copy && !o.copySortSource) {
+ if (parent) {
+ parent.removeChild(item);
+ }
+ return;
+ }
+ var reference;
+ var immediate = getImmediateChild(dropTarget, elementBehindCursor);
+ if (immediate !== null) {
+ reference = getReference(dropTarget, immediate, clientX, clientY);
+ } else if (o.revertOnSpill === true && !_copy) {
+ reference = _initialSibling;
+ dropTarget = _source;
+ } else {
+ if (_copy && parent) {
+ parent.removeChild(item);
+ }
+ return;
+ }
+ if (
+ (reference === null && changed) ||
+ reference !== item &&
+ reference !== nextEl(item)
+ ) {
+ _currentSibling = reference;
+ dropTarget.insertBefore(item, reference);
+ drake.emit('shadow', item, dropTarget, _source);
+ }
+
+ function moved(type) {
+ drake.emit(type, item, _lastDropTarget, _source);
+ }
+
+ function over() {
+ if (changed) {
+ moved('over');
+ }
+ }
+
+ function out() {
+ if (_lastDropTarget) {
+ moved('out');
+ }
+ }
+ }
+
+ function spillOver(el) {
+ classes.rm(el, 'gu-hide');
+ }
+
+ function spillOut(el) {
+ if (drake.dragging) {
+ classes.add(el, 'gu-hide');
+ }
+ }
+
+ function renderMirrorImage() {
+ if (_mirror) {
+ return;
+ }
+ var rect = _item.getBoundingClientRect();
+ _mirror = _item.cloneNode(true);
+ _mirror.style.width = getRectWidth(rect) + 'px';
+ _mirror.style.height = getRectHeight(rect) + 'px';
+ classes.rm(_mirror, 'gu-transit');
+ classes.add(_mirror, 'gu-mirror');
+ o.mirrorContainer.appendChild(_mirror);
+ touchy(documentElement, 'add', 'mousemove', drag);
+ classes.add(o.mirrorContainer, 'gu-unselectable');
+ drake.emit('cloned', _mirror, _item, 'mirror');
+ }
+
+ function removeMirrorImage() {
+ if (_mirror) {
+ classes.rm(o.mirrorContainer, 'gu-unselectable');
+ touchy(documentElement, 'remove', 'mousemove', drag);
+ getParent(_mirror).removeChild(_mirror);
+ _mirror = null;
+ }
+ }
+
+ function getImmediateChild(dropTarget, target) {
+ var immediate = target;
+ while (immediate !== dropTarget && getParent(immediate) !== dropTarget) {
+ immediate = getParent(immediate);
+ }
+ if (immediate === documentElement) {
+ return null;
+ }
+ return immediate;
+ }
+
+ function getReference(dropTarget, target, x, y) {
+ var horizontal = o.direction === 'horizontal';
+ var reference = target !== dropTarget ? inside() : outside();
+ return reference;
+
+ function outside() { // slower, but able to figure out any position
+ var len = dropTarget.children.length;
+ var i;
+ var el;
+ var rect;
+ for (i = 0; i < len; i++) {
+ el = dropTarget.children[i];
+ rect = el.getBoundingClientRect();
+ if (horizontal && (rect.left + rect.width / 2) > x) {
+ return el;
+ }
+ if (!horizontal && (rect.top + rect.height / 2) > y) {
+ return el;
+ }
+ }
+ return null;
+ }
+
+ function inside() { // faster, but only available if dropped inside a child element
+ var rect = target.getBoundingClientRect();
+ if (horizontal) {
+ return resolve(x > rect.left + getRectWidth(rect) / 2);
+ }
+ return resolve(y > rect.top + getRectHeight(rect) / 2);
+ }
+
+ function resolve(after) {
+ return after ? nextEl(target) : target;
+ }
+ }
+
+ function isCopy(item, container) {
+ return typeof o.copy === 'boolean' ? o.copy : o.copy(item, container);
+ }
+ }
+
+ function touchy(el, op, type, fn) {
+ var touch = {
+ mouseup: 'touchend',
+ mousedown: 'touchstart',
+ mousemove: 'touchmove'
+ };
+ var pointers = {
+ mouseup: 'pointerup',
+ mousedown: 'pointerdown',
+ mousemove: 'pointermove'
+ };
+ var microsoft = {
+ mouseup: 'MSPointerUp',
+ mousedown: 'MSPointerDown',
+ mousemove: 'MSPointerMove'
+ };
+ if (global.navigator.pointerEnabled) {
+ crossvent[op](el, pointers[type], fn);
+ } else if (global.navigator.msPointerEnabled) {
+ crossvent[op](el, microsoft[type], fn);
+ } else {
+ crossvent[op](el, touch[type], fn);
+ crossvent[op](el, type, fn);
+ }
+ }
+
+ function whichMouseButton(e) {
+ if (e.touches !== void 0) {
+ return e.touches.length;
+ }
+ if (e.which !== void 0 && e.which !== 0) {
+ return e.which;
+ } // see https://github.com/bevacqua/dragula/issues/261
+ if (e.buttons !== void 0) {
+ return e.buttons;
+ }
+ var button = e.button;
+ if (button !== void 0) { // see https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/event.js#L573-L575
+ return button & 1 ? 1 : button & 2 ? 3 : (button & 4 ? 2 : 0);
+ }
+ }
+
+ function getOffset(el) {
+ var rect = el.getBoundingClientRect();
+ return {
+ left: rect.left + getScroll('scrollLeft', 'pageXOffset'),
+ top: rect.top + getScroll('scrollTop', 'pageYOffset')
+ };
+ }
+
+ function getScroll(scrollProp, offsetProp) {
+ if (typeof global[offsetProp] !== 'undefined') {
+ return global[offsetProp];
+ }
+ if (documentElement.clientHeight) {
+ return documentElement[scrollProp];
+ }
+ return doc.body[scrollProp];
+ }
+
+ function getElementBehindPoint(point, x, y) {
+ var p = point || {};
+ var state = p.className;
+ var el;
+ p.className += ' gu-hide';
+ el = doc.elementFromPoint(x, y);
+ p.className = state;
+ return el;
+ }
+
+ function never() {
+ return false;
+ }
+
+ function always() {
+ return true;
+ }
+
+ function getRectWidth(rect) {
+ return rect.width || (rect.right - rect.left);
+ }
+
+ function getRectHeight(rect) {
+ return rect.height || (rect.bottom - rect.top);
+ }
+
+ function getParent(el) {
+ return el.parentNode === doc ? null : el.parentNode;
+ }
+
+ function isInput(el) {
+ return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT' || isEditable(el);
+ }
+
+ function isEditable(el) {
+ if (!el) {
+ return false;
+ } // no parents were editable
+ if (el.contentEditable === 'false') {
+ return false;
+ } // stop the lookup
+ if (el.contentEditable === 'true') {
+ return true;
+ } // found a contentEditable element in the chain
+ return isEditable(getParent(el)); // contentEditable is set to 'inherit'
+ }
+
+ function nextEl(el) {
+ return el.nextElementSibling || manually();
+
+ function manually() {
+ var sibling = el;
+ do {
+ sibling = sibling.nextSibling;
+ } while (sibling && sibling.nodeType !== 1);
+ return sibling;
+ }
+ }
+
+ function getEventHost(e) {
+ // on touchend event, we have to use `e.changedTouches`
+ // see http://stackoverflow.com/questions/7192563/touchend-event-properties
+ // see https://github.com/bevacqua/dragula/issues/34
+ if (e.targetTouches && e.targetTouches.length) {
+ return e.targetTouches[0];
+ }
+ if (e.changedTouches && e.changedTouches.length) {
+ return e.changedTouches[0];
+ }
+ return e;
+ }
+
+ function getCoord(coord, e) {
+ var host = getEventHost(e);
+ var missMap = {
+ pageX: 'clientX', // IE8
+ pageY: 'clientY' // IE8
+ };
+ if (coord in missMap && !(coord in host) && missMap[coord] in host) {
+ coord = missMap[coord];
+ }
+ return host[coord];
+ }
+
+ module.exports = dragula;
+
+ }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+}, {
+ "./classes": 8,
+ "contra/emitter": 4,
+ "crossvent": 5
+ }],
+ 10: [function (require, module, exports) {
+ var si = typeof setImmediate === 'function',
+ tick;
+ if (si) {
+ tick = function (fn) {
+ setImmediate(fn);
+ };
+ } else {
+ tick = function (fn) {
+ setTimeout(fn, 0);
+ };
+ }
+
+ module.exports = tick;
+}, {}]
+}, {}, [1]);