Support for templating

This commit is contained in:
benweet 2013-04-14 14:24:29 +01:00
parent 98cc144b0a
commit 0420be3a7d
8 changed files with 214 additions and 179 deletions

View File

@ -29,7 +29,7 @@ div, span, a, ul, li, textarea, input, button {
text-shadow: none !important;
}
.btn, .navbar-inner, .ui-layout-east, .ui-layout-south, textarea, .add-on {
.btn, .navbar-inner, .ui-layout-east, .ui-layout-south, .add-on {
border: none !important;
}
@ -39,12 +39,13 @@ div, span, a, ul, li, textarea, input, button {
}
input,
textarea,
.input-prepend .btn,
.input-prepend .add-on {
border: 1px solid #ebebeb !important;
}
input.error {
.error {
border-color: #ff6661 !important;
}
@ -165,6 +166,7 @@ hr {
#wmd-input {
font-family: "Courier New", Courier, monospace;
resize: none;
border: none !important;
}
#wmd-preview {
@ -325,3 +327,8 @@ hr {
.picker-dialog {
z-index: 1050 !important;
}
#modal-settings textarea {
max-width: 206px;
height: 150px;
}

View File

@ -58,6 +58,8 @@
class="icon-download-alt"></i> Save as MD</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>
<li class="divider"></li>
<li class="dropdown-submenu"><a href="#"><i
class="icon-gdrive"></i> Google Drive</a>
@ -314,7 +316,9 @@
name="radio-publish-format" value="markdown"> Markdown
</label> <label class="radio"> <input type="radio"
name="radio-publish-format" value="html"> HTML
</label>
</label> <label class="radio"> <input type="radio"
name="radio-publish-format" value="template"> Template
</label>
</div>
</div>
</div>
@ -395,6 +399,13 @@
<input type="text" id="input-settings-publish-commit-msg">
</div>
</div>
<div class="control-group">
<label class="control-label"
for="textarea-settings-publish-template">Template</label>
<div class="controls">
<textarea id="textarea-settings-publish-template"></textarea>
</div>
</div>
</div>
</div>
</div>

View File

@ -6,7 +6,7 @@
* - an optional onError() function
* - an optional timeout field (default is 30000)
*/
define(function() {
define(["underscore"], function() {
var asyncTaskRunner = {};
@ -75,8 +75,8 @@ define(function() {
}
asyncTaskRunner.runTask = function() {
// Use setTimeout to avoid stack overflow
setTimeout(runTask, 0);
// Use defer to avoid stack overflow
_.defer(runTask);
};
function runSafe(func) {

View File

@ -14,8 +14,9 @@ var SYNC_PERIOD = 180000;
var USER_IDLE_THRESHOLD = 300000;
var SYNC_PROVIDER_GDRIVE = "sync.gdrive.";
var SYNC_PROVIDER_DROPBOX = "sync.dropbox.";
var PUBLISH_PROVIDER_GITHUB = "github";
var PUBLISH_PROVIDER_BLOGGER = "blogger";
var PROVIDER_TYPE_PUBLISH_FLAG = 1;
var PROVIDER_GITHUB = "github";
var PROVIDER_BLOGGER = "blogger";
// Use by Google's client.js
var delayedFunction = undefined;

View File

@ -176,7 +176,14 @@ define(
core.settings = {
layoutOrientation : "horizontal",
editorFontSize : 14,
commitMsg : "Published by StackEdit."
commitMsg : "Published by StackEdit.",
template : ['<!DOCTYPE html>\n',
'<html>\n',
'<head>\n',
'<title><%= documentTitle %></title>\n',
'</head>\n',
'<body><%= documentHTML %></body>\n',
'</html>'].join("")
};
core.loadSettings = function() {
@ -193,6 +200,9 @@ define(
// Commit message
$("#input-settings-publish-commit-msg").val(core.settings.commitMsg);
// Template
$("#textarea-settings-publish-template").val(core.settings.template);
};
core.saveSettings = function(event) {
@ -208,6 +218,9 @@ define(
// Commit message
newSettings.commitMsg = core.getInputValue($("#input-settings-publish-commit-msg"), event);
// Template
newSettings.template = core.getInputValue($("#textarea-settings-publish-template"), event);
if(!event.isPropagationStopped()) {
core.settings = newSettings;
localStorage.settings = JSON.stringify(newSettings);

View File

@ -41,8 +41,7 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
// Update the file titles
fileManager.updateFileTitles();
refreshManageSync();
refreshManagePublish();
publisher.notifyCurrentFile();
publisher.notifyPublish();
// Recreate the editor
fileIndex = fileManager.getCurrentFileIndex();
@ -120,8 +119,7 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
fileManager.updateFileTitles = function() {
$("#file-selector").empty();
fileDescList = _.chain(localStorage["file.list"].split(";"))
.compact()
fileDescList = _.chain(localStorage["file.list"].split(";")).compact()
.reduce(function(fileDescList, fileIndex) {
var title = localStorage[fileIndex + ".title"];
fileDescList.push({ index : fileIndex, title : title });
@ -163,8 +161,7 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
// Update the file selector
$("#file-selector").empty();
for ( var i = 0; i < fileDescList.length; i++) {
var fileDesc = fileDescList[i];
_.each(fileDescList, function(fileDesc) {
var a = $("<a>").html(composeTitle(fileDesc.index));
var li = $("<li>").append(a);
if (fileDesc.index == fileIndex) {
@ -177,19 +174,18 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
})(fileDesc.index));
}
$("#file-selector").append(li);
}
});
synchronizer.useGoogleDrive = useGoogleDrive;
synchronizer.useDropbox = useDropbox;
};
// Remove a synchronized location
// Remove a syncIndex (synchronized location)
fileManager.removeSync = function(syncIndex) {
var currentFileIndex = fileManager.getCurrentFileIndex();
var fileIndex = this.getFileIndexFromSync(syncIndex);
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
if(fileIndex !== undefined) {
localStorage[fileIndex + ".sync"] = localStorage[fileIndex + ".sync"].replace(";"
+ syncIndex + ";", ";");
if(fileIndex == currentFileIndex) {
if(fileManager.isCurrentFileIndex(fileIndex)) {
refreshManageSync();
}
}
@ -200,46 +196,36 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
localStorage.removeItem(syncIndex + ".titleCRC");
};
// Look for local file associated to a synchronized location
// Get the fileIndex associated to a syncIndex
fileManager.getFileIndexFromSync = function(syncIndex) {
var fileIndexList = localStorage["file.list"].split(";");
for ( var i = 1; i < fileIndexList.length - 1; i++) {
var fileIndex = fileIndexList[i];
var sync = localStorage[fileIndex + ".sync"];
if (sync.indexOf(";" + syncIndex + ";") !== -1) {
return fileIndex;
}
}
return undefined;
return _.chain(localStorage["file.list"].split(";")).compact()
.find(function(fileIndex) {
var sync = localStorage[fileIndex + ".sync"];
return sync.indexOf(";" + syncIndex + ";") !== -1;
}).value();
};
// Remove a publish location
// Remove a publishIndex (publish location)
fileManager.removePublish = function(publishIndex) {
var currentFileIndex = fileManager.getCurrentFileIndex();
var fileIndex = this.getFileIndexFromPublish(publishIndex);
var fileIndex = fileManager.getFileIndexFromPublish(publishIndex);
if(fileIndex !== undefined) {
localStorage[fileIndex + ".publish"] = localStorage[fileIndex + ".publish"].replace(";"
+ publishIndex + ";", ";");
if(fileIndex == currentFileIndex) {
refreshManagePublish();
if(fileManager.isCurrentFileIndex(fileIndex)) {
publisher.notifyPublish();
}
}
// Remove publish object
localStorage.removeItem(publishIndex);
publisher.notifyCurrentFile();
};
// Look for local file associated to a publish location
// Get the fileIndex associated to a publishIndex
fileManager.getFileIndexFromPublish = function(publishIndex) {
var fileIndexList = localStorage["file.list"].split(";");
for ( var i = 1; i < fileIndexList.length - 1; i++) {
var fileIndex = fileIndexList[i];
var publish = localStorage[fileIndex + ".publish"];
if (publish.indexOf(";" + publishIndex + ";") !== -1) {
return fileIndex;
}
}
return undefined;
return _.chain(localStorage["file.list"].split(";")).compact()
.find(function(fileIndex) {
var sync = localStorage[fileIndex + ".publish"];
return sync.indexOf(";" + publishIndex + ";") !== -1;
}).value();
};
function uploadGdrive(fileId, folderId) {
@ -350,80 +336,38 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
function refreshManageSync() {
var fileIndex = fileManager.getCurrentFileIndex();
var syncIndexList = localStorage[fileIndex + ".sync"].split(";");
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
$(".msg-no-sync, .msg-sync-list").addClass("hide");
$("#manage-sync-list .input-append").remove();
if (syncIndexList.length > 2) {
if (syncIndexList.length > 0) {
$(".msg-sync-list").removeClass("hide");
} else {
$(".msg-no-sync").removeClass("hide");
}
for ( var i = 1; i < syncIndexList.length - 1; i++) {
var syncIndex = syncIndexList[i];
(function(syncIndex) {
var line = $("<div>").addClass("input-prepend input-append");
if (syncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
line.append($("<span>").addClass("add-on").prop("title", "Google Drive").html(
'<i class="icon-gdrive"></i>'));
line.append($("<input>").prop("type", "text").prop(
"disabled", true).addClass("span5").val(
syncIndex.substring(SYNC_PROVIDER_GDRIVE.length)));
}
else if (syncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
line.append($("<span>").addClass("add-on").prop("title", "Dropbox").html(
'<i class="icon-dropbox"></i>'));
line.append($("<input>").prop("type", "text").prop(
"disabled", true).addClass("span5").val(
decodeURIComponent(syncIndex.substring(SYNC_PROVIDER_DROPBOX.length))));
}
line.append($("<a>").addClass("btn").html(
'<i class="icon-trash"></i>').prop("title",
"Remove this location").click(function() {
fileManager.removeSync(syncIndex);
fileManager.updateFileTitles();
}));
$("#manage-sync-list").append(line);
})(syncIndex);
}
}
function refreshManagePublish() {
var fileIndex = fileManager.getCurrentFileIndex();
var publishIndexList = localStorage[fileIndex + ".publish"].split(";");
$(".msg-no-publish, .msg-publish-list").addClass("hide");
$("#manage-publish-list .input-append").remove();
if (publishIndexList.length > 2) {
$(".msg-publish-list").removeClass("hide");
} else {
$(".msg-no-publish").removeClass("hide");
}
for ( var i = 1; i < publishIndexList.length - 1; i++) {
var publishIndex = publishIndexList[i];
var serializedObject = localStorage[publishIndex];
(function(publishIndex, publishObject, serializedObject) {
var line = $("<div>").addClass("input-prepend input-append");
if (publishObject.provider == PUBLISH_PROVIDER_GITHUB) {
line.append($("<span>").addClass("add-on").prop("title", "GitHub").html(
'<i class="icon-github"></i>'));
line.append($("<input>").prop("type", "text").prop(
"disabled", true).addClass("span5").val(
serializedObject));
}
else if (publishObject.provider == PUBLISH_PROVIDER_BLOGGER) {
line.append($("<span>").addClass("add-on").prop("title", "Blogger").html(
'<i class="icon-blogger"></i>'));
line.append($("<input>").prop("type", "text").prop(
"disabled", true).addClass("span5").val(
serializedObject));
}
line.append($("<a>").addClass("btn").html(
_.each(syncIndexList, function(syncIndex) {
var line = $("<div>").addClass("input-prepend input-append");
if (syncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
line.append($("<span>").addClass("add-on").prop("title", "Google Drive").html(
'<i class="icon-gdrive"></i>'));
line.append($("<input>").prop("type", "text").prop(
"disabled", true).addClass("span5").val(
syncIndex.substring(SYNC_PROVIDER_GDRIVE.length)));
}
else if (syncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
line.append($("<span>").addClass("add-on").prop("title", "Dropbox").html(
'<i class="icon-dropbox"></i>'));
line.append($("<input>").prop("type", "text").prop(
"disabled", true).addClass("span5").val(
decodeURIComponent(syncIndex.substring(SYNC_PROVIDER_DROPBOX.length))));
}
line.append($("<a>").addClass("btn").html(
'<i class="icon-trash"></i>').prop("title",
"Remove this location").click(function() {
fileManager.removePublish(publishIndex);
}));
$("#manage-publish-list").append(line);
})(publishIndex, JSON.parse(serializedObject), serializedObject.replace(/{|}|"/g, ""));
}
fileManager.removeSync(syncIndex);
fileManager.updateFileTitles();
}));
$("#manage-sync-list").append(line);
});
}
// Initialize the "New publication" dialog
@ -443,16 +387,6 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
$("#modal-publish").modal();
}
// Generate a publishIndex, store a publishObject and associate it to a fileIndex
function createPublishIndex(publishObject, fileIndex) {
var publishIndex = undefined;
do {
publishIndex = "publish." + core.randomString();
} while(localStorage[publishIndex] !== undefined);
localStorage[publishIndex] = JSON.stringify(publishObject);
localStorage[fileIndex + ".publish"] += publishIndex + ";";
}
// Create a new publication on GitHub
function newPublishGithub(event) {
var publishObject = {};
@ -463,22 +397,7 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
if(event.isPropagationStopped()) {
return;
}
var fileIndex = fileManager.getCurrentFileIndex();
var title = localStorage[fileIndex + ".title"];
var content = publisher.getPublishContent(publishObject);
var commitMsg = core.settings.commitMsg;
githubHelper.upload(publishObject.repository,
publishObject.branch, publishObject.path, content, commitMsg,
function(error) {
if(error === undefined) {
createPublishIndex(publishObject, fileIndex);
refreshManagePublish();
publisher.notifyCurrentFile();
core.showMessage('"' + title
+ '" will now be published on GitHub.');
}
});
publisher.newLocation(publishObject);
}
// Create a new publication on Blogger
@ -539,6 +458,13 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
+ core.encodeBase64(content);
window.open(uriContent, 'file');
});
$(".action-download-template").click(
function() {
var content = publisher.applyTemplate();
var uriContent = "data:application/octet-stream;base64,"
+ core.encodeBase64(content);
window.open(uriContent, 'file');
});
// Synchronize actions
$(".action-upload-gdrive-root").click(function() {
@ -574,16 +500,16 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
// Publish actions
$(".action-publish-github").click(function() {
initNewPublish(PUBLISH_PROVIDER_GITHUB);
initNewPublish(PROVIDER_GITHUB);
});
$(".action-publish-blogger").click(function() {
initNewPublish(PUBLISH_PROVIDER_BLOGGER, "html");
initNewPublish(PROVIDER_BLOGGER, "html");
});
$(".action-process-publish").click(function(e) {
if(newPublishProvider == PUBLISH_PROVIDER_GITHUB) {
if(newPublishProvider == PROVIDER_GITHUB) {
newPublishGithub(e);
}
else if(newPublishProvider == PUBLISH_PROVIDER_BLOGGER) {
else if(newPublishProvider == PROVIDER_BLOGGER) {
newPublishBlogger(e);
}
});

23
js/github-provider.js Normal file
View File

@ -0,0 +1,23 @@
define(["jquery", "github-helper"], function($, githubHelper) {
// Dependencies
var core = undefined;
var publishGithub = {
providerType: PROVIDER_TYPE_PUBLISH_FLAG,
providerId: PROVIDER_GITHUB,
providerName: "GitHub"
};
publishGithub.publish = function(publishObject, title, content, callback) {
var commitMsg = core.settings.commitMsg;
githubHelper.upload(publishObject.repository, publishObject.branch,
publishObject.path, content, commitMsg, callback);
};
publishGithub.init = function(coreModule) {
core = coreModule;
};
return publishGithub;
});

View File

@ -1,4 +1,4 @@
define(["jquery", "google-helper", "github-helper", "publish-github", "underscore"], function($, googleHelper, githubHelper) {
define(["jquery", "github-provider", "underscore"], function($) {
// Dependencies
var core = undefined;
@ -6,14 +6,17 @@ define(["jquery", "google-helper", "github-helper", "publish-github", "underscor
var publisher = {};
// Providers
var providerMap = {};
// Create a map with providerName: providerObject
var providerMap = _.chain(arguments)
.map(function(argument) {
return argument && argument.providerType & PROVIDER_TYPE_PUBLISH_FLAG && [argument.providerId, argument];
}).compact().object().value();
// Used to know if the current file has publications
var hasPublications = false;
// Allows external modules to update hasPublications flag
publisher.notifyCurrentFile = function() {
publisher.notifyPublish = function() {
var fileIndex = fileManager.getCurrentFileIndex();
// Check that file has publications
@ -24,6 +27,7 @@ define(["jquery", "google-helper", "github-helper", "publish-github", "underscor
hasPublications = true;
}
publisher.updatePublishButton();
publisher.refreshManagePublish();
};
// Used to enable/disable the publish button
@ -36,6 +40,16 @@ define(["jquery", "google-helper", "github-helper", "publish-github", "underscor
}
};
// Apply template to the current document
publisher.applyTemplate = function() {
var fileIndex = fileManager.getCurrentFileIndex();
return _.template(core.settings.template, {
documentTitle: localStorage[fileIndex + ".title"],
documentMarkdown: $("#wmd-input").val(),
documentHTML: $("#wmd-preview").html()
});
};
// Used to get content to publish
function getPublishContent(publishObject) {
if(publishObject.format === undefined) {
@ -44,37 +58,35 @@ define(["jquery", "google-helper", "github-helper", "publish-github", "underscor
if(publishObject.format == "markdown") {
return $("#wmd-input").val();
}
return $("#wmd-preview").html();
else if(publishObject.format == "html") {
return $("#wmd-preview").html();
}
else {
return publisher.applyTemplate();
}
}
// Recursive function to publish a file on multiple locations
var publishIndexList = [];
function publishLocation(callback, error) {
var publishTitle = undefined;
function publishLocation(callback, errorFlag) {
// No more publish location for this document
if (publishIndexList.length === 0) {
callback(error);
callback(errorFlag);
return;
}
// Dequeue a synchronized location
var publishIndex = publishIndexList.pop();
if(!publishIndex) {
publishLocation(callback, error);
return;
}
var publishObject = JSON.parse(localStorage[publishIndex]);
var content = getPublishContent(publishObject);
var commitMsg = core.settings.commitMsg;
// Try to find the provider
if(publishObject.provider == PUBLISH_PROVIDER_GITHUB) {
githubHelper.upload(publishObject.repository, publishObject.branch, publishObject.path, content, commitMsg,
function(error) {
publishLocation(callback, error);
});
}
// Call the provider
var provider = providerMap[publishObject.provider];
provider.publish(publishObject, publishTitle, content, function(error) {
publishLocation(callback, errorFlag);
});
}
var publishRunning = false;
@ -87,32 +99,74 @@ define(["jquery", "google-helper", "github-helper", "publish-github", "underscor
publishRunning = true;
publisher.updatePublishButton();
var fileIndex = fileManager.getCurrentFileIndex();
var title = localStorage[fileIndex + ".title"];
publishIndexList = localStorage[fileIndex + ".publish"].split(";");;
publishLocation(function(error) {
publishTitle = localStorage[fileIndex + ".title"];
publishIndexList = _.compact(localStorage[fileIndex + ".publish"].split(";"));
publishLocation(function(errorFlag) {
publishRunning = false;
publisher.updatePublishButton();
if(error === undefined) {
if(errorFlag === undefined) {
core.showMessage('"' + title + '" successfully published.');
}
});
};
// Generate a publishIndex associated to a fileIndex and store a publishObject
function createPublishIndex(fileIndex, publishObject) {
var publishIndex = undefined;
do {
publishIndex = "publish." + core.randomString();
} while(_.has(localStorage, publishIndex));
localStorage[publishIndex] = JSON.stringify(publishObject);
localStorage[fileIndex + ".publish"] += publishIndex + ";";
}
// Add a new publish location to a local document
publisher.newLocation = function(publishObject, callback) {
publisher.newLocation = function(publishObject) {
var fileIndex = fileManager.getCurrentFileIndex();
var title = localStorage[fileIndex + ".title"];
var content = getPublishContent(publishObject);
var provider = providerMap[publishObject.provider];
provider.publishNew(publishObject, title, content);
provider.publish(publishObject, title, content, function(error) {
if(error === undefined) {
createPublishIndex(fileIndex, publishObject);
publisher.notifyPublish();
core.showMessage('"' + title
+ '" will now be published on GitHub.');
}
});
};
// Associate publish provider to publisher
_.each(arguments, function(argument) {
if(argument !== undefined && argument.publishProvider !== undefined) {
providerMap[argument.publishProvider] = argument;
// Used to populate the "Manage publication" dialog
var lineTemplate = ['<div class="input-prepend input-append">',
'<span class="add-on" title="<%= provider.providerName %>">',
'<i class="icon-<%= provider.providerId %>"></i></span>',
'<input class="span5" type="text" value="<%= publishDesc %>" disabled />',
'</div>'].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
publisher.refreshManagePublish = function() {
var fileIndex = fileManager.getCurrentFileIndex();
var publishIndexList = _.compact(localStorage[fileIndex + ".publish"].split(";"));
$(".msg-no-publish, .msg-publish-list").addClass("hide");
$("#manage-publish-list .input-append").remove();
if (publishIndexList.length > 0) {
$(".msg-publish-list").removeClass("hide");
} else {
$(".msg-no-publish").removeClass("hide");
}
});
_.each(publishIndexList, function(publishIndex) {
var serializedObject = localStorage[publishIndex];
var publishObject = JSON.parse(serializedObject);
var publishDesc = JSON.stringify(_.omit(publishObject, 'provider')).replace(/{|}|"/g, "");
lineElement = $(_.template(lineTemplate, {
provider: providerMap[publishObject.provider],
publishDesc: publishDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
fileManager.removePublish(publishIndex);
}));
$("#manage-publish-list").append(lineElement);
});
};
publisher.init = function(coreModule, fileManagerModule) {
core = coreModule;
@ -120,7 +174,7 @@ define(["jquery", "google-helper", "github-helper", "publish-github", "underscor
// Init providers
_.each(providerMap, function(provider) {
provider.init();
provider.init(core);
});
$(".action-force-publish").click(function() {