mirror of
https://github.com/darkwire/darkwire.io.git
synced 2025-07-18 10:49:02 +00:00
Add JSCS and JSHint to tests and fix errors
This commit is contained in:
parent
4338f1aa8a
commit
cfa5da5695
8
.jscsrc
Normal file
8
.jscsrc
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"preset": "google",
|
||||||
|
"esnext": true,
|
||||||
|
"requireCamelCaseOrUpperCaseIdentifiers": {"ignoreProperties": true},
|
||||||
|
"maxErrors": null,
|
||||||
|
"maximumLineLength": null,
|
||||||
|
"excludeFiles": ["src/public"]
|
||||||
|
}
|
2
.jshintignore
Normal file
2
.jshintignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
src/public
|
42
gulpfile.babel.js
Normal file
42
gulpfile.babel.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import gulp from 'gulp';
|
||||||
|
import uglify from 'gulp-uglify';
|
||||||
|
import nodemon from 'gulp-nodemon';
|
||||||
|
import browserify from 'browserify';
|
||||||
|
import babel from 'babelify';
|
||||||
|
import source from 'vinyl-source-stream';
|
||||||
|
import buffer from 'vinyl-buffer';
|
||||||
|
import childProcess from 'child_process';
|
||||||
|
|
||||||
|
let spawn = childProcess.spawn;
|
||||||
|
|
||||||
|
gulp.task('bundle', function() {
|
||||||
|
return browserify('src/js/main.js', {
|
||||||
|
debug: true
|
||||||
|
}).transform(babel.configure({
|
||||||
|
presets: ['es2015']
|
||||||
|
})).bundle()
|
||||||
|
.pipe(source('main.js'))
|
||||||
|
.pipe(buffer())
|
||||||
|
.pipe(uglify())
|
||||||
|
.pipe(gulp.dest('src/public'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('start', function() {
|
||||||
|
nodemon({
|
||||||
|
script: 'index.js',
|
||||||
|
ext: 'css js mustache',
|
||||||
|
ignore: ['src/public/main.js'],
|
||||||
|
env: {
|
||||||
|
'NODE_ENV': 'development'
|
||||||
|
},
|
||||||
|
tasks: ['bundle']
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('test', function() {
|
||||||
|
let test = spawn(
|
||||||
|
'mocha',
|
||||||
|
['test', '--compilers', 'js:babel-core/register'],
|
||||||
|
{stdio: 'inherit'}
|
||||||
|
);
|
||||||
|
});
|
29
gulpfile.js
29
gulpfile.js
@ -1,29 +0,0 @@
|
|||||||
var gulp = require('gulp');
|
|
||||||
var uglify = require('gulp-uglify');
|
|
||||||
var nodemon = require('gulp-nodemon');
|
|
||||||
var browserify = require('browserify');
|
|
||||||
var babel = require('babelify');
|
|
||||||
var source = require('vinyl-source-stream');
|
|
||||||
var buffer = require('vinyl-buffer');
|
|
||||||
|
|
||||||
gulp.task('bundle', function() {
|
|
||||||
return browserify('src/js/main.js', { debug: true }).transform(babel.configure({
|
|
||||||
presets: ["es2015"]
|
|
||||||
})).bundle()
|
|
||||||
.pipe(source('main.js'))
|
|
||||||
.pipe(buffer())
|
|
||||||
.pipe(uglify())
|
|
||||||
.pipe(gulp.dest('src/public'))
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('start', function() {
|
|
||||||
nodemon({
|
|
||||||
script: 'index.js',
|
|
||||||
ext: 'css js mustache',
|
|
||||||
ignore: ['src/public/main.js'],
|
|
||||||
env: {
|
|
||||||
'NODE_ENV': 'development'
|
|
||||||
},
|
|
||||||
tasks: ['bundle']
|
|
||||||
})
|
|
||||||
});
|
|
5
index.js
5
index.js
@ -1,3 +1,2 @@
|
|||||||
|
require('babel-register')();
|
||||||
require('babel/register');
|
require('./src/app.js');
|
||||||
require('./src/app.js');
|
|
||||||
|
10
package.json
10
package.json
@ -19,17 +19,19 @@
|
|||||||
"uuid": "^2.0.1"
|
"uuid": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"babel-core": "^6.5.2",
|
||||||
"babel-preset-es2015": "^6.3.13",
|
"babel-preset-es2015": "^6.3.13",
|
||||||
|
"babel-register": "^6.5.2",
|
||||||
"compression": "^1.6.0",
|
"compression": "^1.6.0",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-nodemon": "^2.0.6",
|
"gulp-nodemon": "^2.0.6",
|
||||||
|
"jscs": "^2.10.1",
|
||||||
|
"jshint": "^2.9.1",
|
||||||
|
"mocha-jscs": "^4.2.0",
|
||||||
|
"mocha-jshint": "^2.3.1",
|
||||||
"vinyl-buffer": "^1.0.0",
|
"vinyl-buffer": "^1.0.0",
|
||||||
"vinyl-source-stream": "^1.1.0"
|
"vinyl-source-stream": "^1.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"start": "gulp start",
|
|
||||||
"bundle": "gulp bundle"
|
|
||||||
},
|
|
||||||
"author": "Daniel Seripap",
|
"author": "Daniel Seripap",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
Simple encrypted web chat. Powered by [socket.io](http://socket.io) and the [web cryptography API](https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto).
|
Simple encrypted web chat. Powered by [socket.io](http://socket.io) and the [web cryptography API](https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto).
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
|
# For es6 compatability, be sure you have the latest stable version of Node JS installed
|
||||||
|
npm install -g n
|
||||||
|
n stable
|
||||||
|
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
@ -7,7 +7,8 @@ export default class CryptoUtil {
|
|||||||
backdrop: 'static',
|
backdrop: 'static',
|
||||||
show: false,
|
show: false,
|
||||||
keyboard: false
|
keyboard: false
|
||||||
})
|
});
|
||||||
|
|
||||||
$('#no-crypto').modal('show');
|
$('#no-crypto').modal('show');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -28,7 +29,7 @@ export default class CryptoUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
convertArrayBufferViewToString(buffer) {
|
convertArrayBufferViewToString(buffer) {
|
||||||
let str = "";
|
let str = '';
|
||||||
for (let i = 0; i < buffer.byteLength; i++) {
|
for (let i = 0; i < buffer.byteLength; i++) {
|
||||||
str += String.fromCharCode(buffer[i]);
|
str += String.fromCharCode(buffer[i]);
|
||||||
}
|
}
|
||||||
@ -36,39 +37,39 @@ export default class CryptoUtil {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
createSigningKey() {
|
createSigningKey() {
|
||||||
return this._crypto.subtle.generateKey(
|
return this._crypto.subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: "HMAC",
|
name: 'HMAC',
|
||||||
hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
|
hash: {name: 'SHA-256'}, //can be 'SHA-1', 'SHA-256', 'SHA-384', or 'SHA-512'
|
||||||
//length: 256, //optional, if you want your key length to differ from the hash function's block length
|
//length: 256, //optional, if you want your key length to differ from the hash function's block length
|
||||||
},
|
},
|
||||||
true, //whether the key is extractable (i.e. can be used in exportKey)
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
||||||
["sign", "verify"] //can be any combination of "sign" and "verify"
|
['sign', 'verify'] //can be any combination of 'sign' and 'verify'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
createPrimaryKeys() {
|
createPrimaryKeys() {
|
||||||
return this._crypto.subtle.generateKey(
|
return this._crypto.subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: "RSA-OAEP",
|
name: 'RSA-OAEP',
|
||||||
modulusLength: 2048, //can be 1024, 2048, or 4096
|
modulusLength: 2048, //can be 1024, 2048, or 4096
|
||||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||||
hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
|
hash: {name: 'SHA-256'}, //can be 'SHA-1', 'SHA-256', 'SHA-384', or 'SHA-512'
|
||||||
},
|
},
|
||||||
true, //whether the key is extractable (i.e. can be used in exportKey)
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
||||||
["encrypt", "decrypt"] //must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"]
|
['encrypt', 'decrypt'] //must be ['encrypt', 'decrypt'] or ['wrapKey', 'unwrapKey']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
createSecretKey() {
|
createSecretKey() {
|
||||||
return this._crypto.subtle.generateKey(
|
return this._crypto.subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: "AES-CBC",
|
name: 'AES-CBC',
|
||||||
length: 256, //can be 128, 192, or 256
|
length: 256, //can be 128, 192, or 256
|
||||||
},
|
},
|
||||||
true, //whether the key is extractable (i.e. can be used in exportKey)
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
||||||
["encrypt", "decrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
|
['encrypt', 'decrypt'] //can be 'encrypt', 'decrypt', 'wrapKey', or 'unwrapKey'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,24 +77,24 @@ export default class CryptoUtil {
|
|||||||
// Secret key will be recipient's public key
|
// Secret key will be recipient's public key
|
||||||
return this._crypto.subtle.encrypt(
|
return this._crypto.subtle.encrypt(
|
||||||
{
|
{
|
||||||
name: "RSA-OAEP",
|
name: 'RSA-OAEP',
|
||||||
modulusLength: 2048,
|
modulusLength: 2048,
|
||||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||||
hash: {name: "SHA-256"}
|
hash: {name: 'SHA-256'}
|
||||||
},
|
},
|
||||||
secretKey,
|
secretKey,
|
||||||
data //ArrayBuffer of data you want to encrypt
|
data //ArrayBuffer of data you want to encrypt
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptSecretKey(data, key) {
|
decryptSecretKey(data, key) {
|
||||||
// key will be my private key
|
// key will be my private key
|
||||||
return this._crypto.subtle.decrypt(
|
return this._crypto.subtle.decrypt(
|
||||||
{
|
{
|
||||||
name: "RSA-OAEP",
|
name: 'RSA-OAEP',
|
||||||
modulusLength: 2048,
|
modulusLength: 2048,
|
||||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||||
hash: {name: "SHA-256"}
|
hash: {name: 'SHA-256'}
|
||||||
//label: Uint8Array([...]) //optional
|
//label: Uint8Array([...]) //optional
|
||||||
},
|
},
|
||||||
key,
|
key,
|
||||||
@ -105,24 +106,24 @@ export default class CryptoUtil {
|
|||||||
// Secret key will be recipient's public key
|
// Secret key will be recipient's public key
|
||||||
return this._crypto.subtle.encrypt(
|
return this._crypto.subtle.encrypt(
|
||||||
{
|
{
|
||||||
name: "RSA-OAEP",
|
name: 'RSA-OAEP',
|
||||||
modulusLength: 2048,
|
modulusLength: 2048,
|
||||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||||
hash: {name: "SHA-256"}
|
hash: {name: 'SHA-256'}
|
||||||
},
|
},
|
||||||
signingKey,
|
signingKey,
|
||||||
data //ArrayBuffer of data you want to encrypt
|
data //ArrayBuffer of data you want to encrypt
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptSigningKey(data, key) {
|
decryptSigningKey(data, key) {
|
||||||
// key will be my private key
|
// key will be my private key
|
||||||
return this._crypto.subtle.decrypt(
|
return this._crypto.subtle.decrypt(
|
||||||
{
|
{
|
||||||
name: "RSA-OAEP",
|
name: 'RSA-OAEP',
|
||||||
modulusLength: 2048,
|
modulusLength: 2048,
|
||||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||||
hash: {name: "SHA-256"}
|
hash: {name: 'SHA-256'}
|
||||||
//label: Uint8Array([...]) //optional
|
//label: Uint8Array([...]) //optional
|
||||||
},
|
},
|
||||||
key,
|
key,
|
||||||
@ -133,7 +134,7 @@ export default class CryptoUtil {
|
|||||||
encryptMessage(data, secretKey, iv) {
|
encryptMessage(data, secretKey, iv) {
|
||||||
return this._crypto.subtle.encrypt(
|
return this._crypto.subtle.encrypt(
|
||||||
{
|
{
|
||||||
name: "AES-CBC",
|
name: 'AES-CBC',
|
||||||
//Don't re-use initialization vectors!
|
//Don't re-use initialization vectors!
|
||||||
//Always generate a new iv every time your encrypt!
|
//Always generate a new iv every time your encrypt!
|
||||||
iv: iv,
|
iv: iv,
|
||||||
@ -146,65 +147,65 @@ export default class CryptoUtil {
|
|||||||
decryptMessage(data, secretKey, iv) {
|
decryptMessage(data, secretKey, iv) {
|
||||||
return this._crypto.subtle.decrypt(
|
return this._crypto.subtle.decrypt(
|
||||||
{
|
{
|
||||||
name: "AES-CBC",
|
name: 'AES-CBC',
|
||||||
iv: iv, //The initialization vector you used to encrypt
|
iv: iv, //The initialization vector you used to encrypt
|
||||||
},
|
},
|
||||||
secretKey, //from generateKey or importKey above
|
secretKey, //from generateKey or importKey above
|
||||||
data //ArrayBuffer of the data
|
data //ArrayBuffer of the data
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
importSecretKey(jwkData, format) {
|
importSecretKey(jwkData, format) {
|
||||||
return this._crypto.subtle.importKey(
|
return this._crypto.subtle.importKey(
|
||||||
format || "jwk", //can be "jwk" or "raw"
|
format || 'jwk', //can be 'jwk' or 'raw'
|
||||||
//this is an example jwk key, "raw" would be an ArrayBuffer
|
//this is an example jwk key, 'raw' would be an ArrayBuffer
|
||||||
jwkData,
|
jwkData,
|
||||||
{ //this is the algorithm options
|
{ //this is the algorithm options
|
||||||
name: "AES-CBC",
|
name: 'AES-CBC',
|
||||||
},
|
},
|
||||||
true, //whether the key is extractable (i.e. can be used in exportKey)
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
||||||
["encrypt", "decrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
|
['encrypt', 'decrypt'] //can be 'encrypt', 'decrypt', 'wrapKey', or 'unwrapKey'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
importPrimaryKey(jwkData, format) {
|
importPrimaryKey(jwkData, format) {
|
||||||
// Will be someone's public key
|
// Will be someone's public key
|
||||||
let hashObj = {
|
let hashObj = {
|
||||||
name: "RSA-OAEP"
|
name: 'RSA-OAEP'
|
||||||
};
|
};
|
||||||
if (!this._crypto.webkitSubtle) {
|
if (!this._crypto.webkitSubtle) {
|
||||||
hashObj.hash = {name: "SHA-256"};
|
hashObj.hash = {name: 'SHA-256'};
|
||||||
}
|
}
|
||||||
return this._crypto.subtle.importKey(
|
return this._crypto.subtle.importKey(
|
||||||
format || "jwk", //can be "jwk" (public or private), "spki" (public only), or "pkcs8" (private only)
|
format || 'jwk', //can be 'jwk' (public or private), 'spki' (public only), or 'pkcs8' (private only)
|
||||||
jwkData,
|
jwkData,
|
||||||
hashObj,
|
hashObj,
|
||||||
true, //whether the key is extractable (i.e. can be used in exportKey)
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
||||||
["encrypt"] //"encrypt" or "wrapKey" for public key import or
|
['encrypt'] //'encrypt' or 'wrapKey' for public key import or
|
||||||
//"decrypt" or "unwrapKey" for private key imports
|
//'decrypt' or 'unwrapKey' for private key imports
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
exportKey(key, format) {
|
exportKey(key, format) {
|
||||||
// Will be public primary key or public signing key
|
// Will be public primary key or public signing key
|
||||||
return this._crypto.subtle.exportKey(
|
return this._crypto.subtle.exportKey(
|
||||||
format || "jwk", //can be "jwk" (public or private), "spki" (public only), or "pkcs8" (private only)
|
format || 'jwk', //can be 'jwk' (public or private), 'spki' (public only), or 'pkcs8' (private only)
|
||||||
key //can be a publicKey or privateKey, as long as extractable was true
|
key //can be a publicKey or privateKey, as long as extractable was true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
importSigningKey(jwkData) {
|
importSigningKey(jwkData) {
|
||||||
return this._crypto.subtle.importKey(
|
return this._crypto.subtle.importKey(
|
||||||
"raw", //can be "jwk" (public or private), "spki" (public only), or "pkcs8" (private only)
|
'raw', //can be 'jwk' (public or private), 'spki' (public only), or 'pkcs8' (private only)
|
||||||
//this is an example jwk key, other key types are Uint8Array objects
|
//this is an example jwk key, other key types are Uint8Array objects
|
||||||
jwkData,
|
jwkData,
|
||||||
{ //these are the algorithm options
|
{ //these are the algorithm options
|
||||||
name: "HMAC",
|
name: 'HMAC',
|
||||||
hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
|
hash: {name: 'SHA-256'}, //can be 'SHA-1', 'SHA-256', 'SHA-384', or 'SHA-512'
|
||||||
//length: 256, //optional, if you want your key length to differ from the hash function's block length
|
//length: 256, //optional, if you want your key length to differ from the hash function's block length
|
||||||
},
|
},
|
||||||
true, //whether the key is extractable (i.e. can be used in exportKey)
|
true, //whether the key is extractable (i.e. can be used in exportKey)
|
||||||
["verify"] //"verify" for public key import, "sign" for private key imports
|
['verify'] //'verify' for public key import, 'sign' for private key imports
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,26 +213,25 @@ export default class CryptoUtil {
|
|||||||
// Will use my private key
|
// Will use my private key
|
||||||
return this._crypto.subtle.sign(
|
return this._crypto.subtle.sign(
|
||||||
{
|
{
|
||||||
name: "HMAC",
|
name: 'HMAC',
|
||||||
hash: {name: "SHA-256"}
|
hash: {name: 'SHA-256'}
|
||||||
},
|
},
|
||||||
keyToSignWith, //from generateKey or importKey above
|
keyToSignWith, //from generateKey or importKey above
|
||||||
data //ArrayBuffer of data you want to sign
|
data //ArrayBuffer of data you want to sign
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyKey(signature, data, keyToVerifyWith) {
|
verifyKey(signature, data, keyToVerifyWith) {
|
||||||
// Will verify with sender's public key
|
// Will verify with sender's public key
|
||||||
return this._crypto.subtle.verify(
|
return this._crypto.subtle.verify(
|
||||||
{
|
{
|
||||||
name: "HMAC",
|
name: 'HMAC',
|
||||||
hash: {name: "SHA-256"}
|
hash: {name: 'SHA-256'}
|
||||||
},
|
},
|
||||||
keyToVerifyWith, //from generateKey or importKey above
|
keyToVerifyWith, //from generateKey or importKey above
|
||||||
signature, //ArrayBuffer of the signature
|
signature, //ArrayBuffer of the signature
|
||||||
data //ArrayBuffer of the data
|
data //ArrayBuffer of the data
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
108
src/js/main.js
108
src/js/main.js
@ -17,7 +17,7 @@ $(function() {
|
|||||||
'#e21400', '#ffe400', '#ff8f00',
|
'#e21400', '#ffe400', '#ff8f00',
|
||||||
'#58dc00', '#dd9cff', '#4ae8c4',
|
'#58dc00', '#dd9cff', '#4ae8c4',
|
||||||
'#3b88eb', '#f47777', '#d300e7',
|
'#3b88eb', '#f47777', '#d300e7',
|
||||||
];
|
];
|
||||||
|
|
||||||
let $window = $(window);
|
let $window = $(window);
|
||||||
let $messages = $('.messages'); // Messages area
|
let $messages = $('.messages'); // Messages area
|
||||||
@ -41,9 +41,9 @@ $(function() {
|
|||||||
|
|
||||||
let keys = {};
|
let keys = {};
|
||||||
|
|
||||||
if (!roomId) return;
|
if (!roomId) { return; }
|
||||||
|
|
||||||
$('input.share-text').val(document.location.protocol + "//" + document.location.host + roomId);
|
$('input.share-text').val(document.location.protocol + '//' + document.location.host + roomId);
|
||||||
|
|
||||||
$('input.share-text').click(function() {
|
$('input.share-text').click(function() {
|
||||||
$(this).focus();
|
$(this).focus();
|
||||||
@ -55,7 +55,7 @@ $(function() {
|
|||||||
|
|
||||||
FastClick.attach(document.body);
|
FastClick.attach(document.body);
|
||||||
|
|
||||||
function addParticipantsMessage (data) {
|
function addParticipantsMessage(data) {
|
||||||
let message = '';
|
let message = '';
|
||||||
let headerMsg = '';
|
let headerMsg = '';
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sets the client's username
|
// Sets the client's username
|
||||||
function initChat () {
|
function initChat() {
|
||||||
username = window.username;
|
username = window.username;
|
||||||
// warn not incognitor
|
// warn not incognitor
|
||||||
if (!fs) {
|
if (!fs) {
|
||||||
@ -71,7 +71,7 @@ $(function() {
|
|||||||
} else {
|
} else {
|
||||||
fs(window.TEMPORARY,
|
fs(window.TEMPORARY,
|
||||||
100,
|
100,
|
||||||
log.bind(log, "WARNING: Your browser is not in incognito mode!"));
|
log.bind(log, 'WARNING: Your browser is not in incognito mode!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the username is valid
|
// If the username is valid
|
||||||
@ -88,7 +88,7 @@ $(function() {
|
|||||||
private: data[0].privateKey
|
private: data[0].privateKey
|
||||||
};
|
};
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
cryptoUtil.exportKey(data[0].publicKey, "spki")
|
cryptoUtil.exportKey(data[0].publicKey, 'spki')
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
.then(function(exportedKeys) {
|
.then(function(exportedKeys) {
|
||||||
@ -102,7 +102,7 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log a message
|
// Log a message
|
||||||
function log (message, options) {
|
function log(message, options) {
|
||||||
let html = options && options.html === true || false;
|
let html = options && options.html === true || false;
|
||||||
let $el;
|
let $el;
|
||||||
if (html) {
|
if (html) {
|
||||||
@ -114,8 +114,10 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adds the visual chat message to the message list
|
// Adds the visual chat message to the message list
|
||||||
function addChatMessage (data, options) {
|
function addChatMessage(data, options) {
|
||||||
if (!data.message.trim().length) return;
|
if (!data.message.trim().length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't fade the message in if there is an 'X was typing'
|
// Don't fade the message in if there is an 'X was typing'
|
||||||
let $typingMessages = getTypingMessages(data);
|
let $typingMessages = getTypingMessages(data);
|
||||||
@ -141,15 +143,15 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adds the visual chat typing message
|
// Adds the visual chat typing message
|
||||||
function addChatTyping (data) {
|
function addChatTyping(data) {
|
||||||
data.typing = true;
|
data.typing = true;
|
||||||
data.message = 'is typing';
|
data.message = 'is typing';
|
||||||
addChatMessage(data);
|
addChatMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes the visual chat typing message
|
// Removes the visual chat typing message
|
||||||
function removeChatTyping (data) {
|
function removeChatTyping(data) {
|
||||||
getTypingMessages(data).fadeOut(function () {
|
getTypingMessages(data).fadeOut(function() {
|
||||||
$(this).remove();
|
$(this).remove();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -159,7 +161,7 @@ $(function() {
|
|||||||
// options.fade - If the element should fade-in (default = true)
|
// options.fade - If the element should fade-in (default = true)
|
||||||
// options.prepend - If the element should prepend
|
// options.prepend - If the element should prepend
|
||||||
// all other messages (default = false)
|
// all other messages (default = false)
|
||||||
function addMessageElement (el, options) {
|
function addMessageElement(el, options) {
|
||||||
let $el = $(el);
|
let $el = $(el);
|
||||||
|
|
||||||
// Setup default options
|
// Setup default options
|
||||||
@ -187,14 +189,14 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevents input from having injected markup
|
// Prevents input from having injected markup
|
||||||
function cleanInput (input) {
|
function cleanInput(input) {
|
||||||
let message = $('<div/>').html(input).text();
|
let message = $('<div/>').html(input).text();
|
||||||
message = Autolinker.link(message);
|
message = Autolinker.link(message);
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates the typing event
|
// Updates the typing event
|
||||||
function updateTyping () {
|
function updateTyping() {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
if (!typing) {
|
if (!typing) {
|
||||||
typing = true;
|
typing = true;
|
||||||
@ -202,7 +204,7 @@ $(function() {
|
|||||||
}
|
}
|
||||||
lastTypingTime = (new Date()).getTime();
|
lastTypingTime = (new Date()).getTime();
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
let typingTimer = (new Date()).getTime();
|
let typingTimer = (new Date()).getTime();
|
||||||
let timeDiff = typingTimer - lastTypingTime;
|
let timeDiff = typingTimer - lastTypingTime;
|
||||||
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
|
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
|
||||||
@ -214,18 +216,18 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gets the 'X is typing' messages of a user
|
// Gets the 'X is typing' messages of a user
|
||||||
function getTypingMessages (data) {
|
function getTypingMessages(data) {
|
||||||
return $('.typing.message').filter(function (i) {
|
return $('.typing.message').filter(function(i) {
|
||||||
return $(this).data('username') === data.username;
|
return $(this).data('username') === data.username;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the color of a username through our hash function
|
// Gets the color of a username through our hash function
|
||||||
function getUsernameColor (username) {
|
function getUsernameColor(username) {
|
||||||
// Compute hash code
|
// Compute hash code
|
||||||
let hash = 7;
|
let hash = 7;
|
||||||
for (let i = 0; i < username.length; i++) {
|
for (let i = 0; i < username.length; i++) {
|
||||||
hash = username.charCodeAt(i) + (hash << 5) - hash;
|
hash = username.charCodeAt(i) + (hash << 5) - hash;
|
||||||
}
|
}
|
||||||
// Calculate color
|
// Calculate color
|
||||||
let index = Math.abs(hash % COLORS.length);
|
let index = Math.abs(hash % COLORS.length);
|
||||||
@ -234,7 +236,7 @@ $(function() {
|
|||||||
|
|
||||||
// Keyboard events
|
// Keyboard events
|
||||||
|
|
||||||
$window.keydown(function (event) {
|
$window.keydown(function(event) {
|
||||||
// When the client hits ENTER on their keyboard and chat message input is focused
|
// When the client hits ENTER on their keyboard and chat message input is focused
|
||||||
if (event.which === 13 && $('.inputMessage').is(':focus')) {
|
if (event.which === 13 && $('.inputMessage').is(':focus')) {
|
||||||
sendMessage();
|
sendMessage();
|
||||||
@ -255,8 +257,8 @@ $(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Select message input when closing modal
|
// Select message input when closing modal
|
||||||
$('.modal').on('hidden.bs.modal', function (e) {
|
$('.modal').on('hidden.bs.modal', function(e) {
|
||||||
$inputMessage.focus();
|
$inputMessage.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Select message input when clicking message body, unless selecting text
|
// Select message input when clicking message body, unless selecting text
|
||||||
@ -267,29 +269,29 @@ $(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function getSelectedText() {
|
function getSelectedText() {
|
||||||
var text = "";
|
var text = '';
|
||||||
if (typeof window.getSelection != "undefined") {
|
if (typeof window.getSelection != 'undefined') {
|
||||||
text = window.getSelection().toString();
|
text = window.getSelection().toString();
|
||||||
} else if (typeof document.selection != "undefined" && document.selection.type == "Text") {
|
} else if (typeof document.selection != 'undefined' && document.selection.type == 'Text') {
|
||||||
text = document.selection.createRange().text;
|
text = document.selection.createRange().text;
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whenever the server emits 'login', log the login message
|
// Whenever the server emits 'login', log the login message
|
||||||
socket.on('user joined', function (data) {
|
socket.on('user joined', function(data) {
|
||||||
connected = true;
|
connected = true;
|
||||||
addParticipantsMessage(data);
|
addParticipantsMessage(data);
|
||||||
|
|
||||||
let importKeysPromises = [];
|
let importKeysPromises = [];
|
||||||
|
|
||||||
// Import all user keys if not already there
|
// Import all user keys if not already there
|
||||||
_.each(data.users, function(user) {
|
_.each(data.users, function(user) {
|
||||||
if (!_.findWhere(users, {id: user.id})) {
|
if (!_.findWhere(users, {id: user.id})) {
|
||||||
let promise = new Promise(function(resolve, reject) {
|
let promise = new Promise(function(resolve, reject) {
|
||||||
let currentUser = user;
|
let currentUser = user;
|
||||||
Promise.all([
|
Promise.all([
|
||||||
cryptoUtil.importPrimaryKey(currentUser.publicKey, "spki")
|
cryptoUtil.importPrimaryKey(currentUser.publicKey, 'spki')
|
||||||
])
|
])
|
||||||
.then(function(keys) {
|
.then(function(keys) {
|
||||||
users.push({
|
users.push({
|
||||||
@ -320,14 +322,16 @@ $(function() {
|
|||||||
log(data.username + ' joined');
|
log(data.username + ' joined');
|
||||||
|
|
||||||
renderParticipantsList();
|
renderParticipantsList();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sends a chat message
|
// Sends a chat message
|
||||||
function sendMessage () {
|
function sendMessage() {
|
||||||
// Don't send unless other users exist
|
// Don't send unless other users exist
|
||||||
if (users.length <= 1) return;
|
if (users.length <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let message = $inputMessage.val();
|
let message = $inputMessage.val();
|
||||||
// Prevent markup from being injected into the message
|
// Prevent markup from being injected into the message
|
||||||
@ -335,7 +339,7 @@ $(function() {
|
|||||||
// if there is a non-empty message and a socket connection
|
// if there is a non-empty message and a socket connection
|
||||||
if (message && connected) {
|
if (message && connected) {
|
||||||
$inputMessage.val('');
|
$inputMessage.val('');
|
||||||
$('#send-message-btn').removeClass('active');
|
$('#send-message-btn').removeClass('active');
|
||||||
addChatMessage({
|
addChatMessage({
|
||||||
username: username,
|
username: username,
|
||||||
message: message
|
message: message
|
||||||
@ -368,7 +372,7 @@ $(function() {
|
|||||||
let secretKeyStr;
|
let secretKeyStr;
|
||||||
|
|
||||||
// Export secret key
|
// Export secret key
|
||||||
cryptoUtil.exportKey(secretKey, "raw")
|
cryptoUtil.exportKey(secretKey, 'raw')
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
return cryptoUtil.encryptSecretKey(data, thisUser.publicKey);
|
return cryptoUtil.encryptSecretKey(data, thisUser.publicKey);
|
||||||
})
|
})
|
||||||
@ -376,7 +380,7 @@ $(function() {
|
|||||||
let encData = new Uint8Array(encryptedSecretKey);
|
let encData = new Uint8Array(encryptedSecretKey);
|
||||||
secretKeyStr = cryptoUtil.convertArrayBufferViewToString(encData);
|
secretKeyStr = cryptoUtil.convertArrayBufferViewToString(encData);
|
||||||
// Export HMAC signing key
|
// Export HMAC signing key
|
||||||
return cryptoUtil.exportKey(signingKey, "raw");
|
return cryptoUtil.exportKey(signingKey, 'raw');
|
||||||
})
|
})
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
// Encrypt signing key with user's public key
|
// Encrypt signing key with user's public key
|
||||||
@ -422,7 +426,7 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Whenever the server emits 'new message', update the chat body
|
// Whenever the server emits 'new message', update the chat body
|
||||||
socket.on('new message', function (data) {
|
socket.on('new message', function(data) {
|
||||||
// Don't show messages if no key
|
// Don't show messages if no key
|
||||||
if (!windowHandler.isActive) {
|
if (!windowHandler.isActive) {
|
||||||
windowHandler.notifyFavicon();
|
windowHandler.notifyFavicon();
|
||||||
@ -431,13 +435,13 @@ $(function() {
|
|||||||
|
|
||||||
let message = data.message;
|
let message = data.message;
|
||||||
let messageData = cryptoUtil.convertStringToArrayBufferView(message);
|
let messageData = cryptoUtil.convertStringToArrayBufferView(message);
|
||||||
let username = data.username;
|
let username = data.username;
|
||||||
let senderId = data.id
|
let senderId = data.id;
|
||||||
let vector = data.vector;
|
let vector = data.vector;
|
||||||
let vectorData = cryptoUtil.convertStringToArrayBufferView(vector);
|
let vectorData = cryptoUtil.convertStringToArrayBufferView(vector);
|
||||||
let secretKeys = data.secretKeys;
|
let secretKeys = data.secretKeys;
|
||||||
let decryptedMessageData;
|
let decryptedMessageData;
|
||||||
let decryptedMessage;
|
let decryptedMessage;
|
||||||
|
|
||||||
let mySecretKey = _.find(secretKeys, function(key) {
|
let mySecretKey = _.find(secretKeys, function(key) {
|
||||||
return key.id === myUserId;
|
return key.id === myUserId;
|
||||||
@ -449,7 +453,7 @@ $(function() {
|
|||||||
|
|
||||||
cryptoUtil.decryptSecretKey(secretKeyArrayBuffer, keys.private)
|
cryptoUtil.decryptSecretKey(secretKeyArrayBuffer, keys.private)
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
return cryptoUtil.importSecretKey(new Uint8Array(data), "raw");
|
return cryptoUtil.importSecretKey(new Uint8Array(data), 'raw');
|
||||||
})
|
})
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
let secretKey = data;
|
let secretKey = data;
|
||||||
@ -457,11 +461,11 @@ $(function() {
|
|||||||
})
|
})
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
decryptedMessageData = data;
|
decryptedMessageData = data;
|
||||||
decryptedMessage = cryptoUtil.convertArrayBufferViewToString(new Uint8Array(data))
|
decryptedMessage = cryptoUtil.convertArrayBufferViewToString(new Uint8Array(data));
|
||||||
return cryptoUtil.decryptSigningKey(signingKeyArrayBuffer, keys.private)
|
return cryptoUtil.decryptSigningKey(signingKeyArrayBuffer, keys.private);
|
||||||
})
|
})
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
return cryptoUtil.importSigningKey(new Uint8Array(data), "raw");
|
return cryptoUtil.importSigningKey(new Uint8Array(data), 'raw');
|
||||||
})
|
})
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
let signingKey = data;
|
let signingKey = data;
|
||||||
@ -472,13 +476,13 @@ $(function() {
|
|||||||
addChatMessage({
|
addChatMessage({
|
||||||
username: username,
|
username: username,
|
||||||
message: decryptedMessage
|
message: decryptedMessage
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Whenever the server emits 'user left', log it in the chat body
|
// Whenever the server emits 'user left', log it in the chat body
|
||||||
socket.on('user left', function (data) {
|
socket.on('user left', function(data) {
|
||||||
log(data.username + ' left');
|
log(data.username + ' left');
|
||||||
addParticipantsMessage(data);
|
addParticipantsMessage(data);
|
||||||
removeChatTyping(data);
|
removeChatTyping(data);
|
||||||
@ -489,12 +493,12 @@ $(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Whenever the server emits 'typing', show the typing message
|
// Whenever the server emits 'typing', show the typing message
|
||||||
socket.on('typing', function (data) {
|
socket.on('typing', function(data) {
|
||||||
addChatTyping(data);
|
addChatTyping(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Whenever the server emits 'stop typing', kill the typing message
|
// Whenever the server emits 'stop typing', kill the typing message
|
||||||
socket.on('stop typing', function (data) {
|
socket.on('stop typing', function(data) {
|
||||||
removeChatTyping(data);
|
removeChatTyping(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -522,13 +526,13 @@ $(function() {
|
|||||||
let li;
|
let li;
|
||||||
if (user.username === window.username) {
|
if (user.username === window.username) {
|
||||||
// User is me
|
// User is me
|
||||||
li = $("<li>" + user.username + " <span class='you'>(you)</span></li>").css('color', getUsernameColor(user.username));
|
li = $('<li>' + user.username + ' <span class="you">(you)</span></li>').css('color', getUsernameColor(user.username));
|
||||||
} else {
|
} else {
|
||||||
li = $("<li>" + user.username + "</li>").css('color', getUsernameColor(user.username));
|
li = $('<li>' + user.username + '</li>').css('color', getUsernameColor(user.username));
|
||||||
}
|
}
|
||||||
$('#participants-modal ul.users')
|
$('#participants-modal ul.users')
|
||||||
.append(li);
|
.append(li);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#send-message-btn').click(function() {
|
$('#send-message-btn').click(function() {
|
||||||
|
@ -4,8 +4,8 @@ export default class WindowHandler {
|
|||||||
|
|
||||||
this.newMessages = 0;
|
this.newMessages = 0;
|
||||||
this.favicon = new Favico({
|
this.favicon = new Favico({
|
||||||
animation:'pop',
|
animation: 'pop',
|
||||||
type : 'rectangle'
|
type: 'rectangle'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
@ -26,13 +26,13 @@ export default class WindowHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
window.onfocus = () => {
|
window.onfocus = () => {
|
||||||
this._isActive = true;
|
this._isActive = true;
|
||||||
this.newMessages = 0;
|
this.newMessages = 0;
|
||||||
this.favicon.reset();
|
this.favicon.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
window.onblur = () => {
|
window.onblur = () => {
|
||||||
this._isActive = false;
|
this._isActive = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ class Room {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('add user', (data) => {
|
socket.on('add user', (data) => {
|
||||||
if (addedUser) return;
|
if (addedUser) { return; }
|
||||||
|
|
||||||
data.id = uuid.v4();
|
data.id = uuid.v4();
|
||||||
this.users.push(data);
|
this.users.push(data);
|
||||||
|
5
test/app.js
Normal file
5
test/app.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import mochaJSCS from 'mocha-jscs';
|
||||||
|
import mochaJSHint from 'mocha-jshint';
|
||||||
|
|
||||||
|
mochaJSCS();
|
||||||
|
mochaJSHint();
|
Loading…
x
Reference in New Issue
Block a user