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",
"rangy": "~1.2.3",
"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">
<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="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="msvalidate.01" content="5E47EE6F67B069C17E3CDD418351A612"
/>

View File

@ -62,7 +62,7 @@ define([
fileDesc = selectedFileDesc;
});
// Watcher used to detect editor changes
// Used to detect editor changes
function Watcher() {
this.isWatching = false;
var contentObserver;
@ -212,9 +212,6 @@ define([
};
})();
this.getCoordinates = function(inputOffset, container, offset) {
if(inputOffset === textContent.length) {
return this.getCoordinates(inputOffset - 1);
}
if(!container) {
offset = this.findOffset(inputOffset);
container = offset.container;
@ -485,6 +482,8 @@ define([
this.currentMode = undefined;
lastMode = undefined;
contentElt.textContent = content;
// Force this since the content could be the same
checkContentChange();
};
}
var undoMgr = new UndoMgr();
@ -503,12 +502,20 @@ define([
var trailingLfNode;
function checkContentChange() {
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);
}
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;
}
undoMgr.currentMode = undoMgr.currentMode || 'typing';
@ -609,6 +616,15 @@ define([
fileDesc.previewScrollTop = previewElt.scrollTop;
}
});
// 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;
@ -704,10 +720,8 @@ define([
actions[action](state, options);
setValue(state.before + state.selection + state.after);
_.defer(function() {
selectionMgr.setSelectionStartEnd(state.selectionStart, state.selectionEnd);
//$inputElt.trigger('input');
});
selectionMgr.setSelectionStartEnd(state.selectionStart, state.selectionEnd);
};
var actions = {
@ -873,12 +887,19 @@ define([
childNode = nextNode;
}
}
trailingLfNode = document.createTextNode('\n');
contentElt.appendChild(trailingLfNode);
addTrailingLfNode();
selectionMgr.setSelectionStartEnd();
});
}
function addTrailingLfNode() {
trailingLfNode = crel('span', {
class: 'token lf',
});
trailingLfNode.textContent = '\n';
contentElt.appendChild(trailingLfNode);
}
var escape = (function() {
var entityMap = {
"&": "&amp;",

View File

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

View File

@ -87,14 +87,17 @@ define([
isOffline = isOfflineParameter;
updateButtonState();
};
buttonSync.onReady = function() {
mousetrap.bind(buttonSync.config.syncShortcut, function(e) {
synchronizer.sync() && (lastSync = utils.currentTime);
e.preventDefault();
});
$(".action-force-synchronization").click(function() {
synchronizer.sync() && (lastSync = utils.currentTime);
});
};
return buttonSync;
});
});

View File

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

View File

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

View File

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

View File

@ -97,7 +97,7 @@ Header 2
&gt; &gt; And, they can be nested.
&gt; #### Headers in blockquotes
&gt;
&gt;
&gt; * You can quote a list.
&gt; * Etc.
</code></pre>
@ -129,14 +129,14 @@ like `` `this` ``.
* * *
- - - -
- - - -
</code></pre>
<h4>Manual Line Breaks</h4>
<p>End a line with two or more spaces:</p>
<pre><code>Roses are red,
<pre><code>Roses are red,
Violets are blue.
</code></pre>

View File

@ -8,19 +8,33 @@
</div>
<div class="modal-body">
<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>
<code>Google Drive</code>
account and keep it synchronized.
account and keep them synchronized.
</p>
<div class="form-horizontal">
<br/>
<div class="form-group">
<div class="col-lg-3 control-label"></div>
<div class="col-lg-8">
<label> <input id="input-autosync-<%= providerId %>-enabled"
type="checkbox"> Enable AutoSync for <%= providerName %>
</label>
<div class="radio">
<label> <input type="radio"
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>
<br/>

View File

@ -55,6 +55,8 @@
</blockquote>
</div>
<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="#" data-dismiss="modal"
class="btn btn-primary action-sync-export-<%= providerId %>">OK</a>

View File

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

View File

@ -59,7 +59,8 @@ requirejs.config({
'rangy-cssclassapplier': 'bower-libs/rangy/rangy-cssclassapplier',
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',
jsondiffpatch: 'bower-libs/jsondiffpatch/build/bundle'
jsondiffpatch: 'bower-libs/jsondiffpatch/build/bundle',
hammerjs: 'bower-libs/hammerjs/hammer'
},
shim: {
underscore: {

View File

@ -262,14 +262,14 @@ define([
// Initialize the AutoSync dialog fields
gdriveProvider.setAutosyncDialogConfig = function() {
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);
};
// Retrieve the AutoSync dialog fields
gdriveProvider.getAutosyncDialogConfig = function() {
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');
return config;
};

View File

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

View File

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

View File

@ -111,6 +111,7 @@
@btn-info-color: fade(@secondary-desaturated, 35%);
@btn-info-bg: @transparent;
@btn-info-border: @transparent;
@btn-info-hover-border: fade(@secondary, 8%);
@btn-info-hover-bg: lighten(@secondary-desaturated, 45%);
@gray-lighter: @body-bg;
@modal-header-border-color: @secondary-border-color-light;
@ -318,7 +319,7 @@ a {
.info-tooltip &,
.open &.dropdown-toggle {
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
}
}
@ -583,10 +584,17 @@ a {
.layout-panel();
width: @menu-panel-width;
.alert {
padding: 15px 0;
padding: 10px 0;
}
i {
margin-right: 8px;
}
small {
color: @dropdown-header-color;
padding-left: 30px;
}
.nav {
margin: 20px 0;
margin: 10px 0 20px;
> li > * {
padding: 8px 30px;
}
@ -794,18 +802,20 @@ a {
.extension-preview-buttons {
position: absolute;
z-index: 1;
right: 35px;
right: 50px;
margin-top: 6px;
.ui-layout-resizer-south-closed & {
display: none !important;
}
background-color: @btn-info-hover-bg;
border: 1px solid @btn-info-hover-border;
border-radius: @border-radius-base;
.transition-transform(350ms ease-in-out);
.btn-group {
.btn {
position: initial;
border: 0;
}
}
.dropdown-menu {
margin-top: 4px;
margin-top: 6px;
padding-bottom: 20px;
}
@ -1435,10 +1445,6 @@ div.dropdown-menu, {
z-index: 1040 !important;
}
.action-import-image-gplus {
float: left;
}
.tooltip-inner {
text-align: left;
}
@ -1614,10 +1620,6 @@ div.jGrowl {
}
}
.ui-layout-toggler {
display: none !important;
}
.navbar .file-title-navbar {
cursor: initial;
.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)
var uploadCycle = false;
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) {
// New upload cycle
uploadCycle = false;
uploadFileList = _.values(fileSystem);
fileUp(callback);
fileUp();
}
else {
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)
function syncDown(callback) {
providerList = _.values(providerMap);
providerDown(callback);
var providerList = _.values(providerMap);
// 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
@ -214,17 +245,22 @@ define([
return false;
}
syncDown(function(error) {
autosyncAll(function(error) {
if(isError(error)) {
return;
}
syncUp(function(error) {
syncDown(function(error) {
if(isError(error)) {
return;
}
syncRunning = false;
eventMgr.onSyncRunning(false);
eventMgr.onSyncSuccess();
syncUp(function(error) {
if(isError(error)) {
return;
}
syncRunning = false;
eventMgr.onSyncRunning(false);
eventMgr.onSyncSuccess();
});
});
});
return true;
@ -261,7 +297,10 @@ define([
eventMgr.addListener("onFileCreated", function(fileDesc) {
if(_.size(fileDesc.syncLocations) === 0) {
_.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) {
return;
}

View File

@ -8,7 +8,7 @@
<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="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"
/>
<script>