Izmjenjena struktura, dodan backand

This commit is contained in:
GotPPay
2017-10-16 11:19:46 +02:00
parent 1ec88afacb
commit 048e32c4aa
37153 changed files with 2975854 additions and 1 deletions

20
web/node_modules/webpack-dev-server/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
Copyright JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

86
web/node_modules/webpack-dev-server/README.md generated vendored Normal file
View File

@@ -0,0 +1,86 @@
# webpack-dev-server
[![npm][npm]][npm-url]
[![node][node]][node-url]
[![deps][deps]][deps-url]
[![tests][tests]][tests-url]
[![coverage][cover]][cover-url]
[![chat][chat]][chat-url]
<div align="center">
<a href="https://github.com/webpack/webpack">
<img width="200" height="200"
src="https://webpack.js.org/assets/icon-square-big.svg">
</a>
<h1>webpack Dev Server</h1>
</div>
Use [webpack](https://webpack.js.org) with a development server that provides live reloading. This should be used for **development only**.
It uses [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) under the hood, which provides fast in-memory access to the webpack assets.
<h2 align="center">Install</h2>
```
npm install webpack-dev-server --save-dev
```
<h2 align="center">Usage</h2>
The easiest way to use it is with the CLI. In the directory where your `webpack.config.js` is, run:
```
node_modules/.bin/webpack-dev-server
```
This will start a server, listening on connections from `localhost` on port `8080`.
Now, when you change something in your assets, it should live-reload the files.
See [**the documentation**](https://webpack.js.org/configuration/dev-server/#devserver) for more use cases and options.
<h2 align="center">Contributing</h2>
We appreciate all help! Check out [CONTRIBUTING.md](CONTRIBUTING.md) for more information on how to help.
<h2 align="center">Maintainers</h2>
<table>
<tbody>
<tr>
<td align="center">
<img width="150" height="150"
src="https://avatars.githubusercontent.com/SpaceK33z?v=3">
<br />
<a href="https://github.com/">Kees Kluskens</a>
</td>
</tr>
</tbody>
</table>
<h2 align="center">Inspiration</h2>
This project is heavily inspired by [peerigon/nof5](https://github.com/peerigon/nof5).
<h2 align="center">LICENSE</h2>
#### [MIT](./LICENSE)
[npm]: https://img.shields.io/npm/v/webpack-dev-server.svg
[npm-url]: https://npmjs.com/package/webpack-dev-server
[node]: https://img.shields.io/node/v/webpack-dev-server.svg
[node-url]: https://nodejs.org
[deps]: https://david-dm.org/webpack/webpack-dev-server.svg
[deps-url]: https://david-dm.org/webpack/webpack-dev-server
[tests]: http://img.shields.io/travis/webpack/webpack-dev-server.svg
[tests-url]: https://travis-ci.org/webpack/webpack-dev-server
[cover]: https://codecov.io/gh/webpack/webpack-dev-server/branch/master/graph/badge.svg
[cover-url]: https://codecov.io/gh/webpack/webpack-dev-server
[chat]: https://badges.gitter.im/webpack/webpack.svg
[chat-url]: https://gitter.im/webpack/webpack

430
web/node_modules/webpack-dev-server/bin/webpack-dev-server.js generated vendored Executable file
View File

@@ -0,0 +1,430 @@
#!/usr/bin/env node
"use strict";
const path = require("path");
const open = require("opn");
const fs = require("fs");
const net = require("net");
const portfinder = require("portfinder");
const addDevServerEntrypoints = require("../lib/util/addDevServerEntrypoints");
const createDomain = require("../lib/util/createDomain");
// Local version replaces global one
try {
const localWebpackDevServer = require.resolve(path.join(process.cwd(), "node_modules", "webpack-dev-server", "bin", "webpack-dev-server.js"));
if(__filename !== localWebpackDevServer) {
return require(localWebpackDevServer);
}
} catch(e) {}
const Server = require("../lib/Server");
const webpack = require("webpack");
function versionInfo() {
return `webpack-dev-server ${require("../package.json").version}\n` +
`webpack ${require("webpack/package.json").version}`;
}
function colorInfo(useColor, msg) {
if(useColor)
// Make text blue and bold, so it *pops*
return `\u001b[1m\u001b[34m${msg}\u001b[39m\u001b[22m`;
return msg;
}
function colorError(useColor, msg) {
if(useColor)
// Make text red and bold, so it *pops*
return `\u001b[1m\u001b[31m${msg}\u001b[39m\u001b[22m`;
return msg;
}
const yargs = require("yargs")
.usage(`${versionInfo()
}\nUsage: https://webpack.js.org/configuration/dev-server/`);
require("webpack/bin/config-yargs")(yargs);
// It is important that this is done after the webpack yargs config,
// so it overrides webpack's version info.
yargs.version(versionInfo);
const ADVANCED_GROUP = "Advanced options:";
const DISPLAY_GROUP = "Stats options:";
const SSL_GROUP = "SSL options:";
const CONNECTION_GROUP = "Connection options:";
const RESPONSE_GROUP = "Response options:";
const BASIC_GROUP = "Basic options:";
// Taken out of yargs because we must know if
// it wasn't given by the user, in which case
// we should use portfinder.
const DEFAULT_PORT = 8080;
yargs.options({
"lazy": {
type: "boolean",
describe: "Lazy"
},
"inline": {
type: "boolean",
default: true,
describe: "Inline mode (set to false to disable including client scripts like livereload)"
},
"progress": {
type: "boolean",
describe: "Print compilation progress in percentage",
group: BASIC_GROUP
},
"hot-only": {
type: "boolean",
describe: "Do not refresh page if HMR fails",
group: ADVANCED_GROUP
},
"stdin": {
type: "boolean",
describe: "close when stdin ends"
},
"open": {
type: "boolean",
describe: "Open default browser"
},
"color": {
type: "boolean",
alias: "colors",
default: function supportsColor() {
return require("supports-color");
},
group: DISPLAY_GROUP,
describe: "Enables/Disables colors on the console"
},
"info": {
type: "boolean",
group: DISPLAY_GROUP,
default: true,
describe: "Info"
},
"quiet": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Quiet"
},
"client-log-level": {
type: "string",
group: DISPLAY_GROUP,
default: "info",
describe: "Log level in the browser (info, warning, error or none)"
},
"https": {
type: "boolean",
group: SSL_GROUP,
describe: "HTTPS"
},
"key": {
type: "string",
describe: "Path to a SSL key.",
group: SSL_GROUP
},
"cert": {
type: "string",
describe: "Path to a SSL certificate.",
group: SSL_GROUP
},
"cacert": {
type: "string",
describe: "Path to a SSL CA certificate.",
group: SSL_GROUP
},
"pfx": {
type: "string",
describe: "Path to a SSL pfx file.",
group: SSL_GROUP
},
"pfx-passphrase": {
type: "string",
describe: "Passphrase for pfx file.",
group: SSL_GROUP
},
"content-base": {
type: "string",
describe: "A directory or URL to serve HTML content from.",
group: RESPONSE_GROUP
},
"watch-content-base": {
type: "boolean",
describe: "Enable live-reloading of the content-base.",
group: RESPONSE_GROUP
},
"history-api-fallback": {
type: "boolean",
describe: "Fallback to /index.html for Single Page Applications.",
group: RESPONSE_GROUP
},
"compress": {
type: "boolean",
describe: "Enable gzip compression",
group: RESPONSE_GROUP
},
"port": {
describe: "The port",
group: CONNECTION_GROUP
},
"socket": {
type: "String",
describe: "Socket to listen",
group: CONNECTION_GROUP
},
"public": {
type: "string",
describe: "The public hostname/ip address of the server",
group: CONNECTION_GROUP
},
"host": {
type: "string",
default: "localhost",
describe: "The hostname/ip address the server will bind to",
group: CONNECTION_GROUP
}
});
const argv = yargs.argv;
const wpOpt = require("webpack/bin/convert-argv")(yargs, argv, {
outputFilename: "/bundle.js"
});
function processOptions(wpOpt) {
// process Promise
if(typeof wpOpt.then === "function") {
wpOpt.then(processOptions).catch(function(err) {
console.error(err.stack || err);
process.exit(); // eslint-disable-line
});
return;
}
const firstWpOpt = Array.isArray(wpOpt) ? wpOpt[0] : wpOpt;
const options = wpOpt.devServer || firstWpOpt.devServer || {};
if(argv.host !== "localhost" || !options.host)
options.host = argv.host;
if(argv.public)
options.public = argv.public;
if(argv.socket)
options.socket = argv.socket;
if(!options.publicPath) {
options.publicPath = firstWpOpt.output && firstWpOpt.output.publicPath || "";
if(!/^(https?:)?\/\//.test(options.publicPath) && options.publicPath[0] !== "/")
options.publicPath = `/${options.publicPath}`;
}
if(!options.filename)
options.filename = firstWpOpt.output && firstWpOpt.output.filename;
if(!options.watchOptions)
options.watchOptions = firstWpOpt.watchOptions;
if(argv["stdin"]) {
process.stdin.on("end", function() {
process.exit(0); // eslint-disable-line no-process-exit
});
process.stdin.resume();
}
if(!options.hot)
options.hot = argv["hot"];
if(!options.hotOnly)
options.hotOnly = argv["hot-only"];
if(!options.clientLogLevel)
options.clientLogLevel = argv["client-log-level"];
if(options.contentBase === undefined) {
if(argv["content-base"]) {
options.contentBase = argv["content-base"];
if(Array.isArray(options.contentBase)) {
options.contentBase = options.contentBase.map(function(val) {
return path.resolve(val);
});
} else if(/^[0-9]$/.test(options.contentBase))
options.contentBase = +options.contentBase;
else if(!/^(https?:)?\/\//.test(options.contentBase))
options.contentBase = path.resolve(options.contentBase);
// It is possible to disable the contentBase by using `--no-content-base`, which results in arg["content-base"] = false
} else if(argv["content-base"] === false) {
options.contentBase = false;
}
}
if(argv["watch-content-base"])
options.watchContentBase = true;
if(!options.stats) {
options.stats = {
cached: false,
cachedAssets: false
};
}
if(typeof options.stats === "object" && typeof options.stats.colors === "undefined")
options.stats.colors = argv.color;
if(argv["lazy"])
options.lazy = true;
if(!argv["info"])
options.noInfo = true;
if(argv["quiet"])
options.quiet = true;
if(argv["https"])
options.https = true;
if(argv["cert"])
options.cert = fs.readFileSync(path.resolve(argv["cert"]));
if(argv["key"])
options.key = fs.readFileSync(path.resolve(argv["key"]));
if(argv["cacert"])
options.ca = fs.readFileSync(path.resolve(argv["cacert"]));
if(argv["pfx"])
options.pfx = fs.readFileSync(path.resolve(argv["pfx"]));
if(argv["pfx-passphrase"])
options.pfxPassphrase = argv["pfx-passphrase"];
if(argv["inline"] === false)
options.inline = false;
if(argv["history-api-fallback"])
options.historyApiFallback = true;
if(argv["compress"])
options.compress = true;
if(argv["open"])
options.open = true;
// Kind of weird, but ensures prior behavior isn't broken in cases
// that wouldn't throw errors. E.g. both argv.port and options.port
// were specified, but since argv.port is 8080, options.port will be
// tried first instead.
options.port = argv.port === DEFAULT_PORT ? (options.port || argv.port) : (argv.port || options.port);
if(options.port) {
startDevServer(wpOpt, options);
return;
}
portfinder.basePort = DEFAULT_PORT;
portfinder.getPort(function(err, port) {
if(err) throw err;
options.port = port;
startDevServer(wpOpt, options);
});
}
function startDevServer(wpOpt, options) {
addDevServerEntrypoints(wpOpt, options);
let compiler;
try {
compiler = webpack(wpOpt);
} catch(e) {
if(e instanceof webpack.WebpackOptionsValidationError) {
console.error(colorError(options.stats.colors, e.message));
process.exit(1); // eslint-disable-line
}
throw e;
}
if(argv["progress"]) {
compiler.apply(new webpack.ProgressPlugin({
profile: argv["profile"]
}));
}
const uri = createDomain(options) + (options.inline !== false || options.lazy === true ? "/" : "/webpack-dev-server/");
let server;
try {
server = new Server(compiler, options);
} catch(e) {
const OptionsValidationError = require("../lib/OptionsValidationError");
if(e instanceof OptionsValidationError) {
console.error(colorError(options.stats.colors, e.message));
process.exit(1); // eslint-disable-line
}
throw e;
}
["SIGINT", "SIGTERM"].forEach(function(sig) {
process.on(sig, function() {
server.close();
process.exit(); // eslint-disable-line no-process-exit
});
});
if(options.socket) {
server.listeningApp.on("error", function(e) {
if(e.code === "EADDRINUSE") {
const clientSocket = new net.Socket();
clientSocket.on("error", function(e) {
if(e.code === "ECONNREFUSED") {
// No other server listening on this socket so it can be safely removed
fs.unlinkSync(options.socket);
server.listen(options.socket, options.host, function(err) {
if(err) throw err;
});
}
});
clientSocket.connect({ path: options.socket }, function() {
throw new Error("This socket is already used");
});
}
});
server.listen(options.socket, options.host, function(err) {
if(err) throw err;
const READ_WRITE = 438; // chmod 666 (rw rw rw)
fs.chmod(options.socket, READ_WRITE, function(err) {
if(err) throw err;
reportReadiness(uri, options);
});
});
} else {
server.listen(options.port, options.host, function(err) {
if(err) throw err;
reportReadiness(uri, options);
});
}
}
function reportReadiness(uri, options) {
const useColor = argv.color;
let startSentence = `Project is running at ${colorInfo(useColor, uri)}`
if(options.socket) {
startSentence = `Listening to socket at ${colorInfo(useColor, options.socket)}`;
}
console.log((argv["progress"] ? "\n" : "") + startSentence);
console.log(`webpack output is served from ${colorInfo(useColor, options.publicPath)}`);
const contentBase = Array.isArray(options.contentBase) ? options.contentBase.join(", ") : options.contentBase;
if(contentBase)
console.log(`Content not from webpack is served from ${colorInfo(useColor, contentBase)}`);
if(options.historyApiFallback)
console.log(`404s will fallback to ${colorInfo(useColor, options.historyApiFallback.index || "/index.html")}`);
if(options.open) {
open(uri).catch(function() {
console.log("Unable to open browser. If you are running in a headless environment, please do not use the open flag.");
});
}
}
processOptions(wpOpt);

File diff suppressed because one or more lines are too long

184
web/node_modules/webpack-dev-server/client/index.js generated vendored Normal file
View File

@@ -0,0 +1,184 @@
/* global __resourceQuery */
var url = require("url");
var stripAnsi = require("strip-ansi");
var socket = require("./socket");
var overlay = require("./overlay");
function getCurrentScriptSource() {
// `document.currentScript` is the most accurate way to find the current script,
// but is not supported in all browsers.
if(document.currentScript)
return document.currentScript.getAttribute("src");
// Fall back to getting all scripts in the document.
var scriptElements = document.scripts || [];
var currentScript = scriptElements[scriptElements.length - 1];
if(currentScript)
return currentScript.getAttribute("src");
// Fail as there was no script to use.
throw new Error("[WDS] Failed to get current script source");
}
var urlParts;
if(typeof __resourceQuery === "string" && __resourceQuery) {
// If this bundle is inlined, use the resource query to get the correct url.
urlParts = url.parse(__resourceQuery.substr(1));
} else {
// Else, get the url from the <script> this file was called with.
var scriptHost = getCurrentScriptSource();
scriptHost = scriptHost.replace(/\/[^\/]+$/, "");
urlParts = url.parse((scriptHost ? scriptHost : "/"), false, true);
}
var hot = false;
var initial = true;
var currentHash = "";
var logLevel = "info";
var useWarningOverlay = false;
var useErrorOverlay = false;
function log(level, msg) {
if(logLevel === "info" && level === "info")
return console.log(msg);
if(["info", "warning"].indexOf(logLevel) >= 0 && level === "warning")
return console.warn(msg);
if(["info", "warning", "error"].indexOf(logLevel) >= 0 && level === "error")
return console.error(msg);
}
// Send messages to the outside, so plugins can consume it.
function sendMsg(type, data) {
if(typeof self !== "undefined" && self.window) {
self.postMessage({
type: "webpack" + type,
data: data
}, "*");
}
}
var onSocketMsg = {
hot: function() {
hot = true;
log("info", "[WDS] Hot Module Replacement enabled.");
},
invalid: function() {
log("info", "[WDS] App updated. Recompiling...");
sendMsg("Invalid");
},
hash: function(hash) {
currentHash = hash;
},
"still-ok": function() {
log("info", "[WDS] Nothing changed.")
if(useWarningOverlay || useErrorOverlay) overlay.clear();
sendMsg("StillOk");
},
"log-level": function(level) {
logLevel = level;
},
"overlay": function(overlay) {
if(typeof document !== "undefined") {
if(typeof(overlay) === "boolean") {
useWarningOverlay = overlay;
useErrorOverlay = overlay;
} else if(overlay) {
useWarningOverlay = overlay.warnings;
useErrorOverlay = overlay.errors;
}
}
},
ok: function() {
sendMsg("Ok");
if(useWarningOverlay || useErrorOverlay) overlay.clear();
if(initial) return initial = false;
reloadApp();
},
"content-changed": function() {
log("info", "[WDS] Content base changed. Reloading...")
self.location.reload();
},
warnings: function(warnings) {
log("info", "[WDS] Warnings while compiling.");
var strippedWarnings = warnings.map(function(warning) {
return stripAnsi(warning);
});
sendMsg("Warnings", strippedWarnings);
for(var i = 0; i < strippedWarnings.length; i++)
console.warn(strippedWarnings[i]);
if(useWarningOverlay) overlay.showMessage(warnings);
if(initial) return initial = false;
reloadApp();
},
errors: function(errors) {
log("info", "[WDS] Errors while compiling. Reload prevented.");
var strippedErrors = errors.map(function(error) {
return stripAnsi(error);
});
sendMsg("Errors", strippedErrors);
for(var i = 0; i < strippedErrors.length; i++)
console.error(strippedErrors[i]);
if(useErrorOverlay) overlay.showMessage(errors);
},
error: function(error) {
console.error(error);
},
close: function() {
log("error", "[WDS] Disconnected!");
sendMsg("Close");
}
};
var hostname = urlParts.hostname;
var protocol = urlParts.protocol;
//check ipv4 and ipv6 `all hostname`
if(hostname === "0.0.0.0" || hostname === "::") {
// why do we need this check?
// hostname n/a for file protocol (example, when using electron, ionic)
// see: https://github.com/webpack/webpack-dev-server/pull/384
if(self.location.hostname && !!~self.location.protocol.indexOf("http")) {
hostname = self.location.hostname;
}
}
// `hostname` can be empty when the script path is relative. In that case, specifying
// a protocol would result in an invalid URL.
// When https is used in the app, secure websockets are always necessary
// because the browser doesn't accept non-secure websockets.
if(hostname && (self.location.protocol === "https:" || urlParts.hostname === "0.0.0.0")) {
protocol = self.location.protocol;
}
var socketUrl = url.format({
protocol: protocol,
auth: urlParts.auth,
hostname: hostname,
port: (urlParts.port === "0") ? self.location.port : urlParts.port,
pathname: urlParts.path == null || urlParts.path === "/" ? "/sockjs-node" : urlParts.path
});
socket(socketUrl, onSocketMsg);
var isUnloading = false;
self.addEventListener("beforeunload", function() {
isUnloading = true;
});
function reloadApp() {
if(isUnloading) {
return;
}
if(hot) {
log("info", "[WDS] App hot update...");
var hotEmitter = require("webpack/hot/emitter");
hotEmitter.emit("webpackHotUpdate", currentHash);
if(typeof self !== "undefined" && self.window) {
// broadcast update to window
self.postMessage("webpackHotUpdate" + currentHash, "*");
}
} else {
log("info", "[WDS] App updated. Reloading...");
self.location.reload();
}
}

File diff suppressed because one or more lines are too long

1
web/node_modules/webpack-dev-server/client/live.html generated vendored Normal file
View File

@@ -0,0 +1 @@
<!DOCTYPE html><html><head><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta charset="utf-8"/><meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/><script type="text/javascript" charset="utf-8" src="/__webpack_dev_server__/live.bundle.js"></script></head><body></body></html>

119
web/node_modules/webpack-dev-server/client/live.js generated vendored Normal file
View File

@@ -0,0 +1,119 @@
var $ = require("jquery");
var stripAnsi = require("strip-ansi");
var socket = require("./socket");
require("./style.css");
var hot = false;
var currentHash = "";
$(function() {
$("body").html(require("./page.pug")());
var status = $("#status");
var okness = $("#okness");
var $errors = $("#errors");
var iframe = $("#iframe");
var header = $(".header");
var contentPage = window.location.pathname.substr("/webpack-dev-server".length) + window.location.search;
status.text("Connecting to sockjs server...");
$errors.hide();
iframe.hide();
header.css({
borderColor: "#96b5b4"
});
var onSocketMsg = {
hot: function() {
hot = true;
iframe.attr("src", contentPage + window.location.hash);
},
invalid: function() {
okness.text("");
status.text("App updated. Recompiling...");
header.css({
borderColor: "#96b5b4"
});
$errors.hide();
if(!hot) iframe.hide();
},
hash: function(hash) {
currentHash = hash;
},
"still-ok": function() {
okness.text("");
status.text("App ready.");
header.css({
borderColor: ""
});
$errors.hide();
if(!hot) iframe.show();
},
ok: function() {
okness.text("");
$errors.hide();
reloadApp();
},
warnings: function() {
okness.text("Warnings while compiling.");
$errors.hide();
reloadApp();
},
errors: function(errors) {
status.text("App updated with errors. No reload!");
okness.text("Errors while compiling.");
$errors.text("\n" + stripAnsi(errors.join("\n\n\n")) + "\n\n");
header.css({
borderColor: "#ebcb8b"
});
$errors.show();
iframe.hide();
},
close: function() {
status.text("");
okness.text("Disconnected.");
$errors.text("\n\n\n Lost connection to webpack-dev-server.\n Please restart the server to reestablish connection...\n\n\n\n");
header.css({
borderColor: "#ebcb8b"
});
$errors.show();
iframe.hide();
}
};
socket("/sockjs-node", onSocketMsg);
iframe.load(function() {
status.text("App ready.");
header.css({
borderColor: ""
});
iframe.show();
});
function reloadApp() {
if(hot) {
status.text("App hot update.");
try {
iframe[0].contentWindow.postMessage("webpackHotUpdate" + currentHash, "*");
} catch(e) {
console.warn(e);
}
iframe.show();
} else {
status.text("App updated. Reloading app...");
header.css({
borderColor: "#96b5b4"
});
try {
var old = iframe[0].contentWindow.location + "";
if(old.indexOf("about") == 0) old = null;
iframe.attr("src", old || (contentPage + window.location.hash));
old && iframe[0].contentWindow.location.reload();
} catch(e) {
iframe.attr("src", contentPage + window.location.hash);
}
}
}
});

126
web/node_modules/webpack-dev-server/client/overlay.js generated vendored Normal file
View File

@@ -0,0 +1,126 @@
// The error overlay is inspired (and mostly copied) from Create React App (https://github.com/facebookincubator/create-react-app)
// They, in turn, got inspired by webpack-hot-middleware (https://github.com/glenjamin/webpack-hot-middleware).
var ansiHTML = require("ansi-html");
var Entities = require("html-entities").AllHtmlEntities;
var entities = new Entities();
var colors = {
reset: ["transparent", "transparent"],
black: "181818",
red: "E36049",
green: "B3CB74",
yellow: "FFD080",
blue: "7CAFC2",
magenta: "7FACCA",
cyan: "C3C2EF",
lightgrey: "EBE7E3",
darkgrey: "6D7891"
};
ansiHTML.setColors(colors);
function createOverlayIframe(onIframeLoad) {
var iframe = document.createElement("iframe");
iframe.id = "webpack-dev-server-client-overlay";
iframe.src = "about:blank";
iframe.style.position = "fixed";
iframe.style.left = 0;
iframe.style.top = 0;
iframe.style.right = 0;
iframe.style.bottom = 0;
iframe.style.width = "100vw";
iframe.style.height = "100vh";
iframe.style.border = "none";
iframe.style.zIndex = 9999999999;
iframe.onload = onIframeLoad;
return iframe;
}
function addOverlayDivTo(iframe) {
var div = iframe.contentDocument.createElement("div");
div.id = "webpack-dev-server-client-overlay-div";
div.style.position = "fixed";
div.style.boxSizing = "border-box";
div.style.left = 0;
div.style.top = 0;
div.style.right = 0;
div.style.bottom = 0;
div.style.width = "100vw";
div.style.height = "100vh";
div.style.backgroundColor = "black";
div.style.color = "#E8E8E8";
div.style.fontFamily = "Menlo, Consolas, monospace";
div.style.fontSize = "large";
div.style.padding = "2rem";
div.style.lineHeight = "1.2";
div.style.whiteSpace = "pre-wrap";
div.style.overflow = "auto";
iframe.contentDocument.body.appendChild(div);
return div;
}
var overlayIframe = null;
var overlayDiv = null;
var lastOnOverlayDivReady = null;
function ensureOverlayDivExists(onOverlayDivReady) {
if(overlayDiv) {
// Everything is ready, call the callback right away.
onOverlayDivReady(overlayDiv);
return;
}
// Creating an iframe may be asynchronous so we'll schedule the callback.
// In case of multiple calls, last callback wins.
lastOnOverlayDivReady = onOverlayDivReady;
if(overlayIframe) {
// We're already creating it.
return;
}
// Create iframe and, when it is ready, a div inside it.
overlayIframe = createOverlayIframe(function onIframeLoad() {
overlayDiv = addOverlayDivTo(overlayIframe);
// Now we can talk!
lastOnOverlayDivReady(overlayDiv);
});
// Zalgo alert: onIframeLoad() will be called either synchronously
// or asynchronously depending on the browser.
// We delay adding it so `overlayIframe` is set when `onIframeLoad` fires.
document.body.appendChild(overlayIframe);
}
function showMessageOverlay(message) {
ensureOverlayDivExists(function onOverlayDivReady(overlayDiv) {
// Make it look similar to our terminal.
overlayDiv.innerHTML =
"<span style=\"color: #" +
colors.red +
"\">Failed to compile.</span><br><br>" +
ansiHTML(entities.encode(message));
});
}
function destroyErrorOverlay() {
if(!overlayDiv) {
// It is not there in the first place.
return;
}
// Clean up and reset internal state.
document.body.removeChild(overlayIframe);
overlayDiv = null;
overlayIframe = null;
lastOnOverlayDivReady = null;
}
// Successful compilation.
exports.clear = function handleSuccess() {
destroyErrorOverlay();
}
// Compilation with errors (e.g. syntax error or missing modules).
exports.showMessage = function handleMessage(messages) {
showMessageOverlay(messages[0]);
}

7
web/node_modules/webpack-dev-server/client/page.pug generated vendored Normal file
View File

@@ -0,0 +1,7 @@
.header
span#okness
= " "
span#status
pre#errors
#warnings
iframe#iframe(src="javascript:;" allowfullscreen)

41
web/node_modules/webpack-dev-server/client/socket.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
var SockJS = require("sockjs-client");
var retries = 0;
var sock = null;
function socket(url, handlers) {
sock = new SockJS(url);
sock.onopen = function() {
retries = 0;
}
sock.onclose = function() {
if(retries === 0)
handlers.close();
// Try to reconnect.
sock = null;
// After 10 retries stop trying, to prevent logspam.
if(retries <= 10) {
// Exponentially increase timeout to reconnect.
// Respectfully copied from the package `got`.
var retryInMs = 1000 * Math.pow(2, retries) + Math.random() * 100;
retries += 1;
setTimeout(function() {
socket(url, handlers);
}, retryInMs);
}
};
sock.onmessage = function(e) {
// This assumes that all data sent via the websocket is JSON.
var msg = JSON.parse(e.data);
if(handlers[msg.type])
handlers[msg.type](msg.data);
};
}
module.exports = socket;

File diff suppressed because one or more lines are too long

1
web/node_modules/webpack-dev-server/client/sockjs.js generated vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = require("sockjs-client");

58
web/node_modules/webpack-dev-server/client/style.css generated vendored Normal file
View File

@@ -0,0 +1,58 @@
*,
*:before,
*:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body,
html {
margin: 0;
padding: 0;
height: 100%;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.header {
width: 100%;
height: 30px;
padding: 0 10px;
border-left: 10px solid #a3be8c;
font-size: 12px;
line-height: 30px;
color: #eff1f5;
background: #343d46;
overflow: hidden;
}
#iframe {
position: absolute;
top: 30px;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: -webkit-calc(100% - 30px);
height: -moz-calc(100% - 30px);
height: -ms-calc(100% - 30px);
height: -o-calc(100% - 30px);
height: calc(100% - 30px);
border: 0;
}
#errors {
width: 100%;
margin: 0;
padding: 10px;
font-family: monospace;
font-size: 14px;
line-height: 1.4;
color: #eff1f5;
background: #bf616a;
overflow: auto;
}
#okness {
font-weight: bold;
}

View File

@@ -0,0 +1,2 @@
require("./jquery-1.8.1");
module.exports = jQuery;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
module.exports = {
module: {
rules: [
{
test: /\.pug$/,
use: [
"pug-loader?self",
]
},
{
test: /\.css$/,
use: [
"style-loader",
"css-loader"
],
}
]
}
};

View File

@@ -0,0 +1,6 @@
module.exports = {
output: {
library: "SockJS",
libraryTarget: "umd"
}
}

View File

@@ -0,0 +1,159 @@
"use strict";
const optionsSchema = require("./optionsSchema.json");
const getSchemaPart = (path, parents, additionalPath) => {
parents = parents || 0;
path = path.split("/");
path = path.slice(0, path.length - parents);
if(additionalPath) {
additionalPath = additionalPath.split("/");
path = path.concat(additionalPath);
}
let schemaPart = optionsSchema;
for(let i = 1; i < path.length; i++) {
const inner = schemaPart[path[i]];
if(inner)
schemaPart = inner;
}
return schemaPart;
};
const getSchemaPartText = (schemaPart, additionalPath) => {
if(additionalPath) {
for(let i = 0; i < additionalPath.length; i++) {
const inner = schemaPart[additionalPath[i]];
if(inner)
schemaPart = inner;
}
}
while(schemaPart.$ref) schemaPart = getSchemaPart(schemaPart.$ref);
let schemaText = OptionsValidationError.formatSchema(schemaPart);
if(schemaPart.description)
schemaText += `\n${schemaPart.description}`;
return schemaText;
};
const indent = (str, prefix, firstLine) => {
if(firstLine) {
return prefix + str.replace(/\n(?!$)/g, "\n" + prefix);
} else {
return str.replace(/\n(?!$)/g, `\n${prefix}`);
}
};
class OptionsValidationError extends Error {
constructor(validationErrors) {
super();
if(Error.hasOwnProperty("captureStackTrace")) {
Error.captureStackTrace(this, this.constructor);
}
this.name = "WebpackDevServerOptionsValidationError";
this.message = "Invalid configuration object. " +
"webpack-dev-server has been initialised using a configuration object that does not match the API schema.\n" +
validationErrors.map(err => " - " + indent(OptionsValidationError.formatValidationError(err), " ", false)).join("\n");
this.validationErrors = validationErrors;
}
static formatSchema(schema, prevSchemas) {
prevSchemas = prevSchemas || [];
const formatInnerSchema = (innerSchema, addSelf) => {
if(!addSelf) return OptionsValidationError.formatSchema(innerSchema, prevSchemas);
if(prevSchemas.indexOf(innerSchema) >= 0) return "(recursive)";
return OptionsValidationError.formatSchema(innerSchema, prevSchemas.concat(schema));
};
if(schema.type === "string") {
if(schema.minLength === 1)
return "non-empty string";
else if(schema.minLength > 1)
return `string (min length ${schema.minLength})`;
return "string";
} else if(schema.type === "boolean") {
return "boolean";
} else if(schema.type === "number") {
return "number";
} else if(schema.type === "object") {
if(schema.properties) {
const required = schema.required || [];
return `object { ${Object.keys(schema.properties).map(property => {
if(required.indexOf(property) < 0) return property + "?";
return property;
}).concat(schema.additionalProperties ? ["..."] : []).join(", ")} }`;
}
if(schema.additionalProperties) {
return `object { <key>: ${formatInnerSchema(schema.additionalProperties)} }`;
}
return "object";
} else if(schema.type === "array") {
return `[${formatInnerSchema(schema.items)}]`;
}
switch(schema.instanceof) {
case "Function":
return "function";
case "RegExp":
return "RegExp";
}
if(schema.$ref) return formatInnerSchema(getSchemaPart(schema.$ref), true);
if(schema.allOf) return schema.allOf.map(formatInnerSchema).join(" & ");
if(schema.oneOf) return schema.oneOf.map(formatInnerSchema).join(" | ");
if(schema.anyOf) return schema.anyOf.map(formatInnerSchema).join(" | ");
if(schema.enum) return schema.enum.map(item => JSON.stringify(item)).join(" | ");
return JSON.stringify(schema, 0, 2);
}
static formatValidationError(err) {
const dataPath = `configuration${err.dataPath}`;
if(err.keyword === "additionalProperties") {
return `${dataPath} has an unknown property '${err.params.additionalProperty}'. These properties are valid:\n${getSchemaPartText(err.parentSchema)}`;
} else if(err.keyword === "oneOf" || err.keyword === "anyOf") {
if(err.children && err.children.length > 0) {
return `${dataPath} should be one of these:\n${getSchemaPartText(err.parentSchema)}\n` +
`Details:\n${err.children.map(err => " * " + indent(OptionsValidationError.formatValidationError(err), " ", false)).join("\n")}`;
}
return `${dataPath} should be one of these:\n${getSchemaPartText(err.parentSchema)}`;
} else if(err.keyword === "enum") {
if(err.parentSchema && err.parentSchema.enum && err.parentSchema.enum.length === 1) {
return `${dataPath} should be ${getSchemaPartText(err.parentSchema)}`;
}
return `${dataPath} should be one of these:\n${getSchemaPartText(err.parentSchema)}`;
} else if(err.keyword === "allOf") {
return `${dataPath} should be:\n${getSchemaPartText(err.parentSchema)}`;
} else if(err.keyword === "type") {
switch(err.params.type) {
case "object":
return `${dataPath} should be an object.`;
case "string":
return `${dataPath} should be a string.`;
case "boolean":
return `${dataPath} should be a boolean.`;
case "number":
return `${dataPath} should be a number.`;
case "array":
return `${dataPath} should be an array:\n${getSchemaPartText(err.parentSchema)}`;
}
return `${dataPath} should be ${err.params.type}:\n${getSchemaPartText(err.parentSchema)}`;
} else if(err.keyword === "instanceof") {
return `${dataPath} should be an instance of ${getSchemaPartText(err.parentSchema)}.`;
} else if(err.keyword === "required") {
const missingProperty = err.params.missingProperty.replace(/^\./, "");
return `${dataPath} misses the property '${missingProperty}'.\n${getSchemaPartText(err.parentSchema, ["properties", missingProperty])}`;
} else if(err.keyword === "minLength" || err.keyword === "minItems") {
if(err.params.limit === 1)
return `${dataPath} should not be empty.`;
else
return `${dataPath} ${err.message}`;
} else {
// eslint-disable-line no-fallthrough
return `${dataPath} ${err.message} (${JSON.stringify(err, 0, 2)}).\n${getSchemaPartText(err.parentSchema)}`;
}
}
}
module.exports = OptionsValidationError;

551
web/node_modules/webpack-dev-server/lib/Server.js generated vendored Normal file
View File

@@ -0,0 +1,551 @@
"use strict";
const fs = require("fs");
const chokidar = require("chokidar");
const path = require("path");
const webpackDevMiddleware = require("webpack-dev-middleware");
const express = require("express");
const compress = require("compression");
const sockjs = require("sockjs");
const http = require("http");
const spdy = require("spdy");
const httpProxyMiddleware = require("http-proxy-middleware");
const serveIndex = require("serve-index");
const historyApiFallback = require("connect-history-api-fallback");
const webpack = require("webpack");
const OptionsValidationError = require("./OptionsValidationError");
const optionsSchema = require("./optionsSchema.json");
const clientStats = { errorDetails: false };
function Server(compiler, options) {
// Default options
if(!options) options = {};
const validationErrors = webpack.validateSchema(optionsSchema, options);
if(validationErrors.length) {
throw new OptionsValidationError(validationErrors);
}
if(options.lazy && !options.filename) {
throw new Error("'filename' option must be set in lazy mode.");
}
this.hot = options.hot || options.hotOnly;
this.headers = options.headers;
this.clientLogLevel = options.clientLogLevel;
this.clientOverlay = options.overlay;
this.disableHostCheck = !!options.disableHostCheck;
this.publicHost = options.public;
this.sockets = [];
this.contentBaseWatchers = [];
// Listening for events
const invalidPlugin = () => {
this.sockWrite(this.sockets, "invalid");
};
compiler.plugin("compile", invalidPlugin);
compiler.plugin("invalid", invalidPlugin);
compiler.plugin("done", (stats) => {
this._sendStats(this.sockets, stats.toJson(clientStats));
this._stats = stats;
});
// Init express server
const app = this.app = new express();
app.all("*", (req, res, next) => {
if(this.checkHost(req.headers))
return next();
res.send("Invalid Host header");
});
// middleware for serving webpack bundle
this.middleware = webpackDevMiddleware(compiler, options);
app.get("/__webpack_dev_server__/live.bundle.js", (req, res) => {
res.setHeader("Content-Type", "application/javascript");
fs.createReadStream(path.join(__dirname, "..", "client", "live.bundle.js")).pipe(res);
});
app.get("/__webpack_dev_server__/sockjs.bundle.js", (req, res) => {
res.setHeader("Content-Type", "application/javascript");
fs.createReadStream(path.join(__dirname, "..", "client", "sockjs.bundle.js")).pipe(res);
});
app.get("/webpack-dev-server.js", (req, res) => {
res.setHeader("Content-Type", "application/javascript");
fs.createReadStream(path.join(__dirname, "..", "client", "index.bundle.js")).pipe(res);
});
app.get("/webpack-dev-server/*", (req, res) => {
res.setHeader("Content-Type", "text/html");
fs.createReadStream(path.join(__dirname, "..", "client", "live.html")).pipe(res);
});
app.get("/webpack-dev-server", (req, res) => {
res.setHeader("Content-Type", "text/html");
/* eslint-disable quotes */
res.write('<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>');
const path = this.middleware.getFilenameFromUrl(options.publicPath || "/");
const fs = this.middleware.fileSystem;
function writeDirectory(baseUrl, basePath) {
const content = fs.readdirSync(basePath);
res.write("<ul>");
content.forEach(function(item) {
const p = `${basePath}/${item}`;
if(fs.statSync(p).isFile()) {
res.write('<li><a href="');
res.write(baseUrl + item);
res.write('">');
res.write(item);
res.write('</a></li>');
if(/\.js$/.test(item)) {
const htmlItem = item.substr(0, item.length - 3);
res.write('<li><a href="');
res.write(baseUrl + htmlItem);
res.write('">');
res.write(htmlItem);
res.write('</a> (magic html for ');
res.write(item);
res.write(') (<a href="');
res.write(baseUrl.replace(/(^(https?:\/\/[^\/]+)?\/)/, "$1webpack-dev-server/") + htmlItem);
res.write('">webpack-dev-server</a>)</li>');
}
} else {
res.write('<li>');
res.write(item);
res.write('<br>');
writeDirectory(`${baseUrl + item}/`, p);
res.write('</li>');
}
});
res.write("</ul>");
}
/* eslint-enable quotes */
writeDirectory(options.publicPath || "/", path);
res.end("</body></html>");
});
let contentBase;
if(options.contentBase !== undefined) {
contentBase = options.contentBase;
} else {
contentBase = process.cwd();
}
// Keep track of websocket proxies for external websocket upgrade.
const websocketProxies = [];
const features = {
compress() {
if(options.compress) {
// Enable gzip compression.
app.use(compress());
}
},
proxy() {
if(options.proxy) {
/**
* Assume a proxy configuration specified as:
* proxy: {
* 'context': { options }
* }
* OR
* proxy: {
* 'context': 'target'
* }
*/
if(!Array.isArray(options.proxy)) {
options.proxy = Object.keys(options.proxy).map((context) => {
let proxyOptions;
// For backwards compatibility reasons.
const correctedContext = context.replace(/^\*$/, "**").replace(/\/\*$/, "");
if(typeof options.proxy[context] === "string") {
proxyOptions = {
context: correctedContext,
target: options.proxy[context]
};
} else {
proxyOptions = Object.assign({}, options.proxy[context]);
proxyOptions.context = correctedContext;
}
proxyOptions.logLevel = proxyOptions.logLevel || "warn";
return proxyOptions;
});
}
const getProxyMiddleware = (proxyConfig) => {
const context = proxyConfig.context || proxyConfig.path;
// It is possible to use the `bypass` method without a `target`.
// However, the proxy middleware has no use in this case, and will fail to instantiate.
if(proxyConfig.target) {
return httpProxyMiddleware(context, proxyConfig);
}
}
/**
* Assume a proxy configuration specified as:
* proxy: [
* {
* context: ...,
* ...options...
* },
* // or:
* function() {
* return {
* context: ...,
* ...options...
* };
* }
* ]
*/
options.proxy.forEach((proxyConfigOrCallback) => {
let proxyConfig;
let proxyMiddleware;
if(typeof proxyConfigOrCallback === "function") {
proxyConfig = proxyConfigOrCallback();
} else {
proxyConfig = proxyConfigOrCallback;
}
proxyMiddleware = getProxyMiddleware(proxyConfig);
if(proxyConfig.ws) {
websocketProxies.push(proxyMiddleware);
}
app.use((req, res, next) => {
if(typeof proxyConfigOrCallback === "function") {
const newProxyConfig = proxyConfigOrCallback();
if(newProxyConfig !== proxyConfig) {
proxyConfig = newProxyConfig;
proxyMiddleware = getProxyMiddleware(proxyConfig);
}
}
const bypass = typeof proxyConfig.bypass === "function";
const bypassUrl = bypass && proxyConfig.bypass(req, res, proxyConfig) || false;
if(bypassUrl) {
req.url = bypassUrl;
next();
} else if(proxyMiddleware) {
return proxyMiddleware(req, res, next);
} else {
next();
}
});
});
}
},
historyApiFallback() {
if(options.historyApiFallback) {
// Fall back to /index.html if nothing else matches.
app.use(
historyApiFallback(typeof options.historyApiFallback === "object" ? options.historyApiFallback : null)
);
}
},
contentBaseFiles() {
if(Array.isArray(contentBase)) {
contentBase.forEach((item) => {
app.get("*", express.static(item));
});
} else if(/^(https?:)?\/\//.test(contentBase)) {
console.log("Using a URL as contentBase is deprecated and will be removed in the next major version. Please use the proxy option instead.");
console.log('proxy: {\n\t"*": "<your current contentBase configuration>"\n}'); // eslint-disable-line quotes
// Redirect every request to contentBase
app.get("*", (req, res) => {
res.writeHead(302, {
"Location": contentBase + req.path + (req._parsedUrl.search || "")
});
res.end();
});
} else if(typeof contentBase === "number") {
console.log("Using a number as contentBase is deprecated and will be removed in the next major version. Please use the proxy option instead.");
console.log('proxy: {\n\t"*": "//localhost:<your current contentBase configuration>"\n}'); // eslint-disable-line quotes
// Redirect every request to the port contentBase
app.get("*", (req, res) => {
res.writeHead(302, {
"Location": `//localhost:${contentBase}${req.path}${req._parsedUrl.search || ""}`
});
res.end();
});
} else {
// route content request
app.get("*", express.static(contentBase, options.staticOptions));
}
},
contentBaseIndex() {
if(Array.isArray(contentBase)) {
contentBase.forEach((item) => {
app.get("*", serveIndex(item));
});
} else if(!/^(https?:)?\/\//.test(contentBase) && typeof contentBase !== "number") {
app.get("*", serveIndex(contentBase));
}
},
watchContentBase: () => {
if(/^(https?:)?\/\//.test(contentBase) || typeof contentBase === "number") {
throw new Error("Watching remote files is not supported.");
} else if(Array.isArray(contentBase)) {
contentBase.forEach((item) => {
this._watch(item);
});
} else {
this._watch(contentBase);
}
},
middleware: () => {
// include our middleware to ensure it is able to handle '/index.html' request after redirect
app.use(this.middleware);
},
headers: () => {
app.all("*", this.setContentHeaders.bind(this));
},
magicHtml: () => {
app.get("*", this.serveMagicHtml.bind(this));
},
setup: () => {
if(typeof options.setup === "function")
options.setup(app, this);
}
};
const defaultFeatures = ["setup", "headers", "middleware"];
if(options.proxy)
defaultFeatures.push("proxy", "middleware");
if(contentBase !== false)
defaultFeatures.push("contentBaseFiles");
if(options.watchContentBase)
defaultFeatures.push("watchContentBase");
if(options.historyApiFallback) {
defaultFeatures.push("historyApiFallback", "middleware");
if(contentBase !== false)
defaultFeatures.push("contentBaseFiles");
}
defaultFeatures.push("magicHtml");
if(contentBase !== false)
defaultFeatures.push("contentBaseIndex");
// compress is placed last and uses unshift so that it will be the first middleware used
if(options.compress)
defaultFeatures.unshift("compress");
(options.features || defaultFeatures).forEach((feature) => {
features[feature]();
});
if(options.https) {
// for keep supporting CLI parameters
if(typeof options.https === "boolean") {
options.https = {
key: options.key,
cert: options.cert,
ca: options.ca,
pfx: options.pfx,
passphrase: options.pfxPassphrase
};
}
// Use built-in self-signed certificate if no certificate was configured
const fakeCert = fs.readFileSync(path.join(__dirname, "../ssl/server.pem"));
options.https.key = options.https.key || fakeCert;
options.https.cert = options.https.cert || fakeCert;
if(!options.https.spdy) {
options.https.spdy = {
protocols: ["h2", "http/1.1"]
};
}
this.listeningApp = spdy.createServer(options.https, app);
} else {
this.listeningApp = http.createServer(app);
}
// Proxy websockets without the initial http request
// https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
websocketProxies.forEach(function(wsProxy) {
this.listeningApp.on("upgrade", wsProxy.upgrade);
}, this);
}
Server.prototype.use = function() {
this.app.use.apply(this.app, arguments);
}
Server.prototype.setContentHeaders = function(req, res, next) {
if(this.headers) {
for(const name in this.headers) {
res.setHeader(name, this.headers[name]);
}
}
next();
}
Server.prototype.checkHost = function(headers) {
// allow user to opt-out this security check, at own risk
if(this.disableHostCheck) return true;
// get the Host header and extract hostname
// we don't care about port not matching
const hostHeader = headers.host;
if(!hostHeader) return false;
const idx = hostHeader.indexOf(":");
const hostname = idx >= 0 ? hostHeader.substr(0, idx) : hostHeader;
// always allow localhost host, for convience
if(hostname === "127.0.0.1" || hostname === "localhost") return true;
// allow hostname of listening adress
if(hostname === this.listenHostname) return true;
// also allow public hostname if provided
if(typeof this.publicHost === "string") {
const idxPublic = this.publicHost.indexOf(":");
const publicHostname = idxPublic >= 0 ? this.publicHost.substr(0, idxPublic) : this.publicHost;
if(hostname === publicHostname) return true;
}
// disallow
return false;
}
// delegate listen call and init sockjs
Server.prototype.listen = function(port, hostname) {
this.listenHostname = hostname;
const returnValue = this.listeningApp.listen.apply(this.listeningApp, arguments);
const sockServer = sockjs.createServer({
// Use provided up-to-date sockjs-client
sockjs_url: "/__webpack_dev_server__/sockjs.bundle.js",
// Limit useless logs
log: function(severity, line) {
if(severity === "error") {
console.log(line);
}
}
});
sockServer.on("connection", (conn) => {
if(!conn) return;
if(!this.checkHost(conn.headers)) {
this.sockWrite([conn], "error", "Invalid Host header");
conn.close();
return;
}
this.sockets.push(conn);
conn.on("close", () => {
const connIndex = this.sockets.indexOf(conn);
if(connIndex >= 0) {
this.sockets.splice(connIndex, 1);
}
});
if(this.clientLogLevel)
this.sockWrite([conn], "log-level", this.clientLogLevel);
if(this.clientOverlay)
this.sockWrite([conn], "overlay", this.clientOverlay);
if(this.hot) this.sockWrite([conn], "hot");
if(!this._stats) return;
this._sendStats([conn], this._stats.toJson(clientStats), true);
});
sockServer.installHandlers(this.listeningApp, {
prefix: "/sockjs-node"
});
return returnValue;
}
Server.prototype.close = function(callback) {
this.sockets.forEach((sock) => {
sock.close();
});
this.sockets = [];
this.listeningApp.close(() => {
this.middleware.close(callback);
});
this.contentBaseWatchers.forEach((watcher) => {
watcher.close();
});
this.contentBaseWatchers = [];
}
Server.prototype.sockWrite = function(sockets, type, data) {
sockets.forEach((sock) => {
sock.write(JSON.stringify({
type: type,
data: data
}));
});
}
Server.prototype.serveMagicHtml = function(req, res, next) {
const _path = req.path;
try {
if(!this.middleware.fileSystem.statSync(this.middleware.getFilenameFromUrl(`${_path}.js`)).isFile())
return next();
// Serve a page that executes the javascript
/* eslint-disable quotes */
res.write('<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body><script type="text/javascript" charset="utf-8" src="');
res.write(_path);
res.write('.js');
res.write(req._parsedUrl.search || "");
res.end('"></script></body></html>');
/* eslint-enable quotes */
} catch(e) {
return next();
}
}
// send stats to a socket or multiple sockets
Server.prototype._sendStats = function(sockets, stats, force) {
if(!force &&
stats &&
(!stats.errors || stats.errors.length === 0) &&
stats.assets &&
stats.assets.every((asset) => !asset.emitted)
)
return this.sockWrite(sockets, "still-ok");
this.sockWrite(sockets, "hash", stats.hash);
if(stats.errors.length > 0)
this.sockWrite(sockets, "errors", stats.errors);
else if(stats.warnings.length > 0)
this.sockWrite(sockets, "warnings", stats.warnings);
else
this.sockWrite(sockets, "ok");
}
Server.prototype._watch = function(path) {
const watcher = chokidar.watch(path).on("change", () => {
this.sockWrite(this.sockets, "content-changed");
});
this.contentBaseWatchers.push(watcher);
}
Server.prototype.invalidate = function() {
if(this.middleware) this.middleware.invalidate();
}
// Export this logic, so that other implementations, like task-runners can use it
Server.addDevServerEntrypoints = require("./util/addDevServerEntrypoints");
module.exports = Server;

View File

@@ -0,0 +1,293 @@
{
"additionalProperties": false,
"properties": {
"hot": {
"description": "Enables Hot Module Replacement.",
"type": "boolean"
},
"hotOnly": {
"description": "Enables Hot Module Replacement without page refresh as fallback.",
"type": "boolean"
},
"lazy": {
"description": "Disables watch mode and recompiles bundle only on a request.",
"type": "boolean"
},
"host": {
"description": "The host the server listens to.",
"type": "string"
},
"filename": {
"description": "The filename that needs to be requested in order to trigger a recompile (only in lazy mode).",
"anyOf": [
{
"instanceof": "RegExp"
},
{
"type": "string"
}
]
},
"publicPath": {
"description": "URL path where the webpack files are served from.",
"type": "string"
},
"port": {
"description": "The port the server listens to.",
"anyOf": [
{
"type": "number"
},
{
"type": "string"
}
]
},
"socket": {
"description": "The Unix socket to listen to (instead of on a host).",
"type": "string"
},
"watchOptions": {
"description": "Options for changing the watch behavior.",
"type": "object"
},
"headers": {
"description": "Response headers that are added to each response.",
"additionalProperties": {
"type": "string"
},
"type": "object"
},
"clientLogLevel": {
"description": "Controls the log messages shown in the browser.",
"enum": [
"none",
"info",
"warning",
"error"
]
},
"overlay": {
"description": "Shows an error overlay in browser.",
"anyOf": [
{
"type": "boolean"
},
{
"type": "object",
"properties": {
"errors": {
"type": "boolean"
},
"warnings": {
"type": "boolean"
}
}
}
]
},
"key": {
"description": "The contents of a SSL key.",
"anyOf": [
{
"type": "string"
},
{
"instanceof": "Buffer"
}
]
},
"cert": {
"description": "The contents of a SSL certificate.",
"anyOf": [
{
"type": "string"
},
{
"instanceof": "Buffer"
}
]
},
"ca": {
"description": "The contents of a SSL CA certificate.",
"anyOf": [
{
"type": "string"
},
{
"instanceof": "Buffer"
}
]
},
"pfx": {
"description": "The contents of a SSL pfx file.",
"anyOf": [
{
"type": "string"
},
{
"instanceof": "Buffer"
}
]
},
"pfxPassphrase": {
"description": "The passphrase to a (SSL) PFX file.",
"type": "string"
},
"inline": {
"description": "Enable inline mode to include client scripts in bundle (CLI-only).",
"type": "boolean"
},
"disableHostCheck": {
"description": "Disable the Host header check (Security).",
"type": "boolean"
},
"public": {
"description": "The public hostname/ip address of the server.",
"type": "string"
},
"https": {
"description": "Enable HTTPS for server.",
"anyOf": [
{
"type": "object"
},
{
"type": "boolean"
}
]
},
"contentBase": {
"description": "A directory to serve files non-webpack files from.",
"anyOf": [
{
"items": {
"type": "string"
},
"minItems": 1,
"type": "array"
},
{
"enum": [
false
]
},
{
"type": "number"
},
{
"type": "string"
}
]
},
"watchContentBase": {
"description": "Watches the contentBase directory for changes.",
"type": "boolean"
},
"open": {
"description": "Let the CLI open your browser with the URL.",
"type": "boolean"
},
"features": {
"description": "The order of which the features will be triggered.",
"items": {
"type": "string"
},
"type": "array"
},
"compress": {
"description": "Gzip compression for all requests.",
"type": "boolean"
},
"proxy": {
"description": "Proxy requests to another server.",
"anyOf": [
{
"items": {
"anyOf": [
{
"type": "object"
},
{
"instanceof": "Function"
}
]
},
"minItems": 1,
"type": "array"
},
{
"type": "object"
}
]
},
"historyApiFallback": {
"description": "404 fallback to a specified file.",
"anyOf": [
{
"type": "boolean"
},
{
"type": "object"
}
]
},
"staticOptions": {
"description": "Options for static files served with contentBase.",
"type": "object"
},
"setup": {
"description": "Exposes the Express server to add custom middleware or routes.",
"instanceof": "Function"
},
"stats": {
"description": "Decides what bundle information is displayed.",
"anyOf": [
{
"type": "object"
},
{
"type": "boolean"
},
{
"enum": [
"none",
"errors-only",
"minimal",
"normal",
"verbose"
]
}
]
},
"reporter": {
"description": "Customize what the console displays when compiling.",
"instanceof": "Function"
},
"noInfo": {
"description": "Hide all info messages on console.",
"type": "boolean"
},
"quiet": {
"description": "Hide all messages on console.",
"type": "boolean"
},
"serverSideRender": {
"description": "Expose stats for server side rendering (experimental).",
"type": "boolean"
},
"index": {
"description": "The filename that is considered the index file.",
"type": "string"
},
"log": {
"description": "Customize info logs for webpack-dev-middleware.",
"instanceof": "Function"
},
"warn": {
"description": "Customize warn logs for webpack-dev-middleware.",
"instanceof": "Function"
}
},
"type": "object"
}

View File

@@ -0,0 +1,26 @@
"use strict";
const createDomain = require("./createDomain");
module.exports = function addDevServerEntrypoints(webpackOptions, devServerOptions) {
if(devServerOptions.inline !== false) {
const domain = createDomain(devServerOptions);
const devClient = [`${require.resolve("../../client/")}?${domain}`];
if(devServerOptions.hotOnly)
devClient.push("webpack/hot/only-dev-server");
else if(devServerOptions.hot)
devClient.push("webpack/hot/dev-server");
[].concat(webpackOptions).forEach((wpOpt) => {
if(typeof wpOpt.entry === "object" && !Array.isArray(wpOpt.entry)) {
Object.keys(wpOpt.entry).forEach((key) => {
wpOpt.entry[key] = devClient.concat(wpOpt.entry[key]);
});
} else if(typeof wpOpt.entry === "function") {
wpOpt.entry = wpOpt.entry(devClient);
} else {
wpOpt.entry = devClient.concat(wpOpt.entry);
}
});
}
};

View File

@@ -0,0 +1,13 @@
"use strict";
const url = require("url");
module.exports = function createDomain(options) {
const protocol = options.https ? "https" : "http";
// the formatted domain (url without path) of the webpack server
return options.public ? `${protocol}://${options.public}` : url.format({
protocol: protocol,
hostname: options.host,
port: options.socket ? 0 : options.port.toString()
});
};

View File

@@ -0,0 +1,56 @@
'use strict';
function preserveCamelCase(str) {
var isLastCharLower = false;
for (var i = 0; i < str.length; i++) {
var c = str.charAt(i);
if (isLastCharLower && (/[a-zA-Z]/).test(c) && c.toUpperCase() === c) {
str = str.substr(0, i) + '-' + str.substr(i);
isLastCharLower = false;
i++;
} else {
isLastCharLower = (c.toLowerCase() === c);
}
}
return str;
}
module.exports = function () {
var str = [].map.call(arguments, function (str) {
return str.trim();
}).filter(function (str) {
return str.length;
}).join('-');
if (!str.length) {
return '';
}
if (str.length === 1) {
return str.toLowerCase();
}
if (!(/[_.\- ]+/).test(str)) {
if (str === str.toUpperCase()) {
return str.toLowerCase();
}
if (str[0] !== str[0].toLowerCase()) {
return str[0].toLowerCase() + str.slice(1);
}
return str;
}
str = preserveCamelCase(str);
return str
.replace(/^[_.\- ]+/, '')
.toLowerCase()
.replace(/[_.\- ]+(\w|$)/g, function (m, p1) {
return p1.toUpperCase();
});
};

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,72 @@
{
"_from": "camelcase@^3.0.0",
"_id": "camelcase@3.0.0",
"_inBundle": false,
"_integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
"_location": "/webpack-dev-server/camelcase",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "camelcase@^3.0.0",
"name": "camelcase",
"escapedName": "camelcase",
"rawSpec": "^3.0.0",
"saveSpec": null,
"fetchSpec": "^3.0.0"
},
"_requiredBy": [
"/webpack-dev-server/yargs",
"/webpack-dev-server/yargs-parser"
],
"_resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"_shasum": "32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a",
"_spec": "camelcase@^3.0.0",
"_where": "/home/bilal/Saburly/slucajna-televizija/node_modules/webpack-dev-server/node_modules/yargs",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "http://sindresorhus.com"
},
"bugs": {
"url": "https://github.com/sindresorhus/camelcase/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Convert a dash/dot/underscore/space separated string to camelCase: foo-bar → fooBar",
"devDependencies": {
"ava": "*",
"xo": "*"
},
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js"
],
"homepage": "https://github.com/sindresorhus/camelcase#readme",
"keywords": [
"camelcase",
"camel-case",
"camel",
"case",
"dash",
"hyphen",
"dot",
"underscore",
"separator",
"string",
"text",
"convert"
],
"license": "MIT",
"name": "camelcase",
"repository": {
"type": "git",
"url": "git+https://github.com/sindresorhus/camelcase.git"
},
"scripts": {
"test": "xo && ava"
},
"version": "3.0.0"
}

View File

@@ -0,0 +1,57 @@
# camelcase [![Build Status](https://travis-ci.org/sindresorhus/camelcase.svg?branch=master)](https://travis-ci.org/sindresorhus/camelcase)
> Convert a dash/dot/underscore/space separated string to camelCase: `foo-bar` → `fooBar`
## Install
```
$ npm install --save camelcase
```
## Usage
```js
const camelCase = require('camelcase');
camelCase('foo-bar');
//=> 'fooBar'
camelCase('foo_bar');
//=> 'fooBar'
camelCase('Foo-Bar');
//=> 'fooBar'
camelCase('--foo.bar');
//=> 'fooBar'
camelCase('__foo__bar__');
//=> 'fooBar'
camelCase('foo bar');
//=> 'fooBar'
console.log(process.argv[3]);
//=> '--foo-bar'
camelCase(process.argv[3]);
//=> 'fooBar'
camelCase('foo', 'bar');
//=> 'fooBar'
camelCase('__foo__', '--bar');
//=> 'fooBar'
```
## Related
- [decamelize](https://github.com/sindresorhus/decamelize) - The inverse of this module
- [uppercamelcase](https://github.com/SamVerschueren/uppercamelcase) - Like this module, but to PascalCase instead of camelCase
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

View File

@@ -0,0 +1,15 @@
# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
<a name="3.2.0"></a>
# [3.2.0](https://github.com/yargs/cliui/compare/v3.1.2...v3.2.0) (2016-04-11)
### Bug Fixes
* reduces tarball size ([acc6c33](https://github.com/yargs/cliui/commit/acc6c33))
### Features
* adds standard-version for release management ([ff84e32](https://github.com/yargs/cliui/commit/ff84e32))

View File

@@ -0,0 +1,14 @@
Copyright (c) 2015, Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice
appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,110 @@
# cliui
[![Build Status](https://travis-ci.org/yargs/cliui.svg)](https://travis-ci.org/yargs/cliui)
[![Coverage Status](https://coveralls.io/repos/yargs/cliui/badge.svg?branch=)](https://coveralls.io/r/yargs/cliui?branch=)
[![NPM version](https://img.shields.io/npm/v/cliui.svg)](https://www.npmjs.com/package/cliui)
[![Standard Version](https://img.shields.io/badge/release-standard%20version-brightgreen.svg)](https://github.com/conventional-changelog/standard-version)
easily create complex multi-column command-line-interfaces.
## Example
```js
var ui = require('cliui')({
width: 80
})
ui.div('Usage: $0 [command] [options]')
ui.div({
text: 'Options:',
padding: [2, 0, 2, 0]
})
ui.div(
{
text: "-f, --file",
width: 20,
padding: [0, 4, 0, 4]
},
{
text: "the file to load." +
chalk.green("(if this description is long it wraps).")
,
width: 20
},
{
text: chalk.red("[required]"),
align: 'right'
}
)
console.log(ui.toString())
```
<img width="500" src="screenshot.png">
## Layout DSL
cliui exposes a simple layout DSL:
If you create a single `ui.row`, passing a string rather than an
object:
* `\n`: characters will be interpreted as new rows.
* `\t`: characters will be interpreted as new columns.
* `\s`: characters will be interpreted as padding.
**as an example...**
```js
var ui = require('./')({
width: 60
})
ui.div(
'Usage: node ./bin/foo.js\n' +
' <regex>\t provide a regex\n' +
' <glob>\t provide a glob\t [required]'
)
console.log(ui.toString())
```
**will output:**
```shell
Usage: node ./bin/foo.js
<regex> provide a regex
<glob> provide a glob [required]
```
## Methods
```js
cliui = require('cliui')
```
### cliui({width: integer})
Specify the maximum width of the UI being generated.
### cliui({wrap: boolean})
Enable or disable the wrapping of text in a column.
### cliui.div(column, column, column)
Create a row with any number of columns, a column
can either be a string, or an object with the following
options:
* **width:** the width of a column.
* **align:** alignment, `right` or `center`.
* **padding:** `[top, right, bottom, left]`.
* **border:** should a border be placed around the div?
### cliui.span(column, column, column)
Similar to `div`, except the next row will be appended without
a new line being created.

View File

@@ -0,0 +1,316 @@
var stringWidth = require('string-width')
var stripAnsi = require('strip-ansi')
var wrap = require('wrap-ansi')
var align = {
right: alignRight,
center: alignCenter
}
var top = 0
var right = 1
var bottom = 2
var left = 3
function UI (opts) {
this.width = opts.width
this.wrap = opts.wrap
this.rows = []
}
UI.prototype.span = function () {
var cols = this.div.apply(this, arguments)
cols.span = true
}
UI.prototype.div = function () {
if (arguments.length === 0) this.div('')
if (this.wrap && this._shouldApplyLayoutDSL.apply(this, arguments)) {
return this._applyLayoutDSL(arguments[0])
}
var cols = []
for (var i = 0, arg; (arg = arguments[i]) !== undefined; i++) {
if (typeof arg === 'string') cols.push(this._colFromString(arg))
else cols.push(arg)
}
this.rows.push(cols)
return cols
}
UI.prototype._shouldApplyLayoutDSL = function () {
return arguments.length === 1 && typeof arguments[0] === 'string' &&
/[\t\n]/.test(arguments[0])
}
UI.prototype._applyLayoutDSL = function (str) {
var _this = this
var rows = str.split('\n')
var leftColumnWidth = 0
// simple heuristic for layout, make sure the
// second column lines up along the left-hand.
// don't allow the first column to take up more
// than 50% of the screen.
rows.forEach(function (row) {
var columns = row.split('\t')
if (columns.length > 1 && stringWidth(columns[0]) > leftColumnWidth) {
leftColumnWidth = Math.min(
Math.floor(_this.width * 0.5),
stringWidth(columns[0])
)
}
})
// generate a table:
// replacing ' ' with padding calculations.
// using the algorithmically generated width.
rows.forEach(function (row) {
var columns = row.split('\t')
_this.div.apply(_this, columns.map(function (r, i) {
return {
text: r.trim(),
padding: _this._measurePadding(r),
width: (i === 0 && columns.length > 1) ? leftColumnWidth : undefined
}
}))
})
return this.rows[this.rows.length - 1]
}
UI.prototype._colFromString = function (str) {
return {
text: str,
padding: this._measurePadding(str)
}
}
UI.prototype._measurePadding = function (str) {
// measure padding without ansi escape codes
var noAnsi = stripAnsi(str)
return [0, noAnsi.match(/\s*$/)[0].length, 0, noAnsi.match(/^\s*/)[0].length]
}
UI.prototype.toString = function () {
var _this = this
var lines = []
_this.rows.forEach(function (row, i) {
_this.rowToString(row, lines)
})
// don't display any lines with the
// hidden flag set.
lines = lines.filter(function (line) {
return !line.hidden
})
return lines.map(function (line) {
return line.text
}).join('\n')
}
UI.prototype.rowToString = function (row, lines) {
var _this = this
var padding
var rrows = this._rasterize(row)
var str = ''
var ts
var width
var wrapWidth
rrows.forEach(function (rrow, r) {
str = ''
rrow.forEach(function (col, c) {
ts = '' // temporary string used during alignment/padding.
width = row[c].width // the width with padding.
wrapWidth = _this._negatePadding(row[c]) // the width without padding.
ts += col
for (var i = 0; i < wrapWidth - stringWidth(col); i++) {
ts += ' '
}
// align the string within its column.
if (row[c].align && row[c].align !== 'left' && _this.wrap) {
ts = align[row[c].align](ts, wrapWidth)
if (stringWidth(ts) < wrapWidth) ts += new Array(width - stringWidth(ts)).join(' ')
}
// apply border and padding to string.
padding = row[c].padding || [0, 0, 0, 0]
if (padding[left]) str += new Array(padding[left] + 1).join(' ')
str += addBorder(row[c], ts, '| ')
str += ts
str += addBorder(row[c], ts, ' |')
if (padding[right]) str += new Array(padding[right] + 1).join(' ')
// if prior row is span, try to render the
// current row on the prior line.
if (r === 0 && lines.length > 0) {
str = _this._renderInline(str, lines[lines.length - 1])
}
})
// remove trailing whitespace.
lines.push({
text: str.replace(/ +$/, ''),
span: row.span
})
})
return lines
}
function addBorder (col, ts, style) {
if (col.border) {
if (/[.']-+[.']/.test(ts)) return ''
else if (ts.trim().length) return style
else return ' '
}
return ''
}
// if the full 'source' can render in
// the target line, do so.
UI.prototype._renderInline = function (source, previousLine) {
var leadingWhitespace = source.match(/^ */)[0].length
var target = previousLine.text
var targetTextWidth = stringWidth(target.trimRight())
if (!previousLine.span) return source
// if we're not applying wrapping logic,
// just always append to the span.
if (!this.wrap) {
previousLine.hidden = true
return target + source
}
if (leadingWhitespace < targetTextWidth) return source
previousLine.hidden = true
return target.trimRight() + new Array(leadingWhitespace - targetTextWidth + 1).join(' ') + source.trimLeft()
}
UI.prototype._rasterize = function (row) {
var _this = this
var i
var rrow
var rrows = []
var widths = this._columnWidths(row)
var wrapped
// word wrap all columns, and create
// a data-structure that is easy to rasterize.
row.forEach(function (col, c) {
// leave room for left and right padding.
col.width = widths[c]
if (_this.wrap) wrapped = wrap(col.text, _this._negatePadding(col), {hard: true}).split('\n')
else wrapped = col.text.split('\n')
if (col.border) {
wrapped.unshift('.' + new Array(_this._negatePadding(col) + 3).join('-') + '.')
wrapped.push("'" + new Array(_this._negatePadding(col) + 3).join('-') + "'")
}
// add top and bottom padding.
if (col.padding) {
for (i = 0; i < (col.padding[top] || 0); i++) wrapped.unshift('')
for (i = 0; i < (col.padding[bottom] || 0); i++) wrapped.push('')
}
wrapped.forEach(function (str, r) {
if (!rrows[r]) rrows.push([])
rrow = rrows[r]
for (var i = 0; i < c; i++) {
if (rrow[i] === undefined) rrow.push('')
}
rrow.push(str)
})
})
return rrows
}
UI.prototype._negatePadding = function (col) {
var wrapWidth = col.width
if (col.padding) wrapWidth -= (col.padding[left] || 0) + (col.padding[right] || 0)
if (col.border) wrapWidth -= 4
return wrapWidth
}
UI.prototype._columnWidths = function (row) {
var _this = this
var widths = []
var unset = row.length
var unsetWidth
var remainingWidth = this.width
// column widths can be set in config.
row.forEach(function (col, i) {
if (col.width) {
unset--
widths[i] = col.width
remainingWidth -= col.width
} else {
widths[i] = undefined
}
})
// any unset widths should be calculated.
if (unset) unsetWidth = Math.floor(remainingWidth / unset)
widths.forEach(function (w, i) {
if (!_this.wrap) widths[i] = row[i].width || stringWidth(row[i].text)
else if (w === undefined) widths[i] = Math.max(unsetWidth, _minWidth(row[i]))
})
return widths
}
// calculates the minimum width of
// a column, based on padding preferences.
function _minWidth (col) {
var padding = col.padding || []
var minWidth = 1 + (padding[left] || 0) + (padding[right] || 0)
if (col.border) minWidth += 4
return minWidth
}
function alignRight (str, width) {
str = str.trim()
var padding = ''
var strWidth = stringWidth(str)
if (strWidth < width) {
padding = new Array(width - strWidth + 1).join(' ')
}
return padding + str
}
function alignCenter (str, width) {
str = str.trim()
var padding = ''
var strWidth = stringWidth(str.trim())
if (strWidth < width) {
padding = new Array(parseInt((width - strWidth) / 2, 10) + 1).join(' ')
}
return padding + str
}
module.exports = function (opts) {
opts = opts || {}
return new UI({
width: (opts || {}).width || 80,
wrap: typeof opts.wrap === 'boolean' ? opts.wrap : true
})
}

View File

@@ -0,0 +1,96 @@
{
"_from": "cliui@^3.2.0",
"_id": "cliui@3.2.0",
"_inBundle": false,
"_integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
"_location": "/webpack-dev-server/cliui",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "cliui@^3.2.0",
"name": "cliui",
"escapedName": "cliui",
"rawSpec": "^3.2.0",
"saveSpec": null,
"fetchSpec": "^3.2.0"
},
"_requiredBy": [
"/webpack-dev-server/yargs"
],
"_resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
"_shasum": "120601537a916d29940f934da3b48d585a39213d",
"_spec": "cliui@^3.2.0",
"_where": "/home/bilal/Saburly/slucajna-televizija/node_modules/webpack-dev-server/node_modules/yargs",
"author": {
"name": "Ben Coe",
"email": "ben@npmjs.com"
},
"bugs": {
"url": "https://github.com/yargs/cliui/issues"
},
"bundleDependencies": false,
"config": {
"blanket": {
"pattern": [
"index.js"
],
"data-cover-never": [
"node_modules",
"test"
],
"output-reporter": "spec"
}
},
"dependencies": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wrap-ansi": "^2.0.0"
},
"deprecated": false,
"description": "easily create complex multi-column command-line-interfaces",
"devDependencies": {
"chai": "^3.5.0",
"chalk": "^1.1.2",
"coveralls": "^2.11.8",
"mocha": "^2.4.5",
"nyc": "^6.4.0",
"standard": "^6.0.8",
"standard-version": "^2.1.2"
},
"files": [
"index.js"
],
"homepage": "https://github.com/yargs/cliui#readme",
"keywords": [
"cli",
"command-line",
"layout",
"design",
"console",
"wrap",
"table"
],
"license": "ISC",
"main": "index.js",
"name": "cliui",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/yargs/cliui.git"
},
"scripts": {
"coverage": "nyc --reporter=text-lcov mocha | coveralls",
"pretest": "standard",
"test": "nyc mocha",
"version": "standard-version"
},
"standard": {
"ignore": [
"**/example/**"
],
"globals": [
"it"
]
},
"version": "3.2.0"
}

View File

@@ -0,0 +1,10 @@
'use strict';
module.exports = function (flag, argv) {
argv = argv || process.argv;
var terminatorPos = argv.indexOf('--');
var prefix = /^--/.test(flag) ? '' : '--';
var pos = argv.indexOf(prefix + flag);
return pos !== -1 && (terminatorPos !== -1 ? pos < terminatorPos : true);
};

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,92 @@
{
"_from": "has-flag@^1.0.0",
"_id": "has-flag@1.0.0",
"_inBundle": false,
"_integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
"_location": "/webpack-dev-server/has-flag",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "has-flag@^1.0.0",
"name": "has-flag",
"escapedName": "has-flag",
"rawSpec": "^1.0.0",
"saveSpec": null,
"fetchSpec": "^1.0.0"
},
"_requiredBy": [
"/webpack-dev-server/supports-color"
],
"_resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
"_shasum": "9d9e793165ce017a00f00418c43f942a7b1d11fa",
"_spec": "has-flag@^1.0.0",
"_where": "/home/bilal/Saburly/slucajna-televizija/node_modules/webpack-dev-server/node_modules/supports-color",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"bugs": {
"url": "https://github.com/sindresorhus/has-flag/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Check if argv has a specific flag",
"devDependencies": {
"ava": "0.0.4"
},
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js"
],
"homepage": "https://github.com/sindresorhus/has-flag#readme",
"keywords": [
"has",
"check",
"detect",
"contains",
"find",
"flag",
"cli",
"command-line",
"argv",
"process",
"arg",
"args",
"argument",
"arguments",
"getopt",
"minimist",
"optimist"
],
"license": "MIT",
"maintainers": [
{
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
{
"name": "Joshua Appelman",
"email": "jappelman@xebia.com",
"url": "jbnicolai.com"
},
{
"name": "JD Ballard",
"email": "i.am.qix@gmail.com",
"url": "github.com/qix-"
}
],
"name": "has-flag",
"repository": {
"type": "git",
"url": "git+https://github.com/sindresorhus/has-flag.git"
},
"scripts": {
"test": "node test.js"
},
"version": "1.0.0"
}

View File

@@ -0,0 +1,64 @@
# has-flag [![Build Status](https://travis-ci.org/sindresorhus/has-flag.svg?branch=master)](https://travis-ci.org/sindresorhus/has-flag)
> Check if [`argv`](https://nodejs.org/docs/latest/api/process.html#process_process_argv) has a specific flag
Correctly stops looking after an `--` argument terminator.
## Install
```
$ npm install --save has-flag
```
## Usage
```js
// foo.js
var hasFlag = require('has-flag');
hasFlag('unicorn');
//=> true
hasFlag('--unicorn');
//=> true
hasFlag('foo=bar');
//=> true
hasFlag('foo');
//=> false
hasFlag('rainbow');
//=> false
```
```
$ node foo.js --unicorn --foo=bar -- --rainbow
```
## API
### hasFlag(flag, [argv])
Returns a boolean whether the flag exists.
#### flag
Type: `string`
CLI flag to look for. The `--` prefix is optional.
#### argv
Type: `array`
Default: `process.argv`
CLI arguments.
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

View File

@@ -0,0 +1,95 @@
'use strict';
var path = require('path');
var childProcess = require('child_process');
var objectAssign = require('object-assign');
var Promise = require('pinkie-promise');
module.exports = function (target, opts) {
if (typeof target !== 'string') {
return Promise.reject(new Error('Expected a `target`'));
}
opts = objectAssign({wait: true}, opts);
var cmd;
var appArgs = [];
var args = [];
var cpOpts = {};
if (Array.isArray(opts.app)) {
appArgs = opts.app.slice(1);
opts.app = opts.app[0];
}
if (process.platform === 'darwin') {
cmd = 'open';
if (opts.wait) {
args.push('-W');
}
if (opts.app) {
args.push('-a', opts.app);
}
} else if (process.platform === 'win32') {
cmd = 'cmd';
args.push('/c', 'start', '""');
target = target.replace(/&/g, '^&');
if (opts.wait) {
args.push('/wait');
}
if (opts.app) {
args.push(opts.app);
}
if (appArgs.length > 0) {
args = args.concat(appArgs);
}
} else {
if (opts.app) {
cmd = opts.app;
} else {
cmd = path.join(__dirname, 'xdg-open');
}
if (appArgs.length > 0) {
args = args.concat(appArgs);
}
if (!opts.wait) {
// xdg-open will block the process unless
// stdio is ignored even if it's unref'd
cpOpts.stdio = 'ignore';
}
}
args.push(target);
if (process.platform === 'darwin' && appArgs.length > 0) {
args.push('--args');
args = args.concat(appArgs);
}
var cp = childProcess.spawn(cmd, args, cpOpts);
if (opts.wait) {
return new Promise(function (resolve, reject) {
cp.once('error', reject);
cp.once('close', function (code) {
if (code > 0) {
reject(new Error('Exited with code ' + code));
return;
}
resolve(cp);
});
});
}
cp.unref();
return Promise.resolve(cp);
};

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,89 @@
{
"_from": "opn@4.0.2",
"_id": "opn@4.0.2",
"_inBundle": false,
"_integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=",
"_location": "/webpack-dev-server/opn",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "opn@4.0.2",
"name": "opn",
"escapedName": "opn",
"rawSpec": "4.0.2",
"saveSpec": null,
"fetchSpec": "4.0.2"
},
"_requiredBy": [
"/webpack-dev-server"
],
"_resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz",
"_shasum": "7abc22e644dff63b0a96d5ab7f2790c0f01abc95",
"_spec": "opn@4.0.2",
"_where": "/home/bilal/Saburly/slucajna-televizija/node_modules/webpack-dev-server",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"bugs": {
"url": "https://github.com/sindresorhus/opn/issues"
},
"bundleDependencies": false,
"dependencies": {
"object-assign": "^4.0.1",
"pinkie-promise": "^2.0.0"
},
"deprecated": false,
"description": "A better node-open. Opens stuff like websites, files, executables. Cross-platform.",
"devDependencies": {
"ava": "*",
"xo": "*"
},
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js",
"xdg-open"
],
"homepage": "https://github.com/sindresorhus/opn#readme",
"keywords": [
"app",
"open",
"opn",
"opener",
"opens",
"launch",
"start",
"xdg-open",
"xdg",
"default",
"cmd",
"browser",
"editor",
"executable",
"exe",
"url",
"urls",
"arguments",
"args",
"spawn",
"exec",
"child",
"process",
"website",
"file"
],
"license": "MIT",
"name": "opn",
"repository": {
"type": "git",
"url": "git+https://github.com/sindresorhus/opn.git"
},
"scripts": {
"test": "xo && ava"
},
"version": "4.0.2"
}

View File

@@ -0,0 +1,89 @@
# opn
> A better [node-open](https://github.com/pwnall/node-open). Opens stuff like websites, files, executables. Cross-platform.
#### Why?
- Actively maintained
- Supports app arguments
- Safer as it uses `spawn` instead of `exec`
- Fixes most of the open `node-open` issues
- Includes the latest [`xdg-open` script](http://cgit.freedesktop.org/xdg/xdg-utils/commit/?id=c55122295c2a480fa721a9614f0e2d42b2949c18) for Linux
## Install
```
$ npm install --save opn
```
## Usage
```js
const opn = require('opn');
// opens the image in the default image viewer
opn('unicorn.png').then(() => {
// image viewer closed
});
// opens the url in the default browser
opn('http://sindresorhus.com');
// specify the app to open in
opn('http://sindresorhus.com', {app: 'firefox'});
// specify app arguments
opn('http://sindresorhus.com', {app: ['google chrome', '--incognito']});
```
## API
Uses the command `open` on OS X, `start` on Windows and `xdg-open` on other platforms.
### opn(target, [options])
Returns a promise for the [spawned child process](https://nodejs.org/api/child_process.html#child_process_class_childprocess). You'd normally not need to use this for anything, but it can be useful if you'd like to attach custom event listeners or perform other operations directly on the spawned process.
#### target
*Required*
Type: `string`
The thing you want to open. Can be a URL, file, or executable.
Opens in the default app for the file type. Eg. URLs opens in your default browser.
#### options
Type: `object`
##### wait
Type: `boolean`
Default: `true`
Wait for the opened app to exit before calling the `callback`. If `false` it's called immediately when opening the app.
On Windows you have to explicitly specify an app for it to be able to wait.
##### app
Type: `string`, `array`
Specify the app to open the `target` with, or an array with the app and app arguments.
The app name is platform dependent. Don't hard code it in reusable modules. Eg. Chrome is `google chrome` on OS X, `google-chrome` on Linux and `chrome` on Windows.
## Related
- [opn-cli](https://github.com/sindresorhus/opn-cli) - CLI for this module
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)

861
web/node_modules/webpack-dev-server/node_modules/opn/xdg-open generated vendored Executable file
View File

@@ -0,0 +1,861 @@
#!/bin/sh
#---------------------------------------------
# xdg-open
#
# Utility script to open a URL in the registered default application.
#
# Refer to the usage() function below for usage.
#
# Copyright 2009-2010, Fathi Boudra <fabo@freedesktop.org>
# Copyright 2009-2010, Rex Dieter <rdieter@fedoraproject.org>
# Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>
# Copyright 2006, Jeremy White <jwhite@codeweavers.com>
#
# LICENSE:
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
#---------------------------------------------
manualpage()
{
cat << _MANUALPAGE
Name
xdg-open - opens a file or URL in the user's preferred
application
Synopsis
xdg-open { file | URL }
xdg-open { --help | --manual | --version }
Description
xdg-open opens a file or URL in the user's preferred
application. If a URL is provided the URL will be opened in the
user's preferred web browser. If a file is provided the file
will be opened in the preferred application for files of that
type. xdg-open supports file, ftp, http and https URLs.
xdg-open is for use inside a desktop session only. It is not
recommended to use xdg-open as root.
Options
--help
Show command synopsis.
--manual
Show this manual page.
--version
Show the xdg-utils version information.
Exit Codes
An exit code of 0 indicates success while a non-zero exit code
indicates failure. The following failure codes can be returned:
1
Error in command line syntax.
2
One of the files passed on the command line did not
exist.
3
A required tool could not be found.
4
The action failed.
Examples
xdg-open 'http://www.freedesktop.org/'
Opens the freedesktop.org website in the user's default
browser.
xdg-open /tmp/foobar.png
Opens the PNG image file /tmp/foobar.png in the user's default
image viewing application.
_MANUALPAGE
}
usage()
{
cat << _USAGE
xdg-open - opens a file or URL in the user's preferred
application
Synopsis
xdg-open { file | URL }
xdg-open { --help | --manual | --version }
_USAGE
}
#@xdg-utils-common@
#----------------------------------------------------------------------------
# Common utility functions included in all XDG wrapper scripts
#----------------------------------------------------------------------------
DEBUG()
{
[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && return 0;
[ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0;
shift
echo "$@" >&2
}
# This handles backslashes but not quote marks.
first_word()
{
read first rest
echo "$first"
}
#-------------------------------------------------------------
# map a binary to a .desktop file
binary_to_desktop_file()
{
search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
binary="`which "$1"`"
binary="`readlink -f "$binary"`"
base="`basename "$binary"`"
IFS=:
for dir in $search; do
unset IFS
[ "$dir" ] || continue
[ -d "$dir/applications" ] || [ -d "$dir/applnk" ] || continue
for file in "$dir"/applications/*.desktop "$dir"/applications/*/*.desktop "$dir"/applnk/*.desktop "$dir"/applnk/*/*.desktop; do
[ -r "$file" ] || continue
# Check to make sure it's worth the processing.
grep -q "^Exec.*$base" "$file" || continue
# Make sure it's a visible desktop file (e.g. not "preferred-web-browser.desktop").
grep -Eq "^(NoDisplay|Hidden)=true" "$file" && continue
command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
command="`which "$command"`"
if [ x"`readlink -f "$command"`" = x"$binary" ]; then
# Fix any double slashes that got added path composition
echo "$file" | sed -e 's,//*,/,g'
return
fi
done
done
}
#-------------------------------------------------------------
# map a .desktop file to a binary
## FIXME: handle vendor dir case
desktop_file_to_binary()
{
search="${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
desktop="`basename "$1"`"
IFS=:
for dir in $search; do
unset IFS
[ "$dir" ] && [ -d "$dir/applications" ] || continue
file="$dir/applications/$desktop"
[ -r "$file" ] || continue
# Remove any arguments (%F, %f, %U, %u, etc.).
command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
command="`which "$command"`"
readlink -f "$command"
return
done
}
#-------------------------------------------------------------
# Exit script on successfully completing the desired operation
exit_success()
{
if [ $# -gt 0 ]; then
echo "$@"
echo
fi
exit 0
}
#-----------------------------------------
# Exit script on malformed arguments, not enough arguments
# or missing required option.
# prints usage information
exit_failure_syntax()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
echo "Try 'xdg-open --help' for more information." >&2
else
usage
echo "Use 'man xdg-open' or 'xdg-open --manual' for additional info."
fi
exit 1
}
#-------------------------------------------------------------
# Exit script on missing file specified on command line
exit_failure_file_missing()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 2
}
#-------------------------------------------------------------
# Exit script on failure to locate necessary tool applications
exit_failure_operation_impossible()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 3
}
#-------------------------------------------------------------
# Exit script on failure returned by a tool application
exit_failure_operation_failed()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 4
}
#------------------------------------------------------------
# Exit script on insufficient permission to read a specified file
exit_failure_file_permission_read()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 5
}
#------------------------------------------------------------
# Exit script on insufficient permission to write a specified file
exit_failure_file_permission_write()
{
if [ $# -gt 0 ]; then
echo "xdg-open: $@" >&2
fi
exit 6
}
check_input_file()
{
if [ ! -e "$1" ]; then
exit_failure_file_missing "file '$1' does not exist"
fi
if [ ! -r "$1" ]; then
exit_failure_file_permission_read "no permission to read file '$1'"
fi
}
check_vendor_prefix()
{
file_label="$2"
[ -n "$file_label" ] || file_label="filename"
file=`basename "$1"`
case "$file" in
[[:alpha:]]*-*)
return
;;
esac
echo "xdg-open: $file_label '$file' does not have a proper vendor prefix" >&2
echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2
echo 'with a dash ("-"). An example '"$file_label"' is '"'example-$file'" >&2
echo "Use --novendor to override or 'xdg-open --manual' for additional info." >&2
exit 1
}
check_output_file()
{
# if the file exists, check if it is writeable
# if it does not exists, check if we are allowed to write on the directory
if [ -e "$1" ]; then
if [ ! -w "$1" ]; then
exit_failure_file_permission_write "no permission to write to file '$1'"
fi
else
DIR=`dirname "$1"`
if [ ! -w "$DIR" ] || [ ! -x "$DIR" ]; then
exit_failure_file_permission_write "no permission to create file '$1'"
fi
fi
}
#----------------------------------------
# Checks for shared commands, e.g. --help
check_common_commands()
{
while [ $# -gt 0 ] ; do
parm="$1"
shift
case "$parm" in
--help)
usage
echo "Use 'man xdg-open' or 'xdg-open --manual' for additional info."
exit_success
;;
--manual)
manualpage
exit_success
;;
--version)
echo "xdg-open 1.1.0 rc3"
exit_success
;;
esac
done
}
check_common_commands "$@"
[ -z "${XDG_UTILS_DEBUG_LEVEL}" ] && unset XDG_UTILS_DEBUG_LEVEL;
if [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then
# Be silent
xdg_redirect_output=" > /dev/null 2> /dev/null"
else
# All output to stderr
xdg_redirect_output=" >&2"
fi
#--------------------------------------
# Checks for known desktop environments
# set variable DE to the desktop environments name, lowercase
detectDE()
{
# see https://bugs.freedesktop.org/show_bug.cgi?id=34164
unset GREP_OPTIONS
if [ -n "${XDG_CURRENT_DESKTOP}" ]; then
case "${XDG_CURRENT_DESKTOP}" in
ENLIGHTENMENT)
DE=enlightenment;
;;
GNOME)
DE=gnome;
;;
KDE)
DE=kde;
;;
LXDE)
DE=lxde;
;;
MATE)
DE=mate;
;;
XFCE)
DE=xfce
;;
esac
fi
if [ x"$DE" = x"" ]; then
# classic fallbacks
if [ x"$KDE_FULL_SESSION" != x"" ]; then DE=kde;
elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
elif [ x"$MATE_DESKTOP_SESSION_ID" != x"" ]; then DE=mate;
elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome;
elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce
elif echo $DESKTOP | grep -q '^Enlightenment'; then DE=enlightenment;
fi
fi
if [ x"$DE" = x"" ]; then
# fallback to checking $DESKTOP_SESSION
case "$DESKTOP_SESSION" in
gnome)
DE=gnome;
;;
LXDE|Lubuntu)
DE=lxde;
;;
MATE)
DE=mate;
;;
xfce|xfce4|'Xfce Session')
DE=xfce;
;;
esac
fi
if [ x"$DE" = x"" ]; then
# fallback to uname output for other platforms
case "$(uname 2>/dev/null)" in
Darwin)
DE=darwin;
;;
esac
fi
if [ x"$DE" = x"gnome" ]; then
# gnome-default-applications-properties is only available in GNOME 2.x
# but not in GNOME 3.x
which gnome-default-applications-properties > /dev/null 2>&1 || DE="gnome3"
fi
}
#----------------------------------------------------------------------------
# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4
# It also always returns 1 in KDE 3.4 and earlier
# Simply return 0 in such case
kfmclient_fix_exit_code()
{
version=`LC_ALL=C.UTF-8 kde-config --version 2>/dev/null | grep '^KDE'`
major=`echo $version | sed 's/KDE.*: \([0-9]\).*/\1/'`
minor=`echo $version | sed 's/KDE.*: [0-9]*\.\([0-9]\).*/\1/'`
release=`echo $version | sed 's/KDE.*: [0-9]*\.[0-9]*\.\([0-9]\).*/\1/'`
test "$major" -gt 3 && return $1
test "$minor" -gt 5 && return $1
test "$release" -gt 4 && return $1
return 0
}
# This handles backslashes but not quote marks.
last_word()
{
read first rest
echo "$rest"
}
# Get the value of a key in a desktop file's Desktop Entry group.
# Example: Use get_key foo.desktop Exec
# to get the values of the Exec= key for the Desktop Entry group.
get_key()
{
local file="${1}"
local key="${2}"
local desktop_entry=""
IFS_="${IFS}"
IFS=""
while read line
do
case "$line" in
"[Desktop Entry]")
desktop_entry="y"
;;
# Reset match flag for other groups
"["*)
desktop_entry=""
;;
"${key}="*)
# Only match Desktop Entry group
if [ -n "${desktop_entry}" ]
then
echo "${line}" | cut -d= -f 2-
fi
esac
done < "${file}"
IFS="${IFS_}"
}
open_darwin()
{
open "$1"
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_kde()
{
if [ -n "${KDE_SESSION_VERSION}" ]; then
case "${KDE_SESSION_VERSION}" in
4)
kde-open "$1"
;;
5)
kde-open${KDE_SESSION_VERSION} "$1"
;;
esac
else
kfmclient exec "$1"
kfmclient_fix_exit_code $?
fi
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_gnome()
{
if gvfs-open --help 2>/dev/null 1>&2; then
gvfs-open "$1"
else
gnome-open "$1"
fi
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_mate()
{
if gvfs-open --help 2>/dev/null 1>&2; then
gvfs-open "$1"
else
mate-open "$1"
fi
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_xfce()
{
exo-open "$1"
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
open_enlightenment()
{
enlightenment_open "$1"
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
#-----------------------------------------
# Recursively search .desktop file
search_desktop_file()
{
local default="$1"
local dir="$2"
local target="$3"
local file=""
# look for both vendor-app.desktop, vendor/app.desktop
if [ -r "$dir/$default" ]; then
file="$dir/$default"
elif [ -r "$dir/`echo $default | sed -e 's|-|/|'`" ]; then
file="$dir/`echo $default | sed -e 's|-|/|'`"
fi
if [ -r "$file" ] ; then
command="$(get_key "${file}" "Exec" | first_word)"
command_exec=`which $command 2>/dev/null`
icon="$(get_key "${file}" "Icon")"
# FIXME: Actually LC_MESSAGES should be used as described in
# http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s04.html
localised_name="$(get_key "${file}" "Name")"
set -- $(get_key "${file}" "Exec" | last_word)
# We need to replace any occurrence of "%f", "%F" and
# the like by the target file. We examine each
# argument and append the modified argument to the
# end then shift.
local args=$#
local replaced=0
while [ $args -gt 0 ]; do
case $1 in
%[c])
replaced=1
arg="${localised_name}"
shift
set -- "$@" "$arg"
;;
%[fFuU])
replaced=1
arg="$target"
shift
set -- "$@" "$arg"
;;
%[i])
replaced=1
shift
set -- "$@" "--icon" "$icon"
;;
*)
arg="$1"
shift
set -- "$@" "$arg"
;;
esac
args=$(( $args - 1 ))
done
[ $replaced -eq 1 ] || set -- "$@" "$target"
"$command_exec" "$@"
if [ $? -eq 0 ]; then
exit_success
fi
fi
for d in $dir/*/; do
[ -d "$d" ] && search_desktop_file "$default" "$d" "$target"
done
}
open_generic_xdg_mime()
{
filetype="$2"
default=`xdg-mime query default "$filetype"`
if [ -n "$default" ] ; then
xdg_user_dir="$XDG_DATA_HOME"
[ -n "$xdg_user_dir" ] || xdg_user_dir="$HOME/.local/share"
xdg_system_dirs="$XDG_DATA_DIRS"
[ -n "$xdg_system_dirs" ] || xdg_system_dirs=/usr/local/share/:/usr/share/
DEBUG 3 "$xdg_user_dir:$xdg_system_dirs"
for x in `echo "$xdg_user_dir:$xdg_system_dirs" | sed 's/:/ /g'`; do
search_desktop_file "$default" "$x/applications/" "$1"
done
fi
}
open_generic_xdg_file_mime()
{
filetype=`xdg-mime query filetype "$1" | sed "s/;.*//"`
open_generic_xdg_mime "$1" "$filetype"
}
open_generic_xdg_x_scheme_handler()
{
scheme="`echo $1 | sed -n 's/\(^[[:alnum:]+\.-]*\):.*$/\1/p'`"
if [ -n $scheme ]; then
filetype="x-scheme-handler/$scheme"
open_generic_xdg_mime "$1" "$filetype"
fi
}
open_generic()
{
# Paths or file:// URLs
if (echo "$1" | grep -q '^file://' ||
! echo "$1" | egrep -q '^[[:alpha:]+\.\-]+:'); then
local file="$1"
# Decode URLs
if echo "$file" | grep -q '^file:///'; then
file=${file#file://}
file="$(printf "$(echo "$file" | sed -e 's@%\([a-f0-9A-F]\{2\}\)@\\x\1@g')")"
fi
file_check=${file%%#*}
file_check=${file_check%%\?*}
check_input_file "$file_check"
filetype=`xdg-mime query filetype "$file_check" | sed "s/;.*//"`
open_generic_xdg_mime "$file" "$filetype"
if which run-mailcap 2>/dev/null 1>&2; then
run-mailcap --action=view "$file"
if [ $? -eq 0 ]; then
exit_success
fi
fi
if mimeopen -v 2>/dev/null 1>&2; then
mimeopen -L -n "$file"
if [ $? -eq 0 ]; then
exit_success
fi
fi
fi
open_generic_xdg_x_scheme_handler "$1"
IFS=":"
for browser in $BROWSER; do
if [ x"$browser" != x"" ]; then
browser_with_arg=`printf "$browser" "$1" 2>/dev/null`
if [ $? -ne 0 ]; then
browser_with_arg=$browser;
fi
if [ x"$browser_with_arg" = x"$browser" ]; then
eval '$browser "$1"'$xdg_redirect_output;
else eval '$browser_with_arg'$xdg_redirect_output;
fi
if [ $? -eq 0 ]; then
exit_success;
fi
fi
done
exit_failure_operation_impossible "no method available for opening '$1'"
}
open_lxde()
{
# pcmanfm only knows how to handle file:// urls and filepaths, it seems.
if (echo "$1" | grep -q '^file://' ||
! echo "$1" | egrep -q '^[[:alpha:]+\.\-]+:')
then
local file="$1"
# handle relative paths
if ! echo "$file" | egrep -q '^(file://)?/'; then
file="$(pwd)/$file"
fi
pcmanfm "$file"
else
open_generic "$1"
fi
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
[ x"$1" != x"" ] || exit_failure_syntax
url=
while [ $# -gt 0 ] ; do
parm="$1"
shift
case "$parm" in
-*)
exit_failure_syntax "unexpected option '$parm'"
;;
*)
if [ -n "$url" ] ; then
exit_failure_syntax "unexpected argument '$parm'"
fi
url="$parm"
;;
esac
done
if [ -z "${url}" ] ; then
exit_failure_syntax "file or URL argument missing"
fi
detectDE
if [ x"$DE" = x"" ]; then
DE=generic
fi
DEBUG 2 "Selected DE $DE"
# sanitize BROWSER (avoid caling ourselves in particular)
case "${BROWSER}" in
*:"xdg-open"|"xdg-open":*)
BROWSER=$(echo $BROWSER | sed -e 's|:xdg-open||g' -e 's|xdg-open:||g')
;;
"xdg-open")
BROWSER=
;;
esac
# if BROWSER variable is not set, check some well known browsers instead
if [ x"$BROWSER" = x"" ]; then
BROWSER=links2:elinks:links:lynx:w3m
if [ -n "$DISPLAY" ]; then
BROWSER=x-www-browser:firefox:seamonkey:mozilla:epiphany:konqueror:chromium-browser:google-chrome:$BROWSER
fi
fi
case "$DE" in
kde)
open_kde "$url"
;;
gnome*)
open_gnome "$url"
;;
mate)
open_mate "$url"
;;
xfce)
open_xfce "$url"
;;
lxde)
open_lxde "$url"
;;
enlightenment)
open_enlightenment "$url"
;;
generic)
open_generic "$url"
;;
*)
exit_failure_operation_impossible "no method available for opening '$url'"
;;
esac

View File

@@ -0,0 +1,4 @@
dist/*
build/*
tests/html/lib/sockjs.js
tests/html/static/*

View File

@@ -0,0 +1,14 @@
tests/
dist/
!dist/sockjs.js
!dist/sockjs.js.map
!dist/sockjs.min.js
!dist/sockjs.min.js.map
build/
.travis.yml
.zuul.yml
.eslintrc
.jscsrc
bower.json
gulpfile.js
Makefile

View File

@@ -0,0 +1 @@
6.9.4

View File

@@ -0,0 +1,9 @@
Parts of the code are derived from various open source projects.
For code derived from Socket.IO by Guillermo Rauch see
https://github.com/LearnBoost/socket.io/tree/0.6.17#readme.
Snippets derived from jQuery-JSONP by Julian Aubourg, generic MIT
license.
All other code is released on MIT license, see LICENSE.

View File

@@ -0,0 +1,281 @@
1.1.2
==
* Ensure both sender and receiver are cleaned upon close - #342
* Remove event listeners before calling `close` - #344
* Update documentation links - #351, #339, #316
* Explicitly export `undefined` when `WebSocket` does not exist. Fixes Webpack. #321
* Include `dist` folder on npm - #265
* Simplify build setup
* Update to Node.js 6.9
* Add sourcemap for minified version
* Remove unused String.trim shim
1.1.1
==
* Do not pass `protocols` or `options` arguments to browser WebSocket constructor - #309
1.1.0
==
* Fix IE7/8 usage of `console.log` which does not have `apply` - #279
* Remove `dbg` global variable - #282
* Bump `faye-websocket` version to `0.11.0` - #267
* Optimize `arguments` usage - #263
* Add sourcemap file to dist folder - #237
* Add way to transparently pass transport-specific options - #272
1.0.3
==
* Use `https` module for xhr requests in node when url uses https - #254
1.0.2
==
* Fix iframe info receiver url
* Move iframe.contentWindow check inside setTimeout - #246
1.0.1
==
* Use proper base url for iframe-based info receiver - #249
* Don't register unload event in chrome packaged app - #223
* Allow custom session ids - #250
* Remove version property from bower.json - #247
* Update example CDN url - #244
1.0.0
===
* Simplify url handling by delegating to `url-parse` - #242
* Upgrade to `url-parse` 1.0.1 to fix colon issue if auth has no password
1.0.0-beta.13
===
* Transport timeout on connection should fallback - #238
1.0.0-beta.12
====
* Upgrade `url-parse` to 1.0.0 to fix #218 again
1.0.0-beta.10
====
* Upgrade `url-parse` to 0.2.3 to fix #222
1.0.0-beta.9
====
* Upgrade `url-parse` to 0.2.1 to fix 'too much recursion' errors
1.0.0-beta.8
====
* Upgrade `url-parse` to 0.2.0 to fix inheritance issues
1.0.0-beta.7
====
* Upgrade `url-parse` to 0.1.5 to fix #218
* Don't strip basic auth from url - #219
1.0.0-beta.6
====
* Upgrade `url-parse` to 0.1.3 to avoid CSP issues
1.0.0-beta.5
=====
* Upgrade `url-parse` to 0.1.1 to fix #214
1.0.0-beta.4
=====
* Upgrade `url-parse` to 0.1.0 and `sockjs` to 0.3.11
* Update .npmignore
1.0.0-beta.3
=====
* Move `debug` from devDependencies to dependencies
1.0.0-beta.2
=====
* Relax requirements when using same origin XHR - #80
* Upgrade to JSON3 from JSON2 - #123
* Package library with browserify supporting the UMD pattern - #184
* Move tests to JavaScript
* Add Gulp.js build script
* Fix getOrigin for file:/// urls and standard ports - #173
* Add onerror event handlers to Websockets - #169
* Increase RTO lower bound to prevent spurious timeouts on IE8/9 - #161
* Use window.crypto for random values when available - #128
* Fix handling of listeners added and removed mid-dispatch - #127
* Fix XHR Streaming for IE8 - #83
* Remove explicit AMD name - #107
* Check for an empty response from /info request - #143
* Add Content-Type to XHR requests to fix issue over HTTPS on Galaxy S4 - #164
* Fix iframe fallback when message is sent from a popup in IE7/8 - #166
* Add support for query strings on the url - #72
* Now works inside of Web Workers - #181
* Support EventSource / Server Sent Events outside of iframes - #201
* Rename protocols to transports - #65
* Allow transports which need the body to trigger on 'interactive' readyState - #175
* try/catch access to document.domain - #187
* Use `window.location` instead of `document.location` - #195
* Allow usage from node.js with same API
0.3.4
=====
* Mentioned njoyce's fork of sockjs-gevent.
* #90 - Don't catch onbeforeunload event - it breaks javascript://
links in IE.
* IE mangles 204 response code for 1223 on ajax, see:
http://bugs.jquery.com/ticket/1450
* Make `new` optional for SockJS constructor (via substack).
* It is impossible to cancel JSONP polling request - compensate for that.
* Refactored EventEmitter prototype (used only internally)
* #66 - Failure to post data to /xhr_send should kill the session
0.3.2
=====
* #77 - Getting /info on modern browsers when html is served from
file:// urls was broken.
0.3.1
=====
* #61 - Meteor guys found that we unintentionally catch "onopen" errors.
* #63 - Meteorjs guys found that xhr-streaming on Safari sometimes
left busy cursor running.
* Increased allowed time for websocket transport (from 1 rtt to 2),
this should make ws transport more reliable over SSL, at the cost
of slightly longer connection time for users with blocked ws.
* #57 - previous fix didn't really work, sockjs-client still left
a mess in browsers history when using iframe transports. This
is fixed now.
* #60 - Opera 12 (next) claims to do AJAX2 / CORS, but can't
do xhr-streaming.
* #58 - onunload test sometimes failed on Safari on windows
* Updated readme WRT websocket protocols
* Updated readme WRT deployments on heroku
* Add minimalistic license block to every source file.
0.3.0
=====
* Temporarily disabled iframe tests - they are failing unpredictably.
* #57 - pointing an iframe to "about:blank" during cleanup caused
Opera to messup history.
* #55 - Improved iframe abstraction (reduced a possible mem leak)
* Refactored AJAX abstractions, for better CORS handing - again.
* Add additional parent origin security check to an iframe.
* Urls with hashes or query strings can't be passed to SockJS.
* #18 - Mention workaround for Firefox ESC key issue
* #53 - AMD compliance
* sockjs/sockjs-protocol#28 - always use square brackets for
websocket frames
* #51 - initial support for IE10 - try XHR before XDR
* #28 - handle onunload / onbeforeunload in a more robust fashion
* #49 - support SockJS-client being used from files served from
file:// urls.
0.2.1
=====
* "smoke-latency.html" test was unnecesairly sending too much data.
* Bumped core dependencies (coffee-script and uglify-js)
* Minor updates to the README, few cosmetic changes in the code.
0.2.0
=====
* The API had changed - use `protocols_whitelist` option instead of
passing an array of protocols as a second argument to SockJS constructor.
* Dropped 'chunking-test' functionality and replace it with 'info'.
* Rewritten protocol-choosing alogirthm, see "utils.detectProtocols" method.
* Use dynamic protocol timeouts based on RTT, not hardcoded 5 seconds
* #34 - Don't ever reuse `session_id`, especially when trying
fallback protocols.
* The test server got moved from SockJS-client to SockJS-node.
* Don't test unicode surrogates - it can't work in some environments.
* XHR/XDR helpers were rewritten, ajax transports were simplified.
* Added a domain check in the iframe to improve security.
* SockJS will now trigger 1002 error if there is a problem during handshake
instead of 2000 error.
* Smoke-throughput test is renamed to smoke-latency.
0.1.2
=====
* #29 - Allow all unicode characters to be send over SockJS.
* #15 - SockJS should now work fine even if the connection is started
in HEAD, before BODY is loaded.
* #28 - In rare circumstances WebSocket connection can be left intact
after the page is unloaded in FireFox.
* Updated scripts to work with Node 0.6.
* Initial work to do better QUnit testing.
* Updated the minifying script (always escape unicode chars, remove
trailing comment).
* Use string instead of array of chars (utils.js:random_number_string).
0.1.1
=====
* #21 Get JsonP transport working on IE9 (Vladimir Dronnikov).
* #26 Emit heartbeat event.
* #27 Include license inline.
0.1.0
=====
* SockJS-client can only send UTF-8 encodable strings. Previously we
took advantage of rich data structures and automatically
json-encoded them, but this got removed. Now, all data passed to
`send` will be converted to string. This is also how native
* `status` property on `EventClose` is renamed to `code`
as per Websocket API
WebSockets behave.
* The test server was updated to new `sockjs-node` API
* Fixed problem with Jsonp-polling transport on IE9
* Repository was moved - updated links.
0.0.4
=====
* All transports were refactored, some transports were introduced:
htmlfile and separate xhr-streaming.
* Added logic to detect support for http chunking, and thus a
possibility to rule out streaming transports before running them.
* Added 'cookie' option, useful for cookie-based load balancing
(currently, it make a difference only for IE).
* Added hack to prevent EventSource from crashing Safari and Chrome.
* Loads and loads of other small and medium changes.
0.0.2
=====
* Initial support for JSESSIONID based load balancing. Currently
doesn't play nicely with IE XDomainRequest transport.
0.0.1
=====
* Initial release.

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2011-2012 VMware, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,336 @@
# SockJS-client
[![npm version](https://img.shields.io/npm/v/sockjs-client.svg?style=flat-square)](https://www.npmjs.com/package/sockjs-client)[![Build Status](https://img.shields.io/travis/sockjs/sockjs-client/master.svg?style=flat-square)](https://travis-ci.org/sockjs/sockjs-client)[![Dependencies](https://img.shields.io/david/sockjs/sockjs-client.svg?style=flat-square)](https://david-dm.org/sockjs/sockjs-client)[![Chat](https://img.shields.io/badge/Chat-gitter.im-blue.svg?style=flat-square)](https://gitter.im/sockjs/sockjs-client)
[![Sauce Test Status](https://saucelabs.com/buildstatus/brycekahle)](https://saucelabs.com/u/brycekahle)
SockJS is a browser JavaScript library that provides a WebSocket-like
object. SockJS gives you a coherent, cross-browser, Javascript API
which creates a low latency, full duplex, cross-domain communication
channel between the browser and the web server.
Under the hood SockJS tries to use native WebSockets first. If that
fails it can use a variety of browser-specific transport protocols and
presents them through WebSocket-like abstractions.
SockJS is intended to work for all modern browsers and in environments
which don't support the WebSocket protocol -- for example, behind restrictive
corporate proxies.
SockJS-client does require a server counterpart:
* [SockJS-node](https://github.com/sockjs/sockjs-node) is a SockJS
server for Node.js.
Philosophy:
* The API should follow
[HTML5 Websockets API](https://www.w3.org/TR/websockets/) as
closely as possible.
* All the transports must support cross domain connections out of the
box. It's possible and recommended to host a SockJS server on a
different server than your main web site.
* There is support for at least one streaming protocol for every
major browser.
* Streaming transports should work cross-domain and
should support cookies (for cookie-based sticky sessions).
* Polling transports are used as a fallback for old browsers and
hosts behind restrictive proxies.
* Connection establishment should be fast and lightweight.
* No Flash inside (no need to open port 843 - which doesn't work
through proxies, no need to host 'crossdomain.xml', no need
[to wait for 3 seconds](https://github.com/gimite/web-socket-js/issues/49)
in order to detect problems)
Subscribe to
[SockJS mailing list](https://groups.google.com/forum/#!forum/sockjs) for
discussions and support.
SockJS family:
* [SockJS-client](https://github.com/sockjs/sockjs-client) JavaScript client library
* [SockJS-node](https://github.com/sockjs/sockjs-node) Node.js server
* [SockJS-erlang](https://github.com/sockjs/sockjs-erlang) Erlang server
* [SockJS-cyclone](https://github.com/flaviogrossi/sockjs-cyclone) Python/Cyclone/Twisted server
* [SockJS-tornado](https://github.com/MrJoes/sockjs-tornado) Python/Tornado server
* [SockJS-twisted](https://github.com/DesertBus/sockjs-twisted/) Python/Twisted server
* [SockJS-aiohttp](https://github.com/aio-libs/sockjs/) Python/Aiohttp server
* [Spring Framework](https://projects.spring.io/spring-framework) Java [client](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/websocket.html#websocket-fallback-sockjs-client) & server
* [vert.x](https://github.com/vert-x/vert.x) Java/vert.x server
* [Xitrum](https://xitrum-framework.github.io/) Scala server
* [Atmosphere Framework](https://github.com/Atmosphere/atmosphere) JavaEE Server, Play Framework, Netty, Vert.x
Work in progress:
* [SockJS-ruby](https://github.com/nyarly/sockjs-ruby)
* [SockJS-netty](https://github.com/cgbystrom/sockjs-netty)
* [SockJS-gevent](https://github.com/ksava/sockjs-gevent) ([SockJS-gevent fork](https://github.com/njoyce/sockjs-gevent))
* [pyramid-SockJS](https://github.com/fafhrd91/pyramid_sockjs)
* [wildcloud-websockets](https://github.com/wildcloud/wildcloud-websockets)
* [wai-SockJS](https://github.com/Palmik/wai-sockjs)
* [SockJS-perl](https://github.com/vti/sockjs-perl)
* [SockJS-go](https://github.com/igm/sockjs-go/)
Getting Started
-------
SockJS mimics the [WebSockets API](https://www.w3.org/TR/websockets/),
but instead of `WebSocket` there is a `SockJS` Javascript object.
First, you need to load the SockJS JavaScript library. For example, you can
put that in your HTML head:
```html
<script src="https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script>
```
After the script is loaded you can establish a connection with the
SockJS server. Here's a simple example:
```javascript
var sock = new SockJS('https://mydomain.com/my_prefix');
sock.onopen = function() {
console.log('open');
};
sock.onmessage = function(e) {
console.log('message', e.data);
};
sock.onclose = function() {
console.log('close');
};
sock.send('test');
sock.close();
```
SockJS-client API
-----------------
### SockJS class
Similar to the 'WebSocket' API, the 'SockJS' constructor takes one, or more arguments:
```javascript
var sockjs = new SockJS(url, _reserved, options);
```
`url` may contain a query string, if one is desired.
Where `options` is a hash which can contain:
* **server (string)**
String to append to url for actual data connection. Defaults to a random 4 digit number.
* **transports (string OR array of strings)**
Sometimes it is useful to disable some fallback transports. This
option allows you to supply a list transports that may be used by
SockJS. By default all available transports will be used.
* **sessionId (number OR function)**
Both client and server use session identifiers to distinguish connections.
If you specify this option as a number, SockJS will use its random string
generator function to generate session ids that are N-character long
(where N corresponds to the number specified by **sessionId**).
When you specify this option as a function, the function must return a
randomly generated string. Every time SockJS needs to generate a session
id it will call this function and use the returned string directly.
If you don't specify this option, the default is to use the default random
string generator to generate 8-character long session ids.
Although the 'SockJS' object tries to emulate the 'WebSocket'
behaviour, it's impossible to support all of its features. An
important SockJS limitation is the fact that you're not allowed to
open more than one SockJS connection to a single domain at a time.
This limitation is caused by an in-browser limit of outgoing
connections - usually [browsers don't allow opening more than two
outgoing connections to a single domain](https://stackoverflow.com/questions/985431/max-parallel-http-connections-in-a-browser). A single SockJS session
requires those two connections - one for downloading data, the other for
sending messages. Opening a second SockJS session at the same time
would most likely block, and can result in both sessions timing out.
Opening more than one SockJS connection at a time is generally a
bad practice. If you absolutely must do it, you can use
multiple subdomains, using a different subdomain for every
SockJS connection.
Supported transports, by browser (html served from http:// or https://)
-----------------------------------------------------------------------
_Browser_ | _Websockets_ | _Streaming_ | _Polling_
----------------|------------------|-------------|-------------------
IE 6, 7 | no | no | jsonp-polling
IE 8, 9 (cookies=no) | no | xdr-streaming &dagger; | xdr-polling &dagger;
IE 8, 9 (cookies=yes)| no | iframe-htmlfile | iframe-xhr-polling
IE 10 | rfc6455 | xhr-streaming | xhr-polling
Chrome 6-13 | hixie-76 | xhr-streaming | xhr-polling
Chrome 14+ | hybi-10 / rfc6455| xhr-streaming | xhr-polling
Firefox <10 | no &Dagger; | xhr-streaming | xhr-polling
Firefox 10+ | hybi-10 / rfc6455| xhr-streaming | xhr-polling
Safari 5.x | hixie-76 | xhr-streaming | xhr-polling
Safari 6+ | rfc6455 | xhr-streaming | xhr-polling
Opera 10.70+ | no &Dagger; | iframe-eventsource | iframe-xhr-polling
Opera 12.10+ | rfc6455 | xhr-streaming | xhr-polling
Konqueror | no | no | jsonp-polling
* **&dagger;**: IE 8+ supports [XDomainRequest][^9], which is
essentially a modified AJAX/XHR that can do requests across
domains. But unfortunately it doesn't send any cookies, which
makes it inappropriate for deployments when the load balancer uses
JSESSIONID cookie to do sticky sessions.
* **&Dagger;**: Firefox 4.0 and Opera 11.00 and shipped with disabled
Websockets "hixie-76". They can still be enabled by manually
changing a browser setting.
Supported transports, by browser (html served from file://)
-----------------------------------------------------------
Sometimes you may want to serve your html from "file://" address - for
development or if you're using PhoneGap or similar technologies. But
due to the Cross Origin Policy files served from "file://" have no
Origin, and that means some of SockJS transports won't work. For this
reason the SockJS transport table is different than usually, major
differences are:
_Browser_ | _Websockets_ | _Streaming_ | _Polling_
----------------|---------------|--------------------|-------------------
IE 8, 9 | same as above | iframe-htmlfile | iframe-xhr-polling
Other | same as above | iframe-eventsource | iframe-xhr-polling
Supported transports, by name
-----------------------------
_Transport_ | _References_
---------------------|---------------
websocket (rfc6455) | [rfc 6455][^10]
websocket (hixie-76) | [draft-hixie-thewebsocketprotocol-76][^1]
websocket (hybi-10) | [draft-ietf-hybi-thewebsocketprotocol-10][^2]
xhr-streaming | Transport using [Cross domain XHR][^5] [streaming][^7] capability (readyState=3).
xdr-streaming | Transport using [XDomainRequest][^9] [streaming][^7] capability (readyState=3).
eventsource | [EventSource/Server-sent events][^4].
iframe-eventsource | [EventSource/Server-sent events][^4] used from an [iframe via postMessage][^3].
htmlfile | [HtmlFile][^8].
iframe-htmlfile | [HtmlFile][^8] used from an [iframe via postMessage][^3].
xhr-polling | Long-polling using [cross domain XHR][^5].
xdr-polling | Long-polling using [XDomainRequest][^9].
iframe-xhr-polling | Long-polling using normal AJAX from an [iframe via postMessage][^3].
jsonp-polling | Slow and old fashioned [JSONP polling][^6]. This transport will show "busy indicator" (aka: "spinning wheel") when sending data.
[^1]: https://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
[^2]: https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10
[^3]: https://developer.mozilla.org/en/DOM/window.postMessage
[^4]: https://html.spec.whatwg.org/multipage/comms.html#server-sent-events
[^5]: https://secure.wikimedia.org/wikipedia/en/wiki/XMLHttpRequest#Cross-domain_requests
[^6]: https://secure.wikimedia.org/wikipedia/en/wiki/JSONP
[^7]: http://www.debugtheweb.com/test/teststreaming.aspx
[^8]: http://cometdaily.com/2007/11/18/ie-activexhtmlfile-transport-part-ii/
[^9]: https://blogs.msdn.microsoft.com/ieinternals/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds/
[^10]: https://www.rfc-editor.org/rfc/rfc6455.txt
Connecting to SockJS without the client
---------------------------------------
Although the main point of SockJS is to enable browser-to-server
connectivity, it is possible to connect to SockJS from an external
application. Any SockJS server complying with 0.3 protocol does
support a raw WebSocket url. The raw WebSocket url for the test server
looks like:
* ws://localhost:8081/echo/websocket
You can connect any WebSocket RFC 6455 compliant WebSocket client to
this url. This can be a command line client, external application,
third party code or even a browser (though I don't know why you would
want to do so).
Deployment
----------
You should use a version of sockjs-client
that supports the protocol used by your server. For example:
```html
<script src="https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script>
```
For server-side deployment tricks, especially about load balancing and
session stickiness, take a look at the
[SockJS-node readme](https://github.com/sockjs/sockjs-node#readme).
Development and testing
-----------------------
SockJS-client needs [node.js](https://nodejs.org/) for running a test
server and JavaScript minification. If you want to work on
SockJS-client source code, checkout the git repo and follow these
steps:
cd sockjs-client
npm install
To generate JavaScript, run:
gulp browserify
To generate minified JavaScript, run:
gulp browserify:min
Both commands output into the `build` directory.
### Testing
Once you've compiled the SockJS-client you may want to check if your changes
pass all the tests.
npm run test:browser_local
This will start [zuul](https://github.com/defunctzombie/zuul) and a test support server. Open the browser to [http://localhost:9090/_zuul](http://localhost:9090/_zuul) and watch the tests run.
Browser Quirks
--------------
There are various browser quirks which we don't intend to address:
* Pressing ESC in Firefox, before Firefox 20, closes the SockJS connection. For a workaround
and discussion see [#18](https://github.com/sockjs/sockjs-client/issues/18).
* `jsonp-polling` transport will show a "spinning wheel" (aka. "busy indicator")
when sending data.
* You can't open more than one SockJS connection to one domain at the
same time due to [the browser's limit of concurrent connections](https://stackoverflow.com/questions/985431/max-parallel-http-connections-in-a-browser)
(this limit is not counting native WebSocket connections).
* Although SockJS is trying to escape any strange Unicode characters
(even invalid ones - [like surrogates \xD800-\xDBFF](https://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates) or [\xFFFE and \xFFFF](https://en.wikipedia.org/wiki/Unicode#Character_General_Category))
it's advisable to use only valid characters. Using invalid
characters is a bit slower, and may not work with SockJS servers
that have proper Unicode support.
* Having a global function called `onmessage` or such is probably a
bad idea, as it could be called by the built-in `postMessage` API.
* From SockJS' point of view there is nothing special about
SSL/HTTPS. Connecting between unencrypted and encrypted sites
should work just fine.
* Although SockJS does its best to support both prefix and cookie based
sticky sessions, the latter may not work well cross-domain with
browsers that don't accept third-party cookies by default (Safari).
In order to get around this make sure you're connecting to SockJS
from the same parent domain as the main site. For example
'sockjs.a.com' is able to set cookies if you're connecting from
'www.a.com' or 'a.com'.
* Trying to connect from secure "https://" to insecure "http://" is
not a good idea. The other way around should be fine.
* Long polling is known to cause problems on Heroku, but a
[workaround for SockJS is available](https://github.com/sockjs/sockjs-node/issues/57#issuecomment-5242187).
* SockJS [websocket transport is more stable over SSL](https://github.com/sockjs/sockjs-client/issues/94). If
you're a serious SockJS user then consider using SSL
([more info](https://www.ietf.org/mail-archive/web/hybi/current/msg01605.html)).

View File

@@ -0,0 +1,18 @@
#!/bin/bash
set -e
if [ "x${BROWSER}" = "x" ]; then
npm run lint
npm test
elif [ "${TRAVIS_SECURE_ENV_VARS}" = "true" ]; then
npm run test:bundle
if [ "x${BROWSER_NAME}" = "x" ]; then
./node_modules/.bin/zuul tests/browser.js
elif [ "x${BROWSER_PLATFORM}" = "x" ]; then
./node_modules/.bin/zuul --browser-name $BROWSER_NAME --browser-version $BROWSER_VERSION tests/browser.js
else
./node_modules/.bin/zuul --browser-name $BROWSER_NAME --browser-version $BROWSER_VERSION --browser-platform "$BROWSER_PLATFORM" tests/browser.js
fi
else
exit 1
fi

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
'use strict';
var transportList = require('./transport-list');
module.exports = require('./main')(transportList);
// TODO can't get rid of this until all servers do
if ('_sockjs_onload' in global) {
setTimeout(global._sockjs_onload, 1);
}

View File

@@ -0,0 +1,17 @@
'use strict';
var inherits = require('inherits')
, Event = require('./event')
;
function CloseEvent() {
Event.call(this);
this.initEvent('close', false, false);
this.wasClean = false;
this.code = 0;
this.reason = '';
}
inherits(CloseEvent, Event);
module.exports = CloseEvent;

View File

@@ -0,0 +1,57 @@
'use strict';
var inherits = require('inherits')
, EventTarget = require('./eventtarget')
;
function EventEmitter() {
EventTarget.call(this);
}
inherits(EventEmitter, EventTarget);
EventEmitter.prototype.removeAllListeners = function(type) {
if (type) {
delete this._listeners[type];
} else {
this._listeners = {};
}
};
EventEmitter.prototype.once = function(type, listener) {
var self = this
, fired = false;
function g() {
self.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(this, arguments);
}
}
this.on(type, g);
};
EventEmitter.prototype.emit = function() {
var type = arguments[0];
var listeners = this._listeners[type];
if (!listeners) {
return;
}
// equivalent of Array.prototype.slice.call(arguments, 1);
var l = arguments.length;
var args = new Array(l - 1);
for (var ai = 1; ai < l; ai++) {
args[ai - 1] = arguments[ai];
}
for (var i = 0; i < listeners.length; i++) {
listeners[i].apply(this, args);
}
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener = EventTarget.prototype.addEventListener;
EventEmitter.prototype.removeListener = EventTarget.prototype.removeEventListener;
module.exports.EventEmitter = EventEmitter;

View File

@@ -0,0 +1,22 @@
'use strict';
function Event(eventType) {
this.type = eventType;
}
Event.prototype.initEvent = function(eventType, canBubble, cancelable) {
this.type = eventType;
this.bubbles = canBubble;
this.cancelable = cancelable;
this.timeStamp = +new Date();
return this;
};
Event.prototype.stopPropagation = function() {};
Event.prototype.preventDefault = function() {};
Event.CAPTURING_PHASE = 1;
Event.AT_TARGET = 2;
Event.BUBBLING_PHASE = 3;
module.exports = Event;

View File

@@ -0,0 +1,62 @@
'use strict';
/* Simplified implementation of DOM2 EventTarget.
* http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget
*/
function EventTarget() {
this._listeners = {};
}
EventTarget.prototype.addEventListener = function(eventType, listener) {
if (!(eventType in this._listeners)) {
this._listeners[eventType] = [];
}
var arr = this._listeners[eventType];
// #4
if (arr.indexOf(listener) === -1) {
// Make a copy so as not to interfere with a current dispatchEvent.
arr = arr.concat([listener]);
}
this._listeners[eventType] = arr;
};
EventTarget.prototype.removeEventListener = function(eventType, listener) {
var arr = this._listeners[eventType];
if (!arr) {
return;
}
var idx = arr.indexOf(listener);
if (idx !== -1) {
if (arr.length > 1) {
// Make a copy so as not to interfere with a current dispatchEvent.
this._listeners[eventType] = arr.slice(0, idx).concat(arr.slice(idx + 1));
} else {
delete this._listeners[eventType];
}
return;
}
};
EventTarget.prototype.dispatchEvent = function() {
var event = arguments[0];
var t = event.type;
// equivalent of Array.prototype.slice.call(arguments, 0);
var args = arguments.length === 1 ? [event] : Array.apply(null, arguments);
// TODO: This doesn't match the real behavior; per spec, onfoo get
// their place in line from the /first/ time they're set from
// non-null. Although WebKit bumps it to the end every time it's
// set.
if (this['on' + t]) {
this['on' + t].apply(this, args);
}
if (t in this._listeners) {
// Grab a reference to the listeners list. removeEventListener may alter the list.
var listeners = this._listeners[t];
for (var i = 0; i < listeners.length; i++) {
listeners[i].apply(this, args);
}
}
};
module.exports = EventTarget;

View File

@@ -0,0 +1,15 @@
'use strict';
var inherits = require('inherits')
, Event = require('./event')
;
function TransportMessageEvent(data) {
Event.call(this);
this.initEvent('message', false, false);
this.data = data;
}
inherits(TransportMessageEvent, Event);
module.exports = TransportMessageEvent;

View File

@@ -0,0 +1,27 @@
'use strict';
var JSON3 = require('json3')
, iframeUtils = require('./utils/iframe')
;
function FacadeJS(transport) {
this._transport = transport;
transport.on('message', this._transportMessage.bind(this));
transport.on('close', this._transportClose.bind(this));
}
FacadeJS.prototype._transportClose = function(code, reason) {
iframeUtils.postMessage('c', JSON3.stringify([code, reason]));
};
FacadeJS.prototype._transportMessage = function(frame) {
iframeUtils.postMessage('t', frame);
};
FacadeJS.prototype._send = function(data) {
this._transport.send(data);
};
FacadeJS.prototype._close = function() {
this._transport.close();
this._transport.removeAllListeners();
};
module.exports = FacadeJS;

View File

@@ -0,0 +1,102 @@
'use strict';
var urlUtils = require('./utils/url')
, eventUtils = require('./utils/event')
, JSON3 = require('json3')
, FacadeJS = require('./facade')
, InfoIframeReceiver = require('./info-iframe-receiver')
, iframeUtils = require('./utils/iframe')
, loc = require('./location')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:iframe-bootstrap');
}
module.exports = function(SockJS, availableTransports) {
var transportMap = {};
availableTransports.forEach(function(at) {
if (at.facadeTransport) {
transportMap[at.facadeTransport.transportName] = at.facadeTransport;
}
});
// hard-coded for the info iframe
// TODO see if we can make this more dynamic
transportMap[InfoIframeReceiver.transportName] = InfoIframeReceiver;
var parentOrigin;
/* eslint-disable camelcase */
SockJS.bootstrap_iframe = function() {
/* eslint-enable camelcase */
var facade;
iframeUtils.currentWindowId = loc.hash.slice(1);
var onMessage = function(e) {
if (e.source !== parent) {
return;
}
if (typeof parentOrigin === 'undefined') {
parentOrigin = e.origin;
}
if (e.origin !== parentOrigin) {
return;
}
var iframeMessage;
try {
iframeMessage = JSON3.parse(e.data);
} catch (ignored) {
debug('bad json', e.data);
return;
}
if (iframeMessage.windowId !== iframeUtils.currentWindowId) {
return;
}
switch (iframeMessage.type) {
case 's':
var p;
try {
p = JSON3.parse(iframeMessage.data);
} catch (ignored) {
debug('bad json', iframeMessage.data);
break;
}
var version = p[0];
var transport = p[1];
var transUrl = p[2];
var baseUrl = p[3];
debug(version, transport, transUrl, baseUrl);
// change this to semver logic
if (version !== SockJS.version) {
throw new Error('Incompatible SockJS! Main site uses:' +
' "' + version + '", the iframe:' +
' "' + SockJS.version + '".');
}
if (!urlUtils.isOriginEqual(transUrl, loc.href) ||
!urlUtils.isOriginEqual(baseUrl, loc.href)) {
throw new Error('Can\'t connect to different domain from within an ' +
'iframe. (' + loc.href + ', ' + transUrl + ', ' + baseUrl + ')');
}
facade = new FacadeJS(new transportMap[transport](transUrl, baseUrl));
break;
case 'm':
facade._send(iframeMessage.data);
break;
case 'c':
if (facade) {
facade._close();
}
facade = null;
break;
}
};
eventUtils.attachEvent('message', onMessage);
// Start
iframeUtils.postMessage('s');
};
};

View File

@@ -0,0 +1,49 @@
'use strict';
var EventEmitter = require('events').EventEmitter
, inherits = require('inherits')
, JSON3 = require('json3')
, objectUtils = require('./utils/object')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:info-ajax');
}
function InfoAjax(url, AjaxObject) {
EventEmitter.call(this);
var self = this;
var t0 = +new Date();
this.xo = new AjaxObject('GET', url);
this.xo.once('finish', function(status, text) {
var info, rtt;
if (status === 200) {
rtt = (+new Date()) - t0;
if (text) {
try {
info = JSON3.parse(text);
} catch (e) {
debug('bad json', text);
}
}
if (!objectUtils.isObject(info)) {
info = {};
}
}
self.emit('finish', info, rtt);
self.removeAllListeners();
});
}
inherits(InfoAjax, EventEmitter);
InfoAjax.prototype.close = function() {
this.removeAllListeners();
this.xo.close();
};
module.exports = InfoAjax;

View File

@@ -0,0 +1,33 @@
'use strict';
var inherits = require('inherits')
, EventEmitter = require('events').EventEmitter
, JSON3 = require('json3')
, XHRLocalObject = require('./transport/sender/xhr-local')
, InfoAjax = require('./info-ajax')
;
function InfoReceiverIframe(transUrl) {
var self = this;
EventEmitter.call(this);
this.ir = new InfoAjax(transUrl, XHRLocalObject);
this.ir.once('finish', function(info, rtt) {
self.ir = null;
self.emit('message', JSON3.stringify([info, rtt]));
});
}
inherits(InfoReceiverIframe, EventEmitter);
InfoReceiverIframe.transportName = 'iframe-info-receiver';
InfoReceiverIframe.prototype.close = function() {
if (this.ir) {
this.ir.close();
this.ir = null;
}
this.removeAllListeners();
};
module.exports = InfoReceiverIframe;

View File

@@ -0,0 +1,69 @@
'use strict';
var EventEmitter = require('events').EventEmitter
, inherits = require('inherits')
, JSON3 = require('json3')
, utils = require('./utils/event')
, IframeTransport = require('./transport/iframe')
, InfoReceiverIframe = require('./info-iframe-receiver')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:info-iframe');
}
function InfoIframe(baseUrl, url) {
var self = this;
EventEmitter.call(this);
var go = function() {
var ifr = self.ifr = new IframeTransport(InfoReceiverIframe.transportName, url, baseUrl);
ifr.once('message', function(msg) {
if (msg) {
var d;
try {
d = JSON3.parse(msg);
} catch (e) {
debug('bad json', msg);
self.emit('finish');
self.close();
return;
}
var info = d[0], rtt = d[1];
self.emit('finish', info, rtt);
}
self.close();
});
ifr.once('close', function() {
self.emit('finish');
self.close();
});
};
// TODO this seems the same as the 'needBody' from transports
if (!global.document.body) {
utils.attachEvent('load', go);
} else {
go();
}
}
inherits(InfoIframe, EventEmitter);
InfoIframe.enabled = function() {
return IframeTransport.enabled();
};
InfoIframe.prototype.close = function() {
if (this.ifr) {
this.ifr.close();
}
this.removeAllListeners();
this.ifr = null;
};
module.exports = InfoIframe;

View File

@@ -0,0 +1,89 @@
'use strict';
var EventEmitter = require('events').EventEmitter
, inherits = require('inherits')
, urlUtils = require('./utils/url')
, XDR = require('./transport/sender/xdr')
, XHRCors = require('./transport/sender/xhr-cors')
, XHRLocal = require('./transport/sender/xhr-local')
, XHRFake = require('./transport/sender/xhr-fake')
, InfoIframe = require('./info-iframe')
, InfoAjax = require('./info-ajax')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:info-receiver');
}
function InfoReceiver(baseUrl, urlInfo) {
debug(baseUrl);
var self = this;
EventEmitter.call(this);
setTimeout(function() {
self.doXhr(baseUrl, urlInfo);
}, 0);
}
inherits(InfoReceiver, EventEmitter);
// TODO this is currently ignoring the list of available transports and the whitelist
InfoReceiver._getReceiver = function(baseUrl, url, urlInfo) {
// determine method of CORS support (if needed)
if (urlInfo.sameOrigin) {
return new InfoAjax(url, XHRLocal);
}
if (XHRCors.enabled) {
return new InfoAjax(url, XHRCors);
}
if (XDR.enabled && urlInfo.sameScheme) {
return new InfoAjax(url, XDR);
}
if (InfoIframe.enabled()) {
return new InfoIframe(baseUrl, url);
}
return new InfoAjax(url, XHRFake);
};
InfoReceiver.prototype.doXhr = function(baseUrl, urlInfo) {
var self = this
, url = urlUtils.addPath(baseUrl, '/info')
;
debug('doXhr', url);
this.xo = InfoReceiver._getReceiver(baseUrl, url, urlInfo);
this.timeoutRef = setTimeout(function() {
debug('timeout');
self._cleanup(false);
self.emit('finish');
}, InfoReceiver.timeout);
this.xo.once('finish', function(info, rtt) {
debug('finish', info, rtt);
self._cleanup(true);
self.emit('finish', info, rtt);
});
};
InfoReceiver.prototype._cleanup = function(wasClean) {
debug('_cleanup');
clearTimeout(this.timeoutRef);
this.timeoutRef = null;
if (!wasClean && this.xo) {
this.xo.close();
}
this.xo = null;
};
InfoReceiver.prototype.close = function() {
debug('close');
this.removeAllListeners();
this._cleanup(false);
};
InfoReceiver.timeout = 8000;
module.exports = InfoReceiver;

View File

@@ -0,0 +1,10 @@
'use strict';
module.exports = global.location || {
origin: 'http://localhost:80'
, protocol: 'http'
, host: 'localhost'
, port: 80
, href: 'http://localhost/'
, hash: ''
};

View File

@@ -0,0 +1,381 @@
'use strict';
require('./shims');
var URL = require('url-parse')
, inherits = require('inherits')
, JSON3 = require('json3')
, random = require('./utils/random')
, escape = require('./utils/escape')
, urlUtils = require('./utils/url')
, eventUtils = require('./utils/event')
, transport = require('./utils/transport')
, objectUtils = require('./utils/object')
, browser = require('./utils/browser')
, log = require('./utils/log')
, Event = require('./event/event')
, EventTarget = require('./event/eventtarget')
, loc = require('./location')
, CloseEvent = require('./event/close')
, TransportMessageEvent = require('./event/trans-message')
, InfoReceiver = require('./info-receiver')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:main');
}
var transports;
// follow constructor steps defined at http://dev.w3.org/html5/websockets/#the-websocket-interface
function SockJS(url, protocols, options) {
if (!(this instanceof SockJS)) {
return new SockJS(url, protocols, options);
}
if (arguments.length < 1) {
throw new TypeError("Failed to construct 'SockJS: 1 argument required, but only 0 present");
}
EventTarget.call(this);
this.readyState = SockJS.CONNECTING;
this.extensions = '';
this.protocol = '';
// non-standard extension
options = options || {};
if (options.protocols_whitelist) {
log.warn("'protocols_whitelist' is DEPRECATED. Use 'transports' instead.");
}
this._transportsWhitelist = options.transports;
this._transportOptions = options.transportOptions || {};
var sessionId = options.sessionId || 8;
if (typeof sessionId === 'function') {
this._generateSessionId = sessionId;
} else if (typeof sessionId === 'number') {
this._generateSessionId = function() {
return random.string(sessionId);
};
} else {
throw new TypeError('If sessionId is used in the options, it needs to be a number or a function.');
}
this._server = options.server || random.numberString(1000);
// Step 1 of WS spec - parse and validate the url. Issue #8
var parsedUrl = new URL(url);
if (!parsedUrl.host || !parsedUrl.protocol) {
throw new SyntaxError("The URL '" + url + "' is invalid");
} else if (parsedUrl.hash) {
throw new SyntaxError('The URL must not contain a fragment');
} else if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
throw new SyntaxError("The URL's scheme must be either 'http:' or 'https:'. '" + parsedUrl.protocol + "' is not allowed.");
}
var secure = parsedUrl.protocol === 'https:';
// Step 2 - don't allow secure origin with an insecure protocol
if (loc.protocol === 'https' && !secure) {
throw new Error('SecurityError: An insecure SockJS connection may not be initiated from a page loaded over HTTPS');
}
// Step 3 - check port access - no need here
// Step 4 - parse protocols argument
if (!protocols) {
protocols = [];
} else if (!Array.isArray(protocols)) {
protocols = [protocols];
}
// Step 5 - check protocols argument
var sortedProtocols = protocols.sort();
sortedProtocols.forEach(function(proto, i) {
if (!proto) {
throw new SyntaxError("The protocols entry '" + proto + "' is invalid.");
}
if (i < (sortedProtocols.length - 1) && proto === sortedProtocols[i + 1]) {
throw new SyntaxError("The protocols entry '" + proto + "' is duplicated.");
}
});
// Step 6 - convert origin
var o = urlUtils.getOrigin(loc.href);
this._origin = o ? o.toLowerCase() : null;
// remove the trailing slash
parsedUrl.set('pathname', parsedUrl.pathname.replace(/\/+$/, ''));
// store the sanitized url
this.url = parsedUrl.href;
debug('using url', this.url);
// Step 7 - start connection in background
// obtain server info
// http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-26
this._urlInfo = {
nullOrigin: !browser.hasDomain()
, sameOrigin: urlUtils.isOriginEqual(this.url, loc.href)
, sameScheme: urlUtils.isSchemeEqual(this.url, loc.href)
};
this._ir = new InfoReceiver(this.url, this._urlInfo);
this._ir.once('finish', this._receiveInfo.bind(this));
}
inherits(SockJS, EventTarget);
function userSetCode(code) {
return code === 1000 || (code >= 3000 && code <= 4999);
}
SockJS.prototype.close = function(code, reason) {
// Step 1
if (code && !userSetCode(code)) {
throw new Error('InvalidAccessError: Invalid code');
}
// Step 2.4 states the max is 123 bytes, but we are just checking length
if (reason && reason.length > 123) {
throw new SyntaxError('reason argument has an invalid length');
}
// Step 3.1
if (this.readyState === SockJS.CLOSING || this.readyState === SockJS.CLOSED) {
return;
}
// TODO look at docs to determine how to set this
var wasClean = true;
this._close(code || 1000, reason || 'Normal closure', wasClean);
};
SockJS.prototype.send = function(data) {
// #13 - convert anything non-string to string
// TODO this currently turns objects into [object Object]
if (typeof data !== 'string') {
data = '' + data;
}
if (this.readyState === SockJS.CONNECTING) {
throw new Error('InvalidStateError: The connection has not been established yet');
}
if (this.readyState !== SockJS.OPEN) {
return;
}
this._transport.send(escape.quote(data));
};
SockJS.version = require('./version');
SockJS.CONNECTING = 0;
SockJS.OPEN = 1;
SockJS.CLOSING = 2;
SockJS.CLOSED = 3;
SockJS.prototype._receiveInfo = function(info, rtt) {
debug('_receiveInfo', rtt);
this._ir = null;
if (!info) {
this._close(1002, 'Cannot connect to server');
return;
}
// establish a round-trip timeout (RTO) based on the
// round-trip time (RTT)
this._rto = this.countRTO(rtt);
// allow server to override url used for the actual transport
this._transUrl = info.base_url ? info.base_url : this.url;
info = objectUtils.extend(info, this._urlInfo);
debug('info', info);
// determine list of desired and supported transports
var enabledTransports = transports.filterToEnabled(this._transportsWhitelist, info);
this._transports = enabledTransports.main;
debug(this._transports.length + ' enabled transports');
this._connect();
};
SockJS.prototype._connect = function() {
for (var Transport = this._transports.shift(); Transport; Transport = this._transports.shift()) {
debug('attempt', Transport.transportName);
if (Transport.needBody) {
if (!global.document.body ||
(typeof global.document.readyState !== 'undefined' &&
global.document.readyState !== 'complete' &&
global.document.readyState !== 'interactive')) {
debug('waiting for body');
this._transports.unshift(Transport);
eventUtils.attachEvent('load', this._connect.bind(this));
return;
}
}
// calculate timeout based on RTO and round trips. Default to 5s
var timeoutMs = (this._rto * Transport.roundTrips) || 5000;
this._transportTimeoutId = setTimeout(this._transportTimeout.bind(this), timeoutMs);
debug('using timeout', timeoutMs);
var transportUrl = urlUtils.addPath(this._transUrl, '/' + this._server + '/' + this._generateSessionId());
var options = this._transportOptions[Transport.transportName];
debug('transport url', transportUrl);
var transportObj = new Transport(transportUrl, this._transUrl, options);
transportObj.on('message', this._transportMessage.bind(this));
transportObj.once('close', this._transportClose.bind(this));
transportObj.transportName = Transport.transportName;
this._transport = transportObj;
return;
}
this._close(2000, 'All transports failed', false);
};
SockJS.prototype._transportTimeout = function() {
debug('_transportTimeout');
if (this.readyState === SockJS.CONNECTING) {
this._transportClose(2007, 'Transport timed out');
}
};
SockJS.prototype._transportMessage = function(msg) {
debug('_transportMessage', msg);
var self = this
, type = msg.slice(0, 1)
, content = msg.slice(1)
, payload
;
// first check for messages that don't need a payload
switch (type) {
case 'o':
this._open();
return;
case 'h':
this.dispatchEvent(new Event('heartbeat'));
debug('heartbeat', this.transport);
return;
}
if (content) {
try {
payload = JSON3.parse(content);
} catch (e) {
debug('bad json', content);
}
}
if (typeof payload === 'undefined') {
debug('empty payload', content);
return;
}
switch (type) {
case 'a':
if (Array.isArray(payload)) {
payload.forEach(function(p) {
debug('message', self.transport, p);
self.dispatchEvent(new TransportMessageEvent(p));
});
}
break;
case 'm':
debug('message', this.transport, payload);
this.dispatchEvent(new TransportMessageEvent(payload));
break;
case 'c':
if (Array.isArray(payload) && payload.length === 2) {
this._close(payload[0], payload[1], true);
}
break;
}
};
SockJS.prototype._transportClose = function(code, reason) {
debug('_transportClose', this.transport, code, reason);
if (this._transport) {
this._transport.removeAllListeners();
this._transport = null;
this.transport = null;
}
if (!userSetCode(code) && code !== 2000 && this.readyState === SockJS.CONNECTING) {
this._connect();
return;
}
this._close(code, reason);
};
SockJS.prototype._open = function() {
debug('_open', this._transport.transportName, this.readyState);
if (this.readyState === SockJS.CONNECTING) {
if (this._transportTimeoutId) {
clearTimeout(this._transportTimeoutId);
this._transportTimeoutId = null;
}
this.readyState = SockJS.OPEN;
this.transport = this._transport.transportName;
this.dispatchEvent(new Event('open'));
debug('connected', this.transport);
} else {
// The server might have been restarted, and lost track of our
// connection.
this._close(1006, 'Server lost session');
}
};
SockJS.prototype._close = function(code, reason, wasClean) {
debug('_close', this.transport, code, reason, wasClean, this.readyState);
var forceFail = false;
if (this._ir) {
forceFail = true;
this._ir.close();
this._ir = null;
}
if (this._transport) {
this._transport.close();
this._transport = null;
this.transport = null;
}
if (this.readyState === SockJS.CLOSED) {
throw new Error('InvalidStateError: SockJS has already been closed');
}
this.readyState = SockJS.CLOSING;
setTimeout(function() {
this.readyState = SockJS.CLOSED;
if (forceFail) {
this.dispatchEvent(new Event('error'));
}
var e = new CloseEvent('close');
e.wasClean = wasClean || false;
e.code = code || 1000;
e.reason = reason;
this.dispatchEvent(e);
this.onmessage = this.onclose = this.onerror = null;
debug('disconnected');
}.bind(this), 0);
};
// See: http://www.erg.abdn.ac.uk/~gerrit/dccp/notes/ccid2/rto_estimator/
// and RFC 2988.
SockJS.prototype.countRTO = function(rtt) {
// In a local environment, when using IE8/9 and the `jsonp-polling`
// transport the time needed to establish a connection (the time that pass
// from the opening of the transport to the call of `_dispatchOpen`) is
// around 200msec (the lower bound used in the article above) and this
// causes spurious timeouts. For this reason we calculate a value slightly
// larger than that used in the article.
if (rtt > 100) {
return 4 * rtt; // rto > 400msec
}
return 300 + rtt; // 300msec < rto <= 400msec
};
module.exports = function(availableTransports) {
transports = transport(availableTransports);
require('./iframe-bootstrap')(SockJS, availableTransports);
return SockJS;
};

View File

@@ -0,0 +1,452 @@
/* eslint-disable */
/* jscs: disable */
'use strict';
// pulled specific shims from https://github.com/es-shims/es5-shim
var ArrayPrototype = Array.prototype;
var ObjectPrototype = Object.prototype;
var FunctionPrototype = Function.prototype;
var StringPrototype = String.prototype;
var array_slice = ArrayPrototype.slice;
var _toString = ObjectPrototype.toString;
var isFunction = function (val) {
return ObjectPrototype.toString.call(val) === '[object Function]';
};
var isArray = function isArray(obj) {
return _toString.call(obj) === '[object Array]';
};
var isString = function isString(obj) {
return _toString.call(obj) === '[object String]';
};
var supportsDescriptors = Object.defineProperty && (function () {
try {
Object.defineProperty({}, 'x', {});
return true;
} catch (e) { /* this is ES3 */
return false;
}
}());
// Define configurable, writable and non-enumerable props
// if they don't exist.
var defineProperty;
if (supportsDescriptors) {
defineProperty = function (object, name, method, forceAssign) {
if (!forceAssign && (name in object)) { return; }
Object.defineProperty(object, name, {
configurable: true,
enumerable: false,
writable: true,
value: method
});
};
} else {
defineProperty = function (object, name, method, forceAssign) {
if (!forceAssign && (name in object)) { return; }
object[name] = method;
};
}
var defineProperties = function (object, map, forceAssign) {
for (var name in map) {
if (ObjectPrototype.hasOwnProperty.call(map, name)) {
defineProperty(object, name, map[name], forceAssign);
}
}
};
var toObject = function (o) {
if (o == null) { // this matches both null and undefined
throw new TypeError("can't convert " + o + ' to object');
}
return Object(o);
};
//
// Util
// ======
//
// ES5 9.4
// http://es5.github.com/#x9.4
// http://jsperf.com/to-integer
function toInteger(num) {
var n = +num;
if (n !== n) { // isNaN
n = 0;
} else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
return n;
}
function ToUint32(x) {
return x >>> 0;
}
//
// Function
// ========
//
// ES-5 15.3.4.5
// http://es5.github.com/#x15.3.4.5
function Empty() {}
defineProperties(FunctionPrototype, {
bind: function bind(that) { // .length is 1
// 1. Let Target be the this value.
var target = this;
// 2. If IsCallable(Target) is false, throw a TypeError exception.
if (!isFunction(target)) {
throw new TypeError('Function.prototype.bind called on incompatible ' + target);
}
// 3. Let A be a new (possibly empty) internal list of all of the
// argument values provided after thisArg (arg1, arg2 etc), in order.
// XXX slicedArgs will stand in for "A" if used
var args = array_slice.call(arguments, 1); // for normal call
// 4. Let F be a new native ECMAScript object.
// 11. Set the [[Prototype]] internal property of F to the standard
// built-in Function prototype object as specified in 15.3.3.1.
// 12. Set the [[Call]] internal property of F as described in
// 15.3.4.5.1.
// 13. Set the [[Construct]] internal property of F as described in
// 15.3.4.5.2.
// 14. Set the [[HasInstance]] internal property of F as described in
// 15.3.4.5.3.
var binder = function () {
if (this instanceof bound) {
// 15.3.4.5.2 [[Construct]]
// When the [[Construct]] internal method of a function object,
// F that was created using the bind function is called with a
// list of arguments ExtraArgs, the following steps are taken:
// 1. Let target be the value of F's [[TargetFunction]]
// internal property.
// 2. If target has no [[Construct]] internal method, a
// TypeError exception is thrown.
// 3. Let boundArgs be the value of F's [[BoundArgs]] internal
// property.
// 4. Let args be a new list containing the same values as the
// list boundArgs in the same order followed by the same
// values as the list ExtraArgs in the same order.
// 5. Return the result of calling the [[Construct]] internal
// method of target providing args as the arguments.
var result = target.apply(
this,
args.concat(array_slice.call(arguments))
);
if (Object(result) === result) {
return result;
}
return this;
} else {
// 15.3.4.5.1 [[Call]]
// When the [[Call]] internal method of a function object, F,
// which was created using the bind function is called with a
// this value and a list of arguments ExtraArgs, the following
// steps are taken:
// 1. Let boundArgs be the value of F's [[BoundArgs]] internal
// property.
// 2. Let boundThis be the value of F's [[BoundThis]] internal
// property.
// 3. Let target be the value of F's [[TargetFunction]] internal
// property.
// 4. Let args be a new list containing the same values as the
// list boundArgs in the same order followed by the same
// values as the list ExtraArgs in the same order.
// 5. Return the result of calling the [[Call]] internal method
// of target providing boundThis as the this value and
// providing args as the arguments.
// equiv: target.call(this, ...boundArgs, ...args)
return target.apply(
that,
args.concat(array_slice.call(arguments))
);
}
};
// 15. If the [[Class]] internal property of Target is "Function", then
// a. Let L be the length property of Target minus the length of A.
// b. Set the length own property of F to either 0 or L, whichever is
// larger.
// 16. Else set the length own property of F to 0.
var boundLength = Math.max(0, target.length - args.length);
// 17. Set the attributes of the length own property of F to the values
// specified in 15.3.5.1.
var boundArgs = [];
for (var i = 0; i < boundLength; i++) {
boundArgs.push('$' + i);
}
// XXX Build a dynamic function with desired amount of arguments is the only
// way to set the length property of a function.
// In environments where Content Security Policies enabled (Chrome extensions,
// for ex.) all use of eval or Function costructor throws an exception.
// However in all of these environments Function.prototype.bind exists
// and so this code will never be executed.
var bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);
if (target.prototype) {
Empty.prototype = target.prototype;
bound.prototype = new Empty();
// Clean up dangling references.
Empty.prototype = null;
}
// TODO
// 18. Set the [[Extensible]] internal property of F to true.
// TODO
// 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
// 20. Call the [[DefineOwnProperty]] internal method of F with
// arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
// thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
// false.
// 21. Call the [[DefineOwnProperty]] internal method of F with
// arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
// [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
// and false.
// TODO
// NOTE Function objects created using Function.prototype.bind do not
// have a prototype property or the [[Code]], [[FormalParameters]], and
// [[Scope]] internal properties.
// XXX can't delete prototype in pure-js.
// 22. Return F.
return bound;
}
});
//
// Array
// =====
//
// ES5 15.4.3.2
// http://es5.github.com/#x15.4.3.2
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
defineProperties(Array, { isArray: isArray });
var boxedString = Object('a');
var splitString = boxedString[0] !== 'a' || !(0 in boxedString);
var properlyBoxesContext = function properlyBoxed(method) {
// Check node 0.6.21 bug where third parameter is not boxed
var properlyBoxesNonStrict = true;
var properlyBoxesStrict = true;
if (method) {
method.call('foo', function (_, __, context) {
if (typeof context !== 'object') { properlyBoxesNonStrict = false; }
});
method.call([1], function () {
'use strict';
properlyBoxesStrict = typeof this === 'string';
}, 'x');
}
return !!method && properlyBoxesNonStrict && properlyBoxesStrict;
};
defineProperties(ArrayPrototype, {
forEach: function forEach(fun /*, thisp*/) {
var object = toObject(this),
self = splitString && isString(this) ? this.split('') : object,
thisp = arguments[1],
i = -1,
length = self.length >>> 0;
// If no callback function or if callback is not a callable function
if (!isFunction(fun)) {
throw new TypeError(); // TODO message
}
while (++i < length) {
if (i in self) {
// Invoke the callback function with call, passing arguments:
// context, property value, property key, thisArg object
// context
fun.call(thisp, self[i], i, object);
}
}
}
}, !properlyBoxesContext(ArrayPrototype.forEach));
// ES5 15.4.4.14
// http://es5.github.com/#x15.4.4.14
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
var hasFirefox2IndexOfBug = Array.prototype.indexOf && [0, 1].indexOf(1, 2) !== -1;
defineProperties(ArrayPrototype, {
indexOf: function indexOf(sought /*, fromIndex */ ) {
var self = splitString && isString(this) ? this.split('') : toObject(this),
length = self.length >>> 0;
if (!length) {
return -1;
}
var i = 0;
if (arguments.length > 1) {
i = toInteger(arguments[1]);
}
// handle negative indices
i = i >= 0 ? i : Math.max(0, length + i);
for (; i < length; i++) {
if (i in self && self[i] === sought) {
return i;
}
}
return -1;
}
}, hasFirefox2IndexOfBug);
//
// String
// ======
//
// ES5 15.5.4.14
// http://es5.github.com/#x15.5.4.14
// [bugfix, IE lt 9, firefox 4, Konqueror, Opera, obscure browsers]
// Many browsers do not split properly with regular expressions or they
// do not perform the split correctly under obscure conditions.
// See http://blog.stevenlevithan.com/archives/cross-browser-split
// I've tested in many browsers and this seems to cover the deviant ones:
// 'ab'.split(/(?:ab)*/) should be ["", ""], not [""]
// '.'.split(/(.?)(.?)/) should be ["", ".", "", ""], not ["", ""]
// 'tesst'.split(/(s)*/) should be ["t", undefined, "e", "s", "t"], not
// [undefined, "t", undefined, "e", ...]
// ''.split(/.?/) should be [], not [""]
// '.'.split(/()()/) should be ["."], not ["", "", "."]
var string_split = StringPrototype.split;
if (
'ab'.split(/(?:ab)*/).length !== 2 ||
'.'.split(/(.?)(.?)/).length !== 4 ||
'tesst'.split(/(s)*/)[1] === 't' ||
'test'.split(/(?:)/, -1).length !== 4 ||
''.split(/.?/).length ||
'.'.split(/()()/).length > 1
) {
(function () {
var compliantExecNpcg = /()??/.exec('')[1] === void 0; // NPCG: nonparticipating capturing group
StringPrototype.split = function (separator, limit) {
var string = this;
if (separator === void 0 && limit === 0) {
return [];
}
// If `separator` is not a regex, use native split
if (_toString.call(separator) !== '[object RegExp]') {
return string_split.call(this, separator, limit);
}
var output = [],
flags = (separator.ignoreCase ? 'i' : '') +
(separator.multiline ? 'm' : '') +
(separator.extended ? 'x' : '') + // Proposed for ES6
(separator.sticky ? 'y' : ''), // Firefox 3+
lastLastIndex = 0,
// Make `global` and avoid `lastIndex` issues by working with a copy
separator2, match, lastIndex, lastLength;
separator = new RegExp(separator.source, flags + 'g');
string += ''; // Type-convert
if (!compliantExecNpcg) {
// Doesn't need flags gy, but they don't hurt
separator2 = new RegExp('^' + separator.source + '$(?!\\s)', flags);
}
/* Values for `limit`, per the spec:
* If undefined: 4294967295 // Math.pow(2, 32) - 1
* If 0, Infinity, or NaN: 0
* If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
* If negative number: 4294967296 - Math.floor(Math.abs(limit))
* If other: Type-convert, then use the above rules
*/
limit = limit === void 0 ?
-1 >>> 0 : // Math.pow(2, 32) - 1
ToUint32(limit);
while (match = separator.exec(string)) {
// `separator.lastIndex` is not reliable cross-browser
lastIndex = match.index + match[0].length;
if (lastIndex > lastLastIndex) {
output.push(string.slice(lastLastIndex, match.index));
// Fix browsers whose `exec` methods don't consistently return `undefined` for
// nonparticipating capturing groups
if (!compliantExecNpcg && match.length > 1) {
match[0].replace(separator2, function () {
for (var i = 1; i < arguments.length - 2; i++) {
if (arguments[i] === void 0) {
match[i] = void 0;
}
}
});
}
if (match.length > 1 && match.index < string.length) {
ArrayPrototype.push.apply(output, match.slice(1));
}
lastLength = match[0].length;
lastLastIndex = lastIndex;
if (output.length >= limit) {
break;
}
}
if (separator.lastIndex === match.index) {
separator.lastIndex++; // Avoid an infinite loop
}
}
if (lastLastIndex === string.length) {
if (lastLength || !separator.test('')) {
output.push('');
}
} else {
output.push(string.slice(lastLastIndex));
}
return output.length > limit ? output.slice(0, limit) : output;
};
}());
// [bugfix, chrome]
// If separator is undefined, then the result array contains just one String,
// which is the this value (converted to a String). If limit is not undefined,
// then the output array is truncated so that it contains no more than limit
// elements.
// "0".split(undefined, 0) -> []
} else if ('0'.split(void 0, 0).length) {
StringPrototype.split = function split(separator, limit) {
if (separator === void 0 && limit === 0) { return []; }
return string_split.call(this, separator, limit);
};
}
// ECMA-262, 3rd B.2.3
// Not an ECMAScript standard, although ECMAScript 3rd Edition has a
// non-normative section suggesting uniform semantics and it should be
// normalized across all browsers
// [bugfix, IE lt 9] IE < 9 substr() with negative value not working in IE
var string_substr = StringPrototype.substr;
var hasNegativeSubstrBug = ''.substr && '0b'.substr(-1) !== 'b';
defineProperties(StringPrototype, {
substr: function substr(start, length) {
return string_substr.call(
this,
start < 0 ? ((start = this.length + start) < 0 ? 0 : start) : start,
length
);
}
}, hasNegativeSubstrBug);

View File

@@ -0,0 +1,18 @@
'use strict';
module.exports = [
// streaming transports
require('./transport/websocket')
, require('./transport/xhr-streaming')
, require('./transport/xdr-streaming')
, require('./transport/eventsource')
, require('./transport/lib/iframe-wrap')(require('./transport/eventsource'))
// polling transports
, require('./transport/htmlfile')
, require('./transport/lib/iframe-wrap')(require('./transport/htmlfile'))
, require('./transport/xhr-polling')
, require('./transport/xdr-polling')
, require('./transport/lib/iframe-wrap')(require('./transport/xhr-polling'))
, require('./transport/jsonp-polling')
];

View File

@@ -0,0 +1,193 @@
'use strict';
var EventEmitter = require('events').EventEmitter
, inherits = require('inherits')
, utils = require('../../utils/event')
, urlUtils = require('../../utils/url')
, XHR = global.XMLHttpRequest
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:browser:xhr');
}
function AbstractXHRObject(method, url, payload, opts) {
debug(method, url);
var self = this;
EventEmitter.call(this);
setTimeout(function () {
self._start(method, url, payload, opts);
}, 0);
}
inherits(AbstractXHRObject, EventEmitter);
AbstractXHRObject.prototype._start = function(method, url, payload, opts) {
var self = this;
try {
this.xhr = new XHR();
} catch (x) {
// intentionally empty
}
if (!this.xhr) {
debug('no xhr');
this.emit('finish', 0, 'no xhr support');
this._cleanup();
return;
}
// several browsers cache POSTs
url = urlUtils.addQuery(url, 't=' + (+new Date()));
// Explorer tends to keep connection open, even after the
// tab gets closed: http://bugs.jquery.com/ticket/5280
this.unloadRef = utils.unloadAdd(function() {
debug('unload cleanup');
self._cleanup(true);
});
try {
this.xhr.open(method, url, true);
if (this.timeout && 'timeout' in this.xhr) {
this.xhr.timeout = this.timeout;
this.xhr.ontimeout = function() {
debug('xhr timeout');
self.emit('finish', 0, '');
self._cleanup(false);
};
}
} catch (e) {
debug('exception', e);
// IE raises an exception on wrong port.
this.emit('finish', 0, '');
this._cleanup(false);
return;
}
if ((!opts || !opts.noCredentials) && AbstractXHRObject.supportsCORS) {
debug('withCredentials');
// Mozilla docs says https://developer.mozilla.org/en/XMLHttpRequest :
// "This never affects same-site requests."
this.xhr.withCredentials = 'true';
}
if (opts && opts.headers) {
for (var key in opts.headers) {
this.xhr.setRequestHeader(key, opts.headers[key]);
}
}
this.xhr.onreadystatechange = function() {
if (self.xhr) {
var x = self.xhr;
var text, status;
debug('readyState', x.readyState);
switch (x.readyState) {
case 3:
// IE doesn't like peeking into responseText or status
// on Microsoft.XMLHTTP and readystate=3
try {
status = x.status;
text = x.responseText;
} catch (e) {
// intentionally empty
}
debug('status', status);
// IE returns 1223 for 204: http://bugs.jquery.com/ticket/1450
if (status === 1223) {
status = 204;
}
// IE does return readystate == 3 for 404 answers.
if (status === 200 && text && text.length > 0) {
debug('chunk');
self.emit('chunk', status, text);
}
break;
case 4:
status = x.status;
debug('status', status);
// IE returns 1223 for 204: http://bugs.jquery.com/ticket/1450
if (status === 1223) {
status = 204;
}
// IE returns this for a bad port
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa383770(v=vs.85).aspx
if (status === 12005 || status === 12029) {
status = 0;
}
debug('finish', status, x.responseText);
self.emit('finish', status, x.responseText);
self._cleanup(false);
break;
}
}
};
try {
self.xhr.send(payload);
} catch (e) {
self.emit('finish', 0, '');
self._cleanup(false);
}
};
AbstractXHRObject.prototype._cleanup = function(abort) {
debug('cleanup');
if (!this.xhr) {
return;
}
this.removeAllListeners();
utils.unloadDel(this.unloadRef);
// IE needs this field to be a function
this.xhr.onreadystatechange = function() {};
if (this.xhr.ontimeout) {
this.xhr.ontimeout = null;
}
if (abort) {
try {
this.xhr.abort();
} catch (x) {
// intentionally empty
}
}
this.unloadRef = this.xhr = null;
};
AbstractXHRObject.prototype.close = function() {
debug('close');
this._cleanup(true);
};
AbstractXHRObject.enabled = !!XHR;
// override XMLHttpRequest for IE6/7
// obfuscate to avoid firewalls
var axo = ['Active'].concat('Object').join('X');
if (!AbstractXHRObject.enabled && (axo in global)) {
debug('overriding xmlhttprequest');
XHR = function() {
try {
return new global[axo]('Microsoft.XMLHTTP');
} catch (e) {
return null;
}
};
AbstractXHRObject.enabled = !!new XHR();
}
var cors = false;
try {
cors = 'withCredentials' in new XHR();
} catch (ignored) {
// intentionally empty
}
AbstractXHRObject.supportsCORS = cors;
module.exports = AbstractXHRObject;

View File

@@ -0,0 +1 @@
module.exports = global.EventSource;

View File

@@ -0,0 +1,10 @@
'use strict';
var Driver = global.WebSocket || global.MozWebSocket;
if (Driver) {
module.exports = function WebSocketBrowserDriver(url) {
return new Driver(url);
};
} else {
module.exports = undefined;
}

View File

@@ -0,0 +1 @@
module.exports = require('faye-websocket').Client;

View File

@@ -0,0 +1,72 @@
'use strict';
var EventEmitter = require('events').EventEmitter
, inherits = require('inherits')
, http = require('http')
, https = require('https')
, URL = require('url-parse')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:driver:xhr');
}
function XhrDriver(method, url, payload, opts) {
debug(method, url, payload);
var self = this;
EventEmitter.call(this);
var parsedUrl = new URL(url);
var options = {
method: method
, hostname: parsedUrl.hostname.replace(/\[|\]/g, '')
, port: parsedUrl.port
, path: parsedUrl.pathname + (parsedUrl.query || '')
, headers: opts && opts.headers
, agent: false
};
var protocol = parsedUrl.protocol === 'https:' ? https : http;
this.req = protocol.request(options, function(res) {
res.setEncoding('utf8');
var responseText = '';
res.on('data', function(chunk) {
debug('data', chunk);
responseText += chunk;
self.emit('chunk', 200, responseText);
});
res.once('end', function() {
debug('end');
self.emit('finish', res.statusCode, responseText);
self.req = null;
});
});
this.req.on('error', function(e) {
debug('error', e);
self.emit('finish', 0, e.message);
});
if (payload) {
this.req.write(payload);
}
this.req.end();
}
inherits(XhrDriver, EventEmitter);
XhrDriver.prototype.close = function() {
debug('close');
this.removeAllListeners();
if (this.req) {
this.req.abort();
this.req = null;
}
};
XhrDriver.enabled = true;
XhrDriver.supportsCORS = true;
module.exports = XhrDriver;

View File

@@ -0,0 +1,27 @@
'use strict';
var inherits = require('inherits')
, AjaxBasedTransport = require('./lib/ajax-based')
, EventSourceReceiver = require('./receiver/eventsource')
, XHRCorsObject = require('./sender/xhr-cors')
, EventSourceDriver = require('eventsource')
;
function EventSourceTransport(transUrl) {
if (!EventSourceTransport.enabled()) {
throw new Error('Transport created when disabled');
}
AjaxBasedTransport.call(this, transUrl, '/eventsource', EventSourceReceiver, XHRCorsObject);
}
inherits(EventSourceTransport, AjaxBasedTransport);
EventSourceTransport.enabled = function() {
return !!EventSourceDriver;
};
EventSourceTransport.transportName = 'eventsource';
EventSourceTransport.roundTrips = 2;
module.exports = EventSourceTransport;

View File

@@ -0,0 +1,25 @@
'use strict';
var inherits = require('inherits')
, HtmlfileReceiver = require('./receiver/htmlfile')
, XHRLocalObject = require('./sender/xhr-local')
, AjaxBasedTransport = require('./lib/ajax-based')
;
function HtmlFileTransport(transUrl) {
if (!HtmlfileReceiver.enabled) {
throw new Error('Transport created when disabled');
}
AjaxBasedTransport.call(this, transUrl, '/htmlfile', HtmlfileReceiver, XHRLocalObject);
}
inherits(HtmlFileTransport, AjaxBasedTransport);
HtmlFileTransport.enabled = function(info) {
return HtmlfileReceiver.enabled && info.sameOrigin;
};
HtmlFileTransport.transportName = 'htmlfile';
HtmlFileTransport.roundTrips = 2;
module.exports = HtmlFileTransport;

View File

@@ -0,0 +1,141 @@
'use strict';
// Few cool transports do work only for same-origin. In order to make
// them work cross-domain we shall use iframe, served from the
// remote domain. New browsers have capabilities to communicate with
// cross domain iframe using postMessage(). In IE it was implemented
// from IE 8+, but of course, IE got some details wrong:
// http://msdn.microsoft.com/en-us/library/cc197015(v=VS.85).aspx
// http://stevesouders.com/misc/test-postmessage.php
var inherits = require('inherits')
, JSON3 = require('json3')
, EventEmitter = require('events').EventEmitter
, version = require('../version')
, urlUtils = require('../utils/url')
, iframeUtils = require('../utils/iframe')
, eventUtils = require('../utils/event')
, random = require('../utils/random')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:transport:iframe');
}
function IframeTransport(transport, transUrl, baseUrl) {
if (!IframeTransport.enabled()) {
throw new Error('Transport created when disabled');
}
EventEmitter.call(this);
var self = this;
this.origin = urlUtils.getOrigin(baseUrl);
this.baseUrl = baseUrl;
this.transUrl = transUrl;
this.transport = transport;
this.windowId = random.string(8);
var iframeUrl = urlUtils.addPath(baseUrl, '/iframe.html') + '#' + this.windowId;
debug(transport, transUrl, iframeUrl);
this.iframeObj = iframeUtils.createIframe(iframeUrl, function(r) {
debug('err callback');
self.emit('close', 1006, 'Unable to load an iframe (' + r + ')');
self.close();
});
this.onmessageCallback = this._message.bind(this);
eventUtils.attachEvent('message', this.onmessageCallback);
}
inherits(IframeTransport, EventEmitter);
IframeTransport.prototype.close = function() {
debug('close');
this.removeAllListeners();
if (this.iframeObj) {
eventUtils.detachEvent('message', this.onmessageCallback);
try {
// When the iframe is not loaded, IE raises an exception
// on 'contentWindow'.
this.postMessage('c');
} catch (x) {
// intentionally empty
}
this.iframeObj.cleanup();
this.iframeObj = null;
this.onmessageCallback = this.iframeObj = null;
}
};
IframeTransport.prototype._message = function(e) {
debug('message', e.data);
if (!urlUtils.isOriginEqual(e.origin, this.origin)) {
debug('not same origin', e.origin, this.origin);
return;
}
var iframeMessage;
try {
iframeMessage = JSON3.parse(e.data);
} catch (ignored) {
debug('bad json', e.data);
return;
}
if (iframeMessage.windowId !== this.windowId) {
debug('mismatched window id', iframeMessage.windowId, this.windowId);
return;
}
switch (iframeMessage.type) {
case 's':
this.iframeObj.loaded();
// window global dependency
this.postMessage('s', JSON3.stringify([
version
, this.transport
, this.transUrl
, this.baseUrl
]));
break;
case 't':
this.emit('message', iframeMessage.data);
break;
case 'c':
var cdata;
try {
cdata = JSON3.parse(iframeMessage.data);
} catch (ignored) {
debug('bad json', iframeMessage.data);
return;
}
this.emit('close', cdata[0], cdata[1]);
this.close();
break;
}
};
IframeTransport.prototype.postMessage = function(type, data) {
debug('postMessage', type, data);
this.iframeObj.post(JSON3.stringify({
windowId: this.windowId
, type: type
, data: data || ''
}), this.origin);
};
IframeTransport.prototype.send = function(message) {
debug('send', message);
this.postMessage('m', message);
};
IframeTransport.enabled = function() {
return iframeUtils.iframeEnabled;
};
IframeTransport.transportName = 'iframe';
IframeTransport.roundTrips = 2;
module.exports = IframeTransport;

View File

@@ -0,0 +1,34 @@
'use strict';
// The simplest and most robust transport, using the well-know cross
// domain hack - JSONP. This transport is quite inefficient - one
// message could use up to one http request. But at least it works almost
// everywhere.
// Known limitations:
// o you will get a spinning cursor
// o for Konqueror a dumb timer is needed to detect errors
var inherits = require('inherits')
, SenderReceiver = require('./lib/sender-receiver')
, JsonpReceiver = require('./receiver/jsonp')
, jsonpSender = require('./sender/jsonp')
;
function JsonPTransport(transUrl) {
if (!JsonPTransport.enabled()) {
throw new Error('Transport created when disabled');
}
SenderReceiver.call(this, transUrl, '/jsonp', jsonpSender, JsonpReceiver);
}
inherits(JsonPTransport, SenderReceiver);
JsonPTransport.enabled = function() {
return !!global.document;
};
JsonPTransport.transportName = 'jsonp-polling';
JsonPTransport.roundTrips = 1;
JsonPTransport.needBody = true;
module.exports = JsonPTransport;

View File

@@ -0,0 +1,49 @@
'use strict';
var inherits = require('inherits')
, urlUtils = require('../../utils/url')
, SenderReceiver = require('./sender-receiver')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:ajax-based');
}
function createAjaxSender(AjaxObject) {
return function(url, payload, callback) {
debug('create ajax sender', url, payload);
var opt = {};
if (typeof payload === 'string') {
opt.headers = {'Content-type': 'text/plain'};
}
var ajaxUrl = urlUtils.addPath(url, '/xhr_send');
var xo = new AjaxObject('POST', ajaxUrl, payload, opt);
xo.once('finish', function(status) {
debug('finish', status);
xo = null;
if (status !== 200 && status !== 204) {
return callback(new Error('http status ' + status));
}
callback();
});
return function() {
debug('abort');
xo.close();
xo = null;
var err = new Error('Aborted');
err.code = 1000;
callback(err);
};
};
}
function AjaxBasedTransport(transUrl, urlSuffix, Receiver, AjaxObject) {
SenderReceiver.call(this, transUrl, urlSuffix, createAjaxSender(AjaxObject), Receiver, AjaxObject);
}
inherits(AjaxBasedTransport, SenderReceiver);
module.exports = AjaxBasedTransport;

View File

@@ -0,0 +1,87 @@
'use strict';
var inherits = require('inherits')
, EventEmitter = require('events').EventEmitter
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:buffered-sender');
}
function BufferedSender(url, sender) {
debug(url);
EventEmitter.call(this);
this.sendBuffer = [];
this.sender = sender;
this.url = url;
}
inherits(BufferedSender, EventEmitter);
BufferedSender.prototype.send = function(message) {
debug('send', message);
this.sendBuffer.push(message);
if (!this.sendStop) {
this.sendSchedule();
}
};
// For polling transports in a situation when in the message callback,
// new message is being send. If the sending connection was started
// before receiving one, it is possible to saturate the network and
// timeout due to the lack of receiving socket. To avoid that we delay
// sending messages by some small time, in order to let receiving
// connection be started beforehand. This is only a halfmeasure and
// does not fix the big problem, but it does make the tests go more
// stable on slow networks.
BufferedSender.prototype.sendScheduleWait = function() {
debug('sendScheduleWait');
var self = this;
var tref;
this.sendStop = function() {
debug('sendStop');
self.sendStop = null;
clearTimeout(tref);
};
tref = setTimeout(function() {
debug('timeout');
self.sendStop = null;
self.sendSchedule();
}, 25);
};
BufferedSender.prototype.sendSchedule = function() {
debug('sendSchedule', this.sendBuffer.length);
var self = this;
if (this.sendBuffer.length > 0) {
var payload = '[' + this.sendBuffer.join(',') + ']';
this.sendStop = this.sender(this.url, payload, function(err) {
self.sendStop = null;
if (err) {
debug('error', err);
self.emit('close', err.code || 1006, 'Sending error: ' + err);
self.close();
} else {
self.sendScheduleWait();
}
});
this.sendBuffer = [];
}
};
BufferedSender.prototype._cleanup = function() {
debug('_cleanup');
this.removeAllListeners();
};
BufferedSender.prototype.close = function() {
debug('close');
this._cleanup();
if (this.sendStop) {
this.sendStop();
this.sendStop = null;
}
};
module.exports = BufferedSender;

View File

@@ -0,0 +1,33 @@
'use strict';
var inherits = require('inherits')
, IframeTransport = require('../iframe')
, objectUtils = require('../../utils/object')
;
module.exports = function(transport) {
function IframeWrapTransport(transUrl, baseUrl) {
IframeTransport.call(this, transport.transportName, transUrl, baseUrl);
}
inherits(IframeWrapTransport, IframeTransport);
IframeWrapTransport.enabled = function(url, info) {
if (!global.document) {
return false;
}
var iframeInfo = objectUtils.extend({}, info);
iframeInfo.sameOrigin = true;
return transport.enabled(iframeInfo) && IframeTransport.enabled();
};
IframeWrapTransport.transportName = 'iframe-' + transport.transportName;
IframeWrapTransport.needBody = true;
IframeWrapTransport.roundTrips = IframeTransport.roundTrips + transport.roundTrips - 1; // html, javascript (2) + transport - no CORS (1)
IframeWrapTransport.facadeTransport = transport;
return IframeWrapTransport;
};

View File

@@ -0,0 +1,57 @@
'use strict';
var inherits = require('inherits')
, EventEmitter = require('events').EventEmitter
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:polling');
}
function Polling(Receiver, receiveUrl, AjaxObject) {
debug(receiveUrl);
EventEmitter.call(this);
this.Receiver = Receiver;
this.receiveUrl = receiveUrl;
this.AjaxObject = AjaxObject;
this._scheduleReceiver();
}
inherits(Polling, EventEmitter);
Polling.prototype._scheduleReceiver = function() {
debug('_scheduleReceiver');
var self = this;
var poll = this.poll = new this.Receiver(this.receiveUrl, this.AjaxObject);
poll.on('message', function(msg) {
debug('message', msg);
self.emit('message', msg);
});
poll.once('close', function(code, reason) {
debug('close', code, reason, self.pollIsClosing);
self.poll = poll = null;
if (!self.pollIsClosing) {
if (reason === 'network') {
self._scheduleReceiver();
} else {
self.emit('close', code || 1006, reason);
self.removeAllListeners();
}
}
});
};
Polling.prototype.abort = function() {
debug('abort');
this.removeAllListeners();
this.pollIsClosing = true;
if (this.poll) {
this.poll.abort();
}
};
module.exports = Polling;

View File

@@ -0,0 +1,45 @@
'use strict';
var inherits = require('inherits')
, urlUtils = require('../../utils/url')
, BufferedSender = require('./buffered-sender')
, Polling = require('./polling')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:sender-receiver');
}
function SenderReceiver(transUrl, urlSuffix, senderFunc, Receiver, AjaxObject) {
var pollUrl = urlUtils.addPath(transUrl, urlSuffix);
debug(pollUrl);
var self = this;
BufferedSender.call(this, transUrl, senderFunc);
this.poll = new Polling(Receiver, pollUrl, AjaxObject);
this.poll.on('message', function(msg) {
debug('poll message', msg);
self.emit('message', msg);
});
this.poll.once('close', function(code, reason) {
debug('poll close', code, reason);
self.poll = null;
self.emit('close', code, reason);
self.close();
});
}
inherits(SenderReceiver, BufferedSender);
SenderReceiver.prototype.close = function() {
BufferedSender.prototype.close.call(this);
debug('close');
this.removeAllListeners();
if (this.poll) {
this.poll.abort();
this.poll = null;
}
};
module.exports = SenderReceiver;

View File

@@ -0,0 +1,63 @@
'use strict';
var inherits = require('inherits')
, EventEmitter = require('events').EventEmitter
, EventSourceDriver = require('eventsource')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:receiver:eventsource');
}
function EventSourceReceiver(url) {
debug(url);
EventEmitter.call(this);
var self = this;
var es = this.es = new EventSourceDriver(url);
es.onmessage = function(e) {
debug('message', e.data);
self.emit('message', decodeURI(e.data));
};
es.onerror = function(e) {
debug('error', es.readyState, e);
// ES on reconnection has readyState = 0 or 1.
// on network error it's CLOSED = 2
var reason = (es.readyState !== 2 ? 'network' : 'permanent');
self._cleanup();
self._close(reason);
};
}
inherits(EventSourceReceiver, EventEmitter);
EventSourceReceiver.prototype.abort = function() {
debug('abort');
this._cleanup();
this._close('user');
};
EventSourceReceiver.prototype._cleanup = function() {
debug('cleanup');
var es = this.es;
if (es) {
es.onmessage = es.onerror = null;
es.close();
this.es = null;
}
};
EventSourceReceiver.prototype._close = function(reason) {
debug('close', reason);
var self = this;
// Safari and chrome < 15 crash if we close window before
// waiting for ES cleanup. See:
// https://code.google.com/p/chromium/issues/detail?id=89155
setTimeout(function() {
self.emit('close', null, reason);
self.removeAllListeners();
}, 200);
};
module.exports = EventSourceReceiver;

View File

@@ -0,0 +1,87 @@
'use strict';
var inherits = require('inherits')
, iframeUtils = require('../../utils/iframe')
, urlUtils = require('../../utils/url')
, EventEmitter = require('events').EventEmitter
, random = require('../../utils/random')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:receiver:htmlfile');
}
function HtmlfileReceiver(url) {
debug(url);
EventEmitter.call(this);
var self = this;
iframeUtils.polluteGlobalNamespace();
this.id = 'a' + random.string(6);
url = urlUtils.addQuery(url, 'c=' + decodeURIComponent(iframeUtils.WPrefix + '.' + this.id));
debug('using htmlfile', HtmlfileReceiver.htmlfileEnabled);
var constructFunc = HtmlfileReceiver.htmlfileEnabled ?
iframeUtils.createHtmlfile : iframeUtils.createIframe;
global[iframeUtils.WPrefix][this.id] = {
start: function() {
debug('start');
self.iframeObj.loaded();
}
, message: function(data) {
debug('message', data);
self.emit('message', data);
}
, stop: function() {
debug('stop');
self._cleanup();
self._close('network');
}
};
this.iframeObj = constructFunc(url, function() {
debug('callback');
self._cleanup();
self._close('permanent');
});
}
inherits(HtmlfileReceiver, EventEmitter);
HtmlfileReceiver.prototype.abort = function() {
debug('abort');
this._cleanup();
this._close('user');
};
HtmlfileReceiver.prototype._cleanup = function() {
debug('_cleanup');
if (this.iframeObj) {
this.iframeObj.cleanup();
this.iframeObj = null;
}
delete global[iframeUtils.WPrefix][this.id];
};
HtmlfileReceiver.prototype._close = function(reason) {
debug('_close', reason);
this.emit('close', null, reason);
this.removeAllListeners();
};
HtmlfileReceiver.htmlfileEnabled = false;
// obfuscate to avoid firewalls
var axo = ['Active'].concat('Object').join('X');
if (axo in global) {
try {
HtmlfileReceiver.htmlfileEnabled = !!new global[axo]('htmlfile');
} catch (x) {
// intentionally empty
}
}
HtmlfileReceiver.enabled = HtmlfileReceiver.htmlfileEnabled || iframeUtils.iframeEnabled;
module.exports = HtmlfileReceiver;

View File

@@ -0,0 +1,183 @@
'use strict';
var utils = require('../../utils/iframe')
, random = require('../../utils/random')
, browser = require('../../utils/browser')
, urlUtils = require('../../utils/url')
, inherits = require('inherits')
, EventEmitter = require('events').EventEmitter
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:receiver:jsonp');
}
function JsonpReceiver(url) {
debug(url);
var self = this;
EventEmitter.call(this);
utils.polluteGlobalNamespace();
this.id = 'a' + random.string(6);
var urlWithId = urlUtils.addQuery(url, 'c=' + encodeURIComponent(utils.WPrefix + '.' + this.id));
global[utils.WPrefix][this.id] = this._callback.bind(this);
this._createScript(urlWithId);
// Fallback mostly for Konqueror - stupid timer, 35 seconds shall be plenty.
this.timeoutId = setTimeout(function() {
debug('timeout');
self._abort(new Error('JSONP script loaded abnormally (timeout)'));
}, JsonpReceiver.timeout);
}
inherits(JsonpReceiver, EventEmitter);
JsonpReceiver.prototype.abort = function() {
debug('abort');
if (global[utils.WPrefix][this.id]) {
var err = new Error('JSONP user aborted read');
err.code = 1000;
this._abort(err);
}
};
JsonpReceiver.timeout = 35000;
JsonpReceiver.scriptErrorTimeout = 1000;
JsonpReceiver.prototype._callback = function(data) {
debug('_callback', data);
this._cleanup();
if (this.aborting) {
return;
}
if (data) {
debug('message', data);
this.emit('message', data);
}
this.emit('close', null, 'network');
this.removeAllListeners();
};
JsonpReceiver.prototype._abort = function(err) {
debug('_abort', err);
this._cleanup();
this.aborting = true;
this.emit('close', err.code, err.message);
this.removeAllListeners();
};
JsonpReceiver.prototype._cleanup = function() {
debug('_cleanup');
clearTimeout(this.timeoutId);
if (this.script2) {
this.script2.parentNode.removeChild(this.script2);
this.script2 = null;
}
if (this.script) {
var script = this.script;
// Unfortunately, you can't really abort script loading of
// the script.
script.parentNode.removeChild(script);
script.onreadystatechange = script.onerror =
script.onload = script.onclick = null;
this.script = null;
}
delete global[utils.WPrefix][this.id];
};
JsonpReceiver.prototype._scriptError = function() {
debug('_scriptError');
var self = this;
if (this.errorTimer) {
return;
}
this.errorTimer = setTimeout(function() {
if (!self.loadedOkay) {
self._abort(new Error('JSONP script loaded abnormally (onerror)'));
}
}, JsonpReceiver.scriptErrorTimeout);
};
JsonpReceiver.prototype._createScript = function(url) {
debug('_createScript', url);
var self = this;
var script = this.script = global.document.createElement('script');
var script2; // Opera synchronous load trick.
script.id = 'a' + random.string(8);
script.src = url;
script.type = 'text/javascript';
script.charset = 'UTF-8';
script.onerror = this._scriptError.bind(this);
script.onload = function() {
debug('onload');
self._abort(new Error('JSONP script loaded abnormally (onload)'));
};
// IE9 fires 'error' event after onreadystatechange or before, in random order.
// Use loadedOkay to determine if actually errored
script.onreadystatechange = function() {
debug('onreadystatechange', script.readyState);
if (/loaded|closed/.test(script.readyState)) {
if (script && script.htmlFor && script.onclick) {
self.loadedOkay = true;
try {
// In IE, actually execute the script.
script.onclick();
} catch (x) {
// intentionally empty
}
}
if (script) {
self._abort(new Error('JSONP script loaded abnormally (onreadystatechange)'));
}
}
};
// IE: event/htmlFor/onclick trick.
// One can't rely on proper order for onreadystatechange. In order to
// make sure, set a 'htmlFor' and 'event' properties, so that
// script code will be installed as 'onclick' handler for the
// script object. Later, onreadystatechange, manually execute this
// code. FF and Chrome doesn't work with 'event' and 'htmlFor'
// set. For reference see:
// http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html
// Also, read on that about script ordering:
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
if (typeof script.async === 'undefined' && global.document.attachEvent) {
// According to mozilla docs, in recent browsers script.async defaults
// to 'true', so we may use it to detect a good browser:
// https://developer.mozilla.org/en/HTML/Element/script
if (!browser.isOpera()) {
// Naively assume we're in IE
try {
script.htmlFor = script.id;
script.event = 'onclick';
} catch (x) {
// intentionally empty
}
script.async = true;
} else {
// Opera, second sync script hack
script2 = this.script2 = global.document.createElement('script');
script2.text = "try{var a = document.getElementById('" + script.id + "'); if(a)a.onerror();}catch(x){};";
script.async = script2.async = false;
}
}
if (typeof script.async !== 'undefined') {
script.async = true;
}
var head = global.document.getElementsByTagName('head')[0];
head.insertBefore(script, head.firstChild);
if (script2) {
head.insertBefore(script2, head.firstChild);
}
};
module.exports = JsonpReceiver;

View File

@@ -0,0 +1,70 @@
'use strict';
var inherits = require('inherits')
, EventEmitter = require('events').EventEmitter
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:receiver:xhr');
}
function XhrReceiver(url, AjaxObject) {
debug(url);
EventEmitter.call(this);
var self = this;
this.bufferPosition = 0;
this.xo = new AjaxObject('POST', url, null);
this.xo.on('chunk', this._chunkHandler.bind(this));
this.xo.once('finish', function(status, text) {
debug('finish', status, text);
self._chunkHandler(status, text);
self.xo = null;
var reason = status === 200 ? 'network' : 'permanent';
debug('close', reason);
self.emit('close', null, reason);
self._cleanup();
});
}
inherits(XhrReceiver, EventEmitter);
XhrReceiver.prototype._chunkHandler = function(status, text) {
debug('_chunkHandler', status);
if (status !== 200 || !text) {
return;
}
for (var idx = -1; ; this.bufferPosition += idx + 1) {
var buf = text.slice(this.bufferPosition);
idx = buf.indexOf('\n');
if (idx === -1) {
break;
}
var msg = buf.slice(0, idx);
if (msg) {
debug('message', msg);
this.emit('message', msg);
}
}
};
XhrReceiver.prototype._cleanup = function() {
debug('_cleanup');
this.removeAllListeners();
};
XhrReceiver.prototype.abort = function() {
debug('abort');
if (this.xo) {
this.xo.close();
debug('close');
this.emit('close', null, 'user');
this.xo = null;
}
this._cleanup();
};
module.exports = XhrReceiver;

View File

@@ -0,0 +1,99 @@
'use strict';
var random = require('../../utils/random')
, urlUtils = require('../../utils/url')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:sender:jsonp');
}
var form, area;
function createIframe(id) {
debug('createIframe', id);
try {
// ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
return global.document.createElement('<iframe name="' + id + '">');
} catch (x) {
var iframe = global.document.createElement('iframe');
iframe.name = id;
return iframe;
}
}
function createForm() {
debug('createForm');
form = global.document.createElement('form');
form.style.display = 'none';
form.style.position = 'absolute';
form.method = 'POST';
form.enctype = 'application/x-www-form-urlencoded';
form.acceptCharset = 'UTF-8';
area = global.document.createElement('textarea');
area.name = 'd';
form.appendChild(area);
global.document.body.appendChild(form);
}
module.exports = function(url, payload, callback) {
debug(url, payload);
if (!form) {
createForm();
}
var id = 'a' + random.string(8);
form.target = id;
form.action = urlUtils.addQuery(urlUtils.addPath(url, '/jsonp_send'), 'i=' + id);
var iframe = createIframe(id);
iframe.id = id;
iframe.style.display = 'none';
form.appendChild(iframe);
try {
area.value = payload;
} catch (e) {
// seriously broken browsers get here
}
form.submit();
var completed = function(err) {
debug('completed', id, err);
if (!iframe.onerror) {
return;
}
iframe.onreadystatechange = iframe.onerror = iframe.onload = null;
// Opera mini doesn't like if we GC iframe
// immediately, thus this timeout.
setTimeout(function() {
debug('cleaning up', id);
iframe.parentNode.removeChild(iframe);
iframe = null;
}, 500);
area.value = '';
// It is not possible to detect if the iframe succeeded or
// failed to submit our form.
callback(err);
};
iframe.onerror = function() {
debug('onerror', id);
completed();
};
iframe.onload = function() {
debug('onload', id);
completed();
};
iframe.onreadystatechange = function(e) {
debug('onreadystatechange', id, iframe.readyState, e);
if (iframe.readyState === 'complete') {
completed();
}
};
return function() {
debug('aborted', id);
completed(new Error('Aborted'));
};
};

View File

@@ -0,0 +1,103 @@
'use strict';
var EventEmitter = require('events').EventEmitter
, inherits = require('inherits')
, eventUtils = require('../../utils/event')
, browser = require('../../utils/browser')
, urlUtils = require('../../utils/url')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:sender:xdr');
}
// References:
// http://ajaxian.com/archives/100-line-ajax-wrapper
// http://msdn.microsoft.com/en-us/library/cc288060(v=VS.85).aspx
function XDRObject(method, url, payload) {
debug(method, url);
var self = this;
EventEmitter.call(this);
setTimeout(function() {
self._start(method, url, payload);
}, 0);
}
inherits(XDRObject, EventEmitter);
XDRObject.prototype._start = function(method, url, payload) {
debug('_start');
var self = this;
var xdr = new global.XDomainRequest();
// IE caches even POSTs
url = urlUtils.addQuery(url, 't=' + (+new Date()));
xdr.onerror = function() {
debug('onerror');
self._error();
};
xdr.ontimeout = function() {
debug('ontimeout');
self._error();
};
xdr.onprogress = function() {
debug('progress', xdr.responseText);
self.emit('chunk', 200, xdr.responseText);
};
xdr.onload = function() {
debug('load');
self.emit('finish', 200, xdr.responseText);
self._cleanup(false);
};
this.xdr = xdr;
this.unloadRef = eventUtils.unloadAdd(function() {
self._cleanup(true);
});
try {
// Fails with AccessDenied if port number is bogus
this.xdr.open(method, url);
if (this.timeout) {
this.xdr.timeout = this.timeout;
}
this.xdr.send(payload);
} catch (x) {
this._error();
}
};
XDRObject.prototype._error = function() {
this.emit('finish', 0, '');
this._cleanup(false);
};
XDRObject.prototype._cleanup = function(abort) {
debug('cleanup', abort);
if (!this.xdr) {
return;
}
this.removeAllListeners();
eventUtils.unloadDel(this.unloadRef);
this.xdr.ontimeout = this.xdr.onerror = this.xdr.onprogress = this.xdr.onload = null;
if (abort) {
try {
this.xdr.abort();
} catch (x) {
// intentionally empty
}
}
this.unloadRef = this.xdr = null;
};
XDRObject.prototype.close = function() {
debug('close');
this._cleanup(true);
};
// IE 8/9 if the request target uses the same scheme - #79
XDRObject.enabled = !!(global.XDomainRequest && browser.hasDomain());
module.exports = XDRObject;

View File

@@ -0,0 +1,15 @@
'use strict';
var inherits = require('inherits')
, XhrDriver = require('../driver/xhr')
;
function XHRCorsObject(method, url, payload, opts) {
XhrDriver.call(this, method, url, payload, opts);
}
inherits(XHRCorsObject, XhrDriver);
XHRCorsObject.enabled = XhrDriver.enabled && XhrDriver.supportsCORS;
module.exports = XHRCorsObject;

View File

@@ -0,0 +1,24 @@
'use strict';
var EventEmitter = require('events').EventEmitter
, inherits = require('inherits')
;
function XHRFake(/* method, url, payload, opts */) {
var self = this;
EventEmitter.call(this);
this.to = setTimeout(function() {
self.emit('finish', 200, '{}');
}, XHRFake.timeout);
}
inherits(XHRFake, EventEmitter);
XHRFake.prototype.close = function() {
clearTimeout(this.to);
};
XHRFake.timeout = 2000;
module.exports = XHRFake;

View File

@@ -0,0 +1,17 @@
'use strict';
var inherits = require('inherits')
, XhrDriver = require('../driver/xhr')
;
function XHRLocalObject(method, url, payload /*, opts */) {
XhrDriver.call(this, method, url, payload, {
noCredentials: true
});
}
inherits(XHRLocalObject, XhrDriver);
XHRLocalObject.enabled = XhrDriver.enabled;
module.exports = XHRLocalObject;

View File

@@ -0,0 +1,99 @@
'use strict';
var utils = require('../utils/event')
, urlUtils = require('../utils/url')
, inherits = require('inherits')
, EventEmitter = require('events').EventEmitter
, WebsocketDriver = require('./driver/websocket')
;
var debug = function() {};
if (process.env.NODE_ENV !== 'production') {
debug = require('debug')('sockjs-client:websocket');
}
function WebSocketTransport(transUrl, ignore, options) {
if (!WebSocketTransport.enabled()) {
throw new Error('Transport created when disabled');
}
EventEmitter.call(this);
debug('constructor', transUrl);
var self = this;
var url = urlUtils.addPath(transUrl, '/websocket');
if (url.slice(0, 5) === 'https') {
url = 'wss' + url.slice(5);
} else {
url = 'ws' + url.slice(4);
}
this.url = url;
this.ws = new WebsocketDriver(this.url, [], options);
this.ws.onmessage = function(e) {
debug('message event', e.data);
self.emit('message', e.data);
};
// Firefox has an interesting bug. If a websocket connection is
// created after onunload, it stays alive even when user
// navigates away from the page. In such situation let's lie -
// let's not open the ws connection at all. See:
// https://github.com/sockjs/sockjs-client/issues/28
// https://bugzilla.mozilla.org/show_bug.cgi?id=696085
this.unloadRef = utils.unloadAdd(function() {
debug('unload');
self.ws.close();
});
this.ws.onclose = function(e) {
debug('close event', e.code, e.reason);
self.emit('close', e.code, e.reason);
self._cleanup();
};
this.ws.onerror = function(e) {
debug('error event', e);
self.emit('close', 1006, 'WebSocket connection broken');
self._cleanup();
};
}
inherits(WebSocketTransport, EventEmitter);
WebSocketTransport.prototype.send = function(data) {
var msg = '[' + data + ']';
debug('send', msg);
this.ws.send(msg);
};
WebSocketTransport.prototype.close = function() {
debug('close');
var ws = this.ws;
this._cleanup();
if (ws) {
ws.close();
}
};
WebSocketTransport.prototype._cleanup = function() {
debug('_cleanup');
var ws = this.ws;
if (ws) {
ws.onmessage = ws.onclose = ws.onerror = null;
}
utils.unloadDel(this.unloadRef);
this.unloadRef = this.ws = null;
this.removeAllListeners();
};
WebSocketTransport.enabled = function() {
debug('enabled');
return !!WebsocketDriver;
};
WebSocketTransport.transportName = 'websocket';
// In theory, ws should require 1 round trip. But in chrome, this is
// not very stable over SSL. Most likely a ws connection requires a
// separate SSL connection, in which case 2 round trips are an
// absolute minumum.
WebSocketTransport.roundTrips = 2;
module.exports = WebSocketTransport;

View File

@@ -0,0 +1,23 @@
'use strict';
var inherits = require('inherits')
, AjaxBasedTransport = require('./lib/ajax-based')
, XdrStreamingTransport = require('./xdr-streaming')
, XhrReceiver = require('./receiver/xhr')
, XDRObject = require('./sender/xdr')
;
function XdrPollingTransport(transUrl) {
if (!XDRObject.enabled) {
throw new Error('Transport created when disabled');
}
AjaxBasedTransport.call(this, transUrl, '/xhr', XhrReceiver, XDRObject);
}
inherits(XdrPollingTransport, AjaxBasedTransport);
XdrPollingTransport.enabled = XdrStreamingTransport.enabled;
XdrPollingTransport.transportName = 'xdr-polling';
XdrPollingTransport.roundTrips = 2; // preflight, ajax
module.exports = XdrPollingTransport;

View File

@@ -0,0 +1,32 @@
'use strict';
var inherits = require('inherits')
, AjaxBasedTransport = require('./lib/ajax-based')
, XhrReceiver = require('./receiver/xhr')
, XDRObject = require('./sender/xdr')
;
// According to:
// http://stackoverflow.com/questions/1641507/detect-browser-support-for-cross-domain-xmlhttprequests
// http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/
function XdrStreamingTransport(transUrl) {
if (!XDRObject.enabled) {
throw new Error('Transport created when disabled');
}
AjaxBasedTransport.call(this, transUrl, '/xhr_streaming', XhrReceiver, XDRObject);
}
inherits(XdrStreamingTransport, AjaxBasedTransport);
XdrStreamingTransport.enabled = function(info) {
if (info.cookie_needed || info.nullOrigin) {
return false;
}
return XDRObject.enabled && info.sameScheme;
};
XdrStreamingTransport.transportName = 'xdr-streaming';
XdrStreamingTransport.roundTrips = 2; // preflight, ajax
module.exports = XdrStreamingTransport;

View File

@@ -0,0 +1,33 @@
'use strict';
var inherits = require('inherits')
, AjaxBasedTransport = require('./lib/ajax-based')
, XhrReceiver = require('./receiver/xhr')
, XHRCorsObject = require('./sender/xhr-cors')
, XHRLocalObject = require('./sender/xhr-local')
;
function XhrPollingTransport(transUrl) {
if (!XHRLocalObject.enabled && !XHRCorsObject.enabled) {
throw new Error('Transport created when disabled');
}
AjaxBasedTransport.call(this, transUrl, '/xhr', XhrReceiver, XHRCorsObject);
}
inherits(XhrPollingTransport, AjaxBasedTransport);
XhrPollingTransport.enabled = function(info) {
if (info.nullOrigin) {
return false;
}
if (XHRLocalObject.enabled && info.sameOrigin) {
return true;
}
return XHRCorsObject.enabled;
};
XhrPollingTransport.transportName = 'xhr-polling';
XhrPollingTransport.roundTrips = 2; // preflight, ajax
module.exports = XhrPollingTransport;

View File

@@ -0,0 +1,41 @@
'use strict';
var inherits = require('inherits')
, AjaxBasedTransport = require('./lib/ajax-based')
, XhrReceiver = require('./receiver/xhr')
, XHRCorsObject = require('./sender/xhr-cors')
, XHRLocalObject = require('./sender/xhr-local')
, browser = require('../utils/browser')
;
function XhrStreamingTransport(transUrl) {
if (!XHRLocalObject.enabled && !XHRCorsObject.enabled) {
throw new Error('Transport created when disabled');
}
AjaxBasedTransport.call(this, transUrl, '/xhr_streaming', XhrReceiver, XHRCorsObject);
}
inherits(XhrStreamingTransport, AjaxBasedTransport);
XhrStreamingTransport.enabled = function(info) {
if (info.nullOrigin) {
return false;
}
// Opera doesn't support xhr-streaming #60
// But it might be able to #92
if (browser.isOpera()) {
return false;
}
return XHRCorsObject.enabled;
};
XhrStreamingTransport.transportName = 'xhr-streaming';
XhrStreamingTransport.roundTrips = 2; // preflight, ajax
// Safari gets confused when a streaming ajax request is started
// before onload. This causes the load indicator to spin indefinetely.
// Only require body when used in a browser
XhrStreamingTransport.needBody = !!global.document;
module.exports = XhrStreamingTransport;

View File

@@ -0,0 +1,17 @@
'use strict';
if (global.crypto && global.crypto.getRandomValues) {
module.exports.randomBytes = function(length) {
var bytes = new Uint8Array(length);
global.crypto.getRandomValues(bytes);
return bytes;
};
} else {
module.exports.randomBytes = function(length) {
var bytes = new Array(length);
for (var i = 0; i < length; i++) {
bytes[i] = Math.floor(Math.random() * 256);
}
return bytes;
};
}

View File

@@ -0,0 +1,27 @@
'use strict';
module.exports = {
isOpera: function() {
return global.navigator &&
/opera/i.test(global.navigator.userAgent);
}
, isKonqueror: function() {
return global.navigator &&
/konqueror/i.test(global.navigator.userAgent);
}
// #187 wrap document.domain in try/catch because of WP8 from file:///
, hasDomain: function () {
// non-browser client always has a domain
if (!global.document) {
return true;
}
try {
return !!global.document.domain;
} catch (e) {
return false;
}
}
};

View File

@@ -0,0 +1,50 @@
'use strict';
var JSON3 = require('json3');
// Some extra characters that Chrome gets wrong, and substitutes with
// something else on the wire.
// eslint-disable-next-line no-control-regex
var extraEscapable = /[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g
, extraLookup;
// This may be quite slow, so let's delay until user actually uses bad
// characters.
var unrollLookup = function(escapable) {
var i;
var unrolled = {};
var c = [];
for (i = 0; i < 65536; i++) {
c.push( String.fromCharCode(i) );
}
escapable.lastIndex = 0;
c.join('').replace(escapable, function(a) {
unrolled[ a ] = '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
return '';
});
escapable.lastIndex = 0;
return unrolled;
};
// Quote string, also taking care of unicode characters that browsers
// often break. Especially, take care of unicode surrogates:
// http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates
module.exports = {
quote: function(string) {
var quoted = JSON3.stringify(string);
// In most cases this should be very fast and good enough.
extraEscapable.lastIndex = 0;
if (!extraEscapable.test(quoted)) {
return quoted;
}
if (!extraLookup) {
extraLookup = unrollLookup(extraEscapable);
}
return quoted.replace(extraEscapable, function(a) {
return extraLookup[a];
});
}
};

Some files were not shown because too many files have changed in this diff Show More