cryptpad/www/form/main.js

214 lines
5.9 KiB
JavaScript
Raw Normal View History

require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } });
2016-04-21 13:51:04 +00:00
define([
'/api/config?cb=' + Math.random().toString(16).substring(2),
'/common/realtime-input.js',
2016-04-21 13:51:04 +00:00
'/common/crypto.js',
'/bower_components/textpatcher/TextPatcher.amd.js',
'json.sortify',
'/form/ula.js',
'/common/json-ot.js',
2016-04-21 13:51:04 +00:00
'/bower_components/jquery/dist/jquery.min.js',
'/customize/pad.js'
], function (Config, Realtime, Crypto, TextPatcher, Sortify, Formula, JsonOT) {
2016-04-21 13:51:04 +00:00
var $ = window.jQuery;
var key;
var channel = '';
var hash = false;
if (!/#/.test(window.location.href)) {
key = Crypto.genKey();
} else {
hash = window.location.hash.slice(1);
channel = hash.slice(0,32);
key = hash.slice(32);
2016-04-21 13:51:04 +00:00
}
var module = window.APP = {
TextPatcher: TextPatcher,
Sortify: Sortify,
Formula: Formula,
};
2016-04-21 13:51:04 +00:00
var initializing = true;
var uid = module.uid = Formula.uid;
var getInputType = Formula.getInputType;
2016-05-20 11:39:40 +00:00
var $elements = module.elements = $('input, select, textarea');
var eventsByType = Formula.eventsByType;
2016-04-21 13:51:04 +00:00
var Map = module.Map = {};
var UI = module.UI = {
ids: [],
each: function (f) {
UI.ids.forEach(function (id, i, list) {
f(UI[id], i, list);
});
}
};
var cursorTypes = ['textarea', 'password', 'text'];
var canonicalize = function (text) { return text.replace(/\r\n/g, '\n'); };
2016-05-13 14:41:36 +00:00
$elements.each(function (index, element) {
var $this = $(this);
var id = uid();
var type = getInputType($this);
// ignore hidden elements
if (type === 'hidden') { return; }
$this // give each element a uid
.data('rtform-uid', id)
// get its type
.data('rt-ui-type', type);
UI.ids.push(id);
var component = UI[id] = {
id: id,
$: $this,
element: element,
type: type,
preserveCursor: cursorTypes.indexOf(type) !== -1,
name: $this.prop('name'),
};
component.value = (function () {
var checker = ['radio', 'checkbox'].indexOf(type) !== -1;
if (checker) {
return function (content) {
return typeof content !== 'undefined'?
$this.prop('checked', !!content):
$this.prop('checked');
};
} else {
return function (content) {
return typeof content !== 'undefined' ?
$this.val(content):
canonicalize($this.val());
};
}
}());
var update = component.update = function () { Map[id] = component.value(); };
update();
});
2016-04-21 13:51:04 +00:00
var config = module.config = {
initialState: Sortify(Map) || '{}',
websocketURL: Config.websocketURL,
2016-04-21 13:51:04 +00:00
userName: Crypto.rand64(8),
channel: channel,
cryptKey: key,
crypto: Crypto,
transformFunction: JsonOT.validate
2016-04-21 13:51:04 +00:00
};
var setEditable = module.setEditable = function (bool) {
/* (dis)allow editing */
$elements.each(function () {
$(this).attr('disabled', !bool);
});
};
2016-04-21 13:51:04 +00:00
setEditable(false);
var onInit = config.onInit = function (info) {
var realtime = module.realtime = info.realtime;
window.location.hash = info.channel + key;
2016-04-21 13:51:04 +00:00
// create your patcher
module.patchText = TextPatcher.create({
realtime: realtime,
logging: true,
});
2016-04-21 13:51:04 +00:00
};
2016-05-20 11:39:40 +00:00
var readValues = function () {
UI.each(function (ui, i, list) {
Map[ui.id] = ui.value();
});
};
2016-04-21 13:51:04 +00:00
var onLocal = config.onLocal = function () {
if (initializing) { return; }
/* serialize local changes */
readValues();
module.patchText(Sortify(Map));
2016-04-21 13:51:04 +00:00
};
var updateValues = function () {
var userDoc = module.realtime.getUserDoc();
var parsed = JSON.parse(userDoc);
console.log(userDoc);
// flush received values to the map
// but only if you don't have them locally
// this *shouldn't* break cursors
Object.keys(parsed).forEach(function (key) {
if (UI.ids.indexOf(key) === -1) { Map[key] = parsed[key]; }
});
UI.each(function (ui, i, list) {
var newval = parsed[ui.id];
var oldval = ui.value();
if (typeof(newval) === 'undefined') { return; }
if (newval === oldval) { return; }
var op;
2016-05-20 11:39:40 +00:00
var selects;
var element = ui.element;
if (ui.preserveCursor) {
op = TextPatcher.diff(oldval, newval);
2016-05-20 11:39:40 +00:00
selects = ['selectionStart', 'selectionEnd'].map(function (attr) {
var before = element[attr];
var after = TextPatcher.transformCursor(element[attr], op);
return after;
});
}
ui.value(newval);
ui.update();
2016-05-20 11:39:40 +00:00
if (op && ui.preserveCursor) {
//console.log(selects);
element.selectionStart = selects[0];
element.selectionEnd = selects[1];
}
2016-04-21 13:51:04 +00:00
});
};
2016-04-21 13:51:04 +00:00
var onRemote = config.onRemote = function (info) {
if (initializing) { return; }
/* integrate remote changes */
updateValues();
};
var onReady = config.onReady = function (info) {
updateValues();
2016-04-21 13:51:04 +00:00
console.log("READY");
2016-04-21 13:51:04 +00:00
setEditable(true);
initializing = false;
};
var onAbort = config.onAbort = function (info) {
window.alert("Network Connection Lost");
};
2016-04-21 13:51:04 +00:00
var rt = Realtime.start(config);
UI.each(function (ui, i, list) {
var type = ui.type;
var events = eventsByType[type];
ui.$.on(events, onLocal);
});
2016-04-21 13:51:04 +00:00
});