|
|
|
|
define(['json.sortify'], function (Sortify) {
|
|
|
|
|
var UNINIT = 'uninitialized';
|
|
|
|
|
var create = function (sframeChan) {
|
|
|
|
|
var meta = UNINIT;
|
|
|
|
|
var members = [];
|
|
|
|
|
var metadataObj = UNINIT;
|
|
|
|
|
// This object reflects the metadata which is in the document at this moment.
|
|
|
|
|
// Normally when a person leaves the pad, everybody sees them leave and updates
|
|
|
|
|
// their metadata, this causes everyone to fight to change the document and
|
|
|
|
|
// operational transform doesn't like it. So this is a lazy object which is
|
|
|
|
|
// only updated either:
|
|
|
|
|
// 1. On changes to the metadata that come in from someone else
|
|
|
|
|
// 2. On changes connects, disconnects or changes to your own metadata
|
|
|
|
|
var metadataLazyObj = UNINIT;
|
|
|
|
|
var priv = {};
|
|
|
|
|
var dirty = true;
|
|
|
|
|
var changeHandlers = [];
|
|
|
|
|
var lazyChangeHandlers = [];
|
|
|
|
|
var titleChangeHandlers = [];
|
|
|
|
|
|
|
|
|
|
var rememberedTitle;
|
|
|
|
|
|
|
|
|
|
var checkUpdate = function (lazy) {
|
|
|
|
|
if (!dirty) { return; }
|
|
|
|
|
if (meta === UNINIT) { throw new Error(); }
|
|
|
|
|
if (metadataObj === UNINIT) {
|
|
|
|
|
metadataObj = {
|
|
|
|
|
defaultTitle: meta.doc.defaultTitle,
|
|
|
|
|
//title: meta.doc.defaultTitle,
|
|
|
|
|
type: meta.doc.type,
|
|
|
|
|
users: {},
|
|
|
|
|
authors: {}
|
|
|
|
|
};
|
|
|
|
|
metadataLazyObj = JSON.parse(JSON.stringify(metadataObj));
|
|
|
|
|
}
|
|
|
|
|
if (!metadataObj.users) { metadataObj.users = {}; }
|
|
|
|
|
if (!metadataLazyObj.users) { metadataLazyObj.users = {}; }
|
|
|
|
|
|
|
|
|
|
if (!metadataObj.type) { metadataObj.type = meta.doc.type; }
|
|
|
|
|
if (!metadataLazyObj.type) { metadataLazyObj.type = meta.doc.type; }
|
|
|
|
|
|
|
|
|
|
var mdo = {};
|
|
|
|
|
// We don't want to add our user data to the object multiple times.
|
|
|
|
|
//var containsYou = false;
|
|
|
|
|
//console.log(metadataObj);
|
|
|
|
|
Object.keys(metadataObj.users).forEach(function (x) {
|
|
|
|
|
if (members.indexOf(x) === -1) { return; }
|
|
|
|
|
mdo[x] = metadataObj.users[x];
|
|
|
|
|
/*if (metadataObj.users[x].uid === meta.user.uid) {
|
|
|
|
|
//console.log('document already contains you');
|
|
|
|
|
containsYou = true;
|
|
|
|
|
}*/
|
|
|
|
|
});
|
|
|
|
|
//if (!containsYou) { mdo[meta.user.netfluxId] = meta.user; }
|
|
|
|
|
if (!priv.readOnly) {
|
|
|
|
|
mdo[meta.user.netfluxId] = meta.user;
|
|
|
|
|
}
|
|
|
|
|
metadataObj.users = mdo;
|
|
|
|
|
var lazyUserStr = Sortify(metadataLazyObj.users[meta.user.netfluxId]);
|
|
|
|
|
dirty = false;
|
|
|
|
|
if (lazy || lazyUserStr !== Sortify(meta.user)) {
|
|
|
|
|
metadataLazyObj = JSON.parse(JSON.stringify(metadataObj));
|
|
|
|
|
lazyChangeHandlers.forEach(function (f) { f(); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (metadataObj.title !== rememberedTitle) {
|
|
|
|
|
rememberedTitle = metadataObj.title;
|
|
|
|
|
titleChangeHandlers.forEach(function (f) {
|
|
|
|
|
f(metadataObj.title, metadataObj.defaultTitle);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
changeHandlers.forEach(function (f) { f(); });
|
|
|
|
|
};
|
|
|
|
|
var change = function (lazy) {
|
|
|
|
|
dirty = true;
|
|
|
|
|
setTimeout(function () {
|
|
|
|
|
checkUpdate(lazy);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
var addAuthor = function () {
|
|
|
|
|
if (!meta.user || !meta.user.netfluxId || !priv || !priv.edPublic) { return; }
|
|
|
|
|
var authors = metadataObj.authors || {};
|
|
|
|
|
if (!authors[priv.edPublic]) {
|
|
|
|
|
authors[priv.edPublic] = {
|
|
|
|
|
nId: [meta.user.netfluxId],
|
|
|
|
|
name: meta.user.name
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
authors[priv.edPublic].name = meta.user.name;
|
|
|
|
|
if (authors[priv.edPublic].nId.indexOf(meta.user.netfluxId) === -1) {
|
|
|
|
|
authors[priv.edPublic].nId.push(meta.user.netfluxId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
metadataObj.authors = authors;
|
|
|
|
|
metadataLazyObj.authors = JSON.parse(JSON.stringify(authors));
|
|
|
|
|
change();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var netfluxId;
|
|
|
|
|
var isReady = false;
|
|
|
|
|
var readyHandlers = [];
|
|
|
|
|
sframeChan.on('EV_METADATA_UPDATE', function (ev) {
|
|
|
|
|
meta = ev;
|
|
|
|
|
if (ev.priv) {
|
|
|
|
|
priv = ev.priv;
|
|
|
|
|
}
|
|
|
|
|
if (netfluxId) {
|
|
|
|
|
meta.user.netfluxId = netfluxId;
|
|
|
|
|
}
|
|
|
|
|
if (!isReady) {
|
|
|
|
|
isReady = true;
|
|
|
|
|
readyHandlers.forEach(function (f) { f(); });
|
|
|
|
|
}
|
|
|
|
|
change(true);
|
|
|
|
|
});
|
|
|
|
|
sframeChan.on('EV_RT_CONNECT', function (ev) {
|
|
|
|
|
netfluxId = ev.myID;
|
|
|
|
|
members = ev.members;
|
|
|
|
|
if (!meta.user) { return; }
|
|
|
|
|
meta.user.netfluxId = netfluxId;
|
|
|
|
|
change(true);
|
|
|
|
|
});
|
|
|
|
|
sframeChan.on('EV_RT_JOIN', function (ev) {
|
|
|
|
|
var idx = members.indexOf(ev);
|
|
|
|
|
if (idx !== -1) { console.log('Error: ' + ev + ' is already in members'); return; }
|
|
|
|
|
members.push(ev);
|
|
|
|
|
if (!meta.user) { return; }
|
|
|
|
|
change(false);
|
|
|
|
|
});
|
|
|
|
|
sframeChan.on('EV_RT_LEAVE', function (ev) {
|
|
|
|
|
var idx = members.indexOf(ev);
|
|
|
|
|
if (idx === -1) { console.log('Error: ' + ev + ' not in members'); return; }
|
|
|
|
|
members.splice(idx, 1);
|
|
|
|
|
if (!meta.user) { return; }
|
|
|
|
|
change(false);
|
|
|
|
|
});
|
|
|
|
|
sframeChan.on('EV_RT_DISCONNECT', function () {
|
|
|
|
|
members = [];
|
|
|
|
|
if (!meta.user) { return; }
|
|
|
|
|
change(true);
|
|
|
|
|
});
|
|
|
|
|
sframeChan.on('EV_RT_ERROR', function (err) {
|
|
|
|
|
if (err.type !== 'EEXPIRED' && err.type !== 'EDELETED') { return; }
|
|
|
|
|
members = [];
|
|
|
|
|
if (!meta.user) { return; }
|
|
|
|
|
change(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return Object.freeze({
|
|
|
|
|
updateMetadata: function (m) {
|
|
|
|
|
// JSON.parse(JSON.stringify()) reorders the json, so we have to use sortify even
|
|
|
|
|
// if it's on our own computer
|
|
|
|
|
if (!m) { return; }
|
|
|
|
|
if (Sortify(metadataLazyObj) === Sortify(m)) { return; }
|
|
|
|
|
metadataObj = JSON.parse(JSON.stringify(m));
|
|
|
|
|
metadataLazyObj = JSON.parse(JSON.stringify(m));
|
|
|
|
|
change(false);
|
|
|
|
|
},
|
|
|
|
|
updateTitle: function (t) {
|
|
|
|
|
metadataObj.title = t;
|
|
|
|
|
change(true);
|
|
|
|
|
},
|
|
|
|
|
getMetadata: function () {
|
|
|
|
|
checkUpdate(false);
|
|
|
|
|
return Object.freeze(JSON.parse(JSON.stringify(metadataObj)));
|
|
|
|
|
},
|
|
|
|
|
getMetadataLazy: function () {
|
|
|
|
|
return metadataLazyObj;
|
|
|
|
|
},
|
|
|
|
|
onTitleChange: function (f) { titleChangeHandlers.push(f); },
|
|
|
|
|
onChange: function (f) { changeHandlers.push(f); },
|
|
|
|
|
onChangeLazy: function (f) { lazyChangeHandlers.push(f); },
|
|
|
|
|
isConnected : function () {
|
|
|
|
|
return members.indexOf(meta.user.netfluxId) !== -1;
|
|
|
|
|
},
|
|
|
|
|
getViewers : function () {
|
|
|
|
|
checkUpdate(false);
|
|
|
|
|
var list = members.slice().filter(function (m) { return m.length === 32; });
|
|
|
|
|
return list.length - Object.keys(metadataObj.users).length;
|
|
|
|
|
},
|
|
|
|
|
getChannelMembers: function () { return members.slice(); },
|
|
|
|
|
getPrivateData : function () {
|
|
|
|
|
return priv;
|
|
|
|
|
},
|
|
|
|
|
getUserData : function () {
|
|
|
|
|
return meta.user;
|
|
|
|
|
},
|
|
|
|
|
getNetfluxId : function () {
|
|
|
|
|
return meta.user.netfluxId;
|
|
|
|
|
},
|
|
|
|
|
onReady: function (f) {
|
|
|
|
|
if (isReady) { return void f(); }
|
|
|
|
|
readyHandlers.push(f);
|
|
|
|
|
},
|
|
|
|
|
addAuthor: addAuthor,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
return Object.freeze({ create: create });
|
|
|
|
|
});
|