2013-05-27 19:45:33 +00:00
|
|
|
define([
|
|
|
|
"jquery",
|
|
|
|
"underscore",
|
2013-06-22 23:48:57 +00:00
|
|
|
"classes/Extension",
|
|
|
|
"text!html/scrollLinkSettingsBlock.html",
|
2013-06-03 22:19:52 +00:00
|
|
|
"libs/css_browser_selector",
|
|
|
|
"libs/jquery.mousewheel"
|
2013-06-22 23:48:57 +00:00
|
|
|
], function($, _, Extension, scrollLinkSettingsBlockHTML) {
|
2013-05-29 19:55:23 +00:00
|
|
|
|
2013-07-29 00:03:19 +00:00
|
|
|
var scrollLink = new Extension("scrollLink", "Scroll Link", true, true);
|
2013-06-22 23:48:57 +00:00
|
|
|
scrollLink.settingsBlock = scrollLinkSettingsBlockHTML;
|
2013-07-28 10:35:04 +00:00
|
|
|
|
2013-07-26 00:44:12 +00:00
|
|
|
var sectionList = undefined;
|
|
|
|
scrollLink.onSectionsCreated = function(sectionListParam) {
|
|
|
|
sectionList = sectionListParam;
|
|
|
|
};
|
2013-05-29 19:55:23 +00:00
|
|
|
|
2013-08-22 00:19:59 +00:00
|
|
|
var $editorElt = undefined;
|
|
|
|
var $previewElt = undefined;
|
|
|
|
var $textareaElt = undefined;
|
2013-05-29 19:55:23 +00:00
|
|
|
var mdSectionList = [];
|
|
|
|
var htmlSectionList = [];
|
|
|
|
function pxToFloat(px) {
|
|
|
|
return parseFloat(px.substring(0, px.length - 2));
|
|
|
|
}
|
2013-06-05 22:29:32 +00:00
|
|
|
var lastEditorScrollTop = undefined;
|
|
|
|
var lastPreviewScrollTop = undefined;
|
2013-05-29 19:55:23 +00:00
|
|
|
var buildSections = _.debounce(function() {
|
|
|
|
|
|
|
|
// Try to find Markdown sections by looking for titles
|
|
|
|
mdSectionList = [];
|
2013-06-05 22:29:32 +00:00
|
|
|
// It has to be the same width as wmd-input
|
2013-08-22 00:19:59 +00:00
|
|
|
$textareaElt.width($editorElt.width());
|
2013-07-28 10:35:04 +00:00
|
|
|
// Consider wmd-input top padding (will be used for 1st and last
|
|
|
|
// section)
|
2013-08-22 00:19:59 +00:00
|
|
|
var padding = pxToFloat($editorElt.css('padding-top'));
|
2013-07-26 00:44:12 +00:00
|
|
|
var mdSectionOffset = 0;
|
2013-05-29 19:55:23 +00:00
|
|
|
function addMdSection(sectionText) {
|
|
|
|
var sectionHeight = padding;
|
2013-08-22 00:19:59 +00:00
|
|
|
if(sectionText !== undefined) {
|
|
|
|
$textareaElt.val(sectionText);
|
|
|
|
sectionHeight += $textareaElt.prop('scrollHeight');
|
2013-05-29 19:55:23 +00:00
|
|
|
}
|
|
|
|
var newSectionOffset = mdSectionOffset + sectionHeight;
|
|
|
|
mdSectionList.push({
|
|
|
|
startOffset: mdSectionOffset,
|
|
|
|
endOffset: newSectionOffset,
|
|
|
|
height: sectionHeight
|
|
|
|
});
|
|
|
|
mdSectionOffset = newSectionOffset;
|
|
|
|
padding = 0;
|
|
|
|
}
|
2013-07-26 00:44:12 +00:00
|
|
|
_.each(sectionList, function(sectionText, index) {
|
2013-07-28 10:35:04 +00:00
|
|
|
if(index !== sectionList.length - 1) {
|
2013-08-22 00:19:59 +00:00
|
|
|
if(sectionText.length === 0) {
|
|
|
|
sectionText = undefined;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Remove the last \n preceding the next title
|
|
|
|
sectionText = sectionText.substring(0, sectionText.length - 1);
|
|
|
|
}
|
2013-07-28 10:35:04 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-07-26 00:44:12 +00:00
|
|
|
// Last section
|
2013-08-22 00:19:59 +00:00
|
|
|
// Consider wmd-input bottom padding and keep last empty line
|
|
|
|
padding += pxToFloat($editorElt.css('padding-bottom'));
|
2013-05-29 19:55:23 +00:00
|
|
|
}
|
2013-07-26 00:44:12 +00:00
|
|
|
addMdSection(sectionText);
|
2013-05-29 19:55:23 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Try to find corresponding sections in the preview
|
|
|
|
htmlSectionList = [];
|
|
|
|
var htmlSectionOffset = 0;
|
2013-08-22 00:19:59 +00:00
|
|
|
var previewScrollTop = $previewElt.scrollTop();
|
2013-05-29 19:55:23 +00:00
|
|
|
// Each title element is a section separator
|
2013-08-22 00:19:59 +00:00
|
|
|
$previewElt.find(".preview-content > .wmd-title").each(function() {
|
|
|
|
var $titleElt = $(this);
|
2013-05-29 19:55:23 +00:00
|
|
|
// Consider div scroll position and header element top margin
|
2013-08-22 00:19:59 +00:00
|
|
|
var newSectionOffset = $titleElt.position().top + previewScrollTop + pxToFloat($titleElt.css('margin-top'));
|
2013-05-29 19:55:23 +00:00
|
|
|
htmlSectionList.push({
|
|
|
|
startOffset: htmlSectionOffset,
|
|
|
|
endOffset: newSectionOffset,
|
|
|
|
height: newSectionOffset - htmlSectionOffset
|
|
|
|
});
|
|
|
|
htmlSectionOffset = newSectionOffset;
|
|
|
|
});
|
|
|
|
// Last section
|
2013-08-22 00:19:59 +00:00
|
|
|
var scrollHeight = $previewElt.prop('scrollHeight');
|
2013-05-29 19:55:23 +00:00
|
|
|
htmlSectionList.push({
|
|
|
|
startOffset: htmlSectionOffset,
|
|
|
|
endOffset: scrollHeight,
|
|
|
|
height: scrollHeight - htmlSectionOffset
|
|
|
|
});
|
|
|
|
|
2013-06-05 22:42:30 +00:00
|
|
|
// apply Scroll Link (-10 to have a gap > 9 px)
|
|
|
|
lastEditorScrollTop = -10;
|
|
|
|
lastPreviewScrollTop = -10;
|
2013-08-22 00:19:59 +00:00
|
|
|
doScrollLink();
|
2013-05-29 19:55:23 +00:00
|
|
|
}, 500);
|
|
|
|
|
2013-06-03 22:19:52 +00:00
|
|
|
var isScrollEditor = false;
|
2013-05-29 19:55:23 +00:00
|
|
|
var isScrollPreview = false;
|
2013-08-22 00:19:59 +00:00
|
|
|
var doScrollLink = _.debounce(function() {
|
2013-06-02 00:38:23 +00:00
|
|
|
if(mdSectionList.length === 0 || mdSectionList.length !== htmlSectionList.length) {
|
2013-05-29 19:55:23 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-08-22 00:19:59 +00:00
|
|
|
var editorScrollTop = $editorElt.scrollTop();
|
|
|
|
var previewScrollTop = $previewElt.scrollTop();
|
2013-06-05 22:42:30 +00:00
|
|
|
function animate(srcScrollTop, srcSectionList, destElt, destSectionList, currentDestScrollTop, callback) {
|
2013-05-29 19:55:23 +00:00
|
|
|
// Find the section corresponding to the offset
|
|
|
|
var sectionIndex = undefined;
|
|
|
|
var srcSection = _.find(srcSectionList, function(section, index) {
|
|
|
|
sectionIndex = index;
|
|
|
|
return srcScrollTop < section.endOffset;
|
|
|
|
});
|
|
|
|
if(srcSection === undefined) {
|
|
|
|
// Something wrong in the algorithm...
|
2013-06-03 22:19:52 +00:00
|
|
|
return;
|
2013-05-29 19:55:23 +00:00
|
|
|
}
|
|
|
|
var posInSection = (srcScrollTop - srcSection.startOffset) / srcSection.height;
|
|
|
|
var destSection = destSectionList[sectionIndex];
|
|
|
|
var destScrollTop = destSection.startOffset + destSection.height * posInSection;
|
|
|
|
destScrollTop = _.min([
|
|
|
|
destScrollTop,
|
|
|
|
destElt.prop('scrollHeight') - destElt.outerHeight()
|
|
|
|
]);
|
2013-06-05 22:42:30 +00:00
|
|
|
if(Math.abs(destScrollTop - currentDestScrollTop) <= 9) {
|
2013-08-22 00:19:59 +00:00
|
|
|
// Skip the animation if diff is <= 9
|
2013-06-05 22:42:30 +00:00
|
|
|
callback(currentDestScrollTop);
|
2013-05-29 19:55:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
destElt.animate({
|
|
|
|
scrollTop: destScrollTop
|
2013-06-03 22:19:52 +00:00
|
|
|
}, 500, function() {
|
2013-05-29 19:55:23 +00:00
|
|
|
callback(destScrollTop);
|
|
|
|
});
|
|
|
|
}
|
2013-05-29 23:04:52 +00:00
|
|
|
// Perform the animation if diff > 9px
|
2013-06-03 22:19:52 +00:00
|
|
|
if(isScrollEditor === true && Math.abs(editorScrollTop - lastEditorScrollTop) > 9) {
|
|
|
|
isScrollEditor = false;
|
2013-05-29 19:55:23 +00:00
|
|
|
// Animate the preview
|
|
|
|
lastEditorScrollTop = editorScrollTop;
|
2013-08-22 00:19:59 +00:00
|
|
|
animate(editorScrollTop, mdSectionList, $previewElt, htmlSectionList, previewScrollTop, function(destScrollTop) {
|
2013-05-29 19:55:23 +00:00
|
|
|
lastPreviewScrollTop = destScrollTop;
|
|
|
|
});
|
|
|
|
}
|
2013-06-03 22:19:52 +00:00
|
|
|
else if(isScrollPreview === true && Math.abs(previewScrollTop - lastPreviewScrollTop) > 9) {
|
|
|
|
isScrollPreview = false;
|
2013-05-29 19:55:23 +00:00
|
|
|
// Animate the editor
|
|
|
|
lastPreviewScrollTop = previewScrollTop;
|
2013-08-22 00:19:59 +00:00
|
|
|
animate(previewScrollTop, htmlSectionList, $editorElt, mdSectionList, editorScrollTop, function(destScrollTop) {
|
2013-05-29 19:55:23 +00:00
|
|
|
lastEditorScrollTop = destScrollTop;
|
|
|
|
});
|
|
|
|
}
|
2013-06-03 22:19:52 +00:00
|
|
|
}, 500);
|
2013-05-29 19:55:23 +00:00
|
|
|
|
|
|
|
scrollLink.onLayoutConfigure = function(layoutConfig) {
|
2013-06-03 22:19:52 +00:00
|
|
|
layoutConfig.onresize = function() {
|
|
|
|
isScrollEditor = true;
|
|
|
|
buildSections();
|
|
|
|
};
|
2013-05-29 19:55:23 +00:00
|
|
|
};
|
|
|
|
|
2013-08-22 00:19:59 +00:00
|
|
|
scrollLink.onReady = function() {
|
|
|
|
$editorElt = $("#wmd-input");
|
|
|
|
$previewElt = $(".preview-container");
|
2013-07-28 17:14:42 +00:00
|
|
|
|
|
|
|
// This textarea is used to measure sections height
|
2013-08-22 00:19:59 +00:00
|
|
|
$textareaElt = $("#md-section-helper");
|
2013-07-28 17:14:42 +00:00
|
|
|
|
2013-08-22 00:19:59 +00:00
|
|
|
$previewElt.bind("keyup mouseup mousewheel", function() {
|
2013-05-29 19:55:23 +00:00
|
|
|
isScrollPreview = true;
|
2013-06-03 22:19:52 +00:00
|
|
|
isScrollEditor = false;
|
2013-08-22 00:19:59 +00:00
|
|
|
doScrollLink();
|
2013-05-29 19:55:23 +00:00
|
|
|
});
|
2013-08-22 00:19:59 +00:00
|
|
|
$('.table-of-contents').click(function() {
|
|
|
|
isScrollPreview = true;
|
|
|
|
isScrollEditor = false;
|
|
|
|
doScrollLink();
|
|
|
|
});
|
|
|
|
$editorElt.bind("keyup mouseup mousewheel", function() {
|
2013-06-03 22:19:52 +00:00
|
|
|
isScrollEditor = true;
|
2013-05-29 19:55:23 +00:00
|
|
|
isScrollPreview = false;
|
2013-08-22 00:19:59 +00:00
|
|
|
doScrollLink();
|
2013-05-29 19:55:23 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-08-22 00:19:59 +00:00
|
|
|
var $previewContentsElt = undefined;
|
2013-05-29 19:55:23 +00:00
|
|
|
scrollLink.onEditorConfigure = 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) {
|
2013-06-03 22:19:52 +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
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
scrollLink.onPreviewFinished = function() {
|
2013-06-02 00:38:23 +00:00
|
|
|
// Now set the correct height
|
2013-08-22 00:19:59 +00:00
|
|
|
$previewContentsElt.height("auto");
|
2013-06-03 22:19:52 +00:00
|
|
|
isScrollEditor = true;
|
|
|
|
buildSections();
|
2013-05-29 19:55:23 +00:00
|
|
|
};
|
2013-05-25 00:34:04 +00:00
|
|
|
|
2013-05-29 19:55:23 +00:00
|
|
|
return scrollLink;
|
2013-05-25 00:34:04 +00:00
|
|
|
});
|