New layout

This commit is contained in:
benweet 2014-04-14 01:21:06 +01:00
parent 5ce2dea7c8
commit 2c1e1fe131
12 changed files with 703 additions and 531 deletions

View File

@ -9,7 +9,7 @@
<link rel="shortcut icon" sizes="196x196" href="res-min/img/nice-highres.png"> <link rel="shortcut icon" sizes="196x196" href="res-min/img/nice-highres.png">
<meta name="description" content="Full-featured, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites."> <meta name="description" content="Full-featured, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites.">
<meta name="author" content="Benoit Schweblin"> <meta name="author" content="Benoit Schweblin">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="msvalidate.01" content="5E47EE6F67B069C17E3CDD418351A612" <meta name="msvalidate.01" content="5E47EE6F67B069C17E3CDD418351A612"
/> />

View File

@ -4,6 +4,7 @@ define([
"underscore", "underscore",
"crel", "crel",
"editor", "editor",
"layout",
"constants", "constants",
"utils", "utils",
"storage", "storage",
@ -16,9 +17,8 @@ define([
"text!html/settingsTemplateTooltip.html", "text!html/settingsTemplateTooltip.html",
"text!html/settingsUserCustomExtensionTooltip.html", "text!html/settingsUserCustomExtensionTooltip.html",
"storage", "storage",
"uilayout",
'pagedown', 'pagedown',
], function($, _, crel, editor, constants, utils, storage, settings, eventMgr, shortcutMgr, mousetrap, bodyIndexHTML, bodyViewerHTML, settingsTemplateTooltipHTML, settingsUserCustomExtensionTooltipHTML) { ], function($, _, crel, editor, layout, constants, utils, storage, settings, eventMgr, shortcutMgr, mousetrap, bodyIndexHTML, bodyViewerHTML, settingsTemplateTooltipHTML, settingsUserCustomExtensionTooltipHTML) {
var core = {}; var core = {};
@ -204,47 +204,7 @@ define([
} }
} }
// Set the panels visibility
var layout;
var $menuPanelElt;
var $documentPanelElt;
function setPanelVisibility(forceHide) {
if(forceHide === true || layout.state.north.isClosed) {
$menuPanelElt.hide();
$documentPanelElt.hide();
}
else {
$menuPanelElt.show();
$documentPanelElt.show();
}
}
// Set the preview button visibility
var $previewButtonsElt;
function setPreviewButtonsVisibility(forceHide) {
if(forceHide === true || layout.state.east.isClosed) {
$previewButtonsElt.hide();
}
else {
$previewButtonsElt.show();
}
}
// Create the layout // Create the layout
var $editorButtonsElt;
var maxWidthMap = [
{ screenWidth: 0, maxWidth: 600 },
{ screenWidth: 1000, maxWidth: 700 },
{ screenWidth: 1200, maxWidth: 800 },
{ screenWidth: 1400, maxWidth: 900 },
];
var maxWidthMapReversed = maxWidthMap.slice(0).reverse();
function getMaxWidth() {
var actualWidth = $(window).width();
return _.find(maxWidthMapReversed, function(value) {
return actualWidth > value.screenWidth;
}).maxWidth;
}
function createLayout() { function createLayout() {
var layoutGlobalConfig = { var layoutGlobalConfig = {
closable: true, closable: true,
@ -274,81 +234,19 @@ define([
easing: "easeInOutQuad", easing: "easeInOutQuad",
duration: 350 duration: 350
}, },
onopen: function() {
setPanelVisibility();
setPreviewButtonsVisibility();
},
onclose_start: function(paneName) {
if(paneName == 'north') {
setPanelVisibility(true);
}
else if(paneName == 'east') {
setPreviewButtonsVisibility(true);
}
},
onresize_end: function(paneName) { onresize_end: function(paneName) {
if(editor.$contentElt !== undefined && paneName == 'center') {
var padding = ($editorElt.width() - getMaxWidth()) / 2;
if(padding < constants.EDITOR_DEFAULT_PADDING) {
padding = constants.EDITOR_DEFAULT_PADDING;
}
editor.$contentElt.css({
'padding-left': padding + 'px',
'padding-right': padding + 'px'
});
editor.$marginElt.css({
'width': padding + 'px',
});
}
eventMgr.onLayoutResize(paneName); eventMgr.onLayoutResize(paneName);
}, },
}; };
eventMgr.onLayoutConfigure(layoutGlobalConfig); eventMgr.onLayoutConfigure(layoutGlobalConfig);
if(settings.layoutOrientation == "horizontal") { if(settings.layoutOrientation == "horizontal") {
$(".ui-layout-south").remove();
$(".preview-container").html('<div id="preview-contents"><div id="wmd-preview" class="preview-content"></div></div>');
layout = $('body').layout($.extend(layoutGlobalConfig, {
east__resizable: true,
east__size: 0.5,
east__minSize: 300
}));
} }
else if(settings.layoutOrientation == "vertical") { else if(settings.layoutOrientation == "vertical") {
$(".ui-layout-east").remove();
$(".preview-container").html('<div id="preview-contents"><div id="wmd-preview" class="preview-content"></div></div>');
layout = $('body').layout($.extend(layoutGlobalConfig, {
south__resizable: true,
south__size: 0.5,
south__minSize: 200
}));
}
$(".navbar").click(function() {
layout.allowOverflow('north');
});
$(".ui-layout-toggler-south").addClass("btn btn-info").html('<i class="icon-none"></i>');
$(".ui-layout-toggler-east").addClass("btn btn-info").html('<i class="icon-none"></i>');
var $northTogglerElt = $(".ui-layout-toggler-north").addClass("btn btn-info").html('<i class="icon-th"></i>');
// We attach the preview buttons to the UI layout resizer in order to
// have fixed position
// We also move the north toggler to the east or south resizer as the
// north resizer is very small
// var $previewButtonsContainerElt = $('<div
// class="preview-button-container">');
var $resizerDecorator = $('<div class="resizer-decorator">');
$previewButtonsElt = $('<div class="extension-preview-buttons">');
$editorButtonsElt = $('<div class="extension-editor-buttons">');
if(window.viewerMode || settings.layoutOrientation == "horizontal") {
$('.ui-layout-resizer-north').append($previewButtonsElt);
$('.ui-layout-resizer-east').append($resizerDecorator).append($northTogglerElt).append($editorButtonsElt);
}
else {
$('.ui-layout-resizer-south').append($resizerDecorator).append($previewButtonsElt).append($editorButtonsElt).append($northTogglerElt);
} }
setPanelVisibility(); //setPanelVisibility();
setPreviewButtonsVisibility(); //setPreviewButtonsVisibility();
layout.init();
eventMgr.onLayoutCreated(layout); eventMgr.onLayoutCreated(layout);
} }
@ -379,7 +277,6 @@ define([
$titleContainer.before($rightBtnElts); $titleContainer.before($rightBtnElts);
} }
} }
layout.resizeAll();
} }
// Create the PageDown editor // Create the PageDown editor
@ -474,50 +371,6 @@ define([
document.body.innerHTML = bodyIndexHTML; document.body.innerHTML = bodyIndexHTML;
} }
var styleContent = '';
// Apply font
function applyFont(size, screenWidth) {
screenWidth = screenWidth || 0;
//var codeFontSize = settings.editorFontSize;
//var codeLineHeight = Math.round(codeFontSize * 20 / 12);
var previewFontSize = size; // * 13 / 12;
styleContent += [
'@media (min-width: ' + screenWidth + 'px) {',
'#wmd-input, .textarea-helper {',
' font-size: ' + size + 'px;',
//' font-family: ' + settings.editorFontFamily + ';',
'}',
'#preview-contents {',
' font-size: ' + previewFontSize + 'px;',
'}',
'}',
].join('\n');
}
applyFont(16);
applyFont(17, 600);
applyFont(18, 1200);
function applyMaxWidth(maxWidth, screenWidth) {
styleContent += [
'@media (min-width: ' + screenWidth + 'px) {',
'#preview-contents {',
' max-width: ' + (maxWidth + 30) + 'px;',
'}',
'}',
].join('\n');
}
_.each(maxWidthMap, function(entry) {
applyMaxWidth(entry.maxWidth, entry.screenWidth);
});
// Apply dynamic stylesheet
var style = crel('style', {
type : 'text/css'
});
style.innerHTML = styleContent;
document.head.appendChild(style);
$navbarElt = $('.navbar'); $navbarElt = $('.navbar');
$leftBtnElts = $navbarElt.find('.left-buttons'); $leftBtnElts = $navbarElt.find('.left-buttons');
$rightBtnElts = $navbarElt.find('.right-buttons'); $rightBtnElts = $navbarElt.find('.right-buttons');
@ -546,75 +399,6 @@ define([
// Detect user activity // Detect user activity
$(document).mousemove(setUserActive).keypress(setUserActive); $(document).mousemove(setUserActive).keypress(setUserActive);
// Avoid dropdown to close when clicking on submenu
$(".dropdown-submenu > a").click(function(e) {
e.stopPropagation();
});
$menuPanelElt = $('.menu-panel').collapse({
toggle: false
});
var menuPanelBackdropElt;
$menuPanelElt.on('show.bs.collapse', function(e) {
if(e.target === $menuPanelElt[0]) {
isMenuPanelShown = true;
menuPanelBackdropElt = utils.createBackdrop('collapse', '.menu-panel');
$menuPanelElt.addClass('move-to-front');
// To avoid opening delay
$.support.transition && setTimeout(function() {
$menuPanelElt.trigger($.support.transition.end);
}, 50);
}
else {
// Close all open sub-menus when one submenu opens
$menuPanelElt.find('.in').collapse('hide');
}
}).on('hide.bs.collapse', function(e) {
if(e.target === $menuPanelElt[0]) {
isMenuPanelShown = false;
menuPanelBackdropElt.removeBackdrop();
$menuPanelElt.removeClass('move-to-front');
editor.focus();
}
}).on('hidden.bs.collapse', function(e) {
if(e.target === $menuPanelElt[0]) {
// Close all open sub-menus when menu panel is closed
$menuPanelElt.find('.in').collapse('hide');
}
});
$documentPanelElt = $('.document-panel').collapse({
toggle: false
});
var documentPanelBackdropElt;
$documentPanelElt.on('show.bs.collapse', function(e) {
if(e.target === $documentPanelElt[0]) {
isDocumentPanelShown = true;
documentPanelBackdropElt = utils.createBackdrop('collapse', '.document-panel');
$documentPanelElt.addClass('move-to-front');
// To avoid opening delay
$.support.transition && setTimeout(function() {
$documentPanelElt.trigger($.support.transition.end);
}, 50);
}
else {
// Close all open sub-menus when one submenu opens
$documentPanelElt.find('.in').collapse('hide');
}
}).on('hide.bs.collapse', function(e) {
if(e.target === $documentPanelElt[0]) {
isDocumentPanelShown = false;
documentPanelBackdropElt.removeBackdrop();
$documentPanelElt.removeClass('move-to-front');
editor.focus();
}
}).on('hidden.bs.collapse', function(e) {
if(e.target === $documentPanelElt[0]) {
// Close all open sub-menus when menu panel is closed
$documentPanelElt.find('.in').collapse('hide');
}
});
// Create UI layout // Create UI layout
createLayout(); createLayout();
@ -642,18 +426,7 @@ define([
// Other initialization that are not prioritary // Other initialization that are not prioritary
eventMgr.addListener("onReady", function() { eventMgr.addListener("onReady", function() {
// In vertical mode, we have to offset the editor buttons otherwise they hide the editor buttons $('.modal').on('shown.bs.modal', function() {
if(!window.viewerMode && settings.layoutOrientation == "vertical") {
$previewButtonsElt.css('right', parseInt($previewButtonsElt.css('right')) + $editorButtonsElt.width());
}
var isModalShown = false;
$('.modal').on('show.bs.modal', function() {
// Close panel if open
$menuPanelElt.collapse('hide');
$documentPanelElt.collapse('hide');
isModalShown = true;
}).on('shown.bs.modal', function() {
var $elt = $(this); var $elt = $(this);
setTimeout(function() { setTimeout(function() {
// When modal opens focus on the first button // When modal opens focus on the first button
@ -665,7 +438,6 @@ define([
}, 50); }, 50);
}).on('hidden.bs.modal', function() { }).on('hidden.bs.modal', function() {
// Focus on the editor when modal is gone // Focus on the editor when modal is gone
isModalShown = false;
editor.focus(); editor.focus();
// Revert to current theme when settings modal is closed // Revert to current theme when settings modal is closed
applyTheme(window.theme); applyTheme(window.theme);
@ -676,16 +448,6 @@ define([
} }
}); });
// Hide menu panel when clicking 'Save as' button
$('.collapse-save-as a').click(function() {
$menuPanelElt.collapse('hide');
});
// Configure Mousetrap
mousetrap.stopCallback = function(e, element) {
return isMenuPanelShown || isDocumentPanelShown || isModalShown || $(element).is("input, select, textarea:not(.ace_text-input)");
};
// Click events on "insert link" and "insert image" dialog buttons // Click events on "insert link" and "insert image" dialog buttons
$(".action-insert-link").click(function(e) { $(".action-insert-link").click(function(e) {
var value = utils.getInputTextValue($("#input-insert-link"), e); var value = utils.getInputTextValue($("#input-insert-link"), e);

View File

@ -578,17 +578,10 @@ define([
$inputElt = $(inputElt); $inputElt = $(inputElt);
previewElt = previewEltParam; previewElt = previewEltParam;
contentElt = crel('div', { contentElt = inputElt.querySelector('.editor-content');
class: 'editor-content',
contenteditable: true
});
inputElt.appendChild(contentElt);
$contentElt = $(contentElt); $contentElt = $(contentElt);
editor.$contentElt = $contentElt; editor.$contentElt = $contentElt;
marginElt = crel('div', { marginElt = inputElt.querySelector('.editor-margin');
class: 'editor-margin'
});
inputElt.appendChild(marginElt);
$marginElt = $(marginElt); $marginElt = $(marginElt);
editor.$marginElt = $marginElt; editor.$marginElt = $marginElt;

View File

@ -100,7 +100,7 @@ define([
} }
popoverElt.style.left = left + 'px'; popoverElt.style.left = left + 'px';
popoverElt.querySelector('.arrow').style.left = (marginElt.offsetWidth - parseInt(commentElt.style.right) - commentElt.offsetWidth / 2 - left) + 'px'; popoverElt.querySelector('.arrow').style.left = (marginElt.offsetWidth - parseInt(commentElt.style.right) - commentElt.offsetWidth / 2 - left) + 'px';
var popoverTopOffset = document.body.offsetHeight - currentContext.hr.getBoundingClientRect().top; var popoverTopOffset = window.innerHeight - currentContext.hr.getBoundingClientRect().top;
if(popoverTopOffset < 0) { if(popoverTopOffset < 0) {
popoverElt.style.top = (parseInt(popoverElt.style.top) + popoverTopOffset) + 'px'; popoverElt.style.top = (parseInt(popoverElt.style.top) + popoverTopOffset) + 'px';
} }

View File

@ -19,7 +19,7 @@ define([
' class="list-group-item folder clearfix"', ' class="list-group-item folder clearfix"',
' data-folder-index="<%= folderDesc.folderIndex %>"', ' data-folder-index="<%= folderDesc.folderIndex %>"',
' data-toggle="collapse"', ' data-toggle="collapse"',
' data-target=".document-panel .file-list.<%= id %>">', ' data-target=".file-list.<%= id %>">',
' <div class="pull-right file-count">', ' <div class="pull-right file-count">',
' <%= _.size(folderDesc.fileList) %>', ' <%= _.size(folderDesc.fileList) %>',
' </div>', ' </div>',
@ -32,9 +32,7 @@ define([
var documentEltTmpl = [ var documentEltTmpl = [
'<a href="#"', '<a href="#"',
' class="list-group-item file<%= fileDesc === selectedFileDesc ? " active" : "" %>"', ' class="list-group-item file<%= fileDesc === selectedFileDesc ? " active" : "" %>"',
' data-file-index="<%= fileDesc.fileIndex %>"', ' data-file-index="<%= fileDesc.fileIndex %>">',
' data-toggle="collapse"',
' data-target=".document-panel">',
' <%= fileDesc.composeTitle() %>', ' <%= fileDesc.composeTitle() %>',
'</a>', '</a>',
].join(''); ].join('');

View File

@ -106,7 +106,7 @@ define([
dropdownElt = crel('ul', { dropdownElt = crel('ul', {
class: 'dropdown-menu dropdown-file-selector' class: 'dropdown-menu dropdown-file-selector'
}); });
document.querySelector('.ui-layout-resizer-north').appendChild(crel('div', crel('div', { document.querySelector('.navbar').appendChild(crel('div', crel('div', {
'data-toggle': 'dropdown' 'data-toggle': 'dropdown'
}), dropdownElt)); }), dropdownElt));
var $dropdownElt = $(dropdownElt).dropdown(); var $dropdownElt = $(dropdownElt).dropdown();

View File

@ -8,6 +8,10 @@ define([
var scrollLink = new Extension("scrollLink", "Scroll Link", true, true); var scrollLink = new Extension("scrollLink", "Scroll Link", true, true);
scrollLink.settingsBlock = scrollLinkSettingsBlockHTML; scrollLink.settingsBlock = scrollLinkSettingsBlockHTML;
$.easing.easeOutSine = function( p ) {
return Math.cos((1 - p) * Math.PI / 2 );
};
var sectionList; var sectionList;
scrollLink.onSectionsCreated = function(sectionListParam) { scrollLink.onSectionsCreated = function(sectionListParam) {
sectionList = sectionListParam; sectionList = sectionListParam;

View File

@ -1,3 +1,5 @@
<div class="layout-outer-wrapper">
<div class="layout-inner-wrapper">
<div class="navbar navbar-default ui-layout-north"> <div class="navbar navbar-default ui-layout-north">
<div class="navbar-inner"> <div class="navbar-inner">
<div class="nav left-space"></div> <div class="nav left-space"></div>
@ -47,14 +49,24 @@
</ul> </ul>
</div> </div>
</div> </div>
<pre id="wmd-input" class="ui-layout-center form-control"></pre> <pre id="wmd-input" class="ui-layout-center form-control"><div class="editor-content" contenteditable=true></div><div class="editor-margin"></div></pre>
<div class="ui-layout-east preview-container"></div> <div class="preview-panel">
<div class="ui-layout-south preview-container"></div> <div class="extension-preview-buttons"></div>
<div class="layout-resizer layout-resizer-preview"></div>
<div class="layout-toggler layout-toggler-navbar btn btn-info"><i class="icon-th"></i></div>
<div class="layout-toggler layout-toggler-preview btn btn-info"><i class="icon-none"></i></div>
<div class="extension-editor-buttons"></div>
<div class="preview-container">
<div id="preview-contents">
<div id="wmd-preview" class="preview-content"></div>
</div>
</div>
</div>
</div>
<div id="wmd-button-bar" class="hide"></div> <div id="wmd-button-bar" class="hide"></div>
<div class="menu-panel collapse width"> <div class="menu-panel">
<button class="btn collapse-button" data-toggle="collapse" <button class="btn toggle-button" title="Menu">
data-target=".menu-panel" title="Menu">
<img <img
data-stackedit-src="menu-icon.png" width="24" height="24" /> data-stackedit-src="menu-icon.png" width="24" height="24" />
</button> </button>
@ -98,8 +110,7 @@
<div class="sub-menu collapse collapse-publish-on clearfix"> <div class="sub-menu collapse collapse-publish-on clearfix">
<ul class="nav alert alert-danger show-already-published"> <ul class="nav alert alert-danger show-already-published">
<li><div>"<span class="file-title"></span>" is already published.</div></li> <li><div>"<span class="file-title"></span>" is already published.</div></li>
<li><a href="#" class="action-update-publication" <li><a href="#" class="action-update-publication"><i class="icon-upload"></i>
data-toggle="collapse" data-target=".menu-panel"><i class="icon-upload"></i>
Update publication</a></li> Update publication</a></li>
<li><a href="#" data-toggle="modal" data-target=".modal-manage-publish" <li><a href="#" data-toggle="modal" data-target=".modal-manage-publish"
class="action-reset-input"><i class="icon-upload"></i> class="action-reset-input"><i class="icon-upload"></i>
@ -128,20 +139,16 @@
</div> </div>
<div class=dropdown-header>IMPORT</div> <div class=dropdown-header>IMPORT</div>
<div class="list-group"> <div class="list-group">
<a class="list-group-item action-sync-import-dropbox" href="#" <a class="list-group-item action-sync-import-dropbox" href="#"><i
data-toggle="collapse" data-target=".menu-panel"><i
class="icon-provider-dropbox"></i> Open from class="icon-provider-dropbox"></i> Open from
Dropbox</a> Dropbox</a>
<a href="#" class="list-group-item submenu-sync-gdrive action-sync-import-gdrive" <a href="#" class="list-group-item submenu-sync-gdrive action-sync-import-gdrive"><i
data-toggle="collapse" data-target=".menu-panel"><i
class="icon-provider-gdrive"></i> Open from class="icon-provider-gdrive"></i> Open from
Google Drive</a> Google Drive</a>
<a href="#" class="list-group-item submenu-sync-gdrivesec action-sync-import-gdrivesec" <a href="#" class="list-group-item submenu-sync-gdrivesec action-sync-import-gdrivesec"><i
data-toggle="collapse" data-target=".menu-panel"><i
class="icon-provider-gdrive"></i> Open from class="icon-provider-gdrive"></i> Open from
Google Drive <small>(2nd account)</small></a> Google Drive <small>(2nd account)</small></a>
<a href="#" class="list-group-item submenu-sync-gdriveter action-sync-import-gdriveter" <a href="#" class="list-group-item submenu-sync-gdriveter action-sync-import-gdriveter"><i
data-toggle="collapse" data-target=".menu-panel"><i
class="icon-provider-gdrive"></i> Open from class="icon-provider-gdrive"></i> Open from
Google Drive <small>(3rd account)</small></a> Google Drive <small>(3rd account)</small></a>
<a data-toggle="modal" <a data-toggle="modal"
@ -168,14 +175,13 @@
</div> </div>
<div class="document-panel collapse width"> <div class="document-panel">
<button class="btn collapse-button" data-toggle="collapse" <button class="btn toggle-button" title="Select document">
data-target=".document-panel" title="Select document">
<i class="icon-folder-open"></i> <i class="icon-folder-open"></i>
</button> </button>
<div class="search-bar clearfix"> <div class="search-bar clearfix">
<ul class="nav"> <ul class="nav">
<li><a href="#" class="action-create-file" data-toggle="collapse" data-target=".document-panel"><i <li><a href="#" class="action-create-file"><i
class="icon-file"></i> New document</a></li> class="icon-file"></i> New document</a></li>
<li><a href="#" class="action-remove-file-confirm"><i <li><a href="#" class="action-remove-file-confirm"><i
class="icon-trash"></i> Delete document</a></li> class="icon-trash"></i> Delete document</a></li>
@ -192,7 +198,7 @@
<div class="list-group document-list-filtered hide"></div> <div class="list-group document-list-filtered hide"></div>
</div> </div>
</div> </div>
</div>
<div class="modal fade modal-document-manager"> <div class="modal fade modal-document-manager">
<div class="modal-dialog"> <div class="modal-dialog">
@ -1264,4 +1270,3 @@
<div class="lock-ui hide"></div> <div class="lock-ui hide"></div>
<div id="dropboxjs" data-app-key="x0k2l8puemfvg0o"></div> <div id="dropboxjs" data-app-key="x0k2l8puemfvg0o"></div>
<div class="textarea-helper"></div>

373
public/res/layout.js Normal file
View File

@ -0,0 +1,373 @@
define([
'jquery',
'underscore',
'utils',
'constants',
'eventMgr',
'crel',
'mousetrap',
], function($, _, utils, constants, eventMgr, crel, mousetrap) {
var layout = {};
var resizerSize = 32;
var togglerSize = 60;
var navbarHeight = 50;
var editorMaxWidth = 250;
var previewMaxWidth = 330;
var menuPanelWidth = 280;
var documentPanelWidth = 320;
var windowSize;
function DomObject(selector) {
this.selector = selector;
this.elt = document.querySelector(selector);
this.$elt = $(this.elt);
}
var laterCssTimeoutId;
var laterCssQueue = [];
var outerWrapper, innerWrapper, navbar, menuPanel, documentPanel, editor, previewPanel, previewContainer, navbarToggler, previewToggler, previewResizer;
var animate = false;
function applyCssLater() {
if(laterCssQueue.length === 0) {
outerWrapper.$elt.removeClass('layout-animate');
animate = false;
return onResize();
}
laterCssQueue.shift()();
applyCssLater();
}
DomObject.prototype.applyCss = function() {
// Top/left
this.top !== undefined && (this.elt.style.top = this.top + 'px');
this.left !== undefined && (this.elt.style.left = this.left + 'px');
// Translate
if(this.x !== undefined || this.y !== undefined) {
this.x = this.x || 0;
this.y = this.y || 0;
this.elt.style['-webkit-transform'] = 'translate(' + this.x + 'px, ' + this.y + 'px)';
this.elt.style['-ms-transform'] = 'translate(' + this.x + 'px, ' + this.y + 'px)';
this.elt.style.transform = 'translate(' + this.x + 'px, ' + this.y + 'px)';
}
// Width (defer if new width is smaller)
if(animate && this.width < this.oldWidth) {
laterCssQueue.push(_.bind(function() {
this.elt.style.width = this.width + 'px';
}, this));
}
else {
this.width !== undefined && (this.elt.style.width = this.width + 'px');
}
this.oldWidth = this.width;
// Height (defer if new height is smaller)
if(animate && this.height < this.oldHeight) {
laterCssQueue.push(_.bind(function() {
this.elt.style.height = this.height + 'px';
}, this));
}
else {
this.height !== undefined && (this.elt.style.height = this.height + 'px');
}
this.oldHeight = this.height;
clearTimeout(laterCssTimeoutId);
laterCssTimeoutId = setTimeout(applyCssLater, 350);
};
DomObject.prototype.createToggler = function(backdrop) {
var backdropElt;
this.toggle = function(show) {
if(show === this.isOpen) {
return;
}
this.isOpen = _.isBoolean(show) ? show : !this.isOpen;
if(this.isOpen) {
this.$elt.addClass('panel-open');
if(backdrop) {
$(backdropElt = utils.createBackdrop(outerWrapper.elt)).click(_.bind(function() {
this.toggle(false);
}, this));
this.$elt.addClass('bring-to-front');
}
}
else {
backdropElt && backdropElt.removeBackdrop();
backdropElt = undefined;
laterCssQueue.push(_.bind(function() {
!this.isOpen && this.$elt.find('.in').collapse('hide');
this.$elt.toggleClass('panel-open', this.isOpen).toggleClass('bring-to-front', (!!backdrop && this.isOpen));
}, this));
}
animate = true;
outerWrapper.$elt.addClass('layout-animate');
resizeAll();
};
};
var maxWidthMap = [
{ screenWidth: 0, maxWidth: 600 },
{ screenWidth: 1000, maxWidth: 700 },
{ screenWidth: 1200, maxWidth: 800 },
{ screenWidth: 1400, maxWidth: 900 },
];
var maxWidthMapReversed = maxWidthMap.slice(0).reverse();
function getMaxWidth() {
return _.find(maxWidthMapReversed, function(value) {
return windowSize.width > value.screenWidth;
}).maxWidth;
}
function onResize() {
var padding = (editor.elt.offsetWidth - getMaxWidth()) / 2;
if(padding < constants.EDITOR_DEFAULT_PADDING) {
padding = constants.EDITOR_DEFAULT_PADDING;
}
editorContentElt.style.paddingLeft = padding + 'px';
editorContentElt.style.paddingRight = padding + 'px';
editorMarginElt.style.width = padding + 'px',
eventMgr.onLayoutResize();
}
var editorContentElt;
var editorMarginElt;
function resizeAll() {
windowSize = {
width: window.innerWidth,
height: window.innerHeight
};
// Layout outer wrapper
outerWrapper.y = navbar.isOpen ? 0 : -navbarHeight;
outerWrapper.x = menuPanel.isOpen ? 0 : documentPanel.isOpen ? -(menuPanelWidth + documentPanelWidth) : -menuPanelWidth;
outerWrapper.width = windowSize.width + menuPanelWidth + documentPanelWidth;
outerWrapper.height = windowSize.height - outerWrapper.y;
outerWrapper.applyCss();
// Layout inner wrapper
innerWrapper.left = menuPanelWidth;
innerWrapper.width = windowSize.width;
innerWrapper.height = outerWrapper.height;
innerWrapper.applyCss();
// Preview panel
if(!previewPanel.isOpen) {
previewPanel.x = innerWrapper.width - resizerSize;
}
else {
if(previewPanel.halfSize) {
previewPanel.width = (windowSize.width + resizerSize) / 2;
}
if(previewPanel.width < previewMaxWidth) {
previewPanel.halfSize = false;
previewPanel.width = previewMaxWidth;
}
previewPanel.x = innerWrapper.width - previewPanel.width;
if(previewPanel.x < editorMaxWidth) {
previewPanel.halfSize = false;
previewPanel.x = editorMaxWidth;
previewPanel.width = innerWrapper.width - previewPanel.x;
if(previewPanel.width < previewMaxWidth) {
previewPanel.isOpen = false;
previewPanel.width = previewMaxWidth;
previewPanel.x = innerWrapper.width - resizerSize;
}
}
}
previewPanel.top = navbarHeight;
previewPanel.height = innerWrapper.height - previewPanel.top;
previewPanel.applyCss();
// Editor
editor.top = navbarHeight;
editor.width = previewPanel.x;
editor.height = innerWrapper.height - editor.top;
editor.applyCss();
// Preview container
previewContainer.left = resizerSize;
previewContainer.width = previewPanel.width - resizerSize;
previewContainer.height = previewPanel.height;
previewContainer.applyCss();
// Navbar toggler
navbarToggler.applyCss();
navbarToggler.$elt.toggleClass('open', navbar.isOpen);
// Preview toggler
previewToggler.y = (previewPanel.height - togglerSize) / 2;
previewToggler.applyCss();
previewToggler.$elt.toggleClass('open', previewPanel.isOpen);
// Preview resizer
previewResizer.height = previewContainer.height;
previewResizer.applyCss();
previewResizer.$elt.toggleClass('open', previewPanel.isOpen);
onResize();
}
layout.resizeAll = resizeAll;
layout.init = function() {
outerWrapper = new DomObject('.layout-outer-wrapper');
innerWrapper = new DomObject('.layout-inner-wrapper');
navbar = new DomObject('.navbar');
menuPanel = new DomObject('.menu-panel');
documentPanel = new DomObject('.document-panel');
editor = new DomObject('#wmd-input');
previewPanel = new DomObject('.preview-panel');
previewContainer = new DomObject('.preview-container');
navbarToggler = new DomObject('.layout-toggler-navbar');
previewToggler = new DomObject('.layout-toggler-preview');
previewResizer = new DomObject('.layout-resizer-preview');
editorContentElt = editor.elt.querySelector('.editor-content');
editorMarginElt = editor.elt.querySelector('.editor-margin');
navbar.isOpen = true;
navbar.createToggler();
navbarToggler.$elt.click(_.bind(navbar.toggle, navbar));
previewPanel.isOpen = true;
previewPanel.createToggler();
previewPanel.halfSize = true;
previewToggler.$elt.click(_.bind(previewPanel.toggle, previewPanel));
menuPanel.isOpen = false;
menuPanel.createToggler(true);
menuPanel.$elt.find('.toggle-button').click(_.bind(menuPanel.toggle, menuPanel));
documentPanel.isOpen = false;
documentPanel.createToggler(true);
documentPanel.$elt.find('.toggle-button').click(_.bind(documentPanel.toggle, documentPanel));
// Hide menu panel when clicking 'Save as' button
$('.collapse-save-as a').click(function() {
menuPanel.toggle(false);
});
menuPanel.$elt.on('show.bs.collapse', function() {
// Close all open sub-menus when one submenu opens
menuPanel.$elt.find('.in').collapse('hide');
});
var isDragging = false;
var desiredWidth;
var lastCoord;
outerWrapper.$elt.on('mousemove', function(evt) {
if(!isDragging) {
return;
}
if(evt.which !== 1) {
isDragging = false;
}
else {
var newCoord = {
x: evt.pageX,
y: evt.pageY,
};
if(lastCoord.x !== newCoord.x) {
desiredWidth += lastCoord.x - newCoord.x;
previewPanel.width = desiredWidth;
previewPanel.halfSize = false;
resizeAll();
}
lastCoord = newCoord;
evt.preventDefault();
}
});
previewResizer.$elt.on('mousedown', function(evt) {
if(evt.which === 1) {
isDragging = true;
desiredWidth = previewPanel.width;
lastCoord = {
x: evt.pageX,
y: evt.pageY,
};
}
});
var isModalShown = false;
$('.modal').on('show.bs.modal', function() {
// Close panel if open
menuPanel.toggle(false);
documentPanel.toggle(false);
isModalShown = true;
}).on('hidden.bs.modal', function() {
isModalShown = false;
});
// Configure Mousetrap
mousetrap.stopCallback = function(e, element) {
return menuPanel.isOpen || documentPanel.isOpen || isModalShown || $(element).is("input, select, textarea:not(.ace_text-input)");
};
$(window).resize(resizeAll);
$(document.body).on('touchmove', function(evt) {
evt.preventDefault();
});
var styleContent = '';
// Apply font
function applyFont(size, screenWidth) {
screenWidth = screenWidth || 0;
//var codeFontSize = settings.editorFontSize;
//var codeLineHeight = Math.round(codeFontSize * 20 / 12);
var previewFontSize = size; // * 13 / 12;
styleContent += [
'@media (min-width: ' + screenWidth + 'px) {',
'#wmd-input {',
' font-size: ' + size + 'px;',
//' font-family: ' + settings.editorFontFamily + ';',
'}',
'#preview-contents {',
' font-size: ' + previewFontSize + 'px;',
'}',
'}',
].join('\n');
}
applyFont(16);
applyFont(17, 600);
applyFont(18, 1200);
function applyMaxWidth(maxWidth, screenWidth) {
styleContent += [
'@media (min-width: ' + screenWidth + 'px) {',
'#preview-contents {',
' max-width: ' + (maxWidth + 30) + 'px;',
'}',
'}',
].join('\n');
}
_.each(maxWidthMap, function(entry) {
applyMaxWidth(entry.maxWidth, entry.screenWidth);
});
// Apply dynamic stylesheet
var style = crel('style', {
type : 'text/css'
});
style.innerHTML = styleContent;
document.head.appendChild(style);
resizeAll();
};
return layout;
});

View File

@ -71,7 +71,7 @@
@code-color: @secondary-color-darkest; @code-color: @secondary-color-darkest;
@code-bg: fade(@secondary-desaturated, 7.5%); @code-bg: fade(@secondary-desaturated, 7.5%);
@hr-border: fade(@secondary-desaturated, 10%); @hr-border: fade(@secondary-desaturated, 10%);
@navbar-height: 49px; @navbar-height: 50px;
@navbar-default-bg: @primary-bg; @navbar-default-bg: @primary-bg;
@nav-link-hover-bg: @btn-default-hover-bg; @nav-link-hover-bg: @btn-default-hover-bg;
@nav-disabled-link-color: @disabled-color; @nav-disabled-link-color: @disabled-color;
@ -139,18 +139,13 @@
body { body {
tab-size: 4; tab-size: 4;
overflow: hidden;
} }
* { * {
outline: none !important; outline: none !important;
} }
#preview-contents {
padding: 35px;
margin: 0 auto 200px;
text-align: justify;
}
.working { .working {
cursor: progress; cursor: progress;
} }
@ -255,7 +250,13 @@ a {
} }
} }
.move-to-front { .layout-panel() {
position: absolute;
top: 0;
left: 0;
}
.bring-to-front {
z-index: 1050 !important; z-index: 1050 !important;
} }
@ -408,11 +409,14 @@ a {
*******************/ *******************/
.navbar { .navbar {
position: static; .layout-panel();
width: 100%;
padding: 0; padding: 0;
border: 0; border: 0;
border-top: 1px solid @primary-border-color; border-top: 1px solid @primary-border-color;
border-bottom: 1px solid @primary-border-color;
border-radius: 0; border-radius: 0;
z-index: 20;
.left-space, .right-space { .left-space, .right-space {
width: 25px; width: 25px;
height: @input-height-slim; height: @input-height-slim;
@ -538,13 +542,10 @@ a {
// Common style // Common style
.menu-panel, .document-panel { .menu-panel, .document-panel {
display: block;
position: absolute;
top: 0;
height: 100%; height: 100%;
z-index: 10; z-index: 30;
overflow: initial; overflow: initial;
.collapse-button { .toggle-button {
background-color: @panel-button-bg-color; background-color: @panel-button-bg-color;
height: @input-height-slim; height: @input-height-slim;
border-radius: 8px; border-radius: 8px;
@ -560,7 +561,7 @@ a {
font-size: 14px; font-size: 14px;
} }
} }
.collapse-button:hover, &.in .collapse-button, &.collapsing .collapse-button { & .toggle-button:hover, &.panel-open > .toggle-button {
border-width: 1px; border-width: 1px;
} }
.panel-content { .panel-content {
@ -574,13 +575,8 @@ a {
} }
.menu-panel { .menu-panel {
width: @menu-panel-width !important; .layout-panel();
margin-left: (-@menu-panel-width - 15); width: @menu-panel-width;
.transition(~"margin-left ease-in-out 0.35s");
.sub-menu {
background-color: @secondary-bg-lighter;
.box-shadow(~"inset 1px 0 1px rgba(0,0,0,.1), inset 1px 1px 4px rgba(0,0,0,.025)");
}
.alert { .alert {
padding: 15px 0; padding: 15px 0;
} }
@ -596,19 +592,16 @@ a {
} }
} }
} }
&.in { .toggle-button {
margin-left: 0;
}
.collapse-button {
border-top: 4px solid fade(@logo-yellow, @panel-button-border-fade); border-top: 4px solid fade(@logo-yellow, @panel-button-border-fade);
border-right: 5px solid fade(@logo-blue, @panel-button-border-fade); border-right: 5px solid fade(@logo-blue, @panel-button-border-fade);
border-bottom: 4px solid fade(@logo-orange, @panel-button-border-fade); border-bottom: 4px solid fade(@logo-orange, @panel-button-border-fade);
right: -50px; right: -37px;
z-index: -1; z-index: -1;
padding: 0 10px 0 50px; padding: 0 10px 0 50px;
} }
.collapse-button:hover, &.in .collapse-button, &.collapsing .collapse-button { & .toggle-button:hover, &.panel-open > .toggle-button {
right: -55px; right: -45px;
} }
.panel-content { .panel-content {
background-color: @secondary-bg-light; background-color: @secondary-bg-light;
@ -618,18 +611,15 @@ a {
} }
.document-panel { .document-panel {
position: absolute;
top: 0;
right: 0; right: 0;
width: @document-panel-width !important; width: @document-panel-width;
margin-right: (-@document-panel-width - 15); .toggle-button {
.transition(~"margin-right ease-in-out 0.35s");
&.in {
margin-right: 0;
}
.collapse-button {
border-top: 4px solid fade(@logo-yellow, @panel-button-border-fade); border-top: 4px solid fade(@logo-yellow, @panel-button-border-fade);
border-left: 5px solid fade(@logo-green, @panel-button-border-fade); border-left: 5px solid fade(@logo-green, @panel-button-border-fade);
border-bottom: 4px solid fade(@logo-orange, @panel-button-border-fade); border-bottom: 4px solid fade(@logo-orange, @panel-button-border-fade);
left: -50px; left: -37px;
padding: 0 50px 0 3px; padding: 0 50px 0 3px;
z-index: -1; z-index: -1;
i.icon-folder-open { i.icon-folder-open {
@ -637,8 +627,8 @@ a {
padding-bottom: 1px; padding-bottom: 1px;
} }
} }
.collapse-button:hover, &.in .collapse-button, &.collapsing .collapse-button { & .toggle-button:hover, &.panel-open > .toggle-button {
left: -55px; left: -45px;
} }
.panel-content { .panel-content {
background-color: @list-group-bg; background-color: @list-group-bg;
@ -965,9 +955,63 @@ a {
/********************************* /*********************************
* UI Layout * Layout
*********************************/ *********************************/
.layout-outer-wrapper {
.layout-panel();
overflow: hidden;
&.layout-animate {
.transition(350ms ease-in-out all);
}
}
.layout-inner-wrapper {
.layout-panel();
overflow: hidden;
}
.layout-resizer {
.layout-panel();
width: @resizer-size;
height: @resizer-size;
&.open {
cursor: e-resize;
}
}
.layout-toggler {
.layout-panel();
padding: 0;
margin: 0;
width: @resizer-size;
height: 60px;
i {
font-size: 22px;
}
&.layout-toggler-preview {
line-height: 55px;
.layout-animate & {
.transition(350ms ease-in-out all);
}
i:before {
content: '\e87d';
}
&.open > i:before {
content: '\e87e';
}
}
&.layout-toggler-navbar {
line-height: 0;
i {
font-size: 16px;
height: 10.5px;
overflow: hidden;
}
}
}
.ui-layout-resizer { .ui-layout-resizer {
overflow: visible !important; overflow: visible !important;
font-size: 14px !important; font-size: 14px !important;
@ -1019,21 +1063,6 @@ a {
border-bottom: 1px solid @primary-border-color; border-bottom: 1px solid @primary-border-color;
} }
.resizer-decorator {
position: absolute;
display: block !important;
.ui-layout-resizer-east & {
width: @resizer-size;
height: 100%;
border-left: 1px solid fade(@secondary, 6%);
}
.ui-layout-resizer-south & {
height: @resizer-size;
width: 100%;
border-top: 1px solid fade(@secondary, 6%);
}
}
/***************************** /*****************************
* Editor * Editor
@ -1065,6 +1094,7 @@ a {
} }
#wmd-input { #wmd-input {
.layout-panel();
font-family: "PT Sans", sans-serif; font-family: "PT Sans", sans-serif;
line-height: @editor-line-weight; line-height: @editor-line-weight;
letter-spacing: normal; letter-spacing: normal;
@ -1300,23 +1330,31 @@ a {
} }
} }
.textarea-helper {
font-family: @font-family-monospace; /*****************************
font-size: @font-size-base; * Preview
position: absolute; *****************************/
top: -100px;
height: 1px; .preview-panel {
padding: 0; .layout-panel();
line-height: @editor-line-weight; overflow: hidden;
position: absolute; border-left: 1px solid fade(@secondary, 6%);
overflow: auto; background-color: @secondary-bg-light;
white-space: pre-wrap; z-index: 10;
word-wrap: break-word; .layout-animate & {
.transition(350ms ease-in-out all);
}
}
#preview-contents {
padding: 35px;
margin: 0 auto 200px;
text-align: justify;
} }
.preview-container { .preview-container {
position: absolute;
overflow: auto; overflow: auto;
background-color: @secondary-bg-light;
} }
.wmd-prompt-background { .wmd-prompt-background {

View File

@ -231,13 +231,12 @@ define([
}; };
// Create a backdrop and add to the body // Create a backdrop and add to the body
utils.createBackdrop = function(toggle, target) { utils.createBackdrop = function(parent) {
var result = crel('div', { var result = crel('div', {
'class': 'modal-backdrop fade', 'class': 'modal-backdrop fade',
'data-toggle': toggle,
'data-target': target,
}); });
document.body.appendChild(result); parent = parent || document.body;
parent.appendChild(result);
result.offsetWidth; // force reflow result.offsetWidth; // force reflow
result.className = result.className + ' in'; result.className = result.className + ' in';
result.removeBackdrop = function() { result.removeBackdrop = function() {

View File

@ -8,7 +8,7 @@
<link rel="shortcut icon" href="res-min/img/stackedit-32.ico" type="image/x-icon"> <link rel="shortcut icon" href="res-min/img/stackedit-32.ico" type="image/x-icon">
<meta name="description" content="Full-featured, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites."> <meta name="description" content="Full-featured, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites.">
<meta name="author" content="Benoit Schweblin"> <meta name="author" content="Benoit Schweblin">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">
<meta name="msvalidate.01" content="5E47EE6F67B069C17E3CDD418351A612" <meta name="msvalidate.01" content="5E47EE6F67B069C17E3CDD418351A612"
/> />
<script> <script>