Merge branch 'staging' into oo2
This commit is contained in:
@@ -134,7 +134,7 @@ define(function() {
|
||||
// spontaneously, resulting in the deletion of the entire folder's content.
|
||||
// We highly recommend to keep them disabled until they are stable enough to be enabled
|
||||
// by default by the CryptPad developers.
|
||||
config.disableSharedFolders = true;
|
||||
config.disableSharedFolders = false;
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
@@ -13,6 +13,8 @@ define(function () {
|
||||
storageKey: 'filesData',
|
||||
tokenKey: 'loginToken',
|
||||
displayPadCreationScreen: 'displayPadCreationScreen',
|
||||
deprecatedKey: 'deprecated'
|
||||
deprecatedKey: 'deprecated',
|
||||
// Sub
|
||||
plan: 'CryptPad_plan'
|
||||
};
|
||||
});
|
||||
|
||||
@@ -892,7 +892,7 @@ define([
|
||||
h('div.cp-corner-filler', { style: "width:60px;" }),
|
||||
h('div.cp-corner-filler', { style: "width:40px;" }),
|
||||
h('div.cp-corner-filler', { style: "width:20px;" }),
|
||||
h('div.cp-corner-text', text),
|
||||
Pages.setHTML(h('div.cp-corner-text'), text),
|
||||
h('div.cp-corner-actions', actions),
|
||||
Pages.setHTML(h('div.cp-corner-footer'), footer)
|
||||
]);
|
||||
|
||||
@@ -150,7 +150,8 @@ define([
|
||||
}
|
||||
cfg.friendComplete({
|
||||
logText: Messages.contacts_added,
|
||||
netfluxId: sender
|
||||
netfluxId: sender,
|
||||
friend: msgData
|
||||
});
|
||||
var msg = ["FRIEND_REQ_ACK", chan];
|
||||
var msgStr = Crypto.encrypt(JSON.stringify(msg), key);
|
||||
@@ -163,7 +164,7 @@ define([
|
||||
if (i !== -1) { pendingRequests.splice(i, 1); }
|
||||
cfg.friendComplete({
|
||||
logText: Messages.contacts_rejected,
|
||||
netfluxId: sender
|
||||
netfluxId: sender,
|
||||
});
|
||||
cfg.updateMetadata();
|
||||
return;
|
||||
@@ -180,7 +181,8 @@ define([
|
||||
}
|
||||
cfg.friendComplete({
|
||||
logText: Messages.contacts_added,
|
||||
netfluxId: sender
|
||||
netfluxId: sender,
|
||||
friend: data
|
||||
});
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -5,7 +5,9 @@ define([
|
||||
'/common/common-util.js',
|
||||
'/common/common-realtime.js',
|
||||
'/common/common-constants.js',
|
||||
], function (Crypto, Curve, Hash, Util, Realtime, Constants) {
|
||||
|
||||
'/bower_components/nthen/index.js',
|
||||
], function (Crypto, Curve, Hash, Util, Realtime, Constants, nThen) {
|
||||
'use strict';
|
||||
var Msg = {
|
||||
inputs: [],
|
||||
@@ -52,7 +54,7 @@ define([
|
||||
|
||||
var msgAlreadyKnown = function (channel, sig) {
|
||||
return channel.messages.some(function (message) {
|
||||
return message[0] === sig;
|
||||
return message.sig === sig;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -65,6 +67,7 @@ define([
|
||||
update: [],
|
||||
friend: [],
|
||||
unfriend: [],
|
||||
event: []
|
||||
},
|
||||
range_requests: {},
|
||||
};
|
||||
@@ -73,6 +76,12 @@ define([
|
||||
messenger.handlers[type].forEach(g);
|
||||
};
|
||||
|
||||
var emit = function (ev, data) {
|
||||
eachHandler('event', function (f) {
|
||||
f(ev, data);
|
||||
});
|
||||
};
|
||||
|
||||
messenger.on = function (type, f) {
|
||||
var stack = messenger.handlers[type];
|
||||
if (!Array.isArray(stack)) {
|
||||
@@ -95,20 +104,26 @@ define([
|
||||
Msg.hk = network.historyKeeper;
|
||||
var friends = getFriendList(proxy);
|
||||
|
||||
var getChannel = function (curvePublic) {
|
||||
var friend = friends[curvePublic];
|
||||
if (!friend) { return; }
|
||||
var chanId = friend.channel;
|
||||
if (!chanId) { return; }
|
||||
var getChannel = function (chanId) {
|
||||
return channels[chanId];
|
||||
};
|
||||
|
||||
var initRangeRequest = function (txid, curvePublic, sig, cb) {
|
||||
var getFriendFromChannel = function (id) {
|
||||
var friend;
|
||||
for (var k in friends) {
|
||||
if (friends[k].channel === id) {
|
||||
friend = friends[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return friend;
|
||||
};
|
||||
|
||||
var initRangeRequest = function (txid, chanId, cb) {
|
||||
messenger.range_requests[txid] = {
|
||||
messages: [],
|
||||
cb: cb,
|
||||
curvePublic: curvePublic,
|
||||
sig: sig,
|
||||
chanId: chanId,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -120,24 +135,22 @@ define([
|
||||
delete messenger.range_requests[txid];
|
||||
};
|
||||
|
||||
messenger.getMoreHistory = function (curvePublic, hash, count, cb) {
|
||||
messenger.getMoreHistory = function (chanId, hash, count, cb) {
|
||||
if (typeof(cb) !== 'function') { return; }
|
||||
|
||||
if (typeof(hash) !== 'string') {
|
||||
// FIXME hash is not necessarily defined.
|
||||
// What does this mean?
|
||||
console.error("not sure what to do here");
|
||||
return;
|
||||
// Channel is empty!
|
||||
return void cb(void 0, []);
|
||||
}
|
||||
|
||||
var chan = getChannel(curvePublic);
|
||||
var chan = getChannel(chanId);
|
||||
if (typeof(chan) === 'undefined') {
|
||||
console.error("chan is undefined. we're going to have a problem here");
|
||||
return;
|
||||
}
|
||||
|
||||
var txid = Util.uid();
|
||||
initRangeRequest(txid, curvePublic, hash, cb);
|
||||
initRangeRequest(txid, chanId, cb);
|
||||
var msg = [ 'GET_HISTORY_RANGE', chan.id, {
|
||||
from: hash,
|
||||
count: count,
|
||||
@@ -151,38 +164,80 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
var getCurveForChannel = function (id) {
|
||||
/*var getCurveForChannel = function (id) {
|
||||
var channel = channels[id];
|
||||
if (!channel) { return; }
|
||||
return channel.curve;
|
||||
};
|
||||
};*/
|
||||
|
||||
messenger.getChannelHead = function (curvePublic, cb) {
|
||||
var friend = friends[curvePublic];
|
||||
if (!friend) { return void cb('NO_SUCH_FRIEND'); }
|
||||
cb(void 0, friend.lastKnownHash);
|
||||
};
|
||||
/*messenger.getChannelHead = function (id, cb) {
|
||||
var channel = getChannel(id);
|
||||
if (channel.isFriendChat) {
|
||||
var friend;
|
||||
for (var k in friends) {
|
||||
if (friends[k].channel === id) {
|
||||
friend = friends[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!friend) { return void cb('NO_SUCH_FRIEND'); }
|
||||
cb(void 0, friend.lastKnownHash);
|
||||
} else {
|
||||
// TODO room
|
||||
cb('NOT_IMPLEMENTED');
|
||||
}
|
||||
};*/
|
||||
|
||||
messenger.setChannelHead = function (curvePublic, hash, cb) {
|
||||
var friend = friends[curvePublic];
|
||||
if (!friend) { return void cb('NO_SUCH_FRIEND'); }
|
||||
friend.lastKnownHash = hash;
|
||||
messenger.setChannelHead = function (id, hash, cb) {
|
||||
var channel = getChannel(id);
|
||||
if (channel.isFriendChat) {
|
||||
var friend = getFriendFromChannel(id);
|
||||
if (!friend) { return void cb('NO_SUCH_FRIEND'); }
|
||||
friend.lastKnownHash = hash;
|
||||
} else if (channel.isPadChat) {
|
||||
// Nothing to do
|
||||
} else {
|
||||
// TODO room
|
||||
return void cb('NOT_IMPLEMENTED');
|
||||
}
|
||||
cb();
|
||||
};
|
||||
|
||||
// Make sure the data we have about our friends are up-to-date when we see them online
|
||||
var checkFriendData = function (curve, data, channel) {
|
||||
if (curve === proxy.curvePublic) { return; }
|
||||
var friend = getFriend(proxy, curve);
|
||||
if (!friend) { return; }
|
||||
var types = [];
|
||||
Object.keys(data).forEach(function (k) {
|
||||
if (friend[k] !== data[k]) {
|
||||
types.push(k);
|
||||
friend[k] = data[k];
|
||||
}
|
||||
});
|
||||
|
||||
eachHandler('update', function (f) {
|
||||
f(clone(data), types, channel);
|
||||
});
|
||||
};
|
||||
|
||||
// Id message allows us to map a netfluxId with a public curve key
|
||||
var onIdMessage = function (msg, sender) {
|
||||
var channel;
|
||||
var isId = Object.keys(channels).some(function (chanId) {
|
||||
if (channels[chanId].userList.indexOf(sender) !== -1) {
|
||||
channel = channels[chanId];
|
||||
return true;
|
||||
}
|
||||
});
|
||||
var channel, parsed0;
|
||||
|
||||
if (!isId) { return; }
|
||||
try {
|
||||
parsed0 = JSON.parse(msg);
|
||||
channel = channels[parsed0.channel];
|
||||
if (!channel) { return; }
|
||||
if (channel.userList.indexOf(sender) === -1) { return; }
|
||||
} catch (e) {
|
||||
console.log(msg);
|
||||
console.error(e);
|
||||
// Not an ID message
|
||||
return;
|
||||
}
|
||||
|
||||
var decryptedMsg = channel.encryptor.decrypt(msg);
|
||||
var decryptedMsg = channel.encryptor.decrypt(parsed0.msg);
|
||||
|
||||
if (decryptedMsg === null) {
|
||||
return void console.error("Failed to decrypt message");
|
||||
@@ -206,20 +261,26 @@ define([
|
||||
// the sender field. This is to prevent replay attacks.
|
||||
if (parsed[2] !== sender || !parsed[1]) { return; }
|
||||
channel.mapId[sender] = parsed[1];
|
||||
checkFriendData(parsed[1].curvePublic, parsed[1], channel.id);
|
||||
eachHandler('join', function (f) {
|
||||
f(parsed[1], channel.id);
|
||||
});
|
||||
|
||||
if (parsed[0] !== Types.mapId) { return; } // Don't send your key if it's already an ACK
|
||||
// Answer with your own key
|
||||
var rMsg = [Types.mapIdAck, proxy.curvePublic, channel.wc.myID];
|
||||
var myData = createData(proxy);
|
||||
delete myData.channel;
|
||||
var rMsg = [Types.mapIdAck, myData, channel.wc.myID];
|
||||
var rMsgStr = JSON.stringify(rMsg);
|
||||
var cryptMsg = channel.encryptor.encrypt(rMsgStr);
|
||||
network.sendto(sender, cryptMsg);
|
||||
var data = {
|
||||
channel: channel.id,
|
||||
msg: cryptMsg
|
||||
};
|
||||
network.sendto(sender, JSON.stringify(data));
|
||||
};
|
||||
|
||||
var orderMessages = function (curvePublic, new_messages /*, sig */) {
|
||||
var channel = getChannel(curvePublic);
|
||||
var orderMessages = function (channel, new_messages) {
|
||||
var messages = channel.messages;
|
||||
|
||||
// TODO improve performance, guarantee correct ordering
|
||||
@@ -236,9 +297,9 @@ define([
|
||||
};
|
||||
|
||||
var pushMsg = function (channel, cryptMsg) {
|
||||
var msg = channel.encryptor.decrypt(cryptMsg);
|
||||
var sig = cryptMsg.slice(0, 64);
|
||||
if (msgAlreadyKnown(channel, sig)) { return; }
|
||||
var msg = channel.encryptor.decrypt(cryptMsg);
|
||||
|
||||
var parsedMsg = JSON.parse(msg);
|
||||
var curvePublic;
|
||||
@@ -250,43 +311,38 @@ define([
|
||||
author: parsedMsg[1],
|
||||
time: parsedMsg[2],
|
||||
text: parsedMsg[3],
|
||||
channel: channel.id,
|
||||
name: parsedMsg[4] // Display name for multi-user rooms
|
||||
// this makes debugging a whole lot easier
|
||||
curve: getCurveForChannel(channel.id),
|
||||
//curve: getCurveForChannel(channel.id),
|
||||
};
|
||||
|
||||
channel.messages.push(res);
|
||||
eachHandler('message', function (f) {
|
||||
f(res);
|
||||
});
|
||||
if (!joining[channel.id]) {
|
||||
// Channel is ready
|
||||
eachHandler('message', function (f) {
|
||||
f(res);
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (parsedMsg[0] === Types.update) {
|
||||
if (parsedMsg[1] === proxy.curvePublic) { return; }
|
||||
curvePublic = parsedMsg[1];
|
||||
var newdata = parsedMsg[3];
|
||||
var data = getFriend(proxy, parsedMsg[1]);
|
||||
var types = [];
|
||||
Object.keys(newdata).forEach(function (k) {
|
||||
if (data[k] !== newdata[k]) {
|
||||
types.push(k);
|
||||
data[k] = newdata[k];
|
||||
}
|
||||
});
|
||||
|
||||
eachHandler('update', function (f) {
|
||||
f(clone(newdata), curvePublic);
|
||||
});
|
||||
checkFriendData(parsedMsg[1], parsedMsg[3], channel.id);
|
||||
return;
|
||||
}
|
||||
if (parsedMsg[0] === Types.unfriend) {
|
||||
curvePublic = parsedMsg[1];
|
||||
delete friends[curvePublic];
|
||||
|
||||
removeFromFriendList(parsedMsg[1], function () {
|
||||
// If this a removal from our part by in another tab, do nothing.
|
||||
// The channel is already closed in the proxy.on('remove') part
|
||||
if (curvePublic === proxy.curvePublic) { return; }
|
||||
|
||||
removeFromFriendList(curvePublic, function () {
|
||||
channel.wc.leave(Types.unfriend);
|
||||
delete channels[channel.id];
|
||||
eachHandler('unfriend', function (f) {
|
||||
f(curvePublic);
|
||||
f(curvePublic, false);
|
||||
});
|
||||
});
|
||||
return;
|
||||
@@ -324,7 +380,7 @@ define([
|
||||
});
|
||||
});
|
||||
eachHandler('update', function (f) {
|
||||
f(myData, myData.curvePublic);
|
||||
f(myData, ['displayName', 'profile', 'avatar']);
|
||||
});
|
||||
friends.me = myData;
|
||||
}
|
||||
@@ -352,12 +408,26 @@ define([
|
||||
return void console.error("received response to unknown request");
|
||||
}
|
||||
|
||||
if (!req.cb) {
|
||||
// This is the initial history for a pad chat
|
||||
if (type === 'HISTORY_RANGE') {
|
||||
if (!getChannel(req.chanId)) { return; }
|
||||
if (!Array.isArray(parsed[2])) { return; }
|
||||
pushMsg(getChannel(req.chanId), parsed[2][4]);
|
||||
} else if (type === 'HISTORY_RANGE_END') {
|
||||
if (!getChannel(req.chanId)) { return; }
|
||||
getChannel(req.chanId).ready = true;
|
||||
onChannelReady(req.chanId);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'HISTORY_RANGE') {
|
||||
req.messages.push(parsed[2]);
|
||||
} else if (type === 'HISTORY_RANGE_END') {
|
||||
// process all the messages (decrypt)
|
||||
var curvePublic = req.curvePublic;
|
||||
var channel = getChannel(curvePublic);
|
||||
var channel = getChannel(req.chanId);
|
||||
|
||||
var decrypted = req.messages.map(function (msg) {
|
||||
if (msg[2] !== 'MSG') { return; }
|
||||
@@ -371,6 +441,8 @@ define([
|
||||
return null;
|
||||
}
|
||||
}).filter(function (decrypted) {
|
||||
if (!decrypted.d || decrypted.d[0] !== Types.message) { return; }
|
||||
if (msgAlreadyKnown(channel, decrypted.sig)) { return; }
|
||||
return decrypted;
|
||||
}).map(function (O) {
|
||||
return {
|
||||
@@ -379,11 +451,12 @@ define([
|
||||
author: O.d[1],
|
||||
time: O.d[2],
|
||||
text: O.d[3],
|
||||
curve: curvePublic,
|
||||
channel: req.chanId,
|
||||
name: O.d[4]
|
||||
};
|
||||
});
|
||||
|
||||
orderMessages(curvePublic, decrypted, req.sig);
|
||||
orderMessages(channel, decrypted);
|
||||
req.cb(void 0, decrypted);
|
||||
return deleteRangeRequest(txid);
|
||||
} else {
|
||||
@@ -395,20 +468,17 @@ define([
|
||||
if ((parsed.validateKey || parsed.owners) && parsed.channel) {
|
||||
return;
|
||||
}
|
||||
// End of initial history
|
||||
if (parsed.state && parsed.state === 1 && parsed.channel) {
|
||||
if (channels[parsed.channel]) {
|
||||
// parsed.channel is Ready
|
||||
// channel[parsed.channel].ready();
|
||||
channels[parsed.channel].ready = true;
|
||||
onChannelReady(parsed.channel);
|
||||
var updateTypes = channels[parsed.channel].updateOnReady;
|
||||
if (updateTypes) {
|
||||
|
||||
//channels[parsed.channel].updateUI(updateTypes);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Initial history message
|
||||
var chan = parsed[3];
|
||||
if (!chan || !channels[chan]) { return; }
|
||||
pushMsg(channels[chan], parsed[4]);
|
||||
@@ -440,7 +510,7 @@ define([
|
||||
if (!data) {
|
||||
// friend is not valid
|
||||
console.error('friend is not valid');
|
||||
return;
|
||||
return void cb('INVALID_FRIEND');
|
||||
}
|
||||
|
||||
var channel = channels[data.channel];
|
||||
@@ -458,12 +528,13 @@ define([
|
||||
var msgStr = JSON.stringify(msg);
|
||||
var cryptMsg = channel.encryptor.encrypt(msgStr);
|
||||
|
||||
// TODO emit remove_friend event?
|
||||
try {
|
||||
channel.wc.bcast(cryptMsg).then(function () {
|
||||
delete friends[curvePublic];
|
||||
delete channels[curvePublic];
|
||||
Realtime.whenRealtimeSyncs(realtime, function () {
|
||||
removeFromFriendList(curvePublic, function () {
|
||||
delete channels[channel.id];
|
||||
eachHandler('unfriend', function (f) {
|
||||
f(curvePublic, true);
|
||||
});
|
||||
cb();
|
||||
});
|
||||
}, function (err) {
|
||||
@@ -476,9 +547,27 @@ define([
|
||||
};
|
||||
|
||||
var getChannelMessagesSince = function (chan, data, keys) {
|
||||
console.log('Fetching [%s] messages since [%s]', data.curvePublic, data.lastKnownHash || '');
|
||||
console.log('Fetching [%s] messages since [%s]', chan.id, data.lastKnownHash || '');
|
||||
|
||||
if (chan.isPadChat) {
|
||||
// We need to use GET_HISTORY_RANGE to make sure we won't get the full history
|
||||
var txid = Util.uid();
|
||||
initRangeRequest(txid, chan.id, undefined);
|
||||
var msg0 = ['GET_HISTORY_RANGE', chan.id, {
|
||||
//from: hash,
|
||||
count: 10,
|
||||
txid: txid,
|
||||
}
|
||||
];
|
||||
network.sendto(network.historyKeeper, JSON.stringify(msg0)).then(function () {
|
||||
}, function (err) {
|
||||
throw new Error(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var cfg = {
|
||||
validateKey: keys.validateKey,
|
||||
validateKey: keys ? keys.validateKey : undefined,
|
||||
owners: [proxy.edPublic, data.edPublic],
|
||||
lastKnownHash: data.lastKnownHash
|
||||
};
|
||||
@@ -489,79 +578,88 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
var openFriendChannel = function (data, f) {
|
||||
var keys = Curve.deriveKeys(data.curvePublic, proxy.curvePrivate);
|
||||
var encryptor = Curve.createEncryptor(keys);
|
||||
network.join(data.channel).then(function (chan) {
|
||||
var channel = channels[data.channel] = {
|
||||
id: data.channel,
|
||||
sending: false,
|
||||
friendEd: f,
|
||||
keys: keys,
|
||||
curve: data.curvePublic,
|
||||
encryptor: encryptor,
|
||||
messages: [],
|
||||
wc: chan,
|
||||
userList: [],
|
||||
mapId: {},
|
||||
send: function (payload, cb) {
|
||||
if (!network.webChannels.some(function (wc) {
|
||||
if (wc.id === channel.wc.id) { return true; }
|
||||
})) {
|
||||
return void cb('NO_SUCH_CHANNEL');
|
||||
}
|
||||
var openChannel = function (data) {
|
||||
var keys = data.keys;
|
||||
var encryptor = data.encryptor || Curve.createEncryptor(keys);
|
||||
var channel = {
|
||||
id: data.channel,
|
||||
isFriendChat: data.isFriendChat,
|
||||
isPadChat: data.isPadChat,
|
||||
sending: false,
|
||||
encryptor: encryptor,
|
||||
messages: [],
|
||||
userList: [],
|
||||
mapId: {},
|
||||
};
|
||||
|
||||
var msg = [Types.message, proxy.curvePublic, +new Date(), payload];
|
||||
var msgStr = JSON.stringify(msg);
|
||||
var cryptMsg = channel.encryptor.encrypt(msgStr);
|
||||
var onJoining = function (peer) {
|
||||
if (peer === Msg.hk) { return; }
|
||||
if (channel.userList.indexOf(peer) !== -1) { return; }
|
||||
channel.userList.push(peer);
|
||||
|
||||
channel.wc.bcast(cryptMsg).then(function () {
|
||||
pushMsg(channel, cryptMsg);
|
||||
cb();
|
||||
}, function (err) {
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
// Join event will be sent once we are able to ID this peer
|
||||
var myData = createData(proxy);
|
||||
delete myData.channel;
|
||||
var msg = [Types.mapId, myData, channel.wc.myID];
|
||||
var msgStr = JSON.stringify(msg);
|
||||
var cryptMsg = channel.encryptor.encrypt(msgStr);
|
||||
var data = {
|
||||
channel: channel.id,
|
||||
msg: cryptMsg
|
||||
};
|
||||
network.sendto(peer, JSON.stringify(data));
|
||||
};
|
||||
|
||||
var onLeaving = function (peer) {
|
||||
var i = channel.userList.indexOf(peer);
|
||||
while (i !== -1) {
|
||||
channel.userList.splice(i, 1);
|
||||
i = channel.userList.indexOf(peer);
|
||||
}
|
||||
// update status
|
||||
var otherData = channel.mapId[peer];
|
||||
if (!otherData) { return; }
|
||||
|
||||
// Make sure the leaving user is not connected with another netflux id
|
||||
if (channel.userList.some(function (nId) {
|
||||
return channel.mapId[nId]
|
||||
&& channel.mapId[nId].curvePublic === otherData.curvePublic;
|
||||
})) { return; }
|
||||
|
||||
// Send the notification
|
||||
eachHandler('leave', function (f) {
|
||||
f(otherData, channel.id);
|
||||
});
|
||||
};
|
||||
|
||||
var onOpen = function (chan) {
|
||||
channel.wc = chan;
|
||||
channels[data.channel] = channel;
|
||||
|
||||
chan.on('message', function (msg, sender) {
|
||||
onMessage(msg, sender, chan);
|
||||
});
|
||||
|
||||
var onJoining = function (peer) {
|
||||
if (peer === Msg.hk) { return; }
|
||||
if (channel.userList.indexOf(peer) !== -1) { return; }
|
||||
|
||||
channel.userList.push(peer);
|
||||
var msg = [Types.mapId, proxy.curvePublic, chan.myID];
|
||||
var msgStr = JSON.stringify(msg);
|
||||
var cryptMsg = channel.encryptor.encrypt(msgStr);
|
||||
network.sendto(peer, cryptMsg);
|
||||
};
|
||||
chan.members.forEach(function (peer) {
|
||||
if (peer === Msg.hk) { return; }
|
||||
if (channel.userList.indexOf(peer) !== -1) { return; }
|
||||
channel.userList.push(peer);
|
||||
});
|
||||
chan.on('join', onJoining);
|
||||
chan.on('leave', function (peer) {
|
||||
var curvePublic = channel.mapId[peer];
|
||||
var i = channel.userList.indexOf(peer);
|
||||
while (i !== -1) {
|
||||
channel.userList.splice(i, 1);
|
||||
i = channel.userList.indexOf(peer);
|
||||
}
|
||||
// update status
|
||||
if (!curvePublic) { return; }
|
||||
eachHandler('leave', function (f) {
|
||||
f(curvePublic, channel.id);
|
||||
});
|
||||
});
|
||||
chan.on('leave', onLeaving);
|
||||
|
||||
// FIXME don't subscribe to the channel implicitly
|
||||
getChannelMessagesSince(chan, data, keys);
|
||||
}, function (err) {
|
||||
getChannelMessagesSince(channel, data, keys);
|
||||
};
|
||||
network.join(data.channel).then(onOpen, function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
network.on('reconnect', function () {
|
||||
if (!channels[data.channel]) { return; }
|
||||
network.join(data.channel).then(onOpen, function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
messenger.getFriendList = function (cb) {
|
||||
@@ -573,7 +671,7 @@ define([
|
||||
}));
|
||||
};
|
||||
|
||||
messenger.openFriendChannel = function (curvePublic, cb) {
|
||||
/*messenger.openFriendChannel = function (curvePublic, cb) {
|
||||
if (typeof(curvePublic) !== 'string') { return void cb('INVALID_ID'); }
|
||||
if (typeof(cb) !== 'function') { throw new Error('expected callback'); }
|
||||
|
||||
@@ -585,10 +683,10 @@ define([
|
||||
if (!channel) { return void cb('E_NO_CHANNEL'); }
|
||||
joining[channel] = cb;
|
||||
openFriendChannel(friend, curvePublic);
|
||||
};
|
||||
};*/
|
||||
|
||||
messenger.sendMessage = function (curvePublic, payload, cb) {
|
||||
var channel = getChannel(curvePublic);
|
||||
messenger.sendMessage = function (id, payload, cb) {
|
||||
var channel = getChannel(id);
|
||||
if (!channel) { return void cb('NO_CHANNEL'); }
|
||||
if (!network.webChannels.some(function (wc) {
|
||||
if (wc.id === channel.wc.id) { return true; }
|
||||
@@ -597,6 +695,9 @@ define([
|
||||
}
|
||||
|
||||
var msg = [Types.message, proxy.curvePublic, +new Date(), payload];
|
||||
if (!channel.isFriendChat) {
|
||||
msg.push(proxy[Constants.displayNameKey]);
|
||||
}
|
||||
var msgStr = JSON.stringify(msg);
|
||||
var cryptMsg = channel.encryptor.encrypt(msgStr);
|
||||
|
||||
@@ -608,18 +709,27 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
messenger.getStatus = function (curvePublic, cb) {
|
||||
var channel = getChannel(curvePublic);
|
||||
messenger.getStatus = function (chanId, cb) {
|
||||
// Display green status if one member is not me
|
||||
var channel = getChannel(chanId);
|
||||
if (!channel) { return void cb('NO_SUCH_CHANNEL'); }
|
||||
var online = channel.userList.some(function (nId) {
|
||||
return channel.mapId[nId] === curvePublic;
|
||||
var data = channel.mapId[nId] || undefined;
|
||||
if (!data) { return false; }
|
||||
return data.curvePublic !== proxy.curvePublic;
|
||||
});
|
||||
cb(void 0, online);
|
||||
};
|
||||
|
||||
messenger.getFriendInfo = function (curvePublic, cb) {
|
||||
messenger.getFriendInfo = function (channel, cb) {
|
||||
setTimeout(function () {
|
||||
var friend = friends[curvePublic];
|
||||
var friend;
|
||||
for (var k in friends) {
|
||||
if (friends[k].channel === channel) {
|
||||
friend = friends[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!friend) { return void cb('NO_SUCH_FRIEND'); }
|
||||
// this clone will be redundant when ui uses postmessage
|
||||
cb(void 0, clone(friend));
|
||||
@@ -633,28 +743,215 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
// TODO listen for changes to your friend list
|
||||
// emit 'update' events for clients
|
||||
var loadFriend = function (friend, cb) {
|
||||
var channel = friend.channel;
|
||||
if (getChannel(channel)) { return void cb(); }
|
||||
|
||||
//var update = function (curvePublic
|
||||
joining[channel] = cb;
|
||||
var keys = Curve.deriveKeys(friend.curvePublic, proxy.curvePrivate);
|
||||
var data = {
|
||||
keys: keys,
|
||||
channel: friend.channel,
|
||||
lastKnownHash: friend.lastKnownHash,
|
||||
owners: [proxy.edPublic, friend.edPublic],
|
||||
isFriendChat: true
|
||||
};
|
||||
openChannel(data);
|
||||
};
|
||||
|
||||
// Detect friends changes made in another worker
|
||||
proxy.on('change', ['friends'], function (o, n, p) {
|
||||
var curvePublic;
|
||||
if (o === undefined) {
|
||||
// new friend added
|
||||
curvePublic = p.slice(-1)[0];
|
||||
eachHandler('friend', function (f) {
|
||||
f(curvePublic, clone(n));
|
||||
|
||||
// Load channel
|
||||
var friend = friends[curvePublic];
|
||||
if (typeof(friend) !== 'object') { return; }
|
||||
var channel = friend.channel;
|
||||
if (!channel) { return; }
|
||||
loadFriend(friend, function () {
|
||||
eachHandler('friend', function (f) {
|
||||
f(curvePublic);
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(o, n, p);
|
||||
if (typeof(n) === 'undefined') {
|
||||
// Handled by .on('remove')
|
||||
return;
|
||||
}
|
||||
}).on('remove', ['friends'], function (o, p) {
|
||||
var curvePublic = p[1];
|
||||
if (!curvePublic) { return; }
|
||||
if (p[2] !== 'channel') { return; }
|
||||
var channel = channels[o];
|
||||
channel.wc.leave(Types.unfriend);
|
||||
delete channels[channel.id];
|
||||
eachHandler('unfriend', function (f) {
|
||||
f(p[1]); // TODO
|
||||
f(curvePublic, true);
|
||||
});
|
||||
});
|
||||
|
||||
// Friend added in our contacts in the current worker
|
||||
messenger.onFriendAdded = function (friendData) {
|
||||
var friend = friends[friendData.curvePublic];
|
||||
if (typeof(friend) !== 'object') { return; }
|
||||
var channel = friend.channel;
|
||||
if (!channel) { return; }
|
||||
loadFriend(friend, function () {
|
||||
eachHandler('friend', function (f) {
|
||||
f(friend.curvePublic);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var ready = false;
|
||||
var initialized = false;
|
||||
var init = function () {
|
||||
if (initialized) { return; }
|
||||
initialized = true;
|
||||
var friends = getFriendList(proxy);
|
||||
|
||||
nThen(function (waitFor) {
|
||||
Object.keys(friends).forEach(function (key) {
|
||||
if (key === 'me') { return; }
|
||||
var friend = clone(friends[key]);
|
||||
if (typeof(friend) !== 'object') { return; }
|
||||
var channel = friend.channel;
|
||||
if (!channel) { return; }
|
||||
loadFriend(friend, waitFor());
|
||||
});
|
||||
// TODO load rooms
|
||||
}).nThen(function () {
|
||||
ready = true;
|
||||
emit('READY');
|
||||
});
|
||||
};
|
||||
//init();
|
||||
|
||||
var getRooms = function (data, cb) {
|
||||
if (data && data.curvePublic) {
|
||||
var curvePublic = data.curvePublic;
|
||||
// We need to get data about a new friend's room
|
||||
var friend = getFriend(proxy, curvePublic);
|
||||
if (!friend) { return void cb({error: 'NO_SUCH_FRIEND'}); }
|
||||
var channel = getChannel(friend.channel);
|
||||
if (!channel) { return void cb({error: 'NO_SUCH_CHANNEL'}); }
|
||||
return void cb([{
|
||||
id: channel.id,
|
||||
isFriendChat: true,
|
||||
name: friend.displayName,
|
||||
lastKnownHash: friend.lastKnownHash,
|
||||
curvePublic: friend.curvePublic,
|
||||
messages: channel.messages
|
||||
}]);
|
||||
}
|
||||
|
||||
if (data && data.padChat) {
|
||||
var pCChannel = getChannel(data.padChat);
|
||||
if (!pCChannel) { return void cb({error: 'NO_SUCH_CHANNEL'}); }
|
||||
return void cb([{
|
||||
id: pCChannel.id,
|
||||
isPadChat: true,
|
||||
messages: pCChannel.messages
|
||||
}]);
|
||||
}
|
||||
|
||||
var rooms = Object.keys(channels).map(function (id) {
|
||||
var r = getChannel(id);
|
||||
var name, lastKnownHash, curvePublic;
|
||||
if (r.isFriendChat) {
|
||||
var friend = getFriendFromChannel(id);
|
||||
if (!friend) { return null; }
|
||||
name = friend.displayName;
|
||||
lastKnownHash = friend.lastKnownHash;
|
||||
curvePublic = friend.curvePublic;
|
||||
} else if (r.isPadChat) {
|
||||
return;
|
||||
} else {
|
||||
// TODO room get metadata (name) && lastKnownHash
|
||||
}
|
||||
return {
|
||||
id: r.id,
|
||||
isFriendChat: r.isFriendChat,
|
||||
name: name,
|
||||
lastKnownHash: lastKnownHash,
|
||||
curvePublic: curvePublic,
|
||||
messages: r.messages
|
||||
};
|
||||
}).filter(function (x) { return x; });
|
||||
cb(rooms);
|
||||
};
|
||||
|
||||
var getUserList = function (data, cb) {
|
||||
var room = getChannel(data.id);
|
||||
if (!room) { return void cb({error: 'NO_SUCH_CHANNEL'}); }
|
||||
if (room.isFriendChat) {
|
||||
var friend = getFriendFromChannel(data.id);
|
||||
if (!friend) { return void cb({error: 'NO_SUCH_FRIEND'}); }
|
||||
cb([friend]);
|
||||
} else {
|
||||
// TODO room userlist in rooms...
|
||||
// (this is the static userlist, not the netflux one)
|
||||
cb([]);
|
||||
}
|
||||
};
|
||||
|
||||
var openPadChat = function (data, cb) {
|
||||
var channel = data.channel;
|
||||
if (getChannel(channel)) {
|
||||
emit('PADCHAT_READY', channel);
|
||||
return void cb();
|
||||
}
|
||||
var keys = data.secret && data.secret.keys;
|
||||
var cryptKey = keys.viewKeyStr ? Crypto.b64AddSlashes(keys.viewKeyStr) : data.secret.key;
|
||||
var encryptor = Crypto.createEncryptor(cryptKey);
|
||||
var chanData = {
|
||||
encryptor: encryptor,
|
||||
channel: data.channel,
|
||||
isPadChat: true,
|
||||
//lastKnownHash: friend.lastKnownHash,
|
||||
//owners: [proxy.edPublic, friend.edPublic],
|
||||
//isFriendChat: true
|
||||
};
|
||||
openChannel(chanData);
|
||||
joining[channel] = function () {
|
||||
emit('PADCHAT_READY', channel);
|
||||
};
|
||||
cb();
|
||||
};
|
||||
|
||||
network.on('disconnect', function () {
|
||||
emit('DISCONNECT');
|
||||
});
|
||||
network.on('reconnect', function () {
|
||||
emit('RECONNECT');
|
||||
});
|
||||
|
||||
messenger.execCommand = function (obj, cb) {
|
||||
var cmd = obj.cmd;
|
||||
var data = obj.data;
|
||||
if (cmd === 'INIT_FRIENDS') {
|
||||
init();
|
||||
return void cb();
|
||||
}
|
||||
if (cmd === 'IS_READY') {
|
||||
return void cb(ready);
|
||||
}
|
||||
if (cmd === 'GET_ROOMS') {
|
||||
return void getRooms(data, cb);
|
||||
}
|
||||
if (cmd === 'GET_USERLIST') {
|
||||
return void getUserList(data, cb);
|
||||
}
|
||||
if (cmd === 'OPEN_PAD_CHAT') {
|
||||
return void openPadChat(data, cb);
|
||||
}
|
||||
};
|
||||
|
||||
Object.freeze(messenger);
|
||||
|
||||
return messenger;
|
||||
|
||||
@@ -12,10 +12,11 @@ define([
|
||||
'/common/clipboard.js',
|
||||
'/customize/messages.js',
|
||||
'/customize/application_config.js',
|
||||
'/customize/pages.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'css!/customize/fonts/cptools/style.css'
|
||||
], function ($, Config, Util, Hash, Language, UI, Constants, Feedback, h, MediaTag, Clipboard,
|
||||
Messages, AppConfig, NThen) {
|
||||
Messages, AppConfig, Pages, NThen) {
|
||||
var UIElements = {};
|
||||
|
||||
// Configure MediaTags to use our local viewer
|
||||
@@ -630,23 +631,25 @@ define([
|
||||
if (!data.FM) { return; }
|
||||
var $input = $('<input>', {
|
||||
'type': 'file',
|
||||
'style': 'display: none;'
|
||||
'style': 'display: none;',
|
||||
'multiple': 'multiple'
|
||||
}).on('change', function (e) {
|
||||
var file = e.target.files[0];
|
||||
var ev = {
|
||||
target: data.target
|
||||
};
|
||||
if (data.filter && !data.filter(file)) {
|
||||
return;
|
||||
}
|
||||
if (data.transformer) {
|
||||
data.transformer(file, function (newFile) {
|
||||
data.FM.handleFile(newFile, ev);
|
||||
if (callback) { callback(); }
|
||||
});
|
||||
return;
|
||||
}
|
||||
data.FM.handleFile(file, ev);
|
||||
var files = Util.slice(e.target.files);
|
||||
files.forEach(function (file) {
|
||||
var ev = {
|
||||
target: data.target
|
||||
};
|
||||
if (data.filter && !data.filter(file)) {
|
||||
return;
|
||||
}
|
||||
if (data.transformer) {
|
||||
data.transformer(file, function (newFile) {
|
||||
data.FM.handleFile(newFile, ev);
|
||||
});
|
||||
return;
|
||||
}
|
||||
data.FM.handleFile(file, ev);
|
||||
});
|
||||
if (callback) { callback(); }
|
||||
});
|
||||
if (data.accept) { $input.attr('accept', data.accept); }
|
||||
@@ -1792,13 +1795,16 @@ define([
|
||||
|
||||
var $container = $('<div>');
|
||||
var i = 0;
|
||||
AppConfig.availablePadTypes.forEach(function (p) {
|
||||
var types = AppConfig.availablePadTypes.filter(function (p) {
|
||||
if (p === 'drive') { return; }
|
||||
if (p === 'contacts') { return; }
|
||||
if (p === 'todo') { return; }
|
||||
if (p === 'file') { return; }
|
||||
if (!common.isLoggedIn() && AppConfig.registeredOnlyTypes &&
|
||||
AppConfig.registeredOnlyTypes.indexOf(p) !== -1) { return; }
|
||||
return true;
|
||||
});
|
||||
types.forEach(function (p) {
|
||||
var $element = $('<li>', {
|
||||
'class': 'cp-icons-element',
|
||||
'id': 'cp-newpad-icons-'+ (i++)
|
||||
@@ -1822,7 +1828,7 @@ define([
|
||||
|
||||
var selected = -1;
|
||||
var next = function () {
|
||||
selected = ++selected % 5;
|
||||
selected = ++selected % types.length;
|
||||
$container.find('.cp-icons-element-selected').removeClass('cp-icons-element-selected');
|
||||
$container.find('#cp-newpad-icons-'+selected).addClass('cp-icons-element-selected');
|
||||
};
|
||||
@@ -2338,10 +2344,57 @@ define([
|
||||
$(password).find('.cp-password-input').focus();
|
||||
};
|
||||
|
||||
var crowdfundingState = false;
|
||||
UIElements.displayCrowdfunding = function (common) {
|
||||
if (crowdfundingState) { return; }
|
||||
if (AppConfig.disableCrowdfundingMessages) { return; }
|
||||
var priv = common.getMetadataMgr().getPrivateData();
|
||||
if (priv.plan) { return; }
|
||||
|
||||
crowdfundingState = true;
|
||||
setTimeout(function () {
|
||||
common.getAttribute(['general', 'crowdfunding'], function (err, val) {
|
||||
if (err || val === false) { return; }
|
||||
// Display the popup
|
||||
var text = Messages.crowdfunding_popup_text;
|
||||
var yes = h('button.cp-corner-primary', Messages.crowdfunding_popup_yes);
|
||||
var no = h('button.cp-corner-primary', Messages.crowdfunding_popup_no);
|
||||
var never = h('button.cp-corner-cancel', Messages.crowdfunding_popup_never);
|
||||
var actions = h('div', [yes, no, never]);
|
||||
|
||||
var modal = UI.cornerPopup(text, actions, null, {big: true});
|
||||
|
||||
$(yes).click(function () {
|
||||
modal.delete();
|
||||
common.openURL('https://opencollective.com/cryptpad/contribute');
|
||||
Feedback.send('CROWDFUNDING_YES');
|
||||
});
|
||||
$(modal.popup).find('a').click(function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
modal.delete();
|
||||
common.openURL('https://opencollective.com/cryptpad/');
|
||||
Feedback.send('CROWDFUNDING_LINK');
|
||||
});
|
||||
$(no).click(function () {
|
||||
modal.delete();
|
||||
Feedback.send('CROWDFUNDING_NO');
|
||||
});
|
||||
$(never).click(function () {
|
||||
modal.delete();
|
||||
common.setAttribute(['general', 'crowdfunding'], false);
|
||||
Feedback.send('CROWDFUNDING_NEVER');
|
||||
});
|
||||
|
||||
});
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
var storePopupState = false;
|
||||
UIElements.displayStorePadPopup = function (common, data) {
|
||||
if (storePopupState) { return; }
|
||||
storePopupState = true;
|
||||
if (data && data.stored) { return; } // We won't display the popup for dropped files
|
||||
|
||||
var text = Messages.autostore_notstored;
|
||||
var footer = Messages.autostore_settings;
|
||||
@@ -2359,15 +2412,20 @@ define([
|
||||
});
|
||||
|
||||
$(hide).click(function () {
|
||||
UIElements.displayCrowdfunding(common);
|
||||
modal.delete();
|
||||
});
|
||||
$(store).click(function () {
|
||||
modal.delete();
|
||||
common.getSframeChannel().query("Q_AUTOSTORE_STORE", null, function (err, obj) {
|
||||
if (err || (obj && obj.error)) {
|
||||
console.error(err || obj.error);
|
||||
var error = err || (obj && obj.error);
|
||||
if (error) {
|
||||
if (error === 'E_OVER_LIMIT') {
|
||||
return void UI.warn(Messages.pinLimitReached);
|
||||
}
|
||||
return void UI.warn(Messages.autostore_error);
|
||||
}
|
||||
modal.delete();
|
||||
UIElements.displayCrowdfunding(common);
|
||||
UI.log(Messages.autostore_saved);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -345,6 +345,9 @@ define([
|
||||
};
|
||||
common.getPadAttribute = function (attr, cb, href) {
|
||||
href = Hash.getRelativeHref(href || window.location.href);
|
||||
if (!href) {
|
||||
return void cb('E404');
|
||||
}
|
||||
postMessage("GET_PAD_ATTRIBUTE", {
|
||||
href: href,
|
||||
attr: attr,
|
||||
@@ -622,6 +625,12 @@ define([
|
||||
messenger.setChannelHead = function (data, cb) {
|
||||
postMessage("CONTACTS_SET_CHANNEL_HEAD", data, cb);
|
||||
};
|
||||
|
||||
messenger.execCommand = function (data, cb) {
|
||||
postMessage("CHAT_COMMAND", data, cb);
|
||||
};
|
||||
|
||||
messenger.onEvent = Util.mkEvent();
|
||||
messenger.onMessageEvent = Util.mkEvent();
|
||||
messenger.onJoinEvent = Util.mkEvent();
|
||||
messenger.onLeaveEvent = Util.mkEvent();
|
||||
@@ -1059,6 +1068,8 @@ define([
|
||||
CONTACTS_UPDATE: common.messenger.onUpdateEvent.fire,
|
||||
CONTACTS_FRIEND: common.messenger.onFriendEvent.fire,
|
||||
CONTACTS_UNFRIEND: common.messenger.onUnfriendEvent.fire,
|
||||
// Chat
|
||||
CHAT_EVENT: common.messenger.onEvent.fire,
|
||||
// Pad
|
||||
PAD_READY: common.padRpc.onReadyEvent.fire,
|
||||
PAD_MESSAGE: common.padRpc.onMessageEvent.fire,
|
||||
@@ -1425,7 +1436,7 @@ define([
|
||||
postMessage("INIT_RPC", null, waitFor(function (obj) {
|
||||
console.log('RPC handshake complete');
|
||||
if (obj.error) { return; }
|
||||
localStorage.plan = obj.plan;
|
||||
localStorage[Constants.plan] = obj.plan;
|
||||
}));
|
||||
} else if (PINNING_ENABLED) {
|
||||
console.log('not logged in. pads will not be pinned');
|
||||
|
||||
@@ -4,16 +4,32 @@ define([
|
||||
'/common/common-hash.js',
|
||||
'/common/common-util.js',
|
||||
'/common/media-tag.js',
|
||||
'/common/highlight/highlight.pack.js',
|
||||
'/bower_components/diff-dom/diffDOM.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
],function ($, Marked, Hash, Util, MediaTag) {
|
||||
'css!/common/highlight/styles/github.css'
|
||||
],function ($, Marked, Hash, Util, MediaTag, Highlight) {
|
||||
var DiffMd = {};
|
||||
|
||||
var DiffDOM = window.diffDOM;
|
||||
var renderer = new Marked.Renderer();
|
||||
|
||||
var highlighter = function () {
|
||||
return function(code, lang) {
|
||||
if (lang) {
|
||||
try {
|
||||
return Highlight.highlight(lang, code).value;
|
||||
} catch (e) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
};
|
||||
};
|
||||
|
||||
Marked.setOptions({
|
||||
renderer: renderer
|
||||
renderer: renderer,
|
||||
highlight: highlighter(),
|
||||
});
|
||||
|
||||
DiffMd.render = function (md) {
|
||||
@@ -25,9 +41,11 @@ define([
|
||||
// Tasks list
|
||||
var checkedTaskItemPtn = /^\s*(<p>)?\[[xX]\](<\/p>)?\s*/;
|
||||
var uncheckedTaskItemPtn = /^\s*(<p>)?\[ ?\](<\/p>)?\s*/;
|
||||
var bogusCheckPtn = /<input( checked=""){0,1} disabled="" type="checkbox">/;
|
||||
renderer.listitem = function (text) {
|
||||
var isCheckedTaskItem = checkedTaskItemPtn.test(text);
|
||||
var isUncheckedTaskItem = uncheckedTaskItemPtn.test(text);
|
||||
var hasBogusInput = bogusCheckPtn.test(text);
|
||||
if (isCheckedTaskItem) {
|
||||
text = text.replace(checkedTaskItemPtn,
|
||||
'<i class="fa fa-check-square" aria-hidden="true"></i> ') + '\n';
|
||||
@@ -36,6 +54,15 @@ define([
|
||||
text = text.replace(uncheckedTaskItemPtn,
|
||||
'<i class="fa fa-square-o" aria-hidden="true"></i> ') + '\n';
|
||||
}
|
||||
if (!isCheckedTaskItem && !isUncheckedTaskItem && hasBogusInput) {
|
||||
if (/checked/.test(text)) {
|
||||
text = text.replace(bogusCheckPtn,
|
||||
'<i class="fa fa-check-square" aria-hidden="true"></i> ') + '\n';
|
||||
} else if (/disabled/.test(text)) {
|
||||
text = text.replace(bogusCheckPtn,
|
||||
'<i class="fa fa-square-o" aria-hidden="true"></i> ') + '\n';
|
||||
}
|
||||
}
|
||||
var cls = (isCheckedTaskItem || isUncheckedTaskItem) ? ' class="todo-list-item"' : '';
|
||||
return '<li'+ cls + '>' + text + '</li>\n';
|
||||
};
|
||||
|
||||
2
www/common/highlight/highlight.pack.js
Normal file
2
www/common/highlight/highlight.pack.js
Normal file
File diff suppressed because one or more lines are too long
99
www/common/highlight/styles/default.css
Normal file
99
www/common/highlight/styles/default.css
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
|
||||
Original highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
|
||||
/* Base color: saturation 0; */
|
||||
|
||||
.hljs,
|
||||
.hljs-subst {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.hljs-comment {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-attribute,
|
||||
.hljs-selector-tag,
|
||||
.hljs-meta-keyword,
|
||||
.hljs-doctag,
|
||||
.hljs-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/* User color: hue: 0 */
|
||||
|
||||
.hljs-type,
|
||||
.hljs-string,
|
||||
.hljs-number,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-quote,
|
||||
.hljs-template-tag,
|
||||
.hljs-deletion {
|
||||
color: #880000;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #880000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-symbol,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-link,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #BC6060;
|
||||
}
|
||||
|
||||
|
||||
/* Language color: hue: 90; */
|
||||
|
||||
.hljs-literal {
|
||||
color: #78A960;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-bullet,
|
||||
.hljs-code,
|
||||
.hljs-addition {
|
||||
color: #397300;
|
||||
}
|
||||
|
||||
|
||||
/* Meta color: hue: 200 */
|
||||
|
||||
.hljs-meta {
|
||||
color: #1f7199;
|
||||
}
|
||||
|
||||
.hljs-meta-string {
|
||||
color: #4d99bf;
|
||||
}
|
||||
|
||||
|
||||
/* Misc effects */
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
99
www/common/highlight/styles/github.css
Normal file
99
www/common/highlight/styles/github.css
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
|
||||
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #333;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #998;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-subst {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-literal,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag .hljs-attr {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-doctag {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-id {
|
||||
color: #900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-type,
|
||||
.hljs-class .hljs-title {
|
||||
color: #458;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-attribute {
|
||||
color: #000080;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-link {
|
||||
color: #009926;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #990073;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-meta {
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
background: #fdd;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
background: #dfd;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -59,6 +59,9 @@ define([
|
||||
obj[key] = data.value;
|
||||
}
|
||||
broadcast([clientId], "UPDATE_METADATA");
|
||||
if (Array.isArray(path) && path[0] === 'profile' && store.messenger) {
|
||||
store.messenger.updateMyData();
|
||||
}
|
||||
onSync(cb);
|
||||
};
|
||||
|
||||
@@ -462,7 +465,7 @@ define([
|
||||
if (data.password) { pad.password = data.password; }
|
||||
if (data.channel) { pad.channel = data.channel; }
|
||||
store.manager.addPad(data.path, pad, function (e) {
|
||||
if (e) { return void cb({error: "Error while adding the pad:"+ e}); }
|
||||
if (e) { return void cb({error: e}); }
|
||||
sendDriveEvent('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
}, clientId);
|
||||
@@ -597,6 +600,7 @@ define([
|
||||
Store.setDisplayName = function (clientId, value, cb) {
|
||||
store.proxy[Constants.displayNameKey] = value;
|
||||
broadcast([clientId], "UPDATE_METADATA");
|
||||
if (store.messenger) { store.messenger.updateMyData(); }
|
||||
onSync(cb);
|
||||
};
|
||||
|
||||
@@ -796,12 +800,20 @@ define([
|
||||
password: data.password,
|
||||
path: data.path
|
||||
}, cb);
|
||||
// Let inner know that dropped files shouldn't trigger the popup
|
||||
postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", {
|
||||
stored: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
sendDriveEvent('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
}, clientId);
|
||||
// Let inner know that dropped files shouldn't trigger the popup
|
||||
postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", {
|
||||
stored: true
|
||||
});
|
||||
}
|
||||
onSync(cb);
|
||||
};
|
||||
@@ -851,6 +863,9 @@ define([
|
||||
},
|
||||
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
|
||||
friendComplete: function (data) {
|
||||
if (data.friend && store.messenger && store.messenger.onFriendAdded) {
|
||||
store.messenger.onFriendAdded(data.friend);
|
||||
}
|
||||
postMessage(clientId, "EV_FRIEND_COMPLETE", data);
|
||||
},
|
||||
friendRequest: function (data, cb) {
|
||||
@@ -884,6 +899,7 @@ define([
|
||||
|
||||
Store.messenger = {
|
||||
getFriendList: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.getFriendList(function (e, keys) {
|
||||
cb({
|
||||
error: e,
|
||||
@@ -892,6 +908,7 @@ define([
|
||||
});
|
||||
},
|
||||
getMyInfo: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.getMyInfo(function (e, info) {
|
||||
cb({
|
||||
error: e,
|
||||
@@ -900,6 +917,7 @@ define([
|
||||
});
|
||||
},
|
||||
getFriendInfo: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.getFriendInfo(data, function (e, info) {
|
||||
cb({
|
||||
error: e,
|
||||
@@ -908,6 +926,7 @@ define([
|
||||
});
|
||||
},
|
||||
removeFriend: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.removeFriend(data, function (e, info) {
|
||||
cb({
|
||||
error: e,
|
||||
@@ -916,11 +935,13 @@ define([
|
||||
});
|
||||
},
|
||||
openFriendChannel: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.openFriendChannel(data, function (e) {
|
||||
cb({ error: e, });
|
||||
});
|
||||
},
|
||||
getFriendStatus: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.getStatus(data, function (e, online) {
|
||||
cb({
|
||||
error: e,
|
||||
@@ -929,6 +950,7 @@ define([
|
||||
});
|
||||
},
|
||||
getMoreHistory: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.getMoreHistory(data.curvePublic, data.sig, data.count, function (e, history) {
|
||||
cb({
|
||||
error: e,
|
||||
@@ -937,6 +959,7 @@ define([
|
||||
});
|
||||
},
|
||||
sendMessage: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.sendMessage(data.curvePublic, data.content, function (e) {
|
||||
cb({
|
||||
error: e,
|
||||
@@ -944,11 +967,17 @@ define([
|
||||
});
|
||||
},
|
||||
setChannelHead: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.setChannelHead(data.curvePublic, data.sig, function (e) {
|
||||
cb({
|
||||
error: e
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
execCommand: function (clientId, data, cb) {
|
||||
if (!store.messenger) { return void cb({error: 'Messenger is disabled'}); }
|
||||
store.messenger.execCommand(data, cb);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1309,7 +1338,6 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var messengerEventInit = false;
|
||||
var sendMessengerEvent = function (q, data) {
|
||||
messengerEventClients.forEach(function (cId) {
|
||||
postMessage(cId, q, data);
|
||||
@@ -1319,41 +1347,49 @@ define([
|
||||
if (messengerEventClients.indexOf(clientId) === -1) {
|
||||
messengerEventClients.push(clientId);
|
||||
}
|
||||
if (!messengerEventInit) {
|
||||
var messenger = store.messenger = Messenger.messenger(store);
|
||||
messenger.on('message', function (message) {
|
||||
sendMessengerEvent('CONTACTS_MESSAGE', message);
|
||||
};
|
||||
var loadMessenger = function () {
|
||||
if (AppConfig.availablePadTypes.indexOf('contacts') === -1) { return; }
|
||||
var messenger = store.messenger = Messenger.messenger(store);
|
||||
messenger.on('message', function (message) {
|
||||
sendMessengerEvent('CONTACTS_MESSAGE', message);
|
||||
});
|
||||
messenger.on('join', function (curvePublic, channel) {
|
||||
sendMessengerEvent('CONTACTS_JOIN', {
|
||||
curvePublic: curvePublic,
|
||||
channel: channel,
|
||||
});
|
||||
messenger.on('join', function (curvePublic, channel) {
|
||||
sendMessengerEvent('CONTACTS_JOIN', {
|
||||
curvePublic: curvePublic,
|
||||
channel: channel,
|
||||
});
|
||||
});
|
||||
messenger.on('leave', function (curvePublic, channel) {
|
||||
sendMessengerEvent('CONTACTS_LEAVE', {
|
||||
curvePublic: curvePublic,
|
||||
channel: channel,
|
||||
});
|
||||
messenger.on('leave', function (curvePublic, channel) {
|
||||
sendMessengerEvent('CONTACTS_LEAVE', {
|
||||
curvePublic: curvePublic,
|
||||
channel: channel,
|
||||
});
|
||||
});
|
||||
messenger.on('update', function (info, types, channel) {
|
||||
sendMessengerEvent('CONTACTS_UPDATE', {
|
||||
types: types,
|
||||
info: info,
|
||||
channel: channel
|
||||
});
|
||||
messenger.on('update', function (info, curvePublic) {
|
||||
sendMessengerEvent('CONTACTS_UPDATE', {
|
||||
curvePublic: curvePublic,
|
||||
info: info,
|
||||
});
|
||||
});
|
||||
messenger.on('friend', function (curvePublic) {
|
||||
sendMessengerEvent('CONTACTS_FRIEND', {
|
||||
curvePublic: curvePublic,
|
||||
});
|
||||
messenger.on('friend', function (curvePublic) {
|
||||
sendMessengerEvent('CONTACTS_FRIEND', {
|
||||
curvePublic: curvePublic,
|
||||
});
|
||||
});
|
||||
messenger.on('unfriend', function (curvePublic, removedByMe) {
|
||||
sendMessengerEvent('CONTACTS_UNFRIEND', {
|
||||
curvePublic: curvePublic,
|
||||
removedByMe: removedByMe
|
||||
});
|
||||
messenger.on('unfriend', function (curvePublic) {
|
||||
sendMessengerEvent('CONTACTS_UNFRIEND', {
|
||||
curvePublic: curvePublic,
|
||||
});
|
||||
});
|
||||
messenger.on('event', function (ev, data) {
|
||||
sendMessengerEvent('CHAT_EVENT', {
|
||||
ev: ev,
|
||||
data: data
|
||||
});
|
||||
messengerEventInit = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1442,6 +1478,7 @@ define([
|
||||
});
|
||||
userObject.fixFiles();
|
||||
loadSharedFolders(waitFor);
|
||||
loadMessenger();
|
||||
}).nThen(function () {
|
||||
var requestLogin = function () {
|
||||
broadcast([], "REQUEST_LOGIN");
|
||||
|
||||
@@ -69,6 +69,8 @@ define([
|
||||
CONTACTS_GET_MORE_HISTORY: Store.messenger.getMoreHistory,
|
||||
CONTACTS_SEND_MESSAGE: Store.messenger.sendMessage,
|
||||
CONTACTS_SET_CHANNEL_HEAD: Store.messenger.setChannelHead,
|
||||
// Chat
|
||||
CHAT_COMMAND: Store.messenger.execCommand,
|
||||
// Pad
|
||||
SEND_PAD_MSG: Store.sendPadMsg,
|
||||
JOIN_PAD: Store.joinPad,
|
||||
|
||||
@@ -322,6 +322,8 @@ define([
|
||||
if (!readOnly) { onLocal(); }
|
||||
evOnReady.fire(newPad);
|
||||
|
||||
common.openPadChat(onLocal);
|
||||
|
||||
UI.removeLoadingScreen(emitResize);
|
||||
|
||||
var privateDat = cpNfInner.metadataMgr.getPrivateData();
|
||||
@@ -559,6 +561,7 @@ define([
|
||||
}, onLocal);
|
||||
var configTb = {
|
||||
displayed: [
|
||||
'chat',
|
||||
'userlist',
|
||||
'title',
|
||||
'useradmin',
|
||||
|
||||
@@ -36,7 +36,8 @@ define([
|
||||
window.addEventListener('message', onMsg);
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
SFCommonO.start({
|
||||
useCreationScreen: true
|
||||
useCreationScreen: true,
|
||||
messaging: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -262,6 +262,7 @@ define([
|
||||
donateURL: Cryptpad.donateURL,
|
||||
upgradeURL: Cryptpad.upgradeURL
|
||||
},
|
||||
plan: localStorage[Utils.Constants.plan],
|
||||
isNewFile: isNewFile,
|
||||
isDeleted: isNewFile && window.location.hash.length > 0,
|
||||
forceCreationScreen: forceCreationScreen,
|
||||
@@ -370,7 +371,7 @@ define([
|
||||
forceSave: true
|
||||
};
|
||||
Cryptpad.setPadTitle(data, function (err) {
|
||||
cb(err);
|
||||
cb({error: err});
|
||||
});
|
||||
});
|
||||
sframeChan.on('Q_IS_PAD_STORED', function (data, cb) {
|
||||
@@ -776,6 +777,22 @@ define([
|
||||
Cryptpad.messenger.setChannelHead(opt, cb);
|
||||
});
|
||||
|
||||
sframeChan.on('Q_CHAT_OPENPADCHAT', function (data, cb) {
|
||||
Cryptpad.messenger.execCommand({
|
||||
cmd: 'OPEN_PAD_CHAT',
|
||||
data: {
|
||||
channel: data,
|
||||
secret: secret
|
||||
}
|
||||
}, cb);
|
||||
});
|
||||
sframeChan.on('Q_CHAT_COMMAND', function (data, cb) {
|
||||
Cryptpad.messenger.execCommand(data, cb);
|
||||
});
|
||||
Cryptpad.messenger.onEvent.reg(function (data) {
|
||||
sframeChan.event('EV_CHAT_EVENT', data);
|
||||
});
|
||||
|
||||
Cryptpad.messenger.onMessageEvent.reg(function (data) {
|
||||
sframeChan.event('EV_CONTACTS_MESSAGE', data);
|
||||
});
|
||||
|
||||
@@ -161,6 +161,24 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
// Chat
|
||||
var padChatChannel;
|
||||
funcs.getPadChat = function () {
|
||||
return padChatChannel;
|
||||
};
|
||||
funcs.openPadChat = function (saveChanges) {
|
||||
var md = JSON.parse(JSON.stringify(ctx.metadataMgr.getMetadata()));
|
||||
var channel = md.chat || Hash.createChannelId();
|
||||
if (!md.chat) {
|
||||
md.chat = channel;
|
||||
ctx.metadataMgr.updateMetadata(md);
|
||||
setTimeout(saveChanges);
|
||||
}
|
||||
padChatChannel = channel;
|
||||
ctx.sframeChan.query('Q_CHAT_OPENPADCHAT', channel, function (err, obj) {
|
||||
if (err || (obj && obj.error)) { console.error(err || (obj && obj.error)); }
|
||||
});
|
||||
};
|
||||
|
||||
// CodeMirror
|
||||
funcs.initCodeMirrorApp = callWithCommon(CodeMirror.create);
|
||||
@@ -517,6 +535,11 @@ define([
|
||||
UI.alert(Messages.chrome68);
|
||||
});
|
||||
|
||||
funcs.isPadStored(function (err, val) {
|
||||
if (err || !val) { return; }
|
||||
UIElements.displayCrowdfunding(funcs);
|
||||
});
|
||||
|
||||
ctx.sframeChan.ready();
|
||||
cb(funcs);
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ define([], function () {
|
||||
});
|
||||
sFrameChan.on('EV_CONTACTS_UPDATE', function (data) {
|
||||
_handlers.update.forEach(function (f) {
|
||||
f(data.info, data.curvePublic);
|
||||
f(data.info, data.types, data.channel);
|
||||
});
|
||||
});
|
||||
sFrameChan.on('EV_CONTACTS_FRIEND', function (data) {
|
||||
@@ -45,7 +45,7 @@ define([], function () {
|
||||
});
|
||||
sFrameChan.on('EV_CONTACTS_UNFRIEND', function (data) {
|
||||
_handlers.unfriend.forEach(function (f) {
|
||||
f(data.curvePublic);
|
||||
f(data.curvePublic, data.removedByMe);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -171,6 +171,11 @@ define({
|
||||
'Q_CONTACTS_SET_CHANNEL_HEAD': true,
|
||||
'Q_CONTACTS_CLEAR_OWNED_CHANNEL': true,
|
||||
|
||||
// Chat
|
||||
'EV_CHAT_EVENT': true,
|
||||
'Q_CHAT_COMMAND': true,
|
||||
'Q_CHAT_OPENPADCHAT': true,
|
||||
|
||||
// Put one or more entries to the localStore which will go in localStorage.
|
||||
'EV_LOCALSTORE_PUT': true,
|
||||
// Put one entry in the parent sessionStorage
|
||||
@@ -218,6 +223,8 @@ define({
|
||||
// Refresh the drive when the drive has changed ('change' or 'remove' events)
|
||||
'EV_DRIVE_CHANGE': true,
|
||||
'EV_DRIVE_REMOVE': true,
|
||||
// Set shared folder hash in the address bar
|
||||
'EV_DRIVE_SET_HASH': true,
|
||||
|
||||
// Remove an owned pad from the server
|
||||
'Q_REMOVE_OWNED_CHANNEL': true,
|
||||
|
||||
@@ -6,8 +6,11 @@ define([
|
||||
'/common/common-interface.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-feedback.js',
|
||||
'/common/sframe-messenger-inner.js',
|
||||
'/contacts/messenger-ui.js',
|
||||
'/customize/messages.js',
|
||||
], function ($, Config, ApiConfig, UIElements, UI, Hash, Feedback, Messages) {
|
||||
], function ($, Config, ApiConfig, UIElements, UI, Hash, Feedback,
|
||||
Messenger, MessengerUI, Messages) {
|
||||
var Common;
|
||||
|
||||
var Bar = {
|
||||
@@ -231,16 +234,15 @@ define([
|
||||
var name = data.name || Messages.anonymous;
|
||||
var $span = $('<span>', {'class': 'cp-avatar'});
|
||||
var $rightCol = $('<span>', {'class': 'cp-toolbar-userlist-rightcol'});
|
||||
var $nameSpan = $('<span>', {'class': 'cp-toolbar-userlist-name'}).text(name).appendTo($rightCol);
|
||||
var $nameSpan = $('<span>', {'class': 'cp-toolbar-userlist-name'}).appendTo($rightCol);
|
||||
var $nameValue = $('<span>', {
|
||||
'class': 'cp-toolbar-userlist-name-value'
|
||||
}).text(name).appendTo($nameSpan);
|
||||
var isMe = data.uid === user.uid;
|
||||
if (isMe && !priv.readOnly) {
|
||||
$nameSpan.html('');
|
||||
var $nameValue = $('<span>', {
|
||||
'class': 'cp-toolbar-userlist-name-value'
|
||||
}).text(name).appendTo($nameSpan);
|
||||
if (!Config.disableProfile) {
|
||||
var $button = $('<button>', {
|
||||
'class': 'fa fa-pencil cp-toolbar-userlist-name-edit',
|
||||
'class': 'fa fa-pencil cp-toolbar-userlist-button',
|
||||
title: Messages.user_rename
|
||||
}).appendTo($nameSpan);
|
||||
$button.hover(function (e) { e.preventDefault(); e.stopPropagation(); });
|
||||
@@ -296,16 +298,24 @@ define([
|
||||
$('<span>', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending)
|
||||
.appendTo($rightCol);
|
||||
} else {
|
||||
$('<span>', {
|
||||
'class': 'fa fa-user-plus cp-toolbar-userlist-friend',
|
||||
$('<button>', {
|
||||
'class': 'fa fa-user-plus cp-toolbar-userlist-button',
|
||||
'title': Messages._getKey('userlist_addAsFriendTitle', [
|
||||
name
|
||||
])
|
||||
}).appendTo($rightCol).click(function (e) {
|
||||
}).appendTo($nameSpan).click(function (e) {
|
||||
e.stopPropagation();
|
||||
Common.sendFriendRequest(data.netfluxId);
|
||||
});
|
||||
}
|
||||
} else if (Common.isLoggedIn() && data.curvePublic && friends[data.curvePublic]) {
|
||||
$('<button>', {
|
||||
'class': 'fa fa-comments-o cp-toolbar-userlist-button',
|
||||
'title': Messages.userlist_chat
|
||||
}).appendTo($nameSpan).click(function (e) {
|
||||
e.stopPropagation();
|
||||
Common.openURL('/contacts/');
|
||||
});
|
||||
}
|
||||
if (data.profile) {
|
||||
$span.addClass('cp-userlist-clickable');
|
||||
@@ -410,6 +420,77 @@ define([
|
||||
return $container;
|
||||
};
|
||||
|
||||
var initChat = function (toolbar) {
|
||||
var $container = $('<div>', {
|
||||
id: 'cp-app-contacts-container',
|
||||
'class': 'cp-app-contacts-inapp'
|
||||
}).prependTo(toolbar.chatContent);
|
||||
var sframeChan = Common.getSframeChannel();
|
||||
var messenger = Messenger.create(sframeChan);
|
||||
MessengerUI.create(messenger, $container, Common, toolbar);
|
||||
};
|
||||
var createChat = function (toolbar, config) {
|
||||
if (!config.metadataMgr) {
|
||||
throw new Error("You must provide a `metadataMgr` to display the chat");
|
||||
}
|
||||
if (Config.availablePadTypes.indexOf('contacts') === -1) { return; }
|
||||
var $content = $('<div>', {'class': 'cp-toolbar-chat-drawer'});
|
||||
$content.on('drop dragover', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
var $closeIcon = $('<span>', {"class": "fa fa-window-close cp-toolbar-chat-drawer-close"}).appendTo($content);
|
||||
//$('<h2>').text(Messages.users).appendTo($content);
|
||||
//$('<p>', {'class': USERLIST_CLS}).appendTo($content);
|
||||
|
||||
toolbar.chatContent = $content;
|
||||
|
||||
var $container = $('<span>', {id: 'cp-toolbar-chat-drawer-open', title: Messages.chatButton});
|
||||
|
||||
var $button = $('<button>', {'class': 'fa fa-comments'}).appendTo($container);
|
||||
$('<span>',{'class': 'cp-dropdown-button-title'}).appendTo($button);
|
||||
|
||||
toolbar.$leftside.prepend($container);
|
||||
|
||||
if (config.$contentContainer) {
|
||||
config.$contentContainer.prepend($content);
|
||||
}
|
||||
|
||||
var hide = function () {
|
||||
$content.hide();
|
||||
$button.removeClass('cp-toolbar-button-active');
|
||||
config.$contentContainer.removeClass('cp-chat-visible');
|
||||
};
|
||||
var show = function () {
|
||||
if (Bar.isEmbed) { $content.hide(); return; }
|
||||
$content.show();
|
||||
$button.addClass('cp-toolbar-button-active');
|
||||
config.$contentContainer.addClass('cp-chat-visible');
|
||||
$button.removeClass('cp-toolbar-notification');
|
||||
};
|
||||
$closeIcon.click(function () {
|
||||
Common.setAttribute(['toolbar', 'chat-drawer'], false);
|
||||
hide();
|
||||
});
|
||||
$button.click(function () {
|
||||
var visible = $content.is(':visible');
|
||||
if (visible) { hide(); }
|
||||
else { show(); }
|
||||
visible = !visible;
|
||||
Common.setAttribute(['toolbar', 'chat-drawer'], visible);
|
||||
});
|
||||
show();
|
||||
Common.getAttribute(['toolbar', 'chat-drawer'], function (err, val) {
|
||||
if (val === false || ($(window).height() < 800 && $(window).width() < 800)) {
|
||||
return void hide();
|
||||
}
|
||||
show();
|
||||
});
|
||||
|
||||
initChat(toolbar, config);
|
||||
return $container;
|
||||
};
|
||||
|
||||
var createShare = function (toolbar, config) {
|
||||
if (!config.metadataMgr) {
|
||||
throw new Error("You must provide a `metadataMgr` to display the userlist");
|
||||
@@ -997,6 +1078,7 @@ define([
|
||||
// Create the subelements
|
||||
var tb = {};
|
||||
tb['userlist'] = createUserList;
|
||||
tb['chat'] = createChat;
|
||||
tb['share'] = createShare;
|
||||
tb['fileshare'] = createFileShare;
|
||||
tb['title'] = createTitle;
|
||||
|
||||
Reference in New Issue
Block a user