Realtime sync fixes
This commit is contained in:
parent
bfc93ad920
commit
6d8ed95352
@ -50,15 +50,15 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
Provider.prototype.parseSerializedContent = function(content) {
|
Provider.prototype.parseSerializedContent = function(content) {
|
||||||
var discussionList = '{}';
|
var discussionListJSON = '{}';
|
||||||
var discussionExtractor = /<!--se_discussion_list:([\s\S]+)-->$/.exec(content);
|
var discussionExtractor = /<!--se_discussion_list:([\s\S]+)-->$/.exec(content);
|
||||||
if(discussionExtractor && this.parseDiscussionList(discussionExtractor[1])) {
|
if(discussionExtractor && this.parseDiscussionList(discussionExtractor[1])) {
|
||||||
content = content.substring(0, discussionExtractor.index);
|
content = content.substring(0, discussionExtractor.index);
|
||||||
discussionList = discussionExtractor[1];
|
discussionListJSON = discussionExtractor[1];
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
content: content,
|
content: content,
|
||||||
discussionList: discussionList
|
discussionListJSON: discussionListJSON
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
var merge = settings.conflictMode == 'merge';
|
var merge = settings.conflictMode == 'merge';
|
||||||
Provider.prototype.syncMerge = function(fileDesc, syncAttributes, remoteContent, remoteTitle, remoteDiscussionListJSON) {
|
Provider.prototype.syncMerge = function(fileDesc, syncAttributes, remoteContent, remoteTitle, remoteDiscussionList, remoteDiscussionListJSON) {
|
||||||
|
|
||||||
function cleanupDiffs(diffs) {
|
function cleanupDiffs(diffs) {
|
||||||
var result = [];
|
var result = [];
|
||||||
@ -160,7 +160,6 @@ define([
|
|||||||
var localTitle = fileDesc.title;
|
var localTitle = fileDesc.title;
|
||||||
var localDiscussionListJSON = fileDesc.discussionListJSON;
|
var localDiscussionListJSON = fileDesc.discussionListJSON;
|
||||||
var localDiscussionList = fileDesc.discussionList;
|
var localDiscussionList = fileDesc.discussionList;
|
||||||
var remoteDiscussionList = JSON.parse(remoteDiscussionListJSON);
|
|
||||||
|
|
||||||
// Local/Remote CRCs
|
// Local/Remote CRCs
|
||||||
var localContentCRC = utils.crc32(localContent);
|
var localContentCRC = utils.crc32(localContent);
|
||||||
|
@ -359,7 +359,7 @@ define([
|
|||||||
var $titleContainer;
|
var $titleContainer;
|
||||||
var marginWidth = 18 * 2 + 25 + 25;
|
var marginWidth = 18 * 2 + 25 + 25;
|
||||||
var titleWidth = 18 + 348;
|
var titleWidth = 18 + 348;
|
||||||
var leftButtonsWidth = 18 * 4 + 80 + 160 + 200 + 80;
|
var leftButtonsWidth = 18 * 5 + 80 + 160 + 160 + 40 + 80;
|
||||||
var rightButtonsWidth = 18 + 80;
|
var rightButtonsWidth = 18 + 80;
|
||||||
var buttonsDropdownWidth = 40;
|
var buttonsDropdownWidth = 40;
|
||||||
function adjustWindow() {
|
function adjustWindow() {
|
||||||
@ -451,13 +451,11 @@ define([
|
|||||||
$("#wmd-code-button").append($('<i class="icon-code">')).appendTo($btnGroupElt);
|
$("#wmd-code-button").append($('<i class="icon-code">')).appendTo($btnGroupElt);
|
||||||
$("#wmd-image-button").append($('<i class="icon-picture">')).appendTo($btnGroupElt);
|
$("#wmd-image-button").append($('<i class="icon-picture">')).appendTo($btnGroupElt);
|
||||||
$btnGroupElt = $('.wmd-button-group3');
|
$btnGroupElt = $('.wmd-button-group3');
|
||||||
var $openDiscussionElt = $btnGroupElt.find('.button-open-discussion');
|
|
||||||
$("#wmd-olist-button").append($('<i class="icon-list-numbered">')).appendTo($btnGroupElt);
|
$("#wmd-olist-button").append($('<i class="icon-list-numbered">')).appendTo($btnGroupElt);
|
||||||
$("#wmd-ulist-button").append($('<i class="icon-list-bullet">')).appendTo($btnGroupElt);
|
$("#wmd-ulist-button").append($('<i class="icon-list-bullet">')).appendTo($btnGroupElt);
|
||||||
$("#wmd-heading-button").append($('<i class="icon-text-height">')).appendTo($btnGroupElt);
|
$("#wmd-heading-button").append($('<i class="icon-text-height">')).appendTo($btnGroupElt);
|
||||||
$("#wmd-hr-button").append($('<i class="icon-ellipsis">')).appendTo($btnGroupElt);
|
$("#wmd-hr-button").append($('<i class="icon-ellipsis">')).appendTo($btnGroupElt);
|
||||||
$openDiscussionElt.appendTo($btnGroupElt);
|
$btnGroupElt = $('.wmd-button-group5');
|
||||||
$btnGroupElt = $('.wmd-button-group4');
|
|
||||||
$("#wmd-undo-button").append($('<i class="icon-reply">')).appendTo($btnGroupElt);
|
$("#wmd-undo-button").append($('<i class="icon-reply">')).appendTo($btnGroupElt);
|
||||||
$("#wmd-redo-button").append($('<i class="icon-forward">')).appendTo($btnGroupElt);
|
$("#wmd-redo-button").append($('<i class="icon-forward">')).appendTo($btnGroupElt);
|
||||||
};
|
};
|
||||||
|
@ -332,6 +332,7 @@ define([
|
|||||||
range.deleteContents();
|
range.deleteContents();
|
||||||
range.insertNode(document.createTextNode(replacement));
|
range.insertNode(document.createTextNode(replacement));
|
||||||
}
|
}
|
||||||
|
editor.setValue = setValue;
|
||||||
|
|
||||||
function setValueNoWatch(value) {
|
function setValueNoWatch(value) {
|
||||||
setValue(value);
|
setValue(value);
|
||||||
|
@ -397,20 +397,7 @@ define([
|
|||||||
var $removeButton = $(popoverElt.querySelector('.action-remove-discussion'));
|
var $removeButton = $(popoverElt.querySelector('.action-remove-discussion'));
|
||||||
if(evt.target.discussionIndex) {
|
if(evt.target.discussionIndex) {
|
||||||
// If it's an existing discussion
|
// If it's an existing discussion
|
||||||
var $removeCancelButton = $(popoverElt.querySelectorAll('.action-remove-discussion-cancel'));
|
|
||||||
var $removeConfirmButton = $(popoverElt.querySelectorAll('.action-remove-discussion-confirm'));
|
|
||||||
$removeButton.click(function() {
|
$removeButton.click(function() {
|
||||||
$(popoverElt.querySelector('.new-comment-block')).addClass('hide');
|
|
||||||
$(popoverElt.querySelector('.remove-discussion-confirm')).removeClass('hide');
|
|
||||||
popoverElt.querySelector('.scrollport').scrollTop = 9999999;
|
|
||||||
});
|
|
||||||
$removeCancelButton.click(function() {
|
|
||||||
$(popoverElt.querySelector('.new-comment-block')).removeClass('hide');
|
|
||||||
$(popoverElt.querySelector('.remove-discussion-confirm')).addClass('hide');
|
|
||||||
popoverElt.querySelector('.scrollport').scrollTop = 9999999;
|
|
||||||
context.$contentInputElt.focus();
|
|
||||||
});
|
|
||||||
$removeConfirmButton.click(function() {
|
|
||||||
closeCurrentPopover();
|
closeCurrentPopover();
|
||||||
var discussion = context.getDiscussion();
|
var discussion = context.getDiscussion();
|
||||||
delete context.fileDesc.discussionList[discussion.discussionIndex];
|
delete context.fileDesc.discussionList[discussion.discussionIndex];
|
||||||
|
@ -587,7 +587,7 @@ define([
|
|||||||
task.enqueue();
|
task.enqueue();
|
||||||
};
|
};
|
||||||
|
|
||||||
googleHelper.loadRealtime = function(fileId, content, accountId, callback, errorCallback) {
|
googleHelper.loadRealtime = function(fileId, accountId, callback, errorCallback) {
|
||||||
var doc;
|
var doc;
|
||||||
var task = new AsyncTask();
|
var task = new AsyncTask();
|
||||||
connect(task);
|
connect(task);
|
||||||
@ -599,11 +599,7 @@ define([
|
|||||||
// onFileLoaded
|
// onFileLoaded
|
||||||
doc = result;
|
doc = result;
|
||||||
task.chain();
|
task.chain();
|
||||||
}, function(model) {
|
}, undefined, function(err) {
|
||||||
// initializeModel
|
|
||||||
var string = model.createString(content);
|
|
||||||
model.getRoot().set('content', string);
|
|
||||||
}, function(err) {
|
|
||||||
errorCallback(err);
|
errorCallback(err);
|
||||||
task.error(new Error(err.message));
|
task.error(new Error(err.message));
|
||||||
});
|
});
|
||||||
|
@ -19,12 +19,15 @@
|
|||||||
<li class="wmd-button-group2 btn-group"></li>
|
<li class="wmd-button-group2 btn-group"></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav left-buttons">
|
<ul class="nav left-buttons">
|
||||||
<li class="wmd-button-group3 btn-group">
|
<li class="wmd-button-group3 btn-group"></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="nav left-buttons">
|
||||||
|
<li class="wmd-button-group4 btn-group">
|
||||||
<a class="btn btn-success button-open-discussion"><i class="icon-comment-alt"></i></a>
|
<a class="btn btn-success button-open-discussion"><i class="icon-comment-alt"></i></a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav left-buttons">
|
<ul class="nav left-buttons">
|
||||||
<li class="wmd-button-group4 btn-group"></li>
|
<li class="wmd-button-group5 btn-group"></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav pull-right right-buttons">
|
<ul class="nav pull-right right-buttons">
|
||||||
<li class="offline-status hide">
|
<li class="offline-status hide">
|
||||||
|
@ -9,12 +9,5 @@
|
|||||||
<button class="btn btn-primary action-add-comment">Add</button>
|
<button class="btn btn-primary action-add-comment">Add</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="remove-discussion-confirm hide">
|
|
||||||
<blockquote>Remove this discussion, really?</blockquote>
|
|
||||||
<div class="form-group text-right">
|
|
||||||
<button class="btn btn-default action-remove-discussion-cancel">No</button>
|
|
||||||
<button class="btn btn-primary action-remove-discussion-confirm">Yes</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
@ -32,13 +32,21 @@ define([
|
|||||||
return "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
|
return "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSyncAttributes(path, versionTag, content) {
|
var merge = settings.conflictMode == 'merge';
|
||||||
|
function createSyncAttributes(path, versionTag, content, discussionListJSON) {
|
||||||
|
discussionListJSON = discussionListJSON || '{}';
|
||||||
var syncAttributes = {};
|
var syncAttributes = {};
|
||||||
syncAttributes.provider = dropboxProvider;
|
syncAttributes.provider = dropboxProvider;
|
||||||
syncAttributes.path = path;
|
syncAttributes.path = path;
|
||||||
syncAttributes.version = versionTag;
|
syncAttributes.version = versionTag;
|
||||||
syncAttributes.contentCRC = utils.crc32(content);
|
syncAttributes.contentCRC = utils.crc32(content);
|
||||||
|
syncAttributes.discussionListCRC = utils.crc32(discussionListJSON);
|
||||||
syncAttributes.syncIndex = createSyncIndex(path);
|
syncAttributes.syncIndex = createSyncIndex(path);
|
||||||
|
if(merge === true) {
|
||||||
|
// Need to store the whole content for merge
|
||||||
|
syncAttributes.content = content;
|
||||||
|
syncAttributes.discussionList = discussionListJSON;
|
||||||
|
}
|
||||||
return syncAttributes;
|
return syncAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,11 +61,11 @@ define([
|
|||||||
}
|
}
|
||||||
var fileDescList = [];
|
var fileDescList = [];
|
||||||
_.each(result, function(file) {
|
_.each(result, function(file) {
|
||||||
var syncAttributes = createSyncAttributes(file.path, file.versionTag, file.content);
|
var parsedContent = dropboxProvider.parseSerializedContent(file.content);
|
||||||
|
var syncAttributes = createSyncAttributes(file.path, file.versionTag, parsedContent.content, parsedContent.discussionListJSON);
|
||||||
var syncLocations = {};
|
var syncLocations = {};
|
||||||
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||||
var parsedContent = dropboxProvider.parseSerializedContent(file.content);
|
var fileDesc = fileMgr.createFile(file.name, parsedContent.content, parsedContent.discussionListJSON, syncLocations);
|
||||||
var fileDesc = fileMgr.createFile(file.name, parsedContent.content, parsedContent.discussionList, syncLocations);
|
|
||||||
fileMgr.selectFile(fileDesc);
|
fileMgr.selectFile(fileDesc);
|
||||||
fileDescList.push(fileDesc);
|
fileDescList.push(fileDesc);
|
||||||
});
|
});
|
||||||
@ -86,7 +94,7 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
dropboxProvider.exportFile = function(event, title, content, callback) {
|
dropboxProvider.exportFile = function(event, title, content, discussionListJSON, callback) {
|
||||||
var path = utils.getInputTextValue("#input-sync-export-dropbox-path", event);
|
var path = utils.getInputTextValue("#input-sync-export-dropbox-path", event);
|
||||||
path = checkPath(path);
|
path = checkPath(path);
|
||||||
if(path === undefined) {
|
if(path === undefined) {
|
||||||
@ -104,12 +112,11 @@ define([
|
|||||||
if(error) {
|
if(error) {
|
||||||
return callback(error);
|
return callback(error);
|
||||||
}
|
}
|
||||||
var syncAttributes = createSyncAttributes(result.path, result.versionTag, content);
|
var syncAttributes = createSyncAttributes(result.path, result.versionTag, content, discussionListJSON);
|
||||||
callback(undefined, syncAttributes);
|
callback(undefined, syncAttributes);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var merge = settings.conflictMode == 'merge';
|
|
||||||
dropboxProvider.syncUp = function(content, contentCRC, title, titleCRC, discussionList, discussionListCRC, syncAttributes, callback) {
|
dropboxProvider.syncUp = function(content, contentCRC, title, titleCRC, discussionList, discussionListCRC, syncAttributes, callback) {
|
||||||
if(
|
if(
|
||||||
(syncAttributes.contentCRC == contentCRC) && // Content CRC hasn't changed
|
(syncAttributes.contentCRC == contentCRC) && // Content CRC hasn't changed
|
||||||
@ -188,8 +195,9 @@ define([
|
|||||||
var file = change.stat;
|
var file = change.stat;
|
||||||
var parsedContent = dropboxProvider.parseSerializedContent(file.content);
|
var parsedContent = dropboxProvider.parseSerializedContent(file.content);
|
||||||
var remoteContent = parsedContent.content;
|
var remoteContent = parsedContent.content;
|
||||||
var remoteDiscussionList = parsedContent.discussionList;
|
var remoteDiscussionListJSON = parsedContent.discussionListJSON;
|
||||||
var remoteCRC = dropboxProvider.syncMerge(fileDesc, syncAttributes, remoteContent, fileDesc.title, remoteDiscussionList);
|
var remoteDiscussionList = JSON.parse(remoteDiscussionListJSON);
|
||||||
|
var remoteCRC = dropboxProvider.syncMerge(fileDesc, syncAttributes, remoteContent, fileDesc.title, remoteDiscussionList, remoteDiscussionListJSON);
|
||||||
// Update syncAttributes
|
// Update syncAttributes
|
||||||
syncAttributes.version = file.versionTag;
|
syncAttributes.version = file.versionTag;
|
||||||
if(merge === true) {
|
if(merge === true) {
|
||||||
|
@ -30,14 +30,23 @@ define([
|
|||||||
return "sync." + providerId + "." + id;
|
return "sync." + providerId + "." + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSyncAttributes(id, etag, content, title) {
|
var merge = settings.conflictMode == 'merge';
|
||||||
|
function createSyncAttributes(id, etag, content, title, discussionListJSON) {
|
||||||
|
discussionListJSON = discussionListJSON || '{}';
|
||||||
var syncAttributes = {};
|
var syncAttributes = {};
|
||||||
syncAttributes.provider = gdriveProvider;
|
syncAttributes.provider = gdriveProvider;
|
||||||
syncAttributes.id = id;
|
syncAttributes.id = id;
|
||||||
syncAttributes.etag = etag;
|
syncAttributes.etag = etag;
|
||||||
syncAttributes.contentCRC = utils.crc32(content);
|
syncAttributes.contentCRC = utils.crc32(content);
|
||||||
syncAttributes.titleCRC = utils.crc32(title);
|
syncAttributes.titleCRC = utils.crc32(title);
|
||||||
|
syncAttributes.discussionListCRC = utils.crc32(discussionListJSON);
|
||||||
syncAttributes.syncIndex = createSyncIndex(id);
|
syncAttributes.syncIndex = createSyncIndex(id);
|
||||||
|
if(merge === true) {
|
||||||
|
// Need to store the whole content for merge
|
||||||
|
syncAttributes.content = content;
|
||||||
|
syncAttributes.title = title;
|
||||||
|
syncAttributes.discussionList = discussionListJSON;
|
||||||
|
}
|
||||||
return syncAttributes;
|
return syncAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,12 +62,12 @@ define([
|
|||||||
var fileDescList = [];
|
var fileDescList = [];
|
||||||
var fileDesc;
|
var fileDesc;
|
||||||
_.each(result, function(file) {
|
_.each(result, function(file) {
|
||||||
var syncAttributes = createSyncAttributes(file.id, file.etag, file.content, file.title);
|
var parsedContent = gdriveProvider.parseSerializedContent(file.content);
|
||||||
|
var syncAttributes = createSyncAttributes(file.id, file.etag, parsedContent.content, file.title, parsedContent.discussionListJSON);
|
||||||
syncAttributes.isRealtime = file.isRealtime;
|
syncAttributes.isRealtime = file.isRealtime;
|
||||||
var syncLocations = {};
|
var syncLocations = {};
|
||||||
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||||
var parsedContent = gdriveProvider.parseSerializedContent(file.content);
|
fileDesc = fileMgr.createFile(file.title, parsedContent.content, parsedContent.discussionListJSON, syncLocations);
|
||||||
fileDesc = fileMgr.createFile(file.title, parsedContent.content, parsedContent.discussionList, syncLocations);
|
|
||||||
fileDescList.push(fileDesc);
|
fileDescList.push(fileDesc);
|
||||||
});
|
});
|
||||||
if(fileDesc !== undefined) {
|
if(fileDesc !== undefined) {
|
||||||
@ -88,7 +97,7 @@ define([
|
|||||||
}, 'doc', accountId);
|
}, 'doc', accountId);
|
||||||
};
|
};
|
||||||
|
|
||||||
gdriveProvider.exportFile = function(event, title, content, callback) {
|
gdriveProvider.exportFile = function(event, title, content, discussionListJSON, callback) {
|
||||||
var fileId = utils.getInputTextValue('#input-sync-export-' + providerId + '-fileid');
|
var fileId = utils.getInputTextValue('#input-sync-export-' + providerId + '-fileid');
|
||||||
if(fileId) {
|
if(fileId) {
|
||||||
// Check that file is not synchronized with another an existing
|
// Check that file is not synchronized with another an existing
|
||||||
@ -107,24 +116,23 @@ define([
|
|||||||
callback(error);
|
callback(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
|
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title, discussionListJSON);
|
||||||
callback(undefined, syncAttributes);
|
callback(undefined, syncAttributes);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
gdriveProvider.exportRealtimeFile = function(event, title, content, callback) {
|
gdriveProvider.exportRealtimeFile = function(event, title, content, discussionListJSON, callback) {
|
||||||
var parentId = utils.getInputTextValue('#input-sync-export-' + providerId + '-parentid');
|
var parentId = utils.getInputTextValue('#input-sync-export-' + providerId + '-parentid');
|
||||||
googleHelper.createRealtimeFile(parentId, title, accountId, function(error, result) {
|
googleHelper.createRealtimeFile(parentId, title, accountId, function(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
|
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title, discussionListJSON);
|
||||||
callback(undefined, syncAttributes);
|
callback(undefined, syncAttributes);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var merge = settings.conflictMode == 'merge';
|
|
||||||
gdriveProvider.syncUp = function(content, contentCRC, title, titleCRC, discussionList, discussionListCRC, syncAttributes, callback) {
|
gdriveProvider.syncUp = function(content, contentCRC, title, titleCRC, discussionList, discussionListCRC, syncAttributes, callback) {
|
||||||
if(
|
if(
|
||||||
(syncAttributes.contentCRC == contentCRC) && // Content CRC hasn't changed
|
(syncAttributes.contentCRC == contentCRC) && // Content CRC hasn't changed
|
||||||
@ -228,8 +236,9 @@ define([
|
|||||||
var parsedContent = gdriveProvider.parseSerializedContent(file.content);
|
var parsedContent = gdriveProvider.parseSerializedContent(file.content);
|
||||||
var remoteContent = parsedContent.content;
|
var remoteContent = parsedContent.content;
|
||||||
var remoteTitle = file.title;
|
var remoteTitle = file.title;
|
||||||
var remoteDiscussionList = parsedContent.discussionList;
|
var remoteDiscussionListJSON = parsedContent.discussionListJSON;
|
||||||
var remoteCRC = gdriveProvider.syncMerge(fileDesc, syncAttributes, remoteContent, remoteTitle, remoteDiscussionList);
|
var remoteDiscussionList = JSON.parse(remoteDiscussionListJSON);
|
||||||
|
var remoteCRC = gdriveProvider.syncMerge(fileDesc, syncAttributes, remoteContent, remoteTitle, remoteDiscussionList, remoteDiscussionListJSON);
|
||||||
|
|
||||||
// Update syncAttributes
|
// Update syncAttributes
|
||||||
syncAttributes.etag = file.etag;
|
syncAttributes.etag = file.etag;
|
||||||
@ -237,7 +246,7 @@ define([
|
|||||||
// Need to store the whole content for merge
|
// Need to store the whole content for merge
|
||||||
syncAttributes.content = remoteContent;
|
syncAttributes.content = remoteContent;
|
||||||
syncAttributes.title = remoteTitle;
|
syncAttributes.title = remoteTitle;
|
||||||
syncAttributes.discussionList = remoteDiscussionList;
|
syncAttributes.discussionList = remoteDiscussionListJSON;
|
||||||
}
|
}
|
||||||
syncAttributes.contentCRC = remoteCRC.contentCRC;
|
syncAttributes.contentCRC = remoteCRC.contentCRC;
|
||||||
syncAttributes.titleCRC = remoteCRC.titleCRC;
|
syncAttributes.titleCRC = remoteCRC.titleCRC;
|
||||||
@ -286,9 +295,12 @@ define([
|
|||||||
var realtimeContext;
|
var realtimeContext;
|
||||||
|
|
||||||
function toRealtimeDiscussion(context, discussion) {
|
function toRealtimeDiscussion(context, discussion) {
|
||||||
var commentList = context.model.createList();
|
var realtimeCommentList = context.model.createList();
|
||||||
|
realtimeCommentList.addEventListener(gapi.drive.realtime.EventType.VALUES_ADDED, modelEventListener);
|
||||||
|
realtimeCommentList.addEventListener(gapi.drive.realtime.EventType.VALUES_REMOVED, modelEventListener);
|
||||||
|
realtimeCommentList.addEventListener(gapi.drive.realtime.EventType.VALUES_SET, modelEventListener);
|
||||||
discussion.commentList && discussion.commentList.forEach(function(comment) {
|
discussion.commentList && discussion.commentList.forEach(function(comment) {
|
||||||
commentList.push({
|
realtimeCommentList.push({
|
||||||
author: comment.author,
|
author: comment.author,
|
||||||
content: comment.content
|
content: comment.content
|
||||||
});
|
});
|
||||||
@ -298,11 +310,20 @@ define([
|
|||||||
selectionStart: discussion.selectionStart,
|
selectionStart: discussion.selectionStart,
|
||||||
selectionEnd: discussion.selectionEnd,
|
selectionEnd: discussion.selectionEnd,
|
||||||
type: discussion.type,
|
type: discussion.type,
|
||||||
commentList: commentList
|
commentList: realtimeCommentList
|
||||||
});
|
});
|
||||||
return realtimeDiscussion;
|
return realtimeDiscussion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toRealtimeDiscussionList(context) {
|
||||||
|
var realtimeDiscussionList = context.model.createMap();
|
||||||
|
_.each(context.fileDesc.discussionList, function(localDiscussion) {
|
||||||
|
var realtimeDiscussion = toRealtimeDiscussion(context, localDiscussion);
|
||||||
|
realtimeDiscussionList.set(localDiscussion.discussionIndex, realtimeDiscussion);
|
||||||
|
});
|
||||||
|
return realtimeDiscussionList;
|
||||||
|
}
|
||||||
|
|
||||||
function fromRealtimeDiscussion(realtimeDiscussion) {
|
function fromRealtimeDiscussion(realtimeDiscussion) {
|
||||||
var discussion = {
|
var discussion = {
|
||||||
discussionIndex: realtimeDiscussion.get('discussionIndex'),
|
discussionIndex: realtimeDiscussion.get('discussionIndex'),
|
||||||
@ -311,13 +332,25 @@ define([
|
|||||||
};
|
};
|
||||||
var type = realtimeDiscussion.get('type');
|
var type = realtimeDiscussion.get('type');
|
||||||
type && (discussion.type = type);
|
type && (discussion.type = type);
|
||||||
var commentList = realtimeDiscussion.get('discussionIndex').asArray();
|
var commentList = realtimeDiscussion.get('commentList').asArray();
|
||||||
commentList.length && (discussion.commentList = commentList);
|
commentList.length && (discussion.commentList = commentList);
|
||||||
return discussion;
|
return discussion;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeDiscussion(localDiscussion, realtimeDiscussion, takeServer) {
|
function fromRealtimeDiscussionList(realtimeDiscussionList) {
|
||||||
if(!takeServer) {
|
var localDiscussionList = {};
|
||||||
|
realtimeDiscussionList.keys().forEach(function(discussionIndex) {
|
||||||
|
var realtimeDiscussion = realtimeDiscussionList.get(discussionIndex);
|
||||||
|
var discussion = fromRealtimeDiscussion(realtimeDiscussion);
|
||||||
|
localDiscussionList[discussionIndex] = discussion;
|
||||||
|
});
|
||||||
|
return localDiscussionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeDiscussion(localDiscussion, realtimeDiscussion, isServerChange) {
|
||||||
|
var commentsChanged = false;
|
||||||
|
// We only pay attention to local selection modifications
|
||||||
|
if(!isServerChange) {
|
||||||
realtimeDiscussion.set('selectionStart', localDiscussion.selectionStart);
|
realtimeDiscussion.set('selectionStart', localDiscussion.selectionStart);
|
||||||
realtimeDiscussion.set('selectionEnd', localDiscussion.selectionEnd);
|
realtimeDiscussion.set('selectionEnd', localDiscussion.selectionEnd);
|
||||||
}
|
}
|
||||||
@ -329,62 +362,72 @@ define([
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
var realtimeCommentList = realtimeDiscussion.get('commentList');
|
var realtimeCommentList = realtimeDiscussion.get('commentList');
|
||||||
|
var localCommentList = localDiscussion.commentList;
|
||||||
function checkLocalComment(comment, index) {
|
function checkLocalComment(comment, index) {
|
||||||
if(!isInDiscussion(comment, realtimeCommentList.asArray())) {
|
if(!isInDiscussion(comment, realtimeCommentList.asArray())) {
|
||||||
if(takeServer) {
|
if(isServerChange) {
|
||||||
localDiscussion.splice(index, 1);
|
localCommentList.splice(index, 1);
|
||||||
|
commentsChanged = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
realtimeDiscussion.get('commentList').push(comment);
|
realtimeCommentList.push(comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while(localDiscussion.commentList.some(checkLocalComment)) {}
|
while(localCommentList.some(checkLocalComment)) {}
|
||||||
function checkRealtimeComment(comment, index) {
|
function checkRealtimeComment(comment, index) {
|
||||||
if(!isInDiscussion(comment, localDiscussion.commentList)) {
|
if(!isInDiscussion(comment, localCommentList)) {
|
||||||
if(!takeServer) {
|
if(!isServerChange) {
|
||||||
realtimeCommentList.remove(index);
|
realtimeCommentList.remove(index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
localDiscussion.commentList.push(comment);
|
localCommentList.push(comment);
|
||||||
|
commentsChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while(realtimeCommentList.asArray().some(checkRealtimeComment)) {}
|
while(realtimeCommentList.asArray().some(checkRealtimeComment)) {}
|
||||||
|
return commentsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeDiscussionList(context, takeServer) {
|
function mergeDiscussionList(context, isServerChange) {
|
||||||
|
var commentsChanged = false;
|
||||||
var localDiscussionList = context.fileDesc.discussionList;
|
var localDiscussionList = context.fileDesc.discussionList;
|
||||||
_.each(localDiscussionList, function(localDiscussion) {
|
_.values(localDiscussionList).forEach(function(localDiscussion) {
|
||||||
var realtimeDiscussion = context.discussionList.get(localDiscussion.discussionIndex);
|
var realtimeDiscussion = context.realtimeDiscussionList.get(localDiscussion.discussionIndex);
|
||||||
if(realtimeDiscussion) {
|
if(realtimeDiscussion) {
|
||||||
mergeDiscussion(localDiscussion, realtimeDiscussion, takeServer);
|
commentsChanged |= mergeDiscussion(localDiscussion, realtimeDiscussion, isServerChange);
|
||||||
|
}
|
||||||
|
else if(!isServerChange) {
|
||||||
|
realtimeDiscussion = toRealtimeDiscussion(context, localDiscussion);
|
||||||
|
context.realtimeDiscussionList.set(localDiscussion.discussionIndex, realtimeDiscussion);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
realtimeDiscussion = toRealtimeDiscussion(context, localDiscussion);
|
delete localDiscussionList[localDiscussion.discussionIndex];
|
||||||
context.discussionList.set(localDiscussion.discussionIndex, realtimeDiscussion);
|
eventMgr.onDiscussionRemoved(context.fileDesc, localDiscussion);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
context.discussionList.keys().forEach(function(discussionIndex) {
|
context.realtimeDiscussionList.keys().forEach(function(discussionIndex) {
|
||||||
var realtimeDiscussion = context.discussionList.get(discussionIndex);
|
var realtimeDiscussion = context.realtimeDiscussionList.get(discussionIndex);
|
||||||
var localDiscussion = localDiscussionList[discussionIndex];
|
var localDiscussion = localDiscussionList[discussionIndex];
|
||||||
if(localDiscussion) {
|
if(localDiscussion) {
|
||||||
mergeDiscussion(localDiscussion, realtimeDiscussion, takeServer);
|
commentsChanged |= mergeDiscussion(localDiscussion, realtimeDiscussion, isServerChange);
|
||||||
|
}
|
||||||
|
else if(isServerChange) {
|
||||||
|
var discussion = fromRealtimeDiscussion(realtimeDiscussion);
|
||||||
|
localDiscussionList[discussionIndex] = discussion;
|
||||||
|
eventMgr.onDiscussionCreated(context.fileDesc, discussion);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var discussion = {
|
context.realtimeDiscussionList.delete(discussionIndex);
|
||||||
discussionIndex: discussionIndex,
|
|
||||||
selectionStart: realtimeDiscussion.get('selectionStart'),
|
|
||||||
selectionEnd: realtimeDiscussion.get('selectionEnd'),
|
|
||||||
commentList: realtimeDiscussion.get('commentList').asArray()
|
|
||||||
};
|
|
||||||
localDiscussionList[discussionIndex] = discussion;
|
|
||||||
eventMgr.onCommentsChanged(context.fileDesc);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
context.fileDesc.discussionList = localDiscussionList; // Write in localStorage
|
context.fileDesc.discussionList = localDiscussionList; // Write in localStorage
|
||||||
|
if(commentsChanged) {
|
||||||
|
eventMgr.onCommentsChanged(context.fileDesc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCRCs() {
|
function updateCRCs() {
|
||||||
@ -392,27 +435,84 @@ define([
|
|||||||
if(!context) {
|
if(!context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.syncAttributes.contentCRC = utils.crc32(context.string.getText());
|
var syncAttributes = context.syncAttributes;
|
||||||
|
var content = context.realtimeString.getText();
|
||||||
|
syncAttributes.contentCRC = utils.crc32(content);
|
||||||
var discussionList = {};
|
var discussionList = {};
|
||||||
context.discussionList.keys().forEach(function(discussionIndex) {
|
context.realtimeDiscussionList.keys().forEach(function(discussionIndex) {
|
||||||
var discussion = fromRealtimeDiscussion(context.discussionList.get(discussionIndex));
|
var discussion = fromRealtimeDiscussion(context.realtimeDiscussionList.get(discussionIndex));
|
||||||
discussionList[discussion.discussionIndex] = discussion;
|
discussionList[discussion.discussionIndex] = discussion;
|
||||||
});
|
});
|
||||||
context.syncAttributes.discussionListCRC = utils.crc32(JSON.stringify(discussionList));
|
var discussionListJSON = JSON.stringify(discussionList);
|
||||||
|
syncAttributes.discussionListCRC = utils.crc32(discussionListJSON);
|
||||||
|
if(merge === true) {
|
||||||
|
// Need to store the whole content for merge
|
||||||
|
syncAttributes.content = content;
|
||||||
|
syncAttributes.discussionList = discussionListJSON;
|
||||||
|
}
|
||||||
utils.storeAttributes(context.syncAttributes);
|
utils.storeAttributes(context.syncAttributes);
|
||||||
}
|
}
|
||||||
var onChanges = _.debounce(function() {
|
|
||||||
|
var onChange = (function() {
|
||||||
|
var debouncedOnChange = _.debounce(function() {
|
||||||
var context = realtimeContext;
|
var context = realtimeContext;
|
||||||
if(!context) {
|
if(!context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(context.isServerChange) {
|
||||||
|
logger.log('Realtime syncing remote changes');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Model is supposed to be updated on local modifications
|
||||||
|
context.model.beginCompoundOperation();
|
||||||
|
logger.log('Realtime syncing local changes');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check content modifications
|
||||||
|
var localContent = context.fileDesc.content;
|
||||||
|
var remoteContent = context.realtimeString.getText();
|
||||||
|
var contentChanged = localContent != remoteContent;
|
||||||
|
if(contentChanged) {
|
||||||
|
if(context.isServerChange) {
|
||||||
|
editor.setValue(remoteContent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.realtimeString.setText(localContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check discussion modifications
|
||||||
|
mergeDiscussionList(context, context.isServerChange);
|
||||||
|
|
||||||
|
|
||||||
|
// For local changes, CRCs are updated on "save success" event
|
||||||
|
if(context.isServerChange) {
|
||||||
|
updateCRCs();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.model.endCompoundOperation();
|
||||||
|
}
|
||||||
context.isServerChange = false;
|
context.isServerChange = false;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
return function(fileDesc) {
|
||||||
eventMgr.addListener('onContentChanged', onChanges);
|
if(realtimeContext && realtimeContext.fileDesc === fileDesc) {
|
||||||
eventMgr.addListener('onDiscussionCreated', onChanges);
|
debouncedOnChange();
|
||||||
eventMgr.addListener('onDiscussionRemoved', onChanges);
|
}
|
||||||
eventMgr.addListener('onCommentsChanged', onChanges);
|
};
|
||||||
|
})();
|
||||||
|
function modelEventListener(evt) {
|
||||||
|
if(!realtimeContext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(evt.isLocal === false) {
|
||||||
|
realtimeContext.isServerChange = true;
|
||||||
|
}
|
||||||
|
onChange(realtimeContext.fileDesc);
|
||||||
|
}
|
||||||
|
eventMgr.addListener('onContentChanged', onChange);
|
||||||
|
eventMgr.addListener('onDiscussionCreated', onChange);
|
||||||
|
eventMgr.addListener('onDiscussionRemoved', onChange);
|
||||||
|
eventMgr.addListener('onCommentsChanged', onChange);
|
||||||
|
|
||||||
// Start realtime synchronization
|
// Start realtime synchronization
|
||||||
gdriveProvider.startRealtimeSync = function(fileDesc, syncAttributes) {
|
gdriveProvider.startRealtimeSync = function(fileDesc, syncAttributes) {
|
||||||
@ -421,13 +521,12 @@ define([
|
|||||||
syncAttributes: syncAttributes
|
syncAttributes: syncAttributes
|
||||||
};
|
};
|
||||||
realtimeContext = context;
|
realtimeContext = context;
|
||||||
googleHelper.loadRealtime(syncAttributes.id, fileDesc.content, accountId, function(err, doc) {
|
googleHelper.loadRealtime(syncAttributes.id, accountId, function(err, doc) {
|
||||||
if(err || !doc) {
|
if(err || !doc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If user just switched to another document or file has just been
|
// If user just switched to another document or file has just been reselected
|
||||||
// reselected
|
|
||||||
if(context !== realtimeContext) {
|
if(context !== realtimeContext) {
|
||||||
return doc.close();
|
return doc.close();
|
||||||
}
|
}
|
||||||
@ -436,94 +535,51 @@ define([
|
|||||||
context.document = doc;
|
context.document = doc;
|
||||||
var model = doc.getModel();
|
var model = doc.getModel();
|
||||||
context.model = model;
|
context.model = model;
|
||||||
|
|
||||||
|
// Get or create content string
|
||||||
var realtimeString = model.getRoot().get('content');
|
var realtimeString = model.getRoot().get('content');
|
||||||
context.string = realtimeString;
|
if(!realtimeString) {
|
||||||
|
// Initial value
|
||||||
// Saves model content checksum
|
realtimeString = model.createString(fileDesc.content);
|
||||||
function updateContentState() {
|
model.getRoot().set('content', realtimeString);
|
||||||
syncAttributes.contentCRC = utils.crc32(realtimeString.getText());
|
}
|
||||||
utils.storeAttributes(syncAttributes);
|
context.realtimeString = realtimeString;
|
||||||
}
|
// Listen to content modifications
|
||||||
|
realtimeString.addEventListener(gapi.drive.realtime.EventType.TEXT_INSERTED, modelEventListener);
|
||||||
var debouncedRefreshPreview = _.debounce(pagedownEditor.refreshPreview, 100);
|
realtimeString.addEventListener(gapi.drive.realtime.EventType.TEXT_DELETED, modelEventListener);
|
||||||
|
|
||||||
// Listen to insert text events
|
|
||||||
realtimeString.addEventListener(gapi.drive.realtime.EventType.TEXT_INSERTED, function(evt) {
|
|
||||||
if(aceEditor !== undefined && (isAceUpToDate === false || e.isLocal === false)) {
|
|
||||||
// Update ACE editor
|
|
||||||
var position = aceEditor.session.doc.indexToPosition(e.index);
|
|
||||||
aceEditor.session.insert(position, e.text);
|
|
||||||
isAceUpToDate = true;
|
|
||||||
}
|
|
||||||
// If modifications come down from a collaborator
|
|
||||||
if(e.isLocal === false) {
|
|
||||||
logger.log("Google Drive realtime document updated from server");
|
|
||||||
updateContentState();
|
|
||||||
aceEditor === undefined && debouncedRefreshPreview();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Listen to delete text events
|
|
||||||
realtimeString.addEventListener(gapi.drive.realtime.EventType.TEXT_DELETED, function(e) {
|
|
||||||
if(aceEditor !== undefined && (isAceUpToDate === false || e.isLocal === false)) {
|
|
||||||
// Update ACE editor
|
|
||||||
var range = (function(posStart, posEnd) {
|
|
||||||
return new Range(posStart.row, posStart.column, posEnd.row, posEnd.column);
|
|
||||||
})(aceEditor.session.doc.indexToPosition(e.index), aceEditor.session.doc.indexToPosition(e.index + e.text.length));
|
|
||||||
aceEditor.session.remove(range);
|
|
||||||
isAceUpToDate = true;
|
|
||||||
}
|
|
||||||
// If modifications come down from a collaborator
|
|
||||||
if(e.isLocal === false) {
|
|
||||||
logger.log("Google Drive realtime document updated from server");
|
|
||||||
updateContentState();
|
|
||||||
aceEditor === undefined && debouncedRefreshPreview();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
doc.addEventListener(gapi.drive.realtime.EventType.DOCUMENT_SAVE_STATE_CHANGED, function(e) {
|
|
||||||
// Save success event
|
|
||||||
if(e.isPending === false && e.isSaving === false) {
|
|
||||||
logger.log("Google Drive realtime document successfully saved on server");
|
|
||||||
updateContentState();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Try to merge offline modifications
|
|
||||||
var localContent = fileDesc.content;
|
|
||||||
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
|
|
||||||
var remoteContent = realtimeString.getText();
|
|
||||||
var remoteContentCRC = utils.crc32(remoteContent);
|
|
||||||
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
|
|
||||||
var fileContentChanged = localContent != remoteContent;
|
|
||||||
model.beginCompoundOperation('Open and merge');
|
|
||||||
if(fileContentChanged === true && localContentChanged === true) {
|
|
||||||
if(remoteContentChanged === true) {
|
|
||||||
// Conflict detected
|
|
||||||
fileMgr.createFile(fileDesc.title + " (backup)", localContent);
|
|
||||||
eventMgr.onMessage('Conflict detected on "' + fileDesc.title + '". A backup has been created locally.');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Add local modifications if no collaborators change
|
|
||||||
realtimeString.setText(localContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update content state according to collaborators changes
|
|
||||||
if(remoteContentChanged === true) {
|
|
||||||
logger.log("Google Drive realtime document updated from server");
|
|
||||||
aceEditor !== undefined && aceEditor.setValue(remoteContent, -1);
|
|
||||||
updateContentState();
|
|
||||||
aceEditor === undefined && debouncedRefreshPreview();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Get or create discussion map
|
||||||
var realtimeDiscussionList = model.getRoot().get('discussionList');
|
var realtimeDiscussionList = model.getRoot().get('discussionList');
|
||||||
|
|
||||||
if(!realtimeDiscussionList) {
|
if(!realtimeDiscussionList) {
|
||||||
realtimeDiscussionList = model.createMap();
|
// Initial value
|
||||||
|
realtimeDiscussionList = toRealtimeDiscussionList(context);
|
||||||
model.getRoot().set('discussionList', realtimeDiscussionList);
|
model.getRoot().set('discussionList', realtimeDiscussionList);
|
||||||
}
|
}
|
||||||
context.discussionList = realtimeDiscussionList;
|
context.realtimeDiscussionList = realtimeDiscussionList;
|
||||||
mergeDiscussionList(context, remoteContentChanged === true);
|
// Listen to discussion modifications
|
||||||
model.endCompoundOperation();
|
realtimeDiscussionList.addEventListener(gapi.drive.realtime.EventType.VALUE_CHANGED, modelEventListener);
|
||||||
|
realtimeDiscussionList.keys().forEach(function(discussionIndex) {
|
||||||
|
var realtimeDiscussion = context.realtimeDiscussionList.get(discussionIndex);
|
||||||
|
var realtimeCommentList = realtimeDiscussion.get('commentList');
|
||||||
|
// Listen to comment modifications in every discussion
|
||||||
|
realtimeCommentList.addEventListener(gapi.drive.realtime.EventType.VALUES_ADDED, modelEventListener);
|
||||||
|
realtimeCommentList.addEventListener(gapi.drive.realtime.EventType.VALUES_REMOVED, modelEventListener);
|
||||||
|
realtimeCommentList.addEventListener(gapi.drive.realtime.EventType.VALUES_SET, modelEventListener);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also listen to "save success" event
|
||||||
|
doc.addEventListener(gapi.drive.realtime.EventType.DOCUMENT_SAVE_STATE_CHANGED, function(e) {
|
||||||
|
if(e.isPending === false && e.isSaving === false) {
|
||||||
|
updateCRCs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merge offline modifications
|
||||||
|
var remoteContent = realtimeString.getText();
|
||||||
|
var remoteTitle = fileDesc.title; // Not synchronized, so make sure no changes will be detected
|
||||||
|
var remoteDiscussionList = fromRealtimeDiscussionList(realtimeDiscussionList);
|
||||||
|
var remoteDiscussionListJSON = JSON.stringify(remoteDiscussionList);
|
||||||
|
gdriveProvider.syncMerge(fileDesc, syncAttributes, remoteContent, remoteTitle, remoteDiscussionList, remoteDiscussionListJSON);
|
||||||
|
|
||||||
// Save undo/redo buttons default actions
|
// Save undo/redo buttons default actions
|
||||||
undoExecute = pagedownEditor.uiManager.buttons.undo.execute;
|
undoExecute = pagedownEditor.uiManager.buttons.undo.execute;
|
||||||
@ -533,28 +589,20 @@ define([
|
|||||||
// Set temporary actions for undo/redo buttons
|
// Set temporary actions for undo/redo buttons
|
||||||
pagedownEditor.uiManager.buttons.undo.execute = function() {
|
pagedownEditor.uiManager.buttons.undo.execute = function() {
|
||||||
if(model.canUndo) {
|
if(model.canUndo) {
|
||||||
// This flag is used to avoid replaying editor's own
|
|
||||||
// modifications (assuming it's synchronous)
|
|
||||||
isAceUpToDate = false;
|
|
||||||
model.undo();
|
model.undo();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
pagedownEditor.uiManager.buttons.redo.execute = function() {
|
pagedownEditor.uiManager.buttons.redo.execute = function() {
|
||||||
if(model.canRedo) {
|
if(model.canRedo) {
|
||||||
// This flag is used to avoid replaying editor's own
|
|
||||||
// modifications (assuming it's synchronous)
|
|
||||||
isAceUpToDate = false;
|
|
||||||
model.redo();
|
model.redo();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add event handler for model's UndoRedoStateChanged events
|
// Add event handler for model's UndoRedoStateChanged events
|
||||||
pagedownEditor.uiManager.setUndoRedoButtonStates = function() {
|
pagedownEditor.uiManager.setUndoRedoButtonStates = _.debounce(function() {
|
||||||
setTimeout(function() {
|
|
||||||
pagedownEditor.uiManager.setButtonState(pagedownEditor.uiManager.buttons.undo, model.canUndo);
|
pagedownEditor.uiManager.setButtonState(pagedownEditor.uiManager.buttons.undo, model.canUndo);
|
||||||
pagedownEditor.uiManager.setButtonState(pagedownEditor.uiManager.buttons.redo, model.canRedo);
|
pagedownEditor.uiManager.setButtonState(pagedownEditor.uiManager.buttons.redo, model.canRedo);
|
||||||
}, 50);
|
}, 10);
|
||||||
};
|
|
||||||
pagedownEditor.uiManager.setUndoRedoButtonStates();
|
pagedownEditor.uiManager.setUndoRedoButtonStates();
|
||||||
model.addEventListener(gapi.drive.realtime.EventType.UNDO_REDO_STATE_CHANGED, function() {
|
model.addEventListener(gapi.drive.realtime.EventType.UNDO_REDO_STATE_CHANGED, function() {
|
||||||
pagedownEditor.uiManager.setUndoRedoButtonStates();
|
pagedownEditor.uiManager.setUndoRedoButtonStates();
|
||||||
@ -611,14 +659,14 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Perform AutoSync
|
// Perform AutoSync
|
||||||
gdriveProvider.autosyncFile = function(title, content, config, callback) {
|
gdriveProvider.autosyncFile = function(title, content, discussionListJSON, config, callback) {
|
||||||
var parentId = config.parentId;
|
var parentId = config.parentId;
|
||||||
googleHelper.upload(undefined, parentId, title, content, undefined, undefined, accountId, function(error, result) {
|
googleHelper.upload(undefined, parentId, title, content, undefined, undefined, accountId, function(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
|
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title, discussionListJSON);
|
||||||
callback(undefined, syncAttributes);
|
callback(undefined, syncAttributes);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -129,7 +129,7 @@
|
|||||||
@alert-border-radius: 0;
|
@alert-border-radius: 0;
|
||||||
@label-warning-bg: spin(darken(@logo-yellow, 4%), -6);
|
@label-warning-bg: spin(darken(@logo-yellow, 4%), -6);
|
||||||
@label-danger-bg: spin(darken(@logo-orange, 4%), -4);
|
@label-danger-bg: spin(darken(@logo-orange, 4%), -4);
|
||||||
@state-warning-text: spin(darken(@logo-yellow, 15%), -6);
|
@state-warning-text: spin(darken(@logo-yellow, 14%), -6);
|
||||||
@state-warning-bg: fade(spin(@logo-yellow, -6), 12%);
|
@state-warning-bg: fade(spin(@logo-yellow, -6), 12%);
|
||||||
@state-warning-border: fade(spin(@logo-yellow, -6), 24%);
|
@state-warning-border: fade(spin(@logo-yellow, -6), 24%);
|
||||||
@state-danger-text: spin(darken(@logo-orange, 18%), -4);
|
@state-danger-text: spin(darken(@logo-orange, 18%), -4);
|
||||||
|
@ -310,7 +310,7 @@ define([
|
|||||||
eventMgr.addListener("onFileCreated", function(fileDesc) {
|
eventMgr.addListener("onFileCreated", function(fileDesc) {
|
||||||
if(_.size(fileDesc.syncLocations) === 0) {
|
if(_.size(fileDesc.syncLocations) === 0) {
|
||||||
_.each(providerMap, function(provider) {
|
_.each(providerMap, function(provider) {
|
||||||
provider.autosyncConfig.enabled && provider.autosyncFile(fileDesc.title, fileDesc.content, provider.autosyncConfig, function(error, syncAttributes) {
|
provider.autosyncConfig.enabled && provider.autosyncFile(fileDesc.title, fileDesc.content, fileDesc.discussionListJSON, provider.autosyncConfig, function(error, syncAttributes) {
|
||||||
if(error) {
|
if(error) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -350,7 +350,7 @@ define([
|
|||||||
return eventMgr.onError("Real time collaborative document can't be synchronized with multiple locations");
|
return eventMgr.onError("Real time collaborative document can't be synchronized with multiple locations");
|
||||||
}
|
}
|
||||||
// Perform the provider's real time export
|
// Perform the provider's real time export
|
||||||
provider.exportRealtimeFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
provider.exportRealtimeFile(event, fileDesc.title, fileDesc.content, fileDesc.discussionListJSON, function(error, syncAttributes) {
|
||||||
if(error) {
|
if(error) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -370,7 +370,7 @@ define([
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Perform the provider's standard export
|
// Perform the provider's standard export
|
||||||
provider.exportFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
provider.exportFile(event, fileDesc.title, fileDesc.content, fileDesc.discussionListJSON, function(error, syncAttributes) {
|
||||||
if(error) {
|
if(error) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user