kopia lustrzana https://github.com/c9/core
Merge remote-tracking branch 'origin/master' into revert-13609-revert-13539-all-ab-testing
Conflicts: configs/docker.js npm-shrinkwrap.jsonpull/312/head
commit
a3063c246f
|
@ -9,7 +9,10 @@
|
|||
"https://cloud9beta.com/", "https://ide.cloud9beta.com/",
|
||||
"http://localhost:8181/", "http://localhost:5252/",
|
||||
"https://c9.vm:8181/", "https://c9.vm:5252/",
|
||||
"https://c9.dev/", "https://ide.c9.dev/"
|
||||
"https://c9.dev/", "https://ide.c9.dev/",
|
||||
"https://ide.dev.smartface.io",
|
||||
"https://ide.cs50.io",
|
||||
"https://ide.mbed.com"
|
||||
]
|
||||
},
|
||||
"description": "Cloud9 is an online platform for development that makes developing applications more convenient than ever",
|
||||
|
|
|
@ -689,11 +689,7 @@ module.exports = function(options) {
|
|||
pubkey: options.user.pubkey,
|
||||
date_add: options.user.date_add,
|
||||
active: options.user.active,
|
||||
alpha: options.user.alpha,
|
||||
beta: options.user.beta,
|
||||
c9version: options.user.c9version,
|
||||
no_newsletter: options.user.no_newsletter,
|
||||
subscription_on_signup: options.user.subscription_on_signup,
|
||||
premium: options.user.premium,
|
||||
region: options.user.region,
|
||||
},
|
||||
|
@ -920,6 +916,9 @@ module.exports = function(options) {
|
|||
plugins.push({
|
||||
packagePath: "plugins/c9.ide.language.codeintel/codeintel",
|
||||
preinstalled: hosted && !options.ssh,
|
||||
paths: {
|
||||
php: ".:./vendor",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,15 @@ function runCommand(pattern, command, options, callback) {
|
|||
return runCommand(pattern, command, {}, options);
|
||||
}
|
||||
|
||||
var parallel = ""
|
||||
var optionsArray = [];
|
||||
if (options.parallel) {
|
||||
parallel = " -P"
|
||||
optionsArray.push("-P");
|
||||
}
|
||||
if (options.cacheOnly) {
|
||||
optionsArray.push("--cache-only");
|
||||
}
|
||||
|
||||
var gsshCommand = GSSH + " " + parallel + " " + pattern + " '" + command + "'";
|
||||
var gsshCommand = GSSH + " " + optionsArray.join(" ") + " " + pattern + " '" + command + "'";
|
||||
childProcess.exec(gsshCommand, function (err, stdout) {
|
||||
return callback(err, stdout);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
var error = require("http-error");
|
||||
var _ = require("lodash");
|
||||
|
||||
var MAX_EXPIRE_INTERVAL = 5000;
|
||||
|
||||
/**
|
||||
* In memory rate limiter as connect middleware
|
||||
|
@ -6,27 +9,39 @@ var error = require("http-error");
|
|||
module.exports = ratelimit;
|
||||
|
||||
function ratelimit(key, duration, max) {
|
||||
var limit = {};
|
||||
var requests = Object.create(null); // in case there handles like 'constructor'
|
||||
setInterval(function() {
|
||||
Object.keys(requests).forEach(expireRequests);
|
||||
}, Math.min(duration * 0.75, MAX_EXPIRE_INTERVAL));
|
||||
|
||||
function expireRequests(handle) {
|
||||
var requestsForHandle = requests[handle];
|
||||
var totalToSplice = 0;
|
||||
var expireTime = Date.now() - duration;
|
||||
/* Requests are already sorted by date as they are appended, so we just loop
|
||||
until we find one that shouldn't have expired and splice them from the list */
|
||||
for (totalToSplice = 0; totalToSplice < requestsForHandle.length; totalToSplice++) {
|
||||
if (requestsForHandle[totalToSplice] >= expireTime) break;
|
||||
}
|
||||
requests[handle].splice(0, totalToSplice);
|
||||
if (requests[handle].length == 0) {
|
||||
delete requests[handle];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return function(req, res, next) {
|
||||
var handle = req.params[key];
|
||||
|
||||
var lim = limit[handle] || (limit[handle] = []);
|
||||
var now = Date.now();
|
||||
for (var i = 0; i < lim.length; i++) {
|
||||
if (now - lim[i] > duration) {
|
||||
lim.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
if (lim.length > max) {
|
||||
requests[handle] = requests[handle] || [];
|
||||
if (requests[handle].length >= max) {
|
||||
var err = new error.TooManyRequests("Rate limit exceeded");
|
||||
err.retryIn = duration - (Date.now() - lim[0]);
|
||||
next(err);
|
||||
return;
|
||||
err.retryIn = Math.min(duration, 5000);
|
||||
return next(err);
|
||||
}
|
||||
lim.push(Date.now());
|
||||
|
||||
requests[handle].push(Date.now());
|
||||
return next();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
"use server";
|
||||
|
||||
require("c9/inline-mocha")(module);
|
||||
|
||||
var ratelimit = require("./ratelimit");
|
||||
var assert = require("assert");
|
||||
var async = require("async");
|
||||
|
||||
describe("ratelimit", function() {
|
||||
|
||||
it("Should limit based on key", function (done) {
|
||||
var limiter = ratelimit("username", 10, 1);
|
||||
limiter({params: {username: "super"}}, null, function (err) {
|
||||
assert(!err, err);
|
||||
limiter({params: {username: "super"}}, null, function (err) {
|
||||
assert(err);
|
||||
assert.equal(err.code, 429);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("Should work with different keys", function (done) {
|
||||
var limiter = ratelimit("username", 10, 1);
|
||||
limiter({params: {username: "super"}}, null, function (err) {
|
||||
assert(!err, err);
|
||||
limiter({params: {username: "aloha"}}, null, function (err) {
|
||||
assert(!err, err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
it("Should work again after a delay", function (done) {
|
||||
var limiter = ratelimit("username", 10, 1);
|
||||
limiter({params: {username: "super"}}, null, function (err) {
|
||||
assert(!err, err);
|
||||
setTimeout(function() {
|
||||
limiter({params: {username: "super"}}, null, function (err) {
|
||||
assert(!err, err);
|
||||
done();
|
||||
});
|
||||
}, 25);
|
||||
});
|
||||
});
|
||||
|
||||
it("Should work with many requests", function (done) {
|
||||
var MAX_REQUESTS = 5;
|
||||
var limiter = ratelimit("username", 10, MAX_REQUESTS);
|
||||
var successfulRequests = 0;
|
||||
async.times(10, function(n, next) {
|
||||
limiter({params: {username: "super"}}, null, function (err) {
|
||||
if (err) return next(err);
|
||||
successfulRequests++;
|
||||
next();
|
||||
});
|
||||
}, function (err) {
|
||||
assert.equal(successfulRequests, MAX_REQUESTS);
|
||||
setTimeout(function() {
|
||||
limiter({params: {username: "super"}}, null, function (err) {
|
||||
assert(!err, err);
|
||||
done();
|
||||
});
|
||||
}, 25);
|
||||
});
|
||||
});
|
||||
|
||||
it("Should expire keys at the correct times", function (done) {
|
||||
var limiter = ratelimit("username", 50, 2);
|
||||
async.series([
|
||||
function(next) {
|
||||
limiter({params: {username: "mario"}}, null, function(err) {
|
||||
assert(!err, err);
|
||||
setTimeout(next, 25);
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
limiter({params: {username: "mario"}}, null, function(err) {
|
||||
assert(!err, err);
|
||||
setTimeout(next, 40);
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
limiter({params: {username: "mario"}}, null, function(err) {
|
||||
assert(!err, err);
|
||||
limiter({params: {username: "mario"}}, null, function(err) {
|
||||
assert(err);
|
||||
assert.equal(err.code, 429);
|
||||
setTimeout(next, 20);
|
||||
});
|
||||
});
|
||||
},
|
||||
function (next) {
|
||||
limiter({params: {username: "mario"}}, null, function(err) {
|
||||
assert(!err, err);
|
||||
next();
|
||||
});
|
||||
}
|
||||
], done);
|
||||
});
|
||||
|
||||
});
|
|
@ -13,6 +13,7 @@ define(function(require, exports, module) {
|
|||
"bitbucket.org": "bitbucket",
|
||||
"github.com": "github",
|
||||
"source.developers.google.com": "google",
|
||||
"gitlab.com": "gitlab",
|
||||
};
|
||||
var defaultProvider = "unknown";
|
||||
|
||||
|
@ -75,6 +76,9 @@ define(function(require, exports, module) {
|
|||
case "bitbucket":
|
||||
scm = parsed.pathname.match(/\.git$/) ? "git": "hg";
|
||||
break;
|
||||
case "gitlab":
|
||||
scm = "git";
|
||||
break;
|
||||
default:
|
||||
scm = parsed.pathname.match(/\.git$/) ? "git": "hg";
|
||||
}
|
||||
|
|
|
@ -16,57 +16,60 @@ describe(__filename, function() {
|
|||
|
||||
describe("#parse", function() {
|
||||
it("should parse ssh url", function(done) {
|
||||
var url = parse("git@github.com:fjakobs/lispjs.git");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.protocol, "ssh:");
|
||||
assert.equal(url.auth, "git");
|
||||
assert.equal(url.hostname, "github.com");
|
||||
assert.equal(url.pathname, "fjakobs/lispjs.git");
|
||||
done();
|
||||
var url = parse("git@github.com:fjakobs/lispjs.git");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.protocol, "ssh:");
|
||||
assert.equal(url.provider, "github");
|
||||
assert.equal(url.auth, "git");
|
||||
assert.equal(url.hostname, "github.com");
|
||||
assert.equal(url.pathname, "fjakobs/lispjs.git");
|
||||
done();
|
||||
}),
|
||||
|
||||
}),
|
||||
it("should parse git url", function(done) {
|
||||
var url = parse("git://github.com/fjakobs/lispjs.git");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.protocol, "git:");
|
||||
assert.equal(url.provider, "github");
|
||||
assert.equal(url.hostname, "github.com");
|
||||
assert.equal(url.pathname, "fjakobs/lispjs.git");
|
||||
done();
|
||||
}),
|
||||
|
||||
it("should parse git url", function(done) {
|
||||
var url = parse("git://github.com/fjakobs/lispjs.git");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.protocol, "git:");
|
||||
assert.equal(url.hostname, "github.com");
|
||||
assert.equal(url.pathname, "fjakobs/lispjs.git");
|
||||
done();
|
||||
it("should parse https url", function(done) {
|
||||
var url = parse("https://fjakobs@github.com/fjakobs/lispjs.git");
|
||||
assert.equal(url.protocol, "https:");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.auth, "fjakobs");
|
||||
assert.equal(url.provider, "github");
|
||||
assert.equal(url.hostname, "github.com");
|
||||
assert.equal(url.pathname, "fjakobs/lispjs.git");
|
||||
done();
|
||||
|
||||
}),
|
||||
}),
|
||||
|
||||
it("should parse https url", function(done) {
|
||||
var url = parse("https://fjakobs@github.com/fjakobs/lispjs.git");
|
||||
assert.equal(url.protocol, "https:");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.auth, "fjakobs");
|
||||
assert.equal(url.hostname, "github.com");
|
||||
assert.equal(url.pathname, "fjakobs/lispjs.git");
|
||||
done();
|
||||
|
||||
}),
|
||||
|
||||
it("should parse Bitbucket url", function(done) {
|
||||
var url = parse("git@bitbucket.org/Richard/expressling.git");
|
||||
assert.equal(url.protocol, "ssh:");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.auth, "git");
|
||||
assert.equal(url.hostname, "bitbucket.org");
|
||||
assert.equal(url.pathname, "Richard/expressling.git");
|
||||
done();
|
||||
});
|
||||
it("should parse Bitbucket url", function(done) {
|
||||
var url = parse("git@bitbucket.org/Richard/expressling.git");
|
||||
assert.equal(url.protocol, "ssh:");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.auth, "git");
|
||||
assert.equal(url.provider, "bitbucket");
|
||||
assert.equal(url.hostname, "bitbucket.org");
|
||||
assert.equal(url.pathname, "Richard/expressling.git");
|
||||
done();
|
||||
});
|
||||
|
||||
it("should parse Bitbucket hg ssh url", function(done) {
|
||||
var url = parse("ssh://hg@bitbucket.org/fjakobs/juhu");
|
||||
assert.equal(url.protocol, "ssh:");
|
||||
assert.equal(url.scm, "hg");
|
||||
assert.equal(url.provider, "bitbucket");
|
||||
assert.equal(url.hostname, "bitbucket.org");
|
||||
assert.equal(url.pathname, "fjakobs/juhu");
|
||||
done();
|
||||
});
|
||||
|
||||
it("should parse github URL without .git", function(done) {
|
||||
it("should parse Github url without .git", function(done) {
|
||||
var url = parse("https://github.com/arunoda/meteor-streams");
|
||||
assert.equal(url.protocol, "https:");
|
||||
assert.equal(url.scm, "git");
|
||||
|
@ -76,7 +79,27 @@ describe(__filename, function() {
|
|||
done();
|
||||
});
|
||||
|
||||
it("Should refuse a git url with dangerous shell chars in it", function() {
|
||||
it("should parse Gitlab url", function(done) {
|
||||
var url = parse("git@gitlab.com:bdewachter/testfiles.git");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.protocol, "ssh:");
|
||||
assert.equal(url.provider, "gitlab");
|
||||
assert.equal(url.hostname, "gitlab.com");
|
||||
assert.equal(url.pathname, "bdewachter/testfiles.git");
|
||||
done();
|
||||
}),
|
||||
|
||||
it("should parse Gitlab url without .git", function(done) {
|
||||
var url = parse("https://gitlab.com/bdewachter/testfiles.git");
|
||||
assert.equal(url.protocol, "https:");
|
||||
assert.equal(url.scm, "git");
|
||||
assert.equal(url.provider, "gitlab");
|
||||
assert.equal(url.hostname, "gitlab.com");
|
||||
assert.equal(url.pathname, "bdewachter/testfiles.git");
|
||||
done();
|
||||
});
|
||||
|
||||
it("should refuse a git url with dangerous shell chars in it", function() {
|
||||
var validUrls = [
|
||||
"https://github.com/arunoda/meteor-streams",
|
||||
"https://fjakobs@github.com/fjakobs/lispjs.git",
|
||||
|
|
|
@ -39,7 +39,7 @@ module.exports = function setup(mount, vfs, mountOptions) {
|
|||
if (path) {
|
||||
entry.href = path + entry.name;
|
||||
var mime = entry.linkStat ? entry.linkStat.mime : entry.mime;
|
||||
if (mime && mime.match(/(directory|folder)$/)) {
|
||||
if (/(directory|folder)$/.test(mime)) {
|
||||
entry.href += "/";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,15 @@ var pathBasename = require("path").basename;
|
|||
var dirname = require("path").dirname;
|
||||
var basename = require("path").basename;
|
||||
var Stream = require("stream").Stream;
|
||||
var getMime = require("simple-mime")("application/octet-stream");
|
||||
var getMime = (function(simpleMime) {
|
||||
// workaround for a bug in simple-mime
|
||||
return function(path) {
|
||||
var mime = simpleMime(path);
|
||||
if (typeof mime != "string")
|
||||
return "application/octet-stream"
|
||||
return mime;
|
||||
}
|
||||
})(require("simple-mime")());
|
||||
var vm = require("vm");
|
||||
var exists = fs.exists || require("path").exists;
|
||||
var crypto = require("crypto");
|
||||
|
|
14
package.json
14
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "c9",
|
||||
"description": "New Cloud9 Client",
|
||||
"version": "3.1.2362",
|
||||
"version": "3.1.2495",
|
||||
"author": "Ajax.org B.V. <info@ajax.org>",
|
||||
"private": true,
|
||||
"main": "bin/c9",
|
||||
|
@ -58,7 +58,7 @@
|
|||
"c9"
|
||||
],
|
||||
"c9plugins": {
|
||||
"c9.ide.language": "#ebc064ef16",
|
||||
"c9.ide.language": "#79bcb2fe06",
|
||||
"c9.ide.language.css": "#be07d72209",
|
||||
"c9.ide.language.generic": "#3949510863",
|
||||
"c9.ide.language.html": "#22fdc74869",
|
||||
|
@ -66,16 +66,16 @@
|
|||
"c9.ide.language.javascript": "#e626169643",
|
||||
"c9.ide.language.javascript.immediate": "#c8b1e5767a",
|
||||
"c9.ide.language.javascript.eslint": "#4de5457db1",
|
||||
"c9.ide.language.javascript.tern": "#b55d0069bb",
|
||||
"c9.ide.language.javascript.tern": "#64ab01f271",
|
||||
"c9.ide.language.javascript.infer": "#18acb93a3a",
|
||||
"c9.ide.language.jsonalyzer": "#d8183d84b4",
|
||||
"c9.ide.language.codeintel": "#e92ace1a56",
|
||||
"c9.ide.collab": "#cf93793700",
|
||||
"c9.ide.language.codeintel": "#fc867feec4",
|
||||
"c9.ide.collab": "#1c0f841985",
|
||||
"c9.ide.local": "#10eb45842a",
|
||||
"c9.ide.find": "#e33fbaed2f",
|
||||
"c9.ide.find.infiles": "#c0a13737ef",
|
||||
"c9.ide.find.replace": "#810ebf8bfb",
|
||||
"c9.ide.run.debug": "#9acb7f3178",
|
||||
"c9.ide.run.debug": "#3206c81cd4",
|
||||
"c9.automate": "#47e2c429c9",
|
||||
"c9.ide.ace.emmet": "#6dc4585e02",
|
||||
"c9.ide.ace.gotoline": "#a8ff07c8f4",
|
||||
|
@ -94,7 +94,7 @@
|
|||
"c9.ide.imgeditor": "#612e75ef4f",
|
||||
"c9.ide.immediate": "#0e0c18066c",
|
||||
"c9.ide.installer": "#b2e4ba0a92",
|
||||
"c9.ide.language.python": "#6572da93c1",
|
||||
"c9.ide.language.python": "#aff0772c78",
|
||||
"c9.ide.language.go": "#6ce1c7a7ef",
|
||||
"c9.ide.mount": "#4c39359b87",
|
||||
"c9.ide.navigate": "#5d5707058c",
|
||||
|
|
|
@ -30,85 +30,43 @@ define(function(require, exports, module) {
|
|||
return '"' + name + '"';
|
||||
};
|
||||
|
||||
var SupportedIcons = {
|
||||
"c9search": "page_white_magnify",
|
||||
"js": "page_white_code",
|
||||
"jsx": "page_white_code_red",
|
||||
"ts": "page_white_code",
|
||||
"tsx": "page_white_code_red",
|
||||
"json": "page_white_code",
|
||||
"css": "css",
|
||||
"scss": "css",
|
||||
"sass": "css",
|
||||
"less": "css",
|
||||
"xml": "page_white_code_red",
|
||||
"svg": "page_white_picture",
|
||||
"php": "page_white_php",
|
||||
"phtml": "page_white_php",
|
||||
"html": "html",
|
||||
"xhtml": "html",
|
||||
"coffee": "page_white_cup",
|
||||
"py": "page_white_code",
|
||||
"go": "page_white_code",
|
||||
"java": "page_white_cup",
|
||||
"logic": "logiql",
|
||||
"ru": "page_white_ruby",
|
||||
"gemspec": "page_white_ruby",
|
||||
"rake": "page_white_ruby",
|
||||
"rb": "page_white_ruby",
|
||||
"c": "page_white_c",
|
||||
"cc": "page_white_c",
|
||||
"cpp": "page_white_cplusplus",
|
||||
"cxx": "page_white_c",
|
||||
"h": "page_white_h",
|
||||
"hh": "page_white_h",
|
||||
"hpp": "page_white_h",
|
||||
"bmp": "image",
|
||||
"djv": "image",
|
||||
"djvu": "image",
|
||||
"gif": "image",
|
||||
"ico": "image",
|
||||
"jpeg": "image",
|
||||
"jpg": "image",
|
||||
"pbm": "image",
|
||||
"pgm": "image",
|
||||
"png": "image",
|
||||
"pnm": "image",
|
||||
"ppm": "image",
|
||||
"psd": "image",
|
||||
"svgz": "image",
|
||||
"tif": "image",
|
||||
"tiff": "image",
|
||||
"xbm": "image",
|
||||
"xpm": "image",
|
||||
"pdf": "page_white_acrobat",
|
||||
"clj": "page_white_code",
|
||||
"ml": "page_white_code",
|
||||
"mli": "page_white_code",
|
||||
"cfm": "page_white_coldfusion",
|
||||
"sql": "page_white_database",
|
||||
"db": "page_white_database",
|
||||
"sh": "page_white_wrench",
|
||||
"bash": "page_white_wrench",
|
||||
"xq": "page_white_code",
|
||||
"xz": "page_white_zip",
|
||||
"gz": "page_white_zip",
|
||||
"bz": "page_white_zip",
|
||||
"zip": "page_white_zip",
|
||||
"tar": "page_white_zip",
|
||||
"rar": "page_white_compressed",
|
||||
"exe": "page_white_swoosh",
|
||||
"o": "page_white_swoosh",
|
||||
"lnk": "page_white_swoosh",
|
||||
"txt": "page_white_text",
|
||||
"settings": "page_white_gear",
|
||||
"run": "page_white_gear",
|
||||
"build": "page_white_gear",
|
||||
"gitignore": "page_white_gear",
|
||||
"profile": "page_white_gear",
|
||||
"bashrc": "page_white_gear",
|
||||
};
|
||||
|
||||
var SupportedIcons = (function() {
|
||||
var extToClass = Object.create(null);
|
||||
var classToExt = {
|
||||
"page_white_magnify": "c9search",
|
||||
"page_white_code": ["clj", "go", "js", "json", "ml", "mli", "py", "ts", "xq"],
|
||||
"page_white_code_red": ["jsx", "tsx", "xml"],
|
||||
"css": ["css", "less", "sass", "scss"],
|
||||
"page_white_picture": "svg",
|
||||
"page_white_php": ["php", "phtml"],
|
||||
"html": ["html", "xhtml"],
|
||||
"page_white_cup": ["coffee", "java"],
|
||||
"logiql": "logic",
|
||||
"page_white_ruby": ["gemspec", "rake", "rb", "ru"],
|
||||
"page_white_c": ["c", "cc", "cxx"],
|
||||
"page_white_cplusplus": "cpp",
|
||||
"page_white_h": ["h", "hh", "hpp"],
|
||||
"image": ["bmp", "djv", "djvu", "gif", "ico", "jpeg", "jpg", "pbm", "pgm", "png", "pnm", "ppm", "psd", "svgz", "tif", "tiff", "xbm", "xpm"],
|
||||
"page_white_acrobat": "pdf",
|
||||
"page_white_coldfusion": "cfm",
|
||||
"page_white_database": ["db", "sql"],
|
||||
"page_white_wrench": ["bash", "sh"],
|
||||
"page_white_zip": ["bz", "gz", "tar", "xz", "zip"],
|
||||
"page_white_compressed": "rar",
|
||||
"page_white_swoosh": ["exe", "lnk", "o", "bin", "class"],
|
||||
"page_white_text": "txt",
|
||||
"page_white_gear": ["bashrc", "build", "gitignore", "profile", "run", "settings"]
|
||||
};
|
||||
Object.keys(classToExt).forEach(function(k) {
|
||||
var exts = classToExt[k];
|
||||
if (typeof exts == "string")
|
||||
exts = [exts];
|
||||
exts.forEach(function(ext) {
|
||||
extToClass[ext] = k;
|
||||
});
|
||||
});
|
||||
return extToClass;
|
||||
})();
|
||||
plugin.getFileIcon = function(name) {
|
||||
var icon = "page_white_text";
|
||||
var ext;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</p>
|
||||
|
||||
<a href="http://status.c9.io">Status Page</a> |
|
||||
<a href="mailto:support@c9.io">Support</a> |
|
||||
<a href="https://c9.io/support">Support</a> |
|
||||
<a href="https://c9.io/dashboard.html">Dashboard</a> |
|
||||
<a href="https://c9.io">Home</a>
|
||||
</div>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</p>
|
||||
|
||||
<a href="http://status.c9.io">Status Page</a> |
|
||||
<a href="mailto:support@c9.io">Support</a> |
|
||||
<a href="https://c9.io/support">Support</a> |
|
||||
<a href="https://c9.io/dashboard.html">Dashboard</a> |
|
||||
<a href="https://c9.io/">Home</a>
|
||||
</div>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<% } %>
|
||||
|
||||
<a href="http://status.c9.io">Status Page</a> |
|
||||
<a href="mailto:support@c9.io">Support</a> |
|
||||
<a href="https://c9.io/support">Support</a> |
|
||||
<a href="https://c9.io/dashboard.html">Dashboard</a> |
|
||||
<a href="https://c9.io/">Home</a>
|
||||
</div>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<% } %>
|
||||
|
||||
<a href="http://status.c9.io">Status Page</a> |
|
||||
<a href="mailto:support@c9.io">Support</a> |
|
||||
<a href="https://c9.io/support">Support</a> |
|
||||
<a href="https://c9.io/dashboard.html">Dashboard</a> |
|
||||
<a href="https://c9.io/">Home</a>
|
||||
</div>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<% } %>
|
||||
|
||||
<a href="http://status.c9.io">Status Page</a> |
|
||||
<a href="mailto:support@c9.io">Support</a> |
|
||||
<a href="https://c9.io/support">Support</a> |
|
||||
<a href="https://c9.io/dashboard.html">Dashboard</a> |
|
||||
<a href="https://c9.io">Home</a>
|
||||
</div>
|
||||
|
|
|
@ -707,7 +707,7 @@ define(function(require, exports, module) {
|
|||
status: "pending",
|
||||
className: "projectRoot",
|
||||
isEditable: false,
|
||||
map: {}
|
||||
map: Object.create(null)
|
||||
};
|
||||
var root = {};
|
||||
root.map = Object.create(null);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
e
|
|
@ -0,0 +1 @@
|
|||
v
|
|
@ -0,0 +1 @@
|
|||
i
|
|
@ -0,0 +1 @@
|
|||
l
|
|
@ -2,7 +2,7 @@ define(function(require, exports, module) {
|
|||
"use strict";
|
||||
|
||||
main.consumes = [
|
||||
"Plugin", "c9", "ui", "menus", "tree", "info", "vfs"
|
||||
"Plugin", "c9", "ui", "menus", "tree", "info", "vfs", "preferences", "settings"
|
||||
];
|
||||
main.provides = ["download"];
|
||||
return main;
|
||||
|
@ -15,7 +15,12 @@ define(function(require, exports, module) {
|
|||
var tree = imports.tree;
|
||||
var vfs = imports.vfs;
|
||||
var info = imports.info;
|
||||
|
||||
var prefs = imports.preferences;
|
||||
var settings = imports.settings;
|
||||
|
||||
var SETTING_NAME = "downloadFilesAs";
|
||||
var SETTING_PATH = "user/general/@" + SETTING_NAME;
|
||||
|
||||
/***** Initialization *****/
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
|
@ -40,6 +45,28 @@ define(function(require, exports, module) {
|
|||
onclick: download
|
||||
}), 140, plugin);
|
||||
});
|
||||
|
||||
// Preferences
|
||||
prefs.add({
|
||||
"General" : {
|
||||
"Tree & Navigate" : {
|
||||
"Download Files As" : {
|
||||
type: "dropdown",
|
||||
path: SETTING_PATH,
|
||||
items: [
|
||||
{ caption : "auto", value : "auto" },
|
||||
{ caption : "tar.gz", value : "tar.gz" },
|
||||
{ caption : "zip", value : "zip" }
|
||||
],
|
||||
position: 5000
|
||||
}
|
||||
}
|
||||
}
|
||||
}, plugin);
|
||||
|
||||
settings.on("read", function() {
|
||||
settings.setDefaults("user/general", [[SETTING_NAME, "auto"]]);
|
||||
}, plugin);
|
||||
}
|
||||
|
||||
function download() {
|
||||
|
@ -55,25 +82,40 @@ define(function(require, exports, module) {
|
|||
if (node.isFolder && node.path == "/")
|
||||
downloadProject();
|
||||
else if (paths.length > 1)
|
||||
vfs.download(paths);
|
||||
downloadPaths(paths);
|
||||
else if (node.isFolder)
|
||||
downloadFolder(node.path);
|
||||
else
|
||||
downloadFile(node.path);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function downloadProject() {
|
||||
vfs.download("/", info.getWorkspace().name + ".tar.gz");
|
||||
vfs.download("/", info.getWorkspace().name + getArchiveFileExtension());
|
||||
}
|
||||
|
||||
function downloadPaths(paths) {
|
||||
vfs.download(paths, info.getWorkspace().name + getArchiveFileExtension());
|
||||
}
|
||||
|
||||
function downloadFolder(path) {
|
||||
vfs.download(path.replace(/\/*$/, "/"));
|
||||
var withTrailingSlash = path.replace(/\/*$/, "/");
|
||||
var parts = withTrailingSlash.split("/");
|
||||
var lastPart = parts[parts.length - 2];
|
||||
vfs.download(withTrailingSlash, lastPart + getArchiveFileExtension());
|
||||
}
|
||||
|
||||
|
||||
function downloadFile(path) {
|
||||
vfs.download(path.replace(/\/*$/, ""), null, true);
|
||||
}
|
||||
|
||||
function getArchiveFileExtension() {
|
||||
var downloadFilesAs = settings.get(SETTING_PATH);
|
||||
if (downloadFilesAs === 'auto' || !downloadFilesAs) {
|
||||
downloadFilesAs = /Win/.test(navigator.platform) ? 'zip' : 'tar.gz';
|
||||
}
|
||||
return '.' + downloadFilesAs;
|
||||
}
|
||||
|
||||
/***** Lifecycle *****/
|
||||
|
||||
|
|
|
@ -88,9 +88,11 @@ define(function(require, exports, module) {
|
|||
}]);
|
||||
|
||||
statics.addStatics(externalPlugins.map(function(plugin) {
|
||||
if (typeof plugin == "string")
|
||||
plugin = { path: plugin, mount: plugin};
|
||||
return {
|
||||
path: __dirname + "/../../node_modules/" + plugin,
|
||||
mount: "/plugins/" + plugin
|
||||
path: __dirname + "/../../node_modules/" + plugin.path,
|
||||
mount: "/plugins/" + plugin.mount
|
||||
};
|
||||
}));
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</p>
|
||||
|
||||
<a href="http://status.c9.io">Status Page</a> |
|
||||
<a href="mailto:support@c9.io">Support</a> |
|
||||
<a href="https://c9.io/support">Support</a> |
|
||||
<a href="https://c9.io/dashboard.html">Dashboard</a> |
|
||||
<a href="https://c9.io">Home</a>
|
||||
</div>
|
||||
|
|
|
@ -209,6 +209,7 @@ define(function(require, exports, module) {
|
|||
}
|
||||
|
||||
function reconnect(callback) {
|
||||
if (!connection) return;
|
||||
connection.socket.setSocket(null);
|
||||
|
||||
vfsEndpoint.get(protocolVersion, function(err, urls) {
|
||||
|
@ -336,6 +337,11 @@ define(function(require, exports, module) {
|
|||
plugin.on("unload", function(){
|
||||
loaded = false;
|
||||
|
||||
if (consumer)
|
||||
consumer.disconnect();
|
||||
if (connection)
|
||||
connection.disconnect();
|
||||
|
||||
id = null;
|
||||
buffer = [];
|
||||
region = null;
|
||||
|
@ -346,6 +352,7 @@ define(function(require, exports, module) {
|
|||
serviceUrl = null;
|
||||
eioOptions = null;
|
||||
consumer = null;
|
||||
connection = null;
|
||||
vfs = null;
|
||||
showErrorTimer = null;
|
||||
showErrorTimerMessage = null;
|
||||
|
|
|
@ -80,41 +80,62 @@ define(function(require, exports, module) {
|
|||
});
|
||||
}
|
||||
else {
|
||||
// TODO add support for downloding as zip on windows
|
||||
// var cwd;
|
||||
// var args = ["-r", "-"];
|
||||
// paths.forEach(function(path) {
|
||||
// if (!path) return;
|
||||
// var dir = Path.dirname(path);
|
||||
// if (!cwd) cwd = dir;
|
||||
// var name = Path.relative(cwd, path);
|
||||
// if (name[0] == "-") name = "./" + name;
|
||||
// args.push(name);
|
||||
// });
|
||||
// vfs.spawn("zip", { args: args, cwd: cwd }
|
||||
|
||||
var args = ["-zcf", "-"];
|
||||
paths.forEach(function(path) {
|
||||
var executable, args, contentType;
|
||||
|
||||
if (/\.zip$/.test(filename)) {
|
||||
executable = "zip";
|
||||
args = ["-r", "-", "--"];
|
||||
contentType = "application/zip"
|
||||
}
|
||||
else {
|
||||
executable = "tar";
|
||||
args = ["-zcf", "-", "--"];
|
||||
contentType = "application/x-gzip"
|
||||
}
|
||||
|
||||
// Find the longest common parent directory of all the paths.
|
||||
var cwd = null;
|
||||
paths.forEach(function (path) {
|
||||
if (!path) return;
|
||||
var dir = Path.dirname(path);
|
||||
var name = Path.basename(path);
|
||||
if (name[0] == "-")
|
||||
name = "--add-file=" + name;
|
||||
args.push("-C" + dir, name);
|
||||
if (!cwd) {
|
||||
cwd = dir;
|
||||
}
|
||||
else {
|
||||
var relative = Path.relative(cwd, dir).split(Path.sep);
|
||||
var i = 0;
|
||||
while (relative[i] === '..') {
|
||||
cwd = Path.resolve(cwd, '..');
|
||||
i++;
|
||||
}
|
||||
}
|
||||
});
|
||||
vfs.spawn("tar", { args: args }, function (err, meta) {
|
||||
paths.forEach(function(path) {
|
||||
if (!path) return;
|
||||
path = Path.relative(cwd, path);
|
||||
// tar misinterprets the Windows path separator as an escape sequence, so use forward slash.
|
||||
if (Path.sep === '\\') {
|
||||
path = path.replace(/\\/g, '/');
|
||||
}
|
||||
args.push(path);
|
||||
});
|
||||
|
||||
vfs.spawn(executable, {
|
||||
args: args,
|
||||
cwd: cwd
|
||||
}, function (err, meta) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
|
||||
process = meta.process;
|
||||
|
||||
// once we receive data on stdout pipe it to the response
|
||||
|
||||
// once we receive data on stdout pipe it to the response
|
||||
process.stdout.once("data", function (data) {
|
||||
if (res.headerSent)
|
||||
return;
|
||||
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "application/x-gzip",
|
||||
"Content-Type": contentType,
|
||||
"Content-Disposition": filenameHeader
|
||||
});
|
||||
res.write(data);
|
||||
|
@ -125,7 +146,7 @@ define(function(require, exports, module) {
|
|||
process.stderr.on("data", function (data) {
|
||||
stderr += data;
|
||||
});
|
||||
|
||||
|
||||
process.on("exit", function(code, signal) {
|
||||
if (res.headerSent)
|
||||
return;
|
||||
|
@ -133,16 +154,16 @@ define(function(require, exports, module) {
|
|||
var err;
|
||||
if (code == 127) {
|
||||
err = new error.PreconditionFailed(
|
||||
"Your instance seems to be missing the 'tar' utility\n" +
|
||||
"Your instance seems to be missing the '" + executable + "' utility\n" +
|
||||
"If you are using an SSH workspace, please do:\n" +
|
||||
" 'sudo apt-get install tar'");
|
||||
" 'sudo apt-get install " + executable + "'");
|
||||
} else if (code) {
|
||||
err = new error.InternalServerError(
|
||||
"'tar' utility failed with exit code " + code +
|
||||
"'" + executable + "' utility failed with exit code " + code +
|
||||
" and stderr:/n'" + stderr + "'");
|
||||
} else if (signal) {
|
||||
err = new error.InternalServerError(
|
||||
"'tar' utility was terminated by signal " + signal
|
||||
"'" + executable + "' utility was terminated by signal " + signal
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,8 +53,9 @@ describe(__filename, function(){
|
|||
|
||||
describe("download", function() {
|
||||
|
||||
it("should download", function(next) {
|
||||
it("should download as tar", function(next) {
|
||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||
path = path.replace(/\w:/, '');
|
||||
var filename = path + "/download.tar.gz";
|
||||
var file = fs.createWriteStream(filename);
|
||||
http.get("http://localhost:8787/?download=download.tar.gz", function(res) {
|
||||
|
@ -77,21 +78,22 @@ describe(__filename, function(){
|
|||
});
|
||||
});
|
||||
|
||||
it("should download sub directory", function(next) {
|
||||
it("should download sub directory as tar", function(next) {
|
||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||
path = path.replace(/\w:/, '');
|
||||
assert.equal(err, null);
|
||||
|
||||
var filename = path + "/download.tar.gz";
|
||||
var file = fs.createWriteStream(filename);
|
||||
http.get("http://localhost:8787/views?download=download.tar.gz", function(res) {
|
||||
http.get("http://localhost:8787/test?download=download.tar.gz", function(res) {
|
||||
res.pipe(file);
|
||||
|
||||
res.on("end", function() {
|
||||
execFile("tar", ["-zxvf", filename, "views/status.html.ejs"], {cwd: path}, function(err) {
|
||||
execFile("tar", ["-zxvf", filename, "test/dir1/testdata1.txt"], {cwd: path}, function(err) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/views/status.html.ejs", "utf8"),
|
||||
fs.readFileSync(path + "/views/status.html.ejs", "utf8")
|
||||
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||
fs.readFileSync(path + "/test/dir1/testdata1.txt", "utf8")
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
@ -102,22 +104,180 @@ describe(__filename, function(){
|
|||
|
||||
it("should download without specifying a name", function(next) {
|
||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||
path = path.replace(/\w:/, '');
|
||||
assert.equal(err, null);
|
||||
|
||||
var filename = path + "/download.tar.gz";
|
||||
var file = fs.createWriteStream(filename);
|
||||
http.get("http://localhost:8787/views?download", function(res) {
|
||||
http.get("http://localhost:8787/test?download", function(res) {
|
||||
assert.equal(res.headers["content-type"], "application/x-gzip");
|
||||
assert.equal(res.headers["content-disposition"], "attachment; filename*=utf-8''views.tar.gz");
|
||||
assert.equal(res.headers["content-disposition"], "attachment; filename*=utf-8''test.tar.gz");
|
||||
|
||||
res.pipe(file);
|
||||
|
||||
res.on("end", function() {
|
||||
execFile("tar", ["-zxvf", filename, "views/status.html.ejs"], {cwd: path}, function(err) {
|
||||
execFile("tar", ["-zxvf", filename, "test/dir1/testdata1.txt"], {cwd: path}, function(err) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/views/status.html.ejs", "utf8"),
|
||||
fs.readFileSync(path + "/views/status.html.ejs", "utf8")
|
||||
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||
fs.readFileSync(path + "/test/dir1/testdata1.txt", "utf8")
|
||||
);
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should download several files in same directory as tar", function(next) {
|
||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||
path = path.replace(/\w:/, '');
|
||||
assert.equal(err, null);
|
||||
|
||||
var filename = path + "/download.tar.gz";
|
||||
var file = fs.createWriteStream(filename);
|
||||
http.get("http://localhost:8787/test/dir2/testdata2a.txt,/test/dir2/testdata2b.txt?download=download.tar.gz", function(res) {
|
||||
res.pipe(file);
|
||||
res.on("end", function() {
|
||||
execFile("tar", ["-zxvf", filename], {cwd: path}, function(err) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/test/dir2/testdata2a.txt", "utf8"),
|
||||
fs.readFileSync(path + "/testdata2a.txt", "utf8")
|
||||
);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/test/dir2/testdata2b.txt", "utf8"),
|
||||
fs.readFileSync(path + "/testdata2b.txt", "utf8")
|
||||
);
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should download several files in different directories as tar", function(next) {
|
||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||
path = path.replace(/\w:/, '');
|
||||
assert.equal(err, null);
|
||||
|
||||
var filename = path + "/download.tar.gz";
|
||||
var file = fs.createWriteStream(filename);
|
||||
http.get("http://localhost:8787/test/dir1/testdata1.txt,/test/dir2/testdata2a.txt?download=download.tar.gz", function(res) {
|
||||
res.pipe(file);
|
||||
res.on("end", function() {
|
||||
execFile("tar", ["-zxvf", filename], {cwd: path}, function(err) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||
fs.readFileSync(path + "/dir1/testdata1.txt", "utf8")
|
||||
);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/test/dir2/testdata2a.txt", "utf8"),
|
||||
fs.readFileSync(path + "/dir2/testdata2a.txt", "utf8")
|
||||
);
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should download as zip", function(next) {
|
||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||
path = path.replace(/\w:/, '');
|
||||
var filename = path + "/download.zip";
|
||||
var file = fs.createWriteStream(filename);
|
||||
http.get("http://localhost:8787/?download=download.zip", function(res) {
|
||||
assert.equal(res.headers["content-type"], "application/zip");
|
||||
assert.equal(res.headers["content-disposition"], "attachment; filename*=utf-8''download.zip");
|
||||
|
||||
res.pipe(file);
|
||||
|
||||
res.on("end", function() {
|
||||
execFile("unzip", [filename, "c9.vfs.server/download.js"], {cwd: path}, function(err, stdout, stderr) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/download.js", "utf8"),
|
||||
fs.readFileSync(path + "/c9.vfs.server/download.js", "utf8")
|
||||
);
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should download sub directory as zip", function(next) {
|
||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||
path = path.replace(/\w:/, '');
|
||||
assert.equal(err, null);
|
||||
|
||||
var filename = path + "/download.zip";
|
||||
var file = fs.createWriteStream(filename);
|
||||
http.get("http://localhost:8787/test?download=download.zip", function(res) {
|
||||
res.pipe(file);
|
||||
|
||||
res.on("end", function() {
|
||||
execFile("unzip", [filename, "test/dir1/testdata1.txt"], {cwd: path}, function(err) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||
fs.readFileSync(path + "/test/dir1/testdata1.txt", "utf8")
|
||||
);
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should download several files in same directory as zip", function(next) {
|
||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||
path = path.replace(/\w:/, '');
|
||||
assert.equal(err, null);
|
||||
|
||||
var filename = path + "/download.zip";
|
||||
var file = fs.createWriteStream(filename);
|
||||
http.get("http://localhost:8787/test/dir2/testdata2a.txt,/test/dir2/testdata2b.txt?download=download.zip", function(res) {
|
||||
res.pipe(file);
|
||||
res.on("end", function() {
|
||||
execFile("unzip", [filename], {cwd: path}, function(err) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/test/dir2/testdata2a.txt", "utf8"),
|
||||
fs.readFileSync(path + "/testdata2a.txt", "utf8")
|
||||
);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/test/dir2/testdata2b.txt", "utf8"),
|
||||
fs.readFileSync(path + "/testdata2b.txt", "utf8")
|
||||
);
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should download several files in different directories as zip", function(next) {
|
||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||
path = path.replace(/\w:/, '');
|
||||
assert.equal(err, null);
|
||||
|
||||
var filename = path + "/download.zip";
|
||||
var file = fs.createWriteStream(filename);
|
||||
http.get("http://localhost:8787/test/dir1/testdata1.txt,/test/dir2/testdata2a.txt?download=download.zip", function(res) {
|
||||
res.pipe(file);
|
||||
res.on("end", function() {
|
||||
execFile("unzip", [filename], {cwd: path}, function(err) {
|
||||
assert.equal(err, null);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||
fs.readFileSync(path + "/dir1/testdata1.txt", "utf8")
|
||||
);
|
||||
assert.equal(
|
||||
fs.readFileSync(__dirname + "/test/dir2/testdata2a.txt", "utf8"),
|
||||
fs.readFileSync(path + "/dir2/testdata2a.txt", "utf8")
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
test data 1
|
|
@ -0,0 +1 @@
|
|||
test data 2a
|
|
@ -0,0 +1 @@
|
|||
test data 2b
|
|
@ -271,6 +271,7 @@
|
|||
return cb();
|
||||
setTimeout.paused = true;
|
||||
window.app.services.vfs.connection.disconnect();
|
||||
window.app.services["vfs.endpoint"].clearCache();
|
||||
setTimeout.force(function() {
|
||||
setTimeout.paused = false;
|
||||
setTimeout.clear();
|
||||
|
|
Ładowanie…
Reference in New Issue