autosave on blur

pull/483/merge
nightwing 2017-12-18 00:20:17 +04:00
rodzic 882e4b7d16
commit 7c23e8e7ac
5 zmienionych plików z 181 dodań i 171 usunięć

Wyświetl plik

@ -630,7 +630,7 @@ define(function(require, module, exports) {
if (!loaded || tab.document.meta.preview)
return;
var lastTab = focussedTab;
var lastTab = e.lastTab || focussedTab;
if (!focussedTab || focussedTab.pane == tab.pane && focussedTab != tab)
focusTab(tab, true, true);

Wyświetl plik

@ -1,122 +1,117 @@
define(function(require, exports, module) {
main.consumes = [
"Plugin", "c9", "ui", "layout", "tooltip",
"anims", "menus", "tabManager", "save",
"preferences.experimental"
"Plugin", "c9", "settings", "tabManager", "preferences", "save", "apf"
];
main.provides = ["autosave"];
return main;
function main(options, imports, register) {
var c9 = imports.c9;
var Plugin = imports.Plugin;
var apf = imports.apf;
var save = imports.save;
var tooltip = imports.tooltip;
var tabs = imports.tabManager;
var experimental = imports["preferences.experimental"];
var prefs = imports.preferences;
var Plugin = imports.Plugin;
var settings = imports.settings;
/***** Initialization *****/
var plugin = new Plugin("Ajax.org", main.consumes);
var INTERVAL = 60000;
var CHANGE_TIMEOUT = 500;
var SLOW_CHANGE_TIMEOUT = options.slowChangeTimeout || 30000;
var SLOW_SAVE_THRESHOLD = 100 * 1024; // 100KB
var docChangeTimeout = null;
var btnSave, autosave = true, saveInterval;
var enabled = options.testing
|| experimental.addExperiment("autosave", false, "Files/Auto-Save");
var docChangeTimeout;
var autosave;
var loaded = false;
function load() {
if (loaded || !enabled) return false;
loaded = true;
// when we're back online we'll trigger an autosave if enabled
c9.on("stateChange", function(e) {
if (e.state & c9.STORAGE && !(e.last & c9.STORAGE))
check();
}, plugin);
save.getElement("btnSave", function(btn) {
btnSave = btn;
transformButton();
});
tabs.on("tabCreate", function(e) {
var tab = e.tab;
tab.document.undoManager.on("change", function(e) {
if (!autosave || !tab.path)
return;
clearTimeout(docChangeTimeout);
docChangeTimeout = setTimeout(function() {
saveTab(tab);
}, tab.document.meta.$slowSave
? SLOW_CHANGE_TIMEOUT
: CHANGE_TIMEOUT);
}, plugin);
}, plugin);
tabs.on("tabDestroy", function(e) {
if (!e.tab.path)
return;
if (tabs.getTabs().length == 1)
btnSave.hide();
saveTab(e.tab);
}, plugin);
save.on("beforeWarn", function(e) {
if (autosave && !e.tab.document.meta.newfile) {
saveTab(e.tab);
return false;
prefs.add({
"File": {
position: 150,
"Save": {
position: 100,
"Enable Auto-Save On Blur": {
type: "checkbox",
position: 100,
path: "user/general/@autosave"
}
}
}
}, plugin);
}
function transformButton() {
if (!btnSave) return;
if (btnSave.autosave === autosave) return;
if (autosave) {
// Transform btnSave
btnSave.setAttribute("caption", "");
btnSave.setAttribute("margin", "0 20");
btnSave.removeAttribute("tooltip");
btnSave.removeAttribute("command");
apf.setStyleClass(btnSave.$ext, "btnSave");
tooltip.add(btnSave, {
message: "Changes to your file are automatically saved.<br />\
View all your changes through <a href='javascript:void(0)' \
onclick='require(\"ext/revisions/revisions\").toggle();' \
class='revisionsInfoLink'>the Revision History pane</a>. \
Rollback to a previous state, or make comparisons.",
width: "250px",
hideonclick: true
}, plugin);
}
else {
}
btnSave.autosave = autosave;
settings.setDefaults("user/general", [["autosave", false]]);
settings.on("read", onSettingChange, plugin);
settings.on("user/general", onSettingChange, plugin);
save.on("beforeWarn", function(e) {
if (autosave && saveTab(e.tab))
return false;
}, plugin);
}
/***** Helpers *****/
function check() {
if (!autosave) return;
var pages = tabs.getTabs();
for (var tab, i = 0, l = pages.length; i < l; i++) {
if ((tab = pages[i]).document.changed && tab.path)
saveTab(tab);
function onSettingChange() {
autosave = settings.getBool("user/general/@autosave");
if (autosave)
enable();
else
disable();
}
function enable() {
apf.on("movefocus", scheduleCheck);
tabs.on("tabAfterActivate", scheduleCheck, plugin);
window.addEventListener("blur", scheduleCheck);
}
function disable() {
apf.off("movefocus", scheduleCheck);
tabs.off("tabAfterActivate", scheduleCheck);
window.removeEventListener("blur", scheduleCheck);
}
function scheduleCheck(e) {
if (docChangeTimeout)
return;
var tab;
var fromElement = e.fromElement;
var toElement = e.toElement;
if (e.type == "blur") {
tab = tabs.focussedTab;
}
else if (fromElement) {
var fakePage = fromElement.$fake;
if (toElement && (toElement == fakePage || fromElement == toElement.$fake)) {
fakePage = fromElement.$prevFake || toElement.$prevFake;
if (fakePage)
return;
}
tab = fromElement.cloud9tab || fakePage && fakePage.cloud9tab;
if (!tab || !tab.path)
return;
while (toElement) {
if (/window|menu|item/.test(toElement.localName))
return;
toElement = toElement.parentNode;
}
}
else if (e.lastTab) {
tab = e.lastTab;
}
if (!tab || !tab.path)
return;
docChangeTimeout = setTimeout(function() {
docChangeTimeout = null;
var activeElement = apf.document.activeElement;
var nodeName = activeElement && activeElement.localName;
// do nothing if the tab is still focused, or is a clone of the focussed tab
if (nodeName === "page" && tabs.focussedTab && tabs.focussedTab.path === tab.path)
return;
saveTab(tab);
});
}
function saveTab(tab, force) {
@ -133,22 +128,16 @@ define(function(require, exports, module) {
|| doc.meta.newfile
|| doc.meta.nofs
|| doc.meta.error
|| doc.meta.$saving))
|| doc.meta.$saving
|| doc.meta.preview
|| !doc.hasValue()))
return;
var value = doc.value;
var slow = value.length > SLOW_SAVE_THRESHOLD;
if (slow && !doc.meta.$slowSave) {
doc.meta.$slowSave = true;
return;
}
doc.meta.$slowSave = slow;
save.save(tab, {
silentsave: true,
timeout: 1,
value: value
}, function() {});
return true;
}
/***** Lifecycle *****/
@ -156,27 +145,20 @@ define(function(require, exports, module) {
plugin.on("load", function() {
load();
});
plugin.on("enable", function() {
autosave = true;
transformButton();
});
plugin.on("disable", function() {
autosave = false;
transformButton();
});
plugin.on("unload", function() {
if (saveInterval)
clearInterval(saveInterval);
loaded = false;
window.removeEventListener("blur", scheduleCheck);
autosave = false;
if (docChangeTimeout) {
clearTimeout(docChangeTimeout);
docChangeTimeout = null;
}
});
/***** Register and define API *****/
/**
* Implements auto save for Cloud9. When the user enables autosave
* the contents of files are automatically saved about 500ms after the
* change is made.
* the contents of files are automatically saved when the editor is blurred
* @singleton
**/
plugin.freezePublicAPI({ });

Wyświetl plik

@ -2,8 +2,7 @@
"use client";
require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"],
function (architect, chai, baseProc) {
require(["lib/chai/chai"], function (chai) {
var expect = chai.expect;
document.body.appendChild(document.createElement("div"))
@ -16,7 +15,6 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"],
debug: true,
hosted: true,
local: false,
davPrefix: "/"
},
"plugins/c9.core/ext",
@ -37,7 +35,7 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"],
"plugins/c9.ide.editors/undomanager",
{
packagePath: "plugins/c9.ide.editors/editors",
defaultEditor: "texteditor"
defaultEditor: "ace"
},
"plugins/c9.ide.editors/editor",
"plugins/c9.ide.editors/tabmanager",
@ -48,17 +46,15 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"],
"plugins/c9.ide.save/save",
{
packagePath: "plugins/c9.ide.save/autosave",
testing: true
},
{
packagePath: "plugins/c9.vfs.client/vfs_client"
packagePath: "plugins/c9.vfs.client/vfs_client_mock",
storage: false
},
"plugins/c9.vfs.client/endpoint",
"plugins/c9.ide.auth/auth",
"plugins/c9.core/api",
{
packagePath: "plugins/c9.fs/fs",
baseProc: baseProc
cli: true
},
"plugins/c9.fs/fs.cache.xml",
@ -67,27 +63,15 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"],
provides: [],
setup: main
}
], architect);
]);
function main(options, imports, register) {
var settings = imports.settings
var tabs = imports.tabManager;
var fs = imports.fs;
var save = imports.save;
var autosave = imports.autosave;
function countEvents(count, expected, done) {
if (count == expected)
done();
else
throw new Error("Wrong Event Count: "
+ count + " of " + expected);
}
expect.html.setConstructor(function(tab) {
if (typeof tab == "object")
return tab.pane.aml.getPage("editor::" + tab.editorType).$ext;
});
function changeTab(path, done) {
var tab = tabs.findTab(path);
tabs.focusTab(tab);
@ -98,41 +82,90 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root"],
return tab;
}
function createAndChangeTab(path, options, done) {
if (!done) {
done = options;
options = {};
}
fs.writeFile(path, path, function(err) {
if (err) throw err;
options.path = path;
tabs.open(options, function() {
changeTab(path, done);
});
});
}
describe('autosave', function() {
this.timeout(5000);
before(function(done) {
beforeEach(function(done) {
tabs.once("ready", function() {
tabs.getPanes()[0].focus();
var path = "/autosave1.txt";
fs.writeFile(path, path, function(err) {
if (err) throw err;
tabs.openFile(path, function() {
setTimeout(done, 50);
});
tabs.setState(null, function() {
tabs.getPanes()[0].focus();
done();
});
});
bar.$ext.style.background = "rgba(220, 220, 220, 0.93)";
bar.$ext.style.position = "fixed";
bar.$ext.style.left = "20px";
bar.$ext.style.right = "20px";
bar.$ext.style.bottom = "20px";
bar.$ext.style.height = "150px";
document.body.style.marginBottom = "180px";
});
it('should automatically save a tab that is changed', function(done) {
var path = "/autosave1.txt";
changeTab(path, function(tab) {
it("should not autosave when restoring state", function(done) {
settings.set("user/general/@autosave", false);
var pane = tabs.getPanes()[0].hsplit(true);
prepareTabs(testRestoreState);
function prepareTabs(callback) {
createAndChangeTab("/autosave1.txt", function(tab) {
expect(tab.document.changed).to.ok;
createAndChangeTab("/__proto__", function() {
createAndChangeTab("/<h1>", function() {
createAndChangeTab("/__lookupSetter__", { pane: pane }, function() {
callback();
});
});
});
});
}
function testRestoreState() {
settings.set("user/general/@autosave", true);
expect(tabs.getTabs().length).to.equal(4);
var state = tabs.getState(null, true);
tabs.setState(null, function() {
expect(tabs.getTabs().length).to.equal(0);
setTimeout(function() {
tabs.setState(state, function() {
expect(tabs.getTabs().length).to.equal(4);
expect(tabs.getTabs()[0].document.changed).to.ok;
setTimeout(function() {
tabs.setState(null, function() {
save.off("afterSave", preventSave);
done();
});
});
});
});
});
}
function preventSave() {
done(new Error("Save is called"));
}
save.once("afterSave", preventSave);
});
it("should automatically save a tab that is changed when editor is blurred", function(done) {
settings.set("user/general/@autosave", true);
var path = "/autosave2.txt";
createAndChangeTab(path, function(tab) {
expect(tab.document.changed).to.ok;
createAndChangeTab("/__proto__", function() {
});
save.once("afterSave", function() {
fs.readFile(path, function(err, data) {
if (err) throw err;
expect(data).to.equal("test" + path);
expect(tab.document.changed).to.not.ok;
fs.unlink(path, function() {
done();

Wyświetl plik

@ -393,6 +393,7 @@ apf.page = function(struct, tagName) {
if (this.relPage) {
this.relPage.$ext.style.display = "";
this.parentNode.$setStyleClass(this.relPage.$ext, "curpage");
this.relPage.$prevFake = this.relPage.$fake;
this.relPage.$fake = this;
@ -604,6 +605,7 @@ apf.page = function(struct, tagName) {
if (page && page.type == _self.id) {
page.relPage = _self;
if (page.$active) {
_self.$prevFake = _self.$fake;
_self.$fake = page;
page.$activate();
}

Wyświetl plik

@ -10966,16 +10966,9 @@ apf.window = function(){
(apf.window.activeElement = amlNode).focus(true, e);
this.$settingFocus = null;
apf.dispatchEvent("movefocus", {
toElement: amlNode
});
apf.dispatchEvent("movefocus", e);
};
this.$blur = function(amlNode) {