Import files from disk

This commit is contained in:
benweet 2013-06-16 11:47:35 +01:00
parent b3edbdd0c2
commit d334dfe2f8
26 changed files with 627 additions and 882 deletions

File diff suppressed because it is too large Load Diff

View File

@ -11,18 +11,23 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="msvalidate.01" content="5E47EE6F67B069C17E3CDD418351A612" />
<script>
// Use http://.../?debug to serve original CSS/JavaScript files instead of minified
var suffix = "";
if (!location.search.match(/(\?|&)debug/)) {
suffix = "-min";
}
document.write('<link href="css/main' + suffix + '.css" rel="stylesheet">');
var theme = localStorage.theme;
if (theme) {
document.write('<link href="themes/' + theme + '/' + theme + '.css" rel="stylesheet">');
}
var require = { baseUrl : "js", deps : [ "main" + suffix ] };
var viewerMode = false;
// Use http://.../?debug to serve original CSS/JavaScript files instead of minified
var suffix = "";
if(!location.search.match(/(\?|&)debug/)) {
suffix = "-min";
}
document.write('<link href="css/main' + suffix + '.css" rel="stylesheet">');
var theme = localStorage.theme;
if(theme) {
document.write('<link href="themes/' + theme + '/' + theme + '.css" rel="stylesheet">');
}
var require = {
baseUrl: "js",
deps: [
"main" + suffix
]
};
var viewerMode = false;
</script>
<script src="js/libs/require.js"></script>
</head>
@ -34,8 +39,7 @@
<li><div id="wmd-button-bar"></div></li>
</ul>
<ul class="nav pull-right hide" id="menu-bar">
<li id="extension-buttons">
</li>
<li id="extension-buttons"></li>
<li class="btn-group"><button class="btn action-create-file"
title="New local document">
<i class="icon-file"></i>
@ -62,13 +66,27 @@
</button>
<ul class="dropdown-menu">
<li><a href="viewer.html" title="StackEdit Viewer"><i
class="icon-fullscreen"></i> Open viewer</a></li>
<li><a class="action-download-md" href="#"><i
class="icon-download-alt"></i> Save as Markdown</a></li>
<li><a class="action-download-html" href="#"><i
class="icon-download-alt"></i> Save as HTML</a></li>
<li><a class="action-download-template" href="#"><i
class="icon-download-alt"></i> Save using template</a></li>
class="icon-fullscreen"></i> StackEdit Viewer</a></li>
<li class="dropdown-submenu"><a href="#"><i
class="icon-hdd"></i> Open from...</a>
<ul class="dropdown-menu">
<li><a data-toggle="modal"
data-target="#modal-import-harddrive-markdown" class="action-reset-input"
href="#">Import from hard drive</a></li>
<li><a data-toggle="modal"
data-target="#modal-import-harddrive-html" class="action-reset-input"
href="#">Convert HTML to Markdown</a></li>
</ul></li>
<li class="dropdown-submenu"><a href="#"><i
class="icon-hdd"></i> Save as...</a>
<ul class="dropdown-menu">
<li><a class="action-download-md" href="#">Save as
Markdown</a></li>
<li><a class="action-download-html" href="#">Save as
HTML</a></li>
<li><a class="action-download-template" href="#">Save
using template</a></li>
</ul></li>
<li class="divider with-text">synchronize</li>
<li class="dropdown-submenu"><a href="#"><i
class="icon-gdrive"></i> Google Drive</a>
@ -134,9 +152,8 @@
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Cancel</a>
<a href="#" class="btn btn-primary action-insert-link"
data-dismiss="modal">OK</a>
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
class="btn btn-primary action-insert-link" data-dismiss="modal">OK</a>
</div>
</div>
@ -155,9 +172,10 @@
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn action-import-image-gplus" data-dismiss="modal"><i class="icon-gplus"></i> Import from Google+</a>
<a href="#" class="btn" data-dismiss="modal">Cancel</a>
<a href="#" class="btn btn-primary action-insert-image"
<a href="#" class="btn action-import-image-gplus"
data-dismiss="modal"><i class="icon-gplus"></i> Import from
Google+</a> <a href="#" class="btn" data-dismiss="modal">Cancel</a> <a
href="#" class="btn btn-primary action-insert-image"
data-dismiss="modal">OK</a>
</div>
</div>
@ -169,32 +187,33 @@
<h3>Import image</h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<div class="control-group">
<div class="controls">
<img>
</div>
</div>
<div class="control-group">
<label class="control-label" for="input-import-image-title">Title (optional)</label>
<div class="controls">
<input type="text" id="input-import-image-title"
placeholder="Image title">
</div>
</div>
<div class="control-group">
<label class="control-label" for="input-import-image-size">Size limit
(optional)</label>
<div class="controls">
<input type="text" id="input-import-image-size" placeholder="345" class="input-mini"><span class="help-inline">px</span>
</div>
</div>
</div>
<div class="form-horizontal">
<div class="control-group">
<div class="controls">
<img>
</div>
</div>
<div class="control-group">
<label class="control-label" for="input-import-image-title">Title
(optional)</label>
<div class="controls">
<input type="text" id="input-import-image-title"
placeholder="Image title">
</div>
</div>
<div class="control-group">
<label class="control-label" for="input-import-image-size">Size
limit (optional)</label>
<div class="controls">
<input type="text" id="input-import-image-size" placeholder="345"
class="input-mini"><span class="help-inline">px</span>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Cancel</a>
<a href="#" class="btn btn-primary action-import-image"
data-dismiss="modal">OK</a>
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
class="btn btn-primary action-import-image" data-dismiss="modal">OK</a>
</div>
</div>
@ -218,6 +237,40 @@
</div>
</div>
<div id="modal-import-harddrive-markdown" class="modal hide">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h3>Import from hard drive</h3>
</div>
<div class="modal-body">
<p>Please select the Markdown files to import:</p>
<p><input type="file" id="input-file-import-harddrive-markdown" multiple /></p>
<p>Or drag and drop the Markdown files here:</p>
<div id="dropzone-import-harddrive-markdown" class="drop-zone">Drop zone</div>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
</div>
</div>
<div id="modal-import-harddrive-html" class="modal hide">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h3>Import from hard drive</h3>
</div>
<div class="modal-body">
<p>Please select the HTML files to import:</p>
<p><input type="file" id="input-file-import-harddrive-html" multiple /></p>
<p>Or drag and drop the HTML files here:</p>
<div id="dropzone-import-harddrive-html" class="drop-zone">Drop zone</div>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
</div>
</div>
<div id="modal-upload-gdrive" class="modal hide">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
@ -442,7 +495,11 @@
site</label>
<div class="controls">
<input type="text" id="input-publish-wordpress-site"
placeholder="exemple.wordpress.com">
placeholder="exemple.wordpress.com"> <span
class="help-block"> <a target="_blank"
href="http://jetpack.me/">Jetpack plugin</a> is required for
self-hosted sites.
</span>
</div>
</div>
<div
@ -547,16 +604,16 @@
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h3>Settings</h3>
<ul class="nav nav-tabs">
<li class="active"><a class="action-load-settings"
href="#tabpane-settings-editor" data-toggle="tab">Editor</a></li>
<li><a class="action-load-settings"
href="#tabpane-settings-publish" data-toggle="tab">Publish</a></li>
<li><a class="action-load-settings"
href="#tabpane-settings-extensions" data-toggle="tab">Extensions</a></li>
<li><a class="action-load-settings"
href="#tabpane-settings-utils" data-toggle="tab">Utils</a></li>
</ul>
<ul class="nav nav-tabs">
<li class="active"><a class="action-load-settings"
href="#tabpane-settings-editor" data-toggle="tab">Editor</a></li>
<li><a class="action-load-settings"
href="#tabpane-settings-publish" data-toggle="tab">Publish</a></li>
<li><a class="action-load-settings"
href="#tabpane-settings-extensions" data-toggle="tab">Extensions</a></li>
<li><a class="action-load-settings"
href="#tabpane-settings-utils" data-toggle="tab">Utils</a></li>
</ul>
</div>
<div class="modal-body">
@ -647,12 +704,14 @@
<div class="tab-pane" id="tabpane-settings-utils">
<div class="tab-pane-button-container">
<a href="#" class="btn btn-block btn-primary action-welcome-file"
data-dismiss="modal"><i class="icon-info-sign icon-white"></i> Welcome document</a> <a href="#"
data-dismiss="modal"><i class="icon-info-sign icon-white"></i>
Welcome document</a> <a href="#"
class="btn btn-block btn-primary action-default-settings"
data-dismiss="modal"><i class="icon-wrench icon-white"></i> Load default settings</a> <a href="#"
data-dismiss="modal"><i class="icon-wrench icon-white"></i>
Load default settings</a> <a href="#"
class="btn btn-block btn-primary" data-dismiss="modal"
data-toggle="modal" data-target="#modal-app-reset"><i class="icon-fire icon-white"></i> Reset
application</a>
data-toggle="modal" data-target="#modal-app-reset"><i
class="icon-fire icon-white"></i> Reset application</a>
</div>
</div>
</div>
@ -670,8 +729,7 @@
aria-hidden="true">&times;</button>
<img src="img/stackedit-promo.png" />
</div>
<div class="modal-body">
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
</div>

View File

@ -1,198 +0,0 @@
/**
* Used to run asynchronous tasks sequentially (ajax mainly). An asynchronous
* task is composed of different callback types: onRun, onSuccess, onError
*/
define([
"underscore",
"core",
"utils",
"extensionMgr",
"libs/stacktrace",
], function(_, core, utils, extensionMgr) {
var asyncRunner = {};
var taskQueue = [];
var asyncRunning = false;
var currentTask = undefined;
var currentTaskRunning = false;
var currentTaskStartTime = 0;
asyncRunner.createTask = function() {
var task = {};
task.finished = false;
task.timeout = ASYNC_TASK_DEFAULT_TIMEOUT;
task.retryCounter = 0;
task.callPath = [];
/**
* onRun callbacks are called by chain(). These callbacks have to call
* chain() themselves to chain with next onRun callback or error() to
* throw an exception or retry() to restart the task.
*/
// Run callbacks
task.runCallbacks = [];
task.onRun = function(callback) {
task.runCallbacks.push(callback);
};
/**
* onSuccess callbacks are called when every onRun callbacks have
* succeed.
*/
task.successCallbacks = [];
task.onSuccess = function(callback) {
task.successCallbacks.push(callback);
};
/**
* onError callbacks are called when error() is called in a onRun
* callback.
*/
task.errorCallbacks = [];
task.onError = function(callback) {
task.errorCallbacks.push(callback);
};
/**
* chain() calls the next onRun callback or the onSuccess callbacks when
* finished. The optional callback parameter can be used to pass an
* onRun callback during execution.
*/
task.chain = function(callback) {
task.callPath.unshift(printStackTrace()[5]);
if(task.finished === true) {
return;
}
// If first execution
if(task.queue === undefined) {
// Create a copy of the onRun callbacks
task.queue = task.runCallbacks.slice();
}
// If a callback is passed as a parameter
if(callback !== undefined) {
callback();
return;
}
// If all callbacks have been run
if(task.queue.length === 0) {
// Run the onSuccess callbacks
runSafe(task, task.successCallbacks);
return;
}
// Run the next callback
var runCallback = task.queue.shift();
runCallback();
};
/**
* error() calls the onError callbacks passing the error parameter and
* ends the task by throwing an exception.
*/
task.error = function(error) {
task.callPath.unshift(printStackTrace()[5]);
if(task.finished === true) {
return;
}
error = error || new Error("Unknown error|\n" + task.callPath.join("\n"));
if(error.message) {
extensionMgr.onError(error);
}
runSafe(task, task.errorCallbacks, error);
// Exit the current call stack
throw error;
};
/**
* retry() can be called in an onRun callback to restart the task
*/
task.retry = function(error, maxRetryCounter) {
if(task.finished === true) {
return;
}
maxRetryCounter = maxRetryCounter || 5;
task.queue = undefined;
if(task.retryCounter >= maxRetryCounter) {
task.error(error);
return;
}
// Implement an exponential backoff
var delay = Math.pow(2, task.retryCounter++) * 1000;
currentTaskStartTime = utils.currentTime + delay;
currentTaskRunning = false;
task.callPath = [];
asyncRunner.runTask();
};
return task;
};
// Run the next task in the queue if any and no other running
asyncRunner.runTask = function() {
// Use defer to avoid stack overflow
_.defer(function() {
// If there is a task currently running
if(currentTaskRunning === true) {
// If the current task takes too long
if(currentTaskStartTime + currentTask.timeout < utils.currentTime) {
currentTask.error(new Error("A timeout occurred.|\n" + currentTask.callPath.join("\n")));
}
return;
}
if(currentTask === undefined) {
// If no task in the queue
if(taskQueue.length === 0) {
return;
}
// Dequeue an enqueued task
currentTask = taskQueue.shift();
currentTaskStartTime = utils.currentTime;
if(asyncRunning === false) {
asyncRunning = true;
extensionMgr.onAsyncRunning(true);
}
}
// Run the task
if(currentTaskStartTime <= utils.currentTime) {
currentTaskRunning = true;
currentTask.chain();
}
});
};
// Run runTask function periodically
core.addPeriodicCallback(asyncRunner.runTask);
function runSafe(task, callbacks, param) {
try {
_.each(callbacks, function(callback) {
callback(param);
});
}
finally {
task.finished = true;
if(currentTask === task) {
currentTask = undefined;
currentTaskRunning = false;
}
if(taskQueue.length === 0) {
asyncRunning = false;
extensionMgr.onAsyncRunning(false);
}
else {
asyncRunner.runTask();
}
}
}
// Add a task to the queue
asyncRunner.addTask = function(task) {
taskQueue.push(task);
asyncRunner.runTask();
};
// Change current task timeout
asyncRunner.setCurrentTaskTimeout = function(timeout) {
if(currentTask !== undefined) {
currentTask.timeout = timeout;
}
};
return asyncRunner;
});

View File

@ -18,6 +18,7 @@ var ASYNC_TASK_DEFAULT_TIMEOUT = 60000;
var ASYNC_TASK_LONG_TIMEOUT = 180000;
var SYNC_PERIOD = 180000;
var USER_IDLE_THRESHOLD = 300000;
var IMPORT_FILE_MAX_CONTENT_SIZE = 100000;
var TEMPORARY_FILE_INDEX = "file.tempIndex";
var WELCOME_DOCUMENT_TITLE = "Welcome document";
var DOWNLOAD_PROXY_URL = "http://stackedit-download-proxy.herokuapp.com/";

View File

@ -261,6 +261,7 @@ define([
var newDocumentContent = editorElt.val();
if(documentContent !== undefined && documentContent != newDocumentContent) {
fileDesc.content = newDocumentContent;
extensionMgr.onContentChanged(fileDesc);
}
documentContent = newDocumentContent;
}

View File

@ -12,6 +12,7 @@ define([
"extensions/dialogAbout",
"extensions/dialogManagePublication",
"extensions/dialogManageSynchronization",
"extensions/dialogOpenHarddrive",
"extensions/documentSelector",
"extensions/documentTitle",
"extensions/workingIndicator",

View File

@ -10,9 +10,9 @@ define([
settingsBloc: '<p>Populates the "Manage publication" dialog box.</p>'
};
var fileMgr = undefined;
dialogManagePublication.onFileMgrCreated = function(fileMgrParameter) {
fileMgr = fileMgrParameter;
var extensionMgr = undefined;
dialogManagePublication.onExtensionMgrCreated = function(extensionMgrParameter) {
extensionMgr = extensionMgrParameter;
};
var fileDesc = undefined;
@ -42,7 +42,8 @@ define([
publishDesc: publishDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
fileMgr.removePublish(publishAttributes);
fileDesc.removePublishLocation(publishAttributes);
extensionMgr.onPublishRemoved(publishFileDesc, publishAttributes);
}));
publishList.append(lineElement);
});

View File

@ -10,9 +10,9 @@ define([
settingsBloc: '<p>Populates the "Manage synchronization" dialog box.</p>'
};
var fileMgr = undefined;
dialogManageSynchronization.onFileMgrCreated = function(fileMgrParameter) {
fileMgr = fileMgrParameter;
var extensionMgr = undefined;
dialogManageSynchronization.onExtensionMgrCreated = function(extensionMgrParameter) {
extensionMgr = extensionMgrParameter;
};
var fileDesc = undefined;
@ -38,7 +38,8 @@ define([
syncDesc: syncDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
fileMgr.removeSync(syncAttributes);
fileDesc.removeSyncLocation(syncAttributes);
extensionMgr.onSyncRemoved(fileDesc, syncAttributes);
}));
syncList.append(lineElement);
});

View File

@ -38,6 +38,7 @@ define([
var liMap = undefined;
var liArray = undefined;
var sortFunction = undefined;
var selectFileDesc = undefined;
var buildSelector = function() {
function composeTitle(fileDesc) {
@ -68,23 +69,17 @@ define([
});
var li = $("<li>").append(a);
liMap[fileDesc.fileIndex] = li;
if(fileDesc === selectFileDesc) {
li.addClass("disabled");
}
$("#file-selector").append(li);
});
liArray = _.values(liMap);
};
var selectFileDesc = undefined;
documentSelector.onFileSelected = function(fileDesc) {
selectFileDesc = fileDesc;
buildSelector();
$("#file-selector li:not(.stick)").removeClass("disabled");
var li = liMap[fileDesc.fileIndex];
if(li === undefined) {
// It means that we are showing a temporary file (not in the
// selector)
return;
}
li.addClass("disabled");
};
documentSelector.onFileCreated = buildSelector;

View File

@ -6,93 +6,12 @@ define([
"settings",
"extensionMgr",
"fileSystem",
"classes/FileDescriptor",
"text!../WELCOME.md"
], function($, _, core, utils, settings, extensionMgr, fileSystem, welcomeContent) {
], function($, _, core, utils, settings, extensionMgr, fileSystem, FileDescriptor, welcomeContent) {
var fileMgr = {};
// Defines a file descriptor (fileDesc objects)
function FileDescriptor(fileIndex, title, syncLocations, publishLocations) {
this.fileIndex = fileIndex;
this._title = title;
this._editorScrollTop = parseInt(localStorage[fileIndex + ".editorScrollTop"]) || 0;
this._editorStart = parseInt(localStorage[fileIndex + ".editorStart"]) || 0;
this._editorEnd = parseInt(localStorage[fileIndex + ".editorEnd"]) || 0;
this._previewScrollTop = parseInt(localStorage[fileIndex + ".previewScrollTop"]) || 0;
this._selectTime = parseInt(localStorage[fileIndex + ".selectTime"]) || 0;
this.syncLocations = syncLocations || {};
this.publishLocations = publishLocations || {};
Object.defineProperty(this, 'title', {
get: function() {
return this._title;
},
set: function(title) {
this._title = title;
localStorage[this.fileIndex + ".title"] = title;
extensionMgr.onTitleChanged(this);
}
});
Object.defineProperty(this, 'content', {
get: function() {
return localStorage[this.fileIndex + ".content"];
},
set: function(content) {
localStorage[this.fileIndex + ".content"] = content;
extensionMgr.onContentChanged(this);
}
});
Object.defineProperty(this, 'editorScrollTop', {
get: function() {
return this._editorScrollTop;
},
set: function(editorScrollTop) {
this._editorScrollTop = editorScrollTop;
localStorage[this.fileIndex + ".editorScrollTop"] = editorScrollTop;
}
});
Object.defineProperty(this, 'editorStart', {
get: function() {
return this._editorStart;
},
set: function(editorStart) {
this._editorStart = editorStart;
localStorage[this.fileIndex + ".editorStart"] = editorStart;
}
});
Object.defineProperty(this, 'editorEnd', {
get: function() {
return this._editorEnd;
},
set: function(editorEnd) {
this._editorEnd = editorEnd;
localStorage[this.fileIndex + ".editorEnd"] = editorEnd;
}
});
Object.defineProperty(this, 'previewScrollTop', {
get: function() {
return this._previewScrollTop;
},
set: function(previewScrollTop) {
this._previewScrollTop = previewScrollTop;
localStorage[this.fileIndex + ".previewScrollTop"] = previewScrollTop;
}
});
Object.defineProperty(this, 'selectTime', {
get: function() {
return this._selectTime;
},
set: function(selectTime) {
this._selectTime = selectTime;
localStorage[this.fileIndex + ".selectTime"] = selectTime;
}
});
}
// Retrieve file descriptors from localStorage and populate fileSystem
_.each(utils.retrieveIndexArray("file.list"), function(fileIndex) {
fileSystem[fileIndex] = new FileDescriptor(fileIndex, localStorage[fileIndex + ".title"]);
});
// Defines the current file
var currentFile = undefined;
fileMgr.getCurrentFile = function() {
@ -103,12 +22,6 @@ define([
};
fileMgr.setCurrentFile = function(fileDesc) {
currentFile = fileDesc;
if(fileDesc === undefined) {
localStorage.removeItem("file.current");
}
else if(fileDesc.fileIndex != TEMPORARY_FILE_INDEX) {
localStorage["file.current"] = fileDesc.fileIndex;
}
};
fileMgr.selectFile = function(fileDesc) {
@ -116,17 +29,15 @@ define([
if(fileDesc === undefined) {
var fileSystemSize = _.size(fileSystem);
// If fileSystem empty create one file
if(fileSystemSize === 0) {
// If fileSystem empty create one file
fileDesc = fileMgr.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
}
else {
var fileIndex = localStorage["file.current"];
// If no file is selected take the last created
if(fileIndex === undefined) {
fileIndex = _.keys(fileSystem)[fileSystemSize - 1];
}
fileDesc = fileSystem[fileIndex];
// Select the last selected file
fileDesc = _.max(fileSystem, function(fileDesc) {
return fileDesc.selectTime || 0;
});
}
}
@ -174,6 +85,7 @@ define([
// syncIndex associations
syncLocations = syncLocations || {};
var sync = _.reduce(syncLocations, function(sync, syncAttributes) {
utils.storeAttributes(syncAttributes);
return sync + syncAttributes.syncIndex + ";";
}, ";");
@ -204,18 +116,18 @@ define([
if(fileMgr.isCurrentFile(fileDesc) === true) {
// Unset the current fileDesc
fileMgr.setCurrentFile();
// Refresh the editor with an other file
// Refresh the editor with another file
fileMgr.selectFile();
}
// Remove synchronized locations
// Remove synchronized locations from localStorage
_.each(fileDesc.syncLocations, function(syncAttributes) {
fileMgr.removeSync(syncAttributes);
localStorage.removeItem(syncAttributes.syncIndex);
});
// Remove publish locations
// Remove publish locations from localStorage
_.each(fileDesc.publishLocations, function(publishAttributes) {
fileMgr.removePublish(publishAttributes);
localStorage.removeItem(publishAttributes.publishIndex);
});
localStorage.removeItem(fileDesc.fileIndex + ".title");
@ -226,26 +138,6 @@ define([
extensionMgr.onFileDeleted(fileDesc);
};
// Add a synchronized location to a file
fileMgr.addSync = function(fileDesc, syncAttributes) {
utils.appendIndexToArray(fileDesc.fileIndex + ".sync", syncAttributes.syncIndex);
fileDesc.syncLocations[syncAttributes.syncIndex] = syncAttributes;
// addSync is only used for export, not for import
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
};
// Remove a synchronized location
fileMgr.removeSync = function(syncAttributes) {
var fileDesc = fileMgr.getFileFromSyncIndex(syncAttributes.syncIndex);
if(fileDesc !== undefined) {
utils.removeIndexFromArray(fileDesc.fileIndex + ".sync", syncAttributes.syncIndex);
delete fileDesc.syncLocations[syncAttributes.syncIndex];
extensionMgr.onSyncRemoved(fileDesc, syncAttributes);
}
// Remove sync attributes from localStorage
localStorage.removeItem(syncAttributes.syncIndex);
};
// Get the file descriptor associated to a syncIndex
fileMgr.getFileFromSyncIndex = function(syncIndex) {
return _.find(fileSystem, function(fileDesc) {
@ -268,25 +160,6 @@ define([
});
};
// Add a publishIndex (publish location) to a file
fileMgr.addPublish = function(fileDesc, publishAttributes) {
utils.appendIndexToArray(fileDesc.fileIndex + ".publish", publishAttributes.publishIndex);
fileDesc.publishLocations[publishAttributes.publishIndex] = publishAttributes;
extensionMgr.onNewPublishSuccess(fileDesc, publishAttributes);
};
// Remove a publishIndex (publish location)
fileMgr.removePublish = function(publishAttributes) {
var fileDesc = fileMgr.getFileFromPublishIndex(publishAttributes.publishIndex);
if(fileDesc !== undefined) {
utils.removeIndexFromArray(fileDesc.fileIndex + ".publish", publishAttributes.publishIndex);
delete fileDesc.publishLocations[publishAttributes.publishIndex];
extensionMgr.onPublishRemoved(fileDesc, publishAttributes);
}
// Remove publish attributes from localStorage
localStorage.removeItem(publishAttributes.publishIndex);
};
// Get the file descriptor associated to a publishIndex
fileMgr.getFileFromPublishIndex = function(publishIndex) {
return _.find(fileSystem, function(fileDesc) {
@ -327,6 +200,7 @@ define([
var fileDesc = fileMgr.getCurrentFile();
if(title && title != fileDesc.title) {
fileDesc.title = title;
extensionMgr.onTitleChanged(fileDesc);
}
input.val(fileDesc.title);
$("#wmd-input").focus();

View File

@ -1,3 +1,16 @@
// The fileSystem module is empty when created. It's filled by fileMgr when loading.
// syncLocations and publishLocations are respectively loaded by synchronizer and publisher.
define({});
define([
"utils",
"classes/FileDescriptor",
"storage",
], function(utils, FileDescriptor) {
var fileSystem = {};
// Retrieve file descriptors from localStorage and populate fileSystem
_.each(utils.retrieveIndexArray("file.list"), function(fileIndex) {
fileSystem[fileIndex] = new FileDescriptor(fileIndex, localStorage[fileIndex + ".title"]);
});
return fileSystem;
});

View File

@ -3,8 +3,8 @@ define([
"underscore",
"core",
"extensionMgr",
"asyncRunner"
], function($, _, core, extensionMgr, asyncRunner) {
"classes/AsyncTask"
], function($, _, core, extensionMgr, AsyncTask) {
var client = undefined;
var authenticated = false;
@ -88,7 +88,7 @@ define([
dropboxHelper.upload = function(path, content, callback) {
var result = undefined;
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -111,13 +111,13 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
dropboxHelper.checkChanges = function(lastChangeId, callback) {
var changes = [];
var newChangeId = lastChangeId || 0;
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -148,12 +148,12 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
dropboxHelper.downloadMetadata = function(paths, callback) {
var result = [];
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -181,12 +181,12 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
dropboxHelper.downloadContent = function(objects, callback) {
var result = [];
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -229,7 +229,7 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
function handleError(error, task) {
@ -299,7 +299,7 @@ define([
dropboxHelper.picker = function(callback) {
var paths = [];
var task = asyncRunner.createTask();
var task = new AsyncTask();
// Add some time for user to choose his files
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
connect(task);
@ -328,7 +328,7 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
return dropboxHelper;

View File

@ -3,8 +3,8 @@ define([
"core",
"utils",
"extensionMgr",
"asyncRunner"
], function($, core, utils, extensionMgr, asyncRunner) {
"classes/AsyncTask"
], function($, core, utils, extensionMgr, AsyncTask) {
var connected = undefined;
var github = undefined;
@ -111,7 +111,7 @@ define([
}
githubHelper.upload = function(reponame, branch, path, content, commitMsg, callback) {
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -145,11 +145,11 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
githubHelper.uploadGist = function(gistId, filename, isPublic, title, content, callback) {
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -185,11 +185,11 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
githubHelper.downloadGist = function(gistId, filename, callback) {
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
// No need for authentication
var title = undefined;
@ -219,7 +219,7 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
function handleError(error, task) {

View File

@ -3,8 +3,8 @@ define([
"core",
"utils",
"extensionMgr",
"asyncRunner"
], function($, core, utils, extensionMgr, asyncRunner) {
"classes/AsyncTask"
], function($, core, utils, extensionMgr, AsyncTask) {
var connected = false;
var authenticated = false;
@ -86,7 +86,7 @@ define([
googleHelper.upload = function(fileId, parentId, title, content, etag, callback) {
var result = undefined;
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -164,13 +164,13 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
googleHelper.checkChanges = function(lastChangeId, callback) {
var changes = [];
var newChangeId = lastChangeId || 0;
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -216,12 +216,12 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
googleHelper.downloadMetadata = function(ids, callback, skipAuth) {
var result = [];
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
if(!skipAuth) {
authenticate(task);
@ -270,12 +270,12 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
googleHelper.downloadContent = function(objects, callback, skipAuth) {
var result = [];
var task = asyncRunner.createTask();
var task = new AsyncTask();
// Add some time for user to choose his files
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
connect(task);
@ -338,7 +338,7 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
function handleError(error, task) {
@ -389,7 +389,9 @@ define([
timeout: AJAX_TIMEOUT
}).done(function() {
google.load('picker', '1', {
callback: task.chain
callback: function() {
task.chain();
}
});
pickerLoaded = true;
}).fail(function(jqXHR) {
@ -411,7 +413,7 @@ define([
$(".modal-backdrop, .picker").remove();
}
}
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
loadPicker(task);
task.onRun(function() {
@ -451,11 +453,11 @@ define([
hidePicker();
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
googleHelper.uploadBlogger = function(blogUrl, blogId, postId, labelList, title, content, callback) {
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -541,7 +543,7 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
return googleHelper;

View File

@ -1,8 +1,8 @@
define([
"jquery",
"core",
"asyncRunner"
], function($, core, asyncRunner) {
"classes/AsyncTask"
], function($, core, AsyncTask) {
var sshHelper = {};
@ -18,7 +18,7 @@ define([
}
sshHelper.upload = function(host, port, username, password, path, title, content, callback) {
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
task.onRun(function() {
var url = SSH_PROXY_URL + "upload";
@ -57,7 +57,7 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
function handleError(error, task) {

View File

@ -3,8 +3,8 @@ define([
"core",
"utils",
"extensionMgr",
"asyncRunner"
], function($, core, utils, extensionMgr, asyncRunner) {
"classes/AsyncTask"
], function($, core, utils, extensionMgr, AsyncTask) {
var oauthParams = undefined;
@ -96,7 +96,7 @@ define([
}
tumblrHelper.upload = function(blogHostname, postId, tags, format, title, content, callback) {
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -135,7 +135,7 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
function handleError(error, task) {

View File

@ -3,8 +3,8 @@ define([
"core",
"utils",
"extensionMgr",
"asyncRunner"
], function($, core, utils, extensionMgr, asyncRunner) {
"classes/AsyncTask"
], function($, core, utils, extensionMgr, AsyncTask) {
var token = undefined;
@ -80,7 +80,7 @@ define([
}
wordpressHelper.upload = function(site, postId, tags, title, content, callback) {
var task = asyncRunner.createTask();
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
@ -133,7 +133,7 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
function handleError(error, task) {

View File

@ -1,53 +1,43 @@
<blockquote>StackEdit is a free, open-source Markdown
editor based on PageDown, the Markdown library used by Stack Overflow
and the other Stack Exchange sites.</blockquote>
<dl>
<dt>About:</dt>
<dd>
<a target="_blank" href="https://github.com/benweet/stackedit/">GitHub
project</a> / <a target="_blank"
href="https://github.com/benweet/stackedit/issues">issue tracker</a>
</dd>
<dd>
href="https://github.com/benweet/stackedit/issues">issue tracker</a><br />
<a target="_blank"
href="https://chrome.google.com/webstore/detail/stackedit/iiooodelglhkcpgbajoejffhijaclcdg">Chrome
app</a> (thanks for your review!)
</dd>
<dd>
<a target="_blank" href="https://twitter.com/stackedit/">Follow on
Twitter</a>
</dd>
<dd>
<a target="_blank" href="https://www.facebook.com/stackedit/">Follow
on Facebook</a>
</dd>
<dd>
<a target="_blank"
app</a> (thanks for your review!)<br /> <a target="_blank"
href="https://twitter.com/stackedit/">Follow on Twitter</a><br /> <a
target="_blank" href="https://www.facebook.com/stackedit/">Follow
on Facebook</a><br /> <a target="_blank"
href="https://plus.google.com/110816046787593496375" rel="publisher">Follow
on Google+</a>
on Google+</a><br />
</dd>
</dl>
<dl>
<dt>Developers:</dt>
<dd>
<a target="_blank" href="http://www.benoitschweblin.com">Benoit
Schweblin</a><br />
Schweblin</a><br /> Pete Eigel (contributor)
</dd>
<dd>Pete Eigel (contributor)</dd>
</dl>
<dl>
<dt>Credit:</dt>
<% _.each(libraries, function(url, name) { %>
<dd>
<a target="_blank" href="<%= url %>"><%= name %></a>
<% _.each(libraries, function(url, name) { %> <a target="_blank"
href="<%= url %>"><%= name %></a><br /> <% }); %>
</dd>
<% }); %>
</dl>
<dl>
<dt>Related projects:</dt>
<% _.each(projects, function(url, name) { %>
<dd>
<a target="_blank" href="<%= url %>"><%= name %></a>
<% _.each(projects, function(url, name) { %> <a target="_blank"
href="<%= url %>"><%= name %></a><br /> <% }); %>
</dd>
<% }); %>
</dl>
<p>Copyright 2013 <a target="_blank"
href="http://www.benoitschweblin.com">Benoit Schweblin</a><br />

View File

@ -6,6 +6,7 @@ requirejs.config({
"underscore": "libs/underscore",
"jgrowl": "libs/jgrowl",
"mousetrap": "libs/mousetrap",
"toMarkdown": "libs/to-markdown",
"text": "libs/text",
"libs/MathJax": '../lib/MathJax/MathJax.js?config=TeX-AMS_HTML'
},
@ -22,6 +23,12 @@ requirejs.config({
'mousetrap': {
exports: 'Mousetrap'
},
'toMarkdown': {
deps: [
'jquery'
],
exports: 'toMarkdown'
},
'libs/jquery-ui': [
'jquery'
],

View File

@ -1,8 +1,8 @@
define([
"jquery",
"core",
"asyncRunner"
], function($, core, asyncRunner) {
"classes/AsyncTask"
], function($, core, AsyncTask) {
var PROVIDER_DOWNLOAD = "download";
@ -14,9 +14,9 @@ define([
};
downloadProvider.importPublic = function(importParameters, callback) {
var task = asyncRunner.createTask();
var title = undefined;
var content = undefined;
var task = new AsyncTask();
task.onRun(function() {
var url = importParameters.url;
var slashUrl = url.lastIndexOf("/");
@ -43,7 +43,7 @@ define([
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
task.enqueue();
};
return downloadProvider;

View File

@ -39,7 +39,6 @@ define([
syncAttributes.version = versionTag;
syncAttributes.contentCRC = utils.crc32(content);
syncAttributes.syncIndex = createSyncIndex(path);
utils.storeAttributes(syncAttributes);
return syncAttributes;
}
@ -184,7 +183,8 @@ define([
// File deleted
if(change.wasRemoved === true) {
extensionMgr.onError('"' + localTitle + '" has been removed from Dropbox.');
fileMgr.removeSync(syncAttributes);
fileDesc.removeSyncLocation(syncAttributes);
extensionMgr.onSyncRemoved(fileDesc, syncAttributes);
return;
}
var localContent = fileDesc.content;
@ -201,6 +201,7 @@ define([
// If file content changed
if(fileContentChanged && remoteContentChanged === true) {
fileDesc.content = file.content;
extensionMgr.onContentChanged(fileDesc);
extensionMgr.onMessage('"' + localTitle + '" has been updated from Dropbox.');
if(fileMgr.isCurrentFile(fileDesc)) {
fileMgr.selectFile(); // Refresh editor

View File

@ -31,7 +31,6 @@ define([
syncAttributes.contentCRC = utils.crc32(content);
syncAttributes.titleCRC = utils.crc32(title);
syncAttributes.syncIndex = createSyncIndex(id);
utils.storeAttributes(syncAttributes);
return syncAttributes;
}
@ -179,7 +178,8 @@ define([
// File deleted
if(change.deleted === true) {
extensionMgr.onError('"' + localTitle + '" has been removed from Google Drive.');
fileMgr.removeSync(syncAttributes);
fileDesc.removeSyncLocation(syncAttributes);
extensionMgr.onSyncRemoved(fileDesc, syncAttributes);
return;
}
var localTitleChanged = syncAttributes.titleCRC != utils.crc32(localTitle);
@ -200,11 +200,13 @@ define([
// If file title changed
if(fileTitleChanged && remoteTitleChanged === true) {
fileDesc.title = file.title;
extensionMgr.onTitleChanged(fileDesc);
extensionMgr.onMessage('"' + localTitle + '" has been renamed to "' + file.title + '" on Google Drive.');
}
// If file content changed
if(fileContentChanged && remoteContentChanged === true) {
fileDesc.content = file.content;
extensionMgr.onContentChanged(fileDesc);
extensionMgr.onMessage('"' + file.title + '" has been updated from Google Drive.');
if(fileMgr.isCurrentFile(fileDesc)) {
fileMgr.selectFile(); // Refresh editor

View File

@ -54,41 +54,41 @@ define([
});
// Apply template to the current document
publisher.applyTemplate = function(publishAttributes) {
var fileDesc = fileMgr.getCurrentFile();
publisher.applyTemplate = function(fileDesc, publishAttributes, html) {
try {
return _.template(settings.template, {
documentTitle: fileDesc.title,
documentMarkdown: $("#wmd-input").val(),
documentHTML: $("#wmd-preview").html(),
documentMarkdown: fileDesc.content,
documentHTML: html,
publishAttributes: publishAttributes
});
}
catch(e) {
extensionMgr.onError(e);
throw e;
return e.message;
}
};
// Used to get content to publish
function getPublishContent(publishAttributes) {
function getPublishContent(fileDesc, publishAttributes, html) {
if(publishAttributes.format === undefined) {
publishAttributes.format = $("input:radio[name=radio-publish-format]:checked").prop("value");
}
if(publishAttributes.format == "markdown") {
return $("#wmd-input").val();
return fileDesc.content;
}
else if(publishAttributes.format == "html") {
return $("#wmd-preview").html();
return html;
}
else {
return publisher.applyTemplate(publishAttributes);
return publisher.applyTemplate(fileDesc, publishAttributes, html);
}
}
// Recursive function to publish a file on multiple locations
var publishAttributesList = [];
var publishTitle = undefined;
var publishFileDesc = undefined;
var publishHTML = undefined;
function publishLocation(callback, errorFlag) {
// No more publish location for this document
@ -99,14 +99,17 @@ define([
// Dequeue a synchronized location
var publishAttributes = publishAttributesList.pop();
var content = getPublishContent(publishAttributes);
// Format the content
var content = getPublishContent(publishFileDesc, publishAttributes, publishHTML);
// Call the provider
publishAttributes.provider.publish(publishAttributes, publishTitle, content, function(error) {
publishAttributes.provider.publish(publishAttributes, publishFileDesc.title, content, function(error) {
if(error !== undefined) {
var errorMsg = error.toString();
if(errorMsg.indexOf("|removePublish") !== -1) {
fileMgr.removePublish(publishAttributes);
publishFileDesc.removePublishLocation(publishAttributes);
extensionMgr.onPublishRemoved(publishFileDesc, publishAttributes);
}
if(errorMsg.indexOf("|stopPublish") !== -1) {
callback(error);
@ -126,14 +129,14 @@ define([
publishRunning = true;
extensionMgr.onPublishRunning(true);
var fileDesc = fileMgr.getCurrentFile();
publishTitle = fileDesc.title;
publishAttributesList = _.values(fileDesc.publishLocations);
publishFileDesc = fileMgr.getCurrentFile();
publishHTML = $("#wmd-preview").html();
publishAttributesList = _.values(publishFileDesc.publishLocations);
publishLocation(function(errorFlag) {
publishRunning = false;
extensionMgr.onPublishRunning(false);
if(errorFlag === undefined) {
extensionMgr.onPublishSuccess(fileDesc);
extensionMgr.onPublishSuccess(publishFileDesc);
}
});
};
@ -145,8 +148,8 @@ define([
publishIndex = "publish." + utils.randomString();
} while (_.has(localStorage, publishIndex));
publishAttributes.publishIndex = publishIndex;
utils.storeAttributes(publishAttributes);
fileMgr.addPublish(fileDesc, publishAttributes);
fileDesc.addPublishLocation(publishAttributes);
extensionMgr.onNewPublishSuccess(fileDesc, publishAttributes);
}
// Initialize the "New publication" dialog
@ -186,9 +189,9 @@ define([
// Perform provider's publishing
var fileDesc = fileMgr.getCurrentFile();
var title = fileDesc.title;
var content = getPublishContent(publishAttributes);
provider.publish(publishAttributes, title, content, function(error) {
var html = $("#wmd-preview").html();
var content = getPublishContent(fileDesc, publishAttributes, html);
provider.publish(publishAttributes, fileDesc.title, content, function(error) {
if(error === undefined) {
publishAttributes.provider = provider;
sharing.createLink(publishAttributes, function() {
@ -235,9 +238,10 @@ define([
utils.saveAs(content, title + ".html");
});
$(".action-download-template").click(function() {
var content = publisher.applyTemplate();
var title = fileMgr.getCurrentFile().title;
utils.saveAs(content, title + (settings.template.indexOf("documentHTML") === -1 ? ".md" : ".html"));
var fileDesc = fileMgr.getCurrentFile();
var html = $("#wmd-preview").html();
var content = publisher.applyTemplate(fileDesc, undefined, html);
utils.saveAs(content, fileDesc.title + (settings.template.indexOf("documentHTML") === -1 ? ".md" : ".html"));
});
});

View File

@ -5,10 +5,10 @@ define([
"utils",
"extensionMgr",
"fileMgr",
"asyncRunner",
"classes/AsyncTask",
"providers/downloadProvider",
"providers/gistProvider"
], function($, _, core, utils, extensionMgr, fileMgr, asyncRunner) {
], function($, _, core, utils, extensionMgr, fileMgr, AsyncTask) {
var sharing = {};
@ -30,7 +30,7 @@ define([
callback();
return;
}
var task = asyncRunner.createTask();
var task = new AsyncTask();
var shortUrl = undefined;
task.onRun(function() {
if(core.isOffline === true) {
@ -69,7 +69,7 @@ define([
}
task.onSuccess(onFinish);
task.onError(onFinish);
asyncRunner.addTask(task);
task.enqueue();
};
core.onReady(function() {

View File

@ -120,6 +120,16 @@ define([
});
version = "v6";
}
// Upgrade from v6 to v7
if(version == "v6") {
var currentFileIndex = localStorage["file.current"];
if(currentFileIndex !== undefined) {
localStorage[currentFileIndex + ".selectTime"] = new Date().getTime();
localStorage.removeItem("file.current");
}
version = "v7";
}
localStorage["version"] = version;
});

View File

@ -223,7 +223,8 @@ define([
if(error) {
return;
}
fileMgr.addSync(fileDesc, syncAttributes);
fileDesc.addSyncLocation(syncAttributes);
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
});
// Store input values as preferences for next time we open the
@ -241,7 +242,8 @@ define([
if(error) {
return;
}
fileMgr.addSync(fileDesc, syncAttributes);
fileDesc.addSyncLocation(syncAttributes);
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
});
});
});