You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cryptpad/www/common/common-ui-elements.js

4094 lines
162 KiB
JavaScript

define([
'jquery',
'/api/config',
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-language.js',
'/common/common-interface.js',
'/common/common-constants.js',
'/common/common-feedback.js',
'/common/hyperscript.js',
7 years ago
'/common/clipboard.js',
'/customize/messages.js',
'/customize/application_config.js',
'/customize/pages.js',
'/bower_components/nthen/index.js',
5 years ago
'/common/inner/invitation.js',
'/common/visible.js',
'css!/customize/fonts/cptools/style.css',
], function ($, Config, Util, Hash, Language, UI, Constants, Feedback, h, Clipboard,
Messages, AppConfig, Pages, NThen, InviteInner, Visible) {
var UIElements = {};
UIElements.getSvgLogo = function () {
var svg = (function(){/*
<svg width="45" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<path d="m22.422 1.4356-14.641 2.7035c-0.51734-0.36799-1.1561-0.57696-1.8393-0.57696a3.1356 3.1339 0 0 0-3.1373 3.1355 3.1602 3.1585 0 0 0 1.6227 2.7575v21.103c0 1.9871 0.88906 4.1328 2.6508 6.3801 1.5602 1.9878 3.7668 4.027 6.5635 6.0547 1.9912 1.4389 3.9942 2.6545 5.6782 3.6005a3.1431 3.1413 0 0 0 5.9855 0.12499c1.7264-0.96163 3.8202-2.224 5.8986-3.7254 2.7979-2.0277 5.0033-4.0668 6.5635-6.0547 1.7624-2.2501 2.6508-4.393 2.6508-6.3801v-21.12c0.95814-0.53813 1.622-1.5642 1.622-2.74 0-1.733-1.4213-3.1355-3.1556-3.1355a3.1075 3.1059 0 0 0-1.8028 0.57696zm-0.03584 3.118 13.379 2.4687c0.01952 0.19629 0.03611 0.37448 0.08998 0.55867l-7.8432 5.1004c-1.4028-1.5215-3.4192-2.4877-5.6439-2.4877-2.2404 0-4.2687 0.98212-5.6797 2.5235l-7.7539-5.028c0.071056-0.21736 0.10472-0.44988 0.12659-0.68519zm14.426 4.4869c0.2616 0.22946 0.5738 0.4256 0.90137 0.55867v20.526c0 0.27043-0.02367 0.55955-0.07169 0.84677-0.31548 1.2546-1.0076 2.5682-2.0734 3.9289-1.3362 1.7018-3.231 3.4367-5.6264 5.172-1.9203 1.3873-3.8601 2.5452-5.3983 3.3932a3.1431 3.1413 0 0 0-4.3574-0.06325c-1.5186-0.84169-3.4128-1.979-5.2825-3.3299-2.3934-1.7353-4.2716-3.4702-5.6072-5.172-1.2729-1.6234-2.017-3.1789-2.2176-4.6492v-20.616c0.31859-0.12253 0.60591-0.30939 0.86477-0.52285l9.9357 6.3976a5.0789 5.0761 0 0 1 4.4893-2.685c1.9639 0 3.6499 1.0816 4.4901 2.7027zm-21.812 6.6849c-0.20732 0.69421-0.33324 1.4177-0.33324 2.1767 0 2.1872 0.94799 4.1262 2.4242 5.5287l-2.7125 5.4541c-0.01034-1.35e-4 -0.02017-0.0015-0.03051-0.0015-1.2807 0-2.3266 1.045-2.3266 2.3246 0 1.2799 1.046 2.3063 2.3266 2.3063a2.3009 2.2996 0 0 0 1.7349-0.78198h3.999v-2.6119h-3.3974l3.1938-6.4136c0.27643-0.55529 0.08702-1.2992-0.42094-1.6546-1.3154-0.91704-2.178-2.4154-2.178-4.1499 0-0.22399 0.03023-0.43738 0.05796-0.65318zm14.737 0.01524-2.3518 1.5372c0.02538 0.20682 0.04347 0.40998 0.04347 0.62422 0 1.7288-0.83863 3.2165-2.149 4.1356-0.50796 0.3555-0.69738 1.0994-0.42094 1.6546l3.1655 6.4281h-3.3829v2.6112h4.1363c0.42696 0.47986 1.0501 0.78274 1.744 0.78274a2.3009 2.2996 0 0 0 2.3076-2.3056c0-1.2799-1.0273-2.3253-2.3076-2.3253-0.05792 0-0.1147 0.0049-0.17158 0.0092l-2.7186-5.4769c1.4697-1.401 2.4106-3.3321 2.4106-5.5143 0-0.74898-0.10409-1.475-0.30503-2.1607zm-7.4398 0.2477a2.1129 2.1118 0 0 0-2.078 2.1111 2.1132 2.1119 0 1 0 4.2262 0 2.1129 2.1118 0 0 0-2.1482-2.1111z" style="stroke-width:1.2608"/>
</svg>
*/}).toString().slice(14,-3);
return svg;
};
UIElements.prettySize = function (bytes) {
var kB = Util.bytesToKilobytes(bytes);
if (kB < 1024) { return kB + Messages.KB; }
var mB = Util.bytesToMegabytes(bytes);
return mB + Messages.MB;
};
UIElements.updateTags = function (common, hrefs) {
var existing, tags;
var allTags = {};
if (!hrefs || typeof (hrefs) === "string") {
hrefs = [hrefs];
}
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) {
var _err;
hrefs.forEach(function (href) {
common.getPadAttribute('tags', waitFor(function (err, res) {
if (err) {
if (err === 'NO_ENTRY') {
UI.alert(Messages.tags_noentry);
}
waitFor.abort();
_err = err;
return void console.error(err);
}
allTags[href] = res || [];
if (tags) {
// Intersect with tags from previous pads
tags = (res || []).filter(function (tag) {
return tags.indexOf(tag) !== -1;
});
} else {
tags = res || [];
}
}), href);
});
}).nThen(function () {
UI.dialog.tagPrompt(tags, existing, function (newTags) {
if (!Array.isArray(newTags)) { return; }
var added = [];
var removed = [];
newTags.forEach(function (tag) {
if (tags.indexOf(tag) === -1) {
added.push(tag);
}
});
tags.forEach(function (tag) {
if (newTags.indexOf(tag) === -1) {
removed.push(tag);
}
});
var update = function (oldTags) {
Array.prototype.push.apply(oldTags, added);
removed.forEach(function (tag) {
var idx = oldTags.indexOf(tag);
oldTags.splice(idx, 1);
});
};
hrefs.forEach(function (href) {
var oldTags = allTags[href] || [];
update(oldTags);
common.setPadAttribute('tags', Util.deduplicateString(oldTags), null, href);
});
});
});
};
var dcAlert;
UIElements.disconnectAlert = function () {
if (dcAlert && $(dcAlert.element).length) { return; }
dcAlert = UI.alert(Messages.common_connectionLost, undefined, true);
};
UIElements.reconnectAlert = function () {
if (!dcAlert) { return; }
if (!dcAlert.delete) {
dcAlert = undefined;
return;
}
dcAlert.delete();
dcAlert = undefined;
};
var importContent = function (type, f, cfg) {
return function () {
var $files = $('<input>', {type:"file"});
if (cfg && cfg.accept) {
$files.attr('accept', cfg.accept);
}
$files.click();
$files.on('change', function (e) {
var file = e.target.files[0];
var reader = new FileReader();
var parsed = file && file.name && /.+\.([^.]+)$/.exec(file.name);
var ext = parsed && parsed[1];
reader.onload = function (e) { f(e.target.result, file, ext); };
if (cfg && cfg.binary && cfg.binary.indexOf(ext) !== -1) {
reader.readAsArrayBuffer(file, type);
} else {
reader.readAsText(file, type);
}
});
};
};
UIElements.getUserGrid = function (label, config, onSelect) {
var common = config.common;
var users = config.data;
if (!users) { return; }
var icons = Object.keys(users).map(function (key, i) {
var data = users[key];
var name = data.displayName || data.name || Messages.anonymous;
var avatar = h('span.cp-usergrid-avatar.cp-avatar');
common.displayAvatar($(avatar), data.avatar, name);
var removeBtn, el;
if (config.remove) {
removeBtn = h('span.fa.fa-times');
$(removeBtn).click(function () {
config.remove(el);
});
}
el = h('div.cp-usergrid-user'+(data.selected?'.cp-selected':'')+(config.large?'.large':''), {
'data-ed': data.edPublic,
'data-teamid': data.teamId,
'data-curve': data.curvePublic || '',
'data-name': name.toLowerCase(),
'data-order': i,
style: 'order:'+i+';'
},[
avatar,
h('span.cp-usergrid-user-name', name),
data.notRemovable ? undefined : removeBtn
]);
return el;
}).filter(function (x) { return x; });
var noOthers = icons.length === 0 ? '.cp-usergrid-empty' : '';
var classes = noOthers + (config.large?'.large':'') + (config.list?'.list':'');
var inputFilter = h('input', {
placeholder: Messages.share_filterFriend
});
var div = h('div.cp-usergrid-container' + classes, [
label ? h('label', label) : undefined,
h('div.cp-usergrid-filter', (config.noFilter || config.noSelect) ? undefined : [
inputFilter
]),
]);
var $div = $(div);
// Hide friends when they are filtered using the text input
var redraw = function () {
var name = $(inputFilter).val().trim().replace(/"/g, '').toLowerCase();
$div.find('.cp-usergrid-user').show();
if (name) {
$div.find('.cp-usergrid-user:not(.cp-selected):not([data-name*="'+name+'"])').hide();
}
};
$(inputFilter).on('keydown keyup change', redraw);
$(div).append(h('div.cp-usergrid-grid', icons));
if (!config.noSelect) {
$div.on('click', '.cp-usergrid-user', function () {
var sel = $(this).hasClass('cp-selected');
if (!sel) {
$(this).addClass('cp-selected');
} else {
var order = $(this).attr('data-order');
order = order ? 'order:'+order : '';
$(this).removeClass('cp-selected').attr('style', order);
}
onSelect();
});
}
5 years ago
return {
icons: icons,
div: div
};
};
var createShareWithFriends = function (config, onShare, linkGetter) {
var common = config.common;
var sframeChan = common.getSframeChannel();
var title = config.title;
var friends = config.friends || {};
var teams = config.teams || {};
5 years ago
var myName = common.getMetadataMgr().getUserData().name;
var order = [];
var smallCurves = Object.keys(friends).map(function (c) {
return friends[c].curvePublic.slice(0,8);
});
5 years ago
var div = h('div.contains-nav');
var $div = $(div);
// Replace "copy link" by "share with friends" if at least one friend is selected
// Also create the "share with friends" button if it doesn't exist
var refreshButtons = function () {
5 years ago
var $nav = $div.closest('.alertify').find('nav');
var friendMode = $div.find('.cp-usergrid-user.cp-selected').length;
if (friendMode) {
$nav.find('button.cp-share-with-friends').prop('disabled', '');
} else {
$nav.find('button.cp-share-with-friends').prop('disabled', 'disabled');
}
};
5 years ago
config.noInclude = true;
Object.keys(friends).forEach(function (curve) {
var data = friends[curve];
if (curve.length > 40 && data.notifications) { return; }
delete friends[curve];
});
var others = [];
if (Object.keys(friends).length) {
var friendsList = UIElements.getUserGrid(Messages.share_linkFriends, {
common: common,
data: friends,
noFilter: false,
large: true
}, refreshButtons);
var friendDiv = friendsList.div;
$div.append(friendDiv);
others = friendsList.icons;
}
if (Object.keys(teams).length) {
var teamsList = UIElements.getUserGrid(Messages.share_linkTeam, {
common: common,
noFilter: true,
large: true,
data: teams
}, refreshButtons);
$div.append(teamsList.div);
}
var shareButton = {
className: 'primary cp-share-with-friends',
name: Messages.share_withFriends,
onClick: function () {
var href;
NThen(function (waitFor) {
var w = waitFor();
// linkGetter can be async if this is a burn after reading URL
var res = linkGetter({}, function (url) {
if (!url) {
waitFor.abort();
return;
}
href = url;
setTimeout(w);
});
if (res && /^http/.test(res)) {
href = Hash.getRelativeHref(res);
setTimeout(w);
return;
}
}).nThen(function () {
var $friends = $div.find('.cp-usergrid-user.cp-selected');
$friends.each(function (i, el) {
var curve = $(el).attr('data-curve');
// Check if the selected element is a friend or a team
if (curve) { // Friend
if (!curve || !friends[curve]) { return; }
var friend = friends[curve];
if (!friend.notifications || !friend.curvePublic) { return; }
common.mailbox.sendTo("SHARE_PAD", {
href: href,
password: config.password,
isTemplate: config.isTemplate,
name: myName,
title: title
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
});
return;
}
// Team
var ed = $(el).attr('data-ed');
var team = teams[ed];
if (!team) { return; }
sframeChan.query('Q_STORE_IN_TEAM', {
href: href,
password: config.password,
path: config.isTemplate ? ['template'] : undefined,
title: title,
teamId: team.id
}, function (err) {
if (err) { return void console.error(err); }
});
});
UI.findCancelButton().click();
// Update the "recently shared with" array:
// Get the selected curves
var curves = $friends.toArray().map(function (el) {
return ($(el).attr('data-curve') || '').slice(0,8);
}).filter(function (x) { return x; });
// Prepend them to the "order" array
Array.prototype.unshift.apply(order, curves);
order = Util.deduplicateString(order);
// Make sure we don't have "old" friends and save
order = order.filter(function (curve) {
return smallCurves.indexOf(curve) !== -1;
});
common.setAttribute(['general', 'share-friends'], order);
if (onShare) {
onShare.fire();
}
});
},
keys: [13]
};
common.getAttribute(['general', 'share-friends'], function (err, val) {
order = val || [];
// Sort friends by "recently shared with"
others.sort(function (a, b) {
var ca = ($(a).attr('data-curve') || '').slice(0,8);
var cb = ($(b).attr('data-curve') || '').slice(0,8);
if (!ca && !cb) { return 0; }
if (!ca) { return 1; }
if (!cb) { return -1; }
var ia = order.indexOf(ca);
var ib = order.indexOf(cb);
if (ia === -1 && ib === -1) { return 0; }
if (ia === -1) { return 1; }
if (ib === -1) { return -1; }
return ia - ib;
});
// Reorder the friend icons
others.forEach(function (el, i) {
$(el).attr('data-order', i).css('order', i);
});
// Display them
$(friendDiv).find('.cp-usergrid-grid').detach();
$(friendDiv).append(h('div.cp-usergrid-grid', others));
refreshButtons();
});
return {
content: div,
buttons: [shareButton]
};
};
var noContactsMessage = function(common){
var metadataMgr = common.getMetadataMgr();
var data = metadataMgr.getUserData();
var origin = metadataMgr.getPrivateData().origin;
if (common.isLoggedIn()) {
return {
content: h('p', Messages.share_noContactsLoggedIn),
buttons: [{
className: 'secondary',
name: Messages.share_copyProfileLink,
onClick: function () {
var profile = data.profile ? (origin + '/profile/#' + data.profile) : '';
var success = Clipboard.copy(profile);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
}]
};
} else {
return {
content: h('p', Messages.share_noContactsNotLoggedIn),
buttons: [{
className: 'secondary',
name: Messages.login_register,
onClick: function () {
common.setLoginRedirect(function () {
common.gotoURL('/register/');
});
}
}, {
className: 'secondary',
name: Messages.login_login,
onClick: function () {
common.setLoginRedirect(function () {
common.gotoURL('/login/');
});
}
}
]
};
}
};
var getEditableTeams = function (common, config) {
var privateData = common.getMetadataMgr().getPrivateData();
var teamsData = Util.tryParse(JSON.stringify(privateData.teams)) || {};
var teams = {};
Object.keys(teamsData).forEach(function (id) {
// config.teamId only exists when we're trying to share a pad from a team drive
// In this case, we don't want to share the pad with the current team
if (config.teamId && config.teamId === id) { return; }
if (!teamsData[id].hasSecondaryKey) { return; }
var t = teamsData[id];
teams[t.edPublic] = {
notifications: true,
displayName: t.name,
edPublic: t.edPublic,
avatar: t.avatar,
id: id
};
});
return teams;
};
var makeBurnAfterReadingUrl = function (common, href, channel, cb) {
var keyPair = Hash.generateSignPair();
var parsed = Hash.parsePadUrl(href);
var newHref = parsed.getUrl({
ownerKey: keyPair.safeSignKey
});
var sframeChan = common.getSframeChannel();
var rtChannel;
NThen(function (waitFor) {
if (parsed.type !== "sheet") { return; }
common.getPadAttribute('rtChannel', waitFor(function (err, chan) {
rtChannel = chan;
}));
}).nThen(function (waitFor) {
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: 'ADD_OWNERS',
value: [keyPair.validateKey]
}, waitFor(function (err) {
if (err) {
waitFor.abort();
UI.warn(Messages.error);
}
}));
if (rtChannel) {
sframeChan.query('Q_SET_PAD_METADATA', {
channel: rtChannel,
command: 'ADD_OWNERS',
value: [keyPair.validateKey]
}, waitFor(function (err) {
if (err) {
console.error(err);
}
}));
}
}).nThen(function () {
cb(newHref);
});
};
7 years ago
UIElements.createShareModal = function (config) {
var origin = config.origin;
var pathname = config.pathname;
var hashes = config.hashes;
var common = config.common;
if (!hashes || (!hashes.editHash && !hashes.viewHash)) { return; }
// check if the pad is password protected
var hash = hashes.editHash || hashes.viewHash;
var href = origin + pathname + '#' + hash;
var parsedHref = Hash.parsePadUrl(href);
var hasPassword = parsedHref.hashData.password;
var makeFaqLink = function () {
var link = h('span', [
h('i.fa.fa-question-circle'),
h('a', {href: '#'}, Messages.passwordFaqLink)
]);
$(link).click(function () {
common.openURL(config.origin + "/faq.html#security-pad_password");
});
return link;
};
var parsed = Hash.parsePadUrl(pathname);
var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1;
var canBAR = parsed.type !== 'drive';
var burnAfterReading = (hashes.viewHash && canBAR) ?
UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, {
mark: {tabindex:1},
label: {style: "display: none;"}
5 years ago
}) : undefined;
var rights = h('div.msg.cp-inline-radio-group', [
h('label', Messages.share_linkAccess),
h('div.radio-group',[
UI.createRadio('accessRights', 'cp-share-editable-false',
Messages.share_linkView, true, { mark: {tabindex:1} }),
canPresent ? UI.createRadio('accessRights', 'cp-share-present',
Messages.share_linkPresent, false, { mark: {tabindex:1} }) : undefined,
UI.createRadio('accessRights', 'cp-share-editable-true',
Messages.share_linkEdit, false, { mark: {tabindex:1} })]),
burnAfterReading
]);
// Burn after reading
// Check if we are an owner of this pad. If we are, we can show the burn after reading option.
// When BAR is selected, display a red message indicating the consequence and add
// the options to generate the BAR url
var barAlert = h('div.alert.alert-danger.cp-alertify-bar-selected', {
style: 'display: none;'
}, Messages.burnAfterReading_warningLink);
var channel = Hash.getSecrets('pad', hash, config.password).channel;
common.getPadMetadata({
channel: channel
}, function (obj) {
if (!obj || obj.error) { return; }
var priv = common.getMetadataMgr().getPrivateData();
// Not an owner: don't display the burn after reading option
if (!Array.isArray(obj.owners) || obj.owners.indexOf(priv.edPublic) === -1) {
$(burnAfterReading).remove();
return;
}
// When the burn after reading option is selected, transform the modal buttons
$(burnAfterReading).css({
display: 'flex'
});
});
5 years ago
var $rights = $(rights);
5 years ago
7 years ago
var saveValue = function () {
5 years ago
var edit = Util.isChecked($rights.find('#cp-share-editable-true'));
var present = Util.isChecked($rights.find('#cp-share-present'));
7 years ago
common.setAttribute(['general', 'share'], {
edit: edit,
present: present
});
};
var burnAfterReadingUrl;
var getLinkValue = function (initValue, cb) {
var val = initValue || {};
5 years ago
var edit = val.edit !== undefined ? val.edit : Util.isChecked($rights.find('#cp-share-editable-true'));
var embed = val.embed;
5 years ago
var present = val.present !== undefined ? val.present : Util.isChecked($rights.find('#cp-share-present'));
var burnAfterReading = Util.isChecked($rights.find('#cp-share-bar'));
if (burnAfterReading && !burnAfterReadingUrl) {
if (cb) { // Called from the contacts tab, "share" button
var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash);
return makeBurnAfterReadingUrl(common, barHref, channel, function (url) {
cb(url);
});
}
return Messages.burnAfterReading_generateLink;
}
var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash : hashes.viewHash;
var href = burnAfterReading ? burnAfterReadingUrl : (origin + pathname + '#' + hash);
7 years ago
var parsed = Hash.parsePadUrl(href);
return origin + parsed.getUrl({embed: embed, present: present});
};
5 years ago
var makeCancelButton = function() {
5 years ago
return {
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
};
};
// Share link tab
var linkContent = config.sharedFolder ? [
h('label', Messages.sharedFolders_share),
h('br'),
] : [
UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }),
];
5 years ago
linkContent.push(h('div.cp-spacer'));
linkContent.push(UI.dialog.selectableArea('', { id: 'cp-share-link-preview', tabindex: 1, rows:3}));
// Show alert if the pad is password protected
if (hasPassword) {
linkContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink()
5 years ago
]));
}
// warning about sharing links
var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable',
{ style: 'display: none;' },
[
h('span.cp-inline-alert-text', Messages.share_linkWarning),
dismissButton
]);
linkContent.push(shareLinkWarning);
localStore.get('hide-alert-shareLinkWarning', function (val) {
if (val === '1') { return; }
$(shareLinkWarning).show();
$(dismissButton).on('click', function () {
localStore.put('hide-alert-shareLinkWarning', '1');
$(shareLinkWarning).remove();
});
});
linkContent.push($(barAlert).clone()[0]); // Burn after reading
var link = h('div.cp-share-modal', linkContent);
5 years ago
var $link = $(link);
var linkButtons = [
makeCancelButton(),
!config.sharedFolder && {
className: 'secondary cp-nobar',
name: Messages.share_linkOpen,
onClick: function () {
saveValue();
var v = getLinkValue({
5 years ago
embed: Util.isChecked($link.find('#cp-share-embed'))
});
window.open(v);
return true;
},
keys: [[13, 'ctrl']]
}, {
className: 'primary cp-nobar',
name: Messages.share_linkCopy,
onClick: function () {
saveValue();
var v = getLinkValue({
5 years ago
embed: Util.isChecked($link.find('#cp-share-embed'))
});
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
}, {
className: 'primary cp-bar',
name: 'GENERATE LINK',
onClick: function () {
var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash);
makeBurnAfterReadingUrl(common, barHref, channel, function (url) {
burnAfterReadingUrl = url;
$rights.find('input[type="radio"]').trigger('change');
});
return true;
},
keys: []
}
];
5 years ago
var frameLink = UI.dialog.customModal(link, {
buttons: linkButtons,
onClose: config.onClose,
});
$(frameLink).find('.cp-bar').hide();
7 years ago
// Share with contacts tab
var teams = getEditableTeams(common, config);
config.teams = teams;
var hasFriends = Object.keys(config.friends || {}).length ||
Object.keys(teams).length;
var onFriendShare = Util.mkEvent();
var friendsObject = hasFriends ? createShareWithFriends(config, onFriendShare, getLinkValue) : noContactsMessage(common);
var friendsList = friendsObject.content;
onFriendShare.reg(saveValue);
var contactsContent = h('div.cp-share-modal');
var $contactsContent = $(contactsContent);
$contactsContent.append(friendsList);
// Show alert if the pad is password protected
if (hasPassword) {
$contactsContent.append(h('div.alert.alert-primary', [
h('i.fa.fa-unlock'),
Messages.share_contactPasswordAlert, h('br'),
makeFaqLink()
5 years ago
]));
}
$(contactsContent).append($(barAlert).clone()); // Burn after reading
var contactButtons = friendsObject.buttons;
contactButtons.unshift(makeCancelButton());
var onShowContacts = function () {
if (!hasFriends) {
$rights.hide();
}
};
var frameContacts = UI.dialog.customModal(contactsContent, {
buttons: contactButtons,
onClose: config.onClose,
});
7 years ago
// Embed tab
var getEmbedValue = function () {
var url = getLinkValue({
embed: true
});
7 years ago
return '<iframe src="' + url + '"></iframe>';
};
var embedContent = [
7 years ago
h('p', Messages.viewEmbedTag),
UI.dialog.selectableArea(getEmbedValue(), { id: 'cp-embed-link-preview', tabindex: 1, rows: 3})
];
// Show alert if the pad is password protected
if (hasPassword) {
embedContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'), ' ',
Messages.share_embedPasswordAlert, h('br'),
makeFaqLink()
5 years ago
]));
}
var embedButtons = [
5 years ago
makeCancelButton(), {
className: 'primary',
7 years ago
name: Messages.share_linkCopy,
onClick: function () {
var v = getEmbedValue();
7 years ago
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
7 years ago
}];
var onShowEmbed = function () {
$rights.find('#cp-share-bar').closest('label').hide();
$rights.find('input[type="radio"]:enabled').first().prop('checked', 'checked');
$rights.find('input[type="radio"]').trigger('change');
};
var embed = h('div.cp-share-modal', embedContent);
var $embed = $(embed);
var frameEmbed = UI.dialog.customModal(embed, {
buttons: embedButtons,
onClose: config.onClose,
});
// update values for link and embed preview when radio btns change
$embed.find('#cp-embed-link-preview').val(getEmbedValue());
$link.find('#cp-share-link-preview').val(getLinkValue());
5 years ago
$rights.find('input[type="radio"]').on('change', function () {
$link.find('#cp-share-link-preview').val(getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
}));
// Hide or show the burn after reading alert
if (Util.isChecked($rights.find('#cp-share-bar')) && !burnAfterReadingUrl) {
$('.cp-alertify-bar-selected').show();
// Show burn after reading button
$('.alertify').find('.cp-bar').show();
$('.alertify').find('.cp-nobar').hide();
return;
}
$embed.find('#cp-embed-link-preview').val(getEmbedValue());
// Hide burn after reading button
$('.alertify').find('.cp-nobar').show();
$('.alertify').find('.cp-bar').hide();
$('.cp-alertify-bar-selected').hide();
});
$link.find('input[type="checkbox"]').on('change', function () {
$link.find('#cp-share-link-preview').val(getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
}));
});
7 years ago
7 years ago
// Create modal
var resetTab = function () {
$rights.show();
$rights.find('label.cp-radio').show();
};
var tabs = [{
title: Messages.share_contactCategory,
icon: "fa fa-address-book",
content: frameContacts,
active: hasFriends,
onShow: onShowContacts,
onHide: resetTab
}, {
title: Messages.share_linkCategory,
icon: "fa fa-link",
content: frameLink,
active: !hasFriends
7 years ago
}, {
title: Messages.share_embedCategory,
icon: "fa fa-code",
content: frameEmbed,
onShow: onShowEmbed,
onHide: resetTab
}];
7 years ago
if (typeof(AppConfig.customizeShareOptions) === 'function') {
AppConfig.customizeShareOptions(hashes, tabs, {
7 years ago
type: 'DEFAULT',
origin: origin,
pathname: pathname
});
}
var modal = UI.dialog.tabs(tabs);
$(modal).find('.alertify-tabs-titles').after(rights);
// disable edit share options if you don't have edit rights
if (!hashes.editHash) {
5 years ago
$rights.find('#cp-share-editable-false').attr('checked', true);
$rights.find('#cp-share-editable-true').removeAttr('checked').attr('disabled', true);
} else if (!hashes.viewHash) {
5 years ago
$rights.find('#cp-share-editable-false').removeAttr('checked').attr('disabled', true);
$rights.find('#cp-share-present').removeAttr('checked').attr('disabled', true);
$rights.find('#cp-share-editable-true').attr('checked', true);
}
7 years ago
common.getAttribute(['general', 'share'], function (err, val) {
val = val || {};
if (val.present && canPresent) {
5 years ago
$rights.find('#cp-share-editable-false').prop('checked', false);
$rights.find('#cp-share-editable-true').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', true);
} else if ((val.edit === false && hashes.viewHash) || !hashes.editHash) {
5 years ago
$rights.find('#cp-share-editable-false').prop('checked', true);
$rights.find('#cp-share-editable-true').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', false);
} else {
5 years ago
$rights.find('#cp-share-editable-true').prop('checked', true);
$rights.find('#cp-share-editable-false').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', false);
7 years ago
}
delete val.embed;
if (!canPresent) {
delete val.present;
}
5 years ago
$link.find('#cp-share-link-preview').val(getLinkValue(val));
7 years ago
});
common.getMetadataMgr().onChange(function () {
// "hashes" is only available is the secure "share" app
var _hashes = common.getMetadataMgr().getPrivateData().hashes;
if (!_hashes) { return; }
hashes = _hashes;
5 years ago
$link.find('#cp-share-link-preview').val(getLinkValue());
7 years ago
});
return modal;
7 years ago
};
7 years ago
UIElements.createFileShareModal = function (config) {
var origin = config.origin;
var pathname = config.pathname;
var hashes = config.hashes;
var common = config.common;
var fileData = config.fileData;
7 years ago
if (!hashes.fileHash) { throw new Error("You must provide a file hash"); }
var url = origin + pathname + '#' + hashes.fileHash;
// check if the file is password protected
var parsedHref = Hash.parsePadUrl(url);
var hasPassword = parsedHref.hashData.password;
var makeFaqLink = function () {
var link = h('span', [
h('i.fa.fa-question-circle'),
h('a', {href: '#'}, Messages.passwordFaqLink)
]);
$(link).click(function () {
common.openURL(config.origin + "/faq.html#security-pad_password");
});
return link;
};
7 years ago
var getLinkValue = function () { return url; };
var makeCancelButton = function() {
return {className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]};
};
// Share link tab
var linkContent = [
UI.dialog.selectableArea(getLinkValue(), { id: 'cp-share-link-preview', tabindex: 1, rows:2 })
];
// Show alert if the pad is password protected
if (hasPassword) {
linkContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink()
]));
}
// warning about sharing links
var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable',
{ style: 'display: none;' },
[
h('span.cp-inline-alert-text', Messages.share_linkWarning),
dismissButton
]);
linkContent.push(shareLinkWarning);
localStore.get('hide-alert-shareLinkWarning', function (val) {
if (val === '1') { return; }
$(shareLinkWarning).show();
$(dismissButton).on('click', function () {
localStore.put('hide-alert-shareLinkWarning', '1');
$(shareLinkWarning).remove();
});
});
var link = h('div.cp-share-modal', linkContent);
var linkButtons = [
makeCancelButton(),
{
className: 'primary',
name: Messages.share_linkCopy,
onClick: function () {
var v = getLinkValue();
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess);
5 years ago
}
},
keys: [13]
}
];
var frameLink = UI.dialog.customModal(link, {
buttons: linkButtons,
onClose: config.onClose,
});
7 years ago
// share with contacts tab
var teams = getEditableTeams(common, config);
config.teams = teams;
var hasFriends = Object.keys(config.friends || {}).length ||
Object.keys(teams).length;
var friendsObject = hasFriends ? createShareWithFriends(config, null, getLinkValue) : noContactsMessage(common);
var friendsList = friendsObject.content;
var contactsContent = h('div.cp-share-modal');
var $contactsContent = $(contactsContent);
$contactsContent.append(friendsList);
// Show alert if the pad is password protected
if (hasPassword) {
$contactsContent.append(h('div.alert.alert-primary', [
h('i.fa.fa-unlock'),
Messages.share_contactPasswordAlert, h('br'),
makeFaqLink()
]));
}
var contactButtons = friendsObject.buttons;
contactButtons.unshift(makeCancelButton());
var frameContacts = UI.dialog.customModal(contactsContent, {
buttons: contactButtons,
onClose: config.onClose,
});
7 years ago
// Embed tab
var embed = h('div.cp-share-modal', [
h('p', Messages.fileEmbedScript),
UI.dialog.selectable(common.getMediatagScript()),
h('p', Messages.fileEmbedTag),
UI.dialog.selectable(common.getMediatagFromHref(fileData)),
7 years ago
]);
// Show alert if the pad is password protected
if (hasPassword) {
embed.append(h('div.alert.alert-primary', [
h('i.fa.fa-lock'), ' ',
Messages.share_embedPasswordAlert, h('br'),
makeFaqLink()
]));
}
7 years ago
var embedButtons = [{
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
}, {
className: 'primary',
7 years ago
name: Messages.share_mediatagCopy,
onClick: function () {
var v = common.getMediatagFromHref(fileData);
7 years ago
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
7 years ago
}];
var frameEmbed = UI.dialog.customModal(embed, {
buttons: embedButtons,
onClose: config.onClose,
});
7 years ago
// Create modal
var tabs = [{
title: Messages.share_contactCategory,
icon: "fa fa-address-book",
content: frameContacts,
active: hasFriends,
}, {
title: Messages.share_linkCategory,
icon: "fa fa-link",
content: frameLink,
active: !hasFriends
7 years ago
}, {
title: Messages.share_embedCategory,
icon: "fa fa-code",
7 years ago
content: frameEmbed
}];
7 years ago
if (typeof(AppConfig.customizeShareOptions) === 'function') {
AppConfig.customizeShareOptions(hashes, tabs, {
7 years ago
type: 'FILE',
origin: origin,
pathname: pathname
});
}
var modal = UI.dialog.tabs(tabs);
return modal;
7 years ago
};
5 years ago
UIElements.createInviteTeamModal = function (config) {
var common = config.common;
var hasFriends = Object.keys(config.friends || {}).length !== 0;
var privateData = common.getMetadataMgr().getPrivateData();
var team = privateData.teams[config.teamId];
if (!team) { return void UI.warn(Messages.error); }
var origin = privateData.origin;
var module = config.module || common.makeUniversal('team');
5 years ago
// Invite contacts
5 years ago
var $div;
var refreshButton = function () {
if (!$div) { return; }
var $modal = $div.closest('.alertify');
var $nav = $modal.find('nav');
var $btn = $nav.find('button.primary');
var selected = $div.find('.cp-usergrid-user.cp-selected').length;
5 years ago
if (selected) {
$btn.prop('disabled', '');
} else {
$btn.prop('disabled', 'disabled');
}
};
var getContacts = function () {
var list = UIElements.getUserGrid(Messages.team_pickFriends, {
common: common,
data: config.friends,
large: true
}, refreshButton);
var div = h('div.contains-nav');
var $div = $(div);
$div.append(list.div);
var contactsButtons = [{
className: 'primary',
name: Messages.team_inviteModalButton,
onClick: function () {
var $sel = $div.find('.cp-usergrid-user.cp-selected');
var sel = $sel.toArray();
if (!sel.length) { return; }
5 years ago
sel.forEach(function (el) {
var curve = $(el).attr('data-curve');
module.execCommand('INVITE_TO_TEAM', {
teamId: config.teamId,
user: config.friends[curve]
}, function (obj) {
if (obj && obj.error) {
console.error(obj.error);
return UI.warn(Messages.error);
}
});
});
},
keys: [13]
}];
return {
content: div,
buttons: contactsButtons
};
};
var friendsObject = hasFriends ? getContacts() : noContactsMessage(common);
var friendsList = friendsObject.content;
var contactsButtons = friendsObject.buttons;
contactsButtons.unshift({
5 years ago
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
});
5 years ago
var contactsContent = h('div.cp-share-modal', [
friendsList
]);
5 years ago
var frameContacts = UI.dialog.customModal(contactsContent, {
buttons: contactsButtons,
});
var linkName, linkPassword, linkMessage, linkError, linkSpinText;
var linkForm, linkSpin, linkResult;
var linkWarning;
// Invite from link
var dismissButton = h('span.fa.fa-times');
var linkContent = h('div.cp-share-modal', [
h('p', Messages.team_inviteLinkTitle ),
linkError = h('div.alert.alert-danger.cp-teams-invite-alert', {style : 'display: none;'}),
linkForm = h('div.cp-teams-invite-form', [
// autofill: 'off' was insufficient
// adding these two fake inputs confuses firefox and prevents unwanted form autofill
h('input', { type: 'text', style: 'display: none'}),
h('input', { type: 'password', style: 'display: none'}),
linkName = h('input', {
placeholder: Messages.team_inviteLinkTempName
}),
h('br'),
h('div.cp-teams-invite-block', [
h('span', Messages.team_inviteLinkSetPassword),
h('a.cp-teams-help.fa.fa-question-circle', {
href: origin + '/faq.html#security-pad_password',
target: "_blank",
'data-tippy-placement': "right"
})
]),
linkPassword = UI.passwordInput({
id: 'cp-teams-invite-password',
placeholder: Messages.login_password
}),
h('div.cp-teams-invite-block',
h('span', Messages.team_inviteLinkNote)
),
linkMessage = h('textarea.cp-teams-invite-message', {
placeholder: Messages.team_inviteLinkNoteMsg,
rows: 3
})
]),
5 years ago
linkSpin = h('div.cp-teams-invite-spinner', {
style: 'display: none;'
}, [
h('i.fa.fa-spinner.fa-spin'),
linkSpinText = h('span', Messages.team_inviteLinkLoading)
]),
linkResult = h('div', {
style: 'display: none;'
}, h('textarea', {
readonly: 'readonly'
})),
linkWarning = h('div.cp-teams-invite-alert.alert.alert-warning.dismissable', {
style: "display: none;"
}, [
h('span.cp-inline-alert-text', Messages.team_inviteLinkWarning),
dismissButton
])
5 years ago
]);
$(linkMessage).keydown(function (e) {
if (e.which === 13) {
e.stopPropagation();
}
});
var localStore = window.cryptpadStore;
localStore.get('hide-alert-teamInvite', function (val) {
if (val === '1') { return; }
$(linkWarning).show();
$(dismissButton).on('click', function () {
localStore.put('hide-alert-teamInvite', '1');
$(linkWarning).remove();
});
});
var $linkContent = $(linkContent);
var href;
var process = function () {
var $nav = $linkContent.closest('.alertify').find('nav');
$(linkError).text('').hide();
var name = $(linkName).val();
var pw = $(linkPassword).find('input').val();
var msg = $(linkMessage).val();
var hash = Hash.createRandomHash('invite', pw);
var hashData = Hash.parseTypeHash('invite', hash);
href = origin + '/teams/#' + hash;
if (!name || !name.trim()) {
$(linkError).text(Messages.team_inviteLinkErrorName).show();
return true;
}
var seeds = InviteInner.deriveSeeds(hashData.key);
var salt = InviteInner.deriveSalt(pw, AppConfig.loginSalt);
var bytes64;
5 years ago
NThen(function (waitFor) {
$(linkForm).hide();
$(linkSpin).show();
$nav.find('button.cp-teams-invite-create').hide();
$nav.find('button.cp-teams-invite-copy').show();
setTimeout(waitFor(), 150);
}).nThen(function (waitFor) {
InviteInner.deriveBytes(seeds.scrypt, salt, waitFor(function (_bytes) {
bytes64 = _bytes;
}));
}).nThen(function (waitFor) {
module.execCommand('CREATE_INVITE_LINK', {
name: name,
password: pw,
message: msg,
bytes64: bytes64,
hash: hash,
teamId: config.teamId,
seeds: seeds,
5 years ago
}, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();
$(linkSpin).hide();
$(linkForm).show();
$nav.find('button.cp-teams-invite-create').show();
$nav.find('button.cp-teams-invite-copy').hide();
return void $(linkError).text(Messages.team_inviteLinkError).show();
}
// Display result here
$(linkSpin).hide();
$(linkResult).show().find('textarea').text(href);
$nav.find('button.cp-teams-invite-copy').prop('disabled', '');
}));
});
return true;
};
var linkButtons = [{
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
}, {
className: 'primary cp-teams-invite-create',
name: Messages.team_inviteLinkCreate,
onClick: function () {
return process();
},
keys: []
}, {
className: 'primary cp-teams-invite-copy',
name: Messages.team_inviteLinkCopy,
onClick: function () {
if (!href) { return; }
var success = Clipboard.copy(href);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: []
}];
var frameLink = UI.dialog.customModal(linkContent, {
buttons: linkButtons,
});
$(frameLink).find('.cp-teams-invite-copy').prop('disabled', 'disabled').hide();
5 years ago
// Create modal
var tabs = [{
title: Messages.share_contactCategory,
icon: "fa fa-address-book",
content: frameContacts,
active: hasFriends
}, {
title: Messages.share_linkCategory,
icon: "fa fa-link",
content: frameLink,
active: !hasFriends
}];
var modal = UI.dialog.tabs(tabs);
5 years ago
UI.openCustomModal(modal);
};
UIElements.createButton = function (common, type, rightside, data, callback) {
var AppConfig = common.getAppConfig();
var button;
var sframeChan = common.getSframeChannel();
var appType = (common.getMetadataMgr().getMetadata().type || 'pad').toUpperCase();
switch (type) {
case 'export':
button = $('<button>', {
'class': 'fa fa-download cp-toolbar-icon-export',
title: Messages.exportButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.exportButton));
button.click(common.prepareFeedback(type));
if (callback) {
button.click(callback);
}
break;
case 'import':
button = $('<button>', {
'class': 'fa fa-upload cp-toolbar-icon-import',
title: Messages.importButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.importButton));
/*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((data && data.binary) ? 'application/octet-stream' : 'text/plain', callback, {
accept: data ? data.accept : undefined,
binary: data ? data.binary : undefined
}));
//}
break;
case 'upload':
button = $('<button>', {
'class': 'btn btn-primary new',
title: Messages.uploadButtonTitle,
}).append($('<span>', {'class':'fa fa-upload'})).append(' '+Messages.uploadButton);
if (!data.FM) { return; }
var $input = $('<input>', {
'type': 'file',
'style': 'display: none;',
'multiple': 'multiple'
}).on('change', function (e) {
var files = Util.slice(e.target.files);
files.forEach(function (file) {
var ev = {
target: data.target
};
if (data.filter && !data.filter(file)) {
return;
}
if (data.transformer) {
data.transformer(file, function (newFile) {
data.FM.handleFile(newFile, ev);
});
return;
}
data.FM.handleFile(file, ev);
});
if (callback) { callback(); }
});
if (data.accept) { $input.attr('accept', data.accept); }
button.click(function () { $input.click(); });
break;
5 years ago
case 'copy':
button = $('<button>', {
'class': 'fa fa-clone cp-toolbar-icon-import',
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.makeACopy));
button
.click(common.prepareFeedback(type))
.click(function () {
sframeChan.query('EV_MAKE_A_COPY');
});
break;
case 'importtemplate':
if (!AppConfig.enableTemplates) { return; }
if (!common.isLoggedIn()) { return; }
button = $('<button>', {
'class': 'fa fa-upload cp-toolbar-icon-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; }
button = $('<button>', {
'class': 'fa fa-bookmark cp-toolbar-icon-template',
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.saveTemplateButton));
if (data.rt) {
button
.click(function () {
var title = data.getTitle() || document.title;
var todo = function (val) {
if (typeof(val) !== "string") { return; }
var toSave = data.rt.getUserDoc();
if (val.trim()) {
val = val.trim();
title = val;
try {
var parsed = JSON.parse(toSave);
var meta;
if (Array.isArray(parsed) && typeof(parsed[3]) === "object") {
meta = parsed[3].metadata; // pad
} else if (parsed.info) {
meta = parsed.info; // poll
} else {
meta = parsed.metadata;
}
if (typeof(meta) === "object") {
meta.title = val;
meta.defaultTitle = val;
delete meta.users;
}
toSave = JSON.stringify(parsed);
} catch(e) {
console.error("Parse error while setting the title", e);
}
}
sframeChan.query('Q_SAVE_AS_TEMPLATE', {
toSave: toSave,
title: title
}, function () {
UI.alert(Messages.templateSaved);
Feedback.send('TEMPLATE_CREATED');
});
};
UI.prompt(Messages.saveTemplatePrompt, title, todo);
});
}
break;
case 'forget':
button = $('<button>', {
'class': "fa fa-trash cp-toolbar-icon-forget"
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.fc_delete));
callback = typeof callback === "function" ? callback : function () {};
button
.click(common.prepareFeedback(type))
.click(function() {
common.isPadStored(function (err, data) {
if (!data) {
return void UI.alert(Messages.autostore_notAvailable);
}
sframeChan.query('Q_IS_ONLY_IN_SHARED_FOLDER', null, function (err, res) {
if (err || res.error) { return void console.log(err || res.error); }
var msg = Messages.forgetPrompt;
if (res) {
UI.alert(Messages.sharedFolders_forget);
return;
} else if (!common.isLoggedIn()) {
msg = Messages.fm_removePermanentlyDialog;
}
UI.confirm(msg, function (yes) {
if (!yes) { return; }
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err, obj) {
err = err || (obj && obj.error);
if (err) {
callback(err);
return void UI.warn(Messages.fm_forbidden);
}
var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
var msg = common.fixLinks($('<div>').html(cMsg));
UI.alert(msg);
callback();
return;
});
});
});
});
});
break;
case 'present':
button = $(h('button', {
//title: Messages.presentButtonTitle, // TODO display if the label text is collapsed
}, [
h('i.fa.fa-play-circle'),
h('span.cp-toolbar-name', Messages.share_linkPresent)
])).click(common.prepareFeedback(type));
break;
case 'preview':
button = $(h('button', {
//title: Messages.previewButtonTitle, // TODO display if the label text is collapsed
}, [
h('i.fa.fa-eye'),
h('span.cp-toolbar-name', Messages.share_linkOpen)
])).click(common.prepareFeedback(type));
break;
case 'print':
button = $('<button>', {
title: Messages.printButtonTitle2,
'class': "fa fa-print cp-toolbar-icon-print",
// XXX people don't realize this does PDF (https://github.com/xwiki-labs/cryptpad/issues/357#issuecomment-640711724)
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.printText));
break;
case 'history':
if (!AppConfig.enableHistory) {
button = $('<span>');
break;
}
var active = $(".cp-toolbar-history:visible").length !== 0;
button = $('<button>', {
title: active ? Messages.history_closeTitle : Messages.historyButton,
'class': "fa fa-history cp-toolbar-icon-history",
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.historyText));
button.toggleClass("active", active);
if (data.histConfig) {
if (active) {
button.click(function () { $(".cp-toolbar-history-close").trigger("click"); });
}
else {
button
.click(common.prepareFeedback(type))
.on('click', function () {
common.getHistory(data.histConfig);
});
}
}
break;
case 'mediatag':
button = $(h('button.cp-toolbar-mediatag', {
//title: Messages.filePickerButton, // TODO display if the label text is collapsed
}, [
h('i.fa.fa-picture-o'),
h('span.cp-toolbar-name', Messages.toolbar_insert)
])).click(common.prepareFeedback(type));
break;
case 'savetodrive':
button = $(h('button.cp-toolbar-savetodrive', {
title: Messages.canvas_saveToDrive,
}, [
h('i.fa.fa-file-image-o'),
h('span.cp-toolbar-name.cp-toolbar-drawer-element', Messages.toolbar_savetodrive)
])).click(common.prepareFeedback(type));
break;
case 'hashtag':
button = $('<button>', {
'class': 'fa fa-hashtag cp-toolbar-icon-hashtag',
title: Messages.tags_title,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.fc_hashtag));
button.click(common.prepareFeedback(type))
.click(function () {
common.isPadStored(function (err, data) {
if (!data) {
return void UI.alert(Messages.autostore_notAvailable);
}
UIElements.updateTags(common, null);
});
});
break;
case 'toggle':
button = $(h('button.cp-toolbar-tools', {
//title: data.title || '', // TODO display if the label text is collapsed
}, [
h('i.fa.fa-wrench'),
h('span.cp-toolbar-name', Messages.toolbar_tools)
])).click(common.prepareFeedback(type));
/*
window.setTimeout(function () {
button.attr('title', data.title);
});
var updateIcon = function (isVisible) {
button.removeClass('fa-caret-down').removeClass('fa-caret-up');
if (!isVisible) { button.addClass('fa-caret-down'); }
else { button.addClass('fa-caret-up'); }
};
*/
button.click(function (e) {
data.element.toggle();
var isVisible = data.element.is(':visible');
if (callback) { callback(isVisible); }
if (isVisible) {
button.addClass('cp-toolbar-button-active');
if (e.originalEvent) { Feedback.send('TOGGLE_SHOW_' + appType); }
} else {
button.removeClass('cp-toolbar-button-active');
if (e.originalEvent) { Feedback.send('TOGGLE_HIDE_' + appType); }
}
//updateIcon(isVisible);
});
//updateIcon(data.element.is(':visible'));
break;
case 'properties':
button = $('<button>', {
'class': 'fa fa-info-circle cp-toolbar-icon-properties',
title: Messages.propertiesButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'})
.text(Messages.propertiesButton))
.click(common.prepareFeedback(type))
.click(function () {
common.isPadStored(function (err, data) {
if (!data) {
return void UI.alert(Messages.autostore_notAvailable);
}
sframeChan.event('EV_PROPERTIES_OPEN');
});
});
break;
case 'save': // OnlyOffice save
button = $('<button>', {
'class': 'fa fa-save',
title: Messages.settings_save,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'})
.text(Messages.settings_save))
.click(common.prepareFeedback(type));
if (callback) { button.click(callback); }
break;
case 'newpad':
button = $('<button>', {
title: Messages.newButtonTitle,
'class': 'fa fa-plus cp-toolbar-icon-newpad',
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.newButton));
button
.click(common.prepareFeedback(type))
.click(function () {
common.createNewPadModal();
});
break;
default:
data = data || {};
var drawerCls = data.drawer === false ? '' : '.cp-toolbar-drawer-element';
var icon = data.icon || "fa-question";
button = $(h('button', {
title: data.tippy || ''
//title: data.title || '',
}, [
h('i.fa.' + icon),
h('span.cp-toolbar-name'+drawerCls, data.text)
])).click(common.prepareFeedback(data.name || 'DEFAULT'));
if (callback) {
button.click(callback);
}
if (data.style) { button.attr('style', data.style); }
if (data.id) { button.attr('id', data.id); }
if (data.hiddenReadOnly) { button.addClass('cp-hidden-if-readonly'); }
if (data.name) {
button.addClass('cp-toolbar-icon-'+data.name);
}
}
if (rightside) {
button.addClass('cp-toolbar-rightside-button');
}
return button;
};
var createMdToolbar = function (common, editor) {
var $toolbar = $('<div>', {
'class': 'cp-markdown-toolbar'
});
var clean = function (str) {
return str.replace(/^(\n)+/, '').replace(/(\n)+$/, '');
};
var actions = {
'bold': {
expr: '**{0}**',
icon: 'fa-bold'
},
'italic': {
expr: '_{0}_',
icon: 'fa-italic'
},
'strikethrough': {
expr: '~~{0}~~',
icon: 'fa-strikethrough'
},
'heading': {
apply: function (str) {
return '\n'+clean(str).split('\n').map(function (line) {
return '# '+line;
}).join('\n')+'\n';
},
icon: 'fa-header'
},
'link': {
expr: '[{0}](http://)',
icon: 'fa-link'
},
'quote': {
apply: function (str) {
return '\n\n'+str.split('\n').map(function (line) {
return '> '+line;
}).join('\n')+'\n\n';
},
icon: 'fa-quote-right'
},
'nlist': {
apply: function (str) {
return '\n'+clean(str).split('\n').map(function (line) {
return '1. '+line;
}).join('\n')+'\n';
},
icon: 'fa-list-ol'
},
'list': {
apply: function (str) {
return '\n'+clean(str).split('\n').map(function (line) {
return '* '+line;
}).join('\n')+'\n';
},
icon: 'fa-list-ul'
},
'check': {
apply: function (str) {
return '\n' + clean(str).split('\n').map(function (line) {
return '* [ ] ' + line;
}).join('\n') + '\n';
},
icon: 'fa-check-square-o'
},
'code': {
apply: function (str) {
if (str.indexOf('\n') !== -1) {
return '\n```\n' + clean(str) + '\n```\n';
}
return '`' + str + '`';
},
icon: 'fa-code'
},
'toc': {
expr: '[TOC]',
icon: 'fa-newspaper-o'
}
};
var onClick = function () {
var type = $(this).attr('data-type');
var texts = editor.getSelections();
var newTexts = texts.map(function (str) {
str = str || Messages.mdToolbar_defaultText;
if (actions[type].apply) {
return actions[type].apply(str);
}
return actions[type].expr.replace('{0}', str);
});
editor.replaceSelections(newTexts, 'around');
editor.focus();
};
for (var k in actions) {
$('<button>', {
'data-type': k,
'class': 'pure-button fa ' + actions[k].icon,
title: Messages['mdToolbar_' + k] || k
}).click(onClick).appendTo($toolbar);
}
$('<button>', {
'class': 'pure-button fa fa-question cp-markdown-help',
title: Messages.mdToolbar_help
}).click(function () {
var href = Messages.mdToolbar_tutorial;
common.openUnsafeURL(href);
}).appendTo($toolbar);
return $toolbar;
};
UIElements.createMarkdownToolbar = function (common, editor) {
var readOnly = common.getMetadataMgr().getPrivateData().readOnly;
if (readOnly) {
return {
toolbar: $(),
button: $(),
setState: function () {}
};
}
var $toolbar = createMdToolbar(common, editor);
var cfg = {
title: Messages.mdToolbar_button,
element: $toolbar
};
var onClick = function (visible) {
common.setAttribute(['general', 'markdown-help'], visible, function (e) {
if (e) { return void console.error(e); }
});
};
var $toolbarButton = common.createButton('toggle', true, cfg, onClick);
var tbState = true;
common.getAttribute(['general', 'markdown-help'], function (e, data) {
if (e) { return void console.error(e); }
if ($(window).height() < 800 && $(window).width() < 800) { return; }
if (data === true && $toolbarButton.length && tbState) {
$toolbarButton.click();
}
});
// setState provides the ability to disable the toolbar and the button in case we don't
// have the markdown editor available (in code we can switch mode, in poll we can publish)
var setState = function (state) {
tbState = state;
if (!state) {
$toolbar.hide();
$toolbarButton.hide();
return;
}
common.getAttribute(['general', 'markdown-help'], function (e, data) {
if (e) { return void console.error(e); }
if ($(window).height() < 800 && $(window).width() < 800) { return; }
if (data === true && $toolbarButton) {
// Show the toolbar using the button to make sure the icon in the button is
// correct (caret-down / caret-up)
$toolbar.hide();
$toolbarButton.click();
return;
}
$toolbar.show();
$toolbarButton.click();
});
$toolbarButton.show();
};
return {
toolbar: $toolbar,
button: $toolbarButton,
setState: setState
};
};
var setHTML = UIElements.setHTML = function (e, html) {
e.innerHTML = html;
return e;
};
UIElements.createHelpMenu = function (common, categories) {
var type = common.getMetadataMgr().getMetadata().type || 'pad';
var elements = [];
if (Messages.help && Messages.help.generic) {
Object.keys(Messages.help.generic).forEach(function (el) {
elements.push(setHTML(h('li'), Messages.help.generic[el]));
});
}
if (categories) {
categories.forEach(function (cat) {
var msgs = Messages.help[cat];
if (msgs) {
Object.keys(msgs).forEach(function (el) {
elements.push(setHTML(h('li'), msgs[el]));
});
}
});
}
var text = h('p.cp-help-text', [
h('h1', Messages.help.title),
h('ul', elements)
]);
7 years ago
common.fixLinks(text);
var closeButton = h('span.cp-help-close.fa.fa-times');
var $toolbarButton = common.createButton('', true, {
title: Messages.hide_help_button,
text: Messages.help_button,
name: 'help'
}).addClass('cp-toolbar-button-active');
var help = h('div.cp-help-container', [
closeButton,
text
]);
var toggleHelp = function (forceClose) {
if ($(help).hasClass('cp-help-hidden')) {
if (forceClose) { return; }
common.setAttribute(['hideHelp', type], false);
$toolbarButton.addClass('cp-toolbar-button-active');
$toolbarButton.attr('title', Messages.hide_help_button);
return void $(help).removeClass('cp-help-hidden');
}
$toolbarButton.removeClass('cp-toolbar-button-active');
$toolbarButton.attr('title', Messages.show_help_button);
$(help).addClass('cp-help-hidden');
common.setAttribute(['hideHelp', type], true);
};
var showMore = function () {
$(text).addClass("cp-help-small");
var $dot = $('<span>').text('...').appendTo($(text).find('h1'));
$(text).click(function () {
$(text).removeClass('cp-help-small');
$(text).off('click');
$dot.remove();
});
};
$(closeButton).click(function (e) {
e.stopPropagation();
toggleHelp(true);
});
$toolbarButton.click(function () {
toggleHelp();
});
common.getAttribute(['hideHelp', type], function (err, val) {
//if ($(window).height() < 800 || $(window).width() < 800) { return void toggleHelp(true); }
if (val === true) { return void toggleHelp(true); }
// Note: Help is always hidden by default now, to avoid displaying to many things in the UI
// This is why we have (true || ...)
if (!val && (true || $(window).height() < 800 || $(window).width() < 800)) {
return void showMore();
}
});
return {
menu: help,
button: $toolbarButton,
text: text
};
};
/* Create a usage bar which keeps track of how much storage space is used
by your CryptDrive. The getPinnedUsage RPC is one of the heavier calls,
so we throttle its usage. Clients will not update more than once per
LIMIT_REFRESH_RATE. It will be update at least once every three such intervals
If changes are made to your drive in the interim, they will trigger an
update.
*/
5 years ago
// NOTE: The callback must stay SYNCHRONOUS
var LIMIT_REFRESH_RATE = 30000; // milliseconds
UIElements.createUsageBar = function (common, teamId, cb) {
if (AppConfig.hideUsageBar) { return cb('USAGE_BAR_HIDDEN'); }
if (!common.isLoggedIn()) { return cb("NOT_LOGGED_IN"); }
// getPinnedUsage updates common.account.usage, and other values
// 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 || !data) { return void console.error(err || 'No data'); }
var usage = data.usage;
var limit = data.limit;
var plan = data.plan;
$container.html('');
var unit = Util.magnitudeOfBytes(limit);
7 years ago
usage = unit === 'GB'? Util.bytesToGigabytes(usage):
Util.bytesToMegabytes(usage);
7 years ago
limit = unit === 'GB'? Util.bytesToGigabytes(limit):
Util.bytesToMegabytes(limit);
var $limit = $('<span>', {'class': 'cp-limit-bar'}).appendTo($container);
var quota = usage/limit;
var $usage = $('<span>', {'class': 'cp-limit-usage'}).css('width', quota*100+'%');
var $buttons = $(h('span.cp-limit-buttons')).appendTo($container);
var urls = common.getMetadataMgr().getPrivateData().accounts;
var makeDonateButton = function () {
6 years ago
var $a = $('<a>', {
'class': 'cp-limit-upgrade btn btn-primary',
href: urls.donateURL,
rel: "noreferrer noopener",
target: "_blank",
}).text(Messages.crowdfunding_button2).appendTo($buttons);
6 years ago
$a.click(function () {
Feedback.send('SUPPORT_CRYPTPAD');
});
};
var makeUpgradeButton = function () {
6 years ago
var $a = $('<a>', {
'class': 'cp-limit-upgrade btn btn-success',
href: urls.upgradeURL,
rel: "noreferrer noopener",
target: "_blank",
}).text(Messages.upgradeAccount).appendTo($buttons);
6 years ago
$a.click(function () {
Feedback.send('UPGRADE_ACCOUNT');
});
};
if (!Config.removeDonateButton) {
if (!common.isLoggedIn() || !Config.allowSubscriptions) {
// user is not logged in, or subscriptions are disallowed
makeDonateButton();
} else if (!plan) {
// user is logged in and subscriptions are allowed
// and they don't have one. show upgrades
makeDonateButton();
makeUpgradeButton();
} else {
// they have a plan. show nothing
}
}
var prettyUsage;
var prettyLimit;
if (unit === 'GB') {
prettyUsage = Messages._getKey('formattedGB', [usage]);
prettyLimit = Messages._getKey('formattedGB', [limit]);
} else {
prettyUsage = Messages._getKey('formattedMB', [usage]);
prettyLimit = Messages._getKey('formattedMB', [limit]);
}
if (quota < 0.8) { $usage.addClass('cp-limit-usage-normal'); }
else if (quota < 1) { $usage.addClass('cp-limit-usage-warning'); }
else { $usage.addClass('cp-limit-usage-above'); }
var $text = $('<span>', {'class': 'cp-limit-usage-text'});
$text.html(Messages._getKey('storageStatus', [prettyUsage, prettyLimit]));
$container.prepend($text);
$limit.append($usage);
};
var updateUsage = Util.notAgainForAnother(function () {
common.getPinUsage(teamId, todo);
}, LIMIT_REFRESH_RATE);
var interval = setInterval(function () {
updateUsage();
}, LIMIT_REFRESH_RATE * 3);
Visible.onChange(function (state) {
if (!state) {
clearInterval(interval);
return;
}
interval = setInterval(function () {
updateUsage();
}, LIMIT_REFRESH_RATE * 3);
updateUsage();
});
updateUsage();
cb(null, $container);
return {
$container: $container,
stop: function () {
clearInterval(interval);
}
};
};
// Create a button with a dropdown menu
// input is a config object with parameters:
// - container (optional): the dropdown container (span)
// - text (optional): the button text value
// - options: array of {tag: "", attributes: {}, content: "string"}
//
// allowed options tags: ['a', 'hr', 'p']
UIElements.createDropdown = function (config) {
if (typeof config !== "object" || !Array.isArray(config.options)) { return; }
if (config.feedback && !config.common) { return void console.error("feedback in a dropdown requires sframe-common"); }
var isElement = function (o) {
return /HTML/.test(Object.prototype.toString.call(o)) &&
typeof(o.tagName) === 'string';
};
var allowedTags = ['a', 'p', 'hr', 'div'];
var isValidOption = function (o) {
if (typeof o !== "object") { return false; }
if (isElement(o)) { return true; }
if (!o.tag || allowedTags.indexOf(o.tag) === -1) { return false; }
return true;
};
// Container
var $container = $(config.container);
var containerConfig = {
'class': 'cp-dropdown-container'
};
if (config.buttonTitle) {
containerConfig.title = config.buttonTitle;
}
if (!config.container) {
$container = $('<span>', containerConfig);
}
// Button
var $button = $('<button>', {
'class': ''
}).append($('<span>', {'class': 'cp-dropdown-button-title'}).html(config.text || ""));
if (config.caretDown) {
$('<span>', {
'class': 'fa fa-caret-down',
}).prependTo($button);
}
// Menu
var $innerblock = $('<div>', {'class': 'cp-dropdown-content'});
if (config.left) { $innerblock.addClass('cp-dropdown-left'); }
config.options.forEach(function (o) {
if (!isValidOption(o)) { return; }
if (isElement(o)) { return $innerblock.append($(o)); }
var $el = $('<' + o.tag + '>', o.attributes || {}).html(o.content || '');
$el.appendTo($innerblock);
if (typeof(o.action) === 'function') {
$el.click(o.action);
}
});
$container.append($button).append($innerblock);
var value = config.initialValue || '';
var setActive = function ($el) {
if ($el.length !== 1) { return; }
$innerblock.find('.cp-dropdown-element-active').removeClass('cp-dropdown-element-active');
$el.addClass('cp-dropdown-element-active');
var scroll = $el.position().top + $innerblock.scrollTop();
if (scroll < $innerblock.scrollTop()) {
$innerblock.scrollTop(scroll);
} else if (scroll > ($innerblock.scrollTop() + 280)) {
$innerblock.scrollTop(scroll-270);
}
};
var hide = function () {
window.setTimeout(function () { $innerblock.hide(); }, 0);
};
var show = function () {
var wh = $(window).height();
var button = $button[0].getBoundingClientRect();
var topPos = button.bottom;
$innerblock.css('bottom', '');
if (config.noscroll) {
var h = $innerblock.outerHeight();
if ((topPos + h) > wh) {
$innerblock.css('bottom', button.height+'px');
}
} else {
$innerblock.css('max-height', Math.floor(wh - topPos - 1)+'px');
}
$innerblock.show();
$innerblock.find('.cp-dropdown-element-active').removeClass('cp-dropdown-element-active');
if (config.isSelect && value) {
var $val = $innerblock.find('[data-value="'+value+'"]');
setActive($val);
$innerblock.scrollTop($val.position().top + $innerblock.scrollTop());
}
if (config.feedback) { Feedback.send(config.feedback); }
};
$container.click(function (e) {
e.stopPropagation();
var state = $innerblock.is(':visible');
$('.cp-dropdown-content').hide();
var $c = $container.closest('.cp-toolbar-drawer-content');
$c.removeClass('cp-dropdown-visible');
if (!state) { $c.addClass('cp-dropdown-visible'); }
try {
$('iframe').each(function (idx, ifrw) {
$(ifrw).contents().find('.cp-dropdown-content').hide();
});
} catch (er) {
// empty try catch in case this iframe is problematic (cross-origin)
}
if (state) {
hide();
return;
}
show();
});
if (config.isSelect) {
var pressed = '';
var to;
$container.on('click', 'a', function () {
value = $(this).data('value');
var $val = $(this);
var textValue = $val.html() || value;
$button.find('.cp-dropdown-button-title').html(textValue);
});
$container.keydown(function (e) {
var $value = $innerblock.find('[data-value].cp-dropdown-element-active:visible');
if (e.which === 38) { // Up
if ($value.length) {
$value.mouseleave();
var $prev = $value.prev();
$prev.mouseenter();
setActive($prev);
}
}
if (e.which === 40) { // Down
if ($value.length) {
$value.mouseleave();
var $next = $value.next();
$next.mouseenter();
setActive($next);
}
}
if (e.which === 13) { //Enter
if ($value.length) {
$value.click();
hide();
}
}
if (e.which === 27) { // Esc
$value.mouseleave();
hide();
}
});
$container.keypress(function (e) {
window.clearTimeout(to);
var c = String.fromCharCode(e.which);
pressed += c;
var $value = $innerblock.find('[data-value^="'+pressed+'"]:first');
if ($value.length) {
setActive($value);
$innerblock.scrollTop($value.position().top + $innerblock.scrollTop());
}
to = window.setTimeout(function () {
pressed = '';
}, 1000);
});
$container.setValue = function (val, name) {
value = val;
var $val = $innerblock.find('[data-value="'+val+'"]');
var textValue = name || $val.html() || val;
$button.find('.cp-dropdown-button-title').html(textValue);
};
$container.getValue = function () {
return value || '';
};
}
return $container;
};
UIElements.displayInfoMenu = function (Common, metadataMgr) {
//var padType = metadataMgr.getMetadata().type;
var priv = metadataMgr.getPrivateData();
var origin = priv.origin;
// TODO link to the most recent changelog/release notes
// https://github.com/xwiki-labs/cryptpad/releases/latest/ ?
var note = function (content) {
return h('span.cp-info-menu-description', content);
};
Messages.info_imprintFlavour = "Legally mandated information about this service's operators can be found <a>here</a>."; // XXX
Messages.info_privacyFlavour = "Our <a>privacy policy</a> describes how we treat your data."; // XXX
Messages.info_faqFlavour = "Consult our <a>FAQ</a> for answers to common questions."; // XXX
var template = function (line, link) {
if (!line || !link) { return; }
var p = $('<p>').html(line)[0];
var model = link.cloneNode(true);
var sub = link.cloneNode(true);
/* This is a hack to make relative URLs point to the main domain
instead of the sandbox domain. It will break if the admins have specified
some less common URL formats for their customizable links, such as if they've
used a protocal-relative absolute URL. The URL API isn't quite safe to use
because of IE (thanks, Bill). */
var href = sub.getAttribute('href');
if (/^\//.test(href)) { sub.setAttribute('href', origin + href); }
var a = p.querySelector('a');
if (!a) { return; }
sub.innerText = a.innerText;
p.replaceChild(sub, a);
return p;
};
var legalLine = template(Messages.info_imprintFlavour, Pages.imprintLink);
var privacyLine = template(Messages.info_privacyFlavour, Pages.privacyLink);
var faqLine = template(Messages.info_faqFlavour, Pages.faqLink);
var content = h('div.cp-info-menu-container', [
h('h6', Pages.versionString),
h('hr'),
legalLine,
privacyLine,
faqLine,
]);
var buttons = [
{
className: 'primary',
name: Messages.filePicker_close,
onClick: function () {},
keys: [27],
},
];
var modal = UI.dialog.customModal(content, {buttons: buttons });
UI.openCustomModal(modal);
};
UIElements.createUserAdminMenu = function (Common, config) {
var metadataMgr = Common.getMetadataMgr();
var displayNameCls = config.displayNameCls || 'cp-toolbar-user-name';
var $displayedName = $('<span>', {'class': displayNameCls});
var priv = metadataMgr.getPrivateData();
var accountName = priv.accountName;
var origin = priv.origin;
var padType = metadataMgr.getMetadata().type;
var $userName = $('<span>');
var options = [];
if (config.displayNameCls) {
var $userAdminContent = $('<p>');
if (accountName) {
var $userAccount = $('<span>').append(Messages.user_accountName + ': ');
$userAdminContent.append($userAccount).append(Util.fixHTML(accountName));
$userAdminContent.append($('<br>'));
}
if (config.displayName && !AppConfig.disableProfile) {
// Hide "Display name:" in read only mode
$userName.append(Messages.user_displayName + ': ');
$userName.append($displayedName);
}
$userAdminContent.append($userName);
options.push({
tag: 'p',
attributes: {'class': 'cp-toolbar-account'},
content: $userAdminContent.html()
});
}
options.push({
tag: 'a',
attributes: {
'target': '_blank',
'href': origin+'/index.html',
'class': 'fa fa-home'
},
content: h('span', Messages.homePage)
});
if (padType !== 'drive' || (!accountName && priv.newSharedFolder)) {
options.push({
tag: 'a',
attributes: {
'target': '_blank',
'href': origin+'/drive/',
'class': 'fa fa-hdd-o'
},
content: h('span', Messages.type.drive)
});
}
if (padType !== 'teams' && accountName) {
options.push({
tag: 'a',
attributes: {
'target': '_blank',
'href': origin+'/teams/',
'class': 'fa fa-users'
},
content: h('span', Messages.type.teams)
});
}
if (padType !== 'contacts' && accountName) {
options.push({
tag: 'a',
attributes: {
'target': '_blank',
'href': origin+'/contacts/',
'class': 'cptools cptools-contacts'
},
content: h('span', Messages.type.contacts)
});
}
options.push({ tag: 'hr' });
// 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 + ' fa fa-user'},
content: h('span', Messages.user_rename)
});
}
if (accountName && !AppConfig.disableProfile) {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-profile fa fa-user-circle'},
content: h('span', Messages.profileButton),
action: function () {
if (padType) {
window.open(origin+'/profile/');
} else {
window.parent.location = origin+'/profile/';
}
},
});
}
if (padType !== 'settings') {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-settings fa fa-cog'},
content: h('span', Messages.settingsButton),
action: function () {
if (padType) {
window.open(origin+'/settings/');
} else {
window.parent.location = origin+'/settings/';
}
},
});
}
options.push({ tag: 'hr' });
6 years ago
// Add administration panel link if the user is an admin
if (priv.edPublic && Array.isArray(Config.adminKeys) && Config.adminKeys.indexOf(priv.edPublic) !== -1) {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-admin fa fa-cogs'},
content: h('span', Messages.adminPage || 'Admin'),
action: function () {
if (padType) {
window.open(origin+'/admin/');
} else {
window.parent.location = origin+'/admin/';
}
},
6 years ago
});
}
if (padType !== 'support' && accountName && Config.supportMailbox) {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-support fa fa-life-ring'},
content: h('span', Messages.supportPage || 'Support'),
action: function () {
if (padType) {
window.open(origin+'/support/');
} else {
window.parent.location = origin+'/support/';
}
},
});
}
options.push({ tag: 'hr' });
if (Config.allowSubscriptions) {
options.push({
tag: 'a',
attributes: {
'target': '_blank',
'href': priv.plan ? priv.accounts.upgradeURL : origin+'/features.html',
'class': 'fa fa-star-o'
},
content: h('span', priv.plan ? Messages.settings_cat_subscription : Messages.pricing)
});
}
if (!priv.plan && !Config.removeDonateButton) {
options.push({
tag: 'a',
attributes: {
'target': '_blank',
'rel': 'noopener',
'href': priv.accounts.donateURL,
'class': 'fa fa-gift'
},
content: h('span', Messages.crowdfunding_button2)
});
}
if (AppConfig.surveyURL) {
options.push({
tag: 'a',
attributes: {
'target': '_blank',
'rel': 'noopener',
'href': AppConfig.surveyURL,
'class': 'cp-toolbar-survey fa fa-graduation-cap'
},
content: h('span', Messages.survey),
action: function () {
Feedback.send('SURVEY_CLICKED');
},
});
}
Messages.user_about = 'About CryptPad'; // XXX
options.push({
tag: 'a',
attributes: {
'class': 'cp-toolbar-about fa fa-info',
},
content: h('span', Messages.user_about),
action: function () {
UIElements.displayInfoMenu(Common, metadataMgr);
},
});
options.push({ tag: 'hr' });
// Add login or logout button depending on the current status
if (priv.loggedIn) {
options.push({
tag: 'a',
attributes: {
'class': 'cp-toolbar-menu-logout-everywhere fa fa-plug',
},
content: h('span', Messages.logoutEverywhere),
action: function () {
Common.getSframeChannel().query('Q_LOGOUT_EVERYWHERE', null, function () {
window.parent.location = origin + '/';
});
},
});
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-logout fa fa-sign-out'},
content: h('span', Messages.logoutButton),
action: function () {
Common.logout(function () {
window.parent.location = origin+'/';
});
},
});
} else {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-login fa fa-sign-in'},
content: h('span', Messages.login_login),
action: function () {
Common.setLoginRedirect(function () {
window.parent.location = origin+'/login/';
});
},
});
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-register fa fa-user-plus'},
content: h('span', Messages.login_register),
action: function () {
Common.setLoginRedirect(function () {
window.parent.location = origin+'/register/';
});
},
});
}
var $icon = $('<span>', {'class': 'fa fa-user-secret'});
//var $userbig = $('<span>', {'class': 'big'}).append($displayedName.clone());
var $userButton = $('<div>').append($icon);//.append($userbig);
if (accountName) {
$userButton = $('<div>').append(accountName);
}
/*if (account && config.displayNameCls) {
$userbig.append($('<span>', {'class': 'account-name'}).text('(' + accountName + ')'));
} else if (account) {
// If no display name, do not display the parentheses
$userbig.append($('<span>', {'class': 'account-name'}).text(accountName));
}*/
var dropdownConfigUser = {
text: $userButton.html(), // Button initial text
options: options, // Entries displayed in the menu
left: true, // Open to the left of the button
container: config.$initBlock, // optional
feedback: "USER_ADMIN",
common: Common
};
var $userAdmin = UIElements.createDropdown(dropdownConfigUser);
/*
// Uncomment these lines to have a language selector in the admin menu
// FIXME clicking on the inner menu hides the outer one
var $lang = UIElements.createLanguageSelector(Common);
$userAdmin.find('.cp-dropdown-content').append($lang);
*/
var $displayName = $userAdmin.find('.'+displayNameCls);
var $avatar = $userAdmin.find('> button .cp-dropdown-button-title');
var loadingAvatar;
var to;
var oldUrl = '';
var updateButton = function () {
var myData = metadataMgr.getUserData();
if (!myData) { return; }
if (loadingAvatar) {
// Try again in 200ms
window.clearTimeout(to);
to = window.setTimeout(updateButton, 200);
return;
}
loadingAvatar = true;
var newName = myData.name;
var url = myData.avatar;
$displayName.text(newName || Messages.anonymous);
if (accountName && oldUrl !== url) {
$avatar.html('');
Common.displayAvatar($avatar, url,
newName || Messages.anonymous, function ($img) {
oldUrl = url;
$userAdmin.find('> button').removeClass('cp-avatar');
if ($img) { $userAdmin.find('> button').addClass('cp-avatar'); }
loadingAvatar = false;
});
return;
}
loadingAvatar = false;
};
metadataMgr.onChange(updateButton);
updateButton();
return $userAdmin;
};
// Provide $container if you want to put the generated block in another element
// Provide $initBlock if you already have the menu block and you want the content inserted in it
UIElements.createLanguageSelector = function (common, $container, $initBlock) {
var options = [];
var languages = Messages._languages;
var keys = Object.keys(languages).sort();
keys.forEach(function (l) {
options.push({
tag: 'a',
attributes: {
'class': 'cp-language-value',
'data-value': l,
'href': '#',
},
content: languages[l] // Pretty name of the language value
});
});
var dropdownConfig = {
text: Messages.language, // Button initial text
options: options, // Entries displayed in the menu
//left: true, // Open to the left of the button
container: $initBlock, // optional
isSelect: true,
common: common
};
var $block = UIElements.createDropdown(dropdownConfig);
$block.attr('id', 'cp-language-selector');
if ($container) {
$block.appendTo($container);
}
Language.initSelector($block, common);
return $block;
};
UIElements.createNewPadModal = function (common) {
// if in drive, show new pad modal instead
if ($(".cp-app-drive-element-row.cp-app-drive-new-ghost").length !== 0) {
return void $(".cp-app-drive-element-row.cp-app-drive-new-ghost").click();
}
var modal = UI.createModal({
id: 'cp-app-toolbar-creation-dialog',
$body: $('body')
});
var $modal = modal.$modal;
var $title = $('<h3>').text(Messages.fm_newFile);
var $description = $('<p>').html(Messages.creation_newPadModalDescription);
$modal.find('.cp-modal').append($title);
$modal.find('.cp-modal').append($description);
var $advanced;
var $container = $('<div>');
var i = 0;
var types = AppConfig.availablePadTypes.filter(function (p) {
if (p === 'drive') { return; }
if (p === 'teams') { return; }
if (p === 'contacts') { return; }
if (p === 'todo') { return; }
if (p === 'file') { return; }
if (!common.isLoggedIn() && AppConfig.registeredOnlyTypes &&
AppConfig.registeredOnlyTypes.indexOf(p) !== -1) { return; }
return true;
});
types.forEach(function (p) {
var $element = $('<li>', {
'class': 'cp-icons-element',
'id': 'cp-newpad-icons-'+ (i++)
}).prepend(UI.getIcon(p)).appendTo($container);
$element.append($('<span>', {'class': 'cp-icons-name'})
.text(Messages.type[p]));
$element.attr('data-type', p);
$element.click(function () {
$modal.hide();
if ($advanced && Util.isChecked($advanced)) {
common.sessionStorage.put(Constants.displayPadCreationScreen, true, function (){
common.openURL('/' + p + '/');
});
return;
}
common.sessionStorage.put(Constants.displayPadCreationScreen, "", function () {
common.openURL('/' + p + '/');
});
});
});
var selected = -1;
var next = function () {
selected = ++selected % types.length;
$container.find('.cp-icons-element-selected').removeClass('cp-icons-element-selected');
$container.find('#cp-newpad-icons-'+selected).addClass('cp-icons-element-selected');
};
$modal.off('keydown');
$modal.keydown(function (e) {
if (e.which === 9) {
e.preventDefault();
e.stopPropagation();
next();
return;
}
if (e.which === 13) {
if ($container.find('.cp-icons-element-selected').length === 1) {
$container.find('.cp-icons-element-selected').click();
}
return;
}
if (e.which === 32 && $advanced) {
$advanced.prop('checked', !$advanced.prop('checked'));
$modal.focus();
e.stopPropagation();
e.preventDefault();
}
});
$modal.find('.cp-modal').append($container);
window.setTimeout(function () {
modal.show();
$modal.focus();
});
};
UIElements.openFilePicker = function (common, types, cb) {
var sframeChan = common.getSframeChannel();
sframeChan.query("Q_FILE_PICKER_OPEN", types, function (err, data) {
if (err) { return; }
cb(data);
});
};
UIElements.openTemplatePicker = function (common, force) {
var metadataMgr = common.getMetadataMgr();
var type = metadataMgr.getMetadataLazy().type;
var sframeChan = common.getSframeChannel();
var focus;
var pickerCfgInit = {
types: [type],
where: ['template'],
hidden: true
};
var pickerCfg = {
types: [type],
where: ['template'],
};
var onConfirm = function (yes) {
if (!yes) {
if (focus) { focus.focus(); }
return;
}
delete pickerCfg.hidden;
var first = true; // We can only pick a template once (for a new document)
common.openFilePicker(pickerCfg, function (data) {
if (data.type === type && first) {
UI.addLoadingScreen({hideTips: true});
var chatChan = common.getPadChat();
var cursorChan = common.getCursorChannel();
sframeChan.query('Q_TEMPLATE_USE', {
href: data.href,
chat: chatChan,
cursor: cursorChan
}, function () {
first = false;
UI.removeLoadingScreen();
Feedback.send('TEMPLATE_USED');
});
if (focus) { focus.focus(); }
return;
}
});
};
sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) {
if (data) {
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);
}
});
};
UIElements.setExpirationValue = function (val, $expire) {
if (val && typeof (val) === "number") {
$expire.find('#cp-creation-expire').attr('checked', true).trigger('change');
$expire.find('#cp-creation-expire-true').attr('checked', true);
if (val % (3600 * 24 * 30) === 0) {
$expire.find('#cp-creation-expire-unit').val("month");
$expire.find('#cp-creation-expire-val').val(val / (3600 * 24 * 30));
return;
}
if (val % (3600 * 24) === 0) {
$expire.find('#cp-creation-expire-unit').val("day");
$expire.find('#cp-creation-expire-val').val(val / (3600 * 24));
return;
}
if (val % 3600 === 0) {
$expire.find('#cp-creation-expire-unit').val("hour");
$expire.find('#cp-creation-expire-val').val(val / 3600);
return;
}
// if we're here, it means we don't have a valid value so we should check unlimited
$expire.find('#cp-creation-expire-false').attr('checked', true);
}
};
UIElements.getPadCreationScreen = function (common, cfg, appCfg, cb) {
6 years ago
appCfg = appCfg || {};
if (!common.isLoggedIn()) { return void cb(); }
var sframeChan = common.getSframeChannel();
var metadataMgr = common.getMetadataMgr();
var privateData = metadataMgr.getPrivateData();
var type = metadataMgr.getMetadataLazy().type;
var fromFileData = privateData.fromFileData;
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
7 years ago
//var colorClass = 'cp-icon-color-'+type;
//$creation.append(h('h2.cp-creation-title', Messages.newButtonTitle));
var newPadH3Title = Messages['button_new' + type];
$creation.append(h('h3.cp-creation-title', newPadH3Title));
//$creation.append(h('h2.cp-creation-title.'+colorClass, Messages.newButtonTitle));
// Deleted pad warning
if (metadataMgr.getPrivateData().isDeleted) {
$creation.append(h('div.cp-creation-deleted-container',
h('div.cp-creation-deleted', Messages.creation_404)
));
}
var origin = privateData.origin;
var createHelper = function (href, text) {
var q = h('a.cp-creation-help.fa.fa-question-circle', {
5 years ago
'data-cptippy-html': true,
title: text,
href: origin + href,
7 years ago
target: "_blank",
'data-tippy-placement': "right"
});
return q;
};
// Team pad
var team;
var teamExists = privateData.teams && Object.keys(privateData.teams).length;
var teamValue;
// storeInTeam can be
// * a team ID ==> store in the team drive, and the team will be the owner
// * -1 ==> store in the user drive, and the user will be the owner
// * undefined ==> ask
if (teamExists) {
var teams = Object.keys(privateData.teams).map(function (id) {
var data = privateData.teams[id];
var avatar = h('span.cp-creation-team-avatar.cp-avatar');
common.displayAvatar($(avatar), data.avatar, data.name);
return h('div.cp-creation-team', {
'data-id': id,
title: data.name,
},[
avatar,
h('span.cp-creation-team-name', data.name)
]);
});
teams.unshift(h('div.cp-creation-team', {
'data-id': '-1',
title: Messages.settings_cat_drive
}, [
h('span.cp-creation-team-avatar.fa.fa-hdd-o'),
h('span.cp-creation-team-name', Messages.settings_cat_drive)
]));
team = h('div.cp-creation-teams', [
Messages.team_pcsSelectLabel,
h('div.cp-creation-teams-grid', teams),
createHelper('#', Messages.team_pcsSelectHelp)
]);
var $team = $(team);
$team.find('.cp-creation-team').click(function () {
if ($(this).hasClass('cp-selected')) {
teamValue = undefined;
return void $(this).removeClass('cp-selected');
}
$team.find('.cp-creation-team').removeClass('cp-selected');
$(this).addClass('cp-selected');
teamValue = $(this).attr('data-id');
});
if (privateData.storeInTeam) {
$team.find('[data-id="'+privateData.storeInTeam+'"]').addClass('cp-selected');
teamValue = privateData.storeInTeam;
}
}
// Owned pads
// Default is Owned pad
var owned = h('div.cp-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', [
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,
max: 100,
value: 3
}),
h('select#cp-creation-expire-unit', [
h('option', { value: 'hour' }, Messages.creation_expireHours),
h('option', { value: 'day' }, Messages.creation_expireDays),
h('option', {
value: 'month',
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 createDiv = h('div.cp-creation-create');
var $create = $(createDiv);
$(h('div#cp-creation-form', [
team,
owned,
expire,
password,
templates,
createDiv
])).appendTo($creation);
// Display templates
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 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;
});
if (!appCfg.noTemplates) {
allData.unshift({
name: Messages.creation_newTemplate,
id: -1,
//icon: h('span.fa.fa-bookmark')
icon: h('span.cptools.cptools-new-template')
});
}
allData.unshift({
name: Messages.creation_noTemplate,
id: 0,
//icon: h('span.fa.fa-file')
icon: UI.getFileIcon({type: type})
});
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'); }
if (!obj.thumbnail) {
$span.append(obj.icon || h('span.cptools.cptools-template'));
}
$('<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'); }
};
if (fromFileData) {
var todo = function (thumbnail) {
allData = [{
name: fromFileData.title,
id: 0,
thumbnail: thumbnail,
icon: h('span.cptools.cptools-file'),
}];
redraw(0);
};
todo();
sframeChan.query("Q_GET_FILE_THUMBNAIL", null, function (err, res) {
if (err || (res && res.error)) { return; }
todo(res.data);
});
}
else {
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 password 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();
});
// Keyboard shortcuts
$creation.find('#cp-creation-expire-val').keydown(function (e) {
if (e.which === 9) {
e.stopPropagation();
}
});
$creation.find('#cp-creation-expire-unit').keydown(function (e) {
if (e.which === 9 && e.shiftKey) {
e.stopPropagation();
}
});
// Initial values
if (!cfg.owned && typeof cfg.owned !== "undefined") {
$creation.find('#cp-creation-owned').prop('checked', false);
}
UIElements.setExpirationValue(cfg.expire, $creation);
// Create the pad
7 years ago
var getFormValues = function () {
// Type of pad
var ownedVal = $('#cp-creation-owned').is(':checked') ? 1 : 0;
// Life time
var expireVal = 0;
if($('#cp-creation-expire').is(':checked')) {
var unit = 0;
switch ($('#cp-creation-expire-unit').val()) {
case "hour" : unit = 3600; break;
case "day" : unit = 3600 * 24; break;
case "month": unit = 3600 * 24 * 30; break;
default: unit = 0;
}
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');
7 years ago
var templateId = $template.data('id') || undefined;
// Team
var team;
if (teamValue) {
team = privateData.teams[teamValue] || {};
team.id = Number(teamValue);
}
return {
owned: ownedVal,
password: passwordVal,
expire: expireVal,
templateId: templateId,
team: team
};
};
var create = function () {
var val = getFormValues();
common.setAttribute(['general', 'creation', 'owned'], val.owned, function (e) {
if (e) { return void console.error(e); }
});
common.setAttribute(['general', 'creation', 'expire'], val.expire, function (e) {
if (e) { return void console.error(e); }
});
6 years ago
if (val.expire) {
Feedback.send('EXPIRING_PAD-'+val.expire);
}
$creationContainer.remove();
common.createPad(val, function () {
cb();
});
};
var $button = $('<button>').text(Messages.creation_create).appendTo($create);
$button.addClass('cp-creation-button-selected');
$button.click(function () {
create();
});
$creation.keydown(function (e) {
if (e.which === 9) {
e.preventDefault();
e.stopPropagation();
next(e.shiftKey);
return;
}
if (e.which === 13) {
$button.click();
return;
}
});
$creation.focus();
};
UIElements.onServerError = function (common, err, toolbar, cb) {
//if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; }
var priv = common.getMetadataMgr().getPrivateData();
var msg = err.type;
if (err.type === 'EEXPIRED') {
msg = Messages.expiredError;
if (err.loaded) {
msg += Messages.errorCopy;
}
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
} else if (err.type === 'EDELETED') {
if (priv.burnAfterReading) { return void cb(); }
msg = Messages.deletedError;
if (err.loaded) {
msg += Messages.errorCopy;
}
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
} else if (err.type === 'ERESTRICTED') {
msg = Messages.restrictedError;
if (toolbar && typeof toolbar.failed === "function") { toolbar.failed(true); }
}
var sframeChan = common.getSframeChannel();
sframeChan.event('EV_SHARE_OPEN', {hidden: true});
UI.errorLoadingScreen(msg, Boolean(err.loaded), Boolean(err.loaded));
(cb || function () {})();
};
UIElements.displayPasswordPrompt = function (common, cfg, 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 $password = $(password);
var button = h('button', Messages.password_submit);
cfg = cfg || {};
if (cfg.value && !isError) {
$password.find('.cp-password-input').val(cfg.value);
}
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, cfg, 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();
};
UIElements.displayBurnAfterReadingPage = function (common, cb) {
var info = h('p.cp-password-info', Messages.burnAfterReading_warningAccess);
var button = h('button.primary', Messages.burnAfterReading_proceed);
$(button).on('click', function () {
cb();
});
var block = h('div#cp-loading-burn-after-reading', [
info,
button
]);
UI.errorLoadingScreen(block);
};
UIElements.getBurnAfterReadingWarning = function (common) {
var priv = common.getMetadataMgr().getPrivateData();
if (!priv.burnAfterReading) { return; }
return h('div.alert.alert-danger.cp-burn-after-reading', Messages.burnAfterReading_warningDeleted);
};
var crowdfundingState = false;
UIElements.displayCrowdfunding = function (common, force) {
if (crowdfundingState) { return; }
var priv = common.getMetadataMgr().getPrivateData();
var todo = function () {
crowdfundingState = true;
// Display the popup
var text = Messages.crowdfunding_popup_text;
var yes = h('button.cp-corner-primary', [
h('span.fa.fa-external-link'),
'OpenCollective'
]);
var no = h('button.cp-corner-cancel', Messages.crowdfunding_popup_no);
var actions = h('div', [no, yes]);
var dontShowAgain = function () {
common.setAttribute(['general', 'crowdfunding'], false);
Feedback.send('CROWDFUNDING_NEVER');
};
var modal = UI.cornerPopup(text, actions, '', {
big: true,
alt: true,
dontShowAgain: dontShowAgain
});
$(yes).click(function () {
modal.delete();
common.openURL(priv.accounts.donateURL);
Feedback.send('CROWDFUNDING_YES');
});
$(modal.popup).find('a').click(function (e) {
e.stopPropagation();
e.preventDefault();
modal.delete();
common.openURL(priv.accounts.donateURL);
Feedback.send('CROWDFUNDING_LINK');
});
$(no).click(function () {
modal.delete();
Feedback.send('CROWDFUNDING_NO');
});
};
if (force) {
crowdfundingState = true;
return void todo();
}
if (AppConfig.disableCrowdfundingMessages) { return; }
if (priv.plan) { return; }
crowdfundingState = true;
common.getAttribute(['general', 'crowdfunding'], function (err, val) {
if (err || val === false) { return; }
common.getSframeChannel().query('Q_GET_PINNED_USAGE', null, function (err, obj) {
var quotaMb = obj.quota / (1024 * 1024);
if (quotaMb < 10) { return; }
todo();
});
});
};
6 years ago
var storePopupState = false;
var autoStoreModal = {};
UIElements.displayStorePadPopup = function (common, data) {
6 years ago
if (storePopupState) { return; }
storePopupState = true;
if (data && data.stored) { return; } // We won't display the popup for dropped files
var priv = common.getMetadataMgr().getPrivateData();
6 years ago
// This pad will be deleted automatically, it shouldn't be stored
if (priv.burnAfterReading) { return; }
var typeMsg = priv.pathname.indexOf('/file/') !== -1 ? Messages.autostore_file :
priv.pathname.indexOf('/drive/') !== -1 ? Messages.autostore_sf :
Messages.autostore_pad;
var text = Messages._getKey('autostore_notstored', [typeMsg]);
var footer = Messages.autostore_settings;
6 years ago
var hide = h('button.cp-corner-cancel', Messages.autostore_hide);
var store = h('button.cp-corner-primary', Messages.autostore_store);
var actions = h('div', [hide, store]);
6 years ago
var initialHide = data && data.autoStore && data.autoStore === -1;
var modal = UI.cornerPopup(text, actions, footer, {hidden: initialHide});
// Once the store pad popup is created, put the crowdfunding one in the queue
UIElements.displayCrowdfunding(common);
autoStoreModal[priv.channel] = modal;
$(modal.popup).find('.cp-corner-footer a').click(function (e) {
e.preventDefault();
common.openURL('/settings/');
});
6 years ago
$(hide).click(function () {
delete autoStoreModal[priv.channel];
6 years ago
modal.delete();
});
var waitingForStoringCb = false;
6 years ago
$(store).click(function () {
if (waitingForStoringCb) { return; }
waitingForStoringCb = true;
6 years ago
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') {
return void UI.warn(Messages.pinLimitReached);
}
return void UI.warn(Messages.autostore_error);
6 years ago
}
$(document).trigger('cpPadStored');
delete autoStoreModal[priv.channel];
modal.delete();
UI.log(Messages.autostore_saved);
6 years ago
});
});
};
UIElements.displayFriendRequestModal = function (common, data) {
var msg = data.content.msg;
5 years ago
var userData = msg.content.user;
var text = Messages._getKey('contacts_request', [Util.fixHTML(userData.displayName)]);
var todo = function (yes) {
common.getSframeChannel().query("Q_ANSWER_FRIEND_REQUEST", {
data: data,
value: yes
}, function (err, obj) {
var error = err || (obj && obj.error);
if (error) {
return void UI.warn(error);
}
if (yes) {
UI.log(Messages.contacts_added);
} else {
UI.log(Messages.contacts_rejected);
}
});
};
var content = h('div.cp-share-modal', [
setHTML(h('p'), text),
]);
5 years ago
UI.proposal(content, todo);
};
UIElements.displayAddOwnerModal = function (common, data) {
var priv = common.getMetadataMgr().getPrivateData();
var sframeChan = common.getSframeChannel();
var msg = data.content.msg;
var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous;
var title = Util.fixHTML(msg.content.title);
var text = Messages._getKey('owner_add', [name, title]);
var link = h('a', {
href: '#'
}, Messages.requestEdit_viewPad);
$(link).click(function (e) {
e.preventDefault();
e.stopPropagation();
if (msg.content.password) {
common.sessionStorage.put('newPadPassword', msg.content.password, function () {
common.openURL(msg.content.href);
});
return;
}
common.openURL(msg.content.href);
});
var div = h('div', [
UI.setHTML(h('p'), text),
link
]);
var answer = function (yes) {
common.mailbox.sendTo("ADD_OWNER_ANSWER", {
channel: msg.content.channel,
href: msg.content.href,
password: msg.content.password,
title: msg.content.title,
5 years ago
answer: yes
}, {
channel: msg.content.user.notifications,
curvePublic: msg.content.user.curvePublic
});
common.mailbox.dismiss(data, function (err) {
console.log(err);
});
};
var todo = function (yes) {
if (yes) {
// ACCEPT
sframeChan.query('Q_SET_PAD_METADATA', {
channel: msg.content.channel,
command: 'ADD_OWNERS',
value: [priv.edPublic]
}, function (err, res) {
err = err || (res && res.error);
if (err) {
5 years ago
var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden
: Messages.error;
return void UI.warn(text);
}
UI.log(Messages.saved);
// Send notification to the sender
answer(true);
var data = JSON.parse(JSON.stringify(msg.content));
data.metadata = res;
// Add the pad to your drive
// This command will also add your mailbox to the metadata log
// The callback is called when the pad is stored, independantly of the metadata command
sframeChan.query('Q_ACCEPT_OWNERSHIP', data, function (err, res) {
if (err || (res && res.error)) {
return void console.error(err | res.error);
}
UI.log(Messages.saved);
if (autoStoreModal[data.channel]) {
autoStoreModal[data.channel].delete();
delete autoStoreModal[data.channel];
}
});
// Remove yourself from the pending owners
sframeChan.query('Q_SET_PAD_METADATA', {
channel: msg.content.channel,
command: 'RM_PENDING_OWNERS',
value: [priv.edPublic]
}, function (err, res) {
err = err || (res && res.error);
if (err) {
console.error(err);
}
});
});
return;
}
// DECLINE
// Remove yourself from the pending owners
sframeChan.query('Q_SET_PAD_METADATA', {
channel: msg.content.channel,
command: 'RM_PENDING_OWNERS',
value: [priv.edPublic]
}, function (err, res) {
err = err || (res && res.error);
if (err) {
console.error(err);
}
// Send notification to the sender
answer(false);
});
};
5 years ago
UI.proposal(div, todo);
};
UIElements.displayAddTeamOwnerModal = function (common, data) {
var priv = common.getMetadataMgr().getPrivateData();
var sframeChan = common.getSframeChannel();
var msg = data.content.msg;
var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous;
var title = Util.fixHTML(msg.content.title);
var text = Messages._getKey('owner_team_add', [name, title]);
var div = h('div', [
UI.setHTML(h('p'), text),
]);
var answer = function (yes) {
common.mailbox.sendTo("ADD_OWNER_ANSWER", {
teamChannel: msg.content.teamChannel,
title: msg.content.title,
5 years ago
answer: yes
}, {
channel: msg.content.user.notifications,
curvePublic: msg.content.user.curvePublic
});
common.mailbox.dismiss(data, function (err) {
if (err) { console.log(err); }
});
};
var module = common.makeUniversal('team');
var addOwner = function (chan, waitFor, cb) {
// Remove yourself from the pending owners
sframeChan.query('Q_SET_PAD_METADATA', {
channel: chan,
command: 'ADD_OWNERS',
value: [priv.edPublic]
}, function (err, res) {
err = err || (res && res.error);
if (!err) { return; }
waitFor.abort();
cb(err);
});
};
var removePending = function (chan, waitFor, cb) {
// Remove yourself from the pending owners
sframeChan.query('Q_SET_PAD_METADATA', {
channel: chan,
command: 'RM_PENDING_OWNERS',
value: [priv.edPublic]
}, waitFor(function (err, res) {
err = err || (res && res.error);
if (!err) { return; }
waitFor.abort();
cb(err);
}));
};
var changeAll = function (add, _cb) {
var f = add ? addOwner : removePending;
var cb = Util.once(_cb);
NThen(function (waitFor) {
f(msg.content.teamChannel, waitFor, cb);
f(msg.content.chatChannel, waitFor, cb);
f(msg.content.rosterChannel, waitFor, cb);
}).nThen(function () { cb(); });
};
var todo = function (yes) {
if (yes) {
// ACCEPT
changeAll(true, function (err) {
if (err) {
console.error(err);
var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden
: Messages.error;
return void UI.warn(text);
}
UI.log(Messages.saved);
// Send notification to the sender
answer(true);
// Mark ourselves as "owner" in our local team data
module.execCommand("ANSWER_OWNERSHIP", {
teamChannel: msg.content.teamChannel,
answer: true
}, function (obj) {
if (obj && obj.error) { console.error(obj.error); }
});
// Remove yourself from the pending owners
changeAll(false, function (err) {
if (err) { console.error(err); }
});
});
return;
}
// DECLINE
// Remove yourself from the pending owners
changeAll(false, function (err) {
if (err) { console.error(err); }
// Send notification to the sender
answer(false);
// Set our role back to ADMIN
module.execCommand("ANSWER_OWNERSHIP", {
teamChannel: msg.content.teamChannel,
answer: false
}, function (obj) {
if (obj && obj.error) { console.error(obj.error); }
});
});
};
UI.proposal(div, todo);
};
5 years ago
UIElements.getVerifiedFriend = function (common, curve, name) {
var priv = common.getMetadataMgr().getPrivateData();
var verified = h('p');
var $verified = $(verified);
if (priv.friends && priv.friends[curve]) {
$verified.addClass('cp-notifications-requestedit-verified');
var f = priv.friends[curve];
$verified.append(h('span.fa.fa-certificate'));
var $avatar = $(h('span.cp-avatar')).appendTo($verified);
$verified.append(h('p', Messages._getKey('isContact', [f.displayName])));
5 years ago
common.displayAvatar($avatar, f.avatar, f.displayName);
} else {
$verified.append(Messages._getKey('isNotContact', [name]));
5 years ago
}
return verified;
};
UIElements.displayInviteTeamModal = function (common, data) {
var msg = data.content.msg;
var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous;
var teamName = Util.fixHTML(Util.find(msg, ['content', 'team', 'metadata', 'name']) || '');
var verified = UIElements.getVerifiedFriend(common, msg.author, name);
var text = Messages._getKey('team_invitedToTeam', [name, teamName]);
5 years ago
var div = h('div', [
UI.setHTML(h('p'), text),
verified
]);
var module = common.makeUniversal('team');
var answer = function (yes) {
common.mailbox.sendTo("INVITE_TO_TEAM_ANSWER", {
answer: yes,
teamChannel: msg.content.team.channel,
5 years ago
teamName: teamName
5 years ago
}, {
channel: msg.content.user.notifications,
curvePublic: msg.content.user.curvePublic
});
common.mailbox.dismiss(data, function (err) {
console.log(err);
});
};
var MAX_TEAMS_SLOTS = Constants.MAX_TEAMS_SLOTS;
5 years ago
var todo = function (yes) {
var priv = common.getMetadataMgr().getPrivateData();
var numberOfTeams = Object.keys(priv.teams || {}).length;
5 years ago
if (yes) {
if (numberOfTeams >= MAX_TEAMS_SLOTS) {
return void UI.alert(Messages._getKey('team_maxTeams', [MAX_TEAMS_SLOTS]));
}
5 years ago
// ACCEPT
module.execCommand('JOIN_TEAM', {
team: msg.content.team
}, function (obj) {
if (obj && obj.error) {
if (obj.error === 'ENOENT') {
common.mailbox.dismiss(data, function () {});
return void UI.alert(Messages.deletedError);
}
return void UI.warn(Messages.error);
}
5 years ago
answer(true);
if (priv.app !== 'teams') { common.openURL('/teams/'); }
5 years ago
});
return;
}
// DECLINE
answer(false);
};
UI.proposal(div, todo);
};
5 years ago
var insertTextAtCursor = function (text) {
var selection = window.getSelection();
var range = selection.getRangeAt(0);
range.deleteContents();
var node = document.createTextNode(text);
range.insertNode(node);
5 years ago
for (var position = 0; position !== text.length; position++) {
5 years ago
selection.modify("move", "right", "character");
5 years ago
}
};
5 years ago
var getSource = {};
getSource['contacts'] = function (common, sources) {
var priv = common.getMetadataMgr().getPrivateData();
Object.keys(priv.friends || {}).forEach(function (key) {
if (key === 'me') { return; }
var f = priv.friends[key];
if (!f.curvePublic || sources[f.curvePublic]) { return; }
sources[f.curvePublic] = {
avatar: f.avatar,
name: f.displayName,
curvePublic: f.curvePublic,
profile: f.profile,
notifications: f.notifications
};
});
};
UIElements.addMentions = function (common, options) {
if (!options.$input) { return; }
var $t = options.$input;
var getValue = function () { return $t.val(); };
var setValue = function (val) { $t.val(val); };
var div = false;
if (options.contenteditable) {
div = true;
getValue = function () { return $t.html(); };
setValue = function () {}; // Not used, we insert data at the node level
$t.on('paste', function (e) {
try {
insertTextAtCursor(e.originalEvent.clipboardData.getData('text'));
e.preventDefault();
5 years ago
} catch (err) { console.error(err); }
5 years ago
});
// Fix backspace with "contenteditable false" children
$t.on('keydown', function (e) {
if (e.which !== 8 && e.which !== 46) { return; } // Backspace or del
var sel = document.getSelection();
if (sel.anchorNode.nodeType !== Node.TEXT_NODE) { return; } // text nodes only
// Only fix node located after mentions
var n = sel.anchorNode;
var prev = n && n.previousSibling;
// Check if our caret is just after a mention
if (!prev || !prev.classList || !prev.classList.contains('cp-mentions')) { return; }
// Del: if we're at offset 0, make sure we won't delete the text node
if (e.which === 46) {
if (!sel.anchorOffset && sel.anchorNode.length === 1) {
sel.anchorNode.nodeValue = " ";
e.preventDefault();
}
return;
}
// Backspace
// If we're not at offset 0, make sure we won't delete the text node
if (e.which === 8 && sel.anchorOffset) {
if (sel.anchorNode.length === 1) {
sel.anchorNode.nodeValue = " ";
e.preventDefault();
}
return;
}
// If we're at offset 0, We're just after a mention: delete it
prev.parentElement.removeChild(prev);
e.preventDefault();
});
}
// Add the sources
// NOTE: Sources must have a "name". They can have an "avatar".
var sources = options.sources || {};
if (!getSource[options.type]) { return; }
getSource[options.type](common, sources);
// Sort autocomplete result by label
var sort = function (a, b) {
var _a = a.label.toLowerCase();
var _b = b.label.toLowerCase();
if (_a.label < _b.label) { return -1; }
if (_b.label < _a.label) { return 1; }
return 0;
};
// Get the text between the last @ before the cursor and the cursor
var extractLast = function (term, offset) {
5 years ago
offset = typeof(offset) !== "undefined" ? offset : $t[0].selectionStart;
5 years ago
var startOffset = term.slice(0,offset).lastIndexOf('@');
return term.slice(startOffset+1, offset);
};
// Insert the autocomplete value in the input field
var insertValue = function (value, offset, content) {
5 years ago
offset = typeof(offset) !== "undefined" ? offset : $t[0].selectionStart;
content = content || getValue();
5 years ago
var startOffset = content.slice(0,offset).lastIndexOf('@');
var length = offset - startOffset;
if (length <= 0) { return; }
var result = content.slice(0,startOffset) + value + content.slice(offset);
if (content) {
return {
result: result,
startOffset: startOffset
};
}
setValue(result);
};
// Set the value to receive from the autocomplete
var toInsert = function (data, key) {
var name = data.name.replace(/[^a-zA-Z0-9]+/g, "-");
return "[@"+name+"|"+key+"]";
};
// Fix the functions when suing a contenteditable div
if (div) {
var _extractLast = extractLast;
// Use getSelection to get the cursor position in contenteditable
extractLast = function () {
var sel = document.getSelection();
if (sel.anchorNode.nodeType !== Node.TEXT_NODE) { return; }
return _extractLast(sel.anchorNode.nodeValue, sel.anchorOffset);
};
var _insertValue = insertValue;
insertValue = function (value) {
// Get the selected node
var sel = document.getSelection();
if (sel.anchorNode.nodeType !== Node.TEXT_NODE) { return; }
var node = sel.anchorNode;
// Remove the "term"
var insert =_insertValue("", sel.anchorOffset, node.nodeValue);
if (insert) {
node.nodeValue = insert.result;
}
var breakAt = insert ? insert.startOffset : sel.anchorOffset;
var el;
if (typeof(value) === "string") { el = document.createTextNode(value); }
else { el = value; }
node.parentNode.insertBefore(el, node.splitText(breakAt));
var next = el.nextSibling;
if (!next) {
next = document.createTextNode(" ");
el.parentNode.appendChild(next);
} else if (next.nodeType === Node.TEXT_NODE && !next.nodeValue) {
next.nodeValue = " ";
}
var range = document.createRange();
range.setStart(next, 0);
range.setEnd(next, 0);
5 years ago
var newSel = window.getSelection();
newSel.removeAllRanges();
newSel.addRange(range);
5 years ago
};
// Inserting contacts into contenteditable: use mention UI
if (options.type === "contacts") {
5 years ago
toInsert = function (data) {
5 years ago
var avatar = h('span.cp-avatar', {
contenteditable: false
});
common.displayAvatar($(avatar), data.avatar, data.name);
return h('span.cp-mentions', {
'data-curve': data.curvePublic,
'data-notifications': data.notifications,
'data-profile': data.profile,
'data-name': Util.fixHTML(data.name),
'data-avatar': data.avatar || "",
}, [
avatar,
h('span.cp-mentions-name', {
contenteditable: false
}, data.name)
]);
};
}
}
// don't navigate away from the field on tab when selecting an item
$t.on("keydown", function(e) {
// Tab or enter
if ((e.which === 13 || e.which === 9)) {
try {
var visible = $t.autocomplete("instance").menu.activeMenu.is(':visible');
if (visible) {
e.preventDefault();
e.stopPropagation();
}
} catch (err) { console.error(err, $t); }
5 years ago
}
}).autocomplete({
minLength: 0,
source: function(data, cb) {
var term = data.term;
var results = [];
if (term.indexOf("@") >= 0) {
term = extractLast(data.term) || '';
results = Object.keys(sources).filter(function (key) {
var data = sources[key];
return data.name.toLowerCase().indexOf(term.toLowerCase()) !== -1;
}).map(function (key) {
var data = sources[key];
return {
label: data.name,
value: key
};
});
results.sort(sort);
}
cb(results);
// Set max-height to the autocomplete dropdown
try {
var max = window.innerHeight;
var pos = $t[0].getBoundingClientRect();
var menu = $t.autocomplete("instance").menu.activeMenu;
menu.css({
'overflow-y': 'auto',
'max-height': (max-pos.bottom)+'px'
});
} catch (e) {}
5 years ago
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
// add the selected item
var key = ui.item.value;
var data = sources[key];
var value = toInsert(data, key);
insertValue(value);
return false;
}
}).autocomplete( "instance" )._renderItem = function( ul, item ) {
var key = item.value;
var obj = sources[key];
if (!obj) { return; }
var avatar = h('span.cp-avatar');
common.displayAvatar($(avatar), obj.avatar, obj.name);
var li = h('li.cp-autocomplete-value', [
avatar,
h('span', obj.name)
]);
return $(li).appendTo(ul);
};
};
return UIElements;
});