diff --git a/.gitignore b/.gitignore
index ac1e8f7..8039e3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
node_modules
*.log
.DS_Store
+.vscode
diff --git a/README.md b/README.md
index 1893bb6..ce8b9f9 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,15 @@
-# LiveCoding
+# **LiveCoding**
+Coding client-server
-Live Coding Server
+## for teacher:
+pass command-line arguments as your password before starting live-coding lesson
+
+`node ./server.js qwerty123` or `npm start qwerty123`
+
+
+**to login as teacher,**
+go to http://127.0.0.1:8000/teacher
+
+
+## for student:
+enter username in field then press `Enter` to register in system
diff --git a/authorize.html b/authorize.html
new file mode 100644
index 0000000..5ddb09e
--- /dev/null
+++ b/authorize.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+ Welcome!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client.js b/client.js
index e458139..39cfda8 100644
--- a/client.js
+++ b/client.js
@@ -5,83 +5,140 @@ const socket = new WebSocket('ws://127.0.0.1:8000/');
const user = document.getElementById('user');
const source = document.getElementById('source');
const buttons = document.getElementById('buttons');
+const regButton = document.getElementById('register');
const settings = document.getElementById('settings');
+const commit = document.getElementById('commit');
+
+// editor
+const logs = document.getElementById('log');
+const snippets = document.getElementById('snippets');
+
+
+logs.style.fontSize = '16px';
+snippets.onchange = () => {
+ editor.setOptions({
+ enableSnippets: snippets.checked,
+ enableBasicAutocompletion: snippets.checked
+ });
+};
+
const clients = {};
let selectedClient = null;
let selectedButton = null;
let localUser = '';
+const ranges = [];
+const markers = [];
+
+
+const addSymbol = () => {
+ const pos = editor.getCursorPosition();
+ const marker = markers.find(marker =>
+ marker.contains(pos.row, pos.column - 1));
+
+ if (marker) {
+ marker.setEnd(marker.end.row, marker.end.column + 1);
+ editor.session.removeMarker(marker.id);
+ marker.id = editor.session.addMarker(marker, 'commit', 'text');
+ } else {
+ const m = new Range.Range(pos.row, pos.column - 1, pos.row, pos.column);
+ m.id = editor.session.addMarker(m, 'commit', 'text');
+ markers.push(m);
+ }
+};
+
+const removeSymbol = () => {
+ const pos = editor.getCursorPosition();
+ const marker = markers.find(marker =>
+ marker.contains(pos.row, pos.column - 1));
+
+ if (marker) {
+ marker.setEnd(marker.end.row, marker.end.column - 1);
+ editor.session.removeMarker(marker.id);
+ marker.id = editor.session.addMarker(marker, 'commit', 'text');
+ }
+};
const toElement = (html) => new DOMParser()
.parseFromString(html, 'text/html')
.body.childNodes[0];
-const showSource = (clientName) => {
- selectedClient = clientName;
- const client = clients[clientName];
- if (client) {
- source.value = client.source;
- }
+const register = () => {
+ localUser = user.value;
+ const event = {
+ client: {
+ name: user.value
+ }
+ };
+ settings.hidden = true;
+ socket.send(JSON.stringify(event));
+ regButton.style.backgroundColor = 'limegreen';
+ regButton.textContent = 'Registered';
};
-const addClient = (client) => {
- const button = toElement(
- ''
- );
- buttons.appendChild(button);
- button.clientName = client.name;
- button.addEventListener('click', () => {
- if (button.clientName !== selectedButton) {
- if (selectedButton) selectedButton.className = '';
- button.className = 'selected';
- selectedButton = button;
- selectedClient = button.clientName;
- showSource(selectedClient);
+const myLog = (args) => {
+ const parse = (obj) => {
+ if (obj === null) return null;
+ if (typeof obj === 'function') return `[Function ${obj.name}]`;
+ if (typeof obj === 'object') {
+ if (Array.isArray(obj)) {
+ return `[ ${obj.reduce((res, o) => (
+ res + parse(o) + ', '
+ ), '').replace(/, $/, '')} ]`;
+ } else {
+ return `{ ${Object.keys(obj).reduce((res, k) => (
+ res + `${k}: ${parse(obj[k])}, `), ''
+ ).replace(/, $/, '')} }`;
+ }
+ } else {
+ return typeof obj === 'string' ? `'${obj}'` : obj;
}
- });
- clients[client.name] = { button, client, source: '' };
- return button;
+ };
+ return args.reduce((res, o) => (res + parse(o) + ' '), '');
};
-const changeSource = (edit) => {
- const client = clients[edit.name];
- client.source = edit.value;
- if (selectedClient === edit.name) {
- showSource(edit.name);
+const run = () => {
+ logs.style.backgroundColor = 'dodgerblue';
+ logs.textContent = 'Logs:\n\n';
+ const annot = editor.getSession().getAnnotations();
+ if (annot.length > 0 && annot.every(a => a.type === 'error')) {
+ annot.map(a => {
+ logs.textContent += `${a.row}:${a.column}\t${a.text}\n`;
+ });
+ return;
+ }
+ try {
+ (() => {
+ const outLog = [];
+ console.log = (...args) => {
+ outLog.push(myLog(args));
+ };
+ console.dir = (...args) => {
+ outLog.push(myLog(args));
+ };
+ eval(editor.getValue());
+ logs.textContent = 'Logs:\n\n' + outLog.reduce((res, v) => (
+ res + v + '\n'
+ ), '');
+ })();
+ } catch (e) {
+ logs.style.backgroundColor = 'tomato';
+ logs.textContent = 'Errors:\n\n' + e.message;
}
};
user.addEventListener('keydown', (event) => {
if (event.keyCode === 13) {
- localUser = user.value;
- const event = {
- client: {
- name: user.value
- }
- };
- settings.hidden = true;
- socket.send(JSON.stringify(event));
- const button = addClient(event.client);
- button.className = 'selected';
+ register();
}
});
source.addEventListener('input', (event) => {
- const client = clients[localUser];
- client.source = source.value;
socket.send(JSON.stringify({
edit: {
name: localUser,
- value: source.value
+ value: editor.getValue()
}
}));
});
-socket.onmessage = (event) => {
- const change = JSON.parse(event.data);
- console.log(event.data);
- if (change.client) {
- addClient(change.client);
- } else if (change.edit) {
- changeSource(change.edit);
- }
-};
+user.focus();
diff --git a/editor.js b/editor.js
new file mode 100644
index 0000000..fac731e
--- /dev/null
+++ b/editor.js
@@ -0,0 +1,50 @@
+'use strict';
+
+const fontSize = document.getElementById('fontSize');
+const selectTheme = document.getElementById('selectTheme');
+
+ace.require('ace/ext/language_tools');
+const Range = ace.require('ace/range');
+
+const editor = ace.edit('source');
+
+editor.session.setMode('ace/mode/javascript');
+
+editor.setOptions({
+ fontSize: 16,
+ tabSize: 2,
+ useSoftTabs: false,
+ theme: 'ace/theme/cobalt',
+ enableSnippets: false,
+ enableBasicAutocompletion: false
+});
+
+// window.onwheel = (e) => {
+// table.style.height = table.clientHeight + e.deltaY + 'px';
+// };
+
+selectTheme.onchange = (event) => {
+ editor.setTheme(selectTheme.value);
+ if (selectTheme.selectedIndex > 15) {
+ document.body.style.backgroundColor = '#222';
+ document.body.style.color = '#FFF';
+ } else {
+ document.body.style.backgroundColor = '#CCC';
+ document.body.style.color = '#000';
+ }
+};
+
+fontSize.oninput = () => {
+ editor.setFontSize(parseInt(fontSize.value));
+ logs.style.fontSize = `${fontSize.value}px`;
+};
+
+const reset = () => {
+ Array.prototype.forEach.call(document.body.children, el => {
+ if (el.nodeName === 'section') {
+ el.style['width'] = 'initial';
+ el.style['height'] = 'initial';
+ }
+ });
+
+};
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..5e49518
Binary files /dev/null and b/favicon.ico differ
diff --git a/index.html b/index.html
index 672192f..22b0341 100644
--- a/index.html
+++ b/index.html
@@ -1,14 +1,102 @@
+
-
+ Live coding
+
+
+
+
-Name:
-
-