wip
This commit is contained in:
parent
a612f02be2
commit
4b25ab80d6
@ -39,6 +39,7 @@
|
|||||||
"require-css": "0.1.10",
|
"require-css": "0.1.10",
|
||||||
"less": "^2.7.2",
|
"less": "^2.7.2",
|
||||||
"bootstrap": "#v4.0.0-alpha.6",
|
"bootstrap": "#v4.0.0-alpha.6",
|
||||||
"diff-dom": "2.1.1"
|
"diff-dom": "2.1.1",
|
||||||
|
"nthen": "^0.1.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
www/common/metadata-manager.js
Normal file
0
www/common/metadata-manager.js
Normal file
@ -3,7 +3,7 @@
|
|||||||
window.addEventListener('message', function (msg) {
|
window.addEventListener('message', function (msg) {
|
||||||
var data = JSON.parse(msg.data);
|
var data = JSON.parse(msg.data);
|
||||||
if (data.q !== 'INIT') { return; }
|
if (data.q !== 'INIT') { return; }
|
||||||
msg.source.postMessage({ txid: data.txid, content: 'OK' }, '*');
|
msg.source.postMessage(JSON.stringify({ txid: data.txid, content: 'OK' }), '*');
|
||||||
if (data.content && data.content.requireConf) { require.config(data.content.requireConf); }
|
if (data.content && data.content.requireConf) { require.config(data.content.requireConf); }
|
||||||
require(['/common/sframe-boot2.js'], function () { });
|
require(['/common/sframe-boot2.js'], function () { });
|
||||||
});
|
});
|
||||||
@ -1,8 +1,9 @@
|
|||||||
// This is stage 1, it can be changed but you must bump the version of the project.
|
// This is stage 1, it can be changed but you must bump the version of the project.
|
||||||
// Note: This must only be loaded from inside of a sandbox-iframe.
|
// Note: This must only be loaded from inside of a sandbox-iframe.
|
||||||
define([
|
define([
|
||||||
'/common/requireconfig.js'
|
'/common/requireconfig.js',
|
||||||
], function (RequireConfig) {
|
'/common/sframe-channel.js'
|
||||||
|
], function (RequireConfig, SFrameChannel) {
|
||||||
require.config(RequireConfig);
|
require.config(RequireConfig);
|
||||||
console.log('boot2');
|
console.log('boot2');
|
||||||
// most of CryptPad breaks if you don't support isArray
|
// most of CryptPad breaks if you don't support isArray
|
||||||
@ -22,5 +23,7 @@ console.log('boot2');
|
|||||||
window.__defineGetter__('localStorage', function () { return mkFakeStore(); });
|
window.__defineGetter__('localStorage', function () { return mkFakeStore(); });
|
||||||
window.__defineGetter__('sessionStorage', function () { return mkFakeStore(); });
|
window.__defineGetter__('sessionStorage', function () { return mkFakeStore(); });
|
||||||
|
|
||||||
|
SFrameChannel.init(window.top, function () { });
|
||||||
|
|
||||||
require([document.querySelector('script[data-bootload]').getAttribute('data-bootload')]);
|
require([document.querySelector('script[data-bootload]').getAttribute('data-bootload')]);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,39 +15,17 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
define([
|
define([
|
||||||
'/bower_components/netflux-websocket/netflux-client.js',
|
'/common/sframe-channel.js',
|
||||||
'/bower_components/chainpad/chainpad.dist.js',
|
'/bower_components/chainpad/chainpad.dist.js',
|
||||||
], function (Netflux) {
|
], function (SFrameChannel) {
|
||||||
var ChainPad = window.ChainPad;
|
var ChainPad = window.ChainPad;
|
||||||
var USE_HISTORY = true;
|
|
||||||
var module = { exports: {} };
|
var module = { exports: {} };
|
||||||
|
|
||||||
var verbose = function (x) { console.log(x); };
|
var verbose = function (x) { console.log(x); };
|
||||||
verbose = function () {}; // comment out to enable verbose logging
|
verbose = function () {}; // comment out to enable verbose logging
|
||||||
|
|
||||||
var unBencode = function (str) { return str.replace(/^\d+:/, ''); };
|
var mkUserList = function () {
|
||||||
|
var userList = Object.freeze({
|
||||||
module.exports.start = function (config) {
|
|
||||||
console.log(config);
|
|
||||||
var websocketUrl = config.websocketURL;
|
|
||||||
var userName = config.userName;
|
|
||||||
var channel = config.channel;
|
|
||||||
var Crypto = config.crypto;
|
|
||||||
var validateKey = config.validateKey;
|
|
||||||
var readOnly = config.readOnly || false;
|
|
||||||
|
|
||||||
// make sure configuration is defined
|
|
||||||
config = config || {};
|
|
||||||
|
|
||||||
var initializing = true;
|
|
||||||
var toReturn = {};
|
|
||||||
var messagesHistory = [];
|
|
||||||
var chainpadAdapter = {};
|
|
||||||
var realtime;
|
|
||||||
var network = config.network;
|
|
||||||
var lastKnownHash;
|
|
||||||
|
|
||||||
var userList = {
|
|
||||||
change : [],
|
change : [],
|
||||||
onChange : function(newData) {
|
onChange : function(newData) {
|
||||||
userList.change.forEach(function (el) {
|
userList.change.forEach(function (el) {
|
||||||
@ -55,7 +33,7 @@ define([
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
users: []
|
users: []
|
||||||
};
|
});
|
||||||
|
|
||||||
var onJoining = function (peer) {
|
var onJoining = function (peer) {
|
||||||
if(peer.length !== 32) { return; }
|
if(peer.length !== 32) { return; }
|
||||||
@ -67,98 +45,6 @@ define([
|
|||||||
userList.onChange();
|
userList.onChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
var onReady = function(wc, network) {
|
|
||||||
// Trigger onReady only if not ready yet. This is important because the history keeper sends a direct
|
|
||||||
// message through "network" when it is synced, and it triggers onReady for each channel joined.
|
|
||||||
if (!initializing) { return; }
|
|
||||||
|
|
||||||
realtime.start();
|
|
||||||
|
|
||||||
if(config.setMyID) {
|
|
||||||
config.setMyID({
|
|
||||||
myID: wc.myID
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Trigger onJoining with our own Cryptpad username to tell the toolbar that we are synced
|
|
||||||
if (!readOnly) {
|
|
||||||
onJoining(wc.myID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we're fully synced
|
|
||||||
initializing = false;
|
|
||||||
|
|
||||||
if (config.onReady) {
|
|
||||||
config.onReady({
|
|
||||||
realtime: realtime,
|
|
||||||
network: network,
|
|
||||||
userList: userList,
|
|
||||||
myId: wc.myID,
|
|
||||||
leave: wc.leave
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var onMessage = function(peer, msg, wc, network, direct) {
|
|
||||||
// unpack the history keeper from the webchannel
|
|
||||||
var hk = network.historyKeeper;
|
|
||||||
|
|
||||||
// Old server
|
|
||||||
if(wc && (msg === 0 || msg === '0')) {
|
|
||||||
onReady(wc, network);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (direct && peer !== hk) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (direct) {
|
|
||||||
var parsed = JSON.parse(msg);
|
|
||||||
if (parsed.validateKey && parsed.channel) {
|
|
||||||
if (parsed.channel === wc.id && !validateKey) {
|
|
||||||
validateKey = parsed.validateKey;
|
|
||||||
}
|
|
||||||
// We have to return even if it is not the current channel:
|
|
||||||
// we don't want to continue with other channels messages here
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (parsed.state && parsed.state === 1 && parsed.channel) {
|
|
||||||
if (parsed.channel === wc.id) {
|
|
||||||
onReady(wc, network);
|
|
||||||
}
|
|
||||||
// We have to return even if it is not the current channel:
|
|
||||||
// we don't want to continue with other channels messages here
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The history keeper is different for each channel :
|
|
||||||
// no need to check if the message is related to the current channel
|
|
||||||
if (peer === hk){
|
|
||||||
// if the peer is the 'history keeper', extract their message
|
|
||||||
var parsed1 = JSON.parse(msg);
|
|
||||||
msg = parsed1[4];
|
|
||||||
// Check that this is a message for us
|
|
||||||
if (parsed1[3] !== wc.id) { return; }
|
|
||||||
}
|
|
||||||
|
|
||||||
lastKnownHash = msg.slice(0,64);
|
|
||||||
var message = chainpadAdapter.msgIn(peer, msg);
|
|
||||||
|
|
||||||
verbose(message);
|
|
||||||
|
|
||||||
if (!initializing) {
|
|
||||||
if (config.onLocal) {
|
|
||||||
config.onLocal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// slice off the bencoded header
|
|
||||||
// Why are we getting bencoded stuff to begin with?
|
|
||||||
// FIXME this shouldn't be necessary
|
|
||||||
message = unBencode(message);//.slice(message.indexOf(':[') + 1);
|
|
||||||
|
|
||||||
// pass the message into Chainpad
|
|
||||||
realtime.message(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
// update UI components to show that one of the other peers has left
|
// update UI components to show that one of the other peers has left
|
||||||
var onLeaving = function (peer) {
|
var onLeaving = function (peer) {
|
||||||
var list = userList.users;
|
var list = userList.users;
|
||||||
@ -169,246 +55,93 @@ define([
|
|||||||
userList.onChange();
|
userList.onChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
// shim between chainpad and netflux
|
var onReset = function () {
|
||||||
chainpadAdapter = {
|
userList.users.forEach(onLeaving);
|
||||||
msgIn : function(peerId, msg) {
|
|
||||||
msg = msg.replace(/^cp\|/, '');
|
|
||||||
try {
|
|
||||||
var decryptedMsg = Crypto.decrypt(msg, validateKey);
|
|
||||||
messagesHistory.push(decryptedMsg);
|
|
||||||
return decryptedMsg;
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
msgOut : function(msg) {
|
|
||||||
if (readOnly) { return; }
|
|
||||||
try {
|
|
||||||
var cmsg = Crypto.encrypt(msg);
|
|
||||||
if (msg.indexOf('[4') === 0) { cmsg = 'cp|' + cmsg; }
|
|
||||||
return cmsg;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(msg);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var createRealtime = function() {
|
return Object.freeze({
|
||||||
return ChainPad.create({
|
list: userList,
|
||||||
|
onJoin: onJoining,
|
||||||
|
onLeave: onLeaving,
|
||||||
|
onReset: onReset
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.start = function (config) {
|
||||||
|
var onConnectionChange = config.onConnectionChange || function () { };
|
||||||
|
var onRemote = config.onRemote || function () { };
|
||||||
|
var onInit = config.onInit || function () { };
|
||||||
|
var onLocal = config.onLocal || function () { };
|
||||||
|
var setMyID = config.setMyID || function () { };
|
||||||
|
var onReady = config.onReady || function () { };
|
||||||
|
var userName = config.userName;
|
||||||
|
var initialState = config.initialState;
|
||||||
|
var transformFunction = config.transformFunction;
|
||||||
|
var validateContent = config.validateContent;
|
||||||
|
var avgSyncMilliseconds = config.avgSyncMilliseconds;
|
||||||
|
var logLevel = typeof(config.logLevel) !== 'undefined'? config.logLevel : 1;
|
||||||
|
var readOnly = config.readOnly || false;
|
||||||
|
config = undefined;
|
||||||
|
|
||||||
|
var chainpad;
|
||||||
|
var userList = mkUserList();
|
||||||
|
var myID;
|
||||||
|
var isReady = false;
|
||||||
|
|
||||||
|
SFrameChannel.on('EV_RT_JOIN', userList.onJoin);
|
||||||
|
SFrameChannel.on('EV_RT_LEAVE', userList.onLeave);
|
||||||
|
SFrameChannel.on('EV_RT_DISCONNECT', function () {
|
||||||
|
isReady = false;
|
||||||
|
userList.onReset();
|
||||||
|
onConnectionChange({ state: false });
|
||||||
|
});
|
||||||
|
SFrameChannel.on('EV_RT_CONNECT', function (content) {
|
||||||
|
content.members.forEach(userList.onJoin);
|
||||||
|
myID = content.myID;
|
||||||
|
isReady = false;
|
||||||
|
if (chainpad) {
|
||||||
|
// it's a reconnect
|
||||||
|
onConnectionChange({ state: true, myId: myID });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chainpad = ChainPad.create({
|
||||||
userName: userName,
|
userName: userName,
|
||||||
initialState: config.initialState,
|
initialState: initialState,
|
||||||
transformFunction: config.transformFunction,
|
transformFunction: transformFunction,
|
||||||
validateContent: config.validateContent,
|
validateContent: validateContent,
|
||||||
avgSyncMilliseconds: config.avgSyncMilliseconds,
|
avgSyncMilliseconds: avgSyncMilliseconds,
|
||||||
logLevel: typeof(config.logLevel) !== 'undefined'? config.logLevel : 1
|
logLevel: logLevel
|
||||||
});
|
});
|
||||||
};
|
chainpad.onMessage(function(message, cb) {
|
||||||
|
SFrameChannel.query('Q_RT_MESSAGE', message, cb);
|
||||||
// We use an object to store the webchannel so that we don't have to push new handlers to chainpad
|
|
||||||
// and remove the old ones when reconnecting and keeping the same 'realtime' object
|
|
||||||
// See realtime.onMessage below: we call wc.bcast(...) but wc may change
|
|
||||||
var wcObject = {};
|
|
||||||
var onOpen = function(wc, network, initialize) {
|
|
||||||
wcObject.wc = wc;
|
|
||||||
channel = wc.id;
|
|
||||||
|
|
||||||
// Add the existing peers in the userList
|
|
||||||
wc.members.forEach(onJoining);
|
|
||||||
|
|
||||||
// Add the handlers to the WebChannel
|
|
||||||
wc.on('message', function (msg, sender) { //Channel msg
|
|
||||||
onMessage(sender, msg, wc, network);
|
|
||||||
});
|
});
|
||||||
wc.on('join', onJoining);
|
chainpad.onPatch(function () {
|
||||||
wc.on('leave', onLeaving);
|
onRemote({ realtime: chainpad });
|
||||||
|
});
|
||||||
if (initialize) {
|
onInit({
|
||||||
toReturn.realtime = realtime = createRealtime();
|
myID: content.myID,
|
||||||
|
realtime: chainpad,
|
||||||
realtime._patch = realtime.patch;
|
|
||||||
realtime.patch = function (patch, x, y) {
|
|
||||||
if (initializing) {
|
|
||||||
console.error("attempted to change the content before chainpad was synced");
|
|
||||||
}
|
|
||||||
return realtime._patch(patch, x, y);
|
|
||||||
};
|
|
||||||
realtime._change = realtime.change;
|
|
||||||
realtime.change = function (offset, count, chars) {
|
|
||||||
if (initializing) {
|
|
||||||
console.error("attempted to change the content before chainpad was synced");
|
|
||||||
}
|
|
||||||
return realtime._change(offset, count, chars);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (config.onInit) {
|
|
||||||
config.onInit({
|
|
||||||
myID: wc.myID,
|
|
||||||
realtime: realtime,
|
|
||||||
getLag: network.getLag,
|
|
||||||
userList: userList,
|
userList: userList,
|
||||||
network: network,
|
readOnly: readOnly
|
||||||
channel: channel
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
SFrameChannel.on('Q_RT_MESSAGE', function (content, cb) {
|
||||||
|
if (isReady) {
|
||||||
|
onLocal(); // should be onBeforeMessage
|
||||||
}
|
}
|
||||||
|
chainpad.message(content);
|
||||||
// Sending a message...
|
cb('OK');
|
||||||
realtime.onMessage(function(message, cb) {
|
|
||||||
// Filter messages sent by Chainpad to make it compatible with Netflux
|
|
||||||
message = chainpadAdapter.msgOut(message);
|
|
||||||
if(message) {
|
|
||||||
// Do not remove wcObject, it allows us to use a new 'wc' without changing the handler if we
|
|
||||||
// want to keep the same chainpad (realtime) object
|
|
||||||
wcObject.wc.bcast(message).then(function() {
|
|
||||||
cb();
|
|
||||||
}, function(err) {
|
|
||||||
// The message has not been sent, display the error.
|
|
||||||
console.error(err);
|
|
||||||
});
|
});
|
||||||
}
|
SFrameChannel.on('EV_RT_READY', function () {
|
||||||
});
|
if (isReady) { return; }
|
||||||
|
isReady = true;
|
||||||
realtime.onPatch(function () {
|
chainpad.start();
|
||||||
if (config.onRemote) {
|
setMyID({ myID: myID });
|
||||||
config.onRemote({
|
// Trigger onJoining with our own Cryptpad username to tell the toolbar that we are synced
|
||||||
realtime: realtime
|
if (!readOnly) { userList.onJoin(myID); }
|
||||||
});
|
onReady({ realtime: chainpad });
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the channel history
|
|
||||||
if(USE_HISTORY) {
|
|
||||||
var hk;
|
|
||||||
|
|
||||||
wc.members.forEach(function (p) {
|
|
||||||
if (p.length === 16) { hk = p; }
|
|
||||||
});
|
|
||||||
network.historyKeeper = hk;
|
|
||||||
|
|
||||||
var msg = ['GET_HISTORY', wc.id];
|
|
||||||
// Add the validateKey if we are the channel creator and we have a validateKey
|
|
||||||
msg.push(validateKey);
|
|
||||||
msg.push(lastKnownHash);
|
|
||||||
if (hk) { network.sendto(hk, JSON.stringify(msg)); }
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
onReady(wc, network);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set a flag to avoid calling onAbort or onConnectionChange when the user is leaving the page
|
|
||||||
var isIntentionallyLeaving = false;
|
|
||||||
window.addEventListener("beforeunload", function () {
|
|
||||||
isIntentionallyLeaving = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
var findChannelById = function(webChannels, channelId) {
|
|
||||||
var webChannel;
|
|
||||||
|
|
||||||
// Array.some terminates once a truthy value is returned
|
|
||||||
// best case is faster than forEach, though webchannel arrays seem
|
|
||||||
// to consistently have a length of 1
|
|
||||||
webChannels.some(function(chan) {
|
|
||||||
if(chan.id === channelId) { webChannel = chan; return true;}
|
|
||||||
});
|
|
||||||
return webChannel;
|
|
||||||
};
|
|
||||||
|
|
||||||
var onConnectError = function (err) {
|
|
||||||
if (config.onError) {
|
|
||||||
config.onError({
|
|
||||||
error: err.type
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var joinSession = function (endPoint, cb) {
|
|
||||||
// a websocket URL has been provided
|
|
||||||
// connect to it with Netflux.
|
|
||||||
if (typeof(endPoint) === 'string') {
|
|
||||||
Netflux.connect(endPoint).then(cb, onConnectError);
|
|
||||||
} else if (typeof(endPoint.then) === 'function') {
|
|
||||||
// a netflux network promise was provided
|
|
||||||
// connect to it and use a channel
|
|
||||||
endPoint.then(cb, onConnectError);
|
|
||||||
} else {
|
|
||||||
// assume it's a network and try to connect.
|
|
||||||
cb(endPoint);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var firstConnection = true;
|
|
||||||
/* Connect to the Netflux network, or fall back to a WebSocket
|
|
||||||
in theory this lets us connect to more netflux channels using only
|
|
||||||
one network. */
|
|
||||||
var connectTo = function (network) {
|
|
||||||
// join the netflux network, promise to handle opening of the channel
|
|
||||||
network.join(channel || null).then(function(wc) {
|
|
||||||
onOpen(wc, network, firstConnection);
|
|
||||||
firstConnection = false;
|
|
||||||
}, function(error) {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
joinSession(network || websocketUrl, function (network) {
|
|
||||||
// pass messages that come out of netflux into our local handler
|
|
||||||
if (firstConnection) {
|
|
||||||
toReturn.network = network;
|
|
||||||
|
|
||||||
network.on('disconnect', function (reason) {
|
|
||||||
if (isIntentionallyLeaving) { return; }
|
|
||||||
if (reason === "network.disconnect() called") { return; }
|
|
||||||
if (config.onConnectionChange) {
|
|
||||||
config.onConnectionChange({
|
|
||||||
state: false
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
if (config.onAbort) {
|
|
||||||
config.onAbort({
|
|
||||||
reason: reason
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
network.on('reconnect', function (uid) {
|
|
||||||
if (config.onConnectionChange) {
|
|
||||||
config.onConnectionChange({
|
|
||||||
state: true,
|
|
||||||
myId: uid
|
|
||||||
});
|
|
||||||
var afterReconnecting = function () {
|
|
||||||
initializing = true;
|
|
||||||
userList.users=[];
|
|
||||||
joinSession(network, connectTo);
|
|
||||||
};
|
|
||||||
if (config.beforeReconnecting) {
|
|
||||||
config.beforeReconnecting(function (newKey, newContent) {
|
|
||||||
channel = newKey;
|
|
||||||
config.initialState = newContent;
|
|
||||||
afterReconnecting();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
afterReconnecting();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
network.on('message', function (msg, sender) { // Direct message
|
|
||||||
var wchan = findChannelById(network.webChannels, channel);
|
|
||||||
if(wchan) {
|
|
||||||
onMessage(sender, msg, wchan, network, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
connectTo(network);
|
|
||||||
}, onConnectError);
|
|
||||||
|
|
||||||
return toReturn;
|
|
||||||
};
|
};
|
||||||
return module.exports;
|
return module.exports;
|
||||||
});
|
});
|
||||||
@ -15,10 +15,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
define([
|
define([
|
||||||
'/bower_components/netflux-websocket/netflux-client.js',
|
'/common/sframe-channel.js',
|
||||||
'/bower_components/chainpad/chainpad.dist.js',
|
], function (SFrameChannel) {
|
||||||
], function (Netflux) {
|
|
||||||
var ChainPad = window.ChainPad;
|
|
||||||
var USE_HISTORY = true;
|
var USE_HISTORY = true;
|
||||||
var module = { exports: {} };
|
var module = { exports: {} };
|
||||||
|
|
||||||
@ -27,50 +25,53 @@ define([
|
|||||||
|
|
||||||
var unBencode = function (str) { return str.replace(/^\d+:/, ''); };
|
var unBencode = function (str) { return str.replace(/^\d+:/, ''); };
|
||||||
|
|
||||||
module.exports.start = function (conf) {
|
var start = function (conf) {
|
||||||
var websocketUrl = conf.websocketURL;
|
|
||||||
var userName = conf.userName;
|
|
||||||
var channel = conf.channel;
|
var channel = conf.channel;
|
||||||
var Crypto = conf.crypto;
|
var Crypto = conf.crypto;
|
||||||
var validateKey = conf.validateKey;
|
var validateKey = conf.validateKey;
|
||||||
var readOnly = conf.readOnly || false;
|
var readOnly = conf.readOnly || false;
|
||||||
var websocketURL = conf.websocketURL;
|
|
||||||
var network = conf.network;
|
var network = conf.network;
|
||||||
conf = undefined;
|
conf = undefined;
|
||||||
|
|
||||||
var initializing = true;
|
var initializing = true;
|
||||||
var toReturn = {};
|
|
||||||
var messagesHistory = [];
|
|
||||||
var chainpadAdapter = {};
|
|
||||||
var realtime;
|
|
||||||
var lastKnownHash;
|
var lastKnownHash;
|
||||||
|
|
||||||
var onReady = function(wc, network) {
|
var queue = [];
|
||||||
|
var messageFromInner = function (m, cb) { queue.push([ m, cb ]); };
|
||||||
|
SFrameChannel.on('Q_RT_MESSAGE', function (message, cb) {
|
||||||
|
messageFromInner(message, cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
var onReady = function(wc) {
|
||||||
// Trigger onReady only if not ready yet. This is important because the history keeper sends a direct
|
// Trigger onReady only if not ready yet. This is important because the history keeper sends a direct
|
||||||
// message through "network" when it is synced, and it triggers onReady for each channel joined.
|
// message through "network" when it is synced, and it triggers onReady for each channel joined.
|
||||||
if (!initializing) { return; }
|
if (!initializing) { return; }
|
||||||
|
SFrameChannel.event('EV_RT_READY', null);
|
||||||
realtime.start();
|
|
||||||
|
|
||||||
if(setMyID) {
|
|
||||||
setMyID({ myID: wc.myID });
|
|
||||||
}
|
|
||||||
// Trigger onJoining with our own Cryptpad username to tell the toolbar that we are synced
|
|
||||||
if (!readOnly) {
|
|
||||||
onJoining(wc.myID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we're fully synced
|
// we're fully synced
|
||||||
initializing = false;
|
initializing = false;
|
||||||
|
};
|
||||||
|
|
||||||
if (config.onReady) {
|
// shim between chainpad and netflux
|
||||||
config.onReady({
|
var msgIn = function (peerId, msg) {
|
||||||
realtime: realtime,
|
msg = msg.replace(/^cp\|/, '');
|
||||||
network: network,
|
try {
|
||||||
userList: userList,
|
var decryptedMsg = Crypto.decrypt(msg, validateKey);
|
||||||
myId: wc.myID,
|
return decryptedMsg;
|
||||||
leave: wc.leave
|
} catch (err) {
|
||||||
});
|
console.error(err);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var msgOut = function (msg) {
|
||||||
|
if (readOnly) { return; }
|
||||||
|
try {
|
||||||
|
var cmsg = Crypto.encrypt(msg);
|
||||||
|
if (msg.indexOf('[4') === 0) { cmsg = 'cp|' + cmsg; }
|
||||||
|
return cmsg;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(msg);
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,11 +79,6 @@ define([
|
|||||||
// unpack the history keeper from the webchannel
|
// unpack the history keeper from the webchannel
|
||||||
var hk = network.historyKeeper;
|
var hk = network.historyKeeper;
|
||||||
|
|
||||||
// Old server
|
|
||||||
if(wc && (msg === 0 || msg === '0')) {
|
|
||||||
onReady(wc, network);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (direct && peer !== hk) {
|
if (direct && peer !== hk) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -98,7 +94,7 @@ define([
|
|||||||
}
|
}
|
||||||
if (parsed.state && parsed.state === 1 && parsed.channel) {
|
if (parsed.state && parsed.state === 1 && parsed.channel) {
|
||||||
if (parsed.channel === wc.id) {
|
if (parsed.channel === wc.id) {
|
||||||
onReady(wc, network);
|
onReady(wc);
|
||||||
}
|
}
|
||||||
// We have to return even if it is not the current channel:
|
// We have to return even if it is not the current channel:
|
||||||
// we don't want to continue with other channels messages here
|
// we don't want to continue with other channels messages here
|
||||||
@ -116,142 +112,54 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
lastKnownHash = msg.slice(0,64);
|
lastKnownHash = msg.slice(0,64);
|
||||||
var message = chainpadAdapter.msgIn(peer, msg);
|
var message = msgIn(peer, msg);
|
||||||
|
|
||||||
verbose(message);
|
verbose(message);
|
||||||
|
|
||||||
if (!initializing) {
|
|
||||||
if (config.onLocal) {
|
|
||||||
config.onLocal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// slice off the bencoded header
|
// slice off the bencoded header
|
||||||
// Why are we getting bencoded stuff to begin with?
|
// Why are we getting bencoded stuff to begin with?
|
||||||
// FIXME this shouldn't be necessary
|
// FIXME this shouldn't be necessary
|
||||||
message = unBencode(message);//.slice(message.indexOf(':[') + 1);
|
message = unBencode(message);//.slice(message.indexOf(':[') + 1);
|
||||||
|
|
||||||
// pass the message into Chainpad
|
// pass the message into Chainpad
|
||||||
realtime.message(message);
|
SFrameChannel.query('Q_RT_MESSAGE', message, function () { });
|
||||||
};
|
|
||||||
|
|
||||||
// update UI components to show that one of the other peers has left
|
|
||||||
var onLeaving = function(peer) {
|
|
||||||
var list = userList.users;
|
|
||||||
var index = list.indexOf(peer);
|
|
||||||
if(index !== -1) {
|
|
||||||
userList.users.splice(index, 1);
|
|
||||||
}
|
|
||||||
userList.onChange();
|
|
||||||
};
|
|
||||||
|
|
||||||
// shim between chainpad and netflux
|
|
||||||
chainpadAdapter = {
|
|
||||||
msgIn : function(peerId, msg) {
|
|
||||||
msg = msg.replace(/^cp\|/, '');
|
|
||||||
try {
|
|
||||||
var decryptedMsg = Crypto.decrypt(msg, validateKey);
|
|
||||||
messagesHistory.push(decryptedMsg);
|
|
||||||
return decryptedMsg;
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
msgOut : function(msg) {
|
|
||||||
if (readOnly) { return; }
|
|
||||||
try {
|
|
||||||
var cmsg = Crypto.encrypt(msg);
|
|
||||||
if (msg.indexOf('[4') === 0) { cmsg = 'cp|' + cmsg; }
|
|
||||||
return cmsg;
|
|
||||||
} catch (err) {
|
|
||||||
console.log(msg);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var createRealtime = function() {
|
|
||||||
return ChainPad.create({
|
|
||||||
userName: userName,
|
|
||||||
initialState: config.initialState,
|
|
||||||
transformFunction: config.transformFunction,
|
|
||||||
validateContent: config.validateContent,
|
|
||||||
avgSyncMilliseconds: config.avgSyncMilliseconds,
|
|
||||||
logLevel: typeof(config.logLevel) !== 'undefined'? config.logLevel : 1
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// We use an object to store the webchannel so that we don't have to push new handlers to chainpad
|
// We use an object to store the webchannel so that we don't have to push new handlers to chainpad
|
||||||
// and remove the old ones when reconnecting and keeping the same 'realtime' object
|
// and remove the old ones when reconnecting and keeping the same 'realtime' object
|
||||||
// See realtime.onMessage below: we call wc.bcast(...) but wc may change
|
// See realtime.onMessage below: we call wc.bcast(...) but wc may change
|
||||||
var wcObject = {};
|
var wcObject = {};
|
||||||
var onOpen = function(wc, network, initialize) {
|
var onOpen = function(wc, network, firstConnection) {
|
||||||
wcObject.wc = wc;
|
wcObject.wc = wc;
|
||||||
channel = wc.id;
|
channel = wc.id;
|
||||||
|
|
||||||
// Add the existing peers in the userList
|
// Add the existing peers in the userList
|
||||||
wc.members.forEach(onJoining);
|
SFrameChannel.event('EV_RT_CONNECT', { myID: wc.myID, members: wc.members, readOnly: readOnly });
|
||||||
|
|
||||||
// Add the handlers to the WebChannel
|
// Add the handlers to the WebChannel
|
||||||
wc.on('message', function (msg, sender) { //Channel msg
|
wc.on('message', function (msg, sender) { //Channel msg
|
||||||
onMessage(sender, msg, wc, network);
|
onMessage(sender, msg, wc, network);
|
||||||
});
|
});
|
||||||
wc.on('join', onJoining);
|
wc.on('join', function (m) { SFrameChannel.event('EV_RT_JOIN', m); });
|
||||||
wc.on('leave', onLeaving);
|
wc.on('leave', function (m) { SFrameChannel.event('EV_RT_LEAVE', m); });
|
||||||
|
|
||||||
if (initialize) {
|
|
||||||
toReturn.realtime = realtime = createRealtime();
|
|
||||||
|
|
||||||
realtime._patch = realtime.patch;
|
|
||||||
realtime.patch = function (patch, x, y) {
|
|
||||||
if (initializing) {
|
|
||||||
console.error("attempted to change the content before chainpad was synced");
|
|
||||||
}
|
|
||||||
return realtime._patch(patch, x, y);
|
|
||||||
};
|
|
||||||
realtime._change = realtime.change;
|
|
||||||
realtime.change = function (offset, count, chars) {
|
|
||||||
if (initializing) {
|
|
||||||
console.error("attempted to change the content before chainpad was synced");
|
|
||||||
}
|
|
||||||
return realtime._change(offset, count, chars);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (config.onInit) {
|
|
||||||
config.onInit({
|
|
||||||
myID: wc.myID,
|
|
||||||
realtime: realtime,
|
|
||||||
getLag: network.getLag,
|
|
||||||
userList: userList,
|
|
||||||
network: network,
|
|
||||||
channel: channel
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (firstConnection) {
|
||||||
// Sending a message...
|
// Sending a message...
|
||||||
realtime.onMessage(function(message, cb) {
|
messageFromInner = function(message, cb) {
|
||||||
// Filter messages sent by Chainpad to make it compatible with Netflux
|
// Filter messages sent by Chainpad to make it compatible with Netflux
|
||||||
message = chainpadAdapter.msgOut(message);
|
message = msgOut(message);
|
||||||
if (message) {
|
if (message) {
|
||||||
// Do not remove wcObject, it allows us to use a new 'wc' without changing the handler if we
|
// Do not remove wcObject, it allows us to use a new 'wc' without changing the handler if we
|
||||||
// want to keep the same chainpad (realtime) object
|
// want to keep the same chainpad (realtime) object
|
||||||
wcObject.wc.bcast(message).then(function() {
|
wcObject.wc.bcast(message).then(function() {
|
||||||
cb();
|
cb('OK');
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
// The message has not been sent, display the error.
|
// The message has not been sent, display the error.
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
queue.forEach(function (arr) { messageFromInner(arr[0], arr[1]); });
|
||||||
realtime.onPatch(function () {
|
|
||||||
if (config.onRemote) {
|
|
||||||
config.onRemote({
|
|
||||||
realtime: realtime
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the channel history
|
// Get the channel history
|
||||||
@ -268,13 +176,11 @@ define([
|
|||||||
msg.push(validateKey);
|
msg.push(validateKey);
|
||||||
msg.push(lastKnownHash);
|
msg.push(lastKnownHash);
|
||||||
if (hk) { network.sendto(hk, JSON.stringify(msg)); }
|
if (hk) { network.sendto(hk, JSON.stringify(msg)); }
|
||||||
}
|
} else {
|
||||||
else {
|
onReady(wc);
|
||||||
onReady(wc, network);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set a flag to avoid calling onAbort or onConnectionChange when the user is leaving the page
|
|
||||||
var isIntentionallyLeaving = false;
|
var isIntentionallyLeaving = false;
|
||||||
window.addEventListener("beforeunload", function () {
|
window.addEventListener("beforeunload", function () {
|
||||||
isIntentionallyLeaving = true;
|
isIntentionallyLeaving = true;
|
||||||
@ -292,85 +198,24 @@ define([
|
|||||||
return webChannel;
|
return webChannel;
|
||||||
};
|
};
|
||||||
|
|
||||||
var onConnectError = function (err) {
|
var connectTo = function (network, firstConnection) {
|
||||||
if (config.onError) {
|
|
||||||
config.onError({
|
|
||||||
error: err.type
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var joinSession = function (endPoint, cb) {
|
|
||||||
// a websocket URL has been provided
|
|
||||||
// connect to it with Netflux.
|
|
||||||
if (typeof(endPoint) === 'string') {
|
|
||||||
Netflux.connect(endPoint).then(cb, onConnectError);
|
|
||||||
} else if (typeof(endPoint.then) === 'function') {
|
|
||||||
// a netflux network promise was provided
|
|
||||||
// connect to it and use a channel
|
|
||||||
endPoint.then(cb, onConnectError);
|
|
||||||
} else {
|
|
||||||
// assume it's a network and try to connect.
|
|
||||||
cb(endPoint);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var firstConnection = true;
|
|
||||||
/* Connect to the Netflux network, or fall back to a WebSocket
|
|
||||||
in theory this lets us connect to more netflux channels using only
|
|
||||||
one network. */
|
|
||||||
var connectTo = function (network) {
|
|
||||||
// join the netflux network, promise to handle opening of the channel
|
// join the netflux network, promise to handle opening of the channel
|
||||||
network.join(channel || null).then(function(wc) {
|
network.join(channel || null).then(function(wc) {
|
||||||
onOpen(wc, network, firstConnection);
|
onOpen(wc, network, firstConnection);
|
||||||
firstConnection = false;
|
|
||||||
}, function(error) {
|
}, function(error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
joinSession(network || websocketUrl, function (network) {
|
|
||||||
// pass messages that come out of netflux into our local handler
|
|
||||||
if (firstConnection) {
|
|
||||||
toReturn.network = network;
|
|
||||||
|
|
||||||
network.on('disconnect', function (reason) {
|
network.on('disconnect', function (reason) {
|
||||||
if (isIntentionallyLeaving) { return; }
|
if (isIntentionallyLeaving) { return; }
|
||||||
if (reason === "network.disconnect() called") { return; }
|
if (reason === "network.disconnect() called") { return; }
|
||||||
if (config.onConnectionChange) {
|
SFrameChannel.event('EV_RT_DISCONNECT');
|
||||||
config.onConnectionChange({
|
|
||||||
state: false
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (config.onAbort) {
|
|
||||||
config.onAbort({
|
|
||||||
reason: reason
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
network.on('reconnect', function (uid) {
|
network.on('reconnect', function (uid) {
|
||||||
if (config.onConnectionChange) {
|
|
||||||
config.onConnectionChange({
|
|
||||||
state: true,
|
|
||||||
myId: uid
|
|
||||||
});
|
|
||||||
var afterReconnecting = function () {
|
|
||||||
initializing = true;
|
initializing = true;
|
||||||
userList.users=[];
|
connectTo(network, false);
|
||||||
joinSession(network, connectTo);
|
|
||||||
};
|
|
||||||
if (config.beforeReconnecting) {
|
|
||||||
config.beforeReconnecting(function (newKey, newContent) {
|
|
||||||
channel = newKey;
|
|
||||||
config.initialState = newContent;
|
|
||||||
afterReconnecting();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
afterReconnecting();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
network.on('message', function (msg, sender) { // Direct message
|
network.on('message', function (msg, sender) { // Direct message
|
||||||
@ -379,12 +224,13 @@ define([
|
|||||||
onMessage(sender, msg, wchan, network, true);
|
onMessage(sender, msg, wchan, network, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
connectTo(network);
|
connectTo(network, true);
|
||||||
}, onConnectError);
|
};
|
||||||
|
|
||||||
return toReturn;
|
return {
|
||||||
|
start: function (config) {
|
||||||
|
SFrameChannel.whenReg('EV_RT_READY', function () { start(config); });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return module.exports;
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,38 +1,108 @@
|
|||||||
// This file provides the internal API for talking from inside of the sandbox iframe
|
// This file provides the API for the channel for talking to and from the sandbox iframe.
|
||||||
// The external API is in sframe-ctrl.js
|
define([
|
||||||
define([], function () {
|
'/common/sframe-protocol.js'
|
||||||
var iframe;
|
], function (SFrameProtocol) {
|
||||||
|
var otherWindow;
|
||||||
var handlers = {};
|
var handlers = {};
|
||||||
var queries = {};
|
var queries = {};
|
||||||
|
|
||||||
|
// list of handlers which are registered from the other side...
|
||||||
|
var insideHandlers = [];
|
||||||
|
var callWhenRegistered = {};
|
||||||
|
|
||||||
var module = { exports: {} };
|
var module = { exports: {} };
|
||||||
|
|
||||||
var mkTxid = function () {
|
var mkTxid = function () {
|
||||||
return Math.random().toString(16).replace('0.', '') + Math.random().toString(16).replace('0.', '');
|
return Math.random().toString(16).replace('0.', '') + Math.random().toString(16).replace('0.', '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.init = function (ow, cb) {
|
||||||
|
if (otherWindow) { throw new Error('already initialized'); }
|
||||||
|
var intr;
|
||||||
|
var txid;
|
||||||
|
window.addEventListener('message', function (msg) {
|
||||||
|
var data = JSON.parse(msg.data);
|
||||||
|
if (ow !== msg.source) {
|
||||||
|
console.log("DROP Message from unexpected source");
|
||||||
|
console.log(msg);
|
||||||
|
} else if (!otherWindow) {
|
||||||
|
if (data.txid !== txid) {
|
||||||
|
console.log("DROP Message with weird txid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clearInterval(intr);
|
||||||
|
otherWindow = ow;
|
||||||
|
cb();
|
||||||
|
} else if (typeof(data.q) === 'string' && handlers[data.q]) {
|
||||||
|
handlers[data.q](data, msg);
|
||||||
|
} else if (typeof(data.q) === 'undefined' && queries[data.txid]) {
|
||||||
|
queries[data.txid](data, msg);
|
||||||
|
} else if (data.txid === txid) {
|
||||||
|
// stray message from init
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log("DROP Unhandled message");
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (window !== window.top) {
|
||||||
|
// we're in the sandbox
|
||||||
|
otherWindow = ow;
|
||||||
|
cb();
|
||||||
|
} else {
|
||||||
|
require(['/common/requireconfig.js'], function (RequireConfig) {
|
||||||
|
txid = mkTxid();
|
||||||
|
intr = setInterval(function () {
|
||||||
|
ow.postMessage(JSON.stringify({
|
||||||
|
txid: txid,
|
||||||
|
content: { requireConf: RequireConfig },
|
||||||
|
q: 'INIT'
|
||||||
|
}), '*');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports.query = function (q, content, cb) {
|
module.exports.query = function (q, content, cb) {
|
||||||
if (!iframe) { throw new Error('not yet initialized'); }
|
if (!otherWindow) { throw new Error('not yet initialized'); }
|
||||||
|
if (!SFrameProtocol[q]) {
|
||||||
|
throw new Error('please only make queries are defined in sframe-protocol.js');
|
||||||
|
}
|
||||||
var txid = mkTxid();
|
var txid = mkTxid();
|
||||||
var timeout = setTimeout(function () {
|
var timeout = setTimeout(function () {
|
||||||
delete queries[txid];
|
delete queries[txid];
|
||||||
cb("Timeout making query " + q);
|
console.log("Timeout making query " + q);
|
||||||
});
|
}, 30000);
|
||||||
queries[txid] = function (data, msg) {
|
queries[txid] = function (data, msg) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
delete queries[txid];
|
delete queries[txid];
|
||||||
cb(undefined, data.content, msg);
|
cb(undefined, data.content, msg);
|
||||||
};
|
};
|
||||||
iframe.contentWindow.postMessage(JSON.stringify({
|
otherWindow.postMessage(JSON.stringify({
|
||||||
txid: txid,
|
txid: txid,
|
||||||
content: content,
|
content: content,
|
||||||
q: q
|
q: q
|
||||||
}), '*');
|
}), '*');
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.registerHandler = function (queryType, handler) {
|
var event = module.exports.event = function (e, content) {
|
||||||
|
if (!otherWindow) { throw new Error('not yet initialized'); }
|
||||||
|
if (!SFrameProtocol[e]) {
|
||||||
|
throw new Error('please only fire events that are defined in sframe-protocol.js');
|
||||||
|
}
|
||||||
|
if (e.indexOf('EV_') !== 0) {
|
||||||
|
throw new Error('please only use events (starting with EV_) for event messages');
|
||||||
|
}
|
||||||
|
otherWindow.postMessage(JSON.stringify({ content: content, q: e }), '*');
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.on = function (queryType, handler) {
|
||||||
|
if (!otherWindow) { throw new Error('not yet initialized'); }
|
||||||
if (typeof(handlers[queryType]) !== 'undefined') { throw new Error('already registered'); }
|
if (typeof(handlers[queryType]) !== 'undefined') { throw new Error('already registered'); }
|
||||||
handlers[queryType] = function (msg) {
|
if (!SFrameProtocol[queryType]) {
|
||||||
var data = JSON.parse(msg.data);
|
throw new Error('please only register handlers which are defined in sframe-protocol.js');
|
||||||
|
}
|
||||||
|
handlers[queryType] = function (data, msg) {
|
||||||
handler(data.content, function (replyContent) {
|
handler(data.content, function (replyContent) {
|
||||||
msg.source.postMessage(JSON.stringify({
|
msg.source.postMessage(JSON.stringify({
|
||||||
txid: data.txid,
|
txid: data.txid,
|
||||||
@ -40,6 +110,27 @@ define([], function () {
|
|||||||
}), '*');
|
}), '*');
|
||||||
}, msg);
|
}, msg);
|
||||||
};
|
};
|
||||||
|
event('EV_REGISTER_HANDLER', queryType);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.whenReg = function (queryType, handler) {
|
||||||
|
if (!otherWindow) { throw new Error('not yet initialized'); }
|
||||||
|
if (!SFrameProtocol[queryType]) {
|
||||||
|
throw new Error('please only register handlers which are defined in sframe-protocol.js');
|
||||||
|
}
|
||||||
|
if (insideHandlers.indexOf(queryType) > -1) {
|
||||||
|
handler();
|
||||||
|
} else {
|
||||||
|
(callWhenRegistered[queryType] = callWhenRegistered[queryType] || []).push(handler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handlers['EV_REGISTER_HANDLER'] = function (data) {
|
||||||
|
if (callWhenRegistered[data.content]) {
|
||||||
|
callWhenRegistered[data.content].forEach(function (f) { f(); });
|
||||||
|
delete callWhenRegistered[data.content];
|
||||||
|
}
|
||||||
|
insideHandlers.push(data.content);
|
||||||
};
|
};
|
||||||
|
|
||||||
return module.exports;
|
return module.exports;
|
||||||
|
|||||||
@ -1,76 +0,0 @@
|
|||||||
// This file provides the external API for launching and talking to the sandboxed iframe.
|
|
||||||
// The internal API is in sframe-channel.js
|
|
||||||
define([
|
|
||||||
'/common/requireconfig.js'
|
|
||||||
], function (RequireConfig) {
|
|
||||||
var iframe;
|
|
||||||
var handlers = {};
|
|
||||||
var queries = {};
|
|
||||||
var module = { exports: {} };
|
|
||||||
|
|
||||||
var mkTxid = function () {
|
|
||||||
return Math.random().toString(16).replace('0.', '') + Math.random().toString(16).replace('0.', '');
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.init = function (frame, cb) {
|
|
||||||
if (iframe) { throw new Error('already initialized'); }
|
|
||||||
var txid = mkTxid();
|
|
||||||
var intr = setInterval(function () {
|
|
||||||
frame.contentWindow.postMessage(JSON.stringify({
|
|
||||||
txid: txid,
|
|
||||||
content: { requireConf: RequireConfig },
|
|
||||||
q: 'INIT'
|
|
||||||
}), '*');
|
|
||||||
});
|
|
||||||
window.addEventListener('message', function (msg) {
|
|
||||||
var data = JSON.parse(msg.data);
|
|
||||||
if (!iframe) {
|
|
||||||
if (data.txid !== txid) { return; }
|
|
||||||
clearInterval(intr);
|
|
||||||
iframe = frame;
|
|
||||||
cb();
|
|
||||||
} else if (typeof(data.q) === 'string' && handlers[data.q]) {
|
|
||||||
handlers[data.q](data, msg);
|
|
||||||
} else if (typeof(data.q) === 'undefined' && queries[data.txid]) {
|
|
||||||
queries[data.txid](data, msg);
|
|
||||||
} else {
|
|
||||||
console.log("Unhandled message");
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.query = function (q, content, cb) {
|
|
||||||
if (!iframe) { throw new Error('not yet initialized'); }
|
|
||||||
var txid = mkTxid();
|
|
||||||
var timeout = setTimeout(function () {
|
|
||||||
delete queries[txid];
|
|
||||||
cb("Timeout making query " + q);
|
|
||||||
});
|
|
||||||
queries[txid] = function (data, msg) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
delete queries[txid];
|
|
||||||
cb(undefined, data.content, msg);
|
|
||||||
};
|
|
||||||
iframe.contentWindow.postMessage(JSON.stringify({
|
|
||||||
txid: txid,
|
|
||||||
content: content,
|
|
||||||
q: q
|
|
||||||
}), '*');
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.registerHandler = function (queryType, handler) {
|
|
||||||
if (typeof(handlers[queryType]) !== 'undefined') { throw new Error('already registered'); }
|
|
||||||
handlers[queryType] = function (msg) {
|
|
||||||
var data = JSON.parse(msg.data);
|
|
||||||
handler(data.content, function (replyContent) {
|
|
||||||
msg.source.postMessage(JSON.stringify({
|
|
||||||
txid: data.txid,
|
|
||||||
content: replyContent
|
|
||||||
}), '*');
|
|
||||||
}, msg);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return module.exports;
|
|
||||||
});
|
|
||||||
@ -1,5 +1,33 @@
|
|||||||
// This file defines all of the RPC calls
|
// This file defines all of the RPC calls which are used between the inner and outer iframe.
|
||||||
// The internal API is in sframe-channel.js
|
// Define *querys* (which expect a response) using Q_<query name>
|
||||||
|
// Define *events* (which expect no response) using EV_<event name>
|
||||||
|
// Please document the queries and events you create, and please please avoid making generic
|
||||||
|
// "do stuff" events/queries which are used for many different things because it makes the
|
||||||
|
// protocol unclear.
|
||||||
define({
|
define({
|
||||||
|
// When the iframe first launches, this query is sent repeatedly by the controller
|
||||||
|
// to wait for it to awake and give it the requirejs config to use.
|
||||||
|
'Q_INIT': true,
|
||||||
|
|
||||||
|
// When either the outside or inside registers a query handler, this is sent.
|
||||||
|
'EV_REGISTER_HANDLER': true,
|
||||||
|
|
||||||
|
// Realtime events called from the outside.
|
||||||
|
// When someone joins the pad, argument is a string with their netflux id.
|
||||||
|
'EV_RT_JOIN': true,
|
||||||
|
// When someone leaves the pad, argument is a string with their netflux id.
|
||||||
|
'EV_RT_LEAVE': true,
|
||||||
|
// When you have been disconnected, no arguments.
|
||||||
|
'EV_RT_DISCONNECT': true,
|
||||||
|
// When you have connected, argument is an object with myID: string, members: list, readOnly: boolean.
|
||||||
|
'EV_RT_CONNECT': true,
|
||||||
|
// Called after the history is finished synchronizing, no arguments.
|
||||||
|
'EV_RT_READY': true,
|
||||||
|
// Called from both outside and inside, argument is a (string) chainpad message.
|
||||||
|
'Q_RT_MESSAGE': true,
|
||||||
|
|
||||||
|
// Called from the outside, this informs the inside whenever the user's data has been changed.
|
||||||
|
// The argument is the object representing the content of the user profile minus the netfluxID
|
||||||
|
// which changes per-reconnect.
|
||||||
|
'EV_USERDATA_UPDATE': true
|
||||||
});
|
});
|
||||||
137
www/pad2/main.js
137
www/pad2/main.js
@ -13,6 +13,7 @@ define([
|
|||||||
'/common/cryptpad-common.js',
|
'/common/cryptpad-common.js',
|
||||||
'/common/cryptget.js',
|
'/common/cryptget.js',
|
||||||
'/pad/links.js',
|
'/pad/links.js',
|
||||||
|
'/bower_components/nthen/index.js',
|
||||||
|
|
||||||
'/bower_components/file-saver/FileSaver.min.js',
|
'/bower_components/file-saver/FileSaver.min.js',
|
||||||
'/bower_components/diff-dom/diffDOM.js',
|
'/bower_components/diff-dom/diffDOM.js',
|
||||||
@ -21,18 +22,12 @@ define([
|
|||||||
'less!/customize/src/less/cryptpad.less',
|
'less!/customize/src/less/cryptpad.less',
|
||||||
'less!/customize/src/less/toolbar.less'
|
'less!/customize/src/less/toolbar.less'
|
||||||
], function ($, Crypto, realtimeInput, Hyperjson,
|
], function ($, Crypto, realtimeInput, Hyperjson,
|
||||||
Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher, Cryptpad, Cryptget, Links) {
|
Toolbar, Cursor, JsonOT, TypingTest, JSONSortify, TextPatcher, Cryptpad, Cryptget, Links, nThen) {
|
||||||
var saveAs = window.saveAs;
|
var saveAs = window.saveAs;
|
||||||
var Messages = Cryptpad.Messages;
|
var Messages = Cryptpad.Messages;
|
||||||
|
|
||||||
console.log('two');
|
|
||||||
|
|
||||||
var Ckeditor; // to be initialized later...
|
|
||||||
var DiffDom = window.diffDOM;
|
var DiffDom = window.diffDOM;
|
||||||
|
|
||||||
var stringify = function (obj) {
|
var stringify = function (obj) { return JSONSortify(obj); };
|
||||||
return JSONSortify(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.Toolbar = Toolbar;
|
window.Toolbar = Toolbar;
|
||||||
window.Hyperjson = Hyperjson;
|
window.Hyperjson = Hyperjson;
|
||||||
@ -89,7 +84,7 @@ define([
|
|||||||
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
Cryptpad.errorLoadingScreen(Messages.websocketError);
|
||||||
};
|
};
|
||||||
|
|
||||||
var andThen = function (Ckeditor) {
|
var andThen = function (editor) {
|
||||||
//var $iframe = $('#pad-iframe').contents();
|
//var $iframe = $('#pad-iframe').contents();
|
||||||
//var secret = Cryptpad.getSecrets();
|
//var secret = Cryptpad.getSecrets();
|
||||||
//var readOnly = secret.keys && !secret.keys.editKeyStr;
|
//var readOnly = secret.keys && !secret.keys.editKeyStr;
|
||||||
@ -98,12 +93,7 @@ define([
|
|||||||
//}
|
//}
|
||||||
var readOnly = false; // TODO
|
var readOnly = false; // TODO
|
||||||
|
|
||||||
var editor = window.editor = Ckeditor.replace('editor1', {
|
|
||||||
customConfig: '/customize/ckeditor-config.js',
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.on('instanceReady', Links.addSupportForOpeningLinksInNewTab(Ckeditor));
|
|
||||||
editor.on('instanceReady', function () {
|
|
||||||
var $bar = $('#cke_1_toolbox');
|
var $bar = $('#cke_1_toolbox');
|
||||||
|
|
||||||
var $html = $bar.closest('html');
|
var $html = $bar.closest('html');
|
||||||
@ -125,15 +115,10 @@ define([
|
|||||||
el.setAttribute('class', 'non-realtime');
|
el.setAttribute('class', 'non-realtime');
|
||||||
});
|
});
|
||||||
|
|
||||||
var documentBody = document.body;
|
var documentBody = $html.find('iframe')[0].contentWindow.document.body;
|
||||||
|
|
||||||
var inner = window.inner = documentBody;
|
var inner = window.inner = documentBody;
|
||||||
|
|
||||||
// hide all content until the realtime doc is ready
|
|
||||||
$(inner).css({
|
|
||||||
color: '#fff',
|
|
||||||
});
|
|
||||||
|
|
||||||
var cursor = module.cursor = Cursor(inner);
|
var cursor = module.cursor = Cursor(inner);
|
||||||
|
|
||||||
var setEditable = module.setEditable = function (bool) {
|
var setEditable = module.setEditable = function (bool) {
|
||||||
@ -322,15 +307,15 @@ define([
|
|||||||
|
|
||||||
var stringifyDOM = module.stringifyDOM = function (dom) {
|
var stringifyDOM = module.stringifyDOM = function (dom) {
|
||||||
var hjson = Hyperjson.fromDOM(dom, isNotMagicLine, brFilter);
|
var hjson = Hyperjson.fromDOM(dom, isNotMagicLine, brFilter);
|
||||||
hjson[3] = {
|
|
||||||
metadata: {
|
/*hjson[3] = { TODO
|
||||||
users: UserList.userData,
|
users: UserList.userData,
|
||||||
defaultTitle: Title.defaultTitle,
|
defaultTitle: Title.defaultTitle,
|
||||||
type: 'pad'
|
type: 'pad'
|
||||||
}
|
}
|
||||||
};
|
};*/
|
||||||
if (!initializing) {
|
if (!initializing) {
|
||||||
hjson[3].metadata.title = Title.title;
|
//TODO hjson[3].metadata.title = Title.title;
|
||||||
} else if (Cryptpad.initialName && !hjson[3].metadata.title) {
|
} else if (Cryptpad.initialName && !hjson[3].metadata.title) {
|
||||||
hjson[3].metadata.title = Cryptpad.initialName;
|
hjson[3].metadata.title = Cryptpad.initialName;
|
||||||
}
|
}
|
||||||
@ -379,6 +364,9 @@ define([
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var meta;
|
||||||
|
var metaStr;
|
||||||
|
|
||||||
realtimeOptions.onRemote = function () {
|
realtimeOptions.onRemote = function () {
|
||||||
if (initializing) { return; }
|
if (initializing) { return; }
|
||||||
if (isHistoryMode) { return; }
|
if (isHistoryMode) { return; }
|
||||||
@ -391,7 +379,7 @@ define([
|
|||||||
cursor.update();
|
cursor.update();
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
// Update the user list (metadata) from the hyperjson
|
||||||
Metadata.update(shjson);
|
// TODO Metadata.update(shjson);
|
||||||
|
|
||||||
var newInner = JSON.parse(shjson);
|
var newInner = JSON.parse(shjson);
|
||||||
var newSInner;
|
var newSInner;
|
||||||
@ -404,6 +392,10 @@ define([
|
|||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
var shjson2 = stringifyDOM(inner);
|
var shjson2 = stringifyDOM(inner);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//shjson = JSON.stringify(JSON.parse(shjson).slice(0,3));
|
||||||
|
|
||||||
if (shjson2 !== shjson) {
|
if (shjson2 !== shjson) {
|
||||||
console.error("shjson2 !== shjson");
|
console.error("shjson2 !== shjson");
|
||||||
module.patchText(shjson2);
|
module.patchText(shjson2);
|
||||||
@ -438,6 +430,14 @@ define([
|
|||||||
if (newSInner && newSInner !== oldSInner) {
|
if (newSInner && newSInner !== oldSInner) {
|
||||||
Cryptpad.notify();
|
Cryptpad.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newMeta = newInner[3];
|
||||||
|
var newMetaStr = JSON.stringify(newMeta);
|
||||||
|
if (newMetaStr !== metaStr) {
|
||||||
|
metaStr = newMetaStr;
|
||||||
|
meta = newMeta;
|
||||||
|
//meta[] HERE
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var getHTML = function () {
|
var getHTML = function () {
|
||||||
@ -465,6 +465,10 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
realtimeOptions.onInit = function (info) {
|
realtimeOptions.onInit = function (info) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
|
||||||
UserList = Cryptpad.createUserList(info, realtimeOptions.onLocal, Cryptget, Cryptpad);
|
UserList = Cryptpad.createUserList(info, realtimeOptions.onLocal, Cryptget, Cryptpad);
|
||||||
|
|
||||||
var titleCfg = { getHeadingText: getHeadingText };
|
var titleCfg = { getHeadingText: getHeadingText };
|
||||||
@ -531,7 +535,7 @@ define([
|
|||||||
updateIcon();
|
updateIcon();
|
||||||
$collapse.click(function () {
|
$collapse.click(function () {
|
||||||
$(window).trigger('resize');
|
$(window).trigger('resize');
|
||||||
$iframe.find('.cke_toolbox_main').toggle();
|
$('.cke_toolbox_main').toggle();
|
||||||
$(window).trigger('cryptpad-ck-toolbar');
|
$(window).trigger('cryptpad-ck-toolbar');
|
||||||
updateIcon();
|
updateIcon();
|
||||||
});
|
});
|
||||||
@ -588,10 +592,10 @@ define([
|
|||||||
realtimeOptions.onReady = function (info) {
|
realtimeOptions.onReady = function (info) {
|
||||||
if (!module.isMaximized) {
|
if (!module.isMaximized) {
|
||||||
module.isMaximized = true;
|
module.isMaximized = true;
|
||||||
$iframe.find('iframe.cke_wysiwyg_frame').css('width', '');
|
$('iframe.cke_wysiwyg_frame').css('width', '');
|
||||||
$iframe.find('iframe.cke_wysiwyg_frame').css('height', '');
|
$('iframe.cke_wysiwyg_frame').css('height', '');
|
||||||
}
|
}
|
||||||
$iframe.find('body').addClass('app-pad');
|
$('body').addClass('app-pad');
|
||||||
|
|
||||||
if (module.realtime !== info.realtime) {
|
if (module.realtime !== info.realtime) {
|
||||||
module.patchText = TextPatcher.create({
|
module.patchText = TextPatcher.create({
|
||||||
@ -611,15 +615,17 @@ define([
|
|||||||
applyHjson(shjson);
|
applyHjson(shjson);
|
||||||
|
|
||||||
// Update the user list (metadata) from the hyperjson
|
// Update the user list (metadata) from the hyperjson
|
||||||
Metadata.update(shjson);
|
// XXX Metadata.update(shjson);
|
||||||
|
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
var shjson2 = stringifyDOM(inner);
|
var shjson2 = stringifyDOM(inner);
|
||||||
var hjson2 = JSON.parse(shjson2).slice(0,-1);
|
var hjson2 = JSON.parse(shjson2).slice(0,3);
|
||||||
var hjson = JSON.parse(shjson).slice(0,-1);
|
var hjson = JSON.parse(shjson).slice(0,3);
|
||||||
if (stringify(hjson2) !== stringify(hjson)) {
|
if (stringify(hjson2) !== stringify(hjson)) {
|
||||||
console.log('err');
|
console.log('err');
|
||||||
console.error("shjson2 !== shjson");
|
console.error("shjson2 !== shjson");
|
||||||
|
console.log(stringify(hjson2));
|
||||||
|
console.log(stringify(hjson));
|
||||||
Cryptpad.errorLoadingScreen(Messages.wrongApp);
|
Cryptpad.errorLoadingScreen(Messages.wrongApp);
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
@ -634,7 +640,7 @@ define([
|
|||||||
initializing = false;
|
initializing = false;
|
||||||
|
|
||||||
if (readOnly) { return; }
|
if (readOnly) { return; }
|
||||||
UserList.getLastName(toolbar.$userNameButton, newPad);
|
//TODO UserList.getLastName(toolbar.$userNameButton, newPad);
|
||||||
editor.focus();
|
editor.focus();
|
||||||
if (newPad) {
|
if (newPad) {
|
||||||
cursor.setToEnd();
|
cursor.setToEnd();
|
||||||
@ -642,14 +648,14 @@ define([
|
|||||||
cursor.setToStart();
|
cursor.setToStart();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/* unreachable
|
||||||
realtimeOptions.onAbort = function () {
|
realtimeOptions.onAbort = function () {
|
||||||
console.log("Aborting the session!");
|
console.log("Aborting the session!");
|
||||||
// stop the user from continuing to edit
|
// stop the user from continuing to edit
|
||||||
setEditable(false);
|
setEditable(false);
|
||||||
toolbar.failed();
|
toolbar.failed();
|
||||||
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
Cryptpad.alert(Messages.common_connectionLost, undefined, true);
|
||||||
};
|
}; */
|
||||||
|
|
||||||
realtimeOptions.onConnectionChange = function (info) {
|
realtimeOptions.onConnectionChange = function (info) {
|
||||||
setEditable(info.state);
|
setEditable(info.state);
|
||||||
@ -717,26 +723,35 @@ define([
|
|||||||
Cryptpad.feedback(id.toUpperCase());
|
Cryptpad.feedback(id.toUpperCase());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var interval = 100;
|
|
||||||
var second = function (Ckeditor) {
|
var CKEDITOR_CHECK_INTERVAL = 100;
|
||||||
//Cryptpad.ready(function () {
|
var ckEditorAvailable = function (cb) {
|
||||||
andThen(Ckeditor);
|
var intr;
|
||||||
//Cryptpad.reportAppUsage();
|
var check = function () {
|
||||||
//});
|
if (window.CKEDITOR) {
|
||||||
Cryptpad.onError(function (info) {
|
clearTimeout(intr);
|
||||||
if (info && info.type === "store") {
|
cb(window.CKEDITOR);
|
||||||
onConnectError();
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
intr = setInterval(function () {
|
||||||
|
console.log("Ckeditor was not defined. Trying again in %sms", CKEDITOR_CHECK_INTERVAL);
|
||||||
|
check();
|
||||||
|
}, CKEDITOR_CHECK_INTERVAL);
|
||||||
|
check();
|
||||||
};
|
};
|
||||||
|
|
||||||
var first = function () {
|
var main = function () {
|
||||||
Ckeditor = window.CKEDITOR;
|
var Ckeditor;
|
||||||
if (Ckeditor) {
|
var editor;
|
||||||
// mobile configuration
|
|
||||||
|
nThen(function (waitFor) {
|
||||||
|
ckEditorAvailable(waitFor(function (ck) { Ckeditor = ck; }));
|
||||||
|
$(waitFor(function () {
|
||||||
|
Cryptpad.addLoadingScreen();
|
||||||
|
}));
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
Ckeditor.config.toolbarCanCollapse = true;
|
Ckeditor.config.toolbarCanCollapse = true;
|
||||||
if (screen.height < 800) {
|
if (screen.height < 800) {
|
||||||
Ckeditor.config.toolbarStartupExpanded = false;
|
Ckeditor.config.toolbarStartupExpanded = false;
|
||||||
@ -744,15 +759,19 @@ define([
|
|||||||
} else {
|
} else {
|
||||||
$('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1.0, user-scalable=yes');
|
$('meta[name=viewport]').attr('content', 'width=device-width, initial-scale=1.0, user-scalable=yes');
|
||||||
}
|
}
|
||||||
second(Ckeditor);
|
editor = Ckeditor.replace('editor1', {
|
||||||
} else {
|
customConfig: '/customize/ckeditor-config.js',
|
||||||
//console.log("Ckeditor was not defined. Trying again in %sms",interval);
|
});
|
||||||
setTimeout(first, interval);
|
editor.on('instanceReady', waitFor());
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
Links.addSupportForOpeningLinksInNewTab(Ckeditor);
|
||||||
|
Cryptpad.onError(function (info) {
|
||||||
|
if (info && info.type === "store") {
|
||||||
|
onConnectError();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
andThen(editor);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
main();
|
||||||
$(function () {
|
|
||||||
Cryptpad.addLoadingScreen();
|
|
||||||
first();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,13 +1,39 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'/common/sframe-ctrl.js',
|
'/common/sframe-channel.js',
|
||||||
'jquery'
|
'jquery',
|
||||||
], function (SFrameCtrl, $) {
|
'/common/sframe-chainpad-netflux-outer.js',
|
||||||
|
'/bower_components/nthen/index.js',
|
||||||
|
'/common/cryptpad-common.js',
|
||||||
|
'/bower_components/chainpad-crypto/crypto.js'
|
||||||
|
], function (SFrameChannel, $, CpNfOuter, nThen, Cryptpad, Crypto) {
|
||||||
console.log('xxx');
|
console.log('xxx');
|
||||||
$(function () {
|
nThen(function (waitFor) {
|
||||||
console.log('go');
|
$(waitFor());
|
||||||
SFrameCtrl.init($('#sbox-iframe')[0], function () {
|
}).nThen(function (waitFor) {
|
||||||
console.log('\n\ndone\n\n');
|
SFrameChannel.init($('#sbox-iframe')[0].contentWindow, waitFor(function () {
|
||||||
|
console.log('sframe initialized');
|
||||||
|
}));
|
||||||
|
Cryptpad.ready(waitFor());
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
Cryptpad.onError(function (info) {
|
||||||
|
console.log('error');
|
||||||
|
console.log(info);
|
||||||
|
if (info && info.type === "store") {
|
||||||
|
//onConnectError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
|
var secret = Cryptpad.getSecrets();
|
||||||
|
var readOnly = secret.keys && !secret.keys.editKeyStr;
|
||||||
|
if (!secret.keys) { secret.keys = secret.key; }
|
||||||
|
|
||||||
|
var outer = CpNfOuter.start({
|
||||||
|
channel: secret.channel,
|
||||||
|
network: Cryptpad.getNetwork(),
|
||||||
|
validateKey: secret.keys.validateKey || undefined,
|
||||||
|
readOnly: readOnly,
|
||||||
|
crypto: Crypto.createEncryptor(secret.keys),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user