diff --git a/README.md b/README.md index d069272..22143be 100644 --- a/README.md +++ b/README.md @@ -2,54 +2,60 @@ reload ======= [![build status](https://api.travis-ci.org/jprichardson/reload.svg)](http://travis-ci.org/jprichardson/reload) +[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) +[![NPM version](https://img.shields.io/npm/v/reload.svg?style=flat-square)](https://www.npmjs.com/package/reload) -Refresh and reload your code in your browser when your code changes. No browser plugins required. Use with Node.js if you like. - - +Automatically refresh and reload your code in your browser when your code changes. No browser plugins required. Why? ---- -Restarting your Http server and refreshing your browser is annoying. +Restarting your HTTP server and refreshing your browser is annoying. -Express version support ------------------------ +How does it work? +---------- -To use reload with Express 4 support, use reload version `^0.2.0`. +Reload works in two different ways depending on if you're using it: -To use reload with Express 3 support, use reload version `~0.1.0`. +1. In an existing Express application in which it creates a server side route for reload or, +2. As a command line tool which starts its own Express application to monitor the file you're editing for changes and to serve `reload-client.js` to the browser. + +Once reload-server and reload-client are connected, the client side code opens a [WebSocket](https://en.wikipedia.org/wiki/WebSocket) to the server and waits for the WebSocket to close, once it closes, reload waits for the server to come back up (waiting for a socket on open event), once the socket opens we reload the page. Installation ------------- +--- npm install [-g] [--save-dev] reload +Two ways to use reload +--- -Example for Node.js and browser development --------------------------------------------- +There are two different ways to use reload. -Use in conjunction with [supervisor](https://github.com/isaacs/node-supervisor), [nodemon](https://github.com/remy/nodemon), or [forever](https://github.com/nodejitsu/forever). +1. In an [Express](http://expressjs.com/) application, allowing your whole project to utilize reload when the code is altered +2. As a command line application to serve up static HTML files and be able to reload when the code is altered -I recommend `supervisor`, since `nodedemon` time to poll for file changes is too slow and not configurable. Supervisor will feel fast. `forever` tries to do too much. Whenever I look at the docs, I get frustrated and give up. +Using reload in Express +--- +When used with Express reload creates a new Express route for reload. When you restart the server, the client will detect the server being restarted and automatically refresh the page. -Using reload with Express 4 ---------------------------- +Reload can be used in conjunction with tools that allow for automatically restarting the server such as [supervisor](https://github.com/isaacs/node-supervisor) (recommended), [nodemon](https://github.com/remy/nodemon), [forever](https://github.com/nodejitsu/forever), etc. -*Reload version `^0.2.0`.* +### Express Example -**server.js:** +**`server.js`:** ```javascript var express = require('express') - , http = require('http') - , path = require('path') - , reload = require('reload') - , bodyParser = require('body-parser') - , logger = require('morgan') +var http = require('http') +var path = require('path') +var reload = require('reload') +var bodyParser = require('body-parser') +var logger = require('morgan') var app = express() -var publicDir = path.join(__dirname, '') +var publicDir = path.join(__dirname, 'public') app.set('port', process.env.PORT || 3000) app.use(logger('dev')) @@ -61,149 +67,94 @@ app.get('/', function(req, res) { var server = http.createServer(app) -//reload code here -//optional reload delay and wait argument can be given to reload, refer to [API](https://github.com/jprichardson/reload#api) below -reload(server, app, [reloadDelay], [wait]) +// Reload code here +reload(server, app) server.listen(app.get('port'), function(){ console.log("Web server listening on port " + app.get('port')); }); ``` -Using reload with Express 3 ---------------------------- - -*Reload version `~0.1.0`.* - -**server.js:** -```javascript -var express = require('express') - , http = require('http') - , path = require('path') - , reload = require('reload') - -var app = express() - -var publicDir = path.join(__dirname, 'public') - -app.configure(function() { - app.set('port', process.env.PORT || 3000) - app.use(express.logger('dev')) - app.use(express.bodyParser()) //parses json, multi-part (file), url-encoded - app.use(app.router) //need to be explicit, (automatically adds it if you forget) - app.use(express.static(publicDir)) //should cache static assets -}) +**`public/index.html`:** +```html + + + + Reload Express Sample App + + +

Reload Express Sample App12

+ + + + +``` -app.get('/', function(req, res) { - res.sendfile(path.join(publicDir, 'index.html')) -}) +**Refer to the [reload express sample app](https://github.com/jprichardson/reload/tree/master/expressSampleApp) for this working example.** -var server = http.createServer(app) +### Manually firing server-side reload events -//reload code here -//optional reload delay and wait argument can be given to reload, refer to [API](https://github.com/jprichardson/reload#api) below -reload(server, app, [reloadDelay], [wait]) +You can manually call a reload event by calling `reload()` yourself. An example is shown below: -server.listen(app.get('port'), function(){ - console.log("Web server listening on port " + app.get('port')); +```javascript +reloadServer = reload(server, app); +watch.watchTree(__dirname + "/public", function (f, curr, prev) { + // Fire server-side reload event + reloadServer.reload(); }); ``` -**public/index.html:** (very valid HTML5, watch the YouTube video) -```html - - - -My sweet app! - - - - -

Hello!

-``` +### API for Express -install supervisor: ``` -npm install -g supervisor +reload(httpServer, expressApp, [verbose]) ``` -reload on any html or js file change: -``` -supervisor -e 'html|js' node server.js -``` - - - -Example for browser development only -------------------------------------- - -You should install `reload` globally like `npm install -g reload`. Then you can use the `reload` command in your directory without modifying any of your HTML. - -Usage: - -``` +- `httpServer`: The Node.js http server from the module `http`. +- `expressApp`: The express app. It may work with other frameworks, or even with Connect. At this time, it's only been tested with Express. +- `verbose`: If set to true, will show logging on the server and client side -Usage: reload [options] +Using reload as a command line application +--- -Options: +There are two ways to use the command line application. +1. In a directory serving blank static HTML files or +2. In a project with a `package.json` file - -h, --help Output usage information - -V, --version Output the version number - -b, --browser Open in the browser automatically. - -n, --hostname If -b flag is being used, this allows for custom hostnames. Defaults to localhost. - -d, --dir [dir] The directory to serve up. Defaults to current dir. - -e, --exts [extensions] Extensions separated by commas or pipes. Defaults to html,js,css. - -p, --port [port] The port to bind to. Can be set with PORT env variable as well. Defaults to 8080 - -r, --reload-delay [reload-delay] The client side refresh time in milliseconds. Default is `300`. If wait is specified as true (which is by default, see below) this delay becomes the delay of how long the pages waits to reload after the socket is reopened. - -w, --wait [wait] Specify true, if you would like reload to wait until the server comes back up before reloading the page. Defaults to true - -s, --start-page [start-page] Specify a start page. Defaults to index.html. +Each will require different modes of installing. +In case one you should install reload globally with `npm install reload -g`. Also with reload installed globally you can go to any directory with an HTML file and use the command reload to constantly watch it and reload it while you make changes. -``` +In case two you should install locally with `npm install --save-dev`, since this tool is to aid in development you should install it as a dev dependency. Navigate to your html directory: reload -b -this will open your `index.html` file in the browser. Any changes that you make will now reload in the browser. You don't need to modify your HTML at all. - - - -How does it work? ------------------ - -It's actually stupidly simple. We leverage `supervisor` to restart the server if any file changes. The client side keeps a websocket open, once the websocket closes, the client sets a timeout to reload in approximately 300 ms (or any other time you'd like, refer to [API](https://github.com/jprichardson/reload#api) below). Simple huh? +This will open your `index.html` file in the browser. Any changes that you make will now reload in the browser. You don't need to modify your HTML at all. -Reload on your terms ---- -If you would like to fire the reload event on your own terms you can use fire a reload event by calling `reload()` yourself. An example is shown below: +### Usage for Command Line Application ``` -reloadServer = reload(server, app, 1000); -watch.watchTree(__dirname + "/public", function (f, curr, prev) { - console.log("Trying to reload..."); - reloadServer.reload(); -}); -``` - -API ---- - -### reload(httpServer, expressApp, [reloadDelay], [wait]) - -- `httpServer`: The Node.js http server from the module `http`. -- `expressApp`: The express app. It may work with other frameworks, or even with Connect. At this time, it's only been tested with Express. -- `reloadDelay`: The client side refresh time in milliseconds. Default is `300`. If wait is specified as true (which it is be default, see below) this delay becomes the delay of how long the pages waits to reload after the socket is reopened. -- `wait`: If wait is specified as true reload will wait until the server comes back up before reloading the page. Defaults to true. +Usage: reload [options] +Options: + -h, --help Output usage information + -V, --version Output the version number + -b, --browser Open in the browser automatically. + -n, --hostname If -b flag is being used, this allows for custom hostnames. Defaults to localhost. + -d, --dir [dir] The directory to serve up. Defaults to current dir. + -e, --exts [extensions] Extensions separated by commas or pipes. Defaults to html,js,css. + -p, --port [port] The port to bind to. Can be set with PORT env variable as well. Defaults to 8080 + -s, --start-page [start-page] Specify a start page. Defaults to index.html. + -v, --verbose Turns on logging on the server and client side. Defaults to false. +``` License -------- +--- (MIT License) -Copyright 2013, JP Richardson +Copyright 2016, JP Richardson diff --git a/bin/reload b/bin/reload index 417e0da..b59aa2a 100755 --- a/bin/reload +++ b/bin/reload @@ -1,9 +1,9 @@ #!/usr/bin/env node var program = require('commander') + , ON_DEATH = require('death')() , supervisor = require('supervisor') , path = require('path') - , ON_DEATH = require('death') , os = require('os') , fs = require('fs') , clc = require('cli-color') @@ -14,9 +14,8 @@ program.version(require('../package.json').version) .option('-d, --dir [dir]', 'The directory to serve up. Defaults to current dir.', process.cwd()) .option('-e, --exts [extensions]', 'Extensions separated by commas or pipes. Defaults to html,js,css.', 'html|js|css') .option('-p, --port [port]', 'The port to bind to. Can be set with PORT env variable as well. Defaults to 8080', '8080') - .option('-r, --reload-delay [reload-delay]', 'How long (ms) should the server wait before refreshing the page? Defaults to 300 ms. If -w is set to true, this delay becomes how long the page should wait to reload after the socket open.', '300') - .option('-w, --wait [wait]', 'Specify whether or not reload should wait until the server comes back up before reloading the page. Defaults to true', 'true') .option('-s, --start-page [start-page]', 'Specify a start page. Defaults to index.html', 'index.html') + .option('-v, --verbose [verbose]', 'Turning on logging on the server and client side. Defaults to false', false) .parse(process.argv) var runFile = path.join(os.tmpdir(), 'reload-' + Math.random().toString().slice(2)) @@ -25,10 +24,10 @@ var serverFile = path.join(__dirname, '../lib/reload-server.js') if (program.exts.indexOf(',')) program.exts = program.exts.replace(/\,/g,'|') //replace comma for pipe, that's what supervisor likes -var args = ['-e', program.exts, '-q', '--', serverFile, program.port, program.dir, !!program.browser, program.hostname, runFile, program.reloadDelay, program.wait, program.startPage] +var args = ['-e', program.exts, '-q', '--', serverFile, program.port, program.dir, !!program.browser, program.hostname, runFile, program.startPage, program.verbose] supervisor.run(args) -console.log("\n Reload web server:") -console.log(" listening on port " + clc.blue.bold(program.port)) -console.log(" monitoring dir " + clc.green.bold(program.dir)) +console.log("\nReload web server:") +console.log("listening on port " + clc.blue.bold(program.port)) +console.log("monitoring dir " + clc.green.bold(program.dir)) diff --git a/expressSampleApp/.gitignore b/expressSampleApp/.gitignore new file mode 100644 index 0000000..90f0f8b --- /dev/null +++ b/expressSampleApp/.gitignore @@ -0,0 +1,11 @@ +lib-cov +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz + +npm-debug.log +node_modules diff --git a/expressSampleApp/package.json b/expressSampleApp/package.json new file mode 100644 index 0000000..8fb13fe --- /dev/null +++ b/expressSampleApp/package.json @@ -0,0 +1,26 @@ +{ + "name": "reload-sample-app", + "version": "0.0.1", + "description": "Sample app for reload package https://www.npmjs.com/package/reload", + "author": "", + "license": "", + "main": "server.js", + "dependencies": { + "express": "^4.14.0", + "path": "^0.12.7", + "reload": "*", + "body-parser": "^1.15.2", + "morgan": "^1.7.0", + "supervisor": "^0.11.0", + "watch": "0.19.1" + }, + "devDependencies": {}, + "private": true, + "repository": { + "private-repo": "git+ssh://somewhere:port/folder/on/server" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + } +} diff --git a/expressSampleApp/public/index.html b/expressSampleApp/public/index.html new file mode 100644 index 0000000..195b550 --- /dev/null +++ b/expressSampleApp/public/index.html @@ -0,0 +1,11 @@ + + + + Reload Express Sample App + + +

Reload Express Sample App

+ + + + diff --git a/expressSampleApp/server.js b/expressSampleApp/server.js new file mode 100644 index 0000000..d169845 --- /dev/null +++ b/expressSampleApp/server.js @@ -0,0 +1,27 @@ +var express = require('express') +var http = require('http') +var path = require('path') +var reload = require('reload') +var bodyParser = require('body-parser') +var logger = require('morgan') + +var app = express() + +var publicDir = path.join(__dirname, 'public') + +app.set('port', process.env.PORT || 3000) +app.use(logger('dev')) +app.use(bodyParser.json()) // Parses json, multi-part (file), url-encoded + +app.get('/', function (req, res) { + res.sendFile(path.join(publicDir, 'index.html')) +}) + +var server = http.createServer(app) + +// Reload code here +reload(server, app) + +server.listen(app.get('port'), function () { + console.log('Web server listening on port ' + app.get('port')) +}) diff --git a/lib/reload-client.js b/lib/reload-client.js index 6005418..11940fc 100644 --- a/lib/reload-client.js +++ b/lib/reload-client.js @@ -1,52 +1,98 @@ -;(function refresh () { - /* global SockJS */ +(function refresh () { + var verboseLogging = false + var socketUrl = window.location.origin.replace('http://', 'ws://') + var socket - var sock = new SockJS(window.location.origin + '/sockreload') - var reloadDelay = 300 - var socketDelay = 0 - var wait = false - var checkDelay = 1 - var checkDelayCounter = 1 + if (verboseLogging) { + console.log('Reload Script Loaded') + } + + if (!('WebSocket' in window)) { + throw new Error('Reload only works with browsers that support WebSockets') + } - var newConn = function () { - // try and connect a new socket to the server. - sock = new SockJS(window.location.origin + '/sockreload') + // Explanation of the flags below: - // if the server is up then, the sockert will reach this event handler and then call refreshPage - sock.onopen = function () { - setTimeout(function () { - window.location.reload() - }, socketDelay) + // The first change flag is used to tell reload to wait until the socket closes at least once before we allow the page to open on a socket open event. Otherwise reload will go into a inifite loop, as the page will have a socket on open event once it loads for the first time + var firstChangeFlag = false + + // The navigatedAwayFromPageFlag is set to true in the event handler onbeforeunload because we want to short-circuit reload to prevent it from causing the page to reload before the navigation occurs. + var navigatedAwayFromPageFlag = false + + // Wait until the page loads for the first time and then call the webSocketWaiter function so that we can connect the socket for the first time + window.addEventListener('load', function () { + if (verboseLogging === true) { + console.log('Page Loaded - Calling webSocketWaiter') } + websocketWaiter() + }) - // else the server is down try and connect again with another call to newConn + // If the user navigates away from the page, we want to short-circuit reload to prevent it from causing the page to reload before the navigation occurs. + window.addEventListener('onbeforeunload', function () { + if (verboseLogging === true) { + console.log('Navigated away from the current URL') + } - // this will exponentially increase the wait time to slow down the calls to checkServerUp to prevent this script from spamming requests if the server doesn’t come back up quickly. - // this is useful in the case when the user brings down the server but forgets to close their dev tab or a server has a long restart time. - checkDelay = (Math.pow(checkDelayCounter, 6)) / 10000000000 - checkDelayCounter++ + navigatedAwayFromPageFlag = true + }) - setTimeout(function () { - newConn() - }, checkDelay) + // Check to see if the server sent us reload (meaning a manually reload event was fired) and then reloads the page + var socketOnMessage = function (msg) { + if (msg.data === 'reload') { + socket.close() + } } - sock.onclose = function () { - // check for the server only if the user entered in true for a delay - if (wait) { - sock = null + var socketOnOpen = function (msg) { + if (verboseLogging) { + console.log('Socket Opened') + } + + // We only allow the reload on two conditions, one when the socket closed (firstChange === true) and two if we didn't navigate to a new page (navigatedAwayFromPageFlag === false) + if (firstChangeFlag === true && navigatedAwayFromPageFlag !== true) { + if (verboseLogging) { + console.log('Reloaded') + } + + // Reset the firstChangeFlag to false so that when the socket on open events are being fired it won't keep reloading the page + firstChangeFlag = false - newConn() - } else { // else use the default delay or user specified delay - setTimeout(function () { - window.location.reload() - }, reloadDelay) + // Now that everything is set up properly we reload the page + window.location.reload() } } - sock.onmessage = function (e) { - if (e.data === 'reload') { - sock.onclose() + // Socket on close event that sets flags and calls the webSocketWaiter function + var socketOnClose = function (msg) { + if (verboseLogging) { + console.log('Socket Closed - Calling webSocketWaiter') } + + // We encountered a change so we set firstChangeFlag to true so that as soon as the server comes back up and the socket opens we can allow the reload + firstChangeFlag = true + + // Call the webSocketWaiter function so that we can open a new socket and set the event handlers + websocketWaiter() + } + + var socketOnError = function (msg) { + if (verboseLogging) { + console.log(msg) + } + } + + // Function that opens a new socket and sets the event handlers for the socket + function websocketWaiter () { + if (verboseLogging) { + console.log('Waiting for socket') + } + setTimeout(function () { + socket = new WebSocket(socketUrl) // eslint-disable-line + + socket.onopen = socketOnOpen + socket.onclose = socketOnClose + socket.onmessage = socketOnMessage + socket.onerror = socketOnError + }, 250) } })() diff --git a/lib/reload-server.js b/lib/reload-server.js index b62c657..fd0c54c 100644 --- a/lib/reload-server.js +++ b/lib/reload-server.js @@ -4,32 +4,41 @@ var path = require('path') var reload = require('../lib/reload') var fs = require('fs') var open = require('open') +var clc = require('cli-color') +var argv = require('minimist')(process.argv.slice(2)) var app = express() -var port = process.argv[2] -var dir = process.argv[3] -var openBrowser = (process.argv[4] === 'true') -var hostname = process.argv[5] -var runFile = process.argv[6] -var reloadDelay = process.argv[7] -var wait = (process.argv[8] === 'true') -var startPage = process.argv[9] +var port = argv._[0] +var dir = argv._[1] +var openBrowser = (argv._[2] === 'true') +var hostname = argv._[3] +var runFile = argv._[4] +var startPage = argv._[5] +var verbose = (argv._[6] === 'true') var router = express.Router() app.set('port', port) router.get('/', function (req, res, next) { + // Create the start page path. var startFile = path.join(dir, startPage) + fs.exists(startFile, function (itDoes) { - if (itDoes) sendhtml(startFile, res) - else res.status(404).send("Can't find " + startPage) + // If the start page exists send the reload-client code to the HTML. + if (itDoes) { + sendhtml(startFile, res) + } else { + // Else the start page doesn't exist (whether it's the deault index.html or specified). + res.status(404).send("Can't find " + startPage) + } }) }) +// Routing for any HTML, so that we can send the reload-client file to every HTML page when naviagted too. router.get('*.html', function (req, res) { - // this wouldn't be efficient in any other context + // This wouldn't be efficient in any other context. var file = path.join(dir, req.url) sendhtml(file, res) }) @@ -37,20 +46,27 @@ router.get('*.html', function (req, res) { app.use('/', router) app.use('*html', router) -app.use(express.static(dir)) // should cache static assets +app.use(express.static(dir)) // Should cache static assets. var server = http.createServer(app) -reload(server, app, reloadDelay, wait) + +// Reload call and configurations. +reload(server, app, verbose) server.listen(app.get('port'), function () { if (!fs.existsSync(runFile)) { fs.writeFile(runFile, '') - if (openBrowser) open('http://' + hostname + ':' + app.get('port')) + + // If openBrowser, open the browser with the given start page above, at a hostname (localhost default or specified). + if (openBrowser) { + open('http://' + hostname + ':' + app.get('port')) + } } else { - console.log(' restarting...') + console.log(clc.green('restarting...')) } }) +// Function to send reload-client code to the browser. function sendhtml (file, res) { fs.readFile(file, 'utf8', function (err, contents) { if (err) { diff --git a/lib/reload.js b/lib/reload.js index 7494f1f..ebf0a62 100644 --- a/lib/reload.js +++ b/lib/reload.js @@ -1,37 +1,35 @@ -var sockjs = require('sockjs') var path = require('path') var fs = require('fs') -var SOCKJS_FILE = path.join(__dirname, './sockjs-0.3-min.js') var RELOAD_FILE = path.join(__dirname, './reload-client.js') -module.exports = function reload (httpServer, expressApp, reloadDelay, wait) { - // this happens at startup of program, so sync is alright - var sockjsCode = fs.readFileSync(SOCKJS_FILE, 'utf8') +module.exports = function reload (httpServer, expressApp, verboseLogging) { var reloadCode = fs.readFileSync(RELOAD_FILE, 'utf8') - var conn - // if reloadDelay is specified as true then reload will wait for the server to be up before reloading the page - if (wait === true) { - reloadCode = reloadCode.replace('wait = false;', 'wait = true;') - reloadCode = reloadCode.replace('socketDelay = 0;', 'socketDelay = ' + reloadDelay) - } + var conn - reloadCode = reloadCode.replace('reloadDelay = 300;', 'reloadDelay = ' + reloadDelay) + var WebSocketServer = require('ws').Server + var wss = new WebSocketServer({ server: httpServer }) - var clientCode = sockjsCode + '\n\n' + reloadCode + wss.on('connection', (ws) => { + // Take the current web socket connection and save it to webSocketConnetion so we can use it later in the returned reload function for manually firing reload events. + conn = ws - var reload = sockjs.createServer({ log: function () {} }) - reload.on('connection', function (connection) { - conn = connection + if (verboseLogging) { + console.log('Reload client connected to server') + } }) - reload.installHandlers(httpServer, {prefix: '/sockreload'}) + + if (verboseLogging) { + reloadCode = reloadCode.replace('verboseLogging = false', 'verboseLogging = true') + } expressApp.get('/reload/reload.js', function (req, res) { res.type('text/javascript') - res.send(clientCode) + res.send(reloadCode) }) + // Return an object, so that the user can manually reload the server by calling the returned function reload. Using the web socket connection from above, we provide a function called reload which passes the command 'reload' to the function sendMessage. sendMessage sends the message 'reload' over the socket (if the socket is connected) to the client. The client then recieves the messages checks to see if the message is reload and then reloads the page. return { 'server': reload, 'connection': conn, @@ -40,9 +38,11 @@ module.exports = function reload (httpServer, expressApp, reloadDelay, wait) { }, 'sendMessage': function (command) { if (conn) { - conn.write(command) + conn.send(command) } else { - console.log('Cannot send "' + command + '" to client: still not connected') + if (verboseLogging) { + console.log('Cannot send "' + command + '" to client: still not connected') + } } } } diff --git a/lib/sockjs-0.3-min.js b/lib/sockjs-0.3-min.js deleted file mode 100644 index 2b8b11c..0000000 --- a/lib/sockjs-0.3-min.js +++ /dev/null @@ -1,25 +0,0 @@ -/* SockJS client, version 0.3.4, http://sockjs.org, MIT License - -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. -*/ - - -SockJS=function(){var a=document,b=window,c={},d=function(){};d.prototype.addEventListener=function(a,b){this._listeners||(this._listeners={}),a in this._listeners||(this._listeners[a]=[]);var d=this._listeners[a];c.arrIndexOf(d,b)===-1&&d.push(b);return},d.prototype.removeEventListener=function(a,b){if(!(this._listeners&&a in this._listeners))return;var d=this._listeners[a],e=c.arrIndexOf(d,b);if(e!==-1){d.length>1?this._listeners[a]=d.slice(0,e).concat(d.slice(e+1)):delete this._listeners[a];return}return},d.prototype.dispatchEvent=function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d=3e3&&a<=4999},c.countRTO=function(a){var b;return a>100?b=3*a:b=a+200,b},c.log=function(){b.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)},c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b,arguments)}},c.flatUrl=function(a){return a.indexOf("?")===-1&&a.indexOf("#")===-1},c.amendUrl=function(b){var d=a.location;if(!b)throw new Error("Wrong url for SockJS");if(!c.flatUrl(b))throw new Error("Only basic urls are supported in SockJS");return b.indexOf("//")===0&&(b=d.protocol+b),b.indexOf("/")===0&&(b=d.protocol+"//"+d.host+b),b=b.replace(/[/]+$/,""),b},c.arrIndexOf=function(a,b){for(var c=0;c=0},c.delay=function(a,b){return typeof a=="function"&&(b=a,a=0),setTimeout(b,a)};var i=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,j={"\0":"\\u0000","\x01":"\\u0001","\x02":"\\u0002","\x03":"\\u0003","\x04":"\\u0004","\x05":"\\u0005","\x06":"\\u0006","\x07":"\\u0007","\b":"\\b","\t":"\\t","\n":"\\n","\x0b":"\\u000b","\f":"\\f","\r":"\\r","\x0e":"\\u000e","\x0f":"\\u000f","\x10":"\\u0010","\x11":"\\u0011","\x12":"\\u0012","\x13":"\\u0013","\x14":"\\u0014","\x15":"\\u0015","\x16":"\\u0016","\x17":"\\u0017","\x18":"\\u0018","\x19":"\\u0019","\x1a":"\\u001a","\x1b":"\\u001b","\x1c":"\\u001c","\x1d":"\\u001d","\x1e":"\\u001e","\x1f":"\\u001f",'"':'\\"',"\\":"\\\\","\x7f":"\\u007f","\x80":"\\u0080","\x81":"\\u0081","\x82":"\\u0082","\x83":"\\u0083","\x84":"\\u0084","\x85":"\\u0085","\x86":"\\u0086","\x87":"\\u0087","\x88":"\\u0088","\x89":"\\u0089","\x8a":"\\u008a","\x8b":"\\u008b","\x8c":"\\u008c","\x8d":"\\u008d","\x8e":"\\u008e","\x8f":"\\u008f","\x90":"\\u0090","\x91":"\\u0091","\x92":"\\u0092","\x93":"\\u0093","\x94":"\\u0094","\x95":"\\u0095","\x96":"\\u0096","\x97":"\\u0097","\x98":"\\u0098","\x99":"\\u0099","\x9a":"\\u009a","\x9b":"\\u009b","\x9c":"\\u009c","\x9d":"\\u009d","\x9e":"\\u009e","\x9f":"\\u009f","\xad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"},k=/[\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,l,m=JSON&&JSON.stringify||function(a){return i.lastIndex=0,i.test(a)&&(a=a.replace(i,function(a){return j[a]})),'"'+a+'"'},n=function(a){var b,c={},d=[];for(b=0;b<65536;b++)d.push(String.fromCharCode(b));return a.lastIndex=0,d.join("").replace(a,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),a.lastIndex=0,c};c.quote=function(a){var b=m(a);return k.lastIndex=0,k.test(b)?(l||(l=n(k)),b.replace(k,function(a){return l[a]})):b};var o=["websocket","xdr-streaming","xhr-streaming","iframe-eventsource","iframe-htmlfile","xdr-polling","xhr-polling","iframe-xhr-polling","jsonp-polling"];c.probeProtocols=function(){var a={};for(var b=0;b0&&h(a)};return c.websocket!==!1&&h(["websocket"]),d["xhr-streaming"]&&!c.null_origin?e.push("xhr-streaming"):d["xdr-streaming"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-streaming"):h(["iframe-eventsource","iframe-htmlfile"]),d["xhr-polling"]&&!c.null_origin?e.push("xhr-polling"):d["xdr-polling"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-polling"):h(["iframe-xhr-polling","jsonp-polling"]),e};var p="_sockjs_global";c.createHook=function(){var a="a"+c.random_string(8);if(!(p in b)){var d={};b[p]=function(a){return a in d||(d[a]={id:a,del:function(){delete d[a]}}),d[a]}}return b[p](a)},c.attachMessage=function(a){c.attachEvent("message",a)},c.attachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.addEventListener(c,d,!1):(a.attachEvent("on"+c,d),b.attachEvent("on"+c,d))},c.detachMessage=function(a){c.detachEvent("message",a)},c.detachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.removeEventListener(c,d,!1):(a.detachEvent("on"+c,d),b.detachEvent("on"+c,d))};var q={},r=!1,s=function(){for(var a in q)q[a](),delete q[a]},t=function(){if(r)return;r=!0,s()};c.attachEvent("unload",t),c.unload_add=function(a){var b=c.random_string(8);return q[b]=a,r&&c.delay(s),b},c.unload_del=function(a){a in q&&delete q[a]},c.createIframe=function(b,d){var e=a.createElement("iframe"),f,g,h=function(){clearTimeout(f);try{e.onload=null}catch(a){}e.onerror=null},i=function(){e&&(h(),setTimeout(function(){e&&e.parentNode.removeChild(e),e=null},0),c.unload_del(g))},j=function(a){e&&(i(),d(a))},k=function(a,b){try{e&&e.contentWindow&&e.contentWindow.postMessage(a,b)}catch(c){}};return e.src=b,e.style.display="none",e.style.position="absolute",e.onerror=function(){j("onerror")},e.onload=function(){clearTimeout(f),f=setTimeout(function(){j("onload timeout")},2e3)},a.body.appendChild(e),f=setTimeout(function(){j("timeout")},15e3),g=c.unload_add(i),{post:k,cleanup:i,loaded:h}},c.createHtmlfile=function(a,d){var e=new ActiveXObject("htmlfile"),f,g,i,j=function(){clearTimeout(f)},k=function(){e&&(j(),c.unload_del(g),i.parentNode.removeChild(i),i=e=null,CollectGarbage())},l=function(a){e&&(k(),d(a))},m=function(a,b){try{i&&i.contentWindow&&i.contentWindow.postMessage(a,b)}catch(c){}};e.open(),e.write('