Merge branch 'staging' into slide2
This commit is contained in:
@@ -7,9 +7,11 @@ define([
|
||||
'/common/notify.js',
|
||||
'/common/visible.js',
|
||||
'/common/tippy.min.js',
|
||||
'/customize/pages.js',
|
||||
'/common/hyperscript.js',
|
||||
'/bower_components/bootstrap-tokenfield/dist/bootstrap-tokenfield.js',
|
||||
'css!/common/tippy.css',
|
||||
], function ($, Messages, Util, AppConfig, Alertify, Notify, Visible, Tippy) {
|
||||
|
||||
], function ($, Messages, Util, AppConfig, Alertify, Notify, Visible, Tippy, Pages, h) {
|
||||
var UI = {};
|
||||
|
||||
/*
|
||||
@@ -20,11 +22,17 @@ define([
|
||||
// set notification timeout
|
||||
Alertify._$$alertify.delay = AppConfig.notificationTimeout || 5000;
|
||||
|
||||
var findCancelButton = UI.findCancelButton = function () {
|
||||
var findCancelButton = UI.findCancelButton = function (root) {
|
||||
if (root) {
|
||||
return $(root).find('button.cancel').last();
|
||||
}
|
||||
return $('button.cancel').last();
|
||||
};
|
||||
|
||||
var findOKButton = UI.findOKButton = function () {
|
||||
var findOKButton = UI.findOKButton = function (root) {
|
||||
if (root) {
|
||||
return $(root).find('button.ok').last();
|
||||
}
|
||||
return $('button.ok').last();
|
||||
};
|
||||
|
||||
@@ -33,7 +41,6 @@ define([
|
||||
switch (e.which) {
|
||||
case 27: // cancel
|
||||
if (typeof(no) === 'function') { no(e); }
|
||||
no();
|
||||
break;
|
||||
case 13: // enter
|
||||
if (typeof(yes) === 'function') { yes(e); }
|
||||
@@ -49,21 +56,161 @@ define([
|
||||
$(window).off('keyup', handler);
|
||||
};
|
||||
|
||||
var dialog = UI.dialog = {};
|
||||
|
||||
dialog.selectable = function (value) {
|
||||
var input = h('input', {
|
||||
type: 'text',
|
||||
readonly: 'readonly',
|
||||
});
|
||||
$(input).val(value).click(function () {
|
||||
input.select();
|
||||
});
|
||||
return input;
|
||||
};
|
||||
|
||||
dialog.okButton = function () {
|
||||
return h('button.ok', { tabindex: '2', }, Messages.okButton);
|
||||
};
|
||||
|
||||
dialog.cancelButton = function () {
|
||||
return h('button.cancel', { tabindex: '1'}, Messages.cancelButton);
|
||||
};
|
||||
|
||||
dialog.message = function (text) {
|
||||
return h('p.message', text);
|
||||
};
|
||||
|
||||
dialog.textInput = function (opt) {
|
||||
return h('input', opt || {
|
||||
placeholder: '',
|
||||
type: 'text',
|
||||
'class': 'cp-text-input',
|
||||
});
|
||||
};
|
||||
|
||||
dialog.nav = function (content) {
|
||||
return h('nav', content || [
|
||||
dialog.cancelButton(),
|
||||
dialog.okButton(),
|
||||
]);
|
||||
};
|
||||
|
||||
dialog.frame = function (content) {
|
||||
return h('div.alertify', [
|
||||
h('div.dialog', [
|
||||
h('div', content),
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
UI.tokenField = function (target) {
|
||||
var t = {
|
||||
element: target || h('input'),
|
||||
};
|
||||
var $t = t.tokenfield = $(t.element).tokenfield();
|
||||
t.getTokens = function () {
|
||||
return $t.tokenfield('getTokens').map(function (token) {
|
||||
return token.value;
|
||||
});
|
||||
};
|
||||
|
||||
t.preventDuplicates = function (cb) {
|
||||
$t.on('tokenfield:createtoken', function (ev) {
|
||||
var val;
|
||||
if (t.getTokens().some(function (t) {
|
||||
if (t === ev.attrs.value) { return ((val = t)); }
|
||||
})) {
|
||||
ev.preventDefault();
|
||||
if (typeof(cb) === 'function') { cb(val); }
|
||||
}
|
||||
});
|
||||
return t;
|
||||
};
|
||||
|
||||
t.setTokens = function (tokens) {
|
||||
$t.tokenfield('setTokens',
|
||||
tokens.map(function (token) {
|
||||
return {
|
||||
value: token,
|
||||
label: token,
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
t.focus = function () {
|
||||
var $temp = $t.closest('.tokenfield').find('.token-input');
|
||||
$temp.css('width', '20%');
|
||||
$t.tokenfield('focusInput', $temp[0]);
|
||||
};
|
||||
|
||||
return t;
|
||||
};
|
||||
|
||||
dialog.tagPrompt = function (tags, cb) {
|
||||
var input = dialog.textInput();
|
||||
|
||||
var tagger = dialog.frame([
|
||||
dialog.message('make some tags'), // TODO translate
|
||||
input,
|
||||
dialog.nav(),
|
||||
]);
|
||||
|
||||
var field = UI.tokenField(input).preventDuplicates(function (val) {
|
||||
UI.warn('Duplicate tag: ' + val); // TODO translate
|
||||
});
|
||||
|
||||
var close = Util.once(function () {
|
||||
var $t = $(tagger).fadeOut(150, function () { $t.remove(); });
|
||||
});
|
||||
|
||||
var listener = listenForKeys(function () {}, function () {
|
||||
close();
|
||||
stopListening(listener);
|
||||
});
|
||||
|
||||
var CB = Util.once(cb);
|
||||
findOKButton(tagger).click(function () {
|
||||
var tokens = field.getTokens();
|
||||
close();
|
||||
CB(tokens);
|
||||
});
|
||||
findCancelButton(tagger).click(function () {
|
||||
close();
|
||||
CB(null);
|
||||
});
|
||||
|
||||
// :(
|
||||
setTimeout(function () {
|
||||
field.setTokens(tags);
|
||||
field.focus();
|
||||
});
|
||||
|
||||
return tagger;
|
||||
};
|
||||
|
||||
UI.alert = function (msg, cb, force) {
|
||||
cb = cb || function () {};
|
||||
if (force !== true) { msg = Util.fixHTML(msg); }
|
||||
var close = function () {
|
||||
findOKButton().click();
|
||||
};
|
||||
var keyHandler = listenForKeys(close, close);
|
||||
Alertify
|
||||
.okBtn(Messages.okButton || 'OK')
|
||||
.alert(msg, function (ev) {
|
||||
cb(ev);
|
||||
stopListening(keyHandler);
|
||||
});
|
||||
window.setTimeout(function () {
|
||||
findOKButton().focus();
|
||||
if (typeof(msg) === 'string' && force !== true) {
|
||||
msg = Util.fixHTML(msg);
|
||||
}
|
||||
var ok = dialog.okButton();
|
||||
var frame = dialog.frame([
|
||||
dialog.message(msg),
|
||||
dialog.nav(ok),
|
||||
]);
|
||||
|
||||
var listener;
|
||||
var close = Util.once(function () {
|
||||
$(frame).fadeOut(150, function () { $(this).remove(); });
|
||||
stopListening(listener);
|
||||
});
|
||||
listener = listenForKeys(close, close);
|
||||
var $ok = $(ok).click(close);
|
||||
|
||||
document.body.appendChild(frame);
|
||||
setTimeout(function () {
|
||||
$ok.focus();
|
||||
if (typeof(UI.notify) === 'function') {
|
||||
UI.notify();
|
||||
}
|
||||
@@ -165,7 +312,7 @@ define([
|
||||
*/
|
||||
UI.spinner = function (parent) {
|
||||
var $target = $('<span>', {
|
||||
'class': 'fa fa-spinner fa-pulse fa-4x fa-fw'
|
||||
'class': 'fa fa-circle-o-notch fa-spin fa-4x fa-fw',
|
||||
}).hide();
|
||||
|
||||
$(parent).append($target);
|
||||
@@ -206,16 +353,15 @@ define([
|
||||
}
|
||||
$container = $loading.find('.loadingContainer');
|
||||
} else {
|
||||
$loading = $('<div>', {id: LOADING});
|
||||
$container = $('<div>', {'class': 'loadingContainer'});
|
||||
if (!hideLogo) {
|
||||
$container.append('<img class="cryptofist" src="/customize/cryptofist_small.png" />');
|
||||
$loading = $(Pages.loadingScreen());
|
||||
$container = $loading.find('.loadingContainer');
|
||||
if (hideLogo) {
|
||||
$loading.find('img').hide();
|
||||
} else {
|
||||
$loading.find('img').show();
|
||||
}
|
||||
var $spinner = $('<div>', {'class': 'spinnerContainer'});
|
||||
UI.spinner($spinner).show();
|
||||
var $text = $('<p>').text(loadingText || Messages.loading);
|
||||
$container.append($spinner).append($text);
|
||||
$loading.append($container);
|
||||
var $spinner = $loading.find('.spinnerContainer');
|
||||
$spinner.show();
|
||||
$('body').append($loading);
|
||||
}
|
||||
if (Messages.tips && !hideTips) {
|
||||
|
||||
@@ -170,5 +170,15 @@ define([], function () {
|
||||
return parts[0];
|
||||
};
|
||||
|
||||
/* for wrapping async functions such that they can only be called once */
|
||||
Util.once = function (f) {
|
||||
var called;
|
||||
return function () {
|
||||
if (called) { return; }
|
||||
called = true;
|
||||
f.apply(this, Array.prototype.slice.call(arguments));
|
||||
};
|
||||
};
|
||||
|
||||
return Util;
|
||||
});
|
||||
|
||||
@@ -5,6 +5,13 @@ define([
|
||||
var Cred = {};
|
||||
var Scrypt = window.scrypt;
|
||||
|
||||
Cred.MINIMUM_PASSWORD_LENGTH = typeof(AppConfig.minimum_password_length) === 'number'?
|
||||
AppConfig.minimum_password_length: 8;
|
||||
|
||||
Cred.isLongEnoughPassword = function (passwd) {
|
||||
return passwd.length >= Cred.MINIMUM_PASSWORD_LENGTH;
|
||||
};
|
||||
|
||||
var isString = Cred.isString = function (x) {
|
||||
return typeof(x) === 'string';
|
||||
};
|
||||
|
||||
@@ -81,6 +81,8 @@ define([
|
||||
common.addTooltips = UI.addTooltips;
|
||||
common.clearTooltips = UI.clearTooltips;
|
||||
common.importContent = UI.importContent;
|
||||
common.tokenField = UI.tokenField;
|
||||
common.dialog = UI.dialog;
|
||||
|
||||
// import common utilities for export
|
||||
common.find = Util.find;
|
||||
@@ -1391,6 +1393,21 @@ define([
|
||||
})
|
||||
.click(prepareFeedback(type));
|
||||
break;
|
||||
case 'hashtag':
|
||||
button = $('<button>', {
|
||||
'class': 'fa fa-hashtag',
|
||||
})
|
||||
.click(prepareFeedback(type))
|
||||
.click(function () {
|
||||
// TODO fetch pad tags before presenting dialog to user
|
||||
var dialog = UI.dialog.tagPrompt([], function (tags) {
|
||||
if (!Array.isArray(tags)) { return; }
|
||||
console.error(tags);
|
||||
// TODO do something with the tags the user entered
|
||||
});
|
||||
document.body.appendChild(dialog);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
button = $('<button>', {
|
||||
'class': "fa fa-question",
|
||||
@@ -1940,7 +1957,7 @@ define([
|
||||
};
|
||||
var $userAdmin = createDropdown(dropdownConfigUser);
|
||||
|
||||
var oldUrl;
|
||||
var oldUrl = '';
|
||||
if (account && !config.static && store) {
|
||||
var $avatar = $userAdmin.find('.cp-dropdown-button-title');
|
||||
var updateButton = function (newName) {
|
||||
@@ -1950,7 +1967,7 @@ define([
|
||||
if (oldUrl === url) { return; }
|
||||
oldUrl = url;
|
||||
$avatar.html('');
|
||||
common.displayAvatar($avatar, url, newName, function ($img) {
|
||||
common.displayAvatar($avatar, url, newName || Messages.anonymous, function ($img) {
|
||||
if ($img) {
|
||||
$userAdmin.find('button').addClass('avatar');
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ define([
|
||||
// validate inputs
|
||||
if (!Cred.isValidUsername(uname)) { return void cb('INVAL_USER'); }
|
||||
if (!Cred.isValidPassword(passwd)) { return void cb('INVAL_PASS'); }
|
||||
if (!Cred.isLongEnoughPassword(passwd)) { return void cb('PASS_TOO_SHORT'); }
|
||||
|
||||
Cred.deriveFromPassphrase(uname, passwd, 128, function (bytes) {
|
||||
// results...
|
||||
|
||||
@@ -382,9 +382,9 @@ define([
|
||||
var $displayName = $userAdmin.find('.'+displayNameCls);
|
||||
|
||||
var $avatar = $userAdmin.find('.cp-dropdown-button-title');
|
||||
var oldUrl;
|
||||
var loadingAvatar;
|
||||
var to;
|
||||
var oldUrl = '';
|
||||
var updateButton = function () {
|
||||
var myData = metadataMgr.getUserData();
|
||||
if (!myData) { return; }
|
||||
@@ -400,7 +400,7 @@ define([
|
||||
$displayName.text(newName || Messages.anonymous);
|
||||
if (accountName && oldUrl !== url) {
|
||||
$avatar.html('');
|
||||
UI.displayAvatar(Common, $avatar, url, newName, function ($img) {
|
||||
UI.displayAvatar(Common, $avatar, url, newName || Messages.anonymous, function ($img) {
|
||||
oldUrl = url;
|
||||
if ($img) {
|
||||
$userAdmin.find('button').addClass('cp-avatar');
|
||||
@@ -462,28 +462,37 @@ define([
|
||||
UI.openTemplatePicker = function (common) {
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var type = metadataMgr.getMetadataLazy().type;
|
||||
var first = true; // We can only pick a template once (for a new document)
|
||||
var fileDialogCfg = {
|
||||
onSelect: function (data) {
|
||||
if (data.type === type && first) {
|
||||
Cryptpad.addLoadingScreen({hideTips: true});
|
||||
var sframeChan = common.getSframeChannel();
|
||||
sframeChan.query('Q_TEMPLATE_USE', data.href, function () {
|
||||
first = false;
|
||||
Cryptpad.removeLoadingScreen();
|
||||
common.feedback('TEMPLATE_USED');
|
||||
});
|
||||
return;
|
||||
var sframeChan = common.getSframeChannel();
|
||||
|
||||
var onConfirm = function (yes) {
|
||||
if (!yes) { return; }
|
||||
var first = true; // We can only pick a template once (for a new document)
|
||||
var fileDialogCfg = {
|
||||
onSelect: function (data) {
|
||||
if (data.type === type && first) {
|
||||
Cryptpad.addLoadingScreen({hideTips: true});
|
||||
sframeChan.query('Q_TEMPLATE_USE', data.href, function () {
|
||||
first = false;
|
||||
Cryptpad.removeLoadingScreen();
|
||||
common.feedback('TEMPLATE_USED');
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
common.initFilePicker(fileDialogCfg);
|
||||
var pickerCfg = {
|
||||
types: [type],
|
||||
where: ['template']
|
||||
};
|
||||
common.openFilePicker(pickerCfg);
|
||||
};
|
||||
|
||||
sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) {
|
||||
if (data) {
|
||||
Cryptpad.confirm(Messages.useTemplate, onConfirm);
|
||||
}
|
||||
};
|
||||
common.initFilePicker(fileDialogCfg);
|
||||
var pickerCfg = {
|
||||
types: [type],
|
||||
where: ['template']
|
||||
};
|
||||
console.log(pickerCfg);
|
||||
common.openFilePicker(pickerCfg);
|
||||
});
|
||||
};
|
||||
|
||||
return UI;
|
||||
|
||||
@@ -312,6 +312,10 @@ define([
|
||||
sframeChan.on('Q_TEMPLATE_USE', function (href, cb) {
|
||||
Cryptpad.useTemplate(href, Cryptget, cb);
|
||||
});
|
||||
sframeChan.on('Q_TEMPLATE_EXIST', function (type, cb) {
|
||||
var hasTemplate = Cryptpad.listTemplates(type).length > 0;
|
||||
cb(hasTemplate);
|
||||
});
|
||||
|
||||
|
||||
sframeChan.ready();
|
||||
|
||||
@@ -107,6 +107,8 @@ define({
|
||||
|
||||
// Template picked, replace the content of the pad
|
||||
'Q_TEMPLATE_USE': true,
|
||||
// Check if we have template(s) for the selected pad type
|
||||
'Q_TEMPLATE_EXIST': true,
|
||||
|
||||
// File upload queries and events
|
||||
'Q_UPLOAD_FILE': true,
|
||||
|
||||
@@ -516,29 +516,11 @@ define([
|
||||
$('<h3>').text(Messages.fileEmbedTitle).appendTo($content);
|
||||
var $script = $('<p>').text(Messages.fileEmbedScript).appendTo($content);
|
||||
$('<br>').appendTo($script);
|
||||
var scriptId = uid();
|
||||
$('<input>', {
|
||||
type: 'text',
|
||||
id: scriptId,
|
||||
readonly: 'readonly',
|
||||
value: Cryptpad.getMediatagScript(),
|
||||
}).appendTo($script);
|
||||
$script.append(Cryptpad.dialog.selectable(Cryptpad.getMediatagScript()));
|
||||
var $tag = $('<p>').text(Messages.fileEmbedTag).appendTo($content);
|
||||
$('<br>').appendTo($tag);
|
||||
var tagId = uid();
|
||||
$('<input>', {
|
||||
type:'text',
|
||||
id: tagId,
|
||||
readonly:'readonly',
|
||||
value:Cryptpad.getMediatagFromHref(url),
|
||||
}).appendTo($tag);
|
||||
Cryptpad.alert($content.html(), null, true);
|
||||
$('#'+scriptId).click(function () {
|
||||
this.select();
|
||||
});
|
||||
$('#'+tagId).click(function () {
|
||||
this.select();
|
||||
});
|
||||
$tag.append(Cryptpad.dialog.selectable(Cryptpad.getMediatagFromHref(url)));
|
||||
Cryptpad.alert($content[0], null, true);
|
||||
});
|
||||
|
||||
toolbar.$leftside.append($shareBlock);
|
||||
|
||||
Reference in New Issue
Block a user