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

4899 lines
193 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',
'/common/media-tag.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',
'css!/customize/fonts/cptools/style.css',
'/bower_components/croppie/croppie.min.js',
'css!/bower_components/croppie/croppie.css',
7 years ago
], function ($, Config, Util, Hash, Language, UI, Constants, Feedback, h, MediaTag, Clipboard,
Messages, AppConfig, Pages, NThen, InviteInner) {
var UIElements = {};
// Configure MediaTags to use our local viewer
if (MediaTag) {
MediaTag.setDefaultConfig('pdf', {
viewer: '/common/pdfjs/web/viewer.html'
});
}
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, href) {
var existing, tags;
NThen(function(waitFor) {
common.getSframeChannel().query("Q_GET_ALL_TAGS", null, waitFor(function(err, res) {
if (err || res.error) { return void console.error(err || res.error); }
existing = Object.keys(res.tags).sort();
}));
}).nThen(function (waitFor) {
common.getPadAttribute('tags', waitFor(function (err, res) {
if (err) {
if (err === 'NO_ENTRY') {
UI.alert(Messages.tags_noentry);
}
waitFor.abort();
return void console.error(err);
}
tags = res || [];
}), href);
}).nThen(function () {
UI.dialog.tagPrompt(tags, existing, function (newTags) {
if (!Array.isArray(newTags)) { return; }
common.setPadAttribute('tags', newTags, 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);
}
});
};
};
/*
var getPropertiesData = function (common, cb) {
var data = {};
NThen(function (waitFor) {
7 years ago
var base = common.getMetadataMgr().getPrivateData().origin;
common.getPadAttribute('', waitFor(function (err, val) {
if (err || !val) {
waitFor.abort();
return void cb(err || 'EEMPTY');
}
if (!val.fileType) {
delete val.owners;
delete val.expire;
}
Util.extend(data, val);
if (data.href) { data.href = base + data.href; }
if (data.roHref) { data.roHref = base + data.roHref; }
}));
common.getPadMetadata(null, waitFor(function (obj) {
if (obj && obj.error) { return; }
data.owners = obj.owners;
data.expire = obj.expire;
data.pending_owners = obj.pending_owners;
}));
}).nThen(function () {
cb(void 0, data);
});
};
*/
var getPropertiesData = function (common, opts, cb) {
opts = opts || {};
var data = {};
NThen(function (waitFor) {
var base = common.getMetadataMgr().getPrivateData().origin;
common.getPadAttribute('', waitFor(function (err, val) {
if (err || !val) {
waitFor.abort();
return void cb(err || 'EEMPTY');
}
if (!val.fileType) {
delete val.owners;
delete val.expire;
}
Util.extend(data, val);
if (data.href) { data.href = base + data.href; }
if (data.roHref) { data.roHref = base + data.roHref; }
}), opts.href);
5 years ago
// If this is a file, don't try to look for metadata
if (opts.channel && opts.channel.length > 34) { return; }
common.getPadMetadata({
channel: opts.channel // optional, fallback to current pad
}, waitFor(function (obj) {
if (obj && obj.error) { return; }
data.owners = obj.owners;
data.expire = obj.expire;
data.pending_owners = obj.pending_owners;
}));
}).nThen(function () {
cb(void 0, data);
});
};
/*
var createOwnerModal = function (common, data) {
var friends = common.getFriends(true);
5 years ago
var sframeChan = common.getSframeChannel();
var priv = common.getMetadataMgr().getPrivateData();
var user = common.getMetadataMgr().getUserData();
5 years ago
var edPublic = priv.edPublic;
var channel = data.channel;
var owners = data.owners || [];
var pending_owners = data.pending_owners || [];
var teams = priv.teams;
var teamOwner = data.teamId;
var redrawAll = function () {};
var div1 = h('div.cp-usergrid-user.cp-share-column.cp-ownership');
var div2 = h('div.cp-usergrid-user.cp-share-column.cp-ownership');
var $div1 = $(div1);
var $div2 = $(div2);
// Remove owner column
var drawRemove = function (pending) {
5 years ago
var _owners = {};
var o = (pending ? pending_owners : owners) || [];
o.forEach(function (ed) {
5 years ago
var f;
Object.keys(friends).some(function (c) {
if (friends[c].edPublic === ed) {
f = friends[c];
return true;
}
});
Object.keys(teams).some(function (id) {
if (teams[id].edPublic === ed) {
f = teams[id];
f.teamId = id;
}
});
if (ed === edPublic) {
f = f || user;
if (f.name) { f.edPublic = edPublic; }
}
5 years ago
_owners[ed] = f || {
5 years ago
displayName: Messages._getKey('owner_unknownUser', [ed]),
5 years ago
edPublic: ed,
};
});
5 years ago
var msg = pending ? Messages.owner_removePendingText
5 years ago
: Messages.owner_removeText;
var removeCol = UIElements.getUserGrid(msg, {
5 years ago
common: common,
large: true,
data: _owners,
5 years ago
noFilter: true
}, function () {
});
var $div = $(removeCol.div);
5 years ago
// When clicking on the remove button, we check the selected users.
// If you try to remove yourself, we'll display an additional warning message
5 years ago
var btnMsg = pending ? Messages.owner_removePendingButton : Messages.owner_removeButton;
var removeButton = h('button.no-margin', btnMsg);
5 years ago
$(removeButton).click(function () {
// Check selection
var $sel = $div.find('.cp-usergrid-user.cp-selected');
5 years ago
var sel = $sel.toArray();
5 years ago
if (!sel.length) { return; }
5 years ago
var me = false;
var toRemove = sel.map(function (el) {
var ed = $(el).attr('data-ed');
if (!ed) { return; }
if (teamOwner && teams[teamOwner] && teams[teamOwner].edPublic === ed) { me = true; }
if (ed === edPublic && !teamOwner) { me = true; }
5 years ago
return ed;
}).filter(function (x) { return x; });
NThen(function (waitFor) {
5 years ago
var msg = me ? Messages.owner_removeMeConfirm : Messages.owner_removeConfirm;
UI.confirm(msg, waitFor(function (yes) {
if (!yes) {
waitFor.abort();
return;
}
}));
}).nThen(function (waitFor) {
// Send the command
5 years ago
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: pending ? 'RM_PENDING_OWNERS' : 'RM_OWNERS',
value: toRemove,
teamId: teamOwner
}, waitFor(function (err, res) {
5 years ago
err = err || (res && res.error);
if (err) {
waitFor.abort();
redrawAll();
5 years ago
var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden
: Messages.error;
return void UI.warn(text);
}
UI.log(Messages.saved);
}));
}).nThen(function (waitFor) {
sel.forEach(function (el) {
var curve = $(el).attr('data-curve');
var friend = curve === user.curvePublic ? user : friends[curve];
if (!friend) { return; }
common.mailbox.sendTo("RM_OWNER", {
channel: channel,
title: data.title,
5 years ago
pending: pending
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
}, waitFor());
5 years ago
});
}).nThen(function () {
redrawAll();
5 years ago
});
});
$div.append(h('p', removeButton));
return $div;
5 years ago
};
// Add owners column
5 years ago
var drawAdd = function () {
var $div = $(h('div.cp-share-column'));
var _friends = JSON.parse(JSON.stringify(friends));
Object.keys(_friends).forEach(function (curve) {
if (owners.indexOf(_friends[curve].edPublic) !== -1 ||
pending_owners.indexOf(_friends[curve].edPublic) !== -1 ||
!_friends[curve].notifications) {
delete _friends[curve];
}
});
var addCol = UIElements.getUserGrid(Messages.owner_addText, {
5 years ago
common: common,
large: true,
data: _friends
5 years ago
}, function () {
5 years ago
//console.log(arguments);
5 years ago
});
$div.append(addCol.div);
var teamsData = Util.tryParse(JSON.stringify(priv.teams)) || {};
Object.keys(teamsData).forEach(function (id) {
var t = teamsData[id];
t.teamId = id;
if (owners.indexOf(t.edPublic) !== -1 || pending_owners.indexOf(t.edPublic) !== -1) {
delete teamsData[id];
}
});
var teamsList = UIElements.getUserGrid(Messages.owner_addTeamText, {
common: common,
large: true,
noFilter: true,
data: teamsData
}, function () {});
$div.append(teamsList.div);
5 years ago
// When clicking on the add button, we get the selected users.
5 years ago
var addButton = h('button.no-margin', Messages.owner_addButton);
5 years ago
$(addButton).click(function () {
// Check selection
var $sel = $div.find('.cp-usergrid-user.cp-selected');
5 years ago
var sel = $sel.toArray();
5 years ago
if (!sel.length) { return; }
5 years ago
var toAdd = sel.map(function (el) {
var curve = $(el).attr('data-curve');
// If the pad is woned by a team, we can transfer ownership to ourselves
if (curve === user.curvePublic && teamOwner) { return priv.edPublic; }
var friend = friends[curve];
if (!friend) { return; }
return friend.edPublic;
}).filter(function (x) { return x; });
var toAddTeams = sel.map(function (el) {
var team = teamsData[$(el).attr('data-teamid')];
if (!team || !team.edPublic) { return; }
return {
edPublic: team.edPublic,
id: $(el).attr('data-teamid')
};
5 years ago
}).filter(function (x) { return x; });
5 years ago
NThen(function (waitFor) {
5 years ago
var msg = Messages.owner_addConfirm;
UI.confirm(msg, waitFor(function (yes) {
if (!yes) {
waitFor.abort();
return;
}
}));
}).nThen(function (waitFor) {
// Add one of our teams as an owner
if (toAddTeams.length) {
// Send the command
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: 'ADD_OWNERS',
value: toAddTeams.map(function (obj) { return obj.edPublic; }),
teamId: teamOwner
}, waitFor(function (err, res) {
err = err || (res && res.error);
if (err) {
waitFor.abort();
redrawAll();
var text = err === "INSUFFICIENT_PERMISSIONS" ?
Messages.fm_forbidden : Messages.error;
return void UI.warn(text);
}
var isTemplate = priv.isTemplate || data.isTemplate;
toAddTeams.forEach(function (obj) {
sframeChan.query('Q_STORE_IN_TEAM', {
href: data.href || data.rohref,
password: data.password,
path: isTemplate ? ['template'] : undefined,
title: data.title || '',
teamId: obj.id
}, waitFor(function (err) {
if (err) { return void console.error(err); }
}));
});
}));
}
}).nThen(function (waitFor) {
// Offer ownership to a friend
if (toAdd.length) {
// Send the command
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: 'ADD_PENDING_OWNERS',
value: toAdd,
teamId: teamOwner
}, waitFor(function (err, res) {
err = err || (res && res.error);
if (err) {
waitFor.abort();
redrawAll();
var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden
: Messages.error;
return void UI.warn(text);
}
}));
}
}).nThen(function (waitFor) {
5 years ago
sel.forEach(function (el) {
var curve = $(el).attr('data-curve');
var friend = curve === user.curvePublic ? user : friends[curve];
if (!friend) { return; }
common.mailbox.sendTo("ADD_OWNER", {
channel: channel,
href: data.href,
password: data.password,
5 years ago
title: data.title
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
}, waitFor());
5 years ago
});
}).nThen(function () {
redrawAll();
5 years ago
UI.log(Messages.saved);
5 years ago
});
});
$div.append(h('p', addButton));
return $div;
5 years ago
};
redrawAll = function (md) {
var todo = function (obj) {
if (obj && obj.error) { return; }
owners = obj.owners || [];
pending_owners = obj.pending_owners || [];
$div1.empty();
$div2.empty();
$div1.append(drawRemove(false)).append(drawRemove(true));
$div2.append(drawAdd());
};
if (md) { return void todo(md); }
common.getPadMetadata({
channel: data.channel
}, todo);
};
$div1.append(drawRemove(false)).append(drawRemove(true));
$div2.append(drawAdd());
var handler = sframeChan.on('EV_RT_METADATA', function (md) {
if (!$div1.length) {
return void handler.stop();
}
owners = md.owners || [];
pending_owners = md.pending_owners || [];
redrawAll(md);
});
// Create modal
var link = h('div.cp-share-columns', [
div1,
div2
// drawRemove()[0],
//drawAdd()[0]
]);
var linkButtons = [{
className: 'cancel',
5 years ago
name: Messages.filePicker_close,
onClick: function () {},
keys: [27]
}];
return UI.dialog.customModal(link, {buttons: linkButtons});
};
*/
/*
var getRightsProperties = function (common, data, cb) {
var $div = $('<div>');
if (!data) { return void cb(void 0, $div); }
var draw = function () {
var $d = $('<div>');
var priv = common.getMetadataMgr().getPrivateData();
var user = common.getMetadataMgr().getUserData();
var edPublic = priv.edPublic;
var owned = false;
var _owners = {};
if (data.owners && data.owners.length) {
if (data.owners.indexOf(edPublic) !== -1) {
owned = true;
} else {
Object.keys(priv.teams || {}).some(function (id) {
var team = priv.teams[id] || {};
if (team.viewer) { return; }
if (data.owners.indexOf(team.edPublic) === -1) { return; }
owned = Number(id);
return true;
});
}
var strangers = 0;
data.owners.forEach(function (ed) {
// If a friend is an owner, add their name to the list
// otherwise, increment the list of strangers
// Our edPublic? print "Yourself"
if (ed === edPublic) {
_owners[ed] = {
selected: true,
name: user.name,
avatar: user.avatar
};
return;
}
// One of our teams? print the team name
if (Object.keys(priv.teams || {}).some(function (id) {
var team = priv.teams[id] || {};
if (team.edPublic !== ed) { return; }
_owners[ed] = {
name: team.name,
avatar: team.avatar
};
return true;
})) {
return;
}
// One of our friends? print the friend name
if (Object.keys(priv.friends || {}).some(function (c) {
var friend = priv.friends[c] || {};
if (friend.edPublic !== ed || c === 'me') { return; }
_owners[friend.edPublic] = {
name: friend.displayName,
avatar: friend.avatar
};
return true;
})) {
return;
}
// Otherwise it's a stranger
strangers++;
});
if (strangers) {
_owners['stangers'] = {
name: Messages._getKey('properties_unknownUser', [strangers]),
};
}
}
var _ownersGrid = UIElements.getUserGrid(Messages.creation_owners, {
common: common,
noSelect: true,
data: _owners,
large: true
}, function () {});
if (_ownersGrid && Object.keys(_owners).length) {
$d.append(_ownersGrid.div);
} else {
$d.append([
h('label', Messages.creation_owners),
]);
$d.append(UI.dialog.selectable(Messages.creation_noOwner, {
id: 'cp-app-prop-owners',
}));
}
var parsed;
if (data.href || data.roHref) {
parsed = Hash.parsePadUrl(data.href || data.roHref);
}
if (owned && parsed.hashData.type === 'pad') {
5 years ago
var manageOwners = h('button.no-margin', Messages.owner_openModalButton);
$(manageOwners).click(function () {
data.teamId = typeof(owned) !== "boolean" ? owned : undefined;
var modal = createOwnerModal(common, data);
UI.openCustomModal(modal, {
wide: true,
});
});
$d.append(h('p', manageOwners));
}
if (!data.noExpiration) {
var expire = Messages.creation_expireFalse;
if (data.expire && typeof (data.expire) === "number") {
expire = new Date(data.expire).toLocaleString();
}
$('<label>', {'for': 'cp-app-prop-expire'}).text(Messages.creation_expiration)
.appendTo($d);
$d.append(UI.dialog.selectable(expire, {
id: 'cp-app-prop-expire',
}));
}
if (!data.noPassword) {
var hasPassword = data.password;
var $pwLabel = $('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
.hide().appendTo($d);
var password = UI.passwordInput({
id: 'cp-app-prop-password',
readonly: 'readonly'
});
var $password = $(password).hide();
var $pwInput = $password.find('.cp-password-input');
$pwInput.val(data.password).click(function () {
$pwInput[0].select();
});
$d.append(password);
if (hasPassword) {
$pwLabel.show();
$password.css('display', 'flex');
}
// In the properties, we should have the edit href if we know it.
// We should know it because the pad is stored, but it's better to check...
if (!data.noEditPassword && owned && data.href) { // FIXME SHEET fix password change for sheets
var sframeChan = common.getSframeChannel();
5 years ago
var isOO = parsed.type === 'sheet';
var isFile = parsed.hashData.type === 'file';
var isSharedFolder = parsed.type === 'drive';
var changePwTitle = Messages.properties_changePassword;
var changePwConfirm = isFile ? Messages.properties_confirmChangeFile : Messages.properties_confirmChange;
if (!hasPassword) {
changePwTitle = Messages.properties_addPassword;
changePwConfirm = isFile ? Messages.properties_confirmNewFile : Messages.properties_confirmNew;
}
$('<label>', {'for': 'cp-app-prop-change-password'})
.text(changePwTitle).appendTo($d);
var newPassword = UI.passwordInput({
id: 'cp-app-prop-change-password',
style: 'flex: 1;'
});
var passwordOk = h('button', Messages.properties_changePasswordButton);
var changePass = h('span.cp-password-change-container', [
newPassword,
passwordOk
]);
var pLocked = false;
$(passwordOk).click(function () {
var newPass = $(newPassword).find('input').val();
if (data.password === newPass ||
(!data.password && !newPass)) {
return void UI.alert(Messages.properties_passwordSame);
}
if (pLocked) { return; }
pLocked = true;
UI.confirm(changePwConfirm, function (yes) {
if (!yes) { pLocked = false; return; }
$(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'}));
5 years ago
var q = isFile ? 'Q_BLOB_PASSWORD_CHANGE' :
(isOO ? 'Q_OO_PASSWORD_CHANGE' : 'Q_PAD_PASSWORD_CHANGE');
// If this is a file password change, register to the upload events:
// * if there is a pending upload, ask if we shoudl interrupt
// * display upload progress
var onPending;
var onProgress;
if (isFile) {
onPending = sframeChan.on('Q_BLOB_PASSWORD_CHANGE_PENDING', function (data, cb) {
onPending.stop();
UI.confirm(Messages.upload_uploadPending, function (yes) {
cb({cancel: yes});
});
});
onProgress = sframeChan.on('EV_BLOB_PASSWORD_CHANGE_PROGRESS', function (data) {
if (typeof (data) !== "number") { return; }
var p = Math.round(data);
$(passwordOk).text(p + '%');
});
}
sframeChan.query(q, {
teamId: typeof(owned) !== "boolean" ? owned : undefined,
href: data.href,
password: newPass
}, function (err, data) {
$(passwordOk).text(Messages.properties_changePasswordButton);
pLocked = false;
if (err || data.error) {
console.error(err || data.error);
return void UI.alert(Messages.properties_passwordError);
}
UI.findOKButton().click();
$pwLabel.show();
$password.css('display', 'flex');
$pwInput.val(newPass);
// If the current document is a file or if we're changing the password from a drive,
// we don't have to reload the page at the end.
// Tell the user the password change was successful and abort
if (isFile || priv.app !== parsed.type) {
if (onProgress && onProgress.stop) { onProgress.stop(); }
$(passwordOk).text(Messages.properties_changePasswordButton);
var alertMsg = data.warning ? Messages.properties_passwordWarningFile
: Messages.properties_passwordSuccessFile;
return void UI.alert(alertMsg, undefined, {force: true});
}
// Pad password changed: update the href
// Use hidden hash if needed (we're an owner of this pad so we know it is stored)
var useUnsafe = Util.find(priv, ['settings', 'security', 'unsafeLinks']);
var href = (priv.readOnly && data.roHref) ? data.roHref : data.href;
if (useUnsafe === false) {
var newParsed = Hash.parsePadUrl(href);
var newSecret = Hash.getSecrets(newParsed.type, newParsed.hash, newPass);
var newHash = Hash.getHiddenHashFromKeys(parsed.type, newSecret, {});
href = Hash.hashToHref(newHash, parsed.type);
}
if (data.warning) {
return void UI.alert(Messages.properties_passwordWarning, function () {
common.gotoURL(href);
}, {force: true});
}
return void UI.alert(Messages.properties_passwordSuccess, function () {
if (!isSharedFolder) {
common.gotoURL(href);
}
}, {force: true});
});
});
});
$d.append(changePass);
}
}
return $d;
};
var sframeChan = common.getSframeChannel();
var handler = sframeChan.on('EV_RT_METADATA', function (md) {
if (!$div.length) {
handler.stop();
return;
}
md = JSON.parse(JSON.stringify(md));
data.owners = md.owners;
data.expire = md.expire;
data.pending_owners = md.pending_owners;
$div.empty();
$div.append(draw());
});
$div.append(draw());
cb(void 0, $div);
};
*/
var getPadProperties = function (common, data, opts, cb) {
opts = opts || {};
var $d = $('<div>');
if (!data) { return void cb(void 0, $d); }
var priv = common.getMetadataMgr().getPrivateData();
var edPublic = priv.edPublic;
if (data.href) {
$('<label>', {'for': 'cp-app-prop-link'}).text(Messages.editShare).appendTo($d);
$d.append(UI.dialog.selectable(data.href, {
id: 'cp-app-prop-link',
}));
}
if (data.roHref && !opts.noReadOnly) {
$('<label>', {'for': 'cp-app-prop-rolink'}).text(Messages.viewShare).appendTo($d);
$d.append(UI.dialog.selectable(data.roHref, {
id: 'cp-app-prop-rolink',
}));
}
if (data.tags && Array.isArray(data.tags)) {
$d.append(h('div.cp-app-prop', [Messages.fm_prop_tagsList, h('br'), h('span.cp-app-prop-content', data.tags.join(', '))]));
}
if (data.ctime) {
$d.append(h('div.cp-app-prop', [Messages.fm_creation, h('br'), h('span.cp-app-prop-content', new Date(data.ctime).toLocaleString())]));
}
if (data.atime) {
$d.append(h('div.cp-app-prop', [Messages.fm_lastAccess, h('br'), h('span.cp-app-prop-content', new Date(data.atime).toLocaleString())]));
}
var owned = false;
if (common.isLoggedIn()) {
if (Array.isArray(data.owners)) {
if (data.owners.indexOf(edPublic) !== -1) {
owned = true;
} else {
Object.keys(priv.teams || {}).some(function (id) {
var team = priv.teams[id] || {};
if (team.viewer) { return; }
if (data.owners.indexOf(team.edPublic) === -1) { return; }
owned = Number(id);
return true;
});
}
}
// check the size of this file...
var bytes = 0;
var historyBytes;
var chan = [data.channel];
if (data.rtChannel) { chan.push(data.rtChannel); }
if (data.lastVersion) { chan.push(Hash.hrefToHexChannelId(data.lastVersion)); }
var history = common.makeUniversal('history');
var trimChannels = [];
NThen(function (waitFor) {
chan.forEach(function (c) {
common.getFileSize(c, waitFor(function (e, _bytes) {
if (e) {
// there was a problem with the RPC
console.error(e);
}
bytes += _bytes;
}));
});
if (!owned) { return; }
history.execCommand('GET_HISTORY_SIZE', {
pad: true,
channels: chan.filter(function (c) { return c.length === 32; }),
teamId: typeof(owned) === "number" && owned
}, waitFor(function (obj) {
if (obj && obj.error) { return; }
historyBytes = obj.size;
trimChannels = obj.channels;
}));
}).nThen(function () {
if (bytes === 0) { return void cb(void 0, $d); }
var formatted = UIElements.prettySize(bytes);
if (!owned || !historyBytes || historyBytes > bytes || historyBytes < 0) {
$d.append(h('div.cp-app-prop', [
Messages.upload_size,
h('br'),
h('span.cp-app-prop-content', formatted)
]));
return void cb(void 0, $d);
}
var p = Math.round((historyBytes / bytes) * 100);
var historyPrettySize = UIElements.prettySize(historyBytes);
var contentsPrettySize = UIElements.prettySize(bytes - historyBytes);
var button;
var spinner = UI.makeSpinner();
var size = h('div.cp-app-prop', [
Messages.upload_size,
h('br'),
h('div.cp-app-prop-size-container', [
h('div.cp-app-prop-size-history', { style: 'width:'+p+'%;' })
]),
h('div.cp-app-prop-size-legend', [
h('div.cp-app-prop-history-size', [
h('span.cp-app-prop-history-size-color'),
h('span.cp-app-prop-content', Messages._getKey('historyTrim_historySize', [historyPrettySize]))
]),
h('div.cp-app-prop-contents-size', [
h('span.cp-app-prop-contents-size-color'),
h('span.cp-app-prop-content', Messages._getKey('historyTrim_contentsSize', [contentsPrettySize]))
]),
]),
button = h('button.btn.btn-danger-alt.no-margin', Messages.trimHistory_button),
spinner.spinner
]);
$d.append(size);
var $button = $(button);
UI.confirmButton(button, {
classes: 'btn-danger'
}, function () {
$button.remove();
spinner.spin();
history.execCommand('TRIM_HISTORY', {
pad: true,
channels: trimChannels,
teamId: typeof(owned) === "number" && owned
}, function (obj) {
spinner.hide();
if (obj && obj.error) {
$(size).append(h('div.alert.alert-danger', Messages.trimHistory_error));
return;
}
$(size).remove();
var formatted = UIElements.prettySize(bytes - historyBytes);
$d.append(h('div.cp-app-prop', [
Messages.upload_size,
h('br'),
h('span.cp-app-prop-content', formatted)
]));
$d.append(h('div.alert.alert-success', Messages.trimHistory_success));
});
});
cb(void 0, $d);
});
} else {
cb(void 0, $d);
}
};
5 years ago
UIElements.getProperties = function (common, opts, cb) {
var data;
var content;
var button = [{
5 years ago
className: 'cancel',
name: Messages.filePicker_close,
onClick: function () {},
5 years ago
keys: [13,27]
}];
NThen(function (waitFor) {
getPropertiesData(common, opts, waitFor(function (e, _data) {
if (e) {
waitFor.abort();
return void cb(e);
}
data = _data;
}));
}).nThen(function (waitFor) {
getPadProperties(common, data, opts, waitFor(function (e, c) {
if (e) {
waitFor.abort();
return void cb(e);
}
5 years ago
content = UI.dialog.customModal(c[0], {
buttons: button
});
}));
}).nThen(function () {
var tabs = UI.dialog.tabs([{
title: Messages.fc_prop,
5 years ago
icon: "fa fa-info-circle",
content: content
}]);
5 years ago
var modal = UI.openCustomModal(tabs);
cb (void 0, modal);
});
};
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');
UIElements.displayAvatar(common, $(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;
5 years ago
var myName = common.getMetadataMgr().getUserData().name;
if (!friends) { return; }
5 years ago
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 friendsList = UIElements.getUserGrid(Messages.share_linkFriends, {
common: common,
data: friends,
noFilter: false,
large: true
}, refreshButtons);
var friendDiv = friendsList.div;
$div.append(friendDiv);
var others = friendsList.icons;
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
};
});
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 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 hasFriends = Object.keys(config.friends || {}).length !== 0;
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 hasFriends = Object.keys(config.friends || {}).length !== 0;
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',
title: Messages.makeACopy,
}).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',
title: Messages.template_import,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.template_import));
button
.click(common.prepareFeedback(type))
.click(function () {
UIElements.openTemplatePicker(common, true);
});
break;
case 'template':
if (!AppConfig.enableTemplates) { return; }
if (!common.isLoggedIn()) { return; }
button = $('<button>', {
title: Messages.saveTemplateButton,
class: 'fa fa-bookmark cp-toolbar-icon-template'
});
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>', {
title: Messages.forgetButtonTitle,
'class': "fa fa-trash cp-toolbar-icon-forget"
});
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 = $('<button>', {
title: Messages.presentButtonTitle,
'class': "fa fa-play-circle cp-toolbar-icon-present", // used in slide.js
});
break;
case 'preview':
button = $('<button>', {
title: Messages.previewButtonTitle,
'class': "fa fa-eye cp-toolbar-icon-preview",
});
break;
case 'print':
button = $('<button>', {
title: Messages.printButtonTitle2,
'class': "fa fa-print cp-toolbar-icon-print",
}).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 'more':
button = $('<button>', {
title: Messages.moreActions,
'class': "cp-toolbar-drawer-button fa fa-ellipsis-h",
});
break;
case 'mediatag':
button = $('<button>', {
'class': 'fa fa-picture-o cp-toolbar-icon-mediatag',
title: Messages.filePickerButton,
})
.click(common.prepareFeedback(type));
break;
case 'savetodrive':
button = $('<button>', {
'class': 'fa fa-cloud-upload cp-toolbar-icon-savetodrive',
title: Messages.canvas_saveToDrive,
})
.click(common.prepareFeedback(type));
break;
case 'hashtag':
button = $('<button>', {
'class': 'fa fa-hashtag cp-toolbar-icon-hashtag',
title: Messages.tags_title,
})
.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 = $('<button>', {
'class': 'fa fa-caret-down cp-toolbar-icon-toggle',
});
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 'access':
button = $('<button>', {
'class': 'fa fa-unlock-alt cp-toolbar-icon-access',
title: Messages.accessButton,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'})
.text(Messages.accessButton))
.click(common.prepareFeedback(type))
.click(function () {
common.isPadStored(function (err, data) {
if (!data) {
return void UI.alert(Messages.autostore_notAvailable);
}
require(['/common/inner/access.js'], function (Access) {
Access.getAccessModal(common, {}, function (e) {
if (e) { console.error(e); }
});
});
});
});
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);
}
UIElements.getProperties(common, {}, function (e) {
if (e) { return void console.error(e); }
});
});
});
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;
default:
data = data || {};
var icon = data.icon || "fa-question";
button = $('<button>', {
'class': "fa " + icon,
})
.click(common.prepareFeedback(data.name || 'DEFAULT'));
//.click(common.prepareFeedback(type));
if (callback) {
button.click(callback);
}
if (data.title) { button.attr('title', data.title); }
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);
button.click(common.prepareFeedback(data.name));
}
if (data.text) {
$('<span>', {'class': 'cp-toolbar-drawer-element'}).text(data.text)
.appendTo(button);
}
}
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-window-close');
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
};
};
// Avatars
UIElements.displayMediatagImage = function (Common, $tag, cb) {
if (!$tag.length || !$tag.is('media-tag')) { return void cb('NOT_MEDIATAG'); }
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length) {
if (mutation.addedNodes.length > 1 ||
mutation.addedNodes[0].nodeName !== 'IMG') {
return void cb('NOT_IMAGE');
}
var $image = $tag.find('img');
var onLoad = function () {
var img = new Image();
img.onload = function () {
var _cb = cb;
cb = $.noop;
_cb(null, $image, img);
};
img.src = $image.attr('src');
};
if ($image[0].complete) { onLoad(); }
$image.on('load', onLoad);
}
});
});
observer.observe($tag[0], {
attributes: false,
childList: true,
characterData: false
});
7 years ago
MediaTag($tag[0]).on('error', function (data) {
console.error(data);
});
};
var emoji_patt = /([\uD800-\uDBFF][\uDC00-\uDFFF])/;
var isEmoji = function (str) {
return emoji_patt.test(str);
};
var emojiStringToArray = function (str) {
var split = str.split(emoji_patt);
var arr = [];
for (var i=0; i<split.length; i++) {
var char = split[i];
if (char !== "") {
arr.push(char);
}
}
return arr;
};
var getFirstEmojiOrCharacter = UIElements.getFirstCharacter = function (str) {
if (!str || !str.trim()) { return '?'; }
var emojis = emojiStringToArray(str);
return isEmoji(emojis[0])? emojis[0]: str[0];
};
var avatars = {};
UIElements.setAvatar = function (hash, data) {
avatars[hash] = data;
};
UIElements.getAvatar = function (hash) {
return avatars[hash];
};
UIElements.displayAvatar = function (common, $container, href, name, cb) {
var displayDefault = function () {
var text = (href && typeof(href) === "string") ? href : getFirstEmojiOrCharacter(name);
var $avatar = $('<span>', {'class': 'cp-avatar-default'}).text(text);
$container.append($avatar);
if (cb) { cb(); }
};
if (!window.Symbol) { return void displayDefault(); } // IE doesn't have Symbol
if (!href || href.length === 1) { return void displayDefault(); }
var centerImage = function ($img, $image, img) {
var w = img.width;
var h = img.height;
if (w>h) {
$image.css('max-height', '100%');
$img.css('flex-direction', 'column');
if (cb) { cb($img); }
return;
}
$image.css('max-width', '100%');
$img.css('flex-direction', 'row');
if (cb) { cb($img); }
};
var parsed = Hash.parsePadUrl(href);
if (parsed.type !== "file" || parsed.hashData.type !== "file") {
var $img = $('<media-tag>').appendTo($container);
var img = new Image();
$(img).attr('src', href);
img.onload = function () {
centerImage($img, $(img), img);
$(img).appendTo($img);
};
return;
}
// No password for avatars
var privateData = common.getMetadataMgr().getPrivateData();
var origin = privateData.fileHost || privateData.origin;
var secret = Hash.getSecrets('file', parsed.hash);
if (secret.keys && secret.channel) {
var hexFileName = secret.channel;
var cryptKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
var src = origin + Hash.getBlobPathFromHex(hexFileName);
common.getFileSize(hexFileName, function (e, data) {
if (e || !data) { return void displayDefault(); }
if (typeof data !== "number") { return void displayDefault(); }
if (Util.bytesToMegabytes(data) > 0.5) { return void displayDefault(); }
var $img = $('<media-tag>').appendTo($container);
$img.attr('src', src);
$img.attr('data-crypto-key', 'cryptpad:' + cryptKey);
UIElements.displayMediatagImage(common, $img, function (err, $image, img) {
if (err) { return void console.error(err); }
centerImage($img, $image, img);
});
});
}
};
var transformAvatar = function (file, cb) {
if (file.type === 'image/gif') { return void cb(file); }
var $croppie = $('<div>', {
'class': 'cp-app-profile-resizer'
});
if (typeof ($croppie.croppie) !== "function") {
console.warn('fuck');
return void cb(file);
}
var todo = function () {
UI.confirm($croppie[0], function (yes) {
if (!yes) { return; }
$croppie.croppie('result', {
type: 'blob',
size: {width: 300, height: 300}
}).then(function(blob) {
blob.lastModifiedDate = new Date();
blob.name = 'avatar';
cb(blob);
});
});
};
var reader = new FileReader();
reader.onload = function(e) {
$croppie.croppie({
url: e.target.result,
viewport: { width: 100, height: 100 },
boundary: { width: 400, height: 300 },
});
todo();
};
reader.readAsDataURL(file);
};
UIElements.addAvatar = function (common, cb) {
var AVATAR_SIZE_LIMIT = 0.5;
var allowedMediaTypes = [
'image/png',
'image/jpeg',
'image/jpg',
'image/gif',
];
var fmConfig = {
noHandlers: true,
noStore: true,
body: $('body'),
onUploaded: cb
};
var FM = common.createFileManager(fmConfig);
var accepted = ".gif,.jpg,.jpeg,.png";
var data = {
FM: FM,
filter: function (file) {
var sizeMB = Util.bytesToMegabytes(file.size);
var type = file.type;
// We can't resize .gif so we have to display an error if it is too big
if (sizeMB > AVATAR_SIZE_LIMIT && type === 'image/gif') {
UI.log(Messages._getKey('profile_uploadSizeError', [
Messages._getKey('formattedMB', [AVATAR_SIZE_LIMIT])
]));
return false;
}
// Display an error if the image type is not allowed
if (allowedMediaTypes.indexOf(type) === -1) {
UI.log(Messages._getKey('profile_uploadTypeError', [
accepted.split(',').join(', ')
]));
return false;
}
return true;
},
transformer: transformAvatar,
accept: accepted
};
return data;
};
/* 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);
updateUsage();
cb(null, $container);
return {
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)); }
$('<' + o.tag + '>', o.attributes || {}).html(o.content || '').appendTo($innerblock);
});
$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 topPos = $container[0].getBoundingClientRect().bottom;
$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();
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.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)
});
}
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)
});
}
if (padType !== 'settings') {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-settings fa fa-cog'},
content: h('span', Messages.settingsButton)
});
}
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')
});
}
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')
});
}
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)
});
}
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)
});
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-logout fa fa-sign-out'},
content: h('span', Messages.logoutButton)
});
} else {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-login fa fa-sign-in'},
content: h('span', Messages.login_login)
});
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-register fa fa-user-plus'},
content: h('span', Messages.login_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('');
UIElements.displayAvatar(Common, $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();
$userAdmin.find('a.cp-toolbar-menu-logout').click(function () {
Common.logout(function () {
window.parent.location = origin+'/';
});
});
$userAdmin.find('a.cp-toolbar-menu-logout-everywhere').click(function () {
Common.getSframeChannel().query('Q_LOGOUT_EVERYWHERE', null, function () {
window.parent.location = origin + '/';
});
});
$userAdmin.find('a.cp-toolbar-menu-settings').click(function () {
if (padType) {
window.open(origin+'/settings/');
} else {
window.parent.location = origin+'/settings/';
}
});
$userAdmin.find('a.cp-toolbar-menu-support').click(function () {
if (padType) {
window.open(origin+'/support/');
} else {
window.parent.location = origin+'/support/';
}
});
6 years ago
$userAdmin.find('a.cp-toolbar-menu-admin').click(function () {
if (padType) {
window.open(origin+'/admin/');
} else {
window.parent.location = origin+'/admin/';
}
});
$userAdmin.find('a.cp-toolbar-survey').click(function () {
Feedback.send('SURVEY_CLICKED');
});
$userAdmin.find('a.cp-toolbar-menu-profile').click(function () {
if (padType) {
window.open(origin+'/profile/');
} else {
window.parent.location = origin+'/profile/';
}
});
$userAdmin.find('a.cp-toolbar-menu-login').click(function () {
Common.setLoginRedirect(function () {
window.parent.location = origin+'/login/';
});
});
$userAdmin.find('a.cp-toolbar-menu-register').click(function () {
Common.setLoginRedirect(function () {
window.parent.location = origin+'/register/';
});
});
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.createModal = function (cfg) {
var $body = cfg.$body || $('body');
var $blockContainer = $body.find('#'+cfg.id);
if (!$blockContainer.length) {
$blockContainer = $('<div>', {
'class': 'cp-modal-container',
tabindex: 1,
'id': cfg.id
});
}
var hide = function () {
if (cfg.onClose) { return void cfg.onClose(); }
$blockContainer.hide();
};
$blockContainer.html('').appendTo($body);
var $block = $('<div>', {'class': 'cp-modal'}).appendTo($blockContainer);
$('<span>', {
'class': 'cp-modal-close fa fa-times',
'title': Messages.filePicker_close
}).click(hide).appendTo($block);
$body.click(hide);
$block.click(function (e) {
e.stopPropagation();
});
$body.keydown(function (e) {
if (e.which === 27) {
hide();
}
});
return $blockContainer;
};
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 = UIElements.createModal({
id: 'cp-app-toolbar-creation-dialog',
$body: $('body')
});
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 $advancedContainer = $('<div>');
var priv = common.getMetadataMgr().getPrivateData();
var c = (priv.settings.general && priv.settings.general.creation) || {};
if (AppConfig.displayCreationScreen && common.isLoggedIn() && c.skip) {
var $cboxLabel = $(UI.createCheckbox('cp-app-toolbar-creation-advanced',
Messages.creation_newPadModalAdvanced, true))
.appendTo($advancedContainer);
$advanced = $cboxLabel.find('input');
$description.append('<br>');
$description.append(Messages.creation_newPadModalDescriptionAdvanced);
}
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).append($advancedContainer);
window.setTimeout(function () {
$modal.show();
$modal.focus();
});
};
UIElements.initFilePicker = function (common, cfg) {
var onSelect = cfg.onSelect || $.noop;
var sframeChan = common.getSframeChannel();
sframeChan.on("EV_FILE_PICKED", function (data) {
onSelect(data);
});
};
UIElements.openFilePicker = function (common, types) {
var sframeChan = common.getSframeChannel();
sframeChan.event("EV_FILE_PICKER_OPEN", types);
};
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;
common.openFilePicker(pickerCfg);
var first = true; // We can only pick a template once (for a new document)
var fileDialogCfg = {
onSelect: 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;
}
}
};
common.initFilePicker(fileDialogCfg);
};
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', {
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');
UIElements.displayAvatar(common, $(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 settings = h('div.cp-creation-remember', [
UI.createCheckbox('cp-creation-remember', Messages.dontShowAgain, false),
createHelper('/settings/#creation', Messages.creation_settings),
h('div.cp-creation-remember-help.cp-creation-slider', [
h('span.fa.fa-exclamation-circle.cp-creation-warning'),
Messages.creation_rememberHelp
])
]);
var createDiv = h('div.cp-creation-create');
var $create = $(createDiv);
$(h('div#cp-creation-form', [
team,
owned,
expire,
password,
settings,
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();
});
// Display settings help when checkbox checked
$creation.find('#cp-creation-remember').on('change', function () {
if ($(this).is(':checked')) {
$creation.find('.cp-creation-remember-help:not(.active)').addClass('active');
return;
}
$creation.find('.cp-creation-remember-help').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);
}
if (cfg.skip) {
$creation.find('#cp-creation-remember').prop('checked', true).trigger('change');
}
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();
var skip = $('#cp-creation-remember').is(':checked');
common.setAttribute(['general', 'creation', 'skip'], skip, function (e) {
if (e) { return void console.error(e); }
});
common.setAttribute(['general', 'creation', 'noTemplate'], skip, function (e) {
if (e) { return void console.error(e); }
});
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
});
});
};
var createContextMenu = function (menu) {
var $menu = $(menu).appendTo($('body'));
var display = function (e) {
$menu.css({ display: "block" });
var h = $menu.outerHeight();
var w = $menu.outerWidth();
var wH = window.innerHeight;
var wW = window.innerWidth;
if (h > wH) {
$menu.css({
top: '0px',
bottom: ''
});
} else if (e.pageY + h <= wH) {
$menu.css({
top: e.pageY+'px',
bottom: ''
});
} else {
$menu.css({
bottom: '0px',
top: ''
});
}
if(w > wW) {
$menu.css({
left: '0px',
right: ''
});
} else if (e.pageX + w <= wW) {
$menu.css({
left: e.pageX+'px',
right: ''
});
} else {
$menu.css({
left: '',
right: '0px',
});
}
};
var hide = function () {
$menu.hide();
};
var remove = function () {
$menu.remove();
};
$('body').click(hide);
return {
menu: menu,
show: display,
hide: hide,
remove: remove
};
};
var mediatagContextMenu;
UIElements.importMediaTagMenu = function (common) {
if (mediatagContextMenu) { return mediatagContextMenu; }
// Create context menu
var menu = h('div.cp-contextmenu.dropdown.cp-unselectable', [
h('ul.dropdown-menu', {
'role': 'menu',
'aria-labelledBy': 'dropdownMenu',
'style': 'display:block;position:static;margin-bottom:5px;'
}, [
h('li', h('a.cp-app-code-context-saveindrive.dropdown-item', {
'tabindex': '-1',
'data-icon': "fa-cloud-upload",
}, Messages.pad_mediatagImport)),
h('li', h('a.cp-app-code-context-download.dropdown-item', {
'tabindex': '-1',
'data-icon': "fa-download",
}, Messages.download_mt_button)),
])
]);
// create the icon for each contextmenu option
$(menu).find("li a.dropdown-item").each(function (i, el) {
var $icon = $("<span>");
if ($(el).attr('data-icon')) {
var font = $(el).attr('data-icon').indexOf('cptools') === 0 ? 'cptools' : 'fa';
$icon.addClass(font).addClass($(el).attr('data-icon'));
} else {
$icon.text($(el).text());
}
$(el).prepend($icon);
});
var m = createContextMenu(menu);
mediatagContextMenu = m;
var $menu = $(m.menu);
$menu.on('click', 'a', function (e) {
e.stopPropagation();
m.hide();
var $mt = $menu.data('mediatag');
if ($(this).hasClass("cp-app-code-context-saveindrive")) {
common.importMediaTag($mt);
}
else if ($(this).hasClass("cp-app-code-context-download")) {
var media = $mt[0]._mediaObject;
window.saveAs(media._blob.content, media.name);
}
});
return m;
};
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);
};
return UIElements;
});