Use require.js

This commit is contained in:
benweet 2013-04-02 19:42:47 +01:00
parent f32a1e41a6
commit f9e56d1319
17 changed files with 2891 additions and 600 deletions

View File

@ -1 +1 @@
CACHE MANIFEST # v5 CACHE: index.html css/bootstrap.css css/jgrowl.css css/main.css js/async-runner.js js/base64.js js/bootstrap.js js/gdrive.js js/jquery.jgrowl.js js/jquery.js js/jquery.layout.js js/jquery-ui.custom.js js/main.js js/Markdown.Converter.js js/Markdown.Editor.js async-runner.js js/base64.js .js js/synchronizer.js img/ajax-loader.gif img/dropbox.png img/gdrive.png img/glyphicons-halflings.png img/glyphicons-halflings-white.png img/stackedit-16.png img/stackedit-32.ico NETWORK: * CACHE MANIFEST # v5 CACHE: index.html css/bootstrap.css css/jgrowl.css css/main.css js/async-runner.js js/bootstrap.js js/async-runner.js js/base64.js js/bootstrap.js js/config.js js/gdrive.js js/jgrowl.js js/jquery.js async-runner.js js/jquery.layout.js async-runner.js js/jquery-ui.custom.js js/main.js js/Markdown.Converter.js js/Markdown.Editor.js async-runner.js js/main.js js/base64.js .js js/synchronizer.js img/ajax-loader.gif img/dropbox.png img/gdrive.png img/glyphicons-halflings.png img/glyphicons-halflings-white.png img/stackedit-16.png img/stackedit-32.ico NETWORK: *

View File

@ -1,3 +1,6 @@
@import url("bootstrap.css");
@import url("jgrowl.css");
body { body {
background-color: #f5f5f5; background-color: #f5f5f5;
} }

125
img/stackedit-promo.svg Normal file
View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="800"
height="200"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="stackedit-promo.svg"
inkscape:export-filename="C:\Documents and Settings\g550003\Mes documents\Mes images\stackedit-promo.png"
inkscape:export-xdpi="103.5"
inkscape:export-ydpi="103.5">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="521.42724"
inkscape:cy="-97.239171"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1143"
inkscape:window-x="-4"
inkscape:window-y="-4"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid3000"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-852.36217)">
<text
xml:space="preserve"
style="font-size:144px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Corbel;-inkscape-font-specification:Corbel"
x="221.88937"
y="1017.7139"
id="text2991"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan2993"
x="221.88937"
y="1017.7139">stack<tspan
style="font-size:144px;font-weight:bold;-inkscape-font-specification:Corbel Bold"
id="tspan2995">edit</tspan></tspan></text>
<g
id="g3013"
transform="matrix(1.015625,0,0,1.015625,-0.48727889,-13.775147)">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path3001"
d="m 41.18585,941.60943 0,69.99997 140,0 0,-69.99997"
style="fill:none;stroke:#626265;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path3789"
d="m 61.185851,986.60941 89.999999,0"
style="fill:none;stroke:#6c6c70;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path3791"
d="m 61.185851,966.60944 79.999999,0"
style="fill:none;stroke:#95785b;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path3793"
d="m 61.185851,946.60937 39.999999,0"
style="fill:none;stroke:#bc8e48;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path3795"
d="m 61.185851,926.6094 99.999999,0"
style="fill:none;stroke:#d38b28;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path3797"
d="m 61.185851,906.60943 69.999999,0"
style="fill:none;stroke:#fd8a07;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
inkscape:connector-curvature="0"
id="path3799"
d="m 61.185851,886.60945 99.999999,0"
style="fill:none;stroke:#fe7a15;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

115
img/stackedit.svg Normal file
View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="stackedit-dark.svg"
inkscape:export-filename="C:\Documents and Settings\g550003\Mes documents\Mes images\stackedit-128.png"
inkscape:export-xdpi="360"
inkscape:export-ydpi="360">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.4721116"
inkscape:cx="89.111669"
inkscape:cy="-23.177627"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1920"
inkscape:window-height="1143"
inkscape:window-x="-4"
inkscape:window-y="-4"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid3000"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1020.3622)">
<path
style="fill:none;stroke:#626265;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 2,-2 0,14 28,0 0,-14"
id="path3001"
inkscape:connector-curvature="0"
transform="translate(0,1036.3622)"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#6c6c70;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 6,23 18,0"
id="path3789"
inkscape:connector-curvature="0"
transform="translate(0,1020.3622)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#95785b;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 6,19 16,0"
id="path3791"
inkscape:connector-curvature="0"
transform="translate(0,1020.3622)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#bc8e48;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 6,15 8,0"
id="path3793"
inkscape:connector-curvature="0"
transform="translate(0,1020.3622)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#d38b28;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 6,11 20,0"
id="path3795"
inkscape:connector-curvature="0"
transform="translate(0,1020.3622)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#fd8a07;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 6,7 20,7"
id="path3797"
inkscape:connector-curvature="0"
transform="translate(0,1020.3622)"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#fe7a15;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 6,3 26,3"
id="path3799"
inkscape:connector-curvature="0"
transform="translate(0,1020.3622)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -6,24 +6,8 @@
<link rel="shortcut icon" href="img/stackedit-32.ico" <link rel="shortcut icon" href="img/stackedit-32.ico"
type="image/x-icon"> type="image/x-icon">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Bootstrap -->
<link href="css/bootstrap.css" rel="stylesheet" media="screen">
<link href="css/jgrowl.css" rel="stylesheet" media="screen">
<link href="css/main.css" rel="stylesheet" media="screen"> <link href="css/main.css" rel="stylesheet" media="screen">
<script type="text/javascript" src="js/base64.js"></script> <script data-main="js/main" src="js/require.js"></script>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jquery.jgrowl.js"></script>
<script type="text/javascript" src="js/jquery-ui.custom.js"></script>
<script type="text/javascript" src="js/jquery.layout.js"></script>
<script type="text/javascript" src="js/bootstrap.js"></script>
<script type="text/javascript" src="js/Markdown.Converter.js"></script>
<script type="text/javascript" src="js/Markdown.Sanitizer.js"></script>
<script type="text/javascript" src="js/Markdown.Editor.js"></script>
<script type="text/javascript" src="js/main.js"></script>
<script type="text/javascript" src="js/async-runner.js"></script>
<script type="text/javascript" src="js/gdrive.js"></script>
<script type="text/javascript" src="js/synchronizer.js"></script>
<script type="text/javascript" src="js/config.custo.js"></script>
<script> <script>
(function(i, s, o, g, r, a, m) { (function(i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r; i['GoogleAnalyticsObject'] = r;
@ -54,7 +38,7 @@
<i class="icon-refresh"></i> <i class="icon-refresh"></i>
</button></li> </button></li>
<li class="btn-group"><button class="btn action-create-file" <li class="btn-group"><button class="btn action-create-file"
title="Create a local file"> title="Create a new local file">
<i class="icon-file"></i> <i class="icon-file"></i>
</button> </button>
<button class="btn" title="Delete the current file locally" <button class="btn" title="Delete the current file locally"
@ -105,9 +89,9 @@
</ul> </ul>
</div> </div>
</div> </div>
<textarea id="wmd-input" class="ui-layout-center"></textarea> <textarea id="wmd-input" class="ui-layout-center hide"></textarea>
<div class="ui-layout-east"></div> <div class="ui-layout-east hide"></div>
<div class="ui-layout-south"></div> <div class="ui-layout-south hide"></div>
<div id="modal-remove-file-confirm" class="modal hide"> <div id="modal-remove-file-confirm" class="modal hide">
<div class="modal-header"> <div class="modal-header">

View File

@ -1,20 +1,19 @@
var ASYNC_TASK_DEFAULT_TIMEOUT = 30000;
/** /**
* Used to run any asynchronous tasks sequentially (ajax mainly) * Used to run asynchronous tasks sequentially (ajax mainly)
* An asynchronous task must be created with: * An asynchronous task must be created with:
* - a required run() function that may call success(), error() or retry() * - a required run() function that may call success(), error() or retry()
* - an optional onSuccess() function * - an optional onSuccess() function
* - an optional onError() function * - an optional onError() function
* - an optional timeout field (default is 30000) * - an optional timeout field (default is 30000)
*/ */
var asyncTaskRunner = (function() { define(["core"], function(core) {
var asyncTaskRunner = {}; var asyncTaskRunner = {};
var asyncTaskQueue = []; var asyncTaskQueue = [];
var currentTask = undefined; var currentTask = undefined;
var currentTaskRunning = false; var currentTaskRunning = false;
var currentTaskStartTime = currentTime; var currentTaskStartTime = core.currentTime;
// Run the next task in the queue if any and no other is running // Run the next task in the queue if any and no other is running
asyncTaskRunner.runTask = function() { asyncTaskRunner.runTask = function() {
@ -23,7 +22,7 @@ var asyncTaskRunner = (function() {
if(currentTaskRunning === true) { if(currentTaskRunning === true) {
// If the current task takes too long // If the current task takes too long
var timeout = currentTask.timeout || ASYNC_TASK_DEFAULT_TIMEOUT; var timeout = currentTask.timeout || ASYNC_TASK_DEFAULT_TIMEOUT;
if(currentTaskStartTime + timeout < currentTime) { if(currentTaskStartTime + timeout < core.currentTime) {
currentTask.error(); currentTask.error();
} }
return; return;
@ -37,8 +36,8 @@ var asyncTaskRunner = (function() {
// Dequeue an enqueued task // Dequeue an enqueued task
currentTask = asyncTaskQueue.shift(); currentTask = asyncTaskQueue.shift();
currentTaskStartTime = currentTime; currentTaskStartTime = core.currentTime;
showWorkingIndicator(true); core.showWorkingIndicator(true);
// Set task attributes and functions // Set task attributes and functions
currentTask.finished = false; currentTask.finished = false;
@ -48,7 +47,7 @@ var asyncTaskRunner = (function() {
currentTask = undefined; currentTask = undefined;
currentTaskRunning = false; currentTaskRunning = false;
if(asyncTaskQueue.length === 0) { if(asyncTaskQueue.length === 0) {
showWorkingIndicator(false); core.showWorkingIndicator(false);
} }
else { else {
asyncTaskRunner.runTask(); asyncTaskRunner.runTask();
@ -71,14 +70,14 @@ var asyncTaskRunner = (function() {
// Implement an exponential backoff // Implement an exponential backoff
var delay = (Math.pow(2, currentTask.retryCounter++) + Math.random()) * 1000; var delay = (Math.pow(2, currentTask.retryCounter++) + Math.random()) * 1000;
console.log(delay); console.log(delay);
currentTaskStartTime = currentTime + delay; currentTaskStartTime = core.currentTime + delay;
currentTaskRunning = false; currentTaskRunning = false;
asyncTaskRunner.runTask(); asyncTaskRunner.runTask();
}; };
} }
// Run the task // Run the task
if(currentTaskStartTime <= currentTime) { if(currentTaskStartTime <= core.currentTime) {
currentTaskRunning = true; currentTaskRunning = true;
currentTask.run(); currentTask.run();
} }
@ -97,7 +96,7 @@ var asyncTaskRunner = (function() {
currentTask = undefined; currentTask = undefined;
currentTaskRunning = false; currentTaskRunning = false;
if(asyncTaskQueue.length === 0) { if(asyncTaskQueue.length === 0) {
showWorkingIndicator(false); core.showWorkingIndicator(false);
} }
else { else {
asyncTaskRunner.runTask(); asyncTaskRunner.runTask();
@ -111,5 +110,5 @@ var asyncTaskRunner = (function() {
}; };
return asyncTaskRunner; return asyncTaskRunner;
})(); });

18
js/config.js Normal file
View File

@ -0,0 +1,18 @@
var GOOGLE_SCOPES = [ 'https://www.googleapis.com/auth/drive.install',
'https://www.googleapis.com/auth/drive' ];
var DEFAULT_FILE_TITLE = "Filename";
var GDRIVE_DEFAULT_FILE_TITLE = "New Markdown document";
var CHECK_ONLINE_PERIOD = 60000;
var AJAX_TIMEOUT = 5000;
var ASYNC_TASK_DEFAULT_TIMEOUT = 30000;
var AUTH_POPUP_TIMEOUT = 90000;
var SYNC_PERIOD = 60000;
var SYNC_PROVIDER_GDRIVE = "sync.gdrive.";
// Use by Google's client.js
var delayedFunction = undefined;
function runDelayedFunction() {
if (delayedFunction !== undefined) {
delayedFunction();
}
}

218
js/core.js Normal file
View File

@ -0,0 +1,218 @@
define(["jquery", "bootstrap", "jgrowl", "layout", "Markdown.Editor"], function($) {
var core = {};
// Time shared by others modules
core.currentTime = new Date().getTime();
core.updateCurrentTime = function() {
core.currentTime = new Date().getTime();
};
// Usage: callback = callback || core.doNothing;
core.doNothing = function() {};
// Used by asyncTaskRunner
core.showWorkingIndicator = function(show) {
if (show === false) {
$(".working-indicator").addClass("hide");
} else {
$(".working-indicator").removeClass("hide");
}
};
// Used to show a notification message
core.showMessage = function(msg, iconClass, options) {
options = options || {};
iconClass = iconClass || "icon-info-sign";
$.jGrowl("<i class='icon-white " + iconClass + "'></i> " + msg, options);
};
// Used to show an error message
core.showError = function(msg) {
core.showMessage(msg, "icon-warning-sign");
};
// Offline management
core.isOffline = false;
var offlineTime = core.currentTime;
var offlineListeners = [];
core.addOfflineListener = function(listener) {
offlineListeners.push(listener);
};
core.setOffline = function() {
offlineTime = core.currentTime;
if(core.isOffline === false) {
core.isOffline = true;
core.showMessage("You are offline.", "icon-exclamation-sign msg-offline", {
sticky : true,
close : function() {
core.showMessage("You are back online!", "icon-signal");
}
});
for(var i=0; i<offlineListeners.length; i++) {
offlineListeners[i]();
}
}
};
core.setOnline = function() {
if(offline === true) {
$(".msg-offline").parents(".jGrowl-notification").trigger(
'jGrowl.beforeClose');
core.isOffline = false;
for(var i=0; i<offlineListeners.length; i++) {
offlineListeners[i]();
}
}
};
core.checkOnline = function() {
// Try to reconnect if we are offline but we have some network
if (core.isOffline === true && navigator.onLine === true
&& offlineTime + CHECK_ONLINE_PERIOD < core.currentTime) {
offlineTime = core.currentTime;
// Try to download anything to test the connection
$.ajax({
url : "https://apis.google.com/js/client.js",
timeout : AJAX_TIMEOUT, dataType : "script"
}).done(function() {
core.setOnline();
});
}
};
// Setting management
var settings = { layoutOrientation : "horizontal" };
core.loadSettings = function() {
if (localStorage.settings) {
$.extend(settings, JSON.parse(localStorage.settings));
}
// Layout orientation
$("input:radio[name=radio-layout-orientation][value="
+ settings.layoutOrientation + "]").prop("checked", true);
};
core.saveSettings = function() {
// Layout orientation
settings.layoutOrientation = $(
"input:radio[name=radio-layout-orientation]:checked").prop("value");
localStorage.settings = JSON.stringify(settings);
};
// Create the layout
core.createLayout = function() {
var layout = undefined;
var layoutGlobalConfig = {
closable : true,
resizable : false,
slidable : false,
livePaneResizing : true,
enableCursorHotkey : false,
spacing_open : 15,
spacing_closed : 15,
togglerLength_open : 90,
togglerLength_closed : 90,
center__minWidth : 100,
center__minHeight : 100,
stateManagement__enabled : false
};
$(".ui-layout-center, .ui-layout-east, .ui-layout-south").removeClass("hide");
if (settings.layoutOrientation == "horizontal") {
$(".ui-layout-south").remove();
$(".ui-layout-east").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout(
$.extend(layoutGlobalConfig, {
east__resizable : true,
east__size : .5,
east__minSize : 200
})
);
} else if (settings.layoutOrientation == "vertical") {
$(".ui-layout-east").remove();
$(".ui-layout-south").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout(
$.extend(layoutGlobalConfig, {
south__resizable : true,
south__size : .5,
south__minSize : 200
})
);
}
$(".ui-layout-toggler-north").addClass("btn").append(
$("<b>").addClass("caret"));
$(".ui-layout-toggler-south").addClass("btn").append(
$("<b>").addClass("caret"));
$(".ui-layout-toggler-east").addClass("btn").append(
$("<b>").addClass("caret"));
$("#navbar").click(function() {
layout.allowOverflow('north');
});
};
// Create the PageDown editor
core.createEditor = function(onTextChange) {
$("#wmd-button-bar").empty();
var converter = Markdown.getSanitizingConverter();
var firstChange = true;
converter.hooks.chain("preConversion", function(text) {
if (!firstChange) {
onTextChange();
}
return text;
});
var editor = new Markdown.Editor(converter);
editor.run();
firstChange = false;
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)")
.addClass("btn").css("left", 0).find("span").hide();
$("#wmd-bold-button").append($("<i>").addClass("icon-bold"));
$("#wmd-italic-button").append($("<i>").addClass("icon-italic"));
$("#wmd-link-button").append($("<i>").addClass("icon-globe"));
$("#wmd-quote-button").append($("<i>").addClass("icon-indent-left"));
$("#wmd-code-button").append($("<i>").addClass("icon-code"));
$("#wmd-image-button").append($("<i>").addClass("icon-picture"));
$("#wmd-olist-button").append($("<i>").addClass("icon-numbered-list"));
$("#wmd-ulist-button").append($("<i>").addClass("icon-list"));
$("#wmd-heading-button").append($("<i>").addClass("icon-text-height"));
$("#wmd-hr-button").append($("<i>").addClass("icon-hr"));
$("#wmd-undo-button").append($("<i>").addClass("icon-undo"));
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt"));
};
core.init = function() {
// jGrowl configuration
$.jGrowl.defaults.life = 5000;
$.jGrowl.defaults.closer = false;
$.jGrowl.defaults.closeTemplate = '';
$.jGrowl.defaults.position = 'bottom-right';
// listen to online/offline events
$(window).on('offline', core.setOffline);
$(window).on('online', core.setOnline);
if (navigator.onLine === false) {
core.setOffline();
}
this.loadSettings();
this.createLayout();
$(".action-load-settings").click(function() {
core.loadSettings();
});
$(".action-apply-settings").click(function() {
core.saveSettings();
location.reload();
});
};
return core;
});

317
js/file-manager.js Normal file
View File

@ -0,0 +1,317 @@
define(["jquery", "core", "gdrive", "synchronizer", "async-runner", "base64"], function($, core, gdrive, synchronizer, asyncTaskRunner) {
var fileManager = {};
var save = false;
fileManager.init = function() {
gdrive.init();
var changeSyncButtonState = function() {
if(synchronizer.isRunning() || synchronizer.isQueueEmpty() || core.isOffline) {
$(".action-force-sync").addClass("disabled");
}
else {
$(".action-force-sync").removeClass("disabled");
}
};
core.addOfflineListener(changeSyncButtonState);
synchronizer.init({
onSyncBegin : changeSyncButtonState,
onSyncEnd : changeSyncButtonState,
onQueueChanged : changeSyncButtonState
});
$(".action-force-sync").click(function() {
if(!$(this).hasClass("disabled")) {
synchronizer.forceSync();
}
});
fileManager.selectFile();
// Do periodic tasks
window.setInterval(function() {
core.updateCurrentTime();
fileManager.saveFile();
synchronizer.sync();
asyncTaskRunner.runTask();
core.checkOnline();
}, 1000);
$(".action-create-file").click(function() {
fileManager.saveFile();
var fileIndex = fileManager.createFile();
fileManager.selectFile(fileIndex);
$("#file-title").click();
});
$(".action-remove-file").click(function() {
fileManager.deleteFile();
fileManager.selectFile();
});
$(".action-refresh-manage-sync").click(refreshManageSync);
$("#file-title").click(function() {
$(this).hide();
$("#file-title-input").show().focus();
});
$("#file-title-input").blur(function() {
var title = $.trim($(this).val());
if (title) {
var fileIndexTitle = localStorage["file.current"] + ".title";
if (title != localStorage[fileIndexTitle]) {
localStorage[fileIndexTitle] = title;
fileManager.updateFileTitles();
save = true;
}
}
$(this).hide();
$("#file-title").show();
});
$(".action-download-md").click(
function() {
var content = $("#wmd-input").val();
var uriContent = "data:application/octet-stream;base64,"
+ base64.encode(content);
window.open(uriContent, 'file');
});
$(".action-download-html").click(
function() {
var content = $("#wmd-preview").html();
var uriContent = "data:application/octet-stream;base64,"
+ base64.encode(content);
window.open(uriContent, 'file');
});
$(".action-upload-gdrive").click(uploadGdrive);
$(".action-upload-dropbox").click(function() {
core.showMessage("Sorry, Dropbox synchronization is not yet available.");
});
};
var fileDescList = [];
fileManager.selectFile = function(fileIndex) {
// If file system does not exist
if (!localStorage["file.counter"] || !localStorage["file.list"]) {
localStorage.clear();
localStorage["file.counter"] = 0;
localStorage["file.list"] = ";";
}
// If no file create one
if (localStorage["file.list"].length === 1) {
fileIndex = this.createFile();
}
fileIndex = fileIndex || localStorage["file.current"];
if(fileIndex !== undefined) {
localStorage["file.current"] = fileIndex;
}
// Update the file titles
this.updateFileTitles();
// Update the editor
var fileIndex = localStorage["file.current"];
$("#wmd-input").val(localStorage[fileIndex + ".content"]);
core.createEditor(function() {
save = true;
});
};
fileManager.createFile = function(title, content, syncIndexes) {
content = content || "";
syncIndexes = syncIndexes || [];
if (!title) {
// Create a file title
title = DEFAULT_FILE_TITLE;
function exists(title) {
for ( var i = 0; i < fileDescList.length; i++) {
if(fileDescList[i].title == title) {
return true;
}
}
}
var indicator = 2;
while(exists(title)) {
title = DEFAULT_FILE_TITLE + indicator++;
}
}
// Create the fileIndex
var fileCounter = parseInt(localStorage["file.counter"]);
var fileIndex = "file." + fileCounter;
// Create the file in the localStorage
localStorage[fileIndex + ".content"] = content;
localStorage[fileIndex + ".title"] = title;
var sync = ";";
for(var i=0; i<syncIndexes.length; i++) {
sync += syncIndexes[i] + ";";
}
localStorage[fileIndex + ".sync"] = sync;
localStorage["file.counter"] = fileCounter + 1;
localStorage["file.list"] += fileIndex + ";";
return fileIndex;
};
fileManager.deleteFile = function() {
var fileIndex = localStorage["file.current"];
// Remove synchronized locations
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
for ( var i = 1; i < fileSyncIndexList.length - 1; i++) {
var fileSyncIndex = fileSyncIndexList[i];
fileManager.removeSync(fileSyncIndex);
}
localStorage.removeItem(fileIndex + ".sync");
localStorage.removeItem("file.current");
localStorage["file.list"] = localStorage["file.list"].replace(";"
+ fileIndex + ";", ";");
localStorage.removeItem(fileIndex + ".title");
localStorage.removeItem(fileIndex + ".content");
};
fileManager.saveFile = function() {
if (save) {
var content = $("#wmd-input").val();
var fileIndex = localStorage["file.current"];
localStorage[fileIndex + ".content"] = content;
synchronizer.addFileForUpload(fileIndex);
save = false;
}
};
fileManager.updateFileTitles = function() {
fileDescList = [];
$("#file-selector").empty();
var fileIndexList = localStorage["file.list"].split(";");
for ( var i = 1; i < fileIndexList.length - 1; i++) {
var fileIndex = fileIndexList[i];
var title = localStorage[fileIndex + ".title"];
fileDescList.push({ index : fileIndex, title : title });
}
fileDescList.sort(function(a, b) {
if (a.title.toLowerCase() < b.title.toLowerCase())
return -1;
if (a.title.toLowerCase() > b.title.toLowerCase())
return 1;
return 0;
});
var fileIndex = localStorage["file.current"];
// If no default file take first one
if (!fileIndex) {
fileIndex = fileDescList[0].index;
localStorage["file.current"] = fileIndex;
}
var useGoogleDrive = false;
function composeTitle(fileIndex) {
var result = localStorage[fileIndex + ".title"];
var sync = localStorage[fileIndex + ".sync"];
if (sync.indexOf(";" + SYNC_PROVIDER_GDRIVE) !== -1) {
useGoogleDrive = true;
result = '<i class="icon-gdrive"></i> ' + result;
}
return result;
}
synchronizer.useGoogleDrive = useGoogleDrive;
// Update the file title
var title = localStorage[fileIndex + ".title"];
document.title = "StackEdit - " + title;
$("#file-title").html(composeTitle(fileIndex));
$(".file-title").text(title);
$("#file-title-input").val(title);
// Update the file selector
$("#file-selector").empty();
for ( var i = 0; i < fileDescList.length; i++) {
var fileDesc = fileDescList[i];
var a = $("<a>").html(composeTitle(fileDesc.index));
var li = $("<li>").append(a);
if (fileDesc.index == fileIndex) {
li.addClass("disabled");
} else {
a.prop("href", "#").click((function(fileIndex) {
return function() {
localStorage["file.current"] = fileIndex;
fileManager.selectFile();
};
})(fileDesc.index));
}
$("#file-selector").append(li);
}
};
// Remove a synchronized location
fileManager.removeSync = function(fileSyncIndex) {
var fileIndexList = localStorage["file.list"].split(";");
// Look for local files associated to this synchronized location
for ( var i = 1; i < fileIndexList.length - 1; i++) {
var fileIndexSync = fileIndexList[i] + ".sync";
localStorage[fileIndexSync] = localStorage[fileIndexSync].replace(";"
+ fileSyncIndex + ";", ";");
}
// Remove etag
localStorage.removeItem(fileSyncIndex + ".etag");
};
// Look for local file associated to a synchronized location
fileManager.getFileIndexFromSync = function(fileSyncIndex) {
var fileIndex = undefined;
var fileIndexList = localStorage["file.list"].split(";");
for ( var i = 1; i < fileIndexList.length - 1; i++) {
var tempFileIndex = fileIndexList[i];
var sync = localStorage[tempFileIndex + ".sync"];
if (sync.indexOf(";" + fileSyncIndex + ";") !== -1) {
fileIndex = tempFileIndex;
break;
}
}
return fileIndex;
};
function uploadGdrive() {
$(".file-sync-indicator").removeClass("hide");
var fileIndex = localStorage["file.current"];
var content = localStorage[fileIndex + ".content"];
var title = localStorage[fileIndex + ".title"];
gdrive.createFile(title, content, function(fileSyncIndex) {
if (fileSyncIndex) {
localStorage[fileIndex + ".sync"] += fileSyncIndex + ";";
fileManager.updateFileTitles();
core.showMessage('"' + title
+ '" will now be synchronized on Google Drive.');
}
});
}
function refreshManageSync() {
var fileIndex = localStorage["file.current"];
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
$(".msg-no-sync, .msg-sync-list").addClass("hide");
$("#manage-sync-list .input-append").remove();
if (fileSyncIndexList.length > 2) {
$(".msg-sync-list").removeClass("hide");
} else {
$(".msg-no-sync").removeClass("hide");
}
for ( var i = 1; i < fileSyncIndexList.length - 1; i++) {
var fileSyncIndex = fileSyncIndexList[i];
(function(fileSyncIndex) {
var line = $("<div>").addClass("input-append");
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
line.append($("<input>").prop("type", "text").prop(
"disabled", true).addClass("span5").val(
"Google Drive, FileID="
+ fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length)));
line.append($("<a>").addClass("btn").html(
'<i class="icon-trash"></i>').prop("title",
"Remove this synchronized location").click(function() {
fileManager.removeSync(fileSyncIndex);
fileManager.updateFileTitles();
refreshManageSync();
}));
}
$("#manage-sync-list").append(line);
})(fileSyncIndex);
}
}
return fileManager;
});

View File

@ -1,52 +1,37 @@
var GOOGLE_CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googleusercontent.com'; define(["jquery", "core", "file-manager", "async-runner", "base64"], function($, core, fileManager, asyncTaskRunner) {
var SCOPES = [ 'https://www.googleapis.com/auth/drive.install',
'https://www.googleapis.com/auth/drive.file' ];
var AUTH_POPUP_TIMEOUT = 90000;
var DEFAULT_GDRIVE_FILE_TITLE = "New Markdown document";
var gdriveDelayedFunction = undefined;
function runGdriveDelayedFunction() {
if (gdriveDelayedFunction !== undefined) {
gdriveDelayedFunction();
}
}
var gdrive = (function($) {
var connected = false; var connected = false;
var authenticated = false; var authenticated = false;
var doNothing = function() {
};
var gdrive = {}; var gdrive = {};
// Try to connect Gdrive by downloading client.js // Try to connect Gdrive by downloading client.js
function connect(callback) { function connect(callback) {
callback = callback || doNothing; callback = callback || core.doNothing;
var asyncTask = {}; var asyncTask = {};
asyncTask.run = function() { asyncTask.run = function() {
if (connected === true) { if (connected === true) {
asyncTask.success(); asyncTask.success();
return; return;
} }
gdriveDelayedFunction = function() { delayedFunction = function() {
asyncTask.success(); asyncTask.success();
}; };
$.ajax({ $.ajax({
url : "https://apis.google.com/js/client.js?onload=runGdriveDelayedFunction", url : "https://apis.google.com/js/client.js?onload=runDelayedFunction",
dataType : "script", timeout : AJAX_TIMEOUT dataType : "script", timeout : AJAX_TIMEOUT
}).fail(function() { }).fail(function() {
asyncTask.error(); asyncTask.error();
}); });
}; };
asyncTask.onSuccess = function() { asyncTask.onSuccess = function() {
gdriveDelayedFunction = undefined; delayedFunction = undefined;
connected = true; connected = true;
callback(); callback();
}; };
asyncTask.onError = function() { asyncTask.onError = function() {
gdriveDelayedFunction = undefined; delayedFunction = undefined;
onOffline(); core.setOffline();
callback(); callback();
}; };
asyncTaskRunner.addTask(asyncTask); asyncTaskRunner.addTask(asyncTask);
@ -54,7 +39,7 @@ var gdrive = (function($) {
// Try to authenticate with Oauth // Try to authenticate with Oauth
function authenticate(callback, immediate) { function authenticate(callback, immediate) {
callback = callback || doNothing; callback = callback || core.doNothing;
if (immediate === undefined) { if (immediate === undefined) {
immediate = true; immediate = true;
} }
@ -75,10 +60,10 @@ var gdrive = (function($) {
return; return;
} }
if (immediate === false) { if (immediate === false) {
showMessage("Please make sure the Google authorization popup is not blocked by your browser."); core.showMessage("Please make sure the Google authorization popup is not blocked by your browser.");
} }
gapi.auth.authorize({ 'client_id' : GOOGLE_CLIENT_ID, gapi.auth.authorize({ 'client_id' : GOOGLE_CLIENT_ID,
'scope' : SCOPES, 'immediate' : immediate }, function( 'scope' : GOOGLE_SCOPES, 'immediate' : immediate }, function(
authResult) { authResult) {
if (!authResult || authResult.error) { if (!authResult || authResult.error) {
asyncTask.error(); asyncTask.error();
@ -114,13 +99,13 @@ var gdrive = (function($) {
// Retry as described in Google's best practices // Retry as described in Google's best practices
asyncTask.retry(); asyncTask.retry();
return; return;
} else if (error.code === 401) { } else if (error.code === 401 || error.code === 403) {
authenticated = false; authenticated = false;
errorMsg = "Access to Google Drive is not authorized."; errorMsg = "Access to Google Drive is not authorized.";
} else if (error.code <= 0) { } else if (error.code <= 0) {
connected = false; connected = false;
authenticated = false; authenticated = false;
onOffline(); core.setOffline();
} else { } else {
errorMsg = "Google Drive error (" + error.code + ": " errorMsg = "Google Drive error (" + error.code + ": "
+ error.message + ")."; + error.message + ").";
@ -128,7 +113,7 @@ var gdrive = (function($) {
} }
asyncTask.onError = function() { asyncTask.onError = function() {
if (errorMsg !== undefined) { if (errorMsg !== undefined) {
showError(errorMsg); core.showError(errorMsg);
} }
callback(); callback();
}; };
@ -136,7 +121,7 @@ var gdrive = (function($) {
} }
function upload(fileId, parentId, title, content, callback) { function upload(fileId, parentId, title, content, callback) {
callback = callback || doNothing; callback = callback || core.doNothing;
authenticate(function() { authenticate(function() {
if (connected === false) { if (connected === false) {
callback(); callback();
@ -191,7 +176,7 @@ var gdrive = (function($) {
var error = response.error; var error = response.error;
// If file has been removed from Google Drive // If file has been removed from Google Drive
if(error !== undefined && fileId !== undefined && error.code === 404) { if(error !== undefined && fileId !== undefined && error.code === 404) {
showMessage('"' + title + '" has been removed from Google Drive.'); core.showMessage('"' + title + '" has been removed from Google Drive.');
fileManager.removeSync(SYNC_PROVIDER_GDRIVE + fileId); fileManager.removeSync(SYNC_PROVIDER_GDRIVE + fileId);
fileManager.updateFileTitles(); fileManager.updateFileTitles();
// Avoid error analyzed by handleError // Avoid error analyzed by handleError
@ -209,7 +194,7 @@ var gdrive = (function($) {
} }
gdrive.checkUpdates = function(lastChangeId, callback) { gdrive.checkUpdates = function(lastChangeId, callback) {
callback = callback || doNothing; callback = callback || core.doNothing;
authenticate(function() { authenticate(function() {
if (connected === false) { if (connected === false) {
callback(); callback();
@ -263,7 +248,7 @@ var gdrive = (function($) {
}; };
gdrive.downloadMetadata = function(ids, callback, result) { gdrive.downloadMetadata = function(ids, callback, result) {
callback = callback || doNothing; callback = callback || core.doNothing;
result = result || []; result = result || [];
if(ids.length === 0) { if(ids.length === 0) {
callback(result); callback(result);
@ -305,7 +290,7 @@ var gdrive = (function($) {
}; };
gdrive.downloadContent = function(objects, callback, result) { gdrive.downloadContent = function(objects, callback, result) {
callback = callback || doNothing; callback = callback || core.doNothing;
result = result || []; result = result || [];
if(objects.length === 0) { if(objects.length === 0) {
callback(result); callback(result);
@ -377,14 +362,14 @@ var gdrive = (function($) {
localStorage.removeItem("sync.gdrive.state"); localStorage.removeItem("sync.gdrive.state");
state = JSON.parse(state); state = JSON.parse(state);
if (state.action == "create") { if (state.action == "create") {
upload(undefined, state.folderId, DEFAULT_GDRIVE_FILE_TITLE, upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE,
"", function(fileSyncIndex) { "", function(fileSyncIndex) {
if(fileSyncIndex === undefined) { if(fileSyncIndex === undefined) {
return; return;
} }
var fileIndex = fileManager.createFile(DEFAULT_GDRIVE_FILE_TITLE, "", [fileSyncIndex]); var fileIndex = fileManager.createFile(GDRIVE_DEFAULT_FILE_TITLE, "", [fileSyncIndex]);
fileManager.selectFile(fileIndex); fileManager.selectFile(fileIndex);
showMessage('"' + DEFAULT_GDRIVE_FILE_TITLE + '" created successfully on Google Drive.'); core.showMessage('"' + GDRIVE_DEFAULT_FILE_TITLE + '" created successfully on Google Drive.');
}); });
} }
else if (state.action == "open") { else if (state.action == "open") {
@ -413,7 +398,7 @@ var gdrive = (function($) {
localStorage[fileSyncIndex + ".etag"] = file.etag; localStorage[fileSyncIndex + ".etag"] = file.etag;
var fileIndex = fileManager.createFile(file.title, file.content, [fileSyncIndex]); var fileIndex = fileManager.createFile(file.title, file.content, [fileSyncIndex]);
fileManager.selectFile(fileIndex); fileManager.selectFile(fileIndex);
showMessage('"' + file.title + '" imported successfully from Google Drive.'); core.showMessage('"' + file.title + '" imported successfully from Google Drive.');
} }
}); });
}); });
@ -421,4 +406,4 @@ var gdrive = (function($) {
}; };
return gdrive; return gdrive;
})(jQuery); });

View File

@ -1,511 +1,22 @@
var currentTime = new Date().getTime(); requirejs.config({
paths: {
function showError(msg) { custo: [
showMessage(msg, "icon-warning-sign"); 'dev/custo',
} 'prod/custo'
]
function showMessage(msg, iconClass, options) { },
options = options || {}; shim: {
iconClass = iconClass || "icon-info-sign"; 'jquery-ui': ['jquery'],
$.jGrowl("<i class='icon-white " + iconClass + "'></i> " + msg, options); 'bootstrap': ['jquery'],
} 'jgrowl': ['jquery'],
'layout': ['jquery-ui'],
function showWorkingIndicator(show) { 'Markdown.Sanitizer': ['Markdown.Converter'],
if (show === false) { 'Markdown.Editor': ['Markdown.Sanitizer']
$(".working-indicator").addClass("hide");
} else {
$(".working-indicator").removeClass("hide");
} }
} });
require(["jquery", "core", "file-manager", "config", "custo"], function($, core, fileManager) {
var AJAX_TIMEOUT = 5000;
var CHECK_ONLINE_PERIOD = 60000;
var offline = false;
var offlineTime = currentTime;
var offlineListeners = [];
function onOffline() {
offlineTime = currentTime;
if(offline === false) {
offline = true;
showMessage("You are offline.", "icon-exclamation-sign msg-offline", {
sticky : true, close : function() {
showMessage("You are back online!", "icon-signal");
} });
for(var i=0; i<offlineListeners.length; i++) {
offlineListeners[i]();
}
}
}
function onOnline() {
if(offline === true) {
$(".msg-offline").parents(".jGrowl-notification").trigger(
'jGrowl.beforeClose');
offline = false;
for(var i=0; i<offlineListeners.length; i++) {
offlineListeners[i]();
}
}
}
function checkOnline() {
// Try to reconnect if we are offline but we have some network
if (offline === true && navigator.onLine === true
&& offlineTime + CHECK_ONLINE_PERIOD < currentTime) {
offlineTime = currentTime;
// Try to download anything to test the connection
$.ajax(
{ url : "https://apis.google.com/js/client.js",
timeout : AJAX_TIMEOUT, dataType : "script" }).done(function() {
onOnline();
});
}
}
var DEFAULT_FILE_TITLE = "Filename";
var fileManager = (function($) {
var fileManager = {};
var save = false;
fileManager.init = function() {
gdrive.init();
var changeSyncButtonState = function() {
if(synchronizer.isRunning() || synchronizer.isQueueEmpty() || offline) {
$(".action-force-sync").addClass("disabled");
}
else {
$(".action-force-sync").removeClass("disabled");
}
};
offlineListeners.push(changeSyncButtonState);
synchronizer.init({
onSyncBegin : changeSyncButtonState,
onSyncEnd : changeSyncButtonState,
onQueueChanged : changeSyncButtonState
});
$(".action-force-sync").click(function() {
if(!$(this).hasClass("disabled")) {
synchronizer.forceSync();
}
});
fileManager.selectFile();
// Do periodic stuff
window.setInterval(function() {
currentTime = new Date().getTime();
fileManager.saveFile();
synchronizer.sync();
asyncTaskRunner.runTask();
checkOnline();
}, 1000);
$(".action-create-file").click(function() {
fileManager.saveFile();
var fileIndex = fileManager.createFile();
fileManager.selectFile(fileIndex);
$("#file-title").click();
});
$(".action-remove-file").click(function() {
fileManager.deleteFile();
fileManager.selectFile();
});
$(".action-refresh-manage-sync").click(refreshManageSync);
$("#file-title").click(function() {
$(this).hide();
$("#file-title-input").show().focus();
});
$("#file-title-input").blur(function() {
var title = $.trim($(this).val());
if (title) {
var fileIndexTitle = localStorage["file.current"] + ".title";
if (title != localStorage[fileIndexTitle]) {
localStorage[fileIndexTitle] = title;
fileManager.updateFileTitles();
save = true;
}
}
$(this).hide();
$("#file-title").show();
});
$(".action-download-md").click(
function() {
var content = $("#wmd-input").val();
var uriContent = "data:application/octet-stream;base64,"
+ base64.encode(content);
window.open(uriContent, 'file');
});
$(".action-download-html").click(
function() {
var content = $("#wmd-preview").html();
var uriContent = "data:application/octet-stream;base64,"
+ base64.encode(content);
window.open(uriContent, 'file');
});
$(".action-upload-gdrive").click(uploadGdrive);
$(".action-upload-dropbox").click(function() {
showMessage("Sorry, Dropbox synchronization is not yet available.");
});
};
var fileDescList = [];
fileManager.selectFile = function(fileIndex) {
// If file system does not exist
if (!localStorage["file.counter"] || !localStorage["file.list"]) {
localStorage.clear();
localStorage["file.counter"] = 0;
localStorage["file.list"] = ";";
}
// If no file create one
if (localStorage["file.list"].length === 1) {
fileIndex = this.createFile();
}
fileIndex = fileIndex || localStorage["file.current"];
if(fileIndex !== undefined) {
localStorage["file.current"] = fileIndex;
}
// Update the file titles
this.updateFileTitles();
// Update the editor
var fileIndex = localStorage["file.current"];
$("#wmd-input").val(localStorage[fileIndex + ".content"]);
core.createEditor(function() {
save = true;
});
};
fileManager.createFile = function(title, content, syncIndexes) {
content = content || "";
syncIndexes = syncIndexes || [];
if (!title) {
// Create a file title
title = DEFAULT_FILE_TITLE;
function exists(title) {
for ( var i = 0; i < fileDescList.length; i++) {
if(fileDescList[i].title == title) {
return true;
}
}
}
var indicator = 2;
while(exists(title)) {
title = DEFAULT_FILE_TITLE + indicator++;
}
}
// Create the fileIndex
var fileCounter = parseInt(localStorage["file.counter"]);
var fileIndex = "file." + fileCounter;
// Create the file in the localStorage
localStorage[fileIndex + ".content"] = content;
localStorage[fileIndex + ".title"] = title;
var sync = ";";
for(var i=0; i<syncIndexes.length; i++) {
sync += syncIndexes[i] + ";";
}
localStorage[fileIndex + ".sync"] = sync;
localStorage["file.counter"] = fileCounter + 1;
localStorage["file.list"] += fileIndex + ";";
return fileIndex;
};
fileManager.deleteFile = function() {
var fileIndex = localStorage["file.current"];
// Remove synchronized locations
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
for ( var i = 1; i < fileSyncIndexList.length - 1; i++) {
var fileSyncIndex = fileSyncIndexList[i];
fileManager.removeSync(fileSyncIndex);
}
localStorage.removeItem(fileIndex + ".sync");
localStorage.removeItem("file.current");
localStorage["file.list"] = localStorage["file.list"].replace(";"
+ fileIndex + ";", ";");
localStorage.removeItem(fileIndex + ".title");
localStorage.removeItem(fileIndex + ".content");
};
fileManager.saveFile = function() {
if (save) {
var content = $("#wmd-input").val();
var fileIndex = localStorage["file.current"];
localStorage[fileIndex + ".content"] = content;
synchronizer.addFileForUpload(fileIndex);
save = false;
}
};
fileManager.updateFileTitles = function() {
fileDescList = [];
$("#file-selector").empty();
var fileIndexList = localStorage["file.list"].split(";");
for ( var i = 1; i < fileIndexList.length - 1; i++) {
var fileIndex = fileIndexList[i];
var title = localStorage[fileIndex + ".title"];
fileDescList.push({ index : fileIndex, title : title });
}
fileDescList.sort(function(a, b) {
if (a.title.toLowerCase() < b.title.toLowerCase())
return -1;
if (a.title.toLowerCase() > b.title.toLowerCase())
return 1;
return 0;
});
var fileIndex = localStorage["file.current"];
// If no default file take first one
if (!fileIndex) {
fileIndex = fileDescList[0].index;
localStorage["file.current"] = fileIndex;
}
syncGoogleDrive = false;
function composeTitle(fileIndex) {
var result = localStorage[fileIndex + ".title"];
var sync = localStorage[fileIndex + ".sync"];
if (sync.indexOf(";" + SYNC_PROVIDER_GDRIVE) !== -1) {
syncGoogleDrive = true;
result = '<i class="icon-gdrive"></i> ' + result;
}
return result;
}
// Update the file title
var title = localStorage[fileIndex + ".title"];
document.title = "StackEdit - " + title;
$("#file-title").html(composeTitle(fileIndex));
$(".file-title").text(title);
$("#file-title-input").val(title);
// Update the file selector
$("#file-selector").empty();
for ( var i = 0; i < fileDescList.length; i++) {
var fileDesc = fileDescList[i];
var a = $("<a>").html(composeTitle(fileDesc.index));
var li = $("<li>").append(a);
if (fileDesc.index == fileIndex) {
li.addClass("disabled");
} else {
a.prop("href", "#").click((function(fileIndex) {
return function() {
localStorage["file.current"] = fileIndex;
fileManager.selectFile();
};
})(fileDesc.index));
}
$("#file-selector").append(li);
}
};
// Remove a synchronized location
fileManager.removeSync = function(fileSyncIndex) {
var fileIndexList = localStorage["file.list"].split(";");
// Look for local files associated to this synchronized location
for ( var i = 1; i < fileIndexList.length - 1; i++) {
var fileIndexSync = fileIndexList[i] + ".sync";
localStorage[fileIndexSync] = localStorage[fileIndexSync].replace(";"
+ fileSyncIndex + ";", ";");
}
// Remove etag
localStorage.removeItem(fileSyncIndex + ".etag");
};
// Look for local file associated to a synchronized location
fileManager.getFileIndexFromSync = function(fileSyncIndex) {
var fileIndex = undefined;
var fileIndexList = localStorage["file.list"].split(";");
for ( var i = 1; i < fileIndexList.length - 1; i++) {
var tempFileIndex = fileIndexList[i];
var sync = localStorage[tempFileIndex + ".sync"];
if (sync.indexOf(";" + fileSyncIndex + ";") !== -1) {
fileIndex = tempFileIndex;
break;
}
}
return fileIndex;
};
function uploadGdrive() {
$(".file-sync-indicator").removeClass("hide");
var fileIndex = localStorage["file.current"];
var content = localStorage[fileIndex + ".content"];
var title = localStorage[fileIndex + ".title"];
gdrive.createFile(title, content, function(fileSyncIndex) {
if (fileSyncIndex) {
localStorage[fileIndex + ".sync"] += fileSyncIndex + ";";
fileManager.updateFileTitles();
showMessage('The file "' + title
+ '" will now be synchronized on Google Drive.');
} else {
showError("Error while creating file on Google Drive.");
}
});
}
function refreshManageSync() {
var fileIndex = localStorage["file.current"];
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
$(".msg-no-sync, .msg-sync-list").addClass("hide");
$("#manage-sync-list .input-append").remove();
if (fileSyncIndexList.length > 2) {
$(".msg-sync-list").removeClass("hide");
} else {
$(".msg-no-sync").removeClass("hide");
}
for ( var i = 1; i < fileSyncIndexList.length - 1; i++) {
var fileSyncIndex = fileSyncIndexList[i];
(function(fileSyncIndex) {
var line = $("<div>").addClass("input-append");
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
line.append($("<input>").prop("type", "text").prop(
"disabled", true).addClass("span5").val(
"Google Drive, FileID="
+ fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length)));
line.append($("<a>").addClass("btn").html(
'<i class="icon-trash"></i>').prop("title",
"Remove this synchronized location").click(function() {
fileManager.removeSync(fileSyncIndex);
fileManager.updateFileTitles();
refreshManageSync();
}));
}
$("#manage-sync-list").append(line);
})(fileSyncIndex);
}
}
return fileManager;
})(jQuery);
var core = (function($) {
var core = {};
core.init = function() {
this.loadSettings();
this.createLayout();
$(".action-load-settings").click(function() {
core.loadSettings();
});
$(".action-apply-settings").click(function() {
core.saveSettings();
fileManager.saveFile();
location.reload();
});
};
var settings = { layoutOrientation : "horizontal" };
core.loadSettings = function() {
if (localStorage.settings) {
$.extend(settings, JSON.parse(localStorage.settings));
}
// Layout orientation
$(
"input:radio[name=radio-layout-orientation][value="
+ settings.layoutOrientation + "]").prop("checked", true);
};
core.saveSettings = function() {
// Layout orientation
settings.layoutOrientation = $(
"input:radio[name=radio-layout-orientation]:checked").prop("value");
localStorage.settings = JSON.stringify(settings);
};
core.createLayout = function() {
var layout = undefined;
var layoutGlobalConfig = { closable : true, resizable : false,
slidable : false, livePaneResizing : true,
enableCursorHotkey : false, spacing_open : 15, spacing_closed : 15,
togglerLength_open : 90, togglerLength_closed : 90,
center__minWidth : 100, center__minHeight : 100,
stateManagement__enabled : false, };
if (settings.layoutOrientation == "horizontal") {
$(".ui-layout-south").remove();
$(".ui-layout-east").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout(
$.extend(layoutGlobalConfig, { east__resizable : true,
east__size : .5, east__minSize : 200 }));
} else if (settings.layoutOrientation == "vertical") {
$(".ui-layout-east").remove();
$(".ui-layout-south").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout(
$.extend(layoutGlobalConfig, { south__resizable : true,
south__size : .5, south__minSize : 200 }));
}
$(".ui-layout-toggler-north").addClass("btn").append(
$("<b>").addClass("caret"));
$(".ui-layout-toggler-south").addClass("btn").append(
$("<b>").addClass("caret"));
$(".ui-layout-toggler-east").addClass("btn").append(
$("<b>").addClass("caret"));
$("#navbar").click(function() {
layout.allowOverflow('north');
});
};
core.createEditor = function(onTextChange) {
$("#wmd-button-bar").empty();
var converter = Markdown.getSanitizingConverter();
var firstChange = true;
converter.hooks.chain("preConversion", function(text) {
if (!firstChange) {
onTextChange();
}
return text;
});
var editor = new Markdown.Editor(converter);
editor.run();
firstChange = false;
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)")
.addClass("btn").css("left", 0).find("span").hide();
$("#wmd-bold-button").append($("<i>").addClass("icon-bold"));
$("#wmd-italic-button").append($("<i>").addClass("icon-italic"));
$("#wmd-link-button").append($("<i>").addClass("icon-globe"));
$("#wmd-quote-button").append($("<i>").addClass("icon-indent-left"));
$("#wmd-code-button").append($("<i>").addClass("icon-code"));
$("#wmd-image-button").append($("<i>").addClass("icon-picture"));
$("#wmd-olist-button").append($("<i>").addClass("icon-numbered-list"));
$("#wmd-ulist-button").append($("<i>").addClass("icon-list"));
$("#wmd-heading-button").append($("<i>").addClass("icon-text-height"));
$("#wmd-hr-button").append($("<i>").addClass("icon-hr"));
$("#wmd-undo-button").append($("<i>").addClass("icon-undo"));
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt"));
};
return core;
})(jQuery);
(function($) {
$(function() { $(function() {
// jGrowl configuration
$.jGrowl.defaults.life = 5000;
$.jGrowl.defaults.closer = false;
$.jGrowl.defaults.closeTemplate = '';
$.jGrowl.defaults.position = 'bottom-right';
core.init(); core.init();
// listen to online/offline events
$(window).on('offline', onOffline);
$(window).on('online', onOnline);
if (navigator.onLine === false) {
onOffline();
}
fileManager.init(); fileManager.init();
}); });
});
})(jQuery);

1
js/prod/custo.js Normal file
View File

@ -0,0 +1 @@
var GOOGLE_CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googleusercontent.com';

2019
js/require.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,20 @@
var SYNC_PERIOD = 60000; define(["jquery", "core", "file-manager", "gdrive"], function($, core, fileManager, gdrive) {
var SYNC_PROVIDER_GDRIVE = "sync.gdrive.";
var syncGoogleDrive = false;
var synchronizer = (function() {
var synchronizer = {}; var synchronizer = {};
// Used to know the providers we are connected to
synchronizer.useGoogleDrive = false;
var onSyncBegin = undefined; var onSyncBegin = undefined;
var onSyncEnd = undefined; var onSyncEnd = undefined;
var onQueueChanged = undefined; var onQueueChanged = undefined;
var doNothing = function() {
};
// A synchronization queue containing fileIndex that has to be synchronized // A synchronization queue containing fileIndex that has to be synchronized
var syncUpQueue = undefined; var syncUpQueue = undefined;
synchronizer.init = function(options) { synchronizer.init = function(options) {
onSyncBegin = options.onSyncBegin || doNothing; onSyncBegin = options.onSyncBegin || core.doNothing;
onSyncEnd = options.onSyncEnd || doNothing; onSyncEnd = options.onSyncEnd || core.doNothing;
onQueueChanged = options.onQueueChanged || doNothing; onQueueChanged = options.onQueueChanged || core.doNothing;
syncUpQueue = ";"; syncUpQueue = ";";
// Load the queue from localStorage in case a previous synchronization // Load the queue from localStorage in case a previous synchronization
@ -61,7 +57,7 @@ var synchronizer = (function() {
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) { if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
var id = fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length); var id = fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
gdrive.updateFile(id, title, content, function(result) { gdrive.updateFile(id, title, content, function(result) {
if (result === undefined && offline === true) { if (result === undefined && core.isOffline === true) {
// If we detect offline mode we put the fileIndex back in // If we detect offline mode we put the fileIndex back in
// the queue // the queue
synchronizer.addFileForUpload(localStorage["sync.current"]); synchronizer.addFileForUpload(localStorage["sync.current"]);
@ -100,7 +96,7 @@ var synchronizer = (function() {
}; };
function syncDownGdrive(callback) { function syncDownGdrive(callback) {
if (syncGoogleDrive === false) { if (synchronizer.useGoogleDrive === false) {
callback(); callback();
return; return;
} }
@ -132,7 +128,7 @@ var synchronizer = (function() {
if (change.deleted === true) { if (change.deleted === true) {
fileManager.removeSync(fileSyncIndex); fileManager.removeSync(fileSyncIndex);
updateFileTitles = true; updateFileTitles = true;
showMessage('"' + title + '" has been removed from Google Drive.'); core.showMessage('"' + title + '" has been removed from Google Drive.');
continue; continue;
} }
var content = localStorage[fileIndex + ".content"]; var content = localStorage[fileIndex + ".content"];
@ -143,18 +139,18 @@ var synchronizer = (function() {
if ((titleChanged || contentChanged) && syncUpQueue.indexOf(";" + fileIndex + ";") !== -1) { if ((titleChanged || contentChanged) && syncUpQueue.indexOf(";" + fileIndex + ";") !== -1) {
fileManager.createFile(title + " (backup)", content); fileManager.createFile(title + " (backup)", content);
updateFileTitles = true; updateFileTitles = true;
showMessage('Conflict detected on "' + title + '". A backup has been created locally.'); core.showMessage('Conflict detected on "' + title + '". A backup has been created locally.');
} }
// If file title changed // If file title changed
if(titleChanged) { if(titleChanged) {
localStorage[fileIndex + ".title"] = file.title; localStorage[fileIndex + ".title"] = file.title;
updateFileTitles = true; updateFileTitles = true;
showMessage('"' + title + '" has been renamed to "' + file.title + '" on Google Drive.'); core.showMessage('"' + title + '" has been renamed to "' + file.title + '" on Google Drive.');
} }
// If file content changed // If file content changed
if(contentChanged) { if(contentChanged) {
localStorage[fileIndex + ".content"] = file.content; localStorage[fileIndex + ".content"] = file.content;
showMessage('"' + file.title + '" has been updated from Google Drive.'); core.showMessage('"' + file.title + '" has been updated from Google Drive.');
if(fileIndex == localStorage["file.current"]) { if(fileIndex == localStorage["file.current"]) {
updateFileTitles = false; // Done by next function updateFileTitles = false; // Done by next function
fileManager.selectFile(); fileManager.selectFile();
@ -183,11 +179,11 @@ var synchronizer = (function() {
var lastSync = 0; var lastSync = 0;
synchronizer.sync = function() { synchronizer.sync = function() {
// If sync is already running or timeout is not reached or offline // If sync is already running or timeout is not reached or offline
if (syncRunning || lastSync + SYNC_PERIOD > currentTime || offline) { if (syncRunning || lastSync + SYNC_PERIOD > core.currentTime || core.isOffline) {
return; return;
} }
syncRunning = true; syncRunning = true;
lastSync = currentTime; lastSync = core.currentTime;
onSyncBegin(); onSyncBegin();
syncDown(function() { syncDown(function() {
@ -212,4 +208,4 @@ var synchronizer = (function() {
}; };
return synchronizer; return synchronizer;
})(); });