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/poll/inner.js

1389 lines
52 KiB
JavaScript

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

define([
'jquery',
'/common/toolbar.js',
'/common/common-util.js',
'/common/common-hash.js',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'/common/common-realtime.js',
'/customize/application_config.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/poll/render.js',
'/poll/export.js',
'/common/diffMarked.js',
'/common/sframe-common-codemirror.js',
'/common/common-thumbnail.js',
'/common/common-interface.js',
'/common/common-ui-elements.js',
'/common/hyperscript.js',
'/customize/messages.js',
'/common/sframe-common-codemirror.js',
'cm/lib/codemirror',
'/common/test.js',
'cm/addon/display/placeholder',
'cm/mode/markdown/markdown',
'css!cm/lib/codemirror.css',
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/poll/app-poll.less',
], function (
$,
Toolbar,
Util,
Hash,
nThen,
SFCommon,
CommonRealtime,
AppConfig,
Listmap,
Renderer,
Exporter,
DiffMd,
SframeCM,
Thumb,
UI,
UIElements,
h,
Messages,
SFCodeMirror,
CMeditor,
Test)
{
var saveAs = window.saveAs;
var APP = window.APP = {
unlocked: {
row: [],
col: []
},
readOnly: false,
mobile: function () { return $('body').width() <= 600; } // Menu and content area are not inline-block anymore for mobiles
};
var Render = Renderer(APP);
var debug = $.noop; //console.log;
var metadataMgr;
var Title;
var common;
var copyObject = function (obj) {
return JSON.parse(JSON.stringify(obj));
};
APP.getCSV = function () {
return Exporter.getCSV(APP.proxy.content);
};
var exportFile = function () {
Exporter.main(APP.proxy, function (blob, isJson) {
var suggestion = Title.suggestTitle(Title.defaultTitle);
var ext = isJson ? '.json' : '.csv';
UI.prompt(Messages.exportPrompt,
Util.fixFileName(suggestion) + ext, function (filename) {
if (!(typeof(filename) === 'string' && filename)) { return; }
saveAs(blob, filename);
});
});
};
/*
Make sure that the realtime data structure has all the required fields
*/
var prepareProxy = function (proxy, schema) {
if (proxy && proxy.version === 1) { return; }
debug("Configuring proxy schema...");
proxy.metadata = proxy.metadata || schema.metadata;
Object.keys(schema.metadata).forEach(function (k) {
if (!proxy.metadata[k]) { proxy.metadata[k] = schema.metadata[k]; }
});
proxy.content = proxy.content || schema.content;
Object.keys(schema.content).forEach(function (k) {
if (!proxy.content[k]) { proxy.content[k] = schema.content[k]; }
});
proxy.version = 1;
proxy.type = 'poll';
};
/*
Set the user id (user column) in the pad attributes
*/
var setUserId = function (id, cb) {
cb = cb || $.noop;
APP.userid = id;
common.setPadAttribute('userid', id, function (e) {
if (e) {
console.error(e);
return void cb(e);
}
cb();
});
};
var sortColumns = function (order, firstcol) {
var colsOrder = order.slice();
// Never put at the first position an uncommitted column
var idx = APP.proxy.content.colsOrder.indexOf(firstcol);
if (!firstcol || idx === -1) { return colsOrder; }
colsOrder.splice(idx, 1);
colsOrder.unshift(firstcol);
return colsOrder;
};
var isUncommitted = function (id) {
var idArr = id.split('_');
var idx = idArr[0];
var idy = idArr[1] || idArr[0]; // if id is y-{...} (no 'x'), use idArr[0] as 'y' coordinate
return APP.uncommitted.content.colsOrder.indexOf(idx) !== -1 ||
APP.uncommitted.content.rowsOrder.indexOf(idy) !== -1;
};
var mergeUncommitted = function (proxy, uncommitted, commit) {
var newObj;
if (commit) {
newObj = proxy;
} else {
newObj = copyObject(proxy);
}
// Merge uncommitted into the proxy
uncommitted.content.colsOrder = uncommitted.content.colsOrder || [];
uncommitted.content.colsOrder.forEach(function (x) {
if (newObj.content.colsOrder.indexOf(x) !== -1) { return; }
newObj.content.colsOrder.push(x);
});
for (var k in uncommitted.content.cols) {
if (!newObj.content.cols[k]) {
newObj.content.cols[k] = uncommitted.content.cols[k];
}
}
for (var l in uncommitted.content.cells) {
if (!newObj.content.cells[l]) {
newObj.content.cells[l] = uncommitted.content.cells[l];
}
}
// Uncommitted rows
uncommitted.content.rowsOrder = uncommitted.content.rowsOrder || [];
uncommitted.content.rowsOrder.forEach(function (x) {
if (newObj.content.rowsOrder.indexOf(x) !== -1) { return; }
newObj.content.rowsOrder.push(x);
});
for (var m in uncommitted.content.rows) {
if (!newObj.content.rows[m]) {
newObj.content.rows[m] = uncommitted.content.rows[m];
}
}
if (commit) {
APP.uncommited = {};
prepareProxy(APP.uncommitted, copyObject(Render.Example));
}
return newObj;
};
var enableColumn = APP.enableColumn = function (id, table) {
table = table || $('body');
var $input = $(table).find('input[disabled="disabled"][data-rt-id^="' + id + '"]')
.removeAttr('disabled');
$input.closest('td').addClass('cp-app-poll-table-editing');
$(table).find('.cp-app-poll-table-lock[data-rt-id="' + id + '"]').addClass('fa-unlock')
.removeClass('fa-lock').attr('title', Messages.poll_unlocked);
};
var disableColumn = function (id) {
var $input = $('input[data-rt-id^="' + id + '"]')
.attr('disabled', 'disabled');
$input.closest('td').removeClass('cp-app-poll-table-editing');
$('.cp-app-poll-table-lock[data-rt-id="' + id + '"]').addClass('fa-lock')
.removeClass('fa-unlock').attr('title', Messages.poll_locked);
};
var enableRow = APP.enableRow = function (id, table) {
table = table || $('body');
var $input = $(table).find('input[disabled="disabled"][data-rt-id="' + id + '"]')
.removeAttr('disabled');
$input.closest('td').addClass('cp-app-poll-table-editing');
$(table).find('span.cp-app-poll-table-edit[data-rt-id="' + id + '"]')
.css('visibility', 'hidden');
};
var disableRow = function (id) {
var $input = $('input[type="text"][data-rt-id="' + id + '"]')
.attr('disabled', 'disabled');
$input.closest('td').removeClass('cp-app-poll-table-editing');
$('span.cp-app-poll-table-edit[data-rt-id="' + id + '"]').css('visibility', 'visible');
};
var unlockElements = function () {
APP.unlocked.row.forEach(enableRow);
APP.unlocked.col.forEach(enableColumn);
};
var setTablePublished = function (bool) {
if (APP.locked) { bool = true; }
if (APP.markdownTb) { APP.markdownTb.setState(!bool); }
if (bool) {
if (APP.$publish) { APP.$publish.hide(); }
if (APP.$admin) { APP.$admin.show(); }
$('#cp-app-poll-form').addClass('cp-app-poll-published');
} else {
if (APP.$publish) { APP.$publish.show(); }
if (APP.$admin) { APP.$admin.hide(); }
$('#cp-app-poll-form').removeClass('cp-app-poll-published');
}
};
var addScrollClass = function () {
var $scroll = $('#cp-app-poll-table-scroll');
var hasScroll = $scroll.width() < $scroll[0].scrollWidth && $scroll.width() > 100;
if (hasScroll) {
$scroll.addClass('cp-app-poll-table-scrolled');
return;
}
$scroll.removeClass('cp-app-poll-table-scrolled');
};
var updateTableButtons = function () {
var uncomColId = APP.uncommitted.content.colsOrder[0];
var uncomRowId = APP.uncommitted.content.rowsOrder[0];
var $createOption = $('tbody input[data-rt-id="' + uncomRowId+'"]')
.closest('td').find('> div');
$createOption.find('#cp-app-poll-create-option').remove();
$createOption.append(APP.$createRow);
var $createUser = $('thead input[data-rt-id="' + uncomColId + '"]')
.closest('td');
$createUser.find('#cp-app-poll-create-user').remove();
$createUser.prepend(APP.$createCol);
};
var updateDisplayedTable = function () {
setTablePublished(APP.proxy.published);
addScrollClass();
updateTableButtons();
};
var unlockColumn = function (id, cb) {
if (APP.unlocked.col.indexOf(id) === -1) {
APP.unlocked.col.push(id);
}
enableColumn(id);
if (typeof(cb) === "function") { cb(); }
};
var unlockRow = function (id, cb) {
if (APP.unlocked.row.indexOf(id) === -1) {
APP.unlocked.row.push(id);
}
enableRow(id);
if (typeof(cb) === "function") { cb(); }
};
var lockColumn = function (id, cb) {
var idx = APP.unlocked.col.indexOf(id);
if (idx !== -1) {
APP.unlocked.col.splice(idx, 1);
}
disableColumn(id);
if (typeof(cb) === "function") { cb(); }
};
var lockRow = function (id, cb) {
var idx = APP.unlocked.row.indexOf(id);
if (idx !== -1) {
APP.unlocked.row.splice(idx, 1);
}
disableRow(id);
if (typeof(cb) === "function") { cb(); }
};
/* Any time the realtime object changes, call this function */
var change = function (o, n, path, throttle, cb) {
if (path && !Array.isArray(path)) {
return;
}
if (path && path.join) {
debug("Change from [%s] to [%s] at [%s]",
o, n, path.join(', '));
}
var table = APP.$table[0];
common.notify();
var getFocus = function () {
var active = document.activeElement;
if (!active) { return; }
return {
el: active,
id: $(active).attr('data-rt-id'),
start: active.selectionStart,
end: active.selectionEnd
};
};
var setFocus = function (obj) {
var el;
if (document.body.contains(obj.el)) { el = obj.el; }
else if($('input[data-rt-id="' + obj.id + '"]').length) {
el = $('input[data-rt-id="' + obj.id + '"]')[0];
}
else { return; }
el.focus();
if (obj.start) { el.selectionStart = obj.start; }
if (obj.end) { el.selectionEnd = obj.end; }
};
var updateTable = function () {
var displayedObj = mergeUncommitted(APP.proxy, APP.uncommitted);
var colsOrder = sortColumns(displayedObj.content.colsOrder, APP.userid);
var conf = {
cols: colsOrder,
readOnly: APP.locked
};
var f = getFocus();
APP.$createRow.detach();
APP.$createCol.detach();
Render.updateTable(table, displayedObj, conf);
// Fix autocomplete bug:
displayedObj.content.rowsOrder.forEach(function (rowId) {
if (f.id === rowId) { return; }
$('input[data-rt-id="' + rowId +'"]').val(displayedObj.content.rows[rowId] || '');
});
displayedObj.content.colsOrder.forEach(function (colId) {
if (f.id === colId) { return; }
$('input[data-rt-id="' + colId +'"]')
.val(displayedObj.content.cols[colId] || '');
});
updateDisplayedTable();
setFocus(f);
if (typeof(cb) === "function") {
cb();
}
};
if (throttle) {
if (APP.throttled) { window.clearTimeout(APP.throttled); }
APP.throttled = window.setTimeout(function () {
updateTable();
}, throttle);
return;
}
window.setTimeout(updateTable);
};
var getRealtimeId = function (input) {
return input.getAttribute && input.getAttribute('data-rt-id');
};
var handleBookmark = function (id) {
setUserId(id === APP.userid ? '' : id, change);
};
/* Called whenever an event is fired on an input element */
var handleInput = function (input) {
var type = input.type.toLowerCase();
var id = getRealtimeId(input);
debug(input);
var object = APP.proxy;
var x = Render.getCoordinates(id)[0];
if (isUncommitted(id)) { object = APP.uncommitted; }
switch (type) {
case 'text':
debug("text[rt-id='%s'] [%s]", id, input.value);
Render.setValue(object, id, input.value);
change(null, null, null, 1000);
break;
case 'number':
debug("checkbox[tr-id='%s'] %s", id, input.value);
if (APP.unlocked.col.indexOf(x) >= 0 || x === APP.userid) {
var value = parseInt(input.value);
if (isNaN(value)) {
console.error("Got NaN?!");
break;
}
Render.setValue(object, id, value);
change();
} else {
debug('checkbox locked');
}
break;
default:
debug("Input[type='%s']", type);
break;
}
};
var hideInputs = function (id) {
if (APP.locked) { return; }
if (id) {
var type = Render.typeofId(id);
if (type === 'col') { return void lockColumn(id); }
if (type === 'row') { return void lockRow(id); }
return;
}
var keepColUnlocked = [APP.userid, APP.uncommitted.content.colsOrder[0]];
var keepRowUnlocked = APP.uncommitted.content.rowsOrder.slice();
var toLock = [];
APP.unlocked.col.forEach(function (id) {
if (keepColUnlocked.indexOf(id) !== -1) { return; }
toLock.push(id);
});
toLock.forEach(lockColumn);
toLock = [];
APP.unlocked.row.forEach(function (id) {
if (keepRowUnlocked.indexOf(id) !== -1) { return; }
toLock.push(id);
});
toLock.forEach(lockRow);
};
/* Called whenever an event is fired on a span */
var handleSpan = function (span) {
if (!span) { return; }
var id = span.getAttribute('data-rt-id');
var type = Render.typeofId(id);
var isRemove = span.className && span.className.split(' ')
.indexOf('cp-app-poll-table-remove') !== -1;
var isEdit = span.className && span.className.split(' ')
.indexOf('cp-app-poll-table-edit') !== -1;
var isBookmark = span.className && span.className.split(' ')
.indexOf('cp-app-poll-table-bookmark') !== -1;
var isLock = span.className && span.className.split(' ')
.indexOf('cp-app-poll-table-lock') !== -1;
var isLocked = span.className && span.className.split(' ').indexOf('fa-lock') !== -1;
if (type === 'row') {
if (isRemove) {
UI.confirm(Messages.poll_removeOption, function (res) {
if (!res) { return; }
Render.removeRow(APP.proxy, id, function () {
change();
});
});
} else if (isEdit) {
unlockRow(id, function () {
$('input[data-rt-id="' + id + '"]').focus();
});
}
} else if (type === 'col') {
if (isRemove) {
UI.confirm(Messages.poll_removeUser, function (res) {
if (!res) { return; }
Render.removeColumn(APP.proxy, id, function () {
change();
if (id === APP.userid) { setUserId(''); }
});
});
} else if (isBookmark) {
handleBookmark(id);
} else if (isLock && isLocked) {
unlockColumn(id, function () {
$('input[data-rt-id="' + id + '"]').focus();
});
} else if (isLock) {
lockColumn(id);
}
} else if (type === 'cell') {
change();
} else {
debug("UNHANDLED");
}
};
var optionOrder = {
3: 1, // ? => ✔
1: 2, // ✔ => ~
2: 0, // ~ => x
0: 3, // x => ?
// undefined => 3
};
var handleClick = function (e, isKeyup) {
if (APP.locked) { return; }
e.stopPropagation();
if (!APP.ready) { return; }
if (!isKeyup && e.which !== 1) { return; } // only allow left clicks
var target = e && e.target;
if (!target) { return void debug("NO TARGET"); }
var nodeName = target && target.nodeName;
switch (nodeName) {
case 'INPUT':
if ($(target).is('[type="text"]') && !isKeyup) { return; }
if (isKeyup && (e.keyCode === 13 || e.keyCode === 27)) {
var id = target.getAttribute('data-rt-id');
if ($(target).parents('.cp-app-poll-table-uncommitted').length
&& e.keyCode === 13) {
var type = Render.typeofId(id);
if (type === "row") { APP.$createRow.click(); }
else if (type === "col") { APP.$createCol.click(); }
break;
}
hideInputs(id);
break;
}
if ($(target).is('input[type="number"]')) {
// Nothing to do...
//console.error("number input focused?");
break;
}
handleInput(target);
break;
case 'LABEL':
var input = $('input[type="number"][id=' + $(target).attr('for') + ']');
var value = parseInt(input.val());
input.val(optionOrder[value]);
handleInput(input[0]);
break;
case 'SPAN':
handleSpan(target);
break;
case undefined:
//console.error(new Error("C'est pas possible!"));
break;
default:
debug(target, nodeName);
break;
}
};
/*
*/
var updatePublishButton = function () {
if (!APP.ready || !APP.proxy || !APP.$publishButton) { return; }
var p = APP.proxy.published;
var msg = (p ? Messages.poll_edit : Messages.poll_publish_button);
APP.$publishButton.find('.cp-toolbar-name').text(msg);
if (p) {
APP.$publishButton.find('i').removeClass('fa-check').addClass('fa-pencil');
return;
}
APP.$publishButton.find('i').addClass('fa-check').removeClass('fa-pencil');
};
var publish = APP.publish = function (bool) {
if (!APP.readOnly) {
if (!APP.ready) { return; }
if (APP.proxy.published !== bool) {
APP.proxy.published = bool;
}
} else {
// If readOnly, always put the app in published mode
bool = true;
}
$(APP.$mediaTagButton).toggle(!bool);
setTablePublished(bool);
/*['textarea'].forEach(function (sel) {
$(sel).attr('disabled', bool);
});*/
updatePublishButton();
APP.editor.refresh();
};
var setEditable = function (editable) {
APP.locked = APP.readOnly || !editable;
if (editable === false) {
// disable all the things
$('.cp-app-poll-realtime input, .cp-app-poll-realtime button, .cp-app-poll-upper button, .cp-app-poll-realtime textarea').attr('disabled', true);
$('span.cp-app-poll-table-edit, span.cp-app-poll-table-remove').hide();
$('span.cp-app-poll-table-lock').addClass('fa-lock').removeClass('fa-unlock')
.attr('title', Messages.poll_locked)
.css({'cursor': 'default'});
} else {
// enable
$('span.cp-app-poll-table-edit, span.cp-app-poll-table-remove').show();
$('span.cp-app-poll-table-lock').css({'cursor': ''});
$('.cp-app-poll-realtime button, .cp-app-poll-upper button, .cp-app-poll-realtime textarea').attr('disabled', false);
unlockElements();
}
};
var updatePublishedDescription = function () {
var v = APP.editor.getValue();
DiffMd.apply(DiffMd.render(v || ''), APP.$descriptionPublished, common);
};
var updateDescription = function (old, n) {
var o = APP.editor.getValue();
SframeCM.setValueAndCursor(APP.editor, o, n);
updatePublishedDescription();
common.notify();
};
var updateLocalDescription = function (n) {
APP.proxy.description = n;
updatePublishedDescription();
};
var getCommentId = Render.Uid('c');
var removeComment = function (uid) {
var idx = APP.proxy.commentsOrder.indexOf(uid);
if (idx !== -1) { APP.proxy.commentsOrder.splice(idx, 1); }
delete APP.proxy.comments[uid];
APP.updateComments();
};
/*var editComment = function (id) {
// TODO
};*/
var avatars = {};
var updateComments = APP.updateComments = function () {
if (!APP.proxy.comments) { APP.proxy.comments = {}; }
if (!APP.proxy.commentsOrder) { APP.proxy.commentsOrder = []; }
var profile;
if (common.isLoggedIn()) {
profile = metadataMgr.getUserData().profile;
}
var $comments = APP.$comments.html('');
var comments = APP.proxy.comments;
APP.proxy.commentsOrder.forEach(function (k) {
var c = comments[k];
if (!c) { return; }
var name = c.name || Messages.anonymous;
var $c = $('<div>', {
'class': 'cp-app-poll-comments-list-el'
}).prependTo($comments);
// Metadata
var $data = $('<div>', { 'class': 'cp-app-poll-comments-list-data' }).appendTo($c);
var $avatar = $('<span>', {
'class': 'cp-app-poll-comments-list-data-avatar cp-avatar'
}).appendTo($data);
if (c.avatar && avatars[c.avatar]) {
$avatar.append(avatars[c.avatar]);
} else {
common.displayAvatar($avatar, c.avatar, name, function ($img) {
if (c.avatar && $img.length) { avatars[c.avatar] = $img[0].outerHTML; }
});
}
if (c.profile) {
$('<a>', {
'href': APP.origin + '/profile/#' + c.profile,
'target': '_blank',
'class': 'cp-app-poll-comments-list-data-name'
}).appendTo($data).text(name);
} else {
$('<span>', {
'class': 'cp-app-poll-comments-list-data-name'
}).appendTo($data).text(name);
}
$('<span>', {
'class': 'cp-app-poll-comments-list-data-time'
}).appendTo($data).text(new Date(c.time).toLocaleString());
// Message
var $msg = $('<div>', { 'class': 'cp-app-poll-comments-list-msg' }).appendTo($c);
$('<div>', {
'class': 'cp-app-poll-comments-list-msg-text'
}).appendTo($msg).text(c.msg);
var $actions = $('<div>', {
'class': 'cp-app-poll-comments-list-msg-actions'
}).appendTo($msg);
// Actions
if (!APP.readOnly && (!c.profile || c.profile === profile)) {
$('<button>', {
'class': 'btn btn-secondary fa fa-times',
'title': Messages.poll_comment_remove,
'data-rt-id': k
}).appendTo($actions).click(function () { removeComment(k); });
/*$('<button>', {
'class': 'fa fa-pencil',
'title': 'TODO: edit comment',
'data-rt-id': c.uid
}).appendTo($actions).click(editComment);*/
}
});
common.notify();
};
var resetComment = function () {
APP.$addComment.find('.cp-app-poll-comments-add-name')
.val(metadataMgr.getUserData().name || '');
APP.$addComment.find('.cp-app-poll-comments-add-msg').val('');
};
var addComment = function () {
if (!APP.proxy.comments) { APP.proxy.comments = {}; }
if (!APP.proxy.commentsOrder) { APP.proxy.commentsOrder = []; }
var name = APP.$addComment.find('.cp-app-poll-comments-add-name').val().trim();
var msg = APP.$addComment.find('.cp-app-poll-comments-add-msg').val().trim();
var time = +new Date();
if (!msg) { return; }
var profile, avatar;
if (common.isLoggedIn()) {
profile = metadataMgr.getUserData().profile;
avatar = metadataMgr.getUserData().avatar;
}
var uid = getCommentId();
APP.proxy.commentsOrder.push(uid);
APP.proxy.comments[uid] = {
msg: msg,
name: name,
time: time,
profile: profile,
avatar: avatar
};
resetComment();
updateComments();
};
var initThumbnails = function () {
var privateDat = metadataMgr.getPrivateData();
if (!privateDat.thumbnails) { return; } // Thumbnails are disabled
var $el = $('.cp-app-poll-realtime');
//var $el = $('#cp-app-poll-table');
var scrollTop;
var options = {
getContainer: function () { return $el[0]; },
filter: function (el, before) {
if (before) {
$el.parents().css('overflow', 'visible');
scrollTop = $('#cp-app-poll-form').scrollTop();
$el.css('max-height', Math.max(600, $(el).width()) + 'px');
$el.find('tr td:first-child, tr td:last-child, tr td:nth-last-child(2)')
.css('position', 'static');
$el.find('#cp-app-poll-comments').css('display', 'none');
$el.find('#cp-app-poll-table-container').css('text-align', 'center');
$el.find('#cp-app-poll-table-scroll').css('margin', 'auto');
$el.find('#cp-app-poll-table-scroll').css('max-width', '100%');
return;
}
$el.parents().css('overflow', '');
$el.css('max-height', '');
$el.find('#cp-app-poll-comments').css('display', '');
$el.find('#cp-app-poll-table-container').css('text-align', '');
$el.find('#cp-app-poll-table-scroll').css('margin', '');
$el.find('#cp-app-poll-table-scroll').css('max-width', '');
$el.find('tr td:first-child, tr td:last-child, tr td:nth-last-child(2)')
.css('position', '');
$('#cp-app-poll-form').scrollTop(scrollTop);
},
type: 'poll',
getContent: function () { return JSON.stringify(APP.proxy.content); }
};
Thumb.initPadThumbnails(common, options);
};
var checkDeletedCells = function () {
// faster than forEach?
var c;
if (!APP.proxy || !APP.proxy.content) { return; }
for (var k in APP.proxy.content.cells) {
c = Render.getCoordinates(k);
if (APP.proxy.content.colsOrder.indexOf(c[0]) === -1 ||
APP.proxy.content.rowsOrder.indexOf(c[1]) === -1) {
delete APP.proxy.content.cells[k];
}
}
};
var onReady = function (info, userid) {
var proxy = APP.proxy;
var isNew = false;
var userDoc = JSON.stringify(proxy);
if (userDoc === "" || userDoc === "{}") { isNew = true; }
if (APP.toolbar && APP.rt.cpCnInner) {
// Check if we have a new chainpad instance
APP.toolbar.resetChainpad(APP.rt.cpCnInner.chainpad);
}
if (!isNew) {
if (proxy.info) {
// Migration
proxy.metadata = proxy.info;
delete proxy.info;
}
if (proxy.table) {
// Migration
proxy.content = proxy.table;
delete proxy.table;
}
checkDeletedCells();
if (proxy.comments && !proxy.commentsOrder) { // Migration
proxy.commentsOrder = Object.keys(copyObject(proxy.comments)).sort(function (a, b) {
return proxy.comments[a].time > proxy.comments[b].time;
});
}
if (proxy && proxy.metadata) {
metadataMgr.updateMetadata(proxy.metadata);
}
if (typeof (proxy) !== 'object' || Array.isArray(proxy) ||
(proxy.metadata && typeof(proxy.metadata.type) !== 'undefined' &&
proxy.metadata.type !== 'poll')) {
var errorText = Messages.typeError;
UI.errorLoadingScreen(errorText);
throw new Error(errorText);
}
}
if (!proxy.metadata || typeof(proxy.metadata.title) === "undefined") {
Title.updateTitle(Title.defaultTitle);
}
if (typeof(proxy.type) === 'undefined') {
proxy.type = 'poll';
}
// Add uncommitted and unlock uncommited & own column
var uncommitted = APP.uncommitted = {};
prepareProxy(proxy, copyObject(Render.Example));
prepareProxy(uncommitted, copyObject(Render.Example));
var coluid = Render.coluid();
if (userid) {
// If userid exists, it means the user already has a pinned column
// and we should unlock it
unlockColumn(userid);
}
uncommitted.content.colsOrder.push(coluid);
unlockColumn(coluid);
var rowuid = Render.rowuid();
uncommitted.content.rowsOrder.push(rowuid);
unlockRow(rowuid);
/*
Extract uncommitted data (row or column) and create a new uncommitted row or column
*/
var getUncommitted = function (type) {
var ret = {}, toRemove;
var uncommitted = APP.uncommitted.content;
if (type === 'col') {
ret.colsOrder = uncommitted.colsOrder.slice();
ret.cols = copyObject(uncommitted.cols);
// get only the cells corresponding to the committed rows
toRemove = Object.keys(uncommitted.cells).filter(function (coor) {
var c = Render.getCoordinates(coor);
return APP.proxy.content.rowsOrder.indexOf(c[1]) !== -1;
});
ret.cells = {};
toRemove.forEach(function (k) {
ret.cells[k] = uncommitted.cells[k];
delete uncommitted.cells[k];
});
uncommitted.colsOrder = [Render.coluid()];
uncommitted.cols = {};
return ret;
}
// Row
ret.rowsOrder = uncommitted.rowsOrder.slice();
ret.rows = copyObject(uncommitted.rows);
// get only the cells corresponding to the committed rows
toRemove = Object.keys(uncommitted.cells).filter(function (coor) {
var c = Render.getCoordinates(coor);
return APP.proxy.content.colsOrder.indexOf(c[1]) !== -1;
});
ret.cells = {};
toRemove.forEach(function (k) {
ret.cells[k] = uncommitted.cells[k];
delete uncommitted.cells[k];
});
uncommitted.rowsOrder = [Render.rowuid()];
uncommitted.rows = {};
return ret;
};
APP.$createCol = $('#cp-app-poll-create-user').click(function () {
var uncommittedCopy = { content: getUncommitted('col') };
var id = uncommittedCopy.content.colsOrder[0];
if (!APP.userid) { setUserId(id); }
mergeUncommitted(proxy, uncommittedCopy, true);
change(null, null, null, null, function() {
unlockColumn(id);
unlockColumn(APP.uncommitted.content.colsOrder[0]);
});
});
APP.$createRow = $('#cp-app-poll-create-option').click(function () {
var uncommittedCopy = { content: getUncommitted('row') };
mergeUncommitted(proxy, uncommittedCopy, true);
change(null, null, null, null, function() {
var newId = APP.uncommitted.content.rowsOrder[0];
$('input[data-rt-id="' + newId + '"]').focus();
});
});
var displayedObj = mergeUncommitted(proxy, uncommitted, false);
var colsOrder = sortColumns(displayedObj.content.colsOrder, userid);
Render.updateTable($('#cp-app-poll-table-scroll').find('table')[0], displayedObj, {
cols: colsOrder,
readOnly: APP.readOnly
});
// Description
APP.editor.on('change', function () {
var val = APP.editor.getValue();
updateLocalDescription(val);
});
APP.$addComment.find('.cp-app-poll-comments-add-submit').click(addComment);
APP.$addComment.find('.cp-app-poll-comments-add-cancel').click(resetComment);
var $table = APP.$table = $('#cp-app-poll-table-scroll').find('table');
updateDisplayedTable();
updateDescription(null, APP.proxy.description || '');
initThumbnails();
var markdownTb = APP.markdownTb = common.createMarkdownToolbar(APP.editor);
$('.CodeMirror').parent().prepend(markdownTb.toolbar);
APP.toolbar.$bottomL.append(markdownTb.button);
// Add drop and paste handlers
var privateData = metadataMgr.getPrivateData();
var fmConfig = {
dropArea: $('.CodeMirror'),
body: $('body'),
onUploaded: function (ev, data) {
var parsed = Hash.parsePadUrl(data.url);
var secret = Hash.getSecrets('file', parsed.hash, data.password);
var fileHost = privateData.fileHost || privateData.origin;
var src = fileHost + Hash.getBlobPathFromHex(secret.channel);
var key = Hash.encodeBase64(secret.keys.cryptKey);
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>';
APP.editor.replaceSelection(mt);
}
};
common.createFileManager(fmConfig);
SframeCM.handleImagePaste(APP.editor);
// Initialize author name for comments.
// Disable name modification for logged in users
var $cName = APP.$addComment.find('.cp-app-poll-comments-add-name')
.val(metadataMgr.getUserData().name || '');
if (common.isLoggedIn()) { $cName.attr('disabled', 'disabled'); }
updateComments();
$table
.click(handleClick)
.on('keyup', function (e) { handleClick(e, true); });
$(window).click(function(e) {
if (e.which !== 1) { return; }
hideInputs();
});
proxy
.on('change', ['metadata'], function () {
metadataMgr.updateMetadata(proxy.metadata);
})
.on('change', ['content'], function () {
change(null, null, null, 100);
})
.on('change', ['description'], updateDescription)
.on('change', ['comments'], updateComments)
.on('change', ['published'], function () {
publish(proxy.published);
})
.on('remove', [], function () {
change(null, null, null, 100);
});
// If the user's column is not committed, add his username
var $userInput = $('.cp-app-poll-table-uncommitted > input');
if (!APP.userid) {
var uname = metadataMgr.getUserData().name;
APP.uncommitted.content.cols[APP.uncommitted.content.colsOrder[0]] = uname;
$userInput.val(uname);
}
APP.ready = true;
if (!proxy.published) {
publish(false);
} else {
publish(true);
}
var passIfOk = function (t) {
t.assert($('#cp-app-poll-description-published').text().indexOf(
"Content for the description") === 0);
t.assert($('.cp-app-poll-comments-list-data-name').text().indexOf(
"Mr.Me") === 0);
t.assert($('.cp-app-poll-comments-list-msg-text').text().indexOf(
"Example comment yay") === 0);
t.assert($('input[value="Candy"]').length === 1);
t.assert($('input[value="IceCream"]').length === 1);
t.assert($('input[value="Soda"]').length === 1);
t.assert($('input[value="Meeee"]').length === 1);
t.pass();
};
if (!APP.readOnly) {
console.log("Here is the test");
Test(function (t) {
if ($('input[value="Candy"]').length) {
t.fail("Test has already been performed");
return;
}
nThen(function (waitFor) {
console.log("Here is the test1");
APP.editor.setValue("Content for the description");
$('.cp-app-poll-table-editing .cp-app-poll-table-text-cell input').val(
'Candy').keyup();
$('#cp-app-poll-create-option').click();
// TODO(cjd): Need to click outside to lock the first option we create.. bug?
$(window).trigger({ type: "click", which: 1 });
setTimeout(waitFor());
}).nThen(function (waitFor) {
$('.cp-app-poll-table-editing .cp-app-poll-table-text-cell input').val(
'IceCream').keyup();
$('#cp-app-poll-create-option').click();
setTimeout(waitFor());
}).nThen(function (waitFor) {
$('.cp-app-poll-table-editing .cp-app-poll-table-text-cell input').val(
'Soda').keyup();
$('#cp-app-poll-create-option').click();
setTimeout(waitFor());
}).nThen(function (waitFor) {
// Switch to non-admin mode
$('.cp-toolbar-icon-publish').click();
setTimeout(waitFor());
}).nThen(function (waitFor) {
$('.cp-app-poll-comments-add-name').val("Mr.Me").keyup();
$('.cp-app-poll-comments-add-msg').val("Example comment yay").keyup();
setTimeout(waitFor());
}).nThen(function (waitFor) {
$('.cp-app-poll-comments-add-submit').click();
setTimeout(waitFor());
}).nThen(function (waitFor) {
$('#cp-app-poll-create-user').parent().find('input').val('Meeee').keyup();
[1,3,2].forEach(function (num, i) {
var x = $($('.cp-app-poll-table-checkbox-contain label')[i]);
for (var ii = 0; ii < num; ii++) {
x.trigger({ type: 'click', which: 1 });
}
});
setTimeout(waitFor());
}).nThen(function (waitFor) {
$('#cp-app-poll-create-user').click();
setTimeout(waitFor());
}).nThen(function (waitFor) {
APP.rt.realtime.onSettle(waitFor());
}).nThen(function (/*waitFor*/) {
passIfOk(t);
});
});
} else {
Test(passIfOk);
}
// No need for onLocal in openPadChat because in poll, we listen for metadata changes
// and save them everytime.
// See `metadataMgr.onChange(function () {`
common.openPadChat(function () {});
UI.removeLoadingScreen();
common.checkTrimHistory();
};
var onError = function (info) {
if (info && info.type) {
if (info.type === 'CHAINPAD') {
APP.unrecoverable = true;
setEditable(false);
APP.toolbar.errorState(true, info.error);
var msg = Messages.chainpadError;
UI.errorLoadingScreen(msg, true, true);
console.error(info.error);
return;
}
// Server error
return void common.onServerError(info, APP.toolbar, function () {
APP.unrecoverable = true;
setEditable(false);
});
}
};
// Manage disconnections because of network or error
var onDisconnect = function (info) {
if (APP.unrecoverable) { return; }
if (info && info.type) {
// Server error
return void common.onServerError(info, APP.toolbar, function () {
APP.unrecoverable = true;
setEditable(false);
});
}
setEditable(false);
//UIElements.disconnectAlert();
};
var onReconnect = function () {
if (APP.unrecoverable) { return; }
setEditable(true);
//UIElements.reconnectAlert();
};
var getHeadingText = function () {
if (!APP.editor) { return; }
return SframeCM.getHeadingText(APP.editor);
};
var onCreate = function (info) {
APP.myID = info.myID;
if (APP.realtime !== info.realtime) {
APP.realtime = info.realtime;
}
metadataMgr = common.getMetadataMgr();
var titleCfg = { getHeadingText: getHeadingText };
Title = common.createTitle(titleCfg);
var configTb = {
displayed: ['pad'],
title: Title.getTitleConfig(),
metadataMgr: metadataMgr,
readOnly: APP.readOnly,
realtime: info.realtime,
sfCommon: common,
$container: APP.$bar,
$contentContainer: APP.$content
};
APP.toolbar = Toolbar.create(configTb);
Title.setToolbar(APP.toolbar);
var $drawer = APP.toolbar.$drawer;
metadataMgr.onChange(function () {
var md = copyObject(metadataMgr.getMetadata());
APP.proxy.metadata = md;
});
metadataMgr.onRequestSync(function () {
var meta = JSON.parse(JSON.stringify(APP.proxy.metadata));
metadataMgr.updateMetadata(meta);
});
/* add a forget button */
var forgetCb = function (err) {
if (err) { return; }
setEditable(false);
};
var $forgetPad = common.createButton('forget', true, {}, forgetCb);
$drawer.append($forgetPad);
var $properties = common.createButton('properties', true);
$drawer.append($properties);
var privateData = metadataMgr.getPrivateData();
/* save as template */
if (!privateData.isTemplate && common.isLoggedIn()) {
var templateObj = {
rt: info.realtime,
getTitle: function () { return metadataMgr.getMetadata().title; }
};
var $templateButton = common.createButton('template', true, templateObj);
$drawer.append($templateButton);
}
var $copy = common.createButton('copy', true);
$drawer.append($copy);
/* add an export button */
var $export = common.createButton('export', true, {}, exportFile);
$drawer.append($export);
$('#cp-app-poll-form').prepend(common.getBurnAfterReadingWarning());
if (!privateData.isEmbed) {
var helpMenu = common.createHelpMenu(['poll']);
$('#cp-app-poll-form').prepend(helpMenu.menu);
$drawer.append(helpMenu.button);
}
if (APP.readOnly) { publish(true); return; }
var $publish = common.createButton('', true, {
name: 'publish',
text: Messages.poll_publish_button,
icon: 'fa-check',
drawer: false,
hiddenReadOnly: true
}).click(function () { publish(!APP.proxy.published); }).appendTo(APP.toolbar.$bottomM);
APP.$publishButton = $publish;
updatePublishButton();
if (common.isLoggedIn()) {
APP.$mediaTagButton = common.createButton('mediatag', true).click(function () {
var pickerCfg = {
types: ['file'],
where: ['root']
};
common.openFilePicker(pickerCfg, function (data) {
if (data.type === 'file' && APP.editor) {
common.setPadAttribute('atime', +new Date(), null, data.href);
var mt = '<media-tag src="' + data.src + '" data-crypto-key="cryptpad:' + data.key + '"></media-tag>';
APP.editor.replaceSelection(mt);
return;
}
});
}).appendTo(APP.toolbar.$bottomL);
var $importTemplateButton = common.createButton('importtemplate', true);
$drawer.append($importTemplateButton);
var $tags = common.createButton('hashtag', true);
$drawer.append($tags);
}
};
var initialContent = function () {
return [
h('div#cp-toolbar.cp-toolbar-container'),
h('div#cp-app-poll-content', [
h('div#cp-app-poll-form', [
h('div.cp-app-poll-realtime', [
h('br'),
h('div', [
h('textarea#cp-app-poll-description', {
rows: "5",
cols: "50",
placeholder: Messages.poll_descriptionHint,
disabled: true
}),
h('div#cp-app-poll-description-published'),
h('br')
]),
h('div#cp-app-poll-table-container', [
h('div#cp-app-poll-table-scroll', [h('table')]),
h('button#cp-app-poll-create-user.btn.btn-default', {
title: Messages.poll_create_user
}, Messages.poll_commit),
h('button#cp-app-poll-create-option.btn.btn-default', {
title: Messages.poll_create_option
}, h('span.fa.fa-plus')),
]),
h('div#cp-app-poll-comments', [
h('h2#cp-app-poll-comments-add-title', Messages.poll_comment_add),
h('div#cp-app-poll-comments-add', [
h('input.cp-app-poll-comments-add-name', {
type: 'text',
placeholder: Messages.anonymous
}),
h('textarea.cp-app-poll-comments-add-msg', {
placeholder: Messages.poll_comment_placeholder
}),
h('button.cp-app-poll-comments-add-submit.btn.btn-secondary',
Messages.poll_comment_submit),
h('button.cp-app-poll-comments-add-cancel.btn.btn-secondary',
Messages.cancel)
]),
h('h2#cp-app-poll-comments-list-title', Messages.poll_comment_list),
h('div#cp-app-poll-comments-list')
]),
h('div#cp-app-poll-nocomments', Messages.poll_comment_disabled)
])
])
])
];
};
var main = function () {
nThen(function (waitFor) {
$(waitFor(function () {
UI.addLoadingScreen();
var $div = $('<div>').append(initialContent());
$('body').append($div.html());
}));
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (waitFor) {
common.getSframeChannel().onReady(waitFor());
}).nThen(function (waitFor) {
common.handleNewFile(waitFor);
}).nThen(function (/* waitFor */) {
Test.registerInner(common.getSframeChannel());
var metadataMgr = common.getMetadataMgr();
metadataMgr.setDegraded(false); // FIXME degarded mode unsupported (no cursor channel)
APP.locked = APP.readOnly = metadataMgr.getPrivateData().readOnly;
APP.loggedIn = common.isLoggedIn();
APP.SFCommon = common;
if (APP.readOnly) {
$('#cp-app-poll-form').addClass('cp-app-poll-readonly');
}
APP.origin = common.getMetadataMgr().getPrivateData().origin;
APP.$body = $('body');
APP.$bar = $('#cp-toolbar');
APP.$content = $('#cp-app-poll-content');
APP.$descriptionPublished = $('#cp-app-poll-description-published');
APP.$description = $('#cp-app-poll-description');
APP.$comments = $('#cp-app-poll-comments-list');
APP.$addComment = $('#cp-app-poll-comments-add');
var cm = SFCodeMirror.create("gfm", CMeditor, APP.$description[0]);
var editor = APP.editor = cm.editor;
editor.setOption('lineNumbers', true);
editor.setOption('lineWrapping', true);
editor.setOption('styleActiveLine', true);
editor.setOption('readOnly', false);
cm.configureTheme(common, function () {});
APP.$descriptionPublished.click(function (e) {
if (!e.target) { return; }
var $t = $(e.target);
if ($t.is('a') || $t.parents('a').length) {
e.preventDefault();
var $a = $t.is('a') ? $t : $t.parents('a').first();
var href = $a.attr('href');
if (!href) { return; }
common.openUnsafeURL(href);
}
});
var listmapConfig = {
data: {},
common: common,
logLevel: 1
};
if (APP.readOnly) {
$('#cp-app-poll-create-user, #cp-app-poll-create-option, #cp-app-poll-comments-add')
.remove();
$('#cp-app-poll-comments-add-title').remove();
}
var rt = APP.rt = Listmap.create(listmapConfig);
APP.proxy = rt.proxy;
var firstConnection = true;
rt.proxy.on('create', onCreate)
.on('ready', function (info) {
if (!firstConnection) { return; } // TODO fix this issue in listmap
firstConnection = false;
common.getPadAttribute('userid', function (e, userid) {
if (e) { console.error(e); }
APP.userid = userid;
onReady(info, userid);
});
})
.on('disconnect', onDisconnect)
.on('reconnect', onReconnect)
.on('error', onError);
});
};
main();
});