diff --git a/modules/devkit-simulator-client/client.js b/modules/devkit-simulator-client/client.js
index 8b5266bc..beb84617 100644
--- a/modules/devkit-simulator-client/client.js
+++ b/modules/devkit-simulator-client/client.js
@@ -45,6 +45,30 @@ exports.onLaunch = function () {
GLOBAL.NATIVE.onBackButton && GLOBAL.NATIVE.onBackButton(evt);
});
+ /** Puts preserveCache on localStorage so that jsio knows to preserve (and then request)
+ suggestions next load. Optionally also sets partialLoad, which causes jsio to wait for
+ a partialLoadContinue signal to actually load the app, after preloading suggestions. */
+ channel.on('reload', function (data, req) {
+ localStorage.setItem(jsio.__env.getNamespace('preserveCache'), true);
+
+ if (data) {
+ if (data.partialLoad) {
+ localStorage.setItem(jsio.__env.getNamespace('partialLoad'), true);
+ }
+ }
+
+ req.send(true);
+ });
+
+ /** partialLoadContinue is to be used in conjunction with partialLoad */
+ channel.on('partialLoadContinue', function (data, req) {
+ var cb = window._continueLoadCallback;
+ if (cb) {
+ cb();
+ }
+ req.send(true);
+ });
+
channel.on('screenshot', function (data, req) {
devkit.debugging.screenshot(function (err, res) {
if (err) {
diff --git a/package.json b/package.json
index d8bb0d70..7a8defc1 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"bluebird": "^2.9.24",
"body-parser": "1.x",
"chalk": "^1.0.0",
+ "chokidar": "^1.0.5",
"compression": "^1.4.3",
"express": "4.x",
"ff": "^0.2.1",
diff --git a/src/build/DirectoryBuilder.js b/src/build/DirectoryBuilder.js
new file mode 100644
index 00000000..a574ed3b
--- /dev/null
+++ b/src/build/DirectoryBuilder.js
@@ -0,0 +1,41 @@
+var path = require('path');
+var fs = require('fs');
+var logger = require('../util/logging').get('build-directories');
+
+// class for representing a list of resource directories
+function DirectoryBuilder(base) {
+ this._base = base;
+ this._directories = [];
+}
+
+DirectoryBuilder.prototype.add = function (src, target) {
+ var directory;
+ if (arguments.length === 1) {
+ directory = {
+ src: path.join(this._base, src),
+ target: src
+ };
+ } else {
+ directory = {
+ src: src,
+ target: target
+ };
+ }
+
+ if (fs.existsSync(directory.src)) {
+ this._directories.push(directory);
+ } else {
+ logger.warn('Directory does not exist, ignoring files from',
+ directory);
+ }
+};
+
+DirectoryBuilder.prototype.getPaths = function () {
+ return this._directories.map(function (dir) { return dir.src; });
+};
+
+DirectoryBuilder.prototype.getDirectories = function () {
+ return this._directories;
+};
+
+module.exports = DirectoryBuilder;
diff --git a/src/build/index.js b/src/build/index.js
index 956a613d..480f188f 100644
--- a/src/build/index.js
+++ b/src/build/index.js
@@ -55,15 +55,31 @@ exports.build = function (appPath, argv, cb) {
require('./steps/createDirectories').createDirectories(app, config, f());
}, function () {
require('./steps/buildHooks').getDependencies(app, config, f());
- }, function (deps) {
- // deps is an array of objects, merge them into one object and get all keys with false values
- deps = merge.apply(this, deps);
- var toRemove = Object.keys(deps).filter(function (name) { return deps[name] === false; });
- app.removeModules(toRemove);
+ }, function (res) {
+ var modules = res.reduce(function (modules, res) {
+ var toRemove = Object.keys(res.data)
+ .filter(function (name) {
+ return res.data[name] === false;
+ });
+
+ if (toRemove.length) {
+ logger.log('module', res.module.name, 'getDependencies:');
+ toRemove.forEach(function (module) {
+ logger.log(' removing module', module);
+ });
+ }
+ return modules.concat(toRemove);
+ }, []);
+
+ app.removeModules(modules);
}, function () {
require('./steps/addDebugCode')(app, config, f());
}, function () {
require('./steps/moduleConfig').getConfig(app, config, f());
+ }, function () {
+ require('./steps/buildHooks').getResourceDirectories(app, config, f());
+ }, function (directories) {
+ config.directories = directories;
}, function () {
require('./steps/buildHooks').onBeforeBuild(app, config, f());
}, function () {
@@ -72,9 +88,8 @@ exports.build = function (appPath, argv, cb) {
// ONLY print config to stdout
process.stdout.write(JSON.stringify(merge({title: app.manifest.title}, config)));
process.exit(0);
- } else {
- require('./steps/logConfig').log(app, config, f());
}
+ require('./steps/logConfig').log(app, config, f());
}, function () {
require('./steps/executeTargetBuild').build(app, config, f());
}, function () {
diff --git a/src/build/steps/buildHooks.js b/src/build/steps/buildHooks.js
index 27d834a8..55fe1925 100644
--- a/src/build/steps/buildHooks.js
+++ b/src/build/steps/buildHooks.js
@@ -1,32 +1,94 @@
-var ff = require('ff');
+var path = require('path');
var Promise = require('bluebird');
var api = require('../../api');
+var DirectoryBuilder = require('../DirectoryBuilder');
+var fs = require('fs');
+
+var readDir = Promise.promisify(fs.readdir);
+var stat = Promise.promisify(fs.stat);
exports.getDependencies = function (app, config, cb) {
app.reloadModules();
// allows modules to disable other modules
- executeHook('getDependencies', app, config, cb);
-}
+ executeHook('getDependencies', app, config)
+ .nodeify(cb);
+};
exports.onBeforeBuild = function (app, config, cb) {
- executeHook('onBeforeBuild', app, config, cb);
-}
+ executeHook('onBeforeBuild', app, config)
+ .nodeify(cb);
+};
exports.onAfterBuild = function (app, config, cb) {
- executeHook('onAfterBuild', app, config, cb);
-}
+ executeHook('onAfterBuild', app, config)
+ .nodeify(cb);
+};
+
+exports.getResourceDirectories = function (app, config, cb) {
+ var builder = new DirectoryBuilder(app.paths.root);
+ builder.add('resources');
+ executeHook('getResourceDirectories', app, config)
+ .map(function (res) {
+ var module = res.module;
+ var directories = res.data;
+ directories.forEach(function (directory) {
+ var target = path.join('modules', module.name, directory.target);
+ builder.add(directory.src, target);
+ });
+ console.log(res);
+ })
+ .then(function () {
+ // add any localized resource directories
+ return readDir(app.paths.root);
+ })
+ .filter(function (filename) {
+ if (/^resources-/.test(filename)) {
+ return stat(path.join(app.paths.root, filename)).then(function (info) {
+ return info.isDirectory();
+ }, function onStatFail() {
+ return false;
+ });
+ }
+
+ return false;
+ })
+ .map(function (filename) {
+ builder.add(filename);
+ })
+ .then(function () {
+ return builder.getDirectories();
+ })
+ .nodeify(cb);
+};
-function executeHook(buildHook, app, config, cb) {
+function executeHook(buildHook, app, config) {
var modules = app.getModules();
- Promise.all(Object.keys(modules).map(function (moduleName) {
- var module = modules[moduleName];
- var buildExtension = module.loadExtension('build');
- if (!buildExtension || !buildExtension[buildHook]) {
- return;
- }
+ return Promise.resolve(Object.keys(modules))
+ .map(function (moduleName) {
+ var module = modules[moduleName];
+ var buildExtension = module.loadExtension('build');
+ if (!buildExtension || !buildExtension[buildHook]) {
+ return;
+ }
+ return new Promise(function (resolve, reject) {
+ var retVal = buildExtension[buildHook](api, app.toJSON(), config, function (err, res) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(res);
+ }
+ });
- return Promise.fromNode(buildExtension[buildHook].bind(buildExtension, api, app.toJSON(), config));
- })).nodeify(cb);
+ if (retVal) { resolve(retVal); }
+ })
+ .then(function (data) {
+ return {
+ module: module,
+ data: data
+ };
+ });
+ })
+ .filter(function (res) { return res; });
}
diff --git a/src/build/steps/executeTargetBuild.js b/src/build/steps/executeTargetBuild.js
index abbd9592..68f38796 100644
--- a/src/build/steps/executeTargetBuild.js
+++ b/src/build/steps/executeTargetBuild.js
@@ -12,7 +12,6 @@ exports.build = function (app, config, cb) {
}
moduleKeys.forEach(function (moduleName) {
- console.log(moduleName)
if (!buildModule) {
var module = modules[moduleName];
buildModule = module.loadBuildTarget(config.target);
diff --git a/src/serve/appRoutes.js b/src/serve/appRoutes.js
index 805dacdf..219622d0 100644
--- a/src/serve/appRoutes.js
+++ b/src/serve/appRoutes.js
@@ -13,11 +13,14 @@ var jvmtools = require('../jvmtools');
var logging = require('../util/logging');
var buildQueue = require('./buildQueue');
+var chokidar = require('chokidar');
+
var logger = logging.get('routes');
var HOME = process.env.HOME
|| process.env.HOMEPATH
|| process.env.USERPROFILE;
+var ROUTE_INACTIVE_TIME_LIMIT = 30 * 60 * 1000;
exports.addToAPI = function (opts, api) {
@@ -49,10 +52,22 @@ exports.addToAPI = function (opts, api) {
}
buildFromRequest(opts)
- .then(function (mountInfo) {
+ .spread(function (mountInfo, buildResult) {
res.json(mountInfo);
+ // Finally, add all of the resource routes (some are dynamically added at build by devkit modules)
+ var simulatorApp = getAppByPath(mountInfo.appPath);
+ if (!simulatorApp._areResourcesMounted) {
+ simulatorApp._areResourcesMounted = true;
+ buildResult.config.directories.forEach(function(resource) {
+ simulatorApp.use(
+ path.join('/' + resource.target),
+ express.static(resource.src)
+ );
+ });
+ }
})
.catch(function (e) {
+ logger.error('Error mounting app', e.stack);
res.status(500).send({
message: e.message,
stack: e.stack
@@ -70,17 +85,18 @@ exports.addToAPI = function (opts, api) {
return mountAppFromRequest(opts)
.then(function (mountInfo) {
var buildOpts = {
- target: opts.target,
- scheme: opts.scheme,
- simulated: true,
- simulateDeviceId: opts.deviceId,
- simulateDeviceType: opts.deviceType,
- output: mountInfo.buildPath
- };
-
- return buildQueue
- .add(mountInfo.appPath, buildOpts)
- .return(mountInfo);
+ target: opts.target,
+ scheme: opts.scheme,
+ simulated: true,
+ simulateDeviceId: opts.deviceId,
+ simulateDeviceType: opts.deviceType,
+ output: mountInfo.buildPath
+ };
+
+ return buildQueue.add(mountInfo.appPath, buildOpts)
+ .then(function (buildResult) {
+ return [mountInfo, buildResult];
+ });
});
}
@@ -133,16 +149,73 @@ exports.addToAPI = function (opts, api) {
});
}
+ // Promises that resolve with mount info once ready
var _mountedApps = {};
+ // Map of appId -> app
+ var _availableSimulatorApps = {};
+ var getAppByPath = function(appPath) {
+ for (var appId in _availableSimulatorApps) {
+ var app = _availableSimulatorApps[appId];
+ if (app.appPath === appPath) {
+ return app;
+ }
+ }
+ return null;
+ };
+
+ baseApp.use('/apps/:appId', function(req, res, next) {
+ var app = _availableSimulatorApps[req.params.appId];
+ if (app) {
+ app(req, res, next);
+ } else {
+ next();
+ }
+ });
+
+ baseApp.io.on('connection', function(socket) {
+ logger.info('socket connected');
+
+ socket.on('watch', function(appPath) {
+ // Add this socket to the app
+ var app = getAppByPath(appPath);
+ if (app) {
+ app.sockets.push(socket);
+ }
+ });
+
+ socket.on('disconnect', function() {
+ logger.info('socket disconnect');
+ });
+
+ });
+
function mountApp(appPath, buildPath) {
+ var routeId = generateRouteId(appPath);
+
if (!_mountedApps[appPath]) {
_mountedApps[appPath] = apps.get(appPath)
.then(function mountExtensions(app) {
- var routeId = generateRouteId(appPath);
var simulatorApp = express();
- baseApp.use('/apps/' + routeId, simulatorApp);
+ // Special case src directories
+ simulatorApp.use(
+ '/modules',
+ express.static(path.join(appPath, 'modules'))
+ );
+ simulatorApp.use(
+ '/src',
+ express.static(path.join(appPath, 'src'))
+ );
+ simulatorApp.use(
+ '/lib',
+ express.static(path.join(appPath, 'lib'))
+ );
+
+ simulatorApp._areResourcesMounted = false;
+
+ // Static serve builds
simulatorApp.use('/', express.static(buildPath));
+
addSimulatorAPI(simulatorApp);
var modules = app.getModules();
@@ -151,6 +224,7 @@ exports.addToAPI = function (opts, api) {
var loadExtension = function (module) {
var extension = module.loadExtension('debugger');
if (!extension || !extension.getMiddleware) { return; }
+
try {
var routes = extension.getMiddleware(require('../api'), app);
if (!routes) { return; }
@@ -181,6 +255,35 @@ exports.addToAPI = function (opts, api) {
baseModules.forEach(function (module) { loadExtension(module); });
+ // Add socket connection
+ simulatorApp.sockets = [];
+ simulatorApp.socketEmit = function(name, data) {
+ simulatorApp.sockets.forEach(function(socket) {
+ socket.emit(name, data);
+ });
+ };
+
+ // Everything is set, add some file watchers
+ simulatorApp.watchers = [];
+ simulatorApp.watchers.push(
+ chokidar.watch(
+ [
+ path.join(appPath, 'manifest.json'),
+ path.join(appPath, 'src'),
+ path.join(appPath, 'resources')
+ ],
+ { recursive: true, followSymLinks: false, persistent: true, ignoreInitial: true }
+ ).on('all', function(event, path) {
+ logger.info(routeId + ': changed ' + path);
+ simulatorApp.socketEmit('watch:changed', path);
+ })
+ );
+
+ // Meta data
+ simulatorApp.appPath = appPath;
+
+ // Add to available routes, return info
+ _availableSimulatorApps[routeId] = simulatorApp;
return {
id: routeId,
url: '/apps/' + routeId + '/',
@@ -192,7 +295,38 @@ exports.addToAPI = function (opts, api) {
});
}
- return Promise.resolve(_mountedApps[appPath]);
+ // Remove and clean up the routes after a bit of inactivity
+ var mountedApp = _mountedApps[appPath];
+ // remove the old timeout
+ if (mountedApp.cleanupTimeout) {
+ clearTimeout(mountedApp.cleanupTimeout);
+ }
+ mountedApp.cleanupTimeout = setTimeout(
+ unmountApp.bind(null, appPath, routeId),
+ ROUTE_INACTIVE_TIME_LIMIT
+ );
+
+ return Promise.resolve(mountedApp);
+ }
+
+ function unmountApp(appPath, routeId) {
+ logger.info('Shutting down route for: ' + appPath + ' (' + routeId + ')');
+
+ var app = _availableSimulatorApps[routeId];
+
+ // Close sockets
+ app.sockets.forEach(function(socket) {
+ socket.disconnect();
+ });
+
+ // Remove watchers
+ app.watchers.forEach(function(watcher) {
+ watcher.close();
+ });
+
+ // Remove app routes
+ delete _availableSimulatorApps[routeId];
+ delete _mountedApps[appPath];
}
// tracks used route uuids
diff --git a/src/serve/buildQueue.js b/src/serve/buildQueue.js
index 9477fff7..cbdd44f6 100644
--- a/src/serve/buildQueue.js
+++ b/src/serve/buildQueue.js
@@ -107,7 +107,7 @@ var BuildItem = Class(function () {
this.error = msg.err;
this._reject(this.error);
} else {
- this._resolve();
+ this._resolve(msg.res);
}
checkQueue();
diff --git a/src/serve/index.js b/src/serve/index.js
index 03d07001..8ec09ccc 100644
--- a/src/serve/index.js
+++ b/src/serve/index.js
@@ -26,15 +26,11 @@ var Z_BEST_COMPRESSION = 9;
exports.serveWeb = function (opts, cb) {
var port = opts.port;
- // common.track("BasilServe");
var app = express();
var server = http.Server(app);
app.io = require('socket.io')(server);
- // var deviceManager = require('./deviceManager').get();
- // deviceManager.init(app.io);
-
app.use(compression({level: Z_BEST_COMPRESSION}));
app.use(function noCacheControl(req, res, next) {
@@ -54,6 +50,8 @@ exports.serveWeb = function (opts, cb) {
app.use('/compile/', importMiddleware(getPath('static/')));
// serve static files
+ var devkitPath = path.resolve(__dirname, '..', '..');
+ app.use('/devkit/', express.static(devkitPath));
app.use('/', express.static(getPath('static')));
// Serve
diff --git a/src/serve/static/Simulator.js b/src/serve/static/Simulator.js
index 10a17a3d..577122b2 100644
--- a/src/serve/static/Simulator.js
+++ b/src/serve/static/Simulator.js
@@ -33,11 +33,41 @@ exports = Class(function () {
// DOM simulator
this._ui = new ui.Chrome(this);
+ this._hasSocket = false;
+ this.socket = this._makeSocket();
+
+ /** @type {Promise} */
+ this._requestSimulatePromise = null;
+
this._modules = {};
this.loadModules(opts.modules);
this.rebuild();
};
+ this._makeSocket = function() {
+ var socket = new io();
+
+ socket.on('connect', function() {
+ this._hasSocket = true;
+
+ socket.emit('watch', this._app);
+
+ // Do this right off the bat to make sure that the routes are all set up right
+ this._requestSimulate(true, true);
+ }.bind(this));
+
+ socket.on('disconnect', function() {
+ this._hasSocket = false;
+ }.bind(this));
+
+ socket.on('watch:changed', function(data) {
+ // reload
+ this._requestSimulate(true, true);
+ }.bind(this));
+
+ return socket;
+ };
+
this.getUI = function () {
return this._ui;
};
@@ -61,11 +91,16 @@ exports = Class(function () {
}, this);
};
- this.rebuild = function (cb) {
- this._ui.setBuilding(true);
- // get or update a simulator port with the following options
- return util.ajax
- .get({
+ /** Make a request to /simulate and return a promise */
+ this._requestSimulate = function(softReload, ignoreSocket) {
+ if (this._hasSocket && !ignoreSocket) {
+ // Use the socket connection to make a simulate request as soon as a change occurs
+ return this._requestSimulatePromise || Promise.resolve();
+ }
+
+ // Is there an existing promise or do we need a new one
+ if (!this._requestSimulatePromise) {
+ this._requestSimulatePromise = util.ajax.get({
url: '/api/simulate/',
query: {
app: this._app,
@@ -73,19 +108,54 @@ exports = Class(function () {
deviceId: this.id,
scheme: 'debug',
target: this._buildTarget
- }
+ },
+ async: true
})
.bind(this)
.then(function (res) {
var res = res[0];
- this._ui.setBuilding(false);
- this.setURL(res.url);
+ if (!softReload) {
+ this.setURL(res.url);
+ }
this.loadModules(res.debuggerURLs);
}, function (err) {
logger.error('Unable to simulate', this._app);
console.error(err);
})
- .nodeify(cb);
+ .finally(function() {
+ // Clear the promise so that we run it next time
+ this._requestSimulatePromise = null;
+ });
+ }
+
+ return this._requestSimulatePromise;
+ };
+
+ this.rebuild = function (cb, softReload) {
+ var ui = this._ui;
+ var tasks = [];
+
+ ui.setBuilding(true);
+
+ if (softReload) {
+ tasks.push(ui.softReload()
+ .then(ui.refresh.bind(ui))
+ .catch(function (e) {
+ logger.log("Error with soft reload", e);
+ })
+ );
+ }
+
+ // get or update a simulator port with the following options
+ tasks.push(this._requestSimulate(softReload));
+
+ // Some final clean up
+ var promise = Promise.all(tasks);
+ if (softReload) {
+ promise = promise.then(ui.continueLoad.bind(ui));
+ }
+ prommise = promise.then(function() { ui.setBuilding(false); });
+ return promise.nodeify(cb);
};
this.setURL = function (url) {
diff --git a/src/serve/static/devkitConn.js b/src/serve/static/devkitConn.js
index 0797f852..47042b2d 100644
--- a/src/serve/static/devkitConn.js
+++ b/src/serve/static/devkitConn.js
@@ -44,58 +44,6 @@ function connect(namespace) {
});
}
-/*
-exports = Class(function () {
- this.init = function (controller) {
- this._controller = controller;
-
- connect('/devkit-simulator/', bind(this, '_onSocket'));
- }
-
- this._onSocket = function (err, socket) {
- socket.emit('request:devices');
- socket.on('device', function (info) {
- this._controller.onDeviceConn({
- isLocal: false,
- deviceId: info.deviceId,
- type: info.deviceType,
- userAgent: info.userAgent,
- screen: info.screen,
- conn: new ConnectionWrapper(info.deviceId, socket),
- });
- });
-
- socket.on('liveedit', function (data) {
- console.log(data);
- });
- }
-});
-*/
-
exports.getTransport = function (namespace) {
return connect(namespace);
}
-
-// multiplex TargetCuppa into a single socket.io connection
-// all instances can share the same socket, split by deviceId
-// var ConnectionWrapper = Class(TargetCuppa, function () {
-// this.init = function (deviceId, socket) {
-// supr(this, 'init');
-
-// // handle write calls
-// this.transport = {
-// write: function (data) {
-// socket.emit('send', {
-// deviceId: deviceId
-// });
-// },
-// close: function () {}
-// };
-
-// // handle read calls
-// socket.on('device:' + deviceId, bind(this, 'dataReceived'));
-
-// // notify protocol of connection
-// this.connectionMade();
-// }
-// });
diff --git a/src/serve/static/index.html b/src/serve/static/index.html
index 7b67cc63..95802279 100755
--- a/src/serve/static/index.html
+++ b/src/serve/static/index.html
@@ -19,6 +19,7 @@
+
Game Closure SDK
diff --git a/src/serve/static/ui/Chrome.js b/src/serve/static/ui/Chrome.js
index 4be5790a..77efd06e 100644
--- a/src/serve/static/ui/Chrome.js
+++ b/src/serve/static/ui/Chrome.js
@@ -54,6 +54,7 @@ exports = Class(CenterLayout, function (supr) {
this.init = function (simulator) {
this._simulator = simulator;
this._channel = simulator.api.getChannel('devkit-simulator');
+ this._channel.connect();
this._channel.on('hideSplash', bind(this, 'hideSplash'));
this._channel.on('connect', bind(this, '_onConnect'));
@@ -443,7 +444,22 @@ exports = Class(CenterLayout, function (supr) {
className: 'frame'
});
+ var def = this._newIframeLoadDefer();
+
+ // Listen for bootstrapping
+ // TODO: use the proper channel stuff for this
+ window.addEventListener('message', function(event) {
+ if (event.data === 'bootstrapping') {
+ if (this._iframeLoadDefer) {
+ this._iframeLoadDefer.resolve();
+ this._iframeLoadDefer = undefined;
+ }
+ }
+ }.bind(this));
+
this.update();
+
+ return def.promise;
};
this.getDevicePixelRatio = function () {
@@ -494,7 +510,7 @@ exports = Class(CenterLayout, function (supr) {
};
this.reload = function () {
- this._simulator.rebuild();
+ this._simulator.rebuild(null, true);
// this._frame.contentWindow.reload();
};
@@ -600,14 +616,41 @@ exports = Class(CenterLayout, function (supr) {
this.update();
};
+ /** Reject any old deferred, make a new one. */
+ this._newIframeLoadDefer = function() {
+ if (this._iframeLoadDefer) {
+ this._iframeLoadDefer.reject('another load has been called');
+ }
+ var def = Promise.defer();
+ this._iframeLoadDefer = def;
+ return def;
+ };
+
// restart without rebuilding
this.restart =
this.refresh = function () {
if (this._frame) {
+ var def = this._newIframeLoadDefer();
this._frame.src = this._frame.src;
+ return def.promise;
}
+ return Promise.resolve();
+ };
+
+ this.softReload = function() {
+ return this._channel.request('reload', { partialLoad: true });
};
+ /** Send a partialLoadContinue signal to the inner window, return the promise */
+ this.continueLoad = function() {
+ if (this._frame) {
+ this._frame.contentWindow.postMessage('partialLoadContinue', '*');
+ return Promise.resolve();
+ }
+ return Promise.reject('no iframe set');
+ // return this._channel.request('partialLoadContinue');
+ }
+
this.takeScreenshot = function () {
var win = window.open('', '', 'width=' + (this._screenWidth + 2) + ',height=' + (this._screenHeight + 2));
this._channel.request('screenshot').then(function (res) {
diff --git a/src/serve/static/util/Channel.js b/src/serve/static/util/Channel.js
index 8b8ef6a2..3a2da216 100644
--- a/src/serve/static/util/Channel.js
+++ b/src/serve/static/util/Channel.js
@@ -38,7 +38,6 @@ exports = Class(lib.PubSub, function (supr) {
// internal: set an underlying transport
this.setTransport = function (transport) {
-
if (this._transport && this._transport != transport) {
// tear-down an old transport
this._transport
@@ -95,7 +94,6 @@ exports = Class(lib.PubSub, function (supr) {
case 'connect':
// complete the channel connection
this._sendInternalMessage('connectConfirmed');
-
// fall-through
case 'connectConfirmed':
this._isConnected = true;
@@ -132,7 +130,7 @@ exports = Class(lib.PubSub, function (supr) {
if (this._transport) {
this._transport.emit(this._name, data);
} else {
- logger.warn(this._name, 'failed to send', data);
+ logger.warn(this._name, 'transport not set, failed to send', data);
}
};
diff --git a/src/serve/static/util/socket.io-1.3.5.js b/src/serve/static/util/socket.io-1.3.5.js
new file mode 100644
index 00000000..1d1ad7f9
--- /dev/null
+++ b/src/serve/static/util/socket.io-1.3.5.js
@@ -0,0 +1,3 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.io=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0&&!this.encoding){var pack=this.packetBuffer.shift();this.packet(pack)}};Manager.prototype.cleanup=function(){var sub;while(sub=this.subs.shift())sub.destroy();this.packetBuffer=[];this.encoding=false;this.decoder.destroy()};Manager.prototype.close=Manager.prototype.disconnect=function(){this.skipReconnect=true;this.backoff.reset();this.readyState="closed";this.engine&&this.engine.close()};Manager.prototype.onclose=function(reason){debug("close");this.cleanup();this.backoff.reset();this.readyState="closed";this.emit("close",reason);if(this._reconnection&&!this.skipReconnect){this.reconnect()}};Manager.prototype.reconnect=function(){if(this.reconnecting||this.skipReconnect)return this;var self=this;if(this.backoff.attempts>=this._reconnectionAttempts){debug("reconnect failed");this.backoff.reset();this.emitAll("reconnect_failed");this.reconnecting=false}else{var delay=this.backoff.duration();debug("will wait %dms before reconnect attempt",delay);this.reconnecting=true;var timer=setTimeout(function(){if(self.skipReconnect)return;debug("attempting reconnect");self.emitAll("reconnect_attempt",self.backoff.attempts);self.emitAll("reconnecting",self.backoff.attempts);if(self.skipReconnect)return;self.open(function(err){if(err){debug("reconnect attempt error");self.reconnecting=false;self.reconnect();self.emitAll("reconnect_error",err.data)}else{debug("reconnect success");self.onreconnect()}})},delay);this.subs.push({destroy:function(){clearTimeout(timer)}})}};Manager.prototype.onreconnect=function(){var attempt=this.backoff.attempts;this.reconnecting=false;this.backoff.reset();this.updateSocketIds();this.emitAll("reconnect",attempt)}},{"./on":4,"./socket":5,"./url":6,backo2:7,"component-bind":8,"component-emitter":9,debug:10,"engine.io-client":11,indexof:42,"object-component":43,"socket.io-parser":46}],4:[function(_dereq_,module,exports){module.exports=on;function on(obj,ev,fn){obj.on(ev,fn);return{destroy:function(){obj.removeListener(ev,fn)}}}},{}],5:[function(_dereq_,module,exports){var parser=_dereq_("socket.io-parser");var Emitter=_dereq_("component-emitter");var toArray=_dereq_("to-array");var on=_dereq_("./on");var bind=_dereq_("component-bind");var debug=_dereq_("debug")("socket.io-client:socket");var hasBin=_dereq_("has-binary");module.exports=exports=Socket;var events={connect:1,connect_error:1,connect_timeout:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1};var emit=Emitter.prototype.emit;function Socket(io,nsp){this.io=io;this.nsp=nsp;this.json=this;this.ids=0;this.acks={};if(this.io.autoConnect)this.open();this.receiveBuffer=[];this.sendBuffer=[];this.connected=false;this.disconnected=true}Emitter(Socket.prototype);Socket.prototype.subEvents=function(){if(this.subs)return;var io=this.io;this.subs=[on(io,"open",bind(this,"onopen")),on(io,"packet",bind(this,"onpacket")),on(io,"close",bind(this,"onclose"))]};Socket.prototype.open=Socket.prototype.connect=function(){if(this.connected)return this;this.subEvents();this.io.open();if("open"==this.io.readyState)this.onopen();return this};Socket.prototype.send=function(){var args=toArray(arguments);args.unshift("message");this.emit.apply(this,args);return this};Socket.prototype.emit=function(ev){if(events.hasOwnProperty(ev)){emit.apply(this,arguments);return this}var args=toArray(arguments);var parserType=parser.EVENT;if(hasBin(args)){parserType=parser.BINARY_EVENT}var packet={type:parserType,data:args};if("function"==typeof args[args.length-1]){debug("emitting packet with ack id %d",this.ids);this.acks[this.ids]=args.pop();packet.id=this.ids++}if(this.connected){this.packet(packet)}else{this.sendBuffer.push(packet)}return this};Socket.prototype.packet=function(packet){packet.nsp=this.nsp;this.io.packet(packet)};Socket.prototype.onopen=function(){debug("transport is open - connecting");if("/"!=this.nsp){this.packet({type:parser.CONNECT})}};Socket.prototype.onclose=function(reason){debug("close (%s)",reason);this.connected=false;this.disconnected=true;delete this.id;this.emit("disconnect",reason)};Socket.prototype.onpacket=function(packet){if(packet.nsp!=this.nsp)return;switch(packet.type){case parser.CONNECT:this.onconnect();break;case parser.EVENT:this.onevent(packet);break;case parser.BINARY_EVENT:this.onevent(packet);break;case parser.ACK:this.onack(packet);break;case parser.BINARY_ACK:this.onack(packet);break;case parser.DISCONNECT:this.ondisconnect();break;case parser.ERROR:this.emit("error",packet.data);break}};Socket.prototype.onevent=function(packet){var args=packet.data||[];debug("emitting event %j",args);if(null!=packet.id){debug("attaching ack callback to event");args.push(this.ack(packet.id))}if(this.connected){emit.apply(this,args)}else{this.receiveBuffer.push(args)}};Socket.prototype.ack=function(id){var self=this;var sent=false;return function(){if(sent)return;sent=true;var args=toArray(arguments);debug("sending ack %j",args);var type=hasBin(args)?parser.BINARY_ACK:parser.ACK;self.packet({type:type,id:id,data:args})}};Socket.prototype.onack=function(packet){debug("calling ack %s with %j",packet.id,packet.data);var fn=this.acks[packet.id];fn.apply(this,packet.data);delete this.acks[packet.id]};Socket.prototype.onconnect=function(){this.connected=true;this.disconnected=false;this.emit("connect");this.emitBuffered()};Socket.prototype.emitBuffered=function(){var i;for(i=0;i0&&opts.jitter<=1?opts.jitter:0;this.attempts=0}Backoff.prototype.duration=function(){var ms=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var rand=Math.random();var deviation=Math.floor(rand*this.jitter*ms);ms=(Math.floor(rand*10)&1)==0?ms-deviation:ms+deviation}return Math.min(ms,this.max)|0};Backoff.prototype.reset=function(){this.attempts=0};Backoff.prototype.setMin=function(min){this.ms=min};Backoff.prototype.setMax=function(max){this.max=max};Backoff.prototype.setJitter=function(jitter){this.jitter=jitter}},{}],8:[function(_dereq_,module,exports){var slice=[].slice;module.exports=function(obj,fn){if("string"==typeof fn)fn=obj[fn];if("function"!=typeof fn)throw new Error("bind() requires a function");var args=slice.call(arguments,2);return function(){return fn.apply(obj,args.concat(slice.call(arguments)))}}},{}],9:[function(_dereq_,module,exports){module.exports=Emitter;function Emitter(obj){if(obj)return mixin(obj)}function mixin(obj){for(var key in Emitter.prototype){obj[key]=Emitter.prototype[key]}return obj}Emitter.prototype.on=Emitter.prototype.addEventListener=function(event,fn){this._callbacks=this._callbacks||{};(this._callbacks[event]=this._callbacks[event]||[]).push(fn);return this};Emitter.prototype.once=function(event,fn){var self=this;this._callbacks=this._callbacks||{};function on(){self.off(event,on);fn.apply(this,arguments)}on.fn=fn;this.on(event,on);return this};Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(event,fn){this._callbacks=this._callbacks||{};if(0==arguments.length){this._callbacks={};return this}var callbacks=this._callbacks[event];if(!callbacks)return this;if(1==arguments.length){delete this._callbacks[event];return this}var cb;for(var i=0;i=hour)return(ms/hour).toFixed(1)+"h";if(ms>=min)return(ms/min).toFixed(1)+"m";if(ms>=sec)return(ms/sec|0)+"s";return ms+"ms"};debug.enabled=function(name){for(var i=0,len=debug.skips.length;i';iframe=document.createElement(html)}catch(e){iframe=document.createElement("iframe");iframe.name=self.iframeId;iframe.src="javascript:0"}iframe.id=self.iframeId;self.form.appendChild(iframe);self.iframe=iframe}initIframe();data=data.replace(rEscapedNewline,"\\\n");this.area.value=data.replace(rNewline,"\\n");try{this.form.submit()}catch(e){}if(this.iframe.attachEvent){this.iframe.onreadystatechange=function(){if(self.iframe.readyState=="complete"){complete()}}}else{this.iframe.onload=complete}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./polling":18,"component-inherit":21}],17:[function(_dereq_,module,exports){(function(global){var XMLHttpRequest=_dereq_("xmlhttprequest");var Polling=_dereq_("./polling");var Emitter=_dereq_("component-emitter");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling-xhr");module.exports=XHR;module.exports.Request=Request;function empty(){}function XHR(opts){Polling.call(this,opts);if(global.location){var isSSL="https:"==location.protocol;var port=location.port;if(!port){port=isSSL?443:80}this.xd=opts.hostname!=global.location.hostname||port!=opts.port;this.xs=opts.secure!=isSSL}}inherit(XHR,Polling);XHR.prototype.supportsBinary=true;XHR.prototype.request=function(opts){opts=opts||{};opts.uri=this.uri();opts.xd=this.xd;opts.xs=this.xs;opts.agent=this.agent||false;opts.supportsBinary=this.supportsBinary;opts.enablesXDR=this.enablesXDR;opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;return new Request(opts)};XHR.prototype.doWrite=function(data,fn){var isBinary=typeof data!=="string"&&data!==undefined;var req=this.request({method:"POST",data:data,isBinary:isBinary});var self=this;req.on("success",fn);req.on("error",function(err){self.onError("xhr post error",err)});this.sendXhr=req};XHR.prototype.doPoll=function(){debug("xhr poll");var req=this.request();var self=this;req.on("data",function(data){self.onData(data)});req.on("error",function(err){self.onError("xhr poll error",err)});this.pollXhr=req};function Request(opts){this.method=opts.method||"GET";this.uri=opts.uri;this.xd=!!opts.xd;this.xs=!!opts.xs;this.async=false!==opts.async;this.data=undefined!=opts.data?opts.data:null;this.agent=opts.agent;this.isBinary=opts.isBinary;this.supportsBinary=opts.supportsBinary;this.enablesXDR=opts.enablesXDR;this.pfx=opts.pfx;this.key=opts.key;this.passphrase=opts.passphrase;this.cert=opts.cert;this.ca=opts.ca;this.ciphers=opts.ciphers;this.rejectUnauthorized=opts.rejectUnauthorized;this.create()}Emitter(Request.prototype);Request.prototype.create=function(){var opts={agent:this.agent,xdomain:this.xd,xscheme:this.xs,enablesXDR:this.enablesXDR};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;var xhr=this.xhr=new XMLHttpRequest(opts);var self=this;try{debug("xhr open %s: %s",this.method,this.uri);xhr.open(this.method,this.uri,this.async);if(this.supportsBinary){xhr.responseType="arraybuffer"}if("POST"==this.method){try{if(this.isBinary){xhr.setRequestHeader("Content-type","application/octet-stream")}else{xhr.setRequestHeader("Content-type","text/plain;charset=UTF-8")}}catch(e){}}if("withCredentials"in xhr){xhr.withCredentials=true}if(this.hasXDR()){xhr.onload=function(){self.onLoad()};xhr.onerror=function(){self.onError(xhr.responseText)}}else{xhr.onreadystatechange=function(){if(4!=xhr.readyState)return;if(200==xhr.status||1223==xhr.status){self.onLoad()}else{setTimeout(function(){self.onError(xhr.status)},0)}}}debug("xhr data %s",this.data);xhr.send(this.data)}catch(e){setTimeout(function(){self.onError(e)},0);return}if(global.document){this.index=Request.requestsCount++;Request.requests[this.index]=this}};Request.prototype.onSuccess=function(){this.emit("success");this.cleanup()};Request.prototype.onData=function(data){this.emit("data",data);this.onSuccess()};Request.prototype.onError=function(err){this.emit("error",err);this.cleanup(true)};Request.prototype.cleanup=function(fromError){if("undefined"==typeof this.xhr||null===this.xhr){return}if(this.hasXDR()){this.xhr.onload=this.xhr.onerror=empty}else{this.xhr.onreadystatechange=empty}if(fromError){try{this.xhr.abort()}catch(e){}}if(global.document){delete Request.requests[this.index]}this.xhr=null};Request.prototype.onLoad=function(){var data;try{var contentType;try{contentType=this.xhr.getResponseHeader("Content-Type").split(";")[0]}catch(e){}if(contentType==="application/octet-stream"){data=this.xhr.response}else{if(!this.supportsBinary){data=this.xhr.responseText}else{data="ok"}}}catch(e){this.onError(e)}if(null!=data){this.onData(data)}};Request.prototype.hasXDR=function(){return"undefined"!==typeof global.XDomainRequest&&!this.xs&&this.enablesXDR};Request.prototype.abort=function(){this.cleanup()};if(global.document){Request.requestsCount=0;Request.requests={};if(global.attachEvent){global.attachEvent("onunload",unloadHandler)}else if(global.addEventListener){global.addEventListener("beforeunload",unloadHandler,false)}}function unloadHandler(){for(var i in Request.requests){if(Request.requests.hasOwnProperty(i)){Request.requests[i].abort()}}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./polling":18,"component-emitter":9,"component-inherit":21,debug:22,xmlhttprequest:20}],18:[function(_dereq_,module,exports){var Transport=_dereq_("../transport");var parseqs=_dereq_("parseqs");var parser=_dereq_("engine.io-parser");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:polling");module.exports=Polling;var hasXHR2=function(){var XMLHttpRequest=_dereq_("xmlhttprequest");var xhr=new XMLHttpRequest({xdomain:false});return null!=xhr.responseType}();function Polling(opts){var forceBase64=opts&&opts.forceBase64;if(!hasXHR2||forceBase64){this.supportsBinary=false}Transport.call(this,opts)}inherit(Polling,Transport);Polling.prototype.name="polling";Polling.prototype.doOpen=function(){this.poll()};Polling.prototype.pause=function(onPause){var pending=0;var self=this;this.readyState="pausing";function pause(){debug("paused");self.readyState="paused";onPause()}if(this.polling||!this.writable){var total=0;if(this.polling){debug("we are currently polling - waiting to pause");total++;this.once("pollComplete",function(){debug("pre-pause polling complete");--total||pause()})}if(!this.writable){debug("we are currently writing - waiting to pause");total++;this.once("drain",function(){debug("pre-pause writing complete");--total||pause()})}}else{pause()}};Polling.prototype.poll=function(){debug("polling");this.polling=true;this.doPoll();this.emit("poll")};Polling.prototype.onData=function(data){var self=this;debug("polling got data %s",data);var callback=function(packet,index,total){if("opening"==self.readyState){self.onOpen()}if("close"==packet.type){self.onClose();return false}self.onPacket(packet)};parser.decodePayload(data,this.socket.binaryType,callback);if("closed"!=this.readyState){this.polling=false;this.emit("pollComplete");if("open"==this.readyState){this.poll()}else{debug('ignoring poll - transport state "%s"',this.readyState)}}};Polling.prototype.doClose=function(){var self=this;function close(){debug("writing close packet");self.write([{type:"close"}])}if("open"==this.readyState){debug("transport open - closing");close()}else{debug("transport not open - deferring close");this.once("open",close)}};Polling.prototype.write=function(packets){var self=this;this.writable=false;var callbackfn=function(){self.writable=true;self.emit("drain")};var self=this;parser.encodePayload(packets,this.supportsBinary,function(data){self.doWrite(data,callbackfn)})};Polling.prototype.uri=function(){var query=this.query||{};var schema=this.secure?"https":"http";var port="";if(false!==this.timestampRequests){query[this.timestampParam]=+new Date+"-"+Transport.timestamps++}if(!this.supportsBinary&&!query.sid){query.b64=1}query=parseqs.encode(query);if(this.port&&("https"==schema&&this.port!=443||"http"==schema&&this.port!=80)){port=":"+this.port}if(query.length){query="?"+query}return schema+"://"+this.hostname+port+this.path+query}},{"../transport":14,"component-inherit":21,debug:22,"engine.io-parser":25,parseqs:35,xmlhttprequest:20}],19:[function(_dereq_,module,exports){var Transport=_dereq_("../transport");var parser=_dereq_("engine.io-parser");var parseqs=_dereq_("parseqs");var inherit=_dereq_("component-inherit");var debug=_dereq_("debug")("engine.io-client:websocket");var WebSocket=_dereq_("ws");module.exports=WS;function WS(opts){var forceBase64=opts&&opts.forceBase64;if(forceBase64){this.supportsBinary=false}Transport.call(this,opts)}inherit(WS,Transport);WS.prototype.name="websocket";WS.prototype.supportsBinary=true;WS.prototype.doOpen=function(){if(!this.check()){return}var self=this;var uri=this.uri();var protocols=void 0;var opts={agent:this.agent};opts.pfx=this.pfx;opts.key=this.key;opts.passphrase=this.passphrase;opts.cert=this.cert;opts.ca=this.ca;opts.ciphers=this.ciphers;opts.rejectUnauthorized=this.rejectUnauthorized;this.ws=new WebSocket(uri,protocols,opts);if(this.ws.binaryType===undefined){this.supportsBinary=false}this.ws.binaryType="arraybuffer";this.addEventListeners()};WS.prototype.addEventListeners=function(){var self=this;this.ws.onopen=function(){self.onOpen()};this.ws.onclose=function(){self.onClose()};this.ws.onmessage=function(ev){self.onData(ev.data)};this.ws.onerror=function(e){self.onError("websocket error",e)}};if("undefined"!=typeof navigator&&/iPad|iPhone|iPod/i.test(navigator.userAgent)){WS.prototype.onData=function(data){var self=this;setTimeout(function(){Transport.prototype.onData.call(self,data)},0)}}WS.prototype.write=function(packets){var self=this;this.writable=false;for(var i=0,l=packets.length;i=31}exports.formatters.j=function(v){return JSON.stringify(v)};function formatArgs(){var args=arguments;var useColors=this.useColors;args[0]=(useColors?"%c":"")+this.namespace+(useColors?" %c":" ")+args[0]+(useColors?"%c ":" ")+"+"+exports.humanize(this.diff);if(!useColors)return args;var c="color: "+this.color;args=[args[0],c,"color: inherit"].concat(Array.prototype.slice.call(args,1));var index=0;var lastC=0;args[0].replace(/%[a-z%]/g,function(match){if("%"===match)return;index++;if("%c"===match){lastC=index}});args.splice(lastC,0,c);return args}function log(){return"object"==typeof console&&"function"==typeof console.log&&Function.prototype.apply.call(console.log,console,arguments)}function save(namespaces){try{if(null==namespaces){localStorage.removeItem("debug")}else{localStorage.debug=namespaces}}catch(e){}}function load(){var r;try{r=localStorage.debug}catch(e){}return r}exports.enable(load())},{"./debug":23}],23:[function(_dereq_,module,exports){exports=module.exports=debug;exports.coerce=coerce;exports.disable=disable;exports.enable=enable;exports.enabled=enabled;exports.humanize=_dereq_("ms");exports.names=[];exports.skips=[];exports.formatters={};var prevColor=0;var prevTime;function selectColor(){return exports.colors[prevColor++%exports.colors.length]}function debug(namespace){function disabled(){}disabled.enabled=false;function enabled(){var self=enabled;var curr=+new Date;var ms=curr-(prevTime||curr);self.diff=ms;self.prev=prevTime;self.curr=curr;prevTime=curr;if(null==self.useColors)self.useColors=exports.useColors();if(null==self.color&&self.useColors)self.color=selectColor();var args=Array.prototype.slice.call(arguments);args[0]=exports.coerce(args[0]);if("string"!==typeof args[0]){args=["%o"].concat(args)}var index=0;args[0]=args[0].replace(/%([a-z%])/g,function(match,format){if(match==="%")return match;index++;var formatter=exports.formatters[format];if("function"===typeof formatter){var val=args[index];match=formatter.call(self,val);args.splice(index,1);index--}return match});if("function"===typeof exports.formatArgs){args=exports.formatArgs.apply(self,args)}var logFn=enabled.log||exports.log||console.log.bind(console);logFn.apply(self,args)}enabled.enabled=true;var fn=exports.enabled(namespace)?enabled:disabled;fn.namespace=namespace;return fn}function enable(namespaces){exports.save(namespaces);var split=(namespaces||"").split(/[\s,]+/);var len=split.length;for(var i=0;i=d)return Math.round(ms/d)+"d";if(ms>=h)return Math.round(ms/h)+"h";if(ms>=m)return Math.round(ms/m)+"m";if(ms>=s)return Math.round(ms/s)+"s";return ms+"ms"}function long(ms){return plural(ms,d,"day")||plural(ms,h,"hour")||plural(ms,m,"minute")||plural(ms,s,"second")||ms+" ms"}function plural(ms,n,name){if(ms1){return{type:packetslist[type],data:data.substring(1)}}else{return{type:packetslist[type]}}}var asArray=new Uint8Array(data);var type=asArray[0];var rest=sliceBuffer(data,1);if(Blob&&binaryType==="blob"){rest=new Blob([rest])}return{type:packetslist[type],data:rest}};exports.decodeBase64Packet=function(msg,binaryType){var type=packetslist[msg.charAt(0)];if(!global.ArrayBuffer){return{type:type,data:{base64:true,data:msg.substr(1)}}}var data=base64encoder.decode(msg.substr(1));if(binaryType==="blob"&&Blob){data=new Blob([data])}return{type:type,data:data}};exports.encodePayload=function(packets,supportsBinary,callback){if(typeof supportsBinary=="function"){callback=supportsBinary;supportsBinary=null}var isBinary=hasBinary(packets);if(supportsBinary&&isBinary){if(Blob&&!dontSendBlobs){return exports.encodePayloadAsBlob(packets,callback)}return exports.encodePayloadAsArrayBuffer(packets,callback)}if(!packets.length){return callback("0:")}function setLengthHeader(message){return message.length+":"+message}function encodeOne(packet,doneCallback){exports.encodePacket(packet,!isBinary?false:supportsBinary,true,function(message){doneCallback(null,setLengthHeader(message))})}map(packets,encodeOne,function(err,results){return callback(results.join(""))})};function map(ary,each,done){var result=new Array(ary.length);var next=after(ary.length,done);var eachWithIndex=function(i,el,cb){each(el,function(error,msg){result[i]=msg;cb(error,result)})};for(var i=0;i0){var tailArray=new Uint8Array(bufferTail);var isString=tailArray[0]===0;var msgLength="";for(var i=1;;i++){if(tailArray[i]==255)break;if(msgLength.length>310){numberTooLong=true;break}msgLength+=tailArray[i]}if(numberTooLong)return callback(err,0,1);bufferTail=sliceBuffer(bufferTail,2+msgLength.length);msgLength=parseInt(msgLength);var msg=sliceBuffer(bufferTail,0,msgLength);if(isString){try{msg=String.fromCharCode.apply(null,new Uint8Array(msg))}catch(e){var typed=new Uint8Array(msg);msg="";for(var i=0;ibytes){end=bytes}if(start>=bytes||start>=end||bytes===0){return new ArrayBuffer(0)}var abv=new Uint8Array(arraybuffer);var result=new Uint8Array(end-start);for(var i=start,ii=0;i>2];base64+=chars[(bytes[i]&3)<<4|bytes[i+1]>>4];base64+=chars[(bytes[i+1]&15)<<2|bytes[i+2]>>6];base64+=chars[bytes[i+2]&63]}if(len%3===2){base64=base64.substring(0,base64.length-1)+"="}else if(len%3===1){base64=base64.substring(0,base64.length-2)+"=="}return base64};exports.decode=function(base64){var bufferLength=base64.length*.75,len=base64.length,i,p=0,encoded1,encoded2,encoded3,encoded4;if(base64[base64.length-1]==="="){bufferLength--;if(base64[base64.length-2]==="="){bufferLength--}}var arraybuffer=new ArrayBuffer(bufferLength),bytes=new Uint8Array(arraybuffer);for(i=0;i>4;bytes[p++]=(encoded2&15)<<4|encoded3>>2;bytes[p++]=(encoded3&3)<<6|encoded4&63}return arraybuffer}})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")},{}],30:[function(_dereq_,module,exports){(function(global){var BlobBuilder=global.BlobBuilder||global.WebKitBlobBuilder||global.MSBlobBuilder||global.MozBlobBuilder;var blobSupported=function(){try{var b=new Blob(["hi"]);return b.size==2}catch(e){return false}}();var blobBuilderSupported=BlobBuilder&&BlobBuilder.prototype.append&&BlobBuilder.prototype.getBlob;function BlobBuilderConstructor(ary,options){options=options||{};var bb=new BlobBuilder;for(var i=0;i=55296&&value<=56319&&counter65535){value-=65536;
+output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value)}return output}function createByte(codePoint,shift){return stringFromCharCode(codePoint>>shift&63|128)}function encodeCodePoint(codePoint){if((codePoint&4294967168)==0){return stringFromCharCode(codePoint)}var symbol="";if((codePoint&4294965248)==0){symbol=stringFromCharCode(codePoint>>6&31|192)}else if((codePoint&4294901760)==0){symbol=stringFromCharCode(codePoint>>12&15|224);symbol+=createByte(codePoint,6)}else if((codePoint&4292870144)==0){symbol=stringFromCharCode(codePoint>>18&7|240);symbol+=createByte(codePoint,12);symbol+=createByte(codePoint,6)}symbol+=stringFromCharCode(codePoint&63|128);return symbol}function utf8encode(string){var codePoints=ucs2decode(string);var length=codePoints.length;var index=-1;var codePoint;var byteString="";while(++index=byteCount){throw Error("Invalid byte index")}var continuationByte=byteArray[byteIndex]&255;byteIndex++;if((continuationByte&192)==128){return continuationByte&63}throw Error("Invalid continuation byte")}function decodeSymbol(){var byte1;var byte2;var byte3;var byte4;var codePoint;if(byteIndex>byteCount){throw Error("Invalid byte index")}if(byteIndex==byteCount){return false}byte1=byteArray[byteIndex]&255;byteIndex++;if((byte1&128)==0){return byte1}if((byte1&224)==192){var byte2=readContinuationByte();codePoint=(byte1&31)<<6|byte2;if(codePoint>=128){return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&240)==224){byte2=readContinuationByte();byte3=readContinuationByte();codePoint=(byte1&15)<<12|byte2<<6|byte3;if(codePoint>=2048){return codePoint}else{throw Error("Invalid continuation byte")}}if((byte1&248)==240){byte2=readContinuationByte();byte3=readContinuationByte();byte4=readContinuationByte();codePoint=(byte1&15)<<18|byte2<<12|byte3<<6|byte4;if(codePoint>=65536&&codePoint<=1114111){return codePoint}}throw Error("Invalid UTF-8 detected")}var byteArray;var byteCount;var byteIndex;function utf8decode(byteString){byteArray=ucs2decode(byteString);byteCount=byteArray.length;byteIndex=0;var codePoints=[];var tmp;while((tmp=decodeSymbol())!==false){codePoints.push(tmp)}return ucs2encode(codePoints)}var utf8={version:"2.0.0",encode:utf8encode,decode:utf8decode};if(typeof define=="function"&&typeof define.amd=="object"&&define.amd){define(function(){return utf8})}else if(freeExports&&!freeExports.nodeType){if(freeModule){freeModule.exports=utf8}else{var object={};var hasOwnProperty=object.hasOwnProperty;for(var key in utf8){hasOwnProperty.call(utf8,key)&&(freeExports[key]=utf8[key])}}}else{root.utf8=utf8}})(this)}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],34:[function(_dereq_,module,exports){(function(global){var rvalidchars=/^[\],:{}\s]*$/;var rvalidescape=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;var rvalidtokens=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;var rvalidbraces=/(?:^|:|,)(?:\s*\[)+/g;var rtrimLeft=/^\s+/;var rtrimRight=/\s+$/;module.exports=function parsejson(data){if("string"!=typeof data||!data){return null}data=data.replace(rtrimLeft,"").replace(rtrimRight,"");if(global.JSON&&JSON.parse){return JSON.parse(data)}if(rvalidchars.test(data.replace(rvalidescape,"@").replace(rvalidtokens,"]").replace(rvalidbraces,""))){return new Function("return "+data)()}}}).call(this,typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],35:[function(_dereq_,module,exports){exports.encode=function(obj){var str="";for(var i in obj){if(obj.hasOwnProperty(i)){if(str.length)str+="&";str+=encodeURIComponent(i)+"="+encodeURIComponent(obj[i])}}return str};exports.decode=function(qs){var qry={};var pairs=qs.split("&");for(var i=0,l=pairs.length;i1)))/4)-floor((year-1901+month)/100)+floor((year-1601+month)/400)}}if(!(isProperty={}.hasOwnProperty)){isProperty=function(property){var members={},constructor;if((members.__proto__=null,members.__proto__={toString:1},members).toString!=getClass){isProperty=function(property){var original=this.__proto__,result=property in(this.__proto__=null,this);this.__proto__=original;return result}}else{constructor=members.constructor;isProperty=function(property){var parent=(this.constructor||constructor).prototype;return property in this&&!(property in parent&&this[property]===parent[property])}}members=null;return isProperty.call(this,property)}}var PrimitiveTypes={"boolean":1,number:1,string:1,undefined:1};var isHostType=function(object,property){var type=typeof object[property];return type=="object"?!!object[property]:!PrimitiveTypes[type]};forEach=function(object,callback){var size=0,Properties,members,property;(Properties=function(){this.valueOf=0}).prototype.valueOf=0;members=new Properties;for(property in members){if(isProperty.call(members,property)){size++}}Properties=members=null;if(!size){members=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,length;var hasProperty=!isFunction&&typeof object.constructor!="function"&&isHostType(object,"hasOwnProperty")?object.hasOwnProperty:isProperty;for(property in object){if(!(isFunction&&property=="prototype")&&hasProperty.call(object,property)){callback(property)}}for(length=members.length;property=members[--length];hasProperty.call(object,property)&&callback(property));}}else if(size==2){forEach=function(object,callback){var members={},isFunction=getClass.call(object)==functionClass,property;for(property in object){if(!(isFunction&&property=="prototype")&&!isProperty.call(members,property)&&(members[property]=1)&&isProperty.call(object,property)){callback(property)}}}}else{forEach=function(object,callback){var isFunction=getClass.call(object)==functionClass,property,isConstructor;for(property in object){if(!(isFunction&&property=="prototype")&&isProperty.call(object,property)&&!(isConstructor=property==="constructor")){callback(property)}}if(isConstructor||isProperty.call(object,property="constructor")){callback(property)}}}return forEach(object,callback)};if(!has("json-stringify")){var Escapes={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"};var leadingZeroes="000000";var toPaddedString=function(width,value){return(leadingZeroes+(value||0)).slice(-width)};var unicodePrefix="\\u00";var quote=function(value){var result='"',index=0,length=value.length,isLarge=length>10&&charIndexBuggy,symbols;if(isLarge){symbols=value.split("")}for(;index-1/0&&value<1/0){if(getDay){date=floor(value/864e5);for(year=floor(date/365.2425)+1970-1;getDay(year+1,0)<=date;year++);for(month=floor((date-getDay(year,0))/30.42);getDay(year,month+1)<=date;month++);date=1+date-getDay(year,month);time=(value%864e5+864e5)%864e5;hours=floor(time/36e5)%24;minutes=floor(time/6e4)%60;seconds=floor(time/1e3)%60;milliseconds=time%1e3}else{year=value.getUTCFullYear();month=value.getUTCMonth();date=value.getUTCDate();hours=value.getUTCHours();minutes=value.getUTCMinutes();seconds=value.getUTCSeconds();milliseconds=value.getUTCMilliseconds()}value=(year<=0||year>=1e4?(year<0?"-":"+")+toPaddedString(6,year<0?-year:year):toPaddedString(4,year))+"-"+toPaddedString(2,month+1)+"-"+toPaddedString(2,date)+"T"+toPaddedString(2,hours)+":"+toPaddedString(2,minutes)+":"+toPaddedString(2,seconds)+"."+toPaddedString(3,milliseconds)+"Z"}else{value=null}}else if(typeof value.toJSON=="function"&&(className!=numberClass&&className!=stringClass&&className!=arrayClass||isProperty.call(value,"toJSON"))){value=value.toJSON(property)}}if(callback){value=callback.call(object,property,value)}if(value===null){return"null"}className=getClass.call(value);if(className==booleanClass){return""+value}else if(className==numberClass){return value>-1/0&&value<1/0?""+value:"null"}else if(className==stringClass){return quote(""+value)}if(typeof value=="object"){for(length=stack.length;length--;){if(stack[length]===value){throw TypeError()}}stack.push(value);results=[];prefix=indentation;indentation+=whitespace;if(className==arrayClass){for(index=0,length=value.length;index0){for(whitespace="",width>10&&(width=10);whitespace.length=48&&charCode<=57||charCode>=97&&charCode<=102||charCode>=65&&charCode<=70)){abort()}}value+=fromCharCode("0x"+source.slice(begin,Index));break;default:abort()}}else{if(charCode==34){break}charCode=source.charCodeAt(Index);begin=Index;while(charCode>=32&&charCode!=92&&charCode!=34){charCode=source.charCodeAt(++Index)}value+=source.slice(begin,Index)}}if(source.charCodeAt(Index)==34){Index++;return value}abort();default:begin=Index;if(charCode==45){isSigned=true;charCode=source.charCodeAt(++Index)}if(charCode>=48&&charCode<=57){if(charCode==48&&(charCode=source.charCodeAt(Index+1),charCode>=48&&charCode<=57)){abort()}isSigned=false;for(;Index=48&&charCode<=57);Index++);if(source.charCodeAt(Index)==46){position=++Index;for(;position=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}charCode=source.charCodeAt(Index);if(charCode==101||charCode==69){charCode=source.charCodeAt(++Index);if(charCode==43||charCode==45){Index++}for(position=Index;position=48&&charCode<=57);position++);if(position==Index){abort()}Index=position}return+source.slice(begin,Index)}if(isSigned){abort()}if(source.slice(Index,Index+4)=="true"){Index+=4;return true}else if(source.slice(Index,Index+5)=="false"){Index+=5;return false}else if(source.slice(Index,Index+4)=="null"){Index+=4;return null}abort()}}return"$"};var get=function(value){var results,hasMembers;if(value=="$"){abort()}if(typeof value=="string"){if((charIndexBuggy?value.charAt(0):value[0])=="@"){return value.slice(1)}if(value=="["){results=[];for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="]"){break}if(hasMembers){if(value==","){value=lex();if(value=="]"){abort()}}else{abort()}}if(value==","){abort()}results.push(get(value))}return results}else if(value=="{"){results={};for(;;hasMembers||(hasMembers=true)){value=lex();if(value=="}"){break}if(hasMembers){if(value==","){value=lex();if(value=="}"){abort()}}else{abort()}}if(value==","||typeof value!="string"||(charIndexBuggy?value.charAt(0):value[0])!="@"||lex()!=":"){abort()}results[value.slice(1)]=get(lex())}return results}abort()}return value};var update=function(source,property,callback){var element=walk(source,property,callback);if(element===undef){delete source[property]}else{source[property]=element}};var walk=function(source,property,callback){var value=source[property],length;if(typeof value=="object"&&value){if(getClass.call(value)==arrayClass){for(length=value.length;length--;){update(value,length,callback)}}else{forEach(value,function(property){update(value,property,callback)})}}return callback.call(source,property,value)};JSON3.parse=function(source,callback){var result,value;Index=0;Source=""+source;result=get(lex());if(lex()!="$"){abort()}Index=Source=null;return callback&&getClass.call(callback)==functionClass?walk((value={},value[""]=result,value),"",callback):result}}}if(isLoader){define(function(){return JSON3})}})(this)},{}],50:[function(_dereq_,module,exports){module.exports=toArray;function toArray(list,index){var array=[];index=index||0;for(var i=index||0;i