diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..f9ff7be
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,23 @@
+// http://eslint.org/docs/user-guide/configuring
+
+module.exports = {
+ root: true,
+ parser: 'babel-eslint',
+ parserOptions: {
+ sourceType: 'module'
+ },
+ env: {
+ browser: true,
+ },
+ // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
+ extends: 'standard',
+ // add your custom rules here
+ 'rules': {
+ // allow paren-less arrow functions
+ 'arrow-parens': 0,
+ // allow async-await
+ 'generator-star-spacing': 0,
+ // allow debugger during development
+ 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
+ }
+}
diff --git a/README.md b/README.md
index 009af80..8d76d8c 100755
--- a/README.md
+++ b/README.md
@@ -1,36 +1,35 @@
-# Vue-Socket.io
+# vue-native-websocket
-[](https://www.npmjs.com/package/vue-socket.io)
-
-
-
-
-
-socket.io implementation for Vuejs 2 and Vuex
+native websocket implementation for Vuejs 2 and Vuex
## Install
``` bash
-npm install vue-socket.io --save
+yarn add vue-native-websocket
+
+# or
+
+npm install vue-native-websocket --save
```
## Usage
#### Configuration
Automatic socket connection from an URL string
``` js
-import VueSocketio from 'vue-socket.io';
-Vue.use(VueSocketio, 'http://socketserver.com:1923');
+import VueNativeSock from 'vue-native-websocket'
+Vue.use(VueNativeSock, 'ws://localhost:9090')
```
-Bind custom socket.io-client instance
+Enable Vuex integration, where `'./store'` is your local apps store:
``` js
-Vue.use(VueSocketio, socketio('http://socketserver.com:1923'));
+import store from './store'
+Vue.use(VueNativeSock, 'ws://localhost:9090', store)
```
-Enable Vuex integration
+Optionally enable JSON message passing:
``` js
-import store from './yourstore'
-Vue.use(VueSocketio, socketio('http://socketserver.com:1923'), store);
+import store from './store'
+Vue.use(VueNativeSock, 'ws://localhost:9090', store, {format: 'json'})
```
#### On Vuejs instance usage
@@ -67,11 +66,25 @@ delete this.$options.sockets.event_name;
#### Vuex Store integration
-Socket **mutations** always have `SOCKET_` prefix.
+Vuex integration works differently depending on if you've enabled a format
-Socket **actions** always have `socket_` prefix and the socket event name is `camelCased` (ex. `SOCKET_USER_MESSAGE` => `socket_userMessage`)
+##### Without a format enabled
-You can use either one or another or both in your store. Namespaced modules are supported.
+Socket events will commit mutations on the root store corresponding to the following events
+
+`SOCKET_ONOPEN`
+
+`SOCKET_ONCLOSE`
+
+`SOCKET_ONERROR`
+
+`SOCKET_ONMESSAGE`
+
+Each callback is passed the raw websocket event object
+
+Update state in the open, close and error callbacks. You can also check the socket state directly with the `this.$socket` object on the main Vue object.
+
+Handle all the data in the `SOCKET_ONMESSAGE` mutation.
``` js
import Vue from 'vue'
@@ -80,35 +93,50 @@ import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
- state: {
- connect: false,
- message: null
- },
- mutations:{
- SOCKET_CONNECT: (state, status ) => {
- state.connect = true;
- },
- SOCKET_USER_MESSAGE: (state, message) => {
- state.message = message;
- }
- },
- actions: {
- otherAction: (context, type) => {
- return true;
- },
- socket_userMessage: (context, message) => {
- context.dispatch('newMessage', message);
- context.commit('NEW_MESSAGE_RECEIVED', message);
- if (message.is_important) {
- context.dispatch('alertImportantMessage', message);
- }
- ...
- }
+ state: {
+ socket: {
+ isConnected: false,
+ message: '',
}
+ },
+ mutations:{
+ SOCKET_ONOPEN (state, event) {
+ state.socket.isConnected = true
+ },
+ SOCKET_ONCLOSE (state, event) {
+ state.socket.isConnected = false
+ },
+ SOCKET_ONERROR (state, event) {
+ console.error(state, event)
+ },
+ // default handler called for all methods
+ SOCKET_ONMESSAGE (event, message) {
+ state.message = message
+ }
+ }
})
```
-## Example
-[Realtime Car Tracker System](http://metinseylan.com/)
+##### With `format: 'json'` enabled
-[Simple Chat App](http://metinseylan.com/vuesocketio/)
+All data passed through the websocket is expected to be JSON.
+
+Each message is `JSON.parse`d if there is a data (content) response.
+
+If there is no data, the fallback `SOCKET_ON*` mutation is called with the original event data, as above.
+
+If there is a `.namespace` on the data, the message is sent to this `namespaced: true` store (be sure to turn this on in the store module).
+
+If there is a `.mutation` value in the response data, the corresponding mutation is called with the name `SOCKET_[mutation value]`
+
+If there is an `.action` value in the response data, the corresponding action is called with the name `SOCKET_[action value]`
+
+Use the `.sendObj({some: data})` method on the `$socket` object to send stringified json messages.
+
+## Examples
+
+TODO: post your example here!
+
+## Credits
+
+Derived from https://github.com/MetinSeylan/Vue-Socket.io
diff --git a/package.json b/package.json
index bd16cff..aedf4a2 100755
--- a/package.json
+++ b/package.json
@@ -1,40 +1,43 @@
{
- "name": "vue-socket.io",
- "version": "2.1.1-a",
- "description": "socket.io implemantation for vuejs and vuex",
+ "name": "vue-native-websocket",
+ "version": "1.0.0",
+ "description": "native websocket implemantation for vuejs and vuex",
"main": "dist/build.js",
"scripts": {
- "build": "webpack --progress --hide-modules"
+ "build": "eslint --ext .js src && webpack --progress --hide-modules",
+ "lint": "eslint --ext .js src"
},
"repository": {
"type": "git",
- "url": "git+https://github.com/MetinSeylan/Vue-Socket.io.git"
+ "url": "git+https://github.com/nathantsoi/vue-native-websocket.git"
},
"keywords": [
"vuejs",
"socket",
"vue",
- "socket.io",
"websocket",
- "socket.io-client",
"realtime",
"flux",
"vuex",
"redux"
],
- "author": "Metin Seylan",
+ "author": "Nathan Tsoi",
"license": "MIT",
"bugs": {
- "url": "https://github.com/MetinSeylan/Vue-Socket.io/issues"
- },
- "homepage": "https://github.com/MetinSeylan/Vue-Socket.io#readme",
- "dependencies": {
- "socket.io-client": "^1.4.6"
+ "url": "https://github.com/nathantsoi/vue-native-websocket/issues"
},
+ "homepage": "https://github.com/nathantsoi/vue-native-websocket#readme",
"devDependencies": {
- "babel-cli": "^6.11.4",
- "babel-loader": "^6.2.5",
- "babel-preset-es2015": "^6.3.13",
- "webpack": "^2.2.0-rc.3"
+ "babel-cli": "^6.24.1",
+ "babel-eslint": "^7.2.3",
+ "babel-loader": "^7.0.0",
+ "babel-preset-es2015": "^6.24.1",
+ "eslint": "^3.19.0",
+ "eslint-config-standard": "^10.2.1",
+ "eslint-plugin-import": "^2.2.0",
+ "eslint-plugin-node": "^4.2.2",
+ "eslint-plugin-promise": "^3.5.0",
+ "eslint-plugin-standard": "^3.0.1",
+ "webpack": "^2.5.1"
}
}
diff --git a/src/Emitter.js b/src/Emitter.js
index c98984e..f4dfa1c 100755
--- a/src/Emitter.js
+++ b/src/Emitter.js
@@ -1,49 +1,49 @@
-export default new class {
- constructor() {
- this.listeners = new Map();
+class Emitter {
+ constructor () {
+ this.listeners = new Map()
+ }
+
+ addListener (label, callback, vm) {
+ if (typeof callback === 'function') {
+ this.listeners.has(label) || this.listeners.set(label, [])
+ this.listeners.get(label).push({callback: callback, vm: vm})
+ return true
}
+ return false
+ }
- addListener(label, callback, vm) {
- if(typeof callback == 'function'){
- this.listeners.has(label) || this.listeners.set(label, []);
- this.listeners.get(label).push({callback: callback, vm: vm});
+ removeListener (label, callback, vm) {
+ let listeners = this.listeners.get(label)
+ let index
- return true
+ if (listeners && listeners.length) {
+ index = listeners.reduce((i, listener, index) => {
+ if (typeof listener.callback === 'function' && listener.callback === callback && listener.vm === vm) {
+ i = index
}
+ return i
+ }, -1)
- return false
+ if (index > -1) {
+ listeners.splice(index, 1)
+ this.listeners.set(label, listeners)
+ return true
+ }
}
+ return false
+ }
- removeListener(label, callback, vm) {
- let listeners = this.listeners.get(label),
- index;
+ emit (label, ...args) {
+ let listeners = this.listeners.get(label)
- if (listeners && listeners.length) {
- index = listeners.reduce((i, listener, index) => {
- return (typeof listener.callback == 'function' && listener.callback === callback && listener.vm == vm) ?
- i = index :
- i;
- }, -1);
-
- if (index > -1) {
- listeners.splice(index, 1);
- this.listeners.set(label, listeners);
- return true;
- }
- }
- return false;
+ if (listeners && listeners.length) {
+ listeners.forEach((listener) => {
+ listener.callback.call(listener.vm, ...args)
+ })
+ return true
}
+ return false
+ }
+}
- emit(label, ...args) {
- let listeners = this.listeners.get(label);
-
- if (listeners && listeners.length) {
- listeners.forEach((listener) => {
- listener.callback.call(listener.vm,...args)
- });
- return true;
- }
- return false;
- }
-
-}
\ No newline at end of file
+export default new Emitter()
diff --git a/src/Main.js b/src/Main.js
index f19a820..e13fff7 100755
--- a/src/Main.js
+++ b/src/Main.js
@@ -3,50 +3,45 @@ import Emitter from './Emitter'
export default {
- install(Vue, connection, store){
+ install (Vue, connection, store, opts = {}) {
+ if (!connection) { throw new Error('[vue-native-socket] cannot locate connection') }
- if(!connection) throw new Error("[Vue-Socket.io] cannot locate connection")
+ let observer = new Observer(connection, store, opts)
- let observer = new Observer(connection, store)
+ Vue.prototype.$socket = observer.WebSocket
- Vue.prototype.$socket = observer.Socket;
+ Vue.mixin({
+ created () {
+ let sockets = this.$options['sockets']
- Vue.mixin({
- created(){
- let sockets = this.$options['sockets']
-
- this.$options.sockets = new Proxy({}, {
- set: (target, key, value) => {
- Emitter.addListener(key, value, this)
- target[key] = value
- return true;
- },
- deleteProperty: (target, key) => {
- Emitter.removeListener(key, this.$options.sockets[key], this)
- delete target.key;
- return true
- }
- })
-
- if(sockets){
- Object.keys(sockets).forEach((key) => {
- this.$options.sockets[key] = sockets[key];
- });
- }
- },
- beforeDestroy(){
- let sockets = this.$options['sockets']
-
- if(sockets){
- Object.keys(sockets).forEach((key) => {
- delete this.$options.sockets[key]
- });
- }
- }
+ this.$options.sockets = new Proxy({}, {
+ set (target, key, value) {
+ Emitter.addListener(key, value, this)
+ target[key] = value
+ return true
+ },
+ deleteProperty (target, key) {
+ Emitter.removeListener(key, this.$options.sockets[key], this)
+ delete target.key
+ return true
+ }
})
- }
+ if (sockets) {
+ Object.keys(sockets).forEach((key) => {
+ this.$options.sockets[key] = sockets[key]
+ })
+ }
+ },
+ beforeDestroy () {
+ let sockets = this.$options['sockets']
+ if (sockets) {
+ Object.keys(sockets).forEach((key) => {
+ delete this.$options.sockets[key]
+ })
+ }
+ }
+ })
+ }
}
-
-
diff --git a/src/Observer.js b/src/Observer.js
index 7983822..3e36f0e 100755
--- a/src/Observer.js
+++ b/src/Observer.js
@@ -1,59 +1,45 @@
import Emitter from './Emitter'
-import Socket from 'socket.io-client'
-export default class{
-
- constructor(connection, store) {
-
- if(typeof connection == 'string'){
- this.Socket = Socket(connection);
- }else{
- this.Socket = connection
- }
-
- if(store) this.store = store;
-
- this.onEvent()
+export default class {
+ constructor (connectionUrl, store, opts = {}) {
+ this.format = opts.format && opts.format.toLowerCase()
+ this.connect(connectionUrl)
+ if (store) { this.store = store }
+ this.onEvent()
+ }
+ connect (connectionUrl) {
+ this.WebSocket = new WebSocket(connectionUrl)
+ if (this.format === 'json') {
+ if (!('sendObj' in this.WebSocket)) {
+ this.WebSocket.sendObj = (obj) => this.WebSocket.send(JSON.stringify(obj))
+ }
}
+ }
- onEvent(){
- this.Socket.onevent = (packet) => {
- Emitter.emit(packet.data[0], packet.data[1]);
+ onEvent () {
+ ['onmessage', 'onclose', 'onerror', 'onopen'].forEach((eventType) => {
+ this.WebSocket[eventType] = (event) => {
+ Emitter.emit(eventType, event)
+ if (this.store) { this.passToStore('SOCKET_' + eventType, event) }
+ }
+ })
+ }
- if(this.store) this.passToStore('SOCKET_'+packet.data[0], [ ...packet.data.slice(1)])
- };
-
- let _this = this;
-
- ["connect", "error", "disconnect", "reconnect", "reconnect_attempt", "reconnecting", "reconnect_error", "reconnect_failed", "connect_error", "connect_timeout", "connecting", "ping", "pong"]
- .forEach((value) => {
- _this.Socket.on(value, (data) => {
- Emitter.emit(value, data);
- if(_this.store) _this.passToStore('SOCKET_'+value, data)
- })
- })
- }
-
-
- passToStore(event, payload){
- if(!event.startsWith('SOCKET_')) return
-
- for(let namespaced in this.store._mutations) {
- let mutation = namespaced.split('/').pop()
- if(mutation === event.toUpperCase()) this.store.commit(namespaced, payload)
- }
-
- for(let namespaced in this.store._actions) {
- let action = namespaced.split('/').pop()
-
- if(!action.startsWith('socket_')) continue
-
- let camelcased = 'socket_'+event
- .replace('SOCKET_', '')
- .replace(/^([A-Z])|[\W\s_]+(\w)/g, (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase())
-
- if(action === camelcased) this.store.dispatch(namespaced, payload)
- }
+ passToStore (eventName, event) {
+ if (!eventName.startsWith('SOCKET_')) { return }
+ if (this.format === 'json' && event.data) {
+ let msg = JSON.parse(event.data)
+ let target = msg.namespace || ''
+ if (msg.mutation) {
+ this.store.commit([target, msg.mutation].join('/'), msg)
+ }
+ if (msg.action) {
+ this.store.dispatch([target, msg.action].join('/'), msg)
+ }
+ } else {
+ // default mutation
+ this.store.commit(eventName.toUpperCase(), event)
}
+ }
}