Merge branch 'staging' into oo2
This commit is contained in:
@@ -19,17 +19,16 @@
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
width: 50%;
|
||||
min-width: 20%;
|
||||
max-width: 80%;
|
||||
resize: horizontal;
|
||||
overflow: hidden;
|
||||
width: 50%;
|
||||
&.cp-app-code-fullpage {
|
||||
max-width: 100%;
|
||||
resize: none;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
}
|
||||
.CodeMirror {
|
||||
flex: 1;
|
||||
@@ -51,9 +50,13 @@
|
||||
#cp-app-code-container { display: none; }
|
||||
#cp-app-code-preview { border: 0; }
|
||||
}
|
||||
&.cp-chat-visible {
|
||||
#cp-app-code-container {
|
||||
width: 35%;
|
||||
}
|
||||
}
|
||||
}
|
||||
#cp-app-code-preview {
|
||||
flex: 1;
|
||||
padding: 5px 20px;
|
||||
overflow: auto;
|
||||
display: inline-block;
|
||||
@@ -63,6 +66,7 @@
|
||||
font-family: Calibri,Ubuntu,sans-serif;
|
||||
word-wrap: break-word;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
media-tag {
|
||||
* {
|
||||
max-width:100%;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import (reference) '../../customize/src/less2/include/avatar.less';
|
||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
@import (reference) '../../customize/src/less2/include/messenger.less';
|
||||
|
||||
// body
|
||||
&.cp-app-contacts {
|
||||
@@ -9,238 +9,14 @@
|
||||
@color: @colortheme_friends-color
|
||||
);
|
||||
|
||||
@keyframes example {
|
||||
0% {
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
50% {
|
||||
background: rgba(0,0,0,0.3);
|
||||
}
|
||||
100% {
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
}
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
background-color: red !important;
|
||||
@button-border: 2px;
|
||||
@bg-color: @colortheme_friends-bg;
|
||||
@color: @colortheme_friends-color;
|
||||
|
||||
#cp-app-contacts-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 0;
|
||||
&.ready {
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
}
|
||||
|
||||
#cp-toolbar {
|
||||
display: flex; // We need this to remove a 3px border at the bottom of the toolbar
|
||||
}
|
||||
|
||||
#cp-app-contacts-friendlist {
|
||||
width: 350px;
|
||||
height: 100%;
|
||||
background-color: lighten(@bg-color, 10%);
|
||||
overflow-y: auto;
|
||||
.cp-app-contacts-friend {
|
||||
background: rgba(0,0,0,0.1);
|
||||
padding: 5px;
|
||||
margin: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
.cp-app-contacts-right-col {
|
||||
margin-left: 5px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
&:hover {
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
}
|
||||
&.cp-app-contacts-notify {
|
||||
animation: example 2s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-remove {
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
&:hover {
|
||||
color: darken(@color, 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#cp-app-contacts-friendlist .cp-app-contacts-friend, #cp-app-contacts-messaging .cp-avatar {
|
||||
.avatar_main(30px);
|
||||
&.cp-avatar {
|
||||
display: flex;
|
||||
}
|
||||
cursor: pointer;
|
||||
color: @color;
|
||||
media-tag {
|
||||
img {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
media-tag, .cp-avatar-default {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.cp-app-contacts-status {
|
||||
width: 5px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
opacity: 0.7;
|
||||
background-color: #777;
|
||||
&.cp-app-contacts-online {
|
||||
background-color: green;
|
||||
}
|
||||
&.cp-app-contacts-offline {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder (@color: #bbb) {
|
||||
&::-webkit-input-placeholder { /* WebKit, Blink, Edge */
|
||||
color: @color;
|
||||
}
|
||||
&:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
|
||||
color: @color;
|
||||
opacity: 1;
|
||||
}
|
||||
&::-moz-placeholder { /* Mozilla Firefox 19+ */
|
||||
color: @color;
|
||||
opacity: 1;
|
||||
}
|
||||
&:-ms-input-placeholder { /* Internet Explorer 10-11 */
|
||||
color: @color;
|
||||
}
|
||||
&::-ms-input-placeholder { /* Microsoft Edge */
|
||||
color: @color;
|
||||
}
|
||||
}
|
||||
|
||||
#cp-app-contacts-messaging {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
background-color: lighten(@bg-color, 20%);
|
||||
min-width: 0;
|
||||
|
||||
.cp-app-contacts-info {
|
||||
padding: 20px;
|
||||
}
|
||||
.cp-app-contacts-header {
|
||||
background-color: lighten(@bg-color, 15%);
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
|
||||
.hover () {
|
||||
height: 100%;
|
||||
line-height: 30px;
|
||||
padding: 10px;
|
||||
&:hover {
|
||||
background-color: rgba(50,50,50,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.cp-avatar,
|
||||
.cp-app-contacts-right-col {
|
||||
flex:1 1 auto;
|
||||
}
|
||||
.cp-app-contacts-remove-history {
|
||||
.hover;
|
||||
}
|
||||
.cp-avatar {
|
||||
margin: 10px;
|
||||
}
|
||||
.cp-app-contacts-more-history {
|
||||
//display: none;
|
||||
.hover;
|
||||
&.cp-app-contacts-faded {
|
||||
color: darken(@bg-color, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-chat {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
.cp-app-contacts-messages {
|
||||
padding: 0 20px;
|
||||
margin: 10px 0;
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
.cp-app-contacts-message {
|
||||
& > div {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.cp-app-contacts-content {
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
&> * {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-date {
|
||||
display: none;
|
||||
font-style: italic;
|
||||
}
|
||||
.cp-app-contacts-sender {
|
||||
margin-top: 10px;
|
||||
font-weight: bold;
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-input {
|
||||
background-color: lighten(@bg-color, 15%);
|
||||
height: auto;
|
||||
min-height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 75px;
|
||||
textarea {
|
||||
margin: 5px 0;
|
||||
padding: 0 10px;
|
||||
border: none;
|
||||
height: 50px;
|
||||
flex: 1;
|
||||
background-color: darken(@bg-color, 10%);
|
||||
color: @color;
|
||||
resize: none;
|
||||
line-height: 50px;
|
||||
overflow-y: auto;
|
||||
.placeholder(#bbb);
|
||||
&[disabled=true] {
|
||||
.placeholder(#999);
|
||||
}
|
||||
&:placeholder-shown { line-height: 50px; }
|
||||
}
|
||||
button {
|
||||
height: 50px;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
background-color: darken(@bg-color, 15%);
|
||||
&:hover {
|
||||
background-color: darken(@bg-color, 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.messenger_main();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,22 +41,7 @@ define([
|
||||
|
||||
document.body.appendChild(toolbarElement);
|
||||
|
||||
var messaging = h('div#cp-app-contacts-messaging', [
|
||||
h('div.cp-app-contacts-info', [
|
||||
h('h2', Messages.contacts_info1),
|
||||
h('ul', [
|
||||
h('li', Messages.contacts_info2),
|
||||
h('li', Messages.contacts_info3),
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
var friendList = h('div#cp-app-contacts-friendlist');
|
||||
|
||||
var appElement = h('div#cp-app-contacts-container', [
|
||||
friendList,
|
||||
messaging,
|
||||
]);
|
||||
var appElement = h('div#cp-app-contacts-container');
|
||||
|
||||
document.body.appendChild(appElement);
|
||||
|
||||
@@ -73,7 +58,7 @@ define([
|
||||
|
||||
var messenger = Messenger.create(sFrameChan);
|
||||
|
||||
MessengerUI.create(messenger, $(friendList), $(messaging), common);
|
||||
MessengerUI.create(messenger, $(appElement), common);
|
||||
|
||||
UI.removeLoadingScreen();
|
||||
|
||||
|
||||
@@ -3,59 +3,141 @@ define([
|
||||
'/customize/messages.js',
|
||||
'/common/common-util.js',
|
||||
'/common/common-interface.js',
|
||||
'/common/common-notifier.js',
|
||||
'/common/hyperscript.js',
|
||||
'/bower_components/marked/marked.min.js',
|
||||
'/common/media-tag.js',
|
||||
], function ($, Messages, Util, UI, Notifier, h, Marked, MediaTag) {
|
||||
], function ($, Messages, Util, UI, h, Marked, MediaTag) {
|
||||
'use strict';
|
||||
|
||||
var debug = console.log;
|
||||
debug = function () {};
|
||||
|
||||
var MessengerUI = {};
|
||||
|
||||
var dataQuery = function (curvePublic) {
|
||||
return '[data-key="' + curvePublic + '"]';
|
||||
var dataQuery = function (id) {
|
||||
return '[data-key="' + id + '"]';
|
||||
};
|
||||
var userQuery = function (curve) {
|
||||
return '[data-user="' + curve + '"]';
|
||||
};
|
||||
|
||||
var initChannel = function (state, curvePublic, info) {
|
||||
console.log('initializing channel for [%s]', curvePublic);
|
||||
state.channels[curvePublic] = {
|
||||
messages: [],
|
||||
HEAD: info.lastKnownHash,
|
||||
TAIL: null,
|
||||
var initChannel = function (state, info) {
|
||||
console.log('initializing channel for [%s]', info.id);
|
||||
var h, t;
|
||||
if (Array.isArray(info.messages) && info.messages.length) {
|
||||
h = info.messages[info.messages.length -1].sig;
|
||||
t = info.messages[0].sig;
|
||||
}
|
||||
state.channels[info.id] = {
|
||||
messages: info.messages || [],
|
||||
name: info.name,
|
||||
isFriendChat: info.isFriendChat,
|
||||
needMoreHistory: !info.isPadChat,
|
||||
isPadChat: info.isPadChat,
|
||||
curvePublic: info.curvePublic,
|
||||
HEAD: h || info.lastKnownHash,
|
||||
TAIL: t || null,
|
||||
};
|
||||
};
|
||||
|
||||
MessengerUI.create = function (messenger, $userlist, $messages, common) {
|
||||
var origin = common.getMetadataMgr().getPrivateData().origin;
|
||||
MessengerUI.create = function (messenger, $container, common, toolbar) {
|
||||
var sframeChan = common.getSframeChannel();
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var origin = metadataMgr.getPrivateData().origin;
|
||||
|
||||
var isApp = typeof(toolbar) !== "undefined";
|
||||
|
||||
$container.addClass('cp-app-contacts-initializing');
|
||||
|
||||
var messaging = h('div#cp-app-contacts-messaging', [
|
||||
h('span.fa.fa-spinner.fa-pulse.fa-4x.fa-fw.cp-app-contacts-spinner'),
|
||||
h('div.cp-app-contacts-info', [
|
||||
h('h2', Messages.contacts_info1),
|
||||
h('ul', [
|
||||
h('li', Messages.contacts_info2),
|
||||
h('li', Messages.contacts_info3),
|
||||
h('li', Messages.contacts_info4),
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
var friendList = h('div#cp-app-contacts-friendlist', [
|
||||
h('span.fa.fa-spinner.fa-pulse.fa-4x.fa-fw.cp-app-contacts-spinner'),
|
||||
h('div.cp-app-contacts-padchat.cp-app-contacts-category', [
|
||||
h('div.cp-app-contacts-category-content')
|
||||
]),
|
||||
h('div.cp-app-contacts-friends.cp-app-contacts-category', [
|
||||
h('div.cp-app-contacts-category-content'),
|
||||
h('h2.cp-app-contacts-category-title', Messages.contacts_friends),
|
||||
]),
|
||||
h('div.cp-app-contacts-rooms.cp-app-contacts-category', [
|
||||
h('div.cp-app-contacts-category-content'),
|
||||
h('h2.cp-app-contacts-category-title', Messages.contacts_rooms),
|
||||
]),
|
||||
]);
|
||||
|
||||
var $userlist = $(friendList).appendTo($container);
|
||||
var $messages = $(messaging).appendTo($container);
|
||||
|
||||
var state = window.state = {
|
||||
active: '',
|
||||
channels: {}
|
||||
};
|
||||
|
||||
state.channels = {};
|
||||
var displayNames = state.displayNames = {};
|
||||
var contactsData = state.contactsData = {};
|
||||
|
||||
var avatars = state.avatars = {};
|
||||
var setActive = function (curvePublic) {
|
||||
state.active = curvePublic;
|
||||
var setActive = function (id) {
|
||||
state.active = id;
|
||||
};
|
||||
var isActive = function (curvePublic) {
|
||||
return curvePublic === state.active;
|
||||
var isActive = function (id) {
|
||||
return id === state.active;
|
||||
};
|
||||
|
||||
var find = {};
|
||||
find.inList = function (curvePublic) {
|
||||
return $userlist.find(dataQuery(curvePublic));
|
||||
find.inList = function (id) {
|
||||
return $userlist.find(dataQuery(id));
|
||||
};
|
||||
|
||||
var notify = function (curvePublic) {
|
||||
find.inList(curvePublic).addClass('cp-app-contacts-notify');
|
||||
};
|
||||
var unnotify = function (curvePublic) {
|
||||
find.inList(curvePublic).removeClass('cp-app-contacts-notify');
|
||||
var notifyToolbar = function () {
|
||||
if (!toolbar || !toolbar['chat']) { return; }
|
||||
if (toolbar['chat'].find('button').hasClass('cp-toolbar-button-active')) { return; }
|
||||
toolbar['chat'].find('button').addClass('cp-toolbar-notification');
|
||||
};
|
||||
|
||||
var m = function (md) {
|
||||
var notify = function (id) {
|
||||
find.inList(id).addClass('cp-app-contacts-notify');
|
||||
};
|
||||
var unnotify = function (id) {
|
||||
find.inList(id).removeClass('cp-app-contacts-notify');
|
||||
};
|
||||
|
||||
var onResize = function () {
|
||||
// Don't update the width if we are in the contacts app
|
||||
if (!isApp) { return; }
|
||||
var w = $userlist[0].offsetWidth - $userlist[0].clientWidth;
|
||||
$userlist.css('width', (68 + w)+'px');
|
||||
};
|
||||
var reorderRooms = function () {
|
||||
var channels = Object.keys(state.channels).sort(function (a, b) {
|
||||
var m1 = state.channels[a].messages.slice(-1)[0];
|
||||
var m2 = state.channels[b].messages.slice(-1)[0];
|
||||
if (!m2) { return !m1 ? 0 : 1; }
|
||||
if (!m1) { return -1; }
|
||||
return m1.time - m2.time;
|
||||
});
|
||||
|
||||
channels.forEach(function (c, i) {
|
||||
$userlist.find(dataQuery(c)).css('order', i);
|
||||
});
|
||||
|
||||
// Make sure the width is correct even if there is a scrollbar
|
||||
onResize();
|
||||
};
|
||||
|
||||
$(window).on('resize', onResize);
|
||||
|
||||
var m = function (md, hour) {
|
||||
var d = h('div.cp-app-contacts-content');
|
||||
try {
|
||||
d.innerHTML = Marked(md || '');
|
||||
@@ -73,6 +155,9 @@ define([
|
||||
|
||||
// activate media-tags
|
||||
$d.find('media-tag').each(function (i, e) { MediaTag(e); });
|
||||
|
||||
var time = h('div.cp-app-contacts-time', hour);
|
||||
$d.append(time);
|
||||
} catch (e) {
|
||||
console.error(md);
|
||||
console.error(e);
|
||||
@@ -82,25 +167,35 @@ define([
|
||||
|
||||
var markup = {};
|
||||
markup.message = function (msg) {
|
||||
if (msg.type !== 'MSG') { return; }
|
||||
var curvePublic = msg.author;
|
||||
var name = displayNames[msg.author];
|
||||
var name = typeof msg.name !== "undefined" ? (msg.name || Messages.anonymous)
|
||||
: contactsData[msg.author].displayName;
|
||||
var d = msg.time ? new Date(msg.time) : undefined;
|
||||
var day = d ? d.toLocaleDateString() : '';
|
||||
var hour = d ? d.toLocaleTimeString() : '';
|
||||
return h('div.cp-app-contacts-message', {
|
||||
title: msg.time? new Date(msg.time).toLocaleString(): '?',
|
||||
'data-key': curvePublic,
|
||||
//title: time || '?',
|
||||
'data-user': curvePublic,
|
||||
'data-day': day
|
||||
}, [
|
||||
name? h('div.cp-app-contacts-sender', name): undefined,
|
||||
m(msg.text),
|
||||
name? h('div.cp-app-contacts-sender', [
|
||||
h('span.cp-app-contacts-sender-name', name),
|
||||
h('span.cp-app-contacts-sender-time', day)
|
||||
]): undefined,
|
||||
m(msg.text, hour),
|
||||
]);
|
||||
};
|
||||
|
||||
var getChat = function (curvePublic) {
|
||||
return $messages.find(dataQuery(curvePublic));
|
||||
var getChat = function (id) {
|
||||
return $messages.find(dataQuery(id));
|
||||
};
|
||||
|
||||
var normalizeLabels = function ($messagebox) {
|
||||
$messagebox.find('div.cp-app-contacts-message').toArray().reduce(function (a, b) {
|
||||
var $b = $(b);
|
||||
if ($(a).data('key') === $b.data('key')) {
|
||||
if ($(a).data('user') === $b.data('user') &&
|
||||
$(a).data('day') === $b.data('day')) {
|
||||
$b.find('.cp-app-contacts-sender').hide();
|
||||
return a;
|
||||
}
|
||||
@@ -108,29 +203,31 @@ define([
|
||||
}, []);
|
||||
};
|
||||
|
||||
markup.chatbox = function (curvePublic, data) {
|
||||
markup.chatbox = function (id, data, curvePublic) {
|
||||
var moreHistory = h('span.cp-app-contacts-more-history.fa.fa-history', {
|
||||
title: Messages.contacts_fetchHistory,
|
||||
});
|
||||
var displayName = data.displayName;
|
||||
|
||||
var chan = state.channels[id];
|
||||
var displayName = chan.name;
|
||||
|
||||
var fetching = false;
|
||||
var $moreHistory = $(moreHistory).click(function () {
|
||||
if (fetching) { return; }
|
||||
|
||||
// get oldest known message...
|
||||
var channel = state.channels[curvePublic];
|
||||
var channel = state.channels[id];
|
||||
|
||||
if (channel.exhausted) {
|
||||
return void $moreHistory.addClass('cp-app-contacts-faded');
|
||||
}
|
||||
|
||||
console.log('getting history');
|
||||
debug('getting history');
|
||||
var sig = channel.TAIL || channel.HEAD;
|
||||
|
||||
fetching = true;
|
||||
var $messagebox = $(getChat(curvePublic)).find('.cp-app-contacts-messages');
|
||||
messenger.getMoreHistory(curvePublic, sig, 10, function (e, history) {
|
||||
var $messagebox = $(getChat(id)).find('.cp-app-contacts-messages');
|
||||
messenger.getMoreHistory(id, sig, 10, function (e, history) {
|
||||
fetching = false;
|
||||
if (e) { return void console.error(e); }
|
||||
|
||||
@@ -139,13 +236,16 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
history.forEach(function (msg) {
|
||||
history.forEach(function (msg, i) {
|
||||
if (channel.exhausted) { return; }
|
||||
if (msg.sig) {
|
||||
if (i === 0 && history.length > 1 && msg.sig === channel.TAIL) {
|
||||
// First message is usually the lastKnownHash, ignore it
|
||||
return;
|
||||
}
|
||||
if (msg.sig === channel.TAIL) {
|
||||
console.error('No more messages to fetch');
|
||||
channel.exhausted = true;
|
||||
console.log(channel);
|
||||
return void $moreHistory.addClass('cp-app-contacts-faded');
|
||||
} else {
|
||||
channel.TAIL = msg.sig;
|
||||
@@ -156,9 +256,11 @@ define([
|
||||
if (msg.type !== 'MSG') { return; }
|
||||
|
||||
// FIXME Schlameil the painter (performance does not scale well)
|
||||
// XXX trust the server?
|
||||
/*
|
||||
if (channel.messages.some(function (old) {
|
||||
return msg.sig === old.sig;
|
||||
})) { return; }
|
||||
})) { return; }*/
|
||||
|
||||
channel.messages.unshift(msg);
|
||||
var el_message = markup.message(msg);
|
||||
@@ -176,22 +278,43 @@ define([
|
||||
UI.confirm(Messages.contacts_confirmRemoveHistory, function (yes) {
|
||||
if (!yes) { return; }
|
||||
|
||||
messenger.clearOwnedChannel(data.channel, function (e) {
|
||||
messenger.clearOwnedChannel(id, function (e) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
UI.alert(Messages.contacts_removeHistoryServerError);
|
||||
return;
|
||||
}
|
||||
// XXX clear the UI?
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var avatar = h('div.cp-avatar');
|
||||
var header = h('div.cp-app-contacts-header', [
|
||||
avatar,
|
||||
moreHistory,
|
||||
removeHistory,
|
||||
]);
|
||||
|
||||
var headerContent = [avatar, moreHistory, data.isFriendCHat ? removeHistory : undefined];
|
||||
if (isApp) {
|
||||
headerContent = [
|
||||
h('div.cp-app-contacts-header-title', Messages.contacts_padTitle),
|
||||
moreHistory
|
||||
];
|
||||
}
|
||||
var header = h('div.cp-app-contacts-header', headerContent);
|
||||
|
||||
var priv = metadataMgr.getPrivateData();
|
||||
|
||||
var closeTips = h('span.fa.fa-window-close.cp-app-contacts-tips-close');
|
||||
var tips;
|
||||
if (isApp && Util.find(priv.settings, ['general', 'hidetips', 'chat']) !== true) {
|
||||
tips = h('div.cp-app-contacts-tips', [
|
||||
closeTips,
|
||||
Messages.contacts_warning
|
||||
]);
|
||||
}
|
||||
$(closeTips).click(function () {
|
||||
$(tips).hide();
|
||||
common.setAttribute(['general', 'hidetips', 'chat'], true);
|
||||
});
|
||||
|
||||
var messages = h('div.cp-app-contacts-messages');
|
||||
var input = h('textarea', {
|
||||
placeholder: Messages.contacts_typeHere
|
||||
@@ -205,12 +328,13 @@ define([
|
||||
]);
|
||||
|
||||
var $avatar = $(avatar);
|
||||
if (data.avatar && avatars[data.avatar]) {
|
||||
$avatar.append(avatars[data.avatar]).append(rightCol);
|
||||
var friend = contactsData[curvePublic] || {};
|
||||
if (friend.avatar && avatars[friend.avatar]) {
|
||||
$avatar.append(avatars[friend.avatar]).append(rightCol);
|
||||
} else {
|
||||
common.displayAvatar($avatar, data.avatar, data.displayName, function ($img) {
|
||||
if (data.avatar && $img) {
|
||||
avatars[data.avatar] = $img[0].outerHTML;
|
||||
common.displayAvatar($avatar, friend.avatar, displayName, function ($img) {
|
||||
if (friend.avatar && $img) {
|
||||
avatars[friend.avatar] = $img[0].outerHTML;
|
||||
}
|
||||
$(rightCol).insertAfter($avatar);
|
||||
});
|
||||
@@ -221,14 +345,14 @@ define([
|
||||
if (typeof(content) !== 'string' || !content.trim()) { return; }
|
||||
if (sending) { return false; }
|
||||
sending = true;
|
||||
messenger.sendMessage(curvePublic, content, function (e) {
|
||||
messenger.sendMessage(id, content, function (e) {
|
||||
if (e) {
|
||||
// failed to send
|
||||
return void console.error('failed to send');
|
||||
}
|
||||
input.value = '';
|
||||
sending = false;
|
||||
console.log('sent successfully');
|
||||
debug('sent successfully');
|
||||
var $messagebox = $(messages);
|
||||
|
||||
var height = $messagebox[0].scrollHeight;
|
||||
@@ -268,9 +392,11 @@ define([
|
||||
$(sendButton).click(function () { send(input.value); });
|
||||
|
||||
return h('div.cp-app-contacts-chat', {
|
||||
'data-key': curvePublic,
|
||||
'data-key': id,
|
||||
'data-user': data.isFriendChat && curvePublic
|
||||
}, [
|
||||
header,
|
||||
tips,
|
||||
messages,
|
||||
h('div.cp-app-contacts-input', [
|
||||
input,
|
||||
@@ -283,16 +409,20 @@ define([
|
||||
$messages.find('.cp-app-contacts-info').hide();
|
||||
};
|
||||
|
||||
var updateStatus = function (curvePublic) {
|
||||
var $status = find.inList(curvePublic).find('.cp-app-contacts-status');
|
||||
// FIXME this stopped working :(
|
||||
messenger.getStatus(curvePublic, function (e, online) {
|
||||
var showInfo = function () {
|
||||
$messages.find('.cp-app-contacts-info').show();
|
||||
};
|
||||
|
||||
var updateStatus = function (id) {
|
||||
if (!state.channels[id]) { return; }
|
||||
var $status = find.inList(id).find('.cp-app-contacts-status');
|
||||
messenger.getStatus(id, function (e, online) {
|
||||
// if error maybe you shouldn't display this friend...
|
||||
if (e) {
|
||||
find.inList(curvePublic).hide();
|
||||
getChat(curvePublic).hide();
|
||||
find.inList(id).hide();
|
||||
getChat(id).hide();
|
||||
|
||||
return void console.error(curvePublic, e);
|
||||
return void console.error(id, e);
|
||||
}
|
||||
if (online) {
|
||||
return void $status
|
||||
@@ -302,72 +432,81 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
var display = function (curvePublic) {
|
||||
var channel = state.channels[curvePublic];
|
||||
var display = function (chanId) {
|
||||
var channel = state.channels[chanId];
|
||||
var lastMsg = channel.messages.slice(-1)[0];
|
||||
|
||||
if (lastMsg) {
|
||||
channel.HEAD = lastMsg.sig;
|
||||
messenger.setChannelHead(curvePublic, channel.HEAD, function (e) {
|
||||
messenger.setChannelHead(chanId, channel.HEAD, function (e) {
|
||||
if (e) { console.error(e); }
|
||||
});
|
||||
}
|
||||
|
||||
setActive(curvePublic);
|
||||
unnotify(curvePublic);
|
||||
var $chat = getChat(curvePublic);
|
||||
setActive(chanId);
|
||||
unnotify(chanId);
|
||||
var $chat = getChat(chanId);
|
||||
hideInfo();
|
||||
$messages.find('div.cp-app-contacts-chat[data-key]').hide();
|
||||
if ($chat.length) {
|
||||
var $chat_messages = $chat.find('div.cp-app-contacts-message');
|
||||
if (!$chat_messages.length) {
|
||||
if ($chat_messages.length < 10) { //|| channel.needMoreHistory) { XXX
|
||||
delete channel.needMoreHistory;
|
||||
var $more = $chat.find('.cp-app-contacts-more-history');
|
||||
$more.click();
|
||||
}
|
||||
return void $chat.show();
|
||||
$chat.show();
|
||||
return;
|
||||
} else {
|
||||
console.error("Chat is missing... Please reload the page and try again.");
|
||||
}
|
||||
messenger.getFriendInfo(curvePublic, function (e, info) {
|
||||
if (e) { return void console.error(e); } // FIXME
|
||||
var chatbox = markup.chatbox(curvePublic, info);
|
||||
$messages.append(chatbox);
|
||||
});
|
||||
};
|
||||
|
||||
var removeFriend = function (curvePublic) {
|
||||
messenger.removeFriend(curvePublic, function (e /*, removed */) {
|
||||
if (e) { return void console.error(e); }
|
||||
find.inList(curvePublic).remove();
|
||||
//console.log(removed);
|
||||
});
|
||||
};
|
||||
|
||||
markup.friend = function (data) {
|
||||
var curvePublic = data.curvePublic;
|
||||
var friend = h('div.cp-app-contacts-friend.cp-avatar', {
|
||||
'data-key': curvePublic,
|
||||
markup.room = function (id, room, userlist) {
|
||||
var roomEl = h('div.cp-app-contacts-friend.cp-avatar', {
|
||||
'data-key': id,
|
||||
'data-user': room.isFriendChat ? userlist[0].curvePublic : '',
|
||||
title: room.name
|
||||
});
|
||||
|
||||
var remove = h('span.cp-app-contacts-remove.fa.fa-user-times', {
|
||||
title: Messages.contacts_remove
|
||||
});
|
||||
var status = h('span.cp-app-contacts-status');
|
||||
var leaveRoom = h('span.cp-app-contacts-remove.fa.fa-sign-out', {
|
||||
title: Messages.contacts_leaveRoom
|
||||
});
|
||||
|
||||
var status = h('span.cp-app-contacts-status', {
|
||||
title: Messages.contacts_online
|
||||
});
|
||||
var rightCol = h('span.cp-app-contacts-right-col', [
|
||||
h('span.cp-app-contacts-name', [data.displayName]),
|
||||
remove,
|
||||
h('span.cp-app-contacts-name', [room.name]),
|
||||
room.isFriendChat ? remove :
|
||||
room.isPadChat ? undefined : leaveRoom,
|
||||
]);
|
||||
|
||||
var $friend = $(friend)
|
||||
.click(function () {
|
||||
display(curvePublic);
|
||||
})
|
||||
.dblclick(function () {
|
||||
if (data.profile) { window.open(origin + '/profile/#' + data.profile); }
|
||||
var friendData = room.isFriendChat ? userlist[0] : {};
|
||||
|
||||
var $room = $(roomEl).click(function () {
|
||||
display(id);
|
||||
}).dblclick(function () {
|
||||
if (friendData.profile) { window.open(origin + '/profile/#' + friendData.profile); }
|
||||
});
|
||||
|
||||
$(remove).click(function (e) {
|
||||
e.stopPropagation();
|
||||
var channel = state.channels[id];
|
||||
if (!channel.isFriendChat) { return; }
|
||||
var curvePublic = channel.curvePublic;
|
||||
var friend = contactsData[curvePublic] || friendData;
|
||||
UI.confirm(Messages._getKey('contacts_confirmRemove', [
|
||||
Util.fixHTML(data.displayName)
|
||||
Util.fixHTML(friend.name)
|
||||
]), function (yes) {
|
||||
if (!yes) { return; }
|
||||
removeFriend(curvePublic, function (e) {
|
||||
@@ -379,41 +518,42 @@ define([
|
||||
}, undefined, true);
|
||||
});
|
||||
|
||||
if (data.avatar && avatars[data.avatar]) {
|
||||
$friend.append(avatars[data.avatar]);
|
||||
$friend.append(rightCol);
|
||||
if (friendData.avatar && avatars[friendData.avatar]) {
|
||||
$room.append(avatars[friendData.avatar]);
|
||||
$room.append(rightCol);
|
||||
} else {
|
||||
common.displayAvatar($friend, data.avatar, data.displayName, function ($img) {
|
||||
if (data.avatar && $img) {
|
||||
avatars[data.avatar] = $img[0].outerHTML;
|
||||
common.displayAvatar($room, friendData.avatar, room.name, function ($img) {
|
||||
if (friendData.avatar && $img) {
|
||||
avatars[friendData.avatar] = $img[0].outerHTML;
|
||||
}
|
||||
$friend.append(rightCol);
|
||||
$room.append(rightCol);
|
||||
});
|
||||
}
|
||||
$friend.append(status);
|
||||
return $friend;
|
||||
$room.append(status);
|
||||
return $room;
|
||||
};
|
||||
|
||||
var isBottomedOut = function ($elem) {
|
||||
return ($elem[0].scrollHeight - $elem.scrollTop() === $elem.outerHeight());
|
||||
};
|
||||
|
||||
var initializing = true;
|
||||
messenger.on('message', function (message) {
|
||||
if (!initializing) { Notifier.notify(); }
|
||||
var curvePublic = message.curve;
|
||||
var chanId = message.channel;
|
||||
var channel = state.channels[chanId];
|
||||
if (!channel) { return; }
|
||||
|
||||
var name = displayNames[curvePublic];
|
||||
var chat = getChat(curvePublic, name);
|
||||
var chat = getChat(chanId);
|
||||
|
||||
console.log(message);
|
||||
debug(message);
|
||||
|
||||
var el_message = markup.message(message);
|
||||
|
||||
state.channels[curvePublic].messages.push(message);
|
||||
common.notify();
|
||||
notifyToolbar();
|
||||
|
||||
channel.messages.push(message);
|
||||
|
||||
var $chat = $(chat);
|
||||
|
||||
if (!$chat.length) {
|
||||
console.error("Got a message but the chat isn't open");
|
||||
}
|
||||
@@ -427,111 +567,266 @@ define([
|
||||
$messagebox.scrollTop($messagebox.outerHeight());
|
||||
}
|
||||
normalizeLabels($messagebox);
|
||||
reorderRooms();
|
||||
|
||||
var channel = state.channels[curvePublic];
|
||||
if (!channel) {
|
||||
console.error('expected channel [%s] to be open', curvePublic);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isActive(curvePublic)) {
|
||||
if (isActive(chanId)) {
|
||||
channel.HEAD = message.sig;
|
||||
messenger.setChannelHead(curvePublic, message.sig, function (e) {
|
||||
messenger.setChannelHead(chanId, message.sig, function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
});
|
||||
return;
|
||||
}
|
||||
var lastMsg = channel.messages.slice(-1)[0];
|
||||
if (lastMsg.sig !== channel.HEAD) {
|
||||
return void notify(curvePublic);
|
||||
return void notify(chanId);
|
||||
}
|
||||
unnotify(curvePublic);
|
||||
unnotify(chanId);
|
||||
});
|
||||
|
||||
messenger.on('join', function (curvePublic, channel) {
|
||||
channel = channel;
|
||||
updateStatus(curvePublic);
|
||||
messenger.on('join', function (data, channel) {
|
||||
if (data.curvePublic) {
|
||||
contactsData[data.curvePublic] = data;
|
||||
}
|
||||
updateStatus(channel);
|
||||
// TODO room refresh online userlist
|
||||
});
|
||||
messenger.on('leave', function (curvePublic, channel) {
|
||||
channel = channel;
|
||||
updateStatus(curvePublic);
|
||||
messenger.on('leave', function (data, channel) {
|
||||
if (contactsData[data.curvePublic]) {
|
||||
delete contactsData[data.curvePublic];
|
||||
}
|
||||
updateStatus(channel);
|
||||
// TODO room refresh online userlist
|
||||
});
|
||||
|
||||
// change in your friend list
|
||||
messenger.on('update', function (info, curvePublic) {
|
||||
var name = displayNames[curvePublic] = info.displayName;
|
||||
messenger.on('update', function (info, types, channel) {
|
||||
if (!info || !info.curvePublic) { return; }
|
||||
// Make sure we don't store useless data (friends data in pad chat or the other way)
|
||||
if (channel && !state.channels[channel]) { return; }
|
||||
|
||||
// update label in friend list
|
||||
find.inList(curvePublic).find('.cp-app-contacts-name').text(name);
|
||||
var curvePublic = info.curvePublic;
|
||||
contactsData[curvePublic] = info;
|
||||
|
||||
// update title bar and messages
|
||||
$messages.find(dataQuery(curvePublic) + ' .cp-app-contacts-header ' +
|
||||
'.cp-app-contacts-name, div.cp-app-contacts-message'+
|
||||
dataQuery(curvePublic) + ' div.cp-app-contacts-sender').text(name).text(name);
|
||||
if (types.indexOf('displayName') !== -1) {
|
||||
var name = info.displayName;
|
||||
|
||||
// update label in friend list
|
||||
$userlist.find(userQuery(curvePublic)).find('.cp-app-contacts-name').text(name);
|
||||
$userlist.find(userQuery(curvePublic)).attr('title', name);
|
||||
|
||||
// update title bar and messages
|
||||
$messages.find(userQuery(curvePublic) + ' .cp-app-contacts-header ' +
|
||||
'.cp-app-contacts-name, div.cp-app-contacts-message'+
|
||||
userQuery(curvePublic) + ' div.cp-app-contacts-sender').text(name);
|
||||
|
||||
// TODO room
|
||||
// Update name in room userlist
|
||||
}
|
||||
|
||||
if (types.indexOf('profile') !== -1) {
|
||||
// update dblclick event in friend list
|
||||
$userlist.find(userQuery(curvePublic)).off('dblclick').dblclick(function () {
|
||||
if (info.profile) { window.open(origin + '/profile/#' + info.profile); }
|
||||
});
|
||||
}
|
||||
|
||||
if (types.indexOf('avatar') !== -1) {
|
||||
var $mAvatar = $messages
|
||||
.find(userQuery(curvePublic) +' .cp-app-contacts-header .cp-avatar');
|
||||
var $lAvatar = $userlist.find(userQuery(curvePublic));
|
||||
$lAvatar.find('.cp-avatar-default, media-tag').remove();
|
||||
|
||||
var $div = $('<div>');
|
||||
common.displayAvatar($div, info.avatar, info.displayName, function ($img) {
|
||||
if (info.avatar && $img) {
|
||||
avatars[info.avatar] = $img[0].outerHTML;
|
||||
}
|
||||
$mAvatar.html($div.html());
|
||||
$lAvatar.find('.cp-app-contacts-right-col').before($div.html());
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var connectToFriend = function (curvePublic, cb) {
|
||||
messenger.getFriendInfo(curvePublic, function (e, info) {
|
||||
if (e) { return void console.error(e); }
|
||||
var name = displayNames[curvePublic] = info.displayName;
|
||||
initChannel(state, curvePublic, info);
|
||||
var execCommand = function (cmd, data, cb) {
|
||||
sframeChan.query('Q_CHAT_COMMAND', {cmd: cmd, data: data}, function (err, obj) {
|
||||
if (err || (obj && obj.error)) { return void cb(err || (obj && obj.error)); }
|
||||
cb(void 0, obj);
|
||||
});
|
||||
};
|
||||
|
||||
var chatbox = markup.chatbox(curvePublic, info);
|
||||
var initializeRoom = function (room) {
|
||||
var id = room.id;
|
||||
initChannel(state, room);
|
||||
|
||||
execCommand('GET_USERLIST', {id: id}, function (e, list) {
|
||||
if (e || list.error) { return void console.error(e || list.error); }
|
||||
if (!room.isPadChat && (!Array.isArray(list) || !list.length)) {
|
||||
return void console.error("Empty room!");
|
||||
}
|
||||
debug('userlist: ' + JSON.stringify(list), id);
|
||||
|
||||
var friend = {};
|
||||
|
||||
if (room.isFriendChat) {
|
||||
// This is a friend, the userlist is only one user.
|
||||
friend = list[0];
|
||||
contactsData[friend.curvePublic] = friend;
|
||||
}
|
||||
|
||||
var chatbox = markup.chatbox(id, room, friend.curvePublic);
|
||||
$(chatbox).hide();
|
||||
$messages.append(chatbox);
|
||||
|
||||
var friend = markup.friend(info, name);
|
||||
$userlist.append(friend);
|
||||
messenger.openFriendChannel(curvePublic, function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
cb();
|
||||
updateStatus(curvePublic);
|
||||
// don't add friends that are already in your userlist
|
||||
//if (friendExistsInUserList(k)) { return; }
|
||||
var $messagebox = $(chatbox).find('.cp-app-contacts-messages');
|
||||
room.messages.forEach(function (msg) {
|
||||
var el_message = markup.message(msg);
|
||||
$messagebox.append(el_message);
|
||||
});
|
||||
normalizeLabels($messagebox);
|
||||
|
||||
var roomEl = markup.room(id, room, list);
|
||||
|
||||
var $parentEl;
|
||||
if (room.isFriendChat) {
|
||||
$parentEl = $userlist.find('.cp-app-contacts-friends');
|
||||
} else if (room.isPadChat) {
|
||||
$parentEl = $userlist.find('.cp-app-contacts-padchat');
|
||||
} else {
|
||||
$parentEl = $userlist.find('.cp-app-contacts-rooms');
|
||||
}
|
||||
$parentEl.find('.cp-app-contacts-category-content').append(roomEl);
|
||||
|
||||
reorderRooms();
|
||||
|
||||
updateStatus(id);
|
||||
|
||||
if (isApp && room.isPadChat) {
|
||||
$container.removeClass('cp-app-contacts-initializing');
|
||||
display(room.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
messenger.on('friend', function (curvePublic) {
|
||||
console.log('new friend: ', curvePublic);
|
||||
//console.error("TODO redraw user list");
|
||||
//console.error("TODO connect to new friend");
|
||||
// FIXME this doesn't work right now because the friend hasn't been fully added?
|
||||
connectToFriend(curvePublic, function () {
|
||||
//console.error('connected');
|
||||
if (isApp) { return; }
|
||||
debug('new friend: ', curvePublic);
|
||||
execCommand('GET_ROOMS', {curvePublic: curvePublic}, function (err, rooms) {
|
||||
if (err) { return void console.error(err); }
|
||||
debug('rooms: ' + JSON.stringify(rooms));
|
||||
rooms.forEach(initializeRoom);
|
||||
});
|
||||
});
|
||||
|
||||
messenger.on('unfriend', function (curvePublic) {
|
||||
console.log('unfriend', curvePublic);
|
||||
find.inList(curvePublic).remove();
|
||||
console.error('TODO remove chatbox');
|
||||
console.error('TODO show something if that chatbox was active');
|
||||
messenger.on('unfriend', function (curvePublic, removedByMe) {
|
||||
if (isApp) { return; }
|
||||
var channel = state.channels[state.active];
|
||||
$userlist.find(userQuery(curvePublic)).remove();
|
||||
$messages.find(userQuery(curvePublic)).remove();
|
||||
if (channel && channel.curvePublic === curvePublic) {
|
||||
showInfo();
|
||||
}
|
||||
if (!removedByMe) {
|
||||
// TODO UI.alert if this is triggered by the other guy
|
||||
}
|
||||
});
|
||||
|
||||
common.getMetadataMgr().onChange(function () {
|
||||
//messenger.checkNewFriends();
|
||||
messenger.updateMyData();
|
||||
common.getMetadataMgr().onTitleChange(function () {
|
||||
var padChat = common.getPadChat();
|
||||
var md = common.getMetadataMgr().getMetadata();
|
||||
var name = md.title || md.defaultTitle;
|
||||
$userlist.find(dataQuery(padChat)).find('.cp-app-contacts-name').text(name);
|
||||
$userlist.find(dataQuery(padChat)).attr('title', name);
|
||||
$messages.find(dataQuery(padChat) + ' .cp-app-contacts-header .cp-app-contacts-name')
|
||||
.text(name);
|
||||
|
||||
var $mAvatar = $messages.find(dataQuery(padChat) +' .cp-app-contacts-header .cp-avatar');
|
||||
var $lAvatar = $userlist.find(dataQuery(padChat));
|
||||
$lAvatar.find('.cp-avatar-default, media-tag').remove();
|
||||
|
||||
var $div = $('<div>');
|
||||
common.displayAvatar($div, null, name, function () {
|
||||
$mAvatar.html($div.html());
|
||||
$lAvatar.find('.cp-app-contacts-right-col').before($div.html());
|
||||
});
|
||||
});
|
||||
|
||||
// FIXME dirty hack
|
||||
// TODO room
|
||||
// messenger.on('joinroom', function (chanid))
|
||||
// messenger.on('leaveroom', function (chanid))
|
||||
|
||||
|
||||
messenger.getMyInfo(function (e, info) {
|
||||
displayNames[info.curvePublic] = info.displayName;
|
||||
contactsData[info.curvePublic] = info;
|
||||
});
|
||||
|
||||
messenger.getFriendList(function (e, keys) {
|
||||
var count = keys.length + 1;
|
||||
var ready = function () {
|
||||
count--;
|
||||
if (count === 0) {
|
||||
initializing = false;
|
||||
UI.removeLoadingScreen();
|
||||
}
|
||||
};
|
||||
ready();
|
||||
keys.forEach(function (curvePublic) {
|
||||
connectToFriend(curvePublic, ready);
|
||||
var ready = false;
|
||||
var onMessengerReady = function () {
|
||||
if (isApp) { return; }
|
||||
if (ready) { return; }
|
||||
ready = true;
|
||||
|
||||
execCommand('GET_ROOMS', null, function (err, rooms) {
|
||||
if (err) { return void console.error(err); }
|
||||
|
||||
debug('rooms: ' + JSON.stringify(rooms));
|
||||
rooms.forEach(initializeRoom);
|
||||
});
|
||||
|
||||
$container.removeClass('cp-app-contacts-initializing');
|
||||
};
|
||||
|
||||
var onPadChatReady = function (data) {
|
||||
var padChat = common.getPadChat();
|
||||
if (data !== padChat) { return; }
|
||||
if (state.channels[data]) { return; }
|
||||
execCommand('GET_ROOMS', {padChat: data}, function (err, rooms) {
|
||||
if (err) { return void console.error(err); }
|
||||
if (!Array.isArray(rooms) || rooms.length !== 1) {
|
||||
return void console.error('Invalid pad chat');
|
||||
}
|
||||
var room = rooms[0];
|
||||
var md = common.getMetadataMgr().getMetadata();
|
||||
var name = md.title || md.defaultTitle;
|
||||
room.name = name;
|
||||
rooms.forEach(initializeRoom);
|
||||
});
|
||||
};
|
||||
|
||||
var onDisconnect = function () {
|
||||
debug('disconnected');
|
||||
$messages.find('.cp-app-contacts-input textarea').prop('disabled', true);
|
||||
};
|
||||
var onReconnect = function () {
|
||||
debug('reconnected');
|
||||
$messages.find('.cp-app-contacts-input textarea').prop('disabled', false);
|
||||
};
|
||||
|
||||
// Initialize chat when outer is ready (all channels loaded)
|
||||
// TODO: try again in outer if fail to load a channel
|
||||
if (!isApp) {
|
||||
execCommand('INIT_FRIENDS', null, function () {});
|
||||
execCommand('IS_READY', null, function (err, yes) {
|
||||
if (yes) { onMessengerReady(); }
|
||||
});
|
||||
}
|
||||
sframeChan.on('EV_CHAT_EVENT', function (obj) {
|
||||
if (obj.ev === 'READY') {
|
||||
onMessengerReady();
|
||||
return;
|
||||
}
|
||||
if (obj.ev === 'PADCHAT_READY') {
|
||||
onPadChatReady(obj.data);
|
||||
return;
|
||||
}
|
||||
if (obj.ev === 'DISCONNECT') {
|
||||
onDisconnect();
|
||||
return;
|
||||
}
|
||||
if (obj.ev === 'RECONNECT') {
|
||||
onReconnect();
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -653,8 +653,10 @@
|
||||
#cp-app-drive-new-ghost-dialog.cp-modal-container {
|
||||
.drive_fileIcon;
|
||||
|
||||
border: 1px solid @colortheme_modal-fg;
|
||||
li:not(.cp-app-drive-element-selected):hover {
|
||||
border: 1px solid white;
|
||||
background: @colortheme_modal-fg;
|
||||
color: @colortheme_modal-bg;
|
||||
}
|
||||
.cp-modal {
|
||||
display: flex;
|
||||
@@ -663,14 +665,16 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
&> p {
|
||||
margin: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
&> div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
.cp-app-drive-new-upload {
|
||||
break-after: always;
|
||||
page-break-after: always;
|
||||
@@ -682,23 +686,27 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media screen and (max-height: @browser_media-not-big) {
|
||||
@media screen and (max-width: @browser_media-medium-screen),
|
||||
screen and (max-height: @browser_media-medium-screen) {
|
||||
.cp-modal {
|
||||
& > p {
|
||||
display: none;
|
||||
}
|
||||
& > div {
|
||||
align-content: unset;
|
||||
li {
|
||||
height: 40px;
|
||||
width: 90%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: unset;
|
||||
.fa {
|
||||
font-size: 32px;
|
||||
min-width: 50px;
|
||||
}
|
||||
.cp-app-drive-new-name {
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1207,15 +1207,23 @@ define([
|
||||
if (paths) {
|
||||
paths.forEach(function (p) { pathsList.push(p.path); });
|
||||
}
|
||||
var hasOwned = pathsList.some(function (p) {
|
||||
var el = manager.find(p);
|
||||
var data = manager.isSharedFolder(el) ? manager.getSharedFolderData(el)
|
||||
: manager.getFileData(el);
|
||||
return data.owners && data.owners.indexOf(edPublic) !== -1;
|
||||
});
|
||||
var msg = Messages._getKey("fm_removeSeveralPermanentlyDialog", [pathsList.length]);
|
||||
if (pathsList.length === 1) {
|
||||
msg = Messages.fm_removePermanentlyDialog;
|
||||
msg = hasOwned ? Messages.fm_deleteOwnedPad : Messages.fm_removePermanentlyDialog;
|
||||
} else if (hasOwned) {
|
||||
msg = msg + '<br><em>' + Messages.fm_removePermanentlyNote + '</em>';
|
||||
}
|
||||
UI.confirm(msg, function(res) {
|
||||
$(window).focus();
|
||||
if (!res) { return; }
|
||||
manager.delete(pathsList, refresh);
|
||||
});
|
||||
}, null, true);
|
||||
};
|
||||
// Drag & drop:
|
||||
// The data transferred is a stringified JSON containing the path of the dragged element
|
||||
@@ -1290,6 +1298,7 @@ define([
|
||||
if (!oldPaths) { return; }
|
||||
// A moved element should be removed from its previous location
|
||||
var movedPaths = [];
|
||||
|
||||
var sharedF = false;
|
||||
oldPaths.forEach(function (p) {
|
||||
movedPaths.push(p.path);
|
||||
@@ -1301,9 +1310,25 @@ define([
|
||||
var newPath = findDropPath(ev.target);
|
||||
if (!newPath) { return; }
|
||||
if (sharedF && manager.isPathIn(newPath, [TRASH])) {
|
||||
deletePaths(null, movedPaths);
|
||||
return;
|
||||
return void deletePaths(null, movedPaths);
|
||||
}
|
||||
|
||||
if (manager.isPathIn(newPath, [TRASH])) {
|
||||
// Filter the selection to remove shared folders.
|
||||
// Shared folders can't be moved to the trash!
|
||||
var filteredPaths = movedPaths.filter(function (p) {
|
||||
var el = manager.find(p);
|
||||
return !manager.isSharedFolder(el);
|
||||
});
|
||||
|
||||
if (!filteredPaths.length) {
|
||||
// We only have shared folder, delete them
|
||||
return void deletePaths(null, movedPaths);
|
||||
}
|
||||
|
||||
movedPaths = filteredPaths;
|
||||
}
|
||||
|
||||
if (movedPaths && movedPaths.length) {
|
||||
moveElements(movedPaths, newPath, null, refresh);
|
||||
}
|
||||
@@ -1805,14 +1830,17 @@ define([
|
||||
.click(function () {
|
||||
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: $content[0],
|
||||
path: findDropPath($content[0])
|
||||
};
|
||||
APP.FM.handleFile(file, ev);
|
||||
var files = Util.slice(e.target.files);
|
||||
files.forEach(function (file) {
|
||||
var ev = {
|
||||
target: $content[0],
|
||||
path: findDropPath($content[0])
|
||||
};
|
||||
APP.FM.handleFile(file, ev);
|
||||
});
|
||||
});
|
||||
$input.click();
|
||||
});
|
||||
@@ -1889,9 +1917,11 @@ define([
|
||||
|
||||
var createShareButton = function (id, $container) {
|
||||
var $shareBlock = $('<button>', {
|
||||
'class': 'fa fa-shhare-alt cp-toolbar-share-button',
|
||||
'class': 'cp-toolbar-share-button',
|
||||
title: Messages.shareButton
|
||||
});
|
||||
$sharedIcon.clone().appendTo($shareBlock);
|
||||
$('<span>').text(Messages.shareButton).appendTo($shareBlock);
|
||||
var data = manager.getSharedFolderData(id);
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
if (!parsed || !parsed.hash) { return void console.error("Invalid href: "+data.href); }
|
||||
@@ -2574,7 +2604,12 @@ define([
|
||||
createNewButton(isInRoot, $toolbar.find('.cp-app-drive-toolbar-leftside'));
|
||||
var sfId = manager.isInSharedFolder(currentPath);
|
||||
if (sfId) {
|
||||
var sfData = manager.getSharedFolderData(sfId);
|
||||
var parsed = Hash.parsePadUrl(sfData.href);
|
||||
sframeChan.event('EV_DRIVE_SET_HASH', parsed.hash || '');
|
||||
createShareButton(sfId, $toolbar.find('.cp-app-drive-toolbar-leftside'));
|
||||
} else {
|
||||
sframeChan.event('EV_DRIVE_SET_HASH', '');
|
||||
}
|
||||
|
||||
createTitle($toolbar.find('.cp-app-drive-path'), path);
|
||||
@@ -2721,7 +2756,7 @@ define([
|
||||
}
|
||||
var dataPath = isSharedFolder ? path.slice(0, -1) : path;
|
||||
$elementRow.data('path', dataPath);
|
||||
addDragAndDropHandlers($elementRow, path, true, droppable);
|
||||
addDragAndDropHandlers($elementRow, dataPath, true, droppable);
|
||||
if (active) {
|
||||
$elementRow.addClass('cp-app-drive-element-active cp-leftside-active');
|
||||
}
|
||||
@@ -3221,16 +3256,23 @@ define([
|
||||
paths.push($(elmt).data('path'));
|
||||
});
|
||||
if (!paths.length) { return; }
|
||||
// Remove shared folders from the selection (they can't be moved to the trash)
|
||||
// unless the selection is only shared folders
|
||||
var paths2 = paths.filter(function (p) {
|
||||
var el = manager.find(p);
|
||||
return !manager.isSharedFolder(el);
|
||||
});
|
||||
// If we are in the trash or anon pad or if we are holding the "shift" key,
|
||||
// delete permanently
|
||||
// Or if we are in a shared folder
|
||||
// Or if the selection is only shared folders
|
||||
if (!APP.loggedIn || isTrash || manager.isInSharedFolder(currentPath)
|
||||
|| e.shiftKey) {
|
||||
|| e.shiftKey || !paths2.length) {
|
||||
deletePaths(null, paths);
|
||||
return;
|
||||
}
|
||||
// else move to trash
|
||||
moveElements(paths, [TRASH], false, refresh);
|
||||
moveElements(paths2, [TRASH], false, refresh);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,7 +41,7 @@ define([
|
||||
var secret = Utils.Hash.getSecrets('drive', hash);
|
||||
if (hash) {
|
||||
// Add a shared folder!
|
||||
// XXX password?
|
||||
// TODO password?
|
||||
Cryptpad.addSharedFolder(secret, function (id) {
|
||||
window.CryptPad_newSharedFolder = id;
|
||||
// Update the hash in the address bar
|
||||
@@ -87,6 +87,14 @@ define([
|
||||
cb(obj);
|
||||
});
|
||||
});
|
||||
sframeChan.on('EV_DRIVE_SET_HASH', function (hash) {
|
||||
// Update the hash in the address bar
|
||||
var ohc = window.onhashchange;
|
||||
window.onhashchange = function () {};
|
||||
window.location.hash = hash || '';
|
||||
window.onhashchange = ohc;
|
||||
ohc({reset:true});
|
||||
});
|
||||
Cryptpad.onNetworkDisconnect.reg(function () {
|
||||
sframeChan.event('EV_NETWORK_DISCONNECT');
|
||||
});
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#kanban-edit {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
@@ -77,6 +76,7 @@
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
border: 1px solid rgba(0,0,0,0.3);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@button-size: 50px;
|
||||
@@ -117,39 +117,39 @@
|
||||
}
|
||||
|
||||
.kanban-header-yellow {
|
||||
background: #FC3;
|
||||
background: #FC3 !important;
|
||||
}
|
||||
|
||||
.kanban-header-orange {
|
||||
background: #F91;
|
||||
background: #F91 !important;
|
||||
}
|
||||
|
||||
.kanban-header-blue {
|
||||
background: #0AC;
|
||||
background: #0AC !important;
|
||||
}
|
||||
|
||||
.kanban-header-red {
|
||||
background: #E43;
|
||||
background: #E43 !important;
|
||||
}
|
||||
|
||||
.kanban-header-green {
|
||||
background: #8C4;
|
||||
background: #8C4 !important;
|
||||
}
|
||||
|
||||
.kanban-header-purple {
|
||||
background: #c851ff;
|
||||
background: #c851ff !important;
|
||||
}
|
||||
|
||||
.kanban-header-cyan {
|
||||
background: #00ffff;
|
||||
background: #00ffff !important;
|
||||
}
|
||||
|
||||
.kanban-header-lightgreen {
|
||||
background: #c3ff5b;
|
||||
background: #c3ff5b !important;
|
||||
}
|
||||
|
||||
.kanban-header-lightblue {
|
||||
background: #adeeff;
|
||||
background: #adeeff !important;
|
||||
}
|
||||
|
||||
@media (max-width: @browser_media-medium-screen) {
|
||||
|
||||
@@ -10,6 +10,7 @@ define([
|
||||
'/common/modes.js',
|
||||
'/customize/messages.js',
|
||||
'/kanban/jkanban.js',
|
||||
'/kanban/jscolor.js',
|
||||
'css!/kanban/jkanban.css',
|
||||
|
||||
'less!/kanban/app-kanban.less'
|
||||
@@ -107,7 +108,7 @@ define([
|
||||
|
||||
var kanban = new window.jKanban({
|
||||
element: '#cp-app-kanban-content',
|
||||
gutter: '15px',
|
||||
gutter: '5px',
|
||||
widthBoard: '300px',
|
||||
buttonContent: '❌',
|
||||
colors: COLORS,
|
||||
@@ -138,7 +139,7 @@ define([
|
||||
// Remove the input
|
||||
$(el).text(name);
|
||||
// Save the value for the correct board
|
||||
var board = $(el.parentNode.parentNode).attr("data-id");
|
||||
var board = $(el.parentNode.parentNode.parentNode).attr("data-id");
|
||||
var pos = kanban.findElementPosition(el);
|
||||
kanban.getBoardJSON(board).item[pos].title = name;
|
||||
kanban.onChange();
|
||||
@@ -206,24 +207,53 @@ define([
|
||||
}
|
||||
});
|
||||
},
|
||||
colorClick: function (el) {
|
||||
colorClick: function (el, type) {
|
||||
if (framework.isReadOnly() || framework.isLocked()) { return; }
|
||||
verbose("in color click");
|
||||
var board = $(el.parentNode).attr("data-id");
|
||||
var boardJSON = kanban.getBoardJSON(board);
|
||||
verbose("on color click");
|
||||
var boardJSON;
|
||||
var board;
|
||||
if (type === "board") {
|
||||
verbose("board color click");
|
||||
board = $(el.parentNode).attr("data-id");
|
||||
boardJSON = kanban.getBoardJSON(board);
|
||||
} else {
|
||||
verbose("item color click");
|
||||
board = $(el.parentNode.parentNode).attr("data-id");
|
||||
var pos = kanban.findElementPosition(el);
|
||||
boardJSON = kanban.getBoardJSON(board).item[pos];
|
||||
}
|
||||
var onchange = function (colorL) {
|
||||
var elL = el;
|
||||
var typeL = type;
|
||||
var boardJSONL;
|
||||
var boardL;
|
||||
if (typeL === "board") {
|
||||
verbose("board color change");
|
||||
boardL = $(elL.parentNode).attr("data-id");
|
||||
boardJSONL = kanban.getBoardJSON(boardL);
|
||||
} else {
|
||||
verbose("item color change");
|
||||
boardL = $(elL.parentNode.parentNode).attr("data-id");
|
||||
var pos = kanban.findElementPosition(elL);
|
||||
boardJSONL = kanban.getBoardJSON(boardL).item[pos];
|
||||
}
|
||||
var currentColor = boardJSONL.color;
|
||||
verbose("Current color " + currentColor);
|
||||
if (currentColor !== colorL.toString()) {
|
||||
$(elL).removeClass("kanban-header-" + currentColor);
|
||||
boardJSONL.color = colorL.toString();
|
||||
kanban.onChange();
|
||||
}
|
||||
};
|
||||
var jscolorL;
|
||||
el._jscLinkedInstance = undefined;
|
||||
jscolorL = new window.jscolor(el,{onFineChange: onchange, valueElement:undefined});
|
||||
jscolorL.show();
|
||||
var currentColor = boardJSON.color;
|
||||
verbose("Current color " + currentColor);
|
||||
var index = kanban.options.colors.findIndex(function (element) {
|
||||
return (element === currentColor);
|
||||
}) + 1;
|
||||
verbose("Next index " + index);
|
||||
if (index >= kanban.options.colors.length) { index = 0; }
|
||||
var nextColor = kanban.options.colors[index];
|
||||
verbose("Next color " + nextColor);
|
||||
boardJSON.color = nextColor;
|
||||
$(el).removeClass("kanban-header-" + currentColor);
|
||||
$(el).addClass("kanban-header-" + nextColor);
|
||||
kanban.onChange();
|
||||
if (currentColor === undefined) {
|
||||
currentColor = '';
|
||||
}
|
||||
jscolorL.fromString(currentColor);
|
||||
},
|
||||
buttonClick: function (el, boardId, e) {
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
widthBoard: '250px',
|
||||
responsive: '700',
|
||||
colors: ["yellow", "green", "blue", "red", "orange"],
|
||||
responsivePercentage: false,
|
||||
boards: [],
|
||||
dragBoards: true,
|
||||
addItemButton: false,
|
||||
@@ -67,7 +68,7 @@
|
||||
click: function (el) {},
|
||||
boardTitleclick: function (el, boardId) {},
|
||||
buttonClick: function (el, boardId) {},
|
||||
colorClick: function (el, boardId) {},
|
||||
colorClick: function (el, type) {},
|
||||
addItemClick: function (el, boardId) {},
|
||||
onChange: function () {}
|
||||
};
|
||||
@@ -85,12 +86,12 @@
|
||||
//Init Drag Board
|
||||
self.drakeBoard = self.dragula([self.container], {
|
||||
moves: function (el, source, handle, sibling) {
|
||||
if (self.options.readOnly) { return false; }
|
||||
if (self.options.readOnly) { return false; }
|
||||
if (!self.options.dragBoards) return false;
|
||||
return (handle.classList.contains('kanban-board-header') || handle.classList.contains('kanban-title-board'));
|
||||
},
|
||||
accepts: function (el, target, source, sibling) {
|
||||
if (self.options.readOnly) { return false; }
|
||||
if (self.options.readOnly) { return false; }
|
||||
return target.classList.contains('kanban-container');
|
||||
},
|
||||
revertOnSpill: true,
|
||||
@@ -144,15 +145,18 @@
|
||||
|
||||
//Init Drag Item
|
||||
self.drake = self.dragula(self.boardContainer, {
|
||||
moves: function (el, source, handle, sibling) {
|
||||
if (self.options.readOnly) { return false; }
|
||||
return handle.classList.contains('kanban-item');
|
||||
},
|
||||
accepts: function (el, target, source, sibling) {
|
||||
if (self.options.readOnly) { return false; }
|
||||
return true;
|
||||
},
|
||||
revertOnSpill: true
|
||||
moves: function (el, source, handle, sibling) {
|
||||
if (self.options.readOnly) { return false; }
|
||||
return handle.classList.contains('kanban-item');
|
||||
},
|
||||
accepts: function (el, target, source, sibling) {
|
||||
if (self.options.readOnly) { return false; }
|
||||
return true;
|
||||
},
|
||||
revertOnSpill: true
|
||||
})
|
||||
.on('cancel', function(el, container, source) {
|
||||
self.enableAllBoards();
|
||||
})
|
||||
.on('drag', function (el, source) {
|
||||
// we need to calculate the position before starting to drag
|
||||
@@ -184,7 +188,9 @@
|
||||
var boardId = source.parentNode.dataset.id;
|
||||
self.options.dragcancelEl(el, boardId);
|
||||
})
|
||||
.on('drop', function (el, target, source, sibling) {
|
||||
.on('drop', function(el, target, source, sibling) {
|
||||
self.enableAllBoards();
|
||||
|
||||
console.log("In drop");
|
||||
|
||||
// TODO: update board object board order
|
||||
@@ -229,7 +235,7 @@
|
||||
// if (board1==board2 && pos2<pos1)
|
||||
// pos2 = pos2;
|
||||
|
||||
// moving element to target array
|
||||
// moving element to target array
|
||||
board1.item.splice(pos1, 1);
|
||||
board2.item.splice(pos2 - 1, 0, item);
|
||||
|
||||
@@ -240,9 +246,18 @@
|
||||
}
|
||||
};
|
||||
|
||||
this.enableAllBoards = function() {
|
||||
var allB = document.querySelectorAll('.kanban-board');
|
||||
if (allB.length > 0 && allB !== undefined) {
|
||||
for (var i = 0; i < allB.length; i++) {
|
||||
allB[i].classList.remove('disabled-board');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.addElement = function (boardID, element) {
|
||||
|
||||
// add Element to JSON
|
||||
// add Element to JSON
|
||||
var boardJSON = __findBoardJSON(boardID);
|
||||
boardJSON.item.push({
|
||||
title: element.title
|
||||
@@ -260,6 +275,7 @@
|
||||
nodeItem.dragendfn = element.dragend;
|
||||
nodeItem.dropfn = element.drop;
|
||||
__onclickHandler(nodeItem);
|
||||
__onColorClickHandler(nodeItem, "item");
|
||||
board.appendChild(nodeItem);
|
||||
// send event that board has changed
|
||||
self.onChange();
|
||||
@@ -272,8 +288,19 @@
|
||||
return self;
|
||||
};
|
||||
|
||||
this.addBoards = function (boards) {
|
||||
var boardWidth = self.options.widthBoard;
|
||||
|
||||
this.addBoards = function(boards) {
|
||||
if (self.options.responsivePercentage) {
|
||||
self.container.style.width = '100%';
|
||||
self.options.gutter = '1%';
|
||||
if (window.innerWidth > self.options.responsive) {
|
||||
var boardWidth = (100 - boards.length * 2) / boards.length;
|
||||
} else {
|
||||
var boardWidth = 100 - (boards.length * 2);
|
||||
}
|
||||
} else {
|
||||
var boardWidth = self.options.widthBoard;
|
||||
}
|
||||
var addButton = self.options.addItemButton;
|
||||
var buttonContent = self.options.buttonContent;
|
||||
|
||||
@@ -290,7 +317,11 @@
|
||||
boardNode.dataset.id = board.id;
|
||||
boardNode.classList.add('kanban-board');
|
||||
//set style
|
||||
boardNode.style.width = boardWidth;
|
||||
if (self.options.responsivePercentage) {
|
||||
boardNode.style.width = boardWidth + '%';
|
||||
} else {
|
||||
boardNode.style.width = boardWidth;
|
||||
}
|
||||
boardNode.style.marginLeft = self.options.gutter;
|
||||
boardNode.style.marginRight = self.options.gutter;
|
||||
// header board
|
||||
@@ -303,6 +334,10 @@
|
||||
headerBoard.classList.add(value);
|
||||
});
|
||||
if (board.color !== '' && board.color !== undefined) {
|
||||
headerBoard._jscLinkedInstance = undefined;
|
||||
jscolorL = new jscolor(headerBoard,{valueElement:undefined});
|
||||
jscolorL.fromString(board.color);
|
||||
headerBoard._jscLinkedInstance = undefined;
|
||||
headerBoard.classList.add("kanban-header-" + board.color);
|
||||
}
|
||||
titleBoard = document.createElement('div');
|
||||
@@ -311,7 +346,7 @@
|
||||
titleBoard.clickfn = board.boardTitleClick;
|
||||
__onboardTitleClickHandler(titleBoard);
|
||||
headerBoard.appendChild(titleBoard);
|
||||
__onColorClickHandler(headerBoard);
|
||||
__onColorClickHandler(headerBoard, "board");
|
||||
|
||||
// if add button is true, add button to the board
|
||||
if (addButton) {
|
||||
@@ -332,14 +367,24 @@
|
||||
var nodeItem = document.createElement('div');
|
||||
nodeItem.classList.add('kanban-item');
|
||||
nodeItem.dataset.eid = itemKanban.id;
|
||||
nodeItem.innerHTML = itemKanban.title;
|
||||
var nodeItemText = document.createElement('div');
|
||||
nodeItemText.classList.add('kanban-item-text');
|
||||
nodeItemText.dataset.eid = itemKanban.id;
|
||||
nodeItemText.innerHTML = itemKanban.title;
|
||||
nodeItem.appendChild(nodeItemText);
|
||||
//add function
|
||||
nodeItem.clickfn = itemKanban.click;
|
||||
nodeItem.dragfn = itemKanban.drag;
|
||||
nodeItem.dragendfn = itemKanban.dragend;
|
||||
nodeItem.dropfn = itemKanban.drop;
|
||||
nodeItemText.clickfn = itemKanban.click;
|
||||
nodeItemText.dragfn = itemKanban.drag;
|
||||
nodeItemText.dragendfn = itemKanban.dragend;
|
||||
nodeItemText.dropfn = itemKanban.drop;
|
||||
//add click handler of item
|
||||
__onclickHandler(nodeItem);
|
||||
__onclickHandler(nodeItemText);
|
||||
if (itemKanban.color !== '' && itemKanban.color !== undefined) {
|
||||
jscolorL = new jscolor(nodeItem,{valueElement:undefined});
|
||||
jscolorL.fromString(itemKanban.color);
|
||||
}
|
||||
__onColorClickHandler(nodeItem, "item");
|
||||
|
||||
contentBoard.appendChild(nodeItem);
|
||||
}
|
||||
//footer board
|
||||
@@ -487,12 +532,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
function __onColorClickHandler(nodeItem, clickfn) {
|
||||
function __onColorClickHandler(nodeItem, type) {
|
||||
nodeItem.addEventListener('click', function (e) {
|
||||
e.preventDefault;
|
||||
self.options.colorClick(this);
|
||||
if (typeof (this.clickfn) === 'function')
|
||||
this.clickfn(this);
|
||||
self.options.colorClick(this, type);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
1855
www/kanban/jscolor.js
Normal file
1855
www/kanban/jscolor.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@ body.cp-app-pad {
|
||||
height: 100%;
|
||||
border: 0;
|
||||
> .cke_inner {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
position: unset;
|
||||
display: flex;
|
||||
@@ -47,6 +48,20 @@ body.cp-app-pad {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
max-height: 100vh;
|
||||
.cke_dialog_contents {
|
||||
#ck-mediatag-preview {
|
||||
margin: auto;
|
||||
resize: both;
|
||||
max-width: 300px;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
media-tag {
|
||||
display: flex;
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cke_wysiwyg_frame {
|
||||
|
||||
@@ -43,9 +43,7 @@ CKEDITOR.dialog.add('mediatag', function (editor) {
|
||||
type: 'html',
|
||||
id: 'preview',
|
||||
html: '<label>'+Messages.preview+'</label>'+
|
||||
'<div id="ck-mediatag-preview"'+
|
||||
'style="margin:auto;resize:both;max-width:300px;max-height:300px;overflow:auto"'+
|
||||
'></div>'
|
||||
'<div id="ck-mediatag-preview"></div>'
|
||||
},
|
||||
]
|
||||
},
|
||||
@@ -77,11 +75,6 @@ CKEDITOR.dialog.add('mediatag', function (editor) {
|
||||
|
||||
var $preview = $(dialog).find('#ck-mediatag-preview');
|
||||
var $clone = $(el.$).clone();
|
||||
$clone.css({
|
||||
display: 'flex',
|
||||
'border-style': 'solid',
|
||||
'border-color': 'black'
|
||||
});
|
||||
$preview.html('').append($clone);
|
||||
|
||||
var center = function () {
|
||||
@@ -125,7 +118,7 @@ CKEDITOR.dialog.add('mediatag', function (editor) {
|
||||
update();
|
||||
});
|
||||
|
||||
setTimeout(center);
|
||||
setTimeout(update);
|
||||
},
|
||||
onOk: function() {
|
||||
var dialog = this;
|
||||
|
||||
@@ -1092,6 +1092,11 @@ define([
|
||||
Test(passIfOk);
|
||||
}
|
||||
|
||||
// No need for onLocal in openPadChat because in poll, we listen for metadata changes
|
||||
// and save them everytime.
|
||||
// See `metadataMgr.onChange(function () {`
|
||||
common.openPadChat(function () {});
|
||||
|
||||
UI.removeLoadingScreen();
|
||||
var privateDat = metadataMgr.getPrivateData();
|
||||
var skipTemp = Util.find(privateDat,
|
||||
@@ -1160,6 +1165,7 @@ define([
|
||||
|
||||
var configTb = {
|
||||
displayed: [
|
||||
'chat',
|
||||
'userlist',
|
||||
'title',
|
||||
'useradmin',
|
||||
|
||||
@@ -37,7 +37,8 @@ define([
|
||||
window.addEventListener('message', onMsg);
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
SFCommonO.start({
|
||||
useCreationScreen: true
|
||||
useCreationScreen: true,
|
||||
messaging: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -236,9 +236,9 @@ define([
|
||||
label: { class: 'noTitle' }
|
||||
});
|
||||
var $div2 = $(h('div.cp-settings-autostore-radio', [
|
||||
opt1,
|
||||
opt3,
|
||||
opt2,
|
||||
opt3
|
||||
opt1
|
||||
])).appendTo($div);
|
||||
|
||||
$div.find('input[type="radio"]').on('change', function () {
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
.cp-app-slide-viewer {
|
||||
width: 50vw;
|
||||
overflow: hidden;
|
||||
z-index: 998;
|
||||
div#cp-app-slide-modal:not(.cp-app-slide-shown) {
|
||||
position: relative;
|
||||
top: auto;
|
||||
@@ -417,4 +418,4 @@
|
||||
pre.cp-slide-css-error {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<script async data-bootload="/common/sframe-app-outer.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0px;
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// Load #1, load as little as possible because we are in a race to get the loading screen up.
|
||||
define([
|
||||
'/bower_components/nthen/index.js',
|
||||
'/api/config',
|
||||
'/common/dom-ready.js',
|
||||
'/common/requireconfig.js',
|
||||
'/common/sframe-common-outer.js'
|
||||
], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) {
|
||||
var requireConfig = RequireConfig();
|
||||
|
||||
// Loaded in load #2
|
||||
nThen(function (waitFor) {
|
||||
DomReady.onReady(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
var req = {
|
||||
cfg: requireConfig,
|
||||
req: [ '/common/loading.js' ],
|
||||
pfx: window.location.origin
|
||||
};
|
||||
window.rc = requireConfig;
|
||||
window.apiconf = ApiConfig;
|
||||
document.getElementById('sbox-iframe').setAttribute('src',
|
||||
ApiConfig.httpSafeOrigin + '/whiteboard/inner.html?' + requireConfig.urlArgs +
|
||||
'#' + encodeURIComponent(JSON.stringify(req)));
|
||||
|
||||
// This is a cheap trick to avoid loading sframe-channel in parallel with the
|
||||
// loading screen setup.
|
||||
var done = waitFor();
|
||||
var onMsg = function (msg) {
|
||||
var data = JSON.parse(msg.data);
|
||||
if (data.q !== 'READY') { return; }
|
||||
window.removeEventListener('message', onMsg);
|
||||
var _done = done;
|
||||
done = function () { };
|
||||
_done();
|
||||
};
|
||||
window.addEventListener('message', onMsg);
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
SFCommonO.start({
|
||||
useCreationScreen: true
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user