diff --git a/.npmignore b/.npmignore index 8db3e9c..186b43e 100644 --- a/.npmignore +++ b/.npmignore @@ -6,5 +6,13 @@ docker-entrypoint.sh # Heroku stuff app.json -# GitHub documentation -docs/ \ No newline at end of file +# Source code +client/source/ + +# Dev config +rollup.config.js +.eslintignore +.eslintrc.json + +# Github stuff +.github \ No newline at end of file diff --git a/LICENSE b/LICENSE.md similarity index 93% rename from LICENSE rename to LICENSE.md index cf0f47a..cb79db1 100644 --- a/LICENSE +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Robin Nilsson +Copyright (c) 2014-2021 Hexagon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app.json b/app.json index 800a67b..22cead2 100644 --- a/app.json +++ b/app.json @@ -3,13 +3,14 @@ "description": "Client side (E2EE) encrypted instant chat", "keywords": [ "cryptalk", - "fandango", "crypto-js", "AES", "secure", "html5", "encryption", - "privacy" + "privacy", + "chat", + "e2ee" ], "repository": "https://github.com/Hexagon/cryptalk" } \ No newline at end of file diff --git a/client/source/cryptalk.js b/client/source/cryptalk.js index 4f65ac2..487f306 100644 --- a/client/source/cryptalk.js +++ b/client/source/cryptalk.js @@ -1,4 +1,4 @@ -import mediator from '../../../castrato/source/castrato.js'; +import mediator from './vendor/castrato.js'; import win from './window.js'; import notifications from './notifications.js'; import templates from './templates.js'; diff --git a/client/source/vendor/castrato.js b/client/source/vendor/castrato.js new file mode 100644 index 0000000..bad3943 --- /dev/null +++ b/client/source/vendor/castrato.js @@ -0,0 +1,300 @@ +/** + * Licensed under the MIT License + * + * Copyright (c) 2014 Pehr Boman (github.com/unkelpehr) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** @license Licenced under MIT - castrato - ©2014 Pehr Boman */ + +let + /** + * Contains the next unique node id. + * + * @property index + * @type {Integer} + * @private + */ + index = 0, + + /** + * Contains all subscriptions + * + * @property subs + * @type {Object} + * @private + */ + subs = {}, + + /** + * Contains all emits that has been done with the `persistent` parameter set to `true`. + * + * @property emits + * @type {Object} + * @private + */ + emits = {}, + + /** + * An empty function that does not accept any arguments. + * + * @property noop + * @type {function} + * @private + */ + noop = function () {}; + +/** + * Creates a new entry in the `subs` object. + * + * @method on + * @private + * @param {Integer} fromId The unique subscriber ID. + * @param {String} event The event to subscribe to. + * @param {Function} handler A function to execute when the event is triggered. + */ +function on (fromId, event, handler, once) { + let i, item, subscription = [fromId, handler, handler.length > 1]; + + // Create if needed a namespace for this event and push the subscription. + (subs[event] || (subs[event] = [])).push(subscription); + + // If it exists a persistent event that matches that which is currently being bound; + // loop through and each of them and emit to this handler. + if (emits[event]) { + i = 0; + subscription = [subscription]; + while ((item = emits[event][i++])) { + emit( + 0, // `persistent` + 0, // `event` + item[0], // `data` + item[1], // `handler` + subscription // `explicitSubs` + ); + + if (once) { + break; + } + } + } +} + +/** + * Removes all event handlers originating from `fromId` and optionally filter by handler. + * + * @method off + * @private + * @param {Integer} fromId The unique subscriber ID. + * @param {String} event The event to unsubscribe from. + * @param {Function} [handler=null] The original handler that was attached to this event. If not passed, all subscriptions will be removed. + */ +function off (fromId, event, handler) { + let sub, + i = 0, + toSubs = subs[event]; + + if (toSubs) { + while ((sub = toSubs[i++])) { + if (sub[0] === fromId && (!handler || handler === sub[1])) { + toSubs.splice(--i, 1); + } + } + } +} + +/** + * Loops through all subscriptions, calling all handlers attached to given `event`. + * + * @method emit + * @private + * @param {Integer} fromId The unique subscriber ID. + * @param {String} event The event to emit + * @param {Object} [data=undefined] Parameters to pass along to the event handler. + * @param {Function} [handler=undefined] A function to execute when all event handlers has returned. + */ +function emit (persistent, event, data, callback, explicitSubs) { + let sub, + toSubs = explicitSubs || subs[event] || [], + total = toSubs.length, + left, + loop, + answers = [], + done; + + // Add any wildcard subscriptions to the target subscriptions. + if (subs['*']) { + toSubs = toSubs.concat(subs['*']); + } + + // Wildcard subscriptions shouldn't be counted as subscribers when passed to a possible emit callback. + loop = left = toSubs.length; + + // Don't continue setup for calling all the subscribers if there isn't any. + if (loop) { + // If the emit function does not include a callback; + // we still have to set `done` to `noop` so that event callbacks + // does not try to execute something that is not a function. + done = !callback ? noop : function (data) { + if (data) { + answers.push(data); + } + + if (!--left) { + callback(answers, total); + callback = 0; + } + }; + + // Execute all handlers that are bound to this event. + // Passing `done` if the handler expects it - otherwise decrementing the `left` variable. + while ((sub = toSubs[--loop])) { + sub[1](data, sub[2] ? done : left--, event); + } + } + + // `func` get destructed when called. + // It has to be called at least once - even if no one was subscribing. + // Execute it if it still exists. + if (!left && callback) { + callback(answers, total); + } + + // Save this emit if the `persistent` parameter is set to `true`. + if (persistent) { + (emits[event] || (emits[event] = [])).push([data, callback]); + } +} + +/** + * Castrato entrypoint + * + * @constructor + * @returns {Castrato} + */ +function Castrato () { + this.nodeId = index++; + + return this; +} + +/** + * Execute all handlers attached to the given event. + * + * @method emit + * @param {String} event The event to emit + * @param {Object} [data=undefined] Parameters to pass along to the event handler. + * @param {Function} [func=undefined] A function to execute when all event handlers has returned. + * @return {Castrato} `this` + * @example + * $.emit('something'); + * $.emit('something', { foo: 'bar' }); + * $.emit('something', { foo: 'bar' }, function (data, subscribers) { + * console.log('Emit done, a total of ' + subscribers + ' subscribers returned: ', data); + * }); + */ +Castrato.prototype.emit = function (persistent, event, data, handler) { + // emit('something', { data: true }, function () {}); + if (persistent !== true && persistent !== false) { + handler = data; + data = event; + event = persistent; + persistent = false; + } + + emit(persistent, event, data, handler); + + return this; +}; + +/** + * Attach an event handler function for an event. + * + * @method on + * @param {String} event The event to subscribe to. + * @param {Function} handler A function to execute when the event is triggered. + * @return {Castrato} `this` + * @example + * $.on('something', function (data) { + * console.log('Got something!', data); + * }); + */ +Castrato.prototype.on = function (event, handler) { + on(this.nodeId, event, handler); + return this; +}; + +/** + * Attach an event handler function for an event which will only be fired once. + * + * @method once + * @param {String} event The event to subscribe to. + * @param {Function} handler A function to execute when the event is triggered. + * @return {Castrato} `this` + * @example + * $.once('something', function (data) { + * console.log('Got something!', data); + * }); + */ +Castrato.prototype.once = function (event, handler) { + on(this.nodeId, event, function wrapper (data, done) { + off(this.nodeId, event, wrapper); + handler(data, (handler.length > 1) ? done : done()); + }, true); + + return this; +}; + +/** + * Removes an event handler function for an event. + * + * @method off + * @param {String} event The event to unsubscribe from. + * @param {Function} [handler=null] The original handler that was attached to this event. If not passed, all subscriptions will be removed. + * @return {Castrato} `this` + * @example + * $.off('something'); + * $.off('something else', handler); + */ +Castrato.prototype.off = function (event, handler) { + off(this.nodeId, event, handler); + return this; +}; + +// Only used in testing. +// Should get removed in production (and will be removed in the minified version) +Castrato.prototype.destroy = function () { + this.nodeId = 0; + index = 0; + subs = {}; + emits = {}; + return this; +}; + + +// Always return instance? +/** + * @type {Castrato} + */ +let castrato = new Castrato(); + +// Export both named and default +export default castrato; +export { castrato }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 97a3e5a..f52e0ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "win32" ], "dependencies": { - "events-es6": "^1.1.1", "serve": "^12.0.1", "socket.io": "^4.3.1" }, @@ -26,7 +25,6 @@ "@rollup/plugin-node-resolve": "^13.0.6", "crypto-js": "^4.1.1", "eslint": "^8.1.0", - "requirejs": "^2.3.6", "rollup": "^2.59.0", "uglify-js": "^3.14.3" } @@ -1056,14 +1054,6 @@ "node": ">=0.10.0" } }, - "node_modules/events-es6": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events-es6/-/events-es6-1.1.1.tgz", - "integrity": "sha1-L+/EZfigejHh2UxNfPwjWbJqjk4=", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", @@ -1730,19 +1720,6 @@ "node": ">=0.10.0" } }, - "node_modules/requirejs": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", - "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==", - "dev": true, - "bin": { - "r_js": "bin/r.js", - "r.js": "bin/r.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -2932,11 +2909,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "events-es6": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events-es6/-/events-es6-1.1.1.tgz", - "integrity": "sha1-L+/EZfigejHh2UxNfPwjWbJqjk4=" - }, "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", @@ -3454,12 +3426,6 @@ "rc": "^1.0.1" } }, - "requirejs": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", - "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==", - "dev": true - }, "resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", diff --git a/package.json b/package.json index df259a1..dba5940 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,6 @@ "version": "1.2.2", "description": "Encrypted HTML5/Node.JS instant chat", "main": "server/server.js", - "subdomain": "cryptalk", - "analyze": true, "preferGlobal": true, "private": false, "scripts": { @@ -17,13 +15,14 @@ }, "keywords": [ "cryptalk", - "fandango", + "chat", "crypto-js", "AES", "secure", "html5", "encryption", - "privacy" + "privacy", + "e2ee" ], "author": "Hexagon ", "contributors": [ @@ -37,9 +36,8 @@ "type": "git", "url": "git://github.com/Hexagon/cryptalk.git" }, - "bin": "./server.js", + "bin": "./server/server.js", "dependencies": { - "events-es6": "^1.1.1", "serve": "^12.0.1", "socket.io": "^4.3.1" }, @@ -53,7 +51,6 @@ "@rollup/plugin-node-resolve": "^13.0.6", "crypto-js": "^4.1.1", "eslint": "^8.1.0", - "requirejs": "^2.3.6", "rollup": "^2.59.0", "uglify-js": "^3.14.3" }