").insertAfter('.CodeMirror-cursors');
}
if ($('div[data-clientid="' + user.id + '"]').length <= 0) {
var cursor = $('
');
cursor.attr('data-line', user.cursor.line);
cursor.attr('data-ch', user.cursor.ch);
cursor.attr('data-offset-left', 0);
cursor.attr('data-offset-top', 0);
var cursorbar = $('
');
cursorbar[0].style.height = defaultTextHeight + 'px';
cursorbar[0].style.borderLeft = '2px solid ' + user.color;
var icon = '
';
var cursortag = $('
' + icon + ' ' + user.name + '
');
//cursortag[0].style.background = color;
cursortag[0].style.color = user.color;
cursor.attr('data-mode', 'state');
cursor.hover(
function () {
if (cursor.attr('data-mode') == 'hover')
cursortag.stop(true).fadeIn("fast");
},
function () {
if (cursor.attr('data-mode') == 'hover')
cursortag.stop(true).fadeOut("fast");
});
function switchMode(ele) {
if (ele.attr('data-mode') == 'state')
ele.attr('data-mode', 'hover');
else if (ele.attr('data-mode') == 'hover')
ele.attr('data-mode', 'state');
}
function switchTag(ele) {
if (ele.css('display') === 'none')
ele.stop(true).fadeIn("fast");
else
ele.stop(true).fadeOut("fast");
}
var hideCursorTagDelay = 2000;
var hideCursorTagTimer = null;
function hideCursorTag() {
if (cursor.attr('data-mode') == 'hover')
cursortag.fadeOut("fast");
}
cursor.on('touchstart', function (e) {
var display = cursortag.css('display');
cursortag.stop(true).fadeIn("fast");
clearTimeout(hideCursorTagTimer);
hideCursorTagTimer = setTimeout(hideCursorTag, hideCursorTagDelay);
if (display === 'none') {
e.preventDefault();
e.stopPropagation();
}
});
cursortag.on('mousedown touchstart', function (e) {
if (cursor.attr('data-mode') == 'state')
switchTag(cursortag);
switchMode(cursor);
e.preventDefault();
e.stopPropagation();
});
cursor.append(cursorbar);
cursor.append(cursortag);
cursor[0].style.left = coord.left + 'px';
cursor[0].style.top = coord.top + 'px';
$('.other-cursors').append(cursor);
if (!user.idle)
cursor.stop(true).fadeIn();
checkCursorTag(coord, cursortag);
} else {
var cursor = $('div[data-clientid="' + user.id + '"]');
var lineDiff = Math.abs(cursor.attr('data-line') - user.cursor.line);
cursor.attr('data-line', user.cursor.line);
cursor.attr('data-ch', user.cursor.ch);
var cursorbar = cursor.find('.cursorbar');
cursorbar[0].style.height = defaultTextHeight + 'px';
cursorbar[0].style.borderLeft = '2px solid ' + user.color;
var cursortag = cursor.find('.cursortag');
cursortag.find('i').removeClass().addClass('fa').addClass(iconClass);
cursortag.find(".name").text(user.name);
if (cursor.css('display') === 'none') {
cursor[0].style.left = coord.left + 'px';
cursor[0].style.top = coord.top + 'px';
} else {
cursor.animate({
"left": coord.left,
"top": coord.top
}, {
duration: cursorAnimatePeriod,
queue: false
});
}
if (user.idle && cursor.css('display') !== 'none')
cursor.stop(true).fadeOut();
else if (!user.idle && cursor.css('display') === 'none')
cursor.stop(true).fadeIn();
checkCursorTag(coord, cursortag);
}
}
//editor actions
function enforceMaxLength(cm, change) {
var maxLength = cm.getOption("maxLength");
if (maxLength && change.update) {
var str = change.text.join("\n");
var delta = str.length - (cm.indexFromPos(change.to) - cm.indexFromPos(change.from));
if (delta <= 0) {
return false;
}
delta = cm.getValue().length + delta - maxLength;
if (delta > 0) {
str = str.substr(0, str.length - delta);
change.update(change.from, change.to, str.split("\n"));
return true;
}
}
return false;
}
var ignoreEmitEvents = ['setValue', 'ignoreHistory'];
editor.on('beforeChange', function (cm, change) {
if (debug)
console.debug(change);
if (enforceMaxLength(cm, change)) {
$('.limit-modal').modal('show');
}
var isIgnoreEmitEvent = (ignoreEmitEvents.indexOf(change.origin) != -1);
if (!isIgnoreEmitEvent) {
if (!havePermission()) {
change.canceled = true;
switch (permission) {
case "editable":
$('.signin-modal').modal('show');
break;
case "locked":
case "private":
$('.locked-modal').modal('show');
break;
}
}
} else {
if (change.origin == 'ignoreHistory') {
setHaveUnreadChanges(true);
updateTitleReminder();
}
}
if (cmClient && !socket.connected)
cmClient.editorAdapter.ignoreNextChange = true;
});
editor.on('cut', function () {
windowResize(); //workaround for scrollMap
});
editor.on('paste', function () {
windowResize(); //workaround for scrollMap
});
editor.on('changes', function (cm, changes) {
updateHistory();
var docLength = editor.getValue().length;
//workaround for big documents
var newViewportMargin = 20;
if (docLength > 20000) {
newViewportMargin = 1;
} else if (docLength > 10000) {
newViewportMargin = 10;
} else if (docLength > 5000) {
newViewportMargin = 15;
}
if (newViewportMargin != viewportMargin) {
viewportMargin = newViewportMargin;
windowResize();
}
checkEditorScrollbar();
});
editor.on('focus', function (cm) {
for (var i = 0; i < onlineUsers.length; i++) {
if (onlineUsers[i].id == personalInfo.id) {
onlineUsers[i].cursor = editor.getCursor();
}
}
personalInfo['cursor'] = editor.getCursor();
socket.emit('cursor focus', editor.getCursor());
});
editor.on('cursorActivity', function (cm) {
updateStatusBar();
cursorActivity();
});
editor.on('beforeSelectionChange', function (doc, selections) {
if (selections)
selection = selections.ranges[0];
else
selection = null;
updateStatusBar();
});
var cursorActivity = _.debounce(cursorActivityInner, cursorActivityDebounce);
function cursorActivityInner() {
if (editorHasFocus() && !Visibility.hidden()) {
for (var i = 0; i < onlineUsers.length; i++) {
if (onlineUsers[i].id == personalInfo.id) {
onlineUsers[i].cursor = editor.getCursor();
}
}
personalInfo['cursor'] = editor.getCursor();
socket.emit('cursor activity', editor.getCursor());
}
}
editor.on('blur', function (cm) {
for (var i = 0; i < onlineUsers.length; i++) {
if (onlineUsers[i].id == personalInfo.id) {
onlineUsers[i].cursor = null;
}
}
personalInfo['cursor'] = null;
socket.emit('cursor blur');
});
function saveInfo() {
var scrollbarStyle = editor.getOption('scrollbarStyle');
var left = $(window).scrollLeft();
var top = $(window).scrollTop();
switch (currentMode) {
case modeType.edit:
if (scrollbarStyle == 'native') {
lastInfo.edit.scroll.left = left;
lastInfo.edit.scroll.top = top;
} else {
lastInfo.edit.scroll = editor.getScrollInfo();
}
break;
case modeType.view:
lastInfo.view.scroll.left = left;
lastInfo.view.scroll.top = top;
break;
case modeType.both:
lastInfo.edit.scroll = editor.getScrollInfo();
lastInfo.view.scroll.left = ui.area.view.scrollLeft();
lastInfo.view.scroll.top = ui.area.view.scrollTop();
break;
}
lastInfo.edit.cursor = editor.getCursor();
lastInfo.needRestore = true;
}
function restoreInfo() {
var scrollbarStyle = editor.getOption('scrollbarStyle');
if (lastInfo.needRestore) {
var line = lastInfo.edit.cursor.line;
var ch = lastInfo.edit.cursor.ch;
editor.setCursor(line, ch);
switch (currentMode) {
case modeType.edit:
if (scrollbarStyle == 'native') {
$(window).scrollLeft(lastInfo.edit.scroll.left);
$(window).scrollTop(lastInfo.edit.scroll.top);
} else {
var left = lastInfo.edit.scroll.left;
var top = lastInfo.edit.scroll.top;
editor.scrollIntoView();
editor.scrollTo(left, top);
}
break;
case modeType.view:
$(window).scrollLeft(lastInfo.view.scroll.left);
$(window).scrollTop(lastInfo.view.scroll.top);
break;
case modeType.both:
var left = lastInfo.edit.scroll.left;
var top = lastInfo.edit.scroll.top;
editor.scrollIntoView();
editor.scrollTo(left, top);
ui.area.view.scrollLeft(lastInfo.view.scroll.left);
ui.area.view.scrollTop(lastInfo.view.scroll.top);
break;
}
lastInfo.needRestore = false;
}
}
//view actions
function refreshView() {
ui.area.markdown.html('');
isDirty = true;
updateViewInner();
}
var updateView = _.debounce(updateViewInner, updateViewDebounce);
var lastResult = null;
function updateViewInner() {
if (currentMode == modeType.edit || !isDirty) return;
var value = editor.getValue();
var lastMeta = md.meta;
md.meta = {};
var rendered = md.render(value);
// only render again when meta changed
if (JSON.stringify(md.meta) != JSON.stringify(lastMeta)) {
parseMeta(md, ui.area.codemirror, ui.area.markdown, $('#toc'), $('#toc-affix'));
rendered = md.render(value);
}
// prevent XSS
rendered = preventXSS(rendered);
var result = postProcess(rendered).children().toArray();
partialUpdate(result, lastResult, ui.area.markdown.children().toArray());
if (result && lastResult && result.length != lastResult.length)
updateDataAttrs(result, ui.area.markdown.children().toArray());
lastResult = $(result).clone();
finishView(ui.area.markdown);
autoLinkify(ui.area.markdown);
deduplicatedHeaderId(ui.area.markdown);
renderTOC(ui.area.markdown);
generateToc('toc');
generateToc('toc-affix');
generateScrollspy();
updateScrollspy();
smoothHashScroll();
isDirty = false;
clearMap();
//buildMap();
updateTitleReminder();
}
var updateHistoryDebounce = 600;
var updateHistory = _.debounce(updateHistoryInner, updateHistoryDebounce)
function updateHistoryInner() {
writeHistory(ui.area.markdown);
}
function updateDataAttrs(src, des) {
//sync data attr startline and endline
for (var i = 0; i < src.length; i++) {
copyAttribute(src[i], des[i], 'data-startline');
copyAttribute(src[i], des[i], 'data-endline');
}
}
function partialUpdate(src, tar, des) {
if (!src || src.length == 0 || !tar || tar.length == 0 || !des || des.length == 0) {
ui.area.markdown.html(src);
return;
}
if (src.length == tar.length) { //same length
for (var i = 0; i < src.length; i++) {
copyAttribute(src[i], des[i], 'data-startline');
copyAttribute(src[i], des[i], 'data-endline');
var rawSrc = cloneAndRemoveDataAttr(src[i]);
var rawTar = cloneAndRemoveDataAttr(tar[i]);
if (rawSrc.outerHTML != rawTar.outerHTML) {
//console.log(rawSrc);
//console.log(rawTar);
$(des[i]).replaceWith(src[i]);
}
}
} else { //diff length
var start = 0;
var end = 0;
//find diff start position
for (var i = 0; i < tar.length; i++) {
//copyAttribute(src[i], des[i], 'data-startline');
//copyAttribute(src[i], des[i], 'data-endline');
var rawSrc = cloneAndRemoveDataAttr(src[i]);
var rawTar = cloneAndRemoveDataAttr(tar[i]);
if (!rawSrc || !rawTar || rawSrc.outerHTML != rawTar.outerHTML) {
start = i;
break;
}
}
//find diff end position
var srcEnd = 0;
var tarEnd = 0;
for (var i = 0; i < src.length; i++) {
//copyAttribute(src[i], des[i], 'data-startline');
//copyAttribute(src[i], des[i], 'data-endline');
var rawSrc = cloneAndRemoveDataAttr(src[i]);
var rawTar = cloneAndRemoveDataAttr(tar[i]);
if (!rawSrc || !rawTar || rawSrc.outerHTML != rawTar.outerHTML) {
start = i;
break;
}
}
//tar end
for (var i = 1; i <= tar.length + 1; i++) {
var srcLength = src.length;
var tarLength = tar.length;
//copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline');
//copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline');
var rawSrc = cloneAndRemoveDataAttr(src[srcLength - i]);
var rawTar = cloneAndRemoveDataAttr(tar[tarLength - i]);
if (!rawSrc || !rawTar || rawSrc.outerHTML != rawTar.outerHTML) {
tarEnd = tar.length - i;
break;
}
}
//src end
for (var i = 1; i <= src.length + 1; i++) {
var srcLength = src.length;
var tarLength = tar.length;
//copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline');
//copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline');
var rawSrc = cloneAndRemoveDataAttr(src[srcLength - i]);
var rawTar = cloneAndRemoveDataAttr(tar[tarLength - i]);
if (!rawSrc || !rawTar || rawSrc.outerHTML != rawTar.outerHTML) {
srcEnd = src.length - i;
break;
}
}
//check if tar end overlap tar start
var overlap = 0;
for (var i = start; i >= 0; i--) {
var rawTarStart = cloneAndRemoveDataAttr(tar[i - 1]);
var rawTarEnd = cloneAndRemoveDataAttr(tar[tarEnd + 1 + start - i]);
if (rawTarStart && rawTarEnd && rawTarStart.outerHTML == rawTarEnd.outerHTML)
overlap++;
else
break;
}
if (debug)
console.log('overlap:' + overlap);
//show diff content
if (debug) {
console.log('start:' + start);
console.log('tarEnd:' + tarEnd);
console.log('srcEnd:' + srcEnd);
}
tarEnd += overlap;
srcEnd += overlap;
var repeatAdd = (start - srcEnd) < (start - tarEnd);
var repeatDiff = Math.abs(srcEnd - tarEnd) - 1;
//push new elements
var newElements = [];
if (srcEnd >= start) {
for (var j = start; j <= srcEnd; j++) {
if (!src[j]) continue;
newElements.push(src[j].outerHTML);
}
} else if (repeatAdd) {
for (var j = srcEnd - repeatDiff; j <= srcEnd; j++) {
if (!des[j]) continue;
newElements.push(des[j].outerHTML);
}
}
//push remove elements
var removeElements = [];
if (tarEnd >= start) {
for (var j = start; j <= tarEnd; j++) {
if (!des[j]) continue;
removeElements.push(des[j]);
}
} else if (!repeatAdd) {
for (var j = start; j <= start + repeatDiff; j++) {
if (!des[j]) continue;
removeElements.push(des[j]);
}
}
//add elements
if (debug) {
console.log('ADD ELEMENTS');
console.log(newElements.join('\n'));
}
if (des[start])
$(newElements.join('')).insertBefore(des[start]);
else
$(newElements.join('')).insertAfter(des[start - 1]);
//remove elements
if (debug)
console.log('REMOVE ELEMENTS');
for (var j = 0; j < removeElements.length; j++) {
if (debug) {
console.log(removeElements[j].outerHTML);
}
if (removeElements[j])
$(removeElements[j]).remove();
}
}
}
function cloneAndRemoveDataAttr(el) {
if (!el) return;
var rawEl = $(el).clone()[0];
rawEl.removeAttribute('data-startline');
rawEl.removeAttribute('data-endline');
return rawEl;
}
function copyAttribute(src, des, attr) {
if (src && src.getAttribute(attr) && des)
des.setAttribute(attr, src.getAttribute(attr));
}
if ($('.cursor-menu').length <= 0) {
$("