Support for offline

This commit is contained in:
benweet 2013-03-30 11:56:17 +00:00
parent b98ff39296
commit ef6189f9cb
7 changed files with 835 additions and 115 deletions

131
css/jgrowl.css Normal file
View File

@ -0,0 +1,131 @@
div.jGrowl {
z-index: 1000;
color: #fff;
font-size: 12px;
}
/** Special IE6 Style Positioning **/
div.ie6 {
position: absolute;
}
div.ie6.top-right {
right: auto;
bottom: auto;
left: expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
}
div.ie6.top-left {
left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
}
div.ie6.bottom-right {
left: expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
top: expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
}
div.ie6.bottom-left {
left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
top: expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
}
div.ie6.center {
left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
width: 100%;
}
/** Normal Style Positions **/
div.jGrowl {
position: absolute;
}
body > div.jGrowl {
position: fixed;
}
div.jGrowl.top-left {
left: 0px;
top: 0px;
}
div.jGrowl.top-right {
right: 0px;
top: 0px;
}
div.jGrowl.bottom-left {
left: 0px;
bottom: 0px;
}
div.jGrowl.bottom-right {
right: 0px;
bottom: 0px;
}
div.jGrowl.center {
top: 0px;
width: 50%;
left: 25%;
}
/** Cross Browser Styling **/
div.center div.jGrowl-notification, div.center div.jGrowl-closer {
margin-left: auto;
margin-right: auto;
}
div.jGrowl div.jGrowl-notification, div.jGrowl div.jGrowl-closer {
background-color: #777;
zoom: 1;
width: 235px;
padding: 10px;
margin-top: 5px;
margin-bottom: 5px;
text-align: left;
display: none;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
div.jGrowl div.jGrowl-notification {
min-height: 40px;
}
div.jGrowl div.jGrowl-notification,
div.jGrowl div.jGrowl-closer {
margin: 10px;
}
div.jGrowl div.jGrowl-notification div.jGrowl-header {
font-weight: bold;
font-size: .85em;
}
div.jGrowl div.jGrowl-notification div.jGrowl-close {
z-index: 99;
float: right;
font-weight: bold;
font-size: 1em;
cursor: pointer;
}
div.jGrowl div.jGrowl-closer {
padding-top: 4px;
padding-bottom: 4px;
cursor: pointer;
font-size: .9em;
font-weight: bold;
text-align: center;
}
/** Hide jGrowl when printing **/
@media print {
div.jGrowl {
display: none;
}
}

View File

@ -19,7 +19,7 @@ div, span, a, ul, li, textarea, input {
text-shadow: none !important; text-shadow: none !important;
} }
.btn, .navbar-inner, .well, textarea, input { .btn, .navbar-inner, .ui-layout-east, .ui-layout-south, textarea, input {
border: none !important; border: none !important;
} }
@ -51,11 +51,11 @@ div, span, a, ul, li, textarea, input {
.dropdown-menu > .active > a, .dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus { .dropdown-menu > .active > a:focus {
background-color: #999; background-color: #888;
} }
.btn-primary { .btn-primary {
background-color: #888; background-color: #777;
} }
.btn-primary:hover, .btn-primary:hover,
@ -64,7 +64,7 @@ div, span, a, ul, li, textarea, input {
.btn-primary.active, .btn-primary.active,
.btn-primary.disabled, .btn-primary.disabled,
.btn-primary[disabled] { .btn-primary[disabled] {
background-color: #999; background-color: #888;
} }
.btn-group { .btn-group {
@ -232,4 +232,14 @@ hr {
border-top: 5px solid transparent; border-top: 5px solid transparent;
border-right: 5px solid #525252; border-right: 5px solid #525252;
border-left: 0; border-left: 0;
}
#message-container {
position: absolute;
right: 20px;
bottom: 20px;
width: 200px;
background-color: #eee;
margin: 0;
font-weight: bold;
} }

View File

@ -8,23 +8,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="css/bootstrap.css" rel="stylesheet" media="screen"> <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/jquery.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-ui.custom.js"></script>
<script type="text/javascript" src="js/jquery.layout.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/bootstrap.js"></script>
<script type="text/javascript" src="js/Markdown.Converter.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.Sanitizer.js"></script>
<script type="text/javascript" src="js/Markdown.Editor.js"></script> <script type="text/javascript" src="js/Markdown.Editor.js"></script>
<script type="text/javascript" src="js/gdrive.js"></script>
<script type="text/javascript" src="js/main.js"></script> <script type="text/javascript" src="js/main.js"></script>
<script type="text/javascript"> <script type="text/javascript" src="js/async-runner.js"></script>
function handleClientLoad() { <script type="text/javascript" src="js/gdrive.js"></script>
window.setTimeout(gdrive.init, 1); <script type="text/javascript" src="js/custo.js"></script>
}
</script>
<script type="text/javascript"
src="https://apis.google.com/js/client.js?onload=handleClientLoad"></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;
@ -92,6 +89,9 @@
<textarea id="wmd-input" class="ui-layout-center"></textarea> <textarea id="wmd-input" class="ui-layout-center"></textarea>
<div class="ui-layout-east"></div> <div class="ui-layout-east"></div>
<div class="ui-layout-south"></div> <div class="ui-layout-south"></div>
<div id="message-container" class="well 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">

70
js/async-runner.js Normal file
View File

@ -0,0 +1,70 @@
/**
* Used to run any asynchronous tasks sequentially (ajax mainly)
* An asynchronous task must be created with:
* - a run() function (required) that may call success() or error()
* - a onSuccess() function (optional)
* - a onError() function (optional)
*/
var asyncTaskRunner = (function() {
var asyncTaskRunner = {};
var asyncTaskQueue = [];
var currentTask = undefined;
var currentTaskStartTime = new Date().getTime();
// Run the next task in the queue if any and no other is running
asyncTaskRunner.runTask = function() {
// If there is a task currently running
if(currentTask !== undefined) {
// If the current task takes too long
if(currentTaskStartTime + 30000 < currentTime) {
currentTask.error();
}
return;
}
// If no task in the queue
if(asyncTaskQueue.length === 0) {
return;
}
currentTask = asyncTaskQueue.shift();
currentTaskStartTime = currentTime;
showWorkingIndicator(true);
// Set task attributes and functions
currentTask.finished = false;
currentTask.finish = function() {
this.finished = true;
showWorkingIndicator(false);
currentTask = undefined;
asyncTaskRunner.runTask();
};
currentTask.success = function() {
if(this.finished === true) {
return;
}
if(this.onSuccess) {
this.onSuccess();
}
this.finish();
};
currentTask.error = function() {
if(this.finished === true) {
return;
}
if(this.onError) {
this.onError();
}
this.finish();
};
currentTask.run();
};
// Add a task in the queue
asyncTaskRunner.addTask = function(asyncTask) {
asyncTaskQueue.push(asyncTask);
};
return asyncTaskRunner;
})();

View File

@ -1,105 +1,196 @@
var gdrive = (function() { var CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googleusercontent.com';
var SCOPES = [ 'https://www.googleapis.com/auth/drive.install',
'https://www.googleapis.com/auth/drive.file' ];
var CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googleusercontent.com'; var gdriveDelayedFunction = undefined;
var SCOPES = [ 'https://www.googleapis.com/auth/drive.install', function runGdriveDelayedFunction() {
'https://www.googleapis.com/auth/drive.file' ]; if(gdriveDelayedFunction !== undefined) {
gdriveDelayedFunction();
}
}
var driveEnabled = false; var gdrive = (function($) {
var connected = false;
var authenticated = false;
var doNothing = function() {};
var gdrive = {}; var gdrive = {};
function askAuth(immediate, callback) { // Try to connect Gdrive by downloading client.js
if (!driveEnabled) { function connect(callback) {
gapi.auth.authorize({ 'client_id' : CLIENT_ID, 'scope' : SCOPES, callback = callback || doNothing;
'immediate' : immediate }, function(authResult) { var asyncTask = {};
if (authResult && !authResult.error) { asyncTask.run = function() {
// $("#drive-link").hide(); if(connected === true) {
gapi.client.load('drive', 'v2', function() { asyncTask.success();
driveEnabled = true; return;
callback(); }
}); gdriveDelayedFunction = function() {
} asyncTask.success();
};
$.ajax({
url: "https://apis.google.com/js/client.js?onload=runGdriveDelayedFunction",
dataType: "script",
timeout: 5000
})
.fail(function() {
asyncTask.error();
}); });
} else { };
asyncTask.onSuccess = function() {
gdriveDelayedFunction = undefined;
connected = true;
callback(); callback();
};
asyncTask.onError = function() {
gdriveDelayedFunction = undefined;
onOffline();
callback();
};
asyncTaskRunner.addTask(asyncTask);
}
// Try to authenticate with Oauth
function authenticate(callback, immediate) {
callback = callback || doNothing;
if(immediate === undefined) {
immediate = true;
} }
connect(function() {
if(connected === false) {
callback();
return;
}
var asyncTask = {};
asyncTask.run = function() {
if(authenticated === true) {
asyncTask.success();
return;
}
gapi.auth.authorize({ 'client_id' : CLIENT_ID, 'scope' : SCOPES,
'immediate' : immediate }, function(authResult) {
if (!authResult || authResult.error) {
asyncTask.error();
return;
}
gapi.client.load('drive', 'v2', function() {
authenticated = true;
asyncTask.success();
});
});
};
asyncTask.onSuccess = function() {
callback();
};
asyncTask.onError = function() {
// If immediate did not work retry without immediate flag
if(connected === true && immediate === true) {
authenticate(callback, false);
return;
}
callback();
};
asyncTaskRunner.addTask(asyncTask);
});
} }
function uploadFile(fileId, parentId, title, content, callback) { function upload(fileId, parentId, title, content, callback) {
setWorkingIndicator(FLAG_GDRIVE_UPLOAD); callback = callback || doNothing;
var boundary = '-------314159265358979323846'; authenticate(function() {
var delimiter = "\r\n--" + boundary + "\r\n"; if(connected === false) {
var close_delim = "\r\n--" + boundary + "--"; callback();
return;
var contentType = 'text/x-markdown';
var metadata = { title : title, mimeType : contentType };
if (parentId) {
// Specify the directory
metadata.parents = [ { kind : 'drive#fileLink', id : parentId } ];
}
var path = '/upload/drive/v2/files';
var method = 'POST';
if (fileId) {
// If it's an update
path += "/" + fileId;
method = 'PUT';
}
var base64Data = btoa(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;
var request = gapi.client.request({
'path' : path,
'method' : method,
'params' : { 'uploadType' : 'multipart', },
'headers' : { 'Content-Type' : 'multipart/mixed; boundary="'
+ boundary + '"', }, 'body' : multipartRequestBody, });
request.execute(function(file) {
unsetWorkingIndicator(FLAG_GDRIVE_UPLOAD);
var fileSyncIndex = undefined;
if(file.id) {
fileSyncIndex = SYNC_PROVIDER_GDRIVE + file.id;
localStorage[fileSyncIndex + ".etag"] = file.etag;
} }
if (callback) {
callback(fileSyncIndex); var fileIndex = undefined;
} var asyncTask = {};
}); asyncTask.run = function() {
var boundary = '-------314159265358979323846';
var delimiter = "\r\n--" + boundary + "\r\n";
var close_delim = "\r\n--" + boundary + "--";
var contentType = 'text/x-markdown';
var metadata = { title : title, mimeType : contentType };
if (parentId) {
// Specify the directory
metadata.parents = [ { kind : 'drive#fileLink', id : parentId } ];
}
var path = '/upload/drive/v2/files';
var method = 'POST';
if (fileId) {
// If it's an update
path += "/" + fileId;
method = 'PUT';
}
var base64Data = btoa(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;
var request = gapi.client.request({
'path' : path,
'method' : method,
'params' : { 'uploadType' : 'multipart', },
'headers' : { 'Content-Type' : 'multipart/mixed; boundary="'
+ boundary + '"', }, 'body' : multipartRequestBody, });
request.execute(function(file) {
if(file.id) {
// Upload success
fileIndex = SYNC_PROVIDER_GDRIVE + file.id;
localStorage[fileIndex + ".etag"] = file.etag;
asyncTask.success();
return
}
// Upload failed, try to analyse
if(file.error.code === 401) {
showError("Google Drive is not accessible.");
}
else {
connected = false;
authenticated = false;
onOffline();
}
asyncTask.error();
});
};
asyncTask.onSuccess = function() {
callback(fileIndex);
};
asyncTask.onError = function() {
callback();
};
asyncTaskRunner.addTask(asyncTask);
});
} }
; ;
gdrive.init = function() { gdrive.init = function() {
askAuth(true, function() { try {
try { var state = JSON.parse(decodeURI((/state=(.+?)(&|$)/
var state = JSON.parse(decodeURI((/state=(.+?)(&|$)/ .exec(location.search) || [ , null ])[1]));
.exec(location.search) || [ , null ])[1])); if (state.action == 'create') {
if (state.action == 'create') { upload(undefined, state.folderId,
uploadFile(undefined, state.folderId, fileManager.currentFile, fileManager.content, function(
fileManager.currentFile, fileManager.content, function( fileIndex) {
file) { console.log(fileIndex);
console.log(file); });
});
}
} catch (e) {
} }
}); } catch (e) {
}
}; };
gdrive.createFile = function(title, content, callback) { gdrive.createFile = function(title, content, callback) {
askAuth(false, function() { upload(undefined, undefined, title, content, callback);
uploadFile(undefined, undefined, title, content, callback);
});
}; };
gdrive.updateFile = function(id, title, content, callback) { gdrive.updateFile = function(id, title, content, callback) {
askAuth(false, function() { upload(id, undefined, title, content, callback);
uploadFile(id, undefined, title, content, callback);
});
}; };
return gdrive; return gdrive;
})(); })(jQuery);

356
js/jquery.jgrowl.js Normal file
View File

@ -0,0 +1,356 @@
/**
* jGrowl 1.2.11
*
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
*
* Written by Stan Lemon <stosh1985@gmail.com>
* Last updated: 2013.02.14
*
* jGrowl is a jQuery plugin implementing unobtrusive userland notifications. These
* notifications function similarly to the Growl Framework available for
* Mac OS X (http://growl.info).
*
* To Do:
* - Move library settings to containers and allow them to be changed per container
*
* Changes in 1.2.11
* - Fix artifacts left behind by the shutdown method and text-cleanup
*
* Changes in 1.2.10
* - Fix beforeClose to be called in click event
*
* Changes in 1.2.9
* - Fixed BC break in jQuery 2.0 beta
*
* Changes in 1.2.8
* - Fixes for jQuery 1.9 and the MSIE6 check, note that with jQuery 2.0 support
* jGrowl intends to drop support for IE6 altogether
*
* Changes in 1.2.6
* - Fixed js error when a notification is opening and closing at the same time
*
* Changes in 1.2.5
* - Changed wrapper jGrowl's options usage to "o" instead of $.jGrowl.defaults
* - Added themeState option to control 'highlight' or 'error' for jQuery UI
* - Ammended some CSS to provide default positioning for nested usage.
* - Changed some CSS to be prefixed with jGrowl- to prevent namespacing issues
* - Added two new options - openDuration and closeDuration to allow
* better control of notification open and close speeds, respectively
* Patch contributed by Jesse Vincet.
* - Added afterOpen callback. Patch contributed by Russel Branca.
*
* Changes in 1.2.4
* - Fixed IE bug with the close-all button
* - Fixed IE bug with the filter CSS attribute (special thanks to gotwic)
* - Update IE opacity CSS
* - Changed font sizes to use "em", and only set the base style
*
* Changes in 1.2.3
* - The callbacks no longer use the container as context, instead they use the actual notification
* - The callbacks now receive the container as a parameter after the options parameter
* - beforeOpen and beforeClose now check the return value, if it's false - the notification does
* not continue. The open callback will also halt execution if it returns false.
* - Fixed bug where containers would get confused
* - Expanded the pause functionality to pause an entire container.
*
* Changes in 1.2.2
* - Notification can now be theme rolled for jQuery UI, special thanks to Jeff Chan!
*
* Changes in 1.2.1
* - Fixed instance where the interval would fire the close method multiple times.
* - Added CSS to hide from print media
* - Fixed issue with closer button when div { position: relative } is set
* - Fixed leaking issue with multiple containers. Special thanks to Matthew Hanlon!
*
* Changes in 1.2.0
* - Added message pooling to limit the number of messages appearing at a given time.
* - Closing a notification is now bound to the notification object and triggered by the close button.
*
* Changes in 1.1.2
* - Added iPhone styled example
* - Fixed possible IE7 bug when determining if the ie6 class shoudl be applied.
* - Added template for the close button, so that it's content could be customized.
*
* Changes in 1.1.1
* - Fixed CSS styling bug for ie6 caused by a mispelling
* - Changes height restriction on default notifications to min-height
* - Added skinned examples using a variety of images
* - Added the ability to customize the content of the [close all] box
* - Added jTweet, an example of using jGrowl + Twitter
*
* Changes in 1.1.0
* - Multiple container and instances.
* - Standard $.jGrowl() now wraps $.fn.jGrowl() by first establishing a generic jGrowl container.
* - Instance methods of a jGrowl container can be called by $.fn.jGrowl(methodName)
* - Added glue preferenced, which allows notifications to be inserted before or after nodes in the container
* - Added new log callback which is called before anything is done for the notification
* - Corner's attribute are now applied on an individual notification basis.
*
* Changes in 1.0.4
* - Various CSS fixes so that jGrowl renders correctly in IE6.
*
* Changes in 1.0.3
* - Fixed bug with options persisting across notifications
* - Fixed theme application bug
* - Simplified some selectors and manipulations.
* - Added beforeOpen and beforeClose callbacks
* - Reorganized some lines of code to be more readable
* - Removed unnecessary this.defaults context
* - If corners plugin is present, it's now customizable.
* - Customizable open animation.
* - Customizable close animation.
* - Customizable animation easing.
* - Added customizable positioning (top-left, top-right, bottom-left, bottom-right, center)
*
* Changes in 1.0.2
* - All CSS styling is now external.
* - Added a theme parameter which specifies a secondary class for styling, such
* that notifications can be customized in appearance on a per message basis.
* - Notification life span is now customizable on a per message basis.
* - Added the ability to disable the global closer, enabled by default.
* - Added callbacks for when a notification is opened or closed.
* - Added callback for the global closer.
* - Customizable animation speed.
* - jGrowl now set itself up and tears itself down.
*
* Changes in 1.0.1:
* - Removed dependency on metadata plugin in favor of .data()
* - Namespaced all events
*/
(function($) {
/** Compatibility holdover for 1.9 to check IE6 **/
var $ie6 = (function(){
return false === $.support.boxModel && $.support.objectAll && $.support.leadingWhitespace;
})();
/** jGrowl Wrapper - Establish a base jGrowl Container for compatibility with older releases. **/
$.jGrowl = function( m , o ) {
// To maintain compatibility with older version that only supported one instance we'll create the base container.
if ( $('#jGrowl').size() == 0 )
$('<div id="jGrowl"></div>').addClass( (o && o.position) ? o.position : $.jGrowl.defaults.position ).appendTo('body');
// Create a notification on the container.
$('#jGrowl').jGrowl(m,o);
};
/** Raise jGrowl Notification on a jGrowl Container **/
$.fn.jGrowl = function( m , o ) {
if ( $.isFunction(this.each) ) {
var args = arguments;
return this.each(function() {
var self = this;
/** Create a jGrowl Instance on the Container if it does not exist **/
if ( $(this).data('jGrowl.instance') == undefined ) {
$(this).data('jGrowl.instance', $.extend( new $.fn.jGrowl(), { notifications: [], element: null, interval: null } ));
$(this).data('jGrowl.instance').startup( this );
}
/** Optionally call jGrowl instance methods, or just raise a normal notification **/
if ( $.isFunction($(this).data('jGrowl.instance')[m]) ) {
$(this).data('jGrowl.instance')[m].apply( $(this).data('jGrowl.instance') , $.makeArray(args).slice(1) );
} else {
$(this).data('jGrowl.instance').create( m , o );
}
});
};
};
$.extend( $.fn.jGrowl.prototype , {
/** Default JGrowl Settings **/
defaults: {
pool: 0,
header: '',
group: '',
sticky: false,
position: 'top-right',
glue: 'after',
theme: 'default',
themeState: 'highlight',
corners: '10px',
check: 250,
life: 3000,
closeDuration: 'normal',
openDuration: 'normal',
easing: 'swing',
closer: true,
closeTemplate: '&times;',
closerTemplate: '<div>[ close all ]</div>',
log: function(e,m,o) {},
beforeOpen: function(e,m,o) {},
afterOpen: function(e,m,o) {},
open: function(e,m,o) {},
beforeClose: function(e,m,o) {},
close: function(e,m,o) {},
animateOpen: {
opacity: 'show'
},
animateClose: {
opacity: 'hide'
}
},
notifications: [],
/** jGrowl Container Node **/
element: null,
/** Interval Function **/
interval: null,
/** Create a Notification **/
create: function( message , o ) {
var o = $.extend({}, this.defaults, o);
/* To keep backward compatibility with 1.24 and earlier, honor 'speed' if the user has set it */
if (typeof o.speed !== 'undefined') {
o.openDuration = o.speed;
o.closeDuration = o.speed;
}
this.notifications.push({ message: message , options: o });
o.log.apply( this.element , [this.element,message,o] );
},
render: function( notification ) {
var self = this;
var message = notification.message;
var o = notification.options;
// Support for jQuery theme-states, if this is not used it displays a widget header
o.themeState = (o.themeState == '') ? '' : 'ui-state-' + o.themeState;
var notification = $('<div/>')
.addClass('jGrowl-notification ' + o.themeState + ' ui-corner-all' + ((o.group != undefined && o.group != '') ? ' ' + o.group : ''))
.append($('<div/>').addClass('jGrowl-close').html(o.closeTemplate))
.append($('<div/>').addClass('jGrowl-header').html(o.header))
.append($('<div/>').addClass('jGrowl-message').html(message))
.data("jGrowl", o).addClass(o.theme).children('div.jGrowl-close').bind("click.jGrowl", function() {
$(this).parent().trigger('jGrowl.beforeClose');
})
.parent();
/** Notification Actions **/
$(notification).bind("mouseover.jGrowl", function() {
$('div.jGrowl-notification', self.element).data("jGrowl.pause", true);
}).bind("mouseout.jGrowl", function() {
$('div.jGrowl-notification', self.element).data("jGrowl.pause", false);
}).bind('jGrowl.beforeOpen', function() {
if ( o.beforeOpen.apply( notification , [notification,message,o,self.element] ) != false ) {
$(this).trigger('jGrowl.open');
}
}).bind('jGrowl.open', function() {
if ( o.open.apply( notification , [notification,message,o,self.element] ) != false ) {
if ( o.glue == 'after' ) {
$('div.jGrowl-notification:last', self.element).after(notification);
} else {
$('div.jGrowl-notification:first', self.element).before(notification);
}
$(this).animate(o.animateOpen, o.openDuration, o.easing, function() {
// Fixes some anti-aliasing issues with IE filters.
if ($.support.opacity === false)
this.style.removeAttribute('filter');
if ( $(this).data("jGrowl") != null ) // Happens when a notification is closing before it's open.
$(this).data("jGrowl").created = new Date();
$(this).trigger('jGrowl.afterOpen');
});
}
}).bind('jGrowl.afterOpen', function() {
o.afterOpen.apply( notification , [notification,message,o,self.element] );
}).bind('jGrowl.beforeClose', function() {
if ( o.beforeClose.apply( notification , [notification,message,o,self.element] ) != false )
$(this).trigger('jGrowl.close');
}).bind('jGrowl.close', function() {
// Pause the notification, lest during the course of animation another close event gets called.
$(this).data('jGrowl.pause', true);
$(this).animate(o.animateClose, o.closeDuration, o.easing, function() {
if ( $.isFunction(o.close) ) {
if ( o.close.apply( notification , [notification,message,o,self.element] ) !== false )
$(this).remove();
} else {
$(this).remove();
}
});
}).trigger('jGrowl.beforeOpen');
/** Optional Corners Plugin **/
if ( o.corners != '' && $.fn.corner != undefined ) $(notification).corner( o.corners );
/** Add a Global Closer if more than one notification exists **/
if ( $('div.jGrowl-notification:parent', self.element).size() > 1 &&
$('div.jGrowl-closer', self.element).size() == 0 && this.defaults.closer != false ) {
$(this.defaults.closerTemplate).addClass('jGrowl-closer ' + this.defaults.themeState + ' ui-corner-all').addClass(this.defaults.theme)
.appendTo(self.element).animate(this.defaults.animateOpen, this.defaults.speed, this.defaults.easing)
.bind("click.jGrowl", function() {
$(this).siblings().trigger("jGrowl.beforeClose");
if ( $.isFunction( self.defaults.closer ) ) {
self.defaults.closer.apply( $(this).parent()[0] , [$(this).parent()[0]] );
}
});
};
},
/** Update the jGrowl Container, removing old jGrowl notifications **/
update: function() {
$(this.element).find('div.jGrowl-notification:parent').each( function() {
if ( $(this).data("jGrowl") != undefined && $(this).data("jGrowl").created != undefined &&
($(this).data("jGrowl").created.getTime() + parseInt($(this).data("jGrowl").life)) < (new Date()).getTime() &&
$(this).data("jGrowl").sticky != true &&
($(this).data("jGrowl.pause") == undefined || $(this).data("jGrowl.pause") != true) ) {
// Pause the notification, lest during the course of animation another close event gets called.
$(this).trigger('jGrowl.beforeClose');
}
});
if ( this.notifications.length > 0 &&
(this.defaults.pool == 0 || $(this.element).find('div.jGrowl-notification:parent').size() < this.defaults.pool) )
this.render( this.notifications.shift() );
if ( $(this.element).find('div.jGrowl-notification:parent').size() < 2 ) {
$(this.element).find('div.jGrowl-closer').animate(this.defaults.animateClose, this.defaults.speed, this.defaults.easing, function() {
$(this).remove();
});
}
},
/** Setup the jGrowl Notification Container **/
startup: function(e) {
this.element = $(e).addClass('jGrowl').append('<div class="jGrowl-notification"></div>');
this.interval = setInterval( function() {
$(e).data('jGrowl.instance').update();
}, parseInt(this.defaults.check));
if ($ie6) {
$(this.element).addClass('ie6');
}
},
/** Shutdown jGrowl, removing it and clearing the interval **/
shutdown: function() {
$(this.element).removeClass('jGrowl')
.find('div.jGrowl-notification').trigger('jGrowl.close')
.parent().empty()
},
close: function() {
$(this.element).find('div.jGrowl-notification').each(function(){
$(this).trigger('jGrowl.beforeClose');
});
}
});
/** Reference the Defaults Object for compatibility with older versions of jGrowl **/
$.jGrowl.defaults = $.fn.jGrowl.prototype.defaults;
})(jQuery);

View File

@ -1,21 +1,65 @@
var currentTime = new Date().getTime();
function showError(msg) { function showError(msg) {
alert(msg); showMessage(msg, "icon-warning-sign");
} }
var workingIndicator = 0; function showMessage(msg, iconClass, options) {
var FLAG_GDRIVE_UPLOAD = 1; options = options || {};
var FLAG_SYNCHRONIZE = 2; $.jGrowl("<i class='icon-white " + iconClass + "'></i> " + msg, options);
function setWorkingIndicator(flag) { }
workingIndicator |= flag;
if (workingIndicator) { function showWorkingIndicator(show) {
if (show === false) {
$(".working-indicator").addClass("hide");
} else {
$(".working-indicator").removeClass("hide"); $(".working-indicator").removeClass("hide");
} }
} }
function unsetWorkingIndicator(flag) { var offline = false;
workingIndicator &= ~flag; var offlineTime = currentTime;
if (!workingIndicator) { function onOffline() {
$(".working-indicator").addClass("hide"); offline = true;
offlineTime = currentTime;
if ($(".msg-offline").length === 0)
showMessage("You are offline.", "icon-exclamation-sign msg-offline", {
sticky : true, close : function() {
showMessage("You are back online!", "icon-signal");
} });
}
function onOnline() {
offline = false;
$(".msg-offline").parents(".jGrowl-notification").trigger(
'jGrowl.beforeClose');
}
function autoClean() {
$("#message-container .msg-temp").each(function() {
var displayTime = $(this).data("displayTime");
if (displayTime + 5000 < currentTime) {
$(this).animate({ opacity : 'hide' }, 600, function() {
$(this).remove();
if ($("#message-container div").length === 0) {
$("#message-container").addClass("hide");
}
});
}
});
if ($("#message-container div").length === 0) {
$("#message-container").addClass("hide");
}
// Try to reconnect if we are offline but we have some network
if (offline === true && navigator.onLine === true
&& offlineTime + 60000 < currentTime) {
offlineTime = currentTime;
// Try to download anything to test the connection
$.ajax(
{ url : "https://apis.google.com/js/client.js", timeout : 5000,
dataType : "script" }).done(function() {
onOnline();
});
} }
} }
@ -50,7 +94,6 @@ var synchronizer = (function($) {
function sync(fileSyncIndexList, content, title) { function sync(fileSyncIndexList, content, title) {
if (fileSyncIndexList.length === 0) { if (fileSyncIndexList.length === 0) {
localStorage.removeItem("sync.current"); localStorage.removeItem("sync.current");
unsetWorkingIndicator(FLAG_SYNCHRONIZE);
running = false; running = false;
// run the next file synchronization // run the next file synchronization
synchronizer.run(); synchronizer.run();
@ -62,8 +105,13 @@ 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) { if (!result && offline) {
showError("Error while uploading file on Google Drive"); // If we detect offline mode we put the fileIndex back in
// the queue
synchronizer.addFile(localStorage["sync.current"]);
localStorage.removeItem("sync.current");
running = false;
return;
} }
sync(fileSyncIndexList, content, title); sync(fileSyncIndexList, content, title);
}); });
@ -75,12 +123,9 @@ var synchronizer = (function($) {
var running = false; var running = false;
synchronizer.run = function() { synchronizer.run = function() {
// If synchronization is already running or nothing to synchronize // If synchronization is already running or nothing to synchronize
if (running || syncQueue.length === 1) { if (running || syncQueue.length === 1 || offline) {
return; return;
} }
// Start synchronization of the next available in the queue
setWorkingIndicator(FLAG_SYNCHRONIZE);
running = true; running = true;
// Dequeue the fileIndex // Dequeue the fileIndex
@ -107,13 +152,17 @@ var fileManager = (function($) {
var save = false; var save = false;
fileManager.init = function() { fileManager.init = function() {
gdrive.init();
synchronizer.init(); synchronizer.init();
fileManager.selectFile(); fileManager.selectFile();
// Save file automatically and synchronize // Save file automatically and synchronize
window.setInterval(function() { window.setInterval(function() {
currentTime = new Date().getTime();
fileManager.saveFile(); fileManager.saveFile();
synchronizer.run(); synchronizer.run();
asyncTaskRunner.runTask();
autoClean();
}, 1000); }, 1000);
$(".action-create-file").click(function() { $(".action-create-file").click(function() {
@ -331,10 +380,11 @@ var core = (function($) {
core.createLayout = function() { core.createLayout = function() {
var layout = undefined; var layout = undefined;
var layoutGlobalConfig = { closable : true, resizable : false, var layoutGlobalConfig = { closable : true, resizable : false,
slidable : false, livePaneResizing : true, spacing_open : 15, slidable : false, livePaneResizing : true,
spacing_closed : 15, togglerLength_open : 90, enableCursorHotkey : false, spacing_open : 15, spacing_closed : 15,
togglerLength_closed : 90, center__minWidth : 100, togglerLength_open : 90, togglerLength_closed : 90,
center__minHeight : 100, stateManagement__enabled : false, }; center__minWidth : 100, center__minHeight : 100,
stateManagement__enabled : false, };
if (settings.layoutOrientation == "horizontal") { if (settings.layoutOrientation == "horizontal") {
$(".ui-layout-south").remove(); $(".ui-layout-south").remove();
$(".ui-layout-east").addClass("well").prop("id", "wmd-preview"); $(".ui-layout-east").addClass("well").prop("id", "wmd-preview");
@ -396,7 +446,19 @@ var core = (function($) {
$(function() { $(function() {
$.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();
}
if (typeof (Storage) !== "undefined") { if (typeof (Storage) !== "undefined") {
fileManager.init(); fileManager.init();
} else { } else {