Import images from Google+

This commit is contained in:
benweet 2013-06-02 01:38:23 +01:00
parent 200a99b8e9
commit 5b2531472c
52 changed files with 586 additions and 174 deletions

View File

@ -6,7 +6,7 @@ index.html
viewer.html
css/main-min.css
js/main-min.js
js/lib/require.js
js/libs/require.js
img/ajax-loader.gif
img/glyphicons-halflings.png
img/glyphicons-halflings-white.png

View File

@ -85,13 +85,29 @@ input[type="color"]:focus,
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(128, 128, 128, 0.6) !important;
}
input:-moz-placeholder,
textarea:-moz-placeholder {
color: #ccc;
}
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #ccc;
}
input::-webkit-input-placeholder,
textarea::-webkit-input-placeholder {
color: #ccc;
}
.help-block {
color: #999999;
font-size: 12px;
line-height: 17px;
}
.error {
.modal textarea.error,
.modal input.error {
border-color: #ff8661 !important;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(255, 134, 97, 0.6) !important;
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(255, 134, 97, 0.6) !important;
@ -273,7 +289,7 @@ hr {
border: none !important;
}
#wmd-preview {
.preview-container {
overflow: auto;
}
@ -433,6 +449,13 @@ div.dropdown-menu i {
background-position: -127px 0;
}
.icon-gplus {
background-image: url("../img/icons.png") !important;
width: 16px;
height: 16px;
background-position: -145px 0;
}
.working-indicator {
background-image: none !important;
width: 43px;
@ -490,6 +513,10 @@ div.dropdown-menu i {
z-index: 1050 !important;
}
.action-import-image-gplus {
float: left;
}
#modal-settings .modal-header {
padding-bottom: 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -24,7 +24,7 @@
var require = { baseUrl : "js", deps : [ "main" + suffix ] };
var viewerMode = false;
</script>
<script src="js/lib/require.js"></script>
<script src="js/libs/require.js"></script>
</head>
<body>
<div id="navbar" class="navbar navbar-fixed-top ui-layout-north">
@ -116,8 +116,8 @@
</div>
</div>
<textarea id="wmd-input" class="ui-layout-center hide"></textarea>
<div class="ui-layout-east hide"></div>
<div class="ui-layout-south hide"></div>
<div class="ui-layout-east preview-container hide"></div>
<div class="ui-layout-south preview-container hide"></div>
<div id="modal-insert-link" class="modal hide">
<div class="modal-header">
@ -134,7 +134,7 @@
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</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>
@ -155,12 +155,49 @@
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</a>
<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>
<div id="modal-import-image" class="modal hide">
<div class="modal-header">
<button type="button" class="close action-close-insert-link"
data-dismiss="modal" aria-hidden="true">&times;</button>
<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>
<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>
</div>
</div>
<div id="modal-remove-file-confirm" class="modal hide">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
@ -269,14 +306,14 @@
"<span class="file-title"></span>" is not synchronized yet.
</blockquote>
<p>Add a synchronized location manually:</p>
<div class="input-prepend input-append">
<div class="input-prepend input-append sync-manual">
<span class="add-on" title="Google Drive"><i
class="icon-gdrive"></i></span><input id="input-sync-manual-gdrive-id"
type="text" class="span5" placeholder="GoogleDriveFileID"></input>
<a class="btn action-sync-manual-gdrive" title="Add location"
data-dismiss="modal"><i class="icon-ok"></i></a>
</div>
<div class="input-prepend input-append">
<div class="input-prepend input-append sync-manual">
<span class="add-on" title="Dropbox"><i class="icon-dropbox"></i></span><input
id="input-sync-manual-dropbox-path" type="text" class="span5"
placeholder="/dropbox/file/path"></input> <a
@ -718,6 +755,9 @@
<dd>
<a target="_blank" href="http://underscorejs.org/">Underscore.js</a>
</dd>
<dd>
<a target="_blank" href="https://github.com/alexanderdickson/waitForImages">waitForImages</a>
</dd>
</dl>
<dl>
<dt>Related projects:</dt>

View File

@ -14,7 +14,7 @@ var GDRIVE_DEFAULT_FILE_TITLE = "New Markdown document";
var CHECK_ONLINE_PERIOD = 120000;
var AJAX_TIMEOUT = 30000;
var ASYNC_TASK_DEFAULT_TIMEOUT = 60000;
var ASYNC_TASK_LONG_TIMEOUT = 120000;
var ASYNC_TASK_LONG_TIMEOUT = 180000;
var SYNC_PERIOD = 180000;
var USER_IDLE_THRESHOLD = 300000;
var TEMPORARY_FILE_INDEX = "file.tempIndex";

View File

@ -6,9 +6,9 @@ define([
"extension-manager",
"storage",
"config",
"lib/bootstrap",
"lib/layout",
"lib/Markdown.Editor"
"libs/bootstrap",
"libs/layout",
"libs/Markdown.Editor"
], function($, _, utils, settings, extensionMgr) {
var core = {};
@ -172,7 +172,7 @@ define([
extensionMgr.onLayoutConfigure(layoutGlobalConfig);
if(settings.layoutOrientation == "horizontal") {
$(".ui-layout-south").remove();
$(".ui-layout-east").addClass("well").prop("id", "wmd-preview");
$(".preview-container").html('<div id="wmd-preview" class="well"></div>');
layout = $('body').layout($.extend(layoutGlobalConfig, {
east__resizable: true,
east__size: .5,
@ -181,7 +181,7 @@ define([
}
else if(settings.layoutOrientation == "vertical") {
$(".ui-layout-east").remove();
$(".ui-layout-south").addClass("well").prop("id", "wmd-preview");
$(".preview-container").html('<div id="wmd-preview" class="well"></div>');
layout = $('body').layout($.extend(layoutGlobalConfig, {
south__resizable: true,
south__size: .5,
@ -199,32 +199,33 @@ define([
};
// Create the PageDown editor
var insertLinkCallback = undefined;
var editor = undefined;
var documentContent = undefined;
core.createEditor = function(onTextChange) {
documentContent = undefined;
$("#wmd-input, .preview-container").scrollTop(0);
if(editor !== undefined) {
// Only restart if the editor is already created
editor.restart();
return;
}
var converter = new Markdown.Converter();
var editor = new Markdown.Editor(converter);
editor = new Markdown.Editor(converter);
// Custom insert link dialog
editor.hooks.set("insertLinkDialog", function(callback) {
insertLinkCallback = callback;
core.insertLinkCallback = callback;
utils.resetModalInputs();
$("#modal-insert-link").modal();
_.defer(function() {
$("#input-insert-link").focus();
});
return true;
});
// Custom insert image dialog
editor.hooks.set("insertImageDialog", function(callback) {
insertLinkCallback = callback;
core.insertLinkCallback = callback;
utils.resetModalInputs();
$("#modal-insert-image").modal();
_.defer(function() {
$("#input-insert-image").focus();
});
return true;
});
var documentContent = undefined;
function checkDocumentChanges() {
var newDocumentContent = $("#wmd-input").val();
if(documentContent !== undefined && documentContent != newDocumentContent) {
@ -232,7 +233,7 @@ define([
}
documentContent = newDocumentContent;
}
var previewWrapper = undefined;
var previewWrapper;
if(settings.lazyRendering === true) {
previewWrapper = function(makePreview) {
var debouncedMakePreview = _.debounce(makePreview, 500);
@ -255,13 +256,9 @@ define([
};
};
}
editor.hooks.chain("onPreviewRefresh", extensionMgr.onAsyncPreview);
extensionMgr.onEditorConfigure(editor);
$("#wmd-input, #wmd-preview").scrollTop(0);
$("#wmd-button-bar").empty();
editor.hooks.chain("onPreviewRefresh", extensionMgr.onAsyncPreview);
editor.run(previewWrapper);
firstChange = false;
// Hide default buttons
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)").addClass("btn").css("left", 0).find("span").hide();
@ -324,21 +321,44 @@ define([
e.stopPropagation();
});
$(".modal").on('shown', function() {
// Focus on the first input when modal opens
_.defer(function(elt) {
elt.find("input:enabled:visible:first").focus();
}, $(this));
}).on('hidden', function() {
// Focus on the editor when modal is gone
if($(this).is(":hidden")) {
_.defer(function() {
$("#wmd-input").focus();
});
}
}).keyup(function(e) {
// Handle enter key in modals
if(e.which == 13 && !$(e.target).is("textarea")) {
$(this).find(".modal-footer a:last").click();
}
});
// Click events on "insert link" and "insert image" dialog buttons
$(".action-insert-link").click(function(e) {
var value = utils.getInputTextValue($("#input-insert-link"), e);
if(value !== undefined) {
insertLinkCallback(value);
core.insertLinkCallback(value);
}
});
$(".action-insert-image").click(function(e) {
var value = utils.getInputTextValue($("#input-insert-image"), e);
if(value !== undefined) {
insertLinkCallback(value);
core.insertLinkCallback(value);
}
});
$(".action-close-insert-link").click(function(e) {
insertLinkCallback(null);
// Hide events on "insert link" and "insert image" dialogs
$("#modal-insert-link, #modal-insert-image").on('hidden', function() {
if(core.insertLinkCallback !== undefined) {
core.insertLinkCallback(null);
}
});
// Settings loading/saving
@ -374,7 +394,7 @@ define([
"line-height": Math.round(settings.editorFontSize * (20 / 14)) + "px"
});
// Manage tab key
// Handle tab key
$("#wmd-input").keydown(function(e) {
if(e.keyCode === 9) {
var value = $(this).val();
@ -394,12 +414,14 @@ define([
$(".tooltip-lazy-rendering").tooltip({
container: '#modal-settings',
placement: 'right',
trigger: 'hover',
title: 'Disable preview rendering while typing in order to offload CPU. Refresh preview after 500 ms of inactivity.'
});
$(".tooltip-default-content").tooltip({
html: true,
container: '#modal-settings',
placement: 'right',
trigger: 'hover',
title: 'Thanks for supporting StackEdit by adding a backlink in your documents!'
});
$(".tooltip-template").tooltip({
@ -426,13 +448,13 @@ define([
].join("")
}).click(function(e) {
$(this).tooltip('show');
$(document).on("click.tooltip-template", function(e) {
$(".tooltip-template").tooltip('hide');
$(document).off("click.tooltip-template");
});
e.stopPropagation();
});
$(document).click(function(e) {
$(".tooltip-template").tooltip('hide');
});
// Reset inputs
$(".action-reset-input").click(function() {
utils.resetModalInputs();
@ -449,6 +471,11 @@ define([
checkOnline();
}
}, 1000);
// Focus on the editor at startup
_.defer(function() {
$("#wmd-input").focus();
});
});
return core;

View File

@ -18,7 +18,8 @@ define([
"extensions/math-jax",
"extensions/email-converter",
"extensions/scroll-link",
"lib/bootstrap"
"libs/bootstrap",
"libs/jquery.waitforimages"
], function($, _, utils, settings) {
var extensionMgr = {};
@ -120,22 +121,22 @@ define([
var onPreviewFinished = createHook("onPreviewFinished");
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
// The number of times we expect tryFinished to be called
var nbAsyncPreviewCallback = onAsyncPreviewCallbackList.length + 1;
extensionMgr["onAsyncPreview"] = function() {
logger.debug("onAsyncPreview");
// Call onPreviewFinished callbacks when all async preview are finished
var counter = 0;
function tryFinished() {
if(counter === onAsyncPreviewCallbackList.length) {
if(++counter === nbAsyncPreviewCallback) {
onPreviewFinished();
}
}
// We assume images are loading in the preview
$("#wmd-preview").waitForImages(tryFinished);
_.each(onAsyncPreviewCallbackList, function(asyncPreviewCallback) {
asyncPreviewCallback(function() {
counter++;
tryFinished();
});
asyncPreviewCallback(tryFinished);
});
tryFinished();
};
var accordionTmpl = [

View File

@ -9,10 +9,10 @@ define([
extensionName: 'Button "Statistics"',
optional: true,
defaultConfig: {
name1: "Words",
value1: "\\S+",
name2: "Characters",
value2: "\\S",
name1: "Characters",
value1: "\\S",
name2: "Words",
value2: "\\S+",
name3: "Paragraphs",
value3: "\\S.*",
},

View File

@ -88,14 +88,24 @@ define([
}
documentSelector.onReady = function() {
$("#file-selector").click(function() {
_.defer(function() {
$("#wmd-input").focus();
});
});
$(".action-open-file").click(function() {
filterFileSelector();
_.defer(function() {
$("#file-search").val("").focus();
});
});
$("#file-search").keyup(function() {
filterFileSelector($(this).val());
$("#file-search").keyup(function(e) {
if(e.which == 13 || e.which == 27) {
$(this).parent().click();
}
else {
filterFileSelector($(this).val());
}
}).click(function(event) {
event.stopPropagation();
});

View File

@ -59,6 +59,19 @@ define([
manageSynchronization.onSyncExportSuccess = refreshDialog;
manageSynchronization.onSyncRemoved = refreshDialog;
manageSynchronization.onReady = function() {
// Handle enter key in the sync manual inputs
$(".sync-manual").each(function() {
var elt = $(this);
elt.find("input").keyup(function(e) {
if(e.which == 13) {
elt.find("a").click();
e.stopPropagation();
}
});
});
};
return manageSynchronization;
});

View File

@ -1,6 +1,6 @@
define([
"utils",
"lib/Markdown.Extra"
"libs/Markdown.Extra"
], function(utils) {
var markdownExtra = {

View File

@ -1,5 +1,5 @@
define([
"lib/MathJax"
"libs/MathJax"
], function() {
var mathJax = {
@ -175,7 +175,9 @@ define([
// if we haven't done that already.
//
function UpdateMJ() {
if (!pending && ready) {
// sometimes ready flag is not set already (on opera basically)
//if (!pending && ready) {
if (!pending) {
pending = true;
HUB.Cancel();
HUB.Queue(RestartMJ);
@ -194,10 +196,10 @@ define([
var converterObject = editorObject.getConverter();
converterObject.hooks.chain("preConversion", removeMath);
converterObject.hooks.chain("postConversion", replaceMath);
editorObject.hooks.chain("onPreviewRefresh", UpdateMJ);
};
mathJax.onAsyncPreview = function(callback) {
afterRefreshCallback = callback;
UpdateMJ();
};
//

View File

@ -1,7 +1,7 @@
define([
"jquery",
"underscore",
"lib/css_browser_selector"
"libs/css_browser_selector"
], function($, _) {
var scrollLink = {
@ -10,8 +10,8 @@ define([
optional: true,
settingsBloc: [
'<p>Binds together editor and preview scrollbars.</p>',
'<blockquote class="muted"><b>NOTE:</b> ',
' The mapping between Markdown and HTML is based on the position of the title elements (h1, h2, ...) in the page. ',
'<blockquote class="muted"><b>NOTE:</b>',
' The mapping between Markdown and HTML is based on the position of the title elements (h1, h2, ...) in the page.',
' Therefore, if your document does not contain any title, the mapping will be linear and consequently less accurate.',
'</bloquote>'
].join("")
@ -70,12 +70,12 @@ define([
addMdSection(text.substring(offset, text.length - 2));
// Try to find corresponding sections in the preview
var previewElt = $("#wmd-preview");
var previewElt = $(".preview-container");
htmlSectionList = [];
var htmlSectionOffset = 0;
var previewScrollTop = previewElt.scrollTop();
// Each title element is a section separator
previewElt.children("h1,h2,h3,h4,h5,h6").each(function() {
$("#wmd-preview").children("h1,h2,h3,h4,h5,h6").each(function() {
// Consider div scroll position and header element top margin
var newSectionOffset = $(this).position().top + previewScrollTop + pxToFloat($(this).css('margin-top'));
htmlSectionList.push({
@ -95,7 +95,6 @@ define([
// apply Scroll Link
lastEditorScrollTop = -10;
skipScrollLink = false;
isScrollPreview = false;
runScrollLink();
}, 500);
@ -103,15 +102,14 @@ define([
// -10 to be sure the gap is > 9
var lastEditorScrollTop = -10;
var lastPreviewScrollTop = -10;
var skipScrollLink = false;
var isScrollPreview = false;
var runScrollLink = _.debounce(function() {
if(skipScrollLink === true || mdSectionList.length === 0 || mdSectionList.length !== htmlSectionList.length) {
if(mdSectionList.length === 0 || mdSectionList.length !== htmlSectionList.length) {
return;
}
var editorElt = $("#wmd-input");
var editorScrollTop = editorElt.scrollTop();
var previewElt = $("#wmd-preview");
var previewElt = $(".preview-container");
var previewScrollTop = previewElt.scrollTop();
function animate(srcScrollTop, srcSectionList, destElt, destSectionList, lastDestScrollTop, callback) {
// Find the section corresponding to the offset
@ -163,7 +161,7 @@ define([
};
scrollLink.onLayoutCreated = function() {
$("#wmd-preview").scroll(function() {
$(".preview-container").scroll(function() {
isScrollPreview = true;
runScrollLink();
});
@ -174,27 +172,21 @@ define([
};
scrollLink.onEditorConfigure = function(editor) {
skipScrollLink = true;
lastPreviewScrollTop = 0;
editor.hooks.chain("onPreviewRefresh", function() {
skipScrollLink = true;
editor.getConverter().hooks.chain("postConversion", function(text) {
// To avoid losing scrolling position before elements are fully loaded
$("#wmd-preview").height($("#wmd-preview").height());
return text;
});
};
scrollLink.onPreviewFinished = function() {
// MathJax may have change the scrolling position. Restore it.
if(lastPreviewScrollTop >= 0) {
$("#wmd-preview").scrollTop(lastPreviewScrollTop);
}
// Now set the correct height
$("#wmd-preview").height("auto");
_.defer(function() {
// Modify scroll position of the preview not the editor
lastEditorScrollTop = -10;
buildSections();
// Preview may change if images are loading
$("#wmd-preview img").load(function() {
lastEditorScrollTop = -10;
buildSections();
});
});
};

View File

@ -6,7 +6,7 @@ define([
"settings",
"extension-manager",
"file-system",
"lib/text!../WELCOME.md"
"libs/text!../WELCOME.md"
], function($, _, core, utils, settings, extensionMgr, fileSystem, welcomeContent) {
var fileMgr = {};

View File

@ -117,11 +117,11 @@ define([
var headers = {
'Content-Type': 'multipart/mixed; boundary="' + boundary + '"',
};
if(etag !== undefined) {
// Sometimes we have error 412 from Google even with the correct
// etag
// headers["If-Match"] = etag;
}
// Sometimes we have error 412 from Google even with the correct
// etag
// if(etag !== undefined) {
// headers["If-Match"] = etag;
// }
var base64Data = utils.encodeBase64(content);
var multipartRequestBody = delimiter + 'Content-Type: application/json\r\n\r\n' + JSON.stringify(metadata) + delimiter + 'Content-Type: ' + contentType + '\r\n' + 'Content-Transfer-Encoding: base64\r\n' + '\r\n' + base64Data + close_delim;
@ -402,8 +402,8 @@ define([
});
}
googleHelper.picker = function(callback) {
var ids = [];
googleHelper.picker = function(callback, isImagePicker) {
var docs = [];
var picker = undefined;
function hidePicker() {
if(picker !== undefined) {
@ -415,24 +415,23 @@ define([
connect(task);
loadPicker(task);
task.onRun(function() {
var view = new google.picker.View(google.picker.ViewId.DOCS);
view.setMimeTypes("text/x-markdown,text/plain,application/octet-stream");
var pickerBuilder = new google.picker.PickerBuilder();
pickerBuilder.enableFeature(google.picker.Feature.NAV_HIDDEN);
pickerBuilder.enableFeature(google.picker.Feature.MULTISELECT_ENABLED);
pickerBuilder.setAppId(GOOGLE_DRIVE_APP_ID);
var token = gapi.auth.getToken();
if(token) {
pickerBuilder.setOAuthToken(token.access_token);
if(!isImagePicker) {
var view = new google.picker.View(google.picker.ViewId.DOCS);
view.setMimeTypes("text/x-markdown,text/plain,application/octet-stream");
pickerBuilder.enableFeature(google.picker.Feature.NAV_HIDDEN);
pickerBuilder.enableFeature(google.picker.Feature.MULTISELECT_ENABLED);
pickerBuilder.addView(view);
}
else {
pickerBuilder.addView(google.picker.ViewId.PHOTOS);
pickerBuilder.addView(google.picker.ViewId.PHOTO_UPLOAD);
}
pickerBuilder.addView(view);
pickerBuilder.addView(new google.picker.DocsUploadView());
pickerBuilder.setCallback(function(data) {
if(data.action == google.picker.Action.PICKED || data.action == google.picker.Action.CANCEL) {
if(data.action == google.picker.Action.PICKED) {
for ( var i = 0; i < data.docs.length; i++) {
ids.push(data.docs[i].id);
}
docs = data.docs;
}
hidePicker();
task.chain();
@ -446,7 +445,7 @@ define([
picker.setVisible(true);
});
task.onSuccess(function() {
callback(undefined, ids);
callback(undefined, docs);
});
task.onError(function(error) {
hidePicker();

View File

@ -118,14 +118,16 @@
var that = this,
panels;
this.run = function (previewWrapper) { // benweet
// benweet
var undoManager;
this.run = function (previewWrapper) {
if (panels)
return; // already initialized
panels = new PanelCollection(idPostfix);
var commandManager = new CommandManager(hooks, getString);
var previewManager = new PreviewManager(markdownConverter, panels, function () { hooks.onPreviewRefresh(); }, previewWrapper); // benweet
var undoManager, uiManager;
var uiManager;
if (!/\?noundo/.test(doc.location.href)) {
undoManager = new UndoManager(function () {
@ -147,6 +149,12 @@
forceRefresh();
};
// benweet
this.restart = function() {
undoManager.reinit();
that.refreshPreview();
};
}
@ -675,6 +683,18 @@
refreshState(true);
saveState();
};
// benweet
this.reinit = function() {
undoStack = [];
stackPtr = 0;
mode = "none";
lastState = undefined;
timer = undefined;
inputStateObj = undefined;
refreshState();
saveState();
};
init();
}

View File

@ -0,0 +1,142 @@
/*
* waitForImages 1.4.2
* -------------------
* Provides a callback when all images have loaded in your given selector.
* https://github.com/alexanderdickson/waitForImages
*
* Copyright (c) 2013 Alex Dickson
* Licensed under the MIT license.
*/
(function ($) {
// Namespace all events.
var eventNamespace = 'waitForImages';
// CSS properties which contain references to images.
$.waitForImages = {
hasImageProperties: ['backgroundImage', 'listStyleImage', 'borderImage', 'borderCornerImage']
};
// Custom selector to find `img` elements that have a valid `src` attribute and have not already loaded.
$.expr[':'].uncached = function (obj) {
// Ensure we are dealing with an `img` element with a valid `src` attribute.
if (!$(obj).is('img[src!=""]')) {
return false;
}
// Firefox's `complete` property will always be `true` even if the image has not been downloaded.
// Doing it this way works in Firefox.
var img = new Image();
img.src = obj.src;
return !img.complete;
};
$.fn.waitForImages = function (finishedCallback, eachCallback, waitForAll) {
var allImgsLength = 0;
var allImgsLoaded = 0;
// Handle options object.
if ($.isPlainObject(arguments[0])) {
waitForAll = arguments[0].waitForAll;
eachCallback = arguments[0].each;
// This must be last as arguments[0]
// is aliased with finishedCallback.
finishedCallback = arguments[0].finished;
}
// Handle missing callbacks.
finishedCallback = finishedCallback || $.noop;
eachCallback = eachCallback || $.noop;
// Convert waitForAll to Boolean
waitForAll = !! waitForAll;
// Ensure callbacks are functions.
if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
throw new TypeError('An invalid callback was supplied.');
}
return this.each(function () {
// Build a list of all imgs, dependent on what images will be considered.
var obj = $(this);
var allImgs = [];
// CSS properties which may contain an image.
var hasImgProperties = $.waitForImages.hasImageProperties || [];
// To match `url()` references.
// Spec: http://www.w3.org/TR/CSS2/syndata.html#value-def-uri
var matchUrl = /url\(\s*(['"]?)(.*?)\1\s*\)/g;
if (waitForAll) {
// Get all elements (including the original), as any one of them could have a background image.
obj.find('*').andSelf().each(function () {
var element = $(this);
// If an `img` element, add it. But keep iterating in case it has a background image too.
if (element.is('img:uncached')) {
allImgs.push({
src: element.attr('src'),
element: element[0]
});
}
$.each(hasImgProperties, function (i, property) {
var propertyValue = element.css(property);
var match;
// If it doesn't contain this property, skip.
if (!propertyValue) {
return true;
}
// Get all url() of this element.
while (match = matchUrl.exec(propertyValue)) {
allImgs.push({
src: match[2],
element: element[0]
});
}
});
});
} else {
// For images only, the task is simpler.
obj.find('img:uncached')
.each(function () {
allImgs.push({
src: this.src,
element: this
});
});
}
allImgsLength = allImgs.length;
allImgsLoaded = 0;
// If no images found, don't bother.
if (allImgsLength === 0) {
finishedCallback.call(obj[0]);
}
$.each(allImgs, function (i, img) {
var image = new Image();
// Handle the image loading and error with the same callback.
$(image).bind('load.' + eventNamespace + ' error.' + eventNamespace, function (event) {
allImgsLoaded++;
// If an error occurred with loading the image, set the third argument accordingly.
eachCallback.call(img.element, allImgsLoaded, allImgsLength, event.type == 'load');
if (allImgsLoaded == allImgsLength) {
finishedCallback.call(obj[0]);
return false;
}
});
image.src = img.src;
});
});
};
}(jQuery));

View File

@ -1,63 +1,86 @@
// RequireJS configuration
requirejs.config({
waitSeconds: 0,
paths: {
"jquery": "lib/jquery",
"underscore": "lib/underscore",
"jgrowl": "lib/jgrowl",
"lib/MathJax": '../lib/MathJax/MathJax.js?config=TeX-AMS_HTML'
waitSeconds: 0,
paths: {
"jquery": "libs/jquery",
"underscore": "libs/underscore",
"jgrowl": "libs/jgrowl",
"libs/MathJax": '../lib/MathJax/MathJax.js?config=TeX-AMS_HTML'
},
shim: {
'underscore': {
'underscore': {
exports: '_'
},
'jgrowl': {
deps: ['jquery'],
deps: [
'jquery'
],
exports: 'jQuery.jGrowl'
},
'lib/jquery-ui': ['jquery'],
'lib/bootstrap': ['jquery'],
'lib/layout': ['lib/jquery-ui'],
'lib/Markdown.Extra': ['lib/Markdown.Converter', 'lib/prettify'],
'lib/Markdown.Editor': ['lib/Markdown.Converter']
'libs/jquery-ui': [
'jquery'
],
'libs/bootstrap': [
'jquery'
],
'libs/jquery.waitforimages': [
'jquery'
],
'libs/layout': [
'libs/jquery-ui'
],
'libs/Markdown.Extra': [
'libs/Markdown.Converter',
'libs/prettify'
],
'libs/Markdown.Editor': [
'libs/Markdown.Converter'
]
}
});
// Defines the logger object
var logger = {
debug: function() {},
log: function() {},
info: function() {},
warn: function() {},
error: function() {}
debug: function() {
},
log: function() {
},
info: function() {
},
warn: function() {
},
error: function() {
}
};
// Use http://.../?console to print logs in the console
if (location.search.match(/(\?|&)console/)) {
logger = console;
// We can run StackEdit with http://.../?console to print logs in the console
if(location.search.match(/(\?|&)console/)) {
logger = console;
}
// RequireJS entry point. By requiring synchronizer and publisher, we are actually loading all the modules
// RequireJS entry point. By requiring synchronizer, publisher and
// media-importer, we are actually loading all the modules
require([
"jquery",
"core",
"synchronizer",
"publisher"
"jquery",
"core",
"synchronizer",
"publisher",
"media-importer"
], function($, core) {
$(function() {
// If browser has detected a new application cache.
if (window.applicationCache) {
window.applicationCache.addEventListener('updateready', function(e) {
if(window.applicationCache.status === window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
window.location.reload();
}
}, false);
}
// Here, all the modules are loaded and the DOM is ready
core.setReady();
});
$(function() {
// If browser has detected a new application cache.
if(window.applicationCache) {
window.applicationCache.addEventListener('updateready', function(e) {
if(window.applicationCache.status === window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
window.location.reload();
}
}, false);
}
// Here, all the modules are loaded and the DOM is ready
core.setReady();
});
});

38
js/media-importer.js Normal file
View File

@ -0,0 +1,38 @@
define([
"jquery",
"underscore",
"core",
"providers/gplus-provider"
], function($, _, core) {
var mediaImporter = {};
// Create a map with providerId: providerModule
var providerMap = _.chain(arguments).map(function(argument) {
return argument && argument.providerId && [
argument.providerId,
argument
];
}).compact().object().value();
core.onReady(function() {
_.each(providerMap, function(provider) {
// Import image action links (if any)
$(".action-import-image-" + provider.providerId).click(function() {
// Take the insertLinkCallback from core module
var insertLinkCallback = core.insertLinkCallback;
// Unset it to be sure core module will not call it
core.insertLinkCallback = undefined;
provider.importImage(function(error, imageLink) {
if(error) {
insertLinkCallback(null);
return;
}
insertLinkCallback(imageLink || null);
});
});
});
});
return mediaImporter;
});

View File

@ -1,7 +1,7 @@
define([
"underscore",
"utils",
"google-helper"
"helpers/google-helper"
], function(_, utils, googleHelper) {
var PROVIDER_BLOGGER = "blogger";

View File

@ -3,7 +3,7 @@ define([
"utils",
"extension-manager",
"file-manager",
"dropbox-helper"
"helpers/dropbox-helper"
], function(_, utils, extensionMgr, fileMgr, dropboxHelper) {
var PROVIDER_DROPBOX = "dropbox";

View File

@ -5,7 +5,7 @@ define([
"settings",
"extension-manager",
"file-manager",
"google-helper"
"helpers/google-helper"
], function(_, core, utils, settings, extensionMgr, fileMgr, googleHelper) {
var PROVIDER_GDRIVE = "gdrive";
@ -62,19 +62,19 @@ define([
;
gdriveProvider.importFiles = function() {
googleHelper.picker(function(error, ids) {
googleHelper.picker(function(error, docs) {
if(error || ids.length === 0) {
return;
}
var importIds = [];
_.each(ids, function(id) {
var syncIndex = createSyncIndex(id);
_.each(docs, function(doc) {
var syncIndex = createSyncIndex(doc.id);
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
extensionMgr.onError('"' + fileDesc.title + '" was already imported.');
return;
}
importIds.push(id);
importIds.push(doc.id);
});
importFilesFromIds(importIds);
});

View File

@ -1,6 +1,6 @@
define([
"utils",
"github-helper"
"helpers/github-helper"
], function(utils, githubHelper) {
var PROVIDER_GIST = "gist";

View File

@ -1,7 +1,7 @@
define([
"utils",
"settings",
"github-helper"
"helpers/github-helper"
], function(utils, settings, githubHelper) {
var PROVIDER_GITHUB = "github";

View File

@ -0,0 +1,79 @@
define([
"underscore",
"core",
"utils",
"extension-manager",
"helpers/google-helper"
], function(_, core, utils, extensionMgr, googleHelper) {
var PROVIDER_GPLUS = "gplus";
var gplusProvider = {
providerId: PROVIDER_GPLUS,
providerName: "Google+"
};
function getThumbnailUrl(doc, size) {
var result = undefined;
_.find(doc.thumbnails, function(thumbnail) {
var found = false;
thumbnail.url.replace(/(.*\/s)\d.*?(\/[^\/]+)/, function(match, sub1, sub2) {
result = sub1 + size + sub2;
found = true;
});
return found;
});
return result;
}
var importImageCallback = undefined;
var imageDoc = undefined;
gplusProvider.importImage = function(callback) {
importImageCallback = callback;
googleHelper.picker(function(error, docs) {
if(error || docs.length === 0) {
callback(error);
return;
}
imageDoc = docs[0];
if(!imageDoc.thumbnails) {
extensionMgr.onError("Image " + imageDoc.title + " is not accessible.");
callback(true);
return;
}
utils.resetModalInputs();
$("#modal-import-image img").prop("src", getThumbnailUrl(imageDoc, 128));
utils.setInputValue("#input-import-image-title", imageDoc.name);
// Load preferences
var serializedPreferences = localStorage[PROVIDER_GPLUS + ".importImagePreferences"];
if(serializedPreferences) {
var importImagePreferences = JSON.parse(serializedPreferences);
utils.setInputValue("#input-import-image-size", importImagePreferences.size);
}
$("#modal-import-image").modal();
}, true);
};
core.onReady(function() {
$(".action-import-image").click(function(e) {
var size = utils.getInputIntValue("#input-import-image-size", undefined, 0) || 0;
var title = utils.getInputTextValue("#input-import-image-title");
var image = getThumbnailUrl(imageDoc, size);
if(title) {
image += ' \"' + title + '"';
}
importImageCallback(undefined, image);
// Store import preferences for next time
var importImagePreferences = {};
if(size) {
importImagePreferences.size = size;
}
localStorage[PROVIDER_GPLUS + ".importImagePreferences"] = JSON.stringify(importImagePreferences);
});
});
return gplusProvider;
});

View File

@ -1,6 +1,6 @@
define([
"utils",
"ssh-helper"
"helpers/ssh-helper"
], function(utils, sshHelper) {
var PROVIDER_SSH = "ssh";

View File

@ -1,6 +1,6 @@
define([
"utils",
"tumblr-helper"
"helpers/tumblr-helper"
], function(utils, tumblrHelper) {
var PROVIDER_TUMBLR = "tumblr";

View File

@ -1,6 +1,6 @@
define([
"utils",
"wordpress-helper"
"helpers/wordpress-helper"
], function(utils, wordpressHelper) {
var PROVIDER_WORDPRESS = "wordpress";

View File

@ -8,14 +8,14 @@ define([
"file-system",
"file-manager",
"sharing",
"blogger-provider",
"dropbox-provider",
"gist-provider",
"github-provider",
"gdrive-provider",
"ssh-provider",
"tumblr-provider",
"wordpress-provider"
"providers/blogger-provider",
"providers/dropbox-provider",
"providers/gist-provider",
"providers/github-provider",
"providers/gdrive-provider",
"providers/ssh-provider",
"providers/tumblr-provider",
"providers/wordpress-provider"
], function($, _, core, utils, settings, extensionMgr, fileSystem, fileMgr, sharing) {
var publisher = {};

View File

@ -6,8 +6,8 @@ define([
"extension-manager",
"file-manager",
"async-runner",
"download-provider",
"gist-provider"
"providers/download-provider",
"providers/gist-provider"
], function($, _, core, utils, extensionMgr, fileMgr, asyncRunner) {
var sharing = {};

View File

@ -6,8 +6,8 @@ define([
"extension-manager",
"file-system",
"file-manager",
"dropbox-provider",
"gdrive-provider"
"providers/dropbox-provider",
"providers/gdrive-provider"
], function($, _, core, utils, extensionMgr, fileSystem, fileMgr) {
var synchronizer = {};
@ -142,7 +142,6 @@ define([
providerList = _.values(providerMap);
providerDown(callback);
}
;
// Main entry point for synchronization
var syncRunning = false;

View File

@ -1,7 +1,7 @@
define([
"jquery",
"underscore",
"lib/FileSaver"
"libs/FileSaver"
], function($, _) {
var utils = {};

View File

@ -24,7 +24,7 @@
var require = { baseUrl : "js", deps : [ "main" + suffix ] };
var viewerMode = true;
</script>
<script src="js/lib/require.js"></script>
<script src="js/libs/require.js"></script>
</head>
<body class="viewer">
<div id="navbar" class="navbar navbar-fixed-top ui-layout-north">