diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..a213078
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,37 @@
+{
+ "env": {
+ "browser": true,
+ "commonjs": true,
+ "es6": true
+ },
+ "extends": "eslint:recommended",
+ "parserOptions": {
+ "ecmaFeatures": {
+ "experimentalObjectRestSpread": true
+ },
+ "sourceType": "module"
+ },
+ "plugins": [
+ ],
+ "rules": {
+ "indent": [
+ "error",
+ 2,
+ { "SwitchCase": 1 }
+ ],
+ "linebreak-style": [
+ "error",
+ "unix"
+ ],
+ "no-console": "off",
+ "no-unused-vars": "warn",
+ "quotes": [
+ "error",
+ "single"
+ ],
+ "semi": [
+ "error",
+ "always"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2a8d2fb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+dist/
+node_modules/
+env.config.js
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..f6fe7c8
--- /dev/null
+++ b/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "tools-demo",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "start": "webpack-dev-server",
+ "build": "webpack --config webpack.production.config.js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "babel-core": "^6.24.0",
+ "babel-loader": "^6.4.1",
+ "babel-preset-es2015": "^6.24.0",
+ "compression-webpack-plugin": "^0.3.2",
+ "css-loader": "^0.27.2",
+ "eslint": "^4.3.0",
+ "eslint-plugin-react": "^7.1.0",
+ "extract-loader": "^0.1.0",
+ "extract-text-webpack-plugin": "^2.1.0",
+ "favicons-webpack-plugin": "0.0.7",
+ "file-loader": "^0.10.1",
+ "html-loader": "^0.4.5",
+ "html-webpack-plugin": "^2.28.0",
+ "image-webpack-loader": "^3.2.0",
+ "node-sass": "^4.5.0",
+ "raw-loader": "^0.5.1",
+ "sass-loader": "^6.0.3",
+ "style-ext-html-webpack-plugin": "^3.3.0",
+ "style-loader": "^0.13.2",
+ "webpack": "^2.5.1",
+ "webpack-dev-server": "^2.4.1"
+ },
+ "dependencies": {
+ "jquery": "^3.1.1",
+ "lodash": "^4.17.4",
+ "pusher-js": "^4.1.0"
+ }
+}
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..a4019b0
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,20 @@
+
+
+
+ 9to5 : Tools
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/js/constants.js b/src/js/constants.js
new file mode 100644
index 0000000..f2ffef5
--- /dev/null
+++ b/src/js/constants.js
@@ -0,0 +1,4 @@
+export const PUSHER_KEY = process.env.PUSHER_KEY;
+
+export const SERVER_URL = process.env.SERVER_URL;
+export const SERVER_AUTH_URL = SERVER_URL + '/pusher/auth';
\ No newline at end of file
diff --git a/src/js/index.js b/src/js/index.js
new file mode 100644
index 0000000..9336470
--- /dev/null
+++ b/src/js/index.js
@@ -0,0 +1,62 @@
+import $ from 'jquery';
+import Pusher from 'pusher-js';
+import { PUSHER_KEY, SERVER_AUTH_URL } from './constants';
+
+const pusher = new Pusher(PUSHER_KEY, {
+ cluster: 'us2',
+ encrypted: true,
+ authEndpoint: SERVER_AUTH_URL
+});
+const channel = pusher.subscribe('private-tools-demo');
+channel.bind('pusher:subscription_succeeded', function() {
+ console.log('connected');
+});
+
+try {
+ var userId = localStorage.getItem('userId');
+ if (!userId) {
+ userId = Math.floor(Math.random() * 100000);
+ localStorage.setItem('userId', userId);
+ }
+} catch (e) {
+ userId = Math.floor(Math.random() * 100000);
+}
+
+$(document).ready(() => {
+ $('.tools').click(function(e) {
+ var offset = $(this).offset();
+ var relativeX = (e.pageX - offset.left);
+ var relativeY = (e.pageY - offset.top);
+ const payload = {
+ user: userId,
+ action: {
+ type: 'canvas-click',
+ data: {
+ x: relativeX,
+ y: relativeY
+ }
+ }
+ };
+ channel.trigger('client-tool-action', payload);
+ });
+
+ $('.btn').click(function(e) {
+ const payload = {
+ user: userId,
+ action: {
+ type: 'btn-click',
+ data: $(this).text()
+ }
+ };
+ channel.trigger('client-tool-action', payload);
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ });
+
+
+ channel.bind('client-tool-action', function(data) {
+ console.log(data);
+ $('.console pre').text(JSON.stringify(data, null, 2));
+ });
+});
\ No newline at end of file
diff --git a/src/styles/main.scss b/src/styles/main.scss
new file mode 100644
index 0000000..58ab5fa
--- /dev/null
+++ b/src/styles/main.scss
@@ -0,0 +1,44 @@
+$red: #FF2120;
+$blue: #271DFF;
+
+html, body {
+ margin: 0;
+ font-family: Futura;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
+.tools, .console {
+ width: 100%;
+ height: 50%;
+ overflow-y: scroll;
+}
+
+.console {
+ border-top: 1px solid #ccc;
+
+ pre {
+ margin: 16px;
+ padding: 8px;
+ background-color: #eee;
+ }
+}
+
+.btn {
+ cursor: pointer;
+ display: inline-block;
+ margin: 8px;
+ border: 4px solid black;
+ padding: 8px 16px;
+
+ &:hover {
+ color: $blue;
+ border-color: $blue;
+ }
+
+ &:active {
+ color: $red;
+ border-color: $red;
+ }
+}
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..af21e72
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,69 @@
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
+const StyleExtHtmlWebpackPlugin = require('style-ext-html-webpack-plugin');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
+const CompressionPlugin = require('compression-webpack-plugin');
+const webpack = require('webpack');
+const path = require('path');
+const config = require('./env.config.js').development;
+
+module.exports = {
+ devtool: '#cheap-module-eval-source-map',
+ entry: [
+ 'webpack-dev-server/client?http://0.0.0.0:8080',
+ './src/js/index.js',
+ './src/styles/main.scss'
+ ],
+ output: {
+ filename: 'bundle.js',
+ path: path.resolve(__dirname, 'dist')
+ },
+ resolve: {
+ extensions: ['.js', '.jsx'],
+ modules: ['node_modules']
+ },
+ module: {
+ rules: [
+ {
+ test: /\.js?$/,
+ exclude: /node_modules/,
+ use: 'babel-loader?presets[]=es2015'
+ },
+ {
+ test: /\.scss$/,
+ exclude: /node_modules/,
+ use: ExtractTextPlugin.extract({
+ fallback: 'style-loader',
+ use: ['css-loader', 'sass-loader']
+ })
+ }
+ ]
+ },
+ plugins: [
+ new webpack.DefinePlugin({
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('development'),
+ 'PUSHER_KEY': config.PUSHER_KEY,
+ 'SERVER_URL': config.SERVER_URL
+ }
+ }),
+ new webpack.HotModuleReplacementPlugin(),
+ new HtmlWebpackPlugin({ template: './src/index.html' }),
+ new ExtractTextPlugin('styles.css'),
+ // new StyleExtHtmlWebpackPlugin(),
+ // new webpack.optimize.UglifyJsPlugin({ output: { ascii_only: true } })
+ ],
+ devServer: {
+ contentBase: './dist',
+ watchOptions: {
+ aggregateTimeout: 100,
+ poll: 300
+ },
+ historyApiFallback: {
+ index: 'index.html'
+ },
+ hot: true,
+ host: '0.0.0.0',
+ port: 8080
+ }
+};
\ No newline at end of file
diff --git a/webpack.production.config.js b/webpack.production.config.js
new file mode 100644
index 0000000..a37614d
--- /dev/null
+++ b/webpack.production.config.js
@@ -0,0 +1,55 @@
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
+const StyleExtHtmlWebpackPlugin = require('style-ext-html-webpack-plugin');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
+const CompressionPlugin = require('compression-webpack-plugin');
+const webpack = require('webpack');
+const path = require('path');
+const config = require('./env.config.js').production;
+
+module.exports = {
+ devtool: '#cheap-module-eval-source-map',
+ entry: [
+ './src/js/index.js',
+ './src/styles/main.scss'
+ ],
+ output: {
+ filename: 'bundle.js',
+ path: path.resolve(__dirname, 'dist')
+ },
+ resolve: {
+ extensions: ['.js'],
+ modules: ['node_modules']
+ },
+ module: {
+ rules: [
+ {
+ test: /\.js?$/,
+ exclude: /node_modules/,
+ use: 'babel-loader?presets[]=es2015'
+ },
+ {
+ test: /\.scss$/,
+ exclude: /node_modules/,
+ use: ExtractTextPlugin.extract({
+ fallback: 'style-loader',
+ use: ['css-loader', 'sass-loader']
+ })
+ }
+ ]
+ },
+ plugins: [
+ new webpack.DefinePlugin({
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production'),
+ 'PUSHER_KEY': config.PUSHER_KEY,
+ 'SERVER_URL': config.SERVER_URL
+ }
+ }),
+ new webpack.HotModuleReplacementPlugin(),
+ new HtmlWebpackPlugin({ template: './src/index.html' }),
+ new ExtractTextPlugin('styles.css'),
+ // new StyleExtHtmlWebpackPlugin(),
+ // new webpack.optimize.UglifyJsPlugin({ output: { ascii_only: true } })
+ ],
+};
\ No newline at end of file