├── .gitignore ├── .npmignore ├── README.md ├── examples ├── mvc-example │ ├── README.md │ ├── bin │ │ └── deploy-staging.sh │ ├── broker.js │ ├── config.json.sample │ ├── config │ │ └── controllers.js │ ├── controllers │ │ ├── SessionController.js │ │ └── UsersController.js │ ├── database.json.sample │ ├── gulpfile.js │ ├── init.js │ ├── migrations │ │ └── 20160826050855-add-users.js │ ├── models │ │ ├── Lynchpin.js │ │ ├── Password.js │ │ └── Users.js │ ├── package.json │ ├── public │ │ ├── css │ │ │ └── bootstrap.min.css │ │ ├── images │ │ │ └── loading.gif │ │ └── js │ │ │ ├── bin │ │ │ ├── concat-min.js │ │ │ ├── concat.js │ │ │ └── templates.js │ │ │ ├── lib │ │ │ ├── socketcluster.js │ │ │ ├── validate.min.js │ │ │ ├── vue-router.min.js │ │ │ ├── vue-socketcluster.js │ │ │ ├── vue-socketcluster.min.js │ │ │ ├── vue-strap.min.js │ │ │ └── vue.min.js │ │ │ └── src │ │ │ ├── components │ │ │ ├── about │ │ │ │ └── index.js │ │ │ ├── dashboard │ │ │ │ └── index.js │ │ │ ├── session │ │ │ │ └── create.js │ │ │ └── users │ │ │ │ └── create.js │ │ │ └── system │ │ │ ├── app.js │ │ │ └── init.js │ ├── server.js │ ├── templates │ │ ├── about │ │ │ └── index.jade │ │ ├── dashboard │ │ │ └── index.jade │ │ ├── session │ │ │ └── create.jade │ │ └── users │ │ │ └── create.jade │ ├── views │ │ └── home │ │ │ └── index.jade │ └── worker.js ├── sc-crud-mysql │ ├── README.md │ ├── bin │ │ └── deploy-staging.sh │ ├── broker.js │ ├── config.json.sample │ ├── config │ │ └── controllers.js │ ├── controllers │ │ ├── SessionController.js │ │ └── UsersController.js │ ├── database.json.sample │ ├── gulpfile.js │ ├── init.js │ ├── migrations │ │ └── 20160826050855-add-users.js │ ├── models │ │ ├── Lynchpin.js │ │ ├── Password.js │ │ └── Users.js │ ├── package.json │ ├── public │ │ ├── css │ │ │ └── bootstrap.min.css │ │ ├── images │ │ │ └── loading.gif │ │ └── js │ │ │ ├── bin │ │ │ ├── concat-min.js │ │ │ ├── concat.js │ │ │ └── templates.js │ │ │ ├── lib │ │ │ ├── socketcluster.js │ │ │ ├── validate.min.js │ │ │ ├── vue-router.min.js │ │ │ ├── vue-socketcluster.js │ │ │ ├── vue-socketcluster.min.js │ │ │ ├── vue-strap.min.js │ │ │ └── vue.min.js │ │ │ └── src │ │ │ ├── components │ │ │ ├── about │ │ │ │ └── index.js │ │ │ ├── dashboard │ │ │ │ └── index.js │ │ │ ├── session │ │ │ │ └── create.js │ │ │ └── users │ │ │ │ ├── create.js │ │ │ │ └── index.js │ │ │ └── system │ │ │ ├── app.js │ │ │ └── init.js │ ├── server.js │ ├── templates │ │ ├── about │ │ │ └── index.jade │ │ ├── dashboard │ │ │ └── index.jade │ │ ├── session │ │ │ └── create.jade │ │ └── users │ │ │ ├── create.jade │ │ │ └── index.jade │ ├── views │ │ └── home │ │ │ └── index.jade │ └── worker.js └── simple-example │ ├── README.md │ ├── broker.js │ ├── config.json │ ├── init.js │ ├── package.json │ ├── public │ ├── css │ │ └── bootstrap.min.css │ └── js │ │ ├── bin │ │ └── templates.js │ │ ├── lib │ │ ├── require.min.js │ │ ├── socketcluster.js │ │ ├── vue-router.min.js │ │ ├── vue-socketcluster-with-socketcluster-client.min.js │ │ ├── vue-socketcluster.js │ │ ├── vue-socketcluster.min.js │ │ ├── vue-strap.min.js │ │ └── vue.min.js │ │ └── src │ │ └── system │ │ └── app.js │ ├── puglatizer-1.0.3.tgz │ ├── server.js │ ├── templates.js │ ├── templates │ └── session │ │ └── create.pug │ ├── views │ ├── home │ │ └── index.pug │ └── pug.pug │ └── worker.js ├── package.json ├── vue-socketcluster.js └── vue-socketcluster.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | examples/mvc-example/node_modules/ 2 | examples/mvc-example/config.json 3 | examples/mvc-example/database.json 4 | examples/simple-example/node_modules/ 5 | examples/sc-crud-mysql/node_modules/ 6 | examples/sc-crud-mysql/config.json 7 | examples/sc-crud-mysql/database.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples/mvc-example/node_modules/ 2 | examples/mvc-example/config.json 3 | examples/mvc-example/database.json 4 | examples/simple-example/node_modules/ 5 | examples/sc-crud-mysql/node_modules/ 6 | examples/sc-crud-mysql/config.json 7 | examples/sc-crud-mysql/database.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue-Socketcluster.io 2 | Socketcluster implementation for VueJS 3 | 4 | ## Change Log 5 | 6 | ### 1.1.0 7 | 8 | Added a new channel set so you can subscribe to channels as well. The following are some examples 9 | ```js 10 | sockets:{ 11 | connect(status) { 12 | var vm = this 13 | 14 | vm.authenticated = status.isAuthenticated 15 | }, 16 | authenticate() { 17 | this.authenticated = true 18 | this.setUserData() 19 | }, 20 | deauthenticate() { 21 | this.authenticated = false 22 | } 23 | }, 24 | subscriptions:{ 25 | 'crud>create':function(data) { 26 | console.log(data) 27 | } 28 | } 29 | ``` 30 | 31 | ### 1.0.0 32 | 33 | ``` js 34 | MVC-Example ( See MVC-Example README.md for more details ) 35 | - Scripts 36 | 1) Added NPM scripts, minify, and so on 37 | 38 | - Server-Side 39 | 1) Added an ORM class (Called Lynchpin) that can be extended with any model 40 | 2) Added lots of extended functions for the ORM 41 | 3) Completed the basic MVC example. It now has login, user creation, and basic SPA. 42 | 43 | ``` 44 | 45 | ## Installation 46 | ``` bash 47 | npm install gulp templatizer@1.5.4 -g 48 | ``` 49 | 50 | ## How to use examples 51 | 52 | ### 1) Copy contents of folder to your machine 53 | ### 2) install modules 54 | ``` bash 55 | npm i 56 | ``` 57 | ### 3) copy config.json.sample and database.json.sample to config.json and database.json respectivily (only in mvc-example) 58 | ### 4) Run application 59 | #### simple-example 60 | ``` bash 61 | node server.js 62 | ``` 63 | #### mvc-example 64 | ``` bash 65 | npm start 66 | ``` 67 | ### 5) Navigate to localhost:3000 in your browser 68 | 69 | ## Usage 70 | 71 | ``` html 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ``` 83 | 84 | ## Basic Vue 85 | ``` js 86 | Vue.use(VueSocketcluster) 87 | 88 | var vue = new Vue({ 89 | el:'#app', 90 | data() { 91 | return { 92 | 93 | } 94 | }, 95 | sockets:{ 96 | connect() { 97 | console.log('Connected to server!') 98 | 99 | }, 100 | ping() { 101 | this.$sc.emit('pong') 102 | } 103 | } 104 | }) 105 | ``` 106 | 107 | ## Vue Router 108 | ``` js 109 | var router = new VueRouter() 110 | 111 | Vue.use(VueRouter) 112 | Vue.use(VueSocketcluster) 113 | 114 | router.map({ 115 | '/session/create': { 116 | component:Vue.extend({ 117 | template: '

Session Create

' 118 | }),auth:false 119 | } 120 | }) 121 | 122 | router.redirect({ 123 | '*':'/session/create' 124 | }) 125 | 126 | router.beforeEach(function(transition) { 127 | router.app.loading = true 128 | if (transition.to.auth && !router.app.authenticated) { 129 | router.app.loading = false 130 | transition.redirect('/session/create') 131 | } else if (router.app.authenticated && !transition.to.auth) { 132 | router.app.loading = false 133 | transition.redirect('/') 134 | } else { 135 | transition.next() 136 | } 137 | }) 138 | 139 | router.afterEach(function(transition) { 140 | setTimeout(function() { 141 | router.app.loading = false 142 | },20) 143 | }) 144 | 145 | router.start(Vue.extend({ 146 | data() { 147 | return { 148 | loading:true, 149 | authenticated:false 150 | } 151 | }, 152 | sockets:{ 153 | connect() { 154 | console.log('Connected to server!') 155 | 156 | var watcher = this.$sc.subscribe('broadcast') 157 | watcher.watch(function(data) { 158 | console.log(data) 159 | }) 160 | 161 | }, 162 | ping() { 163 | this.$sc.emit('pong') 164 | this.$sc.emit('ping-with-response',{message:'Hello server!'},function(err,response) { 165 | console.log(response) 166 | }) 167 | } 168 | } 169 | }), '#app') 170 | ``` 171 | 172 | # Server-side 173 | ``` js 174 | scServer.on('connection', function (socket) { 175 | console.log(' >> Client',socket.id,'connected at',new Date()) 176 | 177 | console.log('ping sent to',socket.id) 178 | socket.emit('ping') 179 | 180 | socket.on('pong',function() { 181 | console.log('pong received from',socket.id) 182 | }) 183 | 184 | socket.on('ping-with-response',function(data,respond) { 185 | console.log(data) 186 | worker.exchange.publish('broadcast',{message:'Hello from broadcast!'}) 187 | respond(null,{message:'responding..'}) 188 | }) 189 | }) 190 | ``` 191 | 192 | ## Vue-Socketcluster - Config 193 | ``` js 194 | Vue.use(VueSocketcluster,{ 195 | hostname: String - Defaults to the current host (ready from the URL). 196 | secure: Boolean - Defaults to false 197 | port: Number - Defaults to 80 if !secure otherwise defaults to 443. 198 | path: String - The URL which SC uses to make the initial handshake for the WebSocket. Defaults to '/socketcluster/'. 199 | query: Object - A map of key-value pairs which will be used as query parameters for the initial HTTP handshake which will initiate the WebSocket connection. 200 | ackTimeout: Number (milliseconds) - This is the timeout for getting a response to a SCSocket emit event (when a callback is provided). 201 | autoReconnect: Boolean - Whether or not to automatically reconnect the socket when it loses the connection. 202 | autoReconnectOptions: Object - Valid properties are: initialDelay (milliseconds), randomness (milliseconds), multiplier (decimal; default is 1.5) and maxDelay (milliseconds). 203 | multiplex: Boolean - Defaults to true; multiplexing allows you to reuse a socket instead of creating a second socket to the same address. 204 | timestampRequests: Boolean - Whether or not to add a timestamp to the WebSocket handshake request. 205 | timestampParam: String - The query parameter name to use to hold the timestamp. 206 | authEngine: Object - A custom engine to use for storing and loading JWT auth tokens on the client side. 207 | authTokenName: String - The name of the JWT auth token (provided to the authEngine - By default this is the localStorage variable name); defaults to 'socketCluster.authToken'. 208 | binaryType: String - The type to use to represent binary on the client. Defaults to 'arraybuffer'. 209 | rejectUnauthorized: Boolean - Set this to false during debugging - Otherwise client connection will fail when using self-signed certificates. 210 | }) 211 | ``` 212 | ### Example 213 | ``` js 214 | Vue.use(VueSocketcluster,{ 215 | hostname: 'securedomain.com', 216 | secure: true, 217 | port: 443, 218 | rejectUnauthorized: false // Only necessary during debug if using a self-signed certificate 219 | }) 220 | ``` 221 | 222 | ## Vue-Socketcluster - Sockets attribute 223 | The sockets attributes are essentially channels. If you name an attribute test and then emit to test, the function associated with test will fire. 224 | 225 | ## Calling from a component 226 | ``` js 227 | Vue.component('my-component',Vue.extend({ 228 | data() { 229 | return { 230 | 231 | } 232 | }, 233 | ready() { 234 | this.$root.$sc.emit('pong') 235 | } 236 | } 237 | })) 238 | ``` -------------------------------------------------------------------------------- /examples/mvc-example/README.md: -------------------------------------------------------------------------------- 1 | MVC-Example 2 | ====== 3 | 4 | This will be filled out once I am done building the system. -------------------------------------------------------------------------------- /examples/mvc-example/bin/deploy-staging.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit # Exit on error 4 | 5 | git stash save 'Before Deploy' 6 | git checkout deploy 7 | git merge staging --no-edit 8 | npm run build 9 | if $(git commit -am Deploy); 10 | echo 'Changes committed' 11 | fi 12 | git push origin deploy 13 | git checkout staging 14 | git stash pop -------------------------------------------------------------------------------- /examples/mvc-example/broker.js: -------------------------------------------------------------------------------- 1 | module.exports.run = function (broker) { 2 | console.log(' >> Broker PID:', process.pid) 3 | 4 | } 5 | -------------------------------------------------------------------------------- /examples/mvc-example/config.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test APP", 3 | "port":3000, 4 | 5 | "db":{ 6 | "host":"localhost", 7 | "user":"", 8 | "password":"", 9 | "database":"mvc_example" 10 | } 11 | } -------------------------------------------------------------------------------- /examples/mvc-example/config/controllers.js: -------------------------------------------------------------------------------- 1 | 2 | var controllers = { 3 | SessionController:require('../controllers/SessionController.js'), 4 | UsersController:require('../controllers/UsersController.js') 5 | } 6 | 7 | module.exports = { 8 | controllers:controllers, 9 | register(socket) { 10 | 11 | function buildRoute(key) { 12 | socket.on(controllers[key].route,function(data,respond) { 13 | if (typeof data != 'object' || !data.method) return respond('Request must be an object and include a method.') 14 | var method = data.method 15 | if (controllers[key].hasOwnProperty(method)) { 16 | delete data.method 17 | controllers[key][method](data,respond,socket) 18 | } else { 19 | return respond('Method ' + method + ' is not registered') 20 | } 21 | }) 22 | } 23 | 24 | for (key in controllers) { 25 | if (controllers.hasOwnProperty(key) && controllers[key].route) { 26 | buildRoute(key) 27 | } 28 | } 29 | 30 | } 31 | } -------------------------------------------------------------------------------- /examples/mvc-example/controllers/SessionController.js: -------------------------------------------------------------------------------- 1 | var SessionController = {} 2 | async = require('async'), 3 | Users = require('../models/Users'), 4 | Password = require('../models/Password') 5 | 6 | SessionController.route = 'session' 7 | 8 | SessionController.store = function(data,respond,socket) { 9 | if (!data.email || !data.password) { return respond('You must enter an email and a password.') } 10 | var results = {} 11 | 12 | async.series([ 13 | function(cb) { 14 | Users.findBy({ email:data.email },function(err,users) { 15 | if (err) { return respond(err) } 16 | else if (!users.length) { return respond('There is no account with that email.') } 17 | else { 18 | results.user = users[0] 19 | return cb() 20 | } 21 | }) 22 | }, 23 | function(cb) { 24 | if ( Password.verify(data.password,results.user.password) ) return cb() 25 | else return respond('Invalid password.') 26 | } 27 | ],function() { 28 | delete results.user.password 29 | socket.setAuthToken(results.user) 30 | respond(null,results.user) 31 | }) 32 | } 33 | 34 | SessionController.destroy = function(data,respond,socket) { 35 | socket.deauthenticate() 36 | return respond(null) 37 | } 38 | 39 | module.exports = SessionController -------------------------------------------------------------------------------- /examples/mvc-example/controllers/UsersController.js: -------------------------------------------------------------------------------- 1 | var UsersController = {} 2 | async = require('async'), 3 | Users = require('../models/Users'), 4 | Password = require('../models/Password') 5 | 6 | UsersController.route = 'users' 7 | 8 | UsersController.store = function(data,respond) { 9 | if (!data.user) { return respond('You must pass in a user object.') } 10 | 11 | Password.hash(data.user.password,function(err,hash) { 12 | if (err) { return respond(err) } 13 | data.user.password = hash 14 | 15 | Users.store(data.user,function(err,new_user) { 16 | if (err) { return respond(err) } 17 | delete new_user.password 18 | return respond(null,new_user) 19 | }) 20 | 21 | }) 22 | } 23 | 24 | module.exports = UsersController -------------------------------------------------------------------------------- /examples/mvc-example/database.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "dev": { 3 | "host": "localhost", 4 | "user": "", 5 | "password" : "", 6 | "database": "mvc_example", 7 | "driver": "mysql", 8 | "multipleStatements": true 9 | } 10 | } -------------------------------------------------------------------------------- /examples/mvc-example/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | 3 | gulp.task('default',['minify']) 4 | 5 | gulp.task('minify',function(cb) { 6 | return gulp.src([ 7 | 'public/js/src/system/init.js', 8 | 'public/js/src/components/**/*.js', 9 | 'public/js/src/system/app.js' 10 | ]) 11 | .pipe(require('gulp-babel')({ 12 | presets:['es2015'] 13 | })) 14 | .pipe(require('gulp-concat')('concat.js',{ 15 | newLine:'\n;' 16 | })) 17 | .pipe(require('gulp-uglify')()) 18 | .pipe(require('gulp-minify')()) 19 | .pipe(gulp.dest('public/js/bin')) 20 | }) -------------------------------------------------------------------------------- /examples/mvc-example/init.js: -------------------------------------------------------------------------------- 1 | module.exports.run = function(thisProcess) { 2 | 3 | } -------------------------------------------------------------------------------- /examples/mvc-example/migrations/20160826050855-add-users.js: -------------------------------------------------------------------------------- 1 | var dbm = global.dbm || require('db-migrate') 2 | var type = dbm.dataType 3 | var async = require('async') 4 | exports.up = function(db, callback) { 5 | async.series([ 6 | db.createTable.bind(db,'users', { 7 | id: { type: "int", primaryKey:true, autoIncrement: true, notNull: true }, 8 | first: { type: "string", length:100 }, 9 | last: { type: "string", length:100 }, 10 | email: { type: "string", length:100 }, 11 | password: { type: "string", length:100 }, 12 | type_id: { type: "int", length:11, defaultValue:1 }, 13 | reset_pass: { type: "string", length:100 }, 14 | visible: { type: "smallint", length:1, defaultValue:1 } 15 | },function (err) { 16 | if (err) { callback(err);return } 17 | db.connection.query([ 18 | 'ALTER TABLE users', 19 | 'ADD updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP', 20 | 'ON UPDATE CURRENT_TIMESTAMP,', 21 | 'ADD created_at timestamp NOT NULL' 22 | ].join(' '),function (err) { 23 | if (err) { callback(err);return } 24 | db.connection.query([ 25 | 'CREATE TRIGGER users_insert', 26 | 'BEFORE INSERT ON users FOR EACH ROW SET NEW.created_at = CURRENT_TIMESTAMP' 27 | ].join(' '),callback) 28 | }) 29 | }) 30 | ], callback) 31 | } 32 | 33 | exports.down = function(db, callback) { 34 | async.series([ 35 | db.dropTable.bind(db,'users') 36 | ], callback) 37 | } -------------------------------------------------------------------------------- /examples/mvc-example/models/Lynchpin.js: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Database ORM 3 | */ 4 | 5 | var Lynchpin = function(options) { 6 | var self = this 7 | 8 | // Set some default values 9 | var default_options = { 10 | host:'localhost', 11 | user:'root', 12 | password:'password', 13 | database:'mysql' 14 | } 15 | 16 | // Overwrite the default values with passed in values 17 | if (typeof options == 'object') { 18 | for (var key in options) { 19 | if (options.hasOwnProperty(key)) { 20 | default_options[key] = options[key] 21 | } 22 | } 23 | } 24 | 25 | // We grab a MySQL pool instance 26 | self._pool = require('mysql').createPool(default_options) 27 | } 28 | 29 | /* 30 | * Index 31 | * Gets all records from a table. 32 | */ 33 | Lynchpin.prototype.index = function(respond) { 34 | var self = this 35 | self._pool.query('SELECT * FROM ??',[self._table],function(err,rows) { 36 | if (err) { return respond(err) } 37 | return respond(null, (rows.length ? rows[0] : null) ) 38 | }) 39 | } 40 | 41 | 42 | /* 43 | * Find 44 | * Find a record from a table. 45 | */ 46 | Lynchpin.prototype.find = function(id,respond) { 47 | var self = this 48 | self._pool.query('SELECT * FROM ?? WHERE id = ?',[self._table,id],function(err,rows) { 49 | if (err) { return respond(err) } 50 | return respond(null, (rows.length ? rows[0] : null) ) 51 | }) 52 | } 53 | 54 | /* 55 | * FindBy 56 | * Gets all records from a table matching the passed in fields. 57 | */ 58 | Lynchpin.prototype.findBy = function(fields,respond) { 59 | var self = this 60 | var qry = 'SELECT * FROM ??', 61 | params = [self._table], 62 | i = 0 63 | for (var key in fields) { 64 | if (i > 0) { qry += ' AND ' + key + ' = ?' } 65 | else { qry += ' WHERE ' + key + ' = ?' } 66 | params.push(fields[key]) 67 | i++ 68 | } 69 | self._pool.query(qry,params,function(err,rows) { 70 | if (err) { return respond(err) } 71 | return respond(null,rows) 72 | }) 73 | } 74 | 75 | /* 76 | * FindByAnd 77 | * Duplicate of FindBy. 78 | */ 79 | Lynchpin.prototype.findByAnd = function(fields,respond) { 80 | var self = this 81 | var qry = 'SELECT * FROM ??', 82 | params = [self._table], 83 | i = 0 84 | for (var key in fields) { 85 | if (i > 0) { qry += ' AND ' + key + ' = ?' } 86 | else { qry += ' WHERE ' + key + ' = ?' } 87 | params.push(fields[key]) 88 | i++ 89 | } 90 | self._pool.query(qry,params,function(err,rows) { 91 | if (err) { return respond(err) } 92 | return respond(null,rows) 93 | }) 94 | } 95 | 96 | /* 97 | * FindByOr 98 | * Same as FindBy but it uses OR instead of AND. 99 | */ 100 | Lynchpin.prototype.findByOr = function(fields,respond) { 101 | var self = this 102 | var qry = 'SELECT * FROM ??', 103 | params = [self._table], 104 | i = 0 105 | for (var key in fields) { 106 | if (i > 0) { qry += ' OR ' + key + ' = ?' } 107 | else { qry += ' WHERE ' + key + ' = ?' } 108 | params.push(fields[key]) 109 | i++ 110 | } 111 | self._pool.query(qry,params,function(err,rows) { 112 | if (err) { return respond(err) } 113 | return respond(null,rows) 114 | }) 115 | } 116 | 117 | /* 118 | * Store 119 | * Store a record and return it after completion. 120 | */ 121 | Lynchpin.prototype.store = function(post,respond) { 122 | var self = this 123 | self._pool.query('INSERT INTO ?? SET ?',[self._table,post],function(err,rows) { 124 | if (err) { return respond(err) } 125 | self.find(rows.insertId,respond) 126 | }) 127 | } 128 | 129 | // Locate record if not found create it and return it 130 | Lynchpin.prototype.firstOrCreate = function(fields,respond) { 131 | var self = this 132 | self.findBy(fields,function(err,records) { 133 | if (err) { return respond(err) } 134 | else if (rows.length) { return respond(null,records[0]) } 135 | else { 136 | self.store(fields,function(err2,new_obj) { 137 | if (err) { return respond(err2) } 138 | self.find(new_obj.id,respond) 139 | }) 140 | } 141 | }) 142 | } 143 | 144 | // // Locate record if not found NOT create it and return it 145 | // Lynchpin.prototype.firstOrNew = function(respond) { 146 | 147 | // } 148 | 149 | /* 150 | * Update 151 | * Update a record and return it after completion. 152 | */ 153 | Lynchpin.prototype.update = function(id,put,respond) { 154 | var self = this 155 | self._pool.query('UPDATE ?? SET ? WHERE id = ?',[self._table,put,id],function(err,rows) { 156 | if (err) { return respond(err) } 157 | self.find(id,respond) 158 | }) 159 | } 160 | 161 | /* 162 | * Destroy 163 | * Destroy a record. 164 | */ 165 | Lynchpin.prototype.destroy = function(id,respond) { 166 | var self = this 167 | self._pool.query('DELETE FROM ?? WHERE id = ?',[self._table,id],function(err,rows) { 168 | if (err) { return respond(err) } 169 | return respond(null) 170 | }) 171 | } 172 | 173 | module.exports = Lynchpin -------------------------------------------------------------------------------- /examples/mvc-example/models/Password.js: -------------------------------------------------------------------------------- 1 | var bcrypt = require('bcrypt') 2 | var Password = function() { 3 | var self = this 4 | } 5 | 6 | Password.prototype.hash = function(pwd,respond) { 7 | if (!pwd || typeof pwd != 'string') { return respond('Password must be a valid string.') } 8 | return respond(null,bcrypt.hashSync(pwd,bcrypt.genSaltSync(10))) 9 | } 10 | 11 | Password.prototype.verify = function(pwd,hash) { 12 | return bcrypt.compareSync(pwd,hash) 13 | } 14 | 15 | module.exports = new Password() -------------------------------------------------------------------------------- /examples/mvc-example/models/Users.js: -------------------------------------------------------------------------------- 1 | var Lynchpin = require('./Lynchpin') 2 | 3 | var Users = function() { 4 | var self = this 5 | 6 | self._init() 7 | } 8 | 9 | Users.prototype = Object.create(Lynchpin.prototype) 10 | 11 | Users.prototype._init = function() { 12 | var self = this 13 | 14 | self._table = 'users' 15 | Lynchpin.call(self,require('../config.json').db) 16 | } 17 | 18 | module.exports = new Users() -------------------------------------------------------------------------------- /examples/mvc-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mvc-example", 3 | "description": "A sample Vue-Socketcluster app", 4 | "version": "1.1.0", 5 | "contributors": [ 6 | { 7 | "name": "Nick Kotenberg", 8 | "email": "nkotenberg@happilymarrieddad@yahoo.com" 9 | }, 10 | { 11 | "name": "Jonathan Gros-Dubois", 12 | "email": "grosjona@yahoo.com.au" 13 | } 14 | ], 15 | "scripts": { 16 | "start": "npm run build && node server.js", 17 | "start:watch" : "watch 'npm run build' && node server.js", 18 | "test": "npm run build", 19 | "build": "npm run build-jade & npm run build-js", 20 | "build-jade": "templatizer -d /templates -o public/js/bin/templates.js", 21 | "build-js": "gulp", 22 | "deploy": "./bin/deploy-staging.sh", 23 | "preinstall": "sudo npm install gulp templatizer@1.5.4 watch -g", 24 | "postinstall": "npm run build" 25 | }, 26 | "dependencies": { 27 | "async": "^2.0.1", 28 | "babel-preset-es2015": "^6.14.0", 29 | "babel-preset-react": "^6.11.1", 30 | "bcrypt": "^0.8.7", 31 | "connect": "3.0.1", 32 | "express": "4.13.1", 33 | "gulp": "^3.9.1", 34 | "gulp-babel": "^6.1.2", 35 | "gulp-concat": "^2.6.0", 36 | "gulp-minify": "0.0.14", 37 | "gulp-uglify": "^2.0.0", 38 | "jade": "^1.11.0", 39 | "minimist": "1.1.0", 40 | "mysql": "^2.11.1", 41 | "npm-watch": "^0.1.6", 42 | "sc-cluster-broker-client": "1.x.x", 43 | "serve-static": "1.8.0", 44 | "socket.io": "^1.4.8", 45 | "socketcluster": "5.x.x", 46 | "socketcluster-client": "5.x.x", 47 | "templatizer": "^1.5.4", 48 | "vue-socket.io": "^1.0.2" 49 | }, 50 | "devDependencies": { 51 | "babel-preset-es2015": "^6.14.0", 52 | "babel-preset-react": "^6.11.1", 53 | "gulp": "^3.9.1", 54 | "gulp-babel": "^6.1.2", 55 | "gulp-concat": "^2.6.0", 56 | "gulp-minify": "0.0.14", 57 | "gulp-uglify": "^2.0.0", 58 | "templatizer": "^1.5.4" 59 | }, 60 | "keywords": [ 61 | "websocket", 62 | "server", 63 | "realtime", 64 | "cluster", 65 | "scalable" 66 | ], 67 | "readmeFilename": "README.md" 68 | } 69 | -------------------------------------------------------------------------------- /examples/mvc-example/public/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happilymarrieddad/vue-socketcluster/11224796b6528362dc5d893ba4c9f021e41889ef/examples/mvc-example/public/images/loading.gif -------------------------------------------------------------------------------- /examples/mvc-example/public/js/bin/concat-min.js: -------------------------------------------------------------------------------- 1 | "use strict";var router=new VueRouter,aboutComponent=Vue.extend({template:templatizer.about.index({}),data:function(){return{ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(a){a?(e.$root.alert(a,"error"),router.go("/")):(e.ready=!0,t.next())})},waitForData:!0}}),dashboardComponent=Vue.extend({template:templatizer.dashboard.index({}),data:function(){return{ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(a){a?(e.$root.alert(a,"error"),router.go("/")):(e.ready=!0,t.next())})},waitForData:!0}}),sessionComponent=Vue.extend({template:templatizer.session.create({}),data:function(){return{email:"",password:"",ready:!1}},methods:{login:function(){var t=this;validate.async({email:t.email,password:t.password},{email:{presence:!0},password:{presence:!0}}).then(function(e){t.$root.$sc.emit("session",{method:"store",email:e.email,password:e.password},function(e){if(e)return console.log(e),t.$root.alert(e,"error")})},function(e){var a=null;for(var r in e){a=e[r][0];break}t.$root.alert(a,"error")})}},ready:function(){},route:{data:function(t){var e=this;e.ready=!0,t.next()}}}),usersComponent=Vue.extend({template:templatizer.users.create({}),data:function(){return{first:"",last:"",email:"",password:"",ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)},clearFields:function(){this.first="",this.last="",this.email="",this.password=""},create:function(){var t=this;validate.async({first:t.first,last:t.last,email:t.email,password:t.password,visible:1},{first:{presence:!0},last:{presence:!0},email:{presence:!0},password:{presence:!0}}).then(function(e){t.$root.$sc.emit("users",{method:"store",user:e},function(e,a){return e?t.$root.alert(e,"error"):(t.$root.alert("Successfully created account "+a.email+"! Please log in.","success"),void router.go({path:"/session/create"}))})},function(e){var a=null;for(var r in e){a=e[r][0];break}t.$root.alert(a,"error")})}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(a){a?t.abort():(e.ready=!0,e.clearFields(),t.next())})},waitForData:!0}});Vue.use(VueRouter),Vue.use(VueSocketcluster),router.map({"/dashboard":{component:dashboardComponent,auth:!0},"/about":{component:aboutComponent,auth:!0},"/session/create":{component:sessionComponent,auth:!1},"/users/create":{component:usersComponent,auth:!1}}),router.redirect({"*":"/dashboard"}),router.beforeEach(function(t){router.app.loading=!0,t.to.auth&&"authenticated"!=router.app.$sc.authState?(router.app.loading=!1,t.redirect("/session/create")):"authenticated"!=router.app.$sc.authState||t.to.auth?t.next():(router.app.loading=!1,t.redirect("/dashboard"))}),router.afterEach(function(t){setTimeout(function(){router.app.loading=!1},router.app.loading_delay)}),router.start(Vue.extend({data:function(){return{loading_delay:20,show_success:!1,success_msg:"",show_error:!1,error_msg:"",started:!1,loading:!0,authenticated:!1,first:"",last:"",email:""}},watch:{authenticated:function(t,e){t?router.go({path:"/dashboard"}):router.go({path:"/session/create"})}},methods:{setUserData:function(){var t=this,e=t.$sc.getAuthToken()||{};t.first=e.first,t.last=e.last,t.email=e.email},alert:function(t,e){var a=this;try{a[e+"_msg"]=t,a["show_"+e]=!0,setTimeout(function(){a[e+"_msg"]=null},3e3)}catch(t){}},logout:function(){var t=this;t.$root.$sc.emit("session",{method:"destroy"},function(e){if(e)return console.log(e),t.$root.alert(e,"error")})}},sockets:{connect:function(t){var e=this;e.authenticated=t.isAuthenticated},authenticate:function(){this.authenticated=!0,this.setUserData()},deauthenticate:function(){this.authenticated=!1}},components:{alert:VueStrap.alert,navbar:VueStrap.navbar},ready:function(){var t=this;t.started=!0}}),"#app"); -------------------------------------------------------------------------------- /examples/mvc-example/public/js/bin/concat.js: -------------------------------------------------------------------------------- 1 | "use strict";var router=new VueRouter,aboutComponent=Vue.extend({template:templatizer.about.index({}),data:function(){return{ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(a){a?(e.$root.alert(a,"error"),router.go("/")):(e.ready=!0,t.next())})},waitForData:!0}}),dashboardComponent=Vue.extend({template:templatizer.dashboard.index({}),data:function(){return{ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(a){a?(e.$root.alert(a,"error"),router.go("/")):(e.ready=!0,t.next())})},waitForData:!0}}),sessionComponent=Vue.extend({template:templatizer.session.create({}),data:function(){return{email:"",password:"",ready:!1}},methods:{login:function(){var t=this;validate.async({email:t.email,password:t.password},{email:{presence:!0},password:{presence:!0}}).then(function(e){t.$root.$sc.emit("session",{method:"store",email:e.email,password:e.password},function(e){if(e)return console.log(e),t.$root.alert(e,"error")})},function(e){var a=null;for(var r in e){a=e[r][0];break}t.$root.alert(a,"error")})}},ready:function(){},route:{data:function(t){var e=this;e.ready=!0,t.next()}}}),usersComponent=Vue.extend({template:templatizer.users.create({}),data:function(){return{first:"",last:"",email:"",password:"",ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)},clearFields:function(){this.first="",this.last="",this.email="",this.password=""},create:function(){var t=this;validate.async({first:t.first,last:t.last,email:t.email,password:t.password,visible:1},{first:{presence:!0},last:{presence:!0},email:{presence:!0},password:{presence:!0}}).then(function(e){t.$root.$sc.emit("users",{method:"store",user:e},function(e,a){return e?t.$root.alert(e,"error"):(t.$root.alert("Successfully created account "+a.email+"! Please log in.","success"),void router.go({path:"/session/create"}))})},function(e){var a=null;for(var r in e){a=e[r][0];break}t.$root.alert(a,"error")})}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(a){a?t.abort():(e.ready=!0,e.clearFields(),t.next())})},waitForData:!0}});Vue.use(VueRouter),Vue.use(VueSocketcluster),router.map({"/dashboard":{component:dashboardComponent,auth:!0},"/about":{component:aboutComponent,auth:!0},"/session/create":{component:sessionComponent,auth:!1},"/users/create":{component:usersComponent,auth:!1}}),router.redirect({"*":"/dashboard"}),router.beforeEach(function(t){router.app.loading=!0,t.to.auth&&"authenticated"!=router.app.$sc.authState?(router.app.loading=!1,t.redirect("/session/create")):"authenticated"!=router.app.$sc.authState||t.to.auth?t.next():(router.app.loading=!1,t.redirect("/dashboard"))}),router.afterEach(function(t){setTimeout(function(){router.app.loading=!1},router.app.loading_delay)}),router.start(Vue.extend({data:function(){return{loading_delay:20,show_success:!1,success_msg:"",show_error:!1,error_msg:"",started:!1,loading:!0,authenticated:!1,first:"",last:"",email:""}},watch:{authenticated:function(t,e){t?router.go({path:"/dashboard"}):router.go({path:"/session/create"})}},methods:{setUserData:function(){var t=this,e=t.$sc.getAuthToken()||{};t.first=e.first,t.last=e.last,t.email=e.email},alert:function(t,e){var a=this;try{a[e+"_msg"]=t,a["show_"+e]=!0,setTimeout(function(){a[e+"_msg"]=null},3e3)}catch(t){}},logout:function(){var t=this;t.$root.$sc.emit("session",{method:"destroy"},function(e){if(e)return console.log(e),t.$root.alert(e,"error")})}},sockets:{connect:function(t){var e=this;e.authenticated=t.isAuthenticated},authenticate:function(){this.authenticated=!0,this.setUserData()},deauthenticate:function(){this.authenticated=!1}},components:{alert:VueStrap.alert,navbar:VueStrap.navbar},ready:function(){var t=this;t.started=!0}}),"#app"); -------------------------------------------------------------------------------- /examples/mvc-example/public/js/bin/templates.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory(); 6 | } else { 7 | if (typeof root === 'undefined' || root !== Object(root)) { 8 | throw new Error('templatizer: window does not exist or is not an object'); 9 | } 10 | root.templatizer = factory(); 11 | } 12 | }(this, function () { 13 | var jade=function(){function n(n){return null!=n&&""!==n}function t(e){return(Array.isArray(e)?e.map(t):e&&"object"==typeof e?Object.keys(e).filter(function(n){return e[n]}):[e]).filter(n).join(" ")}function e(n){return i[n]||n}function r(n){var t=String(n).replace(o,e);return t===""+n?n:t}var a={};a.merge=function t(e,r){if(1===arguments.length){for(var a=e[0],i=1;i":">",'"':"""},o=/[&<>"]/g;return a.escape=r,a.rethrow=function n(t,e,r,a){if(!(t instanceof Error))throw t;if(!("undefined"==typeof window&&e||a))throw t.message+=" on line "+r,t;try{a=a||require("fs").readFileSync(e,"utf8")}catch(e){n(t,null,r)}var i=3,o=a.split("\n"),s=Math.max(r-i,0),f=Math.min(o.length,r+i),i=o.slice(s,f).map(function(n,t){var e=t+s+1;return(e==r?" > ":" ")+e+"| "+n}).join("\n");throw t.path=e,t.message=(e||"Jade")+":"+r+"\n"+i+"\n\n"+t.message,t},a.DebugItem=function(n,t){this.lineno=n,this.filename=t},a}(); 14 | 15 | var templatizer = {}; 16 | templatizer["about"] = {}; 17 | templatizer["dashboard"] = {}; 18 | templatizer["session"] = {}; 19 | templatizer["users"] = {}; 20 | 21 | // about/index.jade compiled template 22 | templatizer["about"]["index"] = function tmpl_about_index() { 23 | return "

About

"; 24 | }; 25 | 26 | // dashboard/index.jade compiled template 27 | templatizer["dashboard"]["index"] = function tmpl_dashboard_index() { 28 | return "

Dashboard

"; 29 | }; 30 | 31 | // session/create.jade compiled template 32 | templatizer["session"]["create"] = function tmpl_session_create() { 33 | return '
Please Login

'; 34 | }; 35 | 36 | // users/create.jade compiled template 37 | templatizer["users"]["create"] = function tmpl_users_create() { 38 | return '
Create Account

Go To Login Page
'; 39 | }; 40 | 41 | return templatizer; 42 | })); 43 | -------------------------------------------------------------------------------- /examples/mvc-example/public/js/lib/validate.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * validate.js 0.10.0 3 | * http://validatejs.org/ 4 | * (c) 2013-2015 Nicklas Ansman, 2013 Wrapp 5 | * validate.js may be freely distributed under the MIT license. 6 | */ 7 | 8 | (function(a,b,c){"use strict";var d=function(a,b,c){c=e.extend({},e.options,c);var f,g,h=e.runValidations(a,b,c);for(f in h)for(g in h[f])if(e.isPromise(h[f][g]))throw new Error("Use validate.async if you want support for promises");return d.processValidationResults(h,c)},e=d;e.extend=function(a){return[].slice.call(arguments,1).forEach(function(b){for(var c in b)a[c]=b[c]}),a},e.extend(d,{version:{major:0,minor:10,patch:0,metadata:null,toString:function(){var a=e.format("%{major}.%{minor}.%{patch}",e.version);return e.isEmpty(e.version.metadata)||(a+="+"+e.version.metadata),a}},Promise:"undefined"!=typeof Promise?Promise:null,EMPTY_STRING_REGEXP:/^\s*$/,runValidations:function(a,b,c){var d,f,g,h,i,j,k,l=[];(e.isDomElement(a)||e.isJqueryElement(a))&&(a=e.collectFormValues(a));for(d in b){g=e.getDeepObjectValue(a,d),h=e.result(b[d],g,a,d,c,b);for(f in h){if(i=e.validators[f],!i)throw k=e.format("Unknown validator %{name}",{name:f}),new Error(k);j=h[f],j=e.result(j,g,a,d,c,b),j&&l.push({attribute:d,value:g,validator:f,globalOptions:c,attributes:a,options:j,error:i.call(i,g,j,d,a,c)})}}return l},processValidationResults:function(a,b){var c;switch(a=e.pruneEmptyErrors(a,b),a=e.expandMultipleErrors(a,b),a=e.convertErrorMessages(a,b),b.format||"grouped"){case"detailed":break;case"flat":a=e.flattenErrorsToArray(a);break;case"grouped":a=e.groupErrorsByAttribute(a);for(c in a)a[c]=e.flattenErrorsToArray(a[c]);break;default:throw new Error(e.format("Unknown format %{format}",b))}return e.isEmpty(a)?void 0:a},async:function(a,b,c){c=e.extend({},e.async.options,c);var d=c.wrapErrors||function(a){return a};c.cleanAttributes!==!1&&(a=e.cleanAttributes(a,b));var f=e.runValidations(a,b,c);return new e.Promise(function(g,h){e.waitForResults(f).then(function(){var i=e.processValidationResults(f,c);i?h(new d(i,c,a,b)):g(a)},function(a){h(a)})})},single:function(a,b,c){return c=e.extend({},e.single.options,c,{format:"flat",fullMessages:!1}),e({single:a},{single:b},c)},waitForResults:function(a){return a.reduce(function(a,b){return e.isPromise(b.error)?a.then(function(){return b.error.then(function(a){b.error=a||null},function(a){if(a instanceof Error)throw a;e.error("Rejecting promises with the result is deprecated. Please use the resolve callback instead."),b.error=a})}):a},new e.Promise(function(a){a()}))},result:function(a){var b=[].slice.call(arguments,1);return"function"==typeof a&&(a=a.apply(null,b)),a},isNumber:function(a){return"number"==typeof a&&!isNaN(a)},isFunction:function(a){return"function"==typeof a},isInteger:function(a){return e.isNumber(a)&&a%1===0},isBoolean:function(a){return"boolean"==typeof a},isObject:function(a){return a===Object(a)},isDate:function(a){return a instanceof Date},isDefined:function(a){return null!==a&&void 0!==a},isPromise:function(a){return!!a&&e.isFunction(a.then)},isJqueryElement:function(a){return a&&e.isString(a.jquery)},isDomElement:function(a){return a&&a.querySelectorAll&&a.querySelector?e.isObject(document)&&a===document?!0:"object"==typeof HTMLElement?a instanceof HTMLElement:a&&"object"==typeof a&&null!==a&&1===a.nodeType&&"string"==typeof a.nodeName:!1},isEmpty:function(a){var b;if(!e.isDefined(a))return!0;if(e.isFunction(a))return!1;if(e.isString(a))return e.EMPTY_STRING_REGEXP.test(a);if(e.isArray(a))return 0===a.length;if(e.isDate(a))return!1;if(e.isObject(a)){for(b in a)return!1;return!0}return!1},format:e.extend(function(a,b){return e.isString(a)?a.replace(e.format.FORMAT_REGEXP,function(a,c,d){return"%"===c?"%{"+d+"}":String(b[d])}):a},{FORMAT_REGEXP:/(%?)%\{([^\}]+)\}/g}),prettify:function(a){return e.isNumber(a)?100*a%1===0?""+a:parseFloat(Math.round(100*a)/100).toFixed(2):e.isArray(a)?a.map(function(a){return e.prettify(a)}).join(", "):e.isObject(a)?a.toString():(a=""+a,a.replace(/([^\s])\.([^\s])/g,"$1 $2").replace(/\\+/g,"").replace(/[_-]/g," ").replace(/([a-z])([A-Z])/g,function(a,b,c){return""+b+" "+c.toLowerCase()}).toLowerCase())},stringifyValue:function(a){return e.prettify(a)},isString:function(a){return"string"==typeof a},isArray:function(a){return"[object Array]"==={}.toString.call(a)},isHash:function(a){return e.isObject(a)&&!e.isArray(a)&&!e.isFunction(a)},contains:function(a,b){return e.isDefined(a)?e.isArray(a)?-1!==a.indexOf(b):b in a:!1},unique:function(a){return e.isArray(a)?a.filter(function(a,b,c){return c.indexOf(a)==b}):a},forEachKeyInKeypath:function(a,b,c){if(e.isString(b)){var d,f="",g=!1;for(d=0;dk&&(d=b.tooShort||this.tooShort||"is too short (minimum is %{count} characters)",j.push(e.format(d,{count:h}))),e.isNumber(g)&&k>g&&(d=b.tooLong||this.tooLong||"is too long (maximum is %{count} characters)",j.push(e.format(d,{count:g}))),j.length>0?b.message||j:void 0):(e.error(e.format("Attribute %{attr} has a non numeric value for `length`",{attr:c})),b.message||this.notValid||"has an incorrect length")}},numericality:function(a,b){if(!e.isEmpty(a)){b=e.extend({},this.options,b);var c,d,f=[],g={greaterThan:function(a,b){return a>b},greaterThanOrEqualTo:function(a,b){return a>=b},equalTo:function(a,b){return a===b},lessThan:function(a,b){return b>a},lessThanOrEqualTo:function(a,b){return b>=a},divisibleBy:function(a,b){return a%b===0}};if(e.isString(a)&&b.strict){var h="^(0|[1-9]\\d*)";if(b.onlyInteger||(h+="(\\.\\d+)?"),h+="$",!new RegExp(h).test(a))return b.message||b.notValid||this.notValid||"must be a valid number"}if(b.noStrings!==!0&&e.isString(a)&&(a=+a),!e.isNumber(a))return b.message||b.notValid||this.notValid||"is not a number";if(b.onlyInteger&&!e.isInteger(a))return b.message||b.notInteger||this.notInteger||"must be an integer";for(c in g)if(d=b[c],e.isNumber(d)&&!g[c](a,d)){var i="not"+e.capitalize(c),j=b[i]||this[i]||"must be %{type} %{count}";f.push(e.format(j,{count:d,type:e.prettify(c)}))}return b.odd&&a%2!==1&&f.push(b.notOdd||this.notOdd||"must be odd"),b.even&&a%2!==0&&f.push(b.notEven||this.notEven||"must be even"),f.length?b.message||f:void 0}},datetime:e.extend(function(a,b){if(!e.isFunction(this.parse)||!e.isFunction(this.format))throw new Error("Both the parse and format functions needs to be set to use the datetime/date validator");if(!e.isEmpty(a)){b=e.extend({},this.options,b);var c,d=[],f=b.earliest?this.parse(b.earliest,b):NaN,g=b.latest?this.parse(b.latest,b):NaN;return a=this.parse(a,b),isNaN(a)||b.dateOnly&&a%864e5!==0?(c=b.notValid||b.message||this.notValid||"must be a valid date",e.format(c,{value:arguments[0]})):(!isNaN(f)&&f>a&&(c=b.tooEarly||b.message||this.tooEarly||"must be no earlier than %{date}",c=e.format(c,{value:this.format(a,b),date:this.format(f,b)}),d.push(c)),!isNaN(g)&&a>g&&(c=b.tooLate||b.message||this.tooLate||"must be no later than %{date}",c=e.format(c,{date:this.format(g,b),value:this.format(a,b)}),d.push(c)),d.length?e.unique(d):void 0)}},{parse:null,format:null}),date:function(a,b){return b=e.extend({},b,{dateOnly:!0}),e.validators.datetime.call(e.validators.datetime,a,b)},format:function(a,b){(e.isString(b)||b instanceof RegExp)&&(b={pattern:b}),b=e.extend({},this.options,b);var c,d=b.message||this.message||"is invalid",f=b.pattern;return e.isEmpty(a)?void 0:e.isString(a)?(e.isString(f)&&(f=new RegExp(b.pattern,b.flags)),c=f.exec(a),c&&c[0].length==a.length?void 0:d):d},inclusion:function(a,b){if(!e.isEmpty(a)&&(e.isArray(b)&&(b={within:b}),b=e.extend({},this.options,b),!e.contains(b.within,a))){var c=b.message||this.message||"^%{value} is not included in the list";return e.format(c,{value:a})}},exclusion:function(a,b){if(!e.isEmpty(a)&&(e.isArray(b)&&(b={within:b}),b=e.extend({},this.options,b),e.contains(b.within,a))){var c=b.message||this.message||"^%{value} is restricted";return e.format(c,{value:a})}},email:e.extend(function(a,b){b=e.extend({},this.options,b);var c=b.message||this.message||"is not a valid email";if(!e.isEmpty(a))return e.isString(a)&&this.PATTERN.exec(a)?void 0:c},{PATTERN:/^[a-z0-9\u007F-\uffff!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9\u007F-\uffff!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$/i}),equality:function(a,b,c,d){if(!e.isEmpty(a)){e.isString(b)&&(b={attribute:b}),b=e.extend({},this.options,b);var f=b.message||this.message||"is not equal to %{attribute}";if(e.isEmpty(b.attribute)||!e.isString(b.attribute))throw new Error("The attribute must be a non empty string");var g=e.getDeepObjectValue(d,b.attribute),h=b.comparator||function(a,b){return a===b};return h(a,g,b,c,d)?void 0:e.format(f,{attribute:e.prettify(b.attribute)})}},url:function(a,b){if(!e.isEmpty(a)){b=e.extend({},this.options,b);var c=b.message||this.message||"is not a valid url",d=b.schemes||this.schemes||["http","https"],f=b.allowLocal||this.allowLocal||!1;if(!e.isString(a))return c;var g="^(?:(?:"+d.join("|")+"):\\/\\/)(?:\\S+(?::\\S*)?@)?";g+="(?:";var h="(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))";f?h+="?":g+="(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})";var i="(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*"+h+")";g+="(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|"+i+"(?::\\d{2,5})?(?:\\/[^\\s]*)?$";var j=new RegExp(g,"i");return j.exec(a)?void 0:c}}},d.exposeModule(d,this,a,b,c)}).call(this,"undefined"!=typeof exports?exports:null,"undefined"!=typeof module?module:null,"undefined"!=typeof define?define:null); 9 | //# sourceMappingURL=validate.min.map -------------------------------------------------------------------------------- /examples/mvc-example/public/js/lib/vue-router.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-router v0.7.13 3 | * (c) 2016 Evan You 4 | * Released under the MIT License. 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueRouter=e()}(this,function(){"use strict";function t(t,e,n){this.path=t,this.matcher=e,this.delegate=n}function e(t){this.routes={},this.children={},this.target=t}function n(e,r,i){return function(o,a){var s=e+o;return a?void a(n(s,r,i)):new t(e+o,r,i)}}function r(t,e,n){for(var r=0,i=0,o=t.length;o>i;i++)r+=t[i].path.length;e=e.substr(r);var a={path:e,handler:n};t.push(a)}function i(t,e,n,o){var a=e.routes;for(var s in a)if(a.hasOwnProperty(s)){var h=t.slice();r(h,s,a[s]),e.children[s]?i(h,e.children[s],n,o):n.call(o,h)}}function o(t,r){var o=new e;t(n("",o,this.delegate)),i([],o,function(t){r?r(this,t):this.add(t)},this)}function a(t){B||"undefined"==typeof console||console.error("[vue-router] "+t)}function s(t,e){try{return e?decodeURIComponent(t):decodeURI(t)}catch(n){a("malformed URI"+(e?" component: ":": ")+t)}}function h(t){return"[object Array]"===Object.prototype.toString.call(t)}function c(t){this.string=t}function u(t){this.name=t}function l(t){this.name=t}function p(){}function f(t,e,n){"/"===t.charAt(0)&&(t=t.substr(1));var r=t.split("/"),i=[];n.val="";for(var o=0,a=r.length;a>o;o++){var s,h=r[o];(s=h.match(/^:([^\/]+)$/))?(i.push(new u(s[1])),e.push(s[1]),n.val+="3"):(s=h.match(/^\*([^\/]+)$/))?(i.push(new l(s[1])),n.val+="2",e.push(s[1])):""===h?(i.push(new p),n.val+="1"):(i.push(new c(h)),n.val+="4")}return n.val=+n.val,i}function d(t){this.charSpec=t,this.nextStates=[]}function v(t){return t.sort(function(t,e){return e.specificity.val-t.specificity.val})}function g(t,e){for(var n=[],r=0,i=t.length;i>r;r++){var o=t[r];n=n.concat(o.match(e))}return n}function y(t){this.queryParams=t||{}}function m(t,e,n){for(var r=t.handlers,i=t.regex,o=e.match(i),a=1,s=new y(n),h=0,c=r.length;c>h;h++){for(var u=r[h],l=u.names,p={},f=0,d=l.length;d>f;f++)p[l[f]]=o[a++];s.push({handler:u.handler,params:p,isDynamic:!!l.length})}return s}function _(t,e){return e.eachChar(function(e){t=t.put(e)}),t}function w(t){return t=t.replace(/\+/gm,"%20"),s(t,!0)}function b(t){"undefined"!=typeof console&&console.error("[vue-router] "+t)}function C(t,e,n){var r=t.match(/(\?.*)$/);if(r&&(r=r[1],t=t.slice(0,-r.length)),"?"===e.charAt(0))return t+e;var i=t.split("/");n&&i[i.length-1]||i.pop();for(var o=e.replace(/^\//,"").split("/"),a=0;a can only be used inside a router-enabled app.");this._isDynamicLiteral=!0,n.bind.call(this);for(var e=void 0,r=this.vm;r;){if(r._routerView){e=r._routerView;break}r=r.$parent}if(e)this.parentView=e,e.childView=this;else{var i=t.router;i._rootView=this}var o=t.router._currentTransition;if(!e&&o.done||e&&e.activated){var a=e?e.depth+1:0;P(this,o,a)}},unbind:function(){this.parentView&&(this.parentView.childView=null),n.unbind.call(this)}}),t.elementDirective("router-view",r)}function Q(t){function e(t){return t.protocol===location.protocol&&t.hostname===location.hostname&&t.port===location.port}function n(t,e,n){if(e=e.trim(),-1===e.indexOf(" "))return void n(t,e);for(var r=e.split(/\s+/),i=0,o=r.length;o>i;i++)n(t,r[i])}var r=t.util,i=r.bind,o=r.isObject,a=r.addClass,s=r.removeClass,h=t.directive("on").priority,c="__vue-router-link-update__",u=0;t.directive("link-active",{priority:9999,bind:function(){for(var t=this,e=String(u++),n=this.el.querySelectorAll("[v-link]"),r=0,i=n.length;i>r;r++){var o=n[r],a=o.getAttribute(c),s=a?a+","+e:e;o.setAttribute(c,s)}this.vm.$on(c,this.cb=function(n,r){n.activeIds.indexOf(e)>-1&&n.updateClasses(r,t.el)})},unbind:function(){this.vm.$off(c,this.cb)}}),t.directive("link",{priority:h-2,bind:function(){var t=this.vm;if(!t.$route)return void b("v-link can only be used inside a router-enabled app.");this.router=t.$route.router,this.unwatch=t.$watch("$route",i(this.onRouteUpdate,this));var e=this.el.getAttribute(c);e&&(this.el.removeAttribute(c),this.activeIds=e.split(",")),"A"===this.el.tagName&&"_blank"===this.el.getAttribute("target")||(this.handler=i(this.onClick,this),this.el.addEventListener("click",this.handler))},update:function(t){this.target=t,o(t)&&(this.append=t.append,this.exact=t.exact,this.prevActiveClass=this.activeClass,this.activeClass=t.activeClass),this.onRouteUpdate(this.vm.$route)},onClick:function(t){if(!(t.metaKey||t.ctrlKey||t.shiftKey||t.defaultPrevented||0!==t.button)){var n=this.target;if(n)t.preventDefault(),this.router.go(n);else{for(var r=t.target;"A"!==r.tagName&&r!==this.el;)r=r.parentNode;if("A"===r.tagName&&e(r)){t.preventDefault();var i=r.pathname;this.router.history.root&&(i=i.replace(this.router.history.rootRE,"")),this.router.go({path:i,replace:n&&n.replace,append:n&&n.append})}}}},onRouteUpdate:function(t){var e=this.router.stringifyPath(this.target);this.path!==e&&(this.path=e,this.updateActiveMatch(),this.updateHref()),this.activeIds?this.vm.$emit(c,this,t.path):this.updateClasses(t.path,this.el)},updateActiveMatch:function(){this.activeRE=this.path&&!this.exact?new RegExp("^"+this.path.replace(/\/$/,"").replace(at,"").replace(ot,"\\$&")+"(\\/|$)"):null},updateHref:function(){if("A"===this.el.tagName){var t=this.path,e=this.router,n="/"===t.charAt(0),r=t&&("hash"===e.mode||n)?e.history.formatPath(t,this.append):t;r?this.el.href=r:this.el.removeAttribute("href")}},updateClasses:function(t,e){var r=this.activeClass||this.router._linkActiveClass;this.prevActiveClass&&this.prevActiveClass!==r&&n(e,this.prevActiveClass,s);var i=this.path.replace(at,"");t=t.replace(at,""),this.exact?i===t||"/"!==i.charAt(i.length-1)&&i===t.replace(it,"")?n(e,r,a):n(e,r,s):this.activeRE&&this.activeRE.test(t)?n(e,r,a):n(e,r,s)},unbind:function(){this.el.removeEventListener("click",this.handler),this.unwatch&&this.unwatch()}})}function F(t,e){var n=e.component;ht.util.isPlainObject(n)&&(n=e.component=ht.extend(n)),"function"!=typeof n&&(e.component=null,b('invalid component for route "'+t+'".'))}var I={};I.classCallCheck=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},t.prototype={to:function(t,e){var n=this.delegate;if(n&&n.willAddRoute&&(t=n.willAddRoute(this.matcher.target,t)),this.matcher.add(this.path,t),e){if(0===e.length)throw new Error("You must have an argument in the function passed to `to`");this.matcher.addChild(this.path,t,e,this.delegate)}return this}},e.prototype={add:function(t,e){this.routes[t]=e},addChild:function(t,r,i,o){var a=new e(r);this.children[t]=a;var s=n(t,a,o);o&&o.contextEntered&&o.contextEntered(r,s),i(s)}};var U=["/",".","*","+","?","|","(",")","[","]","{","}","\\"],L=new RegExp("(\\"+U.join("|\\")+")","g"),B=!1;c.prototype={eachChar:function(t){for(var e,n=this.string,r=0,i=n.length;i>r;r++)e=n.charAt(r),t({validChars:e})},regex:function(){return this.string.replace(L,"\\$1")},generate:function(){return this.string}},u.prototype={eachChar:function(t){t({invalidChars:"/",repeat:!0})},regex:function(){return"([^/]+)"},generate:function(t){var e=t[this.name];return null==e?":"+this.name:e}},l.prototype={eachChar:function(t){t({invalidChars:"",repeat:!0})},regex:function(){return"(.+)"},generate:function(t){var e=t[this.name];return null==e?":"+this.name:e}},p.prototype={eachChar:function(){},regex:function(){return""},generate:function(){return""}},d.prototype={get:function(t){for(var e=this.nextStates,n=0,r=e.length;r>n;n++){var i=e[n],o=i.charSpec.validChars===t.validChars;if(o=o&&i.charSpec.invalidChars===t.invalidChars)return i}},put:function(t){var e;return(e=this.get(t))?e:(e=new d(t),this.nextStates.push(e),t.repeat&&e.nextStates.push(e),e)},match:function(t){for(var e,n,r,i=this.nextStates,o=[],a=0,s=i.length;s>a;a++)e=i[a],n=e.charSpec,"undefined"!=typeof(r=n.validChars)?-1!==r.indexOf(t)&&o.push(e):"undefined"!=typeof(r=n.invalidChars)&&-1===r.indexOf(t)&&o.push(e);return o}};var N=Object.create||function(t){function e(){}return e.prototype=t,new e};y.prototype=N({splice:Array.prototype.splice,slice:Array.prototype.slice,push:Array.prototype.push,length:0,queryParams:null});var G=function(){this.rootState=new d,this.names={}};G.prototype={add:function(t,e){for(var n,r=this.rootState,i="^",o={},a=[],s=[],h=!0,c=0,u=t.length;u>c;c++){var l=t[c],d=[],v=f(l.path,d,o);s=s.concat(v);for(var g=0,y=v.length;y>g;g++){var m=v[g];m instanceof p||(h=!1,r=r.put({validChars:"/"}),i+="/",r=_(r,m),i+=m.regex())}var w={handler:l.handler,names:d};a.push(w)}h&&(r=r.put({validChars:"/"}),i+="/"),r.handlers=a,r.regex=new RegExp(i+"$"),r.specificity=o,(n=e&&e.as)&&(this.names[n]={segments:s,handlers:a})},handlersFor:function(t){var e=this.names[t],n=[];if(!e)throw new Error("There is no route named "+t);for(var r=0,i=e.handlers.length;i>r;r++)n.push(e.handlers[r]);return n},hasRoute:function(t){return!!this.names[t]},generate:function(t,e){var n=this.names[t],r="";if(!n)throw new Error("There is no route named "+t);for(var i=n.segments,o=0,a=i.length;a>o;o++){var s=i[o];s instanceof p||(r+="/",r+=s.generate(e))}return"/"!==r.charAt(0)&&(r="/"+r),e&&e.queryParams&&(r+=this.generateQueryString(e.queryParams)),r},generateQueryString:function(t){var e=[],n=[];for(var r in t)t.hasOwnProperty(r)&&n.push(r);n.sort();for(var i=0,o=n.length;o>i;i++){r=n[i];var a=t[r];if(null!=a){var s=encodeURIComponent(r);if(h(a))for(var c=0,u=a.length;u>c;c++){var l=r+"[]="+encodeURIComponent(a[c]);e.push(l)}else s+="="+encodeURIComponent(a),e.push(s)}}return 0===e.length?"":"?"+e.join("&")},parseQueryString:function(t){for(var e=t.split("&"),n={},r=0;r2&&"[]"===a.slice(s-2)&&(h=!0,a=a.slice(0,s-2),n[a]||(n[a]=[])),i=o[1]?w(o[1]):""),h?n[a].push(i):n[a]=i}return n},recognize:function(t,e){B=e;var n,r,i,o,a=[this.rootState],h={},c=!1;if(o=t.indexOf("?"),-1!==o){var u=t.substr(o+1,t.length);t=t.substr(0,o),u&&(h=this.parseQueryString(u))}if(t=s(t)){for("/"!==t.charAt(0)&&(t="/"+t),n=t.length,n>1&&"/"===t.charAt(n-1)&&(t=t.substr(0,n-1),c=!0),r=0,i=t.length;i>r&&(a=g(a,t.charAt(r)),a.length);r++);var l=[];for(r=0,i=a.length;i>r;r++)a[r].handlers&&l.push(a[r]);a=v(l);var p=l[0];return p&&p.handlers?(c&&"(.+)$"===p.regex.source.slice(-5)&&(t+="/"),m(p,t,h)):void 0}}},G.prototype.map=o;var K=G.prototype.generateQueryString,X={},Y=void 0,J=/#.*$/,W=function(){function t(e){var n=e.root,r=e.onChange;I.classCallCheck(this,t),n&&"/"!==n?("/"!==n.charAt(0)&&(n="/"+n),this.root=n.replace(/\/$/,""),this.rootRE=new RegExp("^\\"+this.root)):this.root=null,this.onChange=r;var i=document.querySelector("base");this.base=i&&i.getAttribute("href")}return t.prototype.start=function(){var t=this;this.listener=function(e){var n=location.pathname+location.search;t.root&&(n=n.replace(t.rootRE,"")),t.onChange(n,e&&e.state,location.hash)},window.addEventListener("popstate",this.listener),this.listener()},t.prototype.stop=function(){window.removeEventListener("popstate",this.listener)},t.prototype.go=function(t,e,n){var r=this.formatPath(t,n);e?history.replaceState({},"",r):(history.replaceState({pos:{x:window.pageXOffset,y:window.pageYOffset}},"",location.href),history.pushState({},"",r));var i=t.match(J),o=i&&i[0];t=r.replace(J,"").replace(this.rootRE,""),this.onChange(t,null,o)},t.prototype.formatPath=function(t,e){return"/"===t.charAt(0)?this.root?this.root+"/"+t.replace(/^\//,""):t:C(this.base||location.pathname,t,e)},t}(),Z=function(){function t(e){var n=e.hashbang,r=e.onChange;I.classCallCheck(this,t),this.hashbang=n,this.onChange=r}return t.prototype.start=function(){var t=this;this.listener=function(){var e=location.hash,n=e.replace(/^#!?/,"");"/"!==n.charAt(0)&&(n="/"+n);var r=t.formatPath(n);if(r!==e)return void location.replace(r);var i=location.search&&e.indexOf("?")>-1?"&"+location.search.slice(1):location.search;t.onChange(e.replace(/^#!?/,"")+i)},window.addEventListener("hashchange",this.listener),this.listener()},t.prototype.stop=function(){window.removeEventListener("hashchange",this.listener)},t.prototype.go=function(t,e,n){t=this.formatPath(t,n),e?location.replace(t):location.hash=t},t.prototype.formatPath=function(t,e){var n="/"===t.charAt(0),r="#"+(this.hashbang?"!":"");return n?r+t:r+C(location.hash.replace(/^#!?/,""),t,e)},t}(),tt=function(){function t(e){var n=e.onChange;I.classCallCheck(this,t),this.onChange=n,this.currentPath="/"}return t.prototype.start=function(){this.onChange("/")},t.prototype.stop=function(){},t.prototype.go=function(t,e,n){t=this.currentPath=this.formatPath(t,n),this.onChange(t)},t.prototype.formatPath=function(t,e){return"/"===t.charAt(0)?t:C(this.currentPath,t,e)},t}(),et=function(){function t(e,n,r){I.classCallCheck(this,t),this.router=e,this.to=n,this.from=r,this.next=null,this.aborted=!1,this.done=!1}return t.prototype.abort=function(){if(!this.aborted){this.aborted=!0;var t=!this.from.path&&"/"===this.to.path;t||this.router.replace(this.from.path||"/")}},t.prototype.redirect=function(t){this.aborted||(this.aborted=!0,"string"==typeof t?t=$(t,this.to.params,this.to.query):(t.params=t.params||this.to.params,t.query=t.query||this.to.query),this.router.replace(t))},t.prototype.start=function(t){for(var e=this,n=[],r=this.router._rootView;r;)n.unshift(r),r=r.childView;var i=n.slice().reverse(),o=this.activateQueue=D(this.to.matched).map(function(t){return t.handler}),a=void 0,s=void 0;for(a=0;a0&&(s=i.slice(0,a),n=i.slice(a).reverse(),o=o.slice(a)),e.runQueue(n,E,function(){e.runQueue(o,V,function(){e.runQueue(n,S,function(){if(e.router._onTransitionValidated(e),s&&s.forEach(function(t){return O(t,e)}),n.length){var r=n[n.length-1],i=s?s.length:0;P(r,e,i,t)}else t()})})})},t.prototype.runQueue=function(t,e,n){function r(o){o>=t.length?n():e(t[o],i,function(){r(o+1)})}var i=this;r(0)},t.prototype.callHook=function(t,e,n){var r=arguments.length<=3||void 0===arguments[3]?{}:arguments[3],i=r.expectBoolean,o=void 0===i?!1:i,a=r.postActivate,s=void 0===a?!1:a,h=r.processData,c=r.cleanup,u=this,l=!1,p=function(){c&&c(),u.abort()},f=function(t){if(s?v():p(),t&&!u.router._suppress)throw b("Uncaught error during transition: "),t instanceof Error?t:new Error(t)},d=function(t){try{f(t)}catch(e){setTimeout(function(){throw e},0)}},v=function(){return l?void b("transition.next() should be called only once."):(l=!0,u.aborted?void(c&&c()):void(n&&n()))},g=function(e){"boolean"==typeof e?e?v():p():R(e)?e.then(function(t){t?v():p()},d):t.length||v()},y=function(t){var e=void 0;try{e=h(t)}catch(n){return f(n)}R(e)?e.then(v,d):v()},m={to:u.to,from:u.from,abort:p,next:h?y:v,redirect:function(){u.redirect.apply(u,arguments)}},_=void 0;try{_=t.call(e,m)}catch(w){return f(w)}o?g(_):R(_)?h?_.then(y,d):_.then(v,d):h&&M(_)?y(_):t.length||v()},t.prototype.callHooks=function(t,e,n,r){var i=this;Array.isArray(t)?this.runQueue(t,function(t,n,o){i.aborted||i.callHook(t,e,o,r)},n):this.callHook(t,e,n,r)},t}(),nt=/^(component|subRoutes|fullPath)$/,rt=function ut(t,e){var n=this;I.classCallCheck(this,ut);var r=e._recognizer.recognize(t);r&&([].forEach.call(r,function(t){for(var e in t.handler)nt.test(e)||(n[e]=t.handler[e])}),this.query=r.queryParams,this.params=[].reduce.call(r,function(t,e){if(e.params)for(var n in e.params)t[n]=e.params[n];return t},{})),this.path=t,this.matched=r||e._notFoundHandler,Object.defineProperty(this,"router",{enumerable:!1,value:e}),Object.freeze(this)},it=/\/$/,ot=/[-.*+?^${}()|[\]\/\\]/g,at=/\?.*$/,st={"abstract":tt,hash:Z,html5:W},ht=void 0,ct=function(){function t(){var e=this,n=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],r=n.hashbang,i=void 0===r?!0:r,o=n["abstract"],a=void 0===o?!1:o,s=n.history,h=void 0===s?!1:s,c=n.saveScrollPosition,u=void 0===c?!1:c,l=n.transitionOnLoad,p=void 0===l?!1:l,f=n.suppressTransitionError,d=void 0===f?!1:f,v=n.root,g=void 0===v?null:v,y=n.linkActiveClass,m=void 0===y?"v-link-active":y;if(I.classCallCheck(this,t),!t.installed)throw new Error("Please install the Router with Vue.use() before creating an instance.");this.app=null,this._children=[],this._recognizer=new G,this._guardRecognizer=new G,this._started=!1,this._startCb=null,this._currentRoute={},this._currentTransition=null,this._previousTransition=null,this._notFoundHandler=null,this._notFoundRedirect=null,this._beforeEachHooks=[],this._afterEachHooks=[],this._rendered=!1,this._transitionOnLoad=p,this._root=g,this._abstract=a,this._hashbang=i;var _="undefined"!=typeof window&&window.history&&window.history.pushState;this._history=h&&_,this._historyFallback=h&&!_;var w=ht.util.inBrowser;this.mode=!w||this._abstract?"abstract":this._history?"html5":"hash";var b=st[this.mode];this.history=new b({root:g,hashbang:this._hashbang,onChange:function(t,n,r){e._match(t,n,r)}}),this._saveScrollPosition=u,this._linkActiveClass=m,this._suppress=d}return t.prototype.map=function(t){for(var e in t)this.on(e,t[e]);return this},t.prototype.on=function(t,e){return"*"===t?this._notFound(e):this._addRoute(t,e,[]),this},t.prototype.redirect=function(t){for(var e in t)this._addRedirect(e,t[e]);return this},t.prototype.alias=function(t){for(var e in t)this._addAlias(e,t[e]);return this},t.prototype.beforeEach=function(t){return this._beforeEachHooks.push(t),this},t.prototype.afterEach=function(t){return this._afterEachHooks.push(t),this},t.prototype.go=function(t){var e=!1,n=!1;ht.util.isObject(t)&&(e=t.replace,n=t.append),t=this.stringifyPath(t),t&&this.history.go(t,e,n)},t.prototype.replace=function(t){"string"==typeof t&&(t={path:t}),t.replace=!0,this.go(t)},t.prototype.start=function(t,e,n){if(this._started)return void b("already started.");if(this._started=!0,this._startCb=n,!this.app){if(!t||!e)throw new Error("Must start vue-router with a component and a root container.");if(t instanceof ht)throw new Error("Must start vue-router with a component, not a Vue instance.");this._appContainer=e;var r=this._appConstructor="function"==typeof t?t:ht.extend(t);r.options.name=r.options.name||"RouterApp"}if(this._historyFallback){var i=window.location,o=new W({root:this._root}),a=o.root?i.pathname.replace(o.rootRE,""):i.pathname;if(a&&"/"!==a)return void i.assign((o.root||"")+"/"+this.history.formatPath(a)+i.search)}this.history.start()},t.prototype.stop=function(){this.history.stop(),this._started=!1},t.prototype.stringifyPath=function(t){var e="";if(t&&"object"==typeof t){if(t.name){var n=ht.util.extend,r=this._currentTransition&&this._currentTransition.to.params,i=t.params||{},o=r?n(n({},r),i):i;e=encodeURI(this._recognizer.generate(t.name,o))}else t.path&&(e=encodeURI(t.path));if(t.query){var a=this._recognizer.generateQueryString(t.query);e+=e.indexOf("?")>-1?"&"+a.slice(1):a}}else e=encodeURI(t?t+"":"");return e},t.prototype._addRoute=function(t,e,n){if(F(t,e),e.path=t,e.fullPath=(n.reduce(function(t,e){return t+e.path},"")+t).replace("//","/"),n.push({path:t,handler:e}),this._recognizer.add(n,{as:e.name}),e.subRoutes)for(var r in e.subRoutes)this._addRoute(r,e.subRoutes[r],n.slice())},t.prototype._notFound=function(t){F("*",t),this._notFoundHandler=[{handler:t}]},t.prototype._addRedirect=function(t,e){"*"===t?this._notFoundRedirect=e:this._addGuard(t,e,this.replace)},t.prototype._addAlias=function(t,e){this._addGuard(t,e,this._match)},t.prototype._addGuard=function(t,e,n){var r=this;this._guardRecognizer.add([{path:t,handler:function(t,i){var o=$(e,t.params,i);n.call(r,o)}}])},t.prototype._checkGuard=function(t){var e=this._guardRecognizer.recognize(t,!0);return e?(e[0].handler(e[0],e.queryParams),!0):this._notFoundRedirect&&(e=this._recognizer.recognize(t),!e)?(this.replace(this._notFoundRedirect),!0):void 0},t.prototype._match=function(t,e,n){var r=this;if(!this._checkGuard(t)){var i=this._currentRoute,o=this._currentTransition;if(o){if(o.to.path===t)return;if(i.path===t)return o.aborted=!0,void(this._currentTransition=this._prevTransition);o.aborted=!0}var a=new rt(t,this),s=new et(this,a,i);this._prevTransition=o,this._currentTransition=s,this.app||!function(){var t=r;r.app=new r._appConstructor({el:r._appContainer,created:function(){this.$router=t},_meta:{$route:a}})}();var h=this._beforeEachHooks,c=function(){s.start(function(){r._postTransition(a,e,n)})};h.length?s.runQueue(h,function(t,e,n){s===r._currentTransition&&s.callHook(t,null,n,{expectBoolean:!0})},c):c(),!this._rendered&&this._startCb&&this._startCb.call(null),this._rendered=!0}},t.prototype._onTransitionValidated=function(t){var e=this._currentRoute=t.to;this.app.$route!==e&&(this.app.$route=e,this._children.forEach(function(t){t.$route=e})),this._afterEachHooks.length&&this._afterEachHooks.forEach(function(e){return e.call(null,{to:t.to,from:t.from})}),this._currentTransition.done=!0},t.prototype._postTransition=function(t,e,n){var r=e&&e.pos;r&&this._saveScrollPosition?ht.nextTick(function(){window.scrollTo(r.x,r.y)}):n&&ht.nextTick(function(){var t=document.getElementById(n.slice(1));t&&window.scrollTo(window.scrollX,t.offsetTop)})},t}();return ct.installed=!1,ct.install=function(t){return ct.installed?void b("already installed."):(ht=t,q(ht),z(ht),Q(ht),X.Vue=ht,void(ct.installed=!0))},"undefined"!=typeof window&&window.Vue&&window.Vue.use(ct),ct}); -------------------------------------------------------------------------------- /examples/mvc-example/public/js/lib/vue-socketcluster.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | var version = '1.1.0'; 3 | window.VueSocketcluster = {}; 4 | 5 | if (!socketCluster) { 6 | throw new Error("[Vue-Socketcluster] cannot locate socketcluster-client"); 7 | } 8 | 9 | var VueSocketcluster = { 10 | install: function (Vue, config) { 11 | 12 | if (typeof config == 'object') { 13 | if (!config.hostname || !config.port) { 14 | config.hostname = 'localhost'; 15 | config.port = 3000; 16 | } 17 | 18 | } else { 19 | config = { 20 | hostname:'localhost', 21 | port:3000 22 | }; 23 | } 24 | 25 | var socket = socketCluster.connect(config); 26 | 27 | var onevent = socket.onevent; 28 | socket.onevent = function (packet) { 29 | var args = packet.data || []; 30 | onevent.call(this, packet); 31 | packet.data = ["*"].concat(args); 32 | onevent.call(this, packet); 33 | } 34 | 35 | var methods = [ 36 | "error", 37 | "connect", 38 | "disconnect", 39 | "connectAbort", 40 | "raw", 41 | "kickOut", 42 | "subscribe", 43 | "subscribeFail", 44 | "unsubscribe", 45 | "authStateChange", 46 | "authTokenChange", 47 | "subscribeStateChange", 48 | "subscribeRequest", 49 | "authenticate", 50 | "deauthenticate", 51 | "message" 52 | ]; 53 | 54 | Vue.mixin({ 55 | created: function () { 56 | var self = this; 57 | if (this.$options.hasOwnProperty("sockets")) { 58 | 59 | for (var key in self.$options.sockets) { 60 | if (self.$options.sockets.hasOwnProperty(key) && methods.indexOf(key) < 0) { 61 | socket.on(key,function(emit,data,respond) { 62 | self.$options.sockets[key].call(self,data,respond); 63 | }) 64 | } 65 | } 66 | 67 | methods.forEach(function (event) { 68 | socket.on(event, function (first,second) { 69 | if (self.$options.sockets.hasOwnProperty(event)) { 70 | self.$options.sockets[event].call(self, first, second); 71 | } 72 | }) 73 | }) 74 | } 75 | 76 | // Global VueSocketcluster instance 77 | this.$sc = socket; 78 | } 79 | }) 80 | 81 | 82 | } 83 | }; 84 | 85 | if (typeof exports == "object") { 86 | module.exports = VueSocketcluster; 87 | } else if (typeof define == "function" && define.amd) { 88 | define([], function () { 89 | return VueSocketcluster; 90 | }) 91 | } else if (window.Vue) { 92 | window.VueSocketcluster = VueSocketcluster; 93 | } 94 | 95 | 96 | })(); -------------------------------------------------------------------------------- /examples/mvc-example/public/js/lib/vue-socketcluster.min.js: -------------------------------------------------------------------------------- 1 | !function(){if(window.VueSocketcluster={},!socketCluster)throw new Error("[Vue-Socketcluster] cannot locate socketcluster-client");var b={install:function(a,b){"object"==typeof b?b.hostname&&b.port||(b.hostname="localhost",b.port=3e3):b={hostname:"localhost",port:3e3};var c=socketCluster.connect(b),d=c.onevent;c.onevent=function(a){var b=a.data||[];d.call(this,a),a.data=["*"].concat(b),d.call(this,a)};var e=["error","connect","disconnect","connectAbort","raw","kickOut","subscribe","subscribeFail","unsubscribe","authStateChange","authTokenChange","subscribeStateChange","subscribeRequest","authenticate","deauthenticate","message"];a.mixin({created:function(){var a=this;if(this.$options.hasOwnProperty("sockets")){for(var b in a.$options.sockets)a.$options.sockets.hasOwnProperty(b)&&e.indexOf(b)<0&&c.on(b,function(c,d,e){a.$options.sockets[b].call(a,d,e)});e.forEach(function(b){c.on(b,function(c,d){a.$options.sockets.hasOwnProperty(b)&&a.$options.sockets[b].call(a,c,d)})})}this.$sc=c}})}};"object"==typeof exports?module.exports=b:"function"==typeof define&&define.amd?define([],function(){return b}):window.Vue&&(window.VueSocketcluster=b)}(); -------------------------------------------------------------------------------- /examples/mvc-example/public/js/src/components/about/index.js: -------------------------------------------------------------------------------- 1 | var aboutComponent = Vue.extend({ 2 | template: templatizer.about.index({}), 3 | data() { 4 | return { 5 | ready:false 6 | } 7 | }, 8 | methods:{ 9 | fetchData(respond) { 10 | if (respond && typeof respond == 'function') respond(null) 11 | } 12 | }, 13 | ready() { 14 | var vm = this 15 | }, 16 | route: { 17 | data(transition) { 18 | var vm = this 19 | vm.fetchData(function(err) { 20 | if (err) { 21 | vm.$root.alert(err,'error') 22 | router.go('/') 23 | } else { 24 | vm.ready = true 25 | transition.next() 26 | } 27 | }) 28 | }, 29 | waitForData:true 30 | } 31 | }) -------------------------------------------------------------------------------- /examples/mvc-example/public/js/src/components/dashboard/index.js: -------------------------------------------------------------------------------- 1 | var dashboardComponent = Vue.extend({ 2 | template: templatizer.dashboard.index({}), 3 | data() { 4 | return { 5 | ready:false 6 | } 7 | }, 8 | methods:{ 9 | fetchData(respond) { 10 | if (respond && typeof respond == 'function') respond(null) 11 | } 12 | }, 13 | ready() { 14 | var vm = this 15 | }, 16 | route: { 17 | data(transition) { 18 | var vm = this 19 | vm.fetchData(function(err) { 20 | if (err) { 21 | vm.$root.alert(err,'error') 22 | router.go('/') 23 | } else { 24 | vm.ready = true 25 | transition.next() 26 | } 27 | }) 28 | }, 29 | waitForData:true 30 | } 31 | }) -------------------------------------------------------------------------------- /examples/mvc-example/public/js/src/components/session/create.js: -------------------------------------------------------------------------------- 1 | var sessionComponent = Vue.extend({ 2 | template: templatizer.session.create({}), 3 | data() { 4 | return { 5 | email:'', 6 | password:'', 7 | 8 | ready:false 9 | } 10 | }, 11 | methods:{ 12 | login() { 13 | var vm = this 14 | 15 | validate.async({ 16 | email:vm.email, 17 | password:vm.password 18 | },{ 19 | email:{presence:true}, 20 | password:{presence:true} 21 | }).then(function(attributes) { 22 | vm.$root.$sc.emit('session',{ 23 | method:'store', 24 | email:attributes.email, 25 | password:attributes.password 26 | },function(err) { 27 | if (err) { console.log(err);return vm.$root.alert(err,'error') } 28 | // vm.$root.authenticated = true 29 | // router.go({ path:'/dashboard' }) 30 | }) 31 | },function(errors) { 32 | var error = null 33 | for (var key in errors) { 34 | error = errors[key][0] 35 | break; 36 | } 37 | vm.$root.alert(error,'error') 38 | }) 39 | } 40 | }, 41 | ready() { 42 | var vm = this 43 | }, 44 | route: { 45 | data(transition) { 46 | var vm = this 47 | vm.ready = true 48 | transition.next() 49 | } 50 | } 51 | }) -------------------------------------------------------------------------------- /examples/mvc-example/public/js/src/components/users/create.js: -------------------------------------------------------------------------------- 1 | var usersComponent = Vue.extend({ 2 | template: templatizer.users.create({}), 3 | data() { 4 | return { 5 | first:'', 6 | last:'', 7 | email:'', 8 | password:'', 9 | 10 | ready:false 11 | } 12 | }, 13 | methods:{ 14 | fetchData(respond) { 15 | if (respond && typeof respond == 'function') respond(null) 16 | }, 17 | clearFields() { 18 | this.first = '' 19 | this.last = '' 20 | this.email = '' 21 | this.password = '' 22 | }, 23 | create() { 24 | var vm = this 25 | 26 | validate.async({ 27 | first:vm.first, 28 | last:vm.last, 29 | email:vm.email, 30 | password:vm.password, 31 | visible:1 32 | },{ 33 | first:{presence:true}, 34 | last:{presence:true}, 35 | email:{presence:true}, 36 | password:{presence:true} 37 | }).then(function(attributes){ 38 | vm.$root.$sc.emit('users',{ 39 | method:'store', 40 | user:attributes 41 | },function(err,new_user) { 42 | if (err) { return vm.$root.alert(err,'error') } 43 | vm.$root.alert('Successfully created account ' + new_user.email + '! Please log in.','success') 44 | router.go({ path:'/session/create' }) 45 | }) 46 | },function(errors) { 47 | var error = null 48 | for (var key in errors) { 49 | error = errors[key][0] 50 | break; 51 | } 52 | vm.$root.alert(error,'error') 53 | }) 54 | } 55 | }, 56 | ready() { 57 | var vm = this 58 | }, 59 | route: { 60 | data(transition) { 61 | var vm = this 62 | vm.fetchData(function(err) { 63 | if (err) { transition.abort() } 64 | else { 65 | vm.ready = true 66 | vm.clearFields() 67 | transition.next() 68 | } 69 | }) 70 | }, 71 | waitForData:true 72 | } 73 | }) -------------------------------------------------------------------------------- /examples/mvc-example/public/js/src/system/app.js: -------------------------------------------------------------------------------- 1 | Vue.use(VueRouter) 2 | Vue.use(VueSocketcluster) 3 | 4 | router.map({ 5 | '/dashboard' : { component:dashboardComponent, auth:true }, 6 | '/about' : { component:aboutComponent, auth:true }, 7 | '/session/create': { component : sessionComponent, auth:false }, 8 | '/users/create': { component : usersComponent, auth:false } 9 | }) 10 | 11 | router.redirect({ 12 | '*':'/dashboard' 13 | }) 14 | 15 | router.beforeEach(function(transition) { 16 | router.app.loading = true 17 | if (transition.to.auth && router.app.$sc.authState != 'authenticated') { 18 | router.app.loading = false 19 | transition.redirect('/session/create') 20 | } else if (router.app.$sc.authState == 'authenticated' && !transition.to.auth) { 21 | router.app.loading = false 22 | transition.redirect('/dashboard') 23 | } else { 24 | transition.next() 25 | } 26 | }) 27 | 28 | router.afterEach(function(transition) { 29 | setTimeout(function() { 30 | router.app.loading = false 31 | },router.app.loading_delay) 32 | }) 33 | 34 | router.start(Vue.extend({ 35 | data() { 36 | return { 37 | loading_delay:20, 38 | show_success:false, 39 | success_msg:'', 40 | show_error:false, 41 | error_msg:'', 42 | 43 | started:false, 44 | loading:true, 45 | authenticated:false, 46 | 47 | first:'', 48 | last:'', 49 | email:'' 50 | } 51 | }, 52 | watch:{ 53 | authenticated:function(val,oldVal) { 54 | if (val) { router.go({ path:'/dashboard' }) } 55 | else { router.go({ path:'/session/create' }) } 56 | } 57 | }, 58 | methods:{ 59 | setUserData() { 60 | var vm = this 61 | var authToken = vm.$sc.getAuthToken() || {} 62 | vm.first = authToken.first 63 | vm.last = authToken.last 64 | vm.email = authToken.email 65 | }, 66 | alert(msg,type) { 67 | var vm = this 68 | try { 69 | vm[type+'_msg'] = msg 70 | vm['show_'+type] = true 71 | setTimeout(function() { 72 | vm[type+'_msg'] = null 73 | },3000) 74 | }catch(err) {} 75 | }, 76 | logout() { 77 | var vm = this 78 | vm.$root.$sc.emit('session',{ 79 | method:'destroy' 80 | },function(err) { 81 | if (err) { console.log(err);return vm.$root.alert(err,'error') } 82 | }) 83 | } 84 | }, 85 | sockets:{ 86 | connect(status) { 87 | var vm = this 88 | 89 | vm.authenticated = status.isAuthenticated 90 | }, 91 | authenticate() { 92 | this.authenticated = true 93 | this.setUserData() 94 | }, 95 | deauthenticate() { 96 | this.authenticated = false 97 | } 98 | }, 99 | components:{ 100 | alert:VueStrap.alert, 101 | navbar:VueStrap.navbar 102 | }, 103 | ready() { 104 | var vm = this 105 | vm.started = true 106 | } 107 | }), '#app') -------------------------------------------------------------------------------- /examples/mvc-example/public/js/src/system/init.js: -------------------------------------------------------------------------------- 1 | var router = new VueRouter() -------------------------------------------------------------------------------- /examples/mvc-example/server.js: -------------------------------------------------------------------------------- 1 | var SocketCluster = require('socketcluster').SocketCluster, 2 | os = require('os'), 3 | fs = require('fs'), 4 | config = require('./config.json') 5 | 6 | var options = { 7 | workers: os.cpus().length || 1, 8 | brokers: 1, 9 | port: config.port, 10 | wsEngine: 'uws', 11 | appName: config.name || 'sc-app', 12 | workerController: __dirname + '/worker.js', 13 | brokerController: __dirname + '/broker.js', 14 | initController: __dirname + '/init.js', 15 | socketChannelLimit: 1000, 16 | crashWorkerOnError: true 17 | } 18 | 19 | if (config.redis_server) { 20 | options.brokerOptions = { 21 | host:config.redis_server.private_ip || 'localhost', 22 | port:config.redis_server.port || 6379 23 | } 24 | } 25 | 26 | var socketCluster = new SocketCluster(options) 27 | -------------------------------------------------------------------------------- /examples/mvc-example/templates/about/index.jade: -------------------------------------------------------------------------------- 1 | h3 About -------------------------------------------------------------------------------- /examples/mvc-example/templates/dashboard/index.jade: -------------------------------------------------------------------------------- 1 | h3 Dashboard -------------------------------------------------------------------------------- /examples/mvc-example/templates/session/create.jade: -------------------------------------------------------------------------------- 1 | div.row(style='margin-top:100px') 2 | div.col-md-4.col-md-offset-4 3 | div.panel.panel-primary 4 | div.panel-heading 5 | h5 Please Login 6 | div.panel-body 7 | div.form-group 8 | label Email 9 | input.form-control(type='email',v-model='email') 10 | div.form-group 11 | label Password 12 | input.form-control(type='password',v-model='password') 13 | hr 14 | div.form-group 15 | button.btn.btn-success(type='button',@click='login') Login 16 | a.btn.btn-default.pull-right(v-link='{ path:"/users/create" }') Create Account -------------------------------------------------------------------------------- /examples/mvc-example/templates/users/create.jade: -------------------------------------------------------------------------------- 1 | div.row(style='margin-top:100px') 2 | div.col-md-4.col-md-offset-4 3 | div.panel.panel-primary 4 | div.panel-heading 5 | h5 Create Account 6 | div.panel-body 7 | div.form-group 8 | label First 9 | input.form-control(type='text',placeholder='First Name',v-model='first') 10 | div.form-group 11 | label Last 12 | input.form-control(type='text',placeholder='Last Name',v-model='last') 13 | div.form-group 14 | label Email 15 | input.form-control(type='email',placeholder='Email',v-model='email') 16 | div.form-group 17 | label Password 18 | input.form-control(type='password',v-model='password') 19 | hr 20 | div.form-group 21 | button.btn.btn-success(type='button',@click.prevent='create') Create Account 22 | a.btn.btn-default.pull-right(v-link='{ path:"/session/create" }') Go To Login Page -------------------------------------------------------------------------------- /examples/mvc-example/views/home/index.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | head 4 | title SC App 5 | 6 | meta(charset='utf-8') 7 | meta(name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no') 8 | meta(http-equiv='x-ua-compatible',content='ie=edge') 9 | 10 | link(rel='stylesheet',href='/css/bootstrap.min.css') 11 | 12 | body#app 13 | 14 | img(v-show='loading',src='/images/loading.gif',alt='Loading Image',style='position: absolute; margin:auto; left:0; right:0; top:0; bottom:0') 15 | 16 | navbar(v-show='authenticated && !loading && started') 17 | li 18 | a(v-link='{ path:"/dashboard" }') Dashboard 19 | li 20 | a(v-link='{ path:"/about" }') About 21 | li.dropdown(slot='right') 22 | a.dropdown-toggle(data-toggle="dropdown") 23 | span {{ first }} {{ last }} 24 | span.caret 25 | ul.dropdown-menu 26 | li 27 | a(href='/',@click.prevent='logout') Logout 28 | 29 | alert(:show.sync="show_success && loaded",placement="top",duration="3000",type="success",width="400px",dismissable) 30 | strong {{ success_msg }} 31 | alert(:show.sync="show_error && loaded",placement="top",duration="3000",type="danger",width="400px",dismissable) 32 | strong {{ error_msg }} 33 | 34 | div.container-fluid(v-show='!loading && started',style='margin-top:50px') 35 | div.row 36 | div.col-md-12 37 | router-view 38 | 39 | script(src='/js/lib/validate.min.js') 40 | script(src='/js/lib/vue.min.js') 41 | script(src='/js/lib/vue-strap.min.js') 42 | script(src='/js/lib/vue-router.min.js') 43 | script(src='/js/lib/socketcluster.js') 44 | script(src='/js/lib/vue-socketcluster.js') 45 | 46 | script(src='/js/bin/templates.js') 47 | //- Use this if you want to have the minified data. 48 | //-script(src='/js/bin/concat-min.js') 49 | 50 | script(src='/js/src/system/init.js') 51 | 52 | script(src='/js/src/components/about/index.js') 53 | script(src='/js/src/components/dashboard/index.js') 54 | script(src='/js/src/components/session/create.js') 55 | script(src='/js/src/components/users/create.js') 56 | 57 | script(src='/js/src/system/app.js') -------------------------------------------------------------------------------- /examples/mvc-example/worker.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var fs = require('fs'), 4 | express = require('express'), 5 | serveStatic = require('serve-static'), 6 | path = require('path'); 7 | 8 | module.exports.run = function (worker) { 9 | console.log(' >> Worker PID:', process.pid) 10 | 11 | var app = require('express')() 12 | 13 | var httpServer = worker.httpServer 14 | var scServer = worker.scServer 15 | 16 | app.set('views', __dirname+'/views') 17 | app.set('view engine', 'jade') 18 | app.use(serveStatic(path.resolve(__dirname, 'public'))) 19 | 20 | httpServer.on('request', app) 21 | 22 | app.get('*',function(req,res) { 23 | res.render('home/index') 24 | }) 25 | 26 | var controllers = require('./config/controllers.js') 27 | 28 | scServer.on('connection', function (socket) { 29 | console.log(' >> Client',socket.id,'connected at',new Date()) 30 | 31 | console.log('ping sent to',socket.id) 32 | socket.emit('ping') 33 | 34 | socket.on('pong',function() { 35 | console.log('pong received from',socket.id) 36 | }) 37 | 38 | socket.on('ping-with-response',function(data,respond) { 39 | console.log(data) 40 | worker.exchange.publish('broadcast',{message:'Hello from broadcast!'}) 41 | respond(null,{message:'responding..'}) 42 | }) 43 | 44 | // Register Controllers 45 | controllers.register(socket) 46 | }) 47 | 48 | } 49 | -------------------------------------------------------------------------------- /examples/sc-crud-mysql/README.md: -------------------------------------------------------------------------------- 1 | MVC-Example 2 | ====== 3 | 4 | This will be filled out once I am done building the system. -------------------------------------------------------------------------------- /examples/sc-crud-mysql/bin/deploy-staging.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit # Exit on error 4 | 5 | git stash save 'Before Deploy' 6 | git checkout deploy 7 | git merge staging --no-edit 8 | npm run build 9 | if $(git commit -am Deploy); 10 | echo 'Changes committed' 11 | fi 12 | git push origin deploy 13 | git checkout staging 14 | git stash pop -------------------------------------------------------------------------------- /examples/sc-crud-mysql/broker.js: -------------------------------------------------------------------------------- 1 | var scRedis = require('sc-redis') 2 | 3 | module.exports.run = function (broker) { 4 | console.log(' >> Broker PID:', process.pid); 5 | scRedis.attach(broker) 6 | } -------------------------------------------------------------------------------- /examples/sc-crud-mysql/config.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test APP", 3 | "port":3000, 4 | 5 | "db":{ 6 | "host":"localhost", 7 | "user":"", 8 | "password":"", 9 | "database":"mvc_example" 10 | } 11 | } -------------------------------------------------------------------------------- /examples/sc-crud-mysql/config/controllers.js: -------------------------------------------------------------------------------- 1 | 2 | var controllers = { 3 | SessionController:require('../controllers/SessionController.js'), 4 | UsersController:require('../controllers/UsersController.js') 5 | } 6 | 7 | module.exports = { 8 | controllers:controllers, 9 | register(socket) { 10 | 11 | function buildRoute(key) { 12 | socket.on(controllers[key].route,function(data,respond) { 13 | if (typeof data != 'object' || !data.method) return respond('Request must be an object and include a method.') 14 | var method = data.method 15 | if (controllers[key].hasOwnProperty(method)) { 16 | delete data.method 17 | controllers[key][method](data,respond,socket) 18 | } else { 19 | return respond('Method ' + method + ' is not registered') 20 | } 21 | }) 22 | } 23 | 24 | for (key in controllers) { 25 | if (controllers.hasOwnProperty(key) && controllers[key].route) { 26 | buildRoute(key) 27 | } 28 | } 29 | 30 | } 31 | } -------------------------------------------------------------------------------- /examples/sc-crud-mysql/controllers/SessionController.js: -------------------------------------------------------------------------------- 1 | var SessionController = {} 2 | async = require('async'), 3 | Users = require('../models/Users'), 4 | Password = require('../models/Password') 5 | 6 | SessionController.route = 'session' 7 | 8 | SessionController.store = function(data,respond,socket) { 9 | if (!data.email || !data.password) { return respond('You must enter an email and a password.') } 10 | var results = {} 11 | 12 | async.series([ 13 | function(cb) { 14 | Users.findBy({ email:data.email },function(err,users) { 15 | if (err) { return respond(err) } 16 | else if (!users.length) { return respond('There is no account with that email.') } 17 | else { 18 | results.user = users[0] 19 | return cb() 20 | } 21 | }) 22 | }, 23 | function(cb) { 24 | if ( Password.verify(data.password,results.user.password) ) return cb() 25 | else return respond('Invalid password.') 26 | } 27 | ],function() { 28 | delete results.user.password 29 | socket.setAuthToken(results.user) 30 | respond(null,results.user) 31 | }) 32 | } 33 | 34 | SessionController.destroy = function(data,respond,socket) { 35 | socket.deauthenticate() 36 | return respond(null) 37 | } 38 | 39 | module.exports = SessionController -------------------------------------------------------------------------------- /examples/sc-crud-mysql/controllers/UsersController.js: -------------------------------------------------------------------------------- 1 | var UsersController = {} 2 | async = require('async'), 3 | Users = require('../models/Users'), 4 | Password = require('../models/Password') 5 | 6 | UsersController.route = 'users' 7 | 8 | UsersController.store = function(data,respond) { 9 | if (!data.user) { return respond('You must pass in a user object.') } 10 | 11 | Password.hash(data.user.password,function(err,hash) { 12 | if (err) { return respond(err) } 13 | data.user.password = hash 14 | 15 | Users.store(data.user,function(err,new_user) { 16 | if (err) { return respond(err) } 17 | delete new_user.password 18 | return respond(null,new_user) 19 | }) 20 | 21 | }) 22 | } 23 | 24 | UsersController.read = function(data,respond) { 25 | Users.index(data,function(err,users) { 26 | if (err) { console.log(err);return respond('Failed to get user data.') } 27 | return respond(err,users) 28 | }) 29 | } 30 | 31 | module.exports = UsersController -------------------------------------------------------------------------------- /examples/sc-crud-mysql/database.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "dev": { 3 | "host": "localhost", 4 | "user": "", 5 | "password" : "", 6 | "database": "mvc_example", 7 | "driver": "mysql", 8 | "multipleStatements": true 9 | } 10 | } -------------------------------------------------------------------------------- /examples/sc-crud-mysql/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | 3 | gulp.task('default',['minify']) 4 | 5 | gulp.task('minify',function(cb) { 6 | return gulp.src([ 7 | 'public/js/src/system/init.js', 8 | 'public/js/src/components/**/*.js', 9 | 'public/js/src/system/app.js' 10 | ]) 11 | .pipe(require('gulp-babel')({ 12 | presets:['es2015'] 13 | })) 14 | .pipe(require('gulp-concat')('concat.js',{ 15 | newLine:'\n;' 16 | })) 17 | .pipe(require('gulp-uglify')()) 18 | .pipe(require('gulp-minify')()) 19 | .pipe(gulp.dest('public/js/bin')) 20 | }) -------------------------------------------------------------------------------- /examples/sc-crud-mysql/init.js: -------------------------------------------------------------------------------- 1 | module.exports.run = function(thisProcess) { 2 | 3 | } -------------------------------------------------------------------------------- /examples/sc-crud-mysql/migrations/20160826050855-add-users.js: -------------------------------------------------------------------------------- 1 | var dbm = global.dbm || require('db-migrate') 2 | var type = dbm.dataType 3 | var async = require('async') 4 | exports.up = function(db, callback) { 5 | async.series([ 6 | db.createTable.bind(db,'users', { 7 | id: { type: "int", primaryKey:true, autoIncrement: true, notNull: true }, 8 | first: { type: "string", length:100 }, 9 | last: { type: "string", length:100 }, 10 | email: { type: "string", length:100 }, 11 | password: { type: "string", length:100 }, 12 | type_id: { type: "int", length:11, defaultValue:1 }, 13 | reset_pass: { type: "string", length:100 }, 14 | visible: { type: "smallint", length:1, defaultValue:1 } 15 | },function (err) { 16 | if (err) { callback(err);return } 17 | db.connection.query([ 18 | 'ALTER TABLE users', 19 | 'ADD updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP', 20 | 'ON UPDATE CURRENT_TIMESTAMP,', 21 | 'ADD created_at timestamp NOT NULL' 22 | ].join(' '),function (err) { 23 | if (err) { callback(err);return } 24 | db.connection.query([ 25 | 'CREATE TRIGGER users_insert', 26 | 'BEFORE INSERT ON users FOR EACH ROW SET NEW.created_at = CURRENT_TIMESTAMP' 27 | ].join(' '),callback) 28 | }) 29 | }) 30 | ], callback) 31 | } 32 | 33 | exports.down = function(db, callback) { 34 | async.series([ 35 | db.dropTable.bind(db,'users') 36 | ], callback) 37 | } -------------------------------------------------------------------------------- /examples/sc-crud-mysql/models/Lynchpin.js: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Database ORM 3 | */ 4 | 5 | var Lynchpin = function(options) { 6 | var self = this 7 | 8 | // Set some default values 9 | var default_options = { 10 | host:'localhost', 11 | user:'root', 12 | password:'password', 13 | database:'mysql' 14 | } 15 | 16 | // Overwrite the default values with passed in values 17 | if (typeof options == 'object') { 18 | for (var key in options) { 19 | if (options.hasOwnProperty(key)) { 20 | default_options[key] = options[key] 21 | } 22 | } 23 | } 24 | 25 | // We grab a MySQL pool instance 26 | self._pool = require('mysql').createPool(default_options) 27 | } 28 | 29 | /* 30 | * Index 31 | * Gets all records from a table. 32 | */ 33 | Lynchpin.prototype.index = function(args,respond) { 34 | var self = this, 35 | qry = 'SELECT * FROM ??', 36 | params = [self._table], 37 | i = 0 38 | args = args || {} 39 | for (var key in args.fields) { 40 | if (i > 0) { qry += ' AND ' + key + ' = ?' } 41 | else { qry += ' WHERE ' + key + ' = ?' } 42 | params.push(args.fields[key]) 43 | i++ 44 | } 45 | if (args.limit) { 46 | qry += ' LIMIT ' + args.limit 47 | } 48 | if (args.offset) { 49 | qry += ' OFFSET ' + args.offset 50 | } 51 | self._pool.query(args.qry || qry,params,function(err,rows) { 52 | if (err) { return respond(err) } 53 | return respond(null,rows) 54 | }) 55 | } 56 | 57 | 58 | /* 59 | * Find 60 | * Find a record from a table. 61 | */ 62 | Lynchpin.prototype.find = function(id,respond) { 63 | var self = this 64 | self._pool.query('SELECT * FROM ?? WHERE id = ?',[self._table,id],function(err,rows) { 65 | if (err) { return respond(err) } 66 | return respond(null, (rows.length ? rows[0] : null) ) 67 | }) 68 | } 69 | 70 | /* 71 | * FindBy 72 | * Gets all records from a table matching the passed in fields. 73 | */ 74 | Lynchpin.prototype.findBy = function(fields,respond) { 75 | var self = this 76 | var qry = 'SELECT * FROM ??', 77 | params = [self._table], 78 | i = 0 79 | for (var key in fields) { 80 | if (i > 0) { qry += ' AND ' + key + ' = ?' } 81 | else { qry += ' WHERE ' + key + ' = ?' } 82 | params.push(fields[key]) 83 | i++ 84 | } 85 | self._pool.query(qry,params,function(err,rows) { 86 | if (err) { return respond(err) } 87 | return respond(null,rows) 88 | }) 89 | } 90 | 91 | /* 92 | * FindByAnd 93 | * Duplicate of FindBy. 94 | */ 95 | Lynchpin.prototype.findByAnd = function(fields,respond) { 96 | var self = this 97 | var qry = 'SELECT * FROM ??', 98 | params = [self._table], 99 | i = 0 100 | for (var key in fields) { 101 | if (i > 0) { qry += ' AND ' + key + ' = ?' } 102 | else { qry += ' WHERE ' + key + ' = ?' } 103 | params.push(fields[key]) 104 | i++ 105 | } 106 | self._pool.query(qry,params,function(err,rows) { 107 | if (err) { return respond(err) } 108 | return respond(null,rows) 109 | }) 110 | } 111 | 112 | /* 113 | * FindByOr 114 | * Same as FindBy but it uses OR instead of AND. 115 | */ 116 | Lynchpin.prototype.findByOr = function(fields,respond) { 117 | var self = this 118 | var qry = 'SELECT * FROM ??', 119 | params = [self._table], 120 | i = 0 121 | for (var key in fields) { 122 | if (i > 0) { qry += ' OR ' + key + ' = ?' } 123 | else { qry += ' WHERE ' + key + ' = ?' } 124 | params.push(fields[key]) 125 | i++ 126 | } 127 | self._pool.query(qry,params,function(err,rows) { 128 | if (err) { return respond(err) } 129 | return respond(null,rows) 130 | }) 131 | } 132 | 133 | /* 134 | * Store 135 | * Store a record and return it after completion. 136 | */ 137 | Lynchpin.prototype.store = function(post,respond) { 138 | var self = this 139 | self._pool.query('INSERT INTO ?? SET ?',[self._table,post],function(err,rows) { 140 | if (err) { return respond(err) } 141 | self.find(rows.insertId,respond) 142 | }) 143 | } 144 | 145 | // Locate record if not found create it and return it 146 | Lynchpin.prototype.firstOrCreate = function(fields,respond) { 147 | var self = this 148 | self.findBy(fields,function(err,records) { 149 | if (err) { return respond(err) } 150 | else if (rows.length) { return respond(null,records[0]) } 151 | else { 152 | self.store(fields,function(err2,new_obj) { 153 | if (err) { return respond(err2) } 154 | self.find(new_obj.id,respond) 155 | }) 156 | } 157 | }) 158 | } 159 | 160 | // // Locate record if not found NOT create it and return it 161 | // Lynchpin.prototype.firstOrNew = function(respond) { 162 | 163 | // } 164 | 165 | /* 166 | * Update 167 | * Update a record and return it after completion. 168 | */ 169 | Lynchpin.prototype.update = function(id,put,respond) { 170 | var self = this 171 | self._pool.query('UPDATE ?? SET ? WHERE id = ?',[self._table,put,id],function(err,rows) { 172 | if (err) { return respond(err) } 173 | self.find(id,respond) 174 | }) 175 | } 176 | 177 | /* 178 | * Destroy 179 | * Destroy a record. 180 | */ 181 | Lynchpin.prototype.destroy = function(id,respond) { 182 | var self = this 183 | self._pool.query('DELETE FROM ?? WHERE id = ?',[self._table,id],function(err,rows) { 184 | if (err) { return respond(err) } 185 | return respond(null) 186 | }) 187 | } 188 | 189 | module.exports = Lynchpin -------------------------------------------------------------------------------- /examples/sc-crud-mysql/models/Password.js: -------------------------------------------------------------------------------- 1 | var bcrypt = require('bcrypt') 2 | var Password = function() { 3 | var self = this 4 | } 5 | 6 | Password.prototype.hash = function(pwd,respond) { 7 | if (!pwd || typeof pwd != 'string') { return respond('Password must be a valid string.') } 8 | return respond(null,bcrypt.hashSync(pwd,bcrypt.genSaltSync(10))) 9 | } 10 | 11 | Password.prototype.verify = function(pwd,hash) { 12 | return bcrypt.compareSync(pwd,hash) 13 | } 14 | 15 | module.exports = new Password() -------------------------------------------------------------------------------- /examples/sc-crud-mysql/models/Users.js: -------------------------------------------------------------------------------- 1 | var Lynchpin = require('./Lynchpin') 2 | 3 | var Users = function() { 4 | var self = this 5 | 6 | self._init() 7 | } 8 | 9 | Users.prototype = Object.create(Lynchpin.prototype) 10 | 11 | Users.prototype._init = function() { 12 | var self = this 13 | 14 | self._table = 'users' 15 | Lynchpin.call(self,require('../config.json').db) 16 | } 17 | 18 | module.exports = new Users() -------------------------------------------------------------------------------- /examples/sc-crud-mysql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mvc-example", 3 | "description": "A sample Vue-Socketcluster app", 4 | "version": "1.1.0", 5 | "contributors": [ 6 | { 7 | "name": "Nick Kotenberg", 8 | "email": "nkotenberg@happilymarrieddad@yahoo.com" 9 | }, 10 | { 11 | "name": "Jonathan Gros-Dubois", 12 | "email": "grosjona@yahoo.com.au" 13 | } 14 | ], 15 | "scripts": { 16 | "start": "npm run build && npm run update-database && node server.js", 17 | "start:watch": "watch 'npm run build' && node server.js", 18 | "test": "npm run build", 19 | "build": "npm run build-jade & npm run build-js", 20 | "build-jade": "templatizer -d /templates -o public/js/bin/templates.js", 21 | "build-js": "gulp", 22 | "deploy": "./bin/deploy-staging.sh", 23 | "update-database": "db-migrate up", 24 | "preinstall": "sudo npm install gulp templatizer@1.5.4 watch -g", 25 | "postinstall": "npm run build" 26 | }, 27 | "dependencies": { 28 | "async": "^2.0.1", 29 | "babel-preset-es2015": "^6.14.0", 30 | "babel-preset-react": "^6.11.1", 31 | "bcrypt": "^0.8.7", 32 | "connect": "3.0.1", 33 | "express": "4.13.1", 34 | "gulp": "^3.9.1", 35 | "gulp-babel": "^6.1.2", 36 | "gulp-concat": "^2.6.0", 37 | "gulp-minify": "0.0.14", 38 | "gulp-uglify": "^2.0.0", 39 | "jade": "^1.11.0", 40 | "minimist": "1.1.0", 41 | "mysql": "^2.11.1", 42 | "npm-watch": "^0.1.6", 43 | "sc-cluster-broker-client": "1.x.x", 44 | "sc-crud-mysql": "file:sc-crud-mysql-0.0.1.tgz", 45 | "sc-redis": "*", 46 | "serve-static": "1.8.0", 47 | "socket.io": "^1.4.8", 48 | "socketcluster": "5.x.x", 49 | "socketcluster-client": "5.x.x", 50 | "templatizer": "^1.5.4", 51 | "vue-socket.io": "^1.0.2" 52 | }, 53 | "devDependencies": { 54 | "babel-preset-es2015": "^6.14.0", 55 | "babel-preset-react": "^6.11.1", 56 | "gulp": "^3.9.1", 57 | "gulp-babel": "^6.1.2", 58 | "gulp-concat": "^2.6.0", 59 | "gulp-minify": "0.0.14", 60 | "gulp-uglify": "^2.0.0", 61 | "templatizer": "^1.5.4" 62 | }, 63 | "keywords": [ 64 | "websocket", 65 | "server", 66 | "realtime", 67 | "cluster", 68 | "scalable" 69 | ], 70 | "readmeFilename": "README.md" 71 | } 72 | -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happilymarrieddad/vue-socketcluster/11224796b6528362dc5d893ba4c9f021e41889ef/examples/sc-crud-mysql/public/images/loading.gif -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/bin/concat-min.js: -------------------------------------------------------------------------------- 1 | "use strict";var router=new VueRouter,aboutComponent=Vue.extend({template:templatizer.about.index({}),data:function(){return{ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(o){o?(e.$root.alert(o,"error"),router.go("/")):(e.ready=!0,t.next())})},waitForData:!0}}),dashboardComponent=Vue.extend({template:templatizer.dashboard.index({}),data:function(){return{ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(o){o?(e.$root.alert(o,"error"),router.go("/")):(e.ready=!0,t.next())})},waitForData:!0}}),usersCreateComponent=Vue.extend({template:templatizer.users.create({}),data:function(){return{first:"",last:"",email:"",password:"",ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)},clearFields:function(){this.first="",this.last="",this.email="",this.password=""},create:function(){var t=this;validate.async({first:t.first,last:t.last,email:t.email,password:t.password,visible:1},{first:{presence:!0},last:{presence:!0},email:{presence:!0},password:{presence:!0}}).then(function(e){t.$root.$sc.emit("users",{method:"store",user:e},function(e,o){return e?t.$root.alert(e,"error"):(t.$root.alert("Successfully created account "+o.email+"! Please log in.","success"),void router.go({path:"/session/create"}))})},function(e){var o=null;for(var r in e){o=e[r][0];break}t.$root.alert(o,"error")})}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(o){o?t.abort():(e.ready=!0,e.clearFields(),t.next())})},waitForData:!0}}),usersComponent=Vue.extend({template:templatizer.users.index({}),data:function(){return{users:[],page:1,limit:25,num_users:0,total_users:0,pagination:!1,first:"",last:"",email:"",password:"",confirm_password:"",ready:!1}},watch:{search:function(t,e){this.fetchData(null)}},computed:{num_users:function(){return this.users.length},pagination:function(){return this.total_users>=this.limit}},methods:{delete:function(){},create:function(){},fetchData:function(t,e){var o=this;o.$dispatch("read",{table:"users",limit:o.limit,offset:(o.page-1)*o.limit,search:o.search},function(t,r){return t?o.$root.transitionError(t):e&&"function"==typeof e?e(r):void(o.users=r)})},updateData:function(t){this.users=t}},events:{"users-update":function(t){console.log(t)},"users-destroy":function(t){console.log(t)},"users-store":function(t){console.log(t)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData({},function(e){t.next({ready:!0,users:e})})},waitForData:!0}}),sessionCreateComponent=Vue.extend({template:templatizer.session.create({}),data:function(){return{email:"",password:"",ready:!1}},methods:{login:function(){var t=this;validate.async({email:t.email,password:t.password},{email:{presence:!0},password:{presence:!0}}).then(function(e){t.$root.$sc.emit("session",{method:"store",email:e.email,password:e.password},function(e){if(e)return console.log(e),t.$root.alert(e,"error")})},function(e){var o=null;for(var r in e){o=e[r][0];break}t.$root.alert(o,"error")})}},ready:function(){},route:{data:function(t){var e=this;e.ready=!0,t.next()}}}),_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol?"symbol":typeof t};Vue.use(VueRouter),Vue.use(VueSocketcluster),router.map({"/dashboard":{component:dashboardComponent,auth:!0},"/about":{component:aboutComponent,auth:!0},"/users":{component:usersComponent,auth:!0},"/session/create":{component:sessionCreateComponent,auth:!1},"/users/create":{component:usersCreateComponent,auth:!1}}),router.redirect({"*":"/dashboard"}),router.beforeEach(function(t){router.app.loading=!0,t.to.auth&&"authenticated"!=router.app.$sc.authState?(router.app.loading=!1,t.redirect("/session/create")):"authenticated"!=router.app.$sc.authState||t.to.auth?t.next():(router.app.loading=!1,t.redirect("/dashboard"))}),router.afterEach(function(t){setTimeout(function(){router.app.loading=!1},router.app.loading_delay)}),router.start(Vue.extend({data:function(){return{loading_delay:20,show_success:!1,success_msg:"",show_error:!1,error_msg:"",started:!1,loading:!0,authenticated:!1,first:"",last:"",email:""}},watch:{authenticated:function(t,e){t?router.go({path:"/dashboard"}):router.go({path:"/session/create"})}},methods:{transitionError:function(t){this.alert(t,"error"),router.go({path:"/dashboard"})},setUserData:function(){var t=this,e=t.$sc.getAuthToken()||{};t.first=e.first,t.last=e.last,t.email=e.email},alert:function(t,e){var o=this;try{o[e+"_msg"]=t,o["show_"+e]=!0,setTimeout(function(){o[e+"_msg"]=null},3e3)}catch(t){}},logout:function(){var t=this;t.$root.$sc.emit("session",{method:"destroy"},function(e){if(e)return console.log(e),t.$root.alert(e,"error")})}},events:{update:function(t){},destroy:function(t){},store:function(t){},read:function(t,e){var o=this;if(t&&t.table&&"string"==typeof t.table){var r=t.table;return delete t.table,t.method="read",o.$root.$sc.emit(r,t,e&&"function"==typeof e?e:function(){}),!1}return!0}},sockets:{connect:function(t){var e=this;e.authenticated=t.isAuthenticated},authenticate:function(){this.authenticated=!0,this.setUserData()},deauthenticate:function(){this.authenticated=!1},update:function(t,e){var o=this;"object"==("undefined"==typeof t?"undefined":_typeof(t))&&t.table&&o.$broadcast(t.table+"-update",t,e&&"function"==typeof e?e:function(){})},destroy:function(t){var e=this;"object"==("undefined"==typeof t?"undefined":_typeof(t))&&t.table&&e.$broadcast(t.table+"-destroy",t,respond&&"function"==typeof respond?respond:function(){})},store:function(t){var e=this;"object"==("undefined"==typeof t?"undefined":_typeof(t))&&t.table&&e.$broadcast(t.table+"-store",t,respond&&"function"==typeof respond?respond:function(){})}},components:{alert:VueStrap.alert,navbar:VueStrap.navbar},ready:function(){var t=this;t.started=!0}}),"#app"); -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/bin/concat.js: -------------------------------------------------------------------------------- 1 | "use strict";var router=new VueRouter,aboutComponent=Vue.extend({template:templatizer.about.index({}),data:function(){return{ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(o){o?(e.$root.alert(o,"error"),router.go("/")):(e.ready=!0,t.next())})},waitForData:!0}}),dashboardComponent=Vue.extend({template:templatizer.dashboard.index({}),data:function(){return{ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(o){o?(e.$root.alert(o,"error"),router.go("/")):(e.ready=!0,t.next())})},waitForData:!0}}),usersCreateComponent=Vue.extend({template:templatizer.users.create({}),data:function(){return{first:"",last:"",email:"",password:"",ready:!1}},methods:{fetchData:function(t){t&&"function"==typeof t&&t(null)},clearFields:function(){this.first="",this.last="",this.email="",this.password=""},create:function(){var t=this;validate.async({first:t.first,last:t.last,email:t.email,password:t.password,visible:1},{first:{presence:!0},last:{presence:!0},email:{presence:!0},password:{presence:!0}}).then(function(e){t.$root.$sc.emit("users",{method:"store",user:e},function(e,o){return e?t.$root.alert(e,"error"):(t.$root.alert("Successfully created account "+o.email+"! Please log in.","success"),void router.go({path:"/session/create"}))})},function(e){var o=null;for(var r in e){o=e[r][0];break}t.$root.alert(o,"error")})}},ready:function(){},route:{data:function(t){var e=this;e.fetchData(function(o){o?t.abort():(e.ready=!0,e.clearFields(),t.next())})},waitForData:!0}}),usersComponent=Vue.extend({template:templatizer.users.index({}),data:function(){return{users:[],page:1,limit:25,num_users:0,total_users:0,pagination:!1,first:"",last:"",email:"",password:"",confirm_password:"",ready:!1}},watch:{search:function(t,e){this.fetchData(null)}},computed:{num_users:function(){return this.users.length},pagination:function(){return this.total_users>=this.limit}},methods:{delete:function(){},create:function(){},fetchData:function(t,e){var o=this;o.$dispatch("read",{table:"users",limit:o.limit,offset:(o.page-1)*o.limit,search:o.search},function(t,r){return t?o.$root.transitionError(t):e&&"function"==typeof e?e(r):void(o.users=r)})},updateData:function(t){this.users=t}},events:{"users-update":function(t){console.log(t)},"users-destroy":function(t){console.log(t)},"users-store":function(t){console.log(t)}},ready:function(){},route:{data:function(t){var e=this;e.fetchData({},function(e){t.next({ready:!0,users:e})})},waitForData:!0}}),sessionCreateComponent=Vue.extend({template:templatizer.session.create({}),data:function(){return{email:"",password:"",ready:!1}},methods:{login:function(){var t=this;validate.async({email:t.email,password:t.password},{email:{presence:!0},password:{presence:!0}}).then(function(e){t.$root.$sc.emit("session",{method:"store",email:e.email,password:e.password},function(e){if(e)return console.log(e),t.$root.alert(e,"error")})},function(e){var o=null;for(var r in e){o=e[r][0];break}t.$root.alert(o,"error")})}},ready:function(){},route:{data:function(t){var e=this;e.ready=!0,t.next()}}}),_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol?"symbol":typeof t};Vue.use(VueRouter),Vue.use(VueSocketcluster),router.map({"/dashboard":{component:dashboardComponent,auth:!0},"/about":{component:aboutComponent,auth:!0},"/users":{component:usersComponent,auth:!0},"/session/create":{component:sessionCreateComponent,auth:!1},"/users/create":{component:usersCreateComponent,auth:!1}}),router.redirect({"*":"/dashboard"}),router.beforeEach(function(t){router.app.loading=!0,t.to.auth&&"authenticated"!=router.app.$sc.authState?(router.app.loading=!1,t.redirect("/session/create")):"authenticated"!=router.app.$sc.authState||t.to.auth?t.next():(router.app.loading=!1,t.redirect("/dashboard"))}),router.afterEach(function(t){setTimeout(function(){router.app.loading=!1},router.app.loading_delay)}),router.start(Vue.extend({data:function(){return{loading_delay:20,show_success:!1,success_msg:"",show_error:!1,error_msg:"",started:!1,loading:!0,authenticated:!1,first:"",last:"",email:""}},watch:{authenticated:function(t,e){t?router.go({path:"/dashboard"}):router.go({path:"/session/create"})}},methods:{transitionError:function(t){this.alert(t,"error"),router.go({path:"/dashboard"})},setUserData:function(){var t=this,e=t.$sc.getAuthToken()||{};t.first=e.first,t.last=e.last,t.email=e.email},alert:function(t,e){var o=this;try{o[e+"_msg"]=t,o["show_"+e]=!0,setTimeout(function(){o[e+"_msg"]=null},3e3)}catch(t){}},logout:function(){var t=this;t.$root.$sc.emit("session",{method:"destroy"},function(e){if(e)return console.log(e),t.$root.alert(e,"error")})}},events:{update:function(t){},destroy:function(t){},store:function(t){},read:function(t,e){var o=this;if(t&&t.table&&"string"==typeof t.table){var r=t.table;return delete t.table,t.method="read",o.$root.$sc.emit(r,t,e&&"function"==typeof e?e:function(){}),!1}return!0}},sockets:{connect:function(t){var e=this;e.authenticated=t.isAuthenticated},authenticate:function(){this.authenticated=!0,this.setUserData()},deauthenticate:function(){this.authenticated=!1},update:function(t,e){var o=this;"object"==("undefined"==typeof t?"undefined":_typeof(t))&&t.table&&o.$broadcast(t.table+"-update",t,e&&"function"==typeof e?e:function(){})},destroy:function(t){var e=this;"object"==("undefined"==typeof t?"undefined":_typeof(t))&&t.table&&e.$broadcast(t.table+"-destroy",t,respond&&"function"==typeof respond?respond:function(){})},store:function(t){var e=this;"object"==("undefined"==typeof t?"undefined":_typeof(t))&&t.table&&e.$broadcast(t.table+"-store",t,respond&&"function"==typeof respond?respond:function(){})}},components:{alert:VueStrap.alert,navbar:VueStrap.navbar},ready:function(){var t=this;t.started=!0}}),"#app"); -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/bin/templates.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory(); 6 | } else { 7 | if (typeof root === 'undefined' || root !== Object(root)) { 8 | throw new Error('templatizer: window does not exist or is not an object'); 9 | } 10 | root.templatizer = factory(); 11 | } 12 | }(this, function () { 13 | var jade=function(){function n(n){return null!=n&&""!==n}function t(e){return(Array.isArray(e)?e.map(t):e&&"object"==typeof e?Object.keys(e).filter(function(n){return e[n]}):[e]).filter(n).join(" ")}function e(n){return i[n]||n}function r(n){var t=String(n).replace(o,e);return t===""+n?n:t}var a={};a.merge=function t(e,r){if(1===arguments.length){for(var a=e[0],i=1;i":">",'"':"""},o=/[&<>"]/g;return a.escape=r,a.rethrow=function n(t,e,r,a){if(!(t instanceof Error))throw t;if(!("undefined"==typeof window&&e||a))throw t.message+=" on line "+r,t;try{a=a||require("fs").readFileSync(e,"utf8")}catch(e){n(t,null,r)}var i=3,o=a.split("\n"),s=Math.max(r-i,0),f=Math.min(o.length,r+i),i=o.slice(s,f).map(function(n,t){var e=t+s+1;return(e==r?" > ":" ")+e+"| "+n}).join("\n");throw t.path=e,t.message=(e||"Jade")+":"+r+"\n"+i+"\n\n"+t.message,t},a.DebugItem=function(n,t){this.lineno=n,this.filename=t},a}(); 14 | 15 | var templatizer = {}; 16 | templatizer["about"] = {}; 17 | templatizer["dashboard"] = {}; 18 | templatizer["session"] = {}; 19 | templatizer["users"] = {}; 20 | 21 | // about/index.jade compiled template 22 | templatizer["about"]["index"] = function tmpl_about_index() { 23 | return "

About

"; 24 | }; 25 | 26 | // dashboard/index.jade compiled template 27 | templatizer["dashboard"]["index"] = function tmpl_dashboard_index() { 28 | return "

Dashboard

"; 29 | }; 30 | 31 | // session/create.jade compiled template 32 | templatizer["session"]["create"] = function tmpl_session_create() { 33 | return '
Please Login

'; 34 | }; 35 | 36 | // users/create.jade compiled template 37 | templatizer["users"]["create"] = function tmpl_users_create() { 38 | return '
Create Account

Go To Login Page
'; 39 | }; 40 | 41 | // users/index.jade compiled template 42 | templatizer["users"]["index"] = function tmpl_users_index() { 43 | return '
NameEmail
{{ user.name }}{{ user.first }} {{ user.last }}{{ user.email }}
No users
'; 44 | }; 45 | 46 | return templatizer; 47 | })); 48 | -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/lib/validate.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * validate.js 0.10.0 3 | * http://validatejs.org/ 4 | * (c) 2013-2015 Nicklas Ansman, 2013 Wrapp 5 | * validate.js may be freely distributed under the MIT license. 6 | */ 7 | 8 | (function(a,b,c){"use strict";var d=function(a,b,c){c=e.extend({},e.options,c);var f,g,h=e.runValidations(a,b,c);for(f in h)for(g in h[f])if(e.isPromise(h[f][g]))throw new Error("Use validate.async if you want support for promises");return d.processValidationResults(h,c)},e=d;e.extend=function(a){return[].slice.call(arguments,1).forEach(function(b){for(var c in b)a[c]=b[c]}),a},e.extend(d,{version:{major:0,minor:10,patch:0,metadata:null,toString:function(){var a=e.format("%{major}.%{minor}.%{patch}",e.version);return e.isEmpty(e.version.metadata)||(a+="+"+e.version.metadata),a}},Promise:"undefined"!=typeof Promise?Promise:null,EMPTY_STRING_REGEXP:/^\s*$/,runValidations:function(a,b,c){var d,f,g,h,i,j,k,l=[];(e.isDomElement(a)||e.isJqueryElement(a))&&(a=e.collectFormValues(a));for(d in b){g=e.getDeepObjectValue(a,d),h=e.result(b[d],g,a,d,c,b);for(f in h){if(i=e.validators[f],!i)throw k=e.format("Unknown validator %{name}",{name:f}),new Error(k);j=h[f],j=e.result(j,g,a,d,c,b),j&&l.push({attribute:d,value:g,validator:f,globalOptions:c,attributes:a,options:j,error:i.call(i,g,j,d,a,c)})}}return l},processValidationResults:function(a,b){var c;switch(a=e.pruneEmptyErrors(a,b),a=e.expandMultipleErrors(a,b),a=e.convertErrorMessages(a,b),b.format||"grouped"){case"detailed":break;case"flat":a=e.flattenErrorsToArray(a);break;case"grouped":a=e.groupErrorsByAttribute(a);for(c in a)a[c]=e.flattenErrorsToArray(a[c]);break;default:throw new Error(e.format("Unknown format %{format}",b))}return e.isEmpty(a)?void 0:a},async:function(a,b,c){c=e.extend({},e.async.options,c);var d=c.wrapErrors||function(a){return a};c.cleanAttributes!==!1&&(a=e.cleanAttributes(a,b));var f=e.runValidations(a,b,c);return new e.Promise(function(g,h){e.waitForResults(f).then(function(){var i=e.processValidationResults(f,c);i?h(new d(i,c,a,b)):g(a)},function(a){h(a)})})},single:function(a,b,c){return c=e.extend({},e.single.options,c,{format:"flat",fullMessages:!1}),e({single:a},{single:b},c)},waitForResults:function(a){return a.reduce(function(a,b){return e.isPromise(b.error)?a.then(function(){return b.error.then(function(a){b.error=a||null},function(a){if(a instanceof Error)throw a;e.error("Rejecting promises with the result is deprecated. Please use the resolve callback instead."),b.error=a})}):a},new e.Promise(function(a){a()}))},result:function(a){var b=[].slice.call(arguments,1);return"function"==typeof a&&(a=a.apply(null,b)),a},isNumber:function(a){return"number"==typeof a&&!isNaN(a)},isFunction:function(a){return"function"==typeof a},isInteger:function(a){return e.isNumber(a)&&a%1===0},isBoolean:function(a){return"boolean"==typeof a},isObject:function(a){return a===Object(a)},isDate:function(a){return a instanceof Date},isDefined:function(a){return null!==a&&void 0!==a},isPromise:function(a){return!!a&&e.isFunction(a.then)},isJqueryElement:function(a){return a&&e.isString(a.jquery)},isDomElement:function(a){return a&&a.querySelectorAll&&a.querySelector?e.isObject(document)&&a===document?!0:"object"==typeof HTMLElement?a instanceof HTMLElement:a&&"object"==typeof a&&null!==a&&1===a.nodeType&&"string"==typeof a.nodeName:!1},isEmpty:function(a){var b;if(!e.isDefined(a))return!0;if(e.isFunction(a))return!1;if(e.isString(a))return e.EMPTY_STRING_REGEXP.test(a);if(e.isArray(a))return 0===a.length;if(e.isDate(a))return!1;if(e.isObject(a)){for(b in a)return!1;return!0}return!1},format:e.extend(function(a,b){return e.isString(a)?a.replace(e.format.FORMAT_REGEXP,function(a,c,d){return"%"===c?"%{"+d+"}":String(b[d])}):a},{FORMAT_REGEXP:/(%?)%\{([^\}]+)\}/g}),prettify:function(a){return e.isNumber(a)?100*a%1===0?""+a:parseFloat(Math.round(100*a)/100).toFixed(2):e.isArray(a)?a.map(function(a){return e.prettify(a)}).join(", "):e.isObject(a)?a.toString():(a=""+a,a.replace(/([^\s])\.([^\s])/g,"$1 $2").replace(/\\+/g,"").replace(/[_-]/g," ").replace(/([a-z])([A-Z])/g,function(a,b,c){return""+b+" "+c.toLowerCase()}).toLowerCase())},stringifyValue:function(a){return e.prettify(a)},isString:function(a){return"string"==typeof a},isArray:function(a){return"[object Array]"==={}.toString.call(a)},isHash:function(a){return e.isObject(a)&&!e.isArray(a)&&!e.isFunction(a)},contains:function(a,b){return e.isDefined(a)?e.isArray(a)?-1!==a.indexOf(b):b in a:!1},unique:function(a){return e.isArray(a)?a.filter(function(a,b,c){return c.indexOf(a)==b}):a},forEachKeyInKeypath:function(a,b,c){if(e.isString(b)){var d,f="",g=!1;for(d=0;dk&&(d=b.tooShort||this.tooShort||"is too short (minimum is %{count} characters)",j.push(e.format(d,{count:h}))),e.isNumber(g)&&k>g&&(d=b.tooLong||this.tooLong||"is too long (maximum is %{count} characters)",j.push(e.format(d,{count:g}))),j.length>0?b.message||j:void 0):(e.error(e.format("Attribute %{attr} has a non numeric value for `length`",{attr:c})),b.message||this.notValid||"has an incorrect length")}},numericality:function(a,b){if(!e.isEmpty(a)){b=e.extend({},this.options,b);var c,d,f=[],g={greaterThan:function(a,b){return a>b},greaterThanOrEqualTo:function(a,b){return a>=b},equalTo:function(a,b){return a===b},lessThan:function(a,b){return b>a},lessThanOrEqualTo:function(a,b){return b>=a},divisibleBy:function(a,b){return a%b===0}};if(e.isString(a)&&b.strict){var h="^(0|[1-9]\\d*)";if(b.onlyInteger||(h+="(\\.\\d+)?"),h+="$",!new RegExp(h).test(a))return b.message||b.notValid||this.notValid||"must be a valid number"}if(b.noStrings!==!0&&e.isString(a)&&(a=+a),!e.isNumber(a))return b.message||b.notValid||this.notValid||"is not a number";if(b.onlyInteger&&!e.isInteger(a))return b.message||b.notInteger||this.notInteger||"must be an integer";for(c in g)if(d=b[c],e.isNumber(d)&&!g[c](a,d)){var i="not"+e.capitalize(c),j=b[i]||this[i]||"must be %{type} %{count}";f.push(e.format(j,{count:d,type:e.prettify(c)}))}return b.odd&&a%2!==1&&f.push(b.notOdd||this.notOdd||"must be odd"),b.even&&a%2!==0&&f.push(b.notEven||this.notEven||"must be even"),f.length?b.message||f:void 0}},datetime:e.extend(function(a,b){if(!e.isFunction(this.parse)||!e.isFunction(this.format))throw new Error("Both the parse and format functions needs to be set to use the datetime/date validator");if(!e.isEmpty(a)){b=e.extend({},this.options,b);var c,d=[],f=b.earliest?this.parse(b.earliest,b):NaN,g=b.latest?this.parse(b.latest,b):NaN;return a=this.parse(a,b),isNaN(a)||b.dateOnly&&a%864e5!==0?(c=b.notValid||b.message||this.notValid||"must be a valid date",e.format(c,{value:arguments[0]})):(!isNaN(f)&&f>a&&(c=b.tooEarly||b.message||this.tooEarly||"must be no earlier than %{date}",c=e.format(c,{value:this.format(a,b),date:this.format(f,b)}),d.push(c)),!isNaN(g)&&a>g&&(c=b.tooLate||b.message||this.tooLate||"must be no later than %{date}",c=e.format(c,{date:this.format(g,b),value:this.format(a,b)}),d.push(c)),d.length?e.unique(d):void 0)}},{parse:null,format:null}),date:function(a,b){return b=e.extend({},b,{dateOnly:!0}),e.validators.datetime.call(e.validators.datetime,a,b)},format:function(a,b){(e.isString(b)||b instanceof RegExp)&&(b={pattern:b}),b=e.extend({},this.options,b);var c,d=b.message||this.message||"is invalid",f=b.pattern;return e.isEmpty(a)?void 0:e.isString(a)?(e.isString(f)&&(f=new RegExp(b.pattern,b.flags)),c=f.exec(a),c&&c[0].length==a.length?void 0:d):d},inclusion:function(a,b){if(!e.isEmpty(a)&&(e.isArray(b)&&(b={within:b}),b=e.extend({},this.options,b),!e.contains(b.within,a))){var c=b.message||this.message||"^%{value} is not included in the list";return e.format(c,{value:a})}},exclusion:function(a,b){if(!e.isEmpty(a)&&(e.isArray(b)&&(b={within:b}),b=e.extend({},this.options,b),e.contains(b.within,a))){var c=b.message||this.message||"^%{value} is restricted";return e.format(c,{value:a})}},email:e.extend(function(a,b){b=e.extend({},this.options,b);var c=b.message||this.message||"is not a valid email";if(!e.isEmpty(a))return e.isString(a)&&this.PATTERN.exec(a)?void 0:c},{PATTERN:/^[a-z0-9\u007F-\uffff!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9\u007F-\uffff!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$/i}),equality:function(a,b,c,d){if(!e.isEmpty(a)){e.isString(b)&&(b={attribute:b}),b=e.extend({},this.options,b);var f=b.message||this.message||"is not equal to %{attribute}";if(e.isEmpty(b.attribute)||!e.isString(b.attribute))throw new Error("The attribute must be a non empty string");var g=e.getDeepObjectValue(d,b.attribute),h=b.comparator||function(a,b){return a===b};return h(a,g,b,c,d)?void 0:e.format(f,{attribute:e.prettify(b.attribute)})}},url:function(a,b){if(!e.isEmpty(a)){b=e.extend({},this.options,b);var c=b.message||this.message||"is not a valid url",d=b.schemes||this.schemes||["http","https"],f=b.allowLocal||this.allowLocal||!1;if(!e.isString(a))return c;var g="^(?:(?:"+d.join("|")+"):\\/\\/)(?:\\S+(?::\\S*)?@)?";g+="(?:";var h="(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))";f?h+="?":g+="(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})";var i="(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*"+h+")";g+="(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|"+i+"(?::\\d{2,5})?(?:\\/[^\\s]*)?$";var j=new RegExp(g,"i");return j.exec(a)?void 0:c}}},d.exposeModule(d,this,a,b,c)}).call(this,"undefined"!=typeof exports?exports:null,"undefined"!=typeof module?module:null,"undefined"!=typeof define?define:null); 9 | //# sourceMappingURL=validate.min.map -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/lib/vue-router.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-router v0.7.13 3 | * (c) 2016 Evan You 4 | * Released under the MIT License. 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueRouter=e()}(this,function(){"use strict";function t(t,e,n){this.path=t,this.matcher=e,this.delegate=n}function e(t){this.routes={},this.children={},this.target=t}function n(e,r,i){return function(o,a){var s=e+o;return a?void a(n(s,r,i)):new t(e+o,r,i)}}function r(t,e,n){for(var r=0,i=0,o=t.length;o>i;i++)r+=t[i].path.length;e=e.substr(r);var a={path:e,handler:n};t.push(a)}function i(t,e,n,o){var a=e.routes;for(var s in a)if(a.hasOwnProperty(s)){var h=t.slice();r(h,s,a[s]),e.children[s]?i(h,e.children[s],n,o):n.call(o,h)}}function o(t,r){var o=new e;t(n("",o,this.delegate)),i([],o,function(t){r?r(this,t):this.add(t)},this)}function a(t){B||"undefined"==typeof console||console.error("[vue-router] "+t)}function s(t,e){try{return e?decodeURIComponent(t):decodeURI(t)}catch(n){a("malformed URI"+(e?" component: ":": ")+t)}}function h(t){return"[object Array]"===Object.prototype.toString.call(t)}function c(t){this.string=t}function u(t){this.name=t}function l(t){this.name=t}function p(){}function f(t,e,n){"/"===t.charAt(0)&&(t=t.substr(1));var r=t.split("/"),i=[];n.val="";for(var o=0,a=r.length;a>o;o++){var s,h=r[o];(s=h.match(/^:([^\/]+)$/))?(i.push(new u(s[1])),e.push(s[1]),n.val+="3"):(s=h.match(/^\*([^\/]+)$/))?(i.push(new l(s[1])),n.val+="2",e.push(s[1])):""===h?(i.push(new p),n.val+="1"):(i.push(new c(h)),n.val+="4")}return n.val=+n.val,i}function d(t){this.charSpec=t,this.nextStates=[]}function v(t){return t.sort(function(t,e){return e.specificity.val-t.specificity.val})}function g(t,e){for(var n=[],r=0,i=t.length;i>r;r++){var o=t[r];n=n.concat(o.match(e))}return n}function y(t){this.queryParams=t||{}}function m(t,e,n){for(var r=t.handlers,i=t.regex,o=e.match(i),a=1,s=new y(n),h=0,c=r.length;c>h;h++){for(var u=r[h],l=u.names,p={},f=0,d=l.length;d>f;f++)p[l[f]]=o[a++];s.push({handler:u.handler,params:p,isDynamic:!!l.length})}return s}function _(t,e){return e.eachChar(function(e){t=t.put(e)}),t}function w(t){return t=t.replace(/\+/gm,"%20"),s(t,!0)}function b(t){"undefined"!=typeof console&&console.error("[vue-router] "+t)}function C(t,e,n){var r=t.match(/(\?.*)$/);if(r&&(r=r[1],t=t.slice(0,-r.length)),"?"===e.charAt(0))return t+e;var i=t.split("/");n&&i[i.length-1]||i.pop();for(var o=e.replace(/^\//,"").split("/"),a=0;a can only be used inside a router-enabled app.");this._isDynamicLiteral=!0,n.bind.call(this);for(var e=void 0,r=this.vm;r;){if(r._routerView){e=r._routerView;break}r=r.$parent}if(e)this.parentView=e,e.childView=this;else{var i=t.router;i._rootView=this}var o=t.router._currentTransition;if(!e&&o.done||e&&e.activated){var a=e?e.depth+1:0;P(this,o,a)}},unbind:function(){this.parentView&&(this.parentView.childView=null),n.unbind.call(this)}}),t.elementDirective("router-view",r)}function Q(t){function e(t){return t.protocol===location.protocol&&t.hostname===location.hostname&&t.port===location.port}function n(t,e,n){if(e=e.trim(),-1===e.indexOf(" "))return void n(t,e);for(var r=e.split(/\s+/),i=0,o=r.length;o>i;i++)n(t,r[i])}var r=t.util,i=r.bind,o=r.isObject,a=r.addClass,s=r.removeClass,h=t.directive("on").priority,c="__vue-router-link-update__",u=0;t.directive("link-active",{priority:9999,bind:function(){for(var t=this,e=String(u++),n=this.el.querySelectorAll("[v-link]"),r=0,i=n.length;i>r;r++){var o=n[r],a=o.getAttribute(c),s=a?a+","+e:e;o.setAttribute(c,s)}this.vm.$on(c,this.cb=function(n,r){n.activeIds.indexOf(e)>-1&&n.updateClasses(r,t.el)})},unbind:function(){this.vm.$off(c,this.cb)}}),t.directive("link",{priority:h-2,bind:function(){var t=this.vm;if(!t.$route)return void b("v-link can only be used inside a router-enabled app.");this.router=t.$route.router,this.unwatch=t.$watch("$route",i(this.onRouteUpdate,this));var e=this.el.getAttribute(c);e&&(this.el.removeAttribute(c),this.activeIds=e.split(",")),"A"===this.el.tagName&&"_blank"===this.el.getAttribute("target")||(this.handler=i(this.onClick,this),this.el.addEventListener("click",this.handler))},update:function(t){this.target=t,o(t)&&(this.append=t.append,this.exact=t.exact,this.prevActiveClass=this.activeClass,this.activeClass=t.activeClass),this.onRouteUpdate(this.vm.$route)},onClick:function(t){if(!(t.metaKey||t.ctrlKey||t.shiftKey||t.defaultPrevented||0!==t.button)){var n=this.target;if(n)t.preventDefault(),this.router.go(n);else{for(var r=t.target;"A"!==r.tagName&&r!==this.el;)r=r.parentNode;if("A"===r.tagName&&e(r)){t.preventDefault();var i=r.pathname;this.router.history.root&&(i=i.replace(this.router.history.rootRE,"")),this.router.go({path:i,replace:n&&n.replace,append:n&&n.append})}}}},onRouteUpdate:function(t){var e=this.router.stringifyPath(this.target);this.path!==e&&(this.path=e,this.updateActiveMatch(),this.updateHref()),this.activeIds?this.vm.$emit(c,this,t.path):this.updateClasses(t.path,this.el)},updateActiveMatch:function(){this.activeRE=this.path&&!this.exact?new RegExp("^"+this.path.replace(/\/$/,"").replace(at,"").replace(ot,"\\$&")+"(\\/|$)"):null},updateHref:function(){if("A"===this.el.tagName){var t=this.path,e=this.router,n="/"===t.charAt(0),r=t&&("hash"===e.mode||n)?e.history.formatPath(t,this.append):t;r?this.el.href=r:this.el.removeAttribute("href")}},updateClasses:function(t,e){var r=this.activeClass||this.router._linkActiveClass;this.prevActiveClass&&this.prevActiveClass!==r&&n(e,this.prevActiveClass,s);var i=this.path.replace(at,"");t=t.replace(at,""),this.exact?i===t||"/"!==i.charAt(i.length-1)&&i===t.replace(it,"")?n(e,r,a):n(e,r,s):this.activeRE&&this.activeRE.test(t)?n(e,r,a):n(e,r,s)},unbind:function(){this.el.removeEventListener("click",this.handler),this.unwatch&&this.unwatch()}})}function F(t,e){var n=e.component;ht.util.isPlainObject(n)&&(n=e.component=ht.extend(n)),"function"!=typeof n&&(e.component=null,b('invalid component for route "'+t+'".'))}var I={};I.classCallCheck=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},t.prototype={to:function(t,e){var n=this.delegate;if(n&&n.willAddRoute&&(t=n.willAddRoute(this.matcher.target,t)),this.matcher.add(this.path,t),e){if(0===e.length)throw new Error("You must have an argument in the function passed to `to`");this.matcher.addChild(this.path,t,e,this.delegate)}return this}},e.prototype={add:function(t,e){this.routes[t]=e},addChild:function(t,r,i,o){var a=new e(r);this.children[t]=a;var s=n(t,a,o);o&&o.contextEntered&&o.contextEntered(r,s),i(s)}};var U=["/",".","*","+","?","|","(",")","[","]","{","}","\\"],L=new RegExp("(\\"+U.join("|\\")+")","g"),B=!1;c.prototype={eachChar:function(t){for(var e,n=this.string,r=0,i=n.length;i>r;r++)e=n.charAt(r),t({validChars:e})},regex:function(){return this.string.replace(L,"\\$1")},generate:function(){return this.string}},u.prototype={eachChar:function(t){t({invalidChars:"/",repeat:!0})},regex:function(){return"([^/]+)"},generate:function(t){var e=t[this.name];return null==e?":"+this.name:e}},l.prototype={eachChar:function(t){t({invalidChars:"",repeat:!0})},regex:function(){return"(.+)"},generate:function(t){var e=t[this.name];return null==e?":"+this.name:e}},p.prototype={eachChar:function(){},regex:function(){return""},generate:function(){return""}},d.prototype={get:function(t){for(var e=this.nextStates,n=0,r=e.length;r>n;n++){var i=e[n],o=i.charSpec.validChars===t.validChars;if(o=o&&i.charSpec.invalidChars===t.invalidChars)return i}},put:function(t){var e;return(e=this.get(t))?e:(e=new d(t),this.nextStates.push(e),t.repeat&&e.nextStates.push(e),e)},match:function(t){for(var e,n,r,i=this.nextStates,o=[],a=0,s=i.length;s>a;a++)e=i[a],n=e.charSpec,"undefined"!=typeof(r=n.validChars)?-1!==r.indexOf(t)&&o.push(e):"undefined"!=typeof(r=n.invalidChars)&&-1===r.indexOf(t)&&o.push(e);return o}};var N=Object.create||function(t){function e(){}return e.prototype=t,new e};y.prototype=N({splice:Array.prototype.splice,slice:Array.prototype.slice,push:Array.prototype.push,length:0,queryParams:null});var G=function(){this.rootState=new d,this.names={}};G.prototype={add:function(t,e){for(var n,r=this.rootState,i="^",o={},a=[],s=[],h=!0,c=0,u=t.length;u>c;c++){var l=t[c],d=[],v=f(l.path,d,o);s=s.concat(v);for(var g=0,y=v.length;y>g;g++){var m=v[g];m instanceof p||(h=!1,r=r.put({validChars:"/"}),i+="/",r=_(r,m),i+=m.regex())}var w={handler:l.handler,names:d};a.push(w)}h&&(r=r.put({validChars:"/"}),i+="/"),r.handlers=a,r.regex=new RegExp(i+"$"),r.specificity=o,(n=e&&e.as)&&(this.names[n]={segments:s,handlers:a})},handlersFor:function(t){var e=this.names[t],n=[];if(!e)throw new Error("There is no route named "+t);for(var r=0,i=e.handlers.length;i>r;r++)n.push(e.handlers[r]);return n},hasRoute:function(t){return!!this.names[t]},generate:function(t,e){var n=this.names[t],r="";if(!n)throw new Error("There is no route named "+t);for(var i=n.segments,o=0,a=i.length;a>o;o++){var s=i[o];s instanceof p||(r+="/",r+=s.generate(e))}return"/"!==r.charAt(0)&&(r="/"+r),e&&e.queryParams&&(r+=this.generateQueryString(e.queryParams)),r},generateQueryString:function(t){var e=[],n=[];for(var r in t)t.hasOwnProperty(r)&&n.push(r);n.sort();for(var i=0,o=n.length;o>i;i++){r=n[i];var a=t[r];if(null!=a){var s=encodeURIComponent(r);if(h(a))for(var c=0,u=a.length;u>c;c++){var l=r+"[]="+encodeURIComponent(a[c]);e.push(l)}else s+="="+encodeURIComponent(a),e.push(s)}}return 0===e.length?"":"?"+e.join("&")},parseQueryString:function(t){for(var e=t.split("&"),n={},r=0;r2&&"[]"===a.slice(s-2)&&(h=!0,a=a.slice(0,s-2),n[a]||(n[a]=[])),i=o[1]?w(o[1]):""),h?n[a].push(i):n[a]=i}return n},recognize:function(t,e){B=e;var n,r,i,o,a=[this.rootState],h={},c=!1;if(o=t.indexOf("?"),-1!==o){var u=t.substr(o+1,t.length);t=t.substr(0,o),u&&(h=this.parseQueryString(u))}if(t=s(t)){for("/"!==t.charAt(0)&&(t="/"+t),n=t.length,n>1&&"/"===t.charAt(n-1)&&(t=t.substr(0,n-1),c=!0),r=0,i=t.length;i>r&&(a=g(a,t.charAt(r)),a.length);r++);var l=[];for(r=0,i=a.length;i>r;r++)a[r].handlers&&l.push(a[r]);a=v(l);var p=l[0];return p&&p.handlers?(c&&"(.+)$"===p.regex.source.slice(-5)&&(t+="/"),m(p,t,h)):void 0}}},G.prototype.map=o;var K=G.prototype.generateQueryString,X={},Y=void 0,J=/#.*$/,W=function(){function t(e){var n=e.root,r=e.onChange;I.classCallCheck(this,t),n&&"/"!==n?("/"!==n.charAt(0)&&(n="/"+n),this.root=n.replace(/\/$/,""),this.rootRE=new RegExp("^\\"+this.root)):this.root=null,this.onChange=r;var i=document.querySelector("base");this.base=i&&i.getAttribute("href")}return t.prototype.start=function(){var t=this;this.listener=function(e){var n=location.pathname+location.search;t.root&&(n=n.replace(t.rootRE,"")),t.onChange(n,e&&e.state,location.hash)},window.addEventListener("popstate",this.listener),this.listener()},t.prototype.stop=function(){window.removeEventListener("popstate",this.listener)},t.prototype.go=function(t,e,n){var r=this.formatPath(t,n);e?history.replaceState({},"",r):(history.replaceState({pos:{x:window.pageXOffset,y:window.pageYOffset}},"",location.href),history.pushState({},"",r));var i=t.match(J),o=i&&i[0];t=r.replace(J,"").replace(this.rootRE,""),this.onChange(t,null,o)},t.prototype.formatPath=function(t,e){return"/"===t.charAt(0)?this.root?this.root+"/"+t.replace(/^\//,""):t:C(this.base||location.pathname,t,e)},t}(),Z=function(){function t(e){var n=e.hashbang,r=e.onChange;I.classCallCheck(this,t),this.hashbang=n,this.onChange=r}return t.prototype.start=function(){var t=this;this.listener=function(){var e=location.hash,n=e.replace(/^#!?/,"");"/"!==n.charAt(0)&&(n="/"+n);var r=t.formatPath(n);if(r!==e)return void location.replace(r);var i=location.search&&e.indexOf("?")>-1?"&"+location.search.slice(1):location.search;t.onChange(e.replace(/^#!?/,"")+i)},window.addEventListener("hashchange",this.listener),this.listener()},t.prototype.stop=function(){window.removeEventListener("hashchange",this.listener)},t.prototype.go=function(t,e,n){t=this.formatPath(t,n),e?location.replace(t):location.hash=t},t.prototype.formatPath=function(t,e){var n="/"===t.charAt(0),r="#"+(this.hashbang?"!":"");return n?r+t:r+C(location.hash.replace(/^#!?/,""),t,e)},t}(),tt=function(){function t(e){var n=e.onChange;I.classCallCheck(this,t),this.onChange=n,this.currentPath="/"}return t.prototype.start=function(){this.onChange("/")},t.prototype.stop=function(){},t.prototype.go=function(t,e,n){t=this.currentPath=this.formatPath(t,n),this.onChange(t)},t.prototype.formatPath=function(t,e){return"/"===t.charAt(0)?t:C(this.currentPath,t,e)},t}(),et=function(){function t(e,n,r){I.classCallCheck(this,t),this.router=e,this.to=n,this.from=r,this.next=null,this.aborted=!1,this.done=!1}return t.prototype.abort=function(){if(!this.aborted){this.aborted=!0;var t=!this.from.path&&"/"===this.to.path;t||this.router.replace(this.from.path||"/")}},t.prototype.redirect=function(t){this.aborted||(this.aborted=!0,"string"==typeof t?t=$(t,this.to.params,this.to.query):(t.params=t.params||this.to.params,t.query=t.query||this.to.query),this.router.replace(t))},t.prototype.start=function(t){for(var e=this,n=[],r=this.router._rootView;r;)n.unshift(r),r=r.childView;var i=n.slice().reverse(),o=this.activateQueue=D(this.to.matched).map(function(t){return t.handler}),a=void 0,s=void 0;for(a=0;a0&&(s=i.slice(0,a),n=i.slice(a).reverse(),o=o.slice(a)),e.runQueue(n,E,function(){e.runQueue(o,V,function(){e.runQueue(n,S,function(){if(e.router._onTransitionValidated(e),s&&s.forEach(function(t){return O(t,e)}),n.length){var r=n[n.length-1],i=s?s.length:0;P(r,e,i,t)}else t()})})})},t.prototype.runQueue=function(t,e,n){function r(o){o>=t.length?n():e(t[o],i,function(){r(o+1)})}var i=this;r(0)},t.prototype.callHook=function(t,e,n){var r=arguments.length<=3||void 0===arguments[3]?{}:arguments[3],i=r.expectBoolean,o=void 0===i?!1:i,a=r.postActivate,s=void 0===a?!1:a,h=r.processData,c=r.cleanup,u=this,l=!1,p=function(){c&&c(),u.abort()},f=function(t){if(s?v():p(),t&&!u.router._suppress)throw b("Uncaught error during transition: "),t instanceof Error?t:new Error(t)},d=function(t){try{f(t)}catch(e){setTimeout(function(){throw e},0)}},v=function(){return l?void b("transition.next() should be called only once."):(l=!0,u.aborted?void(c&&c()):void(n&&n()))},g=function(e){"boolean"==typeof e?e?v():p():R(e)?e.then(function(t){t?v():p()},d):t.length||v()},y=function(t){var e=void 0;try{e=h(t)}catch(n){return f(n)}R(e)?e.then(v,d):v()},m={to:u.to,from:u.from,abort:p,next:h?y:v,redirect:function(){u.redirect.apply(u,arguments)}},_=void 0;try{_=t.call(e,m)}catch(w){return f(w)}o?g(_):R(_)?h?_.then(y,d):_.then(v,d):h&&M(_)?y(_):t.length||v()},t.prototype.callHooks=function(t,e,n,r){var i=this;Array.isArray(t)?this.runQueue(t,function(t,n,o){i.aborted||i.callHook(t,e,o,r)},n):this.callHook(t,e,n,r)},t}(),nt=/^(component|subRoutes|fullPath)$/,rt=function ut(t,e){var n=this;I.classCallCheck(this,ut);var r=e._recognizer.recognize(t);r&&([].forEach.call(r,function(t){for(var e in t.handler)nt.test(e)||(n[e]=t.handler[e])}),this.query=r.queryParams,this.params=[].reduce.call(r,function(t,e){if(e.params)for(var n in e.params)t[n]=e.params[n];return t},{})),this.path=t,this.matched=r||e._notFoundHandler,Object.defineProperty(this,"router",{enumerable:!1,value:e}),Object.freeze(this)},it=/\/$/,ot=/[-.*+?^${}()|[\]\/\\]/g,at=/\?.*$/,st={"abstract":tt,hash:Z,html5:W},ht=void 0,ct=function(){function t(){var e=this,n=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],r=n.hashbang,i=void 0===r?!0:r,o=n["abstract"],a=void 0===o?!1:o,s=n.history,h=void 0===s?!1:s,c=n.saveScrollPosition,u=void 0===c?!1:c,l=n.transitionOnLoad,p=void 0===l?!1:l,f=n.suppressTransitionError,d=void 0===f?!1:f,v=n.root,g=void 0===v?null:v,y=n.linkActiveClass,m=void 0===y?"v-link-active":y;if(I.classCallCheck(this,t),!t.installed)throw new Error("Please install the Router with Vue.use() before creating an instance.");this.app=null,this._children=[],this._recognizer=new G,this._guardRecognizer=new G,this._started=!1,this._startCb=null,this._currentRoute={},this._currentTransition=null,this._previousTransition=null,this._notFoundHandler=null,this._notFoundRedirect=null,this._beforeEachHooks=[],this._afterEachHooks=[],this._rendered=!1,this._transitionOnLoad=p,this._root=g,this._abstract=a,this._hashbang=i;var _="undefined"!=typeof window&&window.history&&window.history.pushState;this._history=h&&_,this._historyFallback=h&&!_;var w=ht.util.inBrowser;this.mode=!w||this._abstract?"abstract":this._history?"html5":"hash";var b=st[this.mode];this.history=new b({root:g,hashbang:this._hashbang,onChange:function(t,n,r){e._match(t,n,r)}}),this._saveScrollPosition=u,this._linkActiveClass=m,this._suppress=d}return t.prototype.map=function(t){for(var e in t)this.on(e,t[e]);return this},t.prototype.on=function(t,e){return"*"===t?this._notFound(e):this._addRoute(t,e,[]),this},t.prototype.redirect=function(t){for(var e in t)this._addRedirect(e,t[e]);return this},t.prototype.alias=function(t){for(var e in t)this._addAlias(e,t[e]);return this},t.prototype.beforeEach=function(t){return this._beforeEachHooks.push(t),this},t.prototype.afterEach=function(t){return this._afterEachHooks.push(t),this},t.prototype.go=function(t){var e=!1,n=!1;ht.util.isObject(t)&&(e=t.replace,n=t.append),t=this.stringifyPath(t),t&&this.history.go(t,e,n)},t.prototype.replace=function(t){"string"==typeof t&&(t={path:t}),t.replace=!0,this.go(t)},t.prototype.start=function(t,e,n){if(this._started)return void b("already started.");if(this._started=!0,this._startCb=n,!this.app){if(!t||!e)throw new Error("Must start vue-router with a component and a root container.");if(t instanceof ht)throw new Error("Must start vue-router with a component, not a Vue instance.");this._appContainer=e;var r=this._appConstructor="function"==typeof t?t:ht.extend(t);r.options.name=r.options.name||"RouterApp"}if(this._historyFallback){var i=window.location,o=new W({root:this._root}),a=o.root?i.pathname.replace(o.rootRE,""):i.pathname;if(a&&"/"!==a)return void i.assign((o.root||"")+"/"+this.history.formatPath(a)+i.search)}this.history.start()},t.prototype.stop=function(){this.history.stop(),this._started=!1},t.prototype.stringifyPath=function(t){var e="";if(t&&"object"==typeof t){if(t.name){var n=ht.util.extend,r=this._currentTransition&&this._currentTransition.to.params,i=t.params||{},o=r?n(n({},r),i):i;e=encodeURI(this._recognizer.generate(t.name,o))}else t.path&&(e=encodeURI(t.path));if(t.query){var a=this._recognizer.generateQueryString(t.query);e+=e.indexOf("?")>-1?"&"+a.slice(1):a}}else e=encodeURI(t?t+"":"");return e},t.prototype._addRoute=function(t,e,n){if(F(t,e),e.path=t,e.fullPath=(n.reduce(function(t,e){return t+e.path},"")+t).replace("//","/"),n.push({path:t,handler:e}),this._recognizer.add(n,{as:e.name}),e.subRoutes)for(var r in e.subRoutes)this._addRoute(r,e.subRoutes[r],n.slice())},t.prototype._notFound=function(t){F("*",t),this._notFoundHandler=[{handler:t}]},t.prototype._addRedirect=function(t,e){"*"===t?this._notFoundRedirect=e:this._addGuard(t,e,this.replace)},t.prototype._addAlias=function(t,e){this._addGuard(t,e,this._match)},t.prototype._addGuard=function(t,e,n){var r=this;this._guardRecognizer.add([{path:t,handler:function(t,i){var o=$(e,t.params,i);n.call(r,o)}}])},t.prototype._checkGuard=function(t){var e=this._guardRecognizer.recognize(t,!0);return e?(e[0].handler(e[0],e.queryParams),!0):this._notFoundRedirect&&(e=this._recognizer.recognize(t),!e)?(this.replace(this._notFoundRedirect),!0):void 0},t.prototype._match=function(t,e,n){var r=this;if(!this._checkGuard(t)){var i=this._currentRoute,o=this._currentTransition;if(o){if(o.to.path===t)return;if(i.path===t)return o.aborted=!0,void(this._currentTransition=this._prevTransition);o.aborted=!0}var a=new rt(t,this),s=new et(this,a,i);this._prevTransition=o,this._currentTransition=s,this.app||!function(){var t=r;r.app=new r._appConstructor({el:r._appContainer,created:function(){this.$router=t},_meta:{$route:a}})}();var h=this._beforeEachHooks,c=function(){s.start(function(){r._postTransition(a,e,n)})};h.length?s.runQueue(h,function(t,e,n){s===r._currentTransition&&s.callHook(t,null,n,{expectBoolean:!0})},c):c(),!this._rendered&&this._startCb&&this._startCb.call(null),this._rendered=!0}},t.prototype._onTransitionValidated=function(t){var e=this._currentRoute=t.to;this.app.$route!==e&&(this.app.$route=e,this._children.forEach(function(t){t.$route=e})),this._afterEachHooks.length&&this._afterEachHooks.forEach(function(e){return e.call(null,{to:t.to,from:t.from})}),this._currentTransition.done=!0},t.prototype._postTransition=function(t,e,n){var r=e&&e.pos;r&&this._saveScrollPosition?ht.nextTick(function(){window.scrollTo(r.x,r.y)}):n&&ht.nextTick(function(){var t=document.getElementById(n.slice(1));t&&window.scrollTo(window.scrollX,t.offsetTop)})},t}();return ct.installed=!1,ct.install=function(t){return ct.installed?void b("already installed."):(ht=t,q(ht),z(ht),Q(ht),X.Vue=ht,void(ct.installed=!0))},"undefined"!=typeof window&&window.Vue&&window.Vue.use(ct),ct}); -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/lib/vue-socketcluster.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | var version = '1.1.0'; 3 | window.VueSocketcluster = {}; 4 | 5 | if (!socketCluster) { 6 | throw new Error("[Vue-Socketcluster] cannot locate socketcluster-client"); 7 | } 8 | 9 | var VueSocketcluster = { 10 | install: function (Vue, config) { 11 | 12 | if (typeof config == 'object') { 13 | if (!config.hostname || !config.port) { 14 | config.hostname = 'localhost'; 15 | config.port = 3000; 16 | } 17 | 18 | } else { 19 | config = { 20 | hostname:'localhost', 21 | port:3000 22 | }; 23 | } 24 | 25 | var socket = socketCluster.connect(config); 26 | 27 | var onevent = socket.onevent; 28 | socket.onevent = function (packet) { 29 | var args = packet.data || []; 30 | onevent.call(this, packet); 31 | packet.data = ["*"].concat(args); 32 | onevent.call(this, packet); 33 | } 34 | 35 | var methods = [ 36 | "error", 37 | "connect", 38 | "disconnect", 39 | "connectAbort", 40 | "raw", 41 | "kickOut", 42 | "subscribe", 43 | "subscribeFail", 44 | "unsubscribe", 45 | "authStateChange", 46 | "authTokenChange", 47 | "subscribeStateChange", 48 | "subscribeRequest", 49 | "authenticate", 50 | "deauthenticate", 51 | "message" 52 | ]; 53 | 54 | Vue.mixin({ 55 | created: function () { 56 | var self = this; 57 | if (this.$options.hasOwnProperty("sockets")) { 58 | 59 | for (var key in self.$options.sockets) { 60 | if (self.$options.sockets.hasOwnProperty(key) && methods.indexOf(key) < 0) { 61 | socket.on(key,function(emit,data,respond) { 62 | self.$options.sockets[key].call(self,data,respond); 63 | }) 64 | } 65 | } 66 | 67 | methods.forEach(function (event) { 68 | socket.on(event, function (d,r) { 69 | if (self.$options.sockets.hasOwnProperty(event)) { 70 | self.$options.sockets[event].call(self, d, r); 71 | } 72 | }) 73 | }) 74 | } 75 | 76 | // Global VueSocketcluster instance 77 | this.$sc = socket; 78 | } 79 | }) 80 | 81 | 82 | } 83 | }; 84 | 85 | if (typeof exports == "object") { 86 | module.exports = VueSocketcluster; 87 | } else if (typeof define == "function" && define.amd) { 88 | define([], function () { 89 | return VueSocketcluster; 90 | }) 91 | } else if (window.Vue) { 92 | window.VueSocketcluster = VueSocketcluster; 93 | } 94 | 95 | 96 | })(); -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/lib/vue-socketcluster.min.js: -------------------------------------------------------------------------------- 1 | !function(){if(window.VueSocketcluster={},!socketCluster)throw new Error("[Vue-Socketcluster] cannot locate socketcluster-client");var b={install:function(a,b){"object"==typeof b?b.hostname&&b.port||(b.hostname="localhost",b.port=3e3):b={hostname:"localhost",port:3e3};var c=socketCluster.connect(b),d=c.onevent;c.onevent=function(a){var b=a.data||[];d.call(this,a),a.data=["*"].concat(b),d.call(this,a)};var e=["error","connect","disconnect","connectAbort","raw","kickOut","subscribe","subscribeFail","unsubscribe","authStateChange","authTokenChange","subscribeStateChange","subscribeRequest","authenticate","deauthenticate","message"];a.mixin({created:function(){var a=this;if(this.$options.hasOwnProperty("sockets")){for(var b in a.$options.sockets)a.$options.sockets.hasOwnProperty(b)&&e.indexOf(b)<0&&c.on(b,function(c,d,e){a.$options.sockets[b].call(a,d,e)});e.forEach(function(b){c.on(b,function(c,d){a.$options.sockets.hasOwnProperty(b)&&a.$options.sockets[b].call(a,c,d)})})}this.$sc=c}})}};"object"==typeof exports?module.exports=b:"function"==typeof define&&define.amd?define([],function(){return b}):window.Vue&&(window.VueSocketcluster=b)}(); -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/src/components/about/index.js: -------------------------------------------------------------------------------- 1 | var aboutComponent = Vue.extend({ 2 | template: templatizer.about.index({}), 3 | data() { 4 | return { 5 | ready:false 6 | } 7 | }, 8 | methods:{ 9 | fetchData(respond) { 10 | if (respond && typeof respond == 'function') respond(null) 11 | } 12 | }, 13 | ready() { 14 | var vm = this 15 | }, 16 | route: { 17 | data(transition) { 18 | var vm = this 19 | vm.fetchData(function(err) { 20 | if (err) { 21 | vm.$root.alert(err,'error') 22 | router.go('/') 23 | } else { 24 | vm.ready = true 25 | transition.next() 26 | } 27 | }) 28 | }, 29 | waitForData:true 30 | } 31 | }) -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/src/components/dashboard/index.js: -------------------------------------------------------------------------------- 1 | var dashboardComponent = Vue.extend({ 2 | template: templatizer.dashboard.index({}), 3 | data() { 4 | return { 5 | ready:false 6 | } 7 | }, 8 | methods:{ 9 | fetchData(respond) { 10 | if (respond && typeof respond == 'function') respond(null) 11 | } 12 | }, 13 | ready() { 14 | var vm = this 15 | }, 16 | route: { 17 | data(transition) { 18 | var vm = this 19 | vm.fetchData(function(err) { 20 | if (err) { 21 | vm.$root.alert(err,'error') 22 | router.go('/') 23 | } else { 24 | vm.ready = true 25 | transition.next() 26 | } 27 | }) 28 | }, 29 | waitForData:true 30 | } 31 | }) -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/src/components/session/create.js: -------------------------------------------------------------------------------- 1 | var sessionCreateComponent = Vue.extend({ 2 | template: templatizer.session.create({}), 3 | data() { 4 | return { 5 | email:'', 6 | password:'', 7 | 8 | ready:false 9 | } 10 | }, 11 | methods:{ 12 | login() { 13 | var vm = this 14 | 15 | validate.async({ 16 | email:vm.email, 17 | password:vm.password 18 | },{ 19 | email:{presence:true}, 20 | password:{presence:true} 21 | }).then(function(attributes) { 22 | vm.$root.$sc.emit('session',{ 23 | method:'store', 24 | email:attributes.email, 25 | password:attributes.password 26 | },function(err) { 27 | if (err) { console.log(err);return vm.$root.alert(err,'error') } 28 | // vm.$root.authenticated = true 29 | // router.go({ path:'/dashboard' }) 30 | }) 31 | },function(errors) { 32 | var error = null 33 | for (var key in errors) { 34 | error = errors[key][0] 35 | break; 36 | } 37 | vm.$root.alert(error,'error') 38 | }) 39 | } 40 | }, 41 | ready() { 42 | var vm = this 43 | }, 44 | route: { 45 | data(transition) { 46 | var vm = this 47 | vm.ready = true 48 | transition.next() 49 | } 50 | } 51 | }) -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/src/components/users/create.js: -------------------------------------------------------------------------------- 1 | var usersCreateComponent = Vue.extend({ 2 | template: templatizer.users.create({}), 3 | data() { 4 | return { 5 | first:'', 6 | last:'', 7 | email:'', 8 | password:'', 9 | 10 | ready:false 11 | } 12 | }, 13 | methods:{ 14 | fetchData(respond) { 15 | if (respond && typeof respond == 'function') respond(null) 16 | }, 17 | clearFields() { 18 | this.first = '' 19 | this.last = '' 20 | this.email = '' 21 | this.password = '' 22 | }, 23 | create() { 24 | var vm = this 25 | 26 | validate.async({ 27 | first:vm.first, 28 | last:vm.last, 29 | email:vm.email, 30 | password:vm.password, 31 | visible:1 32 | },{ 33 | first:{presence:true}, 34 | last:{presence:true}, 35 | email:{presence:true}, 36 | password:{presence:true} 37 | }).then(function(attributes){ 38 | vm.$root.$sc.emit('users',{ 39 | method:'store', 40 | user:attributes 41 | },function(err,new_user) { 42 | if (err) { return vm.$root.alert(err,'error') } 43 | vm.$root.alert('Successfully created account ' + new_user.email + '! Please log in.','success') 44 | router.go({ path:'/session/create' }) 45 | }) 46 | },function(errors) { 47 | var error = null 48 | for (var key in errors) { 49 | error = errors[key][0] 50 | break; 51 | } 52 | vm.$root.alert(error,'error') 53 | }) 54 | } 55 | }, 56 | ready() { 57 | var vm = this 58 | }, 59 | route: { 60 | data(transition) { 61 | var vm = this 62 | vm.fetchData(function(err) { 63 | if (err) { transition.abort() } 64 | else { 65 | vm.ready = true 66 | vm.clearFields() 67 | transition.next() 68 | } 69 | }) 70 | }, 71 | waitForData:true 72 | } 73 | }) -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/src/components/users/index.js: -------------------------------------------------------------------------------- 1 | var usersComponent = Vue.extend({ 2 | template: templatizer.users.index({}), 3 | data() { 4 | return { 5 | users:[], 6 | 7 | page:1, 8 | limit:25, 9 | num_users:0, 10 | total_users:0, 11 | pagination:false, 12 | 13 | first:'', 14 | last:'', 15 | email:'', 16 | password:'', 17 | confirm_password:'', 18 | 19 | ready:false 20 | } 21 | }, 22 | watch:{ 23 | search:function(val,oldVal) { 24 | this.fetchData(null) 25 | } 26 | }, 27 | computed:{ 28 | num_users:function() { 29 | return this.users.length 30 | }, 31 | pagination:function() { 32 | return ( ( this.total_users >= this.limit ) ? true : false ) 33 | } 34 | }, 35 | methods:{ 36 | delete() { 37 | 38 | }, 39 | create() { 40 | 41 | }, 42 | fetchData(data,respond) { 43 | var vm = this 44 | vm.$dispatch('read',{ 45 | table:'users', 46 | limit:vm.limit, 47 | offset:(vm.page - 1) * vm.limit, 48 | search:vm.search 49 | },function(err,users) { 50 | if (err) { return vm.$root.transitionError(err) } 51 | if (respond && typeof respond == 'function') { return respond(users) } 52 | else { vm.users = users } 53 | }) 54 | 55 | }, 56 | updateData(users) { 57 | this.users = users 58 | } 59 | }, 60 | events:{ 61 | 'users-update':function(response) { 62 | console.log(response) 63 | }, 64 | 'users-destroy':function(response) { 65 | console.log(response) 66 | }, 67 | 'users-store':function(response) { 68 | console.log(response) 69 | } 70 | }, 71 | ready() {}, 72 | route: { 73 | data(transition) { 74 | var vm = this 75 | vm.fetchData({},function(users) { 76 | transition.next({ ready:true,users:users }) 77 | }) 78 | }, 79 | waitForData:true 80 | } 81 | }) -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/src/system/app.js: -------------------------------------------------------------------------------- 1 | Vue.use(VueRouter) 2 | Vue.use(VueSocketcluster) 3 | 4 | router.map({ 5 | '/dashboard' : { component:dashboardComponent, auth:true }, 6 | '/about' : { component:aboutComponent, auth:true }, 7 | '/users' : { component:usersComponent, auth:true }, 8 | '/session/create': { component : sessionCreateComponent, auth:false }, 9 | '/users/create': { component : usersCreateComponent, auth:false } 10 | }) 11 | 12 | router.redirect({ 13 | '*':'/dashboard' 14 | }) 15 | 16 | router.beforeEach(function(transition) { 17 | router.app.loading = true 18 | if (transition.to.auth && router.app.$sc.authState != 'authenticated') { 19 | router.app.loading = false 20 | transition.redirect('/session/create') 21 | } else if (router.app.$sc.authState == 'authenticated' && !transition.to.auth) { 22 | router.app.loading = false 23 | transition.redirect('/dashboard') 24 | } else { 25 | transition.next() 26 | } 27 | }) 28 | 29 | router.afterEach(function(transition) { 30 | setTimeout(function() { 31 | router.app.loading = false 32 | },router.app.loading_delay) 33 | }) 34 | 35 | router.start(Vue.extend({ 36 | data() { 37 | return { 38 | loading_delay:20, 39 | show_success:false, 40 | success_msg:'', 41 | show_error:false, 42 | error_msg:'', 43 | 44 | started:false, 45 | loading:true, 46 | authenticated:false, 47 | 48 | first:'', 49 | last:'', 50 | email:'' 51 | } 52 | }, 53 | watch:{ 54 | authenticated:function(val,oldVal) { 55 | if (val) { router.go({ path:'/dashboard' }) } 56 | else { router.go({ path:'/session/create' }) } 57 | } 58 | }, 59 | methods:{ 60 | transitionError(err) { 61 | this.alert(err,'error') 62 | router.go({ path:'/dashboard' }) 63 | }, 64 | setUserData() { 65 | var vm = this 66 | var authToken = vm.$sc.getAuthToken() || {} 67 | vm.first = authToken.first 68 | vm.last = authToken.last 69 | vm.email = authToken.email 70 | }, 71 | alert(msg,type) { 72 | var vm = this 73 | try { 74 | vm[type+'_msg'] = msg 75 | vm['show_'+type] = true 76 | setTimeout(function() { 77 | vm[type+'_msg'] = null 78 | },3000) 79 | }catch(err) {} 80 | }, 81 | logout() { 82 | var vm = this 83 | vm.$root.$sc.emit('session',{ 84 | method:'destroy' 85 | },function(err) { 86 | if (err) { console.log(err);return vm.$root.alert(err,'error') } 87 | }) 88 | } 89 | }, 90 | events:{ 91 | update(data) { 92 | 93 | }, 94 | destroy(data) { 95 | 96 | }, 97 | store(data) { 98 | // Do the publish here 99 | }, 100 | read(data,respond) { 101 | var vm = this 102 | 103 | if (data && data.table && typeof data.table == 'string') { 104 | var table = data.table 105 | delete data.table 106 | data.method = 'read' 107 | vm.$root.$sc.emit(table,data,( respond && typeof respond == 'function' ? respond : function(){} )) 108 | return false 109 | } 110 | return true 111 | } 112 | }, 113 | sockets:{ 114 | connect(status) { 115 | var vm = this 116 | 117 | vm.authenticated = status.isAuthenticated 118 | }, 119 | authenticate() { 120 | this.authenticated = true 121 | this.setUserData() 122 | }, 123 | deauthenticate() { 124 | this.authenticated = false 125 | }, 126 | update(data,respond) { 127 | var vm = this 128 | 129 | if (typeof data == 'object' && data.table) { 130 | vm.$broadcast( 131 | data.table+'-update',data, 132 | (respond && typeof respond == 'function' ? respond : function(){}) 133 | ) 134 | } 135 | }, 136 | destroy(data) { 137 | var vm = this 138 | 139 | if (typeof data == 'object' && data.table) { 140 | vm.$broadcast( 141 | data.table+'-destroy',data, 142 | (respond && typeof respond == 'function' ? respond : function(){}) 143 | ) 144 | } 145 | }, 146 | store(data) { 147 | var vm = this 148 | 149 | if (typeof data == 'object' && data.table) { 150 | vm.$broadcast( 151 | data.table+'-store',data, 152 | (respond && typeof respond == 'function' ? respond : function(){}) 153 | ) 154 | } 155 | } 156 | }, 157 | components:{ 158 | alert:VueStrap.alert, 159 | navbar:VueStrap.navbar 160 | }, 161 | ready() { 162 | var vm = this 163 | vm.started = true 164 | } 165 | }), '#app') -------------------------------------------------------------------------------- /examples/sc-crud-mysql/public/js/src/system/init.js: -------------------------------------------------------------------------------- 1 | var router = new VueRouter() -------------------------------------------------------------------------------- /examples/sc-crud-mysql/server.js: -------------------------------------------------------------------------------- 1 | var SocketCluster = require('socketcluster').SocketCluster, 2 | os = require('os'), 3 | fs = require('fs'), 4 | config = require('./config.json') 5 | 6 | var options = { 7 | workers: 1|| os.cpus().length || 1, 8 | brokers: 1, 9 | port: config.port, 10 | wsEngine: 'uws', 11 | appName: config.name || 'sc-app', 12 | workerController: __dirname + '/worker.js', 13 | brokerController: __dirname + '/broker.js', 14 | initController: __dirname + '/init.js', 15 | socketChannelLimit: 1000, 16 | crashWorkerOnError: true 17 | } 18 | 19 | var socketCluster = new SocketCluster(options) 20 | -------------------------------------------------------------------------------- /examples/sc-crud-mysql/templates/about/index.jade: -------------------------------------------------------------------------------- 1 | h3 About -------------------------------------------------------------------------------- /examples/sc-crud-mysql/templates/dashboard/index.jade: -------------------------------------------------------------------------------- 1 | h3 Dashboard -------------------------------------------------------------------------------- /examples/sc-crud-mysql/templates/session/create.jade: -------------------------------------------------------------------------------- 1 | div.row(style='margin-top:100px') 2 | div.col-md-4.col-md-offset-4 3 | div.panel.panel-primary 4 | div.panel-heading 5 | h5 Please Login 6 | div.panel-body 7 | div.form-group 8 | label Email 9 | input.form-control(type='email',v-model='email') 10 | div.form-group 11 | label Password 12 | input.form-control(type='password',v-model='password') 13 | hr 14 | div.form-group 15 | button.btn.btn-success(type='button',@click='login') Login 16 | a.btn.btn-default.pull-right(v-link='{ path:"/users/create" }') Create Account -------------------------------------------------------------------------------- /examples/sc-crud-mysql/templates/users/create.jade: -------------------------------------------------------------------------------- 1 | div.row(style='margin-top:100px') 2 | div.col-md-4.col-md-offset-4 3 | div.panel.panel-primary 4 | div.panel-heading 5 | h5 Create Account 6 | div.panel-body 7 | div.form-group 8 | label First 9 | input.form-control(type='text',placeholder='First Name',v-model='first') 10 | div.form-group 11 | label Last 12 | input.form-control(type='text',placeholder='Last Name',v-model='last') 13 | div.form-group 14 | label Email 15 | input.form-control(type='email',placeholder='Email',v-model='email') 16 | div.form-group 17 | label Password 18 | input.form-control(type='password',v-model='password') 19 | hr 20 | div.form-group 21 | button.btn.btn-success(type='button',@click.prevent='create') Create Account 22 | a.btn.btn-default.pull-right(v-link='{ path:"/session/create" }') Go To Login Page -------------------------------------------------------------------------------- /examples/sc-crud-mysql/templates/users/index.jade: -------------------------------------------------------------------------------- 1 | ol.breadcrumb 2 | li Users 3 | li 4 | span.glyphicon.glyphicon-plus-sign 5 | a#user-accordian-button.panel-title(data-toggle='collapse',data-parent='#accordian',href='#collapseUser',aria-expanded='true',aria-controls='collapseUser') Add New User 6 | input.no-slash.pull-right.form-control(style="margin-top:-0.15%;width:180px;height:28px;padding-left:5px",placeholder="User Search",v-model='search') 7 | 8 | div#collapseUser.panel-collapse.collapse(role='tabpanel',aria-labelledby='headingUser') 9 | div.well 10 | div.row 11 | div.col-md-6 12 | form.form.form-horizontal 13 | div.form-group 14 | label.col-md-4.form-label.text-right First 15 | div.col-md-5 16 | input.form-control.input-sm(type='text',placeholder='First Name',v-model='first') 17 | div.form-group 18 | label.col-md-4.form-label.text-right Last 19 | div.col-md-5 20 | input.form-control.input-sm(type='text',placeholder='Last Name',v-model='last') 21 | div.form-group 22 | label.col-md-4.form-label.text-right Email 23 | div.col-md-5 24 | input.form-control.input-sm(type='text',placeholder='Email',v-model='email') 25 | div.form-group 26 | label.col-md-4.form-label.text-right Password 27 | div.col-md-5 28 | input.form-control.input-sm(type='password',placeholder='Password',v-model='password') 29 | div.form-group 30 | label.col-md-4.form-label.text-right Confirm Password 31 | div.col-md-5 32 | input.form-control.input-sm(type='password',placeholder='Confirm Password',v-model='confirm_password') 33 | div.form-group 34 | label.col-md-4.control-label 35 | div.col-md-5 36 | div.col-md-4 37 | button.btn.btn-sm.btn-success(@click.prevent='create') Create 38 | div.col-md-4.col-md-offset-1 39 | button.btn.btn-sm.btn-default(type='reset') Reset 40 | div.table-responsive 41 | table.table.table-striped.table-hover.table-condensed 42 | thead 43 | tr 44 | th Name 45 | th Email 46 | th 47 | tbody(v-show="users.length > 0") 48 | tr(v-for="user in users") 49 | th 50 | a.edit-link(href="#/settings/user/{{ user.id }}") {{ user.name }} 51 | td {{ user.first }} {{ user.last }} 52 | td {{ user.email }} 53 | td.text-right 54 | a.btn.btn-xs.btn-warning(href="#/settings/user/{{ user.id }}",type="button",style="margin-right:5px") 55 | span.glyphicon.glyphicon-edit 56 | button.btn.btn-xs.btn-danger(id="delete-user-{{ user.id }}",type="button",@click="delete(user)") 57 | span.glyphicon.glyphicon-trash 58 | tr(v-show="pagination") 59 | th(colspan="3") 60 | pagination(:num='num_users',:limit='25',:total='total_users') 61 | tbody(v-show="users.length < 1") 62 | tr 63 | th.text-center(colspan="3") 64 | b No users 65 | -------------------------------------------------------------------------------- /examples/sc-crud-mysql/views/home/index.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | head 4 | title SC App 5 | 6 | meta(charset='utf-8') 7 | meta(name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no') 8 | meta(http-equiv='x-ua-compatible',content='ie=edge') 9 | 10 | link(rel='stylesheet',href='/css/bootstrap.min.css') 11 | 12 | body#app 13 | 14 | img(v-show='loading',src='/images/loading.gif',alt='Loading Image',style='position: absolute; margin:auto; left:0; right:0; top:0; bottom:0') 15 | 16 | navbar(v-show='authenticated && !loading && started') 17 | li 18 | a(v-link='{ path:"/dashboard" }') Dashboard 19 | li 20 | a(v-link='{ path:"/about" }') About 21 | li 22 | a(v-link='{ path:"/users" }') Users 23 | li.dropdown(slot='right') 24 | a.dropdown-toggle(data-toggle="dropdown") 25 | span {{ first }} {{ last }} 26 | span.caret 27 | ul.dropdown-menu 28 | li 29 | a(href='/',@click.prevent='logout') Logout 30 | 31 | alert(:show.sync="show_success && loaded",placement="top",duration="3000",type="success",width="400px",dismissable) 32 | strong {{ success_msg }} 33 | alert(:show.sync="show_error && loaded",placement="top",duration="3000",type="danger",width="400px",dismissable) 34 | strong {{ error_msg }} 35 | 36 | div.container-fluid(v-show='!loading && started',style='margin-top:50px') 37 | div.row 38 | div.col-md-12 39 | router-view 40 | 41 | script(src='/js/lib/validate.min.js') 42 | script(src='/js/lib/vue.min.js') 43 | script(src='/js/lib/vue-strap.min.js') 44 | script(src='/js/lib/vue-router.min.js') 45 | script(src='/js/lib/socketcluster.js') 46 | script(src='/js/lib/vue-socketcluster.js') 47 | 48 | script(src='/js/bin/templates.js') 49 | //- Use this if you want to have the minified data. 50 | //-script(src='/js/bin/concat-min.js') 51 | 52 | script(src='/js/src/system/init.js') 53 | 54 | script(src='/js/src/components/about/index.js') 55 | script(src='/js/src/components/dashboard/index.js') 56 | script(src='/js/src/components/session/create.js') 57 | script(src='/js/src/components/users/create.js') 58 | script(src='/js/src/components/users/index.js') 59 | 60 | script(src='/js/src/system/app.js') -------------------------------------------------------------------------------- /examples/sc-crud-mysql/worker.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var fs = require('fs'), 4 | express = require('express'), 5 | serveStatic = require('serve-static'), 6 | path = require('path'), 7 | scCrudMysql = require('sc-crud-mysql'), 8 | config = require('./config.json') 9 | 10 | module.exports.run = function (worker) { 11 | console.log(' >> Worker PID:', process.pid) 12 | 13 | var app = require('express')() 14 | 15 | var httpServer = worker.httpServer 16 | var scServer = worker.scServer 17 | 18 | scCrudMysql.attach(worker,{ 19 | db:config.db 20 | }) 21 | 22 | app.set('views', __dirname+'/views') 23 | app.set('view engine', 'jade') 24 | app.use(serveStatic(path.resolve(__dirname, 'public'))) 25 | 26 | httpServer.on('request', app) 27 | 28 | app.get('*',function(req,res) { 29 | res.render('home/index') 30 | }) 31 | 32 | var controllers = require('./config/controllers.js') 33 | 34 | scServer.on('connection', function (socket) { 35 | console.log(' >> Client',socket.id,'connected at',new Date()) 36 | 37 | console.log('ping sent to',socket.id) 38 | socket.emit('ping') 39 | 40 | socket.on('pong',function() { 41 | console.log('pong received from',socket.id) 42 | }) 43 | 44 | socket.on('ping-with-response',function(data,respond) { 45 | console.log(data) 46 | worker.exchange.publish('broadcast',{message:'Hello from broadcast!'}) 47 | respond(null,{message:'responding..'}) 48 | }) 49 | 50 | // Register Controllers 51 | controllers.register(socket) 52 | }) 53 | 54 | } 55 | -------------------------------------------------------------------------------- /examples/simple-example/README.md: -------------------------------------------------------------------------------- 1 | SocketCluster Sample App 2 | ====== 3 | 4 | This is a sample SocketCluster app. -------------------------------------------------------------------------------- /examples/simple-example/broker.js: -------------------------------------------------------------------------------- 1 | var scClusterBrokerClient = require('sc-cluster-broker-client'); 2 | 3 | module.exports.run = function (broker) { 4 | console.log(' >> Broker PID:', process.pid); 5 | 6 | // This is defined in server.js (taken from environment variable SC_CLUSTER_STATE_SERVER_HOST). 7 | // If this property is defined, the broker will try to attach itself to the SC cluster for 8 | // automatic horizontal scalability. 9 | // This is mostly intended for the Kubernetes deployment of SocketCluster - In this case, 10 | // The clustering/sharding all happens automatically. 11 | 12 | if (broker.options.clusterStateServerHost) { 13 | scClusterBrokerClient.attach(broker, { 14 | stateServerHost: broker.options.clusterStateServerHost, 15 | stateServerPort: broker.options.clusterStateServerPort, 16 | authKey: broker.options.clusterAuthKey, 17 | stateServerConnectTimeout: broker.options.clusterStateServerConnectTimeout, 18 | stateServerAckTimeout: broker.options.clusterStateServerAckTimeout, 19 | stateServerReconnectRandomness: broker.options.clusterStateServerReconnectRandomness 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /examples/simple-example/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Test APP", 3 | "port":3000 4 | } -------------------------------------------------------------------------------- /examples/simple-example/init.js: -------------------------------------------------------------------------------- 1 | module.exports.run = function(thisProcess) { 2 | 3 | } -------------------------------------------------------------------------------- /examples/simple-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socketcluster-sample", 3 | "description": "A sample SocketCluster app", 4 | "version": "1.0.0", 5 | "contributors": [ 6 | { 7 | "name": "Jonathan Gros-Dubois", 8 | "email": "grosjona@yahoo.com.au" 9 | } 10 | ], 11 | "dependencies": { 12 | "connect": "3.0.1", 13 | "express": "4.13.1", 14 | "jade": "^1.11.0", 15 | "minimist": "1.1.0", 16 | "pug": "^2.0.0-beta6", 17 | "pug-runtime": "^2.0.2", 18 | "sc-cluster-broker-client": "1.x.x", 19 | "serve-static": "1.8.0", 20 | "socket.io": "^1.4.8", 21 | "socketcluster": "5.x.x", 22 | "socketcluster-client": "5.x.x", 23 | "templatizer": "^1.5.4", 24 | "vue-socket.io": "^1.0.2" 25 | }, 26 | "keywords": [ 27 | "websocket", 28 | "server", 29 | "realtime", 30 | "cluster", 31 | "scalable" 32 | ], 33 | "readmeFilename": "README.md" 34 | } 35 | -------------------------------------------------------------------------------- /examples/simple-example/public/js/bin/templates.js: -------------------------------------------------------------------------------- 1 | ;(function(root,factory){ 2 | if (typeof define === 'function' && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory(); 6 | } else { 7 | if (typeof root === 'undefined' || root !== Object(root)) { 8 | throw new Error('puglatizer: window does not exist or is not an object'); 9 | } 10 | root.puglatizer = factory(); 11 | } 12 | }(this, function () { 13 | var pug = { 14 | merge:function pug_merge(r,e){if(1===arguments.length){for(var t=r[0],a=1;a]/).exec(a);if(!t)return e;var r,c,n,s="";for(r=t.index,c=0;r ":" ")+t+"| "+n}).join("\n");throw n.path=e,n.message=(e||"Pug")+":"+r+"\n"+a+"\n\n"+n.message,n} 21 | } 22 | 23 | var puglatizer = {} 24 | puglatizer["session"] = {} 25 | puglatizer["session"]["create"] = function template(a){var s,l,e="";try{l=1,e+='
',l=2,e+='
',l=3,e+='
',l=4,e+='
',l=5,e+="
",l=5,e+="Create you login screen here
",l=6,e+='
',l=7,e+='
',l=8,e+="",l=9,e+='
',l=10,e+='
',l=11,e+="",l=12,e+='
',l=13,e+="
",l=14,e+='
',l=15,e+='
"}catch(o){pug.rethrow(o,s,l)}return e}; 26 | 27 | 28 | return puglatizer; 29 | })); 30 | -------------------------------------------------------------------------------- /examples/simple-example/public/js/lib/vue-socketcluster.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | window.VueSocketcluster = {} 3 | 4 | if (!socketCluster) { 5 | throw new Error("[Vue-Socketcluster] cannot locate socketcluster-client") 6 | } 7 | 8 | var VueSocketcluster = { 9 | install: function (Vue, config) { 10 | 11 | if (typeof config == 'object') { 12 | if (!config.hostname || !config.port) { 13 | config.hostname = 'localhost', 14 | config.port = 3000 15 | } 16 | 17 | } else { 18 | config = { 19 | hostname:'localhost', 20 | port:3000 21 | } 22 | } 23 | 24 | var socket = socketCluster.connect(config) 25 | 26 | /* 27 | * Wildcard support 28 | * http://stackoverflow.com/questions/10405070/socket-io-client-respond-to-all-events-with-one-handler 29 | */ 30 | var onevent = socket.onevent; 31 | socket.onevent = function (packet) { 32 | var args = packet.data || []; 33 | onevent.call(this, packet); 34 | packet.data = ["*"].concat(args); 35 | onevent.call(this, packet); 36 | } 37 | 38 | var methods = [ 39 | "connect", 40 | "error", 41 | "disconnect", 42 | "reconnect", 43 | "reconnect_attempt", 44 | "reconnecting", 45 | "reconnect_error", 46 | "reconnect_failed" 47 | ] 48 | 49 | Vue.mixin({ 50 | created: function () { 51 | var self = this 52 | if (this.$options.hasOwnProperty("sockets")) { 53 | 54 | for (var key in self.$options.sockets) { 55 | if (self.$options.sockets.hasOwnProperty(key) && methods.indexOf(key) < 0) { 56 | socket.on(key,function(emit,data,respond) { 57 | self.$options.sockets[key].call(self,data,respond) 58 | }) 59 | } 60 | } 61 | 62 | // socket.on("*", function (emit, data) { 63 | // if (self.$options.sockets.hasOwnProperty(emit)) { 64 | // self.$options.sockets[emit].call(self, data) 65 | // } 66 | // }) 67 | 68 | methods.forEach(function (m) { 69 | socket.on(m, function (data,respond) { 70 | if (self.$options.sockets.hasOwnProperty(m)) { 71 | self.$options.sockets[m].call(self, data, respond) 72 | } 73 | }) 74 | }) 75 | } 76 | 77 | // Global socketio instance 78 | this.$sc = socket 79 | } 80 | }) 81 | 82 | 83 | } 84 | }; 85 | 86 | if (typeof exports == "object") { 87 | module.exports = VueSocketcluster 88 | } else if (typeof define == "function" && define.amd) { 89 | define([], function () { 90 | return VueSocketcluster 91 | }) 92 | } else if (window.Vue) { 93 | window.VueSocketcluster = VueSocketcluster; 94 | } 95 | 96 | 97 | })(); -------------------------------------------------------------------------------- /examples/simple-example/public/js/lib/vue-socketcluster.min.js: -------------------------------------------------------------------------------- 1 | !function(){if(window.VueSocketcluster={},!socketCluster)throw new Error("[Vue-Socketcluster] cannot locate socketcluster-client");var a={install:function(a,b){"object"==typeof b?b.hostname&&b.port||(b.hostname="localhost",b.port=3e3):b={hostname:"localhost",port:3e3};var c=socketCluster.connect(b),d=c.onevent;c.onevent=function(a){var b=a.data||[];d.call(this,a),a.data=["*"].concat(b),d.call(this,a)};var e=["connect","error","disconnect","reconnect","reconnect_attempt","reconnecting","reconnect_error","reconnect_failed"];a.mixin({created:function(){var a=this;if(this.$options.hasOwnProperty("sockets")){for(var b in a.$options.sockets)a.$options.sockets.hasOwnProperty(b)&&e.indexOf(b)<0&&c.on(b,function(c,d,e){a.$options.sockets[b].call(a,d,e)});e.forEach(function(b){c.on(b,function(c,d){a.$options.sockets.hasOwnProperty(b)&&a.$options.sockets[b].call(a,c,d)})})}this.$sc=c}})}};"object"==typeof exports?module.exports=a:"function"==typeof define&&define.amd?define([],function(){return a}):window.Vue&&(window.VueSocketcluster=a)}(); -------------------------------------------------------------------------------- /examples/simple-example/public/js/src/system/app.js: -------------------------------------------------------------------------------- 1 | var router = new VueRouter() 2 | 3 | Vue.use(VueRouter) 4 | Vue.use(VueSocketcluster) 5 | 6 | router.map({ 7 | '/session/create': { 8 | component:Vue.extend({ 9 | template: templatizer.session.create({}) 10 | }) 11 | } 12 | }) 13 | 14 | router.redirect({ 15 | '*':'/session/create' 16 | }) 17 | 18 | router.beforeEach(function(transition) { 19 | transition.next() 20 | }) 21 | 22 | router.afterEach(function(transition) { 23 | 24 | }) 25 | 26 | router.start(Vue.extend({ 27 | data() { 28 | return { 29 | 30 | } 31 | }, 32 | sockets:{ 33 | connect() { 34 | console.log('Connected to server!') 35 | }, 36 | ping() { 37 | this.$sc.emit('pong') 38 | } 39 | } 40 | }), '#app') -------------------------------------------------------------------------------- /examples/simple-example/puglatizer-1.0.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happilymarrieddad/vue-socketcluster/11224796b6528362dc5d893ba4c9f021e41889ef/examples/simple-example/puglatizer-1.0.3.tgz -------------------------------------------------------------------------------- /examples/simple-example/server.js: -------------------------------------------------------------------------------- 1 | var SocketCluster = require('socketcluster').SocketCluster, 2 | os = require('os'), 3 | fs = require('fs'), 4 | config = require('./config.json') 5 | 6 | var options = { 7 | workers: os.cpus().length || 1, 8 | brokers: 1, 9 | port: config.port, 10 | wsEngine: 'uws', 11 | appName: config.name || 'sc-app', 12 | workerController: __dirname + '/worker.js', 13 | brokerController: __dirname + '/broker.js', 14 | initController: __dirname + '/init.js', 15 | socketChannelLimit: 1000, 16 | crashWorkerOnError: true 17 | } 18 | 19 | var start = function () { 20 | var socketCluster = new SocketCluster(options) 21 | } 22 | 23 | var buildJade = function() { 24 | require('puglatizer')( 25 | __dirname+'/templates', 26 | __dirname+'/public/js/bin/templates.js' 27 | ) 28 | start() 29 | } 30 | 31 | buildJade() -------------------------------------------------------------------------------- /examples/simple-example/templates.js: -------------------------------------------------------------------------------- 1 | ;(function(root,factory){ 2 | if (typeof define === 'function' && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory(); 6 | } else { 7 | if (typeof root === 'undefined' || root !== Object(root)) { 8 | throw new Error('templatizer: window does not exist or is not an object'); 9 | } 10 | root.templatizer = factory(); 11 | } 12 | }(this, function () { 13 | var pug = require('pug/runtime') 14 | 15 | var puglatizer = {} 16 | puglatizer[""]["session"] = {} 17 | puglatizer[""]["session"]["create"] = function template(a){var s,l,e="";try{l=1,e+='
',l=2,e+='
',l=3,e+='
',l=4,e+='
',l=5,e+="
",l=5,e+="Create you login screen here
",l=6,e+='
',l=7,e+='
',l=8,e+="",l=9,e+='
',l=10,e+='
',l=11,e+="",l=12,e+='
',l=13,e+="
",l=14,e+='
',l=15,e+='
"}catch(o){pug.rethrow(o,s,l)}return e}; 18 | 19 | 20 | return puglatizer; 21 | })); 22 | -------------------------------------------------------------------------------- /examples/simple-example/templates/session/create.pug: -------------------------------------------------------------------------------- 1 | div.row(style='margin-top:100px') 2 | div.col-md-4.col-md-offset-4 3 | div.panel.panel-primary 4 | div.panel-heading 5 | h5 Create you login screen here 6 | div.panel-body 7 | div.form-group 8 | label Email 9 | input.form-control(type='email',name='email') 10 | div.form-group 11 | label Password 12 | input.form-control(type='password',name='password') 13 | hr 14 | div.form-group 15 | button.btn.btn-success(type='button') Login -------------------------------------------------------------------------------- /examples/simple-example/views/home/index.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | head 4 | title SC App 5 | 6 | meta(charset='utf-8') 7 | meta(name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no') 8 | meta(http-equiv='x-ua-compatible',content='ie=edge') 9 | 10 | link(rel='stylesheet',href='/css/bootstrap.min.css') 11 | 12 | body#app 13 | 14 | div.container-fluid 15 | div.row 16 | div.col-md-12 17 | router-view 18 | 19 | script(src='/js/lib/vue.min.js') 20 | //- Not currently being used but it is cool! (https://yuche.github.io/vue-strap/) 21 | script(src='/js/lib/vue-strap.min.js') 22 | script(src='/js/lib/vue-router.min.js') 23 | script(src='/js/lib/vue-socketcluster-with-socketcluster-client.min.js') 24 | 25 | script(src='/js/bin/templates.js') 26 | 27 | script(src='/js/src/system/app.js') -------------------------------------------------------------------------------- /examples/simple-example/views/pug.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang='en') 3 | head 4 | title SC App 5 | 6 | meta(charset='utf-8') 7 | meta(name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no') 8 | meta(http-equiv='x-ua-compatible',content='ie=edge') 9 | 10 | link(rel='stylesheet',href='/css/bootstrap.min.css') 11 | 12 | body#app 13 | 14 | div.container-fluid 15 | div.row 16 | div.col-md-12 17 | h3 Pug tester 18 | 19 | //- script(src='/js/lib/require.min.js') 20 | //- script. 21 | //- requirejs.config({ 22 | //- //By default load any module IDs from js/lib 23 | //- baseUrl: 'js/bin', 24 | //- //except, if the module ID starts with "app", 25 | //- //load it from the js/app directory. paths 26 | //- //config is relative to the baseUrl, and 27 | //- //never includes a ".js" extension since 28 | //- //the paths config could be for a directory. 29 | //- paths: { 30 | //- pug: '../../../node_modules/pug' 31 | //- } 32 | //- }) 33 | 34 | //var pug = require(['pug']) 35 | 36 | 37 | script(src='/js/bin/templates.js') -------------------------------------------------------------------------------- /examples/simple-example/worker.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | express = require('express'), 3 | serveStatic = require('serve-static'), 4 | path = require('path'); 5 | 6 | module.exports.run = function (worker) { 7 | console.log(' >> Worker PID:', process.pid) 8 | 9 | var app = require('express')() 10 | 11 | var httpServer = worker.httpServer 12 | var scServer = worker.scServer 13 | 14 | app.set('views', __dirname+'/views') 15 | app.set('view engine', 'pug') 16 | app.use(serveStatic(path.resolve(__dirname, 'public'))) 17 | app.use('/scripts',express.static(__dirname + '/node_modules')) 18 | 19 | httpServer.on('request', app) 20 | 21 | app.get('/pug',function(req,res) { 22 | res.render('pug') 23 | }) 24 | 25 | app.get('*',function(req,res) { 26 | res.render('home/index') 27 | }) 28 | 29 | scServer.on('connection', function (socket) { 30 | console.log(' >> Client',socket.id,'connected at',new Date()) 31 | 32 | console.log('ping sent to',socket.id) 33 | socket.emit('ping') 34 | 35 | socket.on('pong',function() { 36 | console.log('pong received from',socket.id) 37 | }) 38 | 39 | }) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-socketcluster", 3 | "version": "1.1.1", 4 | "description": "socketcluster implemantation for vuejs", 5 | "main": "vue-socketcluster.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/happilymarrieddad/vue-socketcluster.git" 12 | }, 13 | "keywords": [ 14 | "vuejs", 15 | "socket", 16 | "vue", 17 | "socketcluster", 18 | "socketcluster-client" 19 | ], 20 | "author": "Nick Kotenberg", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/happilymarrieddad/vue-socketcluster/issues" 24 | }, 25 | "homepage": "https://github.com/happilymarrieddad/vue-socketcluster#README.md", 26 | "dependencies": { 27 | "socketcluster-client": "5.0.6" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vue-socketcluster.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | var version = '1.1.0'; 3 | window.VueSocketcluster = {}; 4 | 5 | if (!socketCluster) { 6 | throw new Error("[Vue-Socketcluster] cannot locate socketcluster-client"); 7 | } 8 | 9 | var VueSocketcluster = { 10 | install: function (Vue, config) { 11 | 12 | if (typeof config == 'object') { 13 | if (!config.hostname || !config.port) { 14 | config.hostname = 'localhost'; 15 | config.port = 3000; 16 | } 17 | 18 | } else { 19 | config = { 20 | hostname:'localhost', 21 | port:3000 22 | }; 23 | } 24 | 25 | var socket = socketCluster.connect(config); 26 | 27 | var onevent = socket.onevent; 28 | socket.onevent = function (packet) { 29 | var args = packet.data || []; 30 | onevent.call(this, packet); 31 | packet.data = ["*"].concat(args); 32 | onevent.call(this, packet); 33 | } 34 | 35 | var methods = [ 36 | "error", 37 | "connect", 38 | "disconnect", 39 | "connectAbort", 40 | "raw", 41 | "kickOut", 42 | "subscribe", 43 | "subscribeFail", 44 | "unsubscribe", 45 | "authStateChange", 46 | "authTokenChange", 47 | "subscribeStateChange", 48 | "subscribeRequest", 49 | "authenticate", 50 | "deauthenticate", 51 | "message" 52 | ]; 53 | 54 | Vue.mixin({ 55 | created: function () { 56 | var self = this; 57 | if (this.$options.hasOwnProperty("sockets")) { 58 | 59 | for (var key in self.$options.sockets) { 60 | if (self.$options.sockets.hasOwnProperty(key) && methods.indexOf(key) < 0) { 61 | socket.on(key,function(emit,data,respond) { 62 | self.$options.sockets[key].call(self,data,respond); 63 | }) 64 | } 65 | } 66 | 67 | methods.forEach(function (event) { 68 | socket.on(event, function (data,respond) { 69 | if (self.$options.sockets.hasOwnProperty(event)) { 70 | self.$options.sockets[event].call(self, data, respond); 71 | } 72 | }) 73 | }) 74 | } 75 | 76 | if (this.$options.hasOwnProperty("subscriptions")) { 77 | 78 | for (var key in self.$options.subscriptions) { 79 | if (self.$options.subscriptions.hasOwnProperty(key) && methods.indexOf(key) < 0) { 80 | var watcher = socket.subscribe(key) 81 | watcher.watch(function(data) { 82 | self.$options.subscriptions[key].call(self,data) 83 | }) 84 | } 85 | } 86 | 87 | } 88 | 89 | // Global VueSocketcluster instance 90 | this.$sc = socket; 91 | } 92 | }) 93 | 94 | 95 | } 96 | }; 97 | 98 | if (typeof exports == "object") { 99 | module.exports = VueSocketcluster; 100 | } else if (typeof define == "function" && define.amd) { 101 | define([], function () { 102 | return VueSocketcluster; 103 | }) 104 | } else if (window.Vue) { 105 | window.VueSocketcluster = VueSocketcluster; 106 | } 107 | 108 | 109 | })(); -------------------------------------------------------------------------------- /vue-socketcluster.min.js: -------------------------------------------------------------------------------- 1 | !function(){if(window.VueSocketcluster={},!socketCluster)throw new Error("[Vue-Socketcluster] cannot locate socketcluster-client");var b={install:function(a,b){"object"==typeof b?b.hostname&&b.port||(b.hostname="localhost",b.port=3e3):b={hostname:"localhost",port:3e3};var c=socketCluster.connect(b),d=c.onevent;c.onevent=function(a){var b=a.data||[];d.call(this,a),a.data=["*"].concat(b),d.call(this,a)};var e=["error","connect","disconnect","connectAbort","raw","kickOut","subscribe","subscribeFail","unsubscribe","authStateChange","authTokenChange","subscribeStateChange","subscribeRequest","authenticate","deauthenticate","message"];a.mixin({created:function(){var a=this;if(this.$options.hasOwnProperty("sockets")){for(var b in a.$options.sockets)a.$options.sockets.hasOwnProperty(b)&&e.indexOf(b)<0&&c.on(b,function(c,d,e){a.$options.sockets[b].call(a,d,e)});e.forEach(function(b){c.on(b,function(c,d){a.$options.sockets.hasOwnProperty(b)&&a.$options.sockets[b].call(a,c,d)})})}if(this.$options.hasOwnProperty("subscriptions"))for(var b in a.$options.subscriptions)if(a.$options.subscriptions.hasOwnProperty(b)&&e.indexOf(b)<0){var d=c.subscribe(b);d.watch(function(c){a.$options.subscriptions[b].call(a,c)})}this.$sc=c}})}};"object"==typeof exports?module.exports=b:"function"==typeof define&&define.amd?define([],function(){return b}):window.Vue&&(window.VueSocketcluster=b)}(); --------------------------------------------------------------------------------