Merge branch 'staging' into exportFolderTests
This commit is contained in:
@@ -230,9 +230,15 @@ define([
|
||||
if (!Visible.currently()) { to = window.setTimeout(interval, Thumb.UPDATE_FIRST); }
|
||||
};
|
||||
|
||||
|
||||
var addThumbnail = function (err, thumb, $span, cb) {
|
||||
var u8 = Nacl.util.decodeBase64(thumb.split(',')[1]);
|
||||
var blob = new Blob([u8], {
|
||||
type: 'image/png'
|
||||
});
|
||||
var url = URL.createObjectURL(blob);
|
||||
var img = new Image();
|
||||
img.src = thumb.slice(0,5) === 'data:' ? thumb : 'data:image/png;base64,'+thumb;
|
||||
img.src = url;
|
||||
$span.find('.cp-icon').hide();
|
||||
$span.prepend(img);
|
||||
cb($(img));
|
||||
|
||||
@@ -573,9 +573,9 @@ define([
|
||||
onFriendShare.reg(saveValue);
|
||||
var getLinkValue = function (initValue) {
|
||||
var val = initValue || {};
|
||||
var edit = initValue ? val.edit : Util.isChecked($(link).find('#cp-share-editable-true'));
|
||||
var embed = initValue ? val.embed : Util.isChecked($(link).find('#cp-share-embed'));
|
||||
var present = initValue ? val.present : Util.isChecked($(link).find('#cp-share-present'));
|
||||
var edit = val.edit !== undefined ? val.edit : Util.isChecked($(link).find('#cp-share-editable-true'));
|
||||
var embed = val.embed !== undefined ? val.embed : Util.isChecked($(link).find('#cp-share-embed'));
|
||||
var present = val.present !== undefined ? val.present : Util.isChecked($(link).find('#cp-share-present'));
|
||||
|
||||
var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash : hashes.viewHash;
|
||||
var href = origin + pathname + '#' + hash;
|
||||
@@ -2104,6 +2104,9 @@ define([
|
||||
};
|
||||
|
||||
UIElements.createNewPadModal = function (common) {
|
||||
// if in drive, show new pad modal instead
|
||||
if ($("body.cp-app-drive").length !== 0) { return void $(".cp-app-drive-element-row.cp-app-drive-new-ghost").click(); }
|
||||
|
||||
var $modal = UIElements.createModal({
|
||||
id: 'cp-app-toolbar-creation-dialog',
|
||||
$body: $('body')
|
||||
@@ -2766,8 +2769,12 @@ define([
|
||||
UIElements.displayCrowdfunding(common);
|
||||
modal.delete();
|
||||
});
|
||||
var waitingForStoringCb = false;
|
||||
$(store).click(function () {
|
||||
if (waitingForStoringCb) { return; }
|
||||
waitingForStoringCb = true;
|
||||
common.getSframeChannel().query("Q_AUTOSTORE_STORE", null, function (err, obj) {
|
||||
waitingForStoringCb = false;
|
||||
var error = err || (obj && obj.error);
|
||||
if (error) {
|
||||
if (error === 'E_OVER_LIMIT') {
|
||||
@@ -2854,11 +2861,27 @@ define([
|
||||
'aria-labelledBy': 'dropdownMenu',
|
||||
'style': 'display:block;position:static;margin-bottom:5px;'
|
||||
}, [
|
||||
h('li', h('a.dropdown-item', {
|
||||
h('li', h('a.cp-app-code-context-saveindrive.dropdown-item', {
|
||||
'tabindex': '-1',
|
||||
}, Messages.pad_mediatagImport))
|
||||
'data-icon': "fa-cloud-upload",
|
||||
}, Messages.pad_mediatagImport)),
|
||||
h('li', h('a.cp-app-code-context-download.dropdown-item', {
|
||||
'tabindex': '-1',
|
||||
'data-icon': "fa-download",
|
||||
}, Messages.download_mt_button)),
|
||||
])
|
||||
]);
|
||||
// create the icon for each contextmenu option
|
||||
$(menu).find("li a.dropdown-item").each(function (i, el) {
|
||||
var $icon = $("<span>");
|
||||
if ($(el).attr('data-icon')) {
|
||||
var font = $(el).attr('data-icon').indexOf('cptools') === 0 ? 'cptools' : 'fa';
|
||||
$icon.addClass(font).addClass($(el).attr('data-icon'));
|
||||
} else {
|
||||
$icon.text($(el).text());
|
||||
}
|
||||
$(el).prepend($icon);
|
||||
});
|
||||
var m = createContextMenu(menu);
|
||||
|
||||
mediatagContextMenu = m;
|
||||
@@ -2868,7 +2891,13 @@ define([
|
||||
e.stopPropagation();
|
||||
m.hide();
|
||||
var $mt = $menu.data('mediatag');
|
||||
common.importMediaTag($mt);
|
||||
if ($(this).hasClass("cp-app-code-context-saveindrive")) {
|
||||
common.importMediaTag($mt);
|
||||
}
|
||||
else if ($(this).hasClass("cp-app-code-context-download")) {
|
||||
var media = $mt[0]._mediaObject;
|
||||
window.saveAs(media._blob.content, media.name);
|
||||
}
|
||||
});
|
||||
|
||||
return m;
|
||||
|
||||
@@ -319,6 +319,12 @@ define([], function () {
|
||||
return window.innerHeight < 800 || window.innerWidth < 800;
|
||||
};
|
||||
|
||||
Util.stripTags = function (text) {
|
||||
var div = document.createElement("div");
|
||||
div.innerHTML = text;
|
||||
return div.innerText;
|
||||
};
|
||||
|
||||
return Util;
|
||||
});
|
||||
}(self));
|
||||
|
||||
@@ -52,11 +52,12 @@ define([
|
||||
Object.keys(b).forEach(function (k) { a[k] = b[k]; });
|
||||
};
|
||||
|
||||
var get = function (hash, cb, opt) {
|
||||
var get = function (hash, cb, opt, progress) {
|
||||
if (typeof(cb) !== 'function') {
|
||||
throw new Error('Cryptget expects a callback');
|
||||
}
|
||||
opt = opt || {};
|
||||
progress = progress || function () {};
|
||||
|
||||
var config = makeConfig(hash, opt);
|
||||
var Session = { cb: cb, hasNetwork: Boolean(opt.network) };
|
||||
@@ -65,6 +66,7 @@ define([
|
||||
var rt = Session.session = info.realtime;
|
||||
Session.network = info.network;
|
||||
Session.leave = info.leave;
|
||||
progress(1);
|
||||
finish(Session, void 0, rt.getUserDoc());
|
||||
};
|
||||
|
||||
@@ -72,6 +74,16 @@ define([
|
||||
finish(Session, info.error);
|
||||
};
|
||||
|
||||
// We use the new onMessage handler to compute the progress:
|
||||
// we should receive 2 checkpoints max, so 100 messages max
|
||||
// We're going to consider that 1 message = 1%, and we'll send 100%
|
||||
// at the end
|
||||
var i = 0;
|
||||
config.onMessage = function () {
|
||||
i++;
|
||||
progress(Math.min(0.99, i/100));
|
||||
};
|
||||
|
||||
overwrite(config, opt);
|
||||
|
||||
Session.realtime = CPNetflux.start(config);
|
||||
|
||||
@@ -254,8 +254,12 @@ define([
|
||||
common.clearOwnedChannel = function (channel, cb) {
|
||||
postMessage("CLEAR_OWNED_CHANNEL", channel, cb);
|
||||
};
|
||||
common.removeOwnedChannel = function (channel, cb) {
|
||||
postMessage("REMOVE_OWNED_CHANNEL", channel, cb);
|
||||
// "force" allows you to delete your drive ID
|
||||
common.removeOwnedChannel = function (channel, cb, force) {
|
||||
postMessage("REMOVE_OWNED_CHANNEL", {
|
||||
channel: channel,
|
||||
force: force
|
||||
}, cb);
|
||||
};
|
||||
|
||||
common.getDeletedPads = function (data, cb) {
|
||||
@@ -950,7 +954,7 @@ define([
|
||||
common.logoutFromAll(waitFor(function () {
|
||||
postMessage("DISCONNECT");
|
||||
}));
|
||||
}));
|
||||
}), true);
|
||||
}
|
||||
}).nThen(function (waitFor) {
|
||||
if (!oldIsOwned) {
|
||||
|
||||
@@ -15,6 +15,7 @@ define([
|
||||
|
||||
var DiffDOM = window.diffDOM;
|
||||
var renderer = new Marked.Renderer();
|
||||
var restrictedRenderer = new Marked.Renderer();
|
||||
|
||||
var Mermaid = {
|
||||
init: function () {}
|
||||
@@ -61,13 +62,18 @@ define([
|
||||
return h('div.cp-md-toc', content).outerHTML;
|
||||
};
|
||||
|
||||
DiffMd.render = function (md, sanitize) {
|
||||
DiffMd.render = function (md, sanitize, restrictedMd) {
|
||||
Marked.setOptions({
|
||||
renderer: restrictedMd ? restrictedRenderer : renderer,
|
||||
});
|
||||
var r = Marked(md, {
|
||||
sanitize: sanitize
|
||||
});
|
||||
|
||||
// Add Table of Content
|
||||
r = r.replace(/<div class="cp-md-toc"><\/div>/g, getTOC());
|
||||
if (!restrictedMd) {
|
||||
r = r.replace(/<div class="cp-md-toc"><\/div>/g, getTOC());
|
||||
}
|
||||
toc = [];
|
||||
|
||||
return r;
|
||||
@@ -83,12 +89,7 @@ define([
|
||||
return defaultCode.apply(renderer, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
var stripTags = function (text) {
|
||||
var div = document.createElement("div");
|
||||
div.innerHTML = text;
|
||||
return div.innerText;
|
||||
};
|
||||
restrictedRenderer.code = renderer.code;
|
||||
|
||||
renderer.heading = function (text, level) {
|
||||
var i = 0;
|
||||
@@ -105,10 +106,13 @@ define([
|
||||
toc.push({
|
||||
level: level,
|
||||
id: id,
|
||||
title: stripTags(text)
|
||||
title: Util.stripTags(text)
|
||||
});
|
||||
return "<h" + level + " id=\"" + id + "\"><a href=\"#" + id + "\" class=\"anchor\"></a>" + text + "</h" + level + ">";
|
||||
};
|
||||
restrictedRenderer.heading = function (text) {
|
||||
return text;
|
||||
};
|
||||
|
||||
// Tasks list
|
||||
var checkedTaskItemPtn = /^\s*(<p>)?\[[xX]\](<\/p>)?\s*/;
|
||||
@@ -138,6 +142,13 @@ define([
|
||||
var cls = (isCheckedTaskItem || isUncheckedTaskItem || hasBogusInput) ? ' class="todo-list-item"' : '';
|
||||
return '<li'+ cls + '>' + text + '</li>\n';
|
||||
};
|
||||
restrictedRenderer.listitem = function (text) {
|
||||
if (bogusCheckPtn.test(text)) {
|
||||
text = text.replace(bogusCheckPtn, '');
|
||||
}
|
||||
return '<li>' + text + '</li>\n';
|
||||
};
|
||||
|
||||
renderer.image = function (href, title, text) {
|
||||
if (href.slice(0,6) === '/file/') {
|
||||
// DEPRECATED
|
||||
@@ -162,12 +173,19 @@ define([
|
||||
out += this.options.xhtml ? '/>' : '>';
|
||||
return out;
|
||||
};
|
||||
restrictedRenderer.image = renderer.image;
|
||||
|
||||
var renderParagraph = function (p) {
|
||||
return /<media\-tag[\s\S]*>/i.test(p)? p + '\n': '<p>' + p + '</p>\n';
|
||||
};
|
||||
renderer.paragraph = function (p) {
|
||||
if (p === '[TOC]') {
|
||||
return '<p><div class="cp-md-toc"></div></p>';
|
||||
}
|
||||
return /<media\-tag[\s\S]*>/i.test(p)? p + '\n': '<p>' + p + '</p>\n';
|
||||
return renderParagraph(p);
|
||||
};
|
||||
restrictedRenderer.paragraph = function (p) {
|
||||
return renderParagraph(p);
|
||||
};
|
||||
|
||||
var MutationObserver = window.MutationObserver;
|
||||
|
||||
@@ -30,9 +30,22 @@
|
||||
};
|
||||
|
||||
|
||||
var isplainTextFile = function (metadata) {
|
||||
// does its type begins with "text/"
|
||||
if (metadata.type.indexOf("text/") === 0) { return true; }
|
||||
// no type and no file extension -> let's guess it's plain text
|
||||
var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(metadata.name) || [];
|
||||
if (!metadata.type && !parsedName[2]) { return true; }
|
||||
// other exceptions
|
||||
if (metadata.type === 'application/x-javascript') { return true; }
|
||||
if (metadata.type === 'application/xml') { return true; }
|
||||
return false;
|
||||
};
|
||||
|
||||
// Default config, can be overriden per media-tag call
|
||||
var config = {
|
||||
allowed: [
|
||||
'text/plain',
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
@@ -53,6 +66,23 @@
|
||||
text: "Download"
|
||||
},
|
||||
Plugins: {
|
||||
/**
|
||||
* @param {object} metadataObject {name, metadatatype, owners} containing metadata of the file
|
||||
* @param {strint} url Url of the blob object
|
||||
* @param {Blob} content Blob object containing the data of the file
|
||||
* @param {object} cfg Object {Plugins, allowed, download, pdf} containing infos about plugins
|
||||
* @param {function} cb Callback function: (err, pluginElement) => {}
|
||||
*/
|
||||
text: function (metadata, url, content, cfg, cb) {
|
||||
var plainText = document.createElement('div');
|
||||
plainText.className = "plain-text-reader";
|
||||
var reader = new FileReader();
|
||||
reader.addEventListener('loadend', function (e) {
|
||||
plainText.innerText = e.srcElement.result;
|
||||
cb(void 0, plainText);
|
||||
});
|
||||
reader.readAsText(content);
|
||||
},
|
||||
image: function (metadata, url, content, cfg, cb) {
|
||||
var img = document.createElement('img');
|
||||
img.setAttribute('src', url);
|
||||
@@ -271,6 +301,9 @@
|
||||
var blob = decrypted.content;
|
||||
|
||||
var mediaType = getType(mediaObject, metadata, cfg);
|
||||
if (isplainTextFile(metadata)) {
|
||||
mediaType = "text";
|
||||
}
|
||||
|
||||
if (mediaType === 'application') {
|
||||
mediaType = mediaObject.extension;
|
||||
|
||||
@@ -151,7 +151,7 @@ define([
|
||||
});
|
||||
try {
|
||||
var $d = $(d);
|
||||
DiffMd.apply(DiffMd.render(md || '', true), $d, common);
|
||||
DiffMd.apply(DiffMd.render(md || '', true, true), $d, common);
|
||||
$d.addClass("cp-app-contacts-content");
|
||||
|
||||
// override link clicking, because we're in an iframe
|
||||
@@ -197,7 +197,7 @@ define([
|
||||
var getChat = function (id) {
|
||||
return $messages.find(dataQuery(id));
|
||||
};
|
||||
|
||||
|
||||
var scrollChatToBottom = function () {
|
||||
var $messagebox = $('.cp-app-contacts-messages');
|
||||
$messagebox.scrollTop($messagebox[0].scrollHeight);
|
||||
|
||||
@@ -99,6 +99,7 @@ define(['json.sortify'], function (Sortify) {
|
||||
var addAuthor = function () {
|
||||
if (!meta.user || !meta.user.netfluxId || !priv || !priv.edPublic) { return; }
|
||||
var authors = metadataObj.authors || {};
|
||||
var old = Sortify(authors);
|
||||
if (!authors[priv.edPublic]) {
|
||||
authors[priv.edPublic] = {
|
||||
nId: [meta.user.netfluxId],
|
||||
@@ -110,9 +111,11 @@ define(['json.sortify'], function (Sortify) {
|
||||
authors[priv.edPublic].nId.push(meta.user.netfluxId);
|
||||
}
|
||||
}
|
||||
metadataObj.authors = authors;
|
||||
metadataLazyObj.authors = JSON.parse(JSON.stringify(authors));
|
||||
change();
|
||||
if (Sortify(authors) !== old) {
|
||||
metadataObj.authors = authors;
|
||||
metadataLazyObj.authors = JSON.parse(JSON.stringify(authors));
|
||||
change();
|
||||
}
|
||||
};
|
||||
|
||||
var netfluxId;
|
||||
|
||||
@@ -34,7 +34,7 @@ define([
|
||||
var sendDriveEvent = function () {};
|
||||
var registerProxyEvents = function () {};
|
||||
|
||||
var storeHash;
|
||||
var storeHash, storeChannel;
|
||||
|
||||
var store = window.CryptPad_AsyncStore = {
|
||||
modules: {}
|
||||
@@ -239,6 +239,20 @@ define([
|
||||
|
||||
Store.removeOwnedChannel = function (clientId, data, cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
|
||||
// "data" used to be a string (channelID), now it can also be an object
|
||||
// data.force tells us we can safely remove the drive ID
|
||||
var channel = data;
|
||||
var force = false;
|
||||
if (data && typeof(data) === "object") {
|
||||
channel = data.channel;
|
||||
force = data.force;
|
||||
}
|
||||
|
||||
if (channel === storeChannel && !force) {
|
||||
return void cb({error: 'User drive removal blocked!'});
|
||||
}
|
||||
|
||||
store.rpc.removeOwnedChannel(data, function (err) {
|
||||
cb({error:err});
|
||||
});
|
||||
@@ -573,7 +587,10 @@ define([
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
// Delete Drive
|
||||
Store.removeOwnedChannel(clientId, secret.channel, waitFor());
|
||||
Store.removeOwnedChannel(clientId, {
|
||||
channel: secret.channel,
|
||||
force: true
|
||||
}, waitFor());
|
||||
}).nThen(function () {
|
||||
store.network.disconnect();
|
||||
cb({
|
||||
@@ -786,6 +803,7 @@ define([
|
||||
var h = p.hashData;
|
||||
|
||||
if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); }
|
||||
if (p.type === "debug") { return void cb(); }
|
||||
|
||||
var channelData = Store.channels && Store.channels[channel];
|
||||
|
||||
@@ -1090,7 +1108,6 @@ define([
|
||||
var channels = Store.channels = store.channels = {};
|
||||
|
||||
Store.joinPad = function (clientId, data) {
|
||||
console.log('joining', data.channel);
|
||||
var isNew = typeof channels[data.channel] === "undefined";
|
||||
var channel = channels[data.channel] = channels[data.channel] || {
|
||||
queue: [],
|
||||
@@ -1915,6 +1932,7 @@ define([
|
||||
}
|
||||
// No password for drive
|
||||
var secret = Hash.getSecrets('drive', hash);
|
||||
storeChannel = secret.channel;
|
||||
var listmapConfig = {
|
||||
data: {},
|
||||
websocketURL: NetConfig.getWebsocketURL(),
|
||||
|
||||
@@ -99,9 +99,17 @@ define([
|
||||
// lines beginning with a hash are potentially valuable
|
||||
// works for markdown, python, bash, etc.
|
||||
var hash = /^#+(.*?)$/;
|
||||
var hashAndLink = /^#+\s*\[(.*?)\]\(.*\)\s*$/;
|
||||
if (hash.test(line)) {
|
||||
// test for link inside the title, and set text just to the name of the link
|
||||
if (hashAndLink.test(line)) {
|
||||
line.replace(hashAndLink, function (a, one) {
|
||||
text = Util.stripTags(one);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
line.replace(hash, function (a, one) {
|
||||
text = one;
|
||||
text = Util.stripTags(one);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -387,21 +395,32 @@ define([
|
||||
exp.mkIndentSettings = function (metadataMgr) {
|
||||
var setIndentation = function (units, useTabs, fontSize, spellcheck) {
|
||||
if (typeof(units) !== 'number') { return; }
|
||||
var doc = editor.getDoc();
|
||||
editor.setOption('indentUnit', units);
|
||||
editor.setOption('tabSize', units);
|
||||
editor.setOption('indentWithTabs', useTabs);
|
||||
editor.setOption('spellcheck', spellcheck);
|
||||
if (!useTabs) {
|
||||
editor.setOption("extraKeys", {
|
||||
Tab: function() {
|
||||
editor.replaceSelection(Array(units + 1).join(" "));
|
||||
editor.setOption("extraKeys", {
|
||||
Tab: function() {
|
||||
if (doc.somethingSelected()) {
|
||||
editor.execCommand("indentMore");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
editor.setOption("extraKeys", {
|
||||
Tab: undefined,
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (!useTabs) { editor.execCommand("insertSoftTab"); }
|
||||
else { editor.execCommand("insertTab"); }
|
||||
}
|
||||
},
|
||||
"Shift-Tab": function () {
|
||||
editor.execCommand("indentLess");
|
||||
},
|
||||
"Backspace": function () {
|
||||
var cursor = doc.getCursor();
|
||||
var line = doc.getLine(cursor.line);
|
||||
if (line.substring(0, cursor.ch).trim() === "") { editor.execCommand("indentLess"); }
|
||||
else { editor.execCommand("delCharBefore"); }
|
||||
|
||||
},
|
||||
});
|
||||
$('.CodeMirror').css('font-size', fontSize+'px');
|
||||
};
|
||||
|
||||
|
||||
@@ -838,13 +838,6 @@ define([
|
||||
Cryptpad.setLanguage(data, cb);
|
||||
});
|
||||
|
||||
sframeChan.on('Q_CLEAR_OWNED_CHANNEL', function (channel, cb) {
|
||||
Cryptpad.clearOwnedChannel(channel, cb);
|
||||
});
|
||||
sframeChan.on('Q_REMOVE_OWNED_CHANNEL', function (channel, cb) {
|
||||
Cryptpad.removeOwnedChannel(channel, cb);
|
||||
});
|
||||
|
||||
sframeChan.on('Q_GET_ALL_TAGS', function (data, cb) {
|
||||
Cryptpad.listAllTags(function (err, tags) {
|
||||
cb({
|
||||
|
||||
Reference in New Issue
Block a user