From e7de9229a89c2e8d60cd4906076131d5fee5fcf0 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Tue, 5 Sep 2017 17:29:03 +0200 Subject: [PATCH 001/415] Do not print toolbar and collaboration (#178). paul --- www/pad/inner.html | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/www/pad/inner.html b/www/pad/inner.html index afbc9e314..e5e58f51c 100644 --- a/www/pad/inner.html +++ b/www/pad/inner.html @@ -33,6 +33,17 @@ .cke_wysiwyg_frame { min-width: 60%; } + + + @media print { + #cke_1_top { + display:none; + } + body.app-pad .userlist-drawer { + display:none; + } + } + From 99712ec2f01ad4ec2bfc60b252466ff2bb46eec9 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 20 Mar 2018 15:09:31 +0100 Subject: [PATCH 002/415] getMetadata RPC --- rpc.js | 27 ++++++++++++++++++++++++--- www/common/outer/async-store.js | 26 ++++++++++++++++++++------ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/rpc.js b/rpc.js index 9cdb440f3..6599d66bb 100644 --- a/rpc.js +++ b/rpc.js @@ -326,6 +326,24 @@ var getFileSize = function (Env, channel, cb) { }); }; +var getMetadata = function (Env, channel, cb) { + if (!isValidId(channel)) { return void cb('INVALID_CHAN'); } + + if (channel.length === 32) { + if (typeof(Env.msgStore.getChannelMetadata) !== 'function') { + return cb('GET_CHANNEL_METADATA_UNSUPPORTED'); + } + + return void Env.msgStore.getChannelMetadata(channel, function (e, data /*:object*/) { + if (e) { + if (e.code === 'INVALID_METADATA') { return void cb(void 0, {}); } + return void cb(e.code); + } + cb(void 0, data); + }); + } +}; + var getMultipleFileSize = function (Env, channels, cb) { if (!Array.isArray(channels)) { return cb('INVALID_PIN_LIST'); } if (typeof(Env.msgStore.getChannelSize) !== 'function') { @@ -1128,6 +1146,7 @@ var isNewChannel = function (Env, channel, cb) { var isUnauthenticatedCall = function (call) { return [ 'GET_FILE_SIZE', + 'GET_METADATA', 'GET_MULTIPLE_FILE_SIZE', 'IS_CHANNEL_PINNED', 'IS_NEW_CHANNEL', @@ -1248,12 +1267,14 @@ RPC.create = function ( } case 'GET_FILE_SIZE': return void getFileSize(Env, msg[1], function (e, size) { - if (e) { - console.error(e); - } WARN(e, msg[1]); respond(e, [null, size, null]); }); + case 'GET_METADATA': + return void getMetadata(Env, msg[1], function (e, data) { + WARN(e, msg[1]); + respond(e, [null, data, null]); + }); case 'GET_MULTIPLE_FILE_SIZE': return void getMultipleFileSize(Env, msg[1], function (e, dict) { if (e) { diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 6790f2ed1..b7f47a1d1 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -429,12 +429,26 @@ define([ toSign.drive = secret.channel; toSign.edPublic = store.proxy.edPublic; var signKey = Crypto.Nacl.util.decodeBase64(secret.keys.signKey); - console.log(Sortify(toSign)); - var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey); - var proofTxt = Crypto.Nacl.util.encodeBase64(proof); - cb({ - proof: proofTxt, - toSign: JSON.parse(Sortify(toSign)) + Store.anonRpcMsg({ + msg: 'GET_METADATA', + data: secret.channel + }, function (data) { + console.log(data[0]); + var metadata = data[0]; + // Owned drive + if (metadata && metadata.owners && metadata.owners.length === 1 && + metadata.owners.indexOf(edPublic) !== -1) { + + return; + } + + // Not owned drive + var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey); + var proofTxt = Crypto.Nacl.util.encodeBase64(proof); + cb({ + proof: proofTxt, + toSign: JSON.parse(Sortify(toSign)) + }); }); }; From a718603b367d02833aac90d67d93b4d80ad370d0 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 21 Mar 2018 18:27:20 +0100 Subject: [PATCH 003/415] Automatic account deletion --- bower.json | 3 +- customize.dist/translations/messages.el.js | 2 +- customize.dist/translations/messages.es.js | 2 +- customize.dist/translations/messages.fr.js | 4 +- customize.dist/translations/messages.js | 4 +- customize.dist/translations/messages.pt-br.js | 2 +- customize.dist/translations/messages.ro.js | 2 +- customize.dist/translations/messages.zh.js | 2 +- rpc.js | 2 +- www/common/common-ui-elements.js | 2 +- www/common/cryptpad-common.js | 14 +++- www/common/outer/async-store.js | 78 ++++++++++++++++--- www/common/outer/local-store.js | 10 ++- www/common/pinpad.js | 11 +++ www/common/sframe-app-framework.js | 2 +- www/common/sframe-chainpad-netflux-inner.js | 2 +- www/common/sframe-common-outer.js | 1 + www/common/sframe-common.js | 30 +++---- www/settings/inner.js | 30 +++++-- 19 files changed, 153 insertions(+), 50 deletions(-) diff --git a/bower.json b/bower.json index f9ee60c20..e2d61a268 100644 --- a/bower.json +++ b/bower.json @@ -46,7 +46,8 @@ "localforage": "^1.5.2", "html2canvas": "^0.4.1", "croppie": "^2.5.0", - "sortablejs": "#^1.6.0" + "sortablejs": "#^1.6.0", + "saferphore": "^0.0.1" }, "resolutions": { "bootstrap": "^v4.0.0" diff --git a/customize.dist/translations/messages.el.js b/customize.dist/translations/messages.el.js index 36a19da87..493be541d 100644 --- a/customize.dist/translations/messages.el.js +++ b/customize.dist/translations/messages.el.js @@ -31,7 +31,7 @@ define(function () { out.websocketError = 'Αδυναμία σύνδεσης στον διακομιστή...'; out.typeError = "Αυτό το pad δεν είναι συμβατό με την επιλεγμένη εφαρμογή"; - out.onLogout = 'Έχετε αποσυνδεθεί, κάντε "κλικ" εδώ για να συνδεθείτε
ή πατήστε Escape για να προσπελάσετε το έγγραφο σε λειτουργία ανάγνωσης μόνο.'; + out.onLogout = 'Έχετε αποσυνδεθεί, {0}κάντε "κλικ" εδώ{1} για να συνδεθείτε
ή πατήστε Escape για να προσπελάσετε το έγγραφο σε λειτουργία ανάγνωσης μόνο.'; out.wrongApp = "Αδυναμία προβολής του περιεχομένου αυτής της συνεδρίας στον περιηγητή σας. Παρακαλώ δοκιμάστε επαναφόρτωση της σελίδας."; out.loading = "Φόρτωση..."; diff --git a/customize.dist/translations/messages.es.js b/customize.dist/translations/messages.es.js index 9e246294f..fc905870a 100644 --- a/customize.dist/translations/messages.es.js +++ b/customize.dist/translations/messages.es.js @@ -152,7 +152,7 @@ define(function () { out.websocketError = "Error al conectarse al servidor WebSocket"; out.typeError = "Este documento no es compatible con la aplicación seleccionada"; - out.onLogout = "Tu sesión está cerrada, haz clic aquí para iniciar sesión
o pulsa Escape para acceder al documento en modo sólo lectura."; + out.onLogout = "Tu sesión está cerrada, {0}haz clic aquí{1} para iniciar sesión
o pulsa Escape para acceder al documento en modo sólo lectura."; out.loading = "Cargando..."; out.error = "Error"; out.language = "Idioma"; diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 37e1cfb18..47bce2af8 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -27,7 +27,7 @@ define(function () { out.websocketError = 'Impossible de se connecter au serveur WebSocket...'; out.typeError = "Ce pad n'est pas compatible avec l'application sélectionnée"; - out.onLogout = 'Vous êtes déconnecté de votre compte utilisateur, cliquez ici pour vous authentifier
ou appuyez sur Échap pour accéder au pad en mode lecture seule.'; + out.onLogout = 'Vous êtes déconnecté de votre compte utilisateur, {0}cliquez ici{1} pour vous authentifier
ou appuyez sur Échap pour accéder au pad en mode lecture seule.'; out.wrongApp = "Impossible d'afficher le contenu de ce document temps-réel dans votre navigateur. Vous pouvez essayer de recharger la page."; out.padNotPinned = 'Ce pad va expirer après 3 mois d\'inactivité, {0}connectez-vous{1} ou {2}enregistrez-vous{3} pour le préserver.'; out.anonymousStoreDisabled = "L'administrateur de cette instance de CryptPad a désactivé le drive pour les utilisateurs non enregistrés. Vous devez vous connecter pour pouvoir utiliser CryptDrive."; @@ -546,6 +546,8 @@ define(function () { out.settings_deleteHint = "La suppression de votre compte utilisateur est permanente. Votre CryptDrive et votre liste de pads seront supprimés du serveur. Le reste de vos pads sera supprimé après 90 jours d'inactivité si personne ne les a stockés dans leur CryptDrive."; out.settings_deleteButton = "Supprimer votre compte"; out.settings_deleteModal = "Veuillez envoyer les informations suivantes à votre administrateur CryptPad afin que vos données soient supprimées du serveur."; + out.settings_deleteConfirm = "Êtes-vous sûr de vouloir supprimer votre compte utilisateur ? Cette action est irréversible."; + out.settings_deleted = "Votre compte utilisateur a été supprimé. Appuyez sur OK pour être rédirigé(e) vers la page d'accueil."; out.settings_anonymous = "Vous n'êtes pas connecté. Ces préférences seront utilisées pour ce navigateur."; out.settings_publicSigningKey = "Clé publique de signature"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 868d107cd..6ab51d35d 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -28,7 +28,7 @@ define(function () { out.websocketError = 'Unable to connect to the websocket server...'; out.typeError = "This pad is not compatible with the selected application"; - out.onLogout = 'You are logged out, click here to log in
or press Escape to access your pad in read-only mode.'; + out.onLogout = 'You are logged out, {0}click here{1} to log in
or press Escape to access your pad in read-only mode.'; out.wrongApp = "Unable to display the content of that realtime session in your browser. Please try to reload that page."; out.padNotPinned = 'This pad will expire after 3 months of inactivity, {0}login{1} or {2}register{3} to preserve it.'; out.anonymousStoreDisabled = "The webmaster of this CryptPad instance has disabled the store for anonymous users. You have to log in to be able to use CryptDrive."; @@ -550,6 +550,8 @@ define(function () { out.settings_deleteHint = "Account deletion is permanent. Your CryptDrive and your list of pads will be deleted from the server. The rest of your pads will be deleted in 90 days if nobody else has stored them in their CryptDrive."; out.settings_deleteButton = "Delete your account"; out.settings_deleteModal = "Share the following information with your CryptPad administrator in order to have your data removed from their server."; + out.settings_deleteConfirm = "Clicking OK will delete your account permanently. Are you sure?"; + out.settings_deleted = "Your user account is now deleted. Press OK to go to the home page."; out.settings_anonymous = "You are not logged in. Settings here are specific to this browser."; out.settings_publicSigningKey = "Public Signing Key"; diff --git a/customize.dist/translations/messages.pt-br.js b/customize.dist/translations/messages.pt-br.js index fbf63d067..a2feea5f9 100644 --- a/customize.dist/translations/messages.pt-br.js +++ b/customize.dist/translations/messages.pt-br.js @@ -38,7 +38,7 @@ define(function () { out.websocketError = 'Incapaz de se conectar com o servidor websocket...'; out.typeError = "Este bloco não é compatível com a aplicação selecionada"; - out.onLogout = 'você foi desconectado, clique aqui para se conectar,
ou pressione ESC para acessar seu bloco em modo somente leitura.'; + out.onLogout = 'você foi desconectado, {0}clique aqui{1} para se conectar,
ou pressione ESC para acessar seu bloco em modo somente leitura.'; out.wrongApp = "Incapaz de mostrar o conteúdo em tempo real no seu navegador. Por favor tente recarregar a página."; out.loading = "Carregando..."; diff --git a/customize.dist/translations/messages.ro.js b/customize.dist/translations/messages.ro.js index 56ec8b05d..fe24c7057 100644 --- a/customize.dist/translations/messages.ro.js +++ b/customize.dist/translations/messages.ro.js @@ -13,7 +13,7 @@ define(function () { out.common_connectionLost = out.updated_0_common_connectionLost; out.websocketError = "Conexiune inexistentă către serverul websocket..."; out.typeError = "Această filă nu este compatibilă cu aplicația aleasă"; - out.onLogout = "Nu mai ești autentificat, apasă aici să te autentifici
sau apasă Escapesă accesezi fila în modul citire."; + out.onLogout = "Nu mai ești autentificat, {0}apasă aici{1} să te autentifici
sau apasă Escapesă accesezi fila în modul citire."; out.wrongApp = "Momentan nu putem arăta conținutul sesiunii în timp real în fereastra ta. Te rugăm reîncarcă pagina."; out.loading = "Încarcă..."; out.error = "Eroare"; diff --git a/customize.dist/translations/messages.zh.js b/customize.dist/translations/messages.zh.js index 25b7fe8c8..422f00f2c 100644 --- a/customize.dist/translations/messages.zh.js +++ b/customize.dist/translations/messages.zh.js @@ -31,7 +31,7 @@ define(function () { out.websocketError = '無法連結上 websocket 伺服器...'; out.typeError = "這個編輯檔與所選的應用程式並不相容"; - out.onLogout = '你已登出, 點擊這裏 來登入
或按Escape 來以唯讀模型使用你的編輯檔案'; + out.onLogout = '你已登出, {0}點擊這裏{1} 來登入
或按Escape 來以唯讀模型使用你的編輯檔案'; out.wrongApp = "無法在瀏覽器顯示即時期間的內容,請試著再重新載入本頁。"; out.loading = "載入中..."; diff --git a/rpc.js b/rpc.js index ce2bf3210..d6f661155 100644 --- a/rpc.js +++ b/rpc.js @@ -1481,7 +1481,7 @@ RPC.create = function ( case 'REMOVE_PINS': return void removePins(Env, safeKey, function (e, response) { if (e) { return void Respond(e); } - Respond(void 0, response); + Respond(void 0, "OK"); }); // restricted to privileged users... case 'UPLOAD': diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 561d7af6a..7f49078e0 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1148,7 +1148,7 @@ define([ // so we can just use those and only check for errors var $container = $('', {'class':'cp-limit-container'}); var todo = function (err, data) { - if (err) { return void console.error(err); } + if (err || !data) { return void console.error(err || 'No data'); } var usage = data.usage; var limit = data.limit; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index e2968aa94..0a9a27f0d 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -622,6 +622,12 @@ define([ window.location.href = '/login/'; }; + common.startAccountDeletion = function (cb) { + // Logout other tabs + LocalStore.logout(null, true); + cb(); + }; + var onMessage = function (cmd, data, cb) { cb = cb || function () {}; switch (cmd) { @@ -701,6 +707,10 @@ define([ case 'DRIVE_REMOVE': { common.drive.onRemove.fire(data); break; } + // Account deletion + case 'DELETE_ACCOUNT': { + common.startAccountDeletion(cb); break; + } } }; @@ -779,11 +789,11 @@ define([ if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); } - if (cfg.userHash && sessionStorage) { + /*if (cfg.userHash && sessionStorage) { // copy User_hash into sessionStorage because cross-domain iframes // on safari replaces localStorage with sessionStorage or something sessionStorage.setItem(Constants.userHashKey, cfg.userHash); - } + }*/ if (cfg.userHash) { var localToken = tryParsing(localStorage.getItem(Constants.tokenKey)); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index b7f47a1d1..a240cf13c 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -16,9 +16,11 @@ define([ '/bower_components/chainpad-crypto/crypto.js?v=0.1.5', '/bower_components/chainpad/chainpad.dist.js', '/bower_components/chainpad-listmap/chainpad-listmap.js', + '/bower_components/nthen/index.js', + '/bower_components/saferphore/index.js', ], function (Sortify, UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger, CpNfWorker, NetConfig, AppConfig, - Crypto, ChainPad, Listmap) { + Crypto, ChainPad, Listmap, nThen, Saferphore) { var Store = {}; var postMessage = function () {}; @@ -421,28 +423,78 @@ define([ }); }; + var getOwnedPads = function () { + var list = []; + store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) { + var data = store.userObject.getFileData(id); + var edPublic = store.proxy.edPublic; + + // Push channels owned by someone else or channel that should have expired + // because of the expiration time + if (data.owners && data.owners.length === 1 && data.owners.indexOf(edPublic) !== -1) { + list.push(Hash.hrefToHexChannelId(data.href)); + } + }); + return list; + }; + var removeOwnedPads = function (waitFor) { + // Delete owned pads + var ownedPads = getOwnedPads(); + var sem = Saferphore.create(10); + ownedPads.forEach(function (c) { + var w = waitFor(); + sem.take(function (give) { + Store.removeOwnedChannel(c, give(function (obj) { + if (obj && obj.error) { console.error(obj.error); } + w(); + })); + }); + }); + }; + Store.deleteAccount = function (data, cb) { - var toSign = { - intent: 'Please delete my account.' - }; + var edPublic = store.proxy.edPublic; var secret = Hash.getSecrets('drive', storeHash); - toSign.drive = secret.channel; - toSign.edPublic = store.proxy.edPublic; - var signKey = Crypto.Nacl.util.decodeBase64(secret.keys.signKey); + console.log(edPublic); + console.log(secret.channel); Store.anonRpcMsg({ msg: 'GET_METADATA', data: secret.channel }, function (data) { - console.log(data[0]); var metadata = data[0]; // Owned drive if (metadata && metadata.owners && metadata.owners.length === 1 && metadata.owners.indexOf(edPublic) !== -1) { - + nThen(function (waitFor) { + var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER); + store.proxy[Constants.tokenKey] = token; + postMessage("DELETE_ACCOUNT", token, waitFor()); + }).nThen(function (waitFor) { + removeOwnedPads(waitFor); + }).nThen(function (waitFor) { + // Delete Pin Store + store.rpc.removePins(waitFor(function (err) { + if (err) { console.error(err); } + })); + }).nThen(function (waitFor) { + // Delete Drive + Store.removeOwnedChannel(secret.channel, waitFor()); + }).nThen(function () { + store.network.disconnect(); + cb({ + state: true + }); + }); return; } // Not owned drive + var toSign = { + intent: 'Please delete my account.' + }; + toSign.drive = secret.channel; + toSign.edPublic = edPublic; + var signKey = Crypto.Nacl.util.decodeBase64(secret.keys.signKey); var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey); var proofTxt = Crypto.Nacl.util.encodeBase64(proof); cb({ @@ -529,8 +581,12 @@ define([ // Reset the drive part of the userObject (from settings) Store.resetDrive = function (data, cb) { - store.proxy.drive = store.fo.getStructure(); - onSync(cb); + nThen(function (waitFor) { + removeOwnedPads(waitFor); + }).nThen(function (waitFor) { + store.proxy.drive = store.fo.getStructure(); + onSync(cb); + }); }; /** diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index 09cc68549..7e93d34e7 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -92,7 +92,7 @@ define([ }); }; var logoutHandlers = []; - LocalStore.logout = function (cb) { + LocalStore.logout = function (cb, isDeletion) { [ Constants.userNameKey, Constants.userHashKey, @@ -112,9 +112,11 @@ define([ } eraseTempSessionValues(); - logoutHandlers.forEach(function (h) { - if (typeof (h) === "function") { h(); } - }); + if (!isDeletion) { + logoutHandlers.forEach(function (h) { + if (typeof (h) === "function") { h(); } + }); + } if (typeof(AppConfig.customizeLogout) === 'function') { return void AppConfig.customizeLogout(cb); diff --git a/www/common/pinpad.js b/www/common/pinpad.js index f38d7fc57..b200f5493 100644 --- a/www/common/pinpad.js +++ b/www/common/pinpad.js @@ -165,6 +165,17 @@ define([ }); }; + exp.removePins = function (cb) { + rpc.send('REMOVE_PINS', undefined, function (e, response) { + if (e) { return void cb(e); } + if (response && response.length && response[0] === "OK") { + cb(); + } else { + cb('INVALID_RESPONSE'); + } + }); + }; + exp.uploadComplete = function (cb) { rpc.send('UPLOAD_COMPLETE', null, function (e, res) { if (e) { return void cb(e); } diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index cbf31bb6e..b3fabee61 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -17,7 +17,7 @@ define([ '/bower_components/file-saver/FileSaver.min.js', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', - 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', + 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/customize/src/less2/main.less', ], function ( $, diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 5b8f883c5..d2b54faf2 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -41,7 +41,7 @@ define([ var patchTransformer = config.patchTransformer; var validateContent = config.validateContent; var avgSyncMilliseconds = config.avgSyncMilliseconds; - var logLevel = typeof(config.logLevel) !== 'undefined'? config.logLevel : 2; + var logLevel = typeof(config.logLevel) !== 'undefined'? config.logLevel : 1; var readOnly = config.readOnly || false; var sframeChan = config.sframeChan; var metadataMgr = config.metadataMgr; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index df933f1a5..0ba403750 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -622,6 +622,7 @@ define([ window.location.hash = hash; }; + console.log(secret.channel); var cpNfCfg = { sframeChan: sframeChan, channel: secret.channel, diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 7433fa2f0..0d513658b 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -397,19 +397,6 @@ define([ UI.addTooltips(); - ctx.sframeChan.on('EV_LOGOUT', function () { - $(window).on('keyup', function (e) { - if (e.keyCode === 27) { - UI.removeLoadingScreen(); - } - }); - UI.addLoadingScreen({hideTips: true}); - UI.errorLoadingScreen(Messages.onLogout, true); - logoutHandlers.forEach(function (h) { - if (typeof (h) === "function") { h(); } - }); - }); - ctx.sframeChan.on('Q_INCOMING_FRIEND_REQUEST', function (confirmMsg, cb) { UI.confirm(confirmMsg, cb, null, true); }); @@ -425,6 +412,23 @@ define([ var feedback = ctx.metadataMgr.getPrivateData().feedbackAllowed; Feedback.init(feedback); } catch (e) { Feedback.init(false); } + + ctx.sframeChan.on('EV_LOGOUT', function () { + $(window).on('keyup', function (e) { + if (e.keyCode === 27) { + UI.removeLoadingScreen(); + } + }); + UI.addLoadingScreen({hideTips: true}); + var origin = ctx.metadataMgr.getPrivateData().origin; + var href = origin + "/login/"; + var onLogoutMsg = Messages._getKey('onLogout', ['', '']); + UI.errorLoadingScreen(onLogoutMsg, true); + logoutHandlers.forEach(function (h) { + if (typeof (h) === "function") { h(); } + }); + }); + ctx.sframeChan.ready(); cb(funcs); }); diff --git a/www/settings/inner.js b/www/settings/inner.js index 2aa785981..c9e1cf72c 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -326,7 +326,7 @@ define([ $('', {'class': 'cp-sidebarlayout-description'}) .append(Messages.settings_deleteHint).appendTo($div); - //var $ok = $('', {'class': 'fa fa-check', title: Messages.saved}); + var $ok = $('', {'class': 'fa fa-check', title: Messages.saved}); var $spinner = $('', {'class': 'fa fa-spinner fa-pulse'}); var $button = $('' + // Remove any existing elements + $(".kanban-container-outer").remove(); + + var kanban = new jKanban({ + element: '#cp-app-kanban-content', + gutter: '15px', + widthBoard: '300px', + onChange: function () { + console.log("Board object has changed"); + framework.localChange(); + }, + click: function (el) { + if (kanban.inEditMode) { + console.log("An edit is already active"); + return; + } + kanban.inEditMode = true; + var name = $(el).text(); + $(el).html(''); + $('') + .attr({ + 'type': 'text', + 'name': 'text', + 'id': 'kanban_edit', + 'size': '30', + 'value': name + }) + .appendTo(el); + $('#kanban_edit').focus(); + $('#kanban_edit').blur(function () { + var name = $('#kanban_edit').val(); + $(el).text(name); + var board = $(el.parentNode.parentNode).attr("data-id"); + var pos = kanban.findElementPosition(el); + console.log(pos); + console.log(board); + kanban.getBoardJSON(board).item[pos].title = name; + kanban.onChange(); + kanban.inEditMode = false; + }); -Kanban.addForm(boardId, formItem); -formItem.addEventListener("submit", function (e) { - e.preventDefault(); - var text = e.target[0].value - Kanban.addElement(boardId, { - "title": text, - }) - formItem.parentNode.removeChild(formItem); - }); -document.getElementById('CancelBtn').onclick = function () { - formItem.parentNode.removeChild(formItem) -} -}, -addItemButton: true, - boards: boards -}); + }, + boardTitleClick: function (el) { + if (kanban.inEditMode) { + console.log("An edit is already active"); + return; + } + kanban.inEditMode = true; + var name = $(el).text(); + $(el).html(''); + $('') + .attr({ + 'type': 'text', + 'name': 'text', + 'id': 'kanban_edit', + 'size': '30', + 'value': name + }) + .appendTo(el); + $('#kanban_edit').focus(); + $('#kanban_edit').blur(function () { + var name = $('#kanban_edit').val(); + $(el).text(name); + var board = $(el.parentNode.parentNode).attr("data-id"); + kanban.getBoardJSON(board).title = name; + kanban.onChange(); + kanban.inEditMode = false; + }); -var addBoardDefault = document.getElementById('kanban-addboard'); -addBoardDefault.addEventListener('click', function () { - var counter = 1; - found = false; - while (found) { - for (var board in Kanban.options.boards) { - if (board.id == "board" + counter) { - counter++; - break; - } - } - found = true; - } + }, + colorClick: function (el, boardId) { + console.log("in color click"); + var board = $(el.parentNode).attr("data-id"); + var boardJSON = kanban.getBoardJSON(board); + var currentColor = boardJSON.color; + console.log("Current color " + currentColor); + var index = kanban.options.colors.findIndex(function (element) { + return (element == currentColor) + }) + 1; + console.log("Next index " + index); + if (index >= kanban.options.colors.length) + index = 0; + var nextColor = kanban.options.colors[index]; + console.log("Next color " + nextColor); + boardJSON.color = nextColor; + $(el).removeClass("kanban-header-" + currentColor); + $(el).addClass("kanban-header-" + nextColor); + kanban.onChange(); + + }, + removeClick: function (el, boardId) { + if (confirm("Do you want to delete this board?")) { + console.log("Delete board"); + var boardName = $(el.parentNode.parentNode).attr("data-id"); + for (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) { + console.log(el); + console.log(boardId); + // create a form to enter element + var formItem = document.createElement('form'); + formItem.setAttribute("class", "itemform"); + formItem.innerHTML = '
' + + kanban.addForm(boardId, formItem); + formItem.addEventListener("submit", function (e) { + e.preventDefault(); + var text = e.target[0].value + kanban.addElement(boardId, { + "title": text, + }) + formItem.parentNode.removeChild(formItem); + }); + document.getElementById('CancelBtn').onclick = function () { + formItem.parentNode.removeChild(formItem) + } + }, + addItemButton: true, + boards: boards + }); - Kanban.addBoards( - [{ - "id" : "board" + counter, - "title": "New Board", - "color": "yellow", - "item": [ - { - "title": "Item 1", - } - ] - }] - ) - Kanban.onChange(); - }); - -/* -var toDoButton = document.getElementById('addToDo'); -toDoButton.addEventListener('click', function () { - Kanban.addElement( - "_todo", - { - "title": "Test Add", - } - ); - }); + var addBoardDefault = document.getElementById('kanban-addboard'); + addBoardDefault.addEventListener('click', function () { + var counter = 1; + found = false; + while (found) { + for (var board in kanban.options.boards) { + if (board.id == "board" + counter) { + counter++; + break; + } + } + found = true; + } -var addBoardDefault = document.getElementById('addDefault'); -addBoardDefault.addEventListener('click', function () { - Kanban.addBoards( + kanban.addBoards( [{ - "id": "_default", - "title": "Kanban Default", - "item": [ - { - "title": "Default Item", - }, - { - "title": "Default Item 2", - }, - { - "title": "Default Item 3", + "id": "board" + counter, + "title": "New Board", + "color": "yellow", + "item": [ + { + "title": "Item 1", } ] }] - ) - }); - -var removeBoard = document.getElementById('removeBoard'); -removeBoard.addEventListener('click', function () { - Kanban.removeBoard('_done'); - }); + ) + kanban.onChange(); + }); -var removeElement = document.getElementById('removeElement'); -removeElement.addEventListener('click', function () { - Kanban.removeElement('_test_delete'); - }); + return kanban; + }; -var allEle = Kanban.getBoardElements('_todo'); -allEle.forEach(function (item, index) { - //console.log(item); - }); -*/ -}; + // Start of the main loop + var andThen2 = function (framework) { - cpNfInner = common.startRealtime(config); - metadataMgr = cpNfInner.metadataMgr; + var kanban = initKanban(framework); - cpNfInner.onInfiniteSpinner(function () { - if (APP.unrecoverable) { return; } - setEditable(false); - UI.confirm(Messages.realtime_unrecoverableError, function (yes) { - if (!yes) { return; } - common.gotoURL(); - }); + framework.onContentUpdate(function (newContent) { + // Need to update the content + console.log("Content should be updated to " + newContent); + var currentContent = kanban.getBoardsJSON(); + var remoteContent = newContent.content; + + if (currentContent !== remoteContent) { + // reinit kanban (TODO: optimize to diff only) + console.log("Content is different.. Applying content"); + kanban.setBoards(remoteContent); + } }); - $('#save').on('click', function () { + framework.setContentGetter(function () { + // var content = $("#cp-app-kanban-content").val(); + var content = kanban.getBoardsJSON(); + console.log("Content current value is " + content); + return { + content: content + }; }); + framework.onReady(function (newPad) { + $("#cp-app-kanban-content").focus(); + }); - common.onLogout(function () { setEditable(false); }); + framework.start(); }; - - var main = function () { - var common; - + // var framework; nThen(function (waitFor) { - $(waitFor(function () { - UI.addLoadingScreen(); - var $div = $('
').append(Pages['/kanban/']()); - $('body').append($div.html()); - $('body').addClass("cp-app-kanban"); + + // Framework initialization + Framework.create({ + toolbarContainer: '#cme_toolbox', + contentContainer: '#cp-app-kanban-editor', + }, waitFor(function (fw) { + framework = fw; + andThen2(framework); })); - SFCommon.create(waitFor(function (c) { APP.common = common = c; })); - }).nThen(function (waitFor) { - common.getSframeChannel().onReady(waitFor()); - }).nThen(function (waitFor) { - common.handleNewFile(waitFor); - }).nThen(function (/*waitFor*/) { - andThen(common); }); }; main(); diff --git a/www/kanban/jkanban.css b/www/kanban/jkanban.css index 13e89c27d..86cda09e7 100644 --- a/www/kanban/jkanban.css +++ b/www/kanban/jkanban.css @@ -1,4 +1,3 @@ - .kanban-container-outer { position: relative; box-sizing: border-box; @@ -30,7 +29,7 @@ vertical-align: top; } -.kanban-board.disabled-board{ +.kanban-board.disabled-board { opacity: .3; } @@ -92,37 +91,52 @@ height: auto !important; } -.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; } +.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; +} #kanban-addboard { - float: left; - margin: 30px; - margin-right: 10px; - padding: 5px; - padding-top: 3; - padding-bottom: 3px; - border: 1px solid; - width: 30px; - text-align: center; - background: #d4d4e8; - font-weight: bold; + float: left; + margin: 30px; + margin-right: 10px; + padding: 5px; + padding-top: 3; + padding-bottom: 3px; + border: 1px solid; + width: 30px; + text-align: center; + background: #d4d4e8; + font-weight: bold; } .kanban-removeboard { - float: right; - margin: 10px; - padding: 3px; - width: 30px; - text-align: center; - background: #eee; - font-weight: bold; + float: right; + margin: 10px; + padding: 3px; + width: 30px; + text-align: center; + background: #eee; + font-weight: bold; } /* Dragula CSS */ + .gu-mirror { position: fixed !important; margin: 0 !important; @@ -148,4 +162,4 @@ .form-group { text-align: right; margin-button: 5px; -} +} \ No newline at end of file diff --git a/www/kanban/jkanban.js b/www/kanban/jkanban.js index 60ca1dcdd..a2c5f3467 100644 --- a/www/kanban/jkanban.js +++ b/www/kanban/jkanban.js @@ -1,1398 +1,1552 @@ -(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 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 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'); + } + }) + } - //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"); + if (confirm("Do you want to remove this item?")) { + var board1 = self.options.boards.find(function (element) { + return element.id == $(source.parentNode).attr("data-id"); + }); + var pos1 = self.dragItemPos; + board1.item.splice(pos1, 1); + $(el).remove(); + self.onChange(); } }) - } + .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); + } - 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"); - if (confirm("Do you want to remove this item?")) { - var board1 = self.options.boards.find(function(element) { return element.id==$(source.parentNode).attr("data-id"); }); - var pos1 = self.dragItemPos; - board1.item.splice(pos1, 1); - $(el).remove(); - self.onChange(); - } - }) - .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 item = board1.item[pos1]; + // if (board1==board2 && pos2' + headerBoard.appendChild(btn); + __onButtonClickHandler(btn, board.id); } - 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); + //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); } - - 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); + return wrapper; } - // send event that board has changed - self.onChange(); + 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; + } + } - return self; - } - - this.setBoards = function (boards) { - for (var boardkey in boards) { - // single board - var board = boards[boardkey]; - this.removeBoard(board.id); + 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; + } + } } - 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); - } + }).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)); + } + } - this.getBoardElements = function (id) { - var board = self.element.querySelector('[data-id="' + id + '"] .kanban-drag'); - return (board.childNodes); - } + module.exports = eventmap; - 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(); + }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +}, {}], + 7: [function (require, module, exports) { + (function (global) { - return self; - }; + var NativeCustomEvent = global.CustomEvent; - 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(); + function useNative() { + try { + var p = new NativeCustomEvent('cat', { + detail: { + foo: 'bar' + } + }); + return 'cat' === p.type && 'bar' === p.detail.foo; + } catch (e) {} + return false; } - - return self; - } - // board button on click function - this.onButtonClick = function (el) { + /** + * 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; + } - } - - this.onChange = function() { - self.options.onChange(); + }).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; } - 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]; - } + function addClass(el, className) { + var current = el.className; + if (!current.length) { + el.className = className; + } else if (!lookupClass(className).test(current)) { + el.className += ' ' + className; } - 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 rmClass(el, className) { + el.className = el.className.replace(lookupClass(className), ' ').trim(); + } + + module.exports = { + add: addClass, + rm: rmClass }; - function __onclickHandler(nodeItem, clickfn) { - nodeItem.addEventListener('click', function (e) { - e.preventDefault; - self.options.click(this); - if (typeof(this.clickfn) === 'function') - this.clickfn(this); - }); - } +}, {}], + 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; + } - function __onboardTitleClickHandler(nodeItem, clickfn) { - nodeItem.addEventListener('click', function (e) { - e.preventDefault; - self.options.boardTitleClick(this); - if (typeof(this.clickfn) === 'function') - this.clickfn(this); - }); - } + var drake = emitter({ + containers: o.containers, + start: manualStart, + end: end, + cancel: cancel, + remove: remove, + destroy: destroy, + canMove: canMove, + dragging: false + }); - function __onColorClickHandler(nodeItem, clickfn) { - nodeItem.addEventListener('click', function (e) { - e.preventDefault; - self.options.colorClick(this); - if (typeof(this.clickfn) === 'function') - this.clickfn(this); - }); - } + if (o.removeOnSpill === true) { + drake.on('over', spillOver).on('out', spillOut); + } - function __onRemoveClickHandler(nodeItem, clickfn) { - nodeItem.addEventListener('click', function (e) { - e.preventDefault; - self.options.removeClick(this); - if (typeof(this.clickfn) === 'function') - this.clickfn(this); - }); - } + events(); - function __onButtonClickHandler(nodeItem, boardId) { - nodeItem.addEventListener('click', function (e) { - e.preventDefault; - self.options.buttonClick(this, boardId); - // if(typeof(this.clickfn) === 'function') - // this.clickfn(this); - }); - } + return drake; - function __findBoardJSON(id) { - var el = [] - self.options.boards.map(function (board) { - if (board.id === id) { - return el.push(board) + function isContainer(el) { + return drake.containers.indexOf(el) !== -1 || o.isContainer(el); } - }) - return el[0] - } + function events(remove) { + var op = remove ? 'remove' : 'add'; + touchy(documentElement, op, 'mousedown', grab); + touchy(documentElement, op, 'mouseup', release); + } - //init plugin - this.init(); - }; -}()); + 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); + } -},{"dragula":9}],2:[function(require,module,exports){ -module.exports = function atoa (a, n) { return Array.prototype.slice.call(a, n); } + function destroy() { + events(true); + release({}); + } -},{}],3:[function(require,module,exports){ -'use strict'; + function preventGrabbed(e) { + if (_grabbed) { + e.preventDefault(); + } + } -var ticky = require('ticky'); + 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 + } + } + } -module.exports = function debounce (fn, args, ctx) { - if (!fn) { return; } - ticky(function run () { - fn.apply(ctx || null, args || []); - }); -}; + 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; + } + } -},{"ticky":10}],4:[function(require,module,exports){ -'use strict'; + var grabbed = _grabbed; // call to end() unsets _grabbed + eventualMovements(true); + movements(); + end(); + start(grabbed); -var atoa = require('atoa'); -var debounce = require('./debounce'); + var offset = getOffset(_item); + _offsetX = getCoord('pageX', e) - offset.left; + _offsetY = getCoord('pageY', e) - offset.top; -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(); - } - } + classes.add(_copy || _item, 'gu-transit'); + renderMirrorImage(); + drag(e); + } - function grab (e) { - _moveX = e.clientX; - _moveY = e.clientY; + 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 + }; + } - 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 canMove(item) { + return !!canStart(item); + } - 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; - } - } + function manualStart(item) { + var context = canStart(item); + if (context) { + start(context); + } + } - var grabbed = _grabbed; // call to end() unsets _grabbed - eventualMovements(true); - movements(); - end(); - start(grabbed); + function start(context) { + if (isCopy(context.item, context.source)) { + _copy = context.item.cloneNode(true); + drake.emit('cloned', _copy, context.item, 'copy'); + } - var offset = getOffset(_item); - _offsetX = getCoord('pageX', e) - offset.left; - _offsetY = getCoord('pageY', e) - offset.top; + _source = context.source; + _item = context.item; + _initialSibling = _currentSibling = nextEl(context.item); - classes.add(_copy || _item, 'gu-transit'); - renderMirrorImage(); - drag(e); - } + drake.dragging = true; + drake.emit('drag', _item, _source); + } - 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; - } + function invalidTarget() { + return false; + } - var movable = o.moves(item, source, handle, nextEl(item)); - if (!movable) { - return; - } + function end() { + if (!drake.dragging) { + return; + } + var item = _copy || _item; + drop(item, getParent(item)); + } - return { - item: item, - source: source - }; - } + function ungrab() { + _grabbed = false; + eventualMovements(true); + movements(true); + } - function canMove (item) { - return !!canStart(item); - } + 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 manualStart (item) { - var context = canStart(item); - if (context) { - start(context); - } - } + 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 start (context) { - if (isCopy(context.item, context.source)) { - _copy = context.item.cloneNode(true); - drake.emit('cloned', _copy, context.item, 'copy'); - } + 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(); + } - _source = context.source; - _item = context.item; - _initialSibling = _currentSibling = nextEl(context.item); + 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(); + } - drake.dragging = true; - drake.emit('drag', _item, _source); - } + 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 invalidTarget () { - return false; - } + 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 end () { - if (!drake.dragging) { - return; - } - var item = _copy || _item; - drop(item, getParent(item)); - } + 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; + } - function ungrab () { - _grabbed = false; - eventualMovements(true); - movements(true); - } + 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 release (e) { - ungrab(); + 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'); + } + } - 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 out() { + if (_lastDropTarget) { + moved('out'); + } + } + } - 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 spillOver(el) { + classes.rm(el, 'gu-hide'); + } - 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 spillOut(el) { + if (drake.dragging) { + classes.add(el, 'gu-hide'); + } + } - 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 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 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 removeMirrorImage() { + if (_mirror) { + classes.rm(o.mirrorContainer, 'gu-unselectable'); + touchy(documentElement, 'remove', 'mousemove', drag); + getParent(_mirror).removeChild(_mirror); + _mirror = null; + } + } - 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 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 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 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]); + 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]); \ No newline at end of file diff --git a/www/kanban/kanban.css b/www/kanban/kanban.css deleted file mode 100644 index ae001b198..000000000 --- a/www/kanban/kanban.css +++ /dev/null @@ -1,25 +0,0 @@ - html, body { - margin: 0px; - padding: 0px; - } - #sbox-iframe { - position:fixed; - top:0px; - left:0px; - bottom:0px; - right:0px; - width:100%; - height:100%; - border:none; - margin:0; - padding:0; - overflow:hidden; - } - #sbox-filePicker-iframe { - position: fixed; - top:0; left:0; - bottom:0; right:0; - width:100%; - height: 100%; - border: 0; - } diff --git a/www/kanban/main.js b/www/kanban/main.js deleted file mode 100644 index 7f2197b24..000000000 --- a/www/kanban/main.js +++ /dev/null @@ -1,43 +0,0 @@ -// Load #1, load as little as possible because we are in a race to get the loading screen up. -define([ - '/bower_components/nthen/index.js', - '/api/config', - '/common/dom-ready.js', - '/common/requireconfig.js', - '/common/sframe-common-outer.js', -], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { - var requireConfig = RequireConfig(); - - // Loaded in load #2 - nThen(function (waitFor) { - DomReady.onReady(waitFor()); - }).nThen(function (waitFor) { - var req = { - cfg: requireConfig, - req: [ '/common/loading.js' ], - pfx: window.location.origin - }; - window.rc = requireConfig; - window.apiconf = ApiConfig; - document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + '/kanban/inner.html?' + requireConfig.urlArgs + - '#' + encodeURIComponent(JSON.stringify(req))); - - // This is a cheap trick to avoid loading sframe-channel in parallel with the - // loading screen setup. - var done = waitFor(); - var onMsg = function (msg) { - var data = JSON.parse(msg.data); - if (data.q !== 'READY') { return; } - window.removeEventListener('message', onMsg); - var _done = done; - done = function () { }; - _done(); - }; - window.addEventListener('message', onMsg); - }).nThen(function (/*waitFor*/) { - SFCommonO.start({ - useCreationScreen: true - }); - }); -}); From ee8651c74e33ebfe66364de37f54c8720e0c1b6c Mon Sep 17 00:00:00 2001 From: Ludovic Dubost Date: Mon, 2 Apr 2018 20:33:35 +0200 Subject: [PATCH 017/415] Kanban styles and color updating --- customize.dist/src/less2/include/colortheme.less | 4 ++-- customize.dist/src/less2/main.less | 3 +-- www/kanban/app-kanban.less | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/customize.dist/src/less2/include/colortheme.less b/customize.dist/src/less2/include/colortheme.less index e2406653d..fdf4ea972 100644 --- a/customize.dist/src/less2/include/colortheme.less +++ b/customize.dist/src/less2/include/colortheme.less @@ -61,12 +61,12 @@ @colortheme_poll-th-fg: #fff; @colortheme_poll-warn: #ffade3; -@colortheme_kanban-bg: #006304; +@colortheme_kanban-bg: #8C4; @colortheme_kanban-color: #fff; @colortheme_kanban-help-bg: #bbffbb; @colortheme_kanban-th-bg: #005bef; @colortheme_kanban-th-fg: #fff; -@colortheme_kanban-warn: #ffade3; +@colortheme_kanban-warn: #e6385d; @colortheme_whiteboard-bg: #800080; @colortheme_whiteboard-color: #fff; diff --git a/customize.dist/src/less2/main.less b/customize.dist/src/less2/main.less index f3c8478ce..1186465da 100644 --- a/customize.dist/src/less2/main.less +++ b/customize.dist/src/less2/main.less @@ -34,12 +34,11 @@ body.cp-app-file { @import "../../../file/app-file.less"; } body.cp-app-filepicker { @import "../../../filepicker/app-filepicker.less"; } body.cp-app-contacts { @import "../../../contacts/app-contacts.less"; } body.cp-app-poll { @import "../../../poll/app-poll.less"; } -body.cp-app-kanban { @import "../../../kanban/app-kanban.less"; } body.cp-app-whiteboard { @import "../../../whiteboard/app-whiteboard.less"; } body.cp-app-todo { @import "../../../todo/app-todo.less"; } 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-miniapp { @import "../../../miniapp/app-miniapp.less"; } +body.cp-app-kanban { @import "../../../kanban/app-kanban.less"; } diff --git a/www/kanban/app-kanban.less b/www/kanban/app-kanban.less index c2108473d..db58861b6 100644 --- a/www/kanban/app-kanban.less +++ b/www/kanban/app-kanban.less @@ -1,9 +1,9 @@ @import (once) "../../customize/src/less2/include/browser.less"; @import (once) "../../customize/src/less2/include/framework.less"; -.framework_main( @bg-color: @colortheme_code-bg, -@warn-color: @colortheme_code-warn, -@color: @colortheme_code-color); +.framework_main( @bg-color: @colortheme_kanban-bg, +@warn-color: @colortheme_kanban-warn, +@color: @colortheme_kanban-color); // body &.cp-app-kanban { From 0993204d1487a8ceb612ebaff5cdd69d6f70de5b Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Apr 2018 13:35:06 +0200 Subject: [PATCH 018/415] Disable thumbnails by default --- www/common/outer/async-store.js | 3 ++- www/settings/inner.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 97d7a6d00..9a30abb33 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -380,6 +380,7 @@ define([ // Get the metadata for sframe-common-outer Store.getMetadata = function (data, cb) { + var disableThumbnails = Util.find(store.proxy, ['settings', 'general', 'disableThumbnails']); var metadata = { // "user" is shared with everybody via the userlist user: { @@ -394,7 +395,7 @@ define([ edPublic: store.proxy.edPublic, friends: store.proxy.friends || {}, settings: store.proxy.settings, - thumbnails: !Util.find(store.proxy, ['settings', 'general', 'disableThumbnails']) + thumbnails: disableThumbnails === false } }; cb(JSON.parse(JSON.stringify(metadata))); diff --git a/www/settings/inner.js b/www/settings/inner.js index 344929988..21763714d 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -259,7 +259,7 @@ define([ $spinner.hide().appendTo($div); common.getAttribute(['general', 'disableThumbnails'], function (e, val) { - $checkbox[0].checked = val; + $checkbox[0].checked = typeof(val) === "undefined" || val; }); // Reset From 31980bef82adb63b304f741d2e552fa2d38b37b5 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Apr 2018 15:16:21 +0200 Subject: [PATCH 019/415] Add a ghost icon in the anonymous drive --- www/drive/app-drive.less | 4 ++++ www/drive/inner.js | 1 + 2 files changed, 5 insertions(+) diff --git a/www/drive/app-drive.less b/www/drive/app-drive.less index 188fcf0bf..29870f560 100644 --- a/www/drive/app-drive.less +++ b/www/drive/app-drive.less @@ -547,6 +547,10 @@ span { cursor: pointer; opacity: 0.5; padding: 0; + flex-flow: column; + align-items: center; + justify-content: center; + display: inline-flex; &:hover { opacity: 0.7; } diff --git a/www/drive/inner.js b/www/drive/inner.js index a5e0b6815..ae0328491 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -2052,6 +2052,7 @@ define([ $element.data('context', 'default'); $container.append($element); }); + createGhostIcon($container); }; var displayTrashRoot = function ($list, $folderHeader, $fileHeader) { From 337138d6e557e7d39e280cb7df43ba4cf039c498 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 4 Apr 2018 12:06:59 +0200 Subject: [PATCH 020/415] Script to check pads to delete when account deletion request --- check-account-deletion.js | 76 +++++++++++++++++++++++++++++++++++++++ pinned.js | 10 ++++-- pinneddata.js | 1 + 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 check-account-deletion.js diff --git a/check-account-deletion.js b/check-account-deletion.js new file mode 100644 index 000000000..39bbf02f5 --- /dev/null +++ b/check-account-deletion.js @@ -0,0 +1,76 @@ +/* jshint esversion: 6, node: true */ +const Fs = require('fs'); +const nThen = require('nthen'); +const Pinned = require('./pinned'); +const Nacl = require('tweetnacl'); + +const hashesFromPinFile = (pinFile, fileName) => { + var pins = {}; + pinFile.split('\n').filter((x)=>(x)).map((l) => JSON.parse(l)).forEach((l) => { + switch (l[0]) { + case 'RESET': { + pins = {}; + if (l[1] && l[1].length) { l[1].forEach((x) => { pins[x] = 1; }); } + //jshint -W086 + // fallthrough + } + case 'PIN': { + l[1].forEach((x) => { pins[x] = 1; }); + break; + } + case 'UNPIN': { + l[1].forEach((x) => { delete pins[x]; }); + break; + } + default: throw new Error(JSON.stringify(l) + ' ' + fileName); + } + }); + return Object.keys(pins); +}; + +var escapeKeyCharacters = function (key) { + return key && key.replace && key.replace(/\//g, '-'); +}; + + +const dataIdx = process.argv.indexOf('--data'); +let edPublic; +if (dataIdx === -1) { + const hasEdPublic = process.argv.indexOf('--ed'); + if (hasEdPublic === -1) { return void console.error("Missing ed argument"); } + edPublic = escapeKeyCharacters(process.argv[hasEdPublic+1]); +} else { + const deleteData = JSON.parse(process.argv[dataIdx+1]); + if (!deleteData.toSign || !deleteData.proof) { return void console.error("Invalid arguments"); } + // Check sig + const ed = Nacl.util.decodeBase64(deleteData.toSign.edPublic); + const signed = Nacl.util.decodeUTF8(JSON.stringify(deleteData.toSign)); + const proof = Nacl.util.decodeBase64(deleteData.proof); + if (!Nacl.sign.detached.verify(signed, proof, ed)) { return void console.error("Invalid signature"); } + edPublic = escapeKeyCharacters(deleteData.toSign.edPublic); +} + +let data = []; +let pinned = []; + +nThen((waitFor) => { + let f = './pins/' + edPublic.slice(0, 2) + '/' + edPublic + '.ndjson'; + Fs.readFile(f, waitFor((err, content) => { + if (err) { throw err; } + pinned = hashesFromPinFile(content.toString('utf8'), f); + })); +}).nThen((waitFor) => { + Pinned.load(waitFor((d) => { + data = Object.keys(d); + }), { + exclude: [edPublic + '.ndjson'] + }); +}).nThen(() => { + console.log('Pads pinned by this user and not pinned by anybody else:'); + pinned.forEach((p) => { + if (data.indexOf(p) === -1) { + console.log(p); + } + }); +}); + diff --git a/pinned.js b/pinned.js index 41a832241..d5df9373a 100644 --- a/pinned.js +++ b/pinned.js @@ -15,6 +15,7 @@ const hashesFromPinFile = (pinFile, fileName) => { switch (l[0]) { case 'RESET': { pins = {}; + if (l[1] && l[1].length) { l[1].forEach((x) => { pins[x] = 1; }); } //jshint -W086 // fallthrough } @@ -32,7 +33,7 @@ const hashesFromPinFile = (pinFile, fileName) => { return Object.keys(pins); }; -module.exports.load = function (cb) { +module.exports.load = function (cb, config) { nThen((waitFor) => { Fs.readdir('./pins', waitFor((err, list) => { if (err) { @@ -49,7 +50,10 @@ module.exports.load = function (cb) { sema.take((returnAfter) => { Fs.readdir('./pins/' + f, waitFor(returnAfter((err, list2) => { if (err) { throw err; } - list2.forEach((ff) => { fileList.push('./pins/' + f + '/' + ff); }); + list2.forEach((ff) => { + if (config && config.exclude && config.exclude.indexOf(ff) > -1) { return; } + fileList.push('./pins/' + f + '/' + ff); + }); }))); }); }); @@ -76,4 +80,4 @@ if (!module.parent) { console.log(x + ' ' + JSON.stringify(data[x])); }); }); -} \ No newline at end of file +} diff --git a/pinneddata.js b/pinneddata.js index 378f34ad7..f9053b9b6 100644 --- a/pinneddata.js +++ b/pinneddata.js @@ -9,6 +9,7 @@ const hashesFromPinFile = (pinFile, fileName) => { switch (l[0]) { case 'RESET': { pins = {}; + if (l[1] && l[1].length) { l[1].forEach((x) => { pins[x] = 1; }); } //jshint -W086 // fallthrough } From c24c06e90c0d19d1900dab97e84149ad044dfdfc Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 4 Apr 2018 14:17:48 +0200 Subject: [PATCH 021/415] sign with your global private signing key --- www/common/outer/async-store.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 9a30abb33..a7bea1fb9 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -495,8 +495,15 @@ define([ }; toSign.drive = secret.channel; toSign.edPublic = edPublic; - var signKey = Crypto.Nacl.util.decodeBase64(secret.keys.signKey); + var signKey = Crypto.Nacl.util.decodeBase64(store.proxy.edPrivate); var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey); + + var check = Crypto.Nacl.sign.detached.verify(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), + proof, + Crypto.Nacl.util.decodeBase64(edPublic)); + + if (!check) { console.error('signed message failed verification'); } + var proofTxt = Crypto.Nacl.util.encodeBase64(proof); cb({ proof: proofTxt, From 52e634885e6a987fca4a498fe1eb14edc184a6e1 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 4 Apr 2018 14:38:24 +0200 Subject: [PATCH 022/415] Hide the import template button if templates are disabled in appconfig --- www/common/common-ui-elements.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 5383206e5..036d63bc8 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -552,6 +552,8 @@ define([ button.click(function () { $input.click(); }); break; case 'importtemplate': + if (!AppConfig.enableTemplates) { return; } + if (!common.isLoggedIn()) { return; } button = $('