diff --git a/node_modules/kaefer/lib/client.js b/node_modules/kaefer/lib/client.js index 5586f63d..3f6bd7f0 100644 --- a/node_modules/kaefer/lib/client.js +++ b/node_modules/kaefer/lib/client.js @@ -86,27 +86,29 @@ var connectClient = module.exports = function(connectEio, options) { }); } - function reconnect() { - if (isReconnecting) + var timer; + function reconnect(delay) { + if (isReconnecting && typeof delay !== "number") return; reconnectSocket.setSocket(null); connectAttempts += 1; - var delay = 250; - if (connectAttempts > 10) { - delay = 10 * 1000; - } - else if (connectAttempts > 5) { - delay = 5 * 1000; - } - else if (connectAttempts > 3) { - delay = 1 * 1000; + if (typeof delay !== "number") { + if (connectAttempts < 10) { + delay = 250; + } + else { + delay = Math.min(60000, 250 * Math.pow(2, connectAttempts - 10)); + } } isReconnecting = true; console.log("Schedule re-connect in: " + delay); - setTimeout(function() { + socket.emit("reconnectDelay", { delay: delay }); + + clearTimeout(timer); + timer = setTimeout(function() { isReconnecting = false; connect(); }, delay); diff --git a/plugins/c9.core/c9.js b/plugins/c9.core/c9.js index 7f0947d2..6e7f9b1c 100644 --- a/plugins/c9.core/c9.js +++ b/plugins/c9.core/c9.js @@ -57,7 +57,7 @@ define(function(require, module, exports) { }, plugin); vfs.on("disconnect", function(reason) { - setStatus(status & ~STORAGE & ~PROCESS & ~NETWORK); + setStatus(state & ~STORAGE & ~PROCESS & ~NETWORK); emit("disconnect"); }, plugin); @@ -67,7 +67,7 @@ define(function(require, module, exports) { }, plugin); vfs.on("error", function(message) { - setStatus(status & ~STORAGE & ~PROCESS); + setStatus(state & ~STORAGE & ~PROCESS); // TODO: Don't display all errors? if (emit("showerrormessage", message) !== false) { console.error( diff --git a/plugins/c9.ide.dialog.common/error.css b/plugins/c9.ide.dialog.common/error.css index f3d14fb7..96f0a115 100644 --- a/plugins/c9.ide.dialog.common/error.css +++ b/plugins/c9.ide.dialog.common/error.css @@ -1,18 +1,23 @@ -.errorlabel{ +.errorlabel, .disconnectlabel{ position: absolute; left: 0; right: 0; top: 0; z-index: 10000000; - text-align: center; + /*text-align: center;*/ pointer-events: none; .font-smoothing(@error-font-smoothing); } -.errorlabel.anim{ +.errorlabel.anim, .disconnectlabel.anim{ transition: top 0.2s; -moz-transition: top 0.2s; /* Gecko */ -webkit-transition: top 0.2s; /* Safari */ } +.errorlabel.fade-in{ + transition: opacity 0.2s; + -moz-transition: opacity 0.2s; /* Gecko */ + -webkit-transition: opacity 0.2s; /* Safari */ +} .errorlabel div{ background: @error-background; padding: @error-padding; @@ -26,6 +31,9 @@ max-width: 100%; pointer-events: auto; } +.errorlabel.fade-in div{ + border-radius: 3px; +} .errorlabel u.close{ .image-2x("@{image-path}/@{error-close-image}", 42px, 28px); background-position: @error-close-idle-position; @@ -33,7 +41,7 @@ height: 14px; position: absolute; right: 8px; - top: 8px; + top: 6px; cursor: pointer; } .errorlabel u.close:hover{ @@ -45,4 +53,25 @@ .errorlabel div span{ border-bottom: 1px dotted rgb(255, 174, 174); cursor: help; +} + +.disconnectlabel{ + z-index: 10000001; +} +.disconnectlabel div{ + background: @disconnect-background; + padding: @disconnect-padding; + color: @disconnect-color; + border-radius: 0 0 3px 3px; + box-shadow: @disconnect-box-shadow; + line-height: 1.4; + display: inline-block; + position: relative; + word-wrap: break-word; + text-align: center; + width: 300px; + pointer-events: auto; +} +.disconnectlabel u{ + cursor: pointer; } \ No newline at end of file diff --git a/plugins/c9.ide.dialog.common/error.js b/plugins/c9.ide.dialog.common/error.js index 3642818a..3d28bb20 100644 --- a/plugins/c9.ide.dialog.common/error.js +++ b/plugins/c9.ide.dialog.common/error.js @@ -12,22 +12,72 @@ define(function(require, exports, module) { /***** Initialization *****/ var plugin = new Plugin("Ajax.org", main.consumes); + var emit = plugin.getEmitter(); var topPx = 0; - var error, hideTimeout; var lastCookie = 0; + var offset = 0; + var error, hideTimeout, disconnect; + + var DISCONNECTDELAY = 1000; - var loaded = false; function load() { - if (loaded) return false; - loaded = true; - ui.insertCss(require("text!./error.css"), options.staticPrefix, plugin); } + function initDisconnectEvents(vfs){ + var timer; + + vfs.once("connect", function(){ + vfs.connection.on("reconnectDelay", function(e){ + clearInterval(timer); + + var delay = e.delay; + if (delay > 999) { + timer = setInterval(function(){ + if (vfs.connected) + return clearInterval(timer); + + delay -= 1000; + showDisconnect({ delay: delay }); + + if (delay <= 0) + clearInterval(timer); + }, 1000); + } + + showDisconnect(e); + }); + }); + vfs.on("connect", function(){ + hideDisconnect(); + }); + vfs.on("disconnect", function(){ + // setTimeout(function(){ + // showDisconnect(); + // }, DISCONNECTDELAY); + }); + vfs.on("connecting", function(){ + showDisconnect({ connecting: true }); + }); + plugin.on("retryConnect", function(){ + vfs.connection.reconnect(0); + }); + } + /***** Methods *****/ + function getCenterX(){ + var bartools = document.querySelector(".bartools"); + if (!bartools) return 0; // For testing + + var b1 = bartools.getBoundingClientRect(); + var b2 = bartools.nextSibling.getBoundingClientRect(); + + return b1.left + b1.width + ((b2.left - b1.left - b1.width)/2); + } + function show(message, timeout) { // Error message container if (!error) { @@ -63,18 +113,21 @@ define(function(require, exports, module) { error.innerHTML = "
" + messageString + "
"; + error.style.opacity = 0; error.style.display = "block"; - error.style.top = (-1 * error.offsetHeight - 10 + topPx) + "px"; + error.style.top = (offset - (error.offsetHeight - 10 + topPx)) + "px"; + error.firstChild.style.marginLeft = Math.max(0, (getCenterX() - (error.firstChild.offsetWidth / 2))) + "px"; // Start anim setTimeout(function() { - error.className = "errorlabel anim"; - error.style.top = topPx + "px"; + error.className = "errorlabel anim " + (offset > 0 ? "fade-in" : ""); + error.style.top = (offset + topPx) + "px"; + error.style.opacity = 1; }, 10); clearTimeout(hideTimeout); if (!(timeout < 0)) - setTimeout(hide, timeout || 15000); + hideTimeout = setTimeout(hide, timeout || 15000); }); return ++lastCookie; @@ -89,19 +142,96 @@ define(function(require, exports, module) { if (!error || error.style.display === "none") return callback && callback(); - error.className = "errorlabel anim"; - error.style.top = (-1 * error.offsetHeight - 10 + topPx) + "px"; + error.className = "errorlabel anim " + (offset > 0 ? "fade-in" : ""); + if (offset > 0) + error.style.opacity = 0; + else + error.style.top = (-1 * error.offsetHeight - 10 + topPx) + "px"; + setTimeout(function() { error.style.display = "none"; callback && callback(); }, 220); } + function showDisconnect(options){ + // Error message container + if (!disconnect) { + disconnect = document.body.appendChild(document.createElement("div")); + disconnect.className = "disconnectlabel"; + disconnect.addEventListener("mouseup", function(e) { + if (e.target.tagName == "U") + emit("retryConnect"); + }); + } + + var message; + if (!options || options.delay < 1000 || options.connecting) + message = "Reconnecting..."; + else if (options.delay) + message = "Reconnecting in " + Math.ceil(options.delay/1000) + + " seconds." + + (options.delay < 2001 ? "" : " Retry Now."); + else + message = "Reconnecting..."; + + disconnect.innerHTML = "
" + message + "
"; + disconnect.firstChild.style.marginLeft + = Math.max(0, (getCenterX() - 150)) + "px"; + + if (disconnect.style.display == "block") + return; + + disconnect.style.display = "block"; + disconnect.style.top = (-1 * disconnect.offsetHeight - 10 + topPx) + "px"; + + // Start anim + setTimeout(function() { + disconnect.className = "disconnectlabel anim"; + disconnect.style.top = (topPx) + "px"; + }, 10); + + offset = 28; + + // document.querySelector(".c9-offline").addEventListener("click", function(){ + // alert("Offline Notication", "You are currently offline.", + // "This indicator notifies you that Cloud9 is unable to reach " + // + "the server. This usually happens because you are offline. " + // + "Some features will be disabled until the " + // + "network connection becomes available again. " + // + "This notication could also show when the server is " + // + "unreachable due to other reasons. Sometimes a refresh of " + // + "the tab will fix an issue. Please e-mail " + // + "support@c9.io for further problem resolution."); + // }, false); + } + + function hideDisconnect(cookie, callback) { + if (!disconnect || disconnect.style.display === "none") + return callback && callback(); + + disconnect.className = "disconnectlabel anim"; + disconnect.style.top = (-1 * disconnect.offsetHeight - 10 + topPx) + "px"; + setTimeout(function() { + disconnect.style.display = "none"; + callback && callback(); + }, 220); + + offset = 0; + } + /***** Lifecycle *****/ plugin.on("load", function() { load(); }); + plugin.on("unload", function() { + topPx = 0; + lastCookie = 0; + offset = 0; + error = null; + hideTimeout = null; + }); /***** Register and define API *****/ @@ -116,6 +246,19 @@ define(function(require, exports, module) { get top(){ return topPx; }, set top(value){ topPx = value; }, + get vfs(){ throw new Error("Permission Denied"); }, + set vfs(v){ initDisconnectEvents(v); }, + + /** + * + */ + showDisconnect: showDisconnect, + + /** + * + */ + hideDisconnect: hideDisconnect, + /** * Displays an error message in the main error reporting UI. * @param {String} message The message to display. diff --git a/plugins/c9.ide.layout.classic/layout.js b/plugins/c9.ide.layout.classic/layout.js index c3124ceb..5d3aee91 100644 --- a/plugins/c9.ide.layout.classic/layout.js +++ b/plugins/c9.ide.layout.classic/layout.js @@ -8,11 +8,11 @@ define(function(require, exports, module) { function main(options, imports, register) { var c9 = imports.c9; - var alert = imports["dialog.alert"].show; var Plugin = imports.Plugin; - var question = imports["dialog.question"]; var settings = imports.settings; var commands = imports.commands; + var alert = imports["dialog.alert"].show; + var question = imports["dialog.question"]; var preload = imports["layout.preload"]; var anims = imports.anims; var ui = imports.ui; @@ -126,33 +126,6 @@ define(function(require, exports, module) { img.src = options.staticPrefix + "/images/" + p; }); - var hideOffline; - c9.on("stateChange", function(e) { - // Online - if (e.state & c9.NETWORK && e.state & c9.STORAGE) { - hideOffline && hideOffline(); - } - // Offline - else if (!hideOffline || hideOffline.hasClosed()) { - hideOffline = notify("
No internet " - + "connection detected. Cloud9 will automatically try to " - + "reconnect when it detects an internet connection." - + "
", true, 1000); - - document.querySelector(".c9-offline").addEventListener("click", function(){ - alert("Offline Notication", "You are currently offline.", - "This indicator notifies you that Cloud9 is unable to reach " - + "the server. This usually happens because you are offline. " - + "Some features will be disabled until the " - + "network connection becomes available again. " - + "This notication could also show when the server is " - + "unreachable due to other reasons. Sometimes a refresh of " - + "the tab will fix an issue. Please e-mail " - + "support@c9.io for further problem resolution."); - }, false); - } - }); - window.addEventListener("resize", resize, false); window.addEventListener("focus", resize, false); @@ -542,6 +515,21 @@ define(function(require, exports, module) { window.removeEventListener("resize", resize); if (removeTheme) removeTheme(); + + logobar = null; + removeTheme = null; + theme = null; + c9console = null; + menus = null; + tabManager = null; + panels = null; + userLayout = null; + ignoreTheme = null; + notify = null; + hideFlagUpdate = null; + activeFindArea = null; + defaultFindArea = null; + activating = null; }); /***** Register and define API *****/ diff --git a/plugins/c9.ide.layout.classic/themes/default-dark-gray.less b/plugins/c9.ide.layout.classic/themes/default-dark-gray.less index 6c78b067..f78fd8a0 100644 --- a/plugins/c9.ide.layout.classic/themes/default-dark-gray.less +++ b/plugins/c9.ide.layout.classic/themes/default-dark-gray.less @@ -632,6 +632,12 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; +// Disconnect Dialog +@disconnect-background: #D8B112; +@disconnect-color: @error-color; +@disconnect-padding: @error-padding; +@disconnect-box-shadow: @error-box-shadow; + // Offline Dialog @offline-gradient: linear-gradient(top, rgb(60, 112, 153) 0%, rgb(61, 97, 126) 100%); @offline-border-bottom: 1px solid rgba(0, 0, 0, 0.61); diff --git a/plugins/c9.ide.layout.classic/themes/default-dark.less b/plugins/c9.ide.layout.classic/themes/default-dark.less index fd62d0f3..26707588 100644 --- a/plugins/c9.ide.layout.classic/themes/default-dark.less +++ b/plugins/c9.ide.layout.classic/themes/default-dark.less @@ -632,6 +632,12 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; +// Disconnect Dialog +@disconnect-background: #D8B112; +@disconnect-color: @error-color; +@disconnect-padding: @error-padding; +@disconnect-box-shadow: @error-box-shadow; + // Offline Dialog @offline-gradient: linear-gradient(top, rgb(60, 112, 153) 0%, rgb(61, 97, 126) 100%); @offline-border-bottom: 1px solid rgba(0, 0, 0, 0.61); diff --git a/plugins/c9.ide.layout.classic/themes/default-flat-light.less b/plugins/c9.ide.layout.classic/themes/default-flat-light.less index b23ca74b..6425061b 100644 --- a/plugins/c9.ide.layout.classic/themes/default-flat-light.less +++ b/plugins/c9.ide.layout.classic/themes/default-flat-light.less @@ -632,6 +632,12 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; +// Disconnect Dialog +@disconnect-background: #D8B112; +@disconnect-color: @error-color; +@disconnect-padding: @error-padding; +@disconnect-box-shadow: @error-box-shadow; + // Offline Dialog @offline-gradient: linear-gradient(top, #3d9ac4 0%, #3d9ac4 100%); @offline-border-bottom: 0; diff --git a/plugins/c9.ide.layout.classic/themes/default-light-gray.less b/plugins/c9.ide.layout.classic/themes/default-light-gray.less index 39472337..ebf4895d 100644 --- a/plugins/c9.ide.layout.classic/themes/default-light-gray.less +++ b/plugins/c9.ide.layout.classic/themes/default-light-gray.less @@ -632,6 +632,12 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; +// Disconnect Dialog +@disconnect-background: #D8B112; +@disconnect-color: @error-color; +@disconnect-padding: @error-padding; +@disconnect-box-shadow: @error-box-shadow; + // Offline Dialog @offline-gradient: linear-gradient(top, rgb(60, 112, 153) 0%, rgb(61, 97, 126) 100%); @offline-border-bottom: 1px solid rgba(0, 0, 0, 0.61); diff --git a/plugins/c9.ide.layout.classic/themes/default-light.less b/plugins/c9.ide.layout.classic/themes/default-light.less index 48928aee..982b7177 100644 --- a/plugins/c9.ide.layout.classic/themes/default-light.less +++ b/plugins/c9.ide.layout.classic/themes/default-light.less @@ -632,6 +632,12 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; +// Disconnect Dialog +@disconnect-background: #D8B112; +@disconnect-color: @error-color; +@disconnect-padding: @error-padding; +@disconnect-box-shadow: @error-box-shadow; + // Offline Dialog @offline-gradient: linear-gradient(top, rgb(60, 112, 153) 0%, rgb(61, 97, 126) 100%); @offline-border-bottom: 1px solid rgba(0, 0, 0, 0.61); diff --git a/plugins/c9.vfs.client/vfs_client.js b/plugins/c9.vfs.client/vfs_client.js index 22a5916f..266974e7 100644 --- a/plugins/c9.vfs.client/vfs_client.js +++ b/plugins/c9.vfs.client/vfs_client.js @@ -30,8 +30,9 @@ define(function(require, exports, module) { var Plugin = imports.Plugin; var auth = imports.auth; var vfsEndpoint = imports["vfs.endpoint"]; - var showError = imports["dialog.error"].show; - var hideError = imports["dialog.error"].hide; + var errorDialog = imports["dialog.error"]; + var showError = errorDialog.show; + var hideError = errorDialog.hide; var showAlert = imports["dialog.alert"].show; var eio = require("engine.io"); @@ -49,6 +50,9 @@ define(function(require, exports, module) { var plugin = new Plugin("Ajax.org", main.consumes); var emit = plugin.getEmitter(); + // Give reference to vfs to plugin + errorDialog.vfs = plugin; + var buffer = []; var installChecked = false; var withInstall = options.withInstall; @@ -330,6 +334,22 @@ define(function(require, exports, module) { }); plugin.on("unload", function(){ loaded = false; + + id = null; + buffer = []; + installChecked = false; + region = null; + vfsBaseUrl = null; + homeUrl = null; + projectUrl = null; + pingUrl = null; + serviceUrl = null; + eioOptions = null; + consumer = null; + vfs = null; + showErrorTimer = null; + showErrorTimerMessage = null; + lastError = null; }); /***** Register and define API *****/