2013-05-27 19:45:33 +00:00
|
|
|
define([
|
|
|
|
"jquery",
|
|
|
|
"underscore",
|
2013-06-22 23:48:57 +00:00
|
|
|
"classes/Extension",
|
2014-04-17 18:51:41 +00:00
|
|
|
"text!html/scrollSyncSettingsBlock.html"
|
|
|
|
], function($, _, Extension, scrollSyncSettingsBlockHTML) {
|
2013-05-29 19:55:23 +00:00
|
|
|
|
2014-04-17 23:41:35 +00:00
|
|
|
var scrollSync = new Extension("scrollSync", "Scroll Sync", true, true);
|
2014-04-17 18:51:41 +00:00
|
|
|
scrollSync.settingsBlock = scrollSyncSettingsBlockHTML;
|
2013-07-28 10:35:04 +00:00
|
|
|
|
2014-04-14 00:21:06 +00:00
|
|
|
$.easing.easeOutSine = function( p ) {
|
|
|
|
return Math.cos((1 - p) * Math.PI / 2 );
|
|
|
|
};
|
|
|
|
|
2013-11-07 23:10:38 +00:00
|
|
|
var sectionList;
|
2014-04-17 18:51:41 +00:00
|
|
|
scrollSync.onSectionsCreated = function(sectionListParam) {
|
2013-07-26 00:44:12 +00:00
|
|
|
sectionList = sectionListParam;
|
|
|
|
};
|
2014-03-19 00:33:57 +00:00
|
|
|
|
2014-03-17 02:01:46 +00:00
|
|
|
var $editorElt;
|
2013-11-07 23:10:38 +00:00
|
|
|
var $previewElt;
|
2013-05-29 19:55:23 +00:00
|
|
|
var mdSectionList = [];
|
|
|
|
var htmlSectionList = [];
|
2013-11-07 23:10:38 +00:00
|
|
|
var lastEditorScrollTop;
|
|
|
|
var lastPreviewScrollTop;
|
2013-05-29 19:55:23 +00:00
|
|
|
var buildSections = _.debounce(function() {
|
|
|
|
mdSectionList = [];
|
2014-03-17 02:01:46 +00:00
|
|
|
var mdSectionOffset;
|
|
|
|
var scrollHeight;
|
2014-03-20 00:24:56 +00:00
|
|
|
var editorScrollTop = $editorElt.scrollTop();
|
|
|
|
$editorElt.find(".wmd-input-section").each(function() {
|
|
|
|
if(mdSectionOffset === undefined) {
|
|
|
|
// Force start to 0 for the first section
|
|
|
|
mdSectionOffset = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var $delimiterElt = $(this.firstChild);
|
|
|
|
// Consider div scroll position
|
|
|
|
var newSectionOffset = $delimiterElt.position().top + editorScrollTop;
|
2014-03-17 02:01:46 +00:00
|
|
|
mdSectionList.push({
|
|
|
|
startOffset: mdSectionOffset,
|
2014-03-20 00:24:56 +00:00
|
|
|
endOffset: newSectionOffset,
|
|
|
|
height: newSectionOffset - mdSectionOffset
|
2013-12-05 00:25:17 +00:00
|
|
|
});
|
2014-03-20 00:24:56 +00:00
|
|
|
mdSectionOffset = newSectionOffset;
|
|
|
|
});
|
|
|
|
// Last section
|
|
|
|
scrollHeight = $editorElt.prop('scrollHeight');
|
|
|
|
mdSectionList.push({
|
|
|
|
startOffset: mdSectionOffset,
|
|
|
|
endOffset: scrollHeight,
|
|
|
|
height: scrollHeight - mdSectionOffset
|
|
|
|
});
|
2013-05-29 19:55:23 +00:00
|
|
|
|
2014-03-17 02:01:46 +00:00
|
|
|
// Find corresponding sections in the preview
|
2013-05-29 19:55:23 +00:00
|
|
|
htmlSectionList = [];
|
2013-11-17 22:59:03 +00:00
|
|
|
var htmlSectionOffset;
|
2013-08-22 00:19:59 +00:00
|
|
|
var previewScrollTop = $previewElt.scrollTop();
|
2014-03-17 02:01:46 +00:00
|
|
|
$previewElt.find(".wmd-preview-section").each(function() {
|
2013-11-19 23:44:31 +00:00
|
|
|
if(htmlSectionOffset === undefined) {
|
|
|
|
// Force start to 0 for the first section
|
|
|
|
htmlSectionOffset = 0;
|
|
|
|
return;
|
|
|
|
}
|
2013-11-17 22:59:03 +00:00
|
|
|
var $delimiterElt = $(this);
|
2013-11-18 19:11:41 +00:00
|
|
|
// Consider div scroll position
|
|
|
|
var newSectionOffset = $delimiterElt.position().top + previewScrollTop;
|
2013-11-19 23:44:31 +00:00
|
|
|
htmlSectionList.push({
|
|
|
|
startOffset: htmlSectionOffset,
|
|
|
|
endOffset: newSectionOffset,
|
|
|
|
height: newSectionOffset - htmlSectionOffset
|
|
|
|
});
|
2013-05-29 19:55:23 +00:00
|
|
|
htmlSectionOffset = newSectionOffset;
|
|
|
|
});
|
|
|
|
// Last section
|
2014-03-17 02:01:46 +00:00
|
|
|
scrollHeight = $previewElt.prop('scrollHeight');
|
2013-05-29 19:55:23 +00:00
|
|
|
htmlSectionList.push({
|
|
|
|
startOffset: htmlSectionOffset,
|
|
|
|
endOffset: scrollHeight,
|
|
|
|
height: scrollHeight - htmlSectionOffset
|
|
|
|
});
|
|
|
|
|
2013-09-15 14:14:42 +00:00
|
|
|
// apply Scroll Link (-10 to have a gap > 9px)
|
2013-06-05 22:42:30 +00:00
|
|
|
lastEditorScrollTop = -10;
|
|
|
|
lastPreviewScrollTop = -10;
|
2014-04-17 18:51:41 +00:00
|
|
|
doScrollSync();
|
2013-05-29 19:55:23 +00:00
|
|
|
}, 500);
|
|
|
|
|
2014-04-22 23:39:00 +00:00
|
|
|
var isPreviewVisible = true;
|
2013-06-03 22:19:52 +00:00
|
|
|
var isScrollEditor = false;
|
2013-05-29 19:55:23 +00:00
|
|
|
var isScrollPreview = false;
|
2013-09-10 16:52:27 +00:00
|
|
|
var isEditorMoving = false;
|
|
|
|
var isPreviewMoving = false;
|
2013-12-02 22:10:19 +00:00
|
|
|
var scrollingHelper = $('<div>');
|
2014-04-22 23:39:00 +00:00
|
|
|
function getDestScrollTop(srcScrollTop, srcSectionList, destSectionList) {
|
|
|
|
// Find the section corresponding to the offset
|
|
|
|
var sectionIndex;
|
|
|
|
var srcSection = _.find(srcSectionList, function(section, index) {
|
|
|
|
sectionIndex = index;
|
|
|
|
return srcScrollTop < section.endOffset;
|
|
|
|
});
|
|
|
|
if(srcSection === undefined) {
|
|
|
|
// Something very bad happened
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var posInSection = (srcScrollTop - srcSection.startOffset) / (srcSection.height || 1);
|
|
|
|
var destSection = destSectionList[sectionIndex];
|
|
|
|
return destSection.startOffset + destSection.height * posInSection;
|
|
|
|
}
|
|
|
|
|
2014-04-17 18:51:41 +00:00
|
|
|
var doScrollSync = _.throttle(function() {
|
2014-03-17 02:01:46 +00:00
|
|
|
if(!isPreviewVisible || mdSectionList.length === 0 || mdSectionList.length !== htmlSectionList.length) {
|
2013-05-29 19:55:23 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-03-20 00:24:56 +00:00
|
|
|
var editorScrollTop = $editorElt.scrollTop();
|
2013-12-04 19:33:47 +00:00
|
|
|
editorScrollTop < 0 && (editorScrollTop = 0);
|
2013-08-22 00:19:59 +00:00
|
|
|
var previewScrollTop = $previewElt.scrollTop();
|
2013-11-07 23:10:38 +00:00
|
|
|
var destScrollTop;
|
2013-05-29 23:04:52 +00:00
|
|
|
// Perform the animation if diff > 9px
|
2013-12-02 22:10:19 +00:00
|
|
|
if(isScrollEditor === true) {
|
|
|
|
if(Math.abs(editorScrollTop - lastEditorScrollTop) <= 9) {
|
|
|
|
return;
|
|
|
|
}
|
2013-06-03 22:19:52 +00:00
|
|
|
isScrollEditor = false;
|
2013-05-29 19:55:23 +00:00
|
|
|
// Animate the preview
|
|
|
|
lastEditorScrollTop = editorScrollTop;
|
2013-11-07 23:10:38 +00:00
|
|
|
destScrollTop = getDestScrollTop(editorScrollTop, mdSectionList, htmlSectionList);
|
2013-09-09 23:32:24 +00:00
|
|
|
destScrollTop = _.min([
|
|
|
|
destScrollTop,
|
|
|
|
$previewElt.prop('scrollHeight') - $previewElt.outerHeight()
|
|
|
|
]);
|
|
|
|
if(Math.abs(destScrollTop - previewScrollTop) <= 9) {
|
|
|
|
// Skip the animation if diff is <= 9
|
|
|
|
lastPreviewScrollTop = previewScrollTop;
|
2013-12-02 22:10:19 +00:00
|
|
|
return;
|
2013-09-09 23:32:24 +00:00
|
|
|
}
|
2014-04-17 18:51:41 +00:00
|
|
|
scrollingHelper.stop('scrollSyncFx', true).css('value', 0).animate({
|
2014-03-19 00:33:57 +00:00
|
|
|
value: destScrollTop - previewScrollTop
|
2013-12-02 22:10:19 +00:00
|
|
|
}, {
|
|
|
|
easing: 'easeOutSine',
|
|
|
|
duration: 200,
|
2014-04-17 18:51:41 +00:00
|
|
|
queue: 'scrollSyncFx',
|
2013-12-02 22:10:19 +00:00
|
|
|
step: function(now) {
|
|
|
|
isPreviewMoving = true;
|
|
|
|
lastPreviewScrollTop = previewScrollTop + now;
|
2014-03-19 00:33:57 +00:00
|
|
|
$previewElt.scrollTop(lastPreviewScrollTop);
|
2013-12-02 22:10:19 +00:00
|
|
|
},
|
|
|
|
done: function() {
|
2014-04-08 23:20:48 +00:00
|
|
|
setTimeout(function() {
|
2013-12-02 22:10:19 +00:00
|
|
|
isPreviewMoving = false;
|
2014-04-08 23:20:48 +00:00
|
|
|
}, 10);
|
2013-12-02 22:10:19 +00:00
|
|
|
},
|
2014-04-17 18:51:41 +00:00
|
|
|
}).dequeue('scrollSyncFx');
|
2014-03-19 00:33:57 +00:00
|
|
|
|
2013-05-29 19:55:23 +00:00
|
|
|
}
|
2013-12-02 22:10:19 +00:00
|
|
|
else if(isScrollPreview === true) {
|
|
|
|
if(Math.abs(previewScrollTop - lastPreviewScrollTop) <= 9) {
|
|
|
|
return;
|
|
|
|
}
|
2013-06-03 22:19:52 +00:00
|
|
|
isScrollPreview = false;
|
2013-05-29 19:55:23 +00:00
|
|
|
// Animate the editor
|
|
|
|
lastPreviewScrollTop = previewScrollTop;
|
2013-11-07 23:10:38 +00:00
|
|
|
destScrollTop = getDestScrollTop(previewScrollTop, htmlSectionList, mdSectionList);
|
2014-03-20 00:24:56 +00:00
|
|
|
destScrollTop = _.min([
|
|
|
|
destScrollTop,
|
|
|
|
$editorElt.prop('scrollHeight') - $editorElt.outerHeight()
|
|
|
|
]);
|
2013-09-09 23:32:24 +00:00
|
|
|
if(Math.abs(destScrollTop - editorScrollTop) <= 9) {
|
|
|
|
// Skip the animation if diff is <= 9
|
|
|
|
lastEditorScrollTop = editorScrollTop;
|
2013-12-02 22:10:19 +00:00
|
|
|
return;
|
2013-09-09 23:32:24 +00:00
|
|
|
}
|
2014-04-17 18:51:41 +00:00
|
|
|
scrollingHelper.stop('scrollSyncFx', true).css('value', 0).animate({
|
2014-03-19 00:33:57 +00:00
|
|
|
value: destScrollTop - editorScrollTop
|
|
|
|
}, {
|
|
|
|
easing: 'easeOutSine',
|
|
|
|
duration: 200,
|
2014-04-17 18:51:41 +00:00
|
|
|
queue: 'scrollSyncFx',
|
2014-03-19 00:33:57 +00:00
|
|
|
step: function(now) {
|
|
|
|
isEditorMoving = true;
|
|
|
|
lastEditorScrollTop = editorScrollTop + now;
|
2014-03-20 00:24:56 +00:00
|
|
|
$editorElt.scrollTop(lastEditorScrollTop);
|
2014-03-19 00:33:57 +00:00
|
|
|
},
|
|
|
|
done: function() {
|
2014-04-08 23:20:48 +00:00
|
|
|
setTimeout(function() {
|
2014-03-19 00:33:57 +00:00
|
|
|
isEditorMoving = false;
|
2014-04-08 23:20:48 +00:00
|
|
|
}, 10);
|
2014-03-19 00:33:57 +00:00
|
|
|
},
|
2014-04-17 18:51:41 +00:00
|
|
|
}).dequeue('scrollSyncFx');
|
2013-05-29 19:55:23 +00:00
|
|
|
}
|
2013-12-02 22:10:19 +00:00
|
|
|
}, 100);
|
2013-05-29 19:55:23 +00:00
|
|
|
|
2014-04-17 18:51:41 +00:00
|
|
|
scrollSync.onLayoutResize = function() {
|
2013-09-09 00:08:55 +00:00
|
|
|
isScrollEditor = true;
|
|
|
|
buildSections();
|
2013-05-29 19:55:23 +00:00
|
|
|
};
|
2014-03-19 00:33:57 +00:00
|
|
|
|
2014-04-17 18:51:41 +00:00
|
|
|
scrollSync.onFileClosed = function() {
|
2013-09-09 23:32:24 +00:00
|
|
|
mdSectionList = [];
|
|
|
|
};
|
|
|
|
|
2013-09-15 14:14:42 +00:00
|
|
|
var scrollAdjust = false;
|
2014-04-17 18:51:41 +00:00
|
|
|
scrollSync.onReady = function() {
|
2013-08-22 00:19:59 +00:00
|
|
|
$previewElt = $(".preview-container");
|
2014-03-17 02:01:46 +00:00
|
|
|
$editorElt = $("#wmd-input");
|
2013-09-09 23:32:24 +00:00
|
|
|
|
2013-09-10 16:52:27 +00:00
|
|
|
$previewElt.scroll(function() {
|
2013-09-15 14:14:42 +00:00
|
|
|
if(isPreviewMoving === false && scrollAdjust === false) {
|
2013-09-10 16:52:27 +00:00
|
|
|
isScrollPreview = true;
|
|
|
|
isScrollEditor = false;
|
2014-04-17 18:51:41 +00:00
|
|
|
doScrollSync();
|
2013-09-10 16:52:27 +00:00
|
|
|
}
|
2013-09-15 14:14:42 +00:00
|
|
|
scrollAdjust = false;
|
2013-08-22 00:19:59 +00:00
|
|
|
});
|
2014-04-16 23:29:51 +00:00
|
|
|
$editorElt.scroll(function() {
|
2013-09-10 16:52:27 +00:00
|
|
|
if(isEditorMoving === false) {
|
|
|
|
isScrollEditor = true;
|
|
|
|
isScrollPreview = false;
|
2014-04-17 18:51:41 +00:00
|
|
|
doScrollSync();
|
2013-09-10 16:52:27 +00:00
|
|
|
}
|
2014-04-16 23:29:51 +00:00
|
|
|
});
|
2014-04-21 01:12:57 +00:00
|
|
|
|
2014-04-22 23:39:00 +00:00
|
|
|
$(".preview-panel").on('hide.layout.toggle', function() {
|
2014-04-21 01:12:57 +00:00
|
|
|
isPreviewVisible = false;
|
2014-04-22 23:39:00 +00:00
|
|
|
}).on('shown.layout.toggle', function() {
|
2014-04-21 01:12:57 +00:00
|
|
|
isPreviewVisible = true;
|
|
|
|
});
|
2014-04-22 23:39:00 +00:00
|
|
|
|
|
|
|
// Reimplement anchor scrolling to work without preview
|
|
|
|
$('.extension-preview-buttons .table-of-contents').on('click', 'a', function(evt) {
|
|
|
|
evt.preventDefault();
|
|
|
|
var id = this.hash;
|
|
|
|
var $anchorElt = $previewElt.find(id);
|
|
|
|
if(!$anchorElt.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var previewScrollTop = $anchorElt.offset().top - $previewElt.offset().top + $previewElt.scrollTop();
|
|
|
|
$previewElt.scrollTop(previewScrollTop);
|
|
|
|
var editorScrollTop = getDestScrollTop(previewScrollTop, htmlSectionList, mdSectionList);
|
|
|
|
$editorElt.scrollTop(editorScrollTop);
|
|
|
|
});
|
2013-05-29 19:55:23 +00:00
|
|
|
};
|
2013-09-10 16:52:27 +00:00
|
|
|
|
2013-11-07 23:10:38 +00:00
|
|
|
var $previewContentsElt;
|
2014-04-17 18:51:41 +00:00
|
|
|
scrollSync.onPagedownConfigure = function(editor) {
|
2013-08-22 00:19:59 +00:00
|
|
|
$previewContentsElt = $("#preview-contents");
|
2013-06-02 00:38:23 +00:00
|
|
|
editor.getConverter().hooks.chain("postConversion", function(text) {
|
2014-04-16 23:29:51 +00:00
|
|
|
// To avoid losing scrolling position before elements are fully loaded
|
2013-08-22 00:19:59 +00:00
|
|
|
$previewContentsElt.height($previewContentsElt.height());
|
2013-06-02 00:38:23 +00:00
|
|
|
return text;
|
2013-05-29 19:55:23 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-04-17 18:51:41 +00:00
|
|
|
scrollSync.onPreviewFinished = function() {
|
2013-06-02 00:38:23 +00:00
|
|
|
// Now set the correct height
|
2013-09-15 14:14:42 +00:00
|
|
|
var previousHeight = $previewContentsElt.height();
|
2013-08-22 00:19:59 +00:00
|
|
|
$previewContentsElt.height("auto");
|
2013-09-15 14:14:42 +00:00
|
|
|
var newHeight = $previewContentsElt.height();
|
2013-06-03 22:19:52 +00:00
|
|
|
isScrollEditor = true;
|
2013-09-15 14:14:42 +00:00
|
|
|
if(newHeight < previousHeight) {
|
|
|
|
// We expect a scroll adjustment
|
|
|
|
scrollAdjust = true;
|
|
|
|
}
|
2013-06-03 22:19:52 +00:00
|
|
|
buildSections();
|
2013-05-29 19:55:23 +00:00
|
|
|
};
|
2013-05-25 00:34:04 +00:00
|
|
|
|
2014-04-17 18:51:41 +00:00
|
|
|
return scrollSync;
|
2014-03-19 00:33:57 +00:00
|
|
|
});
|