This commit is contained in:
benweet 2014-04-20 02:13:43 +01:00
parent b5191a7457
commit ab8b0cca02
19 changed files with 361 additions and 234 deletions

View File

@ -28,6 +28,7 @@
"MutationObservers": "https://github.com/Polymer/MutationObservers.git#~0.2.1", "MutationObservers": "https://github.com/Polymer/MutationObservers.git#~0.2.1",
"rangy": "~1.2.3", "rangy": "~1.2.3",
"google-diff-match-patch-js": "~1.0.0", "google-diff-match-patch-js": "~1.0.0",
"jsondiffpatch": "~0.1.5" "jsondiffpatch": "~0.1.5",
"hammerjs": "~1.0.10"
} }
} }

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, user-scalable=no"> <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<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

@ -62,7 +62,7 @@ define([
fileDesc = selectedFileDesc; fileDesc = selectedFileDesc;
}); });
// Watcher used to detect editor changes // Used to detect editor changes
function Watcher() { function Watcher() {
this.isWatching = false; this.isWatching = false;
var contentObserver; var contentObserver;
@ -212,9 +212,6 @@ define([
}; };
})(); })();
this.getCoordinates = function(inputOffset, container, offset) { this.getCoordinates = function(inputOffset, container, offset) {
if(inputOffset === textContent.length) {
return this.getCoordinates(inputOffset - 1);
}
if(!container) { if(!container) {
offset = this.findOffset(inputOffset); offset = this.findOffset(inputOffset);
container = offset.container; container = offset.container;
@ -485,6 +482,8 @@ define([
this.currentMode = undefined; this.currentMode = undefined;
lastMode = undefined; lastMode = undefined;
contentElt.textContent = content; contentElt.textContent = content;
// Force this since the content could be the same
checkContentChange();
}; };
} }
var undoMgr = new UndoMgr(); var undoMgr = new UndoMgr();
@ -503,12 +502,20 @@ define([
var trailingLfNode; var trailingLfNode;
function checkContentChange() { function checkContentChange() {
var newTextContent = inputElt.textContent; var newTextContent = inputElt.textContent;
if(contentElt.lastChild === trailingLfNode && trailingLfNode.textContent.slice(-1) === '\n') { if(contentElt.lastChild === trailingLfNode && trailingLfNode.textContent.slice(-1) == '\n') {
newTextContent = newTextContent.slice(0, -1); newTextContent = newTextContent.slice(0, -1);
} }
if(fileChanged === false) { if(fileChanged === false) {
if(contentElt.children.length && newTextContent == textContent) { if(newTextContent == textContent) {
// User has removed the empty section
if(contentElt.children.length === 0) {
contentElt.innerHTML = '';
sectionList.forEach(function(section) {
contentElt.appendChild(section.elt);
});
addTrailingLfNode();
}
return; return;
} }
undoMgr.currentMode = undoMgr.currentMode || 'typing'; undoMgr.currentMode = undoMgr.currentMode || 'typing';
@ -610,6 +617,15 @@ define([
} }
}); });
// See https://gist.github.com/shimondoodkin/1081133
if(/AppleWebKit\/([\d.]+)/.exec(navigator.userAgent)) {
var $editableFix = $('<input style="width:1px;height:1px;border:none;margin:0;padding:0;" tabIndex="-1">').appendTo('html');
$contentElt.blur(function () {
$editableFix[0].setSelectionRange(0, 0);
$editableFix.blur();
});
}
inputElt.focus = focus; inputElt.focus = focus;
Object.defineProperty(inputElt, 'value', { Object.defineProperty(inputElt, 'value', {
@ -704,10 +720,8 @@ define([
actions[action](state, options); actions[action](state, options);
setValue(state.before + state.selection + state.after); setValue(state.before + state.selection + state.after);
_.defer(function() { selectionMgr.setSelectionStartEnd(state.selectionStart, state.selectionEnd);
selectionMgr.setSelectionStartEnd(state.selectionStart, state.selectionEnd);
//$inputElt.trigger('input');
});
}; };
var actions = { var actions = {
@ -873,12 +887,19 @@ define([
childNode = nextNode; childNode = nextNode;
} }
} }
trailingLfNode = document.createTextNode('\n'); addTrailingLfNode();
contentElt.appendChild(trailingLfNode);
selectionMgr.setSelectionStartEnd(); selectionMgr.setSelectionStartEnd();
}); });
} }
function addTrailingLfNode() {
trailingLfNode = crel('span', {
class: 'token lf',
});
trailingLfNode.textContent = '\n';
contentElt.appendChild(trailingLfNode);
}
var escape = (function() { var escape = (function() {
var entityMap = { var entityMap = {
"&": "&amp;", "&": "&amp;",

View File

@ -73,6 +73,10 @@ define([
checkPublication(); checkPublication();
}; };
buttonPublish.onReady = function() {
$(".action-update-publication").click(publisher.publish);
};
buttonPublish.onPublishRemoved = checkPublication; buttonPublish.onPublishRemoved = checkPublication;
buttonPublish.onNewPublishSuccess = checkPublication; buttonPublish.onNewPublishSuccess = checkPublication;

View File

@ -93,6 +93,9 @@ define([
synchronizer.sync() && (lastSync = utils.currentTime); synchronizer.sync() && (lastSync = utils.currentTime);
e.preventDefault(); e.preventDefault();
}); });
$(".action-force-synchronization").click(function() {
synchronizer.sync() && (lastSync = utils.currentTime);
});
}; };
return buttonSync; return buttonSync;

View File

@ -21,6 +21,7 @@ define([
var syncListElt; var syncListElt;
var $msgSyncListElt; var $msgSyncListElt;
var $msgNoSyncElt; var $msgNoSyncElt;
var $showAlreadySynchronizedElt;
var refreshDialog = function(fileDescParameter) { var refreshDialog = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) { if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
return; return;
@ -35,6 +36,8 @@ define([
$msgNoSyncElt.removeClass("hide"); $msgNoSyncElt.removeClass("hide");
} }
$showAlreadySynchronizedElt.toggleClass("hide", _.size(fileDesc.syncLocations) === 0);
var syncListHtml = _.reduce(fileDesc.syncLocations, function(result, syncAttributes) { var syncListHtml = _.reduce(fileDesc.syncLocations, function(result, syncAttributes) {
return result + _.template(dialogManageSynchronizationLocationHTML, { return result + _.template(dialogManageSynchronizationLocationHTML, {
syncAttributes: syncAttributes, syncAttributes: syncAttributes,
@ -66,6 +69,8 @@ define([
syncListElt = modalElt.querySelector(".sync-list"); syncListElt = modalElt.querySelector(".sync-list");
$msgSyncListElt = $(modalElt.querySelectorAll(".msg-sync-list")); $msgSyncListElt = $(modalElt.querySelectorAll(".msg-sync-list"));
$msgNoSyncElt = $(modalElt.querySelectorAll(".msg-no-sync")); $msgNoSyncElt = $(modalElt.querySelectorAll(".msg-no-sync"));
$showAlreadySynchronizedElt = $(document.querySelectorAll(".show-already-synchronized"));
}; };
return dialogManageSynchronization; return dialogManageSynchronization;

View File

@ -12,7 +12,7 @@
'mod+z': bindPagedownButton('undo'), 'mod+z': bindPagedownButton('undo'),
'mod+y': bindPagedownButton('redo'), 'mod+y': bindPagedownButton('redo'),
'mod+shift+z': bindPagedownButton('redo'), 'mod+shift+z': bindPagedownButton('redo'),
'mod+p': function(evt) { 'mod+d': function(evt) {
$('.button-open-discussion').click(); $('.button-open-discussion').click();
evt.preventDefault(); evt.preventDefault();
}, },

View File

@ -74,18 +74,25 @@
</button> </button>
<div class="panel-content"> <div class="panel-content">
<div class="list-group"> <div class="list-group">
<a href="viewer" title="StackEdit Viewer" <a href="viewer" title="StackEdit Viewer"
class="list-group-item"><i class="icon-resize-full"></i> class="list-group-item">
StackEdit Viewer</a> <i class="icon-resize-full"></i> StackEdit Viewer
</a>
</div> </div>
<div class=dropdown-header>EXPORT</div>
<div class="list-group"> <div class="list-group">
<a href="#" data-toggle="collapse" data-target=".collapse-synchronize" <a href="#" data-toggle="collapse" data-target=".collapse-synchronize"
class="list-group-item"><i class="list-group-item">
class="icon-refresh"></i> Synchronize...</a> <div><i class="icon-refresh"></i> Synchronize</div>
<small>Backup, collaborate in the cloud</small>
</a>
<div class="sub-menu collapse collapse-synchronize clearfix"> <div class="sub-menu collapse collapse-synchronize clearfix">
<ul class="nav"> <ul class="nav alert alert-danger show-already-synchronized">
<li><div>"<span class="file-title"></span>" is already synchronized.</div></li>
<li><a href="#" class="action-force-synchronization"><i class="icon-refresh"></i>
Force synchronization</a></li>
<li><a href="#" data-toggle="modal" data-target=".modal-manage-sync" <li><a href="#" data-toggle="modal" data-target=".modal-manage-sync"
class="action-reset-input"><i class="action-reset-input"><i
class="icon-refresh"></i> Manage synchronization</a></li> class="icon-refresh"></i> Manage synchronization</a></li>
@ -93,22 +100,27 @@
<ul class="nav"> <ul class="nav">
<li><a href="#" class="action-sync-export-dialog-dropbox"><i <li><a href="#" class="action-sync-export-dialog-dropbox"><i
class="icon-provider-dropbox"></i> Save on Dropbox</a></li> class="icon-provider-dropbox"></i> Save on Dropbox</a></li>
<li><a href="#" class="action-sync-import-dropbox"><i
class="icon-provider-dropbox"></i> Open from Dropbox</a></li>
<li><a href="#" class="submenu-sync-gdrive action-sync-export-dialog-gdrive"><i <li><a href="#" class="submenu-sync-gdrive action-sync-export-dialog-gdrive"><i
class="icon-provider-gdrive"></i> Save on Google Drive</a></li> class="icon-provider-gdrive"></i> Save on Google Drive</a></li>
<li><a href="#" class="submenu-sync-gdrive action-sync-import-gdrive"><i
class="icon-provider-gdrive"></i> Open from Google Drive</a></li>
<li><a href="#" class="submenu-sync-gdrivesec action-sync-export-dialog-gdrivesec"><i <li><a href="#" class="submenu-sync-gdrivesec action-sync-export-dialog-gdrivesec"><i
class="icon-provider-gdrive"></i> Save on Google Drive <small>(2nd account)</small></a></li> class="icon-provider-gdrive"></i> Save on Google Drive <small>(2nd account)</small></a></li>
<li><a href="#" class="submenu-sync-gdrivesec action-sync-import-gdrivesec"><i
class="icon-provider-gdrive"></i> Open from Google Drive <small>(2nd account)</small></a></li>
<li><a href="#" class="submenu-sync-gdriveter action-sync-export-dialog-gdriveter"><i <li><a href="#" class="submenu-sync-gdriveter action-sync-export-dialog-gdriveter"><i
class="icon-provider-gdrive"></i> Save on Google Drive <small>(3rd account)</small></a></li> class="icon-provider-gdrive"></i> Save on Google Drive <small>(3rd account)</small></a></li>
<li><a href="#" class="submenu-sync-gdrive action-autosync-dialog-gdrive"><i <li><a href="#" class="submenu-sync-gdriveter action-sync-import-gdriveter"><i
class="icon-provider-gdrive"></i> Google Drive AutoSync</a></li> class="icon-provider-gdrive"></i> Open from Google Drive <small>(3rd account)</small></a></li>
<li><a href="#" class="submenu-sync-gdrivesec action-autosync-dialog-gdrivesec"><i
class="icon-provider-gdrive"></i> Google Drive AutoSync <small>(2nd account)</small></a></li>
<li><a href="#" class="submenu-sync-gdriveter action-autosync-dialog-gdriveter"><i
class="icon-provider-gdrive"></i> Google Drive AutoSync <small>(3rd account)</small></a></li>
</ul> </ul>
</div> </div>
<a href="#" data-toggle="collapse" data-target=".collapse-publish-on" <a href="#" data-toggle="collapse" data-target=".collapse-publish-on"
class="list-group-item"><i class="icon-upload"></i> Publish...</a> class="list-group-item">
<div><i class="icon-upload"></i>Publish</div>
<small>Export on the web</small>
</a>
<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>
@ -122,8 +134,13 @@
</ul> </ul>
</div> </div>
<a href="#" data-toggle="modal" data-target=".modal-manage-sharing" <a href="#" data-toggle="modal" data-target=".modal-manage-sharing"
class="action-reset-input list-group-item"><i class="icon-link"></i> class="action-reset-input list-group-item">
Sharing links</a> <div><i class="icon-link"></i>Sharing</div>
<small>Share document links</small>
</a>
</div>
<div class="list-group">
<a href="#" data-toggle="collapse" data-target=".collapse-save-as" <a href="#" data-toggle="collapse" data-target=".collapse-save-as"
class="list-group-item"><i class="icon-hdd"></i> Export to disk</a> class="list-group-item"><i class="icon-hdd"></i> Export to disk</a>
<div class="sub-menu collapse collapse-save-as clearfix"> <div class="sub-menu collapse collapse-save-as clearfix">
@ -138,21 +155,7 @@
class="icon-download"></i> Export as PDF</a></li> class="icon-download"></i> Export as PDF</a></li>
</ul> </ul>
</div> </div>
</div>
<div class=dropdown-header>IMPORT</div>
<div class="list-group">
<a class="list-group-item action-sync-import-dropbox" href="#"><i
class="icon-provider-dropbox"></i> Open from
Dropbox</a>
<a href="#" class="list-group-item submenu-sync-gdrive action-sync-import-gdrive"><i
class="icon-provider-gdrive"></i> Open from
Google Drive</a>
<a href="#" class="list-group-item submenu-sync-gdrivesec action-sync-import-gdrivesec"><i
class="icon-provider-gdrive"></i> Open from
Google Drive <small>(2nd account)</small></a>
<a href="#" class="list-group-item submenu-sync-gdriveter action-sync-import-gdriveter"><i
class="icon-provider-gdrive"></i> Open from
Google Drive <small>(3rd account)</small></a>
<a data-toggle="modal" <a data-toggle="modal"
data-target=".modal-import-harddrive-markdown" data-target=".modal-import-harddrive-markdown"
class="list-group-item action-reset-input" href="#"><i class="list-group-item action-reset-input" href="#"><i
@ -303,7 +306,7 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<a href="#" class="btn btn-default action-import-image-gplus" <a href="#" class="btn btn-default pull-left action-import-image-gplus"
data-dismiss="modal"><i class="icon-provider-gplus"></i> Import data-dismiss="modal"><i class="icon-provider-gplus"></i> Import
from Google+</a> <a href="#" class="btn btn-default" from Google+</a> <a href="#" class="btn btn-default"
data-dismiss="modal">Cancel</a> <a href="#" data-dismiss="modal">Cancel</a> <a href="#"

View File

@ -8,19 +8,33 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p> <p>
<b>AutoSync</b> feature automatically exports every new document to your <i <b>AutoSync</b> feature automatically saves all documents to your <i
class="icon-provider-<%= providerId %>"></i> class="icon-provider-<%= providerId %>"></i>
<code>Google Drive</code> <code>Google Drive</code>
account and keep it synchronized. account and keep them synchronized.
</p> </p>
<div class="form-horizontal"> <div class="form-horizontal">
<br/>
<div class="form-group"> <div class="form-group">
<div class="col-lg-3 control-label"></div> <div class="col-lg-3 control-label"></div>
<div class="col-lg-8"> <div class="col-lg-8">
<label> <input id="input-autosync-<%= providerId %>-enabled" <div class="radio">
type="checkbox"> Enable AutoSync for <%= providerName %> <label> <input type="radio"
</label> name="radio-autosync-<%= providerId %>-mode" value="off">
Disabled
</label>
</div>
<div class="radio">
<label> <input type="radio"
name="radio-autosync-<%= providerId %>-mode" value="new">
New documents (not current)
</label>
</div>
<div class="radio">
<label> <input type="radio"
name="radio-autosync-<%= providerId %>-mode" value="all">
Every document not yet synchronized
</label>
</div>
</div> </div>
</div> </div>
<br/> <br/>

View File

@ -55,6 +55,8 @@
</blockquote> </blockquote>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<a href="#" class="btn btn-default pull-left action-autosync-dialog-<%= providerId %>" data-dismiss="modal"><i
class="icon-provider-gdrive"></i> Setup AutoSync</a>
<a href="#" class="btn btn-default" data-dismiss="modal">Cancel</a> <a href="#" class="btn btn-default" data-dismiss="modal">Cancel</a>
<a href="#" data-dismiss="modal" <a href="#" data-dismiss="modal"
class="btn btn-primary action-sync-export-<%= providerId %>">OK</a> class="btn btn-primary action-sync-export-<%= providerId %>">OK</a>

View File

@ -7,7 +7,8 @@ define([
'eventMgr', 'eventMgr',
'crel', 'crel',
'mousetrap', 'mousetrap',
], function($, _, utils, constants, settings, eventMgr, crel, mousetrap) { 'hammerjs',
], function($, _, utils, constants, settings, eventMgr, crel, mousetrap, hammer) {
var layout = {}; var layout = {};
var resizerSize = 32; var resizerSize = 32;
@ -23,10 +24,11 @@ define([
}; };
var menuPanelWidth = 280; var menuPanelWidth = 280;
var documentPanelWidth = 320; var documentPanelWidth = 320;
var titleMinWidth = 200;
var windowSize; var windowSize;
var wrapperL1, wrapperL2, wrapperL3; var wrapperL1, wrapperL2, wrapperL3;
var navbar, menuPanel, documentPanel, editor, previewPanel, previewContainer, navbarToggler, previewToggler, previewResizer; var navbar, menuPanel, documentPanel, editor, previewPanel, previewContainer, navbarToggler, previewToggler, previewResizer, previewButtons;
var animate = false; var animate = false;
function startAnimation() { function startAnimation() {
@ -126,6 +128,19 @@ define([
resizeAll(); resizeAll();
}; };
}; };
DomObject.prototype.initHammer = function(drag) {
this.hammer = hammer(this.elt, {
drag: drag ? true : false,
drag_max_touches: 0,
gesture: false,
hold: false,
release: false,
swipe: drag ? false : true,
tap: false,
touch: false,
transform: false
});
};
var maxWidthMap = [ var maxWidthMap = [
{ screenWidth: 0, maxWidth: 600 }, { screenWidth: 0, maxWidth: 600 },
@ -154,9 +169,9 @@ define([
}); });
var navbarMarginWidth = 18 * 2 + 25 + 25; var navbarMarginWidth = 18 * 2 + 25 + 25;
var buttonsDropdownWidth = 40; var buttonsDropdownWidth = 40;
var titleMinWidth = 200;
var workingIndicatorWidth = 18 + 70; var workingIndicatorWidth = 18 + 70;
function onResize() { function onResize() {
var paddingBottom = wrapperL3.height - 60;
var editorPadding = (editor.elt.offsetWidth - getMaxWidth()) / 2; var editorPadding = (editor.elt.offsetWidth - getMaxWidth()) / 2;
if(editorPadding < constants.EDITOR_DEFAULT_PADDING) { if(editorPadding < constants.EDITOR_DEFAULT_PADDING) {
@ -164,6 +179,7 @@ define([
} }
editorContentElt.style.paddingLeft = editorPadding + 'px'; editorContentElt.style.paddingLeft = editorPadding + 'px';
editorContentElt.style.paddingRight = editorPadding + 'px'; editorContentElt.style.paddingRight = editorPadding + 'px';
editorContentElt.style.paddingBottom = paddingBottom + 'px';
editorMarginElt.style.width = editorPadding + 'px'; editorMarginElt.style.width = editorPadding + 'px';
var previewPadding = (previewContainer.elt.offsetWidth - getMaxWidth()) / 2; var previewPadding = (previewContainer.elt.offsetWidth - getMaxWidth()) / 2;
@ -172,6 +188,7 @@ define([
} }
previewContentElt.style.paddingLeft = previewPadding + 'px'; previewContentElt.style.paddingLeft = previewPadding + 'px';
previewContentElt.style.paddingRight = previewPadding + 'px'; previewContentElt.style.paddingRight = previewPadding + 'px';
previewContentElt.style.paddingBottom = paddingBottom + 'px';
if(!window.viewerMode) { if(!window.viewerMode) {
var maxWidth = navbarMarginWidth + workingIndicatorWidth + titleMinWidth + buttonsDropdownWidth; var maxWidth = navbarMarginWidth + workingIndicatorWidth + titleMinWidth + buttonsDropdownWidth;
@ -322,11 +339,11 @@ define([
navbarToggler = new DomObject('.layout-toggler-navbar'); navbarToggler = new DomObject('.layout-toggler-navbar');
previewToggler = new DomObject('.layout-toggler-preview'); previewToggler = new DomObject('.layout-toggler-preview');
previewResizer = new DomObject('.layout-resizer-preview'); previewResizer = new DomObject('.layout-resizer-preview');
previewButtons = new DomObject('.extension-preview-buttons');
editorContentElt = editor.elt.querySelector('.editor-content'); editorContentElt = editor.elt.querySelector('.editor-content');
previewContentElt = document.getElementById('preview-contents'); previewContentElt = document.getElementById('preview-contents');
editorMarginElt = editor.elt.querySelector('.editor-margin'); editorMarginElt = editor.elt.querySelector('.editor-margin');
var $previewButtonsElt = $('.extension-preview-buttons');
navbarInnerElt = navbar.elt.querySelector('.navbar-inner'); navbarInnerElt = navbar.elt.querySelector('.navbar-inner');
navbarDropdownElt = navbar.elt.querySelector('.buttons-dropdown .dropdown-menu'); navbarDropdownElt = navbar.elt.querySelector('.buttons-dropdown .dropdown-menu');
$navbarDropdownBtnElt = navbar.$elt.find('.buttons-dropdown'); $navbarDropdownBtnElt = navbar.$elt.find('.buttons-dropdown');
@ -354,7 +371,7 @@ define([
previewPanel.isOpen = true; previewPanel.isOpen = true;
previewPanel.createToggler(false, function(isOpen) { previewPanel.createToggler(false, function(isOpen) {
$previewButtonsElt.toggleClass('hide', !isOpen); previewButtons.$elt.toggleClass('hide', !isOpen);
eventMgr.onPreviewToggle(isOpen); eventMgr.onPreviewToggle(isOpen);
}); });
previewPanel.halfSize = true; previewPanel.halfSize = true;
@ -368,57 +385,39 @@ define([
documentPanel.createToggler(true); documentPanel.createToggler(true);
documentPanel.$elt.find('.toggle-button').click(_.bind(documentPanel.toggle, documentPanel)); documentPanel.$elt.find('.toggle-button').click(_.bind(documentPanel.toggle, documentPanel));
// Hide menu panel when clicking 'Save as' button // Gesture events
menuPanel.$elt.on('click', 'a[data-toggle!=collapse]', _.bind(menuPanel.toggle, menuPanel, false)); previewResizer.initHammer(true);
documentPanel.$elt.on('click', 'a[data-toggle!=collapse]', _.bind(documentPanel.toggle, documentPanel, false)); /*
navbar.initHammer();
menuPanel.initHammer();
documentPanel.initHammer();
previewButtons.initHammer();
menuPanel.$elt.on('show.bs.collapse', function() { navbar.hammer.on('swiperight', _.bind(menuPanel.toggle, menuPanel, true));
// Close all open sub-menus when one submenu opens navbar.hammer.on('swipeleft', _.bind(documentPanel.toggle, documentPanel, true));
menuPanel.$elt.find('.in').collapse('hide'); navbar.hammer.on('swipeup', _.bind(navbar.toggle, navbar, false));
});
var isDragging = false; menuPanel.hammer.on('swiperight', _.bind(menuPanel.toggle, menuPanel, true));
var desiredWidth, desiredHeight; menuPanel.hammer.on('swipeleft', _.bind(menuPanel.toggle, menuPanel, false));
var lastCoord;
wrapperL1.$elt.on('mousemove', function(evt) { documentPanel.hammer.on('swipeleft', _.bind(documentPanel.toggle, documentPanel, true));
if(!isDragging) { documentPanel.hammer.on('swiperight', _.bind(documentPanel.toggle, documentPanel, false));
return; */
}
if(evt.which !== 1) { var initialWidth, initialHeight;
isDragging = false; previewResizer.hammer.on('dragstart', function() {
initialWidth = previewPanel.width;
initialHeight = previewPanel.height;
}).on('drag', function(evt) {
if(isVertical) {
previewPanel.height = initialHeight - evt.gesture.deltaY;
} }
else { else {
var newCoord = { previewPanel.width = initialWidth - evt.gesture.deltaX;
x: evt.pageX,
y: evt.pageY,
};
if(isVertical && lastCoord.y !== newCoord.y) {
desiredHeight += lastCoord.y - newCoord.y;
previewPanel.height = desiredHeight;
previewPanel.halfSize = false;
resizeAll();
}
if(!isVertical && 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;
desiredHeight = previewPanel.height;
lastCoord = {
x: evt.pageX,
y: evt.pageY,
};
} }
evt.gesture.preventDefault();
previewPanel.halfSize = false;
resizeAll();
}); });
var isModalShown = false; var isModalShown = false;
@ -431,6 +430,15 @@ define([
isModalShown = false; isModalShown = false;
}); });
// Hide panels when clicking on a non collapse element
menuPanel.$elt.on('click', 'a[data-toggle!=collapse]', _.bind(menuPanel.toggle, menuPanel, false));
documentPanel.$elt.on('click', 'a[data-toggle!=collapse]', _.bind(documentPanel.toggle, documentPanel, false));
// In menu panel, close all open sub-menus when one submenu opens
menuPanel.$elt.on('show.bs.collapse', function() {
menuPanel.$elt.find('.in').collapse('hide');
});
// Configure Mousetrap // Configure Mousetrap
mousetrap.stopCallback = function() { mousetrap.stopCallback = function() {
return menuPanel.isOpen || documentPanel.isOpen || isModalShown; return menuPanel.isOpen || documentPanel.isOpen || isModalShown;
@ -469,11 +477,36 @@ define([
style.innerHTML = styleContent; style.innerHTML = styleContent;
document.head.appendChild(style); document.head.appendChild(style);
resizeAll(); resizeAll();
}; };
eventMgr.addListener('onReady', function() {
var previewButtonsOffset = previewButtons.elt.offsetWidth + 5;
function openPreviewButtons() {
clearTimeout(closeTimeoutId);
previewButtons.x = 0;
previewButtons.applyCss();
}
var closeTimeoutId;
var dropdownOpen = false;
function closePreviewButtons() {
clearTimeout(closeTimeoutId);
closeTimeoutId = setTimeout(function() {
if(!dropdownOpen) {
previewButtons.x = previewButtonsOffset;
previewButtons.applyCss();
}
}, 3000);
}
closePreviewButtons();
previewButtons.$elt.hover(openPreviewButtons, closePreviewButtons).on('show.bs.dropdown', function() {
dropdownOpen = true;
}).on('hidden.bs.dropdown', function() {
dropdownOpen = false;
closePreviewButtons();
});
});
eventMgr.onLayoutCreated(layout); eventMgr.onLayoutCreated(layout);
return layout; return layout;
}); });

View File

@ -59,7 +59,8 @@ requirejs.config({
'rangy-cssclassapplier': 'bower-libs/rangy/rangy-cssclassapplier', 'rangy-cssclassapplier': 'bower-libs/rangy/rangy-cssclassapplier',
diff_match_patch: 'bower-libs/google-diff-match-patch-js/diff_match_patch', diff_match_patch: 'bower-libs/google-diff-match-patch-js/diff_match_patch',
diff_match_patch_uncompressed: 'bower-libs/google-diff-match-patch-js/diff_match_patch_uncompressed', diff_match_patch_uncompressed: 'bower-libs/google-diff-match-patch-js/diff_match_patch_uncompressed',
jsondiffpatch: 'bower-libs/jsondiffpatch/build/bundle' jsondiffpatch: 'bower-libs/jsondiffpatch/build/bundle',
hammerjs: 'bower-libs/hammerjs/hammer'
}, },
shim: { shim: {
underscore: { underscore: {

View File

@ -262,14 +262,14 @@ define([
// Initialize the AutoSync dialog fields // Initialize the AutoSync dialog fields
gdriveProvider.setAutosyncDialogConfig = function() { gdriveProvider.setAutosyncDialogConfig = function() {
var config = gdriveProvider.autosyncConfig; var config = gdriveProvider.autosyncConfig;
utils.setInputChecked('#input-autosync-' + providerId + '-enabled', config.enabled); utils.setInputRadio('radio-autosync-' + providerId + '-mode', config.mode || 'off');
utils.setInputValue('#input-autosync-' + providerId + '-parentid', config.parentId); utils.setInputValue('#input-autosync-' + providerId + '-parentid', config.parentId);
}; };
// Retrieve the AutoSync dialog fields // Retrieve the AutoSync dialog fields
gdriveProvider.getAutosyncDialogConfig = function() { gdriveProvider.getAutosyncDialogConfig = function() {
var config = {}; var config = {};
config.enabled = utils.getInputChecked('#input-autosync-' + providerId + '-enabled'); config.mode = utils.getInputRadio('radio-autosync-' + providerId + '-mode');
config.parentId = utils.getInputTextValue('#input-autosync-' + providerId + '-parentid'); config.parentId = utils.getInputTextValue('#input-autosync-' + providerId + '-parentid');
return config; return config;
}; };

View File

@ -297,9 +297,7 @@ define([
}); });
} }
//
$(".action-process-publish").click(performNewLocation); $(".action-process-publish").click(performNewLocation);
$(".action-update-publication").click(publisher.publish);
var $customTmplCollapseElt = $('.publish-custom-template-collapse').collapse({ var $customTmplCollapseElt = $('.publish-custom-template-collapse').collapse({
toggle: false toggle: false

View File

@ -288,6 +288,7 @@ kbd {
// Custom icons (not from Font Awesome) // Custom icons (not from Font Awesome)
.icon-code { .icon-code {
font-size: 83%; font-size: 83%;
line-height: 1.65em;
&:before { &:before {
margin-left: 0.1em; margin-left: 0.1em;
margin-right: 0.6em; margin-right: 0.6em;

View File

@ -111,6 +111,7 @@
@btn-info-color: fade(@secondary-desaturated, 35%); @btn-info-color: fade(@secondary-desaturated, 35%);
@btn-info-bg: @transparent; @btn-info-bg: @transparent;
@btn-info-border: @transparent; @btn-info-border: @transparent;
@btn-info-hover-border: fade(@secondary, 8%);
@btn-info-hover-bg: lighten(@secondary-desaturated, 45%); @btn-info-hover-bg: lighten(@secondary-desaturated, 45%);
@gray-lighter: @body-bg; @gray-lighter: @body-bg;
@modal-header-border-color: @secondary-border-color-light; @modal-header-border-color: @secondary-border-color-light;
@ -318,7 +319,7 @@ a {
.info-tooltip &, .info-tooltip &,
.open &.dropdown-toggle { .open &.dropdown-toggle {
color: darken(@secondary, 30%); color: darken(@secondary, 30%);
border-color: fade(@secondary, 8%); border-color: @btn-info-hover-border;
background-color: @btn-info-hover-bg !important; // important to override .nav > li > a:hover background-color: @btn-info-hover-bg !important; // important to override .nav > li > a:hover
} }
} }
@ -583,10 +584,17 @@ a {
.layout-panel(); .layout-panel();
width: @menu-panel-width; width: @menu-panel-width;
.alert { .alert {
padding: 15px 0; padding: 10px 0;
} }
i {
margin-right: 8px;
}
small {
color: @dropdown-header-color;
padding-left: 30px;
}
.nav { .nav {
margin: 20px 0; margin: 10px 0 20px;
> li > * { > li > * {
padding: 8px 30px; padding: 8px 30px;
} }
@ -794,18 +802,20 @@ a {
.extension-preview-buttons { .extension-preview-buttons {
position: absolute; position: absolute;
z-index: 1; z-index: 1;
right: 35px; right: 50px;
margin-top: 6px; margin-top: 6px;
.ui-layout-resizer-south-closed & { background-color: @btn-info-hover-bg;
display: none !important; border: 1px solid @btn-info-hover-border;
} border-radius: @border-radius-base;
.transition-transform(350ms ease-in-out);
.btn-group { .btn-group {
.btn { .btn {
position: initial; position: initial;
border: 0;
} }
} }
.dropdown-menu { .dropdown-menu {
margin-top: 4px; margin-top: 6px;
padding-bottom: 20px; padding-bottom: 20px;
} }
@ -1435,10 +1445,6 @@ div.dropdown-menu, {
z-index: 1040 !important; z-index: 1040 !important;
} }
.action-import-image-gplus {
float: left;
}
.tooltip-inner { .tooltip-inner {
text-align: left; text-align: left;
} }
@ -1614,10 +1620,6 @@ div.jGrowl {
} }
} }
.ui-layout-toggler {
display: none !important;
}
.navbar .file-title-navbar { .navbar .file-title-navbar {
cursor: initial; cursor: initial;
.box-shadow(none); .box-shadow(none);

View File

@ -74,118 +74,149 @@ define([
}; };
/*************************************************************************** /***************************************************************************
* Standard synchronization * Synchronization
**************************************************************************/ **************************************************************************/
// Recursive function to upload a single file on multiple locations
var uploadSyncAttributesList = [];
var uploadContent;
var uploadContentCRC;
var uploadTitle;
var uploadTitleCRC;
var uploadDiscussionList;
var uploadDiscussionListCRC;
function locationUp(callback) {
// No more synchronized location for this document
if(uploadSyncAttributesList.length === 0) {
return fileUp(callback);
}
// Dequeue a synchronized location
var syncAttributes = uploadSyncAttributesList.pop();
syncAttributes.provider.syncUp(
uploadContent,
uploadContentCRC,
uploadTitle,
uploadTitleCRC,
uploadDiscussionList,
uploadDiscussionListCRC,
syncAttributes,
function(error, uploadFlag) {
if(uploadFlag === true) {
// If uploadFlag is true, request another upload cycle
uploadCycle = true;
}
if(error) {
return callback(error);
}
if(uploadFlag) {
// Update syncAttributes in storage
utils.storeAttributes(syncAttributes);
}
locationUp(callback);
}
);
}
// Recursive function to upload multiple files
var uploadFileList = [];
function fileUp(callback) {
// No more fileDesc to synchronize
if(uploadFileList.length === 0) {
return syncUp(callback);
}
// Dequeue a fileDesc to synchronize
var fileDesc = uploadFileList.pop();
uploadSyncAttributesList = _.values(fileDesc.syncLocations);
if(uploadSyncAttributesList.length === 0) {
return fileUp(callback);
}
// Get document title/content
uploadContent = fileDesc.content;
uploadContentCRC = utils.crc32(uploadContent);
uploadTitle = fileDesc.title;
uploadTitleCRC = utils.crc32(uploadTitle);
uploadDiscussionList = fileDesc.discussionListJSON;
uploadDiscussionListCRC = utils.crc32(uploadDiscussionList);
locationUp(callback);
}
// Entry point for up synchronization (upload changes) // Entry point for up synchronization (upload changes)
var uploadCycle = false; var uploadCycle = false;
function syncUp(callback) { function syncUp(callback) {
var uploadFileList = [];
// Recursive function to upload multiple files
function fileUp() {
// No more fileDesc to synchronize
if(uploadFileList.length === 0) {
return syncUp(callback);
}
// Dequeue a fileDesc to synchronize
var fileDesc = uploadFileList.pop();
var uploadSyncAttributesList = _.values(fileDesc.syncLocations);
if(uploadSyncAttributesList.length === 0) {
return fileUp();
}
var uploadContent = fileDesc.content;
var uploadContentCRC = utils.crc32(uploadContent);
var uploadTitle = fileDesc.title;
var uploadTitleCRC = utils.crc32(uploadTitle);
var uploadDiscussionList = fileDesc.discussionListJSON;
var uploadDiscussionListCRC = utils.crc32(uploadDiscussionList);
// Recursive function to upload a single file on multiple locations
function locationUp() {
// No more synchronized location for this document
if(uploadSyncAttributesList.length === 0) {
return fileUp();
}
// Dequeue a synchronized location
var syncAttributes = uploadSyncAttributesList.pop();
syncAttributes.provider.syncUp(
uploadContent,
uploadContentCRC,
uploadTitle,
uploadTitleCRC,
uploadDiscussionList,
uploadDiscussionListCRC,
syncAttributes,
function(error, uploadFlag) {
if(uploadFlag === true) {
// If uploadFlag is true, request another upload cycle
uploadCycle = true;
}
if(error) {
return callback(error);
}
if(uploadFlag) {
// Update syncAttributes in storage
utils.storeAttributes(syncAttributes);
}
locationUp();
}
);
}
locationUp();
}
if(uploadCycle === true) { if(uploadCycle === true) {
// New upload cycle // New upload cycle
uploadCycle = false; uploadCycle = false;
uploadFileList = _.values(fileSystem); uploadFileList = _.values(fileSystem);
fileUp(callback); fileUp();
} }
else { else {
callback(); callback();
} }
} }
// Recursive function to download changes from multiple providers
var providerList = [];
function providerDown(callback) {
if(providerList.length === 0) {
return callback();
}
var provider = providerList.pop();
// Check that provider has files to sync
if(!synchronizer.hasSync(provider)) {
return providerDown(callback);
}
// Perform provider's syncDown
provider.syncDown(function(error) {
if(error) {
return callback(error);
}
providerDown(callback);
});
}
// Entry point for down synchronization (download changes) // Entry point for down synchronization (download changes)
function syncDown(callback) { function syncDown(callback) {
providerList = _.values(providerMap); var providerList = _.values(providerMap);
providerDown(callback);
// Recursive function to download changes from multiple providers
function providerDown() {
if(providerList.length === 0) {
return callback();
}
var provider = providerList.pop();
// Check that provider has files to sync
if(!synchronizer.hasSync(provider)) {
return providerDown();
}
// Perform provider's syncDown
provider.syncDown(function(error) {
if(error) {
return callback(error);
}
providerDown();
});
}
providerDown();
}
// Entry point for the autosync feature
function autosyncAll(callback) {
var autosyncFileList = _.filter(fileSystem, function(fileDesc) {
return _.size(fileDesc.syncLocations) === 0;
});
// Recursive function to autosync multiple files
function fileAutosync() {
// No more fileDesc to synchronize
if(autosyncFileList.length === 0) {
return callback();
}
var fileDesc = autosyncFileList.pop();
var providerList = _.filter(providerMap, function(provider) {
return provider.autosyncConfig.mode == 'all';
});
function providerAutosync() {
// No more provider
if(providerList.length === 0) {
return fileAutosync();
}
var provider = providerList.pop();
provider.autosyncFile(fileDesc.title, fileDesc.content, fileDesc.discussionListJSON, provider.autosyncConfig, function(error, syncAttributes) {
if(error) {
return callback(error);
}
fileDesc.addSyncLocation(syncAttributes);
eventMgr.onSyncExportSuccess(fileDesc, syncAttributes);
providerAutosync();
});
}
providerAutosync();
}
fileAutosync();
} }
// Listen to offline status changes // Listen to offline status changes
@ -214,17 +245,22 @@ define([
return false; return false;
} }
syncDown(function(error) { autosyncAll(function(error) {
if(isError(error)) { if(isError(error)) {
return; return;
} }
syncUp(function(error) { syncDown(function(error) {
if(isError(error)) { if(isError(error)) {
return; return;
} }
syncRunning = false; syncUp(function(error) {
eventMgr.onSyncRunning(false); if(isError(error)) {
eventMgr.onSyncSuccess(); return;
}
syncRunning = false;
eventMgr.onSyncRunning(false);
eventMgr.onSyncSuccess();
});
}); });
}); });
return true; return true;
@ -261,7 +297,10 @@ 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, fileDesc.discussionListJSON, provider.autosyncConfig, function(error, syncAttributes) { if(provider.autosyncConfig.mode != 'new') {
return;
}
provider.autosyncFile(fileDesc.title, fileDesc.content, fileDesc.discussionListJSON, provider.autosyncConfig, function(error, syncAttributes) {
if(error) { if(error) {
return; return;
} }

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, user-scalable=no"> <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<meta name="msvalidate.01" content="5E47EE6F67B069C17E3CDD418351A612" <meta name="msvalidate.01" content="5E47EE6F67B069C17E3CDD418351A612"
/> />
<script> <script>