Merge branch 'coati' into kanban
This commit is contained in:
@@ -223,6 +223,33 @@ define([
|
||||
hd.type === 'invite');
|
||||
}, "test support for invite urls");
|
||||
|
||||
// test support for V2
|
||||
assert(function (cb) {
|
||||
var parsed = Hash.parsePadUrl('/pad/#/2/pad/edit/oRE0oLCtEXusRDyin7GyLGcS/');
|
||||
var secret = Hash.getSecrets('pad', '/2/pad/edit/oRE0oLCtEXusRDyin7GyLGcS/');
|
||||
return cb(parsed.hashData.version === 2 &&
|
||||
parsed.hashData.mode === "edit" &&
|
||||
parsed.hashData.type === "pad" &&
|
||||
parsed.hashData.key === "oRE0oLCtEXusRDyin7GyLGcS" &&
|
||||
secret.channel === "d8d51b4aea863f3f050f47f8ad261753" &&
|
||||
window.nacl.util.encodeBase64(secret.keys.cryptKey) === "0Ts1M6VVEozErV2Nx/LTv6Im5SCD7io2LlhasyyBPQo=" &&
|
||||
secret.keys.validateKey === "f5A1FM9Gp55tnOcM75RyHD1oxBG9ZPh9WDA7qe2Fvps=" &&
|
||||
!parsed.hashData.present);
|
||||
}, "test support for version 2 hash failed to parse");
|
||||
assert(function (cb) {
|
||||
var parsed = Hash.parsePadUrl('/pad/#/2/pad/edit/HGu0tK2od-2BBnwAz2ZNS-t4/p/embed');
|
||||
var secret = Hash.getSecrets('pad', '/2/pad/edit/HGu0tK2od-2BBnwAz2ZNS-t4/p/embed', 'pewpew');
|
||||
return cb(parsed.hashData.version === 2 &&
|
||||
parsed.hashData.mode === "edit" &&
|
||||
parsed.hashData.type === "pad" &&
|
||||
parsed.hashData.key === "HGu0tK2od-2BBnwAz2ZNS-t4" &&
|
||||
secret.channel === "3fb6dc93807d903aff390b5f798c92c9" &&
|
||||
window.nacl.util.encodeBase64(secret.keys.cryptKey) === "EeCkGJra8eJgVu7v4Yl2Hc3yUjrgpKpxr0Lcc3bSWVs=" &&
|
||||
secret.keys.validateKey === "WGkBczJf2V6vQZfAScz8V1KY6jKdoxUCckrD+E75gGE=" &&
|
||||
parsed.hashData.embed &&
|
||||
parsed.hashData.password);
|
||||
}, "test support for password in version 2 hash failed to parse");
|
||||
|
||||
assert(function (cb) {
|
||||
var url = '/pad/?utm_campaign=new_comment&utm_medium=email&utm_source=thread_mailer#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/';
|
||||
var secret = Hash.parsePadUrl(url);
|
||||
|
||||
@@ -334,6 +334,7 @@ define([
|
||||
//var cursor = editor.getCursor();
|
||||
//var cleanName = data.name.replace(/[\[\]]/g, '');
|
||||
//var text = '';
|
||||
// PASSWORD_FILES
|
||||
var parsed = Hash.parsePadUrl(data.url);
|
||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||
|
||||
@@ -34,7 +34,7 @@ define([
|
||||
url: '/common/feedback.html?NO_LOCALSTORAGE=' + (+new Date()),
|
||||
});
|
||||
});
|
||||
window.alert("CryptPad needs localStorage to work, try a different browser");
|
||||
window.alert("CryptPad needs localStorage to work. Try changing your cookie permissions, or using a different browser");
|
||||
};
|
||||
|
||||
window.onerror = function (e) {
|
||||
|
||||
@@ -19,7 +19,50 @@ define([
|
||||
.decodeUTF8(JSON.stringify(list))));
|
||||
};
|
||||
|
||||
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) {
|
||||
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (secret) {
|
||||
var version = secret.version;
|
||||
var data = secret.keys;
|
||||
if (version === 0) {
|
||||
return secret.channel + secret.key;
|
||||
}
|
||||
if (version === 1) {
|
||||
if (!data.editKeyStr) { return; }
|
||||
return '/1/edit/' + hexToBase64(secret.channel) +
|
||||
'/' + Crypto.b64RemoveSlashes(data.editKeyStr) + '/';
|
||||
}
|
||||
if (version === 2) {
|
||||
if (!data.editKeyStr) { return; }
|
||||
var pass = secret.password ? 'p/' : '';
|
||||
return '/2/' + secret.type + '/edit/' + Crypto.b64RemoveSlashes(data.editKeyStr) + '/' + pass;
|
||||
}
|
||||
};
|
||||
var getViewHashFromKeys = Hash.getViewHashFromKeys = function (secret) {
|
||||
var version = secret.version;
|
||||
var data = secret.keys;
|
||||
if (version === 0) { return; }
|
||||
if (version === 1) {
|
||||
if (!data.viewKeyStr) { return; }
|
||||
return '/1/view/' + hexToBase64(secret.channel) +
|
||||
'/'+Crypto.b64RemoveSlashes(data.viewKeyStr)+'/';
|
||||
}
|
||||
if (version === 2) {
|
||||
if (!data.viewKeyStr) { return; }
|
||||
var pass = secret.password ? 'p/' : '';
|
||||
return '/2/' + secret.type + '/view/' + Crypto.b64RemoveSlashes(data.viewKeyStr) + '/' + pass;
|
||||
}
|
||||
};
|
||||
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (secret) {
|
||||
var version = secret.version;
|
||||
var data = secret.keys;
|
||||
if (version === 0) { return; }
|
||||
if (version === 1) {
|
||||
return '/1/' + hexToBase64(secret.channel) + '/' +
|
||||
Crypto.b64RemoveSlashes(data.fileKeyStr) + '/';
|
||||
}
|
||||
};
|
||||
|
||||
// V1
|
||||
/*var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) {
|
||||
if (typeof keys === 'string') {
|
||||
return chanKey + keys;
|
||||
}
|
||||
@@ -34,7 +77,7 @@ define([
|
||||
};
|
||||
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (fileKey, cryptKey) {
|
||||
return '/1/' + hexToBase64(fileKey) + '/' + Crypto.b64RemoveSlashes(cryptKey) + '/';
|
||||
};
|
||||
};*/
|
||||
Hash.getUserHrefFromKeys = function (origin, username, pubkey) {
|
||||
return origin + '/user/#/1/' + username + '/' + pubkey.replace(/\//g, '-');
|
||||
};
|
||||
@@ -43,6 +86,24 @@ define([
|
||||
return s.replace(/\/+/g, '/');
|
||||
};
|
||||
|
||||
Hash.createChannelId = function () {
|
||||
var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
|
||||
if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
|
||||
throw new Error('channel ids must consist of 32 hex characters');
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
Hash.createRandomHash = function (type, password) {
|
||||
var cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
|
||||
return getEditHashFromKeys({
|
||||
password: Boolean(password),
|
||||
version: 2,
|
||||
type: type,
|
||||
keys: { editKeyStr: cryptor.editKeyStr }
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Version 0
|
||||
/pad/#67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy
|
||||
@@ -56,25 +117,56 @@ Version 1
|
||||
var hashArr = fixDuplicateSlashes(hash).split('/');
|
||||
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
|
||||
parsed.type = 'pad';
|
||||
if (hash.slice(0,1) !== '/' && hash.length >= 56) {
|
||||
if (hash.slice(0,1) !== '/' && hash.length >= 56) { // Version 0
|
||||
// Old hash
|
||||
parsed.channel = hash.slice(0, 32);
|
||||
parsed.key = hash.slice(32, 56);
|
||||
parsed.version = 0;
|
||||
parsed.getHash = function () { return hash; };
|
||||
return parsed;
|
||||
}
|
||||
if (hashArr[1] && hashArr[1] === '1') {
|
||||
var options;
|
||||
if (hashArr[1] && hashArr[1] === '1') { // Version 1
|
||||
parsed.version = 1;
|
||||
parsed.mode = hashArr[2];
|
||||
parsed.channel = hashArr[3];
|
||||
parsed.key = hashArr[4].replace(/-/g, '/');
|
||||
var options = hashArr.slice(5);
|
||||
parsed.key = Crypto.b64AddSlashes(hashArr[4]);
|
||||
|
||||
options = hashArr.slice(5);
|
||||
parsed.present = options.indexOf('present') !== -1;
|
||||
parsed.embed = options.indexOf('embed') !== -1;
|
||||
|
||||
parsed.getHash = function (opts) {
|
||||
var hash = hashArr.slice(0, 5).join('/') + '/';
|
||||
if (opts.embed) { hash += 'embed/'; }
|
||||
if (opts.present) { hash += 'present/'; }
|
||||
return hash;
|
||||
};
|
||||
return parsed;
|
||||
}
|
||||
if (hashArr[1] && hashArr[1] === '2') { // Version 2
|
||||
parsed.version = 2;
|
||||
parsed.app = hashArr[2];
|
||||
parsed.mode = hashArr[3];
|
||||
parsed.key = hashArr[4];
|
||||
|
||||
options = hashArr.slice(5);
|
||||
parsed.password = options.indexOf('p') !== -1;
|
||||
parsed.present = options.indexOf('present') !== -1;
|
||||
parsed.embed = options.indexOf('embed') !== -1;
|
||||
|
||||
parsed.getHash = function (opts) {
|
||||
var hash = hashArr.slice(0, 5).join('/') + '/';
|
||||
if (parsed.password) { hash += 'p/'; }
|
||||
if (opts.embed) { hash += 'embed/'; }
|
||||
if (opts.present) { hash += 'present/'; }
|
||||
return hash;
|
||||
};
|
||||
return parsed;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
parsed.getHash = function () { return hashArr.join('/'); };
|
||||
if (['media', 'file'].indexOf(type) !== -1) {
|
||||
parsed.type = 'file';
|
||||
if (hashArr[1] && hashArr[1] === '1') {
|
||||
@@ -125,17 +217,9 @@ Version 1
|
||||
url += ret.type + '/';
|
||||
if (!ret.hashData) { return url; }
|
||||
if (ret.hashData.type !== 'pad') { return url + '#' + ret.hash; }
|
||||
if (ret.hashData.version !== 1) { return url + '#' + ret.hash; }
|
||||
url += '#/' + ret.hashData.version +
|
||||
'/' + ret.hashData.mode +
|
||||
'/' + ret.hashData.channel.replace(/\//g, '-') +
|
||||
'/' + ret.hashData.key.replace(/\//g, '-') +'/';
|
||||
if (options.embed) {
|
||||
url += 'embed/';
|
||||
}
|
||||
if (options.present) {
|
||||
url += 'present/';
|
||||
}
|
||||
if (ret.hashData.version === 0) { return url + '#' + ret.hash; }
|
||||
var hash = ret.hashData.getHash(options);
|
||||
url += '#' + hash;
|
||||
return url;
|
||||
};
|
||||
|
||||
@@ -153,12 +237,13 @@ Version 1
|
||||
return '';
|
||||
});
|
||||
idx = href.indexOf('/#');
|
||||
if (idx === -1) { return ret; }
|
||||
ret.hash = href.slice(idx + 2);
|
||||
ret.hashData = parseTypeHash(ret.type, ret.hash);
|
||||
return ret;
|
||||
};
|
||||
|
||||
var getRelativeHref = Hash.getRelativeHref = function (href) {
|
||||
Hash.getRelativeHref = function (href) {
|
||||
if (!href) { return; }
|
||||
if (href.indexOf('#') === -1) { return; }
|
||||
var parsed = parsePadUrl(href);
|
||||
@@ -170,11 +255,13 @@ Version 1
|
||||
* - no argument: use the URL hash or create one if it doesn't exist
|
||||
* - secretHash provided: use secretHash to find the keys
|
||||
*/
|
||||
Hash.getSecrets = function (type, secretHash) {
|
||||
Hash.getSecrets = function (type, secretHash, password) {
|
||||
var secret = {};
|
||||
var generate = function () {
|
||||
secret.keys = Crypto.createEditCryptor();
|
||||
secret.key = Crypto.createEditCryptor().editKeyStr;
|
||||
secret.keys = Crypto.createEditCryptor2(void 0, void 0, password);
|
||||
secret.channel = base64ToHex(secret.keys.chanId);
|
||||
secret.version = 2;
|
||||
secret.type = type;
|
||||
};
|
||||
if (!secretHash && !window.location.hash) { //!/#/.test(window.location.href)) {
|
||||
generate();
|
||||
@@ -191,7 +278,6 @@ Version 1
|
||||
parsed = pHref.hashData;
|
||||
hash = pHref.hash;
|
||||
}
|
||||
//var parsed = parsePadUrl(window.location.href);
|
||||
//var hash = secretHash || window.location.hash.slice(1);
|
||||
if (hash.length === 0) {
|
||||
generate();
|
||||
@@ -203,9 +289,10 @@ Version 1
|
||||
// Old hash
|
||||
secret.channel = parsed.channel;
|
||||
secret.key = parsed.key;
|
||||
}
|
||||
else if (parsed.version === 1) {
|
||||
secret.version = 0;
|
||||
} else if (parsed.version === 1) {
|
||||
// New hash
|
||||
secret.version = 1;
|
||||
if (parsed.type === "pad") {
|
||||
secret.channel = base64ToHex(parsed.channel);
|
||||
if (parsed.mode === 'edit') {
|
||||
@@ -229,49 +316,63 @@ Version 1
|
||||
// version 2 hashes are to be used for encrypted blobs
|
||||
throw new Error("User hashes can't be opened (yet)");
|
||||
}
|
||||
} else if (parsed.version === 2) {
|
||||
// New hash
|
||||
secret.version = 2;
|
||||
secret.type = type;
|
||||
secret.password = password;
|
||||
if (parsed.type === "pad") {
|
||||
if (parsed.mode === 'edit') {
|
||||
secret.keys = Crypto.createEditCryptor2(parsed.key, void 0, password);
|
||||
secret.channel = base64ToHex(secret.keys.chanId);
|
||||
secret.key = secret.keys.editKeyStr;
|
||||
if (secret.channel.length !== 32 || secret.key.length !== 24) {
|
||||
throw new Error("The channel key and/or the encryption key is invalid");
|
||||
}
|
||||
}
|
||||
else if (parsed.mode === 'view') {
|
||||
secret.keys = Crypto.createViewCryptor2(parsed.key, password);
|
||||
secret.channel = base64ToHex(secret.keys.chanId);
|
||||
if (secret.channel.length !== 32) {
|
||||
throw new Error("The channel key is invalid");
|
||||
}
|
||||
}
|
||||
} else if (parsed.type === "file") {
|
||||
throw new Error("File hashes should be version 1");
|
||||
} else if (parsed.type === "user") {
|
||||
throw new Error("User hashes can't be opened (yet)");
|
||||
}
|
||||
}
|
||||
}
|
||||
return secret;
|
||||
};
|
||||
|
||||
Hash.getHashes = function (channel, secret) {
|
||||
Hash.getHashes = function (secret) {
|
||||
var hashes = {};
|
||||
if (!secret.keys) {
|
||||
secret = JSON.parse(JSON.stringify(secret));
|
||||
|
||||
if (!secret.keys && !secret.key) {
|
||||
console.error('e');
|
||||
return hashes;
|
||||
} else if (!secret.keys) {
|
||||
secret.keys = {};
|
||||
}
|
||||
if (secret.keys.editKeyStr) {
|
||||
hashes.editHash = getEditHashFromKeys(channel, secret.keys);
|
||||
|
||||
if (secret.keys.editKeyStr || (secret.version === 0 && secret.key)) {
|
||||
hashes.editHash = getEditHashFromKeys(secret);
|
||||
}
|
||||
if (secret.keys.viewKeyStr) {
|
||||
hashes.viewHash = getViewHashFromKeys(channel, secret.keys);
|
||||
hashes.viewHash = getViewHashFromKeys(secret);
|
||||
}
|
||||
if (secret.keys.fileKeyStr) {
|
||||
hashes.fileHash = getFileHashFromKeys(channel, secret.keys.fileKeyStr);
|
||||
hashes.fileHash = getFileHashFromKeys(secret);
|
||||
}
|
||||
return hashes;
|
||||
};
|
||||
|
||||
var createChannelId = Hash.createChannelId = function () {
|
||||
var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
|
||||
if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
|
||||
throw new Error('channel ids must consist of 32 hex characters');
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
Hash.createRandomHash = function () {
|
||||
// 16 byte channel Id
|
||||
var channelId = Util.hexToBase64(createChannelId());
|
||||
// 18 byte encryption key
|
||||
var key = Crypto.b64RemoveSlashes(Crypto.rand64(18));
|
||||
return '/1/edit/' + [channelId, key].join('/') + '/';
|
||||
};
|
||||
|
||||
// STORAGE
|
||||
Hash.findWeaker = function (href, recents) {
|
||||
var rHref = href || getRelativeHref(window.location.href);
|
||||
var parsed = parsePadUrl(rHref);
|
||||
Hash.findWeaker = function (href, channel, recents) {
|
||||
var parsed = parsePadUrl(href);
|
||||
if (!parsed.hash) { return false; }
|
||||
var weaker;
|
||||
Object.keys(recents).some(function (id) {
|
||||
@@ -279,6 +380,8 @@ Version 1
|
||||
var p = parsePadUrl(pad.href);
|
||||
if (p.type !== parsed.type) { return; } // Not the same type
|
||||
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
|
||||
if (channel !== pad.channel) { return; } // Not the same channel
|
||||
|
||||
var pHash = p.hashData;
|
||||
var parsedHash = parsed.hashData;
|
||||
if (!parsedHash || !pHash) { return; }
|
||||
@@ -287,18 +390,16 @@ Version 1
|
||||
if (pHash.type !== 'pad' && parsedHash.type !== 'pad') { return; }
|
||||
|
||||
if (pHash.version !== parsedHash.version) { return; }
|
||||
if (pHash.channel !== parsedHash.channel) { return; }
|
||||
if (pHash.mode === 'view' && parsedHash.mode === 'edit') {
|
||||
weaker = pad.href;
|
||||
weaker = pad;
|
||||
return true;
|
||||
}
|
||||
return;
|
||||
});
|
||||
return weaker;
|
||||
};
|
||||
var findStronger = Hash.findStronger = function (href, recents) {
|
||||
var rHref = href || getRelativeHref(window.location.href);
|
||||
var parsed = parsePadUrl(rHref);
|
||||
Hash.findStronger = function (href, channel, recents) {
|
||||
var parsed = parsePadUrl(href);
|
||||
if (!parsed.hash) { return false; }
|
||||
// We can't have a stronger hash if we're already in edit mode
|
||||
if (parsed.hashData && parsed.hashData.mode === 'edit') { return; }
|
||||
@@ -308,6 +409,8 @@ Version 1
|
||||
var p = parsePadUrl(pad.href);
|
||||
if (p.type !== parsed.type) { return; } // Not the same type
|
||||
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
|
||||
if (channel !== pad.channel) { return; } // Not the same channel
|
||||
|
||||
var pHash = p.hashData;
|
||||
var parsedHash = parsed.hashData;
|
||||
if (!parsedHash || !pHash) { return; }
|
||||
@@ -316,37 +419,20 @@ Version 1
|
||||
if (pHash.type !== 'pad' && parsedHash.type !== 'pad') { return; }
|
||||
|
||||
if (pHash.version !== parsedHash.version) { return; }
|
||||
if (pHash.channel !== parsedHash.channel) { return; }
|
||||
if (pHash.mode === 'edit' && parsedHash.mode === 'view') {
|
||||
stronger = pad.href;
|
||||
stronger = pad;
|
||||
return true;
|
||||
}
|
||||
return;
|
||||
});
|
||||
return stronger;
|
||||
};
|
||||
Hash.isNotStrongestStored = function (href, recents) {
|
||||
return findStronger(href, recents);
|
||||
};
|
||||
|
||||
Hash.hrefToHexChannelId = function (href) {
|
||||
Hash.hrefToHexChannelId = function (href, password) {
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
if (!parsed || !parsed.hash) { return; }
|
||||
|
||||
parsed = parsed.hashData;
|
||||
if (parsed.version === 0) {
|
||||
return parsed.channel;
|
||||
} else if (parsed.version !== 1 && parsed.version !== 2) {
|
||||
console.error("parsed href had no version");
|
||||
console.error(parsed);
|
||||
return;
|
||||
}
|
||||
|
||||
var channel = parsed.channel;
|
||||
if (!channel) { return; }
|
||||
|
||||
var hex = base64ToHex(channel);
|
||||
return hex;
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, password);
|
||||
return secret.channel;
|
||||
};
|
||||
|
||||
Hash.getBlobPathFromHex = function (id) {
|
||||
|
||||
@@ -6,15 +6,18 @@ define([
|
||||
'/common/common-notifier.js',
|
||||
'/customize/application_config.js',
|
||||
'/bower_components/alertifyjs/dist/js/alertify.js',
|
||||
'/common/tippy.min.js',
|
||||
'/common/tippy/tippy.min.js',
|
||||
'/customize/pages.js',
|
||||
'/common/hyperscript.js',
|
||||
'/customize/loading.js',
|
||||
'/common/test.js',
|
||||
|
||||
'/common/jquery-ui/jquery-ui.min.js',
|
||||
'/bower_components/bootstrap-tokenfield/dist/bootstrap-tokenfield.js',
|
||||
'css!/common/tippy.css',
|
||||
'css!/common/tippy/tippy.css',
|
||||
'css!/common/jquery-ui/jquery-ui.min.css'
|
||||
], function ($, Messages, Util, Hash, Notifier, AppConfig,
|
||||
Alertify, Tippy, Pages, h, Test) {
|
||||
Alertify, Tippy, Pages, h, Loading, Test) {
|
||||
var UI = {};
|
||||
|
||||
/*
|
||||
@@ -182,11 +185,17 @@ define([
|
||||
]);
|
||||
};
|
||||
|
||||
UI.tokenField = function (target) {
|
||||
UI.tokenField = function (target, autocomplete) {
|
||||
var t = {
|
||||
element: target || h('input'),
|
||||
};
|
||||
var $t = t.tokenfield = $(t.element).tokenfield();
|
||||
var $t = t.tokenfield = $(t.element).tokenfield({
|
||||
autocomplete: {
|
||||
source: autocomplete,
|
||||
delay: 100
|
||||
},
|
||||
showAutocompleteOnFocus: false
|
||||
});
|
||||
|
||||
t.getTokens = function (ignorePending) {
|
||||
var tokens = $t.tokenfield('getTokens').map(function (token) {
|
||||
@@ -209,10 +218,17 @@ define([
|
||||
|
||||
t.preventDuplicates = function (cb) {
|
||||
$t.on('tokenfield:createtoken', function (ev) {
|
||||
// Close the suggest list when a token is added because we're going to wipe the input
|
||||
var $input = $t.closest('.tokenfield').find('.token-input');
|
||||
$input.autocomplete('close');
|
||||
|
||||
var val;
|
||||
ev.attrs.value = ev.attrs.value.toLowerCase();
|
||||
if (t.getTokens(true).some(function (t) {
|
||||
if (t === ev.attrs.value) { return ((val = t)); }
|
||||
if (t === ev.attrs.value) {
|
||||
ev.preventDefault();
|
||||
return ((val = t));
|
||||
}
|
||||
})) {
|
||||
ev.preventDefault();
|
||||
if (typeof(cb) === 'function') { cb(val); }
|
||||
@@ -240,7 +256,7 @@ define([
|
||||
return t;
|
||||
};
|
||||
|
||||
dialog.tagPrompt = function (tags, cb) {
|
||||
dialog.tagPrompt = function (tags, existing, cb) {
|
||||
var input = dialog.textInput();
|
||||
|
||||
var tagger = dialog.frame([
|
||||
@@ -254,7 +270,7 @@ define([
|
||||
dialog.nav(),
|
||||
]);
|
||||
|
||||
var field = UI.tokenField(input).preventDuplicates(function (val) {
|
||||
var field = UI.tokenField(input, existing).preventDuplicates(function (val) {
|
||||
UI.warn(Messages._getKey('tags_duplicate', [val]));
|
||||
});
|
||||
|
||||
@@ -395,7 +411,7 @@ define([
|
||||
stopListening(listener);
|
||||
cb();
|
||||
});
|
||||
listener = listenForKeys(close, close, ok);
|
||||
listener = listenForKeys(close, close);
|
||||
var $ok = $(ok).click(close);
|
||||
|
||||
document.body.appendChild(frame);
|
||||
@@ -512,6 +528,50 @@ define([
|
||||
Alertify.error(Util.fixHTML(msg));
|
||||
};
|
||||
|
||||
UI.passwordInput = function (opts, displayEye) {
|
||||
opts = opts || {};
|
||||
var attributes = merge({
|
||||
type: 'password'
|
||||
}, opts);
|
||||
|
||||
var input = h('input.cp-password-input', attributes);
|
||||
var reveal = UI.createCheckbox('cp-password-reveal', Messages.password_show);
|
||||
var eye = h('span.fa.fa-eye.cp-password-reveal');
|
||||
|
||||
$(reveal).find('input').on('change', function () {
|
||||
if($(this).is(':checked')) {
|
||||
$(input).prop('type', 'text');
|
||||
$(input).focus();
|
||||
return;
|
||||
}
|
||||
$(input).prop('type', 'password');
|
||||
$(input).focus();
|
||||
});
|
||||
|
||||
$(eye).mousedown(function () {
|
||||
$(input).prop('type', 'text');
|
||||
$(input).focus();
|
||||
}).mouseup(function(){
|
||||
$(input).prop('type', 'password');
|
||||
$(input).focus();
|
||||
}).mouseout(function(){
|
||||
$(input).prop('type', 'password');
|
||||
$(input).focus();
|
||||
});
|
||||
if (displayEye) {
|
||||
$(reveal).hide();
|
||||
} else {
|
||||
$(eye).hide();
|
||||
}
|
||||
|
||||
return h('span.cp-password-container', [
|
||||
input,
|
||||
reveal,
|
||||
eye
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* spinner
|
||||
*/
|
||||
@@ -539,48 +599,99 @@ define([
|
||||
|
||||
var LOADING = 'cp-loading';
|
||||
|
||||
var getRandomTip = function () {
|
||||
/*var getRandomTip = function () {
|
||||
if (!Messages.tips || !Object.keys(Messages.tips).length) { return ''; }
|
||||
var keys = Object.keys(Messages.tips);
|
||||
var rdm = Math.floor(Math.random() * keys.length);
|
||||
return Messages.tips[keys[rdm]];
|
||||
};*/
|
||||
var loading = {
|
||||
error: false,
|
||||
driveState: 0,
|
||||
padState: 0
|
||||
};
|
||||
UI.addLoadingScreen = function (config) {
|
||||
config = config || {};
|
||||
var loadingText = config.loadingText;
|
||||
var hideTips = config.hideTips || AppConfig.hideLoadingScreenTips;
|
||||
var hideLogo = config.hideLogo;
|
||||
var $loading, $container;
|
||||
if ($('#' + LOADING).length) {
|
||||
$loading = $('#' + LOADING); //.show();
|
||||
var todo = function () {
|
||||
var $loading = $('#' + LOADING); //.show();
|
||||
$loading.css('display', '');
|
||||
$loading.removeClass('cp-loading-hidden');
|
||||
$('.cp-loading-spinner-container').show();
|
||||
if (!config.noProgress && !$loading.find('.cp-loading-progress').length) {
|
||||
var progress = h('div.cp-loading-progress', [
|
||||
h('p.cp-loading-progress-drive'),
|
||||
h('p.cp-loading-progress-pad')
|
||||
]);
|
||||
$loading.find('.cp-loading-container').append(progress);
|
||||
} else if (config.noProgress) {
|
||||
$loading.find('.cp-loading-progress').remove();
|
||||
}
|
||||
if (loadingText) {
|
||||
$('#' + LOADING).find('p').text(loadingText);
|
||||
$('#' + LOADING).find('#cp-loading-message').show().text(loadingText);
|
||||
} else {
|
||||
$('#' + LOADING).find('p').text('');
|
||||
$('#' + LOADING).find('#cp-loading-message').hide().text('');
|
||||
}
|
||||
$container = $loading.find('.cp-loading-container');
|
||||
loading.error = false;
|
||||
};
|
||||
if ($('#' + LOADING).length) {
|
||||
todo();
|
||||
} else {
|
||||
$loading = $(Pages.loadingScreen());
|
||||
$container = $loading.find('.cp-loading-container');
|
||||
if (hideLogo) {
|
||||
$loading.find('img').hide();
|
||||
} else {
|
||||
$loading.find('img').show();
|
||||
}
|
||||
var $spinner = $loading.find('.cp-loading-spinner-container');
|
||||
$spinner.show();
|
||||
$('body').append($loading);
|
||||
Loading();
|
||||
todo();
|
||||
}
|
||||
if (Messages.tips && !hideTips) {
|
||||
var $loadingTip = $('<div>', {'id': 'cp-loading-tip'});
|
||||
$('<span>', {'class': 'tips'}).text(getRandomTip()).appendTo($loadingTip);
|
||||
$loadingTip.css({
|
||||
'bottom': $('body').height()/2 - $container.height()/2 + 20 + 'px'
|
||||
});
|
||||
$('body').append($loadingTip);
|
||||
};
|
||||
UI.updateLoadingProgress = function (data, isDrive) {
|
||||
var $loading = $('#' + LOADING);
|
||||
if (!$loading.length || loading.error) { return; }
|
||||
var $progress;
|
||||
if (isDrive) {
|
||||
console.log(data);
|
||||
// Drive state
|
||||
if (loading.driveState === -1) { return; } // Already loaded
|
||||
$progress = $loading.find('.cp-loading-progress-drive');
|
||||
if (!$progress.length) { return; } // Can't find the box to display data
|
||||
|
||||
// If state is -1, remove the box, drive is loaded
|
||||
if (data.state === -1) {
|
||||
loading.driveState = -1;
|
||||
$progress.remove();
|
||||
} else {
|
||||
if (data.state < loading.driveState) { return; } // We should not display old data
|
||||
// Update the current state
|
||||
loading.driveState = data.state;
|
||||
data.progress = data.progress || 100;
|
||||
data.msg = Messages['loading_drive_'+data.state] || '';
|
||||
$progress.html(data.msg);
|
||||
if (data.progress) {
|
||||
$progress.append(h('div.cp-loading-progress-bar', [
|
||||
h('div.cp-loading-progress-bar-value', {style: 'width:'+data.progress+'%;'})
|
||||
]));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Pad state
|
||||
if (loading.padState === -1) { return; } // Already loaded
|
||||
$progress = $loading.find('.cp-loading-progress-pad');
|
||||
if (!$progress.length) { return; } // Can't find the box to display data
|
||||
|
||||
// If state is -1, remove the box, pad is loaded
|
||||
if (data.state === -1) {
|
||||
loading.padState = -1;
|
||||
$progress.remove();
|
||||
} else {
|
||||
if (data.state < loading.padState) { return; } // We should not display old data
|
||||
// Update the current state
|
||||
loading.padState = data.state;
|
||||
data.progress = data.progress || 100;
|
||||
data.msg = Messages['loading_pad_'+data.state] || '';
|
||||
$progress.html(data.msg);
|
||||
if (data.progress) {
|
||||
$progress.append(h('div.cp-loading-progress-bar', [
|
||||
h('div.cp-loading-progress-bar-value', {style: 'width:'+data.progress+'%;'})
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
UI.removeLoadingScreen = function (cb) {
|
||||
@@ -591,7 +702,7 @@ define([
|
||||
|
||||
$('#' + LOADING).addClass("cp-loading-hidden");
|
||||
setTimeout(cb, 750);
|
||||
//$('#' + LOADING).fadeOut(750, cb);
|
||||
loading.error = false;
|
||||
var $tip = $('#cp-loading-tip').css('top', '')
|
||||
// loading.less sets transition-delay: $wait-time
|
||||
// and transition: opacity $fadeout-time
|
||||
@@ -605,18 +716,27 @@ define([
|
||||
// jquery.fadeout can get stuck
|
||||
};
|
||||
UI.errorLoadingScreen = function (error, transparent, exitable) {
|
||||
if (!$('#' + LOADING).is(':visible') || $('#' + LOADING).hasClass('cp-loading-hidden')) {
|
||||
var $loading = $('#' + LOADING);
|
||||
if (!$loading.is(':visible') || $loading.hasClass('cp-loading-hidden')) {
|
||||
UI.addLoadingScreen({hideTips: true});
|
||||
}
|
||||
loading.error = true;
|
||||
$loading.find('.cp-loading-progress').remove();
|
||||
$('.cp-loading-spinner-container').hide();
|
||||
$('#cp-loading-tip').remove();
|
||||
if (transparent) { $('#' + LOADING).css('opacity', 0.8); }
|
||||
$('#' + LOADING).find('p').html(error || Messages.error);
|
||||
if (transparent) { $loading.css('opacity', 0.9); }
|
||||
var $error = $loading.find('#cp-loading-message').show();
|
||||
if (error instanceof Element) {
|
||||
$error.html('').append(error);
|
||||
} else {
|
||||
$error.html(error || Messages.error);
|
||||
}
|
||||
if (exitable) {
|
||||
$(window).focus();
|
||||
$(window).keydown(function (e) {
|
||||
if (e.which === 27) {
|
||||
$('#' + LOADING).hide();
|
||||
$loading.hide();
|
||||
loading.error = false;
|
||||
if (typeof(exitable) === "function") { exitable(); }
|
||||
}
|
||||
});
|
||||
@@ -660,18 +780,40 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
var delay = typeof(AppConfig.tooltipDelay) === "number" ? AppConfig.tooltipDelay : 500;
|
||||
$.extend(true, Tippy.defaults, {
|
||||
placement: 'bottom',
|
||||
performance: true,
|
||||
delay: [delay, 0],
|
||||
//sticky: true,
|
||||
theme: 'cryptpad',
|
||||
arrow: true,
|
||||
maxWidth: '200px',
|
||||
flip: true,
|
||||
popperOptions: {
|
||||
modifiers: {
|
||||
preventOverflow: { boundariesElement: 'window' }
|
||||
}
|
||||
},
|
||||
//arrowType: 'round',
|
||||
dynamicTitle: true,
|
||||
arrowTransform: 'scale(2)',
|
||||
zIndex: 100000001
|
||||
});
|
||||
UI.addTooltips = function () {
|
||||
var MutationObserver = window.MutationObserver;
|
||||
var delay = typeof(AppConfig.tooltipDelay) === "number" ? AppConfig.tooltipDelay : 500;
|
||||
var addTippy = function (i, el) {
|
||||
if (el._tippy) { return; }
|
||||
if (el.nodeName === 'IFRAME') { return; }
|
||||
Tippy(el, {
|
||||
position: 'bottom',
|
||||
distance: 0,
|
||||
performance: true,
|
||||
delay: [delay, 0],
|
||||
sticky: true
|
||||
var opts = {
|
||||
distance: 15
|
||||
};
|
||||
Array.prototype.slice.apply(el.attributes).filter(function (obj) {
|
||||
return /^data-tippy-/.test(obj.name);
|
||||
}).forEach(function (obj) {
|
||||
opts[obj.name.slice(11)] = obj.value;
|
||||
});
|
||||
Tippy(el, opts);
|
||||
};
|
||||
// This is the robust solution to remove dangling tooltips
|
||||
// The mutation observer does not always find removed nodes.
|
||||
@@ -720,5 +862,9 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
UI.createCheckbox = Pages.createCheckbox;
|
||||
|
||||
UI.createRadio = Pages.createRadio;
|
||||
|
||||
return UI;
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ define([
|
||||
try {
|
||||
var parsed = Hash.parsePadUrl(window.location.href);
|
||||
if (!parsed.hashData) { return; }
|
||||
var chan = parsed.hashData.channel;
|
||||
var chan = Hash.hrefToHexChannelId(window.location.href);
|
||||
// Decrypt
|
||||
var keyStr = parsed.hashData.key;
|
||||
var cryptor = Crypto.createEditCryptor(keyStr);
|
||||
@@ -113,7 +113,7 @@ define([
|
||||
if (!decryptMsg) { return; }
|
||||
// Parse
|
||||
msg = JSON.parse(decryptMsg);
|
||||
if (msg[1] !== parsed.hashData.channel) { return; }
|
||||
if (msg[1] !== chan) { return; }
|
||||
var msgData = msg[2];
|
||||
var msgStr;
|
||||
if (msg[0] === "FRIEND_REQ") {
|
||||
@@ -199,7 +199,7 @@ define([
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
if (!parsed.hashData) { return; }
|
||||
// Message
|
||||
var chan = parsed.hashData.channel;
|
||||
var chan = Hash.hrefToHexChannelId(data.href);
|
||||
var myData = createData(cfg.proxy);
|
||||
var msg = ["FRIEND_REQ", chan, myData];
|
||||
// Encryption
|
||||
|
||||
@@ -205,7 +205,7 @@ define([
|
||||
if (content === oldThumbnailState) { return; }
|
||||
oldThumbnailState = content;
|
||||
Thumb.fromDOM(opts, function (err, b64) {
|
||||
Thumb.setPadThumbnail(common, opts.href, b64);
|
||||
Thumb.setPadThumbnail(common, opts.href, null, b64);
|
||||
});
|
||||
};
|
||||
var nafa = Util.notAgainForAnother(mkThumbnail, Thumb.UPDATE_INTERVAL);
|
||||
@@ -240,20 +240,22 @@ define([
|
||||
Thumb.addThumbnail = function(thumb, $span, cb) {
|
||||
return addThumbnail(null, thumb, $span, cb);
|
||||
};
|
||||
var getKey = function (href) {
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
return 'thumbnail-' + parsed.type + '-' + parsed.hashData.channel;
|
||||
var getKey = function (type, channel) {
|
||||
return 'thumbnail-' + type + '-' + channel;
|
||||
};
|
||||
Thumb.setPadThumbnail = function (common, href, b64, cb) {
|
||||
Thumb.setPadThumbnail = function (common, href, channel, b64, cb) {
|
||||
cb = cb || function () {};
|
||||
var k = getKey(href);
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
channel = channel || common.getMetadataMgr().getPrivateData().channel;
|
||||
var k = getKey(parsed.type, channel);
|
||||
common.setThumbnail(k, b64, cb);
|
||||
};
|
||||
Thumb.displayThumbnail = function (common, href, $container, cb) {
|
||||
Thumb.displayThumbnail = function (common, href, channel, $container, cb) {
|
||||
cb = cb || function () {};
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var k = getKey(href);
|
||||
var k = getKey(parsed.type, channel);
|
||||
var whenNewThumb = function () {
|
||||
// PASSWORD_FILES
|
||||
var secret = Hash.getSecrets('file', parsed.hash);
|
||||
var hexFileName = Util.base64ToHex(secret.channel);
|
||||
var src = Hash.getBlobPathFromHex(hexFileName);
|
||||
@@ -270,7 +272,7 @@ define([
|
||||
if (!v) {
|
||||
v = 'EMPTY';
|
||||
}
|
||||
Thumb.setPadThumbnail(common, href, v, function (err) {
|
||||
Thumb.setPadThumbnail(common, href, hexFileName, v, function (err) {
|
||||
if (!metadata.thumbnail) { return; }
|
||||
addThumbnail(err, metadata.thumbnail, $container, cb);
|
||||
});
|
||||
|
||||
@@ -13,8 +13,6 @@ define([
|
||||
'/customize/messages.js',
|
||||
'/customize/application_config.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
|
||||
'css!/common/tippy.css',
|
||||
], function ($, Config, Util, Hash, Language, UI, Constants, Feedback, h, MediaTag, Clipboard,
|
||||
Messages, AppConfig, NThen) {
|
||||
var UIElements = {};
|
||||
@@ -25,20 +23,27 @@ define([
|
||||
}
|
||||
|
||||
UIElements.updateTags = function (common, href) {
|
||||
var sframeChan = common.getSframeChannel();
|
||||
sframeChan.query('Q_TAGS_GET', href || null, function (err, res) {
|
||||
if (err || res.error) {
|
||||
if (res.error === 'NO_ENTRY') {
|
||||
UI.alert(Messages.tags_noentry);
|
||||
var existing, tags;
|
||||
NThen(function(waitFor) {
|
||||
common.getSframeChannel().query("Q_GET_ALL_TAGS", null, waitFor(function(err, res) {
|
||||
if (err || res.error) { return void console.error(err || res.error); }
|
||||
existing = Object.keys(res.tags).sort();
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
common.getPadAttribute('tags', waitFor(function (err, res) {
|
||||
if (err) {
|
||||
if (err === 'NO_ENTRY') {
|
||||
UI.alert(Messages.tags_noentry);
|
||||
}
|
||||
waitFor.abort();
|
||||
return void console.error(err);
|
||||
}
|
||||
return void console.error(err || res.error);
|
||||
}
|
||||
UI.dialog.tagPrompt(res.data, function (tags) {
|
||||
if (!Array.isArray(tags)) { return; }
|
||||
sframeChan.event('EV_TAGS_SET', {
|
||||
tags: tags,
|
||||
href: href,
|
||||
});
|
||||
tags = res || [];
|
||||
}), href);
|
||||
}).nThen(function () {
|
||||
UI.dialog.tagPrompt(tags, existing, function (newTags) {
|
||||
if (!Array.isArray(newTags)) { return; }
|
||||
common.setPadAttribute('tags', newTags, null, href);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -62,6 +67,10 @@ define([
|
||||
var getPropertiesData = function (common, cb) {
|
||||
var data = {};
|
||||
NThen(function (waitFor) {
|
||||
common.getPadAttribute('password', waitFor(function (err, val) {
|
||||
data.password = val;
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
common.getPadAttribute('href', waitFor(function (err, val) {
|
||||
var base = common.getMetadataMgr().getPrivateData().origin;
|
||||
|
||||
@@ -73,15 +82,19 @@ define([
|
||||
|
||||
// We're not in a read-only pad
|
||||
data.href = base + val;
|
||||
|
||||
// Get Read-only href
|
||||
if (parsed.hashData.type !== "pad") { return; }
|
||||
var i = data.href.indexOf('#') + 1;
|
||||
var hBase = data.href.slice(0, i);
|
||||
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash);
|
||||
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
||||
if (!hrefsecret.keys) { return; }
|
||||
var viewHash = Hash.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys);
|
||||
var viewHash = Hash.getViewHashFromKeys(hrefsecret);
|
||||
data.roHref = hBase + viewHash;
|
||||
}));
|
||||
common.getPadAttribute('channel', waitFor(function (err, val) {
|
||||
data.channel = val;
|
||||
}));
|
||||
common.getPadAttribute('atime', waitFor(function (err, val) {
|
||||
data.atime = val;
|
||||
}));
|
||||
@@ -137,6 +150,22 @@ define([
|
||||
$d.append(UI.dialog.selectable(expire, {
|
||||
id: 'cp-app-prop-expire',
|
||||
}));
|
||||
|
||||
if (typeof data.password !== "undefined") {
|
||||
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
|
||||
.appendTo($d);
|
||||
var password = UI.passwordInput({
|
||||
id: 'cp-app-prop-expire',
|
||||
readonly: 'readonly'
|
||||
});
|
||||
var $pwInput = $(password).find('.cp-password-input');
|
||||
$pwInput.val(data.password).click(function () {
|
||||
$pwInput[0].select();
|
||||
});
|
||||
$(password).find('.cp-checkmark').css('margin-bottom', '15px');
|
||||
$d.append(password);
|
||||
}
|
||||
|
||||
cb(void 0, $d);
|
||||
};
|
||||
var getPadProperties = function (common, data, cb) {
|
||||
@@ -178,7 +207,7 @@ define([
|
||||
|
||||
if (common.isLoggedIn() && AppConfig.enablePinning) {
|
||||
// check the size of this file...
|
||||
common.getFileSize(data.href, function (e, bytes) {
|
||||
common.getFileSize(data.channel, function (e, bytes) {
|
||||
if (e) {
|
||||
// there was a problem with the RPC
|
||||
console.error(e);
|
||||
@@ -237,7 +266,11 @@ define([
|
||||
var link = h('div.cp-share-modal', [
|
||||
h('label', Messages.share_linkAccess),
|
||||
h('br'),
|
||||
h('input#cp-share-editable-true.cp-share-editable-value', {
|
||||
UI.createRadio('cp-share-editable', 'cp-share-editable-true',
|
||||
Messages.share_linkEdit, true, { mark: {tabindex:1} }),
|
||||
UI.createRadio('cp-share-editable', 'cp-share-editable-false',
|
||||
Messages.share_linkView, false, { mark: {tabindex:1} }),
|
||||
/*h('input#cp-share-editable-true.cp-share-editable-value', {
|
||||
type: 'radio',
|
||||
name: 'cp-share-editable',
|
||||
value: 1,
|
||||
@@ -248,25 +281,14 @@ define([
|
||||
name: 'cp-share-editable',
|
||||
value: 0
|
||||
}),
|
||||
h('label', { 'for': 'cp-share-editable-false' }, Messages.share_linkView),
|
||||
h('br'),
|
||||
h('label', { 'for': 'cp-share-editable-false' }, Messages.share_linkView),*/
|
||||
h('br'),
|
||||
h('label', Messages.share_linkOptions),
|
||||
h('br'),
|
||||
h('input#cp-share-embed', {
|
||||
type: 'checkbox',
|
||||
name: 'cp-share-embed'
|
||||
}),
|
||||
h('label', { 'for': 'cp-share-embed' }, Messages.share_linkEmbed),
|
||||
UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }),
|
||||
UI.createCheckbox('cp-share-present', Messages.share_linkPresent, false, { mark: {tabindex:1} }),
|
||||
h('br'),
|
||||
h('input#cp-share-present', {
|
||||
type: 'checkbox',
|
||||
name: 'cp-share-present'
|
||||
}),
|
||||
h('label', { 'for': 'cp-share-present' }, Messages.share_linkPresent),
|
||||
h('br'),
|
||||
h('br'),
|
||||
UI.dialog.selectable('', { id: 'cp-share-link-preview' })
|
||||
UI.dialog.selectable('', { id: 'cp-share-link-preview', tabindex: 1 })
|
||||
]);
|
||||
if (!hashes.editHash) {
|
||||
$(link).find('#cp-share-editable-false').attr('checked', true);
|
||||
@@ -482,13 +504,44 @@ define([
|
||||
'class': 'fa fa-upload cp-toolbar-icon-import',
|
||||
title: Messages.importButtonTitle,
|
||||
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.importButton));
|
||||
if (callback) {
|
||||
/*if (data.types) {
|
||||
// New import button in the toolbar
|
||||
var importFunction = {
|
||||
template: function () {
|
||||
UIElements.openTemplatePicker(common, true);
|
||||
},
|
||||
file: function (cb) {
|
||||
importContent('text/plain', function (content, file) {
|
||||
cb(content, file);
|
||||
}, {accept: data ? data.accept : undefined})
|
||||
}
|
||||
};
|
||||
var toImport = [];
|
||||
Object.keys(data.types).forEach(function (importType) {
|
||||
if (!importFunction[importType] || !data.types[importType]) { return; }
|
||||
var option = h('button', importType);
|
||||
$(option).click(function () {
|
||||
importFunction[importType](data.types[importType]);
|
||||
});
|
||||
toImport.push(options);
|
||||
});
|
||||
|
||||
button.click(common.prepareFeedback(type));
|
||||
|
||||
if (toImport.length === 1) {
|
||||
button.click(function () { $(toImport[0]).click(); });
|
||||
} else {
|
||||
Cryptpad.alert(h('p.cp-import-container', toImport));
|
||||
}
|
||||
}
|
||||
else if (callback) {*/
|
||||
// Old import button, used in settings
|
||||
button
|
||||
.click(common.prepareFeedback(type))
|
||||
.click(importContent('text/plain', function (content, file) {
|
||||
callback(content, file);
|
||||
}, {accept: data ? data.accept : undefined}));
|
||||
}
|
||||
//}
|
||||
break;
|
||||
case 'upload':
|
||||
button = $('<button>', {
|
||||
@@ -520,6 +573,19 @@ define([
|
||||
if (data.accept) { $input.attr('accept', data.accept); }
|
||||
button.click(function () { $input.click(); });
|
||||
break;
|
||||
case 'importtemplate':
|
||||
if (!AppConfig.enableTemplates) { return; }
|
||||
if (!common.isLoggedIn()) { return; }
|
||||
button = $('<button>', {
|
||||
'class': 'fa fa-upload cp-toolbar-icon-import',
|
||||
title: Messages.template_import,
|
||||
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.template_import));
|
||||
button
|
||||
.click(common.prepareFeedback(type))
|
||||
.click(function () {
|
||||
UIElements.openTemplatePicker(common, true);
|
||||
});
|
||||
break;
|
||||
case 'template':
|
||||
if (!AppConfig.enableTemplates) { return; }
|
||||
if (!common.isLoggedIn()) { return; }
|
||||
@@ -584,7 +650,8 @@ define([
|
||||
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
|
||||
if (err) { return void callback(err); }
|
||||
var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
|
||||
UI.alert(cMsg, undefined, true);
|
||||
var msg = common.fixLinks($('<div>').html(cMsg));
|
||||
UI.alert(msg);
|
||||
callback();
|
||||
return;
|
||||
});
|
||||
@@ -890,14 +957,14 @@ define([
|
||||
};
|
||||
};
|
||||
|
||||
var setHTML = function (e, html) {
|
||||
e.innerHTML = html;
|
||||
return e;
|
||||
};
|
||||
|
||||
UIElements.createHelpMenu = function (common, categories) {
|
||||
var type = common.getMetadataMgr().getMetadata().type || 'pad';
|
||||
|
||||
var setHTML = function (e, html) {
|
||||
e.innerHTML = html;
|
||||
return e;
|
||||
};
|
||||
|
||||
var elements = [];
|
||||
if (Messages.help && Messages.help.generic) {
|
||||
Object.keys(Messages.help.generic).forEach(function (el) {
|
||||
@@ -920,18 +987,7 @@ define([
|
||||
h('ul', elements)
|
||||
]);
|
||||
|
||||
var origin = common.getMetadataMgr().getPrivateData().origin || '';
|
||||
$(text).find('a').click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var href = $(this).attr('href');
|
||||
var absolute = /^https?:\/\//i;
|
||||
if (!absolute.test(href)) {
|
||||
if (href.slice(0,1) !== '/') { href = '/' + href; }
|
||||
href = origin + href;
|
||||
}
|
||||
common.openUnsafeURL(href);
|
||||
});
|
||||
common.fixLinks(text);
|
||||
|
||||
var closeButton = h('span.cp-help-close.fa.fa-window-close');
|
||||
var $toolbarButton = common.createButton('', true, {
|
||||
@@ -1110,12 +1166,13 @@ define([
|
||||
};
|
||||
return;
|
||||
}
|
||||
// No password for avatars
|
||||
var secret = Hash.getSecrets('file', parsed.hash);
|
||||
if (secret.keys && secret.channel) {
|
||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
||||
var hexFileName = Util.base64ToHex(secret.channel);
|
||||
var src = Hash.getBlobPathFromHex(hexFileName);
|
||||
Common.getFileSize(href, function (e, data) {
|
||||
Common.getFileSize(hexFileName, function (e, data) {
|
||||
if (e) {
|
||||
displayDefault();
|
||||
return void console.error(e);
|
||||
@@ -1148,7 +1205,7 @@ define([
|
||||
// so we can just use those and only check for errors
|
||||
var $container = $('<span>', {'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;
|
||||
@@ -1422,50 +1479,51 @@ define([
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'target': '_blank',
|
||||
'href': origin+'/drive/'
|
||||
'href': origin+'/drive/',
|
||||
'class': 'fa fa-hdd-o'
|
||||
},
|
||||
content: Messages.login_accessDrive
|
||||
content: h('span', Messages.login_accessDrive)
|
||||
});
|
||||
}
|
||||
// Add the change display name button if not in read only mode
|
||||
if (config.changeNameButtonCls && config.displayChangeName && !AppConfig.disableProfile) {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {'class': config.changeNameButtonCls},
|
||||
content: Messages.user_rename
|
||||
attributes: {'class': config.changeNameButtonCls + ' fa fa-user'},
|
||||
content: h('span', Messages.user_rename)
|
||||
});
|
||||
}
|
||||
if (accountName && !AppConfig.disableProfile) {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {'class': 'cp-toolbar-menu-profile'},
|
||||
content: Messages.profileButton
|
||||
attributes: {'class': 'cp-toolbar-menu-profile fa fa-user-circle'},
|
||||
content: h('span', Messages.profileButton)
|
||||
});
|
||||
}
|
||||
if (padType !== 'settings') {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {'class': 'cp-toolbar-menu-settings'},
|
||||
content: Messages.settingsButton
|
||||
attributes: {'class': 'cp-toolbar-menu-settings fa fa-cog'},
|
||||
content: h('span', Messages.settingsButton)
|
||||
});
|
||||
}
|
||||
// Add login or logout button depending on the current status
|
||||
if (accountName) {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {'class': 'cp-toolbar-menu-logout'},
|
||||
content: Messages.logoutButton
|
||||
attributes: {'class': 'cp-toolbar-menu-logout fa fa-sign-out'},
|
||||
content: h('span', Messages.logoutButton)
|
||||
});
|
||||
} else {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {'class': 'cp-toolbar-menu-login'},
|
||||
content: Messages.login_login
|
||||
attributes: {'class': 'cp-toolbar-menu-login fa fa-sign-in'},
|
||||
content: h('span', Messages.login_login)
|
||||
});
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {'class': 'cp-toolbar-menu-register'},
|
||||
content: Messages.login_register
|
||||
attributes: {'class': 'cp-toolbar-menu-register fa fa-user-plus'},
|
||||
content: h('span', Messages.login_register)
|
||||
});
|
||||
}
|
||||
var $icon = $('<span>', {'class': 'fa fa-user-secret'});
|
||||
@@ -1521,9 +1579,8 @@ define([
|
||||
UIElements.displayAvatar(Common, $avatar, url,
|
||||
newName || Messages.anonymous, function ($img) {
|
||||
oldUrl = url;
|
||||
if ($img) {
|
||||
$userAdmin.find('> button').addClass('cp-avatar');
|
||||
}
|
||||
$userAdmin.find('> button').removeClass('cp-avatar');
|
||||
if ($img) { $userAdmin.find('> button').addClass('cp-avatar'); }
|
||||
loadingAvatar = false;
|
||||
});
|
||||
return;
|
||||
@@ -1651,14 +1708,10 @@ define([
|
||||
var priv = common.getMetadataMgr().getPrivateData();
|
||||
var c = (priv.settings.general && priv.settings.general.creation) || {};
|
||||
if (AppConfig.displayCreationScreen && common.isLoggedIn() && c.skip) {
|
||||
$advanced = $('<input>', {
|
||||
type: 'checkbox',
|
||||
checked: 'checked',
|
||||
id: 'cp-app-toolbar-creation-advanced'
|
||||
}).appendTo($advancedContainer);
|
||||
$('<label>', {
|
||||
for: 'cp-app-toolbar-creation-advanced'
|
||||
}).text(Messages.creation_newPadModalAdvanced).appendTo($advancedContainer);
|
||||
var $cboxLabel = $(UI.createCheckbox('cp-app-toolbar-creation-advanced',
|
||||
Messages.creation_newPadModalAdvanced, true))
|
||||
.appendTo($advancedContainer);
|
||||
$advanced = $cboxLabel.find('input');
|
||||
$description.append('<br>');
|
||||
$description.append(Messages.creation_newPadModalDescriptionAdvanced);
|
||||
}
|
||||
@@ -1742,17 +1795,21 @@ define([
|
||||
sframeChan.event("EV_FILE_PICKER_OPEN", types);
|
||||
};
|
||||
|
||||
UIElements.openTemplatePicker = function (common) {
|
||||
UIElements.openTemplatePicker = function (common, force) {
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var type = metadataMgr.getMetadataLazy().type;
|
||||
var sframeChan = common.getSframeChannel();
|
||||
var focus;
|
||||
|
||||
var pickerCfg = {
|
||||
var pickerCfgInit = {
|
||||
types: [type],
|
||||
where: ['template'],
|
||||
hidden: true
|
||||
};
|
||||
var pickerCfg = {
|
||||
types: [type],
|
||||
where: ['template'],
|
||||
};
|
||||
var onConfirm = function (yes) {
|
||||
if (!yes) {
|
||||
if (focus) { focus.focus(); }
|
||||
@@ -1780,12 +1837,15 @@ define([
|
||||
|
||||
sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) {
|
||||
if (data) {
|
||||
common.openFilePicker(pickerCfg);
|
||||
common.openFilePicker(pickerCfgInit);
|
||||
focus = document.activeElement;
|
||||
if (force) { return void onConfirm(true); }
|
||||
UI.confirm(Messages.useTemplate, onConfirm, {
|
||||
ok: Messages.useTemplateOK,
|
||||
cancel: Messages.useTemplateCancel,
|
||||
});
|
||||
} else if (force) {
|
||||
UI.alert(Messages.template_empty);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1821,11 +1881,15 @@ define([
|
||||
|
||||
var $body = $('body');
|
||||
var $creationContainer = $('<div>', { id: 'cp-creation-container' }).appendTo($body);
|
||||
var urlArgs = (Config.requireConf && Config.requireConf.urlArgs) || '';
|
||||
var l = h('div.cp-creation-logo', h('img', { src: '/customize/loading-logo.png?' + urlArgs }));
|
||||
$(l).appendTo($creationContainer);
|
||||
var $creation = $('<div>', { id: 'cp-creation', tabindex: 1 }).appendTo($creationContainer);
|
||||
|
||||
// Title
|
||||
var colorClass = 'cp-icon-color-'+type;
|
||||
$creation.append(h('h2.cp-creation-title', Messages.newButtonTitle));
|
||||
//var colorClass = 'cp-icon-color-'+type;
|
||||
//$creation.append(h('h2.cp-creation-title', Messages.newButtonTitle));
|
||||
$creation.append(h('h3.cp-creation-title', Messages['button_new'+type]));
|
||||
//$creation.append(h('h2.cp-creation-title.'+colorClass, Messages.newButtonTitle));
|
||||
|
||||
// Deleted pad warning
|
||||
@@ -1837,10 +1901,11 @@ define([
|
||||
|
||||
var origin = common.getMetadataMgr().getPrivateData().origin;
|
||||
var createHelper = function (href, text) {
|
||||
var q = h('a.cp-creation-help.fa.fa-question', {
|
||||
var q = h('a.cp-creation-help.fa.fa-question-circle', {
|
||||
title: text,
|
||||
href: origin + href,
|
||||
target: "_blank"
|
||||
target: "_blank",
|
||||
'data-tippy-placement': "right"
|
||||
});
|
||||
return q;
|
||||
};
|
||||
@@ -1848,30 +1913,14 @@ define([
|
||||
// Owned pads
|
||||
// Default is Owned pad
|
||||
var owned = h('div.cp-creation-owned', [
|
||||
h('label.cp-checkmark', [
|
||||
h('input', {
|
||||
type: 'checkbox',
|
||||
id: 'cp-creation-owned',
|
||||
checked: 'checked'
|
||||
}),
|
||||
h('span.cp-checkmark-mark'),
|
||||
Messages.creation_owned
|
||||
]),
|
||||
UI.createCheckbox('cp-creation-owned', Messages.creation_owned, true),
|
||||
createHelper('/faq.html#keywords-owned', Messages.creation_owned1)
|
||||
]);
|
||||
|
||||
// Life time
|
||||
var expire = h('div.cp-creation-expire', [
|
||||
h('label.cp-checkmark', [
|
||||
h('input', {
|
||||
type: 'checkbox',
|
||||
id: 'cp-creation-expire'
|
||||
}),
|
||||
h('span.cp-checkmark-mark'),
|
||||
Messages.creation_expire
|
||||
]),
|
||||
createHelper('/faq.html#keywords-expiring', Messages.creation_expire2),
|
||||
h('div.cp-creation-expire-picker.cp-creation-slider', [
|
||||
UI.createCheckbox('cp-creation-expire', Messages.creation_expire, false),
|
||||
h('span.cp-creation-expire-picker.cp-creation-slider', [
|
||||
h('input#cp-creation-expire-val', {
|
||||
type: "number",
|
||||
min: 1,
|
||||
@@ -1886,106 +1935,168 @@ define([
|
||||
selected: 'selected'
|
||||
}, Messages.creation_expireMonths)
|
||||
])
|
||||
]),
|
||||
createHelper('/faq.html#keywords-expiring', Messages.creation_expire2),
|
||||
]);
|
||||
|
||||
// Password
|
||||
var password = h('div.cp-creation-password', [
|
||||
UI.createCheckbox('cp-creation-password', Messages.creation_password, false),
|
||||
h('span.cp-creation-password-picker.cp-creation-slider', [
|
||||
UI.passwordInput({id: 'cp-creation-password-val'})
|
||||
/*h('input#cp-creation-password-val', {
|
||||
type: "text" // TODO type password with click to show
|
||||
}),*/
|
||||
]),
|
||||
//createHelper('#', "TODO: password protection adds another layer of security ........") // TODO
|
||||
]);
|
||||
|
||||
var right = h('span.fa.fa-chevron-right.cp-creation-template-more');
|
||||
var left = h('span.fa.fa-chevron-left.cp-creation-template-more');
|
||||
var templates = h('div.cp-creation-template', [
|
||||
left,
|
||||
h('div.cp-creation-template-container', [
|
||||
h('span.fa.fa-circle-o-notch.fa-spin.fa-4x.fa-fw')
|
||||
]),
|
||||
right
|
||||
]);
|
||||
|
||||
var settings = h('div.cp-creation-remember', [
|
||||
UI.createCheckbox('cp-creation-remember', Messages.creation_saveSettings, false),
|
||||
createHelper('/settings/#creation', Messages.creation_settings),
|
||||
h('div.cp-creation-remember-help.cp-creation-slider', [
|
||||
h('span.fa.fa-exclamation-circle.cp-creation-warning'),
|
||||
Messages.creation_rememberHelp
|
||||
])
|
||||
]);
|
||||
|
||||
var createDiv = h('div.cp-creation-create');
|
||||
var $create = $(createDiv);
|
||||
|
||||
var templates = h('div.cp-creation-template', [
|
||||
h('h3.cp-creation-title.'+colorClass, Messages['button_new'+type]),
|
||||
h('div.cp-creation-template-container', [
|
||||
h('span.fa.fa-circle-o-notch.fa-spin.fa-4x.fa-fw')
|
||||
]),
|
||||
createDiv
|
||||
]);
|
||||
|
||||
var settings = h('div.cp-creation-remember', [
|
||||
h('label.cp-checkmark', [
|
||||
h('input', {
|
||||
type: 'checkbox',
|
||||
id: 'cp-creation-remember'
|
||||
}),
|
||||
h('span.cp-checkmark-mark'),
|
||||
Messages.creation_saveSettings
|
||||
]),
|
||||
createHelper('/settings/#creation', Messages.creation_settings),
|
||||
h('div.cp-creation-remember-help.cp-creation-slider', Messages.creation_rememberHelp)
|
||||
]);
|
||||
|
||||
$(h('div#cp-creation-form', [
|
||||
owned,
|
||||
expire,
|
||||
password,
|
||||
settings,
|
||||
templates
|
||||
templates,
|
||||
createDiv
|
||||
])).appendTo($creation);
|
||||
|
||||
// Display templates
|
||||
var selected = 0;
|
||||
|
||||
var selected = 0; // Selected template in the list (highlighted)
|
||||
var TEMPLATES_DISPLAYED = 4; // Max templates displayed per page
|
||||
var next = function () {}; // Function called when pressing tab to highlight the next template
|
||||
var i = 0; // Index of the first template displayed in the current page
|
||||
sframeChan.query("Q_CREATE_TEMPLATES", type, function (err, res) {
|
||||
if (!res.data || !Array.isArray(res.data)) {
|
||||
return void console.error("Error: get the templates list");
|
||||
}
|
||||
var data = res.data.slice().sort(function (a, b) {
|
||||
if (a.name === b.name) { return 0; }
|
||||
return a.name < b.name ? -1 : 1;
|
||||
var allData = res.data.slice().sort(function (a, b) {
|
||||
if (a.used === b.used) {
|
||||
// Sort by name
|
||||
if (a.name === b.name) { return 0; }
|
||||
return a.name < b.name ? -1 : 1;
|
||||
}
|
||||
return b.used - a.used;
|
||||
});
|
||||
data.unshift({
|
||||
name: Messages.creation_noTemplate,
|
||||
id: 0,
|
||||
icon: h('span.fa.fa-file')
|
||||
});
|
||||
data.push({
|
||||
allData.unshift({
|
||||
name: Messages.creation_newTemplate,
|
||||
id: -1,
|
||||
icon: h('span.fa.fa-bookmark')
|
||||
});
|
||||
var $container = $(templates).find('.cp-creation-template-container').html('');
|
||||
data.forEach(function (obj, idx) {
|
||||
var name = obj.name;
|
||||
var $span = $('<span>', {
|
||||
'class': 'cp-creation-template-element',
|
||||
'title': name,
|
||||
}).appendTo($container);
|
||||
$span.data('id', obj.id);
|
||||
if (idx === 0) { $span.addClass('cp-creation-template-selected'); }
|
||||
$span.append(obj.icon || UI.getFileIcon({type: type}));
|
||||
$('<span>', {'class': 'cp-creation-template-element-name'}).text(name)
|
||||
.appendTo($span);
|
||||
$span.click(function () {
|
||||
$container.find('.cp-creation-template-selected')
|
||||
.removeClass('cp-creation-template-selected');
|
||||
$span.addClass('cp-creation-template-selected');
|
||||
selected = idx;
|
||||
});
|
||||
|
||||
// Add thumbnail if it exists
|
||||
if (obj.thumbnail) {
|
||||
common.addThumbnail(obj.thumbnail, $span, function () {});
|
||||
}
|
||||
allData.unshift({
|
||||
name: Messages.creation_noTemplate,
|
||||
id: 0,
|
||||
icon: h('span.fa.fa-file')
|
||||
});
|
||||
});
|
||||
// Change template selection when Tab is pressed
|
||||
var next = function (revert) {
|
||||
var max = $creation.find('.cp-creation-template-element').length;
|
||||
selected = revert ?
|
||||
(--selected < 0 ? max-1 : selected) :
|
||||
++selected % max;
|
||||
$creation.find('.cp-creation-template-element')
|
||||
.removeClass('cp-creation-template-selected');
|
||||
$($creation.find('.cp-creation-template-element').get(selected))
|
||||
.addClass('cp-creation-template-selected');
|
||||
};
|
||||
var redraw = function (index) {
|
||||
if (index < 0) { i = 0; }
|
||||
else if (index > allData.length - 1) { return; }
|
||||
else { i = index; }
|
||||
var data = allData.slice(i, i + TEMPLATES_DISPLAYED);
|
||||
var $container = $(templates).find('.cp-creation-template-container').html('');
|
||||
data.forEach(function (obj, idx) {
|
||||
var name = obj.name;
|
||||
var $span = $('<span>', {
|
||||
'class': 'cp-creation-template-element',
|
||||
'title': name,
|
||||
}).appendTo($container);
|
||||
$span.data('id', obj.id);
|
||||
if (idx === selected) { $span.addClass('cp-creation-template-selected'); }
|
||||
$span.append(obj.icon || UI.getFileIcon({type: type}));
|
||||
$('<span>', {'class': 'cp-creation-template-element-name'}).text(name)
|
||||
.appendTo($span);
|
||||
$span.click(function () {
|
||||
$container.find('.cp-creation-template-selected')
|
||||
.removeClass('cp-creation-template-selected');
|
||||
$span.addClass('cp-creation-template-selected');
|
||||
selected = idx;
|
||||
});
|
||||
|
||||
// Add thumbnail if it exists
|
||||
if (obj.thumbnail) {
|
||||
common.addThumbnail(obj.thumbnail, $span, function () {});
|
||||
}
|
||||
});
|
||||
$(right).off('click').removeClass('hidden').click(function () {
|
||||
selected = 0;
|
||||
redraw(i + TEMPLATES_DISPLAYED);
|
||||
});
|
||||
if (i >= allData.length - TEMPLATES_DISPLAYED ) { $(right).addClass('hidden'); }
|
||||
$(left).off('click').removeClass('hidden').click(function () {
|
||||
selected = TEMPLATES_DISPLAYED - 1;
|
||||
redraw(i - TEMPLATES_DISPLAYED);
|
||||
});
|
||||
if (i < TEMPLATES_DISPLAYED) { $(left).addClass('hidden'); }
|
||||
};
|
||||
redraw(0);
|
||||
|
||||
// Change template selection when Tab is pressed
|
||||
next = function (revert) {
|
||||
var max = $creation.find('.cp-creation-template-element').length;
|
||||
if (selected + 1 === max && !revert) {
|
||||
selected = i + TEMPLATES_DISPLAYED < allData.length ? 0 : max;
|
||||
return void redraw(i + TEMPLATES_DISPLAYED);
|
||||
}
|
||||
if (selected === 0 && revert) {
|
||||
selected = i - TEMPLATES_DISPLAYED >= 0 ? TEMPLATES_DISPLAYED - 1 : 0;
|
||||
return void redraw(i - TEMPLATES_DISPLAYED);
|
||||
}
|
||||
selected = revert ?
|
||||
(--selected < 0 ? 0 : selected) :
|
||||
++selected >= max ? max-1 : selected;
|
||||
$creation.find('.cp-creation-template-element')
|
||||
.removeClass('cp-creation-template-selected');
|
||||
$($creation.find('.cp-creation-template-element').get(selected))
|
||||
.addClass('cp-creation-template-selected');
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
// Display expiration form when checkbox checked
|
||||
$creation.find('#cp-creation-expire').on('change', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
$creation.find('.cp-creation-expire-picker:not(.active)').addClass('active');
|
||||
$creation.find('.cp-creation-expire:not(.active)').addClass('active');
|
||||
$creation.find('#cp-creation-expire-val').focus();
|
||||
return;
|
||||
}
|
||||
$creation.find('.cp-creation-expire-picker').removeClass('active');
|
||||
$creation.find('.cp-creation-expire').removeClass('active');
|
||||
$creation.focus();
|
||||
});
|
||||
|
||||
// Display expiration form when checkbox checked
|
||||
$creation.find('#cp-creation-password').on('change', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
$creation.find('.cp-creation-password-picker:not(.active)').addClass('active');
|
||||
$creation.find('.cp-creation-password:not(.active)').addClass('active');
|
||||
$creation.find('#cp-creation-password-val').focus();
|
||||
return;
|
||||
}
|
||||
$creation.find('.cp-creation-password-picker').removeClass('active');
|
||||
$creation.find('.cp-creation-password').removeClass('active');
|
||||
$creation.focus();
|
||||
});
|
||||
|
||||
@@ -2037,12 +2148,16 @@ define([
|
||||
}
|
||||
expireVal = ($('#cp-creation-expire-val').val() || 0) * unit;
|
||||
}
|
||||
// Password
|
||||
var passwordVal = $('#cp-creation-password').is(':checked') ?
|
||||
$('#cp-creation-password-val').val() : undefined;
|
||||
|
||||
var $template = $creation.find('.cp-creation-template-selected');
|
||||
var templateId = $template.data('id') || undefined;
|
||||
|
||||
return {
|
||||
owned: ownedVal,
|
||||
password: passwordVal,
|
||||
expire: expireVal,
|
||||
templateId: templateId
|
||||
};
|
||||
@@ -2112,5 +2227,38 @@ define([
|
||||
(cb || function () {})();
|
||||
};
|
||||
|
||||
UIElements.displayPasswordPrompt = function (common, isError) {
|
||||
var error;
|
||||
if (isError) { error = setHTML(h('p.cp-password-error'), Messages.password_error); }
|
||||
var info = h('p.cp-password-info', Messages.password_info);
|
||||
var password = UI.passwordInput({placeholder: Messages.password_placeholder});
|
||||
var button = h('button', Messages.password_submit);
|
||||
|
||||
var submit = function () {
|
||||
var value = $(password).find('.cp-password-input').val();
|
||||
UI.addLoadingScreen();
|
||||
common.getSframeChannel().query('Q_PAD_PASSWORD_VALUE', value, function (err, data) {
|
||||
if (!data) {
|
||||
UIElements.displayPasswordPrompt(common, true);
|
||||
}
|
||||
});
|
||||
};
|
||||
$(password).find('.cp-password-input').on('keydown', function (e) { if (e.which === 13) { submit(); } });
|
||||
$(button).on('click', function () { submit(); });
|
||||
|
||||
|
||||
var block = h('div#cp-loading-password-prompt', [
|
||||
error,
|
||||
info,
|
||||
h('p.cp-password-form', [
|
||||
password,
|
||||
button
|
||||
])
|
||||
]);
|
||||
UI.errorLoadingScreen(block);
|
||||
|
||||
$(password).find('.cp-password-input').focus();
|
||||
};
|
||||
|
||||
return UIElements;
|
||||
});
|
||||
|
||||
@@ -20,9 +20,9 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var makeConfig = function (hash) {
|
||||
var makeConfig = function (hash, password) {
|
||||
// We can't use cryptget with a file or a user so we can use 'pad' as hash type
|
||||
var secret = Hash.getSecrets('pad', hash);
|
||||
var secret = Hash.getSecrets('pad', hash, password);
|
||||
if (!secret.keys) { secret.keys = secret.key; } // support old hashses
|
||||
var config = {
|
||||
websocketURL: NetConfig.getWebsocketURL(),
|
||||
@@ -47,8 +47,10 @@ define([
|
||||
if (typeof(cb) !== 'function') {
|
||||
throw new Error('Cryptget expects a callback');
|
||||
}
|
||||
opt = opt || {};
|
||||
|
||||
var config = makeConfig(hash, opt.password);
|
||||
var Session = { cb: cb, };
|
||||
var config = makeConfig(hash);
|
||||
|
||||
config.onReady = function (info) {
|
||||
var rt = Session.session = info.realtime;
|
||||
@@ -64,9 +66,11 @@ define([
|
||||
if (typeof(cb) !== 'function') {
|
||||
throw new Error('Cryptput expects a callback');
|
||||
}
|
||||
opt = opt || {};
|
||||
|
||||
var config = makeConfig(hash);
|
||||
var config = makeConfig(hash, opt.password);
|
||||
var Session = { cb: cb, };
|
||||
|
||||
config.onReady = function (info) {
|
||||
var realtime = Session.session = info.realtime;
|
||||
Session.network = info.network;
|
||||
|
||||
@@ -246,8 +246,8 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
common.getFileSize = function (href, cb) {
|
||||
postMessage("GET_FILE_SIZE", {href: href}, function (obj) {
|
||||
common.getFileSize = function (href, password, cb) {
|
||||
postMessage("GET_FILE_SIZE", {href: href, password: password}, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
cb(undefined, obj.size);
|
||||
});
|
||||
@@ -260,8 +260,8 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
common.isNewChannel = function (href, cb) {
|
||||
postMessage('IS_NEW_CHANNEL', {href: href}, function (obj) {
|
||||
common.isNewChannel = function (href, password, cb) {
|
||||
postMessage('IS_NEW_CHANNEL', {href: href, password: password}, function (obj) {
|
||||
if (obj.error) { return void cb(obj.error); }
|
||||
if (!obj) { return void cb('INVALID_RESPONSE'); }
|
||||
cb(undefined, obj.isNew);
|
||||
@@ -395,8 +395,10 @@ define([
|
||||
common.saveAsTemplate = function (Cryptput, data, cb) {
|
||||
var p = Hash.parsePadUrl(window.location.href);
|
||||
if (!p.type) { return; }
|
||||
var hash = Hash.createRandomHash();
|
||||
// PPP: password for the new template?
|
||||
var hash = Hash.createRandomHash(p.type);
|
||||
var href = '/' + p.type + '/#' + hash;
|
||||
// PPP: add password as cryptput option
|
||||
Cryptput(hash, data.toSave, function (e) {
|
||||
if (e) { throw new Error(e); }
|
||||
postMessage("ADD_PAD", {
|
||||
@@ -419,15 +421,33 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
common.useTemplate = function (href, Crypt, cb, opts) {
|
||||
common.useTemplate = function (href, Crypt, cb, optsPut) {
|
||||
// opts is used to overrides options for chainpad-netflux in cryptput
|
||||
// it allows us to add owners and expiration time if it is a new file
|
||||
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var parsed2 = Hash.parsePadUrl(window.location.href);
|
||||
if(!parsed) { throw new Error("Cannot get template hash"); }
|
||||
Crypt.get(parsed.hash, function (err, val) {
|
||||
if (err) { throw new Error(err); }
|
||||
var p = Hash.parsePadUrl(window.location.href);
|
||||
Crypt.put(p.hash, val, cb, opts);
|
||||
postMessage("INCREMENT_TEMPLATE_USE", href);
|
||||
|
||||
optsPut = optsPut || {};
|
||||
var optsGet = {};
|
||||
Nthen(function (waitFor) {
|
||||
if (parsed.hashData && parsed.hashData.password) {
|
||||
common.getPadAttribute('password', waitFor(function (err, password) {
|
||||
optsGet.password = password;
|
||||
}), href);
|
||||
}
|
||||
if (parsed2.hashData && parsed2.hashData.password) {
|
||||
common.getPadAttribute('password', waitFor(function (err, password) {
|
||||
optsPut.password = password;
|
||||
}));
|
||||
}
|
||||
}).nThen(function () {
|
||||
Crypt.get(parsed.hash, function (err, val) {
|
||||
if (err) { throw new Error(err); }
|
||||
Crypt.put(parsed2.hash, val, cb, optsPut);
|
||||
}, optsGet);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -438,20 +458,18 @@ define([
|
||||
};
|
||||
|
||||
// When opening a new pad or renaming it, store the new title
|
||||
common.setPadTitle = function (title, padHref, path, cb) {
|
||||
var href = padHref || window.location.href;
|
||||
common.setPadTitle = function (data, cb) {
|
||||
if (!data || typeof (data) !== "object") { return cb ('Data is not an object'); }
|
||||
|
||||
var href = data.href || window.location.href;
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
if (!parsed.hash) { return; }
|
||||
href = parsed.getUrl({present: parsed.present});
|
||||
if (!parsed.hash) { return cb ('Invalid hash'); }
|
||||
data.href = parsed.getUrl({present: parsed.present});
|
||||
|
||||
if (title === null) { return; }
|
||||
if (title.trim() === "") { title = Hash.getDefaultName(parsed); }
|
||||
if (typeof (data.title) !== "string") { return cb('Missing title'); }
|
||||
if (data.title.trim() === "") { data.title = Hash.getDefaultName(parsed); }
|
||||
|
||||
postMessage("SET_PAD_TITLE", {
|
||||
href: href,
|
||||
title: title,
|
||||
path: path
|
||||
}, function (obj) {
|
||||
postMessage("SET_PAD_TITLE", data, function (obj) {
|
||||
if (obj && obj.error) {
|
||||
console.log("unable to set pad title");
|
||||
return void cb(obj.error);
|
||||
@@ -472,10 +490,6 @@ define([
|
||||
cb(void 0, data);
|
||||
});
|
||||
};
|
||||
// Set initial path when creating a pad from pad creation screen
|
||||
common.setInitialPath = function (path) {
|
||||
postMessage("SET_INITIAL_PATH", path);
|
||||
};
|
||||
|
||||
// Messaging (manage friends from the userlist)
|
||||
common.inviteFromUserlist = function (netfluxId, cb) {
|
||||
@@ -548,6 +562,10 @@ define([
|
||||
pad.onDisconnectEvent = Util.mkEvent();
|
||||
pad.onErrorEvent = Util.mkEvent();
|
||||
|
||||
// Loading events
|
||||
common.loading = {};
|
||||
common.loading.onDriveEvent = Util.mkEvent();
|
||||
|
||||
common.getFullHistory = function (data, cb) {
|
||||
postMessage("GET_FULL_HISTORY", data, cb);
|
||||
};
|
||||
@@ -555,15 +573,15 @@ define([
|
||||
common.getShareHashes = function (secret, cb) {
|
||||
var hashes;
|
||||
if (!window.location.hash) {
|
||||
hashes = Hash.getHashes(secret.channel, secret);
|
||||
hashes = Hash.getHashes(secret);
|
||||
return void cb(null, hashes);
|
||||
}
|
||||
var parsed = Hash.parsePadUrl(window.location.href);
|
||||
if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); }
|
||||
if (parsed.type === 'file') { secret.channel = Util.base64ToHex(secret.channel); }
|
||||
hashes = Hash.getHashes(secret.channel, secret);
|
||||
hashes = Hash.getHashes(secret);
|
||||
|
||||
if (!hashes.editHash && !hashes.viewHash && parsed.hashData && !parsed.hashData.mode) {
|
||||
if (secret.version === 0) {
|
||||
// It means we're using an old hash
|
||||
hashes.editHash = window.location.hash.slice(1);
|
||||
return void cb(null, hashes);
|
||||
@@ -575,7 +593,8 @@ define([
|
||||
}
|
||||
|
||||
postMessage("GET_STRONGER_HASH", {
|
||||
href: window.location.href
|
||||
href: window.location.href,
|
||||
password: secret.password
|
||||
}, function (hash) {
|
||||
if (hash) { hashes.editHash = hash; }
|
||||
cb(null, hashes);
|
||||
@@ -622,6 +641,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 +726,14 @@ define([
|
||||
case 'DRIVE_REMOVE': {
|
||||
common.drive.onRemove.fire(data); break;
|
||||
}
|
||||
// Account deletion
|
||||
case 'DELETE_ACCOUNT': {
|
||||
common.startAccountDeletion(cb); break;
|
||||
}
|
||||
// Loading
|
||||
case 'LOADING_DRIVE': {
|
||||
common.loading.onDriveEvent.fire(data); break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -779,11 +812,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));
|
||||
@@ -802,18 +835,18 @@ define([
|
||||
window.onhashchange = function (ev) {
|
||||
if (ev && ev.reset) { oldHref = document.location.href; return; }
|
||||
var newHref = document.location.href;
|
||||
var parsedOld = Hash.parsePadUrl(oldHref).hashData;
|
||||
var parsedNew = Hash.parsePadUrl(newHref).hashData;
|
||||
if (parsedOld && parsedNew && (
|
||||
parsedOld.type !== parsedNew.type
|
||||
|| parsedOld.channel !== parsedNew.channel
|
||||
|| parsedOld.mode !== parsedNew.mode
|
||||
|| parsedOld.key !== parsedNew.key)) {
|
||||
if (!parsedOld.channel) { oldHref = newHref; return; }
|
||||
|
||||
// Compare the URLs without /embed and /present
|
||||
var parsedOld = Hash.parsePadUrl(oldHref);
|
||||
var parsedNew = Hash.parsePadUrl(newHref);
|
||||
if (parsedOld.hashData && parsedNew.hashData &&
|
||||
parsedOld.getUrl() !== parsedNew.getUrl()) {
|
||||
if (!parsedOld.hashData.key) { oldHref = newHref; return; }
|
||||
// If different, reload
|
||||
document.location.reload();
|
||||
return;
|
||||
}
|
||||
if (parsedNew) { oldHref = newHref; }
|
||||
if (parsedNew.hashData) { oldHref = newHref; }
|
||||
};
|
||||
// Listen for login/logout in other tabs
|
||||
window.addEventListener('storage', function (e) {
|
||||
|
||||
@@ -41,6 +41,7 @@ define([
|
||||
};
|
||||
renderer.image = function (href, title, text) {
|
||||
if (href.slice(0,6) === '/file/') {
|
||||
// PASSWORD_FILES
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||
|
||||
7
www/common/jquery-ui/jquery-ui.min.css
vendored
Normal file
7
www/common/jquery-ui/jquery-ui.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
6
www/common/jquery-ui/jquery-ui.min.js
vendored
Normal file
6
www/common/jquery-ui/jquery-ui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,94 +1,3 @@
|
||||
define([], function () {
|
||||
var loadingStyle = (function(){/*
|
||||
#cp-loading {
|
||||
transition: opacity 0.75s, visibility 0s 0.75s;
|
||||
visibility: visible;
|
||||
position: fixed;
|
||||
z-index: 10000000;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
background: #222;
|
||||
color: #fafafa;
|
||||
text-align: center;
|
||||
font-size: 1.5em;
|
||||
opacity: 1;
|
||||
}
|
||||
#cp-loading.cp-loading-hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
#cp-loading .cp-loading-container {
|
||||
margin-top: 50vh;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
#cp-loading .cp-loading-cryptofist {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
height: 300px;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
@media screen and (max-height: 450px) {
|
||||
#cp-loading .cp-loading-cryptofist {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
#cp-loading .cp-loading-spinner-container {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
}
|
||||
#cp-loading .cp-loading-spinner-container > div {
|
||||
height: 100px;
|
||||
}
|
||||
#cp-loading-tip {
|
||||
position: fixed;
|
||||
z-index: 10000000;
|
||||
top: 80%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
transition: opacity 750ms;
|
||||
transition-delay: 3000ms;
|
||||
}
|
||||
@media screen and (max-height: 600px) {
|
||||
#cp-loading-tip {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
#cp-loading-tip span {
|
||||
background: #222;
|
||||
color: #fafafa;
|
||||
text-align: center;
|
||||
font-size: 1.5em;
|
||||
opacity: 0.7;
|
||||
font-family: 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
padding: 15px;
|
||||
max-width: 60%;
|
||||
display: inline-block;
|
||||
}
|
||||
*/}).toString().slice(14, -3);
|
||||
var urlArgs = window.location.href.replace(/^.*\?([^\?]*)$/, function (all, x) { return x; });
|
||||
var elem = document.createElement('div');
|
||||
elem.setAttribute('id', 'cp-loading');
|
||||
elem.innerHTML = [
|
||||
'<style>',
|
||||
loadingStyle,
|
||||
'</style>',
|
||||
'<div class="cp-loading-container">',
|
||||
'<img class="cp-loading-cryptofist" src="/customize/cryptpad-new-logo-colors-logoonly.png?' + urlArgs + '">',
|
||||
'<div class="cp-loading-spinner-container">',
|
||||
'<span class="fa fa-circle-o-notch fa-spin fa-4x fa-fw"></span>',
|
||||
'</div>',
|
||||
'<p id="cp-loading-message"></p>',
|
||||
'</div>'
|
||||
].join('');
|
||||
var intr;
|
||||
var append = function () {
|
||||
if (!document.body) { return; }
|
||||
clearInterval(intr);
|
||||
document.body.appendChild(elem);
|
||||
};
|
||||
intr = setInterval(append, 100);
|
||||
append();
|
||||
require(['/customize/loading.js'], function (Loading) {
|
||||
Loading();
|
||||
});
|
||||
|
||||
@@ -122,21 +122,15 @@ define([
|
||||
// Do not migrate a pad if we already have it, it would create a duplicate in the drive
|
||||
if (newHrefs.indexOf(href) !== -1) { return; }
|
||||
// If we have a stronger version, do not add the current href
|
||||
if (Hash.findStronger(href, newRecentPads)) { return; }
|
||||
if (Hash.findStronger(href, oldRecentPads[id].channel, newRecentPads)) { return; }
|
||||
// If we have a weaker version, replace the href by the new one
|
||||
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
|
||||
var weaker = Hash.findWeaker(href, newRecentPads);
|
||||
var weaker = Hash.findWeaker(href, oldRecentPads[id].channel, newRecentPads);
|
||||
if (weaker) {
|
||||
// Update RECENTPADS
|
||||
newRecentPads.some(function (pad) {
|
||||
if (pad.href === weaker) {
|
||||
pad.href = href;
|
||||
return true;
|
||||
}
|
||||
return;
|
||||
});
|
||||
weaker.href = href;
|
||||
// Update the file in the drive
|
||||
newFo.replace(weaker, href);
|
||||
newFo.replace(weaker.href, href);
|
||||
return;
|
||||
}
|
||||
// Here it means we have a new href, so we should add it to the drive at its old location
|
||||
|
||||
@@ -34,6 +34,10 @@ define(['json.sortify'], function (Sortify) {
|
||||
}
|
||||
if (!metadataObj.users) { metadataObj.users = {}; }
|
||||
if (!metadataLazyObj.users) { metadataLazyObj.users = {}; }
|
||||
|
||||
if (!metadataObj.type) { metadataObj.type = meta.doc.type; }
|
||||
if (!metadataLazyObj.type) { metadataLazyObj.type = meta.doc.type; }
|
||||
|
||||
var mdo = {};
|
||||
// We don't want to add our user data to the object multiple times.
|
||||
//var containsYou = false;
|
||||
|
||||
@@ -1,106 +1,152 @@
|
||||
define(['/common/common-feedback.js'], function (Feedback) {
|
||||
define([
|
||||
'/common/common-feedback.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-util.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
], function (Feedback, Hash, Util, nThen) {
|
||||
// Start migration check
|
||||
// Versions:
|
||||
// 1: migrate pad attributes
|
||||
// 2: migrate indent settings (codemirror)
|
||||
|
||||
return function (userObject) {
|
||||
return function (userObject, cb, progress) {
|
||||
var version = userObject.version || 0;
|
||||
|
||||
// DEPRECATED
|
||||
// Migration 1: pad attributes moved to filesData
|
||||
var migratePadAttributesToData = function () {
|
||||
return true;
|
||||
};
|
||||
if (version < 1) {
|
||||
migratePadAttributesToData();
|
||||
}
|
||||
|
||||
// Migration 2: global attributes from root to 'settings' subobjects
|
||||
var migrateAttributes = function () {
|
||||
var drawer = 'cryptpad.userlist-drawer';
|
||||
var polls = 'cryptpad.hide_poll_text';
|
||||
var indentKey = 'cryptpad.indentUnit';
|
||||
var useTabsKey = 'cryptpad.indentWithTabs';
|
||||
var settings = userObject.settings = userObject.settings || {};
|
||||
if (typeof(userObject[indentKey]) !== "undefined") {
|
||||
settings.codemirror = settings.codemirror || {};
|
||||
settings.codemirror.indentUnit = userObject[indentKey];
|
||||
delete userObject[indentKey];
|
||||
nThen(function () {
|
||||
// DEPRECATED
|
||||
// Migration 1: pad attributes moved to filesData
|
||||
var migratePadAttributesToData = function () {
|
||||
return true;
|
||||
};
|
||||
if (version < 1) {
|
||||
migratePadAttributesToData();
|
||||
}
|
||||
if (typeof(userObject[useTabsKey]) !== "undefined") {
|
||||
settings.codemirror = settings.codemirror || {};
|
||||
settings.codemirror.indentWithTabs = userObject[useTabsKey];
|
||||
delete userObject[useTabsKey];
|
||||
}).nThen(function () {
|
||||
// Migration 2: global attributes from root to 'settings' subobjects
|
||||
var migrateAttributes = function () {
|
||||
var drawer = 'cryptpad.userlist-drawer';
|
||||
var polls = 'cryptpad.hide_poll_text';
|
||||
var indentKey = 'cryptpad.indentUnit';
|
||||
var useTabsKey = 'cryptpad.indentWithTabs';
|
||||
var settings = userObject.settings = userObject.settings || {};
|
||||
if (typeof(userObject[indentKey]) !== "undefined") {
|
||||
settings.codemirror = settings.codemirror || {};
|
||||
settings.codemirror.indentUnit = userObject[indentKey];
|
||||
delete userObject[indentKey];
|
||||
}
|
||||
if (typeof(userObject[useTabsKey]) !== "undefined") {
|
||||
settings.codemirror = settings.codemirror || {};
|
||||
settings.codemirror.indentWithTabs = userObject[useTabsKey];
|
||||
delete userObject[useTabsKey];
|
||||
}
|
||||
if (typeof(userObject[drawer]) !== "undefined") {
|
||||
settings.toolbar = settings.toolbar || {};
|
||||
settings.toolbar['userlist-drawer'] = userObject[drawer];
|
||||
delete userObject[drawer];
|
||||
}
|
||||
if (typeof(userObject[polls]) !== "undefined") {
|
||||
settings.poll = settings.poll || {};
|
||||
settings.poll['hide-text'] = userObject[polls];
|
||||
delete userObject[polls];
|
||||
}
|
||||
};
|
||||
if (version < 2) {
|
||||
migrateAttributes();
|
||||
Feedback.send('Migrate-2', true);
|
||||
userObject.version = version = 2;
|
||||
}
|
||||
if (typeof(userObject[drawer]) !== "undefined") {
|
||||
settings.toolbar = settings.toolbar || {};
|
||||
settings.toolbar['userlist-drawer'] = userObject[drawer];
|
||||
delete userObject[drawer];
|
||||
}).nThen(function () {
|
||||
// Migration 3: language from localStorage to settings
|
||||
var migrateLanguage = function () {
|
||||
if (!localStorage.CRYPTPAD_LANG) { return; }
|
||||
var l = localStorage.CRYPTPAD_LANG;
|
||||
userObject.settings.language = l;
|
||||
};
|
||||
if (version < 3) {
|
||||
migrateLanguage();
|
||||
Feedback.send('Migrate-3', true);
|
||||
userObject.version = version = 3;
|
||||
}
|
||||
if (typeof(userObject[polls]) !== "undefined") {
|
||||
settings.poll = settings.poll || {};
|
||||
settings.poll['hide-text'] = userObject[polls];
|
||||
delete userObject[polls];
|
||||
}).nThen(function () {
|
||||
// Migration 4: allowUserFeedback to settings
|
||||
var migrateFeedback = function () {
|
||||
var settings = userObject.settings = userObject.settings || {};
|
||||
if (typeof(userObject['allowUserFeedback']) !== "undefined") {
|
||||
settings.general = settings.general || {};
|
||||
settings.general.allowUserFeedback = userObject['allowUserFeedback'];
|
||||
delete userObject['allowUserFeedback'];
|
||||
}
|
||||
};
|
||||
if (version < 4) {
|
||||
migrateFeedback();
|
||||
Feedback.send('Migrate-4', true);
|
||||
userObject.version = version = 4;
|
||||
}
|
||||
};
|
||||
if (version < 2) {
|
||||
migrateAttributes();
|
||||
Feedback.send('Migrate-2', true);
|
||||
userObject.version = version = 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Migration 3: language from localStorage to settings
|
||||
var migrateLanguage = function () {
|
||||
if (!localStorage.CRYPTPAD_LANG) { return; }
|
||||
var l = localStorage.CRYPTPAD_LANG;
|
||||
userObject.settings.language = l;
|
||||
};
|
||||
if (version < 3) {
|
||||
migrateLanguage();
|
||||
Feedback.send('Migrate-3', true);
|
||||
userObject.version = version = 3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Migration 4: allowUserFeedback to settings
|
||||
var migrateFeedback = function () {
|
||||
var settings = userObject.settings = userObject.settings || {};
|
||||
if (typeof(userObject['allowUserFeedback']) !== "undefined") {
|
||||
settings.general = settings.general || {};
|
||||
settings.general.allowUserFeedback = userObject['allowUserFeedback'];
|
||||
delete userObject['allowUserFeedback'];
|
||||
}
|
||||
};
|
||||
if (version < 4) {
|
||||
migrateFeedback();
|
||||
Feedback.send('Migrate-4', true);
|
||||
userObject.version = version = 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Migration 5: dates to Number
|
||||
var migrateDates = function () {
|
||||
var data = userObject.drive && userObject.drive.filesData;
|
||||
if (data) {
|
||||
for (var id in data) {
|
||||
if (typeof data[id].ctime !== "number") {
|
||||
data[id].ctime = +new Date(data[id].ctime);
|
||||
}
|
||||
if (typeof data[id].atime !== "number") {
|
||||
data[id].atime = +new Date(data[id].atime);
|
||||
}).nThen(function () {
|
||||
// Migration 5: dates to Number
|
||||
var migrateDates = function () {
|
||||
var data = userObject.drive && userObject.drive.filesData;
|
||||
if (data) {
|
||||
for (var id in data) {
|
||||
if (typeof data[id].ctime !== "number") {
|
||||
data[id].ctime = +new Date(data[id].ctime);
|
||||
}
|
||||
if (typeof data[id].atime !== "number") {
|
||||
data[id].atime = +new Date(data[id].atime);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (version < 5) {
|
||||
migrateDates();
|
||||
Feedback.send('Migrate-5', true);
|
||||
userObject.version = version = 5;
|
||||
}
|
||||
};
|
||||
if (version < 5) {
|
||||
migrateDates();
|
||||
Feedback.send('Migrate-5', true);
|
||||
userObject.version = version = 5;
|
||||
}
|
||||
}).nThen(function (waitFor) {
|
||||
var addChannelId = function () {
|
||||
var data = userObject.drive.filesData;
|
||||
var el, parsed;
|
||||
var n = nThen(function () {});
|
||||
var padsLength = Object.keys(data).length;
|
||||
Object.keys(data).forEach(function (k, i) {
|
||||
n = n.nThen(function (w) {
|
||||
setTimeout(w(function () {
|
||||
el = data[k];
|
||||
parsed = Hash.parsePadUrl(el.href);
|
||||
if (!el.href) { return; }
|
||||
if (!el.channel) {
|
||||
if (parsed.hashData && parsed.hashData.type === "file") {
|
||||
// PASSWORD_FILES
|
||||
el.channel = Util.base64ToHex(parsed.hashData.channel);
|
||||
} else {
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||
el.channel = secret.channel;
|
||||
}
|
||||
progress(6, Math.round(100*i/padsLength));
|
||||
console.log('Adding missing channel in filesData ', el.channel);
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
n.nThen(waitFor());
|
||||
};
|
||||
if (version < 6) {
|
||||
addChannelId();
|
||||
Feedback.send('Migrate-6', true);
|
||||
userObject.version = version = 6;
|
||||
}
|
||||
/*}).nThen(function (waitFor) {
|
||||
// Test progress bar in the loading screen
|
||||
var i = 0;
|
||||
var w = waitFor();
|
||||
var it = setInterval(function () {
|
||||
i += 5;
|
||||
if (i >= 100) { w(); clearInterval(it); i = 100;}
|
||||
progress(0, i);
|
||||
}, 500);
|
||||
progress(0, 0);*/
|
||||
}).nThen(function () {
|
||||
cb();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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 () {};
|
||||
@@ -66,8 +68,9 @@ define([
|
||||
var userHash = storeHash;
|
||||
if (!userHash) { return null; }
|
||||
|
||||
var userParsedHash = Hash.parseTypeHash('drive', userHash);
|
||||
var userChannel = userParsedHash && userParsedHash.channel;
|
||||
// No password for drive
|
||||
var secret = Hash.getSecrets('drive', userHash);
|
||||
var userChannel = secret.channel;
|
||||
if (!userChannel) { return null; }
|
||||
|
||||
// Get the list of pads' channel ID in your drive
|
||||
@@ -79,16 +82,16 @@ define([
|
||||
var d = store.userObject.getFileData(id);
|
||||
if (d.owners && d.owners.length && edPublic &&
|
||||
d.owners.indexOf(edPublic) === -1) { return; }
|
||||
return Hash.hrefToHexChannelId(d.href);
|
||||
return d.channel;
|
||||
})
|
||||
.filter(function (x) { return x; });
|
||||
|
||||
// Get the avatar
|
||||
var profile = store.proxy.profile;
|
||||
if (profile) {
|
||||
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit) : null;
|
||||
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
|
||||
if (profileChan) { list.push(profileChan); }
|
||||
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar) : null;
|
||||
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar, null) : null;
|
||||
if (avatarChan) { list.push(avatarChan); }
|
||||
}
|
||||
|
||||
@@ -97,7 +100,7 @@ define([
|
||||
list = list.concat(fList);
|
||||
}
|
||||
|
||||
list.push(Util.base64ToHex(userChannel));
|
||||
list.push(userChannel);
|
||||
list.sort();
|
||||
|
||||
return list;
|
||||
@@ -113,7 +116,7 @@ define([
|
||||
// because of the expiration time
|
||||
if ((data.owners && data.owners.length && data.owners.indexOf(edPublic) === -1) ||
|
||||
(data.expire && data.expire < (+new Date()))) {
|
||||
list.push(Hash.hrefToHexChannelId(data.href));
|
||||
list.push(data.channel);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
@@ -301,7 +304,7 @@ define([
|
||||
Store.getFileSize = function (data, cb) {
|
||||
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
|
||||
|
||||
var channelId = Hash.hrefToHexChannelId(data.href);
|
||||
var channelId = Hash.hrefToHexChannelId(data.href, data.password);
|
||||
store.anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) {
|
||||
if (e) { return void cb({error: e}); }
|
||||
if (response && response.length && typeof(response[0]) === 'number') {
|
||||
@@ -314,7 +317,7 @@ define([
|
||||
|
||||
Store.isNewChannel = function (data, cb) {
|
||||
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
|
||||
var channelId = Hash.hrefToHexChannelId(data.href);
|
||||
var channelId = Hash.hrefToHexChannelId(data.href, data.password);
|
||||
store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) {
|
||||
if (e) { return void cb({error: e}); }
|
||||
if (response && response.length && typeof(response[0]) === 'boolean') {
|
||||
@@ -378,6 +381,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: {
|
||||
@@ -392,7 +396,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)));
|
||||
@@ -413,6 +417,8 @@ define([
|
||||
var pad = makePad(data.href, data.title);
|
||||
if (data.owners) { pad.owners = data.owners; }
|
||||
if (data.expire) { pad.expire = data.expire; }
|
||||
if (data.password) { pad.password = data.password; }
|
||||
if (data.channel) { pad.channel = data.channel; }
|
||||
store.userObject.pushData(pad, function (e, id) {
|
||||
if (e) { return void cb({error: "Error while adding a template:"+ e}); }
|
||||
var path = data.path || ['root'];
|
||||
@@ -421,20 +427,98 @@ 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(data.channel);
|
||||
}
|
||||
});
|
||||
if (store.proxy.todo) {
|
||||
// No password for todo
|
||||
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
|
||||
}
|
||||
if (store.proxy.profile && store.proxy.profile.edit) {
|
||||
// No password for profile
|
||||
list.push(Hash.hrefToHexChannelId('/profile/#' + store.proxy.profile.edit, null));
|
||||
}
|
||||
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;
|
||||
// No password for drive
|
||||
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(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) {
|
||||
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(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,
|
||||
toSign: JSON.parse(Sortify(toSign))
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -446,25 +530,19 @@ define([
|
||||
*/
|
||||
Store.createReadme = function (data, cb) {
|
||||
require(['/common/cryptget.js'], function (Crypt) {
|
||||
var hash = Hash.createRandomHash();
|
||||
var hash = Hash.createRandomHash('pad');
|
||||
Crypt.put(hash, data.driveReadme, function (e) {
|
||||
if (e) {
|
||||
return void cb({ error: "Error while creating the default pad:"+ e});
|
||||
}
|
||||
var href = '/pad/#' + hash;
|
||||
var channel = Hash.hrefToHexChannelId(href, null);
|
||||
var fileData = {
|
||||
href: href,
|
||||
channel: channel,
|
||||
title: data.driveReadmeTitle,
|
||||
atime: +new Date(),
|
||||
ctime: +new Date()
|
||||
};
|
||||
store.userObject.pushData(fileData, function (e, id) {
|
||||
if (e) {
|
||||
return void cb({ error: "Error while creating the default pad:"+ e});
|
||||
}
|
||||
store.userObject.add(id);
|
||||
onSync(cb);
|
||||
});
|
||||
Store.addPad(fileData, cb);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -515,8 +593,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 () {
|
||||
store.proxy.drive = store.fo.getStructure();
|
||||
onSync(cb);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -554,18 +636,7 @@ define([
|
||||
|
||||
// Tags
|
||||
Store.listAllTags = function (data, cb) {
|
||||
var all = [];
|
||||
var files = Util.find(store.proxy, ['drive', 'filesData']);
|
||||
|
||||
if (typeof(files) !== 'object') { return cb({error: 'invalid_drive'}); }
|
||||
Object.keys(files).forEach(function (k) {
|
||||
var file = files[k];
|
||||
if (!Array.isArray(file.tags)) { return; }
|
||||
file.tags.forEach(function (tag) {
|
||||
if (all.indexOf(tag) === -1) { all.push(tag); }
|
||||
});
|
||||
});
|
||||
cb(all);
|
||||
cb(store.userObject.getTagsList());
|
||||
};
|
||||
|
||||
// Templates
|
||||
@@ -578,6 +649,15 @@ define([
|
||||
});
|
||||
cb(res);
|
||||
};
|
||||
Store.incrementTemplateUse = function (href) {
|
||||
store.userObject.getPadAttribute(href, 'used', function (err, data) {
|
||||
// This is a not critical function, abort in case of error to make sure we won't
|
||||
// create any issue with the user object or the async store
|
||||
if (err) { return; }
|
||||
var used = typeof data === "number" ? ++data : 1;
|
||||
store.userObject.setPadAttribute(href, 'used', used);
|
||||
});
|
||||
};
|
||||
|
||||
// Pads
|
||||
Store.moveToTrash = function (data, cb) {
|
||||
@@ -588,17 +668,18 @@ define([
|
||||
Store.setPadTitle = function (data, cb) {
|
||||
var title = data.title;
|
||||
var href = data.href;
|
||||
var channel = data.channel;
|
||||
var p = Hash.parsePadUrl(href);
|
||||
var h = p.hashData;
|
||||
|
||||
if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); }
|
||||
|
||||
var owners;
|
||||
if (Store.channel && Store.channel.wc && Util.base64ToHex(h.channel) === Store.channel.wc.id) {
|
||||
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
|
||||
owners = Store.channel.data.owners || undefined;
|
||||
}
|
||||
var expire;
|
||||
if (Store.channel && Store.channel.wc && Util.base64ToHex(h.channel) === Store.channel.wc.id) {
|
||||
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
|
||||
expire = +Store.channel.data.expire || undefined;
|
||||
}
|
||||
|
||||
@@ -621,13 +702,13 @@ define([
|
||||
// Different types, proceed to the next one
|
||||
// No hash data: corrupted pad?
|
||||
if (p.type !== p2.type || !h2) { continue; }
|
||||
// Different channel: continue
|
||||
if (pad.channel !== channel) { continue; }
|
||||
|
||||
var shouldUpdate = p.hash.replace(/\/$/, '') === p2.hash.replace(/\/$/, '');
|
||||
|
||||
// If the hash is different but represents the same channel, check if weaker or stronger
|
||||
if (!shouldUpdate &&
|
||||
h.version === 1 && h2.version === 1 &&
|
||||
h.channel === h2.channel) {
|
||||
if (!shouldUpdate && h.version !== 0) {
|
||||
// We had view & now we have edit, update
|
||||
if (h2.mode === 'view' && h.mode === 'edit') { shouldUpdate = true; }
|
||||
// Same mode and we had present URL, update
|
||||
@@ -664,10 +745,12 @@ define([
|
||||
if (!contains) {
|
||||
Store.addPad({
|
||||
href: href,
|
||||
channel: channel,
|
||||
title: title,
|
||||
owners: owners,
|
||||
expire: expire,
|
||||
path: data.path || (store.data && store.data.initialPath)
|
||||
password: data.password,
|
||||
path: data.path
|
||||
}, cb);
|
||||
return;
|
||||
}
|
||||
@@ -707,10 +790,7 @@ define([
|
||||
Store.getPadData = function (id, cb) {
|
||||
cb(store.userObject.getFileData(id));
|
||||
};
|
||||
Store.setInitialPath = function (path) {
|
||||
if (!store.data) { return; }
|
||||
store.data.initialPath = path;
|
||||
};
|
||||
|
||||
|
||||
// Messaging (manage friends from the userlist)
|
||||
var getMessagingCfg = function () {
|
||||
@@ -742,9 +822,9 @@ define([
|
||||
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
|
||||
|
||||
// If we have a stronger version in drive, add it and add a redirect button
|
||||
var stronger = Hash.findStronger(data.href, allPads);
|
||||
var stronger = Hash.findStronger(data.href, data.channel, allPads);
|
||||
if (stronger) {
|
||||
var parsed2 = Hash.parsePadUrl(stronger);
|
||||
var parsed2 = Hash.parsePadUrl(stronger.href);
|
||||
return void cb(parsed2.hash);
|
||||
}
|
||||
cb();
|
||||
@@ -836,8 +916,12 @@ define([
|
||||
channel.data = padData || {};
|
||||
postMessage("PAD_READY");
|
||||
}, // post EV_PAD_READY
|
||||
onMessage: function (m) {
|
||||
postMessage("PAD_MESSAGE", m);
|
||||
onMessage: function (user, m, validateKey) {
|
||||
postMessage("PAD_MESSAGE", {
|
||||
user: user,
|
||||
msg: m,
|
||||
validateKey: validateKey
|
||||
});
|
||||
}, // post EV_PAD_MESSAGE
|
||||
onJoin: function (m) {
|
||||
postMessage("PAD_JOIN", m);
|
||||
@@ -906,7 +990,7 @@ define([
|
||||
if (parsed[1][3] !== data.channel) { return; }
|
||||
msg = parsed[1][4];
|
||||
if (msg) {
|
||||
msg = msg.replace(/^cp\|/, '');
|
||||
msg = msg.replace(/cp\|(([A-Za-z0-9+\/=]+)\|)?/, '');
|
||||
//var decryptedMsg = crypto.decrypt(msg, true);
|
||||
msgs.push(msg);
|
||||
}
|
||||
@@ -956,11 +1040,24 @@ define([
|
||||
postMessage("DRIVE_LOG", msg);
|
||||
}
|
||||
});
|
||||
var todo = function () {
|
||||
nThen(function (waitFor) {
|
||||
postMessage('LOADING_DRIVE', {
|
||||
state: 2
|
||||
});
|
||||
userObject.migrate(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
Migrate(proxy, waitFor(), function (version, progress) {
|
||||
postMessage('LOADING_DRIVE', {
|
||||
state: 2,
|
||||
progress: progress
|
||||
});
|
||||
});
|
||||
}).nThen(function () {
|
||||
postMessage('LOADING_DRIVE', {
|
||||
state: 3
|
||||
});
|
||||
userObject.fixFiles();
|
||||
|
||||
Migrate(proxy);
|
||||
|
||||
var requestLogin = function () {
|
||||
postMessage("REQUEST_LOGIN");
|
||||
};
|
||||
@@ -1023,16 +1120,16 @@ define([
|
||||
proxy.on('change', [Constants.tokenKey], function () {
|
||||
postMessage("UPDATE_TOKEN", { token: proxy[Constants.tokenKey] });
|
||||
});
|
||||
};
|
||||
userObject.migrate(todo);
|
||||
});
|
||||
};
|
||||
|
||||
var connect = function (data, cb) {
|
||||
var hash = data.userHash || data.anonHash || Hash.createRandomHash();
|
||||
var hash = data.userHash || data.anonHash || Hash.createRandomHash('drive');
|
||||
storeHash = hash;
|
||||
if (!hash) {
|
||||
throw new Error('[Store.init] Unable to find or create a drive hash. Aborting...');
|
||||
}
|
||||
// No password for drive
|
||||
var secret = Hash.getSecrets('drive', hash);
|
||||
var listmapConfig = {
|
||||
data: {},
|
||||
@@ -1055,7 +1152,7 @@ define([
|
||||
store.realtime = info.realtime;
|
||||
store.network = info.network;
|
||||
if (!data.userHash) {
|
||||
returned.anonHash = Hash.getEditHashFromKeys(info.channel, secret.keys);
|
||||
returned.anonHash = Hash.getEditHashFromKeys(secret);
|
||||
}
|
||||
}).on('ready', function () {
|
||||
if (store.userObject) { return; } // the store is already ready, it is a reconnection
|
||||
@@ -1066,6 +1163,7 @@ define([
|
||||
&& !drive['filesData']) {
|
||||
drive[Constants.oldStorageKey] = [];
|
||||
}
|
||||
postMessage('LOADING_DRIVE', { state: 1 });
|
||||
// Drive already exist: return the existing drive, don't load data from legacy store
|
||||
onReady(returned, cb);
|
||||
})
|
||||
|
||||
@@ -68,7 +68,7 @@ define([], function () {
|
||||
|
||||
// shim between chainpad and netflux
|
||||
var msgIn = function (peerId, msg) {
|
||||
return msg.replace(/^cp\|/, '');
|
||||
return msg.replace(/^cp\|([A-Za-z0-9+\/=]+\|)?/, '');
|
||||
};
|
||||
|
||||
var msgOut = function (msg) {
|
||||
@@ -130,7 +130,7 @@ define([], function () {
|
||||
message = unBencode(message);//.slice(message.indexOf(':[') + 1);
|
||||
|
||||
// pass the message into Chainpad
|
||||
onMessage(message);
|
||||
onMessage(peer, message, validateKey);
|
||||
//sframeChan.query('Q_RT_MESSAGE', message, function () { });
|
||||
};
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ define([
|
||||
});
|
||||
};
|
||||
var logoutHandlers = [];
|
||||
LocalStore.logout = function (cb) {
|
||||
LocalStore.logout = function (cb, isDeletion) {
|
||||
[
|
||||
Constants.userNameKey,
|
||||
Constants.userHashKey,
|
||||
@@ -108,13 +108,15 @@ define([
|
||||
// Make sure we have an FS_hash in localStorage before reloading all the tabs
|
||||
// so that we don't end up with tabs using different anon hashes
|
||||
if (!LocalStore.getFSHash()) {
|
||||
LocalStore.setFSHash(Hash.createRandomHash());
|
||||
LocalStore.setFSHash(Hash.createRandomHash('drive'));
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -120,12 +120,12 @@ define([
|
||||
case 'GET_PAD_DATA': {
|
||||
Store.getPadData(data, cb); break;
|
||||
}
|
||||
case 'SET_INITIAL_PATH': {
|
||||
Store.setInitialPath(data); break;
|
||||
}
|
||||
case 'GET_STRONGER_HASH': {
|
||||
Store.getStrongerHash(data, cb); break;
|
||||
}
|
||||
case 'INCREMENT_TEMPLATE_USE': {
|
||||
Store.incrementTemplateUse(data); break;
|
||||
}
|
||||
// Messaging
|
||||
case 'INVITE_FROM_USERLIST': {
|
||||
Store.inviteFromUserlist(data, cb); break;
|
||||
|
||||
@@ -51,14 +51,28 @@ define([
|
||||
|
||||
var b64Key = Nacl.util.encodeBase64(key);
|
||||
|
||||
var hash = Hash.getFileHashFromKeys(id, b64Key);
|
||||
var secret = {
|
||||
version: 1,
|
||||
channel: id,
|
||||
keys: {
|
||||
fileKeyStr: b64Key
|
||||
}
|
||||
};
|
||||
var hash = Hash.getFileHashFromKeys(secret);
|
||||
var href = '/file/#' + hash;
|
||||
|
||||
var title = metadata.name;
|
||||
|
||||
if (noStore) { return void onComplete(href); }
|
||||
|
||||
common.setPadTitle(title || "", href, path, function (err) {
|
||||
// PASSWORD_FILES
|
||||
var data = {
|
||||
title: title || "",
|
||||
href: href,
|
||||
path: path,
|
||||
channel: id
|
||||
};
|
||||
common.setPadTitle(data, function (err) {
|
||||
if (err) { return void console.error(err); }
|
||||
onComplete(href);
|
||||
common.setPadAttribute('fileType', metadata.type, null, href);
|
||||
|
||||
@@ -75,7 +75,7 @@ define([
|
||||
return void todo();
|
||||
}
|
||||
if (!pinPads) { return; }
|
||||
pinPads([Hash.hrefToHexChannelId(data.href)], function (obj) {
|
||||
pinPads([data.channel], function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
todo();
|
||||
});
|
||||
@@ -98,7 +98,7 @@ define([
|
||||
exp.getFiles([FILES_DATA]).forEach(function (id) {
|
||||
if (filesList.indexOf(id) === -1) {
|
||||
var fd = exp.getFileData(id);
|
||||
var channelId = fd && fd.href && Hash.hrefToHexChannelId(fd.href);
|
||||
var channelId = fd.channel;
|
||||
// If trying to remove an owned pad, remove it from server also
|
||||
if (!isOwnPadRemoved &&
|
||||
fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
|
||||
@@ -552,32 +552,52 @@ define([
|
||||
for (var id in fd) {
|
||||
id = Number(id);
|
||||
var el = fd[id];
|
||||
|
||||
// Clean corrupted data
|
||||
if (!el || typeof(el) !== "object") {
|
||||
debug("An element in filesData was not an object.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
// Clean missing href
|
||||
if (!el.href) {
|
||||
debug("Removing an element in filesData with a missing href.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
|
||||
if (!el.ctime) { el.ctime = el.atime; }
|
||||
|
||||
var parsed = Hash.parsePadUrl(el.href);
|
||||
if (!el.title) { el.title = Hash.getDefaultName(parsed); }
|
||||
// Clean invalid hash
|
||||
if (!parsed.hash) {
|
||||
debug("Removing an element in filesData with a invalid href.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
// Clean invalid type
|
||||
if (!parsed.type) {
|
||||
debug("Removing an element in filesData with a invalid type.", el);
|
||||
toClean.push(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fix href
|
||||
if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
|
||||
// Fix creation time
|
||||
if (!el.ctime) { el.ctime = el.atime; }
|
||||
// Fix title
|
||||
if (!el.title) { el.title = Hash.getDefaultName(parsed); }
|
||||
// Fix channel
|
||||
if (!el.channel) {
|
||||
if (parsed.hashData && parsed.hashData.type === "file") {
|
||||
// PASSWORD_FILES
|
||||
el.channel = Util.base64ToHex(parsed.hashData.channel);
|
||||
} else {
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
||||
el.channel = secret.channel;
|
||||
}
|
||||
console.log('Adding missing channel in filesData ', el.channel);
|
||||
}
|
||||
|
||||
if ((loggedIn || config.testMode) && rootFiles.indexOf(id) === -1) {
|
||||
debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el);
|
||||
var newName = Hash.createChannelId();
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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 (
|
||||
$,
|
||||
@@ -279,6 +279,8 @@ define([
|
||||
var newContentStr = cpNfInner.chainpad.getUserDoc();
|
||||
if (state === STATE.DELETED) { return; }
|
||||
|
||||
//UI.updateLoadingProgress({ state: -1 }, false);
|
||||
|
||||
var newPad = false;
|
||||
if (newContentStr === '') { newPad = true; }
|
||||
|
||||
@@ -315,8 +317,7 @@ define([
|
||||
privateDat.availableHashes.viewHash;
|
||||
var href = privateDat.pathname + '#' + hash;
|
||||
if (AppConfig.textAnalyzer && textContentGetter) {
|
||||
var channelId = Hash.hrefToHexChannelId(href);
|
||||
AppConfig.textAnalyzer(textContentGetter, channelId);
|
||||
AppConfig.textAnalyzer(textContentGetter, privateDat.channel);
|
||||
}
|
||||
|
||||
if (options.thumbnail && privateDat.thumbnails) {
|
||||
@@ -432,6 +433,9 @@ define([
|
||||
nThen(function (waitFor) {
|
||||
UI.addLoadingScreen();
|
||||
SFCommon.create(waitFor(function (c) { common = c; }));
|
||||
/*UI.updateLoadingProgress({
|
||||
state: 1
|
||||
}, false);*/
|
||||
}).nThen(function (waitFor) {
|
||||
common.getSframeChannel().onReady(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
@@ -443,7 +447,7 @@ define([
|
||||
patchTransformer: options.patchTransformer || ChainPad.SmartJSONTransformer,
|
||||
|
||||
// cryptpad debug logging (default is 1)
|
||||
// logLevel: 2,
|
||||
logLevel: 1,
|
||||
validateContent: options.validateContent || function (content) {
|
||||
try {
|
||||
JSON.parse(content);
|
||||
@@ -457,7 +461,12 @@ define([
|
||||
},
|
||||
onRemote: onRemote,
|
||||
onLocal: onLocal,
|
||||
onInit: function () { stateChange(STATE.INITIALIZING); },
|
||||
onInit: function () {
|
||||
/*UI.updateLoadingProgress({
|
||||
state: 2
|
||||
}, false);*/
|
||||
stateChange(STATE.INITIALIZING);
|
||||
},
|
||||
onReady: function () { evStart.reg(onReady); },
|
||||
onConnectionChange: onConnectionChange,
|
||||
onError: onError
|
||||
@@ -572,6 +581,9 @@ define([
|
||||
toolbar.$rightside.append($templateButton);
|
||||
}
|
||||
|
||||
var $importTemplateButton = common.createButton('importtemplate', true);
|
||||
toolbar.$drawer.append($importTemplateButton);
|
||||
|
||||
/* add a forget button */
|
||||
toolbar.$rightside.append(common.createButton('forget', true, {}, function (err) {
|
||||
if (err) { return; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -39,9 +39,11 @@ define([], function () {
|
||||
});
|
||||
|
||||
// shim between chainpad and netflux
|
||||
var msgIn = function (msg) {
|
||||
var msgIn = function (peer, msg) {
|
||||
try {
|
||||
var decryptedMsg = Crypto.decrypt(msg, isNewHash);
|
||||
var isHk = peer.length !== 32;
|
||||
var key = isNewHash ? validateKey : false;
|
||||
var decryptedMsg = Crypto.decrypt(msg, key, isHk);
|
||||
return decryptedMsg;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
@@ -53,7 +55,16 @@ define([], function () {
|
||||
if (readOnly) { return; }
|
||||
try {
|
||||
var cmsg = Crypto.encrypt(msg);
|
||||
if (msg.indexOf('[4') === 0) { cmsg = 'cp|' + cmsg; }
|
||||
if (msg.indexOf('[4') === 0) {
|
||||
var id = '';
|
||||
if (window.nacl) {
|
||||
var hash = window.nacl.hash(window.nacl.util.decodeUTF8(msg));
|
||||
id = window.nacl.util.encodeBase64(hash.slice(0, 8)) + '|';
|
||||
} else {
|
||||
console.log("Checkpoint sent without an ID. Nacl is missing.");
|
||||
}
|
||||
cmsg = 'cp|' + id + cmsg;
|
||||
}
|
||||
return cmsg;
|
||||
} catch (err) {
|
||||
console.log(msg);
|
||||
@@ -67,8 +78,11 @@ define([], function () {
|
||||
padRpc.sendPadMsg(msg, cb);
|
||||
});
|
||||
|
||||
var onMessage = function(msg) {
|
||||
var message = msgIn(msg);
|
||||
var onMessage = function(msgObj) {
|
||||
if (msgObj.validateKey && !validateKey) {
|
||||
validateKey = msgObj.validateKey;
|
||||
}
|
||||
var message = msgIn(msgObj.user, msgObj.msg);
|
||||
|
||||
verbose(message);
|
||||
|
||||
|
||||
@@ -332,6 +332,7 @@ define([
|
||||
//var cursor = editor.getCursor();
|
||||
//var cleanName = data.name.replace(/[\[\]]/g, '');
|
||||
//var text = '';
|
||||
// PASSWORD_FILES
|
||||
var parsed = Hash.parsePadUrl(data.url);
|
||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||
|
||||
@@ -333,6 +333,8 @@ define([
|
||||
var editor = config.ckeditor;
|
||||
editor.document.on('drop', function (ev) {
|
||||
var dropped = ev.data.$.dataTransfer.files;
|
||||
editor.document.focus();
|
||||
if (!dropped || !dropped.length) { return; }
|
||||
onFileDrop(dropped, ev);
|
||||
ev.data.preventDefault(true);
|
||||
});
|
||||
|
||||
@@ -24,6 +24,8 @@ define([
|
||||
var Utils = {};
|
||||
var AppConfig;
|
||||
var Test;
|
||||
var password;
|
||||
var initialPathInDrive;
|
||||
|
||||
nThen(function (waitFor) {
|
||||
// Load #2, the loading screen is up so grab whatever you need...
|
||||
@@ -88,7 +90,16 @@ define([
|
||||
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
|
||||
sframeChan = sfc;
|
||||
}), false, { cache: cache, localStore: localStore, language: Cryptpad.getLanguage() });
|
||||
Cryptpad.ready(waitFor(), {
|
||||
Cryptpad.loading.onDriveEvent.reg(function (data) {
|
||||
if (sframeChan) { sframeChan.event('EV_LOADING_INFO', data); }
|
||||
});
|
||||
Cryptpad.ready(waitFor(function () {
|
||||
if (sframeChan) {
|
||||
sframeChan.event('EV_LOADING_INFO', {
|
||||
state: -1
|
||||
});
|
||||
}
|
||||
}), {
|
||||
messenger: cfg.messaging,
|
||||
driveEvents: cfg.driveEvents
|
||||
});
|
||||
@@ -113,6 +124,7 @@ define([
|
||||
|
||||
if (cfg.getSecrets) {
|
||||
var w = waitFor();
|
||||
// No password for drive, profile and todo
|
||||
cfg.getSecrets(Cryptpad, Utils, waitFor(function (err, s) {
|
||||
secret = s;
|
||||
Cryptpad.getShareHashes(secret, function (err, h) {
|
||||
@@ -121,19 +133,54 @@ define([
|
||||
});
|
||||
}));
|
||||
} else {
|
||||
secret = Utils.Hash.getSecrets();
|
||||
if (!secret.channel) {
|
||||
// New pad: create a new random channel id
|
||||
secret.channel = Utils.Hash.createChannelId();
|
||||
var parsed = Utils.Hash.parsePadUrl(window.location.href);
|
||||
var todo = function () {
|
||||
secret = Utils.Hash.getSecrets(parsed.type, void 0, password);
|
||||
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
|
||||
};
|
||||
|
||||
// Prompt the password here if we have a hash containing /p/
|
||||
// or get it from the pad attributes
|
||||
var needPassword = parsed.hashData && parsed.hashData.password;
|
||||
if (needPassword) {
|
||||
Cryptpad.getPadAttribute('password', waitFor(function (err, val) {
|
||||
if (val) {
|
||||
// We already know the password, use it!
|
||||
password = val;
|
||||
todo();
|
||||
} else {
|
||||
// Ask for the password and check if the pad exists
|
||||
// If the pad doesn't exist, it means the password is oncorrect
|
||||
// or the pad has been deleted
|
||||
var correctPassword = waitFor();
|
||||
sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) {
|
||||
password = data;
|
||||
Cryptpad.isNewChannel(window.location.href, password, function (e, isNew) {
|
||||
if (Boolean(isNew)) {
|
||||
// Ask again in the inner iframe
|
||||
// We should receive a new Q_PAD_PASSWORD_VALUE
|
||||
cb(false);
|
||||
} else {
|
||||
todo();
|
||||
correctPassword();
|
||||
cb(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
sframeChan.event("EV_PAD_PASSWORD");
|
||||
}
|
||||
}), parsed.getUrl());
|
||||
return;
|
||||
}
|
||||
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
|
||||
// If no password, continue...
|
||||
todo();
|
||||
}
|
||||
}).nThen(function (waitFor) {
|
||||
// Check if the pad exists on server
|
||||
if (!window.location.hash) { isNewFile = true; return; }
|
||||
|
||||
if (realtime) {
|
||||
Cryptpad.isNewChannel(window.location.href, waitFor(function (e, isNew) {
|
||||
Cryptpad.isNewChannel(window.location.href, password, waitFor(function (e, isNew) {
|
||||
if (e) { return console.error(e); }
|
||||
isNewFile = Boolean(isNew);
|
||||
}));
|
||||
@@ -188,7 +235,9 @@ define([
|
||||
},
|
||||
isNewFile: isNewFile,
|
||||
isDeleted: isNewFile && window.location.hash.length > 0,
|
||||
forceCreationScreen: forceCreationScreen
|
||||
forceCreationScreen: forceCreationScreen,
|
||||
password: password,
|
||||
channel: secret.channel
|
||||
};
|
||||
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
|
||||
|
||||
@@ -256,7 +305,13 @@ define([
|
||||
sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) {
|
||||
currentTitle = newTitle;
|
||||
setDocumentTitle();
|
||||
Cryptpad.setPadTitle(newTitle, undefined, undefined, function (err) {
|
||||
var data = {
|
||||
password: password,
|
||||
title: newTitle,
|
||||
channel: secret.channel,
|
||||
path: initialPathInDrive // Where to store the pad if we don't have it in our drive
|
||||
};
|
||||
Cryptpad.setPadTitle(data, function (err) {
|
||||
cb(err);
|
||||
});
|
||||
});
|
||||
@@ -325,7 +380,9 @@ define([
|
||||
validateKey: secret.keys.validateKey
|
||||
}, function (encryptedMsgs) {
|
||||
cb(encryptedMsgs.map(function (msg) {
|
||||
return crypto.decrypt(msg, true);
|
||||
// The 3rd parameter "true" means we're going to skip signature validation.
|
||||
// We don't need it since the message is already validated serverside by hk
|
||||
return crypto.decrypt(msg, true, true);
|
||||
}));
|
||||
});
|
||||
});
|
||||
@@ -336,6 +393,7 @@ define([
|
||||
// If we have a stronger hash, use it for pad attributes
|
||||
href = window.location.pathname + '#' + hashes.editHash;
|
||||
}
|
||||
if (data.href) { href = data.href; }
|
||||
Cryptpad.getPadAttribute(data.key, function (e, data) {
|
||||
cb({
|
||||
error: e,
|
||||
@@ -349,6 +407,7 @@ define([
|
||||
// If we have a stronger hash, use it for pad attributes
|
||||
href = window.location.pathname + '#' + hashes.editHash;
|
||||
}
|
||||
if (data.href) { href = data.href; }
|
||||
Cryptpad.setPadAttribute(data.key, data.value, function (e) {
|
||||
cb({error:e});
|
||||
}, href);
|
||||
@@ -429,6 +488,8 @@ define([
|
||||
// File picker
|
||||
var FP = {};
|
||||
var initFilePicker = function (cfg) {
|
||||
// cfg.hidden means pre-loading the filepicker while keeping it hidden.
|
||||
// if cfg.hidden is true and the iframe already exists, do nothing
|
||||
if (!FP.$iframe) {
|
||||
var config = {};
|
||||
config.onFilePicked = function (data) {
|
||||
@@ -447,7 +508,7 @@ define([
|
||||
};
|
||||
FP.$iframe = $('<iframe>', {id: 'sbox-filePicker-iframe'}).appendTo($('body'));
|
||||
FP.picker = FilePicker.create(config);
|
||||
} else {
|
||||
} else if (!cfg.hidden) {
|
||||
FP.$iframe.show();
|
||||
FP.picker.refresh(cfg);
|
||||
}
|
||||
@@ -469,9 +530,9 @@ define([
|
||||
cb(templates.length > 0);
|
||||
});
|
||||
});
|
||||
var getKey = function (href) {
|
||||
var getKey = function (href, channel) {
|
||||
var parsed = Utils.Hash.parsePadUrl(href);
|
||||
return 'thumbnail-' + parsed.type + '-' + parsed.hashData.channel;
|
||||
return 'thumbnail-' + parsed.type + '-' + channel;
|
||||
};
|
||||
sframeChan.on('Q_CREATE_TEMPLATES', function (type, cb) {
|
||||
Cryptpad.getSecureFilesList({
|
||||
@@ -484,12 +545,13 @@ define([
|
||||
var res = [];
|
||||
nThen(function (waitFor) {
|
||||
Object.keys(data).map(function (el) {
|
||||
var k = getKey(data[el].href);
|
||||
var k = getKey(data[el].href, data[el].channel);
|
||||
Utils.LocalStore.getThumbnail(k, waitFor(function (e, thumb) {
|
||||
res.push({
|
||||
id: el,
|
||||
name: data[el].filename || data[el].title || '?',
|
||||
thumbnail: thumb
|
||||
thumbnail: thumb,
|
||||
used: data[el].used || 0
|
||||
});
|
||||
}));
|
||||
});
|
||||
@@ -513,19 +575,6 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
sframeChan.on('Q_TAGS_GET', function (data, cb) {
|
||||
Cryptpad.getPadTags(data, function (err, data) {
|
||||
cb({
|
||||
error: err,
|
||||
data: data
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sframeChan.on('EV_TAGS_SET', function (data) {
|
||||
Cryptpad.resetTags(data.href, data.tags);
|
||||
});
|
||||
|
||||
sframeChan.on('Q_PIN_GET_USAGE', function (data, cb) {
|
||||
Cryptpad.isOverPinLimit(function (err, overLimit, data) {
|
||||
cb({
|
||||
@@ -546,6 +595,15 @@ define([
|
||||
Cryptpad.removeOwnedChannel(channel, cb);
|
||||
});
|
||||
|
||||
sframeChan.on('Q_GET_ALL_TAGS', function (data, cb) {
|
||||
Cryptpad.listAllTags(function (err, tags) {
|
||||
cb({
|
||||
error: err,
|
||||
tags: tags
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (cfg.addRpc) {
|
||||
cfg.addRpc(sframeChan, Cryptpad, Utils);
|
||||
}
|
||||
@@ -630,7 +688,7 @@ define([
|
||||
isNewHash: isNewHash,
|
||||
readOnly: readOnly,
|
||||
crypto: Crypto.createEncryptor(secret.keys),
|
||||
onConnect: function (wc) {
|
||||
onConnect: function () {
|
||||
if (window.location.hash && window.location.hash !== '#') {
|
||||
window.location = parsed.getUrl({
|
||||
present: parsed.hashData.present,
|
||||
@@ -639,20 +697,36 @@ define([
|
||||
return;
|
||||
}
|
||||
if (readOnly || cfg.noHash) { return; }
|
||||
replaceHash(Utils.Hash.getEditHashFromKeys(wc, secret.keys));
|
||||
replaceHash(Utils.Hash.getEditHashFromKeys(secret));
|
||||
}
|
||||
};
|
||||
Object.keys(rtConfig).forEach(function (k) {
|
||||
cpNfCfg[k] = rtConfig[k];
|
||||
|
||||
nThen(function (waitFor) {
|
||||
if (isNewFile && cfg.owned && !window.location.hash) {
|
||||
Cryptpad.getMetadata(waitFor(function (err, m) {
|
||||
cpNfCfg.owners = [m.priv.edPublic];
|
||||
}));
|
||||
} else if (isNewFile && !cfg.useCreationScreen && window.location.hash) {
|
||||
console.log("new file with hash in the address bar in an app without pcs and which requires owners");
|
||||
sframeChan.onReady(function () {
|
||||
sframeChan.query("EV_LOADING_ERROR", "DELETED");
|
||||
});
|
||||
waitFor.abort();
|
||||
}
|
||||
}).nThen(function () {
|
||||
Object.keys(rtConfig).forEach(function (k) {
|
||||
cpNfCfg[k] = rtConfig[k];
|
||||
});
|
||||
CpNfOuter.start(cpNfCfg);
|
||||
});
|
||||
CpNfOuter.start(cpNfCfg);
|
||||
};
|
||||
|
||||
sframeChan.on('Q_CREATE_PAD', function (data, cb) {
|
||||
if (!isNewFile || rtStarted) { return; }
|
||||
// Create a new hash
|
||||
var newHash = Utils.Hash.createRandomHash();
|
||||
secret = Utils.Hash.getSecrets(parsed.type, newHash);
|
||||
password = data.password;
|
||||
var newHash = Utils.Hash.createRandomHash(parsed.type, password);
|
||||
secret = Utils.Hash.getSecrets(parsed.type, newHash, password);
|
||||
|
||||
// Update the hash in the address bar
|
||||
var ohc = window.onhashchange;
|
||||
@@ -664,7 +738,7 @@ define([
|
||||
// Update metadata values and send new metadata inside
|
||||
parsed = Utils.Hash.parsePadUrl(window.location.href);
|
||||
defaultTitle = Utils.Hash.getDefaultName(parsed);
|
||||
hashes = Utils.Hash.getHashes(secret.channel, secret);
|
||||
hashes = Utils.Hash.getHashes(secret);
|
||||
readOnly = false;
|
||||
updateMeta();
|
||||
|
||||
@@ -678,7 +752,7 @@ define([
|
||||
nThen(function(waitFor) {
|
||||
if (data.templateId) {
|
||||
if (data.templateId === -1) {
|
||||
Cryptpad.setInitialPath(['template']);
|
||||
initialPathInDrive = ['template'];
|
||||
return;
|
||||
}
|
||||
Cryptpad.getPadData(data.templateId, waitFor(function (err, d) {
|
||||
@@ -706,8 +780,8 @@ define([
|
||||
|
||||
Utils.Feedback.reportAppUsage();
|
||||
|
||||
if (!realtime) { return; }
|
||||
if (isNewFile && cfg.useCreationScreen) { return; }
|
||||
if (!realtime && !Test.testing) { return; }
|
||||
if (isNewFile && cfg.useCreationScreen && !Test.testing) { return; }
|
||||
//if (isNewFile && Utils.LocalStore.isLoggedIn()
|
||||
// && AppConfig.displayCreationScreen && cfg.useCreationScreen) { return; }
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ define([
|
||||
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
|
||||
};
|
||||
funcs.getMediatagFromHref = function (href) {
|
||||
// PASSWORD_FILES
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var secret = Hash.getSecrets('file', parsed.hash);
|
||||
var data = ctx.metadataMgr.getPrivateData();
|
||||
@@ -126,8 +127,7 @@ define([
|
||||
}
|
||||
return;
|
||||
};
|
||||
funcs.getFileSize = function (href, cb) {
|
||||
var channelId = Hash.hrefToHexChannelId(href);
|
||||
funcs.getFileSize = function (channelId, cb) {
|
||||
funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) {
|
||||
if (!data) { return void cb("No response"); }
|
||||
if (data.error) { return void cb(data.error); }
|
||||
@@ -170,6 +170,7 @@ define([
|
||||
|
||||
// Store
|
||||
funcs.handleNewFile = function (waitFor) {
|
||||
if (window.__CRYPTPAD_TEST__) { return; }
|
||||
var priv = ctx.metadataMgr.getPrivateData();
|
||||
if (priv.isNewFile) {
|
||||
var c = (priv.settings.general && priv.settings.general.creation) || {};
|
||||
@@ -196,6 +197,7 @@ define([
|
||||
ctx.sframeChan.query("Q_CREATE_PAD", {
|
||||
owned: cfg.owned,
|
||||
expire: cfg.expire,
|
||||
password: cfg.password,
|
||||
template: cfg.template,
|
||||
templateId: cfg.templateId
|
||||
}, cb);
|
||||
@@ -233,17 +235,20 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
funcs.getPadAttribute = function (key, cb) {
|
||||
// href is optional here: if not provided, we use the href of the current tab
|
||||
funcs.getPadAttribute = function (key, cb, href) {
|
||||
ctx.sframeChan.query('Q_GET_PAD_ATTRIBUTE', {
|
||||
key: key
|
||||
key: key,
|
||||
href: href
|
||||
}, function (err, res) {
|
||||
cb (err || res.error, res.data);
|
||||
});
|
||||
};
|
||||
funcs.setPadAttribute = function (key, value, cb) {
|
||||
funcs.setPadAttribute = function (key, value, cb, href) {
|
||||
cb = cb || $.noop;
|
||||
ctx.sframeChan.query('Q_SET_PAD_ATTRIBUTE', {
|
||||
key: key,
|
||||
href: href,
|
||||
value: value
|
||||
}, cb);
|
||||
};
|
||||
@@ -343,6 +348,27 @@ define([
|
||||
window.open(bounceHref);
|
||||
};
|
||||
|
||||
funcs.fixLinks = function (domElement) {
|
||||
var origin = ctx.metadataMgr.getPrivateData().origin;
|
||||
$(domElement).find('a[target="_blank"]').click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var href = $(this).attr('href');
|
||||
var absolute = /^https?:\/\//i;
|
||||
if (!absolute.test(href)) {
|
||||
if (href.slice(0,1) !== '/') { href = '/' + href; }
|
||||
href = origin + href;
|
||||
}
|
||||
funcs.openUnsafeURL(href);
|
||||
});
|
||||
$(domElement).find('a[target!="_blank"]').click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
funcs.gotoURL($(this).attr('href'));
|
||||
});
|
||||
return $(domElement)[0];
|
||||
};
|
||||
|
||||
funcs.whenRealtimeSyncs = evRealtimeSynced.reg;
|
||||
|
||||
var logoutHandlers = [];
|
||||
@@ -397,19 +423,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);
|
||||
});
|
||||
@@ -419,12 +432,46 @@ define([
|
||||
UI.log(data.logText);
|
||||
});
|
||||
|
||||
ctx.sframeChan.on("EV_PAD_PASSWORD", function () {
|
||||
UIElements.displayPasswordPrompt(funcs);
|
||||
});
|
||||
|
||||
ctx.sframeChan.on('EV_LOADING_INFO', function (data) {
|
||||
UI.updateLoadingProgress(data, true);
|
||||
});
|
||||
|
||||
ctx.metadataMgr.onReady(waitFor());
|
||||
}).nThen(function () {
|
||||
try {
|
||||
var feedback = ctx.metadataMgr.getPrivateData().feedbackAllowed;
|
||||
Feedback.init(feedback);
|
||||
} catch (e) { Feedback.init(false); }
|
||||
|
||||
ctx.sframeChan.on('EV_LOADING_ERROR', function (err) {
|
||||
if (err === 'DELETED') {
|
||||
var msg = Messages.deletedError + '<br>' + Messages.errorRedirectToHome;
|
||||
UI.errorLoadingScreen(msg, false, function () {
|
||||
funcs.gotoURL('/drive/');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
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', ['<a href="' + href + '" target="_blank">', '</a>']);
|
||||
UI.errorLoadingScreen(onLogoutMsg, true);
|
||||
logoutHandlers.forEach(function (h) {
|
||||
if (typeof (h) === "function") { h(); }
|
||||
});
|
||||
});
|
||||
|
||||
ctx.sframeChan.ready();
|
||||
cb(funcs);
|
||||
});
|
||||
|
||||
@@ -165,10 +165,6 @@ define({
|
||||
// Put one entry in the parent sessionStorage
|
||||
'Q_SESSIONSTORAGE_PUT': true,
|
||||
|
||||
// Set and get the tags using the tag prompt button
|
||||
'Q_TAGS_GET': true,
|
||||
'EV_TAGS_SET': true,
|
||||
|
||||
// Merge the anonymous drive (FS_hash) into the current logged in user's drive, to keep the pads
|
||||
// in the drive at registration.
|
||||
'Q_MERGE_ANON_DRIVE': true,
|
||||
@@ -227,4 +223,17 @@ define({
|
||||
// This is for sending data out of the iframe when we are in testing mode
|
||||
// The exact protocol is defined in common/test.js
|
||||
'EV_TESTDATA': true,
|
||||
|
||||
// Critical error outside the iframe during loading screen
|
||||
'EV_LOADING_ERROR': true,
|
||||
|
||||
// Ask for the pad password when a pad is protected
|
||||
'EV_PAD_PASSWORD': true,
|
||||
'Q_PAD_PASSWORD_VALUE': true,
|
||||
|
||||
// Loading events to display in the loading screen
|
||||
'EV_LOADING_INFO': true,
|
||||
|
||||
// Get all existing tags
|
||||
'Q_GET_ALL_TAGS': true,
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
1
www/common/tippy.min.js
vendored
1
www/common/tippy.min.js
vendored
File diff suppressed because one or more lines are too long
1
www/common/tippy/tippy.css
Normal file
1
www/common/tippy/tippy.css
Normal file
File diff suppressed because one or more lines are too long
1
www/common/tippy/tippy.min.js
vendored
Normal file
1
www/common/tippy/tippy.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -576,9 +576,7 @@ define([
|
||||
if (Common.isLoggedIn()) { return; }
|
||||
var pd = config.metadataMgr.getPrivateData();
|
||||
var o = pd.origin;
|
||||
var hashes = pd.availableHashes;
|
||||
var url = pd.origin + pd.pathname + '#' + (hashes.editHash || hashes.viewHash);
|
||||
var cid = Hash.hrefToHexChannelId(url);
|
||||
var cid = pd.channel;
|
||||
Common.sendAnonRpcMsg('IS_CHANNEL_PINNED', cid, function (x) {
|
||||
if (x.error || !Array.isArray(x.response)) { return void console.log(x); }
|
||||
if (x.response[0] === true) {
|
||||
|
||||
@@ -384,11 +384,9 @@ define([
|
||||
// Get drive ids of files from their channel ids
|
||||
exp.findChannels = function (channels) {
|
||||
var allFilesList = files[FILES_DATA];
|
||||
var channels64 = channels.slice().map(Util.hexToBase64);
|
||||
return getFiles([FILES_DATA]).filter(function (k) {
|
||||
var data = allFilesList[k];
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
return parsed.hashData && channels64.indexOf(parsed.hashData.channel) !== -1;
|
||||
return channels.indexOf(data.channel) !== -1;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -629,6 +627,21 @@ define([
|
||||
if (typeof cb === "function") { cb(); }
|
||||
};
|
||||
|
||||
// Tags
|
||||
exp.getTagsList = function () {
|
||||
var tags = {};
|
||||
var data;
|
||||
var pushTag = function (tag) {
|
||||
tags[tag] = tags[tag] ? ++tags[tag] : 1;
|
||||
};
|
||||
for (var id in files[FILES_DATA]) {
|
||||
data = files[FILES_DATA][id];
|
||||
if (!data.tags || !Array.isArray(data.tags)) { continue; }
|
||||
data.tags.forEach(pushTag);
|
||||
}
|
||||
return tags;
|
||||
};
|
||||
|
||||
return exp;
|
||||
};
|
||||
return module;
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.toolbar_main(
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_friends-bg,
|
||||
@warn-color: @colortheme_friends-warn,
|
||||
@color: @colortheme_friends-color
|
||||
);
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
|
||||
// body
|
||||
&.cp-app-contacts {
|
||||
|
||||
@@ -11,7 +11,7 @@ define([
|
||||
'/common/common-interface.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 (
|
||||
$,
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/tools.less';
|
||||
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.toolbar_main();
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
.tokenfield_main();
|
||||
.framework_min_main();
|
||||
|
||||
// body
|
||||
&.cp-app-debug {
|
||||
|
||||
@@ -15,7 +15,7 @@ define([
|
||||
'/bower_components/secure-fabric.js/dist/fabric.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 (
|
||||
$,
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/leftside-menu.less';
|
||||
@import (once) "../../customize/src/less2/include/tools.less";
|
||||
@import (once) "../../customize/src/less2/include/limit-bar.less";
|
||||
@import (once) "../../customize/src/less2/include/tokenfield.less";
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.toolbar_main(
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_drive-bg,
|
||||
@warn-color: @colortheme_drive-warn,
|
||||
@color: @colortheme_drive-color
|
||||
);
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
|
||||
.limit-bar_main();
|
||||
.tokenfield_main();
|
||||
|
||||
@@ -465,6 +462,8 @@ span {
|
||||
padding-right: 15px;
|
||||
}
|
||||
.cp-app-drive-search-opendir {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
a {
|
||||
cursor: pointer;
|
||||
color: #41b7d8;
|
||||
@@ -498,6 +497,19 @@ span {
|
||||
}
|
||||
}
|
||||
}
|
||||
&.cp-app-drive-tags-list {
|
||||
width: 100%;
|
||||
table {
|
||||
margin: 10px 50px;
|
||||
width: ~"calc(100% - 100px)";
|
||||
table-layout: fixed;
|
||||
td, th {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-drive-element {
|
||||
@@ -547,6 +559,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;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ define([
|
||||
'/customize/messages.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 (
|
||||
$,
|
||||
@@ -51,20 +51,65 @@ define([
|
||||
|
||||
var E_OVER_LIMIT = 'E_OVER_LIMIT';
|
||||
|
||||
var SEARCH = "search";
|
||||
var SEARCH_NAME = Messages.fm_searchName;
|
||||
var ROOT = "root";
|
||||
var ROOT_NAME = Messages.fm_rootName;
|
||||
var SEARCH = "search";
|
||||
var SEARCH_NAME = Messages.fm_searchName;
|
||||
var TRASH = "trash";
|
||||
var TRASH_NAME = Messages.fm_trashName;
|
||||
var FILES_DATA = Constants.storageKey;
|
||||
var FILES_DATA_NAME = Messages.fm_filesDataName;
|
||||
var TEMPLATE = "template";
|
||||
var TEMPLATE_NAME = Messages.fm_templateName;
|
||||
var TRASH = "trash";
|
||||
var TRASH_NAME = Messages.fm_trashName;
|
||||
var RECENT = "recent";
|
||||
var RECENT_NAME = Messages.fm_recentPadsName;
|
||||
var OWNED = "owned";
|
||||
var OWNED_NAME = Messages.fm_ownedPadsName;
|
||||
var TAGS = "tags";
|
||||
var TAGS_NAME = Messages.fm_tagsName;
|
||||
|
||||
// Icons
|
||||
var faFolder = 'fa-folder';
|
||||
var faFolderOpen = 'fa-folder-open';
|
||||
var faReadOnly = 'fa-eye';
|
||||
var faRename = 'fa-pencil';
|
||||
var faTrash = 'fa-trash';
|
||||
var faDelete = 'fa-eraser';
|
||||
var faProperties = 'fa-database';
|
||||
var faTags = 'fa-hashtag';
|
||||
var faEmpty = 'fa-trash-o';
|
||||
var faRestore = 'fa-repeat';
|
||||
var faShowParent = 'fa-location-arrow';
|
||||
var $folderIcon = $('<span>', {
|
||||
"class": faFolder + " fa cp-app-drive-icon-folder cp-app-drive-content-icon"
|
||||
});
|
||||
//var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
|
||||
var $folderEmptyIcon = $folderIcon.clone();
|
||||
var $folderOpenedIcon = $('<span>', {"class": faFolderOpen + " fa cp-app-drive-icon-folder"});
|
||||
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
|
||||
var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
|
||||
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
|
||||
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
|
||||
var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
|
||||
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
|
||||
var $trashIcon = $('<span>', {"class": "fa " + faTrash});
|
||||
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
|
||||
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
|
||||
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
|
||||
var $emptyTrashIcon = $('<button>', {"class": "fa fa-ban"});
|
||||
var $listIcon = $('<button>', {"class": "fa fa-list"});
|
||||
var $gridIcon = $('<button>', {"class": "fa fa-th-large"});
|
||||
var $sortAscIcon = $('<span>', {"class": "fa fa-angle-up sortasc"});
|
||||
var $sortDescIcon = $('<span>', {"class": "fa fa-angle-down sortdesc"});
|
||||
var $closeIcon = $('<span>', {"class": "fa fa-window-close"});
|
||||
//var $backupIcon = $('<span>', {"class": "fa fa-life-ring"});
|
||||
var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-con"});
|
||||
var $addIcon = $('<span>', {"class": "fa fa-plus"});
|
||||
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
|
||||
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
|
||||
var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"});
|
||||
var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
|
||||
var $tagsIcon = $('<span>', {"class": "fa " + faTags});
|
||||
|
||||
var LS_LAST = "app-drive-lastOpened";
|
||||
var LS_OPENED = "app-drive-openedFolders";
|
||||
@@ -157,48 +202,6 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
// Icons
|
||||
var faFolder = 'fa-folder';
|
||||
var faFolderOpen = 'fa-folder-open';
|
||||
var faReadOnly = 'fa-eye';
|
||||
var faRename = 'fa-pencil';
|
||||
var faTrash = 'fa-trash';
|
||||
var faDelete = 'fa-eraser';
|
||||
var faProperties = 'fa-database';
|
||||
var faTags = 'fa-hashtag';
|
||||
var faEmpty = 'fa-trash-o';
|
||||
var faRestore = 'fa-repeat';
|
||||
var faShowParent = 'fa-location-arrow';
|
||||
var $folderIcon = $('<span>', {
|
||||
"class": faFolder + " fa cp-app-drive-icon-folder cp-app-drive-content-icon"
|
||||
});
|
||||
//var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
|
||||
var $folderEmptyIcon = $folderIcon.clone();
|
||||
var $folderOpenedIcon = $('<span>', {"class": faFolderOpen + " fa cp-app-drive-icon-folder"});
|
||||
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
|
||||
var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
|
||||
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
|
||||
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
|
||||
var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
|
||||
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
|
||||
var $trashIcon = $('<span>', {"class": "fa " + faTrash});
|
||||
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
|
||||
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
|
||||
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
|
||||
var $emptyTrashIcon = $('<button>', {"class": "fa fa-ban"});
|
||||
var $listIcon = $('<button>', {"class": "fa fa-list"});
|
||||
var $gridIcon = $('<button>', {"class": "fa fa-th-large"});
|
||||
var $sortAscIcon = $('<span>', {"class": "fa fa-angle-up sortasc"});
|
||||
var $sortDescIcon = $('<span>', {"class": "fa fa-angle-down sortdesc"});
|
||||
var $closeIcon = $('<span>', {"class": "fa fa-window-close"});
|
||||
//var $backupIcon = $('<span>', {"class": "fa fa-life-ring"});
|
||||
var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-con"});
|
||||
var $addIcon = $('<span>', {"class": "fa fa-plus"});
|
||||
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
|
||||
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
|
||||
var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"});
|
||||
var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
|
||||
|
||||
var history = {
|
||||
isHistoryMode: false,
|
||||
};
|
||||
@@ -360,17 +363,24 @@ define([
|
||||
// Categories dislayed in the menu
|
||||
// _WORKGROUP_ : do not display unsorted
|
||||
var displayedCategories = [ROOT, TRASH, SEARCH, RECENT];
|
||||
|
||||
// PCS enabled: display owned pads
|
||||
if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
|
||||
// Templates enabled: display template category
|
||||
if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); }
|
||||
// Tags used: display Tags category
|
||||
if (Object.keys(filesOp.getTagsList()).length) { displayedCategories.push(TAGS); }
|
||||
|
||||
if (isWorkgroup()) { displayedCategories = [ROOT, TRASH, SEARCH]; }
|
||||
var virtualCategories = [SEARCH, RECENT, OWNED];
|
||||
var virtualCategories = [SEARCH, RECENT, OWNED, TAGS];
|
||||
|
||||
if (!APP.loggedIn) {
|
||||
displayedCategories = [FILES_DATA];
|
||||
currentPath = [FILES_DATA];
|
||||
$tree.hide();
|
||||
if (Object.keys(files.root).length && !proxy.anonymousAlert) {
|
||||
UI.alert(Messages.fm_alert_anonymous, null, true);
|
||||
var msg = common.fixLinks($('<div>').html(Messages.fm_alert_anonymous));
|
||||
UI.alert(msg);
|
||||
proxy.anonymousAlert = true;
|
||||
}
|
||||
}
|
||||
@@ -1295,7 +1305,7 @@ define([
|
||||
$span.attr('title', name);
|
||||
|
||||
var type = Messages.type[hrefData.type] || hrefData.type;
|
||||
common.displayThumbnail(data.href, $span, function ($thumb) {
|
||||
common.displayThumbnail(data.href, data.channel, $span, function ($thumb) {
|
||||
// Called only if the thumbnail exists
|
||||
// Remove the .hide() added by displayThumnail() because it hides the icon in
|
||||
// list mode too
|
||||
@@ -1443,6 +1453,7 @@ define([
|
||||
case SEARCH: pName = SEARCH_NAME; break;
|
||||
case RECENT: pName = RECENT_NAME; break;
|
||||
case OWNED: pName = OWNED_NAME; break;
|
||||
case TAGS: pName = TAGS_NAME; break;
|
||||
default: pName = name;
|
||||
}
|
||||
return pName;
|
||||
@@ -1511,18 +1522,14 @@ define([
|
||||
case OWNED:
|
||||
msg = Messages.fm_info_owned;
|
||||
break;
|
||||
case TAGS:
|
||||
break;
|
||||
default:
|
||||
msg = undefined;
|
||||
}
|
||||
if (!APP.loggedIn) {
|
||||
msg = Messages.fm_info_anonymous;
|
||||
$box.html(msg);
|
||||
$box.find('a[target!="_blank"]').click(function (e) {
|
||||
e.preventDefault();
|
||||
var href = $(this).attr('href');
|
||||
common.gotoURL(href);
|
||||
});
|
||||
return $box;
|
||||
return $(common.fixLinks($box.html(msg)));
|
||||
}
|
||||
if (!msg || APP.store['hide-info-' + path[0]] === '1') {
|
||||
$box.hide();
|
||||
@@ -2057,6 +2064,7 @@ define([
|
||||
$element.data('context', 'default');
|
||||
$container.append($element);
|
||||
});
|
||||
createGhostIcon($container);
|
||||
};
|
||||
|
||||
var displayTrashRoot = function ($list, $folderHeader, $fileHeader) {
|
||||
@@ -2100,7 +2108,7 @@ define([
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var $table = $('<table>');
|
||||
var $icon = $('<td>', {'rowspan': '3', 'class': 'cp-app-drive-search-icon'})
|
||||
.append(getFileIcon(href));
|
||||
.append(getFileIcon(r.id));
|
||||
var $title = $('<td>', {
|
||||
'class': 'cp-app-drive-search-col1 cp-app-drive-search-title'
|
||||
}).text(r.data.title)
|
||||
@@ -2140,6 +2148,13 @@ define([
|
||||
}
|
||||
var $openDir = $('<td>', {'class': 'cp-app-drive-search-opendir'}).append($a);
|
||||
|
||||
$('<a>').text(Messages.fc_prop).click(function () {
|
||||
APP.getProperties(r.id, function (e, $prop) {
|
||||
if (e) { return void logError(e); }
|
||||
UI.alert($prop[0], undefined, true);
|
||||
});
|
||||
}).appendTo($openDir);
|
||||
|
||||
// rows 1-3
|
||||
$('<tr>').append($icon).append($title).append($typeName).append($type).appendTo($table);
|
||||
$('<tr>').append($path).append($atimeName).append($atime).appendTo($table);
|
||||
@@ -2230,6 +2245,35 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
// Tags category
|
||||
var displayTags = function ($container) {
|
||||
var list = filesOp.getTagsList();
|
||||
if (Object.keys(list).length === 0) { return; }
|
||||
var sortedTags = Object.keys(list);
|
||||
sortedTags.sort(function (a, b) {
|
||||
return list[b] - list[a];
|
||||
});
|
||||
var lines = [
|
||||
h('tr', [
|
||||
h('th', Messages.fm_tags_name),
|
||||
h('th', Messages.fm_tags_used)
|
||||
])
|
||||
];
|
||||
sortedTags.forEach(function (tag) {
|
||||
var tagLink = h('a', { href: '#' }, '#' + tag);
|
||||
$(tagLink).click(function () {
|
||||
if (displayedCategories.indexOf(SEARCH) !== -1) {
|
||||
APP.Search.$input.val('#' + tag).keyup();
|
||||
}
|
||||
});
|
||||
lines.push(h('tr', [
|
||||
h('td', tagLink),
|
||||
h('td.cp-app-drive-tags-used', list[tag])
|
||||
]));
|
||||
});
|
||||
$(h('li.cp-app-drive-tags-list', h('table', lines))).appendTo($container);
|
||||
};
|
||||
|
||||
// Display the selected directory into the content part (rightside)
|
||||
// NOTE: Elements in the trash are not using the same storage structure as the others
|
||||
// _WORKGROUP_ : do not change the lastOpenedFolder value in localStorage
|
||||
@@ -2259,10 +2303,9 @@ define([
|
||||
var isTrashRoot = filesOp.comparePath(path, [TRASH]);
|
||||
var isTemplate = filesOp.comparePath(path, [TEMPLATE]);
|
||||
var isAllFiles = filesOp.comparePath(path, [FILES_DATA]);
|
||||
var isSearch = path[0] === SEARCH;
|
||||
var isRecent = path[0] === RECENT;
|
||||
var isOwned = path[0] === OWNED;
|
||||
var isVirtual = virtualCategories.indexOf(path[0]) !== -1;
|
||||
var isSearch = path[0] === SEARCH;
|
||||
var isTags = path[0] === TAGS;
|
||||
|
||||
var root = isVirtual ? undefined : filesOp.find(path);
|
||||
if (!isVirtual && typeof(root) === "undefined") {
|
||||
@@ -2296,7 +2339,7 @@ define([
|
||||
|
||||
var $dirContent = $('<div>', {id: FOLDER_CONTENT_ID});
|
||||
$dirContent.data('path', path);
|
||||
if (!isSearch) {
|
||||
if (!isSearch && !isTags) {
|
||||
var mode = getViewMode();
|
||||
if (mode) {
|
||||
$dirContent.addClass(getViewModeClass());
|
||||
@@ -2358,10 +2401,12 @@ define([
|
||||
displayTrashRoot($list, $folderHeader, $fileHeader);
|
||||
} else if (isSearch) {
|
||||
displaySearch($list, path[1]);
|
||||
} else if (isRecent) {
|
||||
} else if (path[0] === RECENT) {
|
||||
displayRecent($list);
|
||||
} else if (isOwned) {
|
||||
} else if (path[0] === OWNED) {
|
||||
displayOwned($list);
|
||||
} else if (isTags) {
|
||||
displayTags($list);
|
||||
} else {
|
||||
$dirContent.contextmenu(openContextMenu('content'));
|
||||
if (filesOp.hasSubfolder(root)) { $list.append($folderHeader); }
|
||||
@@ -2503,25 +2548,6 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
var createTemplate = function ($container, path) {
|
||||
var $icon = $templateIcon.clone();
|
||||
var isOpened = filesOp.comparePath(path, currentPath);
|
||||
var $element = createTreeElement(TEMPLATE_NAME, $icon, [TEMPLATE], false, true, false, isOpened);
|
||||
$element.addClass('cp-app-drive-tree-root');
|
||||
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
|
||||
$container.append($list);
|
||||
};
|
||||
|
||||
var createAllFiles = function ($container, path) {
|
||||
var $icon = $unsortedIcon.clone();
|
||||
var isOpened = filesOp.comparePath(path, currentPath);
|
||||
var $allfilesElement = createTreeElement(FILES_DATA_NAME, $icon, [FILES_DATA], false, false, false, isOpened);
|
||||
$allfilesElement.addClass('root');
|
||||
var $allfilesList = $('<ul>', { 'class': 'cp-app-drive-tree-category' })
|
||||
.append($allfilesElement);
|
||||
$container.append($allfilesList);
|
||||
};
|
||||
|
||||
var createTrash = function ($container, path) {
|
||||
var $icon = filesOp.isFolderEmpty(files[TRASH]) ? $trashEmptyIcon.clone() : $trashIcon.clone();
|
||||
var isOpened = filesOp.comparePath(path, currentPath);
|
||||
@@ -2534,29 +2560,11 @@ define([
|
||||
$container.append($trashList);
|
||||
};
|
||||
|
||||
var createRecent = function ($container, path) {
|
||||
var $icon = $recentIcon.clone();
|
||||
var isOpened = filesOp.comparePath(path, currentPath);
|
||||
var $element = createTreeElement(RECENT_NAME, $icon, [RECENT], false, false, false, isOpened);
|
||||
$element.addClass('root');
|
||||
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
|
||||
$container.append($list);
|
||||
};
|
||||
|
||||
var createOwned = function ($container, path) {
|
||||
var $icon = $ownedIcon.clone(); // TODO
|
||||
var isOpened = filesOp.comparePath(path, currentPath);
|
||||
var $element = createTreeElement(OWNED_NAME, $icon, [OWNED], false, false, false, isOpened);
|
||||
$element.addClass('root');
|
||||
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
|
||||
$container.append($list);
|
||||
};
|
||||
|
||||
var search = APP.Search = {};
|
||||
var createSearch = function ($container) {
|
||||
var isInSearch = currentPath[0] === SEARCH;
|
||||
var $div = $('<div>', {'id': 'cp-app-drive-tree-search', 'class': 'cp-unselectable'});
|
||||
var $input = $('<input>', {
|
||||
var $input = APP.Search.$input = $('<input>', {
|
||||
id: 'cp-app-drive-tree-search-input',
|
||||
type: 'text',
|
||||
draggable: false,
|
||||
@@ -2606,6 +2614,38 @@ define([
|
||||
$container.append($div);
|
||||
};
|
||||
|
||||
var categories = {};
|
||||
categories[FILES_DATA] = {
|
||||
name: FILES_DATA_NAME,
|
||||
$icon: $unsortedIcon
|
||||
};
|
||||
categories[TEMPLATE] = {
|
||||
name: TEMPLATE_NAME,
|
||||
droppable: true,
|
||||
$icon: $templateIcon
|
||||
};
|
||||
categories[RECENT] = {
|
||||
name: RECENT_NAME,
|
||||
$icon: $recentIcon
|
||||
};
|
||||
categories[OWNED] = {
|
||||
name: OWNED_NAME,
|
||||
$icon: $ownedIcon
|
||||
};
|
||||
categories[TAGS] = {
|
||||
name: TAGS_NAME,
|
||||
$icon: $tagsIcon
|
||||
};
|
||||
var createCategory = function ($container, cat) {
|
||||
var options = categories[cat];
|
||||
var $icon = options.$icon.clone();
|
||||
var isOpened = filesOp.comparePath([cat], currentPath);
|
||||
var $element = createTreeElement(options.name, $icon, [cat], options.draggable, options.droppable, false, isOpened);
|
||||
$element.addClass('cp-app-drive-tree-root');
|
||||
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
|
||||
$container.append($list);
|
||||
};
|
||||
|
||||
APP.resetTree = function () {
|
||||
var $categories = $tree.find('.cp-app-drive-tree-categories-container');
|
||||
var s = $categories.scrollTop() || 0;
|
||||
@@ -2614,11 +2654,12 @@ define([
|
||||
if (displayedCategories.indexOf(SEARCH) !== -1) { createSearch($tree); }
|
||||
var $div = $('<div>', {'class': 'cp-app-drive-tree-categories-container'})
|
||||
.appendTo($tree);
|
||||
if (displayedCategories.indexOf(RECENT) !== -1) { createRecent($div, [RECENT]); }
|
||||
if (displayedCategories.indexOf(OWNED) !== -1) { createOwned($div, [OWNED]); }
|
||||
if (displayedCategories.indexOf(TAGS) !== -1) { createCategory($div, TAGS); }
|
||||
if (displayedCategories.indexOf(RECENT) !== -1) { createCategory($div, RECENT); }
|
||||
if (displayedCategories.indexOf(OWNED) !== -1) { createCategory($div, OWNED); }
|
||||
if (displayedCategories.indexOf(ROOT) !== -1) { createTree($div, [ROOT]); }
|
||||
if (displayedCategories.indexOf(TEMPLATE) !== -1) { createTemplate($div, [TEMPLATE]); }
|
||||
if (displayedCategories.indexOf(FILES_DATA) !== -1) { createAllFiles($div, [FILES_DATA]); }
|
||||
if (displayedCategories.indexOf(TEMPLATE) !== -1) { createCategory($div, TEMPLATE); }
|
||||
if (displayedCategories.indexOf(FILES_DATA) !== -1) { createCategory($div, FILES_DATA); }
|
||||
if (displayedCategories.indexOf(TRASH) !== -1) { createTrash($div, [TRASH]); }
|
||||
|
||||
$tree.append(APP.$limit);
|
||||
@@ -2657,9 +2698,9 @@ define([
|
||||
if (parsed.hashData.type !== "pad") { return; }
|
||||
var i = data.href.indexOf('#') + 1;
|
||||
var base = data.href.slice(0, i);
|
||||
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash);
|
||||
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
||||
if (!hrefsecret.keys) { return; }
|
||||
var viewHash = Hash.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys);
|
||||
var viewHash = Hash.getViewHashFromKeys(hrefsecret);
|
||||
return base + viewHash;
|
||||
};
|
||||
|
||||
@@ -2672,7 +2713,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
var getProperties = function (el, cb) {
|
||||
var getProperties = APP.getProperties = function (el, cb) {
|
||||
if (!filesOp.isFile(el)) {
|
||||
return void cb('NOT_FILE');
|
||||
}
|
||||
@@ -2724,24 +2765,6 @@ define([
|
||||
$(window).focus();
|
||||
if (!res) { return; }
|
||||
filesOp.delete(pathsList, refresh);
|
||||
/*
|
||||
// Try to delete each selected pad from server, and delete from drive if no error
|
||||
var n = nThen(function () {});
|
||||
pathsList.forEach(function (p) {
|
||||
var el = filesOp.find(p);
|
||||
var data = filesOp.getFileData(el);
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
var channel = Util.base64ToHex(parsed.hashData.channel);
|
||||
n = n.nThen(function (waitFor) {
|
||||
sframeChan.query('Q_REMOVE_OWNED_CHANNEL', channel,
|
||||
waitFor(function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
filesOp.delete([p], function () {}, false, true);
|
||||
}));
|
||||
});
|
||||
});
|
||||
n.nThen(function () { refresh(); });
|
||||
*/
|
||||
});
|
||||
};
|
||||
$contextMenu.on("click", "a", function(e) {
|
||||
|
||||
@@ -39,6 +39,7 @@ define([
|
||||
var getSecrets = function (Cryptpad, Utils, cb) {
|
||||
var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() ||
|
||||
Utils.LocalStore.getFSHash();
|
||||
// No password for drive
|
||||
cb(null, Utils.Hash.getSecrets('drive', hash));
|
||||
};
|
||||
var addRpc = function (sframeChan, Cryptpad, Utils) {
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.toolbar_main(
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_file-bg,
|
||||
@warn-color: @colortheme_file-warn,
|
||||
@color: @colortheme_file-color
|
||||
);
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
.tokenfield_main();
|
||||
|
||||
@button-border: 2px;
|
||||
|
||||
/*html, body {
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
}*/
|
||||
|
||||
// body
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
@@ -2,7 +2,7 @@ define([
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function () {
|
||||
var Nacl = window.nacl;
|
||||
var PARANOIA = true;
|
||||
//var PARANOIA = true;
|
||||
|
||||
var plainChunkLength = 128 * 1024;
|
||||
var cypherChunkLength = 131088;
|
||||
@@ -36,24 +36,11 @@ define([
|
||||
var increment = function (N) {
|
||||
var l = N.length;
|
||||
while (l-- > 1) {
|
||||
if (PARANOIA) {
|
||||
if (typeof(N[l]) !== 'number') {
|
||||
throw new Error('E_UNSAFE_TYPE');
|
||||
}
|
||||
if (N[l] > 255) {
|
||||
throw new Error('E_OUT_OF_BOUNDS');
|
||||
}
|
||||
}
|
||||
/* jshint probably suspects this is unsafe because we lack types
|
||||
but as long as this is only used on nonces, it should be safe */
|
||||
if (N[l] !== 255) { return void N[l]++; } // jshint ignore:line
|
||||
if (l === 0) { throw new Error('E_NONCE_TOO_LARGE'); }
|
||||
N[l] = 0;
|
||||
|
||||
// you don't need to worry about this running out.
|
||||
// you'd need a REAAAALLY big file
|
||||
if (l === 0) {
|
||||
throw new Error('E_NONCE_TOO_LARGE');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -160,19 +147,21 @@ define([
|
||||
}
|
||||
|
||||
var takeChunk = function (cb) {
|
||||
var start = i * cypherChunkLength + 2 + metadataLength;
|
||||
var end = start + cypherChunkLength;
|
||||
i++;
|
||||
var box = new Uint8Array(u8.subarray(start, end));
|
||||
setTimeout(function () {
|
||||
var start = i * cypherChunkLength + 2 + metadataLength;
|
||||
var end = start + cypherChunkLength;
|
||||
i++;
|
||||
var box = new Uint8Array(u8.subarray(start, end));
|
||||
|
||||
// decrypt the chunk
|
||||
var plaintext = Nacl.secretbox.open(box, nonce, key);
|
||||
increment(nonce);
|
||||
// decrypt the chunk
|
||||
var plaintext = Nacl.secretbox.open(box, nonce, key);
|
||||
increment(nonce);
|
||||
|
||||
if (!plaintext) { return cb('DECRYPTION_ERROR'); }
|
||||
if (!plaintext) { return cb('DECRYPTION_ERROR'); }
|
||||
|
||||
_progress(end);
|
||||
cb(void 0, plaintext);
|
||||
_progress(end);
|
||||
cb(void 0, plaintext);
|
||||
});
|
||||
};
|
||||
|
||||
var chunks = [];
|
||||
@@ -219,7 +208,7 @@ define([
|
||||
|
||||
var state = 0;
|
||||
var next = function (cb) {
|
||||
if (state === 2) { return void cb(); }
|
||||
if (state === 2) { return void setTimeout(cb); }
|
||||
|
||||
var start;
|
||||
var end;
|
||||
@@ -238,7 +227,9 @@ define([
|
||||
.concat(slice(box)));
|
||||
state++;
|
||||
|
||||
return void cb(void 0, prefixed);
|
||||
return void setTimeout(function () {
|
||||
cb(void 0, prefixed);
|
||||
});
|
||||
}
|
||||
|
||||
// encrypt the rest of the file...
|
||||
@@ -253,7 +244,9 @@ define([
|
||||
// regular data is done
|
||||
if (i * plainChunkLength >= u8.length) { state = 2; }
|
||||
|
||||
return void cb(void 0, box);
|
||||
setTimeout(function () {
|
||||
cb(void 0, box);
|
||||
});
|
||||
};
|
||||
|
||||
return next;
|
||||
|
||||
@@ -16,7 +16,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 (
|
||||
@@ -61,6 +61,7 @@ define([
|
||||
if (!priv.filehash) {
|
||||
uploadMode = true;
|
||||
} else {
|
||||
// PASSWORD_FILES
|
||||
secret = Hash.getSecrets('file', priv.filehash);
|
||||
if (!secret.keys) { throw new Error("You need a hash"); }
|
||||
hexFileName = Util.base64ToHex(secret.channel);
|
||||
@@ -124,6 +125,12 @@ define([
|
||||
|
||||
var rightsideDisplayed = false;
|
||||
$(window.document).on('decryption', function (e) {
|
||||
/* FIXME
|
||||
we're listening for decryption events and assuming that only
|
||||
the main media-tag exists. In practice there is also your avatar
|
||||
and there could be other things in the future, so we should
|
||||
figure out a generic way target media-tag decryption events.
|
||||
*/
|
||||
var decrypted = e.originalEvent;
|
||||
if (decrypted.callback) {
|
||||
decrypted.callback();
|
||||
@@ -133,9 +140,6 @@ define([
|
||||
$dlform.hide();
|
||||
var $dlButton = $dlview.find('media-tag button');
|
||||
if (ev) { $dlButton.click(); }
|
||||
if (!$dlButton.length) {
|
||||
$appContainer.css('background', 'white');
|
||||
}
|
||||
$dlButton.addClass('btn btn-success');
|
||||
var text = Messages.download_mt_button + '<br>';
|
||||
text += '<b>' + Util.fixHTML(title) + '</b><br>';
|
||||
@@ -230,8 +234,7 @@ define([
|
||||
if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); }
|
||||
$dlform.find('#cp-app-file-dlfile, #cp-app-file-dlprogress').click(onClick);
|
||||
};
|
||||
var href = priv.origin + priv.pathname + priv.filehash;
|
||||
common.getFileSize(href, function (e, data) {
|
||||
common.getFileSize(hexFileName, function (e, data) {
|
||||
if (e) {
|
||||
return void UI.errorLoadingScreen(e);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
@import (once) '../../customize/src/less2/include/icon-colors.less';
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/tippy.less';
|
||||
|
||||
.iconColors_main();
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
.tippy_main();
|
||||
|
||||
#cp-filepicker-dialog {
|
||||
display: none;
|
||||
@@ -36,7 +38,8 @@
|
||||
line-height: 1em;
|
||||
cursor: pointer;
|
||||
|
||||
background-color: #111;
|
||||
background-color: @colortheme_modal-bg;
|
||||
box-shadow: 2px 2px 5px #000;
|
||||
color: @darker;
|
||||
|
||||
transition: all 0.1s;
|
||||
|
||||
@@ -11,7 +11,7 @@ define([
|
||||
'/customize/messages.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 (
|
||||
$,
|
||||
@@ -40,6 +40,7 @@ define([
|
||||
var parsed = Hash.parsePadUrl(data.url);
|
||||
hideFileDialog();
|
||||
if (parsed.type === 'file') {
|
||||
// PASSWORD_FILES
|
||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||
sframeChan.event("EV_FILE_PICKED", {
|
||||
@@ -138,7 +139,7 @@ define([
|
||||
});
|
||||
|
||||
// Add thumbnail if it exists
|
||||
common.displayThumbnail(data.href, $span);
|
||||
common.displayThumbnail(data.href, data.channel, $span);
|
||||
});
|
||||
$input.focus();
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ define([
|
||||
'/common/outer/local-store.js',
|
||||
'/common/test.js',
|
||||
|
||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
], function ($, Cryptpad, Login, UI, Realtime, Feedback, LocalStore, Test) {
|
||||
$(function () {
|
||||
var $main = $('#mainBlock');
|
||||
|
||||
@@ -3,6 +3,43 @@
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<script async data-bootload="/pad/inner.js" data-main="/common/sframe-boot.js?ver=1.6" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0px;
|
||||
}
|
||||
#cke_1_top {
|
||||
overflow: visible;
|
||||
padding: 0px;
|
||||
display: flex;
|
||||
}
|
||||
#cke_1_toolbox {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
background-color: #c1e7ff;
|
||||
}
|
||||
#cke_1_toolbox .cke_toolbar {
|
||||
height: 28px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
#cke_1_top .cryptpad-toolbar {
|
||||
padding: 0;
|
||||
display: block;
|
||||
}
|
||||
.cke_wysiwyg_frame {
|
||||
min-width: 60%;
|
||||
}
|
||||
|
||||
|
||||
@media print {
|
||||
#cke_1_top {
|
||||
display:none;
|
||||
}
|
||||
body.app-pad .userlist-drawer {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body class="cp-app-pad">
|
||||
<textarea style="display:none" id="editor1" name="editor1"></textarea>
|
||||
|
||||
133
www/pad/inner.js
133
www/pad/inner.js
@@ -36,7 +36,7 @@ define([
|
||||
'/bower_components/diff-dom/diffDOM.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 (
|
||||
$,
|
||||
@@ -82,12 +82,54 @@ define([
|
||||
Cursor: Cursor,
|
||||
};
|
||||
|
||||
// MEDIATAG: Filter elements to serialize
|
||||
// * Remove the drag&drop and resizers from the hyperjson
|
||||
var isWidget = function (el) {
|
||||
return typeof (el.getAttribute) === "function" &&
|
||||
(el.getAttribute('data-cke-hidden-sel') ||
|
||||
(el.getAttribute('class') &&
|
||||
(/cke_widget_drag/.test(el.getAttribute('class')) ||
|
||||
/cke_image_resizer/.test(el.getAttribute('class')))
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
var isNotMagicLine = function (el) {
|
||||
return !(el && typeof(el.getAttribute) === 'function' &&
|
||||
el.getAttribute('class') &&
|
||||
el.getAttribute('class').split(' ').indexOf('non-realtime') !== -1);
|
||||
};
|
||||
|
||||
var shouldSerialize = function (el) {
|
||||
return isNotMagicLine(el) && !isWidget(el);
|
||||
};
|
||||
|
||||
// MEDIATAG: Filter attributes in the serialized elements
|
||||
var widgetFilter = function (hj) {
|
||||
// Send a widget ID == 0 to avoid a fight between browsers and
|
||||
// prevent the container from having the "selected" class (blue border)
|
||||
if (hj[1].class) {
|
||||
var split = hj[1].class.split(' ');
|
||||
if (split.indexOf('cke_widget_wrapper') !== -1 &&
|
||||
split.indexOf('cke_widget_block') !== -1) {
|
||||
hj[1].class = "cke_widget_wrapper cke_widget_block";
|
||||
hj[1]['data-cke-widget-id'] = "0";
|
||||
}
|
||||
if (split.indexOf('cke_widget_wrapper') !== -1 &&
|
||||
split.indexOf('cke_widget_inline') !== -1) {
|
||||
hj[1].class = "cke_widget_wrapper cke_widget_inline";
|
||||
delete hj[1]['data-cke-widget-id'];
|
||||
//hj[1]['data-cke-widget-id'] = "0";
|
||||
}
|
||||
// Remove the title attribute of the drag&drop icons (translation conflicts)
|
||||
if (split.indexOf('cke_widget_drag_handler') !== -1 ||
|
||||
split.indexOf('cke_image_resizer') !== -1) {
|
||||
hj[1].title = undefined;
|
||||
}
|
||||
}
|
||||
return hj;
|
||||
};
|
||||
|
||||
var hjsonFilters = function (hj) {
|
||||
/* catch `type="_moz"` before it goes over the wire */
|
||||
var brFilter = function (hj) {
|
||||
@@ -100,6 +142,7 @@ define([
|
||||
};
|
||||
brFilter(hj);
|
||||
mediatagContentFilter(hj);
|
||||
widgetFilter(hj);
|
||||
return hj;
|
||||
};
|
||||
|
||||
@@ -168,6 +211,36 @@ define([
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MEDIATAG
|
||||
// Never modify widget ids
|
||||
if (info.node && info.node.tagName === 'SPAN' && info.diff.name === 'data-cke-widget-id') {
|
||||
return true;
|
||||
}
|
||||
if (info.node && info.node.tagName === 'SPAN' &&
|
||||
info.node.getAttribute('class') &&
|
||||
/cke_widget_wrapper/.test(info.node.getAttribute('class'))) {
|
||||
if (info.diff.action === 'modifyAttribute' && info.diff.name === 'class') {
|
||||
return true;
|
||||
}
|
||||
//console.log(info);
|
||||
}
|
||||
// CkEditor drag&drop icon container
|
||||
if (info.node && info.node.tagName === 'SPAN' &&
|
||||
info.node.getAttribute('class') &&
|
||||
info.node.getAttribute('class').split(' ').indexOf('cke_widget_drag_handler_container') !== -1) {
|
||||
return true;
|
||||
}
|
||||
// CkEditor drag&drop title (language fight)
|
||||
if (info.node && info.node.getAttribute &&
|
||||
info.node.getAttribute('class') &&
|
||||
(info.node.getAttribute('class').split(' ').indexOf('cke_widget_drag_handler') !== -1 ||
|
||||
info.node.getAttribute('class').split(' ').indexOf('cke_image_resizer') !== -1 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Also reject any elements which would insert any one of
|
||||
our forbidden tag types: script, iframe, object,
|
||||
@@ -199,20 +272,26 @@ define([
|
||||
if (info.node && info.node.tagName === 'SPAN' &&
|
||||
info.node.getAttribute('contentEditable') === "false") {
|
||||
// it seems to be a magicline plugin element...
|
||||
// but it can also be a widget (MEDIATAG), in which case the removal was
|
||||
// probably intentional
|
||||
|
||||
if (info.diff.action === 'removeElement') {
|
||||
// and you're about to remove it...
|
||||
// this probably isn't what you want
|
||||
if (!info.node.getAttribute('class') ||
|
||||
!/cke_widget_wrapper/.test(info.node.getAttribute('class'))) {
|
||||
// This element is not a widget!
|
||||
// this probably isn't what you want
|
||||
/*
|
||||
I have never seen this in the console, but the
|
||||
magic line is still getting removed on remote
|
||||
edits. This suggests that it's getting removed
|
||||
by something other than diffDom.
|
||||
*/
|
||||
console.log("preventing removal of the magic line!");
|
||||
|
||||
/*
|
||||
I have never seen this in the console, but the
|
||||
magic line is still getting removed on remote
|
||||
edits. This suggests that it's getting removed
|
||||
by something other than diffDom.
|
||||
*/
|
||||
console.log("preventing removal of the magic line!");
|
||||
|
||||
// return true to prevent diff application
|
||||
return true;
|
||||
// return true to prevent diff application
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +401,7 @@ define([
|
||||
var src = tag.getAttribute('src');
|
||||
if (mediaTagMap[src]) {
|
||||
mediaTagMap[src].forEach(function (n) {
|
||||
tag.appendChild(n);
|
||||
tag.appendChild(n.cloneNode());
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -370,8 +449,11 @@ define([
|
||||
|
||||
framework.setMediaTagEmbedder(function ($mt) {
|
||||
$mt.attr('contenteditable', 'false');
|
||||
$mt.attr('tabindex', '1');
|
||||
editor.insertElement(new window.CKEDITOR.dom.element($mt[0]));
|
||||
//$mt.attr('tabindex', '1');
|
||||
//MEDIATAG
|
||||
var element = new window.CKEDITOR.dom.element($mt[0]);
|
||||
editor.insertElement(element);
|
||||
editor.widgets.initOn( element, 'mediatag' );
|
||||
});
|
||||
|
||||
framework.setTitleRecommender(function () {
|
||||
@@ -403,7 +485,18 @@ define([
|
||||
|
||||
var patch = (DD).diff(inner, userDocStateDom);
|
||||
(DD).apply(inner, patch);
|
||||
|
||||
// MEDIATAG: Migrate old mediatags to the widget system
|
||||
$(inner).find('media-tag:not(.cke_widget_element)').each(function (i, el) {
|
||||
var element = new window.CKEDITOR.dom.element(el);
|
||||
editor.widgets.initOn( element, 'mediatag' );
|
||||
});
|
||||
|
||||
displayMediaTags(framework, inner, mediaTagMap);
|
||||
|
||||
// MEDIATAG: Initialize mediatag widgets inserted in the document by other users
|
||||
editor.widgets.checkWidgets();
|
||||
|
||||
if (framework.isReadOnly()) {
|
||||
var $links = $(inner).find('a');
|
||||
// off so that we don't end up with multiple identical handlers
|
||||
@@ -425,7 +518,7 @@ define([
|
||||
framework.setContentGetter(function () {
|
||||
displayMediaTags(framework, inner, mediaTagMap);
|
||||
inner.normalize();
|
||||
return Hyperjson.fromDOM(inner, isNotMagicLine, hjsonFilters);
|
||||
return Hyperjson.fromDOM(inner, shouldSerialize, hjsonFilters);
|
||||
});
|
||||
|
||||
$bar.find('#cke_1_toolbar_collapser').hide();
|
||||
@@ -459,11 +552,15 @@ define([
|
||||
ckeditor: editor,
|
||||
body: $('body'),
|
||||
onUploaded: function (ev, data) {
|
||||
// PASSWORD_FILES
|
||||
var parsed = Hash.parsePadUrl(data.url);
|
||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||
var mt = '<media-tag contenteditable="false" src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '" tabindex="1"></media-tag>';
|
||||
editor.insertElement(window.CKEDITOR.dom.element.createFromHtml(mt));
|
||||
// MEDIATAG
|
||||
var element = window.CKEDITOR.dom.element.createFromHtml(mt);
|
||||
editor.insertElement(element);
|
||||
editor.widgets.initOn( element, 'mediatag' );
|
||||
}
|
||||
};
|
||||
window.APP.FM = framework._.sfCommon.createFileManager(fmConfig);
|
||||
@@ -508,6 +605,8 @@ define([
|
||||
Util.blobToImage($(el).data('blob'), waitFor(function (imgSrc) {
|
||||
$clone.find('media-tag[src="' + $(el).attr('src') + '"] img')
|
||||
.attr('src', imgSrc);
|
||||
$clone.find('media-tag').parent()
|
||||
.find('.cke_widget_drag_handler_container').remove();
|
||||
}));
|
||||
});
|
||||
}).nThen(function () {
|
||||
|
||||
@@ -23,7 +23,13 @@ CKEDITOR.dialog.add('mediatag', function (editor) {
|
||||
},
|
||||
],
|
||||
onShow: function () {
|
||||
var el = editor.plugins.mediatag.clicked;
|
||||
var sel = editor.getSelection();
|
||||
element = sel.getSelectedElement();
|
||||
if (!element) { return; }
|
||||
|
||||
var el = element.findOne('media-tag');
|
||||
if (!el) { return; }
|
||||
|
||||
var rect = el.getClientRect();
|
||||
var dialog = this.parts.contents.$;
|
||||
var inputs = dialog.querySelectorAll('input');
|
||||
@@ -34,7 +40,14 @@ CKEDITOR.dialog.add('mediatag', function (editor) {
|
||||
},
|
||||
onOk: function() {
|
||||
var dialog = this;
|
||||
var el = editor.plugins.mediatag.clicked;
|
||||
|
||||
var sel = editor.getSelection();
|
||||
element = sel.getSelectedElement();
|
||||
if (!element) { return; }
|
||||
|
||||
var el = element.findOne('media-tag');
|
||||
if (!el) { return; }
|
||||
|
||||
var dialog = this.parts.contents.$;
|
||||
var inputs = dialog.querySelectorAll('input');
|
||||
var wInput = inputs[0];
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
( function() {
|
||||
|
||||
CKEDITOR.plugins.add( 'mediatag', {
|
||||
requires: 'dialog',
|
||||
requires: 'dialog,widget',
|
||||
//icons: 'image',
|
||||
//hidpi: true,
|
||||
onLoad: function () {
|
||||
@@ -38,144 +38,17 @@
|
||||
// Register the dialog.
|
||||
CKEDITOR.dialog.add( pluginName, this.path + 'mediatag-plugin-dialog.js' );
|
||||
|
||||
var allowed = 'media-tag[!data-crypto-key,!src,contenteditable,width,height]{border-style,border-width,float,height,margin,margin-bottom,margin-left,margin-right,margin-top,width}',
|
||||
required = 'media-tag[data-crypto-key,src]';
|
||||
editor.widgets.add( 'mediatag', {
|
||||
|
||||
// Register the command.
|
||||
editor.addCommand( pluginName, new CKEDITOR.dialogCommand( pluginName, {
|
||||
allowedContent: allowed,
|
||||
requiredContent: required,
|
||||
contentTransformations: [
|
||||
[ 'media-tag{width}: sizeToStyle', 'media-tag[width]: sizeToAttribute' ],
|
||||
[ 'media-tag{float}: alignmentToStyle', 'media-tag[align]: alignmentToAttribute' ]
|
||||
]
|
||||
} ) );
|
||||
|
||||
var isMediaTag = function (el) {
|
||||
if (el.is('media-tag')) { return el; }
|
||||
var mt = el.getParents().slice().filter(function (p) {
|
||||
return p.is('media-tag');
|
||||
});
|
||||
if (mt.length !== 1) { return; }
|
||||
return mt[0];
|
||||
};
|
||||
editor.on('doubleclick', function (evt) {
|
||||
var element = evt.data.element;
|
||||
var mt = isMediaTag(element);
|
||||
if (mt && !element.data('cke-realelement')) {
|
||||
editor.plugins.mediatag.clicked = mt;
|
||||
evt.data.dialog = 'mediatag';
|
||||
getLabel: function () { return " "; },
|
||||
dialog: pluginName,
|
||||
inline: true,
|
||||
upcast: function( element ) {
|
||||
return element.name === 'media-tag';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// If the "contextmenu" plugin is loaded, register the listeners.
|
||||
if (editor.contextMenu) {
|
||||
editor.contextMenu.addListener(function (element) {
|
||||
if (getSelectedMediatag(editor, element)) {
|
||||
return { mediatag: CKEDITOR.TRISTATE_OFF };
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
afterInit: function( editor ) {
|
||||
// Customize the behavior of the alignment commands. (http://dev.ckeditor.com/ticket/7430)
|
||||
setupAlignCommand('left');
|
||||
setupAlignCommand('right');
|
||||
setupAlignCommand('center');
|
||||
setupAlignCommand('block');
|
||||
|
||||
function setupAlignCommand (value) {
|
||||
var command = editor.getCommand('justify' + value);
|
||||
if (command) {
|
||||
if (value === 'left' || value === 'right') {
|
||||
command.on('exec', function (evt) {
|
||||
var img = getSelectedMediatag(editor), align;
|
||||
if (img) {
|
||||
align = getMediatagAlignment(img);
|
||||
if (align === value) {
|
||||
img.removeStyle('float');
|
||||
|
||||
// Remove "align" attribute when necessary.
|
||||
if (value === getMediatagAlignment(img))
|
||||
img.removeAttribute( 'align' );
|
||||
} else {
|
||||
img.setStyle( 'float', value );
|
||||
}
|
||||
|
||||
evt.cancel();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
command.on('refresh', function (evt) {
|
||||
var img = getSelectedMediatag(editor), align;
|
||||
if (img) {
|
||||
align = getMediatagAlignment(img);
|
||||
|
||||
this.setState(
|
||||
(align === value) ? CKEDITOR.TRISTATE_ON : ( value === 'right' || value === 'left' ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
|
||||
|
||||
evt.cancel();
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
function getSelectedMediatag (editor, element) {
|
||||
if (!element) {
|
||||
var sel = editor.getSelection();
|
||||
element = sel.getSelectedElement();
|
||||
}
|
||||
|
||||
if (element && element.is('media-tag') && !element.data('cke-realelement')
|
||||
&& !element.isReadOnly()) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
function getMediatagAlignment (element) {
|
||||
var align = element.getStyle('float');
|
||||
|
||||
if (align === 'inherit' || align === 'none') {
|
||||
align = 0;
|
||||
}
|
||||
|
||||
if (!align) {
|
||||
align = element.getAttribute('align');
|
||||
}
|
||||
|
||||
return align;
|
||||
}
|
||||
} )();
|
||||
|
||||
/**
|
||||
* Determines whether dimension inputs should be automatically filled when the image URL changes in the Image plugin dialog window.
|
||||
*
|
||||
* config.image_prefillDimensions = false;
|
||||
*
|
||||
* @since 4.5
|
||||
* @cfg {Boolean} [image_prefillDimensions=true]
|
||||
* @member CKEDITOR.config
|
||||
*/
|
||||
|
||||
/**
|
||||
* Whether to remove links when emptying the link URL field in the Image dialog window.
|
||||
*
|
||||
* config.image_removeLinkByEmptyURL = false;
|
||||
*
|
||||
* @cfg {Boolean} [image_removeLinkByEmptyURL=true]
|
||||
* @member CKEDITOR.config
|
||||
*/
|
||||
CKEDITOR.config.mediatag_removeLinkByEmptyURL = true;
|
||||
|
||||
/**
|
||||
* Padding text to set off the image in the preview area.
|
||||
*
|
||||
* config.image_previewText = CKEDITOR.tools.repeat( '___ ', 100 );
|
||||
*
|
||||
* @cfg {String} [image_previewText='Lorem ipsum dolor...' (placeholder text)]
|
||||
* @member CKEDITOR.config
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||
@import (once) '../../customize/src/less2/include/tools.less';
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/creation.less';
|
||||
@import (once) "../../customize/src/less2/include/framework.less";
|
||||
|
||||
.toolbar_main(
|
||||
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_poll-bg,
|
||||
@warn-color: @colortheme_poll-warn,
|
||||
@color: @colortheme_poll-color
|
||||
);
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
.tokenfield_main();
|
||||
.creation_main();
|
||||
|
||||
@poll-fore: #555;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ define([
|
||||
'jquery',
|
||||
'/common/toolbar3.js',
|
||||
'/common/common-util.js',
|
||||
'/common/cryptget.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/common/sframe-common.js',
|
||||
'/common/common-realtime.js',
|
||||
@@ -26,13 +25,12 @@ 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 (
|
||||
$,
|
||||
Toolbar,
|
||||
Util,
|
||||
Cryptget,
|
||||
nThen,
|
||||
SFCommon,
|
||||
CommonRealtime,
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
|
||||
|
||||
.toolbar_main(
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_profile-bg,
|
||||
@warn-color: @colortheme_profile-warn,
|
||||
@color: @colortheme_profile-color
|
||||
);
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
.sidebar-layout_main();
|
||||
|
||||
// body
|
||||
|
||||
@@ -19,7 +19,7 @@ define([
|
||||
'css!/bower_components/codemirror/addon/dialog/dialog.css',
|
||||
'css!/bower_components/codemirror/addon/fold/foldgutter.css',
|
||||
'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',
|
||||
'/bower_components/croppie/croppie.min.js',
|
||||
'css!/bower_components/croppie/croppie.css',
|
||||
|
||||
@@ -40,6 +40,7 @@ define([
|
||||
var Hash = Utils.Hash;
|
||||
// 1st case: visiting someone else's profile with hash in the URL
|
||||
if (window.location.hash) {
|
||||
// No password for profiles
|
||||
return void cb(null, Hash.getSecrets('profile', window.location.hash.slice(1)));
|
||||
}
|
||||
var editHash;
|
||||
@@ -50,11 +51,17 @@ define([
|
||||
}));
|
||||
}).nThen(function () {
|
||||
if (editHash) {
|
||||
// No password for profile
|
||||
return void cb(null, Hash.getSecrets('profile', editHash));
|
||||
}
|
||||
// 3rd case: profile creation (create a new random hash, store it later if needed)
|
||||
if (!Utils.LocalStore.isLoggedIn()) { return void cb(); }
|
||||
var hash = Hash.createRandomHash();
|
||||
if (!Utils.LocalStore.isLoggedIn()) {
|
||||
// Unregistered users can't create a profile
|
||||
window.location.href = '/drive/';
|
||||
return void cb();
|
||||
}
|
||||
// No password for profile
|
||||
var hash = Hash.createRandomHash('profile');
|
||||
var secret = Hash.getSecrets('profile', hash);
|
||||
Cryptpad.pinPads([secret.channel], function (e) {
|
||||
if (e) {
|
||||
@@ -65,8 +72,8 @@ define([
|
||||
//return void UI.log(Messages._getKey('profile_error', [e])) // TODO
|
||||
}
|
||||
var profile = {};
|
||||
profile.edit = Utils.Hash.getEditHashFromKeys(secret.channel, secret.keys);
|
||||
profile.view = Utils.Hash.getViewHashFromKeys(secret.channel, secret.keys);
|
||||
profile.edit = Utils.Hash.getEditHashFromKeys(secret);
|
||||
profile.view = Utils.Hash.getViewHashFromKeys(secret);
|
||||
Cryptpad.setNewProfile(profile);
|
||||
});
|
||||
cb(null, secret);
|
||||
@@ -75,7 +82,7 @@ define([
|
||||
var addRpc = function (sframeChan, Cryptpad, Utils) {
|
||||
// Adding a new avatar from the profile: pin it and store it in the object
|
||||
sframeChan.on('Q_PROFILE_AVATAR_ADD', function (data, cb) {
|
||||
var chanId = Utils.Hash.hrefToHexChannelId(data);
|
||||
var chanId = Utils.Hash.hrefToHexChannelId(data, null);
|
||||
Cryptpad.pinPads([chanId], function (e) {
|
||||
if (e) { return void cb(e); }
|
||||
Cryptpad.setAvatar(data, cb);
|
||||
@@ -83,7 +90,7 @@ define([
|
||||
});
|
||||
// Removing the avatar from the profile: unpin it
|
||||
sframeChan.on('Q_PROFILE_AVATAR_REMOVE', function (data, cb) {
|
||||
var chanId = Utils.Hash.hrefToHexChannelId(data);
|
||||
var chanId = Utils.Hash.hrefToHexChannelId(data, null);
|
||||
Cryptpad.unpinPads([chanId], function () {
|
||||
Cryptpad.setAvatar(undefined, cb);
|
||||
});
|
||||
@@ -93,6 +100,7 @@ define([
|
||||
getSecrets: getSecrets,
|
||||
noHash: true, // Don't add the hash in the URL if it doesn't already exist
|
||||
addRpc: addRpc,
|
||||
owned: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ define([
|
||||
'/common/common-feedback.js',
|
||||
'/common/outer/local-store.js',
|
||||
|
||||
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
], function ($, Login, Cryptpad, Test, Cred, UI, Util, Realtime, Constants, Feedback, LocalStore) {
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
@@ -118,15 +118,10 @@ define([
|
||||
}, 500);
|
||||
|
||||
$register.on('keypress', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
console.error(e.which);
|
||||
switch (e.which) {
|
||||
case 13: return clickRegister();
|
||||
case 13: return clickRegister();
|
||||
default:
|
||||
//console.log(e.which);
|
||||
if (e.which === 13) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return clickRegister();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
@import (once) "../../customize/src/less2/include/colortheme-all.less";
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
|
||||
@import (once) "../../customize/src/less2/include/limit-bar.less";
|
||||
@import (once) "../../customize/src/less2/include/creation.less";
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.toolbar_main(
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_settings-bg,
|
||||
@warn-color: @colortheme_settings-warn,
|
||||
@color: @colortheme_settings-color
|
||||
);
|
||||
.alertify_main();
|
||||
.sidebar-layout_main();
|
||||
.limit-bar_main();
|
||||
.creation_main();
|
||||
@@ -21,6 +18,7 @@
|
||||
&.cp-app-settings {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
font: @colortheme_app-font;
|
||||
#cp-sidebarlayout-container {
|
||||
#cp-sidebarlayout-rightside {
|
||||
input[type="checkbox"] {
|
||||
|
||||
@@ -14,7 +14,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 (
|
||||
$,
|
||||
@@ -233,16 +233,14 @@ define([
|
||||
// Disable
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.text(Messages.settings_disableThumbnailsDescription).appendTo($div);
|
||||
var $label = $('<label>', { 'for': 'disableThumbnails', 'class': 'noTitle' })
|
||||
.text(Messages.settings_disableThumbnailsAction);
|
||||
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $checkbox = $('<input>', {
|
||||
'type': 'checkbox',
|
||||
id: 'disableThumbnails'
|
||||
}).on('change', function () {
|
||||
var $cbox = $(UI.createCheckbox('disableThumbnails',
|
||||
Messages.settings_disableThumbnailsAction,
|
||||
false, { label: {class: 'noTitle'} }));
|
||||
var $checkbox = $cbox.find('input').on('change', function () {
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
var val = $checkbox.is(':checked') || false;
|
||||
@@ -252,14 +250,13 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
$checkbox.appendTo($div);
|
||||
$label.appendTo($div);
|
||||
$cbox.appendTo($div);
|
||||
|
||||
$ok.hide().appendTo($div);
|
||||
$spinner.hide().appendTo($div);
|
||||
$ok.hide().appendTo($cbox);
|
||||
$spinner.hide().appendTo($cbox);
|
||||
|
||||
common.getAttribute(['general', 'disableThumbnails'], function (e, val) {
|
||||
$checkbox[0].checked = val;
|
||||
$checkbox[0].checked = typeof(val) === "undefined" || val;
|
||||
});
|
||||
|
||||
// Reset
|
||||
@@ -283,9 +280,6 @@ define([
|
||||
|
||||
$('<span>', {'class': 'label'}).text(Messages.settings_userFeedbackTitle).appendTo($div);
|
||||
|
||||
var $label = $('<label>', { 'for': 'cp-settings-userfeedback', 'class': 'noTitle' })
|
||||
.text(Messages.settings_userFeedback);
|
||||
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.append(Messages.settings_userFeedbackHint1)
|
||||
.append(Messages.settings_userFeedbackHint2).appendTo($div);
|
||||
@@ -293,10 +287,10 @@ define([
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $checkbox = $('<input>', {
|
||||
'type': 'checkbox',
|
||||
id: 'cp-settings-userfeedback'
|
||||
}).on('change', function () {
|
||||
var $cbox = $(UI.createCheckbox('cp-settings-userfeedback',
|
||||
Messages.settings_userFeedback,
|
||||
false, { label: {class: 'noTitle'} }));
|
||||
var $checkbox = $cbox.find('input').on('change', function () {
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
var val = $checkbox.is(':checked') || false;
|
||||
@@ -306,11 +300,10 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
$checkbox.appendTo($div);
|
||||
$label.appendTo($div);
|
||||
$cbox.appendTo($div);
|
||||
|
||||
$ok.hide().appendTo($div);
|
||||
$spinner.hide().appendTo($div);
|
||||
$ok.hide().appendTo($cbox);
|
||||
$spinner.hide().appendTo($cbox);
|
||||
|
||||
if (privateData.feedbackAllowed) {
|
||||
$checkbox[0].checked = true;
|
||||
@@ -326,7 +319,7 @@ define([
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.append(Messages.settings_deleteHint).appendTo($div);
|
||||
|
||||
//var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $button = $('<button>', {'id': 'cp-settings-delete', 'class': 'btn btn-danger'})
|
||||
@@ -334,13 +327,27 @@ define([
|
||||
|
||||
$button.click(function () {
|
||||
$spinner.show();
|
||||
sframeChan.query("Q_SETTINGS_DELETE_ACCOUNT", null, function (err, data) {
|
||||
var msg = h('div.cp-app-settings-delete-alert', [
|
||||
h('p', Messages.settings_deleteModal),
|
||||
h('pre', JSON.stringify(data, 0, 2))
|
||||
]);
|
||||
UI.alert(msg);
|
||||
$spinner.hide();
|
||||
UI.confirm(Messages.settings_deleteConfirm, function (yes) {
|
||||
if (!yes) { return; }
|
||||
sframeChan.query("Q_SETTINGS_DELETE_ACCOUNT", null, function (err, data) {
|
||||
// Owned drive
|
||||
if (data.state === true) {
|
||||
sframeChan.query('Q_SETTINGS_LOGOUT', null, function () {});
|
||||
UI.alert(Messages.settings_deleted, function () {
|
||||
common.gotoURL('/');
|
||||
});
|
||||
$ok.show();
|
||||
$spinner.hide();
|
||||
return;
|
||||
}
|
||||
// Not owned drive
|
||||
var msg = h('div.cp-app-settings-delete-alert', [
|
||||
h('p', Messages.settings_deleteModal),
|
||||
h('pre', JSON.stringify(data, 0, 2))
|
||||
]);
|
||||
UI.alert(msg);
|
||||
$spinner.hide();
|
||||
});
|
||||
});
|
||||
// TODO
|
||||
/*
|
||||
@@ -356,6 +363,7 @@ define([
|
||||
});
|
||||
|
||||
$spinner.hide().appendTo($div);
|
||||
$ok.hide().appendTo($div);
|
||||
|
||||
return $div;
|
||||
};
|
||||
@@ -715,30 +723,28 @@ define([
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $label = $('<label>', { 'for': 'cp-settings-padwidth', 'class': 'noTitle' })
|
||||
.text(Messages.settings_padWidthLabel);
|
||||
var $input = $('<input>', {
|
||||
type: 'checkbox',
|
||||
id: 'cp-settings-padwidth'
|
||||
}).on('change', function () {
|
||||
var $cbox = $(UI.createCheckbox('cp-settings-padwidth',
|
||||
Messages.settings_padWidthLabel,
|
||||
false, { label: {class: 'noTitle'} }));
|
||||
var $checkbox = $cbox.find('input').on('change', function () {
|
||||
$spinner.show();
|
||||
$ok.hide();
|
||||
var val = $input.is(':checked');
|
||||
var val = $checkbox.is(':checked');
|
||||
common.setAttribute(['pad', 'width'], val, function () {
|
||||
$spinner.hide();
|
||||
$ok.show();
|
||||
});
|
||||
}).appendTo($div);
|
||||
$label.appendTo($div);
|
||||
});
|
||||
$cbox.appendTo($div);
|
||||
|
||||
$ok.hide().appendTo($div);
|
||||
$spinner.hide().appendTo($div);
|
||||
$ok.hide().appendTo($cbox);
|
||||
$spinner.hide().appendTo($cbox);
|
||||
|
||||
|
||||
common.getAttribute(['pad', 'width'], function (e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
if (val) {
|
||||
$input.attr('checked', 'checked');
|
||||
$checkbox.attr('checked', 'checked');
|
||||
}
|
||||
});
|
||||
return $div;
|
||||
@@ -790,13 +796,14 @@ define([
|
||||
}).css('flex-flow', 'column')
|
||||
.appendTo($div);
|
||||
|
||||
var $input = $('<input>', {
|
||||
type: 'checkbox',
|
||||
}).on('change', function () {
|
||||
var val = $input.is(':checked');
|
||||
|
||||
var $cbox = $(UI.createCheckbox('cp-settings-codeindent'));
|
||||
var $checkbox = $cbox.find('input').on('change', function () {
|
||||
var val = $checkbox.is(':checked');
|
||||
if (typeof(val) !== 'boolean') { return; }
|
||||
common.setAttribute(['codemirror', key], val);
|
||||
}).appendTo($inputBlock);
|
||||
});
|
||||
$cbox.appendTo($inputBlock);
|
||||
|
||||
/*proxy.on('change', ['settings', 'codemirror', key], function (o, n) {
|
||||
$input[0].checked = !!n;
|
||||
@@ -804,7 +811,7 @@ define([
|
||||
|
||||
common.getAttribute(['codemirror', key], function (e, val) {
|
||||
if (e) { return void console.error(e); }
|
||||
$input[0].checked = !!val;
|
||||
$checkbox[0].checked = !!val;
|
||||
});
|
||||
return $div;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ define([
|
||||
'cm/lib/codemirror',
|
||||
|
||||
'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',
|
||||
|
||||
'css!cm/lib/codemirror.css',
|
||||
@@ -153,53 +153,33 @@ define([
|
||||
$('<b>').text(Messages.printOptions).appendTo($p);
|
||||
$p.append($('<br>'));
|
||||
// Slide number
|
||||
$('<input>', {
|
||||
type: 'checkbox',
|
||||
id: 'cp-app-slide-options-number',
|
||||
checked: slideOptionsTmp.slide
|
||||
}).on('change', function () {
|
||||
var cbox = UI.createCheckbox('cp-app-slide-options-number', Messages.printSlideNumber,
|
||||
slideOptionsTmp.slide);
|
||||
$(cbox).appendTo($p).find('input').on('change', function () {
|
||||
var c = this.checked;
|
||||
slideOptionsTmp.slide = c;
|
||||
}).appendTo($p).css('width', 'auto');
|
||||
$('<label>', {'for': 'cp-app-slide-options-number'}).text(Messages.printSlideNumber)
|
||||
.appendTo($p);
|
||||
$p.append($('<br>'));
|
||||
}).css('width', 'auto');
|
||||
// Date
|
||||
$('<input>', {
|
||||
type: 'checkbox',
|
||||
id: 'cp-app-slide-options-date',
|
||||
checked: slideOptionsTmp.date
|
||||
}).on('change', function () {
|
||||
var cboxDate = UI.createCheckbox('cp-app-slide-options-date', Messages.printDate,
|
||||
slideOptionsTmp.date);
|
||||
$(cboxDate).appendTo($p).find('input').on('change', function () {
|
||||
var c = this.checked;
|
||||
slideOptionsTmp.date = c;
|
||||
}).appendTo($p).css('width', 'auto');
|
||||
$('<label>', {'for': 'cp-app-slide-options-date'}).text(Messages.printDate)
|
||||
.appendTo($p);
|
||||
$p.append($('<br>'));
|
||||
}).css('width', 'auto');
|
||||
// Title
|
||||
$('<input>', {
|
||||
type: 'checkbox',
|
||||
id: 'cp-app-slide-options-title',
|
||||
checked: slideOptionsTmp.title
|
||||
}).on('change', function () {
|
||||
var cboxTitle = UI.createCheckbox('cp-app-slide-options-title', Messages.printTitle,
|
||||
slideOptionsTmp.title);
|
||||
$(cboxTitle).appendTo($p).find('input').on('change', function () {
|
||||
var c = this.checked;
|
||||
slideOptionsTmp.title = c;
|
||||
}).appendTo($p).css('width', 'auto');
|
||||
$('<label>', {'for': 'cp-app-slide-options-title'}).text(Messages.printTitle)
|
||||
.appendTo($p);
|
||||
$p.append($('<br>'));
|
||||
}).css('width', 'auto');
|
||||
// Transition
|
||||
$('<input>', {
|
||||
type: 'checkbox',
|
||||
id: 'cp-app-slide-options-transition',
|
||||
checked: slideOptionsTmp.transition
|
||||
}).on('change', function () {
|
||||
var cboxTransition = UI.createCheckbox('cp-app-slide-options-transition', Messages.printTransition,
|
||||
slideOptionsTmp.transition);
|
||||
$(cboxTransition).appendTo($p).find('input').on('change', function () {
|
||||
var c = this.checked;
|
||||
slideOptionsTmp.transition = c;
|
||||
}).appendTo($p).css('width', 'auto');
|
||||
$('<label>', {'for': 'cp-app-slide-options-transition'}).text(Messages.printTransition)
|
||||
.appendTo($p);
|
||||
$p.append($('<br>'));
|
||||
}).css('width', 'auto');
|
||||
$p.append($('<br>'));
|
||||
// Background image
|
||||
$('<label>', {'for': 'cp-app-slide-options-bg'}).text(Messages.printBackground)
|
||||
@@ -520,9 +500,7 @@ define([
|
||||
dropArea: $('.CodeMirror'),
|
||||
body: $('body'),
|
||||
onUploaded: function (ev, data) {
|
||||
//var cursor = editor.getCursor();
|
||||
//var cleanName = data.name.replace(/[\[\]]/g, '');
|
||||
//var text = '';
|
||||
// PASSWORD_FILES
|
||||
var parsed = Hash.parsePadUrl(data.url);
|
||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
.toolbar_main(
|
||||
.framework_min_main(
|
||||
@bg-color: @colortheme_todo-bg,
|
||||
@warn-color: @colortheme_todo-warn,
|
||||
@color: @colortheme_todo-color
|
||||
);
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
|
||||
// body
|
||||
&.cp-app-todo {
|
||||
|
||||
@@ -12,7 +12,7 @@ define([
|
||||
'/bower_components/sortablejs/Sortable.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 (
|
||||
$,
|
||||
|
||||
@@ -38,7 +38,8 @@ define([
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
var getSecrets = function (Cryptpad, Utils, cb) {
|
||||
Cryptpad.getTodoHash(function (hash) {
|
||||
var nHash = hash || Utils.Hash.createRandomHash();
|
||||
// No password for todo
|
||||
var nHash = hash || Utils.Hash.createRandomHash('todo');
|
||||
if (!hash) { Cryptpad.setTodoHash(nHash); }
|
||||
cb(null, Utils.Hash.getSecrets('todo', nHash));
|
||||
});
|
||||
@@ -46,6 +47,7 @@ define([
|
||||
SFCommonO.start({
|
||||
getSecrets: getSecrets,
|
||||
noHash: true,
|
||||
owned: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/tools.less';
|
||||
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||
@import (once) '../../customize/src/less2/include/creation.less';
|
||||
@import (once) "../../customize/src/less2/include/framework.less";
|
||||
|
||||
.toolbar_main(
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_whiteboard-bg,
|
||||
@warn-color: @colortheme_whiteboard-warn,
|
||||
@color: @colortheme_whiteboard-color
|
||||
);
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
.tokenfield_main();
|
||||
.creation_main();
|
||||
|
||||
// body
|
||||
&.cp-app-whiteboard {
|
||||
|
||||
@@ -20,7 +20,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 (
|
||||
$,
|
||||
@@ -389,7 +389,7 @@ define([
|
||||
var D = Thumb.getResizedDimensions($canvas[0], 'pad');
|
||||
Thumb.fromCanvas($canvas[0], D, function (err, b64) {
|
||||
oldThumbnailState = content;
|
||||
Thumb.setPadThumbnail(common, href, b64);
|
||||
Thumb.setPadThumbnail(common, href, privateDat.channel, b64);
|
||||
});
|
||||
};
|
||||
window.setInterval(mkThumbnail, Thumb.UPDATE_INTERVAL);
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
@import (once) "../../customize/src/less2/include/browser.less";
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) "../../customize/src/less2/include/markdown.less";
|
||||
@import (once) '../../customize/src/less2/include/fileupload.less';
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/avatar.less';
|
||||
@import (once) '../../customize/src/less2/include/framework.less';
|
||||
|
||||
|
||||
.toolbar_main();
|
||||
.fileupload_main();
|
||||
.alertify_main();
|
||||
.framework_min_main();
|
||||
|
||||
// body
|
||||
&.cp-app-worker {
|
||||
|
||||
@@ -8,7 +8,7 @@ define([
|
||||
'/customize/messages.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 (
|
||||
$,
|
||||
|
||||
Reference in New Issue
Block a user