define([ 'jquery', '/bower_components/chainpad-json-validator/json-ot.js', '/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad/chainpad.dist.js', ], function ($, JsonOT, Crypto) { var ChainPad = window.ChainPad; var History = {}; var getStates = function (rt) { var states = []; var b = rt.getAuthBlock(); if (b) { states.unshift(b); } while (b.getParent()) { b = b.getParent(); states.unshift(b); } return states; }; var loadHistory = function (config, common, cb) { var network = common.getNetwork(); var hkn = network.historyKeeper; var wcId = common.hrefToHexChannelId(config.href || window.location.href); var createRealtime = function () { return ChainPad.create({ userName: 'history', initialState: '', transformFunction: JsonOT.validate, logLevel: 0, noPrune: true }); }; var realtime = createRealtime(); var parsed = config.href ? common.parsePadUrl(config.href) : {}; var secret = common.getSecrets(parsed.type, parsed.hash); History.readOnly = 1; if (!secret.keys) { secret.keys = secret.key; History.readOnly = 2; } else if (!secret.keys.validateKey) { History.readOnly = 0; } var crypto = Crypto.createEncryptor(secret.keys); var to = window.setTimeout(function () { cb('[GET_FULL_HISTORY_TIMEOUT]'); }, 30000); var parse = function (msg) { try { return JSON.parse(msg); } catch (e) { return null; } }; var onMsg = function (msg) { var parsed = parse(msg); if (parsed[0] === 'FULL_HISTORY_END') { console.log('END'); window.clearTimeout(to); cb(null, realtime); return; } if (parsed[0] !== 'FULL_HISTORY') { return; } if (parsed[1] && parsed[1].validateKey) { // First message secret.keys.validateKey = parsed[1].validateKey; return; } msg = parsed[1][4]; if (msg) { msg = msg.replace(/^cp\|/, ''); var decryptedMsg = crypto.decrypt(msg, secret.keys.validateKey); realtime.message(decryptedMsg); } }; network.on('message', function (msg) { onMsg(msg); }); network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', wcId, secret.keys.validateKey])); }; History.create = function (common, config) { if (!config.$toolbar) { return void console.error("config.$toolbar is undefined");} if (History.loading) { return void console.error("History is already being loaded..."); } History.loading = true; var $toolbar = config.$toolbar; if (!config.applyVal || !config.setHistory || !config.onLocal || !config.onRemote) { throw new Error("Missing config element: applyVal, onLocal, onRemote, setHistory"); } // config.setHistory(bool, bool) // - bool1: history value // - bool2: reset old content? var render = function (val) { if (typeof val === "undefined") { return; } try { config.applyVal(val); } catch (e) { // Probably a parse error console.error(e); } }; var onClose = function () { config.setHistory(false, true); }; var onRevert = function () { config.setHistory(false, false); config.onLocal(); config.onRemote(); }; var onReady = function () { config.setHistory(true); }; var Messages = common.Messages; var realtime; var states = []; var c = states.length - 1; var $hist = $toolbar.find('.cryptpad-toolbar-history'); var $left = $toolbar.find('.cryptpad-toolbar-leftside'); var $right = $toolbar.find('.cryptpad-toolbar-rightside'); var $cke = $toolbar.find('.cke_toolbox_main'); $hist.html('').show(); $left.hide(); $right.hide(); $cke.hide(); common.spinner($hist).get().show(); var onUpdate; var update = function () { if (!realtime) { return []; } states = getStates(realtime); if (typeof onUpdate === "function") { onUpdate(); } return states; }; // Get the content of the selected version, and change the version number var get = function (i) { i = parseInt(i); if (isNaN(i)) { return; } if (i < 0) { i = 0; } if (i > states.length - 1) { i = states.length - 1; } var val = states[i].getContent().doc; c = i; if (typeof onUpdate === "function") { onUpdate(); } $hist.find('.next, .previous').css('visibility', ''); if (c === states.length - 1) { $hist.find('.next').css('visibility', 'hidden'); } if (c === 0) { $hist.find('.previous').css('visibility', 'hidden'); } return val || ''; }; var getNext = function (step) { return typeof step === "number" ? get(c + step) : get(c + 1); }; var getPrevious = function (step) { return typeof step === "number" ? get(c - step) : get(c - 1); }; // Create the history toolbar var display = function () { $hist.html(''); var $prev =$('<button>', { 'class': 'previous fa fa-step-backward buttonPrimary', title: Messages.history_prev }).appendTo($hist); var $nav = $('<div>', {'class': 'goto'}).appendTo($hist); var $next = $('<button>', { 'class': 'next fa fa-step-forward buttonPrimary', title: Messages.history_next }).appendTo($hist); $('<label>').text(Messages.history_version).appendTo($nav); var $cur = $('<input>', { 'class' : 'gotoInput', 'type' : 'number', 'min' : '1', 'max' : states.length }).val(c + 1).appendTo($nav).mousedown(function (e) { // stopPropagation because the event would be cancelled by the dropdown menus e.stopPropagation(); }); var $label2 = $('<label>').text(' / '+ states.length).appendTo($nav); $('<br>').appendTo($nav); var $close = $('<button>', { 'class':'closeHistory', title: Messages.history_closeTitle }).text(Messages.history_closeTitle).appendTo($nav); var $rev = $('<button>', { 'class':'revertHistory buttonSuccess', title: Messages.history_restoreTitle }).text(Messages.history_restore).appendTo($nav); if (!History.readOnly) { $rev.hide(); } onUpdate = function () { $cur.attr('max', states.length); $cur.val(c+1); $label2.text(' / ' + states.length); }; var close = function () { $hist.hide(); $left.show(); $right.show(); $cke.show(); }; // Buttons actions $prev.click(function () { render(getPrevious()); }); $next.click(function () { render(getNext()); }); $cur.keydown(function (e) { var p = function () { e.preventDefault(); }; if (e.which === 13) { p(); return render( get($cur.val() - 1) ); } // Enter if ([37, 40].indexOf(e.which) >= 0) { p(); return render(getPrevious()); } // Left if ([38, 39].indexOf(e.which) >= 0) { p(); return render(getNext()); } // Right if (e.which === 33) { p(); return render(getNext(10)); } // PageUp if (e.which === 34) { p(); return render(getPrevious(10)); } // PageUp if (e.which === 27) { p(); $close.click(); } }).keyup(function (e) { e.stopPropagation(); }).focus(); $cur.on('change', function () { render( get($cur.val() - 1) ); }); $close.click(function () { states = []; close(); onClose(); }); $rev.click(function () { common.confirm(Messages.history_restorePrompt, function (yes) { if (!yes) { return; } close(); onRevert(); common.log(Messages.history_restoreDone); }); }); // Display the latest content render(get(c)); }; // Load all the history messages into a new chainpad object loadHistory(config, common, function (err, newRt) { History.loading = false; if (err) { throw new Error(err); } realtime = newRt; update(); c = states.length - 1; display(); onReady(); }); }; return History; });