├── .gitignore ├── README.md ├── websocket-react ├── .gitignore ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── LoginForm.js │ ├── NotificationForm.js │ ├── index.css │ ├── index.js │ ├── js │ │ ├── sockjs.min.js │ │ └── stomp.min.js │ ├── logo.svg │ └── registerServiceWorker.js └── yarn.lock └── websocket-spring ├── .gitignore ├── .gradle └── buildOutputCleanup │ ├── built.bin │ ├── cache.properties │ └── cache.properties.lock ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── aptech │ │ ├── Application.java │ │ ├── config │ │ ├── DBLoader.java │ │ ├── OAuth2ServerConfiguration.java │ │ ├── WebSecurityConfig.java │ │ └── WebSocketConfig.java │ │ ├── controller │ │ └── NotificationController.java │ │ ├── model │ │ ├── Notification.java │ │ ├── Role.java │ │ └── User.java │ │ ├── repository │ │ └── UserRepository.java │ │ └── service │ │ ├── NotificationService.java │ │ └── UserService.java └── resources │ └── application.properties └── test └── java └── com └── example └── websocketspring └── WebsocketSpringApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Spring Boot Websocket + React: user notifications with web socket ## 2 | 3 | This example will show how to send notifications, via web socket, to specific logged-in users (definded by access_token). 4 | 5 | Could be useful, for example, if you are trying to implement a real-time user notification system with ReactJS. 6 | 7 | [*DEMO*:](https://giphy.com/gifs/3o6fIRpgI9LnZ3jrXi/fullscreen) 8 | 9 | ![Demo](https://media.giphy.com/media/3ohs7JcKo7aeM2PHdC/giphy.gif) 10 | 11 | 12 | ### Build and run 13 | 14 | #### Configurations 15 | Backend: 16 | 17 | Open the `application.properties` file in *websocket-spring* and set your own database (in my case I'm using MongoDB). You can change User collect to Entity and repository like your project. 18 | 19 | #### Prerequisites 20 | 21 | - Java 8 22 | - Maven > 3.0 23 | 24 | #### From terminal 25 | 1. Start mongodb database 26 | ``` 27 | $ mongod 28 | ``` 29 | 2. Go on the project's *websocket-spring* folder, then type: 30 | ``` 31 | $ mvn spring-boot:run 32 | ``` 33 | Or, just open Maven project on IDE like IntelliJ IDEA and run `main method` in Application class 34 | 3. Go on project:s *websocket-react* folder, then type: 35 | ``` 36 | $ npm install 37 | $ npm start 38 | ``` 39 | or 40 | ``` 41 | $ yarn install 42 | $ yarn start 43 | ``` 44 | ### Usage 45 | 46 | - Launch the application and login into it with one of the following credentials (Username / Password): 47 | * user1 / user1 48 | * user2 / user2 49 | 50 | - Keep a window open on the index and login by user1 51 | - Open a new private/incognito windows of your web browser and login with *user2* 52 | - From this web browser specify *target user* and click the button to send a fake action: **target user** will be notified. 53 | 54 | ### Reference 55 | Special thanks to **azanelli** for your great [example](https://github.com/netgloo/spring-boot-samples/tree/master/spring-boot-web-socket-user-notifications) 56 | -------------------------------------------------------------------------------- /websocket-react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /websocket-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "websocket-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "base-64": "^0.1.0", 7 | "material-ui": "^1.0.0-beta.23", 8 | "react": "^16.2.0", 9 | "react-dom": "^16.2.0", 10 | "react-scripts": "1.0.17", 11 | "react-stomp": "^1.1.0", 12 | "typeface-roboto": "^0.0.45" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test --env=jsdom", 18 | "eject": "react-scripts eject" 19 | }, 20 | "proxy": "http://localhost:9292" 21 | } 22 | -------------------------------------------------------------------------------- /websocket-react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcmgyu/websocket-spring-react/5e5cdb810c6b85381be0e6dc889e5fcfdfcd49e1/websocket-react/public/favicon.ico -------------------------------------------------------------------------------- /websocket-react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /websocket-react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /websocket-react/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | @keyframes App-logo-spin { 26 | from { transform: rotate(0deg); } 27 | to { transform: rotate(360deg); } 28 | } 29 | -------------------------------------------------------------------------------- /websocket-react/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import 'typeface-roboto' 3 | import logo from './logo.svg' 4 | import './App.css' 5 | import LoginFrom from './LoginForm' 6 | import NotificationForm from './NotificationForm' 7 | import base64 from 'base-64' 8 | 9 | class App extends Component { 10 | constructor(props) { 11 | super(props) 12 | this.state = { 13 | is_authenticated: false, 14 | username: '', 15 | password: '', 16 | target: '', 17 | // messages: [] 18 | messages: [], 19 | access_token: '' 20 | } 21 | } 22 | 23 | 24 | 25 | handleSendName() { 26 | this.stompClient.send("/app/hello", {}, JSON.stringify({ 'name': 'Hello' })) 27 | } 28 | 29 | handleChange(key, value) { 30 | this.setState({ 31 | [key]: value, 32 | }) 33 | } 34 | 35 | handleLoginSuccess(json) { 36 | this.setState({ is_authenticated: true, access_token: json.access_token }) 37 | } 38 | 39 | handleLogin() { 40 | const { username, password } = this.state 41 | let headers = new Headers() 42 | headers.append('Authorization', `Basic ${base64.encode('clientapp:123456')}`) 43 | 44 | fetch(`/oauth/token?grant_type=password&username=${username}&password=${password}`, { 45 | method: "post", 46 | headers: headers 47 | }) 48 | .then(response => { 49 | if (!response.ok) { 50 | throw Error(response.statusText); 51 | } 52 | return response.json() 53 | }) 54 | .then((json) => { 55 | this.handleLoginSuccess(json) 56 | }) 57 | .catch(error => { throw error }) 58 | } 59 | 60 | handleSendNotify() { 61 | // const body = { username: this.state.target } 62 | fetch(`/some-action/${this.state.target}?access_token=${this.state.access_token}`, { 63 | headers: { 'content-type': 'application/json' }, 64 | method: 'POST', 65 | // body: JSON.stringify(body) 66 | }) 67 | .then((result) => (result)) 68 | .catch((error) => { throw new Error(error) }); 69 | } 70 | 71 | handleClick() { 72 | 73 | } 74 | 75 | handleMessage(message) { 76 | let { messages } = this.state 77 | this.setState({ messages: [...messages, message.content] }) 78 | } 79 | 80 | render() { 81 | let { is_authenticated, messages, target, access_token } = this.state 82 | 83 | return ( 84 |
85 |
86 | logo 87 |

Welcome to Notification Page

88 |
89 | {is_authenticated ? 90 | : 91 | } 92 |
93 | ) 94 | } 95 | } 96 | 97 | export default App 98 | 99 | 100 | -------------------------------------------------------------------------------- /websocket-react/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /websocket-react/src/LoginForm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types'; 3 | import { withStyles } from 'material-ui/styles' 4 | import TextField from 'material-ui/TextField' 5 | import Button from 'material-ui/Button' 6 | 7 | 8 | const styles = theme => ({ 9 | container: { 10 | display: 'flex', 11 | flexWrap: 'wrap', 12 | }, 13 | textField: { 14 | marginLeft: theme.spacing.unit, 15 | marginRight: theme.spacing.unit, 16 | width: 200, 17 | }, 18 | button: { 19 | margin: theme.spacing.unit * 2, 20 | } 21 | }); 22 | 23 | class LoginForm extends Component { 24 | handleChange = key => event => { 25 | this.props.handleChange(key, event.target.value) 26 | } 27 | 28 | render() { 29 | const { classes, handleLogin, username, password } = this.props 30 | 31 | return ( 32 |
{ 33 | e.preventDefault() 34 | handleLogin() 35 | }}> 36 | 44 | 53 | 56 | 57 | ) 58 | } 59 | } 60 | 61 | LoginForm.propTypes = { 62 | classes: PropTypes.object.isRequired, 63 | } 64 | 65 | export default withStyles(styles)(LoginForm) -------------------------------------------------------------------------------- /websocket-react/src/NotificationForm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { withStyles } from 'material-ui/styles' 4 | import TextField from 'material-ui/TextField' 5 | import Button from 'material-ui/Button' 6 | import Typography from 'material-ui/Typography' 7 | import { SnackbarContent } from 'material-ui/Snackbar' 8 | import SockJsClient from 'react-stomp' 9 | 10 | 11 | const styles = theme => ({ 12 | container: { 13 | display: 'flex', 14 | flexWrap: 'wrap', 15 | }, 16 | textField: { 17 | marginLeft: theme.spacing.unit, 18 | marginRight: theme.spacing.unit, 19 | width: 200, 20 | }, 21 | button: { 22 | margin: theme.spacing.unit * 2, 23 | }, 24 | snackbar: { 25 | margin: theme.spacing.unit, 26 | } 27 | }) 28 | 29 | class LoginForm extends Component { 30 | 31 | constructor(props) { 32 | super(props) 33 | this.state = { 34 | 35 | } 36 | } 37 | 38 | 39 | componentDidMount() { 40 | } 41 | 42 | handleSendNotify(e) { 43 | e.preventDefault(); 44 | this.props.handleSendNotify() 45 | } 46 | 47 | handleChange = key => event => { 48 | this.props.handleChange(key, event.target.value) 49 | } 50 | 51 | 52 | 53 | render() { 54 | const { classes, target, messages, handleMessage, access_token} = this.props 55 | const notification = messages.map((message, index) => ( 56 | 57 | )) 58 | 59 | return ( 60 |
61 |
62 | 70 | 71 | 72 |
73 | {messages.length > 0 ? notification : You have no any message.} 74 |
75 | 76 | handleMessage(message)} /> 80 | 81 |
82 | 83 | ) 84 | } 85 | } 86 | 87 | LoginForm.propTypes = { 88 | classes: PropTypes.object.isRequired, 89 | } 90 | 91 | export default withStyles(styles)(LoginForm) -------------------------------------------------------------------------------- /websocket-react/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /websocket-react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /websocket-react/src/js/sockjs.min.js: -------------------------------------------------------------------------------- 1 | /* sockjs-client v1.1.4 | http://sockjs.org | MIT license */ 2 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.SockJS=t()}}(function(){var t;return function t(e,n,r){function i(s,a){if(!n[s]){if(!e[s]){var l="function"==typeof require&&require;if(!a&&l)return l(s,!0);if(o)return o(s,!0);var u=new Error("Cannot find module '"+s+"'");throw u.code="MODULE_NOT_FOUND",u}var c=n[s]={exports:{}};e[s][0].call(c.exports,function(t){var n=e[s][1][t];return i(n||t)},c,c.exports,t,e,n,r)}return n[s].exports}for(var o="function"==typeof require&&require,s=0;s1?this._listeners[t]=n.slice(0,r).concat(n.slice(r+1)):delete this._listeners[t]):void 0}},r.prototype.dispatchEvent=function(){var t=arguments[0],e=t.type,n=1===arguments.length?[t]:Array.apply(null,arguments);if(this["on"+e]&&this["on"+e].apply(this,n),e in this._listeners)for(var r=this._listeners[e],i=0;i=3e3&&t<=4999}t("./shims");var o,s=t("url-parse"),a=t("inherits"),l=t("json3"),u=t("./utils/random"),c=t("./utils/escape"),f=t("./utils/url"),h=t("./utils/event"),d=t("./utils/transport"),p=t("./utils/object"),v=t("./utils/browser"),m=t("./utils/log"),b=t("./event/event"),y=t("./event/eventtarget"),g=t("./location"),w=t("./event/close"),x=t("./event/trans-message"),_=t("./info-receiver");a(r,y),r.prototype.close=function(t,e){if(t&&!i(t))throw new Error("InvalidAccessError: Invalid code");if(e&&e.length>123)throw new SyntaxError("reason argument has an invalid length");if(this.readyState!==r.CLOSING&&this.readyState!==r.CLOSED){this._close(t||1e3,e||"Normal closure",!0)}},r.prototype.send=function(t){if("string"!=typeof t&&(t=""+t),this.readyState===r.CONNECTING)throw new Error("InvalidStateError: The connection has not been established yet");this.readyState===r.OPEN&&this._transport.send(c.quote(t))},r.version=t("./version"),r.CONNECTING=0,r.OPEN=1,r.CLOSING=2,r.CLOSED=3,r.prototype._receiveInfo=function(t,e){if(this._ir=null,!t)return void this._close(1002,"Cannot connect to server");this._rto=this.countRTO(e),this._transUrl=t.base_url?t.base_url:this.url,t=p.extend(t,this._urlInfo);var n=o.filterToEnabled(this._transportsWhitelist,t);this._transports=n.main,this._transports.length,this._connect()},r.prototype._connect=function(){for(var t=this._transports.shift();t;t=this._transports.shift()){if(t.transportName,t.needBody&&(!n.document.body||void 0!==n.document.readyState&&"complete"!==n.document.readyState&&"interactive"!==n.document.readyState))return this._transports.unshift(t),void h.attachEvent("load",this._connect.bind(this));var e=this._rto*t.roundTrips||5e3;this._transportTimeoutId=setTimeout(this._transportTimeout.bind(this),e);var r=f.addPath(this._transUrl,"/"+this._server+"/"+this._generateSessionId()),i=this._transportOptions[t.transportName],o=new t(r,this._transUrl,i);return o.on("message",this._transportMessage.bind(this)),o.once("close",this._transportClose.bind(this)),o.transportName=t.transportName,void(this._transport=o)}this._close(2e3,"All transports failed",!1)},r.prototype._transportTimeout=function(){this.readyState===r.CONNECTING&&this._transportClose(2007,"Transport timed out")},r.prototype._transportMessage=function(t){var e,n=this,r=t.slice(0,1),i=t.slice(1);switch(r){case"o":return void this._open();case"h":return this.dispatchEvent(new b("heartbeat")),void this.transport}if(i)try{e=l.parse(i)}catch(t){}if(void 0!==e)switch(r){case"a":Array.isArray(e)&&e.forEach(function(t){n.transport,n.dispatchEvent(new x(t))});break;case"m":this.transport,this.dispatchEvent(new x(e));break;case"c":Array.isArray(e)&&2===e.length&&this._close(e[0],e[1],!0)}},r.prototype._transportClose=function(t,e){if(this.transport,this._transport&&(this._transport.removeAllListeners(),this._transport=null,this.transport=null),!i(t)&&2e3!==t&&this.readyState===r.CONNECTING)return void this._connect();this._close(t,e)},r.prototype._open=function(){this._transport.transportName,this.readyState,this.readyState===r.CONNECTING?(this._transportTimeoutId&&(clearTimeout(this._transportTimeoutId),this._transportTimeoutId=null),this.readyState=r.OPEN,this.transport=this._transport.transportName,this.dispatchEvent(new b("open")),this.transport):this._close(1006,"Server lost session")},r.prototype._close=function(t,e,n){this.transport,this.readyState;var i=!1;if(this._ir&&(i=!0,this._ir.close(),this._ir=null),this._transport&&(this._transport.close(),this._transport=null,this.transport=null),this.readyState===r.CLOSED)throw new Error("InvalidStateError: SockJS has already been closed");this.readyState=r.CLOSING,setTimeout(function(){this.readyState=r.CLOSED,i&&this.dispatchEvent(new b("error"));var o=new w("close");o.wasClean=n||!1,o.code=t||1e3,o.reason=e,this.dispatchEvent(o),this.onmessage=this.onclose=this.onerror=null}.bind(this),0)},r.prototype.countRTO=function(t){return t>100?4*t:300+t},e.exports=function(e){return o=d(e),t("./iframe-bootstrap")(r,e),r}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./event/close":2,"./event/event":4,"./event/eventtarget":5,"./event/trans-message":6,"./iframe-bootstrap":8,"./info-receiver":12,"./location":13,"./shims":15,"./utils/browser":44,"./utils/escape":45,"./utils/event":46,"./utils/log":48,"./utils/object":49,"./utils/random":50,"./utils/transport":51,"./utils/url":52,"./version":53,"debug":void 0,"inherits":54,"json3":55,"url-parse":58}],15:[function(t,e,n){"use strict";function r(t){var e=+t;return e!==e?e=0:0!==e&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function i(t){return t>>>0}function o(){}var s,a=Array.prototype,l=Object.prototype,u=Function.prototype,c=String.prototype,f=a.slice,h=l.toString,d=function(t){return"[object Function]"===l.toString.call(t)},p=function(t){return"[object Array]"===h.call(t)},v=function(t){return"[object String]"===h.call(t)},m=Object.defineProperty&&function(){try{return Object.defineProperty({},"x",{}),!0}catch(t){return!1}}();s=m?function(t,e,n,r){!r&&e in t||Object.defineProperty(t,e,{configurable:!0,enumerable:!1,writable:!0,value:n})}:function(t,e,n,r){!r&&e in t||(t[e]=n)};var b=function(t,e,n){for(var r in e)l.hasOwnProperty.call(e,r)&&s(t,r,e[r],n)},y=function(t){if(null==t)throw new TypeError("can't convert "+t+" to object");return Object(t)};b(u,{bind:function(t){var e=this;if(!d(e))throw new TypeError("Function.prototype.bind called on incompatible "+e);for(var n=f.call(arguments,1),r=function(){if(this instanceof l){var r=e.apply(this,n.concat(f.call(arguments)));return Object(r)===r?r:this}return e.apply(t,n.concat(f.call(arguments)))},i=Math.max(0,e.length-n.length),s=[],a=0;a>>0;if(!d(t))throw new TypeError;for(;++i>>0;if(!n)return-1;var i=0;for(arguments.length>1&&(i=r(arguments[1])),i=i>=0?i:Math.max(0,n+i);i1?function(){var t=void 0===/()??/.exec("")[1];c.split=function(e,n){var r=this;if(void 0===e&&0===n)return[];if("[object RegExp]"!==h.call(e))return _.call(this,e,n);var o,s,l,u,c=[],f=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.extended?"x":"")+(e.sticky?"y":""),d=0;for(e=new RegExp(e.source,f+"g"),r+="",t||(o=new RegExp("^"+e.source+"$(?!\\s)",f)),n=void 0===n?-1>>>0:i(n);(s=e.exec(r))&&!((l=s.index+s[0].length)>d&&(c.push(r.slice(d,s.index)),!t&&s.length>1&&s[0].replace(o,function(){for(var t=1;t1&&s.index=n));)e.lastIndex===s.index&&e.lastIndex++;return d===r.length?!u&&e.test("")||c.push(""):c.push(r.slice(d)),c.length>n?c.slice(0,n):c}}():"0".split(void 0,0).length&&(c.split=function(t,e){return void 0===t&&0===e?[]:_.call(this,t,e)});var E=c.substr,j="".substr&&"b"!=="0b".substr(-1);b(c,{substr:function(t,e){return E.call(this,t<0&&(t=this.length+t)<0?0:t,e)}},j)},{}],16:[function(t,e,n){"use strict";e.exports=[t("./transport/websocket"),t("./transport/xhr-streaming"),t("./transport/xdr-streaming"),t("./transport/eventsource"),t("./transport/lib/iframe-wrap")(t("./transport/eventsource")),t("./transport/htmlfile"),t("./transport/lib/iframe-wrap")(t("./transport/htmlfile")),t("./transport/xhr-polling"),t("./transport/xdr-polling"),t("./transport/lib/iframe-wrap")(t("./transport/xhr-polling")),t("./transport/jsonp-polling")]},{"./transport/eventsource":20,"./transport/htmlfile":21,"./transport/jsonp-polling":23,"./transport/lib/iframe-wrap":26,"./transport/websocket":38,"./transport/xdr-polling":39,"./transport/xdr-streaming":40,"./transport/xhr-polling":41,"./transport/xhr-streaming":42}],17:[function(t,e,n){(function(n){"use strict";function r(t,e,n,r){var o=this;i.call(this),setTimeout(function(){o._start(t,e,n,r)},0)}var i=t("events").EventEmitter,o=t("inherits"),s=t("../../utils/event"),a=t("../../utils/url"),l=n.XMLHttpRequest;o(r,i),r.prototype._start=function(t,e,n,i){var o=this;try{this.xhr=new l}catch(t){}if(!this.xhr)return this.emit("finish",0,"no xhr support"),void this._cleanup();e=a.addQuery(e,"t="+ +new Date),this.unloadRef=s.unloadAdd(function(){o._cleanup(!0)});try{this.xhr.open(t,e,!0),this.timeout&&"timeout"in this.xhr&&(this.xhr.timeout=this.timeout,this.xhr.ontimeout=function(){o.emit("finish",0,""),o._cleanup(!1)})}catch(t){return this.emit("finish",0,""),void this._cleanup(!1)}if(i&&i.noCredentials||!r.supportsCORS||(this.xhr.withCredentials="true"),i&&i.headers)for(var u in i.headers)this.xhr.setRequestHeader(u,i.headers[u]);this.xhr.onreadystatechange=function(){if(o.xhr){var t,e,n=o.xhr;switch(n.readyState,n.readyState){case 3:try{e=n.status,t=n.responseText}catch(t){}1223===e&&(e=204),200===e&&t&&t.length>0&&o.emit("chunk",e,t);break;case 4:e=n.status,1223===e&&(e=204),12005!==e&&12029!==e||(e=0),n.responseText,o.emit("finish",e,n.responseText),o._cleanup(!1)}}};try{o.xhr.send(n)}catch(t){o.emit("finish",0,""),o._cleanup(!1)}},r.prototype._cleanup=function(t){if(this.xhr){if(this.removeAllListeners(),s.unloadDel(this.unloadRef),this.xhr.onreadystatechange=function(){},this.xhr.ontimeout&&(this.xhr.ontimeout=null),t)try{this.xhr.abort()}catch(t){}this.unloadRef=this.xhr=null}},r.prototype.close=function(){this._cleanup(!0)},r.enabled=!!l;var u=["Active"].concat("Object").join("X");!r.enabled&&u in n&&(l=function(){try{return new n[u]("Microsoft.XMLHTTP")}catch(t){return null}},r.enabled=!!new l);var c=!1;try{c="withCredentials"in new l}catch(t){}r.supportsCORS=c,e.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../../utils/event":46,"../../utils/url":52,"debug":void 0,"events":3,"inherits":54}],18:[function(t,e,n){(function(t){e.exports=t.EventSource}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],19:[function(t,e,n){(function(t){"use strict";var n=t.WebSocket||t.MozWebSocket;e.exports=n?function(t){return new n(t)}:void 0}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],20:[function(t,e,n){"use strict";function r(t){if(!r.enabled())throw new Error("Transport created when disabled");o.call(this,t,"/eventsource",s,a)}var i=t("inherits"),o=t("./lib/ajax-based"),s=t("./receiver/eventsource"),a=t("./sender/xhr-cors"),l=t("eventsource");i(r,o),r.enabled=function(){return!!l},r.transportName="eventsource",r.roundTrips=2,e.exports=r},{"./lib/ajax-based":24,"./receiver/eventsource":29,"./sender/xhr-cors":35,"eventsource":18,"inherits":54}],21:[function(t,e,n){"use strict";function r(t){if(!o.enabled)throw new Error("Transport created when disabled");a.call(this,t,"/htmlfile",o,s)}var i=t("inherits"),o=t("./receiver/htmlfile"),s=t("./sender/xhr-local"),a=t("./lib/ajax-based");i(r,a),r.enabled=function(t){return o.enabled&&t.sameOrigin},r.transportName="htmlfile",r.roundTrips=2,e.exports=r},{"./lib/ajax-based":24,"./receiver/htmlfile":30,"./sender/xhr-local":37,"inherits":54}],22:[function(t,e,n){"use strict";function r(t,e,n){if(!r.enabled())throw new Error("Transport created when disabled");s.call(this);var i=this;this.origin=l.getOrigin(n),this.baseUrl=n,this.transUrl=e,this.transport=t,this.windowId=f.string(8);var o=l.addPath(n,"/iframe.html")+"#"+this.windowId;this.iframeObj=u.createIframe(o,function(t){i.emit("close",1006,"Unable to load an iframe ("+t+")"),i.close()}),this.onmessageCallback=this._message.bind(this),c.attachEvent("message",this.onmessageCallback)}var i=t("inherits"),o=t("json3"),s=t("events").EventEmitter,a=t("../version"),l=t("../utils/url"),u=t("../utils/iframe"),c=t("../utils/event"),f=t("../utils/random");i(r,s),r.prototype.close=function(){if(this.removeAllListeners(),this.iframeObj){c.detachEvent("message",this.onmessageCallback);try{this.postMessage("c")}catch(t){}this.iframeObj.cleanup(),this.iframeObj=null,this.onmessageCallback=this.iframeObj=null}},r.prototype._message=function(t){if(t.data,!l.isOriginEqual(t.origin,this.origin))return t.origin,void this.origin;var e;try{e=o.parse(t.data)}catch(e){return void t.data}if(e.windowId!==this.windowId)return e.windowId,void this.windowId;switch(e.type){case"s":this.iframeObj.loaded(),this.postMessage("s",o.stringify([a,this.transport,this.transUrl,this.baseUrl]));break;case"t":this.emit("message",e.data);break;case"c":var n;try{n=o.parse(e.data)}catch(t){return void e.data}this.emit("close",n[0],n[1]),this.close()}},r.prototype.postMessage=function(t,e){this.iframeObj.post(o.stringify({windowId:this.windowId,type:t,data:e||""}),this.origin)},r.prototype.send=function(t){this.postMessage("m",t)},r.enabled=function(){return u.iframeEnabled},r.transportName="iframe",r.roundTrips=2,e.exports=r},{"../utils/event":46,"../utils/iframe":47,"../utils/random":50,"../utils/url":52,"../version":53,"debug":void 0,"events":3,"inherits":54,"json3":55}],23:[function(t,e,n){(function(n){"use strict";function r(t){if(!r.enabled())throw new Error("Transport created when disabled");o.call(this,t,"/jsonp",a,s)}var i=t("inherits"),o=t("./lib/sender-receiver"),s=t("./receiver/jsonp"),a=t("./sender/jsonp");i(r,o),r.enabled=function(){return!!n.document},r.transportName="jsonp-polling",r.roundTrips=1,r.needBody=!0,e.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./lib/sender-receiver":28,"./receiver/jsonp":31,"./sender/jsonp":33,"inherits":54}],24:[function(t,e,n){"use strict";function r(t){return function(e,n,r){var i={};"string"==typeof n&&(i.headers={"Content-type":"text/plain"});var o=s.addPath(e,"/xhr_send"),a=new t("POST",o,n,i);return a.once("finish",function(t){if(a=null,200!==t&&204!==t)return r(new Error("http status "+t));r()}),function(){a.close(),a=null;var t=new Error("Aborted");t.code=1e3,r(t)}}}function i(t,e,n,i){a.call(this,t,e,r(i),n,i)}var o=t("inherits"),s=t("../../utils/url"),a=t("./sender-receiver");o(i,a),e.exports=i},{"../../utils/url":52,"./sender-receiver":28,"debug":void 0,"inherits":54}],25:[function(t,e,n){"use strict";function r(t,e){o.call(this),this.sendBuffer=[],this.sender=e,this.url=t}var i=t("inherits"),o=t("events").EventEmitter;i(r,o),r.prototype.send=function(t){this.sendBuffer.push(t),this.sendStop||this.sendSchedule()},r.prototype.sendScheduleWait=function(){var t,e=this;this.sendStop=function(){e.sendStop=null,clearTimeout(t)},t=setTimeout(function(){e.sendStop=null,e.sendSchedule()},25)},r.prototype.sendSchedule=function(){this.sendBuffer.length;var t=this;if(this.sendBuffer.length>0){var e="["+this.sendBuffer.join(",")+"]";this.sendStop=this.sender(this.url,e,function(e){t.sendStop=null,e?(t.emit("close",e.code||1006,"Sending error: "+e),t.close()):t.sendScheduleWait()}),this.sendBuffer=[]}},r.prototype._cleanup=function(){this.removeAllListeners()},r.prototype.close=function(){this._cleanup(),this.sendStop&&(this.sendStop(),this.sendStop=null)},e.exports=r},{"debug":void 0,"events":3,"inherits":54}],26:[function(t,e,n){(function(n){"use strict";var r=t("inherits"),i=t("../iframe"),o=t("../../utils/object");e.exports=function(t){function e(e,n){i.call(this,t.transportName,e,n)}return r(e,i),e.enabled=function(e,r){if(!n.document)return!1;var s=o.extend({},r);return s.sameOrigin=!0,t.enabled(s)&&i.enabled()},e.transportName="iframe-"+t.transportName,e.needBody=!0,e.roundTrips=i.roundTrips+t.roundTrips-1,e.facadeTransport=t,e}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../../utils/object":49,"../iframe":22,"inherits":54}],27:[function(t,e,n){"use strict";function r(t,e,n){o.call(this),this.Receiver=t,this.receiveUrl=e,this.AjaxObject=n,this._scheduleReceiver()}var i=t("inherits"),o=t("events").EventEmitter;i(r,o),r.prototype._scheduleReceiver=function(){var t=this,e=this.poll=new this.Receiver(this.receiveUrl,this.AjaxObject);e.on("message",function(e){t.emit("message",e)}),e.once("close",function(n,r){t.pollIsClosing,t.poll=e=null,t.pollIsClosing||("network"===r?t._scheduleReceiver():(t.emit("close",n||1006,r),t.removeAllListeners()))})},r.prototype.abort=function(){this.removeAllListeners(),this.pollIsClosing=!0,this.poll&&this.poll.abort()},e.exports=r},{"debug":void 0,"events":3,"inherits":54}],28:[function(t,e,n){"use strict";function r(t,e,n,r,i){var l=o.addPath(t,e),u=this;s.call(this,t,n),this.poll=new a(r,l,i),this.poll.on("message",function(t){u.emit("message",t)}),this.poll.once("close",function(t,e){u.poll=null,u.emit("close",t,e),u.close()})}var i=t("inherits"),o=t("../../utils/url"),s=t("./buffered-sender"),a=t("./polling");i(r,s),r.prototype.close=function(){s.prototype.close.call(this),this.removeAllListeners(),this.poll&&(this.poll.abort(),this.poll=null)},e.exports=r},{"../../utils/url":52,"./buffered-sender":25,"./polling":27,"debug":void 0,"inherits":54}],29:[function(t,e,n){"use strict";function r(t){o.call(this);var e=this,n=this.es=new s(t);n.onmessage=function(t){t.data,e.emit("message",decodeURI(t.data))},n.onerror=function(t){n.readyState;var r=2!==n.readyState?"network":"permanent";e._cleanup(),e._close(r)}}var i=t("inherits"),o=t("events").EventEmitter,s=t("eventsource");i(r,o),r.prototype.abort=function(){this._cleanup(),this._close("user")},r.prototype._cleanup=function(){var t=this.es;t&&(t.onmessage=t.onerror=null,t.close(),this.es=null)},r.prototype._close=function(t){var e=this;setTimeout(function(){e.emit("close",null,t),e.removeAllListeners()},200)},e.exports=r},{"debug":void 0,"events":3,"eventsource":18,"inherits":54}],30:[function(t,e,n){(function(n){"use strict";function r(t){a.call(this);var e=this;o.polluteGlobalNamespace(),this.id="a"+l.string(6),t=s.addQuery(t,"c="+decodeURIComponent(o.WPrefix+"."+this.id)),r.htmlfileEnabled;var i=r.htmlfileEnabled?o.createHtmlfile:o.createIframe;n[o.WPrefix][this.id]={start:function(){e.iframeObj.loaded()},message:function(t){e.emit("message",t)},stop:function(){e._cleanup(),e._close("network")}},this.iframeObj=i(t,function(){e._cleanup(),e._close("permanent")})}var i=t("inherits"),o=t("../../utils/iframe"),s=t("../../utils/url"),a=t("events").EventEmitter,l=t("../../utils/random");i(r,a),r.prototype.abort=function(){this._cleanup(),this._close("user")},r.prototype._cleanup=function(){this.iframeObj&&(this.iframeObj.cleanup(),this.iframeObj=null),delete n[o.WPrefix][this.id]},r.prototype._close=function(t){this.emit("close",null,t),this.removeAllListeners()},r.htmlfileEnabled=!1;var u=["Active"].concat("Object").join("X");if(u in n)try{r.htmlfileEnabled=!!new n[u]("htmlfile")}catch(t){}r.enabled=r.htmlfileEnabled||o.iframeEnabled,e.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../../utils/iframe":47,"../../utils/random":50,"../../utils/url":52,"debug":void 0,"events":3,"inherits":54}],31:[function(t,e,n){(function(n){"use strict";function r(t){var e=this;u.call(this),i.polluteGlobalNamespace(),this.id="a"+o.string(6);var s=a.addQuery(t,"c="+encodeURIComponent(i.WPrefix+"."+this.id));n[i.WPrefix][this.id]=this._callback.bind(this),this._createScript(s),this.timeoutId=setTimeout(function(){e._abort(new Error("JSONP script loaded abnormally (timeout)"))},r.timeout)}var i=t("../../utils/iframe"),o=t("../../utils/random"),s=t("../../utils/browser"),a=t("../../utils/url"),l=t("inherits"),u=t("events").EventEmitter;l(r,u),r.prototype.abort=function(){if(n[i.WPrefix][this.id]){var t=new Error("JSONP user aborted read");t.code=1e3,this._abort(t)}},r.timeout=35e3,r.scriptErrorTimeout=1e3,r.prototype._callback=function(t){this._cleanup(),this.aborting||(t&&this.emit("message",t),this.emit("close",null,"network"),this.removeAllListeners())},r.prototype._abort=function(t){this._cleanup(),this.aborting=!0,this.emit("close",t.code,t.message),this.removeAllListeners()},r.prototype._cleanup=function(){if(clearTimeout(this.timeoutId),this.script2&&(this.script2.parentNode.removeChild(this.script2),this.script2=null),this.script){var t=this.script;t.parentNode.removeChild(t),t.onreadystatechange=t.onerror=t.onload=t.onclick=null,this.script=null}delete n[i.WPrefix][this.id]},r.prototype._scriptError=function(){var t=this;this.errorTimer||(this.errorTimer=setTimeout(function(){t.loadedOkay||t._abort(new Error("JSONP script loaded abnormally (onerror)"))},r.scriptErrorTimeout))},r.prototype._createScript=function(t){var e,r=this,i=this.script=n.document.createElement("script");if(i.id="a"+o.string(8),i.src=t,i.type="text/javascript",i.charset="UTF-8",i.onerror=this._scriptError.bind(this),i.onload=function(){ 3 | r._abort(new Error("JSONP script loaded abnormally (onload)"))},i.onreadystatechange=function(){if(i.readyState,/loaded|closed/.test(i.readyState)){if(i&&i.htmlFor&&i.onclick){r.loadedOkay=!0;try{i.onclick()}catch(t){}}i&&r._abort(new Error("JSONP script loaded abnormally (onreadystatechange)"))}},void 0===i.async&&n.document.attachEvent)if(s.isOpera())e=this.script2=n.document.createElement("script"),e.text="try{var a = document.getElementById('"+i.id+"'); if(a)a.onerror();}catch(x){};",i.async=e.async=!1;else{try{i.htmlFor=i.id,i.event="onclick"}catch(t){}i.async=!0}void 0!==i.async&&(i.async=!0);var a=n.document.getElementsByTagName("head")[0];a.insertBefore(i,a.firstChild),e&&a.insertBefore(e,a.firstChild)},e.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../../utils/browser":44,"../../utils/iframe":47,"../../utils/random":50,"../../utils/url":52,"debug":void 0,"events":3,"inherits":54}],32:[function(t,e,n){"use strict";function r(t,e){o.call(this);var n=this;this.bufferPosition=0,this.xo=new e("POST",t,null),this.xo.on("chunk",this._chunkHandler.bind(this)),this.xo.once("finish",function(t,e){n._chunkHandler(t,e),n.xo=null;var r=200===t?"network":"permanent";n.emit("close",null,r),n._cleanup()})}var i=t("inherits"),o=t("events").EventEmitter;i(r,o),r.prototype._chunkHandler=function(t,e){if(200===t&&e)for(var n=-1;;this.bufferPosition+=n+1){var r=e.slice(this.bufferPosition);if(-1===(n=r.indexOf("\n")))break;var i=r.slice(0,n);i&&this.emit("message",i)}},r.prototype._cleanup=function(){this.removeAllListeners()},r.prototype.abort=function(){this.xo&&(this.xo.close(),this.emit("close",null,"user"),this.xo=null),this._cleanup()},e.exports=r},{"debug":void 0,"events":3,"inherits":54}],33:[function(t,e,n){(function(n){"use strict";function r(t){try{return n.document.createElement('