diff --git a/.env.example b/.env.example
index b683e3e..ae0abbc 100644
--- a/.env.example
+++ b/.env.example
@@ -3,7 +3,7 @@ APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_TIMEZONE=UTC
-APP_URL=http://localhost
+APP_URL=http://127.0.0.1:8000
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
diff --git a/.github/workflows/test-on-pull-request-main.yml b/.github/workflows/test-on-pull-request-main.yml
index f03d06e..57c9985 100644
--- a/.github/workflows/test-on-pull-request-main.yml
+++ b/.github/workflows/test-on-pull-request-main.yml
@@ -13,23 +13,8 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- - name: Setup Node
- uses: actions/setup-node@v4
- with:
- node-version: '20.x'
-
- - name: Install and Build NodeJS dependencies
- run: npm install && npm run build
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: '8.3'
- extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite
- coverage: none
-
- - name: Install Composer dependencies
- run: composer install --prefer-dist --no-interaction --no-progress
+ - name: Install Dependencies
+ run: npm install && npm run build && composer install --prefer-dist --no-interaction --no-progress && php artisan dusk:chrome-driver --detect
- name: Copy environment file
run: cp .env.example .env
@@ -46,6 +31,8 @@ jobs:
- name: Initial Database Migration
run: php artisan migrate
- - name: Execute tests
+ - name: Execute Unit Tests
run: php artisan test
-
+
+ - name: Execute Browser Tests
+ run: php artisan dusk
diff --git a/.github/workflows/test-on-push-main.yml b/.github/workflows/test-on-push-main.yml
index 7d532a0..11d1fe2 100644
--- a/.github/workflows/test-on-push-main.yml
+++ b/.github/workflows/test-on-push-main.yml
@@ -13,23 +13,8 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- - name: Setup Node
- uses: actions/setup-node@v4
- with:
- node-version: '20.x'
-
- - name: Install and Build NodeJS dependencies
- run: npm install && npm run build
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: '8.3'
- extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite
- coverage: none
-
- - name: Install Composer dependencies
- run: composer install --prefer-dist --no-interaction --no-progress
+ - name: Install Dependencies
+ run: npm install && npm run build && composer install --prefer-dist --no-interaction --no-progress && php artisan dusk:chrome-driver --detect
- name: Copy environment file
run: cp .env.example .env
@@ -46,6 +31,8 @@ jobs:
- name: Initial Database Migration
run: php artisan migrate
- - name: Execute tests
+ - name: Execute Unit Tests
run: php artisan test
-
+
+ - name: Execute Browser Tests
+ run: php artisan dusk
diff --git a/composer.json b/composer.json
index 69d4388..23db944 100644
--- a/composer.json
+++ b/composer.json
@@ -13,6 +13,7 @@
},
"require-dev": {
"fakerphp/faker": "^1.23",
+ "laravel/dusk": "^8.2",
"laravel/pint": "^1.13",
"laravel/sail": "^1.26",
"mockery/mockery": "^1.6",
diff --git a/composer.lock b/composer.lock
index 6427963..6ea7f9a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "398060acfd420c8e7e5eaeafe9cd15eb",
+ "content-hash": "98754fa2c3a24cebb2869e901a5b34b7",
"packages": [
{
"name": "brick/math",
@@ -5935,6 +5935,78 @@
},
"time": "2020-07-09T08:09:16+00:00"
},
+ {
+ "name": "laravel/dusk",
+ "version": "v8.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/dusk.git",
+ "reference": "773a12dfbd3f84174b0f26fbc2807a414a379a66"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/dusk/zipball/773a12dfbd3f84174b0f26fbc2807a414a379a66",
+ "reference": "773a12dfbd3f84174b0f26fbc2807a414a379a66",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-zip": "*",
+ "guzzlehttp/guzzle": "^7.5",
+ "illuminate/console": "^10.0|^11.0",
+ "illuminate/support": "^10.0|^11.0",
+ "php": "^8.1",
+ "php-webdriver/webdriver": "^1.9.0",
+ "symfony/console": "^6.2|^7.0",
+ "symfony/finder": "^6.2|^7.0",
+ "symfony/process": "^6.2|^7.0",
+ "vlucas/phpdotenv": "^5.2"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.6",
+ "orchestra/testbench": "^8.19|^9.0",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^10.1|^11.0",
+ "psy/psysh": "^0.11.12|^0.12"
+ },
+ "suggest": {
+ "ext-pcntl": "Used to gracefully terminate Dusk when tests are running."
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Laravel\\Dusk\\DuskServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Dusk\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Laravel Dusk provides simple end-to-end testing and browser automation.",
+ "keywords": [
+ "laravel",
+ "testing",
+ "webdriver"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/dusk/issues",
+ "source": "https://github.com/laravel/dusk/tree/v8.2.0"
+ },
+ "time": "2024-04-16T15:51:19+00:00"
+ },
{
"name": "laravel/pint",
"version": "v1.15.1",
@@ -6421,6 +6493,72 @@
},
"time": "2022-02-21T01:04:05+00:00"
},
+ {
+ "name": "php-webdriver/webdriver",
+ "version": "1.15.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-webdriver/php-webdriver.git",
+ "reference": "cd52d9342c5aa738c2e75a67e47a1b6df97154e8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/cd52d9342c5aa738c2e75a67e47a1b6df97154e8",
+ "reference": "cd52d9342c5aa738c2e75a67e47a1b6df97154e8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-json": "*",
+ "ext-zip": "*",
+ "php": "^7.3 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.12",
+ "symfony/process": "^5.0 || ^6.0 || ^7.0"
+ },
+ "replace": {
+ "facebook/webdriver": "*"
+ },
+ "require-dev": {
+ "ergebnis/composer-normalize": "^2.20.0",
+ "ondram/ci-detector": "^4.0",
+ "php-coveralls/php-coveralls": "^2.4",
+ "php-mock/php-mock-phpunit": "^2.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpunit/phpunit": "^9.3",
+ "squizlabs/php_codesniffer": "^3.5",
+ "symfony/var-dumper": "^5.0 || ^6.0"
+ },
+ "suggest": {
+ "ext-SimpleXML": "For Firefox profile creation"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "lib/Exception/TimeoutException.php"
+ ],
+ "psr-4": {
+ "Facebook\\WebDriver\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.",
+ "homepage": "https://github.com/php-webdriver/php-webdriver",
+ "keywords": [
+ "Chromedriver",
+ "geckodriver",
+ "php",
+ "selenium",
+ "webdriver"
+ ],
+ "support": {
+ "issues": "https://github.com/php-webdriver/php-webdriver/issues",
+ "source": "https://github.com/php-webdriver/php-webdriver/tree/1.15.1"
+ },
+ "time": "2023-10-20T12:21:20+00:00"
+ },
{
"name": "phpunit/php-code-coverage",
"version": "11.0.3",
diff --git a/resources/js/app.js b/resources/js/app.js
index 00b94c9..7b8a98d 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -3,263 +3,42 @@ import './lib/goldenLayout';
import './lib/monaco';
import './lib/lucide';
import version from "./lib/version";
+import agreeDialog from './lib/agreeDialog';
+import shareDialog from './lib/shareDialog';
+import defaultLayout from './lib/defaultLayout';
-let sharedFlag = (window.location.pathname.indexOf("/s/") === 0);
-
-let lastPlayerHtml = "";
-
-let layout = null;
-let layoutInitialized = false;
-let compiling = false;
-
-let layoutDefaultConfig = {
- settings: {
- showPopoutIcon: false,
- },
- content: [{
- type: 'row',
- content:[{
- type: 'component',
- componentName: 'editorComponent',
- componentState: {},
- isClosable: false,
- title: 'C++ Editor',
- },{
- type: 'component',
- componentName: 'playerComponent',
- componentState: {},
- isClosable: false,
- title: 'Emscripten Player',
- }],
- }],
-};
-
-let layoutConfig = window.localStorage.getItem("pgetinkerLayout");
-layoutConfig = (layoutConfig !== null) ? JSON.parse(layoutConfig) : layoutDefaultConfig;
-
-let maxFileSize = 50000;
-
-let theme = window.localStorage.getItem("pgetinkerTheme");
-if(theme !== "dark" && theme !== "light")
- theme = "dark";
-
-let consoleShown = window.localStorage.getItem("pgetinkerConsoleShown");
-consoleShown = (consoleShown === "true") ? true : false;
-
-let monacoEditor = null;
-let monacoModel = null;
-let monacoModelIntellisense = null;
-
-function preCompile()
+class PGEtinker
{
- if(monacoEditor.getValue().length > maxFileSize)
- {
- alert("Maximum size exceeded!");
- return false;
- }
-
- compiling = true;
-
+ sharedFlag = false;
lastPlayerHtml = "";
- let playerFrame = document.querySelector("#player-panel iframe");
-
- if(playerFrame != null)
- playerFrame.remove();
-
- document.querySelector("#player-panel .compiling").classList.toggle("display-flex", true);
- document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
-
- monaco.editor.removeAllMarkers("owner");
- monacoEditor.trigger("", "closeMarkersNavigation");
-
- return true;
-}
-function compileSuccessHandler(data)
-{
- lastPlayerHtml = data.html;
-
- let playerFrame = document.createElement('iframe');
- playerFrame.setAttribute("srcdoc", lastPlayerHtml);
- document.querySelector("#player-panel .iframe-container").append(playerFrame);
-
- playerFrame.classList.toggle("display-block", true);
- document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
- document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
-
+ layoutInitialized = false;
compiling = false;
-}
-function compileFailHandler(stderr)
-{
- const compilerRegex = /:(\d+):(\d+): (fatal error|error|warning): (.*)/gm;
- const linkerRegex = /wasm-ld: error: pgetinker.o: (.*): (.*)/gm;
+ layoutConfig = null;
- let markers = [];
+ maxFileSize = 50000;
- let matches;
+ theme = "dark";
+ consoleShown = false;
- while((matches = compilerRegex.exec(stderr)) !== null)
- {
- markers.push({
- message: matches[4],
- severity: (matches[3] === "warning") ? monaco.MarkerSeverity.Warning : monaco.MarkerSeverity.Error,
- startLineNumber: parseInt(matches[1]),
- startColumn: parseInt(matches[2]),
- endLineNumber: parseInt(matches[1]),
- endColumn: monacoModel.getLineLength(parseInt(matches[1])),
- source: "Emscripten Compiler",
- });
- }
-
- while((matches = linkerRegex.exec(stderr)) !== null)
- {
- markers.push({
- message: `${matches[1]} ${matches[2]}`,
- severity: monaco.MarkerSeverity.Error,
- startLineNumber: 1,
- startColumn: 1,
- endLineNumber: 1,
- endColumn: monacoModel.getLineLength(1),
- source: "Emscripten Linker",
- });
- }
+ monacoEditor = null;
+ monacoModel = null;
+ monacoModelIntellisense = null;
- // show errors in the editor, if they exist
- if(markers.length > 0)
+ constructor()
{
- monaco.editor.setModelMarkers(monacoModel, "owner", markers);
- monacoEditor.setPosition({lineNumber: markers[0].startLineNumber, column: markers[0].startColumn });
- setTimeout(() => { monacoEditor.trigger("", "editor.action.marker.next"); }, 50);
- }
-
- document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
- document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", true);
- compiling = false;
-}
-
-function SetupLayout()
-{
- layout = new GoldenLayout(layoutConfig, document.querySelector("#content"))
-
- layout.registerComponent('playerComponent', function(container)
- {
- container.getElement().html(`
-
-
-
-
-
-
-
-
- Compile Failed.
-
-
-
- `);
- });
-
- layout.registerComponent('editorComponent', function(container)
- {
- container.getElement().html(`
-
- `);
- });
-
- layout.on("stateChanged", () =>
- {
- if(layoutInitialized)
- window.localStorage.setItem("pgetinkerLayout", JSON.stringify(layout.toConfig()));
- });
-
- layout.on("initialised", () =>
- {
- layoutInitialized = true;
- window.addEventListener("resize", (event) => layout.updateSize());
-
- if(monacoModel === null)
- {
- monacoModel = monaco.editor.createModel("", "cpp", monaco.Uri.parse("inmemory://pgetinker.cpp"));
-
- let codeBox = document.querySelector("#code");
- if(codeBox.value !== "")
- {
- monacoModel.setValue(document.querySelector("#code").value);
- window.localStorage.setItem("pgetinkerCode", JSON.stringify(document.querySelector("#code").value));
- }
- else
- {
- let code = window.localStorage.getItem("pgetinkerCode");
- code = (code !== null) ? JSON.parse(code) : "";
-
- if(code === "")
- {
- axios.get("/api/default-code").then((response) =>
- {
- monacoModel.setValue(response.data.code);
- }).catch((reason) => console.log(reason));
- }
- else
- {
- monacoModel.setValue(code);
- }
- }
- }
-
- if(monacoModelIntellisense === null)
- {
- monacoModelIntellisense = monaco.editor.createModel("", "cpp", monaco.Uri.parse("inmemory://pgetinker.h"));
- axios.get("/api/model/v0.01").then((response) =>
- {
- monacoModelIntellisense.setValue(response.data);
- });
- }
-
- monacoEditor = monaco.editor.create(document.querySelector('#editor-panel .code-editor'), {
- automaticLayout: true,
- model: monacoModel,
- theme: `vs-${theme}`,
- });
-
- monacoEditor.addAction({
- id: 'build-and-run',
- label: 'Build and Run',
- keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
- run: () =>
- {
- document.querySelector("#compile").dispatchEvent(new Event("click"));
- }
- });
-
- monacoEditor.onDidChangeCursorPosition(() => UpdateStatusBar());
-
- monacoEditor.onDidChangeModelContent(() =>
- {
- window.localStorage.setItem("pgetinkerCode", JSON.stringify(monacoEditor.getValue()));
-
- if(sharedFlag)
- {
- window.history.replaceState({}, "", "/");
- }
- });
+ this.sharedFlag = (window.location.pathname.indexOf("/s/") === 0);
- UpdateStatusBar();
+ this.layoutConfig = window.localStorage.getItem("pgetinkerLayout");
+ this.layoutConfig = (this.layoutConfig !== null) ? JSON.parse(this.layoutConfig) : defaultLayout;
- document.querySelector("#player-panel iframe").setAttribute("srcdoc", lastPlayerHtml);
+ this.theme = window.localStorage.getItem("pgetinkerTheme");
+ if(this.theme !== "dark" && this.theme !== "light")
+ this.theme = "dark";
+
+ this.consoleShown = window.localStorage.getItem("pgetinkerConsoleShown");
+ this.consoleShown = (this.consoleShown === "true") ? true : false;
// Default Code Button
document.querySelector("#default-code").addEventListener("click", (event) =>
@@ -268,21 +47,21 @@ function SetupLayout()
axios.get("/api/default-code").then((response) =>
{
- monacoModel.setValue(response.data.code);
+ this.monacoModel.setValue(response.data.code);
}).catch((reason) => console.log(reason));
});
-
+
// Toggle Theme Button
document.querySelector("#toggle-theme").addEventListener("click", (event) =>
{
event.preventDefault();
- if(theme === "dark")
- theme = "light";
+ if(this.theme === "dark")
+ this.theme = "light";
else
- theme = "dark";
+ this.theme = "dark";
- UpdateTheme();
+ this.UpdateTheme();
});
// Default Layout
@@ -290,19 +69,19 @@ function SetupLayout()
{
event.preventDefault();
- layout.destroy();
- layoutConfig = layoutDefaultConfig;
+ this.layout.destroy();
+ this.layoutConfig = defaultLayout;
- SetupLayout();
+ this.SetupLayout();
});
-
+
// Toggle Console Button
document.querySelector("#toggle-console").addEventListener("click", (event) =>
{
event.preventDefault();
- consoleShown = !consoleShown;
- window.localStorage.setItem("pgetinkerConsoleShown", consoleShown);
+ this.consoleShown = !this.consoleShown;
+ window.localStorage.setItem("pgetinkerConsoleShown", this.consoleShown);
document.querySelector("#player-panel iframe").contentWindow.postMessage({
message: "toggle-console",
@@ -314,7 +93,7 @@ function SetupLayout()
{
event.preventDefault();
- if(!lastPlayerHtml.includes("Emscripten-Generated Code"))
+ if(!this.lastPlayerHtml.includes("Emscripten-Generated Code"))
{
alert("You have to build the code before you can download!")
return;
@@ -323,7 +102,7 @@ function SetupLayout()
const a = document.createElement('a');
// create the data url
- a.href = `data:text/html;base64,${btoa(lastPlayerHtml)}`;
+ a.href = `data:text/html;base64,${btoa(this.lastPlayerHtml)}`;
a.download = "pgetinker.html";
document.body.appendChild(a);
@@ -336,42 +115,22 @@ function SetupLayout()
{
event.preventDefault();
- if(compiling)
+ if(this.compiling)
return;
- if(!preCompile())
+ if(!this.preCompile())
return;
axios.post("/api/share", {
- code: monacoEditor.getValue()
+ code: this.monacoEditor.getValue()
}).then((response) =>
{
- let shareDialog = document.createElement('div');
-
- shareDialog.classList.toggle("dialog", "true");
- shareDialog.classList.toggle("share-dialog", "true");
- shareDialog.innerHTML = `
-
-
-
-
- Share URL:
-
- Copy
-
-
-
`;
-
- shareDialog.querySelector("button").addEventListener("click", (event) =>
- {
- navigator.clipboard.writeText(response.data.shareURL).catch((reason) => console.log(reason));
- shareDialog.remove();
- });
-
- document.body.appendChild(shareDialog);
-
- compileSuccessHandler(response.data);
-
+ shareDialog(response.data.shareURL)
+ .finally(() =>
+ {
+ this.compileSuccessHandler(response.data);
+ });
+
}).catch((error) =>
{
if(error.response)
@@ -380,18 +139,18 @@ function SetupLayout()
{
if(error.response.status == 503)
{
- compileFailHandler("pgetinker.cpp:1:1: error: PGEtinker service has gone offline. try again later.\n");
+ this.compileFailHandler("pgetinker.cpp:1:1: error: PGEtinker service has gone offline. try again later.\n");
return;
}
}
if(error.response.data.stderr)
{
- compileFailHandler(error.response.data.stderr);
+ this.compileFailHandler(error.response.data.stderr);
return;
}
}
- compileFailHandler("pgetinker.cpp:1:1: error: compilation failed in a way that's not being handled. please make a bug report.\n");
+ this.compileFailHandler("pgetinker.cpp:1:1: error: compilation failed in a way that's not being handled. please make a bug report.\n");
});
});
@@ -400,17 +159,17 @@ function SetupLayout()
{
event.preventDefault();
- if(compiling)
+ if(this.compiling)
return;
- if(!preCompile())
+ if(!this.preCompile())
return;
axios.post("/api/compile", {
- code: monacoEditor.getValue()
+ code: this.monacoEditor.getValue()
}).then((response) =>
{
- compileSuccessHandler(response.data);
+ this.compileSuccessHandler(response.data);
}).catch((error) =>
{
@@ -420,201 +179,366 @@ function SetupLayout()
{
if(error.response.status == 503)
{
- compileFailHandler("pgetinker.cpp:1:1: error: PGEtinker service has gone offline. try again later.\n");
+ this.compileFailHandler("pgetinker.cpp:1:1: error: PGEtinker service has gone offline. try again later.\n");
return;
}
}
if(error.response.data.stderr)
{
- compileFailHandler(error.response.data.stderr);
+ this.compileFailHandler(error.response.data.stderr);
return;
}
}
- compileFailHandler("pgetinker.cpp:1:1: error: compilation failed in a way that's not being handled. please make a bug report.\n");
+ this.compileFailHandler("pgetinker.cpp:1:1: error: compilation failed in a way that's not being handled. please make a bug report.\n");
});
});
-
+
document.querySelector("#supporters").addEventListener("click", (event) =>
{
event.preventDefault();
alert("Not Implemented");
});
-
- UpdateTheme();
- });
-
- layout.init();
-}
-function UpdateTheme()
-{
- // update overall theme
- document.body.className = theme;
+ window.addEventListener("message", (event) =>
+ {
+ if(typeof event.data !== "object")
+ return;
+
+ if(typeof event.data.message !== "string")
+ return;
+
+ if(event.data.message === "player-ready")
+ {
+ // update player theme
+ document.querySelector("#player-panel iframe").contentWindow.postMessage({
+ message: "set-theme",
+ theme: this.theme
+ }, "*");
+
+ // update player theme
+ document.querySelector("#player-panel iframe").contentWindow.postMessage({
+ message: "show-console",
+ value: this.consoleShown
+ }, "*");
+ }
+ });
- // update golden layout theme
- let goldenLayoutDarkThemeStyle = document.querySelector("#goldenlayout-dark-theme");
- let goldenLayoutLightThemeStyle = document.querySelector("#goldenlayout-light-theme");
+ let agreedToTerms = window.localStorage.getItem("pgetinkerAgreedToTerms");
+ agreedToTerms = (agreedToTerms == null) ? false : JSON.parse(agreedToTerms);
+
+ if(!agreedToTerms)
+ {
+ agreeDialog()
+ .then(() =>
+ {
+ window.localStorage.setItem("pgetinkerAgreedToTerms", JSON.stringify(true));
+ this.SetupLayout();
+ })
+ .catch(() =>
+ {
+ window.localStorage.removeItem("pgetinkerCode");
+ window.localStorage.removeItem("pgetinkerTheme");
+ window.localStorage.removeItem("pgetinkerLayout");
+ window.localStorage.removeItem("pgetinkerVersion");
+ window.location.pathname = "/disagree";
+ });
+ }
+ else
+ {
+ this.SetupLayout();
+ }
+ }
- if(theme === "dark")
+ preCompile()
{
- goldenLayoutDarkThemeStyle.disabled = false;
- goldenLayoutLightThemeStyle.disabled = true;
+ if(this.monacoEditor.getValue().length > this.maxFileSize)
+ {
+ alert("Maximum size exceeded!");
+ return false;
+ }
+
+ this.compiling = true;
+
+ this.lastPlayerHtml = "";
+ let playerFrame = document.querySelector("#player-panel iframe");
+
+ if(playerFrame != null)
+ playerFrame.remove();
+
+ document.querySelector("#player-panel .compiling").classList.toggle("display-flex", true);
+ document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
+
+ monaco.editor.removeAllMarkers("owner");
+ this.monacoEditor.trigger("", "closeMarkersNavigation");
+
+ return true;
}
-
- if(theme === "light")
+
+ compileSuccessHandler(data)
{
- goldenLayoutDarkThemeStyle.disabled = true;
- goldenLayoutLightThemeStyle.disabled = false;
+ this.lastPlayerHtml = data.html;
+
+ let playerFrame = document.createElement('iframe');
+ playerFrame.setAttribute("srcdoc", this.lastPlayerHtml);
+ document.querySelector("#player-panel .iframe-container").append(playerFrame);
+
+ playerFrame.classList.toggle("display-block", true);
+ document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
+ document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", false);
+
+ this.compiling = false;
}
-
- // update editor theme
- if(monacoEditor !== null)
- monacoEditor.updateOptions({ theme: `vs-${theme}`});
-
- // update player theme
- document.querySelector("#player-panel iframe").contentWindow.postMessage({
- message: "set-theme",
- theme: theme
- }, "*");
-
- // save theme into localStorage
- window.localStorage.setItem("pgetinkerTheme", theme);
-}
-
-function UpdateStatusBar()
-{
- let statusBar = document.querySelector("#editor-panel .status");
-
- let cursor = `Ln ${monacoEditor.getPosition().lineNumber}, Col ${monacoEditor.getPosition().column}`;
- let fileSize = `${new Intl.NumberFormat().format(monacoEditor.getValue().length)} / ${new Intl.NumberFormat().format(maxFileSize)}`;
- statusBar.classList.toggle('too-fucking-big', false);
- if(monacoModel.getValueLength() > maxFileSize)
+ compileFailHandler(stderr)
{
- statusBar.classList.toggle('too-fucking-big', true);
- fileSize += " EXCEEDING MAXIMUM!";
+ const compilerRegex = /:(\d+):(\d+): (fatal error|error|warning): (.*)/gm;
+ const linkerRegex = /wasm-ld: error: pgetinker.o: (.*): (.*)/gm;
+
+ let markers = [];
+
+ let matches;
+
+ while((matches = compilerRegex.exec(stderr)) !== null)
+ {
+ markers.push({
+ message: matches[4],
+ severity: (matches[3] === "warning") ? monaco.MarkerSeverity.Warning : monaco.MarkerSeverity.Error,
+ startLineNumber: parseInt(matches[1]),
+ startColumn: parseInt(matches[2]),
+ endLineNumber: parseInt(matches[1]),
+ endColumn: this.monacoModel.getLineLength(parseInt(matches[1])),
+ source: "Emscripten Compiler",
+ });
+ }
+
+ while((matches = linkerRegex.exec(stderr)) !== null)
+ {
+ markers.push({
+ message: `${matches[1]} ${matches[2]}`,
+ severity: monaco.MarkerSeverity.Error,
+ startLineNumber: 1,
+ startColumn: 1,
+ endLineNumber: 1,
+ endColumn: this.monacoModel.getLineLength(1),
+ source: "Emscripten Linker",
+ });
+ }
+
+ // show errors in the editor, if they exist
+ if(markers.length > 0)
+ {
+ monaco.editor.setModelMarkers(this.monacoModel, "owner", markers);
+ this.monacoEditor.setPosition({lineNumber: markers[0].startLineNumber, column: markers[0].startColumn });
+ setTimeout(() => { this.monacoEditor.trigger("", "editor.action.marker.next"); }, 50);
+ }
+
+ document.querySelector("#player-panel .compiling").classList.toggle("display-flex", false);
+ document.querySelector("#player-panel .compiling-failed").classList.toggle("display-flex", true);
+ this.compiling = false;
}
+
+ SetupLayout()
+ {
+ this.layout = new GoldenLayout(this.layoutConfig, document.querySelector("#content"))
+
+ this.layout.registerComponent('playerComponent', function(container)
+ {
+ container.getElement().html(`
+
+
+
+
+
+
+
+ Compile Failed.
+
+
+
+ `);
+ });
- statusBar.innerHTML = `
-
- Bytes: ${fileSize}
-
-
- ${cursor}
-
- `;
-}
-
-// you're welcome dandistine
-window.addEventListener("click", (event) =>
-{
- let shareDialog = document.querySelector(".share-dialog");
- if(shareDialog == null)
- return;
+ this.layout.registerComponent('editorComponent', function(container)
+ {
+ container.getElement().html(`
+
+ `);
+ });
+
+ this.layout.on("stateChanged", () =>
+ {
+ if(this.layoutInitialized)
+ window.localStorage.setItem("pgetinkerLayout", JSON.stringify(this.layout.toConfig()));
+ });
+
+ this.layout.on("initialised", () =>
+ {
+ this.layoutInitialized = true;
+ window.addEventListener("resize", (event) => this.layout.updateSize());
+
+ if(this.monacoModel === null)
+ {
+ this.monacoModel = monaco.editor.createModel("", "cpp", monaco.Uri.parse("inmemory://pgetinker.cpp"));
- shareDialog.querySelector("button").dispatchEvent(new Event("click"));
-});
-
-window.addEventListener("message", (event) =>
-{
- if(typeof event.data !== "object")
- return;
+ let codeBox = document.querySelector("#code");
+ if(codeBox.value !== "")
+ {
+ this.monacoModel.setValue(document.querySelector("#code").value);
+ window.localStorage.setItem("pgetinkerCode", JSON.stringify(document.querySelector("#code").value));
+ }
+ else
+ {
+ let code = window.localStorage.getItem("pgetinkerCode");
+ code = (code !== null) ? JSON.parse(code) : "";
- if(typeof event.data.message !== "string")
- return;
-
- if(event.data.message === "player-ready")
+ if(code === "")
+ {
+ axios.get("/api/default-code").then((response) =>
+ {
+ this.monacoModel.setValue(response.data.code);
+ }).catch((reason) => console.log(reason));
+ }
+ else
+ {
+ this.monacoModel.setValue(code);
+ }
+ }
+ }
+
+ if(this.monacoModelIntellisense === null)
+ {
+ this.monacoModelIntellisense = monaco.editor.createModel("", "cpp", monaco.Uri.parse("inmemory://pgetinker.h"));
+ axios.get("/api/model/v0.01").then((response) =>
+ {
+ this.monacoModelIntellisense.setValue(response.data);
+ });
+ }
+
+ this.monacoEditor = monaco.editor.create(document.querySelector('#editor-panel .code-editor'), {
+ automaticLayout: true,
+ model: this.monacoModel,
+ theme: `vs-${this.theme}`,
+ });
+
+ this.monacoEditor.addAction({
+ id: 'build-and-run',
+ label: 'Build and Run',
+ keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
+ run: () =>
+ {
+ document.querySelector("#compile").dispatchEvent(new Event("click"));
+ }
+ });
+
+ this.monacoEditor.onDidChangeCursorPosition(() => this.UpdateStatusBar());
+
+ this.monacoEditor.onDidChangeModelContent(() =>
+ {
+ window.localStorage.setItem("pgetinkerCode", JSON.stringify(this.monacoEditor.getValue()));
+
+ if(this.sharedFlag)
+ {
+ window.history.replaceState({}, "", "/");
+ }
+ });
+
+ if(this.lastPlayerHtml != "")
+ {
+ document.querySelector("#player-panel .iframe-container iframe").srcdoc = this.lastPlayerHtml;
+ document.querySelector("#player-panel .iframe-container iframe").classList.toggle("display-block", true);
+ }
+
+ this.UpdateStatusBar();
+ this.UpdateTheme();
+ });
+
+ this.layout.init();
+ }
+
+ UpdateStatusBar()
{
- // update player theme
- document.querySelector("#player-panel iframe").contentWindow.postMessage({
- message: "set-theme",
- theme: theme
- }, "*");
+ let statusBar = document.querySelector("#editor-panel .status");
+
+ let cursor = `Ln ${this.monacoEditor.getPosition().lineNumber}, Col ${this.monacoEditor.getPosition().column}`;
+ let fileSize = `${new Intl.NumberFormat().format(this.monacoEditor.getValue().length)} / ${new Intl.NumberFormat().format(this.maxFileSize)}`;
+
+ statusBar.classList.toggle('too-fucking-big', false);
+ if(this.monacoModel.getValueLength() > this.maxFileSize)
+ {
+ statusBar.classList.toggle('too-fucking-big', true);
+ fileSize += " EXCEEDING MAXIMUM!";
+ }
+
+ statusBar.innerHTML = `
+
+ Bytes: ${fileSize}
+
+
+ ${cursor}
+
+ `;
+ }
+ UpdateTheme()
+ {
+ // update overall theme
+ document.body.className = this.theme;
+
+ // update golden layout theme
+ let goldenLayoutDarkThemeStyle = document.querySelector("#goldenlayout-dark-theme");
+ let goldenLayoutLightThemeStyle = document.querySelector("#goldenlayout-light-theme");
+
+ if(this.theme === "dark")
+ {
+ goldenLayoutDarkThemeStyle.disabled = false;
+ goldenLayoutLightThemeStyle.disabled = true;
+ }
+
+ if(this.theme === "light")
+ {
+ goldenLayoutDarkThemeStyle.disabled = true;
+ goldenLayoutLightThemeStyle.disabled = false;
+ }
+
+ // update editor theme
+ if(this.monacoEditor !== null)
+ this.monacoEditor.updateOptions({ theme: `vs-${this.theme}`});
+
// update player theme
- document.querySelector("#player-panel iframe").contentWindow.postMessage({
- message: "show-console",
- value: consoleShown
- }, "*");
+ let playerFrame = document.querySelector("#player-panel iframe");
+ if(playerFrame != null)
+ {
+ document.querySelector("#player-panel iframe").contentWindow.postMessage({
+ message: "set-theme",
+ theme: this.theme
+ }, "*");
+ }
+
+ // save theme into localStorage
+ window.localStorage.setItem("pgetinkerTheme", this.theme);
}
-});
+
+}
-let pgetinkerVersion = window.localStorage.getItem("pgetinkerVersion");
-pgetinkerVersion = (pgetinkerVersion != "string") ? pgetinkerVersion : "";
+new PGEtinker();
-console.log((version != pgetinkerVersion));
+// let pgetinkerVersion = window.localStorage.getItem("pgetinkerVersion");
+// pgetinkerVersion = (pgetinkerVersion != "string") ? pgetinkerVersion : "";
-let agreedToTerms = window.localStorage.getItem("pgetinkerAgreedToTerms");
-agreedToTerms = (agreedToTerms == null) ? false : JSON.parse(agreedToTerms);
+// console.log((version != pgetinkerVersion));
-if(agreedToTerms)
-{
- SetupLayout();
-}
-else
-{
- let agreeDialog = document.createElement('div');
- agreeDialog.setAttribute("class", "dialog first-time");
- agreeDialog.innerHTML = `
-
-
-
-
Hello and Welcome
-
- It would appear to be the first time you've
- visited this site, at least from this browser.
- In order for PGEtinker to function we require
- your permission to do some things.
-
-
Terms, in Plain English
-
- You agree that PGEtinker has permission to:
-
-
-
- Store information in your browser to be used within
- the confines of the PGEtinker online application.
- It's for stuff like persiting your layout, your
- theme, and other options so when you come back
- it's the way you left it.
-
-
- Compile and display the code you provide.
-
-
- Retain and review the code you have provided
- for the purposes of diagnosing problems with the
- PGEtinker online application.
-
-
- Share your code worldwide. This only applies if you
- use the "Share" functionality.
-
-
-
-
-
`;
- agreeDialog.querySelector("button.cancel").addEventListener("click", (event) =>
- {
- window.localStorage.removeItem("pgetinkerCode");
- window.localStorage.removeItem("pgetinkerTheme");
- window.localStorage.removeItem("pgetinkerLayout");
- window.location.pathname = "/disagree";
- });
-
- agreeDialog.querySelector("button.ok").addEventListener("click", (event) =>
- {
- SetupLayout();
-
- window.localStorage.setItem("pgetinkerAgreedToTerms", JSON.stringify(true));
- agreeDialog.remove();
- });
-
- document.body.appendChild(agreeDialog);
-}
-
diff --git a/resources/js/lib/agreeDialog.js b/resources/js/lib/agreeDialog.js
new file mode 100644
index 0000000..2007866
--- /dev/null
+++ b/resources/js/lib/agreeDialog.js
@@ -0,0 +1,68 @@
+export default function agreeDialog()
+{
+ return new Promise((resolve, reject) =>
+ {
+ let agreeDialog = document.createElement("div");
+
+ agreeDialog.classList.toggle("dialog", true);
+ agreeDialog.classList.toggle("first-time", true);
+
+ agreeDialog.innerHTML = `
+
+
+
+
Hello and Welcome
+
+ It would appear to be the first time you've
+ visited this site, at least from this browser.
+ In order for PGEtinker to function we require
+ your permission to do some things.
+
+
Terms, in Plain English
+
+ You agree that PGEtinker has permission to:
+
+
+
+ Store information in your browser to be used within
+ the confines of the PGEtinker online application.
+ It's for stuff like persiting your layout, your
+ theme, and other options so when you come back
+ it's the way you left it.
+
+
+ Compile and display the code you provide.
+
+
+ Retain and review the code you have provided
+ for the purposes of diagnosing problems with the
+ PGEtinker online application.
+
+
+ Share your code worldwide. This only applies if you
+ use the "Share" functionality.
+
+
+
+
+
`;
+
+ agreeDialog.querySelector("#i-disagree").addEventListener("click", (event) =>
+ {
+ reject();
+ agreeDialog.remove();
+ });
+
+ agreeDialog.querySelector("#i-agree").addEventListener("click", (event) =>
+ {
+ resolve();
+ agreeDialog.remove();
+ });
+
+ document.body.appendChild(agreeDialog);
+ });
+
+}
\ No newline at end of file
diff --git a/resources/js/lib/defaultLayout.js b/resources/js/lib/defaultLayout.js
new file mode 100644
index 0000000..2f8d21a
--- /dev/null
+++ b/resources/js/lib/defaultLayout.js
@@ -0,0 +1,23 @@
+const defaultLayout = {
+ settings: {
+ showPopoutIcon: false,
+ },
+ content: [{
+ type: 'row',
+ content:[{
+ type: 'component',
+ componentName: 'editorComponent',
+ componentState: {},
+ isClosable: false,
+ title: 'C++ Editor',
+ },{
+ type: 'component',
+ componentName: 'playerComponent',
+ componentState: {},
+ isClosable: false,
+ title: 'Emscripten Player',
+ }],
+ }],
+};
+
+export default defaultLayout;
\ No newline at end of file
diff --git a/resources/js/lib/shareDialog.js b/resources/js/lib/shareDialog.js
new file mode 100644
index 0000000..34deab3
--- /dev/null
+++ b/resources/js/lib/shareDialog.js
@@ -0,0 +1,43 @@
+export default function shareDialog(shareUrl)
+{
+ function shareClickAnywhereHandler(event)
+ {
+ let shareDialog = document.querySelector(".share-dialog");
+ if(shareDialog == null)
+ return;
+
+ shareDialog.querySelector("button").dispatchEvent(new Event("click"));
+ }
+
+ return new Promise((resolve) =>
+ {
+ let shareDialog = document.createElement('div');
+
+ shareDialog.classList.toggle("dialog", "true");
+ shareDialog.classList.toggle("share-dialog", "true");
+ shareDialog.innerHTML = `
+
+
+
+
+ Share URL:
+
+ Copy
+
+
+
`;
+
+ shareDialog.querySelector("button").addEventListener("click", (event) =>
+ {
+ navigator.clipboard.writeText(shareUrl).catch((reason) => console.log(reason));
+ shareDialog.remove();
+ window.removeEventListener("click", shareClickAnywhereHandler);
+ resolve();
+ });
+
+ document.body.appendChild(shareDialog);
+
+ // you're welcome dandistine
+ window.addEventListener("click", shareClickAnywhereHandler);
+ });
+}
diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php
index 2b9f7e8..98a4e96 100644
--- a/resources/views/home.blade.php
+++ b/resources/views/home.blade.php
@@ -21,7 +21,7 @@