IE 8 support

This commit is contained in:
benweet 2013-03-31 02:00:03 +01:00
parent ef6189f9cb
commit 71bd87ac1e
6 changed files with 358 additions and 111 deletions

View File

@ -2,7 +2,6 @@
div.jGrowl {
z-index: 1000;
color: #fff;
font-size: 12px;
}
/** Special IE6 Style Positioning **/
@ -83,7 +82,7 @@ div.jGrowl div.jGrowl-notification, div.jGrowl div.jGrowl-closer {
background-color: #777;
zoom: 1;
width: 235px;
padding: 10px;
padding: 15px 20px;
margin-top: 5px;
margin-bottom: 5px;
text-align: left;
@ -98,7 +97,7 @@ div.jGrowl div.jGrowl-notification {
div.jGrowl div.jGrowl-notification,
div.jGrowl div.jGrowl-closer {
margin: 10px;
margin: 20px;
}
div.jGrowl div.jGrowl-notification div.jGrowl-header {

View File

@ -58,6 +58,11 @@ div, span, a, ul, li, textarea, input {
background-color: #777;
}
input[disabled], select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly] {
cursor: not-allowed;
background-color: #f5f5f5;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active,
@ -102,6 +107,18 @@ hr {
margin: 30px 0;
}
#file-title i {
margin: 3px 5px 0;
}
.dropdown-menu {
border-color: #ddd
}
.dropdown-menu i {
margin-right: 5px;
}
#navbar {
position: static;
}
@ -233,13 +250,3 @@ hr {
border-right: 5px solid #525252;
border-left: 0;
}
#message-container {
position: absolute;
right: 20px;
bottom: 20px;
width: 200px;
background-color: #eee;
margin: 0;
font-weight: bold;
}

View File

@ -10,6 +10,7 @@
<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">
<script type="text/javascript" src="js/base64.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>
@ -48,12 +49,12 @@
</ul>
<ul class="pull-right" id="menu-bar">
<li class="btn-group"><a class="btn action-create-file"
href="#" title="Create a local file"><i
class="icon-file"></i></a> <a class="btn" href="#"
title="Delete the current file locally" data-toggle="modal"
data-target="#modal-remove-file-confirm"><i class="icon-trash"></i></a>
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#"
title="Open a local file"><i class="icon-folder-open"></i></a>
href="#" title="Create a local file"><i class="icon-file"></i></a>
<a class="btn" href="#" title="Delete the current file locally"
data-toggle="modal" data-target="#modal-remove-file-confirm"><i
class="icon-trash"></i></a> <a class="btn dropdown-toggle"
data-toggle="dropdown" href="#" title="Open a local file"><i
class="icon-folder-open"></i></a>
<ul id="file-selector" class="dropdown-menu">
</ul></li>
<li class="btn-group"><a class="btn dropdown-toggle"
@ -61,26 +62,31 @@
src="img/stackedit-16.png" />&nbsp;&nbsp;<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a class="action-download-md" href="#"
title="Download the current file as Markdown"><i class="icon-download-alt"></i> Download as MD</a></li>
title="Download the current file as Markdown"><i
class="icon-download-alt"></i> Download as MD</a></li>
<li><a class="action-download-html" href="#"
title="Download the current file as HTML"><i
class="icon-download-alt"></i> Download as HTML</a></li>
<li class="divider"></li>
<li><a class="action-upload-gdrive"
href="#"><i class="icon-gdrive"></i> Save
on Google Drive</a></li>
<li class="divider"></li>
<li><a class="action-upload-gdrive" href="#"
title="Save the current file on Google Drive and synchonize it"><i
class="icon-gdrive"></i> Synchronize on Google Drive</a></li>
<li><a href="#"
title="Modify your preferences" data-toggle="modal"
data-target="#modal-settings" class="action-load-settings"><i class="icon-cog"></i>
title="Change the current file synchronized locations"
data-toggle="modal" data-target="#modal-manage-sync"
class="action-refresh-manage-sync"><i
class="icon-resize-small"></i> Manage synchronization</a></li>
<li class="divider"></li>
<li><a href="#" title="Modify your preferences"
data-toggle="modal" data-target="#modal-settings"
class="action-load-settings"><i class="icon-cog"></i>
Settings</a></li>
</ul></li>
</ul>
<ul class="nav pull-right">
<li><i class="working-indicator icon-spinner hide"></i></li>
<li><a class="brand" id="file-title" href="#"
title="Rename the current file"><span class="file-title"></span>
</a></li>
title="Rename the current file"> </a></li>
<li class="navbar-form"><input id="file-title-input"
type="text" class="span3 hide" placeholder="File title" /></li>
</ul>
@ -89,9 +95,6 @@
<textarea id="wmd-input" class="ui-layout-center"></textarea>
<div class="ui-layout-east"></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 class="modal-header">
@ -102,16 +105,41 @@
<div class="modal-body">
<p>Are you sure you want to remove "<span class="file-title"></span>"?
</p>
<p class="muted"><b>NOTE:</b> The file will be destroyed on the
local machine, not on distant synchronized locations.</p>
<p class="muted"><b>NOTE:</b> The file will be removed on the
local machine, not on synchronized locations.</p>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Cancel</a>
<a href="#"
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
class="btn btn-primary action-remove-file" data-dismiss="modal">Delete</a>
</div>
</div>
<div id="modal-manage-sync" class="modal hide">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h3>Synchronization</h3>
</div>
<div class="modal-body">
<div id="manage-sync-list">
<p class="msg-sync-list hide">The file "<span class="file-title"></span>"
is synchronized with these locations:
</p>
</div>
<br class="msg-sync-list hide" />
<p class="msg-sync-list hide muted"><b>NOTE:</b> Removing a
synchronized location will not delete any file.</p>
<p class="msg-no-sync hide">The file "<span class="file-title"></span>"
is not synchronized.
</p>
<p class="msg-no-sync hide muted"><b>NOTE:</b> You can add
synchronized locations using the top-right menu.</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
</div>
</div>
<div id="modal-settings" class="modal hide">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
@ -123,18 +151,16 @@
<dt>Layout orientation</dt>
<dd>
<label class="radio"> <input type="radio"
name="radio-layout-orientation"
value="horizontal"> Horizontal
name="radio-layout-orientation" value="horizontal">
Horizontal
</label> <label class="radio"> <input type="radio"
name="radio-layout-orientation"
value="vertical"> Vertical
name="radio-layout-orientation" value="vertical"> Vertical
</label>
</dd>
</dl>
</div>
<div class="modal-footer">
<a href="#" class="btn"
data-dismiss="modal">Cancel</a> <a href="#"
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
class="btn btn-primary action-apply-settings" data-dismiss="modal">OK</a>
</div>
</div>

176
js/base64.js Normal file
View File

@ -0,0 +1,176 @@
/*
* Copyright (c) 2010 Nick Galbreath
* http://code.google.com/p/stringencoders/source/browse/#svn/trunk/javascript
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/* base64 encode/decode compatible with window.btoa/atob
*
* window.atob/btoa is a Firefox extension to convert binary data (the "b")
* to base64 (ascii, the "a").
*
* It is also found in Safari and Chrome. It is not available in IE.
*
* if (!window.btoa) window.btoa = base64.encode
* if (!window.atob) window.atob = base64.decode
*
* The original spec's for atob/btoa are a bit lacking
* https://developer.mozilla.org/en/DOM/window.atob
* https://developer.mozilla.org/en/DOM/window.btoa
*
* window.btoa and base64.encode takes a string where charCodeAt is [0,255]
* If any character is not [0,255], then an DOMException(5) is thrown.
*
* window.atob and base64.decode take a base64-encoded string
* If the input length is not a multiple of 4, or contains invalid characters
* then an DOMException(5) is thrown.
*/
var base64 = {};
base64.PADCHAR = '=';
base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
base64.makeDOMException = function() {
// sadly in FF,Safari,Chrome you can't make a DOMException
var e, tmp;
try {
return new DOMException(DOMException.INVALID_CHARACTER_ERR);
} catch (tmp) {
// not available, just passback a duck-typed equiv
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error/prototype
var ex = new Error("DOM Exception 5");
// ex.number and ex.description is IE-specific.
ex.code = ex.number = 5;
ex.name = ex.description = "INVALID_CHARACTER_ERR";
// Safari/Chrome output format
ex.toString = function() { return 'Error: ' + ex.name + ': ' + ex.message; };
return ex;
}
}
base64.getbyte64 = function(s,i) {
// This is oddly fast, except on Chrome/V8.
// Minimal or no improvement in performance by using a
// object with properties mapping chars to value (eg. 'A': 0)
var idx = base64.ALPHA.indexOf(s.charAt(i));
if (idx === -1) {
throw base64.makeDOMException();
}
return idx;
}
base64.decode = function(s) {
// convert to string
s = '' + s;
var getbyte64 = base64.getbyte64;
var pads, i, b10;
var imax = s.length
if (imax === 0) {
return s;
}
if (imax % 4 !== 0) {
throw base64.makeDOMException();
}
pads = 0
if (s.charAt(imax - 1) === base64.PADCHAR) {
pads = 1;
if (s.charAt(imax - 2) === base64.PADCHAR) {
pads = 2;
}
// either way, we want to ignore this last block
imax -= 4;
}
var x = [];
for (i = 0; i < imax; i += 4) {
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) |
(getbyte64(s,i+2) << 6) | getbyte64(s,i+3);
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff));
}
switch (pads) {
case 1:
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6);
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff));
break;
case 2:
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12);
x.push(String.fromCharCode(b10 >> 16));
break;
}
return x.join('');
}
base64.getbyte = function(s,i) {
var x = s.charCodeAt(i);
if (x > 255) {
throw base64.makeDOMException();
}
return x;
}
base64.encode = function(s) {
if (arguments.length !== 1) {
throw new SyntaxError("Not enough arguments");
}
var padchar = base64.PADCHAR;
var alpha = base64.ALPHA;
var getbyte = base64.getbyte;
var i, b10;
var x = [];
// convert to string
s = '' + s;
var imax = s.length - s.length % 3;
if (s.length === 0) {
return s;
}
for (i = 0; i < imax; i += 3) {
b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2);
x.push(alpha.charAt(b10 >> 18));
x.push(alpha.charAt((b10 >> 12) & 0x3F));
x.push(alpha.charAt((b10 >> 6) & 0x3f));
x.push(alpha.charAt(b10 & 0x3f));
}
switch (s.length - imax) {
case 1:
b10 = getbyte(s,i) << 16;
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
padchar + padchar);
break;
case 2:
b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8);
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
alpha.charAt((b10 >> 6) & 0x3f) + padchar);
break;
}
return x.join('');
}

View File

@ -125,7 +125,7 @@ var gdrive = (function($) {
method = 'PUT';
}
var base64Data = btoa(content);
var base64Data = base64.encode(content);
var multipartRequestBody = delimiter
+ 'Content-Type: application/json\r\n\r\n'
+ JSON.stringify(metadata) + delimiter + 'Content-Type: '

View File

@ -6,6 +6,7 @@ function showError(msg) {
function showMessage(msg, iconClass, options) {
options = options || {};
iconClass = iconClass || "icon-info-sign";
$.jGrowl("<i class='icon-white " + iconClass + "'></i> " + msg, options);
}
@ -36,20 +37,6 @@ function onOnline() {
}
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) {
@ -174,6 +161,7 @@ var fileManager = (function($) {
fileManager.deleteFile();
fileManager.selectFile();
});
$(".action-refresh-manage-sync").click(refreshManageSync);
$("#file-title").click(function() {
$(this).hide();
$("#file-title-input").show().focus();
@ -181,53 +169,35 @@ var fileManager = (function($) {
$("#file-title-input").blur(function() {
var title = $.trim($(this).val());
if (title) {
var fileIndex = localStorage["file.current"];
localStorage[fileIndex + ".title"] = title;
var fileIndexTitle = localStorage["file.current"] + ".title";
if (title != localStorage[fileIndexTitle]) {
localStorage[fileIndexTitle] = title;
updateFileDescList();
updateFileTitleUI();
save = true;
}
}
$(this).hide();
$("#file-title").show();
fileManager.updateFileDescList();
fileManager.updateFileTitleUI();
save = true;
});
$(".action-download-md").click(
function() {
var content = $("#wmd-input").val();
var uriContent = "data:application/octet-stream,"
+ encodeURIComponent(content);
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,"
+ encodeURIComponent(content);
var uriContent = "data:application/octet-stream;base64,"
+ base64.encode(content);
window.open(uriContent, 'file');
});
$(".action-upload-gdrive")
.click(
function() {
$(".file-sync-indicator").removeClass("hide");
var fileIndex = localStorage["file.current"];
var content = localStorage[fileIndex + ".content"];
var title = localStorage[fileIndex + ".title"];
(function(fileIndex) {
gdrive
.createFile(
title,
content,
function(fileSyncIndex) {
if (fileSyncIndex) {
localStorage[fileIndex + ".sync"] += fileSyncIndex
+ ";";
} else {
showError("Error while creating file on Google Drive");
}
});
})(fileIndex);
});
$(".action-upload-gdrive").click(uploadGdrive);
};
var fileDescList = [];
fileManager.selectFile = function() {
// If file system does not exist
if (!localStorage["file.counter"] || !localStorage["file.list"]) {
@ -235,15 +205,15 @@ var fileManager = (function($) {
localStorage["file.counter"] = 0;
localStorage["file.list"] = ";";
}
this.updateFileDescList();
updateFileDescList();
// If no file create one
if (this.fileDescList.length === 0) {
if (fileDescList.length === 0) {
this.createFile();
this.updateFileDescList();
updateFileDescList();
}
// If no default file take first one
if (!localStorage["file.current"]) {
localStorage["file.current"] = this.fileDescList[0].index;
localStorage["file.current"] = fileDescList[0].index;
}
// Update the editor and the file title
var fileIndex = localStorage["file.current"];
@ -251,7 +221,7 @@ var fileManager = (function($) {
core.createEditor(function() {
save = true;
});
this.updateFileTitleUI();
updateFileTitleUI();
};
fileManager.createFile = function(title) {
@ -280,36 +250,83 @@ var fileManager = (function($) {
localStorage.removeItem(fileIndex + ".content");
};
fileManager.updateFileDescList = function() {
this.fileDescList = [];
fileManager.saveFile = function() {
if (save) {
var content = $("#wmd-input").val();
var fileIndex = localStorage["file.current"];
localStorage[fileIndex + ".content"] = content;
synchronizer.addFile(fileIndex);
save = false;
}
};
// Remove a synchronization location associated to the file
fileManager.removeSync = function(sync) {
var fileIndexSync = localStorage["file.current"] + ".sync";
localStorage[fileIndexSync] = localStorage[fileIndexSync].replace(";"
+ sync + ";", ";");
localStorage.removeItem(fileIndexSync + ".etag");
};
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 + ";";
updateFileTitleUI();
showMessage('The file "' + title
+ '" will now be synchronized on Google Drive.');
} else {
showError("Error while creating file on Google Drive.");
}
});
}
function updateFileDescList() {
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"];
this.fileDescList.push({ "index" : fileIndex, "title" : title });
fileDescList.push({ index : fileIndex, title : title });
}
this.fileDescList.sort(function(a, b) {
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;
});
};
}
;
function updateFileTitleUI() {
function composeTitle(fileIndex) {
var result = localStorage[fileIndex + ".title"];
var sync = localStorage[fileIndex + ".sync"];
if (sync.indexOf(SYNC_PROVIDER_GDRIVE) !== -1) {
result = '<i class="icon-gdrive"></i> ' + result;
}
return result;
}
fileManager.updateFileTitleUI = function() {
// Update the editor and the file title
var fileIndex = localStorage["file.current"];
var title = localStorage[fileIndex + ".title"];
document.title = "StackEdit - " + title;
$("#file-title").html(composeTitle(fileIndex));
$(".file-title").text(title);
$("#file-title-input").val(title);
$("#file-selector").empty();
for ( var i = 0; i < this.fileDescList.length; i++) {
var fileDesc = this.fileDescList[i];
var a = $("<a>").text(fileDesc.title);
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");
@ -323,17 +340,39 @@ var fileManager = (function($) {
}
$("#file-selector").append(li);
}
};
}
;
fileManager.saveFile = function() {
if (save) {
var content = $("#wmd-input").val();
var fileIndex = localStorage["file.current"];
localStorage[fileIndex + ".content"] = content;
synchronizer.addFile(fileIndex);
save = false;
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 sync = fileSyncIndexList[i];
(function(sync) {
var line = $("<div>").addClass("input-append");
if (sync.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
line.append($("<input>").prop("type", "text").prop(
"disabled", true).addClass("span5").val(
"Google Drive, FileID="
+ sync.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(sync);
updateFileTitleUI();
refreshManageSync();
}));
}
$("#manage-sync-list").append(line);
})(sync);
}
}
return fileManager;
})(jQuery);
@ -451,7 +490,7 @@ var core = (function($) {
$.jGrowl.defaults.position = 'bottom-right';
core.init();
// listen to online/offline events
$(window).on('offline', onOffline);
$(window).on('online', onOnline);