diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..0644565a2 --- /dev/null +++ b/biome.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": [] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab", + "lineWidth": 120 + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/build.ts b/build.ts new file mode 100644 index 000000000..097a5ae56 --- /dev/null +++ b/build.ts @@ -0,0 +1,211 @@ +import html from "bun-plugin-html"; +import { platform } from "bun-utilities/os"; +import { minifySync } from "@swc/html"; + +const cleanDist = () => { + console.log("No file delete function in bun yet. So no `cleanDist`"); + + console.log(Bun.env.npm_lifecycle_script); + console.log(import.meta.dir); +}; + +const pathDiv = (path: string) => path.replaceAll("\\", platform() !== "windows" ? "/" : "\\\\"); + +const limitedLanguageImports = async (fileContents: string, inclLang: string[] = ["en"]) => { + const langUtilsFile = []; + /** This shgould correspond exactly with `language_list in `langUtils.js` */ + const language_list = [ + ["de", "germantrans"], + ["en", "englishtrans"], + ["es", "spanishtrans"], + ["fr", "frenchtrans"], + ["it", "italiantrans"], + ["ja", "japanesetrans"], + ["hu", "hungariantrans"], + ["pl", "polishtrans"], + ["ptbr", "ptbrtrans"], + ["ru", "russiantrans"], + ["tr", "turkishtrans"], + ["uk", "ukrtrans"], + ["zh_CN", "zh_CN_trans"], + ]; + for (let ix = 0; ix < language_list.length; ix++) { + const lang = language_list[ix]; + if (inclLang.includes(lang[0])) { + const absPath = pathDiv(`${import.meta.dir}\\www\\js\\language\\${lang[0]}.json`); + langUtilsFile.push(`import ${lang[1]} from "${absPath}" with {type: "json"};`); + } + } + // Add in the original file + langUtilsFile.push(fileContents); + + return langUtilsFile.join("\n"); +}; + +/** Change all import filepaths to their absolute version */ +const absolutifyImports = async (fileContents: string) => { + const regexImp = /}\s*from\s*['"](?.*)['"]\;/gm; + const impResults = [...fileContents.matchAll(regexImp)]; + if (!impResults.length) { + // Leave the file as-is - and move on + return fileContents; + } + + let fcAbsImp = fileContents; + const repPath = `${import.meta.dir}\\www\\js\\`; + for (let ix = 0; ix < impResults.length; ix++) { + const ir = impResults[ix]; + const impFilePath = pathDiv(ir[1].replace("./", repPath)); + fcAbsImp = fcAbsImp.replace(ir[1], impFilePath); + } + return fcAbsImp; +}; + +const loadAndReplaceHTML = async (filePath: string, fileContents: string) => { + const fcLower = fileContents.toLowerCase(); + const hasLoadHTML = fcLower.includes("loadhtml"); + const hasSVG = fcLower.includes(".svg"); + console.log(`Processing '${filePath}'`); + if (!hasLoadHTML && !hasSVG) { + // Leave the file as-is - and move on + console.log(`No 'loadhtml' or '.svg' in '${filePath}'`); + return fileContents; + } + + let fcProcessed = fileContents; + + if (hasLoadHTML) { + console.log(`Processing '${filePath}' for included HTML files`); + // Remove the script that does the html loading - we won't need it after bundling + const regexScript = /\\<\/script>/gim; + const fcNoLoad = fileContents.replace(regexScript, ""); + + // Now find all of the places where the above script was used + const regexHTML = /\.*\.html)['"]\s*class.*loadhtml.*><\/div>/gm; + const loadHTMLResults = [...fcNoLoad.matchAll(regexHTML)]; + if (!loadHTMLResults.length) { + // Leave the file as-is-ish - and move on + return fcNoLoad; + } + + // Finally replace the original `div` with the actual file + let fcReplLoad = fcNoLoad; + for (let ix = 0; ix < loadHTMLResults.length; ix++) { + const lhr = loadHTMLResults[ix]; + const childFilePath = lhr[1].replace("./sub/", "./www/sub/"); + const hFile = Bun.file(childFilePath); + let hText = await hFile.text(); + if (hText.includes(".svg")) { + const regexSVG = /\.*\.svg)['"].*><\/img>/gim; + const findSVGResults = [...hText.matchAll(regexSVG)]; + if (findSVGResults.length) { + console.log(`found SVGs in ${childFilePath}`); + for (let jx = 0; jx < findSVGResults.length; jx++) { + const svr = findSVGResults[jx]; + const svgPath = svr[1].replace("../images/", "./www/images/"); + const svgFile = Bun.file(svgPath); + const svgExists = await svgFile.exists(); + if (svgExists) { + hText = hText.replace(svr[0], await svgFile.text()); + } + } + } + } + fcReplLoad = fcReplLoad.replace(lhr[0], hText); + + if (hText.includes("loadhtml")) { + fcReplLoad = await loadAndReplaceHTML(childFilePath, fcReplLoad); + } + } + + fcProcessed = fcReplLoad; + } + + return fcProcessed; +}; + +const build = async () => { + await Bun.build({ + entrypoints: ["./www/index.html", "./js/app.js"], + outdir: "./dist", + target: "browser", + format: "esm", + splitting: false, + naming: "[dir]/[name].[ext]", + minify: true, + plugins: [ + html({ + inline: true, + keepOriginalPaths: false, + async preprocessor(processor) { + const files = processor.getFiles(); + + // CSS also gets processed, but it falls right through this loop unaffected + + // Process JS / TS before the HTML + for (const file of files) { + if (![".js", ".ts"].includes(file.extension)) { + continue; + } + console.log(`Processing JS/TS file '${file.path}'`); + if (file.path.endsWith("langUtils.js")) { + const fcLang = await limitedLanguageImports(await file.content); + processor.writeFile(file.path, fcLang); + } else { + const fcAbsImp = await absolutifyImports(await file.content); + processor.writeFile(file.path, fcAbsImp); + } + } + + for (const file of files) { + if (file.extension !== ".html") { + // Now we're only processing html files + continue; + } + console.log(`Processing HTML file '${file.path}'`); + const fc = await file.content; + processor.writeFile(file.path, await loadAndReplaceHTML(file.path, fc)); + } + }, + }), + ], + }); +}; + +const compress = async () => { + const indexFile = Bun.file("./dist/index.html"); + const data = await indexFile.arrayBuffer(); + // const { code, map } = minifySync(data, { + // // filename?: string; + // // iframeSrcdoc?: boolean; + // scriptingEnabled: true, + // // forceSetHtml5Doctype?: boolean; + // collapseWhitespaces: "all", + // removeEmptyMetadataElements: true, + // removeComments: true, + // // preserveComments?: string[], + // minifyConditionalComments: true, + // removeEmptyAttributes: true, + // removeRedundantAttributes: "all", + // collapseBooleanAttributes: true, + // normalizeAttributes: true, + // minifyJson: true, + // // TODO improve me after typing `@swc/css` + // minifyJs: true, + // minifyCss: true, + // // minifyAdditionalScriptsContent?: [string, MinifierType][]; + // // minifyAdditionalAttributes?: [string, MinifierType][]; + // // sortSpaceSeparatedAttributeValues?: boolean; + // // // sortAttributes?: boolean; + // // tagOmission?: boolean; + // // selfClosingVoidElements?: boolean; + // // quotes?: boolean; + // }); + const compressed = Bun.gzipSync(data, { level: 9 }); + Bun.write("./dist/index.html.gz", compressed); +}; + +console.log("Running the build"); +cleanDist(); +await build(); +await compress(); diff --git a/bun.lockb b/bun.lockb index 4e2b785c0..ed0f58a59 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 574857979..8ad067186 100644 --- a/package.json +++ b/package.json @@ -1,42 +1,30 @@ { "name": "ESP3D-WebUI", "description": "Web UI for ESP3D", + "module": "index.ts", + "type": "module", + "repository": "https://github.com/luc-github/ESP3D-WEBUI", + "author": "Luc LEBOSSE", + "license": "(ISC OR GPL-3.0)", "scripts": { "build": "gulp package", "build:en": "gulp package -en", - "serve": "python fluidnc-web-sim.py maslow.local", - "start": "npm run build:en && npm run serve" + "build:bun": "bun run build.ts --lang en", + "serve": "bun run --hot serve.ts" }, "devDependencies": { - "@happy-dom/global-registrator": "^15.11.0", - "@types/bun": "^1.1.13", - "del": "6.0.0", - "deprecated": "latest", - "fs": "latest", - "gulp": "^4.0.2", - "gulp-bytediff": "latest", - "gulp-cdnizer": "latest", - "gulp-clean-css": "latest", - "gulp-concat": "latest", - "gulp-filesize": "latest", - "gulp-gzip": "latest", - "gulp-htmlmin": "latest", - "gulp-if": "latest", - "gulp-jshint": "latest", - "gulp-ng-annotate": "latest", - "gulp-remove-code": "latest", - "gulp-replace": "latest", - "gulp-smoosher": "latest", - "gulp-uglify": "^3.0.2", - "gulp-zip": "latest", - "jshint": "latest", - "merge-stream": "latest", - "uglify-js": "^3.14.2" + "@biomejs/biome": "1.9.4", + "@happy-dom/global-registrator": "^15.11.7", + "@swc/core": "^1.10.1", + "@swc/html": "^1.10.1", + "@types/bun": "^1.1.14", + "bun-plugin-html": "^2.2.4", + "bun-utilities": "^0.2.1" }, - "repository": "https://github.com/luc-github/ESP3D-WEBUI", - "author": "Luc LEBOSSE", - "license": "(ISC OR GPL-3.0)", "dependencies": { "global": "latest" + }, + "peerDependencies": { + "typescript": "^5.6.3" } -} +} \ No newline at end of file diff --git a/serve.ts b/serve.ts new file mode 100644 index 000000000..33d4d162e --- /dev/null +++ b/serve.ts @@ -0,0 +1,106 @@ +const server = Bun.serve({ + port: 3000, + static: { + // serve a file by buffering it in memory + "/index.html": new Response(await Bun.file("./www/index.html").bytes(), { + headers: { + "Content-Type": "text/html", + }, + }), + "/favicon.ico": new Response(await Bun.file("./favicon.ico").bytes(), { + headers: { + "Content-Type": "image/x-icon", + }, + }), + }, + async fetch(req) { + const url = new URL(req.url); + + const fileExists = async (path: string) => { + const checkFile = Bun.file(path); + return (await checkFile.exists()) ? await checkFile.bytes() : null; + } + + const sendFile = async (path: string, contentType = "text/plain") => { + const checkFile = await fileExists(path); + if (checkFile) { + return new Response(checkFile, {headers: {"Content-Type": contentType}}); + } + const errText = `404 requesting '${url.pathname}' translated to '${path}`; + console.error(errText); + // Note that the following statusText is unlikely to show up in the browser's dev/debug window + return new Response(null, { status: 404, statusText: errText}); + } + + const checkFileBase = `./www${url.pathname}`; + + // Handle the special files + if (checkFileBase.endsWith("langUtils.js")) { + // The language utilities JS file, that brings all the translated languages together + // We'll be adding all of the required imports to this, for it to have all languages + + const checkFile = Bun.file(checkFileBase); + if (await checkFile.exists()) { + const langUtilsFile = []; + /** This shgould correspiond exactly with `language_list in `langUtils.js` */ + const language_list = [ + ["de", "germantrans"], + ["en", "englishtrans"], + ["es", "spanishtrans"], + ["fr", "frenchtrans"], + ["it", "italiantrans"], + ["ja", "japanesetrans"], + ["hu", "hungariantrans"], + ["pl", "polishtrans"], + ["ptbr", "ptbrtrans"], + ["ru", "russiantrans"], + ["tr", "turkishtrans"], + ["uk", "ukrtrans"], + ["zh_CN", "zh_CN_trans"], + ]; + for (let ix = 0; ix < language_list.length; ix++) { + const lang = language_list[ix]; + langUtilsFile.push(`import ${lang[1]} from "./language/${lang[0]}.json" with {type: "json"};`); + } + // Add in the original file + langUtilsFile.push(await checkFile.text()); + return new Response(langUtilsFile.join("\n"), {headers: {"Content-Type": "text/javascript"}}); + } + } + + if (checkFileBase.endsWith(".svg")) { + console.log(`Want SVG at '${checkFileBase}`); + return sendFile(checkFileBase, "text/svg+xml"); + } + + // Handle the regular file and mime types to send + const regSendFiles = { + ".html": "text/html", + ".js": "text/javascript", + ".json": "application/json", + ".css": "text/css" + } + for (const [fileExt, mimeType] of Object.entries(regSendFiles)) { + if (checkFileBase.endsWith(fileExt)) return sendFile(checkFileBase, mimeType); + } + + if (checkFileBase.includes("/js/")) { + let checkFileName = `${checkFileBase}.js`; + let checkFile = fileExists(checkFileName); + if (!checkFile) { + checkFileName = `${checkFileBase}.ts`; + checkFile = fileExists(checkFileName); + } + if (checkFile) { + return new Response(await checkFile, { + headers: { + "Content-Type": "text/javascript", + }, + }); + } + } + return new Response(null, { status: 404, statusText: `404 for your '${url.pathname}' request` }); + }, +}); + +console.log(`Listening on http://localhost:${server.port} ...`); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..238655f2c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/www/css/tablet.css b/www/css/tablet.css index 968fb0525..85a5ed836 100644 --- a/www/css/tablet.css +++ b/www/css/tablet.css @@ -843,18 +843,18 @@ background-color: #cd654c!important; /* z-axis-popup */ .calibration-modal-info { -display: flex; -align-items: center; -justify-content: center; -padding: 15px; -background-color: #f2f0e4; -font-size: 16px; -border: none; -min-width: 150px; -height: 75px; -color: #000; -text-transform: uppercase; -transition: background-color 0.3s ease, color 0.3s ease; + display: flex; + align-items: center; + justify-content: center; + padding: 15px; + background-color: #f2f0e4; + font-size: 16px; + border: none; + min-width: 150px; + height: 75px; + color: #000; + text-transform: uppercase; + transition: background-color 0.3s ease, color 0.3s ease; } #z-axis-popup .calibration-modal-info, #z-axis-popup .calibration-modal-button { diff --git a/www/docs/modal.html b/www/docs/modal.html deleted file mode 100644 index ced64d9ff..000000000 --- a/www/docs/modal.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - - - -

Bottom Modal

- - - - - - - - - - - diff --git a/www/docs/roundtab-index.html b/www/docs/roundtab-index.html deleted file mode 100644 index b61fbcc21..000000000 --- a/www/docs/roundtab-index.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - - (Better) Round Out Tabs - - - - - - - - - - Hi -
-

Tab 0

-

London is the capital city of England.

-
- -
-

Tab 1

-

Paris is the capital of France.

-
- -
-

Tab 2

-

Tokyo is the capital of Japan.

-
- - - - \ No newline at end of file diff --git a/www/docs/tabs.html b/www/docs/tabs.html deleted file mode 100644 index 62cc3ba3d..000000000 --- a/www/docs/tabs.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - -

Click on the x button in the top right corner to close the current tab:

- -
- - - -
- -
- x -

London

-

London is the capital city of England.

-
- -
- x -

Paris

-

Paris is the capital of France.

-
- -
- x -

Tokyo

-

Tokyo is the capital of Japan.

-
- - - - - - \ No newline at end of file diff --git a/www/index.html b/www/index.html index 8c131a5ef..ba5197f4f 100644 --- a/www/index.html +++ b/www/index.html @@ -16,21 +16,18 @@ var primary_sd = "/ext/"; var secondary_sd = "/sd/"; - - - - - - - - - + - + + + + + + + - - - + + @@ -40,92 +37,46 @@

Loading....

- +
- - - - - +
+
+
+
+
- - - - - - - - - - - - - - - - - +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - + \ No newline at end of file diff --git a/www/js/SPIFFSdlg.js b/www/js/SPIFFSdlg.js index 1557b3786..cc4ebc021 100644 --- a/www/js/SPIFFSdlg.js +++ b/www/js/SPIFFSdlg.js @@ -1,9 +1,29 @@ -//import - get_icon_svg, conErr, stdErrMsg, displayBlock, displayNone, id, setValue, setHTML, closeModal, setactiveModal, showModal, alertdlg, confirmdlg, inputdlg, SendFileHttp, SendGetHttp, translate_text_item +import { + Common, + get_icon_svg, + conErr, + stdErrMsg, + displayBlock, + displayNone, + id, + setValue, + setHTML, + closeModal, + setactiveModal, + showModal, + alertdlg, + confirmdlg, + inputdlg, + SendFileHttp, + SendGetHttp, + translate_text_item, +} from "./common.js"; + +//SPIFFS dialog let SPIFFS_currentfile = ""; let SPIFFS_upload_ongoing = false; -/** SPIFFS dialog */ const SPIFFSdlg = (root) => { const modal = setactiveModal("SPIFFSdlg.html"); if (modal == null) { @@ -20,7 +40,8 @@ const SPIFFSdlg = (root) => { id("refreshSPIFFSbtn").addEventListener("click", (event) => refreshSPIFFS()); if (typeof root !== "undefined") { - SPIFFS_currentpath = root; + const common = new Common(); + common.SPIFFS_currentpath = root; } setValue("SPIFFS-select", ""); setHTML("SPIFFS_file_name", translate_text_item("No file chosen")); @@ -34,10 +55,7 @@ const SPIFFSdlg = (root) => { function closeSPIFFSDialog(msg) { if (SPIFFS_upload_ongoing) { - alertdlg( - translate_text_item("Busy..."), - translate_text_item("Upload is ongoing, please wait and retry."), - ); + alertdlg(translate_text_item("Busy..."), translate_text_item("Upload is ongoing, please wait and retry.")); return; } closeModal(msg); @@ -47,13 +65,15 @@ const buildTable = (content) => `${content}
`; const buildTr = (content) => `${content}`; function SPIFFSselect_dir(directoryname) { - SPIFFS_currentpath = directoryname + directoryname.endsWith("/") ? "" : "/"; + const common = new Common(); + common.SPIFFS_currentpath = directoryname + directoryname.endsWith("/") ? "" : "/"; SPIFFSSendCommand("list", "all"); } /** Builds the SPIFFS nav bar, adds it to the parent element, and sets up the event handlers */ const SPIFFSnavbar = () => { - const tlist = SPIFFS_currentpath.split("/"); + const common = new Common(); + const tlist = common.SPIFFS_currentpath.split("/"); let path = "/"; let nb = 1; @@ -78,9 +98,7 @@ const SPIFFSnavbar = () => { }); }; -function SPIFFS_Createdir() { - inputdlg(translate_text_item("Please enter directory name"), translate_text_item("Name:"), processSPIFFS_Createdir); -} +const SPIFFS_Createdir = () => inputdlg(translate_text_item("Please enter directory name"), translate_text_item("Name:"), processSPIFFS_Createdir); function processSPIFFS_Createdir(answer) { if (answer.length > 0) { @@ -130,30 +148,16 @@ function processSPIFFSRename(new_file_name) { if (!new_file_name) { return; } - let url = `/files?action=rename&path=${encodeURIComponent(SPIFFS_currentpath)}`; + const common = new Common(); + let url = `/files?action=rename&path=${encodeURIComponent(common.SPIFFS_currentpath)}`; url += `&filename=${encodeURIComponent(old_file_name)}`; url += `&newname=${encodeURIComponent(new_file_name)}`; SendGetHttp(url, SPIFFSsuccess, SPIFFSfailed); } -const testResponse = [ - '{"files":[', - '{"name":"config.html.gz","size":"4.76 KB"},', - '{"name":"index.html.gz","size":"21.44 KB"},', - '{"name":"favicon.ico","size":"1.12 KB"},', - '{"name":"config.htm","size":"19.65 KB"},', - '{"name":"config2.htm","size":"19.98 KB"},', - '{"name":"Testname","size":"-1"},', - '{"name":"index2.html.gz","size":"28.89 KB"}', - '],"path":"/","status":"Ok","total":"2.81 MB","used":"118.88 KB","occupation":"4"}', -]; - function SPIFFSSendCommand(action, filename) { - //removeIf(production) - SPIFFSsuccess(testResponse.join("")); - return; - //endRemoveIf(production) - let url = `/files?action=${action}&filename=${encodeURI(filename)}&path=${encodeURI(SPIFFS_currentpath)}`; + const common = new Common(); + const url = `/files?action=${action}&filename=${encodeURI(filename)}&path=${encodeURI(common.SPIFFS_currentpath)}`; id("SPIFFS_loader").style.visibility = "visible"; console.log(url); SendGetHttp(url, SPIFFSsuccess, SPIFFSfailed); @@ -202,9 +206,10 @@ function SPIFFSdispatchfilestatus(jsonresponse) { let content = ""; const actions = []; - if (SPIFFS_currentpath !== "/") { - const pos = SPIFFS_currentpath.lastIndexOf("/", SPIFFS_currentpath.length - 2); - const previouspath = SPIFFS_currentpath.slice(0, pos + 1); + const common = new Common(); + if (common.SPIFFS_currentpath !== "/") { + const pos = common.SPIFFS_currentpath.lastIndexOf("/", common.SPIFFS_currentpath.length - 2); + const previouspath = common.SPIFFS_currentpath.slice(0, pos + 1); const rowId = "SPIFFS_row_up_dir"; content += `${get_icon_svg("level-up")} Up..`; actions.push({ id: rowId, method: upDirAndRelist, filename: previouspath }); @@ -242,7 +247,7 @@ function SPIFFSdispatchfilestatus(jsonresponse) { } const dirname = jsonresponse.files[i].name; const selectDirBtn = ``; - actions.push({ id: `${bIdD}select_${i}`, method: SPIFFSselect_dir, filename: `${SPIFFS_currentpath}${dirname}` }); + actions.push({ id: `${bIdD}select_${i}`, method: SPIFFSselect_dir, filename: `${common.SPIFFS_currentpath}${dirname}` }); let dircontent = `${get_icon_svg("folder-close")}`; dircontent += `${selectDirBtn}`; dircontent += ""; // No size field @@ -270,10 +275,6 @@ function refreshSPIFFS() { displayNone("SPIFFS_uploadbtn"); displayNone("refreshSPIFFSbtn"); displayNone("SPIFFS_select_files"); - //removeIf(production) - SPIFFSsuccess(testResponse.join("")); - return; - //endRemoveIf(production) SPIFFSSendCommand("list", "all"); } @@ -306,20 +307,21 @@ function SPIFFSUploadProgressDisplay(oEvent) { } function SPIFFS_UploadFile() { - if (http_communication_locked) { + const common = new Common(); + if (common.http_communication_locked) { alertdlg(translate_text_item("Busy..."), translate_text_item("Communications are currently locked, please wait and retry."),); return; } const files = id("SPIFFS-select").files; const formData = new FormData(); const url = "/files"; - formData.append("path", SPIFFS_currentpath); + formData.append("path", common.SPIFFS_currentpath); for (let i = 0; i < files.length; i++) { const file = files[i]; - const arg = `${SPIFFS_currentpath}${file.name}S`; + const arg = `${common.SPIFFS_currentpath}${file.name}S`; //append file size first to check upload is complete formData.append(arg, file.size); - formData.append("myfile[]", file, `${SPIFFS_currentpath}${file.name}`); + formData.append("myfile[]", file, `${common.SPIFFS_currentpath}${file.name}`); } displayNone("SPIFFS-select_form"); displayNone("SPIFFS_uploadbtn"); @@ -352,10 +354,11 @@ function SPIFFSUploadfailed(error_code, response) { displayNone("uploadSPIFFSmsg"); displayBlock("refreshSPIFFSbtn"); conErr(stdErrMsg(error_code, response)); - if (esp_error_code !== 0) { - alertdlg(translate_text_item("Error"), stdErrMsg(`(${esp_error_code})`, esp_error_message)); - setHTML("SPIFFS_status", translate_text_item("Error : ") + esp_error_message); - esp_error_code = 0; + const common = new Common(); + if (common.esp_error_code !== 0) { + alertdlg(translate_text_item("Error"), stdErrMsg(`(${common.esp_error_code})`, common.esp_error_message)); + setHTML("SPIFFS_status", translate_text_item("Error : ") + common.esp_error_message); + common.esp_error_code = 0; } else { alertdlg(translate_text_item("Error"), stdErrMsg(error_code, response)); setHTML("SPIFFS_status", stdErrMsg(error_code, response, translate_text_item("Upload failed"))); @@ -363,3 +366,5 @@ function SPIFFSUploadfailed(error_code, response) { SPIFFS_upload_ongoing = false; refreshSPIFFS(); } + +export { SPIFFSdlg }; diff --git a/www/js/UIdisableddlg.js b/www/js/UIdisableddlg.js index dcebb0bd4..bdc129aac 100644 --- a/www/js/UIdisableddlg.js +++ b/www/js/UIdisableddlg.js @@ -1,4 +1,11 @@ -// import translate_text_item, id, setHTML, setactiveModal, showModal, saveSerialMessages +import { + translate_text_item, + id, + setHTML, + setactiveModal, + showModal, + saveSerialMessages, +} from "./common.js"; //UIdisabled dialog const UIdisableddlg = (lostcon) => { @@ -15,3 +22,5 @@ const UIdisableddlg = (lostcon) => { } showModal(); }; + +export { UIdisableddlg }; diff --git a/www/js/alertdlg.js b/www/js/alertdlg.js index 71c97cc6c..9f6e84588 100644 --- a/www/js/alertdlg.js +++ b/www/js/alertdlg.js @@ -1,4 +1,4 @@ -// This uses: closeModal, setactiveModal, showModal, id +import { closeModal, setactiveModal, showModal, id } from "./common.js"; /** alert dialog */ const alertdlg = (titledlg, textdlg, closefunc) => { @@ -16,3 +16,5 @@ const alertdlg = (titledlg, textdlg, closefunc) => { body.innerHTML = textdlg; showModal(); }; + +export { alertdlg }; diff --git a/www/js/app.js b/www/js/app.js index e3d16181a..f40a78f3b 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -1,33 +1,52 @@ -var ESP3D_authentication = false -var async_webcommunication = false -var websocket_port = 0 -var websocket_ip = '' -var esp_hostname = 'ESP3D WebUI' -var EP_HOSTNAME -var EP_STA_SSID -var EP_STA_PASSWORD -var EP_STA_IP_MODE -var EP_STA_IP_VALUE -var EP_STA_GW_VALUE -var EP_STA_MK_VALUE -var EP_WIFI_MODE -var EP_AP_SSID -var EP_AP_PASSWORD -var EP_AP_IP_VALUE -var EP_BAUD_RATE = 112 -var EP_AUTH_TYPE = 119 -var EP_TARGET_FW = 461 -var EP_IS_DIRECT_SD = 850 -var EP_PRIMARY_SD = 851 -var EP_SECONDARY_SD = 852 -var EP_DIRECT_SD_CHECK = 853 -var SETTINGS_AP_MODE = 1 -var SETTINGS_STA_MODE = 2 -var SETTINGS_FALLBACK_MODE = 3 -var last_ping = 0 -var enable_ping = true -var esp_error_message = '' -var esp_error_code = 0 +import { + connectdlg, + Common, + displayBlock, + displayFlex, + displayNone, + id, + setHTML, + closeModal, + init_command_panel, + ControlsPanel, + init_controls_panel, + getpreferenceslist, + initpreferences, + init_files_panel, + build_axis_selection, + tryAutoReport, + grblpanel, + build_HTML_setting_list, + refreshSettings, + navbar, + setupdlg, + tabletInit, +} from "./common.js"; + +var websocket_port = 0; +var websocket_ip = ""; +var esp_hostname = "ESP3D WebUI"; +var EP_STA_SSID; +var EP_STA_PASSWORD; +var EP_STA_IP_MODE; +var EP_STA_IP_VALUE; +var EP_STA_GW_VALUE; +var EP_STA_MK_VALUE; +var EP_WIFI_MODE; +var EP_AP_SSID; +var EP_AP_PASSWORD; +var EP_AP_IP_VALUE; +var EP_BAUD_RATE = 112; +var EP_AUTH_TYPE = 119; +var EP_TARGET_FW = 461; +var EP_IS_DIRECT_SD = 850; +var EP_PRIMARY_SD = 851; +var EP_SECONDARY_SD = 852; +var EP_DIRECT_SD_CHECK = 853; +var SETTINGS_AP_MODE = 1; +var SETTINGS_STA_MODE = 2; +var SETTINGS_FALLBACK_MODE = 3; +var interval_ping = -1; //Check for IE //Edge @@ -35,20 +54,20 @@ var esp_error_code = 0 function browser_is(bname) { const ua = navigator.userAgent; switch (bname) { - case 'IE': - if (ua.indexOf('Trident/') !== -1) return true; + case "IE": + if (ua.indexOf("Trident/") !== -1) return true; break; - case 'Edge': - if (ua.indexOf('Edge') !== -1) return true; + case "Edge": + if (ua.indexOf("Edge") !== -1) return true; break; - case 'Chrome': - if (ua.indexOf('Chrome') !== -1) return true; + case "Chrome": + if (ua.indexOf("Chrome") !== -1) return true; break; - case 'Firefox': - if (ua.indexOf('Firefox') !== -1) return true; + case "Firefox": + if (ua.indexOf("Firefox") !== -1) return true; break; - case 'MacOSX': - if (ua.indexOf('Mac OS X') !== -1) return true; + case "MacOSX": + if (ua.indexOf("Mac OS X") !== -1) return true; break; default: return false; @@ -97,91 +116,99 @@ window.onload = () => { // Ensure that we always break out of this failSafe--; }, 500); -} +}; + +//window.addEventListener("resize", OnresizeWindow); +//function OnresizeWindow(){ +//} const total_boot_steps = 5; let current_boot_steps = 0; function display_boot_progress(step) { let val = 1; - if (typeof step !== 'undefined') { + if (typeof step !== "undefined") { val = step; } current_boot_steps += val; //console.log(current_boot_steps); //console.log(Math.round((current_boot_steps*100)/total_boot_steps)); - id('load_prg').value = Math.round((current_boot_steps * 100) / total_boot_steps); + id("load_prg").value = Math.round((current_boot_steps * 100) / total_boot_steps); } function update_UI_firmware_target() { + const common = new Common(); + let fwName; initpreferences(); - id('control_x_position_label').innerHTML = 'X'; - id('control_y_position_label').innerHTML = 'Y'; - id('control_z_position_label').innerHTML = 'Z'; + setHTML("control_x_position_label", "X"); + setHTML("control_y_position_label", "Y"); + setHTML("control_z_position_label", "Z"); showAxiscontrols(); - const fwName = 'FluidNC'; - last_grbl_pos = ''; - displayNone('configtablink'); - displayNone('auto_check_control'); - displayNone('progress_btn'); - displayNone('abort_btn'); - displayNone('motor_off_control'); - setHTML("tab_title_configuration", "GRBL configuration",); + fwName = "FluidNC"; + last_grbl_pos = ""; + displayNone("configtablink"); + displayNone("auto_check_control"); + displayNone("progress_btn"); + displayNone("abort_btn"); + displayNone("motor_off_control"); + setHTML( + "tab_title_configuration", + "GRBL configuration", + ); setHTML("tab_printer_configuration", "GRBL"); const fif = id("files_input_file"); if (fif) { fif.accept = " .g, .gco, .gcode, .txt, .ncc, .G, .GCO, .GCODE, .TXT, .NC"; } - displayInitial('zero_xyz_btn'); - displayInitial('zero_x_btn'); - displayInitial('zero_y_btn'); - if (grblaxis > 2) { + displayInitial("zero_xyz_btn"); + displayInitial("zero_x_btn"); + displayInitial("zero_y_btn"); + if (common.grblaxis > 2) { //displayInitial('control_z_position_display'); setHTML("control_z_position_label", "Zw"); } else { hideAxiscontrols(); - displayNone('preferences_control_z_velocity_group'); + displayNone("z_feedrate_group"); } - if (grblaxis > 3) { - id('zero_xyz_btn_txt').innerHTML += 'A'; - grblzerocmd += ' A0'; + if (common.grblaxis > 3) { + id("zero_xyz_btn_txt").innerHTML += "A"; + common.grblzerocmd += " A0"; build_axis_selection(); - displayBlock('preferences_control_a_velocity_group'); - id('positions_labels2').style.display = 'inline-grid'; - displayBlock('control_a_position_display'); + displayBlock("a_feedrate_group"); + id("positions_labels2").style.display = "inline-grid"; + displayBlock("control_a_position_display"); } - if (grblaxis > 4) { - displayBlock('control_b_position_display') - id('zero_xyz_btn_txt').innerHTML += 'B' - grblzerocmd += ' B0' - displayBlock('preferences_control_b_velocity_group') + if (common.grblaxis > 4) { + displayBlock("control_b_position_display"); + id("zero_xyz_btn_txt").innerHTML += "B"; + common.grblzerocmd += " B0"; + displayBlock("b_feedrate_group"); } - if (grblaxis > 5) { - displayBlock('control_c_position_display'); - id('zero_xyz_btn_txt').innerHTML += 'C'; - displayBlock('preferences_control_c_velocity_group'); + if (common.grblaxis > 5) { + displayBlock("control_c_position_display"); + id("zero_xyz_btn_txt").innerHTML += "C"; + displayBlock("c_feedrate_group"); } else { - displayNone('control_c_position_display'); + displayNone("control_c_position_display"); } - displayFlex('grblPanel'); + displayFlex("grblPanel"); grblpanel(); // id('FW_github').href = 'https://github.com/bdring/FluidNC'; - displayBlock('settings_filters'); // TODO: Or should this be 'preferences_filters'? - id('control_x_position_label').innerHTML = 'Xw'; - id('control_y_position_label').innerHTML = 'Yw'; + displayBlock("settings_filters"); + setHTML("control_x_position_label", "Xw"); + setHTML("control_y_position_label", "Yw"); - EP_HOSTNAME = 'Hostname'; - EP_STA_SSID = 'Sta/SSID'; - EP_STA_PASSWORD = 'Sta/Password'; - EP_STA_IP_MODE = 'Sta/IPMode'; - EP_STA_IP_VALUE = 'Sta/IP'; - EP_STA_GW_VALUE = 'Sta/Gateway'; - EP_STA_MK_VALUE = 'Sta/Netmask'; - EP_WIFI_MODE = 'WiFi/Mode'; - EP_AP_SSID = 'AP/SSID'; - EP_AP_PASSWORD = 'AP/Password'; - EP_AP_IP_VALUE = 'AP/IP'; + EP_STA_SSID = "Sta/SSID"; + EP_STA_PASSWORD = "Sta/Password"; + EP_STA_IP_MODE = "Sta/IPMode"; + EP_STA_IP_VALUE = "Sta/IP"; + EP_STA_GW_VALUE = "Sta/Gateway"; + EP_STA_MK_VALUE = "Sta/Netmask"; + EP_WIFI_MODE = "WiFi/Mode"; + EP_AP_SSID = "AP/SSID"; + EP_AP_PASSWORD = "AP/Password"; + EP_AP_IP_VALUE = "AP/IP"; SETTINGS_AP_MODE = 2; SETTINGS_STA_MODE = 1; @@ -194,23 +221,28 @@ function update_UI_firmware_target() { : "", ); - return fwName + return fwName; } function Set_page_title(page_title) { - if (typeof page_title !== 'undefined') esp_hostname = page_title - document.title = esp_hostname + if (typeof page_title !== "undefined") esp_hostname = page_title; + document.title = esp_hostname; } function initUI() { - console.log('Init UI'); - if (ESP3D_authentication) { + console.log("Init UI"); + const common = new Common(); + if (common.ESP3D_authentication) { connectdlg(false); } AddCmd(display_boot_progress); //initial check - if (typeof target_firmware === 'undefined' || typeof web_ui_version === 'undefined' || typeof direct_sd === 'undefined') - alert('Missing init data!'); + if ( + typeof target_firmware === "undefined" || + typeof web_ui_version === "undefined" || + typeof direct_sd === "undefined" + ) + alert("Missing init data!"); //check FW update_UI_firmware_target(); //set title using hostname @@ -220,15 +252,12 @@ function initUI() { //update FW version setHTML("FW_VERSION", fw_version); // Get the element with id="defaultOpen" and click on it - id('tablettablink').click() + id("tablettablink").click(); - if (typeof id('grblcontroltablink') !== 'undefined') { - id('grblcontroltablink').click() + if (typeof id("grblcontroltablink") !== "undefined") { + id("grblcontroltablink").click(); } - //removeIf(production) - console.log(JSON.stringify(translated_list)); - //endRemoveIf(production) initUI_2(); setTimeout(tryAutoReport, 500); //Not sure why this needs a delay but it seems like a hack @@ -237,7 +266,7 @@ function initUI() { function initUI_2() { AddCmd(display_boot_progress); //get all settings from ESP3D - console.log('Get settings'); + console.log("Get settings"); //query settings but do not update list in case wizard is showed refreshSettings(true); initUI_3(); @@ -246,10 +275,10 @@ function initUI_2() { function initUI_3() { AddCmd(display_boot_progress); //init panels - console.log('Get macros'); + console.log("Get macros"); init_controls_panel(); init_grbl_panel(); - console.log('Get preferences'); + console.log("Get preferences"); getpreferenceslist(); initUI_4(); } @@ -266,17 +295,18 @@ function initUI_4() { setupdlg(); } else { //wizard is done UI can be updated - setup_is_done = true; + const common = new Common(); + common.setup_is_done = true; do_not_build_settings = false; AddCmd(display_boot_progress); - build_HTML_setting_list(current_setting_filter); + build_HTML_setting_list(common.current_setting_filter); AddCmd(closeModal); AddCmd(show_main_UI); } } function show_main_UI() { - displayUndoNone('main_ui'); + displayUndoNone("main_ui"); } // var socket_response = '' diff --git a/www/js/calculatesCalibrationStuff.js b/www/js/calculatesCalibrationStuff.js index 529154e0e..7367c7de3 100644 --- a/www/js/calculatesCalibrationStuff.js +++ b/www/js/calculatesCalibrationStuff.js @@ -1,23 +1,28 @@ -// When we can change to proper ESM - uncomment this -// import M from "constants"; -// import { sendCommand } from "./maslow"; - -var tlZ = 100 -var trZ = 56 -var blZ = 34 -var brZ = 78 -var acceptableCalibrationThreshold = 0.5 +import { + Common, + M, + sendCommand, + refreshSettings, + saveMaslowYaml, + onCalibrationButtonsClick, +} from "./common.js"; + +var tlZ = 100; +var trZ = 56; +var blZ = 34; +var brZ = 78; +var acceptableCalibrationThreshold = 0.5; //Establish initial guesses for the corners var initialGuess = { - tl: { x: 0, y: 2000 }, - tr: { x: 3000, y: 2000 }, - bl: { x: 0, y: 0 }, - br: { x: 3000, y: 0 }, - fitness: 100000000, -} + tl: { x: 0, y: 2000 }, + tr: { x: 3000, y: 2000 }, + bl: { x: 0, y: 0 }, + br: { x: 3000, y: 0 }, + fitness: 100000000, +}; -let result +let result; /**------------------------------------Intro------------------------------------ * @@ -30,19 +35,18 @@ let result *------------------------------------------------------------------------------ */ - /** - * Computes the distance between two points. - * @param {number} a - The x-coordinate of the first point. - * @param {number} b - The y-coordinate of the first point. - * @param {number} c - The x-coordinate of the second point. - * @param {number} d - The y-coordinate of the second point. + * Computes the distance between two points. Standard Pythagorean theorem + * @param {number} ax - The x-coordinate of the first point. + * @param {number} ay - The y-coordinate of the first point. + * @param {number} bx - The x-coordinate of the second point. + * @param {number} by - The y-coordinate of the second point. * @returns {number} - The distance between the two points. */ -function distanceBetweenPoints(a, b, c, d) { - var dx = c - a - var dy = d - b - return Math.sqrt(dx * dx + dy * dy) +function distanceBetweenPoints(ax, ay, bx, by) { + const dx = bx - ax; + const dy = by - ay; + return Math.sqrt((dx ** 2) + (dy ** 2)); } /** @@ -54,9 +58,9 @@ function distanceBetweenPoints(a, b, c, d) { * @returns {Object} - An object containing the x and y coordinates of the line's end point. */ function getEndPoint(startX, startY, angle, length) { - var endX = startX + length * Math.cos(angle) - var endY = startY + length * Math.sin(angle) - return { x: endX, y: endY } + const endX = startX + length * Math.cos(angle); + const endY = startY + length * Math.sin(angle); + return { x: endX, y: endY }; } /** @@ -68,16 +72,46 @@ function getEndPoint(startX, startY, angle, length) { * @returns {number} - The fitness value, which is the average distance between all line end points. */ function computeEndpointFitness(line1, line2, line3, line4) { - const a = distanceBetweenPoints(line1.xEnd, line1.yEnd, line2.xEnd, line2.yEnd) - const b = distanceBetweenPoints(line1.xEnd, line1.yEnd, line3.xEnd, line3.yEnd) - const c = distanceBetweenPoints(line1.xEnd, line1.yEnd, line4.xEnd, line4.yEnd) - const d = distanceBetweenPoints(line2.xEnd, line2.yEnd, line3.xEnd, line3.yEnd) - const e = distanceBetweenPoints(line2.xEnd, line2.yEnd, line4.xEnd, line4.yEnd) - const f = distanceBetweenPoints(line3.xEnd, line3.yEnd, line4.xEnd, line4.yEnd) - - const fitness = (a + b + c + d + e + f) / 6 - - return fitness + const a = distanceBetweenPoints( + line1.xEnd, + line1.yEnd, + line2.xEnd, + line2.yEnd, + ); + const b = distanceBetweenPoints( + line1.xEnd, + line1.yEnd, + line3.xEnd, + line3.yEnd, + ); + const c = distanceBetweenPoints( + line1.xEnd, + line1.yEnd, + line4.xEnd, + line4.yEnd, + ); + const d = distanceBetweenPoints( + line2.xEnd, + line2.yEnd, + line3.xEnd, + line3.yEnd, + ); + const e = distanceBetweenPoints( + line2.xEnd, + line2.yEnd, + line4.xEnd, + line4.yEnd, + ); + const f = distanceBetweenPoints( + line3.xEnd, + line3.yEnd, + line4.xEnd, + line4.yEnd, + ); + + const fitness = (a + b + c + d + e + f) / 6; + + return fitness; } /** @@ -86,69 +120,68 @@ function computeEndpointFitness(line1, line2, line3, line4) { * @returns {Object} - The line with the end point added. */ function computeLineEndPoint(line) { - const end = getEndPoint(line.xBegin, line.yBegin, line.theta, line.length) - line.xEnd = end.x - line.yEnd = end.y - return line + const end = getEndPoint(line.xBegin, line.yBegin, line.theta, line.length); + line.xEnd = end.x; + line.yEnd = end.y; + return line; } /** * Walks the four lines in the given set, adjusting their endpoints to minimize the distance between them. - * @param {Object} tlLine - The top-left line in the set. - * @param {Object} trLine - The top-right line in the set. - * @param {Object} blLine - The bottom-left line in the set. - * @param {Object} brLine - The bottom-right line in the set. + * @param {Array[Object]} lines - An array of `line` objects in the order TL, TR, BL, BR * @param {number} stepSize - The amount to adjust the angle of each line by on each iteration. - * @returns {Object} - An object containing the final positions of each line. + * @returns {Array[Object]} - An array of the final positions of each `line`. */ -function walkLines(tlLine, trLine, blLine, brLine, stepSize) { - let changeMade = true; - let bestFitness = computeEndpointFitness(tlLine, trLine, blLine, brLine); - - while (changeMade) { - changeMade = false; - - const lines = [tlLine, trLine, blLine, brLine]; - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - - for (let direction of [-1, 1]) { - const newLine = computeLineEndPoint({ - xBegin: line.xBegin, - yBegin: line.yBegin, - theta: line.theta + direction * stepSize, - length: line.length, - }); - - const newFitness = computeEndpointFitness( - i === 0 ? newLine : tlLine, - i === 1 ? newLine : trLine, - i === 2 ? newLine : blLine, - i === 3 ? newLine : brLine - ); - - if (newFitness < bestFitness) { - lines[i] = newLine; - bestFitness = newFitness; - changeMade = true; - } - } - } - - tlLine = lines[0]; - trLine = lines[1]; - blLine = lines[2]; - brLine = lines[3]; - } - - const result = { tlLine, trLine, blLine, brLine, changeMade }; - - sendCalibrationEvent({ - walkedlines: result, - }); - - return result; +function walkLines(lines, stepSize) { + let changeMade = true; + let bestFitness = computeEndpointFitness(...lines); + + let [tl, tr, bl, br] = lines; + + while (changeMade) { + changeMade = false; + + const lines = [tl, tr, bl, br]; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + for (const direction of [-1, 1]) { + const newLine = computeLineEndPoint({ + xBegin: line.xBegin, + yBegin: line.yBegin, + theta: line.theta + direction * stepSize, + length: line.length, + }); + + const newFitness = computeEndpointFitness( + i === 0 ? newLine : tl, + i === 1 ? newLine : tr, + i === 2 ? newLine : bl, + i === 3 ? newLine : br, + ); + + if (newFitness < bestFitness) { + lines[i] = newLine; + bestFitness = newFitness; + changeMade = true; + } + } + } + + tl = lines[0]; + tr = lines[1]; + bl = lines[2]; + br = lines[3]; + } + + const result = { tlLine: tl, trLine: tr, blLine: bl, brLine: br, changeMade: changeMade }; + + sendCalibrationEvent({ + walkedlines: result, + }); + + return result; } /** @@ -158,76 +191,74 @@ function walkLines(tlLine, trLine, blLine, brLine, stepSize) { * @returns {Object} - An object containing the fitness value and the final positions of each line. */ function magneticallyAttractedLinesFitness(measurement, individual) { - //These set the inital conditions for theta. They don't really matter, they just have to kinda point to the middle of the frame. - if (typeof measurement.tlTheta === 'undefined') { - measurement.tlTheta = -0.3; - } - if (typeof measurement.trTheta === 'undefined') { - measurement.trTheta = 3.5; - } - if (typeof measurement.blTheta === 'undefined') { - measurement.blTheta = 0.5; - } - if (typeof measurement.brTheta === 'undefined') { - measurement.brTheta = 2.6; - } - - //Define the four lines with starting points and lengths - var tlLine = computeLineEndPoint({ - xBegin: individual.tl.x, - yBegin: individual.tl.y, - theta: measurement.tlTheta, - length: measurement.tl, - }); - var trLine = computeLineEndPoint({ - xBegin: individual.tr.x, - yBegin: individual.tr.y, - theta: measurement.trTheta, - length: measurement.tr, - }); - var blLine = computeLineEndPoint({ - xBegin: individual.bl.x, - yBegin: individual.bl.y, - theta: measurement.blTheta, - length: measurement.bl, - }); - var brLine = computeLineEndPoint({ - xBegin: individual.br.x, - yBegin: individual.br.y, - theta: measurement.brTheta, - length: measurement.br, - }); - - var { tlLine, trLine, blLine, brLine } = walkLines(tlLine, trLine, blLine, brLine, 0.1); - var { tlLine, trLine, blLine, brLine } = walkLines(tlLine, trLine, blLine, brLine, 0.01); - var { tlLine, trLine, blLine, brLine } = walkLines(tlLine, trLine, blLine, brLine, 0.001); - var { tlLine, trLine, blLine, brLine } = walkLines(tlLine, trLine, blLine, brLine, 0.0001); - var { tlLine, trLine, blLine, brLine } = walkLines(tlLine, trLine, blLine, brLine, 0.00001); - var { tlLine, trLine, blLine, brLine } = walkLines(tlLine, trLine, blLine, brLine, 0.000001); - var { tlLine, trLine, blLine, brLine } = walkLines(tlLine, trLine, blLine, brLine, 0.0000001); - var { tlLine, trLine, blLine, brLine } = walkLines(tlLine, trLine, blLine, brLine, 0.00000001); - - measurement.tlTheta = tlLine.theta; - measurement.trTheta = trLine.theta; - measurement.blTheta = blLine.theta; - measurement.brTheta = brLine.theta; - - //Compute the final fitness - const finalFitness = computeEndpointFitness(tlLine, trLine, blLine, brLine); - - //Compute the tension in the two upper belts - const { TL, TR } = calculateTensions(tlLine.xEnd, tlLine.yEnd, individual); - measurement.TLtension = TL; - measurement.TRtension = TR; - - const result = { fitness: finalFitness, lines: { tlLine: tlLine, trLine: trLine, blLine: blLine, brLine: brLine } } - sendCalibrationEvent({ - lines: result, - individual, - measurement - }); - - return result; + //These set the inital conditions for theta. They don't really matter, they just have to kinda point to the middle of the frame. + if (typeof measurement.tlTheta === "undefined") { + measurement.tlTheta = -0.3; + } + if (typeof measurement.trTheta === "undefined") { + measurement.trTheta = 3.5; + } + if (typeof measurement.blTheta === "undefined") { + measurement.blTheta = 0.5; + } + if (typeof measurement.brTheta === "undefined") { + measurement.brTheta = 2.6; + } + + //Define the four lines with starting points and lengths + let tlLine = computeLineEndPoint({ + xBegin: individual.tl.x, + yBegin: individual.tl.y, + theta: measurement.tlTheta, + length: measurement.tl, + }); + let trLine = computeLineEndPoint({ + xBegin: individual.tr.x, + yBegin: individual.tr.y, + theta: measurement.trTheta, + length: measurement.tr, + }); + let blLine = computeLineEndPoint({ + xBegin: individual.bl.x, + yBegin: individual.bl.y, + theta: measurement.blTheta, + length: measurement.bl, + }); + let brLine = computeLineEndPoint({ + xBegin: individual.br.x, + yBegin: individual.br.y, + theta: measurement.brTheta, + length: measurement.br, + }); + + for (let ix = 0.1; ix >= 0.00000001; ix /= 10) { + [tlLine, trLine, blLine, brLine] = walkLines([tlLine, trLine, blLine, brLine], ix); + } + + measurement.tlTheta = tlLine.theta; + measurement.trTheta = trLine.theta; + measurement.blTheta = blLine.theta; + measurement.brTheta = brLine.theta; + + //Compute the final fitness + const finalFitness = computeEndpointFitness(tlLine, trLine, blLine, brLine); + + //Compute the tension in the two upper belts + const { TL, TR } = calculateTensions(tlLine.xEnd, tlLine.yEnd, individual); + measurement.TLtension = TL; + measurement.TRtension = TR; + + const result = { + fitness: finalFitness, + lines: { tlLine: tlLine, trLine: trLine, blLine: blLine, brLine: brLine }, + }; + sendCalibrationEvent({ + lines: result, + individual, + measurement, + }); + + return result; } /** @@ -239,11 +270,11 @@ function magneticallyAttractedLinesFitness(measurement, individual) { * @returns {Object} - An object containing the x and y distances from the center of mass. */ function computeDistanceFromCenterOfMass(lineToCompare, line2, line3, line4) { - //Compute the center of mass - const x = (line2.xEnd + line3.xEnd + line4.xEnd) / 3 - const y = (line2.yEnd + line3.yEnd + line4.yEnd) / 3 + //Compute the center of mass + const x = (line2.xEnd + line3.xEnd + line4.xEnd) / 3; + const y = (line2.yEnd + line3.yEnd + line4.yEnd) / 3; - return { x: lineToCompare.xEnd - x, y: lineToCompare.yEnd - y } + return { x: lineToCompare.xEnd - x, y: lineToCompare.yEnd - y }; } /** @@ -252,15 +283,40 @@ function computeDistanceFromCenterOfMass(lineToCompare, line2, line3, line4) { * @returns {Object} - An object containing the distances from the center of mass for tlX, tlY, trX, trY, and brX. */ function generateTweaks(lines) { - //We care about the distances for tlX, tlY, trX, trY, brX - - const tlX = computeDistanceFromCenterOfMass(lines.tlLine, lines.trLine, lines.blLine, lines.brLine).x - const tlY = computeDistanceFromCenterOfMass(lines.tlLine, lines.trLine, lines.blLine, lines.brLine).y - const trX = computeDistanceFromCenterOfMass(lines.trLine, lines.tlLine, lines.blLine, lines.brLine).x - const trY = computeDistanceFromCenterOfMass(lines.trLine, lines.tlLine, lines.blLine, lines.brLine).y - const brX = computeDistanceFromCenterOfMass(lines.brLine, lines.tlLine, lines.trLine, lines.blLine).x - - return { tlX: tlX, tly: tlY, trX: trX, trY: trY, brX: brX } + //We care about the distances for tlX, tlY, trX, trY, brX + + const tlX = computeDistanceFromCenterOfMass( + lines.tlLine, + lines.trLine, + lines.blLine, + lines.brLine, + ).x; + const tlY = computeDistanceFromCenterOfMass( + lines.tlLine, + lines.trLine, + lines.blLine, + lines.brLine, + ).y; + const trX = computeDistanceFromCenterOfMass( + lines.trLine, + lines.tlLine, + lines.blLine, + lines.brLine, + ).x; + const trY = computeDistanceFromCenterOfMass( + lines.trLine, + lines.tlLine, + lines.blLine, + lines.brLine, + ).y; + const brX = computeDistanceFromCenterOfMass( + lines.brLine, + lines.tlLine, + lines.trLine, + lines.blLine, + ).x; + + return { tlX: tlX, tly: tlY, trX: trX, trY: trY, brX: brX }; } /** @@ -270,62 +326,62 @@ function generateTweaks(lines) { * @returns {Object} - The updated guess with the furthest tweaks applied. */ function computeFurthestFromCenterOfMass(lines, lastGuess) { - let tlX = 0; - let tlY = 0; - let trX = 0; - let trY = 0; - let brX = 0; - - lines.forEach((line) => { - const tweaks = generateTweaks(line); - - tlX += tweaks.tlX; - tlY += tweaks.tly; - trX += tweaks.trX; - trY += tweaks.trY; - brX += tweaks.brX; - }) - - tlX /= lines.length; - tlY /= lines.length; - trX /= lines.length; - trY /= lines.length; - brX /= lines.length; - - const tlXAbs = Math.abs(tlX); - const tlyAbs = Math.abs(tlY); - const trXAbs = Math.abs(trX); - const tryAbs = Math.abs(trY); - const brXAbs = Math.abs(brX); - const maxError = Math.max(tlXAbs, tlyAbs, trXAbs, tryAbs, brXAbs); - - var scalor = -1; - switch (maxError) { - case tlXAbs: - //console.log("Move tlX by: " + tlX/divisor); - lastGuess.tl.x = lastGuess.tl.x + tlX * scalor; - break; - case tlyAbs: - //console.log("Move tlY by: " + tlY/divisor); - lastGuess.tl.y = lastGuess.tl.y + tlY * scalor; - break; - case trXAbs: - //console.log("Move trX by: " + trX/divisor); - lastGuess.tr.x = lastGuess.tr.x + trX * scalor; - break; - case tryAbs: - //console.log("Move trY by: " + trY/divisor); - lastGuess.tr.y = lastGuess.tr.y + trY * scalor; - break; - case brXAbs: - //console.log("Move brX by: " + brX/divisor); - lastGuess.br.x = lastGuess.br.x + brX * scalor; - break; - default: - // Do nothing - } - - return lastGuess; + let tlX = 0; + let tlY = 0; + let trX = 0; + let trY = 0; + let brX = 0; + + for (const line in lines) { + const tweaks = generateTweaks(line); + + tlX += tweaks.tlX; + tlY += tweaks.tly; + trX += tweaks.trX; + trY += tweaks.trY; + brX += tweaks.brX; + }; + + tlX /= lines.length; + tlY /= lines.length; + trX /= lines.length; + trY /= lines.length; + brX /= lines.length; + + const tlXAbs = Math.abs(tlX); + const tlyAbs = Math.abs(tlY); + const trXAbs = Math.abs(trX); + const tryAbs = Math.abs(trY); + const brXAbs = Math.abs(brX); + const maxError = Math.max(tlXAbs, tlyAbs, trXAbs, tryAbs, brXAbs); + + const scalor = -1; + switch (maxError) { + case tlXAbs: + //console.log("Move tlX by: " + tlX/divisor); + lastGuess.tl.x = lastGuess.tl.x + tlX * scalor; + break; + case tlyAbs: + //console.log("Move tlY by: " + tlY/divisor); + lastGuess.tl.y = lastGuess.tl.y + tlY * scalor; + break; + case trXAbs: + //console.log("Move trX by: " + trX/divisor); + lastGuess.tr.x = lastGuess.tr.x + trX * scalor; + break; + case tryAbs: + //console.log("Move trY by: " + trY/divisor); + lastGuess.tr.y = lastGuess.tr.y + trY * scalor; + break; + case brXAbs: + //console.log("Move brX by: " + brX/divisor); + lastGuess.br.x = lastGuess.br.x + brX * scalor; + break; + default: + // Do nothing + } + + return lastGuess; } /** @@ -335,70 +391,75 @@ function computeFurthestFromCenterOfMass(lines, lastGuess) { * @returns {Object} - An object containing the fitness of the guess and the lines used to calculate the fitness. */ function computeLinesFitness(measurements, lastGuess) { - var fitnesses = [] - var allLines = [] + const fitnesses = []; + const allLines = []; - //Check each of the measurements against the guess - measurements.forEach((measurement) => { - const { fitness, lines } = magneticallyAttractedLinesFitness(measurement, lastGuess) - fitnesses.push(fitness) - allLines.push(lines) - }) + //Check each of the measurements against the guess + for (const measurement in measurements) { + const { fitness, lines } = magneticallyAttractedLinesFitness( + measurement, + lastGuess, + ); + fitnesses.push(fitness); + allLines.push(lines); + }; - //Computes the average fitness of all of the measurements - const fitness = calculateAverage(fitnesses) + //Computes the average fitness of all of the measurements + const fitness = calculateAverage(fitnesses); - // console.log(fitnesses) + // console.log(fitnesses) - //Here is where we need to do the calculation of which corner is the worst and which direction to move it - lastGuess = computeFurthestFromCenterOfMass(allLines, lastGuess) - lastGuess.fitness = fitness + //Here is where we need to do the calculation of which corner is the worst and which direction to move it + lastGuess = computeFurthestFromCenterOfMass(allLines, lastGuess); + lastGuess.fitness = fitness; - return lastGuess + return lastGuess; } function calculateTensions(x, y, guess) { - let Xtl = guess.tl.x - let Ytl = guess.tl.y - let Xtr = guess.tr.x - let Ytr = guess.tr.y - let Xbl = guess.bl.x - let Ybl = guess.bl.y - let Xbr = guess.br.x - let Ybr = guess.br.y - - let mass = 5.0 - const G_CONSTANT = 9.80665 - let alpha = 0.26 - let TL, TR - - let A, C, sinD, cosD, sinE, cosE - let Fx, Fy - - A = (Xtl - x) / (Ytl - y) - C = (Xtr - x) / (Ytr - y) - A = Math.abs(A) - C = Math.abs(C) - sinD = x / Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) - cosD = y / Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) - sinE = Math.abs(Xbr - x) / Math.sqrt(Math.pow(Xbr - x, 2) + Math.pow(y, 2)) - cosE = y / Math.sqrt(Math.pow(Xbr - x, 2) + Math.pow(y, 2)) - - Fx = Ybr * sinE - Ybl * sinD - Fy = Ybr * cosE + Ybl * cosD + mass * G_CONSTANT * Math.cos(alpha) - // console.log(`Fx = ${Fx.toFixed(1)}, Fy = ${Fy.toFixed(1)}`) - - let TLy = (Fx + C * Fy) / (A + C) - let TRy = Fy - TLy - let TRx = C * (Fy - TLy) - let TLx = A * TLy - - // console.log(`TLy = ${TLy.toFixed(1)}, TRy = ${TRy.toFixed(1)}, TRx = ${TRx.toFixed(1)}, TLx = ${TLx.toFixed(1)}`); - - TL = Math.sqrt(Math.pow(TLx, 2) + Math.pow(TLy, 2)) - TR = Math.sqrt(Math.pow(TRx, 2) + Math.pow(TRy, 2)) - - return { TL, TR } + const Xtl = guess.tl.x; + const Ytl = guess.tl.y; + const Xtr = guess.tr.x; + const Ytr = guess.tr.y; + const Xbl = guess.bl.x; + const Ybl = guess.bl.y; + const Xbr = guess.br.x; + const Ybr = guess.br.y; + + const mass = 5.0; + const G_CONSTANT = 9.80665; + const alpha = 0.26; + + const A = Math.abs((Xtl - x) / (Ytl - y)); + const C = Math.abs((Xtr - x) / (Ytr - y)); + + const xSq = x ** 2; + const ySq = y ** 2; + const xyR = Math.sqrt(xSq + ySq) + const sinD = x / xyR; + const cosD = y / xyR; + + const xb_x = Xbr - x; + const xb_xSq = xb_x ** 2; + const xb_xyR = Math.sqrt(xb_xSq + ySq); + const sinE = Math.abs(xb_x) / xb_xyR; + const cosE = y / xb_xyR; + + const Fx = Ybr * sinE - Ybl * sinD; + const Fy = Ybr * cosE + Ybl * cosD + mass * G_CONSTANT * Math.cos(alpha); + // console.log(`Fx = ${Fx.toFixed(1)}, Fy = ${Fy.toFixed(1)}`) + + const TLy = (Fx + C * Fy) / (A + C); + const TRy = Fy - TLy; + const TRx = C * (Fy - TLy); + const TLx = A * TLy; + + // console.log(`TLy = ${TLy.toFixed(1)}, TRy = ${TRy.toFixed(1)}, TRx = ${TRx.toFixed(1)}, TLx = ${TLx.toFixed(1)}`); + + const TL = Math.sqrt(TLx ** 2 + TLy ** 2); + const TR = Math.sqrt(TRx ** 2 + TRy ** 2); + + return { TL, TR }; } /** @@ -407,18 +468,17 @@ function calculateTensions(x, y, guess) { * @returns {number} - The average of the array. */ function calculateAverage(array) { - var total = 0 - var count = 0 + let total = 0; + let count = 0; - array.forEach(function (item, index) { - total += Math.abs(item) - count++ - }) + array.forEach((item, index) => { + total += Math.abs(item); + count++; + }); - return total / count + return total / count; } - /** * Projects the measurements to the plane of the machine. This is needed * because the belts are not parallel to the surface of the machine. @@ -426,12 +486,12 @@ function calculateAverage(array) { * @returns {Object} - An object containing the projected measurements */ function projectMeasurement(measurement) { - const tl = Math.sqrt(Math.pow(measurement.tl, 2) - Math.pow(tlZ, 2)) - const tr = Math.sqrt(Math.pow(measurement.tr, 2) - Math.pow(trZ, 2)) - const bl = Math.sqrt(Math.pow(measurement.bl, 2) - Math.pow(blZ, 2)) - const br = Math.sqrt(Math.pow(measurement.br, 2) - Math.pow(brZ, 2)) + const tl = Math.sqrt((measurement.tl ** 2) - (tlZ ** 2)); + const tr = Math.sqrt((measurement.tr ** 2) - (trZ ** 2)); + const bl = Math.sqrt((measurement.bl ** 2) - (blZ ** 2)); + const br = Math.sqrt((measurement.br ** 2) - (brZ ** 2)); - return { tl: tl, tr: tr, bl: bl, br: br } + return { tl: tl, tr: tr, bl: bl, br: br }; } /** @@ -440,13 +500,13 @@ function projectMeasurement(measurement) { * @returns {Object[]} - An array of objects containing the projected measurements of the top left, top right, bottom left, and bottom right corners of a rectangle. */ function projectMeasurements(measurements) { - var projectedMeasurements = [] + const projectedMeasurements = []; - measurements.forEach((measurement) => { - projectedMeasurements.push(projectMeasurement(measurement)) - }) + measurements.forEach((measurement) => { + projectedMeasurements.push(projectMeasurement(measurement)); + }); - return projectedMeasurements + return projectedMeasurements; } /** @@ -456,16 +516,16 @@ function projectMeasurements(measurements) { * @returns {Object[]} - An array of objects containing the updated measurements of the top left, top right, bottom left, and bottom right corners of a rectangle. */ function offsetMeasurements(measurements, offset) { - const newMeasurements = measurements.map((measurement) => { - return { - tl: measurement.tl + offset, - tr: measurement.tr + offset, - bl: measurement.bl + offset, - br: measurement.br + offset, - } - }) - - return newMeasurements + const newMeasurements = measurements.map((measurement) => { + return { + tl: measurement.tl + offset, + tr: measurement.tr + offset, + bl: measurement.bl + offset, + br: measurement.br + offset, + }; + }); + + return newMeasurements; } /** @@ -475,174 +535,175 @@ function offsetMeasurements(measurements, offset) { * @returns {Object[]} - An array of objects containing the updated measurements of the top left, top right, bottom left, and bottom right corners of a rectangle. */ function scaleMeasurements(measurements, scale) { - const newMeasurements = measurements.map((measurement) => { - return { - tl: measurement.tl * scale, - tr: measurement.tr * scale, - bl: measurement.bl, // * scale, - br: measurement.br, // * scale - } - }) - - return newMeasurements + const newMeasurements = measurements.map((measurement) => { + return { + tl: measurement.tl * scale, + tr: measurement.tr * scale, + bl: measurement.bl, // * scale, + br: measurement.br, // * scale + }; + }); + + return newMeasurements; } function scaleMeasurementsBasedOnTension(measurements, guess) { - const maxScale = 0.995 - const minScale = 0.994 - const maxTension = 60 - const minTension = 20 - - const scaleRange = maxScale - minScale - const tensionRange = maxTension - minTension - - const newMeasurements = measurements.map((measurement) => { - const tensionAdjustedTLScale = (1 - (measurement.TLtension - minTension) / tensionRange) * scaleRange + minScale - const tensionAdjustedTRScale = (1 - (measurement.TRtension - minTension) / tensionRange) * scaleRange + minScale - - return { - tl: measurement.tl * tensionAdjustedTLScale, - tr: measurement.tr * tensionAdjustedTRScale, - bl: measurement.bl, // * scale, - br: measurement.br, // * scale - } - }) - - return newMeasurements -} - - -function findMaxFitness(measurements) { - sendCalibrationEvent({ - initialGuess - }, true); - - //Project the measurements into the XY plane...this is now done on the firmware side - //measurements = projectMeasurements(measurements); - - let currentGuess = JSON.parse(JSON.stringify(initialGuess)); - let stagnantCounter = 0; - let totalCounter = 0; - var bestGuess = JSON.parse(JSON.stringify(initialGuess)); - - function iterate() { - const messagesBox = document.getElementById('messages'); - if (stagnantCounter < 1000 && totalCounter < 200000) { - //Clear the canvass - clearCanvas(); - - currentGuess = computeLinesFitness(measurements, currentGuess); - - if (1 / currentGuess.fitness > 1 / bestGuess.fitness) { - bestGuess = JSON.parse(JSON.stringify(currentGuess)); - stagnantCounter = 0; - } else { - stagnantCounter++; - } - totalCounter++; - // console.log("Total Counter: " + totalCounter); - sendCalibrationEvent({ - final: false, - guess: currentGuess, - bestGuess: bestGuess, - totalCounter - }); - - if (totalCounter % 100 == 0) { - messagesBox.textContent += `Fitness: ${(1 / bestGuess.fitness).toFixed(7)} in ${totalCounter}\n`; - messagesBox.scrollTop = messagesBox.scrollHeight; - } - - // Schedule the next iteration - setTimeout(iterate, 0); - } else { - if (1 / bestGuess.fitness < acceptableCalibrationThreshold) { - messagesBox.value += '\nWARNING FITNESS TOO LOW. DO NOT USE THESE CALIBRATION VALUES!'; - } - - messagesBox.textContent += '\nCalibration values:'; - messagesBox.textContent += '\nFitness: ' + 1 / bestGuess.fitness.toFixed(7); - - const tlxStr = bestGuess.tl.x.toFixed(1), tlyStr = bestGuess.tl.y.toFixed(1); - const trxStr = bestGuess.tr.x.toFixed(1), tryStr = bestGuess.tr.y.toFixed(1); - const blxStr = bestGuess.bl.x.toFixed(1), blyStr = bestGuess.bl.y.toFixed(1); - const brxStr = bestGuess.br.x.toFixed(1), bryStr = bestGuess.br.y.toFixed(1); - - messagesBox.textContent += `\n${M}_tlX: ${tlxStr}`; - messagesBox.textContent += `\n${M}_tlY: ${tlyStr}`; - messagesBox.textContent += `\n${M}_trX: ${trxStr}`; - messagesBox.textContent += `\n${M}_trY: ${tryStr}`; - messagesBox.textContent += `\n${M}_blX: ${blxStr}`; - messagesBox.textContent += `\n${M}_blY: ${blyStr}`; - messagesBox.textContent += `\n${M}_brX: ${brxStr}`; - messagesBox.textContent += `\n${M}_brY: ${bryStr}`; - messagesBox.scrollTop - messagesBox.scrollTop = messagesBox.scrollHeight; - - if (1 / bestGuess.fitness > acceptableCalibrationThreshold) { - sendCommand(`$/${M}_tlX= ${tlxStr}`); - sendCommand(`$/${M}_tlY= ${tlyStr}`); - sendCommand(`$/${M}_trX= ${trxStr}`); - sendCommand(`$/${M}_trY= ${tryStr}`); - sendCommand(`$/${M}_blX= ${blxStr}`); - sendCommand(`$/${M}_blY= ${blyStr}`); - sendCommand(`$/${M}_brX= ${brxStr}`); - sendCommand(`$/${M}_brY= ${bryStr}`); - - sendCalibrationEvent({ - good: true, - final: true, - bestGuess: bestGuess - }, true); - refreshSettings(current_setting_filter); - saveMaslowYaml(); - - messagesBox.textContent += '\nA command to save these values has been successfully sent for you. Please check for any error messages.'; - messagesBox.scrollTop = messagesBox.scrollHeight; - - initialGuess = bestGuess; - initialGuess.fitness = 100000000; - - // This restarts calibration process for the next stage - setTimeout(function () { - onCalibrationButtonsClick('$CAL', 'Calibrate'); - }, 2000); - } else { - sendCalibrationEvent({ - good: false, - final: true, - guess: bestGuess - }, true); - } - } - } - - // Start the iteration - iterate(); + const maxScale = 0.995; + const minScale = 0.994; + const maxTension = 60; + const minTension = 20; + + const scaleRange = maxScale - minScale; + const tensionRange = maxTension - minTension; + + const newMeasurements = measurements.map((measurement) => { + const tensionAdjustedTLScale = + (1 - (measurement.TLtension - minTension) / tensionRange) * scaleRange + + minScale; + const tensionAdjustedTRScale = + (1 - (measurement.TRtension - minTension) / tensionRange) * scaleRange + + minScale; + + return { + tl: measurement.tl * tensionAdjustedTLScale, + tr: measurement.tr * tensionAdjustedTRScale, + bl: measurement.bl, // * scale, + br: measurement.br, // * scale + }; + }); + + return newMeasurements; } +const findMaxFitness = (measurements) => { + sendCalibrationEvent( + { + initialGuess, + }, + true, + ); + + //Project the measurements into the XY plane...this is now done on the firmware side + //measurements = projectMeasurements(measurements); + + let currentGuess = JSON.parse(JSON.stringify(initialGuess)); + let stagnantCounter = 0; + let totalCounter = 0; + let bestGuess = JSON.parse(JSON.stringify(initialGuess)); + + function iterate() { + const messagesBox = document.getElementById("messages"); + if (stagnantCounter < 1000 && totalCounter < 200000) { + //Clear the canvass + clearCanvas(); + + currentGuess = computeLinesFitness(measurements, currentGuess); + + if (1 / currentGuess.fitness > 1 / bestGuess.fitness) { + bestGuess = JSON.parse(JSON.stringify(currentGuess)); + stagnantCounter = 0; + } else { + stagnantCounter++; + } + totalCounter++; + // console.log("Total Counter: " + totalCounter); + sendCalibrationEvent({ + final: false, + guess: currentGuess, + bestGuess: bestGuess, + totalCounter, + }); + + if (totalCounter % 100 === 0) { + messagesBox.textContent += `Fitness: ${(1 / bestGuess.fitness).toFixed(7)} in ${totalCounter}\n`; + messagesBox.scrollTop = messagesBox.scrollHeight; + } + + // Schedule the next iteration + setTimeout(iterate, 0); + } else { + if (1 / bestGuess.fitness < acceptableCalibrationThreshold) { + messagesBox.value += + "\nWARNING FITNESS TOO LOW. DO NOT USE THESE CALIBRATION VALUES!"; + } + + messagesBox.textContent += '\nCalibration values:'; + messagesBox.textContent += `\nFitness: ${1 / bestGuess.fitness.toFixed(7)}`; + + if (1 / bestGuess.fitness > acceptableCalibrationThreshold) { + sendCommand(`$/${M}_tlX= ${tlxStr}`); + sendCommand(`$/${M}_tlY= ${tlyStr}`); + sendCommand(`$/${M}_trX= ${trxStr}`); + sendCommand(`$/${M}_trY= ${tryStr}`); + sendCommand(`$/${M}_blX= ${blxStr}`); + sendCommand(`$/${M}_blY= ${blyStr}`); + sendCommand(`$/${M}_brX= ${brxStr}`); + sendCommand(`$/${M}_brY= ${bryStr}`); + + sendCalibrationEvent( + { + good: true, + final: true, + bestGuess: bestGuess, + }, + true, + ); + + const common = new Common(); + refreshSettings(common.current_setting_filter); + saveMaslowYaml(); + + messagesBox.textContent += + "\nA command to save these values has been successfully sent for you. Please check for any error messages."; + messagesBox.scrollTop = messagesBox.scrollHeight; + + initialGuess = bestGuess; + initialGuess.fitness = 100000000; + + // This restarts calibration process for the next stage + setTimeout(() => { + onCalibrationButtonsClick("$CAL", "Calibrate"); + }, 2000); + } else { + sendCalibrationEvent( + { + good: false, + final: true, + guess: bestGuess, + }, + true, + ); + } + } + } + + // Start the iteration + iterate(); +}; /** * This function will allow us to hook data into events that we can just copy this file into another project * to have the calibration run in other contexts and still gather events from the calculations to plot things, gather data, etc. */ function sendCalibrationEvent(dataToSend, log = false) { - try { - if (log) { - console.log(JSON.stringify(dataToSend, null, 2)); - } else if (dataToSend.totalCounter) { - console.log("total counter:", dataToSend.totalCounter); - } - document.body.dispatchEvent(new CustomEvent(CALIBRATION_EVENT_NAME, { - bubbles: true, - cancelable: true, - detail: dataToSend - })); - } catch (err) { - console.error('Unexpected:', err); - } + try { + if (log) { + console.log(JSON.stringify(dataToSend, null, 2)); + } else if (dataToSend.totalCounter) { + console.log("total counter:", dataToSend.totalCounter); + } + document.body.dispatchEvent( + new CustomEvent(CALIBRATION_EVENT_NAME, { + bubbles: true, + cancelable: true, + detail: dataToSend, + }), + ); + } catch (err) { + console.error("Unexpected:", err); + } } -const CALIBRATION_EVENT_NAME = 'calibration-data'; +const CALIBRATION_EVENT_NAME = "calibration-data"; //This is where the program really begins. The above is all function definitions //The way that the progam works is that we basically guess where the four corners are and then //check to see how good that guess was. To see how good a guess was we "draw" circles from the four corner points @@ -651,3 +712,5 @@ const CALIBRATION_EVENT_NAME = 'calibration-data'; //Once we've figured out how good our guess was we try a different guess. We keep the good guesses and throw away the bad guesses //using a genetic algorithm + +export { CALIBRATION_EVENT_NAME, findMaxFitness }; diff --git a/www/js/camera.js b/www/js/camera.js index ecebd43fb..85f832a66 100644 --- a/www/js/camera.js +++ b/www/js/camera.js @@ -1,4 +1,11 @@ -// import: displayBlock, displayNone, id, SavePreferences +import { + displayBlock, + displayNone, + id, + getPrefValue, + setPrefValue, + SavePreferences, +} from "./common.js"; /** Set up the event handlers for the camera tab */ const cameratab = () => { @@ -51,9 +58,9 @@ function camera_OnKeyUp(event) { } function camera_saveaddress() { - cameraformataddress(); - preferenceslist[0].camera_address = HTMLEncode(id('camera_webaddress').value); - SavePreferences(true); + cameraformataddress(); + setPrefValue("camera_address", id("camera_webaddress").value); + SavePreferences(true); } function camera_detachcam() { @@ -66,7 +73,9 @@ function camera_detachcam() { function camera_GetAddress() { id("camera_webaddress").value = - typeof (preferenceslist[0].camera_address) !== "undefined" - ? HTMLDecode(preferenceslist[0].camera_address) + typeof getPrefValue("camera_address") !== "undefined" + ? getPrefValue("camera_address") : ""; } + +export { cameratab, camera_GetAddress }; diff --git a/www/js/commands.js b/www/js/commands.js index acdd6504c..8e1a51e2d 100644 --- a/www/js/commands.js +++ b/www/js/commands.js @@ -1,4 +1,13 @@ -// import - conErr, stdErrMsg, getChecked, id, HTMLDecode, SendGetHttp, translate_text_item, process_socket_response +import { + conErr, + stdErrMsg, + getChecked, + id, + HTMLDecode, + SendGetHttp, + translate_text_item, + process_socket_response, +} from "./common.js"; const CustomCommand_history = []; let CustomCommand_history_index = -1; @@ -173,4 +182,9 @@ function SendCustomCommandFailed(error_code, response) { conErr(error_code, HTMLDecode(response), "cmd Error"); } -// export - init_command_panel, Monitor_check_autoscroll, Monitor_check_verbose_mode, Monitor_output_Update \ No newline at end of file +export { + init_command_panel, + Monitor_check_autoscroll, + Monitor_check_verbose_mode, + Monitor_output_Update, +}; diff --git a/www/js/common.js b/www/js/common.js new file mode 100644 index 000000000..4713472b9 --- /dev/null +++ b/www/js/common.js @@ -0,0 +1,450 @@ +/** Imports everything else, and re-exports them, helping to flatten out all of the myriad circular dependencies + * Also has its own class `common` that can funtion as an alternative to globals + */ +// The following must be imported first, and in this order +import { M } from "./constants.js"; +import { + classes, + conErr, + stdErrMsg, + disable_items, + displayBlock, + displayFlex, + displayInline, + displayNone, + getChecked, + setChecked, + getValue, + setValue, + setHTML, + setDisabled, + HTMLEncode, + HTMLDecode, + id, +} from "./util.js"; +import { numpad } from "./numpad.js"; +import { checkValue, valueIsFloat } from "./utilValidation.js"; + +import { alertdlg } from "./alertdlg.js"; +import { + CALIBRATION_EVENT_NAME, + findMaxFitness, +} from "./calculatesCalibrationStuff.js"; +import { cameratab, camera_GetAddress } from "./camera.js"; +import { + init_command_panel, + Monitor_check_autoscroll, + Monitor_check_verbose_mode, + Monitor_output_Update, +} from "./commands.js"; +import { + Apply_config_override, + Delete_config_override, + refreshconfig, +} from "./config.js"; +import { configtab } from "./configtab.js"; +import { confirmdlg } from "./confirmdlg.js"; +import { connectdlg } from "./connectdlg.js"; +import { + ControlsPanel, + get_Position, + init_controls_panel, + JogFeedrate, + on_autocheck_position, +} from "./controls.js"; +import { creditsdlg } from "./creditsdlg.js"; +import { clear_drop_menu, hide_drop_menu, showhide_drop_menu } from "./dropmenu.js"; +import { + build_file_filter_list, + files_list_success, + files_select_upload, + init_files_panel, +} from "./files.js"; +import { + build_axis_selection, + grblHandleMessage, + grbl_reset, + onAutoReportIntervalChange, + onstatusIntervalChange, + onprobemaxtravelChange, + onprobefeedrateChange, + onproberetractChange, + onprobetouchplatethicknessChange, + reportNone, + tryAutoReport, + reportPolled, + SendRealtimeCmd, + StartProbeProcess, + MPOS, + WPOS, +} from "./grbl.js"; +import { grblpanel } from "./grblpanel.js"; +import { clear_cmd_list, SendFileHttp, SendGetHttp } from "./http.js"; +import { get_icon_svg } from "./icons.js"; +import { inputdlg } from "./inputdlg.js"; +import { build_language_list, translate_text_item } from "./langUtils.js"; +import { DisconnectLogin, logindlg } from "./logindlg.js"; +import { showmacrodlg } from "./macrodlg.js"; +import { + MaslowErrMsgKeyValueCantUse, + MaslowErrMsgNoKey, + MaslowErrMsgNoValue, + MaslowErrMsgNoMatchingKey, + MaslowErrMsgKeyValueSuffix, + maslowInfoMsgHandling, + maslowErrorMsgHandling, + maslowMsgHandling, + checkHomed, + sendCommand, + loadConfigValues, + loadCornerValues, + saveConfigValues, +} from "./maslow.js"; +import { + listmodal, + closeModal, + getactiveModal, + setactiveModal, + showModal, +} from "./modaldlg.js"; +import { navbar, ontoggleLock } from "./navbar.js"; +import { changepassworddlg } from "./passworddlg.js"; +import { prefDefs } from "./prefDefs.js"; +import { + buildFieldId, + enable_ping, + getPref, + getPrefValue, + setPrefValue, + preferences, +} from "./prefUtils.js"; +import { + getpreferenceslist, + initpreferences, + showpreferencesdlg, + SavePreferences, +} from "./preferencesdlg.js"; +import { SendPrinterCommand } from "./printercmd.js"; +import { restartdlg } from "./restartdlg.js"; +import { scanwifidlg } from "./scanwifidlg.js"; +import { + build_control_from_pos, + build_HTML_setting_list, + define_esp_role, + define_esp_role_from_pos, + refreshSettings, + restart_esp, + saveMaslowYaml, +} from "./settings.js"; +import { settingstab } from "./settingstab.js"; +import { setupdlg } from "./setupdlg.js"; +import { Toolpath } from "./simple-toolpath.js"; +import { + CancelCurrentUpload, + handlePing, + EventListenerSetup, + pageID, + process_socket_response, + startSocket, +} from "./socket.js"; +import { SPIFFSdlg } from "./SPIFFSdlg.js"; +import { statusdlg } from "./statusdlg.js"; +import { opentab } from "./tabs.js"; +import { + loadedValues, + openModal, + hideModal, + goAxisByValue, + onCalibrationButtonsClick, + saveSerialMessages, + tabletInit, +} from "./tablet.js"; +import { translate_text } from "./translate.js"; +import { UIdisableddlg } from "./UIdisableddlg.js"; +import { updatedlg } from "./updatedlg.js"; +import { openstep } from "./wizard.js"; + +/** Selected values that were globals, now set up as members of a singleton */ +class Common { + constructor() { + if (Common.instance instanceof Common) { + // biome-ignore lint/correctness/noConstructorReturn: + return Common.instance; + } + + /** See connectdlg.js */ + this.esp_hostname = "ESP3D WebUI"; + this.websocket_port = 0; + this.websocket_ip = ""; + + /** See controls.js */ + this.control_macrolist = []; + + /** See file.js */ + this.gCodeFilename = ""; + + /** See grbl.js */ + this.calibrationResults = {}; + this.grblaxis = 3; + this.grblzerocmd = "X0 Y0 Z0"; + this.reportType = 'none'; + this.spindleTabSpindleSpeed = 1; + this.modal = { + modes: "", + plane: "G17", + units: "G21", + wcs: "G54", + distance: "G90", + }; + this.MPOS = [0, 0, 0]; + this.WPOS = [0, 0, 0]; + + /** See http.js */ + this.http_communication_locked = false; + this.page_id = ""; + this.xmlhttpupload; + + /** See loadHTML.js - coming soon */ + this.loadedHTML = []; + + /** See preferencesdlg.js */ + this.enable_ping = true; + /** This clunker piece of code works with/against `this.language` below */ + this.language_save = "en"; + + /** See printercmd.js */ + this.grbl_errorfn = null; + this.grbl_processfn = null; + + /** See settings.js */ + this.current_setting_filter = "nvs"; + this.setup_is_done = false; + this.do_not_build_settings = false; + + this.SETTINGS_AP_MODE = 1; + this.SETTINGS_STA_MODE = 2; + this.SETTINGS_FALLBACK_MODE = 3; + + /** See setupdlg.js */ + this.EP_STA_SSID = "Sta/SSID"; + + /** See socket.js */ + this.async_webcommunication = false; + + /** See SPIFFSdlg.js */ + this.SPIFFS_currentpath = "/"; + + /** see tablet.js */ + this.gCodeDisplayable = false; + + /** see translate.js */ + /** This clunker piece of code works with/against `this.language_save` above */ + this.language = 'en'; + + /** See util.js */ + this.last_ping = 0; + + /** See wizard.js - this is always false for some reason */ + this.can_revert_wizard = false; + + this.ESP3D_authentication = false; + this.esp_error_code = 0; + this.esp_error_message = ""; + + // Use Object.freeze(this.whatever) if you need something to stay unchanged + + Common.instance = this; + } +} + +export { + Common, + // from alertdlg.js + alertdlg, + // from calculatesCalibrationStuff.js + CALIBRATION_EVENT_NAME, + findMaxFitness, + // from camera.js + cameratab, + camera_GetAddress, + // from commands.js + init_command_panel, + Monitor_check_autoscroll, + Monitor_check_verbose_mode, + Monitor_output_Update, + // from config.js + Apply_config_override, + Delete_config_override, + refreshconfig, + // from configtab.js + configtab, + // from confirmdlg.js + confirmdlg, + // from connectdlg.js + connectdlg, + // from constants.js + M, + // from controls.js + ControlsPanel, + get_Position, + init_controls_panel, + JogFeedrate, + on_autocheck_position, + // from creditsdlg.js + creditsdlg, + // from dropmenu.js + clear_drop_menu, + hide_drop_menu, + showhide_drop_menu, + // files.js + build_file_filter_list, + files_list_success, + files_select_upload, + init_files_panel, + // from grbl.js + build_axis_selection, + grblHandleMessage, + grbl_reset, + onAutoReportIntervalChange, + onstatusIntervalChange, + onprobemaxtravelChange, + onprobefeedrateChange, + onproberetractChange, + onprobetouchplatethicknessChange, + reportNone, + tryAutoReport, + reportPolled, + SendRealtimeCmd, + StartProbeProcess, + MPOS, + WPOS, + // from grblpanel.js + grblpanel, + // from http.js + clear_cmd_list, + SendFileHttp, + SendGetHttp, + // from icons.js + get_icon_svg, + // from inputdlg.js + inputdlg, + // from langUtils.js + build_language_list, + translate_text_item, + // from logindlg.js + DisconnectLogin, + logindlg, + // from macrodlg.js + showmacrodlg, + // from maslow.js + MaslowErrMsgKeyValueCantUse, + MaslowErrMsgNoKey, + MaslowErrMsgNoValue, + MaslowErrMsgNoMatchingKey, + MaslowErrMsgKeyValueSuffix, + maslowInfoMsgHandling, + maslowErrorMsgHandling, + maslowMsgHandling, + checkHomed, + sendCommand, + loadConfigValues, + loadCornerValues, + saveConfigValues, + // from modaldlg.js + listmodal, + closeModal, + getactiveModal, + setactiveModal, + showModal, + // from navbar.js + navbar, + ontoggleLock, + // from numpad.js + numpad, + // from passworddlg.js + changepassworddlg, + // from prefDefs.js + prefDefs, + // from prefUtils.js + buildFieldId, + enable_ping, + getPref, + getPrefValue, + setPrefValue, + preferences, + // from preferencesdlg.js + getpreferenceslist, + initpreferences, + showpreferencesdlg, + SavePreferences, + // from printercmd.js + SendPrinterCommand, + // from restartdlg.js + restartdlg, + // from scanwifidlg.js + scanwifidlg, + // from settings.js + build_control_from_pos, + build_HTML_setting_list, + define_esp_role, + define_esp_role_from_pos, + refreshSettings, + restart_esp, + saveMaslowYaml, + // from setupdlg.js + setupdlg, + // from settingstab.js + settingstab, + // from simple-toolpath.js + Toolpath, + // from socket.js + CancelCurrentUpload, + handlePing, + EventListenerSetup, + pageID, + process_socket_response, + startSocket, + // from SPIFFSdlg.js + SPIFFSdlg, + // from statusdlg.js + statusdlg, + // from tabs.js + opentab, + // from tablet.js + loadedValues, + openModal, + hideModal, + goAxisByValue, + onCalibrationButtonsClick, + saveSerialMessages, + tabletInit, + // from translate.js + translate_text, + // from UIdisableddlg.js + UIdisableddlg, + // from updatedlg.js + updatedlg, + // from util.js + classes, + conErr, + stdErrMsg, + disable_items, + displayBlock, + displayFlex, + displayInline, + displayNone, + getChecked, + setChecked, + getValue, + setValue, + setHTML, + setDisabled, + HTMLEncode, + HTMLDecode, + id, + // from utilValidation.js + checkValue, + valueIsFloat, + // from wizard.js + openstep, +}; diff --git a/www/js/config.js b/www/js/config.js index b745adb0f..1fe6ec5fd 100644 --- a/www/js/config.js +++ b/www/js/config.js @@ -1,418 +1,462 @@ +import { + Common, + get_icon_svg, + conErr, + stdErrMsg, + displayBlock, + displayNone, + id, + setChecked, + setHTML, + alertdlg, + SendGetHttp, + translate_text_item, +} from "./common.js"; + var config_configList = []; var config_override_List = []; -var config_lastindex = -1 +var config_lastindex = -1; var config_error_msg = ""; var config_lastindex_is_override = false; var commandtxt = "$$"; var is_override_config = false; var config_file_name = "/sd/config"; - -function refreshconfig(is_override) { - if (http_communication_locked) { - id('config_status').innerHTML = translate_text_item("Communication locked by another process, retry later."); - return; - } - is_override_config = false; - if ((typeof is_override != 'undefined') && is_override) is_override_config = is_override; - config_display_override(is_override_config); - displayBlock('config_loader'); - displayNone('config_list_content'); - displayNone('config_status'); - displayNone('config_refresh_btn'); - if (!is_override) config_configList = []; - config_override_List = []; - getprinterconfig(is_override_config); -} +const refreshconfig = (is_override) => { + const common = new Common(); + if (common.http_communication_locked) { + setHTML("config_status", translate_text_item("Communication locked by another process, retry later.")); + return; + } + is_override_config = false; + if (typeof is_override !== "undefined" && is_override) { + is_override_config = is_override; + } + config_display_override(is_override_config); + displayBlock("config_loader"); + displayNone("config_list_content"); + displayNone("config_status"); + displayNone("config_refresh_btn"); + if (!is_override) { + config_configList = []; + } + config_override_List = []; + getprinterconfig(is_override_config); +}; function config_display_override(display_it) { - if (display_it) { - displayBlock('config_overrdisplayBlocke_list_content'); - displayNone('config_main_content'); - id('config_override_file').checked = true; - } else { - id('config_overrdisplayNonee_list_content'); - displayBlock('config_main_content'); - id('config_main_file').checked = true; - } + if (display_it) { + displayBlock("config_overrdisplayBlocke_list_content"); + displayNone("config_main_content"); + setChecked("config_override_file", true); + } else { + id("config_overrdisplayNonee_list_content"); + displayBlock("config_main_content"); + setChecked("config_main_file", true); + } } function getprinterconfig(is_override) { - var cmd = commandtxt; - if ((typeof is_override != 'undefined') && is_override) { - cmd = "M503"; - config_override_List = []; - is_override_config = true; - } else is_override_config = false; - var url = "/command?plain=" + encodeURIComponent(cmd); - SendGetHttp(url); + var cmd = commandtxt; + if (typeof is_override != "undefined" && is_override) { + cmd = "M503"; + config_override_List = []; + is_override_config = true; + } else { + is_override_config = false; + } + var url = "/command?plain=" + encodeURIComponent(cmd); + SendGetHttp(url); } -function Apply_config_override() { - var url = "/command?plain=" + encodeURIComponent("M500"); - SendGetHttp(url, getESPUpdateconfigSuccess); -} +const Apply_config_override = () => { + var url = "/command?plain=" + encodeURIComponent("M500"); + SendGetHttp(url, getESPUpdateconfigSuccess); +}; -function Delete_config_override() { - var url = "/command?plain=" + encodeURIComponent("M502"); - SendGetHttp(url, getESPUpdateconfigSuccess); -} +const Delete_config_override = () => { + var url = "/command?plain=" + encodeURIComponent("M502"); + SendGetHttp(url, getESPUpdateconfigSuccess); +}; function getESPUpdateconfigSuccess(response) { - refreshconfig(true); + refreshconfig(true); } function build_HTML_config_list() { - var content = ""; - var array_len = config_configList.length; - if (is_override_config) array_len = config_override_List.length; - for (var i = 0; i < array_len; i++) { - var item; - var prefix = ""; - if (is_override_config) { - item = config_override_List[i]; - prefix = "_override" - } else item = config_configList[i]; - content += ""; - if (item.showcomment) { - content += ""; - content += item.comment; - } else { - content += ""; - content += item.label; - content += ""; - content += ""; - content += "
" - content += "
"; - content += "
"; - content += "
"; - content += "
"; - content += ""; - content += ""; - content += ""; - content += ""; - content += "
"; - content += "
"; - content += "
"; - content += ""; - content += ""; - content += ""; - content += ""; - content += "
"; - content += "
"; - content += "
"; - content += ""; - content += ""; - content += " "; - content += ""; - content += "
"; - content += "
"; - content += "
"; - content += "
"; - content += ""; - content += ""; - content += item.help; - } - content += ""; - content += "\n"; - } - if (content.length > 0) { - id('config_list_data').innerHTML = content; - } - displayNone('config_loader'); - displayBlock('config_list_content'); - displayNone('config_status'); - displayBlock('config_refresh_btn'); + var content = ""; + const array_len = is_override_config + ? config_override_List.length + : config_configList.length; + const prefix = is_override_config ? "override" : ""; + const actions = []; + for (var i = 0; i < array_len; i++) { + let item = is_override_config + ? config_override_List[i] + : config_configList[i]; + content += ""; + let fullpref = `${prefix}${i}`; + if (item.showcomment) { + content += ""; + content += item.comment; + } else { + content += `${item.label}`; + content += ""; + content += "
"; + content += `
`; + content += "
"; + content += ""; + content += "
"; + content += "
"; + content += ""; + content += ``; + content += ""; + actions.push({ + id: `btn_revert_config_${fullpref}`, + type: "click", + method: config_revert_to_default(i, is_override_config), + }); + content += ""; + content += "
"; + content += "
"; + content += "
"; + content += ""; + content += ``; + actions.push({ + id: `config_${fullpref}`, + type: "keyup", + method: config_checkchange(i, is_override_config), + }); + content += ``; + content += ""; + content += "
"; + content += "
"; + content += "
"; + content += ""; + content += ""; + content += ` `; + content += ""; + actions.push({ + id: `btn_config_${fullpref}`, + type: "click", + method: configGetvalue(i, is_override_config), + }); + content += "
"; + content += "
"; + content += "
"; + content += "
"; + content += ""; + content += ""; + content += item.help; + } + content += ""; + content += "\n"; + } + if (content.length > 0) { + setHTML("config_list_data", content); + actions.forEach((action) => { + id(action.id).addEventListener(action.type, (event) => action.method); + }); + } + displayNone("config_loader"); + displayBlock("config_list_content"); + displayNone("config_status"); + displayBlock("config_refresh_btn"); } function config_check_value(value, index, is_override) { - var isvalid = true; - if ((value.trim()[0] == '-') || (value.length === 0) || (value.toLowerCase().indexOf("#") != -1)) { - isvalid = false; - config_error_msg = translate_text_item("cannot have '-', '#' char or be empty"); - } - return isvalid; + let isvalid = true; + if ( + value.trim()[0] === "-" || + value.length === 0 || + value.toLowerCase().indexOf("#") !== -1 + ) { + isvalid = false; + config_error_msg = translate_text_item("cannot have '-', '#' char or be empty"); + } + return isvalid; } function process_config_answer(response_text) { - var result = true; - var tlines = response_text.split("\n"); - //console.log(tlines.length); - //console.log("Config has " + tlines.length + " entries"); - var vindex = 0; - for (var i = 0; i < tlines.length; i++) { - vindex = create_config_entry(tlines[i], vindex); - } - if (vindex > 0) build_HTML_config_list(); - else result = false; + var result = true; + var tlines = response_text.split("\n"); + //console.log(tlines.length); + //console.log("Config has " + tlines.length + " entries"); + var vindex = 0; + for (var i = 0; i < tlines.length; i++) { + vindex = create_config_entry(tlines[i], vindex); + } + if (vindex > 0) build_HTML_config_list(); + else result = false; - return result; + return result; } function create_config_entry(sentry, vindex) { - var iscomment; - var ssentry = sentry; - if (!is_config_entry(ssentry)) return vindex; - while (ssentry.indexOf("\t") > -1) { - ssentry = ssentry.replace("\t", " "); - } - while (ssentry.indexOf(" ") > -1) { - ssentry = ssentry.replace(" ", " "); - } - while (ssentry.indexOf("##") > -1) { - ssentry = ssentry.replace("##", "#"); - } + var iscomment; + var ssentry = sentry; + if (!is_config_entry(ssentry)) return vindex; + while (ssentry.indexOf("\t") > -1) { + ssentry = ssentry.replace("\t", " "); + } + while (ssentry.indexOf(" ") > -1) { + ssentry = ssentry.replace(" ", " "); + } + while (ssentry.indexOf("##") > -1) { + ssentry = ssentry.replace("##", "#"); + } - iscomment = is_config_commented(ssentry); - if (iscomment) { - while (ssentry.indexOf("<") != -1) { - var m = ssentry.replace("<", "<"); - ssentry = m.replace(">", ">"); - } - var config_entry = { - comment: ssentry, - showcomment: true, - index: vindex, - label: "", - help: "", - defaultvalue: "", - cmd: "" - }; - if (is_override_config) config_override_List.push(config_entry); - else config_configList.push(config_entry); - } else { - var slabel = get_config_label(ssentry); - var svalue = get_config_value(ssentry); - var shelp = get_config_help(ssentry); - var scmd = get_config_command(ssentry) - var config_entry = { - comment: ssentry, - showcomment: false, - index: vindex, - label: slabel, - help: shelp, - defaultvalue: svalue, - cmd: scmd, - is_override: is_override_config - }; - if (is_override_config) config_override_List.push(config_entry); - else config_configList.push(config_entry); - } - vindex++; - return vindex; + iscomment = is_config_commented(ssentry); + if (iscomment) { + while (ssentry.indexOf("<") != -1) { + var m = ssentry.replace("<", "<"); + ssentry = m.replace(">", ">"); + } + var config_entry = { + comment: ssentry, + showcomment: true, + index: vindex, + label: "", + help: "", + defaultvalue: "", + cmd: "", + }; + if (is_override_config) config_override_List.push(config_entry); + else config_configList.push(config_entry); + } else { + var slabel = get_config_label(ssentry); + var svalue = get_config_value(ssentry); + var shelp = get_config_help(ssentry); + var scmd = get_config_command(ssentry); + var config_entry = { + comment: ssentry, + showcomment: false, + index: vindex, + label: slabel, + help: shelp, + defaultvalue: svalue, + cmd: scmd, + is_override: is_override_config, + }; + if (is_override_config) config_override_List.push(config_entry); + else config_configList.push(config_entry); + } + vindex++; + return vindex; } //check it is valid entry function is_config_entry(sline) { - var line = sline.trim(); - if (line.length == 0) return false; - return (line.indexOf("$") == 0) && (line.indexOf("=") != -1); + var line = sline.trim(); + if (line.length == 0) return false; + return line.indexOf("$") == 0 && line.indexOf("=") != -1; } function get_config_label(sline) { - var tline = sline.trim().split(" "); - var tsize = tline.length; + var tline = sline.trim().split(" "); + var tsize = tline.length; - var tline2 = sline.trim().split("="); - return tline2[0]; + var tline2 = sline.trim().split("="); + return tline2[0]; } function get_config_value(sline) { - var tline = sline.trim().split(" "); - var tline2 = sline.trim().split("="); - return tline2.length > 1 ? tline2[1] : "???"; + const tline = sline.trim().split(" "); + const tline2 = sline.trim().split("="); + return tline2.length > 1 ? tline2[1] : "???"; } const get_config_help = (sline) => (is_override_config) ? "" : inline_help(get_config_label(sline)); function get_config_command(sline) { - return get_config_label(sline) + "="; + return `${get_config_label(sline)}=`; } function is_config_commented(sline) { - var line = sline.trim(); + const line = sline.trim(); if (!line.length) return false; return (is_override_config) ? line.startsWith(";") : false; } function config_revert_to_default(index, is_override) { - var prefix = ""; - var item = config_configList[index]; - if (is_override) { - prefix = "_override"; - item = config_override_List[index]; - } - console.log() - id('config_' + prefix + index).value = item.defaultvalue; - id('btn_config_' + prefix + index).className = "btn btn-default"; - id('status_config_' + prefix + index).className = "form-group has-feedback"; - id('icon_config_' + prefix + index).innerHTML = ""; + let prefix = ""; + let item = config_configList[index]; + if (is_override) { + prefix = "_override"; + item = config_override_List[index]; + } + console.log(); + id(`config_${prefix}${index}`).value = item.defaultvalue; + id(`btn_config_${prefix}${index}`).className = "btn btn-default"; + id(`status_config_${prefix}${index}`).className = "form-group has-feedback"; + setHTML(`icon_config_${prefix}${index}`, ""); } function is_config_override_file() { - if (config_override_List.length > 5) { - for (i = 0; i < 5; i++) { - if (config_override_List[i].comment.startsWith("; No config override")) return true; - } - } - return false; + if (config_override_List.length > 5) { + for (i = 0; i < 5; i++) { + if (config_override_List[i].comment.startsWith("; No config override")) + return true; + } + } + return false; } function configGetvalue(index, is_override) { - var prefix = ""; - var item = config_configList[index]; - if (is_override) { - prefix = "_override"; - item = config_override_List[index]; - } - //remove possible spaces - value = id('config_' + prefix + index).value.trim(); - if (value == item.defaultvalue) return; - //check validity of value - var isvalid = config_check_value(value, index, is_override); - //if not valid show error - if (!isvalid) { - id('btn_config_' + prefix + index).className = "btn btn-danger"; - id('icon_config_' + prefix + index).className = "form-control-feedback has-error ico_feedback"; - id('icon_config_' + prefix + index).innerHTML = get_icon_svg("remove"); - id('status_config_' + prefix + index).className = "form-group has-feedback has-error"; - alertdlg(translate_text_item("Out of range"), translate_text_item("Value ") + config_error_msg + " !"); - } else { - //value is ok save it - var cmd = item.cmd + value; - config_lastindex = index; - config_lastindex_is_override = is_override; - item.defaultvalue = value; - id('btn_config_' + prefix + index).className = "btn btn-success"; - id('icon_config_' + prefix + index).className = "form-control-feedback has-success ico_feedback"; - id('icon_config_' + prefix + index).innerHTML = get_icon_svg("ok"); - id('status_config_' + prefix + index).className = "form-group has-feedback has-success"; - var url = "/command?plain=" + encodeURIComponent(cmd); - SendGetHttp(url, setESPconfigSuccess, setESPconfigfailed); - } + var prefix = ""; + var item = config_configList[index]; + if (is_override) { + prefix = "_override"; + item = config_override_List[index]; + } + //remove possible spaces + value = id(`config_${prefix}${index}`).value.trim(); + if (value === item.defaultvalue) return; + //check validity of value + const isvalid = config_check_value(value, index, is_override); + //if not valid show error + if (!isvalid) { + id(`btn_config_${prefix}${index}`).className = "btn btn-danger"; + id(`icon_config_${prefix}${index}`).className = "form-control-feedback has-error ico_feedback"; + setHTML(`icon_config_${prefix}${index}`, get_icon_svg("remove")); + id(`status_config_${prefix}${index}`).className = "form-group has-feedback has-error"; + alertdlg(translate_text_item("Out of range"), `${translate_text_item("Value ") + config_error_msg} !`); + } else { + //value is ok save it + var cmd = item.cmd + value; + config_lastindex = index; + config_lastindex_is_override = is_override; + item.defaultvalue = value; + id(`btn_config_${prefix}${index}`).className = "btn btn-success"; + id(`icon_config_${prefix}${index}`).className = "form-control-feedback has-success ico_feedback"; + setHTML(`icon_config_${prefix}${index}`, get_icon_svg("ok")); + id(`status_config_${prefix}${index}`).className = "form-group has-feedback has-success"; + const url = `/command?plain=${encodeURIComponent(cmd)}`; + SendGetHttp(url, setESPconfigSuccess, setESPconfigfailed); + } } function config_checkchange(index, is_override) { - //console.log("check " + "config_"+index); - var prefix = ""; - var item = config_configList[index]; - if (is_override) { - prefix = "_override"; - item = config_override_List[index]; - } - var val = id('config_' + prefix + index).value.trim(); - //console.log("value: " + val); - if (item.defaultvalue == val) { - id('btn_config_' + prefix + index).className = "btn btn-default"; - id('icon_config_' + prefix + index).className = "form-control-feedback"; - id('icon_config_' + prefix + index).innerHTML = ""; - id('status_config_' + prefix + index).className = "form-group has-feedback"; - } else if (config_check_value(val, index, is_override)) { - id('status_config_' + prefix + index).className = "form-group has-feedback has-warning"; - id('btn_config_' + prefix + index).className = "btn btn-warning"; - id('icon_config_' + prefix + index).className = "form-control-feedback has-warning ico_feedback"; - id('icon_config_' + prefix + index).innerHTML = get_icon_svg("warning-sign"); - //console.log("change ok"); - } else { - //console.log("change bad"); - id('btn_config_' + prefix + index).className = "btn btn-danger"; - id('icon_config_' + prefix + index).className = "form-control-feedback has-error ico_feedback"; - id('icon_config_' + prefix + index).innerHTML = get_icon_svg("remove"); - id('status_config_' + prefix + index).className = "form-group has-feedback has-error"; - } - + //console.log("check " + "config_"+index); + var prefix = ""; + var item = config_configList[index]; + if (is_override) { + prefix = "_override"; + item = config_override_List[index]; + } + var val = id("config_" + prefix + index).value.trim(); + //console.log("value: " + val); + if (item.defaultvalue == val) { + id("btn_config_" + prefix + index).className = "btn btn-default"; + id("icon_config_" + prefix + index).className = "form-control-feedback"; + setHTML("icon_config_" + prefix + index, ""); + id("status_config_" + prefix + index).className = "form-group has-feedback"; + } else if (config_check_value(val, index, is_override)) { + id("status_config_" + prefix + index).className = + "form-group has-feedback has-warning"; + id("btn_config_" + prefix + index).className = "btn btn-warning"; + id("icon_config_" + prefix + index).className = + "form-control-feedback has-warning ico_feedback"; + setHTML("icon_config_" + prefix + index, get_icon_svg("warning-sign")); + //console.log("change ok"); + } else { + //console.log("change bad"); + id("btn_config_" + prefix + index).className = "btn btn-danger"; + id("icon_config_" + prefix + index).className = + "form-control-feedback has-error ico_feedback"; + setHTML("icon_config_" + prefix + index, get_icon_svg("remove")); + id("status_config_" + prefix + index).className = + "form-group has-feedback has-error"; + } } function setESPconfigSuccess(response) { - //console.log(response); + //console.log(response); } -var grbl_help = { - "$0": "Step pulse, microseconds", - "$1": "Step idle delay, milliseconds", - "$2": "Step port invert, mask", - "$3": "Direction port invert, mask", - "$4": "Step enable invert, boolean", - "$5": "Limit pins invert, boolean", - "$6": "Probe pin invert, boolean", - "$10": "Status report, mask", - "$11": "Junction deviation, mm", - "$12": "Arc tolerance, mm", - "$13": "Report inches, boolean", - "$20": "Soft limits, boolean", - "$21": "Hard limits, boolean", - "$22": "Homing cycle, boolean", - "$23": "Homing dir invert, mask", - "$24": "Homing feed, mm/min", - "$25": "Homing seek, mm/min", - "$26": "Homing debounce, milliseconds", - "$27": "Homing pull-off, mm", - "$30": "Max spindle speed, RPM", - "$31": "Min spindle speed, RPM", - "$32": "Laser mode, boolean", - "$100": "X steps/mm", - "$101": "Y steps/mm", - "$102": "Z steps/mm", - "$103": "A steps/mm", - "$104": "B steps/mm", - "$105": "C steps/mm", - "$110": "X Max rate, mm/min", - "$111": "Y Max rate, mm/min", - "$112": "Z Max rate, mm/min", - "$113": "A Max rate, mm/min", - "$114": "B Max rate, mm/min", - "$115": "C Max rate, mm/min", - "$120": "X Acceleration, mm/sec^2", - "$121": "Y Acceleration, mm/sec^2", - "$122": "Z Acceleration, mm/sec^2", - "$123": "A Acceleration, mm/sec^2", - "$124": "B Acceleration, mm/sec^2", - "$125": "C Acceleration, mm/sec^2", - "$130": "X Max travel, mm", - "$131": "Y Max travel, mm", - "$132": "Z Max travel, mm", - "$133": "A Max travel, mm", - "$134": "B Max travel, mm", - "$135": "C Max travel, mm" - +const grbl_help = { + $0: "Step pulse, microseconds", + $1: "Step idle delay, milliseconds", + $2: "Step port invert, mask", + $3: "Direction port invert, mask", + $4: "Step enable invert, boolean", + $5: "Limit pins invert, boolean", + $6: "Probe pin invert, boolean", + $10: "Status report, mask", + $11: "Junction deviation, mm", + $12: "Arc tolerance, mm", + $13: "Report inches, boolean", + $20: "Soft limits, boolean", + $21: "Hard limits, boolean", + $22: "Homing cycle, boolean", + $23: "Homing dir invert, mask", + $24: "Homing feed, mm/min", + $25: "Homing seek, mm/min", + $26: "Homing debounce, milliseconds", + $27: "Homing pull-off, mm", + $30: "Max spindle speed, RPM", + $31: "Min spindle speed, RPM", + $32: "Laser mode, boolean", + $100: "X steps/mm", + $101: "Y steps/mm", + $102: "Z steps/mm", + $103: "A steps/mm", + $104: "B steps/mm", + $105: "C steps/mm", + $110: "X Max rate, mm/min", + $111: "Y Max rate, mm/min", + $112: "Z Max rate, mm/min", + $113: "A Max rate, mm/min", + $114: "B Max rate, mm/min", + $115: "C Max rate, mm/min", + $120: "X Acceleration, mm/sec^2", + $121: "Y Acceleration, mm/sec^2", + $122: "Z Acceleration, mm/sec^2", + $123: "A Acceleration, mm/sec^2", + $124: "B Acceleration, mm/sec^2", + $125: "C Acceleration, mm/sec^2", + $130: "X Max travel, mm", + $131: "Y Max travel, mm", + $132: "Z Max travel, mm", + $133: "A Max travel, mm", + $134: "B Max travel, mm", + $135: "C Max travel, mm", }; function inline_help(label) { - var shelp = ""; - shelp = grbl_help[label]; - if (typeof shelp === 'undefined') shelp = ""; - return translate_text_item(shelp); + var shelp = ""; + shelp = grbl_help[label]; + if (typeof shelp === "undefined") shelp = ""; + return translate_text_item(shelp); } function setESPconfigfailed(error_code, response) { - const errMsg = stdErrMsg(error_code, response); - alertdlg(translate_text_item("Set failed"), errMsg); - conErr(errMsg); - const prefix = (config_lastindex_is_override) ? "_override" : ""; - id('btn_config_' + prefix + config_lastindex).className = "btn btn-danger"; - id('icon_config_' + prefix + config_lastindex).className = "form-control-feedback has-error ico_feedback"; - id('icon_config_' + prefix + config_lastindex).innerHTML = get_icon_svg("remove"); - id('status_config_' + prefix + config_lastindex).className = "form-group has-feedback has-error"; + const errMsg = stdErrMsg(error_code, response); + alertdlg(translate_text_item("Set failed"), errMsg); + conErr(errMsg); + const prefix = config_lastindex_is_override ? "_override" : ""; + id(`btn_config_${prefix}${config_lastindex}`).className = "btn btn-danger"; + id(`icon_config_${prefix}${config_lastindex}`).className = "form-control-feedback has-error ico_feedback"; + setHTML(`icon_config_${prefix}${config_lastindex}`, get_icon_svg("remove")); + id(`status_config_${prefix}${config_lastindex}`).className = "form-group has-feedback has-error"; } function getESPconfigSuccess(response) { - //console.log(response); - if (!process_config_answer(response)) { - getESPconfigfailed(406, translate_text_item("Wrong data")); - displayNone('config_loader'); - displayBlock('config_list_content'); - displayNone('config_status'); - displayBlock('config_refresh_btn'); - return; - } + //console.log(response); + if (!process_config_answer(response)) { + getESPconfigfailed(406, translate_text_item("Wrong data")); + displayNone("config_loader"); + displayBlock("config_list_content"); + displayNone("config_status"); + displayBlock("config_refresh_btn"); + return; + } } function getESPconfigfailed(error_code, response) { - conErr(error_code, response); - displayNone('config_loader'); - displayBlock('config_status'); - id('config_status').innerHTML = stdErrMsg(error_code, response, translate_text_item("Failed")); - displayBlock('config_refresh_btn'); + conErr(error_code, response); + displayNone("config_loader"); + displayBlock("config_status"); + setHTML("config_status", stdErrMsg(error_code, response, translate_text_item("Failed"))); + displayBlock("config_refresh_btn"); } + +export { Apply_config_override, Delete_config_override, refreshconfig }; diff --git a/www/js/configtab.js b/www/js/configtab.js index 4f3c23ae2..1e0a50f88 100644 --- a/www/js/configtab.js +++ b/www/js/configtab.js @@ -1,4 +1,4 @@ -// import - Apply_config_override, Delete_config_override, refreshconfig, id +import { Apply_config_override, Delete_config_override, refreshconfig, id } from "./common.js"; /** Set up the event handlers for the config tab */ const configtab = () => { @@ -6,3 +6,5 @@ const configtab = () => { id("config_apply_override").addEventListener("click", (event) => Apply_config_override()); id("config_delete_override").addEventListener("click", (event) => Delete_config_override()); }; + +export { configtab }; diff --git a/www/js/confirmdlg.js b/www/js/confirmdlg.js index bc679ccf7..b01adef44 100644 --- a/www/js/confirmdlg.js +++ b/www/js/confirmdlg.js @@ -1,4 +1,4 @@ -// import - closeModal, setactiveModal, showModal, id +import {closeModal, setactiveModal, showModal, id} from "./common.js"; /** confirm dialog */ const confirmdlg = (titledlg, textdlg, closefunc) => { @@ -17,3 +17,5 @@ const confirmdlg = (titledlg, textdlg, closefunc) => { body.innerHTML = textdlg; showModal(); }; + +export { confirmdlg }; \ No newline at end of file diff --git a/www/js/connectdlg.js b/www/js/connectdlg.js index 689b5b189..5e2739394 100644 --- a/www/js/connectdlg.js +++ b/www/js/connectdlg.js @@ -1,4 +1,18 @@ -// import conErr, displayBlock, displayInline, displayNone, id, closeModal, setactiveModal, showModal, SendGetHttp, logindlg, EventListenerSetup, startSocket, +import { + Common, + conErr, + displayBlock, + displayInline, + displayNone, + id, + closeModal, + setactiveModal, + showModal, + SendGetHttp, + logindlg, + EventListenerSetup, + startSocket, +} from "./common.js"; /** Connect Dialog */ const connectdlg = (getFw = true) => { @@ -60,7 +74,10 @@ const getFWdata = (response) => { if (sublist.length !== 2) { return false; } - ESP3D_authentication = sublist[0].trim() === "authentication" && sublist[1].trim() === "yes"; + + const common = new Common(); + common.ESP3D_authentication = sublist[0].trim() === "authentication" && sublist[1].trim() === "yes"; + //async communications if (tlist.length > 6) { sublist = tlist[6].split(":"); @@ -68,9 +85,9 @@ const getFWdata = (response) => { sublist[0].trim() === "webcommunication" && sublist[1].trim() === "Async" ) { - async_webcommunication = true; + common.async_webcommunication = true; } else { - async_webcommunication = false; + common.async_webcommunication = false; websocket_port = sublist[2].trim(); if (sublist.length > 3) { websocket_ip = sublist[3].trim(); @@ -82,13 +99,13 @@ const getFWdata = (response) => { } if (tlist.length > 7) { sublist = tlist[7].split(":"); - if (sublist[0].trim() === "hostname") esp_hostname = sublist[1].trim(); + if (sublist[0].trim() == "hostname") esp_hostname = sublist[1].trim(); } if (tlist.length > 8) { sublist = tlist[8].split(":"); if (sublist[0].trim() === "axis") { - grblaxis = Number.parseInt(sublist[1].trim()); + common.grblaxis = Number.parseInt(sublist[1].trim()); } } @@ -108,7 +125,8 @@ const connectfailed = (error_code, response) => { const connectsuccess = (response) => { if (getFWdata(response)) { console.log(`Fw identification:${response}`); - if (ESP3D_authentication) { + const common = new Common(); + if (common.ESP3D_authentication) { closeModal("Connection successful"); displayInline("menu_authentication"); logindlg(initUI, true); @@ -129,3 +147,5 @@ const retryconnect = () => { const url = `/command?plain=${encodeURIComponent("[ESP800]")}`; SendGetHttp(url, connectsuccess, connectfailed); }; + +export { connectdlg }; diff --git a/www/js/constants.js b/www/js/constants.js index f78002d97..876fe8405 100644 --- a/www/js/constants.js +++ b/www/js/constants.js @@ -1,4 +1,5 @@ // Selected constants that turn up in several files and/or between this project and FluidNC -// When we can change to proper ESM - prefix this with 'export' (minus the quotes of course) const M = "Maslow"; + +export { M }; \ No newline at end of file diff --git a/www/js/controls.js b/www/js/controls.js index 5a2b962f9..410771fc3 100644 --- a/www/js/controls.js +++ b/www/js/controls.js @@ -1,11 +1,31 @@ +import { + Common, + get_icon_svg, + conErr, + displayBlock, + displayInline, + displayNone, + getChecked, + id, + setChecked, + setHTML, + alertdlg, + getPrefValue, + SendPrinterCommand, + SendGetHttp, + translate_text_item, + showmacrodlg, + valueIsFloat, +} from "./common.js"; + let interval_position = -1; -var control_macrolist = []; /** Set up the macro list for the Controls Panel */ const init_controls_panel = () => { loadmacrolist(); }; + /** Set up the event handlers for the Controls Panel */ const ControlsPanel = () => { id("autocheck_position").addEventListener("click", (event) => on_autocheck_position()); @@ -44,22 +64,24 @@ function showAxiscontrols() { } function loadmacrolist() { - control_macrolist = []; - var url = "/macrocfg.json"; + const common = new Common(); + common.control_macrolist = []; + const url = "/macrocfg.json"; SendGetHttp(url, processMacroGetSuccess, processMacroGetFailed); } function Macro_build_list(response_text) { - var response = []; + let response = []; try { - if (response_text.length != 0) { + if (response_text.length !== 0) { response = JSON.parse(response_text); } } catch (e) { console.error("Parsing error:", e); } - for (var i = 0; i < 9; i++) { - var entry; + const common = new Common(); + for (let i = 0; i < 9; i++) { + let entry; if ( response.length !== 0 && typeof response[i].name !== "undefined" && @@ -92,50 +114,48 @@ function Macro_build_list(response_text) { control_build_macro_ui(); } -const processMacroGetSuccess = (response) => Macro_build_list(response.indexOf("") == -1 ? response : ""); +const processMacroGetSuccess = (response) => Macro_build_list(response.indexOf("") === -1 ? response : ""); function processMacroGetFailed(error_code, response) { conErr(error_code, response); Macro_build_list(""); } -function on_autocheck_position(use_value) { - if (typeof (use_value) !== 'undefined') { - id('autocheck_position').checked = use_value; +const on_autocheck_position = (use_value) => { + if (typeof use_value !== "undefined") { + setChecked("autocheck_position", !!use_value); } - if (id('autocheck_position').checked) { - var interval = parseInt(id('controlpanel_interval_positions').value); + if (getChecked("autocheck_position") !== "false") { + const intPosElem = id("controlpanel_interval_positions"); + const interval = Number.parseInt(intPosElem?.value || undefined); if (!Number.isNaN(interval) && interval > 0 && interval < 100) { - if (interval_position != -1) { - clearInterval(interval_position); - } - interval_position = setInterval(function () { get_Position() }, interval * 1000); + if (interval_position !== -1) clearInterval(interval_position); + interval_position = setInterval(() => { + get_Position(); + }, interval * 1000); } else { - id('autocheck_position').checked = false; - id('controlpanel_interval_positions').value = 0; - if (interval_position != -1) { - clearInterval(interval_position); + setChecked("autocheck_position", false); + if (intPosElem) { + intPosElem.value = 0; } + if (interval_position !== -1) clearInterval(interval_position); interval_position = -1; } } else { - if (interval_position != -1) { - clearInterval(interval_position); - } + if (interval_position !== -1) clearInterval(interval_position); interval_position = -1; } -} +}; function onPosIntervalChange() { - var interval = parseInt(id('controlpanel_interval_positions').value); + const interval = Number.parseInt(id("controlpanel_interval_positions").value); if (!Number.isNaN(interval) && interval > 0 && interval < 100) { on_autocheck_position(); } else { - id('autocheck_position').checked = false; - id('controlpanel_interval_positions').value = 0; - if (interval != 0) { + setChecked("autocheck_position", false); + id("controlpanel_interval_positions").value = 0; + if (interval !== 0) alertdlg(translate_text_item("Out of range"), translate_text_item("Value of auto-check must be between 0s and 99s !!")); - } on_autocheck_position(); } } @@ -143,11 +163,11 @@ function onPosIntervalChange() { const get_Position = () => SendPrinterCommand("?", false, null, null, 114, 1); function Control_get_position_value(label, result_data) { - var result = ""; - var pos1 = result_data.indexOf(label, 0); + let result = ""; + let pos1 = result_data.indexOf(label, 0); if (pos1 > -1) { pos1 += label.length; - var pos2 = result_data.indexOf(" ", pos1); + const pos2 = result_data.indexOf(" ", pos1); if (pos2 > -1) { result = result_data.substring(pos1, pos2); } else result = result_data.substring(pos1); @@ -164,18 +184,20 @@ function control_motorsOff() { } function SendHomecommand(cmd) { - if (id('lock_UI').checked) { + if (getChecked("lock_UI") !== "false") { return; } + const common = new Common(); + let grblCmd = ""; switch (cmd) { - case 'G28': cmd = '$H'; break; - case 'G28 X0': cmd = '$HX'; break; - case 'G28 Y0': cmd = '$HY'; break; - case 'G28 Z0': cmd = (grblaxis > 3) ? `$H${id("control_select_axis").value}` : '$HZ'; break; - default: cmd = '$H'; break; + case "G28": grblCmd = "$H"; break; + case "G28 X0": grblCmd = "$HX"; break; + case "G28 Y0": grblCmd = "$HY"; break; + case "G28 Z0": grblCmd = (common.grblaxis > 3) ? `$H${id("control_select_axis").value}` : "$HZ"; break; + default: grblCmd = "$H"; break; } - SendPrinterCommand(cmd, true, get_Position); + SendPrinterCommand(grblCmd, true, get_Position); } function SendZerocommand(cmd) { @@ -212,16 +234,17 @@ function SendJogcommand(cmd, feedrate) { return; } - const controlName = feedrate.startsWith("Z") ? "controlpanel_z_feedrate" : "controlpanel_xy_feedrate"; - const prefName = feedrate.startsWith("Z") ? "z_feedrate" : "xy_feedrate"; - const valueDef = buildFeedRateValueDef(feedrate); + const controlName = axis.startsWith("Z") ? "controlpanel_z_feedrate" : "controlpanel_xy_feedrate"; + const prefName = axis.startsWith("Z") ? "z_feedrate" : "xy_feedrate"; + const valueDef = buildFeedRateValueDef(axis); let letter = "Z"; - let cmd = ""; - if (grblaxis > 3) { + const common = new Common(); + let aCmd = cmd; + if (common.grblaxis > 3) { letter = "Axis"; valueDef.label = valueDef.label.replace("Z axis", letter); - cmd = cmd.replace("Z", id("control_select_axis").value); + aCmd = cmd.replace("Z", id("control_select_axis").value); } const feedrateValue = id(controlName).value; @@ -230,32 +253,31 @@ function SendJogcommand(cmd, feedrate) { if (errorList.length) { // error text was "(something) Feedrate value must be at least 1 mm/min!" alertdlg(translate_text_item("Out of range"), errorList.join("\n")); - id(controlName).value = feedrate.startsWith("Z") ? preferenceslist[0].z_feedrate : preferenceslist[0].xy_feedrate ; + id(controlName).value = getPrefValue(prefName); return; } - let command = `$J=G91 G21 F${feedrateValue} ${cmd}`; + const command = `$J=G91 G21 F${feedrateValue} ${aCmd}`; console.log(command); SendPrinterCommand(command, true, get_Position); } function onXYvelocityChange() { - var feedratevalue = parseFloat(id('control_xy_velocity').value); - if (feedratevalue < 1 || feedratevalue > 9999 || Number.isNaN(feedratevalue) || (feedratevalue === null)) { + const feedrateValue = Number.parseInt(id("controlpanel_xy_feedrate").value); + if (feedrateValue < 1 || feedrateValue > 9999 || Number.isNaN(feedrateValue) || feedrateValue === null) { //we could display error but we do not } } function onZvelocityChange() { - var feedratevalue = parseFloat(id('control_z_velocity').value); - if (feedratevalue < 1 || feedratevalue > 999 || Number.isNaN(feedratevalue) || (feedratevalue === null)) { + const feedrateValue = Number.parseInt(id("controlpanel_z_feedrate").value); + if (feedrateValue < 1 || feedrateValue > 999 || Number.isNaN(feedrateValue) || feedrateValue === null) { //we could display error but we do not } } - function processMacroSave(answer) { - if (answer == "ok") { + if (answer === "ok") { //console.log("now rebuild list"); control_build_macro_ui(); } @@ -274,9 +296,10 @@ function control_build_macro_button(index, entry) { } function control_build_macro_ui() { + const common = new Common(); const actions = []; - var content = "
"; + let content = "
"; content += "Manage macros" content += ""; content += "
"; - for (var i = 0; i < 9; i++) { - const entry = control_macrolist[i]; + for (let i = 0; i < 9; i++) { + const entry = common.control_macrolist[i]; content += control_build_macro_button(i, entry); - actions.push({ id: `control_macro_${i}`, type: "click", method: (event) => macro_command(entry.target, entry.filename) }); + actions.push({ id: `control_macro_${i}`, type: "click", method: macro_command(entry.target, entry.filename) }); } - id("Macro_list").innerHTML = content; - actions.forEach((action) => { + setHTML("Macro_list", content); + for (const action in actions) { id(action.id).addEventListener(action.type, (event) => action.method); - }); + } } function macro_command(target, filename) { @@ -313,3 +336,12 @@ function macro_command(target, filename) { default: break; // do nothing } } + +export { + ControlsPanel, + get_Position, + init_controls_panel, + JogFeedrate, + on_autocheck_position, + SendJogcommand, +}; diff --git a/www/js/creditsdlg.js b/www/js/creditsdlg.js index f330ce63f..816f0a272 100644 --- a/www/js/creditsdlg.js +++ b/www/js/creditsdlg.js @@ -1,4 +1,4 @@ -// import - closeModal, setactiveModal, showModal, id +import { closeModal, setactiveModal, showModal, id } from "./common.js"; //Credits dialog const creditsdlg = () => { @@ -12,3 +12,5 @@ const creditsdlg = () => { showModal(); }; + +export { creditsdlg }; diff --git a/www/js/dropmenu.js b/www/js/dropmenu.js index c2ce14a7c..1e0f20b28 100644 --- a/www/js/dropmenu.js +++ b/www/js/dropmenu.js @@ -1,36 +1,43 @@ -function clear_drop_menu(event) { - var item = get_parent_by_class(event.target, "dropdownselect"); - var ignore_id = "-1"; - if (item !== null && typeof item.id !== 'undefined') { - ignore_id = item.id; - } - var list = classes("dropmenu-content"); - for (var index = 0; index < list.length; index++) { - var item2 = get_parent_by_class(list[index], "dropdownselect"); - if (item2 !== null && typeof item2.id !== 'undefined' && item2.id != ignore_id && list[index].classList.contains('show')) { - list[index].classList.remove('show'); - } - } -} +import { classes } from "./common.js"; + +const clear_drop_menu = (event) => { + const item = get_parent_by_class(event.target, "dropdownselect"); + const ignore_id = item?.id || "-1"; + const list = classes("dropmenu-content"); + for (let index = 0; index < list.length; index++) { + const item2 = get_parent_by_class(list[index], "dropdownselect"); + if (item2?.id !== ignore_id) { + list[index].classList.remove("show"); + } + } +}; function get_parent_by_class(item, classname) { - if (item === null || typeof item === 'undefined') return null; - if (item.classList.contains(classname)) { - return item; - } - return get_parent_by_class(item.parentElement, classname); + if (item === null || typeof item === "undefined") { + return null; + } + if (item.classList.contains(classname)) { + return item; + } + return get_parent_by_class(item.parentElement, classname); } function hide_drop_menu(event) { - var item = get_parent_by_class(event.target, "dropmenu-content"); - if (typeof item !== 'undefined' && item.classList.contains('show')) { - item.classList.remove('show'); - } + const item = get_parent_by_class(event.target, "dropmenu-content"); + if (item) { + item.classList.remove("show"); + } } -function showhide_drop_menu(event) { - var item = get_parent_by_class(event.target, "dropdownselect"); - if (item === null) return; - var menu = item.getElementsByClassName("dropmenu-content")[0]; - if (typeof menu !== 'undefined') menu.classList.toggle("show"); -} \ No newline at end of file +const showhide_drop_menu = (event) => { + const item = get_parent_by_class(event.target, "dropdownselect"); + if (item === null) { + return; + } + const menu = item.getElementsByClassName("dropmenu-content")[0]; + if (menu) { + menu.classList.toggle("show"); + } +}; + +export { clear_drop_menu, hide_drop_menu, showhide_drop_menu }; diff --git a/www/js/files.js b/www/js/files.js index 4c55d4e2a..e5d10c633 100644 --- a/www/js/files.js +++ b/www/js/files.js @@ -1,4 +1,22 @@ -// import - get_icon_svg, displayBlock, displayInline, displayNone, id, stdErrMsg, setHTML, alertdlg, confirmdlg, inputdlg, SendPrinterCommand, tryAutoReport, SendFileHttp, SendGetHttp, translate_text_item +import { + Monitor_output_Update, + Common, + get_icon_svg, + displayBlock, + displayInline, + displayNone, + id, + stdErrMsg, + setHTML, + alertdlg, + confirmdlg, + inputdlg, + SendPrinterCommand, + tryAutoReport, + SendFileHttp, + SendGetHttp, + translate_text_item, +} from "./common.js"; let files_currentPath = "/"; let files_filter_sd_list = false; @@ -173,6 +191,13 @@ function files_build_file_line(index, actions) { return content; } +function tabletSelectGCodeFile(filename) { + const selector = id("filelist"); + const options = Array.from(selector.options); + const option = options.find((item) => item.text === filename); + option.selected = true; +} + function files_print(index) { const file = files_file_list[index]; const path = files_currentPath + file.name; @@ -351,6 +376,7 @@ function addOption(selector, name, value, isDisabled, isSelected) { } const populateTabletFileSelector = (files, path) => { + const common = new Common(); const selector = id("filelist"); if (!selector) { return; @@ -358,7 +384,7 @@ const populateTabletFileSelector = (files, path) => { selector.length = 0; selector.selectedIndex = 0; - const selectedFile = gCodeFilename.split("/").slice(-1)[0]; + const selectedFile = common.gCodeFilename.split("/").slice(-1)[0]; if (!files.length) { addOption(selector, "No files found", -3, true, selectedFile === ""); @@ -382,7 +408,7 @@ const populateTabletFileSelector = (files, path) => { } }); if (!gCodeFileFound) { - gCodeFilename = ""; + common.gCodeFilename = ""; gCodeDisplayable = false; showGCode(""); } @@ -451,13 +477,15 @@ const files_list_success = (response_text) => { /** Shows an alert dialog for the ESP error, and then clears the ESP error_code */ const alertEspError = () => { - alertdlg(translate_text_item("Error"), stdErrMsg(`(${esp_error_code})`, esp_error_message)); - esp_error_code = 0; + const common = new Common(); + alertdlg(translate_text_item("Error"), stdErrMsg(`(${common.esp_error_code})`, common.esp_error_message)); + common.esp_error_code = 0; }; function files_list_failed(error_code, response) { displayBlock("files_navigation_buttons"); - if (esp_error_code !== 0) { + const common = new Common(); + if (common.esp_error_code !== 0) { alertEspError(); } else { alertdlg(translate_text_item("Error"), translate_text_item("No connection")); @@ -466,7 +494,8 @@ function files_list_failed(error_code, response) { } function files_directSD_upload_failed(error_code, response) { - if (esp_error_code !== 0) { + const common = new Common(); + if (common.esp_error_code !== 0) { alertEspError(); } else { alertdlg(translate_text_item("Error"), translate_text_item("Upload failed")); @@ -603,7 +632,8 @@ function process_check_sd_presence(answer) { } function files_start_upload() { - if (http_communication_locked) { + const common = new Common(); + if (common.http_communication_locked) { alertdlg(translate_text_item("Busy..."), translate_text_item("Communications are currently locked, please wait and retry.")); console.log("communication locked"); return; @@ -650,3 +680,10 @@ function FilesUploadProgressDisplay(oEvent) { // Impossible because size is unknown } } + +export { + build_file_filter_list, + files_list_success, + files_select_upload, + init_files_panel, +}; diff --git a/www/js/grbl.js b/www/js/grbl.js index 77ff03627..cf550a375 100644 --- a/www/js/grbl.js +++ b/www/js/grbl.js @@ -1,579 +1,590 @@ -// When we can change to proper ESM - uncomment this -// import { sendCommand } from "./maslow"; - -var interval_status = -1 -var probe_progress_status = 0 -var grbl_error_msg = '' -var WCO = undefined -var OVR = { feed: undefined, rapid: undefined, spindle: undefined } -var MPOS = [0, 0, 0] -var WPOS = [0, 0, 0] -var grblaxis = 3 -var grblzerocmd = 'X0 Y0 Z0' -var feedrate = [0, 0, 0, 0, 0, 0] -var last_axis_letter = 'Z' - -var axisNames = ['x', 'y', 'z', 'a', 'b', 'c'] - -var modal = { modes: '', plane: 'G17', units: 'G21', wcs: 'G54', distance: 'G90' } - -let calibrationResults = {} +import { + CALIBRATION_EVENT_NAME, + findMaxFitness, + Common, + get_icon_svg, + getChecked, + getValue, + setValue, + id, + setChecked, + setHTML, + alertdlg, + SendPrinterCommand, + translate_text_item, + sendCommand, +} from "./common.js"; + +/** interval timer ID */ +let interval_status = -1; +let probe_progress_status = 0; +let grbl_error_msg = ""; +let WCO = undefined; +let OVR = { feed: undefined, rapid: undefined, spindle: undefined }; +let mpos = [0, 0, 0]; +/** gets/sets MPOS array [x, y, z] */ +const MPOS = (value) => { + if (Array.isArray(value) && value.length === 3) { + mpos = value; + } + return mpos; +}; +let wpos = [0, 0, 0]; +/** gets/sets WPOS array [x, y, z] */ +const WPOS = (value) => { + if (Array.isArray(value) && value.length === 3) { + wpos = value; + } + return wpos; +}; + +var feedrate = [0, 0, 0, 0, 0, 0]; +let last_axis_letter = "Z"; + +const axisNames = ["x", "y", "z", "a", "b", "c"]; function setClickability(element, visible) { - setDisplay(element, visible ? 'table-row' : 'none') + setDisplay(element, visible ? "table-row" : "none"); } -var autocheck = 'report_auto' +const autocheck = "report_auto"; function getAutocheck() { - return getChecked(autocheck) + return getChecked(autocheck) !== "false"; } function setAutocheck(flag) { - setChecked(autocheck, flag) + setChecked(autocheck, flag); } function build_axis_selection() { - var html = ""; + for (let i = 3; i <= common.grblaxis; i++) { + let letter; + let sel = ""; + if (i === 3) { + letter = "Z"; + sel = "selected"; + } else if (i === 4) letter = "A"; + else if (i === 5) letter = "B"; + else if (i === 6) letter = "C"; + html += `\n`; + } + html += "\n"; + + setHTML("axis_selection", html); + id("control_select_axis").addEventListener("change", (event) => + control_changeaxis(), + ); + setHTML("axis_label", `${translate_text_item("Axis")}:`); + setClickability("axis_selection", true); } function control_changeaxis() { - const letter = getValue('control_select_axis') - setHTML('axisup', `+${letter}`) - setHTML('axisdown', `-${letter}`) - setHTML('homeZlabel', ` ${letter} `) - switch (last_axis_letter) { - case 'Z': - axis_feedrate[2] = getValue('control_z_velocity') - break - case 'A': - axis_feedrate[3] = getValue('control_a_velocity') - break - case 'B': - axis_feedrate[4] = getValue('control_b_velocity') - break - case 'C': - axis_feedrate[5] = getValue('control_c_velocity') - break - } - - last_axis_letter = letter - switch (last_axis_letter) { - case 'Z': - setValue('control_z_velocity', axis_feedrate[2]) - break - case 'A': - setValue('control_a_velocity', axis_feedrate[3]) - break - case 'B': - setValue('control_b_velocity', axis_feedrate[4]) - break - case 'C': - setValue('control_c_velocity', axis_feedrate[5]) - break - } + const letter = getValue("control_select_axis"); + setHTML("axisup", `+${letter}`); + setHTML("axisdown", `-${letter}`); + setHTML("homeZlabel", ` ${letter} `); + switch (last_axis_letter) { + case "Z": + axis_feedrate[2] = getValue("z_feedrate"); + break; + case "A": + axis_feedrate[3] = getValue("a_feedrate"); + break; + case "B": + axis_feedrate[4] = getValue("b_feedrate"); + break; + case "C": + axis_feedrate[5] = getValue("c_feedrate"); + break; + } + + last_axis_letter = letter; + switch (last_axis_letter) { + case "Z": + setValue("z_feedrate", axis_feedrate[2]); + break; + case "A": + setValue("a_feedrate", axis_feedrate[3]); + break; + case "B": + setValue("b_feedrate", axis_feedrate[4]); + break; + case "C": + setValue("c_feedrate", axis_feedrate[5]); + break; + } } function init_grbl_panel() { - grbl_set_probe_detected(false) + grbl_set_probe_detected(false); } function grbl_clear_status() { - grbl_set_probe_detected(false) - grbl_error_msg = '' - setHTML('grbl_status_text', grbl_error_msg) - setHTML('grbl_status', '') + grbl_set_probe_detected(false); + grbl_error_msg = ""; + setHTML("grbl_status_text", grbl_error_msg); + setHTML("grbl_status", ""); } function grbl_set_probe_detected(state) { - const color = state ? 'green' : 'grey' - const glyph = state ? 'ok-circle' : 'record' - setHTML('touch_status_icon', get_icon_svg(glyph, '1.3em', '1.3em', color)) -} - -function onprobemaxtravelChange() { - const travel = Number.parseFloat(getValue('grblpanel_probemaxtravel')) - if (travel > 9999 || travel <= 0 || Number.isNaN(travel) || travel === null) { - alertdlg( - translate_text_item('Out of range'), - translate_text_item('Value of maximum probe travel must be between 1 mm and 9999 mm !') - ) - return false - } - return true -} - -function onprobefeedrateChange() { - const feedratevalue = Number.parseInt(getValue('grblpanel_probefeedrate')) - if (feedratevalue <= 0 || feedratevalue > 9999 || Number.isNaN(feedratevalue) || feedratevalue === null) { - alertdlg( - translate_text_item('Out of range'), - translate_text_item('Value of probe feedrate must be between 1 mm/min and 9999 mm/min !') - ) - return false - } - return true -} - -function onproberetractChange() { - const thickness = Number.parseFloat(getValue('grblpanel_proberetract')) - if (thickness < 0 || thickness > 999 || Number.isNaN(thickness) || thickness === null) { - alertdlg( - translate_text_item('Out of range'), - translate_text_item('Value of probe retract must be between 0 mm and 9999 mm !') - ) - return false - } - return true -} - -function onprobetouchplatethicknessChange() { - const thickness = Number.parseFloat(getValue('grblpanel_probetouchplatethickness')) - if (thickness < 0 || thickness > 999 || Number.isNaN(thickness) || thickness === null) { - alertdlg( - translate_text_item('Out of range'), - translate_text_item('Value of probe touch plate thickness must be between 0 mm and 9999 mm !') - ) - return false - } - return true -} - -var reportType = 'none' + const color = state ? "green" : "grey"; + const glyph = state ? "ok-circle" : "record"; + setHTML("touch_status_icon", get_icon_svg(glyph, "1.3em", "1.3em", color)); +} + +const onprobemaxtravelChange = () => { + const travel = Number.parseFloat(getValue("grblpanel_probemaxtravel")); + if (travel > 9999 || travel <= 0 || Number.isNaN(travel) || travel === null) { + alertdlg(translate_text_item("Out of range"), translate_text_item("Value of maximum probe travel must be between 1 mm and 9999 mm !")); + return false; + } + return true; +}; + +const onprobefeedrateChange = () => { + const feedratevalue = Number.parseInt(getValue("grblpanel_probefeedrate")); + if (feedratevalue <= 0 || feedratevalue > 9999 || Number.isNaN(feedratevalue) || feedratevalue === null) { + alertdlg(translate_text_item("Out of range"), translate_text_item("Value of probe feedrate must be between 1 mm/min and 9999 mm/min !")); + return false; + } + return true; +}; + +const onproberetractChange = () => { + const thickness = Number.parseFloat(getValue("grblpanel_proberetract")); + if (thickness < 0 || thickness > 999 || Number.isNaN(thickness) || thickness === null) { + alertdlg(translate_text_item("Out of range"), translate_text_item("Value of probe retract must be between 0 mm and 9999 mm !")); + return false; + } + return true; +}; + +const onprobetouchplatethicknessChange = () => { + const thickness = Number.parseFloat(getValue("grblpanel_probetouchplatethickness")); + if (thickness < 0 || thickness > 999 || Number.isNaN(thickness) || thickness === null) { + alertdlg(translate_text_item("Out of range"), translate_text_item("Value of probe touch plate thickness must be between 0 mm and 9999 mm !")); + return false; + } + return true; +}; + +let reportType = "none"; function disablePolling() { - setAutocheck(false) - // setValue('grblpanel_interval_status', 0); - if (interval_status !== -1) { - clearInterval(interval_status) - interval_status = -1 - } - - grbl_clear_status() - reportType = 'none' + setAutocheck(false); + // setValue('grblpanel_interval_status', 0); + if (interval_status !== -1) { + clearInterval(interval_status); + interval_status = -1; + } + + grbl_clear_status(); + reportType = "none"; } function enablePolling() { - const interval = Number.parseFloat(getValue('grblpanel_interval_status')) - if (!Number.isNaN(interval) && interval === 0) { - if (interval_status !== -1) { - clearInterval(interval_status) - } - disablePolling() - reportNone() - return - } - if (!Number.isNaN(interval) && interval > 0 && interval < 100) { - if (interval_status !== -1) { - clearInterval(interval_status) - } - interval_status = setInterval(() => { get_status() }, interval * 1000) - reportType = 'polled' - setChecked('report_poll', true) - return - } - setValue('grblpanel_interval_status', 0) - alertdlg(translate_text_item('Out of range'), translate_text_item('Value of auto-check must be between 0s and 99s !!')) - disablePolling() - reportNone() + const interval = Number.parseFloat(getValue("grblpanel_interval_status")); + if (!Number.isNaN(interval) && interval === 0) { + if (interval_status !== -1) { + clearInterval(interval_status); + } + disablePolling(); + reportNone(); + return; + } + if (!Number.isNaN(interval) && interval > 0 && interval < 100) { + if (interval_status !== -1) { + clearInterval(interval_status); + } + interval_status = setInterval(() => get_status(), interval * 1000); + reportType = "polled"; + setChecked("report_poll", true); + return; + } + setValue("grblpanel_interval_status", 0); + alertdlg( translate_text_item("Out of range"), translate_text_item("Value of auto-check must be between 0s and 99s !!") ); + disablePolling(); + reportNone(); } function tryAutoReport() { - if (reportType === 'polled') { - disablePolling(); - } - reportType = "auto"; - const interval = id("grblpanel_autoreport_interval").value ?? 0; - if (interval === 0) { - enablePolling(); - return; - } - setChecked("report_auto", true); - reportType = 'auto' - SendPrinterCommand( - `$Report/Interval=${interval}`, - true, - // Do nothing more on success - () => { }, - - // Fall back to polling if the firmware does not support auto-reports - () => { - enablePolling(); - }, - - 99.1, - 1 - ) -} -function onAutoReportIntervalChange() { - tryAutoReport() -} + if (reportType === "polled") { + disablePolling(); + } + reportType === "auto"; + const interval = getValue("grblpanel_autoreport_interval"); + if (interval === 0) { + enablePolling(); + return; + } + setChecked("report_auto", true); + reportType = "auto"; + SendPrinterCommand( + `$Report/Interval=${interval}`, + true, + // Do nothing more on success + () => { }, + // Fall back to polling if the firmware does not support auto-reports + () => enablePolling(), + 99.1, + 1, + ); +} + +const onAutoReportIntervalChange = () => tryAutoReport(); function disableAutoReport() { - SendPrinterCommand('$Report/Interval=0', true, null, null, 99.0, 1) - setChecked('report_auto', false) -} - -function reportNone() { - switch (reportType) { - case 'polled': - disablePolling() - break - case 'auto': - disableAutoReport() - break - } - setChecked('report_none', true) - reportType = 'none' -} - -function reportPolled() { - if (reportType === 'auto') { - disableAutoReport() - } - enablePolling() -} - -function onstatusIntervalChange() { - enablePolling() -} + SendPrinterCommand("$Report/Interval=0", true, null, null, 99.0, 1); + setChecked("report_auto", false); +} + +const reportNone = () => { + switch (reportType) { + case "polled": + disablePolling(); + break; + case "auto": + disableAutoReport(); + break; + } + setChecked("report_none", true); + reportType = "none"; +}; + +const reportPolled = () => { + if (reportType === "auto") { + disableAutoReport(); + } + enablePolling(); +}; + +const onstatusIntervalChange = () => enablePolling(); //TODO handle authentication issues //errorfn cannot be NULL function get_status() { - //ID 114 is same as M114 as '?' cannot be an ID - SendPrinterCommand('?', false, null, null, 114, 1) + //ID 114 is same as M114 as '?' cannot be an ID + SendPrinterCommand("?", false, null, null, 114, 1); } function parseGrblStatus(response) { - var grbl = { - stateName: '', - message: '', - wco: undefined, - mpos: undefined, - wpos: undefined, - feedrate: 0, - spindle: undefined, - spindleSpeed: undefined, - ovr: undefined, - lineNumber: undefined, - flood: undefined, - mist: undefined, - pins: undefined, - } - response = response.replace('<', '').replace('>', '') - var fields = response.split('|') - fields.forEach(function (field) { - var tv = field.split(':') - var tag = tv[0] - var value = tv[1] - switch (tag) { - case 'Door': - grbl.stateName = tag - grbl.message = field - break - case 'Hold': - grbl.stateName = tag - grbl.message = field - break - case 'Run': - case 'Jog': - case 'Idle': - case 'Home': - case 'Alarm': - case 'Check': - case 'Sleep': - grbl.stateName = tag - break - - case 'Ln': - grbl.lineNumber = parseInt(value) - break - case 'MPos': - grbl.mpos = value.split(',').map(function (v) { - return parseFloat(v) - }) - break - case 'WPos': - grbl.wpos = value.split(',').map(function (v) { - return parseFloat(v) - }) - break - case 'WCO': - grbl.wco = value.split(',').map(function (v) { - return parseFloat(v) - }) - break - case 'FS': - var rates = value.split(',') - grbl.feedrate = parseFloat(rates[0]) - grbl.spindleSpeed = parseInt(rates[1]) - break - case 'Ov': - var rates = value.split(',') - grbl.ovr = { - feed: parseInt(rates[0]), - rapid: parseInt(rates[1]), - spindle: parseInt(rates[2]), - } - break - case 'A': - grbl.spindleDirection = 'M5' - Array.from(value).forEach(function (v) { - switch (v) { - case 'S': - grbl.spindleDirection = 'M3' - break - case 'C': - grbl.spindleDirection = 'M4' - break - case 'F': - grbl.flood = true - break - case 'M': - grbl.mist = true - break - } - }) - break - case 'SD': - var sdinfo = value.split(',') - grbl.sdPercent = parseFloat(sdinfo[0]) - grbl.sdName = sdinfo[1] - break - case 'Pn': - // pin status - grbl.pins = value - break - default: - // ignore other fields that might happen to be present - break - } - }) - return grbl + const grbl = { + stateName: "", + message: "", + wco: undefined, + mpos: undefined, + wpos: undefined, + feedrate: 0, + spindle: undefined, + spindleSpeed: undefined, + ovr: undefined, + lineNumber: undefined, + flood: undefined, + mist: undefined, + pins: undefined, + }; + response = response.replace("<", "").replace(">", ""); + const fields = response.split("|"); + for (const field in fields) { + const tv = field.split(":"); + const tag = tv[0]; + const value = tv[1]; + switch (tag) { + case "Door": + grbl.stateName = tag; + grbl.message = field; + break; + case "Hold": + grbl.stateName = tag; + grbl.message = field; + break; + case "Run": + case "Jog": + case "Idle": + case "Home": + case "Alarm": + case "Check": + case "Sleep": + grbl.stateName = tag; + break; + + case "Ln": + grbl.lineNumber = Number.parseInt(value); + break; + case "MPos": + grbl.mpos = value.split(",").map((v) => Number.parseFloat(v)); + break; + case "WPos": + grbl.wpos = value.split(",").map((v) => Number.parseFloat(v)); + break; + case "WCO": + grbl.wco = value.split(",").map((v) => Number.parseFloat(v)); + break; + case "FS": { + const rates = value.split(","); + grbl.feedrate = Number.parseFloat(rates[0]); + grbl.spindleSpeed = Number.parseInt(rates[1]); + break; + } + case "Ov": { + const rates = value.split(","); + grbl.ovr = { + feed: Number.parseInt(rates[0]), + rapid: Number.parseInt(rates[1]), + spindle: Number.parseInt(rates[2]), + }; + break; + } + case "A": + grbl.spindleDirection = "M5"; + for (const v in value) { + switch (v) { + case "S": + grbl.spindleDirection = "M3"; + break; + case "C": + grbl.spindleDirection = "M4"; + break; + case "F": + grbl.flood = true; + break; + case "M": + grbl.mist = true; + break; + } + }; + break; + case "SD": { + const sdinfo = value.split(","); + grbl.sdPercent = Number.parseFloat(sdinfo[0]); + grbl.sdName = sdinfo[1]; + break; + } + case "Pn": + // pin status + grbl.pins = value; + break; + default: + // ignore other fields that might happen to be present + break; + } + }; + return grbl; } function clickableFromStateName(state, hasSD) { - var clickable = { - resume: false, - pause: false, - reset: false, - } - switch (state) { - case 'Run': - clickable.pause = true - clickable.reset = true - break - case 'Hold': - clickable.resume = true - clickable.reset = true - break - case 'Alarm': - if (hasSD) { - //guess print is stopped because of alarm so no need to pause - clickable.resume = true - } - break - case 'Idle': - case 'Jog': - case 'Home': - case 'Check': - case 'Sleep': - break - } - return clickable + const clickable = { + resume: false, + pause: false, + reset: false, + }; + switch (state) { + case "Run": + clickable.pause = true; + clickable.reset = true; + break; + case "Hold": + clickable.resume = true; + clickable.reset = true; + break; + case "Alarm": + if (hasSD) { + //guess print is stopped because of alarm so no need to pause + clickable.resume = true; + } + break; + case "Idle": + case "Jog": + case "Home": + case "Check": + case "Sleep": + break; + } + return clickable; } function show_grbl_position(wpos, mpos) { - if (wpos) { - wpos.forEach(function (pos, axis) { - var element = 'control_' + axisNames[axis] + '_position' - setHTML(element, pos.toFixed(3)) - }) - } - if (mpos) { - mpos.forEach(function (pos, axis) { - var element = 'control_' + axisNames[axis] + 'm_position' - setHTML(element, pos.toFixed(3)) - }) - } + if (wpos) { + wpos.forEach((pos, axis) => { + const element = `control_${axisNames[axis]}_position`; + setHTML(element, pos.toFixed(3)); + }); + } + if (mpos) { + mpos.forEach((pos, axis) => { + const element = `control_${axisNames[axis]}m_position`; + setHTML(element, pos.toFixed(3)); + }); + } } function show_grbl_status(stateName, message, hasSD) { - if (stateName) { - var clickable = clickableFromStateName(stateName, hasSD) - setHTML('grbl_status', stateName) - setHTML('systemStatus', stateName) - if (stateName === 'Alarm') { - id('systemStatus').classList.add('system-status-alarm') - } else { - id('systemStatus').classList.remove('system-status-alarm') - } - setClickability('sd_resume_btn', clickable.resume) - setClickability('sd_pause_btn', clickable.pause) - setClickability('sd_reset_btn', clickable.reset) - if (stateName == 'Hold' && probe_progress_status != 0) { - probe_failed_notification() - } - } - - setHTML('grbl_status_text', translate_text_item(message)) - setClickability('clear_status_btn', stateName == 'Alarm') + if (stateName) { + const clickable = clickableFromStateName(stateName, hasSD); + setHTML("grbl_status", stateName); + setHTML("systemStatus", stateName); + if (stateName === "Alarm") { + id("systemStatus").classList.add("system-status-alarm"); + } else { + id("systemStatus").classList.remove("system-status-alarm"); + } + setClickability("sd_resume_btn", clickable.resume); + setClickability("sd_pause_btn", clickable.pause); + setClickability("sd_reset_btn", clickable.reset); + if (stateName === "Hold" && probe_progress_status !== 0) { + probe_failed_notification(); + } + } + + setHTML("grbl_status_text", translate_text_item(message)); + setClickability("clear_status_btn", stateName === "Alarm"); } function finalize_probing() { - // No need for this when using the FluidNC-specific G38.6 probe command. - // SendPrinterCommand("G90", true, null, null, 90, 1); - probe_progress_status = 0 - setClickability('probingbtn', true) - setClickability('probingtext', false) - setClickability('sd_pause_btn', false) - setClickability('sd_resume_btn', false) - setClickability('sd_reset_btn', false) + // No need for this when using the FluidNC-specific G38.6 probe command. + // SendPrinterCommand("G90", true, null, null, 90, 1); + probe_progress_status = 0; + setClickability("probingbtn", true); + setClickability("probingtext", false); + setClickability("sd_pause_btn", false); + setClickability("sd_resume_btn", false); + setClickability("sd_reset_btn", false); } function show_grbl_SD(sdName, sdPercent) { - const status = sdName - ? `${sdName} ${sdPercent}%` - : '' - setHTML('grbl_SD_status', status) + const status = sdName + ? `${sdName} ${sdPercent}%` + : ""; + setHTML("grbl_SD_status", status); } function show_grbl_probe_status(probed) { - grbl_set_probe_detected(probed) + grbl_set_probe_detected(probed); } -function SendRealtimeCmd(code) { - var cmd = String.fromCharCode(code) - SendPrinterCommand(cmd, false, null, null, code, 1) -} +const SendRealtimeCmd = (code) => { + const cmd = String.fromCharCode(code); + SendPrinterCommand(cmd, false, null, null, code, 1); +}; function pauseGCode() { - SendRealtimeCmd(0x21) // '!' + SendRealtimeCmd(0x21); // '!' } function resumeGCode() { - SendRealtimeCmd(0x7e) // '~' + SendRealtimeCmd(0x7e); // '~' } function stopGCode() { - grbl_reset() // 0x18, ctrl-x + grbl_reset(); // 0x18, ctrl-x } function grblProcessStatus(response) { - var grbl = parseGrblStatus(response) - // Record persistent values of data - if (grbl.wco) { - WCO = grbl.wco - } - if (grbl.ovr) { - OVR = grbl.ovr - } - if (grbl.mpos) { - MPOS = grbl.mpos - if (WCO) { - WPOS = grbl.mpos.map((v, index) => v - WCO[index]) - } - } else if (grbl.wpos) { - WPOS = grbl.wpos - if (WCO) { - MPOS = grbl.wpos.map((v, index) => v + WCO[index]) - } - } - show_grbl_position(WPOS, MPOS) - show_grbl_status(grbl.stateName, grbl.message, grbl.sdName) - show_grbl_SD(grbl.sdName, grbl.sdPercent) - show_grbl_probe_status(grbl.pins && grbl.pins.indexOf('P') !== -1) - tabletGrblState(grbl, response) -} - -function grbl_reset() { - if (probe_progress_status !== 0) probe_failed_notification() - SendRealtimeCmd(0x18) -} + const grbl = parseGrblStatus(response); + // Record persistent values of data + if (grbl.wco) { + WCO = grbl.wco; + } + if (grbl.ovr) { + OVR = grbl.ovr; + } + if (grbl.mpos) { + MPOS(grbl.mpos); + if (WCO) { + WPOS(grbl.mpos.map((v, index) => v - WCO[index])); + } + } else if (grbl.wpos) { + WPOS(grbl.wpos); + if (WCO) { + MPOS(grbl.wpos.map((v, index) => v + WCO[index])); + } + } + show_grbl_position(WPOS(), MPOS()); + show_grbl_status(grbl.stateName, grbl.message, grbl.sdName); + show_grbl_SD(grbl.sdName, grbl.sdPercent); + show_grbl_probe_status(grbl.pins && grbl.pins.indexOf("P") !== -1); + tabletGrblState(grbl, response); +} + +const grbl_reset = () => { + if (probe_progress_status !== 0) { + probe_failed_notification(); + } + SendRealtimeCmd(0x18); +}; function grblGetProbeResult(response) { - const tab1 = response.split(':') - if (tab1.length > 2) { - const status = tab1[2].replace(']', '') - if (Number.parseInt(status.trim()) === 1) { - if (probe_progress_status !== 0) { - const cmd = - `$J=G90 G21 F1000 Z${Number.parseFloat(getValue('probetouchplatethickness')) + Number.parseFloat(getValue('grblpanel_proberetract'))}` - SendPrinterCommand(cmd, true, null, null, 0, 1) - finalize_probing() - } - } else { - probe_failed_notification() - } - } + const tab1 = response.split(":"); + if (tab1.length > 2) { + const status = tab1[2].replace("]", ""); + if (Number.parseInt(status.trim()) === 1) { + if (probe_progress_status !== 0) { + const cmd = `$J=G90 G21 F1000 Z${Number.parseFloat(getValue("grblpanel_probetouchplatethickness")) + Number.parseFloat(getValue("grblpanel_proberetract"))}`; + SendPrinterCommand(cmd, true, null, null, 0, 1); + finalize_probing(); + } + } else { + probe_failed_notification(); + } + } } function probe_failed_notification() { - finalize_probing() - alertdlg(translate_text_item('Error'), translate_text_item('Probe failed !')) - beep(3, 140, 261) + finalize_probing(); + alertdlg(translate_text_item("Error"), translate_text_item("Probe failed !")); + beep(3, 140, 261); } const modalModes = [ - { name: 'motion', values: ['G80', 'G0', 'G1', 'G2', 'G3', 'G38.1', 'G38.2', 'G38.3', 'G38.4'] }, - { name: 'wcs', values: ['G54', 'G55', 'G56', 'G57', 'G58', 'G59'] }, - { name: 'plane', values: ['G17', 'G18', 'G19'] }, - { name: 'units', values: ['G20', 'G21'] }, - { name: 'distance', values: ['G90', 'G91'] }, - { name: 'arc_distance', values: ['G90.1', 'G91.1'] }, - { name: 'feed', values: ['G93', 'G94'] }, - { name: 'program', values: ['M0', 'M1', 'M2', 'M30'] }, - { name: 'spindle', values: ['M3', 'M4', 'M5'] }, - { name: 'mist', values: ['M7'] }, // Also M9, handled separately - { name: 'flood', values: ['M8'] }, // Also M9, handled separately - { name: 'parking', values: ['M56'] }, -] + { + name: "motion", + values: ["G80", "G0", "G1", "G2", "G3", "G38.1", "G38.2", "G38.3", "G38.4"], + }, + { name: "wcs", values: ["G54", "G55", "G56", "G57", "G58", "G59"] }, + { name: "plane", values: ["G17", "G18", "G19"] }, + { name: "units", values: ["G20", "G21"] }, + { name: "distance", values: ["G90", "G91"] }, + { name: "arc_distance", values: ["G90.1", "G91.1"] }, + { name: "feed", values: ["G93", "G94"] }, + { name: "program", values: ["M0", "M1", "M2", "M30"] }, + { name: "spindle", values: ["M3", "M4", "M5"] }, + { name: "mist", values: ["M7"] }, // Also M9, handled separately + { name: "flood", values: ["M8"] }, // Also M9, handled separately + { name: "parking", values: ["M56"] }, +]; function grblGetModal(msg) { - modal.modes = msg.replace('[GC:', '').replace(']', '') - var modes = modal.modes.split(' ') - modal.parking = undefined // Otherwise there is no way to turn it off - modal.program = '' // Otherwise there is no way to turn it off - modes.forEach(function (mode) { - if (mode == 'M9') { - modal.flood = mode - modal.mist = mode - } else { - if (mode.charAt(0) === 'T') { - modal.tool = mode.substring(1) - } else if (mode.charAt(0) === 'F') { - modal.feedrate = mode.substring(1) - } else if (mode.charAt(0) === 'S') { - modal.spindle = mode.substring(1) - } else { - modalModes.forEach(function (modeType) { - modeType.values.forEach(function (s) { - if (mode == s) { - modal[modeType.name] = mode - } - }) - }) - } - } - }) - tabletUpdateModal() + modal("modes", msg.replace("[GC:", "").replace("]", "")); + const modes = modal("modes").split(" "); + Modal.parking = undefined; // Otherwise there is no way to turn it off + modal("program", ""); // Otherwise there is no way to turn it off + for(const mode in modes) { + if (mode === "M9") { + modal("flood", mode); + modal("mist", mode); + } else { + if (mode.charAt(0) === "T") { + modal("tool", mode.substring(1)); + } else if (mode.charAt(0) === "F") { + modal("feedrate", mode.substring(1)); + } else if (mode.charAt(0) === "S") { + modal("spindle", mode.substring(1)); + } else { + for (const modeType in modalModes) { + for (const s in modeType.values) { + if (mode === s) { + modal(modeType.name, mode); + } + }; + }; + } + } + }; + tabletUpdateModal(); } // Whenever [MSG: BeginData] is seen, subsequent lines are collected @@ -581,177 +592,219 @@ function grblGetModal(msg) { // is called, if it is defined. // To run a command that generates such data, first set collectHandler // to a callback function to receive the data, then issue the command. -var collecting = false -var collectedData = '' -var collectHandler = undefined +let collecting = false; +let collectedData = ""; +let collectHandler = undefined; // Settings are collected separately because they bracket the data with // the legacy protocol messages $0= ... ok -var collectedSettings = null +let collectedSettings = null; async function handleCalibrationData(measurements) { - document.querySelector('#messages').textContent += '\nComputing... This may take several minutes' - sendCommand("$ACKCAL"); - await sleep(500) - try { - calibrationResults = await findMaxFitness(measurements) - } catch (error) { - console.error('An error occurred:', error) - } -} - -function grblHandleMessage(msg) { - tabletShowMessage(msg, collecting) - - // We handle these two before collecting data because they can be - // sent at any time, maybe requested by a timer. - - if (msg.startsWith('CLBM:')) { - const validJsonMSG = msg - .replace(/(\b(?:bl|br|tr|tl)\b):/g, '"$1":') - .replace('CLBM:', '') - .replace(/,]$/, ']') - const measurements = JSON.parse(validJsonMSG) - handleCalibrationData(measurements) - } - if (msg.startsWith('<')) { - grblProcessStatus(msg) - return - } - if (msg.startsWith('[GC:')) { - grblGetModal(msg) - console.log(msg) - return - } - - // Block data collection - if (collecting) { - if (msg.startsWith('[MSG: EndData]')) { - collecting = false - // Finish collecting data - if (collectHandler) { - collectHandler(collectedData) - collectHandler = undefined - } - collectedData = '' - } else { - // Continue collecting data - collectedData += msg - } - return - } - if (msg.startsWith('[MSG: BeginData]')) { - // Start collecting data - collectedData = '' - collecting = true - return - } - - // Setting collection - if (collectedSettings) { - if (msg.startsWith('ok')) { - // Finish collecting settings - getESPconfigSuccess(collectedSettings) - collectedSettings = null - if (grbl_errorfn) { - grbl_errorfn() - grbl_errorfn = null - grbl_processfn = null - } - } else { - // Continue collecting settings - collectedSettings += msg - } - return - } - if (msg.startsWith('$0=') || msg.startsWith('$10=')) { - // Start collecting settings - collectedSettings = msg - return - } - - // Handlers for standard Grbl protocol messages - - if (msg.startsWith('ok')) { - if (grbl_processfn) { - grbl_processfn() - grbl_processfn = null - grbl_errorfn = null - } - return - } - if (msg.startsWith('[PRB:')) { - grblGetProbeResult(msg) - return - } - if (msg.startsWith('[MSG:')) { - return - } - if (msg.startsWith('error:')) { - if (grbl_errorfn) { - grbl_errorfn() - grbl_errorfn = null - grbl_processfn = null - } - } - if (msg.startsWith('error:') || msg.startsWith('ALARM:') || msg.startsWith('Hold:') || msg.startsWith('Door:')) { - if (probe_progress_status !== 0) { - probe_failed_notification() - } - if (grbl_error_msg.length === 0) { - grbl_error_msg = translate_text_item(msg.trim()) - } - return - } - if (msg.startsWith('Grbl ')) { - console.log('Reset detected') - return - } -} - -function StartProbeProcess() { - // G38.6 is FluidNC-specific. It is like G38.2 except that the units - // are always G21 units, i.e. mm in the usual case, and distance is - // always incremental. This avoids problems with probing when in G20 - // inches mode and undoing a preexisting G91 incremental mode - var cmd = 'G38.2 Z-' - if ( - !onprobemaxtravelChange() || - !onprobefeedrateChange() || - !onproberetractChange() || - !onprobetouchplatethicknessChange() - ) { - return - } - cmd += - `${Number.parseFloat(getValue('grblpanel_probemaxtravel'))} F${Number.parseInt(getValue('grblpanel_probefeedrate'))} P${getValue('grblpanel_probetouchplatethickness')}` - console.log(cmd) - probe_progress_status = 1 - let restoreReport = false - if (reportType === 'none') { - tryAutoReport() // will fall back to polled if autoreport fails - restoreReport = true - } - SendPrinterCommand(cmd, true, null, null, 38.6, 1) - setClickability('probingbtn', false) - setClickability('probingtext', true) - grbl_error_msg = '' - setHTML('grbl_status_text', grbl_error_msg) - if (restoreReport) { - reportNone() - } -} - -var spindleSpeedSetTimeout -var spindleTabSpindleSpeed = 1 + document.body.addEventListener(CALIBRATION_EVENT_NAME, (event) => { + const calData = event.detail.dataToSend; + const common = new Common(); + console.info( + `Received calibration results that were ${calData.good ? "good" : "not good"} and ${calData.final ? "final" : "not final"}`, + ); + if (calData.good && calData.final) { + common.calibrationResults = calData.bestGuess; + } + }); + + document.querySelector("#messages").textContent += + "\nComputing... This may take several minutes"; + sendCommand("$ACKCAL"); + + // Wait half a second and then kick off the party + setTimeout(() => findMaxFitness(measurements), 500); +} + +const grblHandleMessage = (msg) => { + tabletShowMessage(msg, collecting); + + // We handle these two before collecting data because they can be + // sent at any time, maybe requested by a timer. + + if (msg.startsWith("CLBM:")) { + const validJsonMSG = msg + .replace(/(\b(?:bl|br|tr|tl)\b):/g, '"$1":') + .replace("CLBM:", "") + .replace(/,]$/, "]"); + const measurements = JSON.parse(validJsonMSG); + handleCalibrationData(measurements); + } + if (msg.startsWith("<")) { + grblProcessStatus(msg); + return; + } + if (msg.startsWith("[GC:")) { + grblGetModal(msg); + console.log(msg); + return; + } + + // Block data collection + if (collecting) { + if (msg.startsWith("[MSG: EndData]")) { + collecting = false; + // Finish collecting data + if (collectHandler) { + collectHandler(collectedData); + collectHandler = undefined; + } + collectedData = ""; + } else { + // Continue collecting data + collectedData += msg; + } + return; + } + if (msg.startsWith("[MSG: BeginData]")) { + // Start collecting data + collectedData = ""; + collecting = true; + return; + } + + // Setting collection + if (collectedSettings) { + if (msg.startsWith("ok")) { + // Finish collecting settings + getESPconfigSuccess(collectedSettings); + collectedSettings = null; + if (grbl_errorfn) { + grbl_errorfn(); + grbl_errorfn = null; + grbl_processfn = null; + } + } else { + // Continue collecting settings + collectedSettings += msg; + } + return; + } + if (msg.startsWith("$0=") || msg.startsWith("$10=")) { + // Start collecting settings + collectedSettings = msg; + return; + } + + // Handlers for standard Grbl protocol messages + + if (msg.startsWith("ok")) { + if (grbl_processfn) { + grbl_processfn(); + grbl_processfn = null; + grbl_errorfn = null; + } + return; + } + if (msg.startsWith("[PRB:")) { + grblGetProbeResult(msg); + return; + } + if (msg.startsWith("[MSG:")) { + return; + } + if (msg.startsWith("error:")) { + if (grbl_errorfn) { + grbl_errorfn(); + grbl_errorfn = null; + grbl_processfn = null; + } + } + if ( + msg.startsWith("error:") || + msg.startsWith("ALARM:") || + msg.startsWith("Hold:") || + msg.startsWith("Door:") + ) { + if (probe_progress_status !== 0) { + probe_failed_notification(); + } + if (grbl_error_msg.length === 0) { + grbl_error_msg = translate_text_item(msg.trim()); + } + return; + } + if (msg.startsWith("Grbl ")) { + console.log("Reset detected"); + return; + } +}; + +const StartProbeProcess = () => { + // G38.6 is FluidNC-specific. It is like G38.2 except that the units + // are always G21 units, i.e. mm in the usual case, and distance is + // always incremental. This avoids problems with probing when in G20 + // inches mode and undoing a preexisting G91 incremental mode + let cmd = "G38.2 Z-"; + if ( + !onprobemaxtravelChange() || + !onprobefeedrateChange() || + !onproberetractChange() || + !onprobetouchplatethicknessChange() + ) { + return; + } + cmd += `${Number.parseFloat(getValue("grblpanel_probemaxtravel"))} F${Number.parseInt(getValue("grblpanel_probefeedrate"))} P${getValue("grblpanel_probetouchplatethickness")}`; + console.log(cmd); + probe_progress_status = 1; + let restoreReport = false; + if (reportType === "none") { + tryAutoReport(); // will fall back to polled if autoreport fails + restoreReport = true; + } + SendPrinterCommand(cmd, true, null, null, 38.6, 1); + setClickability("probingbtn", false); + setClickability("probingtext", true); + grbl_error_msg = ""; + setHTML("grbl_status_text", grbl_error_msg); + if (restoreReport) { + reportNone(); + } +}; + +let spindleSpeedSetTimeout; function setSpindleSpeed(speed) { - if (spindleSpeedSetTimeout) clearTimeout(spindleSpeedSetTimeout) - if (speed >= 1) { - spindleTabSpindleSpeed = speed - spindleSpeedSetTimeout = setTimeout( - () => SendPrinterCommand('S' + spindleTabSpindleSpeed, false, null, null, 1, 1), - 500 - ) - } -} + const common = new Common(); + if (spindleSpeedSetTimeout) { + clearTimeout(spindleSpeedSetTimeout); + } + if (speed >= 1) { + common.spindleTabSpindleSpeed = speed; + spindleSpeedSetTimeout = setTimeout( + () => + SendPrinterCommand( + `S${common.spindleTabSpindleSpeed}`, + false, + null, + null, + 1, + 1, + ), + 500, + ); + } +} + +export { + build_axis_selection, + grblHandleMessage, + grbl_reset, + onAutoReportIntervalChange, + onstatusIntervalChange, + onprobemaxtravelChange, + onprobefeedrateChange, + onproberetractChange, + onprobetouchplatethicknessChange, + reportNone, + tryAutoReport, + reportPolled, + SendRealtimeCmd, + StartProbeProcess, + MPOS, + WPOS, +}; diff --git a/www/js/grblpanel.js b/www/js/grblpanel.js index 65b1fe1b9..46a461dbe 100644 --- a/www/js/grblpanel.js +++ b/www/js/grblpanel.js @@ -1,13 +1,32 @@ -// import - id, opentab, SendPrinterCommand, grbl_reset, reportNone, tryAutoReport, reportPolled, onAutoReportIntervalChange, onstatusIntervalChange, onprobemaxtravelChange, onprobefeedrateChange, onproberetractChange, onprobetouchplatethicknessChange, SendRealtimeCmd, StartProbeProcess +import { + Common, + id, + opentab, + SendPrinterCommand, + grbl_reset, + reportNone, + tryAutoReport, + reportPolled, + onAutoReportIntervalChange, + onstatusIntervalChange, + onprobemaxtravelChange, + onprobefeedrateChange, + onproberetractChange, + onprobetouchplatethicknessChange, + SendRealtimeCmd, + StartProbeProcess, +} from "./common.js"; /** Set up the event handlers for the grblpanel */ const grblpanel = () => { + const common = new Common(); + // GRBL reporting id("report_none").addEventListener("change", (event) => onReportType(event)); - id("report_auto").addEventListener("change", (event) => onReportType(event)); - id("grblpanel_autoreport_interval").addEventListener("change", (event) => onAutoReportIntervalChange()); - id("report_poll").addEventListener("change", (event) => onReportType(event)); - id("grblpanel_interval_status").addEventListener("change", (event) => onstatusIntervalChange()); + id("report_auto").addEventListener("change", (event) => onReportType(event)); + id("grblpanel_autoreport_interval").addEventListener("change", (event) => onAutoReportIntervalChange()); + id("report_poll").addEventListener("change", (event) => onReportType(event)); + id("grblpanel_interval_status").addEventListener("change", (event) => onstatusIntervalChange()); id("clear_status_btn").addEventListener("click", (event) => SendPrinterCommand("$X", true, null, null, 114, 1)); id("sd_pause_btn").addEventListener("click", (event) => SendRealtimeCmd(0x21)); @@ -41,25 +60,27 @@ const grblpanel = () => { id("grblpanel_proberetract").addEventListener("change", (event) => onproberetractChange()); id("grblpanel_probetouchplatethickness").addEventListener("change", (event) => onprobetouchplatethicknessChange()); - id("probingbtn").addEventListener("click", (event) => StartProbeProcess()); + id("probingbtn").addEventListener("click", (event) => StartProbeProcess()); id("grblcontroltablink").addEventListener("click", (event) => opentab(event, "grblcontroltab", "grbluitabscontent", "grbluitablinks")); id("grblspindletablink").addEventListener("click", (event) => opentab(event, "grblspindletab", "grbluitabscontent", "grbluitablinks")); id("grblpanel_probetablink").addEventListener("click", (event) => opentab(event, "grblprobetab", "grbluitabscontent", "grbluitablinks")); - id("global_reset_btn").addEventListener("click", (event) => grbl_reset()); + id("global_reset_btn").addEventListener("click", (event) => grbl_reset()); }; const onReportType = (e) => { - switch (e.value) { - case "none": - reportNone(); - break; - case "auto": - tryAutoReport(); - break; - case "poll": - reportPolled(); - break; - } + switch (e.value) { + case "none": + reportNone(); + break; + case "auto": + tryAutoReport(); + break; + case "poll": + reportPolled(); + break; + } }; + +export { grblpanel }; diff --git a/www/js/http.js b/www/js/http.js index 410bb898a..7e64d0e0f 100644 --- a/www/js/http.js +++ b/www/js/http.js @@ -1,259 +1,302 @@ -var http_communication_locked = false; -var http_cmd_list = []; -var processing_cmd = false; -var xmlhttpupload; -var page_id = "" +import { Common, setHTML, translate_text_item, logindlg, pageID } from "./common.js"; -var max_cmd = 20; +let http_cmd_list = []; +let processing_cmd = false; +let xmlhttpupload; -function clear_cmd_list() { - http_cmd_list = []; - processing_cmd = false; -} +const max_cmd = 20; + +const clear_cmd_list = () => { + http_cmd_list = []; + processing_cmd = false; +}; function http_resultfn(response_text) { - if ((http_cmd_list.length > 0) && (typeof http_cmd_list[0].resultfn != 'undefined')) { - var fn = http_cmd_list[0].resultfn; - fn(response_text); - } //else console.log ("No resultfn"); - http_cmd_list.shift(); - processing_cmd = false; - process_cmd(); + if ( + http_cmd_list.length > 0 && + typeof http_cmd_list[0].resultfn !== "undefined" + ) { + const fn = http_cmd_list[0].resultfn; + fn(response_text); + } //else console.log ("No resultfn"); + http_cmd_list.shift(); + processing_cmd = false; + process_cmd(); } function http_errorfn(error_code, response_text) { - var fn = http_cmd_list[0].errorfn; - if ((http_cmd_list.length > 0) && (typeof http_cmd_list[0].errorfn != 'undefined') && http_cmd_list[0].errorfn != null) { - if (error_code == 401) { - logindlg(); - console.log("Authentication issue pls log"); - } - http_cmd_list[0].errorfn(error_code, response_text); - } //else console.log ("No errorfn"); - http_cmd_list.shift(); - processing_cmd = false; - process_cmd(); + const fn = http_cmd_list[0].errorfn; + if ( + http_cmd_list.length > 0 && + typeof http_cmd_list[0].errorfn !== "undefined" && + http_cmd_list[0].errorfn != null + ) { + if (error_code === 401) { + logindlg(); + console.log("Authentication issue pls log"); + } + http_cmd_list[0].errorfn(error_code, response_text); + } //else console.log ("No errorfn"); + http_cmd_list.shift(); + processing_cmd = false; + process_cmd(); } function process_cmd() { - if (!http_cmd_list.length || processing_cmd) { - // if (processing_cmd) { - // console.log("Currently processing a command"); - // } - return; - } + if (!http_cmd_list.length || processing_cmd) { + // if (processing_cmd) { + // console.log("Currently processing a command"); + // } + return; + } - const cmdType = http_cmd_list[0].type; - if (!["GET", "POST", "CMD"].includes(cmdType)) { - console.error(`Unknown command type ${cmdType} for command ${http_cmd_list[0].cmd}`); - // This should never be true, but just in case we'll deliberately set it to false - processing_cmd = false; - return; - } - // console.log("Processing 1/" + http_cmd_list.length); - // console.log("Processing " + http_cmd_list[0].cmd); - processing_cmd = true; - switch (cmdType) { - case "GET": - ProcessGetHttp(http_cmd_list[0].cmd, http_resultfn, http_errorfn); - break; - case "POST": - if (!(http_cmd_list[0].isupload)) { - ProcessPostHttp(http_cmd_list[0].cmd, http_cmd_list[0].data, http_resultfn, http_errorfn); - } else { - //console.log("Uploading"); - ProcessFileHttp(http_cmd_list[0].cmd, http_cmd_list[0].data, http_cmd_list[0].progressfn, http_resultfn, http_errorfn); - } - break; - case "CMD": - var fn = http_cmd_list[0].cmd; - fn(); - http_cmd_list.shift(); - processing_cmd = false; - process_cmd(); - break; - } + const cmdType = http_cmd_list[0].type; + if (!["GET", "POST", "CMD"].includes(cmdType)) { + console.error(`Unknown command type ${cmdType} for command ${http_cmd_list[0].cmd}`); + // This should never be true, but just in case we'll deliberately set it to false + processing_cmd = false; + return; + } + // console.log("Processing 1/" + http_cmd_list.length); + // console.log("Processing " + http_cmd_list[0].cmd); + processing_cmd = true; + switch (cmdType) { + case "GET": + ProcessGetHttp(http_cmd_list[0].cmd, http_resultfn, http_errorfn); + break; + case "POST": + if (!(http_cmd_list[0].isupload)) { + ProcessPostHttp(http_cmd_list[0].cmd, http_cmd_list[0].data, http_resultfn, http_errorfn); + } else { + //console.log("Uploading"); + ProcessFileHttp(http_cmd_list[0].cmd, http_cmd_list[0].data, http_cmd_list[0].progressfn, http_resultfn, http_errorfn); + } + break; + case "CMD": { + const fn = http_cmd_list[0].cmd; + fn(); + http_cmd_list.shift(); + processing_cmd = false; + process_cmd(); + break; + } + } } function AddCmd(cmd_fn, id) { - if (http_cmd_list.length > max_cmd) { - http_errorfn(999, translate_text_item("Server not responding")); - return; - } - const cmd_id = (typeof id !== 'undefined') ? id : 0; - //console.log("adding command"); - var cmd = { - cmd: cmd_fn, - type: "CMD", - id: cmd_id - }; - http_cmd_list.push(cmd); - //console.log("Now " + http_cmd_list.length); - process_cmd(); + if (http_cmd_list.length > max_cmd) { + http_errorfn(999, translate_text_item("Server not responding")); + return; + } + let cmd_id = 0; + if (typeof id !== "undefined") cmd_id = id; + //console.log("adding command"); + const cmd = { + cmd: cmd_fn, + type: "CMD", + id: cmd_id, + }; + http_cmd_list.push(cmd); + //console.log("Now " + http_cmd_list.length); + process_cmd(); +} + +function GetIdentificationStatus() { + const url = "/login"; + SendGetHttp(url, GetIdentificationStatusSuccess); } -function SendGetHttp(url, result_fn, error_fn, id, max_id) { - if ((http_cmd_list.length > max_cmd) && (max_cmd != -1)) { - error_fn(999, translate_text_item("Server not responding")); - return; - } - var cmd_id = 0; - var cmd_max_id = 1; - //console.log("ID = " + id); - //console.log("Max ID = " + max_id); - //console.log("+++ " + url); - if (typeof id != 'undefined') { - cmd_id = id; - if (typeof max_id != 'undefined') cmd_max_id = max_id; - //else console.log("No Max ID defined"); - for (p = 0; p < http_cmd_list.length; p++) { - //console.log("compare " + (max_id - cmd_max_id)); - if (http_cmd_list[p].id == cmd_id) { - cmd_max_id--; - //console.log("found " + http_cmd_list[p].id + " and " + cmd_id); - } - if (cmd_max_id <= 0) { - console.log("Limit reached for " + id); - return; - } - } - } //else console.log("No ID defined"); - //console.log("adding " + url); - var cmd = { - cmd: url, - type: "GET", - isupload: false, - resultfn: result_fn, - errorfn: error_fn, - id: cmd_id - }; - http_cmd_list.push(cmd); - //console.log("Now " + http_cmd_list.length); - process_cmd(); +/** This expects the logindlg to be visible */ +function GetIdentificationStatusSuccess(response_text) { + const response = JSON.parse(response_text); + if (typeof response.authentication_lvl !== "undefined") { + if (response.authentication_lvl === "guest") { + setHTML("current_ID", translate_text_item("guest")); + setHTML("current_auth_level", ""); + } + } } +const SendGetHttp = (url, result_fn, error_fn, id, max_id) => { + if (http_cmd_list.length > max_cmd && max_cmd !== -1) { + error_fn(999, translate_text_item("Server not responding")); + return; + } + let cmd_id = 0; + let cmd_max_id = 1; + //console.log("ID = " + id); + //console.log("Max ID = " + max_id); + //console.log("+++ " + url); + if (typeof id !== "undefined") { + cmd_id = id; + if (typeof max_id !== "undefined") cmd_max_id = max_id; + //else console.log("No Max ID defined"); + for (let p = 0; p < http_cmd_list.length; p++) { + //console.log("compare " + (max_id - cmd_max_id)); + if (http_cmd_list[p].id === cmd_id) { + cmd_max_id--; + //console.log("found " + http_cmd_list[p].id + " and " + cmd_id); + } + if (cmd_max_id <= 0) { + console.log(`Limit reached for ${id}`); + return; + } + } + } //else console.log("No ID defined"); + //console.log("adding " + url); + const cmd = { + cmd: url, + type: "GET", + isupload: false, + resultfn: result_fn, + errorfn: error_fn, + id: cmd_id, + }; + http_cmd_list.push(cmd); + //console.log("Now " + http_cmd_list.length); + process_cmd(); +}; + function ProcessGetHttp(url, resultfn, errorfn) { - if (http_communication_locked) { - errorfn(503, translate_text_item("Communication locked!")); - console.log("locked"); - return; - } - var xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = function() { - if (xmlhttp.readyState == 4) { - if (xmlhttp.status == 200) { - //console.log("*** " + url + " done"); - if (typeof resultfn != 'undefined' && resultfn != null) resultfn(xmlhttp.responseText); - } else { - if (xmlhttp.status == 401) GetIdentificationStatus(); - if (typeof errorfn != 'undefined' && errorfn != null) errorfn(xmlhttp.status, xmlhttp.responseText); - } - } - } + const common = new Common(); + if (common.http_communication_locked) { + errorfn(503, translate_text_item("Communication locked!")); + console.log("locked"); + return; + } + const xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = () => { + if (xmlhttp.readyState === 4) { + if (xmlhttp.status === 200) { + //console.log("*** " + url + " done"); + if (typeof resultfn !== "undefined" && resultfn != null) + resultfn(xmlhttp.responseText); + } else { + if (xmlhttp.status === 401) GetIdentificationStatus(); + if (typeof errorfn !== "undefined" && errorfn != null) + errorfn(xmlhttp.status, xmlhttp.responseText); + } + } + }; - if (url.startsWith("/command")) { - url += (url.indexOf("?") == -1) ? "?" : "&"; - url += "PAGEID=" + page_id; - } - //console.log("GET:" + url); - xmlhttp.open("GET", url, true); - xmlhttp.send(); + let gUrl = url; + if (gUrl.startsWith("/command")) { + gUrl += gUrl.indexOf("?") === -1 ? "?" : "&"; + gUrl += `PAGEID=${pageID()}`; + } + //console.log("GET:" + url); + xmlhttp.open("GET", gUrl, true); + xmlhttp.send(); } -function SendPostHttp(url, postdata, result_fn, error_fn, id, max_id) { - if ((http_cmd_list.length > max_cmd) && (max_cmd != -1)) { - error_fn(999, translate_text_item("Server not responding")); - return; - } - var cmd_id = 0; - var cmd_max_id = 1; - if (typeof id != 'undefined') { - cmd_id = id; - if (typeof max_id != 'undefined') cmd_max_id = max_id; - for (p = 0; p < http_cmd_list.length; p++) { - if (http_cmd_list[p].id == cmd_id) cmd_max_id--; - if (cmd_max_id <= 0) return; - } - } +// function SendPostHttp(url, postdata, result_fn, error_fn, id, max_id) { +// if (http_cmd_list.length > max_cmd && max_cmd !== -1) { +// error_fn(999, translate_text_item("Server not responding")); +// return; +// } +// let cmd_id = 0; +// let cmd_max_id = 1; +// if (typeof id !== "undefined") { +// cmd_id = id; +// if (typeof max_id !== "undefined") cmd_max_id = max_id; +// for (p = 0; p < http_cmd_list.length; p++) { +// if (http_cmd_list[p].id === cmd_id) cmd_max_id--; +// if (cmd_max_id <= 0) return; +// } +// } - //console.log("adding " + url); - var cmd = { - cmd: url, - type: "POST", - isupload: false, - data: postdata, - resultfn: result_fn, - errorfn: error_fn, - initfn: init_fn, - id: cmd_id - }; - http_cmd_list.push(cmd); - process_cmd(); -} +// //console.log("adding " + url); +// const cmd = { +// cmd: url, +// type: "POST", +// isupload: false, +// data: postdata, +// resultfn: result_fn, +// errorfn: error_fn, +// initfn: init_fn, +// id: cmd_id, +// }; +// http_cmd_list.push(cmd); +// process_cmd(); +// } function ProcessPostHttp(url, postdata, resultfn, errorfn) { - if (http_communication_locked) { - errorfn(503, translate_text_item("Communication locked!")); - return; - } - var xmlhttp = new XMLHttpRequest(); - xmlhttp.onreadystatechange = function() { - if (xmlhttp.readyState == 4) { - if (xmlhttp.status == 200) { - if (typeof resultfn != 'undefined' && resultfn != null) resultfn(xmlhttp.responseText); - } else { - if (xmlhttp.status == 401) GetIdentificationStatus(); - if (typeof errorfn != 'undefined' && errorfn != null) errorfn(xmlhttp.status, xmlhttp.responseText); - } - } - } - url += (url.indexOf("?") == -1) ? "?" : "&"; - url += "PAGEID=" + page_id; - //console.log(url); - xmlhttp.open("POST", url, true); - xmlhttp.send(postdata); + const common = new Common(); + if (common.http_communication_locked()) { + errorfn(503, translate_text_item("Communication locked!")); + return; + } + const xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = () => { + if (xmlhttp.readyState === 4) { + if (xmlhttp.status === 200) { + if (typeof resultfn !== "undefined" && resultfn != null) + resultfn(xmlhttp.responseText); + } else { + if (xmlhttp.status === 401) GetIdentificationStatus(); + if (typeof errorfn !== "undefined" && errorfn != null) + errorfn(xmlhttp.status, xmlhttp.responseText); + } + } + }; + let pUrl = url; + pUrl += pUrl.indexOf("?") === -1 ? "?" : "&"; + pUrl += `PAGEID=${pageID()}`; + //console.log(pUrl); + xmlhttp.open("POST", pUrl, true); + xmlhttp.send(postdata); } function SendFileHttp(url, postdata, progress_fn, result_fn, error_fn) { - if ((http_cmd_list.length > max_cmd) && (max_cmd != -1)) { - error_fn(999, translate_text_item("Server not responding")); - return; - } - if (http_cmd_list.length != 0) process = false; - var cmd = { - cmd: url, - type: "POST", - isupload: true, - data: postdata, - progressfn: progress_fn, - resultfn: result_fn, - errorfn: error_fn, - id: 0 - }; - http_cmd_list.push(cmd); - process_cmd(); + if (http_cmd_list.length > max_cmd && max_cmd !== -1) { + error_fn(999, translate_text_item("Server not responding")); + return; + } + if (http_cmd_list.length !== 0) { + // TODO: figure out what, if anything this did + // biome-ignore lint/suspicious/noGlobalAssign: + process = false; + } + const cmd = { + cmd: url, + type: "POST", + isupload: true, + data: postdata, + progressfn: progress_fn, + resultfn: result_fn, + errorfn: error_fn, + id: 0, + }; + http_cmd_list.push(cmd); + process_cmd(); } function ProcessFileHttp(url, postdata, progressfn, resultfn, errorfn) { - if (http_communication_locked) { - errorfn(503, translate_text_item("Communication locked!")); - return; - } - http_communication_locked = true; - xmlhttpupload = new XMLHttpRequest(); - xmlhttpupload.onreadystatechange = function() { - if (xmlhttpupload.readyState == 4) { - http_communication_locked = false; - if (xmlhttpupload.status == 200) { - if (typeof resultfn != 'undefined' && resultfn != null) resultfn(xmlhttpupload.responseText); - } else { - if (xmlhttpupload.status == 401) GetIdentificationStatus(); - if (typeof errorfn != 'undefined' && errorfn != null) errorfn(xmlhttpupload.status, xmlhttpupload.responseText); - } - } - } - //console.log(url); - xmlhttpupload.open("POST", url, true); - if (typeof progressfn != 'undefined' && progressfn != null) xmlhttpupload.upload.addEventListener("progress", progressfn, false); - xmlhttpupload.send(postdata); + const common = new Common(); + if (common.http_communication_locked) { + errorfn(503, translate_text_item("Communication locked!")); + return; + } + common.http_communication_locked = true; + xmlhttpupload = new XMLHttpRequest(); + xmlhttpupload.onreadystatechange = () => { + if (xmlhttpupload.readyState === 4) { + common.http_communication_locked = false; + if (xmlhttpupload.status === 200) { + if (typeof resultfn !== "undefined" && resultfn != null) + resultfn(xmlhttpupload.responseText); + } else { + if (xmlhttpupload.status === 401) GetIdentificationStatus(); + if (typeof errorfn !== "undefined" && errorfn != null) + errorfn(xmlhttpupload.status, xmlhttpupload.responseText); + } + } + }; + //console.log(url); + xmlhttpupload.open("POST", url, true); + if (typeof progressfn !== "undefined" && progressfn != null) + xmlhttpupload.upload.addEventListener("progress", progressfn, false); + xmlhttpupload.send(postdata); } + +export { clear_cmd_list, SendFileHttp, SendGetHttp }; diff --git a/www/js/icons.js b/www/js/icons.js index 2701865db..3f34a0203 100644 --- a/www/js/icons.js +++ b/www/js/icons.js @@ -37,11 +37,8 @@ var list_icon = { 'M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z', } -function get_icon_svg(name, w, h, color) { +const get_icon_svg = (name, w = '1.3em', h= '1.2em', color = 'currentColor') => { var content = '' - if (typeof w == 'undefined') w = '1.3em' - if (typeof h == 'undefined') h = '1.2em' - if (typeof color == 'undefined') color = 'currentColor' var has_error = false try { content = list_icon[name] @@ -50,15 +47,11 @@ function get_icon_svg(name, w, h, color) { has_error = true } if (has_error) return '' - var icon = - "" + let icon =`` + icon += ""; + icon += `` + icon += ""; return icon } + +export { get_icon_svg }; diff --git a/www/js/inputdlg.js b/www/js/inputdlg.js index 2543959da..cc53d71c8 100644 --- a/www/js/inputdlg.js +++ b/www/js/inputdlg.js @@ -1,4 +1,4 @@ -// import - closeModal, setactiveModal, showModal, id +import { closeModal, setactiveModal, showModal, id } from "./common.js"; //input dialog const inputdlg = (titledlg, textdlg, closefunc, preset = "") => { @@ -28,3 +28,5 @@ function closeInputModal(response) { } closeModal(answer); } + +export { inputdlg }; diff --git a/www/js/langUtils.js b/www/js/langUtils.js new file mode 100644 index 000000000..31b7b36a9 --- /dev/null +++ b/www/js/langUtils.js @@ -0,0 +1,68 @@ +import { getPrefValue} from "./common.js"; + +/** List of currently support languages + * There should be a json file for each one of these in js/language + * Also check serve.ts when serving locally + */ +const language_list = [ + ["de", "Deutsch", typeof germantrans === "undefined" ? null : germantrans], + ["en", "English", typeof englishtrans === "undefined" ? null : englishtrans], + ["es", "Español", typeof spanishtrans === "undefined" ? null : spanishtrans], + ["fr", "Français", typeof frenchtrans === "undefined" ? null : frenchtrans], + ["it", "Italiano", typeof italiantrans === "undefined" ? null : italiantrans], + ["ja", "日本語", typeof japanesetrans === "undefined" ? null : japanesetrans], + ["hu", "Magyar", typeof hungariantrans === "undefined" ? null : hungariantrans], + ["pl", "Polski", typeof polishtrans === "undefined" ? null : polishtrans], + ["ptbr", "Português-Br", typeof ptbrtrans === "undefined" ? null : ptbrtrans], + ["ru", "Русский", typeof russiantrans === "undefined" ? null : russiantrans], + ["tr", "Türkçe", typeof turkishtrans === "undefined" ? null : turkishtrans], + ["uk", "Українська", typeof ukrtrans === "undefined" ? null : ukrtrans], + ["zh_CN", "简体中文", typeof zh_CN_trans === "undefined" ? null : zh_CN_trans], +]; + +/** Build a language list select element */ +const build_language_list = (id_item) => { + const content = [`"); + return content.join("\n"); +} + +const getCurrentTrans = () => { + let currenttrans = {}; + + for (let lang_i = 0; lang_i < language_list.length; lang_i++) { + const langCode = language_list[lang_i][0]; + const langEnabled = language_list[lang_i][2]; + if (langCode === getPrefValue("language_list") && langEnabled) { + currenttrans = language_list[lang_i][2]; + break; + } + } + + return currenttrans; +} + +/** Translate the supplied item_text, putting it into a `` tag if required */ +const translate_text_item = (item_text, withtag = false) => { + const currenttrans = getCurrentTrans(); + + let translated_content = currenttrans[item_text]; + if (typeof translated_content === 'undefined') { + translated_content = item_text; + } + if (withtag) { + return `${translated_content}`; + } + + return translated_content; +} + +export { build_language_list, translate_text_item }; diff --git a/www/js/language/de.js b/www/js/language/de.json similarity index 98% rename from www/js/language/de.js rename to www/js/language/de.json index 494eb052f..fd068b3a6 100644 --- a/www/js/language/de.js +++ b/www/js/language/de.json @@ -1,6 +1,4 @@ -//removeIf(de_lang_disabled) -//german -var germantrans = { +{ "de":"Deutsch", "ESP3D for":"ESP3D für", "Value of auto-check must be between 0s and 99s !!":"Der Wert des Auto-Checks muss zwischen 0s und 99s liegen!!", @@ -46,7 +44,7 @@ var germantrans = { "Controls":"Steuerung", "Credits":"Credits", "Dashboard":"Dashboard", -"Data mofified":"Daten geändert", +"Data modified":"Daten geändert", "Do you want to save?":"Möchten Sie speichern?", "Enable second extruder controls":"Zweiten Extruder aktivieren", "Error":"Fehler", @@ -286,7 +284,6 @@ var germantrans = { "Web Update":"Webupate", "Pin Recovery":"Reset-Taste", "Disabled":"Deaktiviert", -"Authentication":"Authentifizierung", "Target Firmware":"Firmware", "SD Card Support":"SD-Karten-Unterstützung", "Time Support":"Zeitserver", @@ -371,6 +368,5 @@ var germantrans = { "error:62":"SD card failed to open directory", "error:63":"SD Card directory not found", "error:64":"SD Card file empty", -"error:70":"Bluetooth failed to start", -}; -//endRemoveIf(de_lang_disabled) +"error:70":"Bluetooth failed to start" +} diff --git a/www/js/language/en.js b/www/js/language/en.json similarity index 96% rename from www/js/language/en.js rename to www/js/language/en.json index 93ecad328..56bd85784 100644 --- a/www/js/language/en.js +++ b/www/js/language/en.json @@ -1,6 +1,4 @@ -//removeIf(en_lang_disabled) -//english -var englishtrans = { +{ "en":"English", "STA":"Client Station", "AP":"Access Point", @@ -61,6 +59,5 @@ var englishtrans = { "error:62":"SD card failed to open directory", "error:63":"SD Card directory not found", "error:64":"SD Card file empty", -"error:70":"Bluetooth failed to start", -}; -//endRemoveIf(en_lang_disabled) +"error:70":"Bluetooth failed to start" +} \ No newline at end of file diff --git a/www/js/language/es.js b/www/js/language/es.json similarity index 98% rename from www/js/language/es.js rename to www/js/language/es.json index 374857d03..7ce2c9076 100644 --- a/www/js/language/es.js +++ b/www/js/language/es.json @@ -1,6 +1,4 @@ -//removeIf(es_lang_disabled) -//Spanish -var spanishtrans = { +{ "es":"Español", "ESP3D for":"ESP3D para", "Value of auto-check must be between 0s and 99s !!":"¡El valor del auto-control tiene que estar entre 0s y 99s !", @@ -46,8 +44,8 @@ var spanishtrans = { "Controls":"Controles", "Credits":"Créditos", "Dashboard":"Tablero", -"Data mofified":"Cambio de datos", -"Do you want to save?":" ¿Desea guardar ?", +"Data modified":"Cambio de datos", +"Do you want to save?":"¿Desea guardar ?", "Enable second extruder controls":"Activar segundo extrusor", "Error":"Error", "ESP3D Filesystem":"Archivos ESP3D", @@ -286,7 +284,6 @@ var spanishtrans = { "Web Update":"Actualización web", "Pin Recovery":"Botón restablecer", "Disabled":"Desactivado", -"Authentication":"Autenticación", "Target Firmware":"Firmware de destino", "SD Card Support":"Soporte de tarjeta SD", "Time Support":"Servidor de tiempo", @@ -371,6 +368,5 @@ var spanishtrans = { "error:62":"SD card failed to open directory", "error:63":"SD Card directory not found", "error:64":"SD Card file empty", -"error:70":"Bluetooth failed to start", -}; -//endRemoveIf(es_lang_disabled) +"error:70":"Bluetooth failed to start" +} diff --git a/www/js/language/fl.js b/www/js/language/fl.json similarity index 97% rename from www/js/language/fl.js rename to www/js/language/fl.json index 9de126dd8..67502e18e 100644 --- a/www/js/language/fl.js +++ b/www/js/language/fl.json @@ -1,6 +1,4 @@ -//removeIf(fl_lang_disabled) -//english for FluidNC -var fltrans = { +{ "ESP3D Filesystem":"FluidNC Local Filesystem", "ESP3D Settings":"FluidNC Settings", "ESP3D Status":"FluidNC Status", @@ -66,6 +64,5 @@ var fltrans = { "error:62":"SD card failed to open directory", "error:63":"SD Card directory not found", "error:64":"SD Card file empty", - "error:70":"Bluetooth failed to start", -}; -//endRemoveIf(en_lang_disabled) + "error:70":"Bluetooth failed to start" +} diff --git a/www/js/language/fr.js b/www/js/language/fr.json similarity index 99% rename from www/js/language/fr.js rename to www/js/language/fr.json index 077ccaf46..03e24d160 100644 --- a/www/js/language/fr.js +++ b/www/js/language/fr.json @@ -1,5 +1,4 @@ -//french -var frenchtrans = { +{ "fr":"Français", "ESP3D for":"ESP3D pour", "Value of auto-check must be between 0s and 99s !!":"La valeur de contrôle doit être entre 0s et 99s !!", @@ -45,7 +44,7 @@ var frenchtrans = { "Controls":"Controles", "Credits":"Crédits", "Dashboard":"Tableau de bord", -"Data mofified":"Données modifiées", +"Data modified":"Données modifiées", "Do you want to save?":"Voulez-vous enregister?", "Enable second extruder controls":"Activer le controle du second extrudeur", "Error":"Erreur", @@ -285,7 +284,6 @@ var frenchtrans = { "Web Update":"M.A.J. Internet", "Pin Recovery":"Bouton de R.A.Z.", "Disabled":"Désactivé", -"Authentication":"Authentification", "Target Firmware":"Firmware cible", "SD Card Support":"Support Carte SD", "Time Support":"Serveur NTP", @@ -370,5 +368,5 @@ var frenchtrans = { "error:62":"Impossible d'ouvrir un répertoire sur la carte SD", "error:63":"Répertoire non trouvé sur la carte SD", "error:64":"Fichier vide sur la carte SD", -"error:70":"Echec de démarrage du Bluetooth", -}; +"error:70":"Echec de démarrage du Bluetooth" +} diff --git a/www/js/language/hu.js b/www/js/language/hu.json similarity index 99% rename from www/js/language/hu.js rename to www/js/language/hu.json index f62994bea..527fc2c13 100644 --- a/www/js/language/hu.js +++ b/www/js/language/hu.json @@ -1,7 +1,4 @@ -//removeIf(hu_lang_disabled) -//hungarian -//Hungary by kondorzs -var hungariantrans = { +{ "hu":"Magyar", "ESP3D for":"ESP3D hez", "Value of auto-check must be between 0s and 99s !!":"Az automatikus ellenőrzés értékének 0 és 99 s között kell lennie!", @@ -28,7 +25,6 @@ var hungariantrans = { "Feed rate":"Előtolás", "Touch plate thickness":"Érintőlemez vastagsága", "Redundant":"Redundant", -"Probe":"Probe", "Bed":"Fűtött ágy", "Chamber":"Chamber", "Board":"Board", @@ -50,7 +46,7 @@ var hungariantrans = { "Controls":"Irányítás", "Credits":"Köszönet", "Dashboard":"Alaplap", -"Data mofified":"Az adatok frissítve", +"Data modified":"Az adatok frissítve", "Do you want to save?":"Meg kell menteni?", "Enable second extruder controls":"Aktiválja a második extrudert", "Error":"Hiba", @@ -375,7 +371,6 @@ var hungariantrans = { "error:63":"Az SD kártya könyvtára nem található", "error:64":"SD Kártya fájl üres", "error:70":"Bluetooth nem indult el!", -"Max travel":"Max utazás", "Plate thickness":"Érintőlemez vastagsága", "Show probe panel":"Mutassa a szonda panelt", "Probe":"Szonda", @@ -424,5 +419,4 @@ var hungariantrans = { "Z Max travel, mm":"Z Max utazás, mm", "File extensions (use ; to separate)":"Fájl kiterjesztések (használja; szétválasztáshoz)", "Web Socket":"Web modulon" -}; -//endRemoveIf(hu_lang_disabled) +} diff --git a/www/js/language/it.js b/www/js/language/it.json similarity index 98% rename from www/js/language/it.js rename to www/js/language/it.json index 359560a1e..1e9e3450c 100644 --- a/www/js/language/it.js +++ b/www/js/language/it.json @@ -1,6 +1,4 @@ -//removeIf(it_lang_disabled) -//italian -var italiantrans = { +{ "it":"Italiano", "ESP3D for":"ESP3D per", "Value of auto-check must be between 0s and 99s !!":"Il valore di controllo deve essere tra 0 e 99 secondi!!", @@ -46,7 +44,7 @@ var italiantrans = { "Controls":"Controlli", "Credits":"Crediti", "Dashboard":"Dashboard", -"Data mofified":"Dati modificati", +"Data modified":"Dati modificati", "Do you want to save?":"Vuoi salvare?", "Enable second extruder controls":"Attiva controlli per il secondo estrusore", "Error":"Errore", @@ -286,7 +284,6 @@ var italiantrans = { "Web Update":"Aggiorna via Web", "Pin Recovery":"Pin Ripristino", "Disabled":"Disattivato", -"Authentication":"Autenticazione", "Target Firmware":"Target Firmware", "SD Card Support":"Supporto scheda SD", "Time Support":"Server di tempo", @@ -371,6 +368,5 @@ var italiantrans = { "error:62":"SD card failed to open directory", "error:63":"SD Card directory not found", "error:64":"SD Card file empty", -"error:70":"Bluetooth failed to start", -}; -//endRemoveIf(it_lang_disabled) +"error:70":"Bluetooth failed to start" +} diff --git a/www/js/language/ja.js b/www/js/language/ja.json similarity index 99% rename from www/js/language/ja.js rename to www/js/language/ja.json index 9af6ea9ca..e60016f89 100644 --- a/www/js/language/ja.js +++ b/www/js/language/ja.json @@ -1,7 +1,4 @@ -//ja -//removeIf(ja_lang_disabled) -//use https://www.mobilefish.com/services/unicode_converter/unicode_converter.php -var japanesetrans = { +{ "ja":"日本語", "ESP3D for":"ESP3D for", "Value of auto-check must be between 0s and 99s !!":"オートチェックの値は0~99秒の間である必要があります!", @@ -50,7 +47,7 @@ var japanesetrans = { "Controls":"コントロール", "Credits":"クレジット", "Dashboard":"ダッシュボード", -"Data mofified":"データ変更", +"Data modified":"データ変更", "Do you want to save?":"保存しますか?", "Enable second extruder controls":"セカンドエクストルーダーのコントロールを有効化", "Error":"Error", @@ -376,10 +373,8 @@ var japanesetrans = { "error:63":"SDカードのディレクトリが見つかりません", "error:64":"SDカードファイルが空です", "error:70":"Bluetoothの開始に失敗しました", -"Max travel":"最大移動量", "Plate thickness":"タッチプレート厚さ", "Show probe panel":"プローブパネルを表示", -"Probe":"プローブ", "Start Probe":"プロービングを開始", "Touch status":"タッチステータス", "Value of maximum probe travel must be between 1 mm and 9999 mm !":"プローブの最大移動量の値は1m~9999mmである必要があります!", @@ -425,5 +420,4 @@ var japanesetrans = { "Z Max travel, mm":"Z 最大移動量, mm", "File extensions (use ; to separate)":"ファイル拡張子(分離には;を使用))", "Web Socket":"Web Socket" -}; -//endRemoveIf(zh_cn_lang_disabled) +} diff --git a/www/js/language/lang.tpl b/www/js/language/lang.tpl index 008c3342e..fa16e86a1 100644 --- a/www/js/language/lang.tpl +++ b/www/js/language/lang.tpl @@ -21,11 +21,11 @@ var langtrans = { "Abort":"Abort", "auto-check every:":"auto-check every:", "auto-check position every:":"auto-check position every:", -"Autoscroll":"Autoscroll","Max travel":"Max travel", +"Autoscroll":"Autoscroll", +"Max travel":"Max travel", "Feed rate":"Feed rate", "Touch plate thickness":"Touch plate thickness", "Redundant":"Redundant", -"Probe":"Probe", "Bed":"Bed", "Chamber":"Chamber", "Board":"Board", @@ -47,7 +47,7 @@ var langtrans = { "Controls":"Controls", "Credits":"Credits", "Dashboard":"Dashboard", -"Data mofified":"Data mofified", +"Data modified":"Data modified", "Do you want to save?":"Do you want to save?", "Enable second extruder controls":"Enable second extruder controls", "Error":"Error", @@ -373,7 +373,6 @@ var langtrans = { "error:63":"SD Card directory not found", "error:64":"SD Card file empty", "error:70":"Bluetooth failed to start", -"Max travel":"Max travel", "Plate thickness":"Touch plate thickness", "Show probe panel":"Show probe panel", "Probe":"Probe", diff --git a/www/js/language/pl.js b/www/js/language/pl.json similarity index 98% rename from www/js/language/pl.js rename to www/js/language/pl.json index b56f806a4..dfeb4e559 100644 --- a/www/js/language/pl.js +++ b/www/js/language/pl.json @@ -1,6 +1,4 @@ -//removeIf(pl_lang_disabled) -//Polish -var polishtrans = { +{ "pl":"Polski", "ESP3D for":"ESP3D dla", "Value of auto-check must be between 0s and 99s !!":"Wartość automatycznego spawdzania musi być z zakresu 0-99s !", @@ -46,7 +44,7 @@ var polishtrans = { "Controls":"Sterowanie", "Credits":"Informacje", "Dashboard":"Panel główny", -"Data mofified":"Dane zmodyfikowane", +"Data modified":"Dane zmodyfikowane", "Do you want to save?":"Czy zapisać?", "Enable second extruder controls":"Enable second extruder controls", "Error":"Błąd", @@ -287,7 +285,6 @@ var polishtrans = { "Web Update":"Aktualizacja przez Web", "Pin Recovery":"Odzyskiwanie pinu", "Disabled":"Wyłączone", -"Authentication":"Autoryzacja", "Target Firmware":"Typ oprogramowania drukarki", "SD Card Support":"Wsparcie dla karty SD", "Time Support":"Czas", @@ -373,6 +370,5 @@ var polishtrans = { "error:63":"Nie znaleziono folderu na karcie SD", "error:64":"Plik na karcie SD jest pusty", "error:70":"Błąd uruchamiania Bluetooth", -"error:700":"Błąd: Nieznana komenda", -}; -//endRemoveIf(pl_lang_disabled) +"error:700":"Błąd: Nieznana komenda" +} diff --git a/www/js/language/ptbr.js b/www/js/language/ptbr.json similarity index 94% rename from www/js/language/ptbr.js rename to www/js/language/ptbr.json index ed953f4fd..0c14ffe0d 100644 --- a/www/js/language/ptbr.js +++ b/www/js/language/ptbr.json @@ -1,433 +1,420 @@ -//removeIf(ptbr_lang_disabled) -//Brazilian Portuguese -var ptbrtrans = { -"pt-br":"Português", -"ESP3D for":"ESP3D para", -"Value of auto-check must be between 0s and 99s !!":"O valor do auto-check deve estar entre 0s e 99s !!", -"Value of extruder velocity must be between 1 mm/min and 9999 mm/min !":"O valor da velocidade da extrusora deve estar entre 1 mm/min e 9999 mm/min !", -"Value of filament length must be between 0.001 mm and 9999 mm !":"O valor do comprimento do filamento deve estar entre 0.001 mm e 9999 mm !", -"cannot have '-', '#' char or be empty":"Não pode ter caracter '-', '#' ou estar vazio", -"cannot have '-', 'e' char or be empty":"Não pode ter caracter '-', 'e' ou estar vazio", -"Failed:":"Falhou:", -"File config / config.txt not found!":"Arquivo de configuração / config.txt não encontrado!", -"File name cannot be empty!":"O nome do arquivo não pode estar vazio!", -"Value must be ":"O valor deve ser ", -"Value must be between 0 degres and 999 degres !":"O valor deve estar entre 0 graus e 999 graus !", -"Value must be between 0% and 100% !":"O valor deve estar entre 0% e 100% !", -"Value must be between 25% and 150% !":"O valor deve estar entre 25% e 150% !", -"Value must be between 50% and 300% !":"O valor deve estar entre 50% e 300% !", -"XY feedrate value must be between 1 mm/min and 9999 mm/min !":"O valor da velocidade de avanço XY deve estar entre 1 mm/min e 9999 mm/min !", -"Z feedrate value must be between 1 mm/min and 999 mm/min !":"O valor da velocidade de avanço Z deve estar entre 1 mm/min e 999 mm/min !", -" seconds":" segundos", -"Abort":"Aborta", -"auto-check every:":"auto-check a cada:", -"auto-check position every:":"auto-check posição a cada:", -"Autoscroll":"Rolagem automática","Max travel":"Max travel", -"Feed rate":"Taxa de avanço", -"Touch plate thickness":"Touch plate thickness", -"Show probe panel":"Show probe panel", -"Probe":"Probe", -"Start Probe":"Start Probe", -"Touch status":"Touch status", -"Value of maximum probe travel must be between 1 mm and 9999 mm !":"Value of maximum probe travel must be between 1 mm and 9999 mm !", -"Value of probe touch plate thickness must be between 0 mm and 9999 mm !":"Value of probe touch plate thickness must be between 0 mm and 9999 mm !", -"Value of probe feedrate must be between 1 mm/min and 9999 mm/min !":"Value of probe feedrate must be between 1 mm/min and 9999 mm/min !", -"Redundant":"Redundant", -"Probe":"Probe", -"Bed":"Base", -"Chamber":"Chamber", -"Board":"Controladora", -"Busy...":"Ocupado...", -"Camera":"Camera", -"Cancel":"Cancela", -"Cannot get EEPROM content!":"Não foi possível ler a EEPROM!", -"Clear":"Limpa", -"Close":"Fecha", -"Color":"Cor", -"Commands":"Comandos", -"Communication locked by another process, retry later.":"Comunicação bloqueada por outro processo, tente mais tarde.", -"Communication locked!":"Comunicação bloqueada!", -"Communications are currently locked, please wait and retry.":"Comunicações estão bloqueadas no momento, por favor aguarde e tente novamente.", -"Confirm deletion of directory: ":"Confirma exclusão da pasta: ", -"Confirm deletion of file: ":"Conforma exclusão do arquivo: ", -"Connecting ESP3D...":"Conectando ESP3D...", -"Connection failed! is your FW correct?":"Falha na conexão! Seu FW é o correto?", -"Controls":"Controles", -"Credits":"Créditos", -"Dashboard":"Painel de Controle", -"Data mofified":"Data da modificação", -"Do you want to save?":"Você quer salvar?", -"Enable second extruder controls":"Habilita controle do segundo extrusor", -"Error":"Erro", -"ESP3D Filesystem":"Sistema de arquivos ESP3D", -"ESP3D Settings":"Configurações ESP3D", -"ESP3D Status":"Estado ESP3D", -"ESP3D Update":"Atualização ESP3D", -"Extrude":"Extrudar", -"Extruder T0":"Extrusor T0", -"Extruder T1":"Extrusor T1", -"Extruders":"Extrusores", -"Fan (0-100%)":"Vendilador (0-100%)", -"Feed (25-150%)":"Velocidade (25-150%)", -"Feedrate :":"Avanço :", -"Filename":"Nome do arquivo", -"Filename/URI":"Nome do arquivo/URI", -"Verbose mode":"Modo completo", -"Firmware":"Firmware", -"Flow (50-300%)":"Fluxo (50-300%)", -"Heater T0":"Aquecimento T0", -"Heater T1":"Aquecimento T1", -"Help":"Ajuda", -"Icon":"Ícone", -"Interface":"Interface", -"Join":"Unir", -"Label":"Etiqueta", -"List of available Access Points":"Lista de Pontos de Acesso disponíveis", -"Macro Editor":"Macro Editor", -"mm":"mm", -"mm/min":"mm/min", -"Motors off":"Motores off", -"Name":"Nome", -"Name:":"Nome:", -"Network":"Rede", -"No SD card detected":"SD card não detectado", -"No":"Não", -"Occupation:":"Ocupado:", -"Ok":"Ok", -"Options":"Opções", -"Out of range":"Fora de alcance", -"Please Confirm":"Por favor, Confirme", -"Please enter directory name":"Por favor, coloque o nome da pasta", -"Please wait...":"Por favor, espere...", -"Printer configuration":"Configuração da impressora", -"GRBL configuration":"Configuração GRBL", -"Printer":"Impressora", -"Progress":"Progresso", -"Protected":"Protegido", -"Refresh":"Atualizar", -"Restart ESP3D":"Reiniciar ESP3D", -"Restarting ESP3D":"Reiniciando ESP3D", -"Restarting":"Reiniciando", -"Restarting, please wait....":"Reiniciando, aguarde...", -"Retry":"Tente novamente", -"Reverse":"Voltar", -"Save macro list failed!":"Falha ao salvar o lista macro!", -"Save":"Salvar", -"Saving":"Salvando", -"Scanning":"Escaneando", -"SD Files":"Arquivos SD", -"sec":"seg", -"Send Command...":"Envia Comando...", -"Send":"Envia", -"Set failed":"Falha ao setar", -"Set":"Setar", -"Signal":"Sinal", -"Size":"Tamanho", -"SSID":"SSID", -"Target":"Alvo", -"Temperatures":"Temperaturas", -"Total:":"Total:", -"Type":"Tipo", -"Update Firmware ?":"Atualiza Firmware ?", -"Update is ongoing, please wait and retry.":"Atualização em andamento. Aguarde e tente depois.", -"Update":"Atualiza", -"Upload failed : ":"Falha no upload : ", -"Upload failed":"Falha no upload", -"Upload":"Upload", -"Uploading ":"Fazendo upload ", -"Upload done":"Upload completo", -"Used:":"Usado:", -"Value | Target":"Valor | Alvo", -"Value":"Valor", -"Wrong data":"Dado errado", -"Yes":"Sim", -"Light":"Luz", -"None":"Não", -"Modem":"Modem", -"STA":"STA", -"AP":"AP", -"Baud Rate":"Baud Rate", -"Sleep Mode":"Modo Sleep", -"Web Port":"Porta Web", -"Data Port":"Porta Dados", -"Hostname":"Hostname", -"Wifi mode":"Modo Wifi", -"Station SSID":"Estação SSID", -"Station Password":"Estação Password", -"Station Network Mode":"Estação Modo de Rede", -"Station IP Mode":"Estação Modo IP", -"DHCP":"DHCP", -"Static":"Estático", -"Station Static IP":"Estação Estática IP", -"Station Static Mask":"Estação Estática Máscara", -"Station Static Gateway":"Estação Estática Gateway", -"AP SSID":"AP SSID", -"AP Password":"AP Senha", -"AP Network Mode":"AP Modo de Rede", -"SSID Visible":"SSID Visível", -"AP Channel":"AP Canal", -"Open":"Aberto", -"Authentication":"Autenticação", -"AP IP Mode":"AP IP Modo", -"AP Static IP":"AP Estático IP", -"AP Static Mask":"AP Estático Mask", -"AP Static Gateway":"AP Estático Gateway", -"Time Zone":"Time Zone", -"Day Saving Time":"Horário de Verão", -"Time Server 1":"Time Server 1", -"Time Server 2":"Time Server 2", -"Time Server 3":"Time Server 3", -"Target FW":"Firmware", -"Direct SD access":"Acesso direto SD", -"Direct SD Boot Check":"Acesso direto Boot Check", -"Primary SD":"Primário SD", -"Secondary SD":"Secundário SD", -"Temperature Refresh Time":"Temperatura-Tempo Atualização", -"Position Refresh Time":"Posição-Tempo Atualização", -"Status Refresh Time":"Status-Tempo de Atualização", -"XY feedrate":"XY feedrate", -"Z feedrate":"Z feedrate", -"E feedrate":"E feedrate", -"Camera address":"Endereço da Câmera", -"Setup":"Configuração", -"Start setup":"Inicia configuração", -"This wizard will help you to configure the basic settings.":"Este assistente te ajudará com as configurações básicas.", -"Press start to proceed.":"Pressione start para iniciar.", -"Save your printer's firmware base:":"Salve os firmwares base das impressoras:", -"This is mandatory to get ESP working properly.":"Isto é mandatório para que o ESP funcione corretamente.", -"Save your printer's board current baud rate:":"Salve os baud rate de suas impressoras:", -"Printer and ESP board must use same baud rate to communicate properly.":"A impressora e o ESP precisa ter o mesmo Baud Rate para comunicar corretamente.", -"Continue":"Continue", -"WiFi Configuration":"Configuração WiFi", -"Define ESP role:":"Defina a função do ESP:", -"AP define access point / STA allows to join existing network":"AP define ponto de acesso / STA permite unir uma rede existente", -"What access point ESP need to be connected to:":"Qual ponto de acesso o ESP precisa para conectar:", -"You can use scan button, to list available access points.":"Você pode usar o botão de pequisa, para listar os APs disponíveis.", -"Password to join access point:":"Senha para entrar no AP:", -"Define ESP name:":"Define nome ESP:", -"What is ESP access point SSID:":"Qual é o SSID do ESP:", -"Password for access point:":"Senha para o ponto de acesso:", -"Define security:":"Define segurança:", -"SD Card Configuration":"Configuração Cartão SD", -"Is ESP connected to SD card:":"Se ESP conectado ao cartão SD:", -"Check update using direct SD access:":"Checa atualização usando acesso direto SD:", -"SD card connected to ESP":"Cartão SD conectado ao ESP", -"SD card connected to printer":"Cartão SD conectado a impressora", -"Setup is finished.":"Configuração finalizada.", -"After closing, you will still be able to change or to fine tune your settings in main interface anytime.":"Após fechar, você ainda poderá alterar suas configurações na interface principal a qualquer momento.", -"You may need to restart the board to apply the new settings and connect again.":"Você precisa reiniciar para aplicar as novas configurações e conectar novamente.", -"Identification requested":"Identificação necessária", -"admin":"admin", -"user":"usuário", -"guest":"convidado", -"Identification invalid!":"Identificação inválida!", -"Passwords do not matches!":"Senhas não conferem!", -"Password must be >1 and <16 without space!":"Senhas precisam ser >1 e <16 sem espaço!", -"User:":"Usuário:", -"Password:":"Senha:", -"Submit":"Enviar", -"Change Password":"Alterar Senha", -"Current Password:":"Senha Corrente:", -"New Password:":"Nova Senha:", -"Confirm New Password:":"Confirma Nova Senha:", -"Error : Incorrect User":"Erro : Usuário Incorreto", -"Error: Incorrect password":"Erro: Senha Incorreta", -"Error: Missing data":"Erro: Faltando dados", -"Error: Cannot apply changes":"Erro: Não aplica alterações", -"Error: Too many connections":"Erro: Muitas conexões", -"Authentication failed!":"Falnha na Autenticação!", -"Serial is busy, retry later!":"Serial está ocupada, tente depois!", -"Login":"Login", -"Log out":"Sair", -"Password":"Senha", -"No SD Card":"Sem Cartão SD", -"Check for Update":"Checa por Atualização", -"Please use 8.3 filename only.":"Use somente nome de arquivos 8.3.", -"Preferences":"Preferências", -"Feature":"Função", -"Show camera panel":"Exibe painel da câmera", -"Auto load camera":"Auto load da câmera", -"Enable heater T0 redundant temperatures":"Enable heater T0 redundant temperatures", -"Enable probe temperatures":"Enable probe temperatures", -"Enable bed controls":"Habilita controles da base", -"Enable chamber controls":"Enable chamber controls", -"Enable fan controls":"Habilita controles do ventilador", -"Enable Z controls":"Habilita controles do Z", -"Panels":"Painéis", -"Show control panel":"Exibe painel de controle", -"Show temperatures panel":"Exibe painel de temperatura", -"Show extruder panel":"Exibe painel de extrusor", -"Show files panel":"Exibe painel de arquivos", -"Show GRBL panel":"Exibe painel GRBL", -"Show commands panel":"Exibe painel de comandos", -"Select files":"Seleciona arquivos", -"Upload files":"Fazer upload de arquivos", -"Select file":"Seleciona arquivo", -"$n files":"$n arquivos", -"No file chosen":"Nenhum arquivo selecionado", -"Length":"Tamanho", -"Output msg":"Saída de msg", -"Enable":"Habilita", -"Disable":"Desabilita", -"Serial":"Serial", -"Chip ID":"Chip ID", -"CPU Frequency":"Frequência CPU", -"CPU Temperature":"Temperatura CPU", -"Free memory":"Memória Livre", -"Flash Size":"Tamanho Flash", -"Available Size for update":"Tamanho Disponível para Atualização", -"Available Size for SPIFFS":"Tamanho Disponível para SPIFFS", -"Baud rate":"Baud rate", -"Sleep mode":"Modo sleep", -"Channel":"Canal", -"Phy Mode":"Modo Phy", -"Web port":"Porta Web", -"Data port":"Porta Data", -"Active Mode":"Modo Ativo", -"Connected to":"Conectado a", -"IP Mode":"Modo IP", -"Gateway":"Gateway", -"Mask":"Mask", -"DNS":"DNS", -"Disabled Mode":"Modo Desabilitado", -"Captive portal":"Captive portal", -"Enabled":"Habilitado", -"Web Update":"Atualiza Web", -"Pin Recovery":"Pino Restauro", -"Disabled":"Desabilitado", -"Authentication":"Autenticação", -"Target Firmware":"Firmware Alvo", -"SD Card Support":"Suporte Cartão SD", -"Time Support":"Suporte de tempo", -"M117 output":"Saída M117", -"Oled output":"Saída Oled", -"Serial output":"Saída Serial", -"Web socket output":"Saída Web socket", -"TCP output":"Saída TCP", -"FW version":"Versão FW", -"Show DHT output":"Exibe saída DHT", -"DHT Type":"Tipo DHT", -"DHT check (seconds)":"Checagem DHT (segundos)", -"SD speed divider":"Divisor velocidade SD", -"Number of extruders":"Número de extrusores", -"Mixed extruders":"Extrusores Mix", -"Extruder":"Extrusor", -"Enable lock interface":"Habilita bloqueio da interface", -"Lock interface":"Bloqueia interface", -"Unlock interface":"Desbloqueia interface", -"You are disconnected":"Você está disconectado", -"Looks like you are connected from another place, so this page is now disconnected":"Você deve estar conectado de outro local, esta página está desconectada agora", -"Please reconnect me":"Por favor, reconecte", -"Mist":"Mist", -"Flood":"Flood", -"Spindle":"Spindle", -"Connection monitoring":"Monitorando conexão", -"XY Feedrate value must be at least 1 mm/min!":"Valor do avanço XY deve ser de pelo menos 1 mm/min!", -"Z Feedrate value must be at least 1 mm/min!":"Valor do avanço Z deve ser de pelo menos 1 mm/min!", -"Hold:0":"Hold complete. Ready to resume.", -"Hold:1":"Hold in-progress. Reset will throw an alarm.", -"Door:0":"Door closed. Ready to resume.", -"Door:1":"Machine stopped. Door still ajar. Can't resume until closed.", -"Door:2":"Door opened. Hold (or parking retract) in-progress. Reset will throw an alarm.", -"Door:3":"Door closed and resuming. Restoring from park, if applicable. Reset will throw an alarm.", -"ALARM:1":"Hard limit has been triggered. Machine position is likely lost due to sudden halt. Re-homing is highly recommended.", -"ALARM:2":"Soft limit alarm. G-code motion target exceeds machine travel. Machine position retained. Alarm may be safely unlocked.", -"ALARM:3":"Reset while in motion. Machine position is likely lost due to sudden halt. Re-homing is highly recommended.", -"ALARM:4":"Probe fail. Probe is not in the expected initial state before starting probe cycle when G38.2 and G38.3 is not triggered and G38.4 and G38.5 is triggered.", -"ALARM:5":"Probe fail. Probe did not contact the workpiece within the programmed travel for G38.2 and G38.4.", -"ALARM:6":"Homing fail. The active homing cycle was reset.", -"ALARM:7":"Homing fail. Safety door was opened during homing cycle.", -"ALARM:8":"Homing fail. Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring.", -"ALARM:9":"Homing fail. Could not find limit switch within search distances. Try increasing max travel, decreasing pull-off distance, or check wiring.", -"error:1":"G-code consiste de uma letra e número. Letra não encontrada.", -"error:2":"Faltando uma palavra G-code ou valor numérico, formato inválido.", -"error:3":"Grbl '$' comando não reconhecido ou suportado.", -"error:4":"Valor Negativo recebido de um valor positivo esperado.", -"error:5":"Falha no ciclo de Homing. Homing não está habilitado via configurações.", -"error:6":"Tempo Mínimo no pulso de passo precisa ser maior que 3useg.", -"error:7":"Falha na leitura da EEPROM. Restaurando valores afetados pelos valores padrões.", -"error:8":"Comando Grbl '$' não pode ser usado a menos que Grbl esteja ocioso. Garante uma bom funcionamento durante o trabalho.", -"error:9":"Comando G-code está bloqueado durante alarme ou trabalhando.", -"error:10":"Limite por software não pode ser habilitado sem o homing estar habilitado.", -"error:11":"Caracteres máximos por linha excedido. Linha de comando recebida não executada.", -"error:12":"Grbl '$' valor setado excede o máximo step rate suportado.", -"error:13":"Detectado porta de segurança aberta e estado de porta iniciado.", -"error:14":"Informação de Compilação or linha de inicialização excede limite do tamanho da linha na EEPROM. Linha não armazenada.", -"error:15":"Alvo do Jog excede movimento da máquina. Comando Jog foi ignorado.", -"error:16":"Comando Jog não contem '=' ou contai g-code inválido.", -"error:17":"Modo Laser requer saída PWM.", -"error:20":"Inválido ou não suportado comando g-code encontrado no bloco.", -"error:21":"Mais de um comando g-code do mesmo grupo modal encontrado no bloco.", -"error:22":"Feed rate não foi corretamente setado ou está indefinido.", -"error:23":"Comando g-code no bloco requer um valor inteiro.", -"error:24":"Mais de um comando g-code requer informação de eixos encontrados no bloco.", -"error:25":"Palavra g-code repetida encontrada no bloco.", -"error:26":"Não encontrado informaçõs de eixos no bloco para um comando g-code ou estado do modal corrente requer isso.", -"error:27":"Valor do número de linha é inválido.", -"error:28":"Está faltando um valor requerido no comando g-code.", -"error:29":"G59.x trabalho com sistema de coordenada não é suportado.", -"error:30":"G53 somente permitido com modos de movimentação G0 e G1.", -"error:31":"Palavras de eixo encontradas no bloco quando nenhum comando ou estado modal atual as utiliza.", -"error:32":"Os arcos G2 e G3 exigem pelo menos uma palavra no eixo no plano.", -"error:33":"O destino do comando de movimento é inválido.", -"error:34":"O valor do raio do arco é inválido.", -"error:35":"Os arcos G2 e G3 exigem pelo menos uma palavra de deslocamento no plano.", -"error:36":"Palavras de valor não utilizadas encontradas no bloco.", -"error:37":"O deslocamento do comprimento da ferramenta dinâmica G43.1 não é atribuído ao eixo de comprimento da ferramenta configurado.", -"error:38":"Número da ferramenta maior que o valor máximo suportado.", -"error:60":"Falha ao montar cartão SD", -"error:61":"Cartão SD falhou ao abrir arquivo para leitura", -"error:62":"Cartão SD falhou ao abrir pasta", -"error:63":"Pasta não encontrada no Cartão SD", -"error:64":"Cartão SD arquivo vazio", -"error:70":"Falha ao iniciar Bluetooth", -"Max travel":"Max travel", -"Feed rate":"Feed rate", -"Plate thickness":"Touch plate thickness", -"Show probe panel":"Show probe panel", -"Probe":"Probe", -"Start Probe":"Start Probe", -"Touch status":"Touch status", -"Value of maximum probe travel must be between 1 mm and 9999 mm !":"Value of maximum probe travel must be between 1 mm and 9999 mm !", -"Value of probe touch plate thickness must be between 0 mm and 9999 mm !":"Value of probe touch plate thickness must be between 0 mm and 9999 mm !", -"Value of probe feedrate must be between 1 mm/min and 9999 mm/min !":"Value of probe feedrate must be between 1 mm/min and 9999 mm/min !", -"Probe failed !":"Probe failed !", -"Probe result saved.":"Probe result saved.", -"Browser:":"Browser:", -"Probing...":"Probing...", -"Step pulse, microseconds":"Step pulse, microseconds", -"Step idle delay, milliseconds":"Step idle delay, milliseconds", -"Step port invert, mask2":"Step port invert, mask", -"Direction port invert, mask":"Direction port invert, mask", -"Step enable invert, boolean":"Step enable invert, boolean", -"Limit pins invert, boolean":"Limit pins invert, boolean", -"Probe pin invert, boolean":"Probe pin invert, boolean", -"Status report, mask":"Status report, mask", -"Junction deviation, mm":"Junction deviation, mm", -"Arc tolerance, mm":"Arc tolerance, mm", -"Report inches, boolean":"Report inches, boolean", -"Soft limits, boolean":"Soft limits, boolean", -"Hard limits, boolean":"Hard limits, boolean", -"Homing cycle, boolean":"Homing cycle, boolean", -"Homing dir invert, mask":"Homing dir invert, mask", -"Homing feed, mm/min":"Homing feed, mm/min", -"Homing seek, mm/min":"Homing seek, mm/min", -"Homing debounce, milliseconds":"Homing debounce, milliseconds", -"Homing pull-off, mm":"Homing pull-off, mm", -"Max spindle speed, RPM":"Max spindle speed, RPM", -"Min spindle speed, RPM":"Min spindle speed, RPM", -"Laser mode, boolean":"Laser mode, boolean", -"X steps/mm":"X steps/mm", -"Y steps/mm":"Y steps/mm", -"Z steps/mm":"Z steps/mm", -"X Max rate, mm/min":"X Max rate, mm/min", -"Y Max rate, mm/min":"Y Max rate, mm/min", -"Z Max rate, mm/min":"Z Max rate, mm/min", -"X Acceleration, mm/sec^2":"X Acceleration, mm/sec^2", -"Y Acceleration, mm/sec^2":"Y Acceleration, mm/sec^2", -"Z Acceleration, mm/sec^2":"Z Acceleration, mm/sec^2", -"X Max travel, mm":"X Max travel, mm", -"Y Max travel, mm":"Y Max travel, mm", -"Z Max travel, mm":"Z Max travel, mm" -}; -//endRemoveIf(ptbr_lang_disabled) +{ +"pt-br":"Português", +"ESP3D for":"ESP3D para", +"Value of auto-check must be between 0s and 99s !!":"O valor do auto-check deve estar entre 0s e 99s !!", +"Value of extruder velocity must be between 1 mm/min and 9999 mm/min !":"O valor da velocidade da extrusora deve estar entre 1 mm/min e 9999 mm/min !", +"Value of filament length must be between 0.001 mm and 9999 mm !":"O valor do comprimento do filamento deve estar entre 0.001 mm e 9999 mm !", +"cannot have '-', '#' char or be empty":"Não pode ter caracter '-', '#' ou estar vazio", +"cannot have '-', 'e' char or be empty":"Não pode ter caracter '-', 'e' ou estar vazio", +"Failed:":"Falhou:", +"File config / config.txt not found!":"Arquivo de configuração / config.txt não encontrado!", +"File name cannot be empty!":"O nome do arquivo não pode estar vazio!", +"Value must be ":"O valor deve ser ", +"Value must be between 0 degres and 999 degres !":"O valor deve estar entre 0 graus e 999 graus !", +"Value must be between 0% and 100% !":"O valor deve estar entre 0% e 100% !", +"Value must be between 25% and 150% !":"O valor deve estar entre 25% e 150% !", +"Value must be between 50% and 300% !":"O valor deve estar entre 50% e 300% !", +"XY feedrate value must be between 1 mm/min and 9999 mm/min !":"O valor da velocidade de avanço XY deve estar entre 1 mm/min e 9999 mm/min !", +"Z feedrate value must be between 1 mm/min and 999 mm/min !":"O valor da velocidade de avanço Z deve estar entre 1 mm/min e 999 mm/min !", +" seconds":" segundos", +"Abort":"Aborta", +"auto-check every:":"auto-check a cada:", +"auto-check position every:":"auto-check posição a cada:", +"Autoscroll":"Rolagem automática", +"Max travel":"Max travel", +"Feed rate":"Taxa de avanço", +"Touch plate thickness":"Touch plate thickness", +"Show probe panel":"Show probe panel", +"Probe":"Probe", +"Start Probe":"Start Probe", +"Touch status":"Touch status", +"Value of maximum probe travel must be between 1 mm and 9999 mm !":"Value of maximum probe travel must be between 1 mm and 9999 mm !", +"Value of probe touch plate thickness must be between 0 mm and 9999 mm !":"Value of probe touch plate thickness must be between 0 mm and 9999 mm !", +"Value of probe feedrate must be between 1 mm/min and 9999 mm/min !":"Value of probe feedrate must be between 1 mm/min and 9999 mm/min !", +"Redundant":"Redundant", +"Bed":"Base", +"Chamber":"Chamber", +"Board":"Controladora", +"Busy...":"Ocupado...", +"Camera":"Camera", +"Cancel":"Cancela", +"Cannot get EEPROM content!":"Não foi possível ler a EEPROM!", +"Clear":"Limpa", +"Close":"Fecha", +"Color":"Cor", +"Commands":"Comandos", +"Communication locked by another process, retry later.":"Comunicação bloqueada por outro processo, tente mais tarde.", +"Communication locked!":"Comunicação bloqueada!", +"Communications are currently locked, please wait and retry.":"Comunicações estão bloqueadas no momento, por favor aguarde e tente novamente.", +"Confirm deletion of directory: ":"Confirma exclusão da pasta: ", +"Confirm deletion of file: ":"Conforma exclusão do arquivo: ", +"Connecting ESP3D...":"Conectando ESP3D...", +"Connection failed! is your FW correct?":"Falha na conexão! Seu FW é o correto?", +"Controls":"Controles", +"Credits":"Créditos", +"Dashboard":"Painel de Controle", +"Data modified":"Data da modificação", +"Do you want to save?":"Você quer salvar?", +"Enable second extruder controls":"Habilita controle do segundo extrusor", +"Error":"Erro", +"ESP3D Filesystem":"Sistema de arquivos ESP3D", +"ESP3D Settings":"Configurações ESP3D", +"ESP3D Status":"Estado ESP3D", +"ESP3D Update":"Atualização ESP3D", +"Extrude":"Extrudar", +"Extruder T0":"Extrusor T0", +"Extruder T1":"Extrusor T1", +"Extruders":"Extrusores", +"Fan (0-100%)":"Vendilador (0-100%)", +"Feed (25-150%)":"Velocidade (25-150%)", +"Feedrate :":"Avanço :", +"Filename":"Nome do arquivo", +"Filename/URI":"Nome do arquivo/URI", +"Verbose mode":"Modo completo", +"Firmware":"Firmware", +"Flow (50-300%)":"Fluxo (50-300%)", +"Heater T0":"Aquecimento T0", +"Heater T1":"Aquecimento T1", +"Help":"Ajuda", +"Icon":"Ícone", +"Interface":"Interface", +"Join":"Unir", +"Label":"Etiqueta", +"List of available Access Points":"Lista de Pontos de Acesso disponíveis", +"Macro Editor":"Macro Editor", +"mm":"mm", +"mm/min":"mm/min", +"Motors off":"Motores off", +"Name":"Nome", +"Name:":"Nome:", +"Network":"Rede", +"No SD card detected":"SD card não detectado", +"No":"Não", +"Occupation:":"Ocupado:", +"Ok":"Ok", +"Options":"Opções", +"Out of range":"Fora de alcance", +"Please Confirm":"Por favor, Confirme", +"Please enter directory name":"Por favor, coloque o nome da pasta", +"Please wait...":"Por favor, espere...", +"Printer configuration":"Configuração da impressora", +"GRBL configuration":"Configuração GRBL", +"Printer":"Impressora", +"Progress":"Progresso", +"Protected":"Protegido", +"Refresh":"Atualizar", +"Restart ESP3D":"Reiniciar ESP3D", +"Restarting ESP3D":"Reiniciando ESP3D", +"Restarting":"Reiniciando", +"Restarting, please wait....":"Reiniciando, aguarde...", +"Retry":"Tente novamente", +"Reverse":"Voltar", +"Save macro list failed!":"Falha ao salvar o lista macro!", +"Save":"Salvar", +"Saving":"Salvando", +"Scanning":"Escaneando", +"SD Files":"Arquivos SD", +"sec":"seg", +"Send Command...":"Envia Comando...", +"Send":"Envia", +"Set failed":"Falha ao setar", +"Set":"Setar", +"Signal":"Sinal", +"Size":"Tamanho", +"SSID":"SSID", +"Target":"Alvo", +"Temperatures":"Temperaturas", +"Total:":"Total:", +"Type":"Tipo", +"Update Firmware ?":"Atualiza Firmware ?", +"Update is ongoing, please wait and retry.":"Atualização em andamento. Aguarde e tente depois.", +"Update":"Atualiza", +"Upload failed : ":"Falha no upload : ", +"Upload failed":"Falha no upload", +"Upload":"Upload", +"Uploading ":"Fazendo upload ", +"Upload done":"Upload completo", +"Used:":"Usado:", +"Value | Target":"Valor | Alvo", +"Value":"Valor", +"Wrong data":"Dado errado", +"Yes":"Sim", +"Light":"Luz", +"None":"Não", +"Modem":"Modem", +"STA":"STA", +"AP":"AP", +"Baud Rate":"Baud Rate", +"Sleep Mode":"Modo Sleep", +"Web Port":"Porta Web", +"Data Port":"Porta Dados", +"Hostname":"Hostname", +"Wifi mode":"Modo Wifi", +"Station SSID":"Estação SSID", +"Station Password":"Estação Password", +"Station Network Mode":"Estação Modo de Rede", +"Station IP Mode":"Estação Modo IP", +"DHCP":"DHCP", +"Static":"Estático", +"Station Static IP":"Estação Estática IP", +"Station Static Mask":"Estação Estática Máscara", +"Station Static Gateway":"Estação Estática Gateway", +"AP SSID":"AP SSID", +"AP Password":"AP Senha", +"AP Network Mode":"AP Modo de Rede", +"SSID Visible":"SSID Visível", +"AP Channel":"AP Canal", +"Open":"Aberto", +"Authentication":"Autenticação", +"AP IP Mode":"AP IP Modo", +"AP Static IP":"AP Estático IP", +"AP Static Mask":"AP Estático Mask", +"AP Static Gateway":"AP Estático Gateway", +"Time Zone":"Time Zone", +"Day Saving Time":"Horário de Verão", +"Time Server 1":"Time Server 1", +"Time Server 2":"Time Server 2", +"Time Server 3":"Time Server 3", +"Target FW":"Firmware", +"Direct SD access":"Acesso direto SD", +"Direct SD Boot Check":"Acesso direto Boot Check", +"Primary SD":"Primário SD", +"Secondary SD":"Secundário SD", +"Temperature Refresh Time":"Temperatura-Tempo Atualização", +"Position Refresh Time":"Posição-Tempo Atualização", +"Status Refresh Time":"Status-Tempo de Atualização", +"XY feedrate":"XY feedrate", +"Z feedrate":"Z feedrate", +"E feedrate":"E feedrate", +"Camera address":"Endereço da Câmera", +"Setup":"Configuração", +"Start setup":"Inicia configuração", +"This wizard will help you to configure the basic settings.":"Este assistente te ajudará com as configurações básicas.", +"Press start to proceed.":"Pressione start para iniciar.", +"Save your printer's firmware base:":"Salve os firmwares base das impressoras:", +"This is mandatory to get ESP working properly.":"Isto é mandatório para que o ESP funcione corretamente.", +"Save your printer's board current baud rate:":"Salve os baud rate de suas impressoras:", +"Printer and ESP board must use same baud rate to communicate properly.":"A impressora e o ESP precisa ter o mesmo Baud Rate para comunicar corretamente.", +"Continue":"Continue", +"WiFi Configuration":"Configuração WiFi", +"Define ESP role:":"Defina a função do ESP:", +"AP define access point / STA allows to join existing network":"AP define ponto de acesso / STA permite unir uma rede existente", +"What access point ESP need to be connected to:":"Qual ponto de acesso o ESP precisa para conectar:", +"You can use scan button, to list available access points.":"Você pode usar o botão de pequisa, para listar os APs disponíveis.", +"Password to join access point:":"Senha para entrar no AP:", +"Define ESP name:":"Define nome ESP:", +"What is ESP access point SSID:":"Qual é o SSID do ESP:", +"Password for access point:":"Senha para o ponto de acesso:", +"Define security:":"Define segurança:", +"SD Card Configuration":"Configuração Cartão SD", +"Is ESP connected to SD card:":"Se ESP conectado ao cartão SD:", +"Check update using direct SD access:":"Checa atualização usando acesso direto SD:", +"SD card connected to ESP":"Cartão SD conectado ao ESP", +"SD card connected to printer":"Cartão SD conectado a impressora", +"Setup is finished.":"Configuração finalizada.", +"After closing, you will still be able to change or to fine tune your settings in main interface anytime.":"Após fechar, você ainda poderá alterar suas configurações na interface principal a qualquer momento.", +"You may need to restart the board to apply the new settings and connect again.":"Você precisa reiniciar para aplicar as novas configurações e conectar novamente.", +"Identification requested":"Identificação necessária", +"admin":"admin", +"user":"usuário", +"guest":"convidado", +"Identification invalid!":"Identificação inválida!", +"Passwords do not matches!":"Senhas não conferem!", +"Password must be >1 and <16 without space!":"Senhas precisam ser >1 e <16 sem espaço!", +"User:":"Usuário:", +"Password:":"Senha:", +"Submit":"Enviar", +"Change Password":"Alterar Senha", +"Current Password:":"Senha Corrente:", +"New Password:":"Nova Senha:", +"Confirm New Password:":"Confirma Nova Senha:", +"Error : Incorrect User":"Erro : Usuário Incorreto", +"Error: Incorrect password":"Erro: Senha Incorreta", +"Error: Missing data":"Erro: Faltando dados", +"Error: Cannot apply changes":"Erro: Não aplica alterações", +"Error: Too many connections":"Erro: Muitas conexões", +"Authentication failed!":"Falnha na Autenticação!", +"Serial is busy, retry later!":"Serial está ocupada, tente depois!", +"Login":"Login", +"Log out":"Sair", +"Password":"Senha", +"No SD Card":"Sem Cartão SD", +"Check for Update":"Checa por Atualização", +"Please use 8.3 filename only.":"Use somente nome de arquivos 8.3.", +"Preferences":"Preferências", +"Feature":"Função", +"Show camera panel":"Exibe painel da câmera", +"Auto load camera":"Auto load da câmera", +"Enable heater T0 redundant temperatures":"Enable heater T0 redundant temperatures", +"Enable probe temperatures":"Enable probe temperatures", +"Enable bed controls":"Habilita controles da base", +"Enable chamber controls":"Enable chamber controls", +"Enable fan controls":"Habilita controles do ventilador", +"Enable Z controls":"Habilita controles do Z", +"Panels":"Painéis", +"Show control panel":"Exibe painel de controle", +"Show temperatures panel":"Exibe painel de temperatura", +"Show extruder panel":"Exibe painel de extrusor", +"Show files panel":"Exibe painel de arquivos", +"Show GRBL panel":"Exibe painel GRBL", +"Show commands panel":"Exibe painel de comandos", +"Select files":"Seleciona arquivos", +"Upload files":"Fazer upload de arquivos", +"Select file":"Seleciona arquivo", +"$n files":"$n arquivos", +"No file chosen":"Nenhum arquivo selecionado", +"Length":"Tamanho", +"Output msg":"Saída de msg", +"Enable":"Habilita", +"Disable":"Desabilita", +"Serial":"Serial", +"Chip ID":"Chip ID", +"CPU Frequency":"Frequência CPU", +"CPU Temperature":"Temperatura CPU", +"Free memory":"Memória Livre", +"Flash Size":"Tamanho Flash", +"Available Size for update":"Tamanho Disponível para Atualização", +"Available Size for SPIFFS":"Tamanho Disponível para SPIFFS", +"Baud rate":"Baud rate", +"Sleep mode":"Modo sleep", +"Channel":"Canal", +"Phy Mode":"Modo Phy", +"Web port":"Porta Web", +"Data port":"Porta Data", +"Active Mode":"Modo Ativo", +"Connected to":"Conectado a", +"IP Mode":"Modo IP", +"Gateway":"Gateway", +"Mask":"Mask", +"DNS":"DNS", +"Disabled Mode":"Modo Desabilitado", +"Captive portal":"Captive portal", +"Enabled":"Habilitado", +"Web Update":"Atualiza Web", +"Pin Recovery":"Pino Restauro", +"Disabled":"Desabilitado", +"Target Firmware":"Firmware Alvo", +"SD Card Support":"Suporte Cartão SD", +"Time Support":"Suporte de tempo", +"M117 output":"Saída M117", +"Oled output":"Saída Oled", +"Serial output":"Saída Serial", +"Web socket output":"Saída Web socket", +"TCP output":"Saída TCP", +"FW version":"Versão FW", +"Show DHT output":"Exibe saída DHT", +"DHT Type":"Tipo DHT", +"DHT check (seconds)":"Checagem DHT (segundos)", +"SD speed divider":"Divisor velocidade SD", +"Number of extruders":"Número de extrusores", +"Mixed extruders":"Extrusores Mix", +"Extruder":"Extrusor", +"Enable lock interface":"Habilita bloqueio da interface", +"Lock interface":"Bloqueia interface", +"Unlock interface":"Desbloqueia interface", +"You are disconnected":"Você está disconectado", +"Looks like you are connected from another place, so this page is now disconnected":"Você deve estar conectado de outro local, esta página está desconectada agora", +"Please reconnect me":"Por favor, reconecte", +"Mist":"Mist", +"Flood":"Flood", +"Spindle":"Spindle", +"Connection monitoring":"Monitorando conexão", +"XY Feedrate value must be at least 1 mm/min!":"Valor do avanço XY deve ser de pelo menos 1 mm/min!", +"Z Feedrate value must be at least 1 mm/min!":"Valor do avanço Z deve ser de pelo menos 1 mm/min!", +"Hold:0":"Hold complete. Ready to resume.", +"Hold:1":"Hold in-progress. Reset will throw an alarm.", +"Door:0":"Door closed. Ready to resume.", +"Door:1":"Machine stopped. Door still ajar. Can't resume until closed.", +"Door:2":"Door opened. Hold (or parking retract) in-progress. Reset will throw an alarm.", +"Door:3":"Door closed and resuming. Restoring from park, if applicable. Reset will throw an alarm.", +"ALARM:1":"Hard limit has been triggered. Machine position is likely lost due to sudden halt. Re-homing is highly recommended.", +"ALARM:2":"Soft limit alarm. G-code motion target exceeds machine travel. Machine position retained. Alarm may be safely unlocked.", +"ALARM:3":"Reset while in motion. Machine position is likely lost due to sudden halt. Re-homing is highly recommended.", +"ALARM:4":"Probe fail. Probe is not in the expected initial state before starting probe cycle when G38.2 and G38.3 is not triggered and G38.4 and G38.5 is triggered.", +"ALARM:5":"Probe fail. Probe did not contact the workpiece within the programmed travel for G38.2 and G38.4.", +"ALARM:6":"Homing fail. The active homing cycle was reset.", +"ALARM:7":"Homing fail. Safety door was opened during homing cycle.", +"ALARM:8":"Homing fail. Pull off travel failed to clear limit switch. Try increasing pull-off setting or check wiring.", +"ALARM:9":"Homing fail. Could not find limit switch within search distances. Try increasing max travel, decreasing pull-off distance, or check wiring.", +"error:1":"G-code consiste de uma letra e número. Letra não encontrada.", +"error:2":"Faltando uma palavra G-code ou valor numérico, formato inválido.", +"error:3":"Grbl '$' comando não reconhecido ou suportado.", +"error:4":"Valor Negativo recebido de um valor positivo esperado.", +"error:5":"Falha no ciclo de Homing. Homing não está habilitado via configurações.", +"error:6":"Tempo Mínimo no pulso de passo precisa ser maior que 3useg.", +"error:7":"Falha na leitura da EEPROM. Restaurando valores afetados pelos valores padrões.", +"error:8":"Comando Grbl '$' não pode ser usado a menos que Grbl esteja ocioso. Garante uma bom funcionamento durante o trabalho.", +"error:9":"Comando G-code está bloqueado durante alarme ou trabalhando.", +"error:10":"Limite por software não pode ser habilitado sem o homing estar habilitado.", +"error:11":"Caracteres máximos por linha excedido. Linha de comando recebida não executada.", +"error:12":"Grbl '$' valor setado excede o máximo step rate suportado.", +"error:13":"Detectado porta de segurança aberta e estado de porta iniciado.", +"error:14":"Informação de Compilação or linha de inicialização excede limite do tamanho da linha na EEPROM. Linha não armazenada.", +"error:15":"Alvo do Jog excede movimento da máquina. Comando Jog foi ignorado.", +"error:16":"Comando Jog não contem '=' ou contai g-code inválido.", +"error:17":"Modo Laser requer saída PWM.", +"error:20":"Inválido ou não suportado comando g-code encontrado no bloco.", +"error:21":"Mais de um comando g-code do mesmo grupo modal encontrado no bloco.", +"error:22":"Feed rate não foi corretamente setado ou está indefinido.", +"error:23":"Comando g-code no bloco requer um valor inteiro.", +"error:24":"Mais de um comando g-code requer informação de eixos encontrados no bloco.", +"error:25":"Palavra g-code repetida encontrada no bloco.", +"error:26":"Não encontrado informaçõs de eixos no bloco para um comando g-code ou estado do modal corrente requer isso.", +"error:27":"Valor do número de linha é inválido.", +"error:28":"Está faltando um valor requerido no comando g-code.", +"error:29":"G59.x trabalho com sistema de coordenada não é suportado.", +"error:30":"G53 somente permitido com modos de movimentação G0 e G1.", +"error:31":"Palavras de eixo encontradas no bloco quando nenhum comando ou estado modal atual as utiliza.", +"error:32":"Os arcos G2 e G3 exigem pelo menos uma palavra no eixo no plano.", +"error:33":"O destino do comando de movimento é inválido.", +"error:34":"O valor do raio do arco é inválido.", +"error:35":"Os arcos G2 e G3 exigem pelo menos uma palavra de deslocamento no plano.", +"error:36":"Palavras de valor não utilizadas encontradas no bloco.", +"error:37":"O deslocamento do comprimento da ferramenta dinâmica G43.1 não é atribuído ao eixo de comprimento da ferramenta configurado.", +"error:38":"Número da ferramenta maior que o valor máximo suportado.", +"error:60":"Falha ao montar cartão SD", +"error:61":"Cartão SD falhou ao abrir arquivo para leitura", +"error:62":"Cartão SD falhou ao abrir pasta", +"error:63":"Pasta não encontrada no Cartão SD", +"error:64":"Cartão SD arquivo vazio", +"error:70":"Falha ao iniciar Bluetooth", +"Plate thickness":"Touch plate thickness", +"Probe failed !":"Probe failed !", +"Probe result saved.":"Probe result saved.", +"Browser:":"Browser:", +"Probing...":"Probing...", +"Step pulse, microseconds":"Step pulse, microseconds", +"Step idle delay, milliseconds":"Step idle delay, milliseconds", +"Step port invert, mask2":"Step port invert, mask", +"Direction port invert, mask":"Direction port invert, mask", +"Step enable invert, boolean":"Step enable invert, boolean", +"Limit pins invert, boolean":"Limit pins invert, boolean", +"Probe pin invert, boolean":"Probe pin invert, boolean", +"Status report, mask":"Status report, mask", +"Junction deviation, mm":"Junction deviation, mm", +"Arc tolerance, mm":"Arc tolerance, mm", +"Report inches, boolean":"Report inches, boolean", +"Soft limits, boolean":"Soft limits, boolean", +"Hard limits, boolean":"Hard limits, boolean", +"Homing cycle, boolean":"Homing cycle, boolean", +"Homing dir invert, mask":"Homing dir invert, mask", +"Homing feed, mm/min":"Homing feed, mm/min", +"Homing seek, mm/min":"Homing seek, mm/min", +"Homing debounce, milliseconds":"Homing debounce, milliseconds", +"Homing pull-off, mm":"Homing pull-off, mm", +"Max spindle speed, RPM":"Max spindle speed, RPM", +"Min spindle speed, RPM":"Min spindle speed, RPM", +"Laser mode, boolean":"Laser mode, boolean", +"X steps/mm":"X steps/mm", +"Y steps/mm":"Y steps/mm", +"Z steps/mm":"Z steps/mm", +"X Max rate, mm/min":"X Max rate, mm/min", +"Y Max rate, mm/min":"Y Max rate, mm/min", +"Z Max rate, mm/min":"Z Max rate, mm/min", +"X Acceleration, mm/sec^2":"X Acceleration, mm/sec^2", +"Y Acceleration, mm/sec^2":"Y Acceleration, mm/sec^2", +"Z Acceleration, mm/sec^2":"Z Acceleration, mm/sec^2", +"X Max travel, mm":"X Max travel, mm", +"Y Max travel, mm":"Y Max travel, mm", +"Z Max travel, mm":"Z Max travel, mm" +} diff --git a/www/js/language/ru.js b/www/js/language/ru.json similarity index 99% rename from www/js/language/ru.js rename to www/js/language/ru.json index 8cab48a1e..b76e0e543 100644 --- a/www/js/language/ru.js +++ b/www/js/language/ru.json @@ -1,6 +1,4 @@ -//removeIf(ru_lang_disabled) -//Russian -var russiantrans = { +{ "ru": "Русский", "ESP3D for": "ESP3D для", "Value of auto-check must be between 0s and 99s !!": "Значение автоматической проверки должно быть от 0 до 99 секунд !!", @@ -46,7 +44,7 @@ var russiantrans = { "Controls": "Управление", "Credits": "Благодарности", "Dashboard": "Панель управления", -"Data mofified": "Данные изменены", +"Data modified": "Данные изменены", "Do you want to save?": "Сохранить?", "Enable second extruder controls": "Включить управление вторым экструдером", "Error": "Ошибка", @@ -370,5 +368,4 @@ var russiantrans = { "error:63": "Каталог не найден на SD-карте", "error:64": "Файл отсутствует на SD-карте", "error:70": "Запуск Bluetooth невозможен" -}; -//endRemoveIf(ru_lang_disabled) +} diff --git a/www/js/language/tr.js b/www/js/language/tr.json similarity index 99% rename from www/js/language/tr.js rename to www/js/language/tr.json index d9ecc206d..3012fa24c 100644 --- a/www/js/language/tr.js +++ b/www/js/language/tr.json @@ -1,6 +1,4 @@ -//removeIf(tr_lang_disabled) -//Turkish -var turkishtrans = { +{ "tr":"Türkçe", "ESP3D for":"ESP3D için", "Value of auto-check must be between 0s and 99s !!":"Otomatik kontrol değeri 0s ile 99s arasında olmalıdır !!", @@ -49,7 +47,7 @@ var turkishtrans = { "Controls":"Kontroller", "Credits":"Credits", "Dashboard":"Gösterge paneli", -"Data mofified":"Data değiştirildi", +"Data modified":"Data değiştirildi", "Do you want to save?":"Kaydetmek istiyor musunuz?", "Enable second extruder controls":"İkinci ekstruder kontrolünü etkinleştir", "Error":"Hata", @@ -375,10 +373,8 @@ var turkishtrans = { "error:63":"SD Card directory not found", "error:64":"SD Card file empty", "error:70":"Bluetooth failed to start", -"Max travel":"Max travel", "Plate thickness":"Touch plate thickness", "Show probe panel":"Show probe panel", -"Probe":"Probe", "Start Probe":"Start Probe", "Touch status":"Touch status", "Value of maximum probe travel must be between 1 mm and 9999 mm !":"Value of maximum probe travel must be between 1 mm and 9999 mm !", @@ -424,5 +420,4 @@ var turkishtrans = { "Z Max travel, mm":"Z Max haraket, mm", "File extensions (use ; to separate)":"Dosya uzantıları (kullan ; ayırma)", "Web Socket":"Web Socket" -}; -//endRemoveIf(tr_lang_disabled) +} \ No newline at end of file diff --git a/www/js/language/uk.js b/www/js/language/uk.json similarity index 99% rename from www/js/language/uk.js rename to www/js/language/uk.json index e39a31043..d6c19644b 100644 --- a/www/js/language/uk.js +++ b/www/js/language/uk.json @@ -1,6 +1,4 @@ -//removeIf(uk_lang_disabled) -//Ukrainian -var ukrtrans = { +{ "uk": "Українська", "ESP3D for": "ESP3D для", "Value of auto-check must be between 0s and 99s !!": "Значення автоматичної перевірки має бути від 0 до 99 секунд !!", @@ -46,7 +44,7 @@ var ukrtrans = { "Controls": "Управління", "Credits": "Подяки", "Dashboard": "Панель управління", -"Data mofified": "Дані змінено", +"Data modified": "Дані змінено", "Do you want to save?": "Зберегти?", "Enable second extruder controls": "Увімкнути керування другим екструдером", "Error": "Помилка", @@ -371,5 +369,4 @@ var ukrtrans = { "error:63": "Каталог не знайдено на SD-карті", "error:64": "Файл відсутній на SD-карті", "error:70": "Запуск Bluetooth неможливий" -}; -//endRemoveIf(uk_lang_disabled) +} diff --git a/www/js/language/zh_CN.js b/www/js/language/zh_CN.json similarity index 99% rename from www/js/language/zh_CN.js rename to www/js/language/zh_CN.json index 8f62c2f2b..911f437b4 100644 --- a/www/js/language/zh_CN.js +++ b/www/js/language/zh_CN.json @@ -1,7 +1,4 @@ -//zh_CN -//removeIf(zh_cn_lang_disabled) -//use https://www.mobilefish.com/services/unicode_converter/unicode_converter.php -var zh_CN_trans = { +{ "zh_cn":"简体中文", "ESP3D for":"ESP3D for", "Value of auto-check must be between 0s and 99s !!":"自动检查的值必须在0s到99s之间!!", @@ -28,7 +25,6 @@ var zh_CN_trans = { "Feed rate":"进给率", "Touch plate thickness":"Touch plate thickness", "Redundant":"Redundant", -"Probe":"Probe", "Bed":"热床", "Chamber":"Chamber", "Board":"主板", @@ -50,7 +46,7 @@ var zh_CN_trans = { "Controls":"控制", "Credits":"Credits", "Dashboard":"仪表盘", -"Data mofified":"数据已修改", +"Data modified":"数据已修改", "Do you want to save?":"您要保存吗?", "Enable second extruder controls":"启用第二挤出机", "Error":"错误", @@ -376,7 +372,6 @@ var zh_CN_trans = { "error:63":"找不到SD卡目录", "error:64":"SD卡文件为空", "error:70":"蓝牙无法启动", -"Max travel":"最大行程", "Plate thickness":"探针数据修正", "Show probe panel":"显示探针面板", "Probe":"探针", @@ -424,5 +419,4 @@ var zh_CN_trans = { "Y Max travel, mm":"Y轴最大行程,毫米", "Z Max travel, mm":"Z轴最大行程,毫米", "File extensions (use ; to separate)":"文件扩展名 (使用 ; 分隔)" -}; -//endRemoveIf(zh_cn_lang_disabled) +} \ No newline at end of file diff --git a/www/js/loadHTML.js b/www/js/loadHTML.js new file mode 100644 index 000000000..86627223c --- /dev/null +++ b/www/js/loadHTML.js @@ -0,0 +1,45 @@ +import { Common } from "./common.js"; + +const getHMTLIdsToLoad = (node, htmlElemIdsToLoad) => { + if (node.nodeType !== Node.ELEMENT_NODE) { + return; + } + for (const elem of node.getElementsByClassName("loadhtml")) { + htmlElemIdsToLoad.push(elem.id); + } +}; + +/** The `elemId` should equate to a correct path for the html file to be loaded */ +const loadHTML = (elemId) => { + fetch(elemId) + .then((response) => response.text()) + .then((data) => { + const origDiv = document.getElementById(elemId); + origDiv.innerHTML = data; + const newElems = origDiv.childNodes; + const htmlElemIdsToLoad = []; + while (newElems.length > 1) { + getHMTLIdsToLoad(newElems[0], htmlElemIdsToLoad); + origDiv.parentElement.insertBefore(newElems[0], origDiv); + } + if (newElems.length) { + getHMTLIdsToLoad(newElems[0], htmlElemIdsToLoad); + origDiv.parentNode.replaceChild(newElems[0], origDiv); + } + for (const elem_Id of htmlElemIdsToLoad) { + loadHTML(elem_Id); + } + const common = new Common(); + common.loadedHTML.push(elemId); + }); +}; + +/** Load up the HTML identified via a div with the correct class replacing the div at its current location */ +window.addEventListener("DOMContentLoaded", (event) => { + const common = new Common(); + common.loadedHTML.push("In dev mode"); + const htmlToLoad = document.getElementsByClassName("loadhtml"); + for (const elem of htmlToLoad) { + loadHTML(elem.id); + } +}); diff --git a/www/js/logindlg.js b/www/js/logindlg.js index eff6fd89a..be6693683 100644 --- a/www/js/logindlg.js +++ b/www/js/logindlg.js @@ -1,4 +1,15 @@ -// import translate_text_item, conErr, displayBlock, displayNone, id, setHTML, closeModal, setactiveModal, showModal, SendGetHttp +import { + translate_text_item, + conErr, + displayBlock, + displayNone, + id, + setHTML, + closeModal, + setactiveModal, + showModal, + SendGetHttp, +} from "./common.js"; /** login dialog */ const logindlg = (closefunc, check_first = false) => { @@ -28,10 +39,7 @@ function checkloginsuccess(response_text) { if (typeof response.authentication_lvl !== "undefined") { if (response.authentication_lvl !== "guest") { if (typeof response.authentication_lvl !== "undefined") { - setHTML( - "current_auth_level", - `(${translate_text_item(response.authentication_lvl)})`, - ); + setHTML("current_auth_level", `(${translate_text_item(response.authentication_lvl)})`); } if (typeof response.user !== "undefined") { setHTML("current_ID", response.user); @@ -71,10 +79,7 @@ function loginfailed(error_code, response_text) { function loginsuccess(response_text) { const response = JSON.parse(response_text); if (typeof response.authentication_lvl !== "undefined") { - setHTML( - "current_auth_level", - `(${translate_text_item(response.authentication_lvl)})`, - ); + setHTML("current_auth_level", `(${translate_text_item(response.authentication_lvl)})`); } displayNone("login_loader"); displayBlock("logout_menu"); @@ -118,3 +123,5 @@ function DisconnectLogin(answer) { SendGetHttp(url, DisconnectionSuccess, DisconnectionFailed); } } + +export { DisconnectLogin, logindlg }; diff --git a/www/js/macrodlg.js b/www/js/macrodlg.js index f784b008a..46116bc3a 100644 --- a/www/js/macrodlg.js +++ b/www/js/macrodlg.js @@ -1,4 +1,21 @@ -// import get_icon_svg, displayBlock, displayNone, id, setHTML, clear_drop_menu, hide_drop_menu, showhide_drop_menu, closeModal, setactiveModal, showModal, alertdlg, confirmdlg, SendFileHttp, translate_text_item +import { + Common, + get_icon_svg, + displayBlock, + displayNone, + id, + setHTML, + clear_drop_menu, + hide_drop_menu, + showhide_drop_menu, + closeModal, + setactiveModal, + showModal, + alertdlg, + confirmdlg, + SendFileHttp, + translate_text_item, +} from "./common.js"; //Macro dialog let macrodlg_macrolist = []; @@ -174,10 +191,7 @@ function on_macro_filename(event, index) { const filename = event.value.trim(); entry.filename = event.value; if (filename.length === 0) { - alertdlg( - translate_text_item("Out of range"), - translate_text_item("File name cannot be empty!"), - ); + alertdlg(translate_text_item("Out of range"), translate_text_item("File name cannot be empty!")); } build_dlg_macrolist_line(index); } @@ -258,11 +272,7 @@ const closeMacroDialog = () => { } } if (modified) { - confirmdlg( - translate_text_item("Data modified"), - translate_text_item("Do you want to save?"), - process_macroCloseDialog, - ); + confirmdlg(translate_text_item("Data modified"), translate_text_item("Do you want to save?"), process_macroCloseDialog); } else { closeModal("cancel"); } @@ -280,12 +290,7 @@ function process_macroCloseDialog(answer) { function SaveNewMacroList() { if (http_communication_locked) { - alertdlg( - translate_text_item("Busy..."), - translate_text_item( - "Communications are currently locked, please wait and retry.", - ), - ); + alertdlg(translate_text_item("Busy..."), translate_text_item("Communications are currently locked, please wait and retry.")); return; } for (let i = 0; i < 9; i++) { @@ -293,10 +298,7 @@ function SaveNewMacroList() { macrodlg_macrolist[i].filename.length === 0 && macrodlg_macrolist[i].class !== "" ) { - alertdlg( - translate_text_item("Out of range"), - translate_text_item("File name cannot be empty!"), - ); + alertdlg(translate_text_item("Out of range"), translate_text_item("File name cannot be empty!")); return; } } @@ -364,9 +366,8 @@ function macroUploadsuccess(response) { } function macroUploadfailed(error_code, response) { - alertdlg( - translate_text_item("Error"), - translate_text_item("Save macro list failed!"), - ); + alertdlg(translate_text_item("Error"), translate_text_item("Save macro list failed!")); displayNone("macrodlg_upload_msg"); } + +export { showmacrodlg }; diff --git a/www/js/maslow.js b/www/js/maslow.js index 0f183bdd6..b51fdac85 100644 --- a/www/js/maslow.js +++ b/www/js/maslow.js @@ -1,5 +1,13 @@ -// When we can change to proper ESM - uncomment this -// import M from "constants"; +import { + Common, + M, + get_Position, + SendPrinterCommand, + refreshSettings, + saveMaslowYaml, + hideModal, + loadedValues, +} from "./common.js"; /** Maslow Status */ let maslowStatus = { homed: false, extended: false }; @@ -8,201 +16,254 @@ let maslowStatus = { homed: false, extended: false }; let lastHeartBeatTime = new Date().getTime(); const err = "error: "; -// When we can change to proper ESM - prefix these const strings and functions with 'export' (minus the quotes of course) const MaslowErrMsgKeyValueCantUse = `${err}Could not use supplied key-value pair.`; const MaslowErrMsgNoKey = `${err}No key supplied for value.`; const MaslowErrMsgNoValue = `${err}No value supplied for key.`; const MaslowErrMsgNoMatchingKey = `${err}Could not find key for value in reference table.`; -const MaslowErrMsgKeyValueSuffix = "This is probably a programming error\nKey-Value pair supplied was:"; +const MaslowErrMsgKeyValueSuffix = + "This is probably a programming error\nKey-Value pair supplied was:"; /** Perform maslow specific-ish info message handling */ const maslowInfoMsgHandling = (msg) => { - if (msg.startsWith('MINFO: ')) { - maslowStatus = JSON.parse(msg.substring(7)); - return true; - } + if (msg.startsWith("MINFO: ")) { + maslowStatus = JSON.parse(msg.substring(7)); + return true; + } - if (msg.startsWith('[MSG:INFO: Heartbeat')) { - lastHeartBeatTime = new Date().getTime(); - return true; - } + if (msg.startsWith("[MSG:INFO: Heartbeat")) { + lastHeartBeatTime = new Date().getTime(); + return true; + } - //Catch the calibration complete message and alert the user - if (msg.startsWith('[MSG:INFO: Calibration complete')) { - alert('Calibration complete. You do not need to do calibration ever again unless your frame changes size. You might want to store a backup of your maslow.yaml file in case you need to restore it later.'); - return true; - } + //Catch the calibration complete message and alert the user + if (msg.startsWith("[MSG:INFO: Calibration complete")) { + alert( + "Calibration complete. You do not need to do calibration ever again unless your frame changes size. You might want to store a backup of your maslow.yaml file in case you need to restore it later.", + ); + return true; + } - return false; -} + return false; +}; /** Perform maslow specific-ish error message handling */ const maslowErrorMsgHandling = (msg) => { - if (!msg.startsWith("error:")) { - // Nothing to see here - move along - return ""; - } + if (!msg.startsWith("error:")) { + // Nothing to see here - move along + return ""; + } - // And extra information for certain error codes - const msgExtra = { - "8": " - Command requires idle state. Unlock machine?", - "152": " - Configuration is invalid. Maslow.yaml file may be corrupt. Turning off and back on again can often fix this issue.", - "153": " - Configuration is invalid. ESP32 probably did a panic reset. Config changes cannot be saved. Try restarting", - }; + // And extra information for certain error codes + const msgExtra = { + 8: " - Command requires idle state. Unlock machine?", + 152: " - Configuration is invalid. Maslow.yaml file may be corrupt. Turning off and back on again can often fix this issue.", + 153: " - Configuration is invalid. ESP32 probably did a panic reset. Config changes cannot be saved. Try restarting", + }; - return `${msg}${msgExtra[msg.split(":")[1]] || ""}`; -} + return `${msg}${msgExtra[msg.split(":")[1]] || ""}`; +}; /** Handle Maslow specific configuration messages * These would have all started with `$/Maslow_` which is expected to have been stripped away before calling this function */ const maslowMsgHandling = (msg) => { - const keyValue = msg.split("="); - const errMsgSuffix = `${MaslowErrMsgKeyValueSuffix}${msg}`; - if (keyValue.length != 2) { - return maslowErrorMsgHandling(`${MaslowErrMsgKeyValueCantUse} ${errMsgSuffix}`); - } - const key = keyValue[0] || ""; - const value = (keyValue[1] || "").trim(); - if (!key) { - return maslowErrorMsgHandling(`${MaslowErrMsgNoKey} ${errMsgSuffix}`); - } - if (!value) { - return maslowErrorMsgHandling(`${MaslowErrMsgNoValue} ${errMsgSuffix}`); - } - - const stdAction = (id, value) => { - document.getElementById(id).value = value; - loadedValues[id] = value; - } - const fullDimensionAction = (id, value) => { - stdAction(id, value); - return stdDimensionAction(value); - } - const stdDimensionAction = (value) => parseFloat(value); - const nullAction = () => { }; - - const msgExtra = { - "calibration_grid_size": (value) => stdAction("gridSize", value), - "calibration_grid_width_mm_X": (value) => stdAction("gridWidth", value), - "calibration_grid_height_mm_Y": (value) => stdAction("gridHeight", value), - "Retract_Current_Threshold": (value) => stdAction("retractionForce", value), - "vertical": (value) => stdAction("machineOrientation", value === "false" ? "horizontal" : "vertical"), - "trX": (value) => {initialGuess.tr.x = fullDimensionAction("machineWidth", value)}, - "trY": (value) => {initialGuess.tr.y = fullDimensionAction("machineHeight", value)}, - "trZ": (value) => {initialGuess.tr.z = stdDimensionAction(value)}, - "tlX": (value) => {initialGuess.tl.x = stdDimensionAction(value)}, - "tlY": (value) => {initialGuess.tl.y = stdDimensionAction(value)}, - "tlZ": (value) => {initialGuess.tl.z = stdDimensionAction(value)}, - "brX": (value) => {initialGuess.br.x = stdDimensionAction(value)}, - "brY": (value) => nullAction(), - "brZ": (value) => {initialGuess.br.z = stdDimensionAction(value)}, - "blX": (value) => nullAction(), - "blY": (value) => nullAction(), - "blZ": (value) => {initialGuess.bl.z = stdDimensionAction(value)}, - "Acceptable_Calibration_Threshold": (value) => {acceptableCalibrationThreshold = stdDimensionAction(value)}, - } - const action = msgExtra[key] || ""; - if (!action) { - return maslowErrorMsgHandling(`error: Could not find key for value in reference table. ${errMsgSuffix}`); - } - action(value); - - // Success - return an empty string - return ""; -} + const keyValue = msg.split("="); + const errMsgSuffix = `${MaslowErrMsgKeyValueSuffix}${msg}`; + if (keyValue.length != 2) { + return maslowErrorMsgHandling( + `${MaslowErrMsgKeyValueCantUse} ${errMsgSuffix}`, + ); + } + const key = keyValue[0] || ""; + const value = (keyValue[1] || "").trim(); + if (!key) { + return maslowErrorMsgHandling(`${MaslowErrMsgNoKey} ${errMsgSuffix}`); + } + if (!value) { + return maslowErrorMsgHandling(`${MaslowErrMsgNoValue} ${errMsgSuffix}`); + } + + const stdAction = (id, value) => { + document.getElementById(id).value = value; + loadedValues(id, value); + }; + const fullDimensionAction = (id, value) => { + stdAction(id, value); + return stdDimensionAction(value); + }; + const stdDimensionAction = (value) => parseFloat(value); + const nullAction = () => {}; + + const msgExtra = { + calibration_grid_size: (value) => stdAction("gridSize", value), + calibration_grid_width_mm_X: (value) => stdAction("gridWidth", value), + calibration_grid_height_mm_Y: (value) => stdAction("gridHeight", value), + Retract_Current_Threshold: (value) => stdAction("retractionForce", value), + vertical: (value) => + stdAction( + "machineOrientation", + value === "false" ? "horizontal" : "vertical", + ), + trX: (value) => { + initialGuess.tr.x = fullDimensionAction("machineWidth", value); + }, + trY: (value) => { + initialGuess.tr.y = fullDimensionAction("machineHeight", value); + }, + trZ: (value) => { + initialGuess.tr.z = stdDimensionAction(value); + }, + tlX: (value) => { + initialGuess.tl.x = stdDimensionAction(value); + }, + tlY: (value) => { + initialGuess.tl.y = stdDimensionAction(value); + }, + tlZ: (value) => { + initialGuess.tl.z = stdDimensionAction(value); + }, + brX: (value) => { + initialGuess.br.x = stdDimensionAction(value); + }, + brY: (value) => nullAction(), + brZ: (value) => { + initialGuess.br.z = stdDimensionAction(value); + }, + blX: (value) => nullAction(), + blY: (value) => nullAction(), + blZ: (value) => { + initialGuess.bl.z = stdDimensionAction(value); + }, + Acceptable_Calibration_Threshold: (value) => { + acceptableCalibrationThreshold = stdDimensionAction(value); + }, + }; + const action = msgExtra[key] || ""; + if (!action) { + return maslowErrorMsgHandling( + `error: Could not find key for value in reference table. ${errMsgSuffix}`, + ); + } + action(value); + + // Success - return an empty string + return ""; +}; const checkHomed = () => { - if (!maslowStatus.homed) { - const err_msg = `${M} does not know belt lengths. Please retract and extend before continuing.`; - alert(err_msg); + if (!maslowStatus.homed) { + const err_msg = `${M} does not know belt lengths. Please retract and extend before continuing.`; + alert(err_msg); - // Write to the console too, in case the system alerts are not visible - const msgWindow = document.getElementById('messages') - msgWindow.textContent = `${msgWindow.textContent}\n${err_msg}`; - msgWindow.scrollTop = msgWindow.scrollHeight; - } + // Write to the console too, in case the system alerts are not visible + const msgWindow = document.getElementById("messages"); + msgWindow.textContent = `${msgWindow.textContent}\n${err_msg}`; + msgWindow.scrollTop = msgWindow.scrollHeight; + } - return maslowStatus.homed; -} + return maslowStatus.homed; +}; /** Short hand convenience call to SendPrinterCommand with some preset values. * Uses the global function get_position, which is also a SendPrinterCommand with presets */ const sendCommand = (cmd) => { - SendPrinterCommand(cmd, true, get_Position); -} - -// The following functions are all defined as global functions, and are used by tablettab.html and other places -// They rely on the global function SendPrinterCommand defined in printercmd.js + SendPrinterCommand(cmd, true, get_Position); +}; /** Used to populate the config popup when it loads */ -function loadConfigValues() { - SendPrinterCommand(`$/${M}_vertical`); - SendPrinterCommand(`$/${M}_calibration_grid_width_mm_X`); - SendPrinterCommand(`$/${M}_calibration_grid_height_mm_Y`); - SendPrinterCommand(`$/${M}_calibration_grid_size`); - SendPrinterCommand(`$/${M}_Retract_Current_Threshold`); - SendPrinterCommand(`$/${M}_trX`); - SendPrinterCommand(`$/${M}_trY`); - SendPrinterCommand(`$/${M}_Acceptable_Calibration_Threshold`); -} +const loadConfigValues = () => { + SendPrinterCommand(`$/${M}_vertical`); + SendPrinterCommand(`$/${M}_calibration_grid_width_mm_X`); + SendPrinterCommand(`$/${M}_calibration_grid_height_mm_Y`); + SendPrinterCommand(`$/${M}_calibration_grid_size`); + SendPrinterCommand(`$/${M}_Retract_Current_Threshold`); + SendPrinterCommand(`$/${M}_trX`); + SendPrinterCommand(`$/${M}_trY`); + SendPrinterCommand(`$/${M}_Acceptable_Calibration_Threshold`); +}; /** Load all of the corner values */ -function loadCornerValues() { - SendPrinterCommand(`$/${M}_tlX`); - SendPrinterCommand(`$/${M}_tlY`); - SendPrinterCommand(`$/${M}_trX`); - SendPrinterCommand(`$/${M}_trY`); - SendPrinterCommand(`$/${M}_brX`); -} +const loadCornerValues = () => { + SendPrinterCommand(`$/${M}_tlX`); + SendPrinterCommand(`$/${M}_tlY`); + SendPrinterCommand(`$/${M}_trX`); + SendPrinterCommand(`$/${M}_trY`); + SendPrinterCommand(`$/${M}_brX`); +}; + +// The following functions are all defined as global functions, and are used by tablettab.html and other places +// They rely on the global function SendPrinterCommand defined in printercmd.js /** Save the Maslow configuration values */ -function saveConfigValues() { - let gridWidth = document.getElementById('gridWidth').value; - let gridHeight = document.getElementById('gridHeight').value; - let gridSize = document.getElementById('gridSize').value; - let retractionForce = document.getElementById('retractionForce').value; - let machineOrientation = document.getElementById('machineOrientation').value; - let machineWidth = document.getElementById('machineWidth').value; - let machineHeight = document.getElementById('machineHeight').value; - - var gridSpacingWidth = gridWidth / (gridSize - 1); - var gridSpacingHeight = gridHeight / (gridSize - 1); - - //If the grid spacing is going to be more than 200 don't save the values - if (gridSpacingWidth > 260 || gridSpacingHeight > 260) { - alert('Grid spacing is too large. Please reduce the grid size or increase the number of points.'); - return; - } - - if (gridWidth != loadedValues['gridWidth']) { - sendCommand(`$/${M}_calibration_grid_width_mm_X=${gridWidth}`); - } - if (gridHeight != loadedValues['gridHeight']) { - sendCommand(`$/${M}_calibration_grid_height_mm_Y=${gridHeight}`); - } - if (gridSize != loadedValues['gridSize']) { - sendCommand(`$/${M}_calibration_grid_size=${gridSize}`); - } - if (retractionForce != loadedValues['retractionForce']) { - sendCommand(`$/${M}_Retract_Current_Threshold=${retractionForce}`); - } - if (machineOrientation != loadedValues['machineOrientation']) { - sendCommand(`$/${M}_vertical=${machineOrientation === 'horizontal' ? 'false' : 'true'}`); - } - if (machineWidth != loadedValues['machineWidth'] || machineHeight != loadedValues['machineHeight']) { - sendCommand(`$/${M}_tlX=0`); - sendCommand(`$/${M}_tlY=${machineHeight}`); - sendCommand(`$/${M}_trX=${machineWidth}`); - sendCommand(`$/${M}_trY=${machineHeight}`); - sendCommand(`$/${M}_brX=${machineWidth}`); - } - - refreshSettings(current_setting_filter); - saveMaslowYaml(); - loadCornerValues(); - - hideModal('configuration-popup'); -} +const saveConfigValues = () => { + let gridWidth = document.getElementById("gridWidth").value; + let gridHeight = document.getElementById("gridHeight").value; + let gridSize = document.getElementById("gridSize").value; + let retractionForce = document.getElementById("retractionForce").value; + let machineOrientation = document.getElementById("machineOrientation").value; + let machineWidth = document.getElementById("machineWidth").value; + let machineHeight = document.getElementById("machineHeight").value; + + var gridSpacingWidth = gridWidth / (gridSize - 1); + var gridSpacingHeight = gridHeight / (gridSize - 1); + + //If the grid spacing is going to be more than 200 don't save the values + if (gridSpacingWidth > 260 || gridSpacingHeight > 260) { + alert( + "Grid spacing is too large. Please reduce the grid size or increase the number of points.", + ); + return; + } + + if (gridWidth !== loadedValues("gridWidth")) { + sendCommand(`$/${M}_calibration_grid_width_mm_X=${gridWidth}`); + } + if (gridHeight !== loadedValues("gridHeight")) { + sendCommand(`$/${M}_calibration_grid_height_mm_Y=${gridHeight}`); + } + if (gridSize !== loadedValues("gridSize")) { + sendCommand(`$/${M}_calibration_grid_size=${gridSize}`); + } + if (retractionForce !== loadedValues("retractionForce")) { + sendCommand(`$/${M}_Retract_Current_Threshold=${retractionForce}`); + } + if (machineOrientation !== loadedValues("machineOrientation")) { + sendCommand( + `$/${M}_vertical=${machineOrientation === "horizontal" ? "false" : "true"}`, + ); + } + if ( + machineWidth !== loadedValues("machineWidth") || + machineHeight != loadedValues("machineHeight") + ) { + sendCommand(`$/${M}_tlX=0`); + sendCommand(`$/${M}_tlY=${machineHeight}`); + sendCommand(`$/${M}_trX=${machineWidth}`); + sendCommand(`$/${M}_trY=${machineHeight}`); + sendCommand(`$/${M}_brX=${machineWidth}`); + } + + const common = new Common(); + refreshSettings(common.current_setting_filter); + saveMaslowYaml(); + loadCornerValues(); + + hideModal("configuration-popup"); +}; +export { + MaslowErrMsgKeyValueCantUse, + MaslowErrMsgNoKey, + MaslowErrMsgNoValue, + MaslowErrMsgNoMatchingKey, + MaslowErrMsgKeyValueSuffix, + maslowInfoMsgHandling, + maslowErrorMsgHandling, + maslowMsgHandling, + checkHomed, + sendCommand, + loadConfigValues, + loadCornerValues, + saveConfigValues, +}; diff --git a/www/js/modaldlg.js b/www/js/modaldlg.js index f524a642f..e599c22cc 100644 --- a/www/js/modaldlg.js +++ b/www/js/modaldlg.js @@ -1,52 +1,47 @@ -// Create the modal -var listmodal = []; +import { id } from "./common.js"; +/** A list of currently opened modals */ +const listmodal = []; -function setactiveModal(html_template, closefunc) { - if (typeof id(html_template) === 'undefined') { - console.error(`Error: no ${html_template}`); +const setactiveModal = (html_template_name, closefunc) => { + const mdlTemplate = id(html_template_name); + if (!mdlTemplate) { + console.error(`Error: no template named '${html_template_name}'`); return null; } - var modal = new Object; - modal.element = id(html_template); + const modal = new Object; + modal.element = mdlTemplate; modal.id = listmodal.length; - modal.name = html_template; - if (typeof closefunc !== 'undefined') modal.closefn = closefunc; - else modal.closefn = myfnclose; - listmodal.push(modal) + modal.name = html_template_name; + modal.closefn = (closefunc instanceof Function) ? closefunc : (response) => {/* Do Nothing*/}; + + listmodal.push(modal); //console.log("Creation of modal " + modal.name + " with ID " +modal.id); - return listmodal[listmodal.length - 1];; + return listmodal[listmodal.length - 1]; } -function getactiveModal() { - if (listmodal.length > 0) { - return listmodal[listmodal.length - 1]; - } else return null; -} +const getactiveModal = () => (listmodal.length > 0) ? listmodal[listmodal.length - 1] : null; -// open the modal -function showModal() { - var currentmodal = getactiveModal(); +/** Show the modal dialog */ +const showModal = () => { + const currentmodal = getactiveModal(); + if (!currentmodal) { + return; + } currentmodal.element.style.display = "block"; //console.log("Show modal " + currentmodal.name + " with ID " + currentmodal.id ); } -// When the user clicks on (x), close the modal -function closeModal(response) { - var currentmodal = getactiveModal(); - if (currentmodal != null) { - currentmodal.element.style.display = "none"; - var closefn = currentmodal.closefn; - //console.log("Deletetion of modal " + currentmodal.name + " with ID " + currentmodal.id); - listmodal.pop(); - delete currentmodal; - currentmodal = getactiveModal(); - //if (currentmodal != null)console.log("New active modal is " + currentmodal.name + " with ID " + currentmodal.id); - //else console.log("No active modal"); - closefn(response); +/** Close the modal dialog - normally triggered when the user clicks on */ +const closeModal = (response) => { + let currentmodal = listmodal.pop(); + if (!currentmodal) { + // the list of modals is empty + return; } + + currentmodal.element.style.display = "none"; + currentmodal.closefn(response); } -//default close function -function myfnclose(value) { - //console.log("modale closed: " + value); -} \ No newline at end of file + +export { listmodal, closeModal, getactiveModal, setactiveModal, showModal }; diff --git a/www/js/navbar.js b/www/js/navbar.js index d3f006733..9f52617b4 100644 --- a/www/js/navbar.js +++ b/www/js/navbar.js @@ -1,4 +1,21 @@ -// import - disable_items, displayNone, getChecked, id, setChecked, setHTML, opentab, creditsdlg, cameratab, configtab, changepassworddlg, showpreferencesdlg, translate_text_item, DisconnectLogin, setupdlg, settingstab +import { + disable_items, + displayNone, + getChecked, + id, + setChecked, + setHTML, + opentab, + creditsdlg, + cameratab, + configtab, + changepassworddlg, + showpreferencesdlg, + translate_text_item, + DisconnectLogin, + setupdlg, + settingstab, +} from "./common.js"; /** Set up the event handlers for the navbar */ const navbar = () => { @@ -21,7 +38,7 @@ const navbar = () => { cameratab(); configtab(); - // settingstab(); + settingstab(); }; const enableItem = (itemName) => { @@ -64,3 +81,5 @@ const ontoggleLock = (forcevalue) => { } } }; + +export { navbar, ontoggleLock }; diff --git a/www/js/numpad.js b/www/js/numpad.js index 878f65320..90c039d61 100644 --- a/www/js/numpad.js +++ b/www/js/numpad.js @@ -1,16 +1,21 @@ -var numpad = { +import { goAxisByValue, id } from "./common.js"; + +const numpad = { // (A) CREATE NUMPAD HTML hwrap: null, // numpad wrapper container hpad: null, // numpad itself hdisplay: null, // number display hbwrap: null, // buttons wrapper hbuttons: {}, // individual buttons - init: function(){ + init: () => { + if (id("numWrap")) { + // Singleton - test if numWrap already exists + return; + } // (A1) WRAPPER numpad.hwrap = document.createElement("div"); numpad.hwrap.id = "numWrap"; - // (A2) ENTIRE NUMPAD ITSELF numpad.hpad = document.createElement("div"); numpad.hpad.id = "numPad"; @@ -18,7 +23,7 @@ var numpad = { numpad.hpad.tabindex = "0"; numpad.hpad.contentEditable = false; numpad.hpad.addEventListener("keydown", numpad.keypr); - + // (A3) DISPLAY numpad.hdisplay = document.createElement("input"); numpad.hdisplay.id = "numDisplay"; @@ -27,8 +32,6 @@ var numpad = { numpad.hdisplay.value = "0"; numpad.hpad.appendChild(numpad.hdisplay); - - // (A4) NUMBER BUTTONS numpad.hbwrap = document.createElement("div"); numpad.hbwrap.id = "numBWrap"; @@ -44,25 +47,25 @@ var numpad = { numpad.hbuttons[txt] = button; }; - var spacer = function() { + var spacer = function () { buttonator("", "spacer", null); - } + } // 7 8 9 _ Goto - for (var i=7; i<=9; i++) { buttonator(i, "num", numpad.digit); } + for (var i = 7; i <= 9; i++) { buttonator(i, "num", numpad.digit); } buttonator("⤆", "del", numpad.delete); spacer(); //buttonator("Goto", "goto", numpad.gotoCoordinate); //This is a nice feature, but it uses gcode instead of jog which is triggering errors buttonator("", "num", numpad.doNothing); // 4 5 6 C _ _ - for (var i=4; i<=6; i++) { buttonator(i, "num", numpad.digit); } + for (var i = 4; i <= 6; i++) { buttonator(i, "num", numpad.digit); } buttonator("C", "clr", numpad.reset); spacer(); spacer(); // 1 2 3 +- Set - for (var i=1; i<=3; i++) { buttonator(i, "num", numpad.digit); } + for (var i = 1; i <= 3; i++) { buttonator(i, "num", numpad.digit); } buttonator("", "num", (numpad.doNothing)); //buttonator("+-", "num", numpad.toggleSign); buttonator("Set", "set", numpad.setCoordinate); @@ -73,152 +76,151 @@ var numpad = { buttonator("", "get", numpad.doNothing); buttonator("Cancel", "cxwide", numpad.hide); - // (A6) ATTACH NUMPAD TO HTML BODY document.body.appendChild(numpad.hwrap); }, - // (B) BUTTON ACTIONS - // (B1) CURRENTLY SELECTED FIELD + MAX LIMIT - nowTarget: null, // Current selected input field - nowMax: 0, // Current max allowed digits - - keypr: function(event) { - event.preventDefault(); - switch(event.key) { - case "Escape": - case "q": - numpad.hide(); - break; - case "0": - case "1": - case "2": - case "3": - case "4": - case "5": - case "6": - case "7": - case "8": - case "9": - numpad.digitv(event.key); - break; - case '.': - numpad.dot(); - break; - case 'Backspace': - case 'Del': - numpad.delete(); - break; - case 'x': - case 'X': - numpad.reset(); - break; - case 'c': - case 'C': - numpad.reset(); - break; - case 'g': - case 'G': - numpad.recall(); - break; - case 's': - case 'S': - case 'Enter': - numpad.setCoordinate(); - break; - } - }, - - // (B2) NUMBER (0 TO 9) - - digitv: function(n) { - var current = numpad.hdisplay.value; - if (current.length < numpad.nowMax) { - if (current=="0") { - numpad.hdisplay.value = n; - } else { - numpad.hdisplay.value += n; - } - } - }, - - digit: function() { - numpad.digitv(this.innerHTML); - }, - - // Change sign - toggleSign: function(){ - numpad.hdisplay.value = -numpad.hdisplay.value; - }, - - // Do nothing function - doNothing: function(){}, - - - // ADD DECIMAL POINT - dot: function(){ - if (numpad.hdisplay.value.indexOf(".") == -1) { - if (numpad.hdisplay.value=="0") { - numpad.hdisplay.value = "0."; - } else { - numpad.hdisplay.value += "."; - } - } - }, - - // BACKSPACE - delete: function(){ + // (B) BUTTON ACTIONS + // (B1) CURRENTLY SELECTED FIELD + MAX LIMIT + nowTarget: null, // Current selected input field + nowMax: 0, // Current max allowed digits + + keypr: function (event) { + event.preventDefault(); + switch (event.key) { + case "Escape": + case "q": + numpad.hide(); + break; + case "0": + case "1": + case "2": + case "3": + case "4": + case "5": + case "6": + case "7": + case "8": + case "9": + numpad.digitv(event.key); + break; + case '.': + numpad.dot(); + break; + case 'Backspace': + case 'Del': + numpad.delete(); + break; + case 'x': + case 'X': + numpad.reset(); + break; + case 'c': + case 'C': + numpad.reset(); + break; + case 'g': + case 'G': + numpad.recall(); + break; + case 's': + case 'S': + case 'Enter': + numpad.setCoordinate(); + break; + } + }, + + // (B2) NUMBER (0 TO 9) + + digitv: function (n) { + var current = numpad.hdisplay.value; + if (current.length < numpad.nowMax) { + if (current == "0") { + numpad.hdisplay.value = n; + } else { + numpad.hdisplay.value += n; + } + } + }, + + digit: function () { + numpad.digitv(this.innerHTML); + }, + + // Change sign + toggleSign: function () { + numpad.hdisplay.value = -numpad.hdisplay.value; + }, + + // Do nothing function + doNothing: function () { }, + + + // ADD DECIMAL POINT + dot: function () { + if (numpad.hdisplay.value.indexOf(".") == -1) { + if (numpad.hdisplay.value == "0") { + numpad.hdisplay.value = "0."; + } else { + numpad.hdisplay.value += "."; + } + } + }, + + // BACKSPACE + delete: function () { var length = numpad.hdisplay.value.length; if (length == 1) { numpad.hdisplay.value = 0; } else { numpad.hdisplay.value = numpad.hdisplay.value.substring(0, length - 1); } }, // (B5) CLEAR ALL - reset: function(){ numpad.hdisplay.value = "0"; }, + reset: function () { numpad.hdisplay.value = "0"; }, // (B6) Recall - recall: function(){ + recall: function () { numpad.hdisplay.value = numpad.nowTarget.textContent; }, - setCoordinate: function(){ + setCoordinate: function () { numpad.nowTarget.textContent = numpad.hdisplay.value; saveJogDists(); //setAxisByValue(numpad.nowTarget.dataset.axis, numpad.hdisplay.value); numpad.hide(); }, - gotoCoordinate: function(){ + gotoCoordinate: function () { numpad.nowTarget.textContent = numpad.hdisplay.value; goAxisByValue(numpad.nowTarget.dataset.axis, numpad.hdisplay.value); numpad.hide(); }, // (C) ATTACH NUMPAD TO INPUT FIELD - attach: function(opt){ - // OPTIONS - // target: required, ID of target field. - // max: optional, maximum number of characters. Default 255. - // decimal: optional, allow decimal? Default true. + attach: function (opt) { + // OPTIONS + // target: required, ID of target field. + // max: optional, maximum number of characters. Default 255. + // decimal: optional, allow decimal? Default true. // (C1) DEFAULT OPTIONS if (opt.max === undefined) { opt.max = 255; } if (opt.decimal === undefined) { opt.decimal = true; } - + // (C2) GET + SET TARGET OPTIONS - var target = document.getElementById(opt.target); - target.readOnly = true; - target.dataset.max = opt.max; - target.dataset.decimal = opt.decimal; - target.dataset.axis = opt.axis; - target.dataset.elementName = opt.target; - target.addEventListener("click", numpad.show); + const target = document.getElementById(opt.target); + if (target) { + target.readOnly = true; + target.dataset.max = opt.max; + target.dataset.decimal = opt.decimal; + target.dataset.axis = opt.axis; + target.dataset.elementName = opt.target; + target.addEventListener("click", numpad.show); + } }, // (D) SHOW NUMPAD - show: function() { - - + show: function () { // (D1) SET CURRENT DISPLAY VALUE //var cv = this.value; var cv = ""; @@ -239,12 +241,15 @@ var numpad = { numpad.nowTarget = this; // (D5) SHOW NUMPAD - numpad.hwrap.classList.add("open"); + numpad.hwrap.classList.add("open"); // numpad.hpad.focus(); }, // (E) HIDE NUMPAD - hide: function(){ numpad.hwrap.classList.remove("open"); }, + hide: function () { numpad.hwrap.classList.remove("open"); }, }; + window.addEventListener("DOMContentLoaded", numpad.init); + +export { numpad }; diff --git a/www/js/passworddlg.js b/www/js/passworddlg.js index d905961b3..0294d9c19 100644 --- a/www/js/passworddlg.js +++ b/www/js/passworddlg.js @@ -1,4 +1,15 @@ -// import conErr, displayBlock, displayNone, id, setHTML, closeModal, setactiveModal, showModal, SendGetHttp, translate_text_item +import { + conErr, + displayBlock, + displayNone, + id, + setHTML, + closeModal, + setactiveModal, + showModal, + SendGetHttp, + translate_text_item, +} from "./common.js"; /** Change Password dialog */ const changepassworddlg = () => { @@ -29,15 +40,9 @@ function checkpassword() { setHTML("password_content", ""); displayNone("change_password_btn"); if (pwd1 !== pwd2) { - setHTML( - "password_content", - translate_text_item("Passwords do not matches!"), - ); + setHTML("password_content", translate_text_item("Passwords do not matches!")); } else if (pwd1.length < 1 || pwd1.length > 16 || pwd1.indexOf(" ") > -1) { - setHTML( - "password_content", - translate_text_item("Password must be >1 and <16 without space!"), - ); + setHTML("password_content", translate_text_item("Password must be >1 and <16 without space!")); } else { displayBlock("change_password_btn"); } @@ -67,3 +72,5 @@ function SubmitChangePassword() { displayNone("change_password_content"); SendGetHttp(url, ChangePasswordsuccess, ChangePasswordfailed); } + +export { changepassworddlg }; diff --git a/www/js/prefDefs.js b/www/js/prefDefs.js new file mode 100644 index 000000000..10760477a --- /dev/null +++ b/www/js/prefDefs.js @@ -0,0 +1,192 @@ +/** Definitions for all the preferences */ +export const prefDefs = { + "language_list": { + "defValue": "en", + "valueType": "select", + "fieldId": "language_preferences", + "label": "language", + }, + + "enable_lock_UI": { "defValue": false, "valueType": "bool", "label": "Enable lock interface" }, + "enable_ping": { "defValue": true, "valueType": "bool", "label": "Connection monitoring" }, + "enable_DHT": { "defValue": false, "valueType": "bool", "label": "Show DHT output" }, + + "enable_camera": { + "defValue": false, + "valueType": "panel", + "panel": "camera_preferences", + "fieldId": "show_camera_panel", + "label": "Show camera panel", + "prefDefs": { + "auto_load_camera": { "defValue": false, "valueType": "bool", "label": "Auto load camera" }, + "camera_address": { + "defValue": "", + "valueType": "enctext", + "label": "Camera address", + "placeholder": "Camera address", + "inpClass": "w14", + }, + }, + }, + "enable_control_panel": { + "defValue": true, + "valueType": "panel", + "panel": "control_preferences", + "fieldId": "show_control_panel", + "label": "Show control panel", + "prefDefs": { + "interval_positions": { + "defValue": 3, + "valueType": "int", + "units": "sec", + "label": "Position Refresh Time", + "inpClass": "w4", + "min": 1, + "max": 99 + }, + "xy_feedrate": { + "defValue": 2500, + "valueType": "float", + "units": "mm/min", + "label": "XY axis feedrate", + "inpClass": "w8", + "min": 0.00001 + }, + "z_feedrate": { + "defValue": 300, + "valueType": "float", + "units": "mm/min", + "label": "Z axis feedrate", + "inpClass": "w8", + "min": 0.00001 + }, + "a_feedrate": { + "defValue": 100, + "valueType": "float", + "units": "mm/min", + "label": "A axis feedrate", + "inpClass": "w6", + "min": 0.00001 + }, + "b_feedrate": { + "defValue": 100, + "valueType": "float", + "units": "mm/min", + "label": "B axis feedrate", + "inpClass": "w6", + "min": 0.00001 + }, + "c_feedrate": { + "defValue": 100, + "valueType": "float", + "units": "mm/min", + "label": "C axis feedrate", + "inpClass": "w6", + "min": 0.00001 + }, + }, + }, + + "enable_grbl_panel": { + "defValue": false, + "valueType": "panel", + "panel": "grbl_preferences", + "fieldId": "show_grbl_panel", + "label": "Show GRBL panel", + "prefDefs": { + "autoreport_interval": { + "defValue": 50, + "valueType": "int", + "units": "ms", + "label": "AutoReport Interval", + "inpClass": "w6", + "min": 0, + "max": 30000, + }, + "interval_status": { + "defValue": 3, + "valueType": "int", + "units": "sec", + "label": "Status Refresh Time", + "inpClass": "w4", + "min": 0, + "max": 99 + }, + "enable_grbl_probe_panel": { + "defValue": false, + "valueType": "panel", + "panel": "grblprobetablink", + "fieldId": "show_grbl_probe_tab", + "label": "Show probe panel", + "prefDefs": { + "probemaxtravel": { + "defValue": 40, + "valueType": "float", + "units": "mm", + "label": "Max travel", + "inpClass": "w8", + "min": 0.00001, + "max": 9999 + }, + "probefeedrate": { + "defValue": 100, + "valueType": "float", + "units": "mm/min", + "label": "Feed rate", + "inpClass": "w8", + "min": 0.00001, + "max": 9999 + }, + "probetouchplatethickness": { + "defValue": 0.5, + "valueType": "float", + "units": "mm", + "label": "Touch plate thickness", + "inpClass": "w8", + "min": 0, + "max": 9999 + }, + "proberetract": { + "defValue": 1.0, + "valueType": "float", + "units": "mm", + "label": "Retract distance", + "inpClass": "w8", + "min": 0, + "max": 9999 + }, + } + } + } + }, + + "enable_files_panel": { + "defValue": true, + "valueType": "panel", + "panel": "files_preferences", + "fieldId": "show_files_panel", + "label": "Show files panel", + "prefDefs": { + "has_TFT_SD": { "defValue": false, "valueType": "bool", "label": "TFT SD card" }, + "has_TFT_USB": { "defValue": false, "valueType": "bool", "label": "TFT USB disk" }, + "f_filters": { + "defValue": "gco;gcode;nc", + "valueType": "text", + "label": "File extensions (use ; to separate)", + "inpClass": "w25", + }, + }, + }, + + "enable_commands_panel": { + "defValue": true, + "valueType": "panel", + "panel": "cmd_preferences", + "fieldId": "show_commands_panel", + "label": "Show commands panel", + "prefDefs": { + "enable_autoscroll": { "defValue": true, "valueType": "bool", "label": "Autoscroll" }, + "enable_verbose_mode": { "defValue": true, "valueType": "bool", "label": "Verbose mode" }, + }, + }, +}; diff --git a/www/js/prefUtils.js b/www/js/prefUtils.js new file mode 100644 index 000000000..6145fb457 --- /dev/null +++ b/www/js/prefUtils.js @@ -0,0 +1,133 @@ +import { prefDefs, HTMLDecode, translate_text_item } from "./common.js"; + +// Add in the validation function definitions to the prefDefs +prefDefs.enable_grbl_panel.prefDefs.autoreport_interval.valFunc = (value) => { + const vInt = Number.parseInt(value); + return !Number.isNaN(vInt) && (vInt === 0 || (vInt >= 50 && vInt <= 30000)) + ? "" + : translate_text_item("Value of auto-report must be 0 or between 50ms and 30000ms !!"); +}; + +prefDefs.enable_files_panel.prefDefs.f_filters.valFunc = (value) => { + const extPat = /^[a-z0-9;]*$/i; + return value.match(extPat) ? "" : translate_text_item("Only alphanumeric chars separated by ; for extensions filters !!"); +}; + +/** Return the `fieldId`, if defined, otherwise return the `key` */ +const buildFieldId = (key, value) => value.fieldId || key; + +/** Build a complete set of preferences from the prefDefs. + * Useful for initialisation of preferences.json + * * `defValue` - is the original value as defined in the prefDefs above + * * `fileValue` - is the value as currently stored in the value (or that soon will be if the preferences are being saved) + * * `value` - is the value as currently set and in use in the UI + */ +const buildPrefsFromDefs = (prefLevel = prefDefs) => { + const prefs = {}; + for (const [key, value] of Object.entries(prefLevel)) { + prefs[key] = { + valueType: value.valueType, + defValue: value.defValue, + fileValue: value.defValue, + value: value.defValue, + fieldId: buildFieldId(key, value), + }; + + if (value.valueType === "enctext") { + // all values are stored as HTML encoded text + prefs[key].defValue = HTMLDecode(prefs[key].defValue); + prefs[key].fileValue = HTMLDecode(prefs[key].fileValue); + prefs[key].value = HTMLDecode(prefs[key].value); + } + + if ("prefDefs" in value) { + // Transfer the child level values back to this parent level + for (const [cKey, cValue] of Object.entries( + buildPrefsFromDefs(value.prefDefs), + )) { + prefs[cKey] = cValue; + } + } + } + + return prefs; +}; + +/** Get the named preference object */ +const getPref = (prefName) => { + let pref = preferences[prefName]; + if (!pref) { + // try to find it by looking for the fieldId + for (const [key, value] of Object.entries(preferences)) { + if (value.fieldId === prefName) { + pref = value; + break; + } + } + } + if (!pref) { + console.error( + stdErrMsg( + "Unknown Preference", + `'${prefName}' not found as a preference key or as the fieldId within a preference value`, + ), + ); + return undefined; + } + return pref; +}; + +/** Get the part of the prefDefs structure identified by the name supplied. + * If the name is not found then undefined is returned + */ +const getPrefDefPath = (prefName) => { + const prefPath = prefName + .trim() + .replace(".", ".prefDefs.") + .replace(".prefDefs.prefDefs.", ".prefDefs."); + let pref = prefDefs; + for (let ix = 0; ix < prefPath.length; ix++) { + if (typeof pref[prefPath[ix]] === "undefined") { + return undefined; + } + pref = pref[prefPath[ix]]; + } + return pref; +}; + +/** Get the named preference value */ +const getPrefValue = (prefName) => { + const pref = getPref(prefName); + if (!pref) { + return undefined; + } + return pref.value; +}; + +/** Set the preference item to the supplied value. + * Returns true for success, false for failure - usually because the preference item does not exist + */ +const setPrefValue = (prefName, value) => { + const pref = getPrefDefPath(prefName); + if (typeof pref === "undefined") { + return false; + } + // TODO: test the typeof the value is compatible with the valueType + pref.value = value; + return true; +}; + +/** The actual preferences as used throught the app */ +const preferences = buildPrefsFromDefs(prefDefs); + +/** Helper method to get the `enable_ping` preference value */ +const enable_ping = () => getPrefValue("enable_ping"); + +export { + buildFieldId, + enable_ping, + getPref, + getPrefValue, + setPrefValue, + preferences, +}; diff --git a/www/js/preferencesdlg.js b/www/js/preferencesdlg.js index cfd561664..86befe257 100644 --- a/www/js/preferencesdlg.js +++ b/www/js/preferencesdlg.js @@ -1,137 +1,505 @@ +import { + Monitor_check_autoscroll, + Monitor_check_verbose_mode, + Common, + get_icon_svg, + prefDefs, + conErr, + displayBlock, + displayFlex, + displayNone, + checkValue, + getChecked, + getValue, + id, + setChecked, + setValue, + setHTML, + stdErrMsg, + clear_drop_menu, + closeModal, + setactiveModal, + showModal, + alertdlg, + confirmdlg, + camera_GetAddress, + buildFieldId, + getPref, + getPrefValue, + preferences, + build_file_filter_list, + onAutoReportIntervalChange, + reportNone, + grblpanel, + SendFileHttp, + SendGetHttp, + build_language_list, + ontoggleLock, + translate_text_item, + build_HTML_setting_list, + handlePing, + translate_text, +} from "./common.js"; + //Preferences dialog -var preferenceslist = []; -var language_save = language; -var default_preferenceslist = []; -var defaultpreferenceslist = "[{\ - \"language\":\"en\",\ - \"enable_lock_UI\":\"false\",\ - \"enable_ping\":\"true\",\ - \"enable_DHT\":\"false\",\ - \"enable_camera\":\"false\",\ - \"auto_load_camera\":\"false\",\ - \"camera_address\":\"\",\ - \"enable_redundant\":\"false\",\ - \"enable_probe\":\"false\",\ - \"enable_control_panel\":\"true\",\ - \"enable_grbl_panel\":\"false\",\ - \"autoreport_interval\":\"50\",\ - \"interval_positions\":\"3\",\ - \"interval_status\":\"3\",\ - \"xy_feedrate\":\"2500\",\ - \"z_feedrate\":\"300\",\ - \"a_feedrate\":\"100\",\ - \"b_feedrate\":\"100\",\ - \"c_feedrate\":\"100\",\ - \"e_feedrate\":\"400\",\ - \"e_distance\":\"5\",\ - \"f_filters\":\"gco;gcode\",\ - \"enable_files_panel\":\"true\",\ - \"has_TFT_SD\":\"false\",\ - \"has_TFT_USB\":\"false\",\ - \"enable_commands_panel\":\"true\",\ - \"enable_autoscroll\":\"true\",\ - \"enable_verbose_mode\":\"true\",\ - \"enable_grbl_probe_panel\":\"false\",\ - \"probemaxtravel\":\"40\",\ - \"probefeedrate\":\"100\",\ - \"proberetract\":\"1.0\",\ - \"probetouchplatethickness\":\"0.5\"\ - }]"; -var preferences_file_name = '/preferences.json'; - -function initpreferences() { - defaultpreferenceslist = "[{\ - \"language\":\"en\",\ - \"enable_lock_UI\":\"false\",\ - \"enable_ping\":\"true\",\ - \"enable_DHT\":\"false\",\ - \"enable_camera\":\"false\",\ - \"auto_load_camera\":\"false\",\ - \"camera_address\":\"\",\ - \"number_extruders\":\"1\",\ - \"is_mixed_extruder\":\"false\",\ - \"enable_redundant\":\"false\",\ - \"enable_probe\":\"false\",\ - \"enable_control_panel\":\"true\",\ - \"enable_grbl_panel\":\"true\",\ - \"autoreport_interval\":\"50\",\ - \"interval_positions\":\"3\",\ - \"interval_status\":\"3\",\ - \"xy_feedrate\":\"2500\",\ - \"z_feedrate\":\"300\",\ - \"a_feedrate\":\"100\",\ - \"b_feedrate\":\"100\",\ - \"c_feedrate\":\"100\",\ - \"e_feedrate\":\"400\",\ - \"e_distance\":\"5\",\ - \"enable_files_panel\":\"true\",\ - \"has_TFT_SD\":\"false\",\ - \"has_TFT_USB\":\"false\",\ - \"f_filters\":\"g;G;gco;GCO;gcode;GCODE;nc;NC;ngc;NCG;tap;TAP;txt;TXT\",\ - \"enable_commands_panel\":\"true\",\ - \"enable_autoscroll\":\"true\",\ - \"enable_verbose_mode\":\"true\",\ - \"enable_grbl_probe_panel\":\"false\",\ - \"probemaxtravel\":\"40\",\ - \"probefeedrate\":\"100\",\ - \"proberetract\":\"1.0\",\ - \"probetouchplatethickness\":\"0.5\"\ - }]"; - - displayNone('DHT_pref_panel'); - displayBlock('grbl_pref_panel'); - displayTable('has_tft_sd'); - displayTable('has_tft_usb'); - - default_preferenceslist = JSON.parse(defaultpreferenceslist); -} + +const prefFile = "/preferences.json"; + +const buildElem = (elem, contents, classVal) => { + const elemPanel = document.createElement(elem); + if (classVal) { + elemPanel.setAttribute("class", classVal); + } + elemPanel.innerHTML = contents; + return elemPanel; +}; + +const buildDiv = (contents, classVal) => buildElem("div", contents, classVal); +const buildTable = (contents, classVal) => buildElem("table", contents, classVal); + +const buildDivPanel = (contents) => buildDiv(`
${contents}
`, "panel panel-default"); + +const buildTdIcon = (icon) => `${get_icon_svg(icon)} `; +const buildTdLabel = (key, value) => `${translate_text_item(value.label || key, true)}: `; +const buildTdInp = (inpFld, key, value) => `
${inpFld}${buildSpnErrFld(key, value)}
`; +const buildSpnErrFld = (key, value) => ``; + +const buildFieldIdAttr = (key, value) => `id="${buildFieldId(key, value)}"`; +const buildMinMaxAttr = (value) => `${typeof value.min !== "undefined" ? ` min="${value.min}"` : ""}${typeof value.max !== "undefined" ? ` max="${value.max}"` : ""}`; +const buildPlaceholderAttr = (value) => value.placeholder ? `placeholder="${value.placeholder}" translateph` : ""; + +const setGroupId = (elem, fId) => elem.setAttribute("id", `${fId}_group`); + +/** Build an input within a label for a checkbox element */ +const buildCheckBox = (key, value) => { + const inpCheckBox = ``; + return ``; +}; + +/** Generate a panel controlled by a checkbox */ +const buildPanel = (key, value, parentElem, isFirstLevel = false) => { + const fId = buildFieldId(key, value); + const inpCheckBox = buildCheckBox(key, value); + const panelCheckBox = isFirstLevel + ? buildDivPanel(`
${inpCheckBox}
`) + : buildDiv(inpCheckBox, "checkbox"); + + setGroupId(panelCheckBox, fId); + parentElem.append(panelCheckBox); + + // prefDefs is tested for, but we do expect it always to be present + if ("prefDefs" in value) { + const pBody = buildDiv("", "panel-body"); + if ("panel" in value) { + pBody.setAttribute("id", value.panel); + } + panelCheckBox.append(pBody); + id(fId).addEventListener("click", (event) => togglePanel(fId, value.panel)); + // Loop around for the child elements + buildDialog(pBody, value.prefDefs); + } +}; + +/** Generate a checkbox that represents a boolean value */ +const buildBoolean = (key, value, parentElem) => { + const fId = buildFieldId(key, value); + const panelCheckBox = buildDiv(buildCheckBox(key, value), "checkbox"); + + setGroupId(panelCheckBox, fId); + parentElem.append(panelCheckBox); + id(fId).addEventListener("click", (event) => handleCheckboxClick(fId)); +}; + +/** Generate a mini table for various numeric inputs */ +const buildNumeric = (key, value, parentElem) => { + const fId = buildFieldId(key, value); + const inpNFld = ``; + const inpNTd = buildTdInp(inpNFld, key, value); + + const unitTd = `
${value.units}
`; + + const inpNTable = buildTable( + `${buildTdLabel(key, value)}${inpNTd}${unitTd}`, + ); + setGroupId(inpNTable, fId); + // Check for feedrate that might not be visible + if (fId.endsWith("_feedrate")) { + if (["a_feedrate", "b_feedrate", "c_feedrate"].includes(fId)) { + inpNTable.setAttribute("class", "hide_it topmarginspace"); + } + } + + parentElem.append(inpNTable); + id(fId).addEventListener("change", (event) => { + if (CheckValue(fId, value)) { + handleInputChange(fId); + } + }); +}; + +/** Generate a mini table for text inputs */ +const buildText = (key, value, parentElem) => { + const fId = buildFieldId(key, value); + const inpTFld = ``; + const inpTTd = `
${inpTFld}${buildSpnErrFld(value, key)}
`; + + const inpTTable = buildTable(`${buildTdLabel(key, value)}${inpTTd}`); + setGroupId(inpTTable, fId); + + parentElem.append(inpTTable); + id(fId).addEventListener("change", (event) => { + if (CheckValue(fId, value)) { + handleInputChange(fId); + } + }); +}; + +/** Generate a mini table for selects */ +const buildSelect = (key, value, parentElem) => { + const fId = buildFieldId(key, value); + const inpSTable = buildTable( + `${buildTdIcon("flag")}${build_language_list(fId)}`, + ); + // Use the key for the containing table, instead of the fId, which has been used for the select + inpSTable.setAttribute("id", key); + setGroupId(inpSTable, fId); + + parentElem.append(inpSTable); + id(fId).addEventListener("change", (event) => { + if (CheckValue(fId, value)) { + handleInputChange(fId); + } + }); +}; + +/** Build the dialog from the prefDefs metadata */ +const buildDialog = (parentElem, definitions, isFirstLevel = false) => { + for (const [key, value] of Object.entries(definitions)) { + const fId = buildFieldId(key, value); + // Check if the dialog has already been built + if (id(fId)) { + return; + } + switch (value.valueType) { + case "panel": + buildPanel(key, value, parentElem, isFirstLevel); + break; + case "bool": + buildBoolean(key, value, parentElem); + break; + case "int": + case "float": + buildNumeric(key, value, parentElem); + break; + case "enctext": + case "text": + buildText(key, value, parentElem); + break; + case "select": + buildSelect(key, value, parentElem); + break; + default: + console.log(`${key}: ${JSON.stringify(value)}`); + break; + } + } +}; + +/** Set the values into the dialog, and then for any checkbox, trigger the default behaviour */ +const setDialog = (prefs) => { + for (const [key, value] of Object.entries(prefs)) { + // It is possible for the field element to not exist based on what happens in buildDialog + const fElem = id(value.fieldId); + switch (value.valueType) { + case "panel": + case "bool": + if (fElem) { + // Set the `checked` attribute of a checkbox to the default value + // - note that `checked` does not change, once set, it functions as the starting default value. + // The actual value is in the checkbox `value` + fElem.checked = + "defValue" in value + ? typeof value.defValue === "string" && + value.defValue.toLowerCase() === "false" + ? false + : !!value.defValue + : false; + // Because `click`ing the checkbox will toggle it (one way or another) we click it twice + fElem.value = + typeof value.value === "string" && + value.value.toLowerCase() === "false" + ? "false" + : `${!!value.value}`; + fElem.click(); + fElem.click(); + } + break; + case "int": + case "float": + case "text": + case "select": + if (fElem) { + fElem.value = value.value; + } + break; + case "enctext": + if (fElem) { + fElem.value = value.value; + } + break; + default: + console.log(`${key}: ${JSON.stringify(value)}`); + break; + } + } +}; + +/** Initialise the preferences dialog */ +const initpreferences = () => { + buildDialog(id("preferences_body"), prefDefs, true); + setDialog(preferences); + + // And handlers for the other parts of the app + setupNavbarHandlers(); + setupPreferenceHandlers(); + setupControlsHandlers(); + setupFilesHandlers(); + setupGRBLHandlers(); + setupCommandsHandlers(); + + // Do final setup for other parts of the app + setupNavbar(); + setupPreference(); + setupControls(); + setupGRBL(); + setupFiles(); + setupCommands(); +}; + +const elemBlockOrNone = (elemName, enable) => { + if (enable) { + displayBlock(elemName); + } else { + displayNone(elemName); + } +}; + +const navbar_lockUI = (enable) => { + elemBlockOrNone("lock_ui_btn", enable); + ontoggleLock(enable); +}; + +const navbar_enableDHT = (enable) => { + elemBlockOrNone("DHT_humidity", enable); + elemBlockOrNone("DHT_temperature", enable); +}; + +const navbar_enableCamTab = (enable) => { + if (typeof id("camtab") === "undefined") { + return; + } + + let camoutput = false; + + elemBlockOrNone("camtablink", enable); + if (enable) { + camera_GetAddress(); + if (getPrefValue("auto_load_camera")) { + camera_loadframe(); + camoutput = true; + } + } else { + id("tablettablink").click(); + } + + if (!camoutput) { + id("camera_frame").src = ""; + displayNone("camera_frame_display"); + displayNone("camera_detach_button"); + } +}; + +/** Initial setup of the navbar from the preferences coming from the file */ +const setupNavbar = () => { + navbar_lockUI(getPrefValue("enable_lock_UI")); + navbar_enableDHT(getPrefValue("enable_DHT")); + navbar_enableCamTab(getPrefValue("show_camera_panel")); +}; + +const setupNavbarHandlers = () => { + id("enable_lock_UI").addEventListener("change", (event) => navbar_lockUI(getPrefValue("enable_lock_UI"))); + id("enable_DHT").addEventListener("change", (event) => navbar_enableDHT(getPrefValue("enable_DHT"))); + id("show_camera_panel").addEventListener("change", (event) => navbar_enableCamTab(getPrefValue("show_camera_panel"))); +}; + +const language_pref = (value) => { + translate_text(value); +}; + +/** Initial setup of other preferences coming from the file */ +const setupPreference = () => { + language_pref(getPrefValue("language_preferences")); +}; + +const setupPreferenceHandlers = () => { + id("language_preferences").addEventListener("change", (event) => language_pref(getPrefValue("language_preferences"))); +}; + +const controls_showControlsPanel = (enable) => { + elemBlockOrNone("control_preferences", enable); +}; + +/** Initial setup of the Controls Panel from the preferences coming from the file */ +const setupControls = () => { + controls_showControlsPanel(getPrefValue("show_control_panel")); +}; + +const setupControlsHandlers = () => { + id("show_control_panel").addEventListener("change", (event) => controls_showControlsPanel(getPrefValue("show_control_panel"))); +}; + +const grbl_showGRBLPanel = (enable) => { + elemBlockOrNone("grblPanel", enable); + if (enable) { + grblpanel(); + } else { + reportNone(false); + } +}; + +const grbl_ReportIntervalChange = (changed) => { + if (changed) { + onAutoReportIntervalChange(); + } +}; + +const grbl_showProbeTab = (enable) => { + elemBlockOrNone("grblprobetablink", enable); + if (!enable) { + id("grblcontroltablink").click(); + } +}; + +/** Initial setup of the GRBL Panel from the preferences coming from the file */ +const setupGRBL = () => { + grbl_showGRBLPanel(getPrefValue("show_grbl_panel")); + grbl_ReportIntervalChange(getPrefValue("autoreport_interval") || getPrefValue("interval_status")); + grbl_showProbeTab(getPrefValue("show_grbl_probe_tab")); +}; + +const setupGRBLHandlers = () => { + id("show_grbl_panel").addEventListener("change", (event) => grbl_showGRBLPanel(getPrefValue("show_grbl_panel"))); + id("autoreport_interval").addEventListener("change", (event) => grbl_ReportIntervalChange(getPrefValue("autoreport_interval") || getPrefValue("interval_status"))); + id("interval_status").addEventListener("change", (event) => grbl_ReportIntervalChange(getPrefValue("autoreport_interval") || getPrefValue("interval_status"))); + id("show_grbl_probe_tab").addEventListener("change", (event) => grbl_showProbeTab(getPrefValue("show_grbl_probe_tab"))); +}; + +const files_showFilesPanel = (enable) => elemBlockOrNone("filesPanel", enable); + +const files_refreshBtns = (enableSD, enableUSB) => { + if (enableSD || enableUSB) { + displayFlex("files_refresh_printer_sd_btn"); + displayNone("files_refresh_btn"); + } else { + displayNone("files_refresh_printer_sd_btn"); + displayFlex("files_refresh_btn"); + } +}; + +const files_TFTSD = (enableSD, enableUSB) => { + elemBlockOrNone("files_refresh_tft_sd", enableSD, enableUSB); + files_refreshBtns(enableSD, enableUSB); +}; + +const files_TFTUSB = (enableSD, enableUSB) => { + elemBlockOrNone("files_refresh_tft_usb", enableSD, enableUSB); + files_refreshBtns(enableSD, enableUSB); +}; + +const files_filterList = (value) => build_file_filter_list(value); + +/** Initial setup of the Files Dialog from the preferences coming from the file */ +const setupFiles = () => { + files_showFilesPanel(getPrefValue("show_files_panel")); + files_TFTUSB(getPrefValue("has_TFT_SD"), getPrefValue("has_TFT_USB")); + files_TFTUSB(getPrefValue("has_TFT_SD"), getPrefValue("has_TFT_USB")); + files_filterList(getPrefValue("f_filters")); +}; + +const setupFilesHandlers = () => { + id("show_files_panel").addEventListener("change", (event) => files_showFilesPanel(getPrefValue("show_files_panel"))); + id("has_TFT_SD").addEventListener("change", (event) => files_TFTSD(getPrefValue("has_TFT_SD"), getPrefValue("has_TFT_USB"))); + id("has_TFT_USB").addEventListener("change", (event) => files_TFTUSB(getPrefValue("has_TFT_SD"), getPrefValue("has_TFT_USB"))); + // TODO: Check if this one needs a debounce + id("f_filters").addEventListener("change", (event) => files_filterList(getPrefValue("f_filters"))); +}; + +const commands_showCommandsPanel = (enable) => elemBlockOrNone("commandsPanel", enable); + +const commands_autoScroll = (enable) => { + setChecked("monitor_enable_autoscroll", enable); + if (enable) { + Monitor_check_autoscroll(); + } +}; + +const commands_verboseMode = (enable) => { + setChecked("monitor_enable_verbose_mode", enable); + if (enable) { + Monitor_check_verbose_mode(); + } +}; + +/** Initial setup of the Commands Panel from the preferences coming from the file */ +const setupCommands = () => { + commands_showCommandsPanel(getPrefValue("show_commands_panel")); + commands_autoScroll(getPrefValue("enable_autoscroll")); + commands_verboseMode(getPrefValue("enable_verbose_mode")); +}; + +const setupCommandsHandlers = () => { + id("show_commands_panel").addEventListener("change", (event) => commands_showCommandsPanel(getPrefValue("show_commands_panel"))); + id("enable_autoscroll").addEventListener("change", (event) => commands_autoScroll(getPrefValue("enable_autoscroll"))); + id("enable_verbose_mode").addEventListener("change", (event) => commands_verboseMode(getPrefValue("enable_verbose_mode"))); +}; function getpreferenceslist() { - var url = preferences_file_name; - preferenceslist = []; - //removeIf(production) - var response = defaultpreferenceslist; - processPreferencesGetSuccess(response); - return; - //endRemoveIf(production) + const url = prefFile; SendGetHttp(url, processPreferencesGetSuccess, processPreferencesGetFailed); } -function prefs_toggledisplay(id_source, forcevalue) { - if (typeof forcevalue != 'undefined') { - id(id_source).checked = forcevalue; - } - switch (id_source) { - case 'show_files_panel': - if (id(id_source).checked) displayBlock("files_preferences"); - else displayNone("files_preferences"); - break; - case 'show_grbl_panel': - if (id(id_source).checked) displayBlock("grbl_preferences"); - else displayNone("grbl_preferences"); - break; - case 'show_camera_panel': - if (id(id_source).checked) displayBlock("camera_preferences"); - else displayNone("camera_preferences"); - break; - case 'show_control_panel': - if (id(id_source).checked) displayBlock("control_preferences"); - else displayNone("control_preferences"); - break; - case 'show_commands_panel': - if (id(id_source).checked) displayBlock("cmd_preferences"); - else displayNone("cmd_preferences"); - break; - case 'show_grbl_probe_tab': - if (id(id_source).checked) displayBlock("grbl_probe_preferences"); - else displayNone("grbl_probe_preferences"); - break; +/** Gets a Select, Text, Int or Float's new value after a change, and stores it in the preferences */ +const handleInputChange = (fieldId) => { + const newValue = getValue(fieldId); + if (typeof newValue === "undefined") { + console.error(stdErrMsg("Unknown Field", `'${fieldId}' not found as an input field with a value`)); + return undefined; + } + const pref = getPref(fieldId); + pref.value = newValue; + return newValue; +}; + +/** Gets the checkbox's current value, reverses it, and stores the reversed value in the preferences */ +const handleCheckboxClick = (checkboxId) => { + const currentValue = getChecked(checkboxId); + const newValue = !(currentValue !== "false"); + const pref = getPref(checkboxId); + pref.value = newValue; + setChecked(checkboxId, newValue); + return newValue; +}; + +const togglePanel = (checkboxId, panelId) => { + if (handleCheckboxClick(checkboxId)) { + displayBlock(panelId); + } else { + displayNone(panelId); } -} +}; function processPreferencesGetSuccess(response) { - if (response.indexOf("") == -1) Preferences_build_list(response); - else Preferences_build_list(defaultpreferenceslist); + Preferences_build_list((response.indexOf("") === -1) ? response : defaultpreferenceslist); } function processPreferencesGetFailed(error_code, response) { @@ -142,8 +510,8 @@ function processPreferencesGetFailed(error_code, response) { function Preferences_build_list(response_text) { preferenceslist = []; try { - if (response_text.length != 0) { - //console.log(response_text); + if (response_text.length !== 0) { + //console.log(response_text); preferenceslist = JSON.parse(response_text); } else { preferenceslist = JSON.parse(defaultpreferenceslist); @@ -159,642 +527,177 @@ function ontogglePing(forcevalue) { if (typeof forcevalue !== 'undefined') enable_ping = forcevalue else enable_ping = !enable_ping if (enable_ping) { - if (interval_ping !== -1) clearInterval(interval_ping) + if (interval_ping !== -1) { + clearInterval(interval_ping); + } last_ping = Date.now() interval_ping = setInterval(() => { check_ping() }, 10 * 1000) console.log('enable ping') } else { - if (interval_ping !== -1) clearInterval(interval_ping) + if (interval_ping !== -1) { + clearInterval(interval_ping); + } console.log('disable ping') } } function applypreferenceslist() { //Assign each control state - translate_text(preferenceslist[0].language); - build_HTML_setting_list(current_setting_filter); - if (typeof id('camtab') !== "undefined") { - let camoutput = false; - if (typeof (preferenceslist[0].enable_camera) !== 'undefined') { - if (preferenceslist[0].enable_camera === 'true') { - displayBlock('camtablink'); - camera_GetAddress(); - if (typeof (preferenceslist[0].auto_load_camera) !== 'undefined') { - if (preferenceslist[0].auto_load_camera === 'true') { - const saddress = id('camera_webaddress').value - camera_loadframe(); - camoutput = true; - } - } - } else { - id("tablettablink").click(); - displayNone('camtablink'); - } - } else { - id("tablettablink").click(); - displayNone('camtablink'); - } - if (!camoutput) { - id('camera_frame').src = ""; - displayNone('camera_frame_display'); - displayNone('camera_detach_button'); - } - } - if (preferenceslist[0].enable_grbl_probe_panel === 'true') { - displayBlock('grblprobetablink'); - } else { - id("grblcontroltablink").click(); - displayNone('grblprobetablink'); - } - - if (preferenceslist[0].enable_DHT === 'true') { - displayBlock('DHT_humidity'); - displayBlock('DHT_temperature'); - } else { - displayNone('DHT_humidity'); - displayNone('DHT_temperature'); - } - if (preferenceslist[0].enable_lock_UI === 'true') { - displayBlock('lock_ui_btn'); - ontoggleLock(true); - } else { - displayNone('lock_ui_btn'); - ontoggleLock(false); - } - if (preferenceslist[0].enable_ping === 'true') { - ontogglePing(true); - } else { - ontogglePing(false); - } - - if (preferenceslist[0].enable_grbl_panel === 'true') displayFlex('grblPanel'); - else { - displayNone('grblPanel'); - reportNone(false); - } - - if (preferenceslist[0].enable_control_panel === 'true') displayFlex('controlPanel'); - else { - displayNone('controlPanel'); - on_autocheck_position(false); - } - if (preferenceslist[0].enable_verbose_mode === 'true') { - id('monitor_enable_verbose_mode').checked = true; - Monitor_check_verbose_mode(); - } else id('monitor_enable_verbose_mode').checked = false; - - if (preferenceslist[0].enable_files_panel === 'true') displayFlex('filesPanel'); - else displayNone('filesPanel'); + const common = new Common(); + build_HTML_setting_list(common.current_setting_filter); - if (preferenceslist[0].has_TFT_SD === 'true') { - displayFlex('files_refresh_tft_sd_btn'); - } else { - displayNone('files_refresh_tft_sd_btn'); - } - - if (preferenceslist[0].has_TFT_USB === 'true') { - displayFlex('files_refresh_tft_usb_btn'); - } else { - displayNone('files_refresh_tft_usb_btn'); - } - - if ((preferenceslist[0].has_TFT_SD === 'true') || (preferenceslist[0].has_TFT_USB === 'true')) { - displayFlex('files_refresh_printer_sd_btn'); - displayNone('files_refresh_btn'); - } else { - displayNone('files_refresh_printer_sd_btn'); - displayFlex('files_refresh_btn'); - } + handlePing(); +} - if (preferenceslist[0].enable_commands_panel === 'true') { - displayFlex('commandsPanel'); - if (preferenceslist[0].enable_autoscroll === 'true') { - id('monitor_enable_autoscroll').checked = true; - Monitor_check_autoscroll(); - } else id('monitor_enable_autoscroll').checked = false; - } else displayNone('commandsPanel'); - - const autoReportValue = Number.parseInt(preferenceslist[0].autoreport_interval); - const autoReportChanged = id("preferences_autoReport_Interval").value !== autoReportValue; - if (autoReportChanged) { - id("preferences_autoReport_Interval").value = autoReportValue; - } - const statusIntervalValue = Number.parseInt(preferenceslist[0].interval_status); - statusIntervalChanged = id('preferences_status_Interval_check').value !== statusIntervalValue; - if (statusIntervalChanged) { - id('preferences_status_Interval_check').value = statusIntervalValue; - } - if (autoReportChanged || statusIntervalChanged) { - onAutoReportIntervalChange(); +const showpreferencesdlg = () => { + const modal = setactiveModal("preferencesdlg.html"); + if (modal == null) { + return; } - id('preferences_pos_Interval_check').value = Number.parseInt(preferenceslist[0].interval_positions); - id('preferences_control_xy_velocity').value = Number.parseFloat(preferenceslist[0].xy_feedrate); - id('preferences_control_z_velocity').value = Number.parseFloat(preferenceslist[0].z_feedrate); - - if (grblaxis > 2) axis_Z_feedrate = Number.parseFloat(preferenceslist[0].z_feedrate); - if (grblaxis > 3) axis_A_feedrate = Number.parseFloat(preferenceslist[0].a_feedrate); - if (grblaxis > 4) axis_B_feedrate = Number.parseFloat(preferenceslist[0].b_feedrate); - if (grblaxis > 5) axis_C_feedrate = Number.parseFloat(preferenceslist[0].c_feedrate); + initpreferences(); - if (grblaxis > 3) { - const letter = id('control_select_axis').value; - switch (letter) { - case "Z": - id('preferences_control_z_velocity').value = axis_Z_feedrate; - break; - case "A": - id('preferences_control_z_velocity').value = axis_A_feedrate; - break; - case "B": - id('preferences_control_z_velocity').value = axis_B_feedrate; - break; - case "C": - id('preferences_control_z_velocity').value = axis_C_feedrate; - break; - } - } - - id('preferences_probemaxtravel').value = Number.parseFloat(preferenceslist[0].probemaxtravel); - id('preferences_probefeedrate').value = Number.parseFloat(preferenceslist[0].probefeedrate); - id('preferences_proberetract').value = Number.parseFloat(preferenceslist[0].proberetract); - id('preferences_probetouchplatethickness').value = Number.parseFloat(preferenceslist[0].probetouchplatethickness); - build_file_filter_list(preferenceslist[0].f_filters); -} + id("preferencesdlg.html").addEventListener("click", (event) => clear_drop_menu(event),); + id("PreferencesDialogClose").addEventListener("click", (event) => closePreferencesDialog(),); + id("PreferencesDialogCancel").addEventListener("click", (event) => closePreferencesDialog(),); + id("PreferencesDialogSave").addEventListener("click", (event) => SavePreferences(),); -function showpreferencesdlg() { - const modal = setactiveModal('preferencesdlg.html'); - if (modal == null) return; - language_save = language; - build_dlg_preferences_list(); - displayNone('preferencesdlg_upload_msg'); + displayNone("preferencesdlg_upload_msg"); showModal(); -} - -function build_dlg_preferences_list() { - //use preferenceslist to set dlg status - let content = "
"; - content += `${get_icon_svg("flag")} `; - content += build_language_list("language_preferences"); - content += "
"; - id("preferences_langage_list").innerHTML = content; - //camera - if (typeof (preferenceslist[0].enable_camera) !== 'undefined') { - id('show_camera_panel').checked = (preferenceslist[0].enable_camera === 'true'); - } else id('show_camera_panel').checked = false; - //autoload camera - if (typeof (preferenceslist[0].auto_load_camera) !== 'undefined') { - id('autoload_camera_panel').checked = (preferenceslist[0].auto_load_camera === 'true'); - } else id('autoload_camera_panel').checked = false; - //camera address - if (typeof (preferenceslist[0].camera_address) !== 'undefined') { - id('preferences_camera_webaddress').value = HTMLDecode(preferenceslist[0].camera_address); - } else id('preferences_camera_webaddress').value = ""; - //DHT - if (typeof (preferenceslist[0].enable_DHT) !== 'undefined') { - id('enable_DHT').checked = (preferenceslist[0].enable_DHT === 'true'); - } else id('enable_DHT').checked = false; - //lock UI - if (typeof (preferenceslist[0].enable_lock_UI) !== 'undefined') { - id('enable_lock_UI').checked = (preferenceslist[0].enable_lock_UI === 'true'); - } else id('enable_lock_UI').checked = false; - //Monitor connection - if (typeof (preferenceslist[0].enable_ping) !== 'undefined') { - id('enable_ping').checked = (preferenceslist[0].enable_ping === 'true'); - } else id('enable_ping').checked = false; - - //grbl panel - if (typeof (preferenceslist[0].enable_grbl_panel) !== 'undefined') { - id('show_grbl_panel').checked = (preferenceslist[0].enable_grbl_panel === 'true'); - } else id('show_grbl_panel').checked = false; - //grbl probe panel - if (typeof (preferenceslist[0].enable_grbl_probe_panel) !== 'undefined') { - id('show_grbl_probe_tab').checked = (preferenceslist[0].enable_grbl_probe_panel === 'true'); - } else id('show_grbl_probe_tab').checked = false; - //control panel - if (typeof (preferenceslist[0].enable_control_panel) !== 'undefined') { - id('show_control_panel').checked = (preferenceslist[0].enable_control_panel === 'true'); - } else id('show_control_panel').checked = false; - //files panel - if (typeof (preferenceslist[0].enable_files_panel) !== 'undefined') { - id('show_files_panel').checked = (preferenceslist[0].enable_files_panel === 'true'); - } else id('show_files_panel').checked = false; - //TFT SD - if (typeof (preferenceslist[0].has_TFT_SD) !== 'undefined') { - id('has_tft_sd').checked = (preferenceslist[0].has_TFT_SD === 'true'); - } else id('has_tft_sd').checked = false; - //TFT USB - if (typeof (preferenceslist[0].has_TFT_USB) !== 'undefined') { - id('has_tft_usb').checked = (preferenceslist[0].has_TFT_USB === 'true'); - } else id('has_tft_usb').checked = false; - //commands - if (typeof (preferenceslist[0].enable_commands_panel) !== 'undefined') { - id('show_commands_panel').checked = (preferenceslist[0].enable_commands_panel === 'true'); - } else id('show_commands_panel').checked = false; - //autoreport interval - if (typeof (preferenceslist[0].autoreport_interval) !== 'undefined') { - id('preferences_autoReport_Interval').value = Number.parseInt(preferenceslist[0].autoreport_interval); - } else id('preferences_autoReport_Interval').value = Number.parseInt(default_preferenceslist[0].autoreport_interval); - //interval positions - if (typeof (preferenceslist[0].interval_positions) !== 'undefined') { - id('preferences_pos_Interval_check').value = Number.parseInt(preferenceslist[0].interval_positions); - } else id('preferences_pos_Interval_check').value = Number.parseInt(default_preferenceslist[0].interval_positions); - //interval status - if (typeof (preferenceslist[0].interval_status) !== 'undefined') { - id('preferences_status_Interval_check').value = Number.parseInt(preferenceslist[0].interval_status); - } else id('preferences_status_Interval_check').value = Number.parseInt(default_preferenceslist[0].interval_status); - //xy feedrate - if (typeof (preferenceslist[0].xy_feedrate) !== 'undefined') { - id('preferences_control_xy_velocity').value = Number.parseFloat(preferenceslist[0].xy_feedrate); - } else id('preferences_control_xy_velocity').value = Number.parseFloat(default_preferenceslist[0].xy_feedrate); - if (grblaxis > 2) { - //z feedrate - if (typeof (preferenceslist[0].z_feedrate) !== 'undefined') { - id('preferences_control_z_velocity').value = Number.parseFloat(preferenceslist[0].z_feedrate); - } else id('preferences_control_z_velocity').value = Number.parseFloat(default_preferenceslist[0].z_feedrate); - } - if (grblaxis > 3) { - //a feedrate - if (typeof (preferenceslist[0].a_feedrate) !== 'undefined') { - id('preferences_control_a_velocity').value = Number.parseFloat(preferenceslist[0].a_feedrate); - } else id('preferences_control_a_velocity').value = Number.parseFloat(default_preferenceslist[0].a_feedrate); - } - if (grblaxis > 4) { - //b feedrate - if (typeof (preferenceslist[0].b_feedrate) !== 'undefined') { - id('preferences_control_b_velocity').value = Number.parseFloat(preferenceslist[0].b_feedrate); - } else id('preferences_control_b_velocity').value = Number.parseFloat(default_preferenceslist[0].b_feedrate); - } - if (grblaxis > 5) { - //c feedrate - if (typeof (preferenceslist[0].c_feedrate) !== 'undefined') { - id('preferences_control_c_velocity').value = Number.parseFloat(preferenceslist[0].c_feedrate); - } else id('preferences_control_c_velocity').value = Number.parseFloat(default_preferenceslist[0].c_feedrate); - } - - //probemaxtravel - if ((typeof (preferenceslist[0].probemaxtravel) !== 'undefined') && (preferenceslist[0].probemaxtravel.length !== 0)) { - id('preferences_probemaxtravel').value = Number.parseFloat(preferenceslist[0].probemaxtravel); - } else { - id('preferences_probemaxtravel').value = Number.parseFloat(default_preferenceslist[0].probemaxtravel); - } - //probefeedrate - if ((typeof (preferenceslist[0].probefeedrate) !== 'undefined') && (preferenceslist[0].probefeedrate.length !== 0)) { - id('preferences_probefeedrate').value = Number.parseFloat(preferenceslist[0].probefeedrate); - } else id('preferences_probefeedrate').value = Number.parseFloat(default_preferenceslist[0].probefeedrate); - //proberetract - if ((typeof (preferenceslist[0].proberetract) !== 'undefined') && (preferenceslist[0].proberetract.length !== 0)) { - id('preferences_proberetract').value = Number.parseFloat(preferenceslist[0].proberetract); - } else id('preferences_proberetract').value = Number.parseFloat(default_preferenceslist[0].proberetract); - //probetouchplatethickness - if ((typeof (preferenceslist[0].probetouchplatethickness) !== 'undefined') && (preferenceslist[0].probetouchplatethickness.length !== 0)) { - id('preferences_probetouchplatethickness').value = Number.parseFloat(preferenceslist[0].probetouchplatethickness); - } else id('preferences_probetouchplatethickness').value = Number.parseFloat(default_preferenceslist[0].probetouchplatethickness); - //autoscroll - if (typeof (preferenceslist[0].enable_autoscroll) !== 'undefined') { - id('preferences_autoscroll').checked = (preferenceslist[0].enable_autoscroll === 'true'); - } else id('preferences_autoscroll').checked = false; - //Verbose Mode - if (typeof (preferenceslist[0].enable_verbose_mode) !== 'undefined') { - id('preferences_verbose_mode').checked = (preferenceslist[0].enable_verbose_mode === 'true'); - } else id('preferences_verbose_mode').checked = false; - //file filters - if (typeof (preferenceslist[0].f_filters) !== 'undefined') { - console.log("Use prefs filters"); - id('preferences_filters').value = preferenceslist[0].f_filters; - } else { - console.log("Use default filters"); - id('preferences_filters').value = String(default_preferenceslist[0].f_filters); - } - - prefs_toggledisplay('show_camera_panel'); - prefs_toggledisplay('show_grbl_panel'); - prefs_toggledisplay('show_control_panel'); - prefs_toggledisplay('show_commands_panel'); - prefs_toggledisplay('show_files_panel'); - prefs_toggledisplay('show_grbl_probe_tab'); -} +}; function closePreferencesDialog() { let modified = false; - if (preferenceslist[0].length !== 0) { - //check dialog compare to global state - if ((typeof (preferenceslist[0].language) === 'undefined') || - (typeof (preferenceslist[0].enable_camera) === 'undefined') || - (typeof (preferenceslist[0].auto_load_camera) === 'undefined') || - (typeof (preferenceslist[0].camera_address) === 'undefined') || - (typeof (preferenceslist[0].enable_DHT) === 'undefined') || - (typeof (preferenceslist[0].enable_lock_UI) === 'undefined') || - (typeof (preferenceslist[0].enable_ping) === 'undefined') || - (typeof (preferenceslist[0].enable_redundant) === 'undefined') || - (typeof (preferenceslist[0].enable_probe) === 'undefined') || - (typeof (preferenceslist[0].xy_feedrate) === 'undefined') || - (typeof (preferenceslist[0].z_feedrate) === 'undefined') || - (typeof (preferenceslist[0].e_feedrate) === 'undefined') || - (typeof (preferenceslist[0].e_distance) === 'undefined') || - (typeof (preferenceslist[0].enable_control_panel) === 'undefined') || - (typeof (preferenceslist[0].enable_grbl_panel) === 'undefined') || - (typeof (preferenceslist[0].enable_grbl_probe_panel) === 'undefined') || - (typeof (preferenceslist[0].probemaxtravel) === 'undefined') || - (typeof (preferenceslist[0].probefeedrate) === 'undefined') || - (typeof (preferenceslist[0].proberetract) === 'undefined') || - (typeof (preferenceslist[0].probetouchplatethickness) === 'undefined') || - (typeof (preferenceslist[0].enable_files_panel) === 'undefined') || - (typeof (preferenceslist[0].has_TFT_SD) === 'undefined') || - (typeof (preferenceslist[0].has_TFT_USB) === 'undefined') || - (typeof (preferenceslist[0].autoreport_interval) === 'undefined') || - (typeof (preferenceslist[0].interval_positions) === 'undefined') || - (typeof (preferenceslist[0].interval_status) === 'undefined') || - (typeof (preferenceslist[0].enable_autoscroll) === 'undefined') || - (typeof (preferenceslist[0].enable_verbose_mode) === 'undefined') || - (typeof (preferenceslist[0].enable_commands_panel) === 'undefined')) { + //check dialog compare to global state + if (typeof getPrefValue("enable_ping") === "undefined") { + modified = true; + } else { + //camera address + if (getChecked("camera_address") !== getPrefValue("camera_address")) + modified = true; + //Monitor connection + if (getChecked("enable_ping") !== getPrefValue("enable_ping")) modified = true; - } else { - //camera - if (id('show_camera_panel').checked !== (preferenceslist[0].enable_camera === 'true')) modified = true; - //Autoload - if (id('autoload_camera_panel').checked !== (preferenceslist[0].auto_load_camera === 'true')) modified = true; - //camera address - if (id('preferences_camera_webaddress').value !== HTMLDecode(preferenceslist[0].camera_address)) modified = true; - //DHT - if (id('enable_DHT').checked !== (preferenceslist[0].enable_DHT === 'true')) modified = true; - //Lock UI - if (id('enable_lock_UI').checked !== (preferenceslist[0].enable_lock_UI === 'true')) modified = true; - //Monitor connection - if (id('enable_ping').checked !== (preferenceslist[0].enable_ping === 'true')) modified = true; - //probe - if (id('show_grbl_probe_tab').checked !== (preferenceslist[0].enable_probe === 'true')) modified = true; - //control panel - if (id('show_control_panel').checked !== (preferenceslist[0].enable_control_panel === 'true')) modified = true; - //grbl panel - if (id('show_grbl_panel').checked !== (preferenceslist[0].enable_grbl_panel === 'true')) modified = true; - //grbl probe panel - if (id('show_grbl_probe_tab').checked !== (preferenceslist[0].enable_grbl_probe_panel === 'true')) modified = true; - //files panel - if (id('show_files_panel').checked !== (preferenceslist[0].enable_files_panel === 'true')) modified = true; - //TFT SD - if (id('has_tft_sd').checked !== (preferenceslist[0].has_TFT_SD === 'true')) modified = true; - //TFT USB - if (id('has_tft_usb').checked !== (preferenceslist[0].has_TFT_USB === 'true')) modified = true; - //commands - if (id('show_commands_panel').checked !== (preferenceslist[0].enable_commands_panel === 'true')) modified = true; - //interval positions - if (id('preferences_autoReport_Interval').value !== Number.parseInt(preferenceslist[0].autoReport_interval)) modified = true; - if (id('preferences_pos_Interval_check').value !== Number.parseInt(preferenceslist[0].interval_positions)) modified = true; - //interval status - if (id('preferences_status_Interval_check').value !== Number.parseInt(preferenceslist[0].interval_status)) modified = true; - //xy feedrate - if (id('preferences_control_xy_velocity').value !== Number.parseFloat(preferenceslist[0].xy_feedrate)) modified = true; - if (grblaxis > 2) { - //z feedrate - if (id('preferences_control_z_velocity').value !== Number.parseFloat(preferenceslist[0].z_feedrate)) modified = true; - } - if (grblaxis > 3) { - //a feedrate - if (id('preferences_control_a_velocity').value !== Number.parseFloat(preferenceslist[0].a_feedrate)) modified = true; - } - if (grblaxis > 4) { - //b feedrate - if (id('preferences_control_b_velocity').value !== Number.parseFloat(preferenceslist[0].b_feedrate)) modified = true; - } - if (grblaxis > 5) { - //c feedrate - if (id('preferences_control_c_velocity').value !== Number.parseFloat(preferenceslist[0].c_feedrate)) modified = true; - } - } - //autoscroll - if (id('preferences_autoscroll').checked !== (preferenceslist[0].enable_autoscroll === 'true')) modified = true; - //Verbose Mode - if (id('preferences_verbose_mode').checked !== (preferenceslist[0].enable_verbose_mode === 'true')) modified = true; - //file filters - if (id('preferences_filters').value !== preferenceslist[0].f_filters) modified = true; - //probemaxtravel - if (id('preferences_probemaxtravel').value !== Number.parseFloat(preferenceslist[0].probemaxtravel)) modified = true; - //probefeedrate - if (id('preferences_probefeedrate').value !== Number.parseFloat(preferenceslist[0].probefeedrate)) modified = true; - //proberetract - if (id('preferences_proberetract').value !== Number.parseFloat(preferenceslist[0].proberetract)) modified = true; - //probetouchplatethickness - if (id('preferences_probetouchplatethickness').value !== Number.parseFloat(preferenceslist[0].probetouchplatethickness)) modified = true; } - - if (language_save !== language) modified = true; if (modified) { - confirmdlg(translate_text_item("Data mofified"), translate_text_item("Do you want to save?"), process_preferencesCloseDialog) + confirmdlg(translate_text_item("Data modified"), translate_text_item("Do you want to save?"), process_preferencesCloseDialog); } else { - closeModal('cancel'); + closeModal("cancel"); } } function process_preferencesCloseDialog(answer) { - if (answer == 'no') { + if (answer === "no") { //console.log("Answer is no so exit"); - translate_text(language_save); - closeModal('cancel'); + translate_text(getPrefValue("language_list")); + closeModal("cancel"); } else { // console.log("Answer is yes so let's save"); SavePreferences(); } } -function SavePreferences(current_preferences) { - if (http_communication_locked) { +const SavePreferences = (save_current_preferences = false) => { + const common = new Common(); + if (common.http_communication_locked) { alertdlg(translate_text_item("Busy..."), translate_text_item("Communications are currently locked, please wait and retry.")); return; } console.log("save prefs"); - if (((typeof (current_preferences) !== 'undefined') && !current_preferences) || (typeof (current_preferences) == 'undefined')) { - if (!Checkvalues("preferences_autoReport_Interval") || - !Checkvalues("preferences_pos_Interval_check") || - !Checkvalues("preferences_status_Interval_check") || - !Checkvalues("preferences_control_xy_velocity") || - !Checkvalues("preferences_filters") || - !Checkvalues("preferences_probemaxtravel") || - !Checkvalues("preferences_probefeedrate") || - !Checkvalues("preferences_proberetract") || - !Checkvalues("preferences_probetouchplatethickness") - ) return; - if (grblaxis > 2) { - if (!Checkvalues("preferences_control_z_velocity")) return; - } - if ((grblaxis > 3) && (!Checkvalues("preferences_control_a_velocity"))) return; - if ((grblaxis > 4) && (!Checkvalues("preferences_control_b_velocity"))) return; - if ((grblaxis > 5) && (!Checkvalues("preferences_control_c_velocity"))) return; + if (save_current_preferences) { preferenceslist = []; - let saveprefs = `[{\"language\":\"${language}`; - saveprefs += `\",\"enable_camera\":\"${id('show_camera_panel').checked}`; - saveprefs += `\",\"auto_load_camera\":\"${id('autoload_camera_panel').checked}`; - saveprefs += `\",\"camera_address\":\"${HTMLEncode(id('preferences_camera_webaddress').value)}`; - saveprefs += `\",\"enable_DHT\":\"${id('enable_DHT').checked}`; - saveprefs += `\",\"enable_lock_UI\":\"${id('enable_lock_UI').checked}`; - saveprefs += `\",\"enable_ping\":\"${id('enable_ping').checked}`; - saveprefs += `\",\"enable_control_panel\":\"${id('show_control_panel').checked}`; - saveprefs += `\",\"enable_grbl_probe_panel\":\"${id('show_grbl_probe_tab').checked}`; - saveprefs += `\",\"enable_grbl_panel\":\"${id('show_grbl_panel').checked}`; - saveprefs += `\",\"enable_files_panel\":\"${id('show_files_panel').checked}`; - saveprefs += `\",\"has_TFT_SD\":\"${id('has_tft_sd').checked}`; - saveprefs += `\",\"has_TFT_USB\":\"${id('has_tft_usb').checked}`; - saveprefs += `\",\"probemaxtravel\":\"${id('preferences_probemaxtravel').value}`; - saveprefs += `\",\"probefeedrate\":\"${id('preferences_probefeedrate').value}`; - saveprefs += `\",\"proberetract\":\"${id('preferences_proberetract').value}`; - saveprefs += `\",\"probetouchplatethickness\":\"${id('preferences_probetouchplatethickness').value}`; - saveprefs += `\",\"autoreport_interval\":\"${id('preferences_autoReport_Interval').value}`; - saveprefs += `\",\"interval_positions\":\"${id('preferences_pos_Interval_check').value}`; - saveprefs += `\",\"interval_status\":\"${id('preferences_status_Interval_check').value}`; - saveprefs += `\",\"xy_feedrate\":\"${id('preferences_control_xy_velocity').value}`; - if (grblaxis > 2) { - saveprefs += `\",\"z_feedrate\":\"${id('preferences_control_z_velocity').value}`; - } - if (grblaxis > 3) { - saveprefs += `\",\"a_feedrate\":\"${id('preferences_control_a_velocity').value}`; - } - if (grblaxis > 4) { - saveprefs += `\",\"b_feedrate\":\"${id('preferences_control_b_velocity').value}`; - } - if (grblaxis > 5) { - saveprefs += `\",\"c_feedrate\":\"${id('preferences_control_c_velocity').value}`; - } - - saveprefs += `\",\"f_filters\":\"${id('preferences_filters').value}`; - saveprefs += `\",\"enable_autoscroll\":\"${id('preferences_autoscroll').checked}`; - saveprefs += `\",\"enable_verbose_mode\":\"${id('preferences_verbose_mode').checked}`; - saveprefs += `\",\"enable_commands_panel\":\"${id('show_commands_panel').checked}\"}]`; + let saveprefs = `[{"language":"${getPrefValue("language_list")}`; + saveprefs += `","enable_ping":"${getChecked("enable_ping")}`; preferenceslist = JSON.parse(saveprefs); } - const blob = new Blob([JSON.stringify(preferenceslist, null, " ")], { type: 'application/json' }); - var file; - if (browser_is("IE") || browser_is("Edge")) { - file = blob; - file.name = preferences_file_name; - file.lastModifiedDate = new Date(); - } else file = new File([blob], preferences_file_name); - var formData = new FormData(); - var url = "/files"; - formData.append('path', '/'); - formData.append('myfile[]', file, preferences_file_name); - if ((typeof (current_preferences) != 'undefined') && current_preferences) SendFileHttp(url, formData); - else SendFileHttp(url, formData, preferencesdlgUploadProgressDisplay, preferencesUploadsuccess, preferencesUploadfailed); -} + const blob = new Blob([JSON.stringify(preferenceslist, null, " ")], { type: "application/json" }); + const file = new File([blob], prefFile); + const formData = new FormData(); + const url = "/files"; + formData.append("path", "/"); + formData.append("myfile[]", file, prefFile); + if (save_current_preferences) { + SendFileHttp(url, formData); + } else { + SendFileHttp( + url, + formData, + preferencesdlgUploadProgressDisplay, + preferencesUploadsuccess, + preferencesUploadfailed, + ); + } +}; function preferencesdlgUploadProgressDisplay(oEvent) { if (oEvent.lengthComputable) { - var percentComplete = (oEvent.loaded / oEvent.total) * 100; - id('preferencesdlg_prg').value = percentComplete; - id('preferencesdlg_upload_percent').innerHTML = percentComplete.toFixed(0); - displayBlock('preferencesdlg_upload_msg'); + const percentComplete = (oEvent.loaded / oEvent.total) * 100; + setValue("preferencesdlg_prg", percentComplete); + setHTML("preferencesdlg_upload_percent", percentComplete.toFixed(0)); + displayBlock("preferencesdlg_upload_msg"); } else { // Impossible because size is unknown } } function preferencesUploadsuccess(response) { - displayNone('preferencesdlg_upload_msg'); + displayNone("preferencesdlg_upload_msg"); applypreferenceslist(); - closeModal('ok'); + closeModal("ok"); } function preferencesUploadfailed(error_code, response) { alertdlg(translate_text_item("Error"), translate_text_item("Save preferences failed!")); } - -function Checkvalues(id_2_check) { - let status = true; - let value = 0; - switch (id_2_check) { - case "preferences_autoReport_Interval": - value = Number.parseInt(id(id_2_check).value); - if (!(!Number.isNaN(value) && (value === 0 || (value >= 50 && value <= 30000)))) { - error_message = translate_text_item("Value of auto-report must be 0 or between 50ms and 30000ms !!"); - status = false; - } - break; - case "preferences_status_Interval_check": - value = Number.parseInt(id(id_2_check).value); - if (!(!Number.isNaN(value) && value >= 0 && value <= 100)) { - error_message = translate_text_item("Value of auto-check must be between 0s and 99s !!"); - status = false; - } - break; - case "preferences_pos_Interval_check": - value = Number.parseInt(id(id_2_check).value); - if (!(!Number.isNaN(value) && value >= 1 && value <= 100)) { - error_message = translate_text_item("Value of auto-check must be between 0s and 99s !!"); - status = false; - } - break; - case "preferences_control_xy_velocity": - value = Number.parseFloat(id(id_2_check).value); - if (!(!Number.isNaN(value) && value >= 0.00001)) { - error_message = translate_text_item("XY Feedrate value must be at least 0.00001 mm/min!"); - status = false; - } - break; - case "preferences_control_z_velocity": - value = Number.parseFloat(id(id_2_check).value); - if (!(!Number.isNaN(value) && value >= 0.00001)) { - error_message = translate_text_item("Z Feedrate value must be at least 0.00001 mm/min!"); - status = false; - } - break; - case "preferences_control_a_velocity": - case "preferences_control_b_velocity": - case "preferences_control_c_velocity": - value = Number.parseFloat(id(id_2_check).value); - if (!(!Number.isNaN(value) && value >= 0.00001)) { - error_message = translate_text_item("Axis Feedrate value must be at least 0.00001 mm/min!"); - status = false; - } - break; - case "preferences_probefeedrate": - value = Number.parseFloat(id(id_2_check).value); - if (!(!Number.isNaN(value) && value >= 0.00001 && value <= 9999)) { - error_message = translate_text_item("Value of probe feedrate must be between 0.00001 mm/min and 9999 mm/min !"); - status = false; - } - break; - case "preferences_probemaxtravel": - value = Number.parseFloat(id(id_2_check).value); - if (!(!Number.isNaN(value) && value >= 0.00001 && value <= 9999)) { - error_message = translate_text_item("Value of maximum probe travel must be between 0.00001 mm and 9999 mm !"); - status = false; - } - break; - case "preferences_proberetract": - value = Number.parseFloat(id(id_2_check).value); - if (!(!Number.isNaN(value) && value >= 0 && value <= 9999)) { - error_message = translate_text_item("Value of probe retract must be between 0 mm and 9999 mm !"); - status = false; - } - break; - case "preferences_probetouchplatethickness": - value = Number.parseFloat(id(id_2_check).value); - if (!(!Number.isNaN(value) && value >= 0 && value <= 9999)) { - error_message = translate_text_item("Value of probe touch plate thickness must be between 0 mm and 9999 mm !"); - status = false; - } - break; - case "preferences_filters": - //TODO a regex would be better - value = id(id_2_check).value; - if ((value.indexOf(".") !== -1) || - (value.indexOf("*") !== -1)) { - error_message = translate_text_item("Only alphanumeric chars separated by ; for extensions filters"); - status = false; +const CheckValue = (fId, valueDef) => { + let errorList = []; + if (typeof valueDef === "undefined") { + errorList.push(`No definition provided for the field '${fId}'. Its value cannot be checked`); + } else { + const elem = id(fId); + const value = elem ? elem.value : valueDef.defValue; + // Check for any specific test and use that in preference + if ("valFunc" in valueDef) { + const vfTest = valueDef.valFunc(value); + if (vfTest) { + errorList.push(vfTest); } - break; + } else { + errorList.push(checkValue(value, valueDef)); + } } - if (status) { - id(`${id_2_check}_group`).classList.remove("has-feedback"); - id(`${id_2_check}_group`).classList.remove("has-error"); - id(`${id_2_check}_icon`).innerHTML = ""; + + errorList = errorList.filter((err) => err); + + const elemIdGroup = id(`${fId}_group`); + const elemIdIcon = id(`${fId}_icon`); + if (!errorList.length) { + if (elemIdGroup) { + elemIdGroup.classList.remove("has-feedback"); + elemIdGroup.classList.remove("has-error"); + } + if (elemIdIcon) { + elemIdIcon.innerHTML = ""; + } } else { - // has-feedback hides the value so it is hard to fix it - // id(id_2_check + "_group").classList.add("has-feedback"); - id(`${id_2_check}_group`).classList.add("has-error"); - // id(id_2_check + "_icon").innerHTML = get_icon_svg("remove"); - alertdlg(translate_text_item("Out of range"), error_message); + if (elemIdGroup) { + // has-feedback hides the value so it is hard to fix it + // elemIdGroup.classList.add("has-feedback"); + elemIdGroup.classList.add("has-error"); + } + if (elemIdIcon) { + // elemIdIcon.innerHTML = get_icon_svg("remove"); + } + alertdlg(translate_text_item("Errors with settings & preferences"), errorList.join("\n")); } - return status; -} + return errorList.length === 0; +}; + +export { + getpreferenceslist, + initpreferences, + showpreferencesdlg, + SavePreferences, +}; diff --git a/www/js/printercmd.js b/www/js/printercmd.js index abab79d42..a2172463d 100644 --- a/www/js/printercmd.js +++ b/www/js/printercmd.js @@ -1,67 +1,73 @@ -var grbl_processfn = null; -var grbl_errorfn = null; +import { + conErr, + HTMLDecode, + stdErrMsg, + Monitor_output_Update, + SendGetHttp, + translate_text_item, +} from "./common.js"; -function noop() {} -function SendPrinterCommand(cmd, echo_on, processfn, errorfn, id, max_id, extra_arg) { - var url = "/command?commandText="; - var push_cmd = true; - if (typeof echo_on !== 'undefined') { - push_cmd = echo_on; - } - if (cmd.length == 0) return; - if (push_cmd) Monitor_output_Update("[#]" + cmd + "\n"); - //removeIf(production) - console.log(cmd); - if (typeof processfn !== 'undefined') processfn("Test response"); - else SendPrinterCommandSuccess("Test response"); - return; - //endRemoveIf(production) - if (typeof processfn === 'undefined' || processfn == null) processfn = SendPrinterCommandSuccess; - if (typeof errorfn === 'undefined' || errorfn == null) errorfn = SendPrinterCommandFailed; - if (!cmd.startsWith("[ESP")) { - grbl_processfn = processfn; - grbl_errorfn = errorfn; - processfn = noop; - errorfn = noop; - } - cmd = encodeURI(cmd); - cmd = cmd.replace("#", "%23"); - if (extra_arg) { - cmd += "&" + extra_arg; - } - SendGetHttp(url + cmd, processfn, errorfn, id, max_id); - //console.log(cmd); -} +let grbl_processfn = null; +let grbl_errorfn = null; + +function noop() { } + +const cleanFunc = (fn, cleanFn) => fn instanceof Function ? fn : cleanFn; + +const SendPrinterCommand = (cmd, echo_on, processfn, errorfn, id, max_id, extra_arg) => { + if (!cmd.length) { + return; + } + const url = "/command?commandText="; + const push_cmd = typeof echo_on !== "undefined" ? echo_on : true; + if (push_cmd) { + Monitor_output_Update(`[#]${cmd}\n`); + } + + let procFn = cleanFunc(processfn, SendPrinterCommandSuccess); + let errFn = cleanFunc(errorfn, SendPrinterCommandFailed); + + if (!cmd.startsWith("[ESP")) { + grbl_processfn = procFn; + grbl_errorfn = errFn; + procFn = noop; + errFn = noop; + } + let encCmd = encodeURI(cmd).replace("#", "%23"); + if (extra_arg) { + encCmd += `&${extra_arg}`; + } + SendGetHttp(url + encCmd, procFn, errFn, id, max_id); + //console.log(cmd); +}; function SendPrinterSilentCommand(cmd, processfn, errorfn, id, max_id) { - var url = "/command_silent?commandText="; - if (cmd.length == 0) return; - //removeIf(production) - console.log(cmd); - if (typeof processfn !== 'undefined') processfn("Test response"); - else SendPrinterCommandSuccess("Test response"); - return; - //endRemoveIf(production) - if (typeof processfn === 'undefined' || processfn == null) processfn = SendPrinterSilentCommandSuccess; - if (typeof errorfn === 'undefined' || errorfn == null) errorfn = SendPrinterCommandFailed; - cmd = encodeURI(cmd); - cmd = cmd.replace("#", "%23"); - SendGetHttp(url + cmd, processfn, errorfn, id, max_id); - //console.log(cmd); + const url = "/command_silent?commandText="; + if (cmd.length === 0) { + return; + } + + const procFn = cleanFunc(processfn, SendPrinterSilentCommandSuccess); + const errFn = cleanFunc(errorfn, SendPrinterCommandFailed); + const encCmd = encodeURI(cmd).replace("#", "%23"); + + SendGetHttp(url + encCmd, procFn, errFn, id, max_id); + //console.log(cmd); } function SendPrinterSilentCommandSuccess(response) { - //console.log(response); + //console.log(response); } -function SendPrinterCommandSuccess(response) { -} +function SendPrinterCommandSuccess(response) { } function SendPrinterCommandFailed(error_code, response) { - const errMsg = (error_code === 0) - ? translate_text_item("Connection error") - : stdErrMsg(error_code, HTMLDecode(response), translate_text_item("Error")); - Monitor_output_Update(`${errMsg}\n`); + const errMsg = (error_code === 0) + ? translate_text_item("Connection error") + : stdErrMsg(error_code, HTMLDecode(response), translate_text_item("Error")); + Monitor_output_Update(`${errMsg}\n`); - conErr(error_code, HTMLDecode(response), "printer cmd Error"); + conErr(error_code, HTMLDecode(response), "printer cmd Error"); } + +export { SendPrinterCommand }; diff --git a/www/js/restartdlg.js b/www/js/restartdlg.js index 35b13f158..d6b0a8f02 100644 --- a/www/js/restartdlg.js +++ b/www/js/restartdlg.js @@ -1,4 +1,17 @@ -// import conErr, stdErrMsg, displayBlock, displayNone, id, setHTML, closeModal, setactiveModal, showModal, SendPrinterCommand, translate_text_item +import { + Common, + conErr, + stdErrMsg, + displayBlock, + displayNone, + id, + setHTML, + closeModal, + setactiveModal, + showModal, + SendPrinterCommand, + translate_text_item, +} from "./common.js"; /** Restart dialog */ const restartdlg = () => { @@ -11,28 +24,21 @@ const restartdlg = () => { displayBlock("prgrestart"); setHTML("restartmsg", translate_text_item("Restarting, please wait....")); showModal(); - SendPrinterCommand( - "[ESP444]RESTART", - false, - restart_esp_success, - restart_esp_failed, - ); + SendPrinterCommand("[ESP444]RESTART", false, restart_esp_success, restart_esp_failed); }; function restart_esp_success(response) { let i = 0; const x = id("prgrestart"); - http_communication_locked = true; + const common = new Common(); + common.http_communication_locked = true; x.max = 10; const interval = setInterval(() => { - last_ping = Date.now(); + common.last_ping = Date.now(); i = i + 1; const x = id("prgrestart"); x.value = i; - setHTML( - "restartmsg", - `${translate_text_item("Restarting, please wait....")} ${x.max + 1 - i} ${translate_text_item("seconds")}`, - ); + setHTML("restartmsg", `${translate_text_item("Restarting, please wait....")} ${x.max + 1 - i} ${translate_text_item("seconds")}`); if (i > x.max) { clearInterval(interval); location.reload(); @@ -43,10 +49,9 @@ function restart_esp_success(response) { function restart_esp_failed(error_code, response) { displayNone("prgrestart"); - setHTML( - "restartmsg", - stdErrMsg(error_code, response, translate_text_item("Upload failed")), - ); + setHTML("restartmsg", stdErrMsg(error_code, response, translate_text_item("Upload failed"))); conErr(error_code, response); closeModal("Cancel"); } + +export { restartdlg }; diff --git a/www/js/scanwifidlg.js b/www/js/scanwifidlg.js index 3e832717a..a2b575379 100644 --- a/www/js/scanwifidlg.js +++ b/www/js/scanwifidlg.js @@ -1,4 +1,19 @@ -// import get_icon_svg, conErr, stdErrMsg, displayBlock, displayNone, id, getValue, setValue, setHTML, closeModal, setactiveModal, showModal, SendGetHttp, translate_text_item +import { + get_icon_svg, + conErr, + stdErrMsg, + displayBlock, + displayNone, + id, + getValue, + setValue, + setHTML, + closeModal, + setactiveModal, + showModal, + SendGetHttp, + translate_text_item, +} from "./common.js"; let ssid_item_scanwifi = -1; let ssid_subitem_scanwifi = -1; @@ -26,26 +41,6 @@ function refresh_scanwifi() { displayBlock("AP_scan_status"); setHTML("AP_scan_status", translate_text_item("Scanning")); displayNone("refresh_scanwifi_btn"); - //removeIf(production) - const testResponse = [ - '{"AP_LIST":[', - '{"SSID":"HP-Setup>71-M277LaserJet","SIGNAL":"90","IS_PROTECTED":"0"},', - '{"SSID":"NETGEAR_2GEXT_OFFICE2","SIGNAL":"58","IS_PROTECTED":"1"},', - '{"SSID":"NETGEAR_2GEXT_OFFICE","SIGNAL":"34","IS_PROTECTED":"1"},', - '{"SSID":"NETGEAR_2GEXT_COULOIR","SIGNAL":"18","IS_PROTECTED":"1"},', - '{"SSID":"HP-Print-D3-ColorLaserJetPro","SIGNAL":"14","IS_PROTECTED":"0"},', - '{"SSID":"external-wifi","SIGNAL":"20","IS_PROTECTED":"1"},', - '{"SSID":"Livebox-4D0F","SIGNAL":"24","IS_PROTECTED":"1"},', - '{"SSID":"SFR_2000","SIGNAL":"20","IS_PROTECTED":"1"}', - '{"SSID":"SFR_0D90","SIGNAL":"26","IS_PROTECTED":"1"},', - '{"SSID":"SFRWiFiFON","SIGNAL":"18","IS_PROTECTED":"0"},', - '{"SSID":"SFRWiFiMobile","SIGNAL":"18","IS_PROTECTED":"1"},', - '{"SSID":"FreeWifi","SIGNAL":"16","IS_PROTECTED":"0"}', - ']}' - ]; - getscanWifiSuccess(testResponse.join("")); - return; - //endRemoveIf(production) const url = `/command?plain=${encodeURIComponent("[ESP410]")}`; SendGetHttp(url, getscanWifiSuccess, getscanWififailed); } @@ -61,9 +56,7 @@ function process_scanWifi_answer(response_text) { } else { const aplist = response.AP_LIST; //console.log("found " + aplist.length + " AP"); - aplist.sort((a, b) => Number.parseInt(a.SIGNAL) < Number.parseInt(b.SIGNAL) - ? -1 - : Number.parseInt(a.SIGNAL) > Number.parseInt(b.SIGNAL) ? 1 : 0); + aplist.sort((a, b) => Number.parseInt(a.SIGNAL) - Number.parseInt(b.SIGNAL)); for (let i = aplist.length - 1; i >= 0; i--) { const protIcon = aplist[i].IS_PROTECTED === "1" ? get_icon_svg("lock") : ""; const escapedSSID = aplist[i].SSID.replace("'", "\\'").replace('"', '\\"',); @@ -83,6 +76,7 @@ function process_scanWifi_answer(response_text) { } setHTML("AP_scan_data", content); + // biome-ignore lint/complexity/noForEach: actions.forEach((action) => { id(action.id).addEventListener(action.type, (event) => action.method); }); @@ -116,13 +110,12 @@ function getscanWififailed(error_code, response) { conErr(error_code, response); displayNone("AP_scan_loader"); displayBlock("AP_scan_status"); - setHTML( - "AP_scan_status", - stdErrMsg(error_code, response, translate_text_item("Failed")), - ); + setHTML("AP_scan_status", stdErrMsg(error_code, response, translate_text_item("Failed"))); displayBlock("refresh_scanwifi_btn"); } function scanwifidlg_close(response) { //console.log(response); } + +export { scanwifidlg }; diff --git a/www/js/settings.js b/www/js/settings.js index 1d3467e9b..e5fe2c0c6 100644 --- a/www/js/settings.js +++ b/www/js/settings.js @@ -1,56 +1,76 @@ -// When we can change to proper ESM - uncomment this -// import M from "constants"; - -var scl = [] // setting_configList -var setting_error_msg = '' -var setting_lasti = -1 -var setting_lastj = -1 -var current_setting_filter = 'nvs' -var setup_is_done = false -var do_not_build_settings = false +import { + Common, + get_icon_svg, + M, + conErr, + stdErrMsg, + displayBlock, + displayNone, + id, + setChecked, + setHTML, + alertdlg, + confirmdlg, + init_files_panel, + SendGetHttp, + translate_text_item, + restartdlg, +} from "./common.js"; + +/** setting_configList */ +let scl = []; +var setting_error_msg = ""; +var setting_lasti = -1; +var setting_lastj = -1; + +var do_not_build_settings = false; const CONFIG_TOOLTIPS = { - Maslow_vertical: `If the ${M} is oriented horizontally, set this to false`, - Maslow_calibration_offset_X: "mm offset from the edge of the frame, X", - Maslow_calibration_offset_Y: "mm offset from the edge of the frame, Y", - Maslow_calibration_size_X: "Number of X points to use in calibration", - Maslow_calibration_size_Y: "Number of Y points to use in calibration", - Maslow_brX: "Bottom right anchor x (normally width in mm)", - Maslow_brY: "Bottom right anchor y (normally 0)", - Maslow_brZ: "Bottom right z (normally 117)", - Maslow_tlX: "Top left anchor x (normally 0)", - Maslow_tlY: "Top left anchor y (normally height in mm)", - Maslow_tlZ: "Top left z (normally 144)", - Maslow_trX: "Top right anchor x (normally width in mm)", - Maslow_trY: "Top right anchor y (normally height in mm)", - Maslow_trZ: "Top right z (normally 97)", - Maslow_blX: "Bottom left anchor x (normally 0)", - Maslow_blY: "Bottom left anchor y (normally 0)", - Maslow_blZ: "Bottom left z (normally 75)", - Maslow_Retract_Current_Threshold: `Sets how hard should ${M} pull on the belts to retract before considering them to be all the way in`, - Maslow_Calibration_Current_Threshold: `Sets how hard should ${M} pull on the belts during the calibration process.`, - Maslow_calibration_extend_top_y: "starting Y for top belts on extend all (-1000 to 1000) default 0", - Maslow_calibration_extend_bottom_y: "starting Y for bottom belts on extend all (-1000 to 1000) default ", -} - -function refreshSettings(hide_setting_list) { - if (http_communication_locked) { - id('config_status').innerHTML = translate_text_item('Communication locked by another process, retry later.') - return - } - do_not_build_settings = typeof hide_setting_list == 'undefined' ? false : !hide_setting_list - - displayBlock('settings_loader') - displayNone('settings_list_content') - displayNone('settings_status') - displayNone('settings_refresh_btn') - - scl = [] - var url = '/command?plain=' + encodeURIComponent('[ESP400]') - SendGetHttp(url, getESPsettingsSuccess, getESPsettingsfailed) -} + Maslow_vertical: `If the ${M} is oriented horizontally, set this to false`, + Maslow_calibration_offset_X: "mm offset from the edge of the frame, X", + Maslow_calibration_offset_Y: "mm offset from the edge of the frame, Y", + Maslow_calibration_size_X: "Number of X points to use in calibration", + Maslow_calibration_size_Y: "Number of Y points to use in calibration", + Maslow_brX: "Bottom right anchor x (normally width in mm)", + Maslow_brY: "Bottom right anchor y (normally 0)", + Maslow_brZ: "Bottom right z (normally 117)", + Maslow_tlX: "Top left anchor x (normally 0)", + Maslow_tlY: "Top left anchor y (normally height in mm)", + Maslow_tlZ: "Top left z (normally 144)", + Maslow_trX: "Top right anchor x (normally width in mm)", + Maslow_trY: "Top right anchor y (normally height in mm)", + Maslow_trZ: "Top right z (normally 97)", + Maslow_blX: "Bottom left anchor x (normally 0)", + Maslow_blY: "Bottom left anchor y (normally 0)", + Maslow_blZ: "Bottom left z (normally 75)", + Maslow_Retract_Current_Threshold: `Sets how hard should ${M} pull on the belts to retract before considering them to be all the way in`, + Maslow_Calibration_Current_Threshold: `Sets how hard should ${M} pull on the belts during the calibration process.`, + Maslow_calibration_extend_top_y: + "starting Y for top belts on extend all (-1000 to 1000) default 0", + Maslow_calibration_extend_bottom_y: + "starting Y for bottom belts on extend all (-1000 to 1000) default ", +}; + +const refreshSettings = (hide_setting_list) => { + const common = new Common(); + if (common.http_communication_locked) { + setHTML("config_status", translate_text_item("Communication locked by another process, retry later.")); + return; + } + do_not_build_settings = + typeof hide_setting_list === "undefined" ? false : !hide_setting_list; + + displayBlock("settings_loader"); + displayNone("settings_list_content"); + displayNone("settings_status"); + displayNone("settings_refresh_btn"); + + scl = []; + const url = `/command?plain=${encodeURIComponent("[ESP400]")}`; + SendGetHttp(url, getESPsettingsSuccess, getESPsettingsfailed); +}; function defval(i) { - return scl[i].defaultvalue + return scl[i].defaultvalue; } /** Build a 'setting' id, any prefix (pf) if supplied should include an '_' at the end of its value */ @@ -59,528 +79,597 @@ const fCall = (fn, i, j) => `${fn}(${i},${j})`; /** Build a select option, includes ugly workaround for OSX Chrome and Safari. * Also note that the `translate` attribute is set to yes to instruct the browser to use its own translation * Therefore do NOT supply a span with translation details to this function e.g. from a call to `translate_text_item` -*/ -const bOpt = (value, isSelected, label) => `\n`; + */ +const bOpt = (value, isSelected, label) => + `\n`; function build_select_flag_for_setting_list(i, j) { - var html = `\n' - //console.log("default:" + defval(i)); - //console.log(html); - return html + var html = `"; + //console.log("default:" + defval(i)); + //console.log(html); + return html; } function build_select_for_setting_list(i, j) { - var html = `\n' - //console.log("default:" + defval(i)); - //console.log(html); - return html + var html = `"; + //console.log("default:" + defval(i)); + //console.log(html); + return html; } function update_UI_setting() { - for (var i = 0; i < scl.length; i++) { - switch (scl[i].pos) { - //EP_TARGET_FW 461 - case '850': - direct_sd = defval(i) == 1 ? true : false - update_UI_firmware_target() - init_files_panel(false) - break - case '130': - //set title using hostname - Set_page_title(defval(i)) - break - } - } -} -//to generate setting editor in setting or setup -function build_control_from_index(i, extra_set_function) { - var content = '' - if (i < scl.length) { - nbsub = scl[i].type == 'F' ? scl[i].Options.length : 1 - for (var j = 0; j < nbsub; j++) { - let params = `${i},${j}`; - if (j > 0) { - content += "" - } - content += "' - } - } - content += '
" - if (scl[i].type == 'F') { - content += translate_text_item(scl[i].Options[j].display, true) - content += ' ' - } - - content += `
` - content += "
" - content += '
' - content += "
" - content += "
" - // setting_revert_to_default() does not work for FluidNC, which cannot report default values - // content += ""; - content += '
' - content += "" - content += '
' - content += '
' - content += "
" - content += "" - if (scl[i].type == 'F') { - //flag - //console.log(scl[i].label + " " + scl[i].type); - //console.log(scl[i].Options.length); - content += build_select_flag_for_setting_list(i, j) - } else if (scl[i].Options.length > 0) { - //drop list - content += build_select_for_setting_list(i, j) - } else { - //text - input_type = defval(i).startsWith('******') ? 'password' : 'text'; - let action = `setting_checkchange(${params})`; - content += - `
`; - } - content += ``; - content += "" - content += '
' - content += '
' - content += "
" - content += "" - content += "
" - content += `' - } - content += '
' - content += '
' - content += '
' - content += '
' - content += '
' - return content -} - -//get setting UI for specific component instead of parse all + for (var i = 0; i < scl.length; i++) { + switch (scl[i].pos) { + //EP_TARGET_FW 461 + case "850": + direct_sd = defval(i) == 1 ? true : false; + update_UI_firmware_target(); + init_files_panel(false); + break; + case "130": + //set title using hostname + Set_page_title(defval(i)); + break; + } + } +} + +/** to generate setting editor in setting or setup */ +const build_control_from_index = (i, actions, extra_set_function = (i) => { }) => { + let content = ""; + if (i < scl.length && i > -1) { + nbsub = scl[i].type === "F" ? scl[i].Options.length : 1; + for (let j = 0; j < nbsub; j++) { + if (j > 0) { + content += ""; + } + content += ""; + } + } + content += "
"; + if (scl[i].type === "F") { + content += translate_text_item(scl[i].Options[j].display, true); + content += " "; + } + + const statId = sId(i, j, "status_"); + content += `
`; + content += "
"; + content += "
"; + content += "
"; + content += "
"; + // setting_revert_to_default() does not work for FluidNC, which cannot report default values + // content += `"; + content += "
"; + content += ""; + content += "
"; + content += "
"; + content += "
"; + content += ""; + const sfId = sId(i, j); + if (scl[i].type == "F") { + //flag + //console.log(scl[i].label + " " + scl[i].type); + //console.log(scl[i].Options.length); + content += build_select_flag_for_setting_list(i, j); + actions.push({ + id: sfId, + type: "change", + method: fCall("setting_checkchange", i, j), + }); + } else if (scl[i].Options.length > 0) { + //drop list + content += build_select_for_setting_list(i, j); + actions.push({ + id: sfId, + type: "change", + method: fCall("setting_checkchange", i, j), + }); + } else { + //text + input_type = defval(i).startsWith("******") ? "password" : "text"; + content += `
`; + actions.push({ + id: sfId, + type: "keyup", + method: setting_checkchange(i, j), + }); + } + content += ``; + content += ""; + content += "
"; + content += "
"; + content += "
"; + content += ""; + content += "
"; + const btnId = sId(i, j, "btn_"); + content += ``; + actions.push({ + id: btnId, + type: "click", + method: (i, j) => { + settingsetvalue(i, j); + extra_set_function(i); + }, + }); + if (scl[i].pos === EP_STA_SSID) { + const btnId = sId(i, j, "scanwifi_"); + content += ``; + actions.push({ id: btnId, type: "click", method: scanwifidlg(i, j) }); + } + content += "
"; + content += "
"; + content += "
"; + content += "
"; + content += "
"; + return content; +}; + +/** get setting UI for specific component instead of parse all */ function get_index_from_eeprom_pos(pos) { - for (var i = 0; i < scl.length; i++) { - if (pos == scl[i].pos) { - return i - } - } - return -1 -} + for (let i = 0; i < scl.length; i++) { + if (pos == scl[i].pos) { + return i; + } + } -function build_control_from_pos(pos, extra) { - return build_control_from_index(get_index_from_eeprom_pos(pos), extra) + // Indicates failure + return -1; } +const build_control_from_pos = (pos, actions, extra) => build_control_from_index(get_index_from_eeprom_pos(pos), actions, extra); + /** Send a command to call Config/Overwrite. - * + * * If the configuration is invalid, e.g. because the ESP32 performed a panic reset, * Then the error code 153 will be returned via the socket. * @see maslow.js maslowErrorMsgHandling() */ -function saveMaslowYaml() { - SendGetHttp('/command?plain=' + encodeURIComponent("$CO")); -} +const saveMaslowYaml = () => SendGetHttp(`/command?plain=${encodeURIComponent("$CO")}`); + +const build_HTML_setting_list = (filter) => { + //this to prevent concurrent process to update after we clean content + if (do_not_build_settings) { + return; + } -function build_HTML_setting_list(filter) { - //this to prevent concurrent process to update after we clean content - if (do_not_build_settings) { - return; - } + const buildTR = (tds) => `${tds}`; - const buildTR = (tds) => `${tds}`; + const actions = []; - var content = buildTR('Click "Set" after changing a value to set it'); - if (filter === 'tree') { - content += ` + let content = buildTR( + 'Click "Set" after changing a value to set it', + ); + if (filter === "tree") { + const btnId = "maslow_save_btn"; + content += ` Click "Save" after changing all values to save the
whole configuration to maslow.yaml and restart - + `; - } - current_setting_filter = filter; - id(current_setting_filter + '_setting_filter').checked = true; - - for (var i = 0; i < scl.length; i++) { - fname = scl[i].F.trim().toLowerCase(); - if (fname == 'network' || fname == filter || filter == 'all') { - let tr = `${translate_text_item(scl[i].label, true)}`; - const tooltip = CONFIG_TOOLTIPS[scl[i].label.substring(1)]; - if (tooltip) { - tr += `
+ actions.push({ id: btnId, type: "click", method: saveMaslowYaml() }); + } + const common = new Common(); + common.current_setting_filter = filter; + setChecked(`${common.current_setting_filter}_setting_filter`, true); + + for (let i = 0; i < scl.length; i++) { + fname = scl[i].F.trim().toLowerCase(); + if (fname === "network" || fname === filter || filter === "all") { + let tr = `${translate_text_item(scl[i].label, true)}`; + const tooltip = CONFIG_TOOLTIPS[scl[i].label.substring(1)]; + if (tooltip) { + tr += `
${tooltip}
`; - } - tr += '\n'; - tr += `
${build_control_from_index(i)}
\n`; - tr += '\n'; - content += tr; - } - } - id('settings_list_data').innerHTML = content; - if (filter === 'tree') { - document.querySelector('#setting_32_0').value = result; - } - // set calibration values if exists - if (Object.keys(calibrationResults).length) { - document.querySelector('#setting_153_0').value = calibrationResults.br.x; - document.querySelector('#setting_154_0').value = calibrationResults.br.y; - document.querySelector('#setting_155_0').value = calibrationResults.tl.x; - document.querySelector('#setting_156_0').value = calibrationResults.tl.y; - document.querySelector('#setting_157_0').value = calibrationResults.tr.x; - document.querySelector('#setting_158_0').value = calibrationResults.tr.y; - document.querySelector('#setting_159_0').value = calibrationResults.bl.x; - document.querySelector('#setting_160_0').value = calibrationResults.bl.y; - } - // set calibration values if exists END -} + } + tr += "\n"; + tr += `
${build_control_from_index(i, actions)}
\n`; + tr += "\n"; + content += tr; + } + } + // From settingstab + setHTML("settings_list_data", content); + actions.forEach((action) => { + id(action.id).addEventListener(action.type, (event) => action.method); + }); + if (filter === "tree") { + document.querySelector("#setting_32_0").value = result; + } + // set calibration values if exists + const calRes = common.calibrationResults; + if (Object.keys(calRes).length) { + document.querySelector("#setting_153_0").value = calRes.br.x; + document.querySelector("#setting_154_0").value = calRes.br.y; + document.querySelector("#setting_155_0").value = calRes.tl.x; + document.querySelector("#setting_156_0").value = calRes.tl.y; + document.querySelector("#setting_157_0").value = calRes.tr.x; + document.querySelector("#setting_158_0").value = calRes.tr.y; + document.querySelector("#setting_159_0").value = calRes.bl.x; + document.querySelector("#setting_160_0").value = calRes.bl.y; + } + // set calibration values if exists END +}; function setting_check_value(value, i) { - var valid = true - var entry = scl[i] - //console.log("checking value"); - if (entry.type == 'F') return valid - //does it part of a list? - if (entry.Options.length > 0) { - var in_list = false - for (var oi = 0; oi < entry.Options.length; oi++) { - //console.log("checking *" + entry.Options[oi].id + "* and *"+ value + "*" ); - if (entry.Options[oi].id == value) in_list = true - } - valid = in_list - if (!valid) setting_error_msg = ' in provided list' - } - //check byte / integer - if (entry.type == 'B' || entry.type == 'I') { - //cannot be empty - value.trim() - if (value.length == 0) valid = false - //check minimum? - if (parseInt(entry.min_val) > parseInt(value)) valid = false - //check maximum? - if (parseInt(entry.max_val) < parseInt(value)) valid = false - if (!valid) setting_error_msg = ' between ' + entry.min_val + ' and ' + entry.max_val - if (isNaN(value)) valid = false - } else if (entry.type == 'S') { - if (entry.min_val > value.length) valid = false - if (entry.max_val < value.length) valid = false - if (value == '********') valid = false - if (!valid) - setting_error_msg = - ' between ' + entry.min_val + ' char(s) and ' + entry.max_val + " char(s) long, and not '********'" - } else if (entry.type == 'A') { - //check ip address - var ipformat = - /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ - if (!value.match(ipformat)) { - valid = false - setting_error_msg = ' a valid IP format (xxx.xxx.xxx.xxx)' - } - } - return valid + var valid = true; + var entry = scl[i]; + //console.log("checking value"); + if (entry.type == "F") return valid; + //does it part of a list? + if (entry.Options.length > 0) { + var in_list = false; + for (var oi = 0; oi < entry.Options.length; oi++) { + //console.log("checking *" + entry.Options[oi].id + "* and *"+ value + "*" ); + if (entry.Options[oi].id == value) in_list = true; + } + valid = in_list; + if (!valid) setting_error_msg = " in provided list"; + } + //check byte / integer + if (entry.type == "B" || entry.type == "I") { + //cannot be empty + value.trim(); + if (value.length == 0) valid = false; + //check minimum? + if (parseInt(entry.min_val) > parseInt(value)) valid = false; + //check maximum? + if (parseInt(entry.max_val) < parseInt(value)) valid = false; + if (!valid) + setting_error_msg = " between " + entry.min_val + " and " + entry.max_val; + if (isNaN(value)) valid = false; + } else if (entry.type == "S") { + if (entry.min_val > value.length) valid = false; + if (entry.max_val < value.length) valid = false; + if (value == "********") valid = false; + if (!valid) + setting_error_msg = + " between " + + entry.min_val + + " char(s) and " + + entry.max_val + + " char(s) long, and not '********'"; + } else if (entry.type == "A") { + //check ip address + var ipformat = + /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + if (!value.match(ipformat)) { + valid = false; + setting_error_msg = " a valid IP format (xxx.xxx.xxx.xxx)"; + } + } + return valid; } function process_settings_answer(response_text) { - var result = true - try { - var response = JSON.parse(response_text) - if (typeof response.EEPROM == 'undefined') { - result = false - console.log('No EEPROM') - } else { - //console.log("EEPROM has " + response.EEPROM.length + " entries"); - if (response.EEPROM.length > 0) { - var vi = 0 - for (var i = 0; i < response.EEPROM.length; i++) { - vi = create_setting_entry(response.EEPROM[i], vi) - } - if (vi > 0) { - if (setup_is_done) build_HTML_setting_list(current_setting_filter) - update_UI_setting() - } else result = false - } else result = false - } - } catch (e) { - console.error('Parsing error:', e) - result = false - } - return result + let result = true; + try { + const response = JSON.parse(response_text); + if (typeof response.EEPROM == "undefined") { + result = false; + console.log("No EEPROM"); + } else { + //console.log("EEPROM has " + response.EEPROM.length + " entries"); + if (response.EEPROM.length > 0) { + let vi = 0; + for (let i = 0; i < response.EEPROM.length; i++) { + vi = create_setting_entry(response.EEPROM[i], vi); + } + if (vi > 0) { + const common = new Common(); + if (common.setup_is_done) { + build_HTML_setting_list(common.current_setting_filter); + } + update_UI_setting(); + } else result = false; + } else result = false; + } + } catch (e) { + console.error("Parsing error:", e); + result = false; + } + return result; } function create_setting_entry(sentry, vi) { - if (!is_setting_entry(sentry)) return vi - var slabel = sentry.H - var svalue = sentry.V - var scmd = '[ESP401]P=' + sentry.P + ' T=' + sentry.T + ' V=' - var options = [] - var min - var max - if (typeof sentry.M !== 'undefined') { - min = sentry.M - } else { - //add limit according the type - if (sentry.T == 'B') min = -127 - else if (sentry.T == 'S') min = 0 - else if (sentry.T == 'A') min = 7 - else if (sentry.T == 'I') min = 0 - } - if (typeof sentry.S !== 'undefined') { - max = sentry.S - } else { - //add limit according the type - if (sentry.T == 'B') max = 255 - else if (sentry.T == 'S') max = 255 - else if (sentry.T == 'A') max = 15 - else if (sentry.T == 'I') max = 2147483647 - } - //list possible options if defined - if (typeof sentry.O !== 'undefined') { - for (var i in sentry.O) { - var key = i - var val = sentry.O[i] - for (var j in val) { - var option = { - id: val[j].trim(), - display: j.trim(), - } - options.push(option) - //console.log("*" + option.display + "* and *" + option.id + "*"); - } - } - } - svalue = svalue.trim() - //create entry in list - var config_entry = { - index: vi, - F: sentry.F, - label: slabel, - defaultvalue: svalue, - cmd: scmd, - Options: options, - min_val: min, - max_val: max, - type: sentry.T, - pos: sentry.P, - } - scl.push(config_entry) - vi++ - return vi + if (!is_setting_entry(sentry)) { + return vi; + } + + var scmd = `[ESP401]P=${sentry.P} T=${sentry.T} V=`; + var options = []; + let min; + let max; + if (typeof sentry.M !== "undefined") { + min = sentry.M; + } else { + //add limit according the type + switch (sentry.T) { + case "B": + min = -127; + break; + case "A": + min = 7; + break; + case "S": + case "I": + min = 0; + break; + default: + min = 0; + break; + } + } + if (typeof sentry.S !== "undefined") { + max = sentry.S; + } else { + //add limit according the type + switch (sentry.T) { + case "B": + case "S": + max = 255; + break; + case "A": + max = 15; + break; + case "I": + max = 2147483647; + break; + default: + max = 2147483647; + break; + } + } + + //list possible options if defined + if (typeof sentry.O !== "undefined") { + for (var i in sentry.O) { + var val = sentry.O[i]; + for (var j in val) { + var option = { + id: val[j].trim(), + display: j.trim(), + }; + options.push(option); + //console.log("*" + option.display + "* and *" + option.id + "*"); + } + } + } + + //create entry in list + var config_entry = { + index: vi, + F: sentry.F, + label: sentry.H, + defaultvalue: sentry.V.trim(), + cmd: scmd, + Options: options, + min_val: min, + max_val: max, + type: sentry.T, + pos: sentry.P, + }; + scl.push(config_entry); + vi++; + return vi; } //check it is valid entry function is_setting_entry(sline) { - if ( - typeof sline.T === 'undefined' || - typeof sline.V === 'undefined' || - typeof sline.P === 'undefined' || - typeof sline.H === 'undefined' - ) { - return false - } - return true + if ( + typeof sline.T === "undefined" || + typeof sline.V === "undefined" || + typeof sline.P === "undefined" || + typeof sline.H === "undefined" + ) { + return false; + } + return true; } -const getFlag = (i, j) => (scl[i].type !== 'F' || scl[i].Options.length <= j) ? -1 : parseInt(scl[i].Options[j].id); +const getFlag = (i, j) => + scl[i].type !== "F" || scl[i].Options.length <= j + ? -1 + : parseInt(scl[i].Options[j].id); -function getFlag_description(i, j) { - if (scl[i].type != 'F' || scl[i].Options.length <= j) { - return -1; - } - return scl[i].Options[j].display; -} +const setting = (i, j) => id(sId(i, j)); -function setting(i, j) { - return id(sId(i, j)); -} function setBtn(i, j, value) { - id(sId(i, j, 'btn_')).className = `btn ${value}`; + id(sId(i, j, "btn_")).className = `btn ${value}`; } function setStatus(i, j, value) { - id(sId(i, j, 'status_')).className = `form-group ${value}`; + id(sId(i, j, "status_")).className = `form-group ${value}`; } function setIcon(i, j, value) { - id(sId(i, j, 'icon_')).className = `form-control-feedback ${value}`; + id(sId(i, j, "icon_")).className = `form-control-feedback ${value}`; } function setIconHTML(i, j, value) { - id(sId(i, j, 'icon_')).innerHTML = value; -} - -function setting_revert_to_default(i, j) { - if (typeof j == 'undefined') { - j = 0; - } - if (scl[i].type == 'F') { - const tst = parseInt(defval(i)); - setting(i, j).value = (tst == (tst | getFlag(i, j))) ? '1' : '0'; - } else { - setting(i, j).value = defval(i); - } - setBtn(i, j, 'btn-default'); - setStatus(i, j, 'form-group has-feedback'); - setIconHTML(i, j, ''); -} - -function settingsetvalue(i, j) { - if (typeof j == 'undefined') j = 0 - //remove possible spaces - value = setting(i, j).value.trim() - //Apply flag here - if (scl[i].type == 'F') { - var tmp = defval(i) - if (value == '1') { - tmp |= getFlag(i, j) - } else { - tmp &= ~getFlag(i, j) - } - value = tmp - } - if (value == defval(i)) return - //check validity of value - var isvalid = setting_check_value(value, i) - //if not valid show error - if (!isvalid) { - setsettingerror(i) - alertdlg(translate_text_item('Out of range'), translate_text_item('Value must be ') + setting_error_msg + ' !') - } else { - //value is ok save it - var cmd = scl[i].cmd + value - setting_lasti = i - setting_lastj = j - scl[i].defaultvalue = value - setBtn(i, j, 'btn-success') - setIcon(i, j, 'has-success ico_feedback') - setIconHTML(i, j, get_icon_svg('ok')) - setStatus(i, j, 'has-feedback has-success') - var url = '/command?plain=' + encodeURIComponent(cmd) - SendGetHttp(url, setESPsettingsSuccess, setESPsettingsfailed) - } + setHTML(sId(i, j, "icon_"), value); +} + +function setting_revert_to_default(i, j = 0) { + if (scl[i].type === "F") { + const tst = Number.parseInt(defval(i)); + setting(i, j).value = tst === (tst | getFlag(i, j)) ? "1" : "0"; + } else { + setting(i, j).value = defval(i); + } + setBtn(i, j, "btn-default"); + setStatus(i, j, "form-group has-feedback"); + setIconHTML(i, j, ""); +} + +function settingsetvalue(i, j = 0) { + //remove possible spaces + value = setting(i, j).value.trim(); + //Apply flag here + if (scl[i].type === "F") { + let tmp = defval(i); + if (value === "1") { + tmp |= getFlag(i, j); + } else { + tmp &= ~getFlag(i, j); + } + value = tmp; + } + if (value === defval(i)) return; + //check validity of value + const isvalid = setting_check_value(value, i); + //if not valid show error + if (!isvalid) { + setsettingerror(i); + alertdlg(translate_text_item("Out of range"), `${translate_text_item("Value must be ") + setting_error_msg} !`); + } else { + //value is ok save it + const cmd = scl[i].cmd + value; + setting_lasti = i; + setting_lastj = j; + scl[i].defaultvalue = value; + setBtn(i, j, "btn-success"); + setIcon(i, j, "has-success ico_feedback"); + setIconHTML(i, j, get_icon_svg("ok")); + setStatus(i, j, "has-feedback has-success"); + const url = `/command?plain=${encodeURIComponent(cmd)}`; + SendGetHttp(url, setESPsettingsSuccess, setESPsettingsfailed); + } } function setting_checkchange(i, j) { - //console.log("list value changed"); - var val = setting(i, j).value.trim() - if (scl[i].type == 'F') { - //console.log("it is flag value"); - var tmp = defval(i) - if (val == '1') { - tmp |= getFlag(i, j) - } else { - tmp &= ~getFlag(i, j) - } - val = tmp - } - //console.log("value: " + val); - //console.log("default value: " + defval(i)); - if (defval(i) == val) { - console.log('values are identical') - setBtn(i, j, 'btn-default') - setIcon(i, j, '') - setIconHTML(i, j, '') - setStatus(i, j, 'has-feedback') - } else if (setting_check_value(val, i)) { - //console.log("Check passed"); - setsettingchanged(i, j) - } else { - console.log('change bad') - setsettingerror(i, j) - } + //console.log("list value changed"); + var val = setting(i, j).value.trim(); + if (scl[i].type == "F") { + //console.log("it is flag value"); + var tmp = defval(i); + if (val == "1") { + tmp |= getFlag(i, j); + } else { + tmp &= ~getFlag(i, j); + } + val = tmp; + } + //console.log("value: " + val); + //console.log("default value: " + defval(i)); + if (defval(i) === val) { + console.log("values are identical"); + setBtn(i, j, "btn-default"); + setIcon(i, j, ""); + setIconHTML(i, j, ""); + setStatus(i, j, "has-feedback"); + } else if (setting_check_value(val, i)) { + //console.log("Check passed"); + setsettingchanged(i, j); + } else { + console.log("change bad"); + setsettingerror(i, j); + } } function setsettingchanged(i, j) { - setStatus(i, j, 'has-feedback has-warning') - setBtn(i, j, 'btn-warning') - setIcon(i, j, 'has-warning ico_feedback') - setIconHTML(i, j, get_icon_svg('warning-sign')) + setStatus(i, j, "has-feedback has-warning"); + setBtn(i, j, "btn-warning"); + setIcon(i, j, "has-warning ico_feedback"); + setIconHTML(i, j, get_icon_svg("warning-sign")); } function setsettingerror(i, j) { - setBtn(i, j, 'btn-danger') - setIcon(i, j, 'has-error ico_feedback') - setIconHTML(i, j, get_icon_svg('remove')) - setStatus(i, j, 'has-feedback has-error') + setBtn(i, j, "btn-danger"); + setIcon(i, j, "has-error ico_feedback"); + setIconHTML(i, j, get_icon_svg("remove")); + setStatus(i, j, "has-feedback has-error"); } function setESPsettingsSuccess(response) { - //console.log(response); - update_UI_setting() + //console.log(response); + update_UI_setting(); } function setESPsettingsfailed(error_code, response) { - const errMsg = stdErrMsg(error_code, response); - alertdlg(translate_text_item('Set failed'), errMsg); - conErr(errMsg); - setBtn(setting_lasti, setting_lastj, 'btn-danger'); - id('icon_setting_' + setting_lasti + '_' + setting_lastj).className = 'form-control-feedback has-error ico_feedback'; - id('icon_setting_' + setting_lasti + '_' + setting_lastj).innerHTML = get_icon_svg('remove'); - setStatus(setting_lasti, setting_lastj, 'has-feedback has-error'); + const errMsg = stdErrMsg(error_code, response); + alertdlg(translate_text_item("Set failed"), errMsg); + conErr(errMsg); + setBtn(setting_lasti, setting_lastj, "btn-danger"); + const iconName = `icon_setting_${setting_lasti}_${setting_lastj}`; + id(iconName).className = "form-control-feedback has-error ico_feedback"; + setHTML(iconName, get_icon_svg("remove")); + setStatus(setting_lasti, setting_lastj, "has-feedback has-error"); } function getESPsettingsSuccess(response) { - if (!process_settings_answer(response)) { - getESPsettingsfailed(406, translate_text_item('Wrong data')) - console.log(response) - return - } - displayNone('settings_loader') - displayBlock('settings_list_content') - displayNone('settings_status') - displayBlock('settings_refresh_btn') + if (!process_settings_answer(response)) { + getESPsettingsfailed(406, translate_text_item("Wrong data")); + console.log(response); + return; + } + displayNone("settings_loader"); + displayBlock("settings_list_content"); + displayNone("settings_status"); + displayBlock("settings_refresh_btn"); } function getESPsettingsfailed(error_code, response) { - conErr(error_code, response); - displayNone('settings_loader'); - displayBlock('settings_status'); - id('settings_status').innerHTML = stdErrMsg(error_code, response, translate_text_item('Failed')); - displayBlock('settings_refresh_btn'); + conErr(error_code, response); + displayNone("settings_loader"); + displayBlock("settings_status"); + setHTML("settings_status", stdErrMsg(error_code, response, translate_text_item("Failed"))); + displayBlock("settings_refresh_btn"); } -function restart_esp() { - confirmdlg(translate_text_item('Please Confirm'), translate_text_item('Restart FluidNC'), process_restart_esp) -} +const restart_esp = () => { + confirmdlg(translate_text_item("Please Confirm"), translate_text_item("Restart FluidNC"), process_restart_esp); +}; function process_restart_esp(answer) { - if (answer == 'yes') { - restartdlg() - } -} - -function define_esp_role(index) { - switch (Number(defval(index))) { - case SETTINGS_FALLBACK_MODE: - displayBlock('setup_STA') - displayBlock('setup_AP') - break - case SETTINGS_AP_MODE: - displayNone('setup_STA') - displayBlock('setup_AP') - break - case SETTINGS_STA_MODE: - displayBlock('setup_STA') - displayNone('setup_AP') - break - default: - displayNone('setup_STA') - displayNone('setup_AP') - break - } -} -function define_esp_role_from_pos(pos) { - define_esp_role(get_index_from_eeprom_pos(pos)) -} + if (answer === "yes") { + restartdlg(); + } +} + +const define_esp_role = (index) => { + switch (Number(defval(index))) { + case SETTINGS_FALLBACK_MODE: + displayBlock("setup_STA"); + displayBlock("setup_AP"); + break; + case SETTINGS_AP_MODE: + displayNone("setup_STA"); + displayBlock("setup_AP"); + break; + case SETTINGS_STA_MODE: + displayBlock("setup_STA"); + displayNone("setup_AP"); + break; + default: + displayNone("setup_STA"); + displayNone("setup_AP"); + break; + } +}; + +const define_esp_role_from_pos = (pos) => + define_esp_role(get_index_from_eeprom_pos(pos)); + +export { + build_control_from_pos, + build_HTML_setting_list, + define_esp_role, + define_esp_role_from_pos, + refreshSettings, + restart_esp, + saveMaslowYaml, +}; diff --git a/www/js/settingstab.js b/www/js/settingstab.js new file mode 100644 index 000000000..95ab02533 --- /dev/null +++ b/www/js/settingstab.js @@ -0,0 +1,25 @@ +import { + Common, + id, + refreshSettings, + restart_esp, + SPIFFSdlg, + statusdlg, + updatedlg, +} from "./common.js"; + +/** Set up the event handlers for the settings tab */ +const settingstab = () => { + id("nvs_setting_filter").addEventListener("click", (event) => build_HTML_setting_list("nvs") ); + id("tree_setting_filter").addEventListener("click", (event) => build_HTML_setting_list("tree") ); + + id("settings_status_btn").addEventListener("click", (event) => statusdlg()); + id("settings_SPIFFS_btn").addEventListener("click", (event) => SPIFFSdlg("/") ); + id("settings_update_fw_btn").addEventListener("click", (event) => updatedlg() ); + id("settings_restart_btn").addEventListener("click", (event) => restart_esp() ); + + const common = new Common(); + id("settings_refresh_btn").addEventListener("click", (event) => refreshSettings(common.current_setting_filter) ); +}; + +export { settingstab }; diff --git a/www/js/setupdlg.js b/www/js/setupdlg.js index 28210455f..643302ec0 100644 --- a/www/js/setupdlg.js +++ b/www/js/setupdlg.js @@ -1,20 +1,39 @@ +import { + Common, + get_icon_svg, + displayBlock, + displayNone, + displayUndoNone, + id, + setHTML, + closeModal, + setactiveModal, + showModal, + openstep, + getPrefValue, + setPrefValue, + SavePreferences, + build_language_list, + translate_text_item, + build_control_from_pos, + build_HTML_setting_list, + define_esp_role, + define_esp_role_from_pos, + translate_text, +} from "./common.js"; + //setup dialog +const common = new Common(); var active_wizard_page = 0; -var maz_page_wizard = 5; +var EP_HOSTNAME = "Hostname"; + +const td = (content) => `${content}`; +const table = (content) => `${content}
`; +const heading = (label) => `

${translate_text_item(label)}


`; + +const buildControlItem = (label, pos, actions, extra) => (translate_text_item(label) + table(build_control_from_pos(pos, actions, extra))); -function td(value) { - return "" + value + ""; -} -function table(value) { - return "" + value + "
"; -} -function heading(label) { - return "

" + translate_text_item(label) + "


"; -} -function item(label, pos, extra) { - return translate_text_item(label) + table(build_control_from_pos(pos, extra)); -} function wizardDone(element) { id(element).className = id(element).className.replace(" wizard_done", ""); } @@ -30,56 +49,72 @@ function openStep(wizard, step) { id(step).className = id(step).className.replace(" disabled", ""); } function closeStep(step) { - if (id(step).className.indexOf(" wizard_done") == -1) { - id(step).className += " wizard_done"; - if (!can_revert_wizard) id(step).className += " no_revert_wizard"; + if (id(step).className.indexOf(" wizard_done") !== -1) { + return; + } + + id(step).className += " wizard_done"; + + if (!common.can_revert_wizard) { + id(step).className += " no_revert_wizard"; } } -function spacer() { - return "
\n"; -} -function div(name) { - return "
"; -} -function endDiv() { - return "
"; -} -function setupdlg() { - setup_is_done = false; - language_save = language; - displayNone('main_ui'); - id('settings_list_data').innerHTML = ""; +const hardRule = () => "
\n"; +const div = (name) => `
`; +const endDiv = () => "
"; + +const setupdlg = () => { + common.setup_is_done = false; + displayNone("main_ui"); + // From settingstab + const settingstab_list_elem = id("settings_list_data"); + if (settingstab_list_elem) { + settingstab_list_elem.innerHTML = ""; + } active_wizard_page = 0; + const modal = setactiveModal("setupdlg.html", setupdone); + if (modal == null) { + return; + } + + id("setupDlgCancel").addEventListener("click", (event) => closeModal("cancel")); + + id("startsteplink").addEventListener("click", (event) => openstep(event, "startstep")); + id("step1link").addEventListener("click", (event) => openstep(event, "step1")); + id("step2link").addEventListener("click", (event) => openstep(event, "step2")); + id("step3link").addEventListener("click", (event) => openstep(event, "step3")); + id("endsteplink").addEventListener("click", (event) => openstep(event, "endstep")); + + id("wizard_button").addEventListener("click", (event) => continue_setup_wizard()); + wizardDone("startsteplink"); - id("wizard_button").innerHTML = translate_text_item("Start setup"); + setHTML("wizard_button", translate_text_item("Start setup")); disableStep("wizard_line1", "step1link"); disableStep("wizard_line2", "step2link"); disableStep("wizard_line3", "step3link"); displayNone("step3link"); - displayNone("wizard_line4") + displayNone("wizard_line4"); disableStep("wizard_line4", "endsteplink"); - var content = table( td(get_icon_svg("flag") + " ") + td(build_language_list("language_selection"))); - id("setup_langage_list").innerHTML = content; + const content = table(td(`${get_icon_svg("flag")} `) + td(build_language_list("language_selection"))); + setHTML("setup_langage_list", content); + id("language_selection").addEventListener("change", (event) => translate_text(getPrefValue("language_list"))); - var modal = setactiveModal('setupdlg.html', setupdone); - if (modal == null) return; showModal(); id("startsteplink", true).click(); -} - +}; function setupdone(response) { - setup_is_done = true; + common.setup_is_done = true; do_not_build_settings = false; - build_HTML_setting_list(current_setting_filter); - translate_text(language_save); - displayUndoNone('main_ui'); + build_HTML_setting_list(common.current_setting_filter); + translate_text(getPrefValue("language_list")); + displayUndoNone("main_ui"); closeModal("setup done"); } @@ -88,14 +123,14 @@ function continue_setup_wizard() { switch (active_wizard_page) { case 1: enablestep1(); - preferenceslist[0].language = language; + setPrefValue("language", getPrefValue("language_list")); SavePreferences(true); - language_save = language; break; case 2: enablestep2(); break; case 3: + // Pre-emptively step over step 3 active_wizard_page++; id("wizard_line3").style.background = "#337AB7"; enablestep4(); @@ -104,98 +139,114 @@ function continue_setup_wizard() { enablestep4(); break; case 5: - closeModal('ok') + closeModal("ok"); break; default: console.log("wizard page out of range"); + break; } } +const addActions = (actions) => { + actions.forEach((action) => { + id(action.id).addEventListener(action.type, (event) => action.method); + }); +}; + function enablestep1() { - var content = ""; - closeStep("startsteplink") - id("wizard_button").innerHTML = translate_text_item("Continue"); + closeStep("startsteplink"); + setHTML("wizard_button", translate_text_item("Continue")); openStep("wizard_line1", "step1link"); - content += heading("FluidNC Settings"); - content += item("Define ESP name:", EP_HOSTNAME); - id("step1").innerHTML = content + const actions = []; + + let content = heading("FluidNC Settings"); + content += buildControlItem("Define ESP name:", EP_HOSTNAME, actions); + + setHTML("step1", content); + addActions(actions); + id("step1link").click(); } function enablestep2() { + const actions = []; + var content = ""; closeStep("step1link"); openStep("wizard_line2", "step2link"); content += heading("WiFi Configuration"); - content += item("Define ESP role:", EP_WIFI_MODE, "define_esp_role"); - content += translate_text_item("AP define access point / STA allows to join existing network") + "
"; - content += spacer(); + content += buildControlItem("Define ESP role:", EP_WIFI_MODE, actions, define_esp_role); + content += `${translate_text_item("AP define access point / STA allows to join existing network")}
`; - content += div("setup_STA"); + content += hardRule(); - content += item("What access point ESP need to be connected to:", EP_STA_SSID); - content += translate_text_item("You can use scan button, to list available access points.") + "
"; - content += spacer(); - - content += item("Password to join access point:", EP_STA_PASSWORD); + content += div("setup_STA"); + content += buildControlItem("What access point ESP need to be connected to:", EP_STA_SSID, actions); + content += `${translate_text_item("You can use scan button, to list available access points.")}
`; + content += hardRule(); + content += buildControlItem("Password to join access point:", EP_STA_PASSWORD, actions); content += endDiv(); content += div("setup_AP"); - - content += item("What is ESP access point SSID:", EP_AP_SSID); - content += spacer(); - - content += item("Password for access point:", EP_AP_PASSWORD); - + content += buildControlItem("What is ESP access point SSID:", EP_AP_SSID, actions); + content += hardRule(); + content += buildControlItem("Password for access point:", EP_AP_PASSWORD, actions); content += endDiv(); - id("step2").innerHTML = content; + setHTML("step2", content); + addActions(actions); define_esp_role_from_pos(EP_WIFI_MODE); + id("step2link").click(); } -function define_sd_role(index) { +const define_sd_role = (index) => { if (setting_configList[index].defaultvalue == 1) { displayBlock("setup_SD"); - displayNone("setup_primary_SD");; + displayNone("setup_primary_SD"); } else { displayNone("setup_SD"); displayNone("setup_primary_SD"); } -} +}; function enablestep3() { + const actions = []; + var content = ""; closeStep("step2link"); openStep("wizard_line3", "step3link"); content += heading("SD Card Configuration"); - content += item("Is ESP connected to SD card:", EP_IS_DIRECT_SD, "define_sd_role"); - content += spacer(); + content += buildControlItem("Is ESP connected to SD card:", EP_IS_DIRECT_SD, actions, define_sd_role); + content += hardRule(); content += div("setup_SD"); - content += item("Check update using direct SD access:", EP_DIRECT_SD_CHECK); - content += spacer(); + content += buildControlItem("Check update using direct SD access:", EP_DIRECT_SD_CHECK, actions); + content += hardRule(); content += div("setup_primary_SD"); - content += item("SD card connected to ESP", EP_PRIMARY_SD); - content += spacer(); - - content += item("SD card connected to printer", EP_SECONDARY_SD); - content += spacer(); + content += buildControlItem("SD card connected to ESP", EP_PRIMARY_SD, actions); + content += hardRule(); + content += buildControlItem("SD card connected to printer", EP_SECONDARY_SD, actions); + content += hardRule(); content += endDiv(); content += endDiv(); - id("step3").innerHTML = content; + setHTML("step3", content); + addActions(actions); define_sd_role(get_index_from_eeprom_pos(EP_IS_DIRECT_SD)); + id("step3link").click(); } function enablestep4() { closeStep("step3link"); - id("wizard_button").innerHTML = translate_text_item("Close"); + setHTML("wizard_button", translate_text_item("Close")); openStep("wizard_line4", "endsteplink"); id("endsteplink").click(); } + +export { setupdlg }; diff --git a/www/js/simple-interpreter.js b/www/js/simple-interpreter.js index 9141d856e..fc5f6d01a 100644 --- a/www/js/simple-interpreter.js +++ b/www/js/simple-interpreter.js @@ -15,6 +15,10 @@ // d) Replaced arrow functions with real functions // e) Replaced let with var +// and all of the above is a profoundly bad way of supporting old browsers, so it will be progressively reverted +// More importantly anyone still using IE should NOT be supported, it is NOT in their interest to do so. +// Yes that means they MUST update to a more modern browser. This is NOT optional. + /** * Returns an object composed from arrays of property names and values. * @example @@ -141,3 +145,5 @@ Interpreter.prototype.loadFromLinesSync = function(lines) { } } } + +export { Interpreter }; diff --git a/www/js/simple-toolpath.js b/www/js/simple-toolpath.js index 489f162c2..316a8170d 100644 --- a/www/js/simple-toolpath.js +++ b/www/js/simple-toolpath.js @@ -3,6 +3,10 @@ // by Babel (http://babeljs.io/repl), with preset "stage-2" // The import and export statements were first removed from Toolpath.js +// and then the imports and exports were added back in, because using globals everywhere is a @#$%#$%#$% recipe for complete disaster + +import { Interpreter } from "./simple-interpreter.js"; + 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; @@ -834,3 +838,5 @@ var Toolpath = function () { return Toolpath; }(); + +export { Toolpath }; \ No newline at end of file diff --git a/www/js/socket.js b/www/js/socket.js index 5eb954a77..f2900eb66 100644 --- a/www/js/socket.js +++ b/www/js/socket.js @@ -1,4 +1,17 @@ -// import - Monitor_output_Update, id, HTMLDecode, setHTML, on_autocheck_position, enable_ping, grblHandleMessage, reportNone, clear_cmd_list, translate_text_item, UIdisableddlg +import { + Monitor_output_Update, + Common, + id, + HTMLDecode, + setHTML, + on_autocheck_position, + enable_ping, + grblHandleMessage, + reportNone, + clear_cmd_list, + translate_text_item, + UIdisableddlg, +} from "./common.js"; let convertDHT2Fahrenheit = false; let event_source; @@ -8,12 +21,14 @@ let ws_source; const CancelCurrentUpload = () => { xmlhttpupload.abort(); - //http_communication_locked = false; + //const common = new Common(); + //common.http_communication_locked = false; console.log("Cancel Upload"); }; const check_ping = () => { - if (Date.now() - last_ping > 20000) { + const common = new Common(); + if (Date.now() - common.last_ping > 20000) { Disable_interface(true); console.log("No heart beat for more than 20s"); } @@ -22,12 +37,13 @@ const check_ping = () => { let interval_ping = -1; /** Turn ping on or off based on its current value */ const handlePing = () => { - if (enable_ping) { + if (enable_ping()) { // First clear any existing interval if (interval_ping) { clearInterval(interval_ping); } - last_ping = Date.now(); + const common = new Common(); + common.last_ping = Date.now(); interval_ping = setInterval(() => check_ping(), 10 * 1000); console.log("enable ping"); } else { @@ -37,12 +53,12 @@ const handlePing = () => { } }; -let log_off = false; const Disable_interface = (lostconnection) => { let lostcon = false; if (typeof lostconnection !== "undefined") lostcon = lostconnection; //block all communication - http_communication_locked = true; + const common = new Common(); + common.http_communication_locked = true; log_off = true; if (interval_ping !== -1) clearInterval(interval_ping); //clear all waiting commands @@ -52,7 +68,7 @@ const Disable_interface = (lostconnection) => { //No auto check on_autocheck_position(false); reportNone(); - if (async_webcommunication) { + if (common.async_webcommunication) { event_source.removeEventListener("ActiveID", ActiveID_events, false); event_source.removeEventListener("InitID", Init_events, false); event_source.removeEventListener("DHT", DHT_events, false); @@ -63,10 +79,11 @@ const Disable_interface = (lostconnection) => { }; const EventListenerSetup = () => { - if (!async_webcommunication) { + const common = new Common(); + if (!common.async_webcommunication) { return; } - if (!!window.EventSource) { + if (window.EventSource) { event_source = new EventSource("/events"); event_source.addEventListener("InitID", Init_events, false); event_source.addEventListener("ActiveID", ActiveID_events, false); @@ -74,13 +91,20 @@ const EventListenerSetup = () => { } }; -const Init_events = (e) => { - page_id = e.data; - console.log(`connection id = ${page_id}`); -}; +let page_id = ""; +/** Get/Set the current page_id */ +const pageID = (value) => { + if (typeof value !== "undefined") { + page_id = value; + } + return page_id; +} + +/** Initialiase the page_id from the event data */ +const Init_events = (e) => console.log(`connection id = ${pageID(e.data)}`); const ActiveID_events = (e) => { - if (page_id === e.data) { + if (pageID() === e.data) { return; } @@ -100,9 +124,7 @@ const Handle_DHT = (data) => { return; } - const temp = convertDHT2Fahrenheit - ? Number.parseFloat(tdata[0]) * 1.8 + 32 - : Number.parseFloat(tdata[0]); + const temp = convertDHT2Fahrenheit ? Number.parseFloat(tdata[0]) * 1.8 + 32 : Number.parseFloat(tdata[0]); setHTML("DHT_humidity", `${Number.parseFloat(tdata[1]).toFixed(2).toString()}%`); const temps = `${temp.toFixed(2).toString()}°${convertDHT2Fahrenheit ? "F" : "C"}`; setHTML("DHT_temperature", temps); @@ -111,8 +133,9 @@ const Handle_DHT = (data) => { const process_socket_response = (msg) => msg.split("\n").forEach(grblHandleMessage); const startSocket = () => { + const common = new Common(); try { - if (async_webcommunication) { + if (common.async_webcommunication) { ws_source = new WebSocket(`ws://${document.location.host}/ws`, [ "arduino", ]); @@ -171,14 +194,13 @@ const startSocket = () => { const tval = msg.split(":"); if (tval.length >= 2) { if (tval[0] === "CURRENT_ID") { - page_id = tval[1]; - console.log(`connection id = ${page_id}`); + console.log(`connection id = ${pageID(tval[1])}`); } - if (enable_ping) { + if (enable_ping()) { if (tval[0] === "PING") { - page_id = tval[1]; - // console.log("ping from id = " + page_id); - last_ping = Date.now(); + pageID(tval[1]); + // console.log("ping from id = " + pageID()); + common.last_ping = Date.now(); if (interval_ping === -1) interval_ping = setInterval(() => { check_ping(); @@ -186,7 +208,7 @@ const startSocket = () => { } } if (tval[0] === "ACTIVE_ID") { - if (page_id !== tval[1]) { + if (pageID() !== tval[1]) { Disable_interface(); } } @@ -194,8 +216,9 @@ const startSocket = () => { Handle_DHT(tval[1]); } if (tval[0] === "ERROR") { - esp_error_message = tval[2]; - esp_error_code = tval[1]; + const common = new Common(); + common.esp_error_message = tval[2]; + common.esp_error_code = tval[1]; console.error(`ERROR: ${tval[2]} code:${tval[1]}`); CancelCurrentUpload(); } @@ -207,3 +230,12 @@ const startSocket = () => { //console.log(msg); }; }; + +export { + CancelCurrentUpload, + handlePing, + EventListenerSetup, + pageID, + process_socket_response, + startSocket, +}; diff --git a/www/js/statusdlg.js b/www/js/statusdlg.js index f78edee69..be5c1afa3 100644 --- a/www/js/statusdlg.js +++ b/www/js/statusdlg.js @@ -1,4 +1,18 @@ -// import get_icon_svg, conErr, stdErrMsg, displayBlock, displayNone, id, setHTML, closeModal, getactiveModal, setactiveModal, showModal, SendGetHttp, translate_text_item +import { + get_icon_svg, + conErr, + stdErrMsg, + displayBlock, + displayNone, + id, + setHTML, + closeModal, + getactiveModal, + setactiveModal, + showModal, + SendGetHttp, + translate_text_item, +} from "./common.js"; let statuspage = 0; let statuscontent = ""; @@ -51,7 +65,7 @@ function statussuccess(response) { if (modal == null) { return; } - + const text = modal.element.getElementsByClassName("modal-text")[0]; const tresponse = response.split("\n"); statuscontent = ""; @@ -101,3 +115,5 @@ function refreshstatus() { const url = `/command?plain=${encodeURIComponent("[ESP420]plain")}`; SendGetHttp(url, statussuccess, statusfailed); } + +export { statusdlg }; diff --git a/www/js/tablet.js b/www/js/tablet.js index 3619650a9..b92781a8f 100644 --- a/www/js/tablet.js +++ b/www/js/tablet.js @@ -1,21 +1,46 @@ -// When we can change to proper ESM - uncomment this -// import { checkHomed, maslowErrorMsgHandling, maslowInfoMsgHandling, maslowMsgHandling, sendCommand } from "maslow"; - -var gCodeLoaded = false -var gCodeDisplayable = false - -var snd = null -var sndok = true +import { + Common, + getValue, + id, + setValue, + setDisabled, + JogFeedrate, + numpad, + SendPrinterCommand, + files_list_success, + files_select_upload, + SendRealtimeCmd, + MPOS, + WPOS, + SendGetHttp, + checkHomed, + loadConfigValues, + loadCornerValues, + maslowErrorMsgHandling, + maslowInfoMsgHandling, + maslowMsgHandling, + saveConfigValues, + sendCommand, +} from "./common.js"; +// import { arrayToXYZ, displayer, refreshGcode } from "./toolpath-displayer.js"; + +var gCodeLoaded = false; +var gCodeDisplayable = false; + +var snd = null; +var sndok = true; var versionNumber = 0.87 /** Print the version number to the console */ const showVersionNumber = () => { const msgWindow = document.getElementById('messages'); - let text = msgWindow.textContent; - text = `${text}\nIndex.html Version: ${versionNumber}`; - msgWindow.textContent = text; - msgWindow.scrollTop = msgWindow.scrollHeight; + if (msgWindow) { + let text = msgWindow.textContent; + text = `${text}\nIndex.html Version: ${versionNumber}`; + msgWindow.textContent = text; + msgWindow.scrollTop = msgWindow.scrollHeight; + } } function beep(vol, freq, duration) { @@ -23,16 +48,16 @@ function beep(vol, freq, duration) { if (sndok) { try { snd = new Audio( - 'data:audio/wav;base64,//uQRAAAAWMSLwUIYAAsYkXgoQwAEaYLWfkWgAI0wWs/ItAAAGDgYtAgAyN+QWaAAihwMWm4G8QQRDiMcCBcH3Cc+CDv/7xA4Tvh9Rz/y8QADBwMWgQAZG/ILNAARQ4GLTcDeIIIhxGOBAuD7hOfBB3/94gcJ3w+o5/5eIAIAAAVwWgQAVQ2ORaIQwEMAJiDg95G4nQL7mQVWI6GwRcfsZAcsKkJvxgxEjzFUgfHoSQ9Qq7KNwqHwuB13MA4a1q/DmBrHgPcmjiGoh//EwC5nGPEmS4RcfkVKOhJf+WOgoxJclFz3kgn//dBA+ya1GhurNn8zb//9NNutNuhz31f////9vt///z+IdAEAAAK4LQIAKobHItEIYCGAExBwe8jcToF9zIKrEdDYIuP2MgOWFSE34wYiR5iqQPj0JIeoVdlG4VD4XA67mAcNa1fhzA1jwHuTRxDUQ//iYBczjHiTJcIuPyKlHQkv/LHQUYkuSi57yQT//uggfZNajQ3Vmz+Zt//+mm3Wm3Q576v////+32///5/EOgAAADVghQAAAAA//uQZAUAB1WI0PZugAAAAAoQwAAAEk3nRd2qAAAAACiDgAAAAAAABCqEEQRLCgwpBGMlJkIz8jKhGvj4k6jzRnqasNKIeoh5gI7BJaC1A1AoNBjJgbyApVS4IDlZgDU5WUAxEKDNmmALHzZp0Fkz1FMTmGFl1FMEyodIavcCAUHDWrKAIA4aa2oCgILEBupZgHvAhEBcZ6joQBxS76AgccrFlczBvKLC0QI2cBoCFvfTDAo7eoOQInqDPBtvrDEZBNYN5xwNwxQRfw8ZQ5wQVLvO8OYU+mHvFLlDh05Mdg7BT6YrRPpCBznMB2r//xKJjyyOh+cImr2/4doscwD6neZjuZR4AgAABYAAAABy1xcdQtxYBYYZdifkUDgzzXaXn98Z0oi9ILU5mBjFANmRwlVJ3/6jYDAmxaiDG3/6xjQQCCKkRb/6kg/wW+kSJ5//rLobkLSiKmqP/0ikJuDaSaSf/6JiLYLEYnW/+kXg1WRVJL/9EmQ1YZIsv/6Qzwy5qk7/+tEU0nkls3/zIUMPKNX/6yZLf+kFgAfgGyLFAUwY//uQZAUABcd5UiNPVXAAAApAAAAAE0VZQKw9ISAAACgAAAAAVQIygIElVrFkBS+Jhi+EAuu+lKAkYUEIsmEAEoMeDmCETMvfSHTGkF5RWH7kz/ESHWPAq/kcCRhqBtMdokPdM7vil7RG98A2sc7zO6ZvTdM7pmOUAZTnJW+NXxqmd41dqJ6mLTXxrPpnV8avaIf5SvL7pndPvPpndJR9Kuu8fePvuiuhorgWjp7Mf/PRjxcFCPDkW31srioCExivv9lcwKEaHsf/7ow2Fl1T/9RkXgEhYElAoCLFtMArxwivDJJ+bR1HTKJdlEoTELCIqgEwVGSQ+hIm0NbK8WXcTEI0UPoa2NbG4y2K00JEWbZavJXkYaqo9CRHS55FcZTjKEk3NKoCYUnSQ0rWxrZbFKbKIhOKPZe1cJKzZSaQrIyULHDZmV5K4xySsDRKWOruanGtjLJXFEmwaIbDLX0hIPBUQPVFVkQkDoUNfSoDgQGKPekoxeGzA4DUvnn4bxzcZrtJyipKfPNy5w+9lnXwgqsiyHNeSVpemw4bWb9psYeq//uQZBoABQt4yMVxYAIAAAkQoAAAHvYpL5m6AAgAACXDAAAAD59jblTirQe9upFsmZbpMudy7Lz1X1DYsxOOSWpfPqNX2WqktK0DMvuGwlbNj44TleLPQ+Gsfb+GOWOKJoIrWb3cIMeeON6lz2umTqMXV8Mj30yWPpjoSa9ujK8SyeJP5y5mOW1D6hvLepeveEAEDo0mgCRClOEgANv3B9a6fikgUSu/DmAMATrGx7nng5p5iimPNZsfQLYB2sDLIkzRKZOHGAaUyDcpFBSLG9MCQALgAIgQs2YunOszLSAyQYPVC2YdGGeHD2dTdJk1pAHGAWDjnkcLKFymS3RQZTInzySoBwMG0QueC3gMsCEYxUqlrcxK6k1LQQcsmyYeQPdC2YfuGPASCBkcVMQQqpVJshui1tkXQJQV0OXGAZMXSOEEBRirXbVRQW7ugq7IM7rPWSZyDlM3IuNEkxzCOJ0ny2ThNkyRai1b6ev//3dzNGzNb//4uAvHT5sURcZCFcuKLhOFs8mLAAEAt4UWAAIABAAAAAB4qbHo0tIjVkUU//uQZAwABfSFz3ZqQAAAAAngwAAAE1HjMp2qAAAAACZDgAAAD5UkTE1UgZEUExqYynN1qZvqIOREEFmBcJQkwdxiFtw0qEOkGYfRDifBui9MQg4QAHAqWtAWHoCxu1Yf4VfWLPIM2mHDFsbQEVGwyqQoQcwnfHeIkNt9YnkiaS1oizycqJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=' - ) + "data:audio/wav;base64,//uQRAAAAWMSLwUIYAAsYkXgoQwAEaYLWfkWgAI0wWs/ItAAAGDgYtAgAyN+QWaAAihwMWm4G8QQRDiMcCBcH3Cc+CDv/7xA4Tvh9Rz/y8QADBwMWgQAZG/ILNAARQ4GLTcDeIIIhxGOBAuD7hOfBB3/94gcJ3w+o5/5eIAIAAAVwWgQAVQ2ORaIQwEMAJiDg95G4nQL7mQVWI6GwRcfsZAcsKkJvxgxEjzFUgfHoSQ9Qq7KNwqHwuB13MA4a1q/DmBrHgPcmjiGoh//EwC5nGPEmS4RcfkVKOhJf+WOgoxJclFz3kgn//dBA+ya1GhurNn8zb//9NNutNuhz31f////9vt///z+IdAEAAAK4LQIAKobHItEIYCGAExBwe8jcToF9zIKrEdDYIuP2MgOWFSE34wYiR5iqQPj0JIeoVdlG4VD4XA67mAcNa1fhzA1jwHuTRxDUQ//iYBczjHiTJcIuPyKlHQkv/LHQUYkuSi57yQT//uggfZNajQ3Vmz+Zt//+mm3Wm3Q576v////+32///5/EOgAAADVghQAAAAA//uQZAUAB1WI0PZugAAAAAoQwAAAEk3nRd2qAAAAACiDgAAAAAAABCqEEQRLCgwpBGMlJkIz8jKhGvj4k6jzRnqasNKIeoh5gI7BJaC1A1AoNBjJgbyApVS4IDlZgDU5WUAxEKDNmmALHzZp0Fkz1FMTmGFl1FMEyodIavcCAUHDWrKAIA4aa2oCgILEBupZgHvAhEBcZ6joQBxS76AgccrFlczBvKLC0QI2cBoCFvfTDAo7eoOQInqDPBtvrDEZBNYN5xwNwxQRfw8ZQ5wQVLvO8OYU+mHvFLlDh05Mdg7BT6YrRPpCBznMB2r//xKJjyyOh+cImr2/4doscwD6neZjuZR4AgAABYAAAABy1xcdQtxYBYYZdifkUDgzzXaXn98Z0oi9ILU5mBjFANmRwlVJ3/6jYDAmxaiDG3/6xjQQCCKkRb/6kg/wW+kSJ5//rLobkLSiKmqP/0ikJuDaSaSf/6JiLYLEYnW/+kXg1WRVJL/9EmQ1YZIsv/6Qzwy5qk7/+tEU0nkls3/zIUMPKNX/6yZLf+kFgAfgGyLFAUwY//uQZAUABcd5UiNPVXAAAApAAAAAE0VZQKw9ISAAACgAAAAAVQIygIElVrFkBS+Jhi+EAuu+lKAkYUEIsmEAEoMeDmCETMvfSHTGkF5RWH7kz/ESHWPAq/kcCRhqBtMdokPdM7vil7RG98A2sc7zO6ZvTdM7pmOUAZTnJW+NXxqmd41dqJ6mLTXxrPpnV8avaIf5SvL7pndPvPpndJR9Kuu8fePvuiuhorgWjp7Mf/PRjxcFCPDkW31srioCExivv9lcwKEaHsf/7ow2Fl1T/9RkXgEhYElAoCLFtMArxwivDJJ+bR1HTKJdlEoTELCIqgEwVGSQ+hIm0NbK8WXcTEI0UPoa2NbG4y2K00JEWbZavJXkYaqo9CRHS55FcZTjKEk3NKoCYUnSQ0rWxrZbFKbKIhOKPZe1cJKzZSaQrIyULHDZmV5K4xySsDRKWOruanGtjLJXFEmwaIbDLX0hIPBUQPVFVkQkDoUNfSoDgQGKPekoxeGzA4DUvnn4bxzcZrtJyipKfPNy5w+9lnXwgqsiyHNeSVpemw4bWb9psYeq//uQZBoABQt4yMVxYAIAAAkQoAAAHvYpL5m6AAgAACXDAAAAD59jblTirQe9upFsmZbpMudy7Lz1X1DYsxOOSWpfPqNX2WqktK0DMvuGwlbNj44TleLPQ+Gsfb+GOWOKJoIrWb3cIMeeON6lz2umTqMXV8Mj30yWPpjoSa9ujK8SyeJP5y5mOW1D6hvLepeveEAEDo0mgCRClOEgANv3B9a6fikgUSu/DmAMATrGx7nng5p5iimPNZsfQLYB2sDLIkzRKZOHGAaUyDcpFBSLG9MCQALgAIgQs2YunOszLSAyQYPVC2YdGGeHD2dTdJk1pAHGAWDjnkcLKFymS3RQZTInzySoBwMG0QueC3gMsCEYxUqlrcxK6k1LQQcsmyYeQPdC2YfuGPASCBkcVMQQqpVJshui1tkXQJQV0OXGAZMXSOEEBRirXbVRQW7ugq7IM7rPWSZyDlM3IuNEkxzCOJ0ny2ThNkyRai1b6ev//3dzNGzNb//4uAvHT5sURcZCFcuKLhOFs8mLAAEAt4UWAAIABAAAAAB4qbHo0tIjVkUU//uQZAwABfSFz3ZqQAAAAAngwAAAE1HjMp2qAAAAACZDgAAAD5UkTE1UgZEUExqYynN1qZvqIOREEFmBcJQkwdxiFtw0qEOkGYfRDifBui9MQg4QAHAqWtAWHoCxu1Yf4VfWLPIM2mHDFsbQEVGwyqQoQcwnfHeIkNt9YnkiaS1oizycqJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=", + ); } catch (error) { - snd = null - sndok = false + snd = null; + sndok = false; } } } if (snd) { - snd.play() + snd.play(); } } @@ -46,12 +71,12 @@ function tabletClick() { const moveTo = (location) => { // Always force G90 mode because synchronization of modal reports is unreliable sendCommand(`G90 G0 ${location}`); -} +}; const MDIcmd = (value) => { tabletClick(); sendCommand(value); -} +}; // const MDI = (field) => { // MDIcmd(id(field).value) @@ -59,37 +84,35 @@ const MDIcmd = (value) => { // const enterFullscreen = () => { // try { -// document.documentElement.requestFullscreen() +// document.documentElement.requestFullscreen(); // } catch (exception) { // try { -// document.documentElement.webkitRequestFullscreen() +// document.documentElement.webkitRequestFullscreen(); // } catch (exception) { -// return +// return; // } // } -// } -const exitFullscreen = () => { - try { - document.exitFullscreen() - } catch (exception) { - try { - document.webkitExitFullscreen() - } catch (exception) { - return - } - } -} - -/** This does nothing, but it does get called */ -const toggleFullscreen = () => { } +// }; +// const exitFullscreen = () => { +// try { +// document.exitFullscreen(); +// } catch (exception) { +// try { +// document.webkitExitFullscreen(); +// } catch (exception) { +// return; +// } +// } +// }; -// const inputFocused = () => { isInputFocused = true; }; +const toggleFullscreen = () => { }; -// const inputBlurred = () => { isInputFocused = false; }; +// const inputFocused = () => isInputFocused = true; +// const inputBlurred = () => isInputFocused = false; // Define XY Home functions let xyHomeTimerId = null; -const xyHomeBtnId = "defineHomeBTN"; +const xyHomeBtnId = "tablettab_set_xy_home"; const xyHomeLabelDefault = "Define XY Home"; const xyHomeLabelInstr = "Press+Hold Tap_x2"; const xyHomeLabelRedefined = "XY Home Redefined"; @@ -104,16 +127,16 @@ const clearXYHomeTimer = () => { xyHomeTimerId = null; // Reset the button label setTimeout(setXYHomeBtnText, 1000); -} +}; const setXYHome = () => { clearXYHomeTimer(); - zeroAxis('X'); - zeroAxis('Y'); + zeroAxis("X"); + zeroAxis("Y"); // This changed label will only show for 1 second before being reset setXYHomeBtnText(xyHomeLabelRedefined); setTimeout(refreshGcode, 100); -} +}; const xyHomeTimer = () => { const buttonText = getXYHomeBtnText(); @@ -129,104 +152,105 @@ const xyHomeTimer = () => { // The user clicked / tapped once or didn't press+hold for 5 full seconds setTimeout(setXYHomeBtnText, 1000); } -} +}; /** Click down starts the xyHomeTimer function and sets the button text to 5 */ const setHomeClickDown = () => { setXYHomeBtnText(5); xyHomeTimer(); -} +}; /** Click up cancels the xyHomeTimer and cleans up */ const setHomeClickUp = () => { if (xyHomeTimerId != null) { setXYHomeBtnText(xyHomeLabelInstr); } -} +}; const zeroAxis = (axis) => { - tabletClick() - setAxisByValue(axis, 0) - - const msgWindow = document.getElementById('messages') - let text = msgWindow.textContent - text += `\nHome pos set for: ${axis}` - msgWindow.textContent = text - msgWindow.scrollTop = msgWindow.scrollHeight -} + tabletClick(); + setAxisByValue(axis, 0); + + const msgWindow = document.getElementById("messages"); + let text = msgWindow.textContent; + text += `\nHome pos set for: ${axis}`; + msgWindow.textContent = text; + msgWindow.scrollTop = msgWindow.scrollHeight; +}; const toggleUnits = () => { - tabletClick() - sendCommand(modal.units === 'G21' ? 'G20' : 'G21'); + tabletClick(); + const common = new Common(); + sendCommand(common.modal.units === "G21" ? "G20" : "G21"); // The button label will be fixed by the response to $G - sendCommand('$G'); -} + sendCommand("$G"); +}; // const btnSetDistance = () => { -// tabletClick() -// var distance = event.target.innerText -// id('jog-distance').value = distance -// } +// tabletClick(); +// const distance = event.target.innerText; +// setValue("jog-distance", distance); +// }; // const setDistance = (distance) => { -// tabletClick() -// id('jog-distance').value = distance -// } +// tabletClick(); +// setValue("jog-distance", distance); +// }; const jogTo = (axisAndDistance) => { // Always force G90 mode because synchronization of modal reports is unreliable - var feedrate = JogFeedrate(axisAndDistance) - if (modal.units === 'G20') { + let feedrate = JogFeedrate(axisAndDistance); + const common = new Common(); + if (common.modal.units === "G20") { feedrate /= 25.4; feedrate = feedrate.toFixed(2); } // tabletShowMessage("JogTo " + cmd); sendCommand(`$J=G91F${feedrate}${axisAndDistance}\n`); -} +}; const goAxisByValue = (axis, coordinate) => { - tabletClick() - moveTo(axis + coordinate) -} + tabletClick(); + moveTo(axis + coordinate); +}; const setAxisByValue = (axis, coordinate) => { tabletClick(); sendCommand(`G10 L20 P0 ${axis}${coordinate}`); -} +}; const setAxis = (axis, field) => { tabletClick(); sendCommand(`G10 L20 P1 ${axis}${id(field).value}`); -} -var timeout_id = 0, - hold_time = 1000 +}; -var longone = false +var longone = false; function long_jog(target) { - longone = true - distance = 1000 - var axisAndDirection = target.value - var feedrate = JogFeedrate(axisAndDirection) - if (modal.units == 'G20') { - distance /= 25.4 - distance = distance.toFixed(3) - feedrate /= 25.4 - feedrate = feedrate.toFixed(2) + longone = true; + distance = 1000; + const axisAndDirection = target.value; + let feedrate = JogFeedrate(axisAndDirection); + const common = new Common(); + if (common.modal.units === "G20") { + distance /= 25.4; + distance = distance.toFixed(3); + feedrate /= 25.4; + feedrate = feedrate.toFixed(2); } // tabletShowMessage("Long Jog " + cmd); - sendCommand(`$J=G91F${feedrate}${axisAndDirection}${distance}\n`) + sendCommand(`$J=G91F${feedrate}${axisAndDirection}${distance}\n`); } const sendMove = (cmd) => { - tabletClick() + tabletClick(); const jog = (params) => { - params = params || {} - let s = '' + params = params || {}; + var s = ""; for (key in params) { - s += key + params[key] + s += key + params[key]; } - jogTo(s) + jogTo(s); const msgWindow = document.getElementById('messages') let text = msgWindow.textContent @@ -239,10 +263,10 @@ const sendMove = (cmd) => { params = params || {} let s = '' for (key in params) { - s += key + params[key] + s += key + params[key]; } - moveTo(s) - } + moveTo(s); + }; let distance = cmd.includes('Z') ? Number(id('disZ').innerText) || 0 : Number(id('disM').innerText) || 0 @@ -255,54 +279,54 @@ const sendMove = (cmd) => { Z0: () => move({ Z: 0 }), 'X-Y+': () => { if (checkHomed()) { - jog({ X: -distance, Y: distance }) + jog({ X: -distance, Y: distance }); } }, 'X+Y+': () => { if (checkHomed()) { - jog({ X: distance, Y: distance }) + jog({ X: distance, Y: distance }); } }, 'X-Y-': () => { if (checkHomed()) { - jog({ X: -distance, Y: -distance }) + jog({ X: -distance, Y: -distance }); } }, 'X+Y-': () => { if (checkHomed()) { - jog({ X: distance, Y: -distance }) + jog({ X: distance, Y: -distance }); } }, 'X-': () => { if (checkHomed()) { - jog({ X: -distance }) + jog({ X: -distance }); } }, 'X+': () => { if (checkHomed()) { - jog({ X: distance }) + jog({ X: distance }); } }, 'Y-': () => { if (checkHomed()) { - jog({ Y: -distance }) + jog({ Y: -distance }); } }, 'Y+': () => { if (checkHomed()) { - jog({ Y: distance }) + jog({ Y: distance }); } }, 'Z-': () => jog({ Z: -distance }), 'Z+': () => jog({ Z: distance }), 'Z_TOP': () => { // She's got legs ♫ - move({ Z: 70 }) + move({ Z: 70 }); }, - }[cmd] + }[cmd]; - fn && fn() -} + fn && fn(); +}; const moveHome = () => { if (!checkHomed()) { @@ -310,21 +334,20 @@ const moveHome = () => { } //We want to move to the opposite of the machine's current X,Y cordinates - const x = Number.parseFloat(id('mpos-x').innerText) - const y = Number.parseFloat(id('mpos-y').innerText) + var x = parseFloat(id("mpos-x").innerText); + var y = parseFloat(id("mpos-y").innerText); const jog = (params) => { - params = params || {} - let s = '' + params = params || {}; + var s = ""; for (key in params) { - s += key + params[key] + s += key + params[key]; } - jogTo(s) - } - - jog({ X: -1 * x, Y: -1 * y }) -} + jogTo(s); + }; + jog({ X: -1 * x, Y: -1 * y }); +}; // setInterval(checkOnHeartbeat, 500); // function checkOnHeartbeat() { @@ -337,89 +360,118 @@ const moveHome = () => { // lastHeartBeatTime = new Date().getTime(); // } // } -function saveSerialMessages() { - // save off the serial messages - const msgs = document.getElementById('messages').value; - const link = document.createElement('a'); - link.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURI(msgs)}`); - link.setAttribute('download', "Maslow-serial.log"); + +/** save off the serial messages */ +const saveSerialMessages = () => { + const msgs = getValue("messages"); + const link = document.createElement("a"); + link.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURI(msgs)); + link.setAttribute("download", "Maslow-serial.log"); document.body.appendChild(link); link.click(); document.body.removeChild(link); -} +}; + +/** Loaded Values of the maslow config, this can be a const because we only change the fields within it */ +const loaded_values = {}; +/** Work with the maslow config loaded values. + * If `fieldName` is undefined, or `value` is undefined and `fieldname` is not in the values, then return the values we have. + * If `value` is undefined, but `fieldname` exists, just return the value for `fieldname` + * Otherwise set `fieldname` to the `value` and return it + */ +const loadedValues = (fieldName, value) => { + if (typeof fieldName === "undefined") { + return loaded_values; + } + if (typeof value === "undefined") { + return !(fieldName in loaded_values) + ? loaded_values + : loaded_values[fieldName]; + } + loaded_values[fieldName] = value; + return loaded_values[fieldName]; +}; -var loadedValues = {}; function tabletShowMessage(msg, collecting) { if ( collecting || - msg === '' || - msg.startsWith('<') || - msg.startsWith('ok') || - msg.startsWith('\n') || - msg.startsWith('\r') + msg === "" || + msg.startsWith("<") || + msg.startsWith("ok") || + msg.startsWith("\n") || + msg.startsWith("\r") ) { - return + return; } if (maslowInfoMsgHandling(msg)) { return; } - if (msg.startsWith('[GC')) { + if (msg.startsWith("[GC")) { return; } let errMsg = ""; //These are used for populating the configuration popup - if (msg.startsWith('$/Maslow_') || msg.startsWith('$/maslow_')) { + if (msg.startsWith("$/Maslow_") || msg.startsWith("$/maslow_")) { errMsg = maslowMsgHandling(msg.substring(9)); return; //We don't want to display these messages } - const msgWindow = document.getElementById('messages') + const msgWindow = document.getElementById("messages"); msgWindow.textContent = `${msgWindow.textContent}\n${maslowErrorMsgHandling(msg) || msg}`; msgWindow.scrollTop = msgWindow.scrollHeight; } function tabletShowResponse(response) { } -function clearAlarm() { - if (id('systemStatus').innerText == 'Alarm') { - id('systemStatus').classList.remove('system-status-alarm') - SendPrinterCommand('$X', true, null, null, 114, 1) +const clearAlarm = () => { + const sysStatus = id("systemStatus"); + if (sysStatus.innerText == "Alarm") { + sysStatus.classList.remove("system-status-alarm"); + SendPrinterCommand("$X", true, null, null, 114, 1); } -} +}; function setJogSelector(units) { - var buttonDistances = [] - var menuDistances = [] - var selected = 0 - if (units == 'G20') { + var buttonDistances = []; + var menuDistances = []; + var selected = 0; + if (units == "G20") { // Inches - buttonDistances = [0.001, 0.01, 0.1, 1, 0.003, 0.03, 0.3, 3, 0.005, 0.05, 0.5, 5] - menuDistances = [0.00025, 0.0005, 0.001, 0.003, 0.005, 0.01, 0.03, 0.05, 0.1, 0.3, 0.5, 1, 3, 5, 10, 30] - selected = '1' + buttonDistances = [ + 0.001, 0.01, 0.1, 1, 0.003, 0.03, 0.3, 3, 0.005, 0.05, 0.5, 5, + ]; + menuDistances = [ + 0.00025, 0.0005, 0.001, 0.003, 0.005, 0.01, 0.03, 0.05, 0.1, 0.3, 0.5, 1, + 3, 5, 10, 30, + ]; + selected = "1"; } else { // millimeters - buttonDistances = [0.1, 1, 10, 100, 0.3, 3, 30, 300, 0.5, 5, 50, 500] - menuDistances = [0.005, 0.01, 0.03, 0.05, 0.1, 0.3, 0.5, 1, 3, 5, 10, 30, 50, 100, 300, 500, 1000] - selected = '10' - } - const buttonNames = [ - 'jog00', - 'jog01', - 'jog02', - 'jog03', - 'jog10', - 'jog11', - 'jog12', - 'jog13', - 'jog20', - 'jog21', - 'jog22', - 'jog23', - ] + buttonDistances = [0.1, 1, 10, 100, 0.3, 3, 30, 300, 0.5, 5, 50, 500]; + menuDistances = [ + 0.005, 0.01, 0.03, 0.05, 0.1, 0.3, 0.5, 1, 3, 5, 10, 30, 50, 100, 300, + 500, 1000, + ]; + selected = "10"; + } + var buttonNames = [ + "jog00", + "jog01", + "jog02", + "jog03", + "jog10", + "jog11", + "jog12", + "jog13", + "jog20", + "jog21", + "jog22", + "jog23", + ]; //buttonNames.forEach( function(n, i) { id(n).innerHTML = buttonDistances[i]; } ); // var selector = id('jog-distance'); @@ -445,104 +497,109 @@ function addJogDistance(distance) { //return selector.appendChild(option); } -var runTime = 0 +var runTime = 0; function setButton(name, isEnabled, color, text) { - var button = id(name) - button.disabled = !isEnabled - button.style.backgroundColor = color - button.innerText = text + var button = id(name); + button.disabled = !isEnabled; + button.style.backgroundColor = color; + button.innerText = text; } -var playButtonHandler +var playButtonHandler; function setPlayButton(isEnabled, color, text, click) { - setButton('playBtn', isEnabled, color, text); - playButtonHandler = click + setButton("playBtn", isEnabled, color, text); + playButtonHandler = click; } function doPlayButton() { if (playButtonHandler) { - playButtonHandler() + playButtonHandler(); } - const msgWindow = document.getElementById('messages') - let text = msgWindow.textContent - text += `\nStarting File: ${document.getElementById('filelist').options[selectElement.selectedIndex].text}` - msgWindow.textContent = text - msgWindow.scrollTop = msgWindow.scrollHeight - + // selectElement does not exist anywhere else but here, looks like this code was orphaned a long time ago ... + const msgWindow = document.getElementById("messages"); + let text = msgWindow.textContent; + text += `\nStarting File: ${document.getElementById("filelist").options[selectElement.selectedIndex].text}`; + msgWindow.textContent = text; + msgWindow.scrollTop = msgWindow.scrollHeight; } -var pauseButtonHandler +var pauseButtonHandler; function setPauseButton(isEnabled, color, text, click) { - setButton('pauseBtn', isEnabled, color, text); - pauseButtonHandler = click + setButton("pauseBtn", isEnabled, color, text); + pauseButtonHandler = click; } function doPauseButton() { if (pauseButtonHandler) { - pauseButtonHandler() + pauseButtonHandler(); } } -var green = '#86f686' -var red = '#f64646' -var gray = '#f6f6f6' +var green = "#86f686"; +var red = "#f64646"; +var gray = "#f6f6f6"; function setRunControls() { if (gCodeLoaded) { // A GCode file is ready to go - setPlayButton(true, green, 'Start', runGCode) - setPauseButton(false, gray, 'Pause', null) + setPlayButton(true, green, "Start", runGCode); + setPauseButton(false, gray, "Pause", null); } else { // Can't start because no GCode to run - setPlayButton(false, gray, 'Start', null) - setPauseButton(false, gray, 'Pause', null) + setPlayButton(false, gray, "Start", null); + setPauseButton(false, gray, "Pause", null); } } -var grblReportingUnits = 0 -var startTime = 0 +var grblReportingUnits = 0; +var startTime = 0; -var spindleDirection = '' -var spindleSpeed = '' +var spindleDirection = ""; +var spindleSpeed = ""; function stopAndRecover() { - stopGCode() + stopGCode(); // To stop GRBL you send a reset character, which causes some modes // be reset to their default values. In particular, it sets G21 mode, // which affects the coordinate display and the jog distances. - requestModes() + requestModes(); } -var oldCannotClick = null +var oldCannotClick = null; function scaleUnits(target) { //Scale the units to move when jogging down or up by 25 to keep them reasonable let disMElement = id(target); let currentValue = Number(disMElement.innerText); - if (!isNaN(currentValue)) { - disMElement.innerText = modal.units == 'G20' ? currentValue / 25 : currentValue * 25; + const common = new Common(); + + if (!Number.isNaN(currentValue)) { + disMElement.innerText = + common.modal.units === "G20" ? currentValue / 25.4 : currentValue * 25.4; } else { - console.error('Invalid number in disM element'); + console.error("Invalid number in disM element"); } } - function tabletUpdateModal() { - const newUnits = modal.units === 'G21' ? 'mm' : 'Inch' - if (getText('units') !== newUnits) { - setText('units', newUnits) - setJogSelector(modal.units) - scaleUnits("disM") - scaleUnits("disZ") + const common = new Common(); + const newUnits = common.modal.units === "G21" ? "mm" : "Inch"; + if (getText("tablettab_toggle_units") !== newUnits) { + setText("tablettab_toggle_units", newUnits); + setJogSelector(common.modal.units); + scaleUnits("disM"); + scaleUnits("disZ"); } } function tabletGrblState(grbl, response) { // tabletShowResponse(response) - var stateName = grbl.stateName + const stateName = grbl.stateName; // Unit conversion factor - depends on both $13 setting and parser units - var factor = 1.0 + let factor = 1.0; + + const common = new Common(); // spindleSpeed = grbl.spindleSpeed; // spindleDirection = grbl.spindle; @@ -551,159 +608,148 @@ function tabletGrblState(grbl, response) { // rapidOverride = OVR.rapid/100.0; // spindleOverride = OVR.spindle/100.0; - var mmPerInch = 25.4 - switch (modal.units) { - case 'G20': - factor = grblReportingUnits === 0 ? 1 / mmPerInch : 1.0 - break - case 'G21': - factor = grblReportingUnits === 0 ? 1.0 : mmPerInch - break + const mmPerInch = 25.4; + switch (common.modal.units) { + case "G20": + factor = grblReportingUnits === 0 ? 1 / mmPerInch : 1.0; + break; + case "G21": + factor = grblReportingUnits === 0 ? 1.0 : mmPerInch; + break; } - const cannotClick = stateName === 'Run' || stateName === 'Hold' + const cannotClick = stateName === "Run" || stateName === "Hold"; // Recompute the layout only when the state changes if (oldCannotClick !== cannotClick) { - setDisabled('.dropdown-toggle', cannotClick) - setDisabled('.axis-position .position', cannotClick) - setDisabled('.axis-position .form-control', cannotClick) - setDisabled('.axis-position .btn', cannotClick) - setDisabled('.axis-position .position', cannotClick) + setDisabled(".dropdown-toggle", cannotClick); + setDisabled(".axis-position .position", cannotClick); + setDisabled(".axis-position .form-control", cannotClick); + setDisabled(".axis-position .btn", cannotClick); + setDisabled(".axis-position .position", cannotClick); // if (!cannotClick) { // contractVisualizer(); // } } - oldCannotClick = cannotClick + oldCannotClick = cannotClick; - tabletUpdateModal() + tabletUpdateModal(); switch (stateName) { - case 'Sleep': - case 'Alarm': - setPlayButton(true, gray, 'Start', null) - setPauseButton(false, gray, 'Pause', null) - break - case 'Idle': - setRunControls() - break - case 'Hold': - setPlayButton(true, green, 'Resume', resumeGCode) - setPauseButton(true, red, 'Stop', stopAndRecover) - break - case 'Jog': - case 'Home': - case 'Run': - setPlayButton(false, gray, 'Start', null) - setPauseButton(true, red, 'Pause', pauseGCode) - break - case 'Check': - setPlayButton(true, gray, 'Start', null) - setPauseButton(true, red, 'Stop', stopAndRecover) - break + case "Sleep": + case "Alarm": + setPlayButton(true, gray, "Start", null); + setPauseButton(false, gray, "Pause", null); + break; + case "Idle": + setRunControls(); + break; + case "Hold": + setPlayButton(true, green, "Resume", resumeGCode); + setPauseButton(true, red, "Stop", stopAndRecover); + break; + case "Jog": + case "Home": + case "Run": + setPlayButton(false, gray, "Start", null); + setPauseButton(true, red, "Pause", pauseGCode); + break; + case "Check": + setPlayButton(true, gray, "Start", null); + setPauseButton(true, red, "Stop", stopAndRecover); + break; } if (grbl.spindleDirection) { switch (grbl.spindleDirection) { - case 'M3': - spindleDirection = 'CW' - break - case 'M4': - spindleDirection = 'CCW' - break - case 'M5': - spindleDirection = 'Off' - break + case "M3": + spindleDirection = "CW"; + break; + case "M4": + spindleDirection = "CCW"; + break; + case "M5": + spindleDirection = "Off"; + break; default: - spindleDirection = '' - break + spindleDirection = ""; + break; } } //setText('spindle-direction', spindleDirection); - spindleSpeed = grbl.spindleSpeed ? Number(grbl.spindleSpeed) : '' + spindleSpeed = grbl.spindleSpeed ? Number(grbl.spindleSpeed) : ""; //setText('spindle-speed', spindleSpeed); - var now = new Date() + var now = new Date(); //setText('time-of-day', now.getHours() + ':' + String(now.getMinutes()).padStart(2, '0')); - if (stateName == 'Run') { - var elapsed = now.getTime() - startTime - if (elapsed < 0) elapsed = 0 - var seconds = Math.floor(elapsed / 1000) - var minutes = Math.floor(seconds / 60) - seconds = seconds % 60 - if (seconds < 10) seconds = '0' + seconds - runTime = minutes + ':' + seconds + if (stateName == "Run") { + var elapsed = now.getTime() - startTime; + if (elapsed < 0) elapsed = 0; + var seconds = Math.floor(elapsed / 1000); + var minutes = Math.floor(seconds / 60); + seconds = seconds % 60; + if (seconds < 10) seconds = "0" + seconds; + runTime = minutes + ":" + seconds; } else { - startTime = now.getTime() + startTime = now.getTime(); } //setText('runtime', runTime); - //setText('wpos-label', modal.wcs); - var distanceText = modal.distance == 'G90' ? modal.distance : "
" + modal.distance + '
' + //setText('wpos-label', common.modal.wcs); + const distanceText = + common.modal.distance === "G90" + ? common.modal.distance + : `
${common.modal.distance}
`; //setHTML('distance', distanceText); - var stateText = '' - if (stateName == 'Run') { - var rateNumber = modal.units == 'G21' ? Number(grbl.feedrate).toFixed(0) : Number(grbl.feedrate / 25.4).toFixed(2) + var stateText = ""; + if (stateName === "Run") { + const rateNumber = + common.modal.units === "G21" + ? Number(grbl.feedrate).toFixed(0) + : Number(grbl.feedrate / 25.4).toFixed(2); - var rateText = rateNumber + (modal.units == 'G21' ? ' mm/min' : ' in/min') + const rateText = + rateNumber + (common.modal.units === "G21" ? " mm/min" : " in/min"); - stateText = rateText + ' ' + spindleSpeed + ' ' + spindleDirection + stateText = `${rateText} ${spindleSpeed} ${spindleDirection}`; } else { // var stateText = errorText == 'Error' ? "Error: " + errorMessage : stateName; - stateText = stateName + stateText = stateName; } //setText('active-state', stateText); - var modeText = - modal.distance + - ' ' + - modal.wcs + - ' ' + - modal.units + - ' ' + - 'T' + - modal.tool + - ' ' + - 'F' + - modal.feedrate + - ' ' + - 'S' + - modal.spindle + - ' ' - - if (grbl.lineNumber && (stateName == 'Run' || stateName == 'Hold' || stateName == 'Stop')) { + const modeText = `${common.modal.distance} ${common.modal.wcs} ${common.modal.units} T${common.modal.tool} F${common.modal.feedrate} S${common.modal.spindle}`; + + if ( + grbl.lineNumber && + (stateName === "Run" || stateName === "Hold" || stateName === "Stop") + ) { //setText('line', grbl.lineNumber); if (gCodeDisplayable) { - scrollToLine(grbl.lineNumber) + scrollToLine(grbl.lineNumber); } } if (gCodeDisplayable) { - displayer.reDrawTool(modal, arrayToXYZ(WPOS)) + displayer.reDrawTool(common.modal, arrayToXYZ(WPOS())); } - var digits = modal.units == 'G20' ? 4 : 2 + const digits = common.modal.units === "G20" ? 4 : 2; - if (WPOS) { - WPOS.forEach(function (pos, index) { - setTextContent('mpos-' + axisNames[index], Number(pos * factor).toFixed(index > 2 ? 2 : digits)) - }) + if (WPOS()) { + WPOS().forEach((pos, index) => { + setTextContent( + `mpos-${axisNames[index]}`, + Number(pos * factor).toFixed(index > 2 ? 2 : digits), + ); + }); } - MPOS.forEach(function (pos, index) { + MPOS().forEach(function (pos, index) { //setTextContent('mpos-'+axisNames[index], Number(pos*factor).toFixed(index > 2 ? 2 : digits)); - }) -} - -function addOption(selector, name, value, isDisabled, isSelected) { - var opt = document.createElement('option') - opt.appendChild(document.createTextNode(name)) - opt.disabled = isDisabled - opt.selected = isSelected - opt.value = value - selector.appendChild(opt) + }); } var gCodeFilename = '' @@ -712,11 +758,12 @@ var filename = 'TEST.NC' var watchPath = '' function tabletGetFileList(path) { - gCodeFilename = '' - SendGetHttp('/upload?path=' + encodeURI(path), files_list_success) + const common = new Common(); + common.gCodeFilename = ""; + SendGetHttp(`/upload?path=${encodeURI(path)}`, files_list_success); } -function tabletInit() { +const tabletInit = () => { // put in a timeout to allow things to settle. when they were here at startup ui froze from time to time. setTimeout(() => { showVersionNumber(); @@ -724,174 +771,232 @@ function tabletInit() { // get grbl status SendRealtimeCmd(0x3f); // ? // print startup messages in serial - SendPrinterCommand('$SS'); + SendPrinterCommand("$SS"); // get maslow info - SendPrinterCommand('$MINFO'); - tabletGetFileList('/'); + SendPrinterCommand("$MINFO"); + tabletGetFileList("/"); requestModes(); loadConfigValues(); loadCornerValues(); + numpad.attach({ target: "disM", axis: "D" }); + numpad.attach({ target: "disZ", axis: "Z" }); + //numpad.attach({target: "wpos-y", axis: "Y"}); + //numpad.attach({target: "wpos-z", axis: "Z"}); + //numpad.attach({target: "wpos-a", axis: "A"}); + setJogSelector('mm'); loadJogDists(); id("tablettablink").addEventListener("DOMActivate", () => { fullscreenIfMobile(); setBottomHeight(); - }, false); + }); + + id("filelist").addEventListener("change", (event) => selectFile()); + id("tabelttab_config_popup_content").addEventListener("click", (event) => event.stopPropagation(),); + + id("tablettab_zUp").addEventListener("click", (event) => sendMove("Z+")); + id("tablettab_topLeft").addEventListener("click", (event) => sendMove("X-Y+"),); + id("tablettab_top").addEventListener("click", (event) => sendMove("Y+")); + id("tablettab_topRight").addEventListener("click", (event) => sendMove("X+Y+"),); + + id("calibrationBTN").addEventListener("click", (event) => { + loadCornerValues(); + openModal("calibration-popup"); + }); + + id("tablettab_left").addEventListener("click", (event) => sendMove("X-")); + id("tablettab_right").addEventListener("click", (event) => sendMove("X+")); + + id("tablettab_zDown").addEventListener("click", (event) => sendMove("Z-")); + id("tablettab_bottomLeft").addEventListener("click", (event) => sendMove("X-Y-"),); + id("tablettab_bottom").addEventListener("click", (event) => sendMove("Y-")); + id("tablettab_bottomRight").addEventListener("click", (event) => sendMove("X+Y-"),); + + id("tablettab_set_z_home").addEventListener("mousedown", (event) => zeroAxis("Z"),); + id("tablettab_set_z_home").addEventListener("mouseup", (event) => refreshGcode(),); + id("tablettab_move_to_xy_home").addEventListener("click", (event) => moveHome(),); + id("tablettab_toggle_units").addEventListener("click", (event) => toggleUnits(),); + id("tablettab_set_xy_home").addEventListener("mousedown", (event) => setHomeClickDown(),); + id("tablettab_set_xy_home").addEventListener("mouseup", (event) => setHomeClickUp(),); + id("tablettab_set_xy_home").addEventListener("dblclick", (event) => setXYHome(),); + + id("tablettab_gcode_upload").addEventListener("click", (event) => files_select_upload(),); + id("tablettab_gcode_play").addEventListener("click", (event) => doPlayButton(),); + id("tablettab_gcode_pause").addEventListener("click", (event) => doPauseButton(),); + id("tablettab_gcode_stop").addEventListener("click", (event) => onCalibrationButtonsClick("$STOP", "Stop Maslow and Gcode"),); + + id("tablettab_cal_retract").addEventListener("click", (event) => onCalibrationButtonsClick("$ALL", "Retract All"),); + id("tablettab_cal_extend").addEventListener("click", (event) => onCalibrationButtonsClick("$EXT", "Extend All"),); + id("tablettab_cal_calibrate").addEventListener("click", (event) => { + onCalibrationButtonsClick("$CAL", "Calibrate"); + setTimeout(() => { + hideModal("calibration-popup"); + }, 1000); + }); + id("tablettab_cal_tense").addEventListener("click", (event) => { + onCalibrationButtonsClick("$TKSLK", "Apply Tension"); + setTimeout(() => { + hideModal("calibration-popup"); + }, 1000); + }); + // id('tablettab_cal_homez').addEventListener('click', (event) => onCalibrationButtonsClick('$TKSLK','Home Z')); + id("tablettab_cal_config").addEventListener("click", (event) => { + loadConfigValues(); + openModal("configuration-popup"); + }); + id("tablettab_cal_stop").addEventListener("click", (event) => onCalibrationButtonsClick("$STOP", "Stop"),); + id("tablettab_cal_zstop").addEventListener("click", (event) => onCalibrationButtonsClick("$SETZSTOP", "Set Z-Stop"),); + id("tablettab_cal_test").addEventListener("click", (event) => onCalibrationButtonsClick("$TEST", "Test"),); + id("tablettab_cal_relax").addEventListener("click", (event) => onCalibrationButtonsClick("$CMP", "Release Tension"),); + id("tablettab_config_save").addEventListener("click", (event) => saveConfigValues(),); + + id("tablettab_save_serial_msg").addEventListener("click", (event) => saveSerialMessages(),); + + id("calibration-popup").addEventListener("click", (event) => hideModal("calibration-popup"),); + id("calibration_popup_content").addEventListener("click", (event) => event.stopPropagation(),); + id("configuration-popup").addEventListener("click", (event) => hideModal("configuration-popup"),); + + id("systemStatus").addEventListener("click", (event) => clearAlarm()); }, 1000); -} - -function arrayToXYZ(a) { - return { - x: a[0], - y: a[1], - z: a[2], - } -} +}; function showGCode(gcode) { - gCodeLoaded = gcode != '' + gCodeLoaded = gcode !== ""; if (!gCodeLoaded) { - id('gcode').value = '(No GCode loaded)' - displayer.clear() + setValue("tablettab_gcode", "(No GCode loaded)"); + displayer.clear(); } else { - id('gcode').value = gcode - var initialPosition = { - x: WPOS[0], - y: WPOS[1], - z: WPOS[2], - } - + setValue("tablettab_gcode", gcode); + const common = new Common(); if (gCodeDisplayable) { - displayer.showToolpath(gcode, modal, arrayToXYZ(WPOS)) + displayer.showToolpath(gcode, common.modal, arrayToXYZ(WPOS())); } } // XXX this needs to take into account error states - setRunControls() + setRunControls(); } function nthLineEnd(str, n) { - if (n <= 0) return 0 - var L = str.length, - i = -1 + if (n <= 0) return 0; + const L = str.length; + let i = -1; while (n-- && i++ < L) { - i = str.indexOf('\n', i) - if (i < 0) break + i = str.indexOf("\n", i); + if (i < 0) break; } - return i + return i; } function scrollToLine(lineNumber) { - var gCodeLines = id('gcode') - var lineHeight = parseFloat(getComputedStyle(gCodeLines).getPropertyValue('line-height')) - var gCodeText = gCodeLines.value + const gCodeLines = id("tablettab_gcode"); + const lineHeight = Number.parseFloat(getComputedStyle(gCodeLines).getPropertyValue("line-height")); + const gCodeText = gCodeLines.value; - gCodeLines.scrollTop = lineNumber * lineHeight + gCodeLines.scrollTop = lineNumber * lineHeight; - var start - var end + let start; + let end; if (lineNumber <= 0) { - start = 0 - end = 1 + start = 0; + end = 1; } else { - start = lineNumber == 1 ? 0 : (start = nthLineEnd(gCodeText, lineNumber) + 1) - end = gCodeText.indexOf('\n', start) + start = lineNumber === 1 ? 0 : nthLineEnd(gCodeText, lineNumber) + 1; + end = gCodeText.indexOf("\n", start); } - gCodeLines.select() - gCodeLines.setSelectionRange(start, end) + gCodeLines.select(); + gCodeLines.setSelectionRange(start, end); } function runGCode() { - gCodeFilename && sendCommand(`$sd/run=${gCodeFilename}`) + const common = new Common(); + common.gCodeFilename && sendCommand(`$sd/run=${common.gCodeFilename}`); setTimeout(() => { - SendRealtimeCmd(0x7e) - }, 1500) + SendRealtimeCmd(0x7e); + }, 1500); // expandVisualizer() } -function tabletSelectGCodeFile(filename) { - var selector = id('filelist') - var options = Array.from(selector.options) - var option = options.find((item) => item.text == filename) - option.selected = true -} function tabletLoadGCodeFile(path, size) { - gCodeFilename = path - if ((isNaN(size) && size.endsWith('GB')) || size > 10000000) { - showGCode('GCode file too large to display (> 1MB)') - gCodeDisplayable = false - displayer.clear() + const common = new Common(); + common.gCodeFilename = path; + if ((Number.isNaN(size) && size.endsWith("GB")) || size > 10000000) { + showGCode("GCode file too large to display (> 1MB)"); + gCodeDisplayable = false; + displayer.clear(); } else { - gCodeDisplayable = true - fetch(encodeURIComponent('SD' + gCodeFilename)) + gCodeDisplayable = true; + fetch(encodeURIComponent(`SD${common.gCodeFilename}`)) .then((response) => response.text()) - .then((gcode) => showGCode(gcode)) + .then((gcode) => showGCode(gcode)); } } function selectFile() { - tabletClick() - var filelist = id('filelist') - var index = Number(filelist.options[filelist.selectedIndex].value) + tabletClick(); + const filelist = id("filelist"); + const index = Number(filelist.options[filelist.selectedIndex].value); if (index === -3) { // No files - return + return; } if (index === -2) { // Blank entry selected - return + return; } if (index === -1) { // Go up - gCodeFilename = '' - files_go_levelup() - return + const common = new Common(); + common.gCodeFilename = ""; + files_go_levelup(); + return; } - var file = files_file_list[index] - var filename = file.name + const file = files_file_list[index]; + const filename = file.name; if (file.isdir) { - gCodeFilename = '' - files_enter_dir(filename) + const common = new Common(); + common.gCodeFilename = ""; + files_enter_dir(filename); } else { - tabletLoadGCodeFile(files_currentPath + filename, file.size) + tabletLoadGCodeFile(files_currentPath + filename, file.size); } } function toggleDropdown() { - id('tablet-dropdown-menu').classList.toggle('show') + id("tablet-dropdown-menu").classList.toggle("show"); } function hideMenu() { - toggleDropdown() + toggleDropdown(); } function menuFullscreen() { - toggleFullscreen() - hideMenu() + toggleFullscreen(); + hideMenu(); } function menuReset() { - stopAndRecover() - hideMenu() + stopAndRecover(); + hideMenu(); } function menuUnlock() { - sendCommand('$X') - hideMenu() + sendCommand("$X"); + hideMenu(); } function menuHomeAll() { - sendCommand('$H') - hideMenu() + sendCommand("$H"); + hideMenu(); } function menuHomeA() { - sendCommand('$HA') - hideMenu() + sendCommand("$HA"); + hideMenu(); } function menuSpindleOff() { - sendCommand('M5') - hideMenu() + sendCommand("M5"); + hideMenu(); } function requestModes() { - sendCommand('$G') + sendCommand("$G"); } const cycleDistance = (up) => { @@ -901,145 +1006,145 @@ const cycleDistance = (up) => { // tabletClick(); // sel.selectedIndex = newIndex; //} -} +}; const clickon = (name) => { // $('[data-route="workspace"] .btn').removeClass('active'); - var button = id(name) - button.classList.add('active') - button.dispatchEvent(new Event('click')) -} -var ctrlDown = false -var oldIndex = null -var newChild = null + const button = id(name); + button.classList.add("active"); + button.dispatchEvent(new Event("click")); +}; +let ctrlDown = false; +let oldIndex = null; +let newChild = null; function shiftUp() { if (!newChild) { - return + return; } - removeJogDistance(newChild, oldIndex) - newChild = null + removeJogDistance(newChild, oldIndex); + newChild = null; } function altUp() { if (!newChild) { - return + return; } - removeJogDistance(newChild, oldIndex) - newChild = null + removeJogDistance(newChild, oldIndex); + newChild = null; } function shiftDown() { if (newChild) { - return + return; } - var sel = id('jog-distance') - var distance = sel.value - oldIndex = sel.selectedIndex - newChild = addJogDistance(distance * 10) + const sel = id("jog-distance"); + const distance = sel.value; + oldIndex = sel.selectedIndex; + newChild = addJogDistance(distance * 10); } function altDown() { if (newChild) { - return + return; } - var sel = id('jog-distance') - var distance = sel.value - oldIndex = sel.selectedIndex - newChild = addJogDistance(distance / 10) + const sel = id("jog-distance"); + const distance = sel.value; + oldIndex = sel.selectedIndex; + newChild = addJogDistance(distance / 10); } function jogClick(name) { - clickon(name) + clickon(name); } // Reports whether a text input box has focus - see the next comment -var isInputFocused = false +var isInputFocused = false; function tabletIsActive() { - return id('tablettab').style.display !== 'none' + return id("tablettab").style.display !== "none"; } function handleKeyDown(event) { // When we are in a modal input field like the MDI text boxes // or the numeric entry boxes, disable keyboard jogging so those // keys can be used for text editing. if (!tabletIsActive()) { - return + return; } if (isInputFocused) { - return + return; } switch (event.key) { - case 'ArrowRight': - jogClick('jog-x-plus') - event.preventDefault() - break - case 'ArrowLeft': - jogClick('jog-x-minus') - event.preventDefault() - break - case 'ArrowUp': - jogClick('jog-y-plus') - event.preventDefault() - break - case 'ArrowDown': - jogClick('jog-y-minus') - event.preventDefault() - break - case 'PageUp': - jogClick('jog-z-plus') - event.preventDefault() - break - case 'PageDown': - jogClick('jog-z-minus') - event.preventDefault() - break - case 'Escape': - case 'Pause': - clickon('pauseBtn') - break - case 'Shift': - shiftDown() - break - case 'Control': - ctrlDown = true - break - case 'Alt': - altDown() - break - case '=': // = is unshifted + on US keyboards - case '+': - cycleDistance(true) - event.preventDefault() - break - case '-': - cycleDistance(false) - event.preventDefault() - break + case "ArrowRight": + jogClick("jog-x-plus"); + event.preventDefault(); + break; + case "ArrowLeft": + jogClick("jog-x-minus"); + event.preventDefault(); + break; + case "ArrowUp": + jogClick("jog-y-plus"); + event.preventDefault(); + break; + case "ArrowDown": + jogClick("jog-y-minus"); + event.preventDefault(); + break; + case "PageUp": + jogClick("jog-z-plus"); + event.preventDefault(); + break; + case "PageDown": + jogClick("jog-z-minus"); + event.preventDefault(); + break; + case "Escape": + case "Pause": + clickon("pauseBtn"); + break; + case "Shift": + shiftDown(); + break; + case "Control": + ctrlDown = true; + break; + case "Alt": + altDown(); + break; + case "=": // = is unshifted + on US keyboards + case "+": + cycleDistance(true); + event.preventDefault(); + break; + case "-": + cycleDistance(false); + event.preventDefault(); + break; default: - console.log(event) + console.log(event); } } function handleKeyUp(event) { if (!tabletIsActive()) { - return + return; } if (isInputFocused) { - return + return; } switch (event.key) { - case 'Shift': - shiftUp() - break - case 'Control': - ctrlDown = false - break - case 'Alt': - altUp() - break + case "Shift": + shiftUp(); + break; + case "Control": + ctrlDown = false; + break; + case "Alt": + altUp(); + break; } } function mdiEnterKey(event) { - if (event.key === 'Enter') { - MDIcmd(event.target.value) - event.target.blur() + if (event.key === "Enter") { + MDIcmd(event.target.value); + event.target.blur(); } } @@ -1048,35 +1153,40 @@ function mdiEnterKey(event) { // to lose focus, in which case it does not receive keys. The solution is to // delegate the event to window and then have the handler check to see if the // tablet is active. -window.addEventListener('keydown', handleKeyDown) -window.addEventListener('keyup', handleKeyUp) - -numpad.attach({ target: 'disM', axis: 'D' }) -numpad.attach({ target: 'disZ', axis: 'Z' }) -//numpad.attach({target: "wpos-y", axis: "Y"}); -//numpad.attach({target: "wpos-z", axis: "Z"}); -//numpad.attach({target: "wpos-a", axis: "A"}); +window.addEventListener("keydown", handleKeyDown); +window.addEventListener("keyup", handleKeyUp); function saveJogDists() { - localStorage.setItem("disM", id('disM').innerText); - localStorage.setItem("disZ", id('disZ').innerText); + localStorage.setItem("disM", id("disM").innerText); + localStorage.setItem("disZ", id("disZ").innerText); } function loadJogDists() { const disM = localStorage.getItem("disM"); if (disM != null) { - id('disM').innerText = disM; + id("disM").innerText = disM; } const disZ = localStorage.getItem("disZ"); if (disZ != null) { - id('disZ').innerText = disZ; + id("disZ").innerText = disZ; } } function fullscreenIfMobile() { if (/Mobi|Android/i.test(navigator.userAgent)) { - toggleFullscreen() + toggleFullscreen(); + } +} +function setBottomHeight() { + if (!tabletIsActive()) { + return; } + const residue = bodyHeight() - heightId("navbar") - controlHeight(); + const tStyle = getComputedStyle(id("tablettab")); + let tPad = + Number.parseFloat(tStyle.paddingTop) + + Number.parseFloat(tStyle.paddingBottom); + tPad += 20; } // setMessageHeight(), with these helper functions, adjusts the size of the message @@ -1085,109 +1195,98 @@ function fullscreenIfMobile() { // a) required setting a fixed message window height, or // b) the message window would extend past the screen bottom when messages were added function height(element) { - return element?.getBoundingClientRect()?.height + return element?.getBoundingClientRect()?.height; } function heightId(eid) { - return height(id(eid)) + return height(id(eid)); } function bodyHeight() { - return height(document.body) + return height(document.body); } function controlHeight() { - return heightId('nav-panel') + heightId('axis-position') + heightId('setAxis') -} -function setBottomHeight() { - if (!tabletIsActive()) { - return - } - var residue = bodyHeight() - heightId('navbar') - controlHeight() - const tStyle = getComputedStyle(id('tablettab')) - let tPad = Number.parseFloat(tStyle.paddingTop) + Number.parseFloat(tStyle.paddingBottom) - tPad += 20 + return ( + heightId("nav-panel") + heightId("axis-position") + heightId("setAxis") + ); } -window.onresize = setBottomHeight -function updateGcodeViewerAngle() { - const gcode = id('gcode').value - displayer.cycleCameraAngle(gcode, modal, arrayToXYZ(WPOS)) -} +window.onresize = setBottomHeight; -function fullscreenIfMobile() { - if (/Mobi|Android/i.test(navigator.userAgent)) { - toggleFullscreen() - } -} - -function showCalibrationPopup() { - document.getElementById('calibration-popup').style.display = 'block' -} +// function showCalibrationPopup() { +// document.getElementById("calibration-popup").style.display = "block"; +// } -function homeZ() { - console.log('Homing Z latest') +// function homeZ() { +// console.log("Homing Z latest"); - const move = (params) => { - params = params || {} - let s = '' - for (key in params) { - s += key + params[key] - } - moveTo(s) - } - - move({ Z: 85 }) - sendCommand('G91 G0 Z-28') - //This is a total hack to make set the z to zero after the moves complete and should be done better - setTimeout(() => { - sendCommand('$HZ') - }, 25000) - setTimeout(() => { - zeroAxis('Z') - }, 26000) -} +// const move = (params) => { +// params = params || {}; +// let s = ""; +// for (key in params) { +// s += key + params[key]; +// } +// moveTo(s); +// }; + +// move({ Z: 85 }); +// sendCommand("G91 G0 Z-28"); +// //This is a total hack to make set the z to zero after the moves complete and should be done better +// setTimeout(() => { sendCommand("$HZ"); }, 25000); +// setTimeout(() => { zeroAxis("Z"); }, 26000); +// } -document.addEventListener('click', (event) => { - if ( - !document.getElementById('calibration-popup').contains(event.target) && - !document.getElementById('calibrationBTN').contains(event.target) && - !document.getElementById('numPad').contains(event.target) - ) { - document.getElementById('calibration-popup').style.display = 'none' +document.addEventListener("click", (event) => { + const elemIdsToTest = ["calibration-popup", "calibrationBTN", "numPad"]; + const turnOffCalPopup = elemIdsToTest.every((elemId) => { + const elem = document.getElementById(elemId); + return !elem || !elem.contains(event.target); + }); + if (turnOffCalPopup) { + document.getElementById("calibration-popup").style.display = "none"; } -}) +}); /* Calibration modal */ -function openModal(modalId) { - const modal = document.getElementById(modalId) +const openModal = (modalId) => { + const modal = document.getElementById(modalId); if (modal) { - modal.style.display = 'flex' + modal.style.display = "flex"; } -} +}; -function hideModal(modalId) { - const modal = document.getElementById(modalId) +const hideModal = (modalId) => { + const modal = document.getElementById(modalId); if (modal) { - modal.style.display = 'none' + modal.style.display = "none"; } -} +}; const onCalibrationButtonsClick = async (command, msg) => { - sendCommand(command) + sendCommand(command); //Prints out the index.html version number when test is pressed - if (command === '$TEST') { - const msgWindow = document.getElementById('messages') - let text = msgWindow.textContent - text = `${text}\nIndex.html Version: ${versionNumber}` - msgWindow.textContent = text - msgWindow.scrollTop = msgWindow.scrollHeight - } - - if (command !== '$MINFO') { - setTimeout(() => { sendCommand('$MINFO'); }, 1000) - } -} - + if (command === "$TEST") { + const msgWindow = document.getElementById("messages"); + let text = msgWindow.textContent; + text = `${text}\nIndex.html Version: ${versionNumber}`; + msgWindow.textContent = text; + msgWindow.scrollTop = msgWindow.scrollHeight; + } + + if (command !== "$MINFO") { + setTimeout(() => { sendCommand("$MINFO"); }, 1000); + } +}; + +export { + loadedValues, + openModal, + hideModal, + goAxisByValue, + onCalibrationButtonsClick, + saveSerialMessages, + tabletInit, +}; /* Calibration modal END */ diff --git a/www/js/tabs.js b/www/js/tabs.js index 4196f35cf..9268ddc0e 100644 --- a/www/js/tabs.js +++ b/www/js/tabs.js @@ -1,22 +1,27 @@ -function opentab(evt, tabname, tabcontentid, tablinkid) { - var i, tabcontent, tablinks; - tabcontent = classes("tabcontent"); - const activateEvent = new Event("activate"); - const deactivateEvent = new Event("deactivate"); +import { classes, displayBlock, id } from "./common.js"; - for (i = 0; i < tabcontent.length; i++) { - if (tabcontent[i].parentNode.id == tabcontentid) { - tabcontent[i].dispatchEvent(deactivateEvent); - tabcontent[i].style.display = "none"; - } - } - tablinks = classes("tablinks"); - for (i = 0; i < tablinks.length; i++) { - if (tablinks[i].parentNode.id == tablinkid) { - tablinks[i].className = tablinks[i].className.replace("active", ""); - } - } - id(tabname).dispatchEvent(activateEvent); - displayBlock(tabname); - evt.currentTarget.className += " active"; -} +const opentab = (evt, tabname, tabcontentid, tablinkid) => { + let i; + let tablinks; + const tabcontent = classes("tabcontent"); + const activateEvent = new Event("activate"); + const deactivateEvent = new Event("deactivate"); + + for (i = 0; i < tabcontent.length; i++) { + if (tabcontent[i].parentNode.id === tabcontentid) { + tabcontent[i].dispatchEvent(deactivateEvent); + tabcontent[i].style.display = "none"; + } + } + tablinks = classes("tablinks"); + for (i = 0; i < tablinks.length; i++) { + if (tablinks[i].parentNode.id === tablinkid) { + tablinks[i].className = tablinks[i].className.replace("active", ""); + } + } + id(tabname).dispatchEvent(activateEvent); + displayBlock(tabname); + evt.currentTarget.className += " active"; +}; + +export { opentab }; diff --git a/www/js/tests/SPIFFSdlg.test.ts b/www/js/tests/SPIFFSdlg.test.ts new file mode 100644 index 000000000..cb934cd6b --- /dev/null +++ b/www/js/tests/SPIFFSdlg.test.ts @@ -0,0 +1,154 @@ +import { beforeEach, describe, expect, test } from "bun:test"; +import { + maslowMsgHandling, + loadedValues, +} from "../common.js"; + +describe("SPIFFSdlg", () => { + beforeEach(() => { + // Set up the DOM elements and initial values + document.body.innerHTML = ` + + + + + + + + `; + global.initialGuess = { + tr: { x: 0, y: 0, z: 0 }, + tl: { x: 0, y: 0, z: 0 }, + br: { x: 0, y: 0, z: 0 }, + bl: { x: 0, y: 0, z: 0 }, + }; + global.acceptableCalibrationThreshold = 0.5; + }); + + const testResponse = [ + '{"files":[', + '{"name":"config.html.gz","size":"4.76 KB"},', + '{"name":"index.html.gz","size":"21.44 KB"},', + '{"name":"favicon.ico","size":"1.12 KB"},', + '{"name":"config.htm","size":"19.65 KB"},', + '{"name":"config2.htm","size":"19.98 KB"},', + '{"name":"Testname","size":"-1"},', + '{"name":"index2.html.gz","size":"28.89 KB"}', + '],"path":"/","status":"Ok","total":"2.81 MB","used":"118.88 KB","occupation":"4"}', + ]; + + function SPIFFSSendCommand(action, filename) { + //removeIf(production) + SPIFFSsuccess(testResponse.join("")); + return; + //endRemoveIf(production) + const common = new Common(); + let url = `/files?action=${action}&filename=${encodeURI(filename)}&path=${encodeURI(common.SPIFFS_currentpath)}`; + id("SPIFFS_loader").style.visibility = "visible"; + console.log(url); + SendGetHttp(url, SPIFFSsuccess, SPIFFSfailed); + } + + function refreshSPIFFS() { + setValue("SPIFFS-select", ""); + setHTML("uploadSPIFFSmsg", ""); + setHTML("SPIFFS_file_name", translate_text_item("No file chosen")); + displayNone("SPIFFS_uploadbtn"); + displayNone("refreshSPIFFSbtn"); + displayNone("SPIFFS_select_files"); + //removeIf(production) + SPIFFSsuccess(testResponse.join("")); + return; + //endRemoveIf(production) + SPIFFSSendCommand("list", "all"); + } + + test.each(inputValidation)("Input %p results in %p", (inp, expected) => { + const result = maslowMsgHandling(inp); + expect(result).toBe(expected); + }); + + const stdActions = [ + ["calibration_grid_size", "10", "gridSize"], + ["calibration_grid_width_mm_X", "2000", "gridWidth"], + ["calibration_grid_height_mm_Y", "1000", "gridHeight"], + ["Retract_Current_Threshold", "1500", "retractionForce"], + ]; + + const noErrorResult = (key, value) => { + const result = maslowMsgHandling(`${key}=${value}`); + expect(result).toBe(""); + }; + + test.each(stdActions)( + "Key %p sets value %p into %p", + (key, value, outputValueName) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const orientationActions = [ + ["vertical", "false", "machineOrientation", "horizontal"], + ["vertical", "true", "machineOrientation", "vertical"], + ["vertical", "something else", "machineOrientation", "vertical"], + ]; + + test.each(orientationActions)( + "Key %p with value %p sets %p to %p", + (key, value, outputValueName, outputValue) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(outputValue); + }, + ); + + const setDim = (key, value, outDim, outValue) => { + noErrorResult(key, value); + if (typeof outValue === "undefined") { + outValue = Number.parseFloat(value); + } + if (Array.isArray(outDim)) { + expect(global.initialGuess[outDim[0]][outDim[1]]).toBe(outValue); + } else { + expect(global[outDim]).toBe(outValue); + } + }; + + const fullDimensionActions = [ + ["trX", "3000", "machineWidth", ["tr", "x"]], + ["trY", "2000", "machineHeight", ["tr", "y"]], + ]; + + test.each(fullDimensionActions)( + "Key %p with value %p sets %p and %p.%p as well", + (key, value, outputValueName, outDim) => { + setDim(key, value, outDim, undefined); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const stdDimensionActions = [ + ["trZ", "50", ["tr", "z"]], + ["tlX", "20", ["tl", "x"]], + ["tlY", "10", ["tl", "y"]], + ["tlZ", "40", ["tl", "z"]], + ["brX", "20", ["br", "x"]], + ["brY", "15", ["br", "y"], 0], + ["brZ", "40", ["br", "z"]], + ["blX", "16", ["bl", "x"], 0], + ["blY", "17", ["bl", "y"], 0], + ["blZ", "70", ["bl", "z"]], + [ + "Acceptable_Calibration_Threshold", + "1500", + "acceptableCalibrationThreshold", + ], + ]; + + test.each(stdDimensionActions)( + "Key %p with value %p sets %p", + (key, value, outDim, outValue = undefined) => { + setDim(key, value, outDim, outValue); + }, + ); +}); diff --git a/www/js/tests/maslow.test.ts b/www/js/tests/maslow.test.ts index 7813a0332..ab42b67c3 100644 --- a/www/js/tests/maslow.test.ts +++ b/www/js/tests/maslow.test.ts @@ -1,17 +1,18 @@ import { beforeEach, describe, expect, test } from "bun:test"; import { - MaslowErrMsgNoKey, - MaslowErrMsgNoValue, - MaslowErrMsgNoMatchingKey, - MaslowErrMsgKeyValueCantUse, - MaslowErrMsgKeyValueSuffix, - maslowMsgHandling -} from "../maslow"; + MaslowErrMsgNoKey, + MaslowErrMsgNoValue, + MaslowErrMsgNoMatchingKey, + MaslowErrMsgKeyValueCantUse, + MaslowErrMsgKeyValueSuffix, + maslowMsgHandling, + loadedValues, +} from "../common.js"; -describe('maslowMsgHandling', () => { - beforeEach(() => { - // Set up the DOM elements and initial values - document.body.innerHTML = ` +describe("maslowMsgHandling", () => { + beforeEach(() => { + // Set up the DOM elements and initial values + document.body.innerHTML = ` @@ -20,93 +21,114 @@ describe('maslowMsgHandling', () => { `; - global.loadedValues = {}; - global.initialGuess = { - tr: { x: 0, y: 0, z: 0 }, - tl: { x: 0, y: 0, z: 0 }, - br: { x: 0, y: 0, z: 0 }, - bl: { x: 0, y: 0, z: 0 } - }; - global.acceptableCalibrationThreshold = 0.5; - }); + global.initialGuess = { + tr: { x: 0, y: 0, z: 0 }, + tl: { x: 0, y: 0, z: 0 }, + br: { x: 0, y: 0, z: 0 }, + bl: { x: 0, y: 0, z: 0 }, + }; + global.acceptableCalibrationThreshold = 0.5; + }); - const inputValidation = [ - ["invalidMessage", `${MaslowErrMsgKeyValueCantUse} ${MaslowErrMsgKeyValueSuffix}invalidMessage`], - ["=value", `${MaslowErrMsgNoKey} ${MaslowErrMsgKeyValueSuffix}=value`], - ["key=", `${MaslowErrMsgNoValue} ${MaslowErrMsgKeyValueSuffix}key=`], - ["unknownKey=value", `${MaslowErrMsgNoMatchingKey} ${MaslowErrMsgKeyValueSuffix}unknownKey=value`], - ]; + const inputValidation = [ + [ + "invalidMessage", + `${MaslowErrMsgKeyValueCantUse} ${MaslowErrMsgKeyValueSuffix}invalidMessage`, + ], + ["=value", `${MaslowErrMsgNoKey} ${MaslowErrMsgKeyValueSuffix}=value`], + ["key=", `${MaslowErrMsgNoValue} ${MaslowErrMsgKeyValueSuffix}key=`], + [ + "unknownKey=value", + `${MaslowErrMsgNoMatchingKey} ${MaslowErrMsgKeyValueSuffix}unknownKey=value`, + ], + ]; - test.each(inputValidation)("Input %p results in %p", (inp, expected) => { - const result = maslowMsgHandling(inp); - expect(result).toBe(expected); - }); + test.each(inputValidation)("Input %p results in %p", (inp, expected) => { + const result = maslowMsgHandling(inp); + expect(result).toBe(expected); + }); - const stdActions = [ - ["calibration_grid_size", "10", "gridSize"], - ["calibration_grid_width_mm_X", "2000", "gridWidth"], - ["calibration_grid_height_mm_Y", "1000", "gridHeight"], - ["Retract_Current_Threshold", "1500", "retractionForce"], - ]; + const stdActions = [ + ["calibration_grid_size", "10", "gridSize"], + ["calibration_grid_width_mm_X", "2000", "gridWidth"], + ["calibration_grid_height_mm_Y", "1000", "gridHeight"], + ["Retract_Current_Threshold", "1500", "retractionForce"], + ]; - const noErrorResult = (key, value) => { - const result = maslowMsgHandling(`${key}=${value}`); - expect(result).toBe(""); - } + const noErrorResult = (key, value) => { + const result = maslowMsgHandling(`${key}=${value}`); + expect(result).toBe(""); + }; - test.each(stdActions)("Key %p sets value %p into %p", (key, value, outputValueName) => { - noErrorResult(key, value); - expect(global.loadedValues[outputValueName]).toBe(value); - }); + test.each(stdActions)( + "Key %p sets value %p into %p", + (key, value, outputValueName) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); - const orientationActions = [ - ["vertical", "false", "machineOrientation", "horizontal"], - ["vertical", "true", "machineOrientation", "vertical"], - ["vertical", "something else", "machineOrientation", "vertical"], - ]; + const orientationActions = [ + ["vertical", "false", "machineOrientation", "horizontal"], + ["vertical", "true", "machineOrientation", "vertical"], + ["vertical", "something else", "machineOrientation", "vertical"], + ]; - test.each(orientationActions)("Key %p with value %p sets %p to %p", (key, value, outputValueName, outputValue) => { - noErrorResult(key, value); - expect(global.loadedValues[outputValueName]).toBe(outputValue); - }); + test.each(orientationActions)( + "Key %p with value %p sets %p to %p", + (key, value, outputValueName, outputValue) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(outputValue); + }, + ); - const setDim = (key, value, outDim, outValue) => { - noErrorResult(key, value); - if (typeof outValue === "undefined") { - outValue = parseFloat(value); - } - if (Array.isArray(outDim)) { - expect(global.initialGuess[outDim[0]][outDim[1]]).toBe(outValue); - } else { - expect(global[outDim]).toBe(outValue); - } - }; + const setDim = (key, value, outDim, outValue) => { + noErrorResult(key, value); + if (typeof outValue === "undefined") { + outValue = Number.parseFloat(value); + } + if (Array.isArray(outDim)) { + expect(global.initialGuess[outDim[0]][outDim[1]]).toBe(outValue); + } else { + expect(global[outDim]).toBe(outValue); + } + }; - const fullDimensionActions = [ - ["trX", "3000", "machineWidth", ["tr", "x"]], - ["trY", "2000", "machineHeight", ["tr", "y"]], - ]; + const fullDimensionActions = [ + ["trX", "3000", "machineWidth", ["tr", "x"]], + ["trY", "2000", "machineHeight", ["tr", "y"]], + ]; - test.each(fullDimensionActions)("Key %p with value %p sets %p and %p.%p as well", (key, value, outputValueName, outDim) => { - setDim(key, value, outDim, undefined); - expect(global.loadedValues[outputValueName]).toBe(value); - }); + test.each(fullDimensionActions)( + "Key %p with value %p sets %p and %p.%p as well", + (key, value, outputValueName, outDim) => { + setDim(key, value, outDim, undefined); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); - const stdDimensionActions = [ - ["trZ", "50", ["tr", "z"]], - ["tlX", "20", ["tl", "x"]], - ["tlY", "10", ["tl", "y"]], - ["tlZ", "40", ["tl", "z"]], - ["brX", "20", ["br", "x"]], - ["brY", "15", ["br", "y"], 0], - ["brZ", "40", ["br", "z"]], - ["blX", "16", ["bl", "x"], 0], - ["blY", "17", ["bl", "y"], 0], - ["blZ", "70", ["bl", "z"]], - ["Acceptable_Calibration_Threshold", "1500", "acceptableCalibrationThreshold"], - ]; + const stdDimensionActions = [ + ["trZ", "50", ["tr", "z"]], + ["tlX", "20", ["tl", "x"]], + ["tlY", "10", ["tl", "y"]], + ["tlZ", "40", ["tl", "z"]], + ["brX", "20", ["br", "x"]], + ["brY", "15", ["br", "y"], 0], + ["brZ", "40", ["br", "z"]], + ["blX", "16", ["bl", "x"], 0], + ["blY", "17", ["bl", "y"], 0], + ["blZ", "70", ["bl", "z"]], + [ + "Acceptable_Calibration_Threshold", + "1500", + "acceptableCalibrationThreshold", + ], + ]; - test.each(stdDimensionActions)("Key %p with value %p sets %p", (key, value, outDim, outValue = undefined) => { - setDim(key, value, outDim, outValue); - }); + test.each(stdDimensionActions)( + "Key %p with value %p sets %p", + (key, value, outDim, outValue = undefined) => { + setDim(key, value, outDim, outValue); + }, + ); }); diff --git a/www/js/tests/preferencesdlg.test.ts b/www/js/tests/preferencesdlg.test.ts new file mode 100644 index 000000000..6ee16652e --- /dev/null +++ b/www/js/tests/preferencesdlg.test.ts @@ -0,0 +1,126 @@ +import { beforeEach, describe, expect, test } from "bun:test"; +import { + maslowMsgHandling, + loadedValues, +} from "../common.js"; + +describe("preferncesdlg", () => { + beforeEach(() => { + // Set up the DOM elements and initial values + document.body.innerHTML = ` + + + + + + + + `; + global.initialGuess = { + tr: { x: 0, y: 0, z: 0 }, + tl: { x: 0, y: 0, z: 0 }, + br: { x: 0, y: 0, z: 0 }, + bl: { x: 0, y: 0, z: 0 }, + }; + global.acceptableCalibrationThreshold = 0.5; + }); + + function getpreferenceslist() { + var url = prefFile; + //removeIf(production) + var response = defaultpreferenceslist; + processPreferencesGetSuccess(response); + return; + //endRemoveIf(production) + SendGetHttp(url, processPreferencesGetSuccess, processPreferencesGetFailed); + } + + test.each(inputValidation)("Input %p results in %p", (inp, expected) => { + const result = maslowMsgHandling(inp); + expect(result).toBe(expected); + }); + + const stdActions = [ + ["calibration_grid_size", "10", "gridSize"], + ["calibration_grid_width_mm_X", "2000", "gridWidth"], + ["calibration_grid_height_mm_Y", "1000", "gridHeight"], + ["Retract_Current_Threshold", "1500", "retractionForce"], + ]; + + const noErrorResult = (key, value) => { + const result = maslowMsgHandling(`${key}=${value}`); + expect(result).toBe(""); + }; + + test.each(stdActions)( + "Key %p sets value %p into %p", + (key, value, outputValueName) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const orientationActions = [ + ["vertical", "false", "machineOrientation", "horizontal"], + ["vertical", "true", "machineOrientation", "vertical"], + ["vertical", "something else", "machineOrientation", "vertical"], + ]; + + test.each(orientationActions)( + "Key %p with value %p sets %p to %p", + (key, value, outputValueName, outputValue) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(outputValue); + }, + ); + + const setDim = (key, value, outDim, outValue) => { + noErrorResult(key, value); + if (typeof outValue === "undefined") { + outValue = Number.parseFloat(value); + } + if (Array.isArray(outDim)) { + expect(global.initialGuess[outDim[0]][outDim[1]]).toBe(outValue); + } else { + expect(global[outDim]).toBe(outValue); + } + }; + + const fullDimensionActions = [ + ["trX", "3000", "machineWidth", ["tr", "x"]], + ["trY", "2000", "machineHeight", ["tr", "y"]], + ]; + + test.each(fullDimensionActions)( + "Key %p with value %p sets %p and %p.%p as well", + (key, value, outputValueName, outDim) => { + setDim(key, value, outDim, undefined); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const stdDimensionActions = [ + ["trZ", "50", ["tr", "z"]], + ["tlX", "20", ["tl", "x"]], + ["tlY", "10", ["tl", "y"]], + ["tlZ", "40", ["tl", "z"]], + ["brX", "20", ["br", "x"]], + ["brY", "15", ["br", "y"], 0], + ["brZ", "40", ["br", "z"]], + ["blX", "16", ["bl", "x"], 0], + ["blY", "17", ["bl", "y"], 0], + ["blZ", "70", ["bl", "z"]], + [ + "Acceptable_Calibration_Threshold", + "1500", + "acceptableCalibrationThreshold", + ], + ]; + + test.each(stdDimensionActions)( + "Key %p with value %p sets %p", + (key, value, outDim, outValue = undefined) => { + setDim(key, value, outDim, outValue); + }, + ); +}); diff --git a/www/js/tests/printercmd.test.ts b/www/js/tests/printercmd.test.ts new file mode 100644 index 000000000..2207dd42c --- /dev/null +++ b/www/js/tests/printercmd.test.ts @@ -0,0 +1,171 @@ +import { beforeEach, describe, expect, test } from "bun:test"; +import { + maslowMsgHandling, + loadedValues, +} from "../common.js"; + +describe("preferncesdlg", () => { + beforeEach(() => { + // Set up the DOM elements and initial values + document.body.innerHTML = ` + + + + + + + + `; + global.initialGuess = { + tr: { x: 0, y: 0, z: 0 }, + tl: { x: 0, y: 0, z: 0 }, + br: { x: 0, y: 0, z: 0 }, + bl: { x: 0, y: 0, z: 0 }, + }; + global.acceptableCalibrationThreshold = 0.5; + }); + + const SendPrinterCommand = (cmd, echo_on, processfn, errorfn, id, max_id, extra_arg) => { + if (cmd.length === 0) { + return; + } + const url = "/command?commandText="; + const push_cmd = typeof echo_on !== "undefined" ? echo_on : true; + if (push_cmd) { + Monitor_output_Update(`[#]${cmd}\n`); + } + //removeIf(production) + console.log(cmd); + if (processfn instanceof Function) { + processfn("Test response"); + } else { + SendPrinterCommandSuccess("Test response"); + } + return; + //endRemoveIf(production) + if (!(processfn instanceof Function)) processfn = SendPrinterCommandSuccess; + if (!(errorfn instanceof Function)) errorfn = SendPrinterCommandFailed; + if (!cmd.startsWith("[ESP")) { + grbl_processfn = processfn; + grbl_errorfn = errorfn; + processfn = noop; + errorfn = noop; + } + cmd = encodeURI(cmd); + cmd = cmd.replace("#", "%23"); + if (extra_arg) { + cmd += "&" + extra_arg; + } + SendGetHttp(url + cmd, processfn, errorfn, id, max_id); + //console.log(cmd); + }; + + function SendPrinterSilentCommand(cmd, processfn, errorfn, id, max_id) { + const url = "/command_silent?commandText="; + if (cmd.length === 0) { + return; + } + //removeIf(production) + console.log(cmd); + if (processfn instanceof Function) processfn("Test response"); + else SendPrinterCommandSuccess("Test response"); + return; + //endRemoveIf(production) + if (!(processfn instanceof Function)) + processfn = SendPrinterSilentCommandSuccess; + if (!(errorfn instanceof Function)) errorfn = SendPrinterCommandFailed; + cmd = encodeURI(cmd); + cmd = cmd.replace("#", "%23"); + SendGetHttp(url + cmd, processfn, errorfn, id, max_id); + //console.log(cmd); + } + + test.each(inputValidation)("Input %p results in %p", (inp, expected) => { + const result = maslowMsgHandling(inp); + expect(result).toBe(expected); + }); + + const stdActions = [ + ["calibration_grid_size", "10", "gridSize"], + ["calibration_grid_width_mm_X", "2000", "gridWidth"], + ["calibration_grid_height_mm_Y", "1000", "gridHeight"], + ["Retract_Current_Threshold", "1500", "retractionForce"], + ]; + + const noErrorResult = (key, value) => { + const result = maslowMsgHandling(`${key}=${value}`); + expect(result).toBe(""); + }; + + test.each(stdActions)( + "Key %p sets value %p into %p", + (key, value, outputValueName) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const orientationActions = [ + ["vertical", "false", "machineOrientation", "horizontal"], + ["vertical", "true", "machineOrientation", "vertical"], + ["vertical", "something else", "machineOrientation", "vertical"], + ]; + + test.each(orientationActions)( + "Key %p with value %p sets %p to %p", + (key, value, outputValueName, outputValue) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(outputValue); + }, + ); + + const setDim = (key, value, outDim, outValue) => { + noErrorResult(key, value); + if (typeof outValue === "undefined") { + outValue = Number.parseFloat(value); + } + if (Array.isArray(outDim)) { + expect(global.initialGuess[outDim[0]][outDim[1]]).toBe(outValue); + } else { + expect(global[outDim]).toBe(outValue); + } + }; + + const fullDimensionActions = [ + ["trX", "3000", "machineWidth", ["tr", "x"]], + ["trY", "2000", "machineHeight", ["tr", "y"]], + ]; + + test.each(fullDimensionActions)( + "Key %p with value %p sets %p and %p.%p as well", + (key, value, outputValueName, outDim) => { + setDim(key, value, outDim, undefined); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const stdDimensionActions = [ + ["trZ", "50", ["tr", "z"]], + ["tlX", "20", ["tl", "x"]], + ["tlY", "10", ["tl", "y"]], + ["tlZ", "40", ["tl", "z"]], + ["brX", "20", ["br", "x"]], + ["brY", "15", ["br", "y"], 0], + ["brZ", "40", ["br", "z"]], + ["blX", "16", ["bl", "x"], 0], + ["blY", "17", ["bl", "y"], 0], + ["blZ", "70", ["bl", "z"]], + [ + "Acceptable_Calibration_Threshold", + "1500", + "acceptableCalibrationThreshold", + ], + ]; + + test.each(stdDimensionActions)( + "Key %p with value %p sets %p", + (key, value, outDim, outValue = undefined) => { + setDim(key, value, outDim, outValue); + }, + ); +}); diff --git a/www/js/tests/scanwifidlg.test.ts b/www/js/tests/scanwifidlg.test.ts new file mode 100644 index 000000000..02181909b --- /dev/null +++ b/www/js/tests/scanwifidlg.test.ts @@ -0,0 +1,147 @@ +import { beforeEach, describe, expect, test } from "bun:test"; +import { + displayBlock, displayNone, setHTML, translate_text_item, + maslowMsgHandling, + loadedValues, +} from "../common.js"; + +describe("scanwifidlg", () => { + beforeEach(() => { + // Set up the DOM elements and initial values + document.body.innerHTML = ` + + + + + + + + `; + global.initialGuess = { + tr: { x: 0, y: 0, z: 0 }, + tl: { x: 0, y: 0, z: 0 }, + br: { x: 0, y: 0, z: 0 }, + bl: { x: 0, y: 0, z: 0 }, + }; + global.acceptableCalibrationThreshold = 0.5; + }); + + function refresh_scanwifi() { + displayBlock("AP_scan_loader"); + displayNone("AP_scan_list"); + displayBlock("AP_scan_status"); + setHTML("AP_scan_status", translate_text_item("Scanning")); + displayNone("refresh_scanwifi_btn"); + //removeIf(production) + const testResponse = [ + '{"AP_LIST":[', + '{"SSID":"HP-Setup>71-M277LaserJet","SIGNAL":"90","IS_PROTECTED":"0"},', + '{"SSID":"NETGEAR_2GEXT_OFFICE2","SIGNAL":"58","IS_PROTECTED":"1"},', + '{"SSID":"NETGEAR_2GEXT_OFFICE","SIGNAL":"34","IS_PROTECTED":"1"},', + '{"SSID":"NETGEAR_2GEXT_COULOIR","SIGNAL":"18","IS_PROTECTED":"1"},', + '{"SSID":"HP-Print-D3-ColorLaserJetPro","SIGNAL":"14","IS_PROTECTED":"0"},', + '{"SSID":"external-wifi","SIGNAL":"20","IS_PROTECTED":"1"},', + '{"SSID":"Livebox-4D0F","SIGNAL":"24","IS_PROTECTED":"1"},', + '{"SSID":"SFR_2000","SIGNAL":"20","IS_PROTECTED":"1"}', + '{"SSID":"SFR_0D90","SIGNAL":"26","IS_PROTECTED":"1"},', + '{"SSID":"SFRWiFiFON","SIGNAL":"18","IS_PROTECTED":"0"},', + '{"SSID":"SFRWiFiMobile","SIGNAL":"18","IS_PROTECTED":"1"},', + '{"SSID":"FreeWifi","SIGNAL":"16","IS_PROTECTED":"0"}', + ']}' + ]; + getscanWifiSuccess(testResponse.join("")); + return; + //endRemoveIf(production) + const url = `/command?plain=${encodeURIComponent("[ESP410]")}`; + SendGetHttp(url, getscanWifiSuccess, getscanWififailed); + } + + test.each(inputValidation)("Input %p results in %p", (inp, expected) => { + const result = maslowMsgHandling(inp); + expect(result).toBe(expected); + }); + + const stdActions = [ + ["calibration_grid_size", "10", "gridSize"], + ["calibration_grid_width_mm_X", "2000", "gridWidth"], + ["calibration_grid_height_mm_Y", "1000", "gridHeight"], + ["Retract_Current_Threshold", "1500", "retractionForce"], + ]; + + const noErrorResult = (key, value) => { + const result = maslowMsgHandling(`${key}=${value}`); + expect(result).toBe(""); + }; + + test.each(stdActions)( + "Key %p sets value %p into %p", + (key, value, outputValueName) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const orientationActions = [ + ["vertical", "false", "machineOrientation", "horizontal"], + ["vertical", "true", "machineOrientation", "vertical"], + ["vertical", "something else", "machineOrientation", "vertical"], + ]; + + test.each(orientationActions)( + "Key %p with value %p sets %p to %p", + (key, value, outputValueName, outputValue) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(outputValue); + }, + ); + + const setDim = (key, value, outDim, outValue) => { + noErrorResult(key, value); + if (typeof outValue === "undefined") { + outValue = Number.parseFloat(value); + } + if (Array.isArray(outDim)) { + expect(global.initialGuess[outDim[0]][outDim[1]]).toBe(outValue); + } else { + expect(global[outDim]).toBe(outValue); + } + }; + + const fullDimensionActions = [ + ["trX", "3000", "machineWidth", ["tr", "x"]], + ["trY", "2000", "machineHeight", ["tr", "y"]], + ]; + + test.each(fullDimensionActions)( + "Key %p with value %p sets %p and %p.%p as well", + (key, value, outputValueName, outDim) => { + setDim(key, value, outDim, undefined); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const stdDimensionActions = [ + ["trZ", "50", ["tr", "z"]], + ["tlX", "20", ["tl", "x"]], + ["tlY", "10", ["tl", "y"]], + ["tlZ", "40", ["tl", "z"]], + ["brX", "20", ["br", "x"]], + ["brY", "15", ["br", "y"], 0], + ["brZ", "40", ["br", "z"]], + ["blX", "16", ["bl", "x"], 0], + ["blY", "17", ["bl", "y"], 0], + ["blZ", "70", ["bl", "z"]], + [ + "Acceptable_Calibration_Threshold", + "1500", + "acceptableCalibrationThreshold", + ], + ]; + + test.each(stdDimensionActions)( + "Key %p with value %p sets %p", + (key, value, outDim, outValue = undefined) => { + setDim(key, value, outDim, outValue); + }, + ); +}); diff --git a/www/js/tests/translate.test.ts b/www/js/tests/translate.test.ts new file mode 100644 index 000000000..059059a7b --- /dev/null +++ b/www/js/tests/translate.test.ts @@ -0,0 +1,165 @@ +import { beforeEach, describe, expect, test } from "bun:test"; +import { + maslowMsgHandling, + loadedValues, +} from "../common.js"; + +describe("translate", () => { + beforeEach(() => { + // Set up the DOM elements and initial values + document.body.innerHTML = ` + + + + + + + + `; + global.initialGuess = { + tr: { x: 0, y: 0, z: 0 }, + tl: { x: 0, y: 0, z: 0 }, + br: { x: 0, y: 0, z: 0 }, + bl: { x: 0, y: 0, z: 0 }, + }; + global.acceptableCalibrationThreshold = 0.5; + +//removeIf(production) +const translated_list = []; +//endRemoveIf(production) + }); + + const translate_text = (lang) => { + let translated_content = ""; + + const All = document.getElementsByTagName("*"); + for (let i = 0; i < All.length; i++) { + if (All[i].hasAttribute("translate")) { + let content = ""; + if (!All[i].hasAttribute("english_content")) { + content = All[i].innerHTML; + content.trim(); + All[i].setAttribute("english_content", content); + //removeIf(production) + const item = { + content: content, + }; + translated_list.push(item); + //endRemoveIf(production) + } + content = All[i].getAttribute("english_content"); + translated_content = translate_text_item(content); + + All[i].innerHTML = translated_content; + } + //add support for placeholder attribute + if (All[i].hasAttribute('translateph') && All[i].hasAttribute('placeholder')) { + let content = ""; + if (!All[i].hasAttribute("english_content")) { + content = All[i].getAttribute("placeholder"); + content.trim(); + //removeIf(production) + const item = { + content: content, + }; + translated_list.push(item); + //endRemoveIf(production) + All[i].setAttribute("english_content", content); + } + content = All[i].getAttribute("english_content"); + + translated_content = HTMLDecode(translate_text_item(content)); + All[i].setAttribute("placeholder", translated_content); + } + } + }; + + test.each(inputValidation)("Input %p results in %p", (inp, expected) => { + const result = maslowMsgHandling(inp); + expect(result).toBe(expected); + }); + + const stdActions = [ + ["calibration_grid_size", "10", "gridSize"], + ["calibration_grid_width_mm_X", "2000", "gridWidth"], + ["calibration_grid_height_mm_Y", "1000", "gridHeight"], + ["Retract_Current_Threshold", "1500", "retractionForce"], + ]; + + const noErrorResult = (key, value) => { + const result = maslowMsgHandling(`${key}=${value}`); + expect(result).toBe(""); + }; + + test.each(stdActions)( + "Key %p sets value %p into %p", + (key, value, outputValueName) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const orientationActions = [ + ["vertical", "false", "machineOrientation", "horizontal"], + ["vertical", "true", "machineOrientation", "vertical"], + ["vertical", "something else", "machineOrientation", "vertical"], + ]; + + test.each(orientationActions)( + "Key %p with value %p sets %p to %p", + (key, value, outputValueName, outputValue) => { + noErrorResult(key, value); + expect(loadedValues(outputValueName)).toBe(outputValue); + }, + ); + + const setDim = (key, value, outDim, outValue) => { + noErrorResult(key, value); + if (typeof outValue === "undefined") { + outValue = Number.parseFloat(value); + } + if (Array.isArray(outDim)) { + expect(global.initialGuess[outDim[0]][outDim[1]]).toBe(outValue); + } else { + expect(global[outDim]).toBe(outValue); + } + }; + + const fullDimensionActions = [ + ["trX", "3000", "machineWidth", ["tr", "x"]], + ["trY", "2000", "machineHeight", ["tr", "y"]], + ]; + + test.each(fullDimensionActions)( + "Key %p with value %p sets %p and %p.%p as well", + (key, value, outputValueName, outDim) => { + setDim(key, value, outDim, undefined); + expect(loadedValues(outputValueName)).toBe(value); + }, + ); + + const stdDimensionActions = [ + ["trZ", "50", ["tr", "z"]], + ["tlX", "20", ["tl", "x"]], + ["tlY", "10", ["tl", "y"]], + ["tlZ", "40", ["tl", "z"]], + ["brX", "20", ["br", "x"]], + ["brY", "15", ["br", "y"], 0], + ["brZ", "40", ["br", "z"]], + ["blX", "16", ["bl", "x"], 0], + ["blY", "17", ["bl", "y"], 0], + ["blZ", "70", ["bl", "z"]], + [ + "Acceptable_Calibration_Threshold", + "1500", + "acceptableCalibrationThreshold", + ], + ]; + + test.each(stdDimensionActions)( + "Key %p with value %p sets %p", + (key, value, outDim, outValue = undefined) => { + setDim(key, value, outDim, outValue); + }, + ); +}); diff --git a/www/js/toolpath-displayer.js b/www/js/toolpath-displayer.js index eadfb2084..21123c3df 100644 --- a/www/js/toolpath-displayer.js +++ b/www/js/toolpath-displayer.js @@ -1,24 +1,25 @@ // Display the XY-plane projection of a GCode toolpath on a 2D canvas +import { Common, getValue, id, MPOS, WPOS, Toolpath } from "./common.js"; -var root = window; - -const canvas = document.getElementById("small-toolpath"); const scale = window.devicePixelRatio; -const width = window.innerWidth; -canvas.width = width * scale; -canvas.height = (width / 2) * scale; -var tp = canvas.getContext("2d", { willReadFrequently: true }); -var tpRect; +const width = window.innerWidth; +const tp_canvas = id("small-toolpath"); +if (tp_canvas) { + tp_canvas.width = width * scale; + tp_canvas.height = (width / 2) * scale; +} +const tp = tp_canvas.getContext("2d", { willReadFrequently: true }); +let tpRect; tp.lineWidth = 0.1; -tp.lineCap = 'round'; -tp.strokeStyle = 'black'; +tp.lineCap = "round"; +tp.strokeStyle = "black"; var cameraAngle = 0; var tlX = -8.339; var tlY = 2209; -var trX = 3505; +var trX = 3505; var trY = 2209; var blX = 0; var blY = 0; @@ -36,8 +37,8 @@ tlC.lineTo(230, 40); tlC.lineTo(90, 40); tlC.closePath(); tlC.lineWidth = 5; -tlC.strokeStyle = 'white'; -tlC.fillStyle = 'white'; +tlC.strokeStyle = "white"; +tlC.fillStyle = "white"; tlC.fill(); tlC.stroke(); @@ -51,8 +52,8 @@ trC.lineTo(230, 40); trC.lineTo(90, 40); trC.closePath(); trC.lineWidth = 5; -trC.strokeStyle = 'white'; -trC.fillStyle = 'white'; +trC.strokeStyle = "white"; +trC.fillStyle = "white"; trC.fill(); trC.stroke(); @@ -66,8 +67,8 @@ blC.lineTo(90, 140); blC.lineTo(90, 40); blC.closePath(); blC.lineWidth = 5; -blC.strokeStyle = 'white'; -blC.fillStyle = 'white'; +blC.strokeStyle = "white"; +blC.fillStyle = "white"; blC.fill(); blC.stroke(); @@ -81,8 +82,8 @@ brC.lineTo(230, 40); brC.lineTo(90, 140); brC.closePath(); brC.lineWidth = 5; -brC.strokeStyle = 'white'; -brC.fillStyle = 'white'; +brC.strokeStyle = "white"; +brC.fillStyle = "white"; brC.fill(); brC.stroke(); @@ -91,46 +92,44 @@ upC.fillStyle = "#9d88c0"; upC.fillRect(0, 0, 500, 500); // #rect441 upC.beginPath(); -upC.fillStyle = 'white'; +upC.fillStyle = "white"; upC.lineWidth = 1; -upC.rect(60+49.213840, 99.622299, 93.976021, 74.721062); +upC.rect(60 + 49.21384, 99.622299, 93.976021, 74.721062); upC.fill(); - + // #path608 upC.beginPath(); -upC.strokeStyle = 'white'; +upC.strokeStyle = "white"; upC.lineWidth = 1; -upC.lineCap = 'butt'; -upC.lineJoin = 'miter'; -upC.moveTo(60+5.109692, 104.666810); -upC.lineTo(60+94.679220, 4.145211); -upC.lineTo(60+189.305070, 103.959000); -upC.lineTo(60+5.109692, 104.666810); +upC.lineCap = "butt"; +upC.lineJoin = "miter"; +upC.moveTo(60 + 5.109692, 104.66681); +upC.lineTo(60 + 94.67922, 4.145211); +upC.lineTo(60 + 189.30507, 103.959); +upC.lineTo(60 + 5.109692, 104.66681); upC.closePath(); upC.stroke(); upC.fill(); - - const dnC = document.getElementById("dnBtn").getContext("2d"); dnC.fillStyle = "#9d88c0"; dnC.fillRect(0, 0, 500, 500); // #rect441 dnC.save(); -dnC.transform(1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000); -dnC.fillStyle = 'white'; +dnC.transform(1.0, 0.0, 0.0, -1.0, 0.0, 0.0); +dnC.fillStyle = "white"; dnC.lineWidth = 1; -dnC.rect(60 + 49.213840, -75.901474, 93.976021, 74.721062); +dnC.rect(60 + 49.21384, -75.901474, 93.976021, 74.721062); dnC.fill(); dnC.restore(); - + // #path608 dnC.beginPath(); -dnC.strokeStyle = 'white'; -dnC.fillStyle = 'white'; +dnC.strokeStyle = "white"; +dnC.fillStyle = "white"; dnC.lineWidth = 1; -dnC.lineCap = 'butt'; -dnC.lineJoin = 'miter'; +dnC.lineCap = "butt"; +dnC.lineJoin = "miter"; dnC.moveTo(60 + 5, 70 - 20); dnC.lineTo(60 + 94, 171 - 20); dnC.lineTo(60 + 189, 71 - 20); @@ -144,51 +143,50 @@ rC.fillStyle = "#9d88c0"; rC.fillRect(0, 0, 500, 500); // #g1100 rC.save(); -rC.transform(0.000000, 1.000000, -1.000000, 0.000000, 187.481000, 0.273690); - +rC.transform(0.0, 1.0, -1.0, 0.0, 187.481, 0.27369); + // #rect441 -rC.fillStyle = 'white'; +rC.fillStyle = "white"; rC.lineWidth = 1; -rC.rect(-20 + 49.213840, 99.622299 - 80, 93.976021, 74.721062); +rC.rect(-20 + 49.21384, 99.622299 - 80, 93.976021, 74.721062); rC.fill(); - + // #path608 rC.beginPath(); -rC.strokeStyle = 'white'; +rC.strokeStyle = "white"; rC.lineWidth = 1; -rC.lineCap = 'butt'; -rC.lineJoin = 'miter'; -rC.moveTo(-20+5.109692, 104.666810 - 80); -rC.lineTo(-20+94.679220, 4.145213 - 80); -rC.lineTo(-20+189.305070, 103.959000 - 80); +rC.lineCap = "butt"; +rC.lineJoin = "miter"; +rC.moveTo(-20 + 5.109692, 104.66681 - 80); +rC.lineTo(-20 + 94.67922, 4.145213 - 80); +rC.lineTo(-20 + 189.30507, 103.959 - 80); rC.closePath(); rC.stroke(); rC.fill(); rC.restore(); - const lC = document.getElementById("lBtn").getContext("2d"); lC.fillStyle = "#9d88c0"; lC.fillRect(0, 0, 500, 500); // #g1100 lC.save(); -lC.transform(0.000000, 1.000000, 1.000000, 0.000000, 11.957500, 0.273690); - +lC.transform(0.0, 1.0, 1.0, 0.0, 11.9575, 0.27369); + // #rect441 -lC.fillStyle = 'white'; +lC.fillStyle = "white"; lC.lineWidth = 1; -lC.rect(-20 + 49.213840, 99.622299, 93.976021, 74.721062); +lC.rect(-20 + 49.21384, 99.622299, 93.976021, 74.721062); lC.fill(); - + // #path608 lC.beginPath(); -lC.strokeStyle = 'white'; +lC.strokeStyle = "white"; lC.lineWidth = 1; -lC.lineCap = 'butt'; -lC.lineJoin = 'miter'; -lC.moveTo(-20 + 5.109692, 104.666810); -lC.lineTo(-20 + 94.679220, 4.145213); -lC.lineTo(-20 + 189.305070, 103.959000); +lC.lineCap = "butt"; +lC.lineJoin = "miter"; +lC.moveTo(-20 + 5.109692, 104.66681); +lC.lineTo(-20 + 94.67922, 4.145213); +lC.lineTo(-20 + 189.30507, 103.959); lC.closePath(); lC.stroke(); lC.fill(); @@ -201,41 +199,41 @@ const yO = -45; // #path5094 hC.beginPath(); -hC.fillStyle = 'rgb(183, 161, 208)'; -hC.strokeStyle = 'rgb(0, 0, 0)'; +hC.fillStyle = "rgb(183, 161, 208)"; +hC.strokeStyle = "rgb(0, 0, 0)"; hC.lineWidth = 0.472615; -hC.lineCap = 'butt'; -hC.lineJoin = 'miter'; -hC.moveTo(xO + 55.719343, 197.549650 + yO); -hC.lineTo(xO + 152.150650, 197.549650 + yO); -hC.lineTo(xO + 152.609520, 74.078285 + yO); -hC.lineTo(xO + 132.404810, 73.680279 + yO); -hC.lineTo(xO + 131.393420, 110.310850 + yO); -hC.lineTo(xO + 103.475730, 84.035976 + yO); -hC.lineTo(xO + 54.341657, 131.433070 + yO); +hC.lineCap = "butt"; +hC.lineJoin = "miter"; +hC.moveTo(xO + 55.719343, 197.54965 + yO); +hC.lineTo(xO + 152.15065, 197.54965 + yO); +hC.lineTo(xO + 152.60952, 74.078285 + yO); +hC.lineTo(xO + 132.40481, 73.680279 + yO); +hC.lineTo(xO + 131.39342, 110.31085 + yO); +hC.lineTo(xO + 103.47573, 84.035976 + yO); +hC.lineTo(xO + 54.341657, 131.43307 + yO); hC.fill(); hC.stroke(); - + // #rect1898 hC.beginPath(); -hC.fillStyle = 'rgb(218, 208, 230)'; +hC.fillStyle = "rgb(218, 208, 230)"; hC.lineWidth = 0.472615; -hC.rect(xO + 74.087212, 146.169600 + yO, 29.847790, 50.981743); +hC.rect(xO + 74.087212, 146.1696 + yO, 29.84779, 50.981743); hC.fill(); - + // #path13430 hC.beginPath(); -hC.fillStyle = 'rgb(151, 132, 181)'; -hC.strokeStyle = 'rgb(0, 0, 0)'; +hC.fillStyle = "rgb(151, 132, 181)"; +hC.strokeStyle = "rgb(0, 0, 0)"; hC.lineWidth = 0.472615; -hC.lineCap = 'butt'; -hC.lineJoin = 'miter'; -hC.moveTo(xO + 103.475730, 84.035976 + yO); -hC.lineTo(xO + 167.304170, 144.974770 + yO); -hC.lineTo(xO + 181.080090, 132.229340 + yO); -hC.lineTo(xO + 103.016580, 56.951581 + yO); -hC.lineTo(xO + 24.953156, 131.432760 + yO); -hC.lineTo(xO + 40.565818, 144.974770 + yO); +hC.lineCap = "butt"; +hC.lineJoin = "miter"; +hC.moveTo(xO + 103.47573, 84.035976 + yO); +hC.lineTo(xO + 167.30417, 144.97477 + yO); +hC.lineTo(xO + 181.08009, 132.22934 + yO); +hC.lineTo(xO + 103.01658, 56.951581 + yO); +hC.lineTo(xO + 24.953156, 131.43276 + yO); +hC.lineTo(xO + 40.565818, 144.97477 + yO); hC.fill(); hC.stroke(); @@ -246,14 +244,14 @@ playC.fillStyle = "#4aa85c"; playC.fillRect(0, 0, 500, 500); playC.beginPath(); -playC.strokeStyle = 'white'; -playC.fillStyle = 'white'; +playC.strokeStyle = "white"; +playC.fillStyle = "white"; playC.lineWidth = 1; -playC.lineCap = 'butt'; -playC.lineJoin = 'miter'; -playC.moveTo(60 + 44.053484, 147.608260 - 35); +playC.lineCap = "butt"; +playC.lineJoin = "miter"; +playC.moveTo(60 + 44.053484, 147.60826 - 35); playC.lineTo(60 + 44.053484, 68.502834 - 35); -playC.lineTo(60 + 112.311470, 106.828610 - 35); +playC.lineTo(60 + 112.31147, 106.82861 - 35); playC.closePath; playC.fill(); playC.stroke(); @@ -264,14 +262,14 @@ pauseC.fillRect(0, 0, 500, 500); // #rect1967 pauseC.beginPath(); -pauseC.fillStyle = 'white'; +pauseC.fillStyle = "white"; pauseC.lineWidth = 1; pauseC.rect(75 + 44, 66 - 35, 20, 81); pauseC.fill(); - + // #rect1967-4 pauseC.beginPath(); -pauseC.fillStyle = 'white'; +pauseC.fillStyle = "white"; pauseC.lineWidth = 1; pauseC.rect(75 + 75, 66 - 35, 20, 81); pauseC.fill(); @@ -280,36 +278,36 @@ const stopC = document.getElementById("stopBtn").getContext("2d"); stopC.fillStyle = "#cd654c"; stopC.fillRect(0, 0, 500, 500); -stopC.strokeStyle = 'white'; -stopC.fillStyle = 'white'; +stopC.strokeStyle = "white"; +stopC.fillStyle = "white"; stopC.beginPath(); -stopC.fillStyle = 'white'; +stopC.fillStyle = "white"; stopC.lineWidth = 1; stopC.rect(60 + 44, 65 - 35, 100, 80); stopC.fill(); stopC.stroke(); -var tpUnits = 'G21'; - -var tpBbox = { - min: { - x: Infinity, - y: Infinity - }, - max: { - x: -Infinity, - y: -Infinity - } +var tpUnits = "G21"; + +const tpBbox = { + min: { + x: Number.POSITIVE_INFINITY, + y: Number.POSITIVE_INFINITY, + }, + max: { + x: Number.NEGATIVE_INFINITY, + y: Number.NEGATIVE_INFINITY, + }, }; var bboxIsSet = false; -var resetBbox = function() { - tpBbox.min.x = Infinity; - tpBbox.min.y = Infinity; - tpBbox.max.x = -Infinity; - tpBbox.max.y = -Infinity; - bboxIsSet = false; -} +const resetBbox = () => { + tpBbox.min.x = Number.POSITIVE_INFINITY; + tpBbox.min.y = Number.POSITIVE_INFINITY; + tpBbox.max.x = Number.NEGATIVE_INFINITY; + tpBbox.max.y = Number.NEGATIVE_INFINITY; + bboxIsSet = false; +}; // Project the 3D toolpath onto the 2D Canvas // The coefficients determine the type of projection @@ -317,650 +315,746 @@ var resetBbox = function() { var xx = 0.707; var xy = 0.707; var xz = 0.0; -var yx = -0.707/2; -var yy = 0.707/2; +var yx = -0.707 / 2; +var yy = 0.707 / 2; var yz = 1.0; -var isoView = function() { - xx = 0.707; - xy = 0.707; - xz = 0.0; - yx = -0.707; - yy = 0.707; - yz = 1.0; -} -var obliqueView = function() { - xx = 0.707; - xy = 0.707; - xz = 0.0; - yx = -0.707/2; - yy = 0.707/2; - yz = 1.0; -} -var topView = function() { - xx = 1.0; - xy = 0.0; - xz = 0.0; - yx = 0.0; - yy = 1.0; - yz = 0.0; -} -var projection = function(wpos) { - outpoint = {} - outpoint.x = wpos.x * xx + wpos.y * xy + wpos.z * xz; - outpoint.y = wpos.x * yx + wpos.y * yy + wpos.z * yz; - return outpoint; -} +const isoView = function () { + xx = 0.707; + xy = 0.707; + xz = 0.0; + yx = -0.707; + yy = 0.707; + yz = 1.0; +}; +const obliqueView = function () { + xx = 0.707; + xy = 0.707; + xz = 0.0; + yx = -0.707 / 2; + yy = 0.707 / 2; + yz = 1.0; +}; +const topView = function () { + xx = 1.0; + xy = 0.0; + xz = 0.0; + yx = 0.0; + yy = 1.0; + yz = 0.0; +}; +const projection = function (wpos) { + outpoint = {}; + outpoint.x = wpos.x * xx + wpos.y * xy + wpos.z * xz; + outpoint.y = wpos.x * yx + wpos.y * yy + wpos.z * yz; + return outpoint; +}; -var formatLimit = function(mm) { - return (tpUnits == 'G20') ? (mm/25.4).toFixed(3)+'"' : mm.toFixed(2)+'mm'; -} +const formatLimit = function (mm) { + return tpUnits == "G20" ? (mm / 25.4).toFixed(3) + '"' : mm.toFixed(2) + "mm"; +}; var toolX = null; var toolY = null; var toolSave = null; var toolRadius = 6; -var toolRectWH = toolRadius*2 + 4; // Slop to encompass the entire image area - -var drawTool = function(dpos) { - pp = projection(dpos) - toolX = xToPixel(pp.x)-toolRadius-2; - toolY = yToPixel(pp.y)-toolRadius-2; - toolSave = tp.getImageData(toolX, toolY, toolRectWH, toolRectWH); - - tp.beginPath(); - tp.strokeStyle = 'magenta'; - tp.fillStyle = 'magenta'; - tp.arc(pp.x, pp.y, toolRadius/scaler, 0, Math.PI*2, true); - tp.fill(); - tp.stroke(); -} +var toolRectWH = toolRadius * 2 + 4; // Slop to encompass the entire image area + +const drawTool = function (dpos) { + pp = projection(dpos); + toolX = xToPixel(pp.x) - toolRadius - 2; + toolY = yToPixel(pp.y) - toolRadius - 2; + toolSave = tp.getImageData(toolX, toolY, toolRectWH, toolRectWH); + + tp.beginPath(); + tp.strokeStyle = "magenta"; + tp.fillStyle = "magenta"; + tp.arc(pp.x, pp.y, toolRadius / scaler, 0, Math.PI * 2, true); + tp.fill(); + tp.stroke(); +}; -var drawOrigin = function(radius) { - po = projection({x: 0.0, y:0.0, z:0.0}) - tp.beginPath(); - tp.strokeStyle = 'red'; - tp.arc(po.x, po.y, radius, 0, Math.PI*2, false); - tp.moveTo(-radius*1.5, 0); - tp.lineTo(radius*1.5, 0); - tp.moveTo(0,-radius*1.5); - tp.lineTo(0, radius*1.5); - tp.stroke(); -} +var drawOrigin = function (radius) { + po = projection({ x: 0.0, y: 0.0, z: 0.0 }); + tp.beginPath(); + tp.strokeStyle = "red"; + tp.arc(po.x, po.y, radius, 0, Math.PI * 2, false); + tp.moveTo(-radius * 1.5, 0); + tp.lineTo(radius * 1.5, 0); + tp.moveTo(0, -radius * 1.5); + tp.lineTo(0, radius * 1.5); + tp.stroke(); +}; -var drawMachineBounds = function() { - - //Work codinates offset the maxTravel part centers it in the view so 0,0 is the middle of the sheet - var woodWidth = 2438; - var woodHeight = 2438/2; - - //Project onto the camera view - const p0 = projection({x: -woodWidth/2, y: -woodHeight/2, z: 0}); - const p1 = projection({x: woodWidth/2, y: -woodHeight/2, z: 0}); - const p2 = projection({x: woodWidth/2, y: woodHeight/2, z: 0}); - const p3 = projection({x: -woodWidth/2, y: woodHeight/2, z: 0}); - - //This is used to fit everything in the camera view later - tpBbox.min.x = Math.min(tpBbox.min.x, p0.x); - tpBbox.min.y = Math.min(tpBbox.min.y, p0.y); - tpBbox.max.x = Math.max(tpBbox.max.x, p2.x); - tpBbox.max.y = Math.max(tpBbox.max.y, p2.y); - bboxIsSet = true; - - //Draw to the actual display - tp.beginPath(); - tp.moveTo(p0.x, p0.y); - tp.lineTo(p0.x, p0.y); - tp.lineTo(p1.x, p1.y); - tp.lineTo(p2.x, p2.y); - tp.lineTo(p3.x, p3.y); - tp.lineTo(p0.x, p0.y); - tp.strokeStyle = "green"; - tp.stroke(); +var drawMachineBounds = function () { + //Work codinates offset the maxTravel part centers it in the view so 0,0 is the middle of the sheet + var woodWidth = 2438; + var woodHeight = 2438 / 2; + + //Project onto the camera view + const p0 = projection({ x: -woodWidth / 2, y: -woodHeight / 2, z: 0 }); + const p1 = projection({ x: woodWidth / 2, y: -woodHeight / 2, z: 0 }); + const p2 = projection({ x: woodWidth / 2, y: woodHeight / 2, z: 0 }); + const p3 = projection({ x: -woodWidth / 2, y: woodHeight / 2, z: 0 }); + + //This is used to fit everything in the camera view later + tpBbox.min.x = Math.min(tpBbox.min.x, p0.x); + tpBbox.min.y = Math.min(tpBbox.min.y, p0.y); + tpBbox.max.x = Math.max(tpBbox.max.x, p2.x); + tpBbox.max.y = Math.max(tpBbox.max.y, p2.y); + bboxIsSet = true; + + //Draw to the actual display + tp.beginPath(); + tp.moveTo(p0.x, p0.y); + tp.lineTo(p0.x, p0.y); + tp.lineTo(p1.x, p1.y); + tp.lineTo(p2.x, p2.y); + tp.lineTo(p3.x, p3.y); + tp.lineTo(p0.x, p0.y); + tp.strokeStyle = "green"; + tp.stroke(); +}; -} +var drawMachineBelts = function () { + console.log("Draw belts"); + + const tl = projection({ x: tlX - trX / 2, y: tlY / 2, z: 0 }); + const tr = projection({ x: trX / 2, y: trY / 2, z: 0 }); + const bl = projection({ x: blX - brX / 2, y: blY - tlY / 2, z: 0 }); + const br = projection({ x: brX / 2, y: brY - trY / 2, z: 0 }); + + tpBbox.min.x = Math.min(tpBbox.min.x, bl.x); + tpBbox.min.y = Math.min(tpBbox.min.y, bl.y); + tpBbox.max.x = Math.max(tpBbox.max.x, tr.x); + tpBbox.max.y = Math.max(tpBbox.max.y, tr.y); + + tp.beginPath(); + tp.strokeStyle = "grey"; + tp.moveTo(0, 0); + tp.lineTo(tl.x, tl.y); + tp.moveTo(0, 0); + tp.lineTo(tr.x, tr.y); + tp.moveTo(0, 0); + tp.lineTo(bl.x, bl.y); + tp.moveTo(0, 0); + tp.lineTo(br.x, br.y); + tp.stroke(); + + tp.fillStyle = "black"; + tp.beginPath(); + tp.arc(tl.x, tl.y, 10, 0, 2 * Math.PI); + tp.closePath(); + tp.fill(); + tp.beginPath(); + tp.arc(tr.x, tr.y, 10, 0, 2 * Math.PI); + tp.closePath(); + tp.fill(); + tp.beginPath(); + tp.arc(br.x, br.y, 10, 0, 2 * Math.PI); + tp.closePath(); + tp.fill(); + tp.beginPath(); + tp.arc(bl.x, bl.y, 10, 0, 2 * Math.PI); + tp.closePath(); + tp.fill(); + + const squareSize = projection({ x: 50, y: 0, z: 0 }); + + var i = bl.x; + var j = bl.y; + while (i < tr.x) { + while (j < tr.y) { + drawARect( + i, + j, + squareSize.x, + computPositonGradient(i, j, tl, tr, bl, br), + ); + j = j + squareSize.x; + } + j = bl.y; + i = i + squareSize.x; + } +}; -var drawMachineBelts = function() { - console.log("Draw belts"); - - const tl = projection({x: tlX - trX/2, y: tlY/2, z: 0}); - const tr = projection({x: trX/2, y: trY/2, z: 0}); - const bl = projection({x: blX - brX/2, y: blY - tlY/2, z: 0}); - const br = projection({x: brX/2, y: brY - trY/2, z: 0}); - - tpBbox.min.x = Math.min(tpBbox.min.x, bl.x); - tpBbox.min.y = Math.min(tpBbox.min.y, bl.y); - tpBbox.max.x = Math.max(tpBbox.max.x, tr.x); - tpBbox.max.y = Math.max(tpBbox.max.y, tr.y); - - tp.beginPath(); - tp.strokeStyle = "grey"; - tp.moveTo(0, 0); - tp.lineTo(tl.x, tl.y); - tp.moveTo(0, 0); - tp.lineTo(tr.x, tr.y); - tp.moveTo(0, 0); - tp.lineTo(bl.x, bl.y); - tp.moveTo(0, 0); - tp.lineTo(br.x, br.y); - tp.stroke(); - - tp.fillStyle = "black"; - tp.beginPath(); - tp.arc(tl.x, tl.y, 10, 0, 2 * Math.PI); - tp.closePath(); - tp.fill(); - tp.beginPath(); - tp.arc(tr.x, tr.y, 10, 0, 2 * Math.PI); - tp.closePath(); - tp.fill(); - tp.beginPath(); - tp.arc(br.x, br.y, 10, 0, 2 * Math.PI); - tp.closePath(); - tp.fill(); - tp.beginPath(); - tp.arc(bl.x, bl.y, 10, 0, 2 * Math.PI); - tp.closePath(); - tp.fill(); - - - const squareSize = projection({x: 50, y: 0, z: 0}); - - - var i = bl.x; - var j = bl.y; - while(i < tr.x){ - while(j < tr.y){ - drawARect(i,j,squareSize.x, computPositonGradient(i, j, tl, tr, bl, br)); - j = j + squareSize.x; - } - j = bl.y; - i = i + squareSize.x; - } -} +var checkMinBeltLength = function (x1, y1, x2, y2) { + const dist = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + if (dist < 1200) { + return 1 - dist / 1200; + } else { + return 0; + } +}; -var checkMinBeltLength = function(x1, y1, x2, y2){ - const dist = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); - if(dist < 1200){ - return 1 - dist/1200; - } - else{ - return 0; - } -} +var computPositonGradient = function (x, y, tl, tr, bl, br) { + var opacity = 0; -var computPositonGradient = function(x,y, tl, tr, bl, br){ - var opacity = 0; - - //Check distance from the mounting points - opacity = opacity + checkMinBeltLength(x,y,tl.x, tl.y); - opacity = opacity + checkMinBeltLength(x,y,tr.x, tr.y); - opacity = opacity + checkMinBeltLength(x,y,bl.x, bl.y); - opacity = opacity + checkMinBeltLength(x,y,br.x, br.y); + //Check distance from the mounting points + opacity = opacity + checkMinBeltLength(x, y, tl.x, tl.y); + opacity = opacity + checkMinBeltLength(x, y, tr.x, tr.y); + opacity = opacity + checkMinBeltLength(x, y, bl.x, bl.y); + opacity = opacity + checkMinBeltLength(x, y, br.x, br.y); - opacity = Math.max(opacity, computeTension(x,y, tl, tr, bl, br)); + opacity = Math.max(opacity, computeTension(x, y, tl, tr, bl, br)); - return opacity; -} + return opacity; +}; -var computeTension = function(x,y, tl, tr, bl, br){ - const A = Math.atan((y-tl.y)/(tr.x - x)); - const B = Math.atan((y-tl.y)/(x-tl.x)); +var computeTension = function (x, y, tl, tr, bl, br) { + const A = Math.atan((y - tl.y) / (tr.x - x)); + const B = Math.atan((y - tl.y) / (x - tl.x)); - const T1 = 1 / (Math.cos(A) * Math.sin(B) / Math.cos(B) + Math.sin(A)); - const T2 = 1 / (Math.cos(B) * Math.sin(A) / Math.cos(A) + Math.sin(B)); + const T1 = 1 / ((Math.cos(A) * Math.sin(B)) / Math.cos(B) + Math.sin(A)); + const T2 = 1 / ((Math.cos(B) * Math.sin(A)) / Math.cos(A) + Math.sin(B)); - const T1Scaled = T1/-3; - const T2Scaled = T2/-3; //This is some arbitrary scaling to make it look right in terms of color + const T1Scaled = T1 / -3; + const T2Scaled = T2 / -3; //This is some arbitrary scaling to make it look right in terms of color - const max = Math.max(T1Scaled, T2Scaled); + const max = Math.max(T1Scaled, T2Scaled); - if(max > .15){ - return max; - } - else{ - return 0; - } -} + if (max > 0.15) { + return max; + } else { + return 0; + } +}; // License: MIT - https://opensource.org/licenses/MIT // Author: Michele Locati // Source: https://gist.github.com/mlocati/7210513 function perc2color(perc) { - console.log(perc); - var r, g, b = 0; - if(perc < 50) { - r = 255; - g = Math.round(5.1 * perc); - } - else { - g = 255; - r = Math.round(510 - 5.10 * perc); - } - var h = r * 0x10000 + g * 0x100 + b * 0x1; - - console.log(r + " " + g + " " + b) - return "rgba("+r+", "+g+", "+b+", .3)";//'#' + ('000000' + h.toString(16)).slice(-6); -} - -var drawARect = function(x,y,size, opacity){ + console.log(perc); + var r, + g, + b = 0; + if (perc < 50) { + r = 255; + g = Math.round(5.1 * perc); + } else { + g = 255; + r = Math.round(510 - 5.1 * perc); + } + var h = r * 0x10000 + g * 0x100 + b * 0x1; - const posP = projection({x: x - size/2, y: y - size/2, z: 0}); - tp.beginPath(); - tp.fillStyle = perc2color(100 - 100*opacity);//"rgba(255, 0, 0, " + opacity + ")"; - tp.rect(posP.x, posP.y, size, size); - tp.fill(); + console.log(r + " " + g + " " + b); + return "rgba(" + r + ", " + g + ", " + b + ", .3)"; //'#' + ('000000' + h.toString(16)).slice(-6); } +var drawARect = function (x, y, size, opacity) { + const posP = projection({ x: x - size / 2, y: y - size / 2, z: 0 }); + tp.beginPath(); + tp.fillStyle = perc2color(100 - 100 * opacity); //"rgba(255, 0, 0, " + opacity + ")"; + tp.rect(posP.x, posP.y, size, size); + tp.fill(); +}; + var xOffset = 0; var yOffset = 0; var scaler = 1; -var xToPixel = function(x) { return scaler * x + xOffset; } -var yToPixel = function(y) { return -scaler * y + yOffset; } +var xToPixel = function (x) { + return scaler * x + xOffset; +}; +var yToPixel = function (y) { + return -scaler * y + yOffset; +}; -var clearCanvas = function() { - // Reset the transform and clear the canvas - tp.setTransform(1,0,0,1,0,0); +var clearCanvas = function () { + // Reset the transform and clear the tp_canvas + tp.setTransform(1, 0, 0, 1, 0, 0); -// if (tpRect == undefined) { - var tpRect = canvas.parentNode.getBoundingClientRect(); - // canvas.width = tpRect.width ? tpRect.width : 400; - // canvas.height = tpRect.height ? tpRect.height : 400; -// } + // if (tpRect == undefined) { + var tpRect = tp_canvas.parentNode.getBoundingClientRect(); + // tp_canvas.width = tpRect.width ? tpRect.width : 400; + // tp_canvas.height = tpRect.height ? tpRect.height : 400; + // } - tp.fillStyle = "white"; - tp.fillRect(0, 0, canvas.width, canvas.height); -} + tp.fillStyle = "white"; + tp.fillRect(0, 0, tp_canvas.width, tp_canvas.height); +}; -var transformCanvas = function() { - toolSave = null; - - clearCanvas(); - - var imageWidth; - var imageHeight; - var inset; - if (!bboxIsSet) { - // imageWidth = canvas.width; - // imageHeight = canvas.height; - inset = 0; - scaler = 1; - xOffset = 0; - yOffset = 0; - return; - } - - var imageWidth = tpBbox.max.x - tpBbox.min.x; - var imageHeight = tpBbox.max.y - tpBbox.min.y; - if (imageWidth == 0) { - imageWidth = 1; - } - if (imageHeight == 0) { - imageHeight = 1; - } - var shrink = 0.90; - inset = 5; - var scaleX = (canvas.width - inset*2) / imageWidth; - var scaleY = (canvas.height - inset*2) / imageHeight; - var minScale = Math.min(scaleX, scaleY); - - scaler = minScale * shrink; - if (scaler < 0) { - scaler = -scaler; - } - xOffset = inset - tpBbox.min.x * scaler; - yOffset = (canvas.height-inset) - tpBbox.min.y * (-scaler); - - // Canvas coordinates of image bounding box top and right - var imageTop = scaler * imageHeight; - var imageRight = scaler * imageWidth; - - // Show the X and Y limit coordinates of the GCode program. - // We do this before scaling because after we invert the Y coordinate, - // text would be displayed upside-down. - // tp.fillStyle = "black"; - // tp.font = "14px Ariel"; - // tp.textAlign = "center"; - // tp.textBaseline = "bottom"; - // tp.fillText(formatLimit(tpBbox.min.y), imageRight/2, canvas.height-inset); - // tp.textBaseline = "top"; - // tp.fillText(formatLimit(tpBbox.max.y), imageRight/2, canvas.height-inset - imageTop); - // tp.textAlign = "left"; - // tp.textBaseline = "center"; - // tp.fillText(formatLimit(tpBbox.min.x), inset, canvas.height-inset - imageTop/2); - // tp.textAlign = "right"; - // tp.textBaseline = "center"; - // tp.fillText(formatLimit(tpBbox.max.x), inset+imageRight, canvas.height-inset - imageTop/2); - // Transform the path coordinate system so the image fills the canvas - // with a small inset, and +Y goes upward. - // The net transform from image space (x,y) to pixel space (x',y') is: - // x' = scaler*x + xOffset - // y' = -scaler*y + yOffset - // We use setTransform() instead of a sequence of scale() and translate() calls - // because we need to perform the transform manually for getImageData(), which - // uses pixel coordinates, and there is no standard way to read back the current - // transform matrix. - - tp.setTransform(scaler, 0, 0, -scaler, xOffset, yOffset); - - tp.lineWidth = 0.5 / scaler; - - drawOrigin(imageWidth * 0.04); -} -var wrappedDegrees = function(radians) { - var degrees = radians * 180 / Math.PI; - return degrees >= 0 ? degrees : degrees + 360; -} +var transformCanvas = function () { + toolSave = null; + + clearCanvas(); + + var imageWidth; + var imageHeight; + var inset; + if (!bboxIsSet) { + // imageWidth = tp_canvas.width; + // imageHeight = tp_canvas.height; + inset = 0; + scaler = 1; + xOffset = 0; + yOffset = 0; + return; + } + + var imageWidth = tpBbox.max.x - tpBbox.min.x; + var imageHeight = tpBbox.max.y - tpBbox.min.y; + if (imageWidth == 0) { + imageWidth = 1; + } + if (imageHeight == 0) { + imageHeight = 1; + } + var shrink = 0.9; + inset = 5; + var scaleX = (tp_canvas.width - inset * 2) / imageWidth; + var scaleY = (tp_canvas.height - inset * 2) / imageHeight; + var minScale = Math.min(scaleX, scaleY); + + scaler = minScale * shrink; + if (scaler < 0) { + scaler = -scaler; + } + xOffset = inset - tpBbox.min.x * scaler; + yOffset = tp_canvas.height - inset - tpBbox.min.y * -scaler; + + // Canvas coordinates of image bounding box top and right + var imageTop = scaler * imageHeight; + var imageRight = scaler * imageWidth; + + // Show the X and Y limit coordinates of the GCode program. + // We do this before scaling because after we invert the Y coordinate, + // text would be displayed upside-down. + // tp.fillStyle = "black"; + // tp.font = "14px Ariel"; + // tp.textAlign = "center"; + // tp.textBaseline = "bottom"; + // tp.fillText(formatLimit(tpBbox.min.y), imageRight/2, tp_canvas.height-inset); + // tp.textBaseline = "top"; + // tp.fillText(formatLimit(tpBbox.max.y), imageRight/2, tp_canvas.height-inset - imageTop); + // tp.textAlign = "left"; + // tp.textBaseline = "center"; + // tp.fillText(formatLimit(tpBbox.min.x), inset, tp_canvas.height-inset - imageTop/2); + // tp.textAlign = "right"; + // tp.textBaseline = "center"; + // tp.fillText(formatLimit(tpBbox.max.x), inset+imageRight, tp_canvas.height-inset - imageTop/2); + // Transform the path coordinate system so the image fills the tp_canvas + // with a small inset, and +Y goes upward. + // The net transform from image space (x,y) to pixel space (x',y') is: + // x' = scaler*x + xOffset + // y' = -scaler*y + yOffset + // We use setTransform() instead of a sequence of scale() and translate() calls + // because we need to perform the transform manually for getImageData(), which + // uses pixel coordinates, and there is no standard way to read back the current + // transform matrix. + + tp.setTransform(scaler, 0, 0, -scaler, xOffset, yOffset); + + tp.lineWidth = 0.5 / scaler; + + drawOrigin(imageWidth * 0.04); +}; +var wrappedDegrees = function (radians) { + var degrees = (radians * 180) / Math.PI; + return degrees >= 0 ? degrees : degrees + 360; +}; var bboxHandlers = { - addLine: function(modal, start, end) { - // Update tpUnits in case it changed in a previous line - tpUnits = modal.units; - - ps = projection(start); - pe = projection(end); - - tpBbox.min.x = Math.min(tpBbox.min.x, ps.x, pe.x); - tpBbox.min.y = Math.min(tpBbox.min.y, ps.y, pe.y); - tpBbox.max.x = Math.max(tpBbox.max.x, ps.x, pe.x); - tpBbox.max.y = Math.max(tpBbox.max.y, ps.y, pe.y); - bboxIsSet = true; - }, - addArcCurve: function(modal, start, end, center, extraRotations) { - // To determine the precise bounding box of a circular arc we - // must account for the possibility that the arc crosses one or - // more axes. If so, the bounding box includes the "bulges" of - // the arc across those axes. - - // Update units in case it changed in a previous line - tpUnits = modal.units; - - if (modal.motion == 'G2') { // clockwise - var tmp = start; - start = end; - end = tmp; - } - - ps = projection(start); - pc = projection(center); - pe = projection(end); - - // Coordinates relative to the center of the arc - var sx = ps.x - pc.x; - var sy = ps.y - pc.y; - var ex = pe.x - pc.x; - var ey = pe.y - pc.y; - - var radius = Math.hypot(sx, sy); - - // Axis crossings - plus and minus x and y - var px = false; - var py = false; - var mx = false; - var my = false; - - // There are ways to express this decision tree in fewer lines - // of code by converting to alternate representations like angles, - // but this way is probably the most computationally efficient. - // It avoids any use of transcendental functions. Every path - // through this decision tree is either 4 or 5 simple comparisons. - if (ey >= 0) { // End in upper half plane - if (ex > 0) { // End in quadrant 0 - X+ Y+ - if (sy >= 0) { // Start in upper half plane - if (sx > 0) { // Start in quadrant 0 - X+ Y+ - if (sx <= ex) { // wraparound - px = py = mx = my = true; - } - } else { // Start in quadrant 1 - X- Y+ - mx = my = px = true; - } - } else { // Start in lower half plane - if (sx > 0) { // Start in quadrant 3 - X+ Y- - px = true; - } else { // Start in quadrant 2 - X- Y- - my = px = true; - } - } - } else { // End in quadrant 1 - X- Y+ - if (sy >= 0) { // Start in upper half plane - if (sx > 0) { // Start in quadrant 0 - X+ Y+ - py = true; - } else { // Start in quadrant 1 - X- Y+ - if (sx <= ex) { // wraparound - px = py = mx = my = true; - } - } - } else { // Start in lower half plane - if (sx > 0) { // Start in quadrant 3 - X+ Y- - px = py = true; - } else { // Start in quadrant 2 - X- Y- - my = px = py = true; - } + addLine: function (modal, start, end) { + // Update tpUnits in case it changed in a previous line + tpUnits = modal.units; + + ps = projection(start); + pe = projection(end); + + tpBbox.min.x = Math.min(tpBbox.min.x, ps.x, pe.x); + tpBbox.min.y = Math.min(tpBbox.min.y, ps.y, pe.y); + tpBbox.max.x = Math.max(tpBbox.max.x, ps.x, pe.x); + tpBbox.max.y = Math.max(tpBbox.max.y, ps.y, pe.y); + bboxIsSet = true; + }, + addArcCurve: function (modal, start, end, center, extraRotations) { + // To determine the precise bounding box of a circular arc we + // must account for the possibility that the arc crosses one or + // more axes. If so, the bounding box includes the "bulges" of + // the arc across those axes. + + // Update units in case it changed in a previous line + tpUnits = modal.units; + + if (modal.motion == "G2") { + // clockwise + var tmp = start; + start = end; + end = tmp; } - } - } else { // ey < 0 - end in lower half plane - if (ex > 0) { // End in quadrant 3 - X+ Y+ - if (sy >= 0) { // Start in upper half plane - if (sx > 0) { // Start in quadrant 0 - X+ Y+ - py = mx = my = true; - } else { // Start in quadrant 1 - X- Y+ - mx = my = true; - } - } else { // Start in lower half plane - if (sx > 0) { // Start in quadrant 3 - X+ Y- - if (sx >= ex) { // wraparound - px = py = mx = my = true; + + ps = projection(start); + pc = projection(center); + pe = projection(end); + + // Coordinates relative to the center of the arc + var sx = ps.x - pc.x; + var sy = ps.y - pc.y; + var ex = pe.x - pc.x; + var ey = pe.y - pc.y; + + var radius = Math.hypot(sx, sy); + + // Axis crossings - plus and minus x and y + var px = false; + var py = false; + var mx = false; + var my = false; + + // There are ways to express this decision tree in fewer lines + // of code by converting to alternate representations like angles, + // but this way is probably the most computationally efficient. + // It avoids any use of transcendental functions. Every path + // through this decision tree is either 4 or 5 simple comparisons. + if (ey >= 0) { + // End in upper half plane + if (ex > 0) { + // End in quadrant 0 - X+ Y+ + if (sy >= 0) { + // Start in upper half plane + if (sx > 0) { + // Start in quadrant 0 - X+ Y+ + if (sx <= ex) { + // wraparound + px = py = mx = my = true; + } + } else { + // Start in quadrant 1 - X- Y+ + mx = my = px = true; + } + } else { + // Start in lower half plane + if (sx > 0) { + // Start in quadrant 3 - X+ Y- + px = true; + } else { + // Start in quadrant 2 - X- Y- + my = px = true; + } + } + } else { + // End in quadrant 1 - X- Y+ + if (sy >= 0) { + // Start in upper half plane + if (sx > 0) { + // Start in quadrant 0 - X+ Y+ + py = true; + } else { + // Start in quadrant 1 - X- Y+ + if (sx <= ex) { + // wraparound + px = py = mx = my = true; + } + } + } else { + // Start in lower half plane + if (sx > 0) { + // Start in quadrant 3 - X+ Y- + px = py = true; + } else { + // Start in quadrant 2 - X- Y- + my = px = py = true; + } + } } - } else { // Start in quadrant 2 - X- Y- - my = true; - } - } - } else { // End in quadrant 2 - X- Y+ - if (sy >= 0) { // Start in upper half plane - if (sx > 0) { // Start in quadrant 0 - X+ Y+ - py = mx = true; - } else { // Start in quadrant 1 - X- Y+ - mx = true; - } - } else { // Start in lower half plane - if (sx > 0) { // Start in quadrant 3 - X+ Y- - px = py = mx = true; - } else { // Start in quadrant 2 - X- Y- - if (sx >= ex) { // wraparound - px = py = mx = my = true; + } else { + // ey < 0 - end in lower half plane + if (ex > 0) { + // End in quadrant 3 - X+ Y+ + if (sy >= 0) { + // Start in upper half plane + if (sx > 0) { + // Start in quadrant 0 - X+ Y+ + py = mx = my = true; + } else { + // Start in quadrant 1 - X- Y+ + mx = my = true; + } + } else { + // Start in lower half plane + if (sx > 0) { + // Start in quadrant 3 - X+ Y- + if (sx >= ex) { + // wraparound + px = py = mx = my = true; + } + } else { + // Start in quadrant 2 - X- Y- + my = true; + } + } + } else { + // End in quadrant 2 - X- Y+ + if (sy >= 0) { + // Start in upper half plane + if (sx > 0) { + // Start in quadrant 0 - X+ Y+ + py = mx = true; + } else { + // Start in quadrant 1 - X- Y+ + mx = true; + } + } else { + // Start in lower half plane + if (sx > 0) { + // Start in quadrant 3 - X+ Y- + px = py = mx = true; + } else { + // Start in quadrant 2 - X- Y- + if (sx >= ex) { + // wraparound + px = py = mx = my = true; + } + } + } } - } } - } - } - var maxX = px ? pc.x + radius : Math.max(ps.x, pe.x); - var maxY = py ? pc.y + radius : Math.max(ps.y, pe.y); - var minX = mx ? pc.x - radius : Math.min(ps.x, pe.x); - var minY = my ? pc.y - radius : Math.min(ps.y, pe.y); - - var minZ = Math.min(start.z, end.z); - var maxZ = Math.max(start.z, end.z); - - const p0 = projection({x: minX, y: minY, z: minZ}); - const p1 = projection({x: minX, y: maxY, z: minZ}); - const p2 = projection({x: maxX, y: maxY, z: minZ}); - const p3 = projection({x: maxX, y: minY, z: minZ}); - const p4 = projection({x: minX, y: minY, z: maxZ}); - const p5 = projection({x: minX, y: maxY, z: maxZ}); - const p6 = projection({x: maxX, y: maxY, z: maxZ}); - const p7 = projection({x: maxX, y: minY, z: maxZ}); - - tpBbox.min.x = Math.min(tpBbox.min.x, p0.x, p1.x, p2.x, p3.x, p4.x, p5.x, p6.x, p7.x); - tpBbox.min.y = Math.min(tpBbox.min.y, p0.y, p1.y, p2.y, p3.y, p4.y, p5.y, p6.y, p7.y); - tpBbox.max.x = Math.max(tpBbox.max.x, p0.x, p1.x, p2.x, p3.x, p4.x, p5.x, p6.x, p7.x); - tpBbox.max.y = Math.max(tpBbox.max.y, p0.y, p1.y, p2.y, p3.y, p4.y, p5.y, p6.y, p7.y); - bboxIsSet = true; - } + var maxX = px ? pc.x + radius : Math.max(ps.x, pe.x); + var maxY = py ? pc.y + radius : Math.max(ps.y, pe.y); + var minX = mx ? pc.x - radius : Math.min(ps.x, pe.x); + var minY = my ? pc.y - radius : Math.min(ps.y, pe.y); + + var minZ = Math.min(start.z, end.z); + var maxZ = Math.max(start.z, end.z); + + const p0 = projection({ x: minX, y: minY, z: minZ }); + const p1 = projection({ x: minX, y: maxY, z: minZ }); + const p2 = projection({ x: maxX, y: maxY, z: minZ }); + const p3 = projection({ x: maxX, y: minY, z: minZ }); + const p4 = projection({ x: minX, y: minY, z: maxZ }); + const p5 = projection({ x: minX, y: maxY, z: maxZ }); + const p6 = projection({ x: maxX, y: maxY, z: maxZ }); + const p7 = projection({ x: maxX, y: minY, z: maxZ }); + + tpBbox.min.x = Math.min( + tpBbox.min.x, + p0.x, + p1.x, + p2.x, + p3.x, + p4.x, + p5.x, + p6.x, + p7.x, + ); + tpBbox.min.y = Math.min( + tpBbox.min.y, + p0.y, + p1.y, + p2.y, + p3.y, + p4.y, + p5.y, + p6.y, + p7.y, + ); + tpBbox.max.x = Math.max( + tpBbox.max.x, + p0.x, + p1.x, + p2.x, + p3.x, + p4.x, + p5.x, + p6.x, + p7.x, + ); + tpBbox.max.y = Math.max( + tpBbox.max.y, + p0.y, + p1.y, + p2.y, + p3.y, + p4.y, + p5.y, + p6.y, + p7.y, + ); + bboxIsSet = true; + }, }; var initialMoves = true; var displayHandlers = { - addLine: function(modal, start, end) { - var motion = modal.motion; - if (motion == 'G0') { - tp.strokeStyle = initialMoves ? 'red' : 'green'; - } else { - tp.strokeStyle = 'black'; - // Don't cancel initialMoves on no-motion G1 (e.g. G1 F30) - // or on Z-only moves - if (start.x != end.x || start.y != end.y) { - initialMoves = false; - } - } - - ps = projection(start); - pe = projection(end); - tp.beginPath(); - // tp.moveTo(start.x, start.y); - // tp.lineTo(end.x, end.y); - tp.moveTo(ps.x, ps.y); - tp.lineTo(pe.x, pe.y); - tp.stroke(); - }, - addArcCurve: function(modal, start, end, center, extraRotations) { - var motion = modal.motion; - - var deltaX1 = start.x - center.x; - var deltaY1 = start.y - center.y; - var radius = Math.hypot(deltaX1, deltaY1); - var deltaX2 = end.x - center.x; - var deltaY2 = end.y - center.y; - var theta1 = Math.atan2(deltaY1, deltaX1); - var theta2 = Math.atan2(deltaY2, deltaX2); - var cw = modal.motion == "G2"; - if (!cw && theta2 < theta1) { - theta2 += Math.PI * 2; - } else if (cw && theta2 > theta1) { - theta2 -= Math.PI * 2; - } - if (theta1 == theta2) { - theta2 += Math.PI * ((cw) ? -2 : 2); - } - if (extraRotations > 1) { - theta2 += (extraRotations-1) * Math.PI * ((cw) ? -2 : 2);; - } - - initialMoves = false; - - tp.beginPath(); - tp.strokeStyle = 'black'; - deltaTheta = theta2 - theta1; - n = 10 * Math.ceil(Math.abs(deltaTheta) / Math.PI); - dt = (deltaTheta) / n; - dz = (end.z - start.z) / n; - ps = projection(start); - tp.moveTo(ps.x, ps.y); - next = {}; - theta = theta1; - next.z = start.z; - for (i = 0; i < n; i++) { - theta += dt; - next.x = center.x + radius * Math.cos(theta); - next.y = center.y + radius * Math.sin(theta); - next.z += dz; - pe = projection(next) - tp.lineTo(pe.x, pe.y); - } - tp.stroke(); - }, -}; + addLine: function (modal, start, end) { + var motion = modal.motion; + if (motion == "G0") { + tp.strokeStyle = initialMoves ? "red" : "green"; + } else { + tp.strokeStyle = "black"; + // Don't cancel initialMoves on no-motion G1 (e.g. G1 F30) + // or on Z-only moves + if (start.x != end.x || start.y != end.y) { + initialMoves = false; + } + } -var ToolpathDisplayer = function() { + ps = projection(start); + pe = projection(end); + tp.beginPath(); + // tp.moveTo(start.x, start.y); + // tp.lineTo(end.x, end.y); + tp.moveTo(ps.x, ps.y); + tp.lineTo(pe.x, pe.y); + tp.stroke(); + }, + addArcCurve: function (modal, start, end, center, extraRotations) { + var motion = modal.motion; + + var deltaX1 = start.x - center.x; + var deltaY1 = start.y - center.y; + var radius = Math.hypot(deltaX1, deltaY1); + var deltaX2 = end.x - center.x; + var deltaY2 = end.y - center.y; + var theta1 = Math.atan2(deltaY1, deltaX1); + var theta2 = Math.atan2(deltaY2, deltaX2); + var cw = modal.motion == "G2"; + if (!cw && theta2 < theta1) { + theta2 += Math.PI * 2; + } else if (cw && theta2 > theta1) { + theta2 -= Math.PI * 2; + } + if (theta1 == theta2) { + theta2 += Math.PI * (cw ? -2 : 2); + } + if (extraRotations > 1) { + theta2 += (extraRotations - 1) * Math.PI * (cw ? -2 : 2); + } + + initialMoves = false; + + tp.beginPath(); + tp.strokeStyle = "black"; + deltaTheta = theta2 - theta1; + n = 10 * Math.ceil(Math.abs(deltaTheta) / Math.PI); + dt = deltaTheta / n; + dz = (end.z - start.z) / n; + ps = projection(start); + tp.moveTo(ps.x, ps.y); + next = {}; + theta = theta1; + next.z = start.z; + for (i = 0; i < n; i++) { + theta += dt; + next.x = center.x + radius * Math.cos(theta); + next.y = center.y + radius * Math.sin(theta); + next.z += dz; + pe = projection(next); + tp.lineTo(pe.x, pe.y); + } + tp.stroke(); + }, }; +var ToolpathDisplayer = function () {}; + // var offset; -ToolpathDisplayer.prototype.clear = function() { - clearCanvas(); -} +ToolpathDisplayer.prototype.clear = function () { + clearCanvas(); +}; + +ToolpathDisplayer.prototype.showToolpath = function ( + gcode, + modal, + initialPosition, +) { + cameraAngle = cameraAngle; + + var drawBounds = false; + var drawBelts = false; + + switch (cameraAngle) { + case 0: + obliqueView(); + break; + case 1: + obliqueView(); + drawBounds = true; + break; + case 2: + topView(); + break; + case 3: + topView(); + drawBounds = true; + break; + case 4: + topView(); + drawBounds = true; + drawBelts = true; + break; + default: + obliqueView(); + } + + resetBbox(); + bboxHandlers.position = initialPosition; + bboxHandlers.modal = modal; + + if (drawBounds) { + drawMachineBounds(); //Adds the machine bounds to the bounding box...this does not draw + } + if (drawBelts) { + drawMachineBelts(); //Adds the belts to the bounding box...does not draw yet + } + + var gcodeLines = gcode.split("\n"); + new Toolpath(bboxHandlers).loadFromLinesSync(gcodeLines); + transformCanvas(); + if (!bboxIsSet) { + return; + } + initialMoves = true; + displayHandlers.position = initialPosition; + const common = new Common(); + displayHandlers.modal = common.modal; + new Toolpath(displayHandlers).loadFromLinesSync(gcodeLines); + + drawTool(initialPosition); + + if (drawBounds) { + drawMachineBounds(); //Actually draws the bounding box + } + if (drawBelts) { + drawMachineBelts(); //Actually draws the belts + } +}; -ToolpathDisplayer.prototype.showToolpath = function(gcode, modal, initialPosition) { - cameraAngle = cameraAngle; - - var drawBounds = false; - var drawBelts = false; - - switch (cameraAngle) { - case 0: - obliqueView(); - break; - case 1: - obliqueView(); - drawBounds = true; - break; - case 2: - topView(); - break; - case 3: - topView(); - drawBounds = true; - break; - case 4: - topView(); - drawBounds = true; - drawBelts = true; - break; - default: - obliqueView(); - } - - resetBbox(); - bboxHandlers.position = initialPosition; - bboxHandlers.modal = modal; - - if(drawBounds){ - drawMachineBounds(); //Adds the machine bounds to the bounding box...this does not draw - } - if(drawBelts){ - drawMachineBelts(); //Adds the belts to the bounding box...does not draw yet - } - - var gcodeLines = gcode.split('\n'); - new Toolpath(bboxHandlers).loadFromLinesSync(gcodeLines); - transformCanvas(); - if (!bboxIsSet) { - return; - } - initialMoves = true; - displayHandlers.position = initialPosition; - displayHandlers.modal = modal; - new Toolpath(displayHandlers).loadFromLinesSync(gcodeLines); - - drawTool(initialPosition); - - if(drawBounds){ - drawMachineBounds(); //Actually draws the bounding box - } - if(drawBelts){ - drawMachineBelts(); //Actually draws the belts - } +ToolpathDisplayer.prototype.reDrawTool = function (modal, dpos) { + if (toolSave != null) { + tp.putImageData(toolSave, toolX, toolY); + drawTool(dpos); + } }; -ToolpathDisplayer.prototype.reDrawTool = function(modal, dpos) { - if (toolSave != null) { - tp.putImageData(toolSave, toolX, toolY); - drawTool(dpos); - } -} +const displayer = new ToolpathDisplayer(); -displayer = new ToolpathDisplayer(); +ToolpathDisplayer.prototype.cycleCameraAngle = function (gcode, position) { + cameraAngle = cameraAngle + 1; + if (cameraAngle > 4) { + cameraAngle = 0; + } -ToolpathDisplayer.prototype.cycleCameraAngle = function(gcode, modal, position) { - cameraAngle = cameraAngle + 1; - if(cameraAngle > 4){ - cameraAngle = 0; - } + const common = new Common(); + displayer.showToolpath(gcode, common.modal, position); +}; - displayer.showToolpath(gcode, modal, position); -} +/** Expects a simple array with 3 elements, and converts it to an xyz object */ +const arrayToXYZ = (arr) => { + return { x: arr[0], y: arr[1], z: arr[2] }; +}; -canvas.addEventListener("mouseup", updateGcodeViewerAngle); -var refreshGcode = function() { - const gcode = id('gcode').value; - displayer.showToolpath(gcode, WPOS, MPOS, cameraAngle); -} +const updateGcodeViewerAngle = () => { + const gcode = id("tablettab_gcode").value; + displayer.cycleCameraAngle(gcode, arrayToXYZ(WPOS())); +}; + +tp_canvas.addEventListener("mouseup", updateGcodeViewerAngle); +const refreshGcode = () => { + const gcode = getValue("tablettab_gcode"); + // This call has one too many parameters, no idea how it was supposed to work + displayer.showToolpath(gcode, WPOS(), MPOS(), cameraAngle); +}; + +// id("small-toolpath").addEventListener("mouseup", updateGcodeViewerAngle); -// id("small-toolpath").addEventListener("mouseup", updateGcodeViewerAngle); +export { arrayToXYZ, displayer, refreshGcode }; diff --git a/www/js/translate.js b/www/js/translate.js index 1a01ea29f..284aa8d0d 100644 --- a/www/js/translate.js +++ b/www/js/translate.js @@ -1,129 +1,37 @@ -var language = 'en'; - - -var language_list = [ -//removeIf(de_lang_disabled) - ['de', 'Deutsch', 'germantrans'], -//endRemoveIf(de_lang_disabled) -//removeIf(en_lang_disabled) - ['en', 'English', 'englishtrans'], -//endRemoveIf(en_lang_disabled) -//removeIf(es_lang_disabled) - ['es', 'Español', 'spanishtrans'], -//endRemoveIf(es_lang_disabled) -//removeIf(fr_lang_disabled) - ['fr', 'Français', 'frenchtrans'], -//endRemoveIf(fr_lang_disabled) -//removeIf(it_lang_disabled) - ['it', 'Italiano', 'italiantrans'], -//endRemoveIf(it_lang_disabled) -//removeIf(ja_lang_disabled) - ['ja', '日本語', 'japanesetrans'], -//endRemoveIf(ja_lang_disabled) -//removeIf(hu_lang_disabled) - ['hu', 'Magyar', 'hungariantrans'], -//endRemoveIf(hu_lang_disabled) -//removeIf(pl_lang_disabled) - ['pl', 'Polski', 'polishtrans'], -//endRemoveIf(pl_lang_disabled) -//removeIf(ptbr_lang_disabled) - ['ptbr', 'Português-Br', 'ptbrtrans'], -//endRemoveIf(ptbr_lang_disabled) -//removeIf(ru_lang_disabled) - ['ru', 'Русский', 'russiantrans'], -//endRemoveIf(ru_lang_disabled) -//removeIf(tr_lang_disabled) - ['tr', 'Türkçe', 'turkishtrans'], -//endRemoveIf(tr_lang_disabled) -//removeIf(uk_lang_disabled) - ['uk', 'Українська', 'ukrtrans'], -//endRemoveIf(uk_lang_disabled) -//removeIf(zh_cn_lang_disabled) - ['zh_CN', '简体中文', 'zh_CN_trans'], -//endRemoveIf(zh_cn_lang_disabled) -]; - -//removeIf(production) -var translated_list = []; -//endRemoveIf(production) - -function build_language_list(id_item) { - var content = "\n"; - return content; -} - -function translate_text(lang) { - var currenttrans = {}; - var translated_content = ""; - language = lang; - for (var lang_i = 0; lang_i < language_list.length; lang_i++) { - if (language_list[lang_i][0] == lang) { - currenttrans = eval(language_list[lang_i][2]); - } - } - var All = document.getElementsByTagName('*'); - for (var i = 0; i < All.length; i++) { - if (All[i].hasAttribute('translate')) { - var content = ""; - if (!All[i].hasAttribute('english_content')) { - content = All[i].innerHTML; - content.trim(); - All[i].setAttribute('english_content', content); - //removeIf(production) - var item = { - content: content - }; - translated_list.push(item); - //endRemoveIf(production) - } - content = All[i].getAttribute('english_content'); - translated_content = translate_text_item(content); - - All[i].innerHTML = translated_content; - } - //add support for placeholder attribut - if (All[i].hasAttribute('translateph') && All[i].hasAttribute('placeholder')) { - let content = ""; - if (!All[i].hasAttribute('english_content')) { - content = All[i].getAttribute('placeholder'); - content.trim(); - //removeIf(production) - const item = { - content: content - }; - translated_list.push(item); - //endRemoveIf(production) - All[i].setAttribute('english_content', content); - } - content = All[i].getAttribute('english_content'); - - translated_content = HTMLDecode(translate_text_item(content)); - All[i].setAttribute('placeholder', translated_content) - } - } +import { translate_text_item, HTMLDecode } from "./common.js"; + +/** Set up text translation to the selected language */ +const translate_text = (lang) => { + let translated_content = ""; + + const All = document.getElementsByTagName("*"); + for (let i = 0; i < All.length; i++) { + if (All[i].hasAttribute("translate")) { + let content = ""; + if (!All[i].hasAttribute("english_content")) { + content = All[i].innerHTML; + content.trim(); + All[i].setAttribute("english_content", content); + } + content = All[i].getAttribute("english_content"); + translated_content = translate_text_item(content); + + All[i].innerHTML = translated_content; + } + //add support for placeholder attribute + if (All[i].hasAttribute('translateph') && All[i].hasAttribute('placeholder')) { + let content = ""; + if (!All[i].hasAttribute("english_content")) { + content = All[i].getAttribute("placeholder"); + content.trim(); + All[i].setAttribute("english_content", content); + } + content = All[i].getAttribute("english_content"); + + translated_content = HTMLDecode(translate_text_item(content)); + All[i].setAttribute("placeholder", translated_content); + } + } }; -function translate_text_item(item_text, withtag) { - var currenttrans = {}; - var translated_content; - var with_tag = false; - if (typeof withtag != "undefined") with_tag = withtag; - for (var lang_i = 0; lang_i < language_list.length; lang_i++) { - if (language_list[lang_i][0] == language) { - currenttrans = eval(language_list[lang_i][2]); - } - } - translated_content = currenttrans[item_text]; - if (typeof translated_content === 'undefined') translated_content = item_text; - if (with_tag) { - var translated_content_tmp = "" + translated_content + ""; - translated_content = translated_content_tmp; - } - return translated_content; -} +export { translate_text }; diff --git a/www/js/updatedlg.js b/www/js/updatedlg.js index 903c0dd0e..311bc4dca 100644 --- a/www/js/updatedlg.js +++ b/www/js/updatedlg.js @@ -1,4 +1,21 @@ -// import - conErr, stdErrMsg, id, displayBlock, displayNone, setValue, setHTML, closeModal, setactiveModal, showModal, alertdlg, confirmdlg, SendFileHttp, SendGetHttp, translate_text_item +import { + Common, + conErr, + stdErrMsg, + id, + displayBlock, + displayNone, + setValue, + setHTML, + closeModal, + setactiveModal, + showModal, + alertdlg, + confirmdlg, + SendFileHttp, + SendGetHttp, + translate_text_item, +} from "./common.js"; let update_ongoing = false; let current_update_filename = ""; @@ -75,11 +92,9 @@ function StartUploadUpdatefile(response) { if (response !== "yes") { return; } - if (http_communication_locked) { - alertdlg( - translate_text_item("Busy..."), - translate_text_item("Communications are currently locked, please wait and retry."), - ); + const common = new Common(); + if (common.http_communication_locked) { + alertdlg(translate_text_item("Busy..."), translate_text_item("Communications are currently locked, please wait and retry.")); return; } const files = id("fw-select").files; @@ -134,10 +149,11 @@ function updatefailed(error_code, response) { displayNone("uploadfw-button"); //setHTML('updatemsg', ""); id("fw-select").value = ""; - if (esp_error_code !== 0) { - alertdlg(translate_text_item("Error"), stdErrMsg(`(${esp_error_code})`, esp_error_message)); - setHTML("updatemsg", translate_text_item("Upload failed : ") + esp_error_message); - esp_error_code = 0; + const common = new Common(); + if (common.esp_error_code !== 0) { + alertdlg(translate_text_item("Error"), stdErrMsg(`(${common.esp_error_code})`, common.esp_error_message)); + setHTML("updatemsg", translate_text_item("Upload failed : ") + common.esp_error_message); + common.esp_error_code = 0; } else { alertdlg(translate_text_item("Error"), stdErrMsg(error_code, response)); setHTML("updatemsg", stdErrMsg(error_code, response, translate_text_item("Upload failed"))); @@ -146,3 +162,5 @@ function updatefailed(error_code, response) { update_ongoing = false; SendGetHttp("/updatefw"); } + +export { updatedlg }; diff --git a/www/js/util.js b/www/js/util.js index 075db0fb4..8c27eedc0 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -73,7 +73,6 @@ function displayInitial(name) { function displayUndoNone(name) { setDisplay(name, '') } - /** Set the disabled value for the elements matching the selector */ function setDisabled(selector, value) { for ((element) of document.querySelectorAll(selector)) { @@ -110,8 +109,8 @@ const stdErrMsg = (error_code, response = "", error_prefix = "Error") => `${erro */ const conErr = (error_code, response, error_prefix = "Error") => { const errMsg = (!response && error_prefix === "Error") - ? error_code - : stdErrMsg(error_code, response || "", error_prefix); + ? error_code + : stdErrMsg(error_code, response || "", error_prefix); console.error(errMsg); } @@ -139,4 +138,18 @@ const HTMLDecode = (value) => { value = tmpelement.textContent; tmpelement.textContent = ''; return value; -} \ No newline at end of file +} + +export { + classes, + conErr, + stdErrMsg, + disable_items, + displayBlock, displayFlex, displayInline, displayNone, displayUndoNone, + getChecked, setChecked, + getValue, setValue, + setHTML, + setDisabled, + HTMLEncode, HTMLDecode, + id +}; diff --git a/www/js/utilValidation.js b/www/js/utilValidation.js index 95123a4e9..b57f68277 100644 --- a/www/js/utilValidation.js +++ b/www/js/utilValidation.js @@ -50,8 +50,8 @@ const valueIsInt = (value, valueDef) => { const valueIsFloat = (value, valueDef) => { const errorList = []; const vFloat = Number.parseFloat(value); - if (Number.isNaN(vFloat)) { - errorList.push(`'${valueDef.label}' ${translate_text_item("must be a float")}`); + if (Number.isNaN(vInt)) { + errorList.push(`'${valueDef.label}' ${translate_text_item("must be an float")}`); } else { errorList.push(valueMinTest(vFloat, valueDef)); errorList.push(valueMaxTest(vFloat, valueDef)); @@ -98,13 +98,14 @@ const checkValue = (value, valueDef, errorList = []) => { case "select": // This is effectively an enum - no specific test for this yet break; - default: + default: { const valueDefError = `'${valueDef.label}' ${translate_text_item("is an unknown value type")} '${valueDef.valueType}'`; console.error(`${valueDefError}: ${JSON.stringify(value)}`); errorList.push(valueDefError); break; + } } return errorList.filter((err) => err); } -// export { checkValue, valueIsFloat }; +export { checkValue, valueIsFloat }; diff --git a/www/js/wizard.js b/www/js/wizard.js index 513b4ef47..22378b7a7 100644 --- a/www/js/wizard.js +++ b/www/js/wizard.js @@ -1,16 +1,27 @@ -var can_revert_wizard = false; +import { Common, classes, displayBlock } from "./common.js"; -function openstep(evt, stepname) { - var i, stepcontent, steplinks; - if (evt.currentTarget.className.indexOf("wizard_done") > -1 && !can_revert_wizard) return; - stepcontent = classes("stepcontent"); - for (i = 0; i < stepcontent.length; i++) { - stepcontent[i].style.display = "none"; - } - steplinks = classes("steplinks"); - for (i = 0; i < steplinks.length; i++) { - steplinks[i].className = steplinks[i].className.replace(" active", ""); - } - displayBlock(stepname); - evt.currentTarget.className += " active"; -} +const openstep = (evt, stepname) => { + let i; + let stepcontent; + let steplinks; + const common = new Common(); + if ( + evt.currentTarget.className.indexOf("wizard_done") > -1 && + !common.can_revert_wizard + ) { + return; + } + + stepcontent = classes("stepcontent"); + for (i = 0; i < stepcontent.length; i++) { + stepcontent[i].style.display = "none"; + } + steplinks = classes("steplinks"); + for (i = 0; i < steplinks.length; i++) { + steplinks[i].className = steplinks[i].className.replace(" active", ""); + } + displayBlock(stepname); + evt.currentTarget.className += " active"; +}; + +export { openstep }; diff --git a/www/sub/cameratab.html b/www/sub/cameratab.html index 44a1f6e44..8d3cbffc5 100644 --- a/www/sub/cameratab.html +++ b/www/sub/cameratab.html @@ -1,73 +1,68 @@ -
-
-
-
-
-
-
@
- -
+
+
+ +
+
+
@
+
- - - - -
- - - - - - -
-
- -
-
- - -        -
-
+
+ + + + +
+ + + + + + +
+
+ +
+
+ + +        +
\ No newline at end of file diff --git a/www/sub/commandpanel.html b/www/sub/commandpanel.html index 60d5f3ce5..af9637b15 100644 --- a/www/sub/commandpanel.html +++ b/www/sub/commandpanel.html @@ -39,4 +39,4 @@
- + \ No newline at end of file diff --git a/www/sub/controlspanel.html b/www/sub/controlspanel.html index 643176aff..1bf9ccb19 100644 --- a/www/sub/controlspanel.html +++ b/www/sub/controlspanel.html @@ -35,7 +35,7 @@
- + Jog Dial control
diff --git a/www/sub/dashtab.html b/www/sub/dashtab.html index c342385dd..9aecc3ed4 100644 --- a/www/sub/dashtab.html +++ b/www/sub/dashtab.html @@ -1,10 +1,11 @@
- - - - + +
+
+
+
-
+
\ No newline at end of file diff --git a/www/sub/logindlg.html b/www/sub/logindlg.html index 9714a0e9c..4d37664cb 100644 --- a/www/sub/logindlg.html +++ b/www/sub/logindlg.html @@ -20,14 +20,12 @@

diff --git a/www/sub/navbar.html b/www/sub/navbar.html index 6a9d666a2..c11522475 100644 --- a/www/sub/navbar.html +++ b/www/sub/navbar.html @@ -12,49 +12,49 @@ diff --git a/www/sub/preferencesdlg.html b/www/sub/preferencesdlg.html index 566d7d0f3..d23133590 100644 --- a/www/sub/preferencesdlg.html +++ b/www/sub/preferencesdlg.html @@ -1,484 +1,14 @@ - - + \ No newline at end of file diff --git a/www/sub/settingstab.html b/www/sub/settingstab.html index 8bf701b27..36d9486aa 100644 --- a/www/sub/settingstab.html +++ b/www/sub/settingstab.html @@ -1,95 +1,103 @@ -
-
-

- FluidNC Settings -

- - -
- Show system status +
+

+ FluidNC Settings +

+ + + - + - + - - - + + + + + +
+ Show system status - - - Manage local files + + + Manage local files - - - Update the firmware wirelessly (OTA) + + + Update the firmware wirelessly (OTA) - - - Restart FluidNC - - - Refresh this list of settings - -
+ Restart FluidNC + + + Refresh this list of settings + +
+
+ +
+
+
+ +
+ Settings that are saved immediately + +
+
+ Settings from the config file, saved only on command + +
+
+ + + + + + + +
LabelValue
-
- -
-
-
- -
- Settings that are saved immediately - -
-
- Settings from the config file, saved only on command - -
-
- - - - - - - - -
LabelValue
-
- -
+
+ \ No newline at end of file diff --git a/www/sub/setupdlg.html b/www/sub/setupdlg.html index a2025b329..12e604f9b 100644 --- a/www/sub/setupdlg.html +++ b/www/sub/setupdlg.html @@ -3,46 +3,56 @@
-
Idle
+
Idle

- - + +
-
-
-
-
- - - -
- - -
-
- - -
-
-
- - - - -
-
- - -
- -
+
+
+
+
+ + + +
+ + +
+
+ + +
+
+
+ + + + +
+
+ + +
+
-
-
-
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+ +
-
- -
-
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
+ +
+
+ +
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
-
- -
-
- -
-
-
+ +
- + +
+
+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+ +
- - - - - +
\ No newline at end of file diff --git a/www/tools/includes.js b/www/tools/includes.js deleted file mode 100644 index 71bb876fa..000000000 --- a/www/tools/includes.js +++ /dev/null @@ -1,148 +0,0 @@ -/* W3Data ver 1.31 by W3Schools.com */ -var w3DataObject = {}; -function w3DisplayData(elementId, data) { - var htmlObj, htmlTemplate, html, arr = [], a, l, rowClone, x, j, i, ii, cc, repeat, repeatObj, repeatX = ""; - htmlObj = id(elementId); - htmlTemplate = w3InitTemplate(elementId, htmlObj); - html = htmlTemplate.cloneNode(true); - arr = w3GetElementsByAttribute(html, "w3-repeat"); - l = arr.length; - for (j = (l - 1); j >= 0; j -= 1) { - cc = arr[j].getAttribute("w3-repeat").split(" "); - if (cc.length == 1) { - repeat = cc[0]; - } else { - repeatX = cc[0]; - repeat = cc[2]; - } - arr[j].removeAttribute("w3-repeat"); - repeatObj = data[repeat]; - if (repeatObj && typeof repeatObj == "object" && repeatObj.length != "undefined") { - i = 0; - for (x in repeatObj) { - i += 1; - rowClone = arr[j]; - rowClone = w3NeedleInHaystack(rowClone, "element", repeatX, repeatObj[x]); - a = rowClone.attributes; - for (ii = 0; ii < a.length; ii += 1) { - a[ii].value = w3NeedleInHaystack(a[ii], "attribute", repeatX, repeatObj[x]).value; - } - (i === repeatObj.length) ? arr[j].parentNode.replaceChild(rowClone, arr[j]) : arr[j].parentNode.insertBefore(rowClone, arr[j]); - } - } else { - console.log("w3-repeat must be an array. " + repeat + " is not an array."); - continue; - } - } - html = w3NeedleInHaystack(html, "element"); - htmlObj.parentNode.replaceChild(html, htmlObj); - function w3InitTemplate(elementId, obj) { - var template; - template = obj.cloneNode(true); - if (w3DataObject.hasOwnProperty(elementId)) {return w3DataObject[elementId];} - w3DataObject[elementId] = template; - return template; - } - function w3GetElementsByAttribute(x, att) { - var arr = [], arrCount = -1, i, l, y = x.getElementsByTagName("*"), z = att.toUpperCase(); - l = y.length; - for (i = -1; i < l; i += 1) { - if (i == -1) {y[i] = x;} - if (y[i].getAttribute(z) !== null) {arrCount += 1; arr[arrCount] = y[i];} - } - return arr; - } - function w3NeedleInHaystack(elmnt, typ, repeatX, x) { - var value, rowClone, pos1, haystack, pos2, needle = [], needleToReplace, i, cc, r; - rowClone = elmnt.cloneNode(true); - pos1 = 0; - while (pos1 > -1) { - haystack = (typ == "attribute") ? rowClone.value : rowClone.innerHTML; - pos1 = haystack.indexOf("{{", pos1); - if (pos1 === -1) {break;} - pos2 = haystack.indexOf("}}", pos1 + 1); - needleToReplace = haystack.substring(pos1 + 2, pos2); - needle = needleToReplace.split("||"); - value = undefined; - for (i = 0; i < needle.length; i += 1) { - needle[i] = needle[i].replace(/^\s+|\s+$/gm, ''); //trim - //value = ((x && x[needle[i]]) || (data && data[needle[i]])); - if (x) {value = x[needle[i]];} - if (value == undefined && data) {value = data[needle[i]];} - if (value == undefined) { - cc = needle[i].split("."); - if (cc[0] == repeatX) {value = x[cc[1]]; } - } - if (value == undefined) { - if (needle[i] == repeatX) {value = x;} - } - if (value == undefined) { - if (needle[i].substr(0, 1) == '"') { - value = needle[i].replace(/"/g, ""); - } else if (needle[i].substr(0,1) == "'") { - value = needle[i].replace(/'/g, ""); - } - } - if (value != undefined) {break;} - } - if (value != undefined) { - r = "{{" + needleToReplace + "}}"; - if (typ == "attribute") { - rowClone.value = rowClone.value.replace(r, value); - } else { - w3ReplaceHTML(rowClone, r, value); - } - } - pos1 = pos1 + 1; - } - return rowClone; - } - function w3ReplaceHTML(a, r, result) { - var b, l, i, a, x, j; - if (a.hasAttributes()) { - b = a.attributes; - l = b.length; - for (i = 0; i < l; i += 1) { - if (b[i].value.indexOf(r) > -1) {b[i].value = b[i].value.replace(r, result);} - } - } - x = a.getElementsByTagName("*"); - l = x.length; - a.innerHTML = a.innerHTML.replace(r, result); - } -} -function w3IncludeHTML() { - var z, i, elmnt, file, xhttp; - z = document.getElementsByTagName("*"); - for (i = 0; i < z.length; i++) { - elmnt = z[i]; - file = elmnt.getAttribute("w3-include-html"); - if (file) { - xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if (this.readyState == 4 && this.status == 200) { - elmnt.innerHTML = this.responseText; - elmnt.removeAttribute("w3-include-html"); - w3IncludeHTML(); - } - } - xhttp.open("GET", file, true); - xhttp.send(); - return; - } - } -} -function w3Http(target, readyfunc, xml, method) { - var httpObj; - if (!method) {method = "GET"; } - if (window.XMLHttpRequest) { - httpObj = new XMLHttpRequest(); - } else if (window.ActiveXObject) { - httpObj = new ActiveXObject("Microsoft.XMLHTTP"); - } - if (httpObj) { - if (readyfunc) {httpObj.onreadystatechange = readyfunc;} - httpObj.open(method, target, true); - httpObj.send(xml); - } -}