Theunis J. Botha 2021-06-17 21:48:17 +02:00
parent 736ddb9951
commit a00c7154b3
34 changed files with 10466 additions and 0 deletions

View File

1
README.md Symbolic link
View File

@ -0,0 +1 @@
src/README.md

23
dist/index.html vendored Normal file
View File

@ -0,0 +1,23 @@
<!doctype html>
<html>
<head>
<title>Getting Started</title>
</head>
<body>
<div id="log">
</div>
<script>
(function () {
var logger = document.getElementById('log');
console.log = function (message) {
if (typeof message == 'object') {
logger.innerHTML += (JSON && JSON.stringify ? JSON.stringify(message) : message) + '<br />';
} else {
logger.innerHTML += message + '<br />';
}
}
})();
</script>
<script src="r3.js"></script>
</body>
</html>

6
dist/r3-node/index.js vendored Normal file
View File

@ -0,0 +1,6 @@
const R3 = require('./r3.js');
R3.System.Linking.start();
R3.System.Socket.start();
module.exports = R3;

170
dist/r3-node/r3-event.js vendored Normal file
View File

@ -0,0 +1,170 @@
const Utils = require('r3-utils.js');
class Event {
constructor() {
console.log('Event created');
}
/**
* Some nice Events handling
* @type {{}}
*/
static Subscriptions = {};
static Subscribe(
eventName,
fn
) {
/**
* Todo - maybe eventually store a boolean which indicates if the function has been executed
*/
let subscriptionId = Utils.RandomId(10);
if (Event.Subscriptions.hasOwnProperty(eventName)) {
if (Event.Subscriptions[eventName][subscriptionId]) {
throw new Error('A component can only subscribe to a particular event ID once');
}
Event.Subscriptions[eventName][subscriptionId] = fn;
} else {
Event.Subscriptions[eventName] = {};
Event.Subscriptions[eventName][subscriptionId] = fn;
}
/**
* Return a handle to the caller to allow us to unsubscribe to this event
*/
return {
fn: fn,
remove: function (eventId, subscriptionId) {
return function () {
/**
* Stop listening for this event from this component
*/
delete Event.Subscriptions[eventId][subscriptionId];
/**
* If the length of listeners is 0, stop referencing this event
* @type {string[]}
*/
let listeners = Object.keys(Event.Subscriptions[eventId]);
if (listeners.length === 0) {
delete Event.Subscriptions[eventId];
}
}
}(eventName, subscriptionId),
subscriptionId : subscriptionId
};
};
/**
* Subscribe to some events
* @param eventName
* @param callback
*/
subscribe(
eventName,
callback
) {
return Event.Subscribe(eventName, callback.bind(this));
};
/**
* Static Synchronous Event - Calls clientCallback directly after the event result is obtained
* @param eventId
* @param data
* @param clientCallback is executed ideally when the event completed
* @param clientErrorCallback
* @returns {number} of callbacks executed
* @constructor
*/
static Emit(
eventId,
data,
clientCallback,
clientErrorCallback
) {
if (Event.Subscriptions.hasOwnProperty(eventId)) {
let subscriptionIds = Object.keys(Event.Subscriptions[eventId]);
subscriptionIds.map(
function(subscriptionId) {
try {
let result = Event.Subscriptions[eventId][subscriptionId](data);
if (clientCallback) {
clientCallback(result);
}
} catch (error) {
if (clientErrorCallback) {
clientErrorCallback(error);
} else {
console.error(error);
throw error;
}
}
}
)
}
}
emit(
eventName,
data,
clientCallback,
clientErrorCallback
) {
return Event.Emit(
eventName,
data,
clientCallback,
clientErrorCallback
);
}
/**
* Execute the functions which subscribe to this event, but don't process the client callback - the subscription function
* should execute the client callback
* @param eventId
* @param data
* @param clientCallback
* @param clientErrorCallback
* @returns {number}
* @constructor
*/
static Async(
eventId,
data,
clientCallback,
clientErrorCallback
) {
if (Event.Subscriptions.hasOwnProperty(eventId)) {
let subscriptionIds = Object.keys(Event.Subscriptions[eventId]);
subscriptionIds.map(
function(subscriptionId) {
try {
Event.Subscriptions[eventId][subscriptionId](data, clientCallback, clientErrorCallback);
} catch (error) {
if (clientErrorCallback) {
clientErrorCallback(error);
} else {
console.error(error);
throw error;
}
}
}
)
}
};
}
module.exports = Event;

9
dist/r3-node/r3-object.js vendored Normal file
View File

@ -0,0 +1,9 @@
class Object {
constructor() {
console.log('Object created');
}
}
module.exports = Object;

24
dist/r3-node/r3-system-linking.js vendored Normal file
View File

@ -0,0 +1,24 @@
const System = require('./r3-system.js');
class LinkingSystem extends System {
constructor() {
super();
console.log('hi there from linking system 2');
}
static start() {
super.start();
console.log('starting linking system');
return true;
}
}
module.exports = LinkingSystem;

24
dist/r3-node/r3-system-socket.js vendored Normal file
View File

@ -0,0 +1,24 @@
const System = require('./r3-system.js');
module.exports = class SocketSystem extends System {
constructor() {
super();
console.log('hi there from socket system');
}
static start() {
super.start();
console.log('starting socket system');
return true;
}
}

13
dist/r3-node/r3-system.js vendored Normal file
View File

@ -0,0 +1,13 @@
class System {
constructor() {
console.log('hi from system b');
}
static start() {
console.log('starting a system');
}
}
module.exports = System;

1245
dist/r3-node/r3-utils.js vendored Normal file

File diff suppressed because it is too large Load Diff

23
dist/r3-node/r3.js vendored Normal file
View File

@ -0,0 +1,23 @@
const System = require('./r3-system.js');
const SystemLinking = require('./r3-system-linking.js');
const SystemSocket = require('./r3-system-socket.js');
const Object = require('./r3-object.js');
class R3 {
constructor() {
}
static version() {
return 'Thu Apr 23 2020 14:42:47 GMT+0200 (Central European Summer Time)';
}
}
R3.Object = Object;
R3.System = System;
R3.System.Linking = SystemLinking;
R3.System.Socket = SystemSocket;
module.exports = R3;

254
dist/r3.js vendored Normal file

File diff suppressed because one or more lines are too long

1
gulpfile.js Symbolic link
View File

@ -0,0 +1 @@
src/gulpfile.js

1
package-lock.json generated Symbolic link
View File

@ -0,0 +1 @@
src/package-lock.json

1
package.json Symbolic link
View File

@ -0,0 +1 @@
src/package.json

2
src/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
*.swp

2
src/README.md Normal file
View File

@ -0,0 +1,2 @@
[R3 v2.0]

66
src/api/index.js Normal file
View File

@ -0,0 +1,66 @@
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const socketio = require('@feathersjs/socketio');
// A messages service that allows to create new
// and return all existing messages
class MessageService {
constructor() {
this.messages = [];
}
async find () {
// Just return all our messages
return this.messages;
}
async create (data) {
// The new message is the data merged with a unique identifier
// using the messages length since it changes whenever we add one
const message = {
id: this.messages.length,
text: data.text
};
// Add new message to the list
this.messages.push(message);
return message;
}
}
// Creates an ExpressJS compatible Feathers application
const app = express(feathers());
// Parse HTTP JSON bodies
app.use(express.json());
// Parse URL-encoded params
app.use(express.urlencoded({ extended: true }));
// Host static files from the current folder
app.use(express.static(__dirname + '/../portal'));
// Add REST API support
app.configure(express.rest());
// Configure Socket.io real-time APIs
app.configure(socketio());
// Register an in-memory messages service
app.use('/messages', new MessageService());
// Register a nicer error handler than the default Express one
app.use(express.errorHandler());
// Add any new real-time connection to the `everybody` channel
app.on('connection', connection =>
app.channel('everybody').join(connection)
);
// Publish all events to the `everybody` channel
app.publish(data => app.channel('everybody'));
// Start the server
app.listen(3030).on('listening', () =>
console.log('Feathers server listening on localhost:3030')
);
// For good measure let's create a message
// So our API doesn't look so empty
app.service('messages').create({
text: 'Hello world from the server'
});

205
src/gulpfile.js Normal file
View File

@ -0,0 +1,205 @@
const gulp = require('gulp');
const clean = require('gulp-clean');
const stringReplace = require('gulp-string-replace');
const webpack_stream = require('webpack-stream');
const webpack_config = require('./webpack.config.js');
const map = require('map-stream');
gulp.task('build', gulp.series([clearBuild, gulp.parallel(build, buildNode)]));
gulp.task('default', gulp.series([clearBuild, gulp.parallel(build, buildNode), monitor]));
gulp.task('buildEvents', buildEvents);
const paths = {
source : './src/r3/*.js',
testSource : './test/*.js',
web : './dist',
node : './dist/r3-node'
};
function replace() {
return stringReplace('__DATE__', new Date().toString());
}
function clearBuild() {
return gulp.src(paths.node, {allowEmpty: true})
.pipe(clean());
}
function build() {
return gulp.src(paths.source)
.pipe(webpack_stream( webpack_config ))
.pipe(replace())
.pipe(gulp.dest(paths.web));
}
function buildNode() {
return gulp.src(paths.source)
.pipe(replace())
.pipe(gulp.dest(paths.node));
}
function logMatches(regex) {
return map(function(file, done) {
//console.log(file);
let contents = file.contents.toString();
let m;
do {
m = regex.exec(contents);
if (m) {
console.log(m[0]);
}
} while (m);
// let matches = [...contents.matchAll(regex)];
// if (matches) {
// matches.map(
// function(match) {
// console.log(match[0]);
// }
// )
// }
// file.contents.toString().match(regex).forEach(function(match) {
// console.log(file.path + ': ' + match);
// });
done(null, file);
});
}
function buildEvents() {
return gulp.src(
[
paths.source,
paths.testSource
]
).pipe(logMatches(/Event\.([A-Z]+_*){2,}/g));
// $files = scandir('.', SCANDIR_SORT_DESCENDING);
//
// $events = [];
//
// foreach ($files as $file) {
//
//
// if (
// preg_match('/\.js$/', $file) &&
// !preg_match('/r3\-a\-2\-event/', $file)
// ) {
// echo "processing file " . $file . "\n";
//
// $fn = fopen($file, "r");
//
// while (!feof($fn)) {
// $line = fgets($fn);
//
//
// if (
// preg_match('/R3.Event\./', $line) &&
// !preg_match('/Emit|Subscribe|.call|GetEventName|Async|prototype/', $line)
// ) {
// $matches = [];
// preg_match_all('/(R3.Event\..*?)(\s+|,|;|$|\))/', $line, $matches);
//
// if ($matches[1] && $matches[1][0]) {
//
// $event = $matches[1][0];
//
// if (in_array($event, $events)) {
// // Do nothing
// } else {
// array_push($events, $event);
// }
//
// }
//
//
// }
//
//
// }
//
// fclose($fn);
// }
//
//
// }
//
// array_push($events, 'R3.Event.START');
// array_push($events, 'R3.Event.PAUSE');
// array_push($events, 'R3.Event.RESTART');
//
// sort($events);
//
// $i = 1;
//
// $eventList = '';
//
// $eventFunction = "/**\n * R3.Event.GetEventName\n * @param eventId\n * @returns {string}\n * @constructor\n */\nR3.Event.GetEventName = function(eventId) {\n\n\tswitch(eventId) {\n";
//
// foreach ($events as $event) {
// $eventList .= $event . " = " . "0x" . dechex($i) . ";\n";
//
// $eventFunction .= "\t\tcase 0x" . dechex($i). " : return '" . strtolower(str_replace('R3.Event.', '', $event)) . "';\n";
//
// $i++;
// }
//
// $eventList .= "R3.Event.MAX_EVENTS = " . "0x" . dechex($i) . ";\n\n";
//
// $eventFunction .= "\t\tdefault :\n\t\t\tthrow new Error('Event type not defined : ' + eventId);\n";
// $eventFunction .= "\t}\n\n";
// $eventFunction .= "};\n";
//
// echo $eventList;
// echo $eventFunction;
//
// file_put_contents('r3-a-2-event-1.js', $eventList . $eventFunction);
}
function monitor() {
const watcher = gulp.watch(
[paths.source],
gulp.series[
clearBuild,
gulp.parallel(build, buildNode)
]
);
watcher.on(
'add',
function (path, stats) {
gulp.series([
clearBuild,
gulp.parallel(build, buildNode)
])();
}
);
watcher.on(
'unlink',
function (path, stats) {
gulp.series([
clearBuild,
gulp.parallel(build, buildNode)
])();
}
);
watcher.on(
'change',
function (path, stats) {
gulp.series([
clearBuild,
gulp.parallel(build, buildNode)
])();
}
);
}

6692
src/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

33
src/package.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "r3",
"version": "2.0.0",
"description": "",
"private": true,
"dependencies": {
"@feathersjs/express": "^4.5.3",
"@feathersjs/feathers": "^4.5.3",
"@feathersjs/socketio": "^4.5.3",
"chai": "^4.2.0",
"mocha": "^7.1.1",
"webpack": "^4.41.6",
"webpack-stream": "^5.2.1"
},
"devDependencies": {
"gulp": "^4.0.2",
"gulp-clean": "^0.4.0",
"gulp-concat": "^2.6.1",
"gulp-sort": "^2.0.0",
"gulp-string-replace": "^1.1.2",
"map-stream": "0.0.7",
"webpack-cli": "^3.3.11"
},
"scripts": {
"test": "mocha --recursive",
"test-debug": "mocha --inspect-brk --recursive",
"build": "webpack --webpack-config.js",
"watch": "webpack --webpack-config.js --watch"
},
"keywords": [],
"author": "",
"license": "ISC"
}

62
src/portal/index.html Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Feathers Example</title>
<link rel="stylesheet" href="//unpkg.com/feathers-chat@4.0.0/public/base.css">
<link rel="stylesheet" href="//unpkg.com/feathers-chat@4.0.0/public/chat.css">
</head>
<body>
<main id="main" class="container">
<h1>Welcome to Feathers</h1>
<form class="form" onsubmit="sendMessage(event.preventDefault())">
<input type="text" id="message-text" placeholder="Enter message here">
<button type="submit" class="button button-primary">Send message</button>
</form>
<h2>Here are the current messages:</h2>
</main>
<script src="//unpkg.com/@feathersjs/client@^4.3.0/dist/feathers.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
<script type="text/javascript">
// Set up socket.io
const socket = io('http://localhost:3030');
// Initialize a Feathers app
const app = feathers();
// Register socket.io to talk to our server
app.configure(feathers.socketio(socket));
// Form submission handler that sends a new message
async function sendMessage () {
const messageInput = document.getElementById('message-text');
// Create a new message with the input field value
await app.service('messages').create({
text: messageInput.value
});
messageInput.value = '';
}
// Renders a single message on the page
function addMessage (message) {
document.getElementById('main').innerHTML += `<p>${message.text}</p>`;
}
const main = async () => {
// Find all existing messages
const messages = await app.service('messages').find();
// Add existing messages to the list
messages.forEach(addMessage);
// Add any newly created message to the list in real-time
app.service('messages').on('created', addMessage);
};
main();
</script>
</body>
</html>

6
src/r3/index.js Normal file
View File

@ -0,0 +1,6 @@
const R3 = require('./r3.js');
R3.System.Linking.start();
R3.System.Socket.start();
module.exports = R3;

170
src/r3/r3-event.js Normal file
View File

@ -0,0 +1,170 @@
const Utils = require('r3-utils.js');
class Event {
constructor() {
console.log('Event created');
}
/**
* Some nice Events handling
* @type {{}}
*/
static Subscriptions = {};
static Subscribe(
eventName,
fn
) {
/**
* Todo - maybe eventually store a boolean which indicates if the function has been executed
*/
let subscriptionId = Utils.RandomId(10);
if (Event.Subscriptions.hasOwnProperty(eventName)) {
if (Event.Subscriptions[eventName][subscriptionId]) {
throw new Error('A component can only subscribe to a particular event ID once');
}
Event.Subscriptions[eventName][subscriptionId] = fn;
} else {
Event.Subscriptions[eventName] = {};
Event.Subscriptions[eventName][subscriptionId] = fn;
}
/**
* Return a handle to the caller to allow us to unsubscribe to this event
*/
return {
fn: fn,
remove: function (eventId, subscriptionId) {
return function () {
/**
* Stop listening for this event from this component
*/
delete Event.Subscriptions[eventId][subscriptionId];
/**
* If the length of listeners is 0, stop referencing this event
* @type {string[]}
*/
let listeners = Object.keys(Event.Subscriptions[eventId]);
if (listeners.length === 0) {
delete Event.Subscriptions[eventId];
}
}
}(eventName, subscriptionId),
subscriptionId : subscriptionId
};
};
/**
* Subscribe to some events
* @param eventName
* @param callback
*/
subscribe(
eventName,
callback
) {
return Event.Subscribe(eventName, callback.bind(this));
};
/**
* Static Synchronous Event - Calls clientCallback directly after the event result is obtained
* @param eventId
* @param data
* @param clientCallback is executed ideally when the event completed
* @param clientErrorCallback
* @returns {number} of callbacks executed
* @constructor
*/
static Emit(
eventId,
data,
clientCallback,
clientErrorCallback
) {
if (Event.Subscriptions.hasOwnProperty(eventId)) {
let subscriptionIds = Object.keys(Event.Subscriptions[eventId]);
subscriptionIds.map(
function(subscriptionId) {
try {
let result = Event.Subscriptions[eventId][subscriptionId](data);
if (clientCallback) {
clientCallback(result);
}
} catch (error) {
if (clientErrorCallback) {
clientErrorCallback(error);
} else {
console.error(error);
throw error;
}
}
}
)
}
}
emit(
eventName,
data,
clientCallback,
clientErrorCallback
) {
return Event.Emit(
eventName,
data,
clientCallback,
clientErrorCallback
);
}
/**
* Execute the functions which subscribe to this event, but don't process the client callback - the subscription function
* should execute the client callback
* @param eventId
* @param data
* @param clientCallback
* @param clientErrorCallback
* @returns {number}
* @constructor
*/
static Async(
eventId,
data,
clientCallback,
clientErrorCallback
) {
if (Event.Subscriptions.hasOwnProperty(eventId)) {
let subscriptionIds = Object.keys(Event.Subscriptions[eventId]);
subscriptionIds.map(
function(subscriptionId) {
try {
Event.Subscriptions[eventId][subscriptionId](data, clientCallback, clientErrorCallback);
} catch (error) {
if (clientErrorCallback) {
clientErrorCallback(error);
} else {
console.error(error);
throw error;
}
}
}
)
}
};
}
module.exports = Event;

9
src/r3/r3-object.js Normal file
View File

@ -0,0 +1,9 @@
class Object {
constructor() {
console.log('Object created');
}
}
module.exports = Object;

View File

@ -0,0 +1,24 @@
const System = require('./r3-system.js');
class LinkingSystem extends System {
constructor() {
super();
console.log('hi there from linking system 2');
}
static start() {
super.start();
console.log('starting linking system');
return true;
}
}
module.exports = LinkingSystem;

View File

@ -0,0 +1,24 @@
const System = require('./r3-system.js');
module.exports = class SocketSystem extends System {
constructor() {
super();
console.log('hi there from socket system');
}
static start() {
super.start();
console.log('starting socket system');
return true;
}
}

13
src/r3/r3-system.js Normal file
View File

@ -0,0 +1,13 @@
class System {
constructor() {
console.log('hi from system b');
}
static start() {
console.log('starting a system');
}
}
module.exports = System;

1245
src/r3/r3-utils.js Normal file

File diff suppressed because it is too large Load Diff

23
src/r3/r3.js Normal file
View File

@ -0,0 +1,23 @@
const System = require('./r3-system.js');
const SystemLinking = require('./r3-system-linking.js');
const SystemSocket = require('./r3-system-socket.js');
const Object = require('./r3-object.js');
class R3 {
constructor() {
}
static version() {
return '__DATE__';
}
}
R3.Object = Object;
R3.System = System;
R3.System.Linking = SystemLinking;
R3.System.Socket = SystemSocket;
module.exports = R3;

14
src/webpack.config.js Normal file
View File

@ -0,0 +1,14 @@
const path = require('path');
module.exports = {
entry: './src/r3/index.js',
output: {
filename: 'r3.js',
path: path.resolve(
__dirname + '/../',
'dist'
)
},
mode: 'development',
devtool: 'inline-source-map'
};

25
test/Event.test.js Normal file
View File

@ -0,0 +1,25 @@
const {assert} = require('chai');
describe('Event Tests', () => {
it (
'Tests Events',
() => {
let event = new Event();
let result = event.subscribe(
R3.Event.TEST_EVENT,
() => {
}
);
console.log(result);
assert.typeOf(result, 'string');
}
);
});

View File

@ -0,0 +1,34 @@
const {expect} = require('chai');
const LinkingSystem = require('../src/r3/r3-system-linking');
const System = require('../src/r3/r3-system');
describe('Linking System Tests', () => {
it (
'Tests that the linking system instance of System',
() => {
let linkingSystem = new LinkingSystem();
let result = (linkingSystem instanceof System);
expect(result).to.be.true;
}
);
it (
'Tests that the linking system starts',
() => {
let result = LinkingSystem.start();
expect(result).to.be.true;
}
);
});

21
test/R3.test.js Normal file
View File

@ -0,0 +1,21 @@
const {assert} = require('chai');
const Event = require('../src/r3/r3-event.js');
describe('Event Tests', () => {
it (
'Tests Event Subscriptions',
() => {
const R3 = require('../dist/r3-node/');
let result = R3.version();
console.log(result);
assert.typeOf(result, 'string');
}
);
});

1
webpack.config.js Symbolic link
View File

@ -0,0 +1 @@
src/webpack.config.js