Browse Source

Reworking project for inclusion in private root mono-repo

master
Mobius K 3 years ago
parent
commit
3c84f0f48d
  1. 13
      .editorconfig
  2. 5
      .firebaserc
  3. 8
      .gitignore
  4. 14
      .gitlab-ci.yml
  5. 2
      README.md
  6. 198
      assets/en.json
  7. 198
      assets/es.json
  8. 0
      assets/favicon.ico
  9. 31
      firebase.json
  10. 89
      gulpfile.js
  11. 11114
      package-lock.json
  12. 26
      package.json
  13. 21
      routes/index.pug
  14. 68
      scripts/bluetooth.js
  15. 14
      scripts/index.js
  16. 2
      src/assets/robots.txt
  17. 21
      src/routes/index.pug
  18. 68
      src/scripts/bluetooth.js
  19. 114
      src/scripts/i18n.js
  20. 12
      src/scripts/index.js
  21. 39
      src/styles/_material.scss
  22. 21
      src/styles/_wrapper.scss
  23. 32
      src/templates/layout.pug
  24. 20
      src/templates/major-device.pug
  25. 19
      src/templates/major-service.pug
  26. 82
      src/templates/minor-device.pug
  27. 39
      styles/_material.scss
  28. 26
      styles/_variables.scss
  29. 21
      styles/_wrapper.scss
  30. 80
      styles/index.scss
  31. 32
      templates/layout.pug
  32. 20
      templates/major-device.pug
  33. 19
      templates/major-service.pug
  34. 82
      templates/minor-device.pug

13
.editorconfig

@ -1,13 +0,0 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

5
.firebaserc

@ -1,5 +0,0 @@
{
"projects": {
"default": "bluetooth-class"
}
}

8
.gitignore

@ -1,8 +0,0 @@
# Editor files
.idea
# Build dependencies
node_modules
# Build output
dist

14
.gitlab-ci.yml

@ -1,14 +0,0 @@
image: node:latest
cache:
paths:
- node_modules/
deploy:
only:
- master
stage: deploy
script:
- npm install
- npm run build
- node_modules/.bin/firebase deploy -m "Pipeline $CI_PIPELINE_ID" --non-interactive --token $FIREBASE_CI_TOKEN

2
README.md

@ -1,3 +1,3 @@
# Bluetooth Class
Builder for hcitool class codes
Builder for hcitool class codes.

198
src/assets/en.json → assets/en.json

@ -1,99 +1,99 @@
{
"bluetoothClass": "BlueTooth class",
"builtBy": "Built by",
"clickToCopy": "Click to copy",
"majorService": {
"group": "Major service",
"Information": "Information",
"Telephony": "Telephony",
"Audio": "Audio",
"Transfer": "Transfer",
"Capturing": "Capturing",
"Rendering": "Rendering",
"AccessPoint": "Access point",
"Positioning": "Positioning",
"LimitedDiscovery": "Limited discovery"
},
"majorDevice": {
"group": "Major device",
"Uncategorized": "Uncategorized",
"Miscellaneous": "Miscellaneous",
"Toy": "Toy",
"Wearable": "Wearable",
"Imaging": "Imaging",
"Peripheral": "Peripheral",
"AudioVideo": "Media",
"AccessPoint": "Access point",
"Phone": "Phone",
"Computer": "Computer"
},
"minorDevice": {
"group": "Minor device",
"None": "No minor devices",
"Toy": {
"Game": "Game",
"Controller": "Controller",
"Doll": "Doll",
"Vehicle": "Vehicle",
"Robot": "Robot"
},
"Wearable": {
"Watch": "Watch",
"Pager": "Pager",
"Jacket": "Jacket",
"Helmet": "Helmet",
"Glasses": "Glasses"
},
"Imaging": {
"Display": "Display",
"Camera": "Camera",
"Scanner": "Scanner",
"Printer": "Printer"
},
"Peripheral": {
"Keyboard": "Keyboard",
"Pointer": "Pointer",
"Uncategorized": "Uncategorized",
"Joystick": "Joystick",
"Gamepad": "Gamepad",
"RemoteControl": "Remote control",
"Sensor": "Sensor",
"Digitizer": "Digitizer",
"CardReader": "Card reader"
},
"AudioVideo": {
"Uncategorized": "Uncategorized",
"Headset": "Headset",
"Microphone": "Microphone",
"Loudspeaker": "Loudspeaker",
"Headphones": "Headphones",
"CarAudio": "Car audio",
"VideoCamera": "Video camera",
"Monitor": "Monitor",
"Conferencing": "Conferencing"
},
"AccessPoint": {
"Available": "Available",
"Level1": "1-17% use",
"Level2": "17-33% use",
"Level3": "33-50% use",
"Level4": "50-67% use",
"Level5": "67-83% use",
"Level6": "83-99% use",
"Unavailable": "Unavailable"
},
"Phone": {
"Uncategorized": "Uncategorized",
"Cell": "Cell",
"Cordless": "Cordless"
},
"Computer": {
"Uncategorized": "Uncategorized",
"Desktop": "Desktop",
"Server": "Server",
"Laptop": "Laptop",
"PDA": "PDA",
"Watch": "Watch"
}
}
}
{
"bluetoothClass": "BlueTooth class",
"builtBy": "Built by",
"clickToCopy": "Click to copy",
"majorService": {
"group": "Major service",
"Information": "Information",
"Telephony": "Telephony",
"Audio": "Audio",
"Transfer": "Transfer",
"Capturing": "Capturing",
"Rendering": "Rendering",
"AccessPoint": "Access point",
"Positioning": "Positioning",
"LimitedDiscovery": "Limited discovery"
},
"majorDevice": {
"group": "Major device",
"Uncategorized": "Uncategorized",
"Miscellaneous": "Miscellaneous",
"Toy": "Toy",
"Wearable": "Wearable",
"Imaging": "Imaging",
"Peripheral": "Peripheral",
"AudioVideo": "Media",
"AccessPoint": "Access point",
"Phone": "Phone",
"Computer": "Computer"
},
"minorDevice": {
"group": "Minor device",
"None": "No minor devices",
"Toy": {
"Game": "Game",
"Controller": "Controller",
"Doll": "Doll",
"Vehicle": "Vehicle",
"Robot": "Robot"
},
"Wearable": {
"Watch": "Watch",
"Pager": "Pager",
"Jacket": "Jacket",
"Helmet": "Helmet",
"Glasses": "Glasses"
},
"Imaging": {
"Display": "Display",
"Camera": "Camera",
"Scanner": "Scanner",
"Printer": "Printer"
},
"Peripheral": {
"Keyboard": "Keyboard",
"Pointer": "Pointer",
"Uncategorized": "Uncategorized",
"Joystick": "Joystick",
"Gamepad": "Gamepad",
"RemoteControl": "Remote control",
"Sensor": "Sensor",
"Digitizer": "Digitizer",
"CardReader": "Card reader"
},
"AudioVideo": {
"Uncategorized": "Uncategorized",
"Headset": "Headset",
"Microphone": "Microphone",
"Loudspeaker": "Loudspeaker",
"Headphones": "Headphones",
"CarAudio": "Car audio",
"VideoCamera": "Video camera",
"Monitor": "Monitor",
"Conferencing": "Conferencing"
},
"AccessPoint": {
"Available": "Available",
"Level1": "1-17% use",
"Level2": "17-33% use",
"Level3": "33-50% use",
"Level4": "50-67% use",
"Level5": "67-83% use",
"Level6": "83-99% use",
"Unavailable": "Unavailable"
},
"Phone": {
"Uncategorized": "Uncategorized",
"Cell": "Cell",
"Cordless": "Cordless"
},
"Computer": {
"Uncategorized": "Uncategorized",
"Desktop": "Desktop",
"Server": "Server",
"Laptop": "Laptop",
"PDA": "PDA",
"Watch": "Watch"
}
}
}

198
src/assets/es.json → assets/es.json

@ -1,99 +1,99 @@
{
"bluetoothClass": "Clase bluetooth",
"builtBy": "Construido por",
"clickToCopy": "Haga clic para copiar",
"majorService": {
"group": "Servicio mayor",
"Information": "Información",
"Telephony": "Telefonía",
"Audio": "Audio",
"Transfer": "Transferir",
"Capturing": "Capturando",
"Rendering": "Representación",
"AccessPoint": "Punto de acceso",
"Positioning": "Posicionamiento",
"LimitedDiscovery": "Descubrimiento limitado"
},
"majorDevice": {
"group": "Dispositivo principal",
"Uncategorized": "Sin categorizar",
"Miscellaneous": "Diverso",
"Toy": "Juguete",
"Wearable": "Usable",
"Imaging": "Imágenes",
"Peripheral": "Periférico",
"AudioVideo": "Medios de comunicación",
"AccessPoint": "Punto de acceso",
"Phone": "Teléfono",
"Computer": "Computadora"
},
"minorDevice": {
"group": "Dispositivo menor",
"None": "No hay dispositivos menores",
"Toy": {
"Game": "Juego",
"Controller": "Controlador",
"Doll": "Muñeca",
"Vehicle": "Vehículo",
"Robot": "Robot"
},
"Wearable": {
"Watch": "Reloj",
"Pager": "Buscapersonas",
"Jacket": "Chaqueta",
"Helmet": "Casco",
"Glasses": "Lentes"
},
"Imaging": {
"Display": "Monitor",
"Camera": "Cámara",
"Scanner": "Escáner",
"Printer": "Impresora"
},
"Peripheral": {
"Keyboard": "Teclado",
"Pointer": "Puntero",
"Uncategorized": "Sin categorizar",
"Joystick": "Palanca de mando",
"Gamepad": "Gamepad",
"RemoteControl": "Control remoto",
"Sensor": "Sensor",
"Digitizer": "Digitalizador",
"CardReader": "Lector de tarjetas"
},
"AudioVideo": {
"Uncategorized": "Sin categorizar",
"Headset": "Auriculares",
"Microphone": "Micrófono",
"Loudspeaker": "Altoparlante",
"Headphones": "Auriculares",
"CarAudio": "Audio del coche",
"VideoCamera": "Camara de video",
"Monitor": "Monitor",
"Conferencing": "Conferencia"
},
"AccessPoint": {
"Available": "Disponible",
"Level1": "1-17% de uso",
"Level2": "17-33% de uso",
"Level3": "33-50% de uso",
"Level4": "50-67% de uso",
"Level5": "67-83% de uso",
"Level6": "83-99% de uso",
"Unavailable": "Indisponible"
},
"Phone": {
"Uncategorized": "Sin categorizar",
"Cell": "Célula",
"Cordless": "Sin cable"
},
"Computer": {
"Uncategorized": "Sin categorizar",
"Desktop": "Escritorio",
"Server": "Servidor",
"Laptop": "Ordenador portátil",
"PDA": "PDA",
"Watch": "Reloj"
}
}
}
{
"bluetoothClass": "Clase bluetooth",
"builtBy": "Construido por",
"clickToCopy": "Haga clic para copiar",
"majorService": {
"group": "Servicio mayor",
"Information": "Información",
"Telephony": "Telefonía",
"Audio": "Audio",
"Transfer": "Transferir",
"Capturing": "Capturando",
"Rendering": "Representación",
"AccessPoint": "Punto de acceso",
"Positioning": "Posicionamiento",
"LimitedDiscovery": "Descubrimiento limitado"
},
"majorDevice": {
"group": "Dispositivo principal",
"Uncategorized": "Sin categorizar",
"Miscellaneous": "Diverso",
"Toy": "Juguete",
"Wearable": "Usable",
"Imaging": "Imágenes",
"Peripheral": "Periférico",
"AudioVideo": "Medios de comunicación",
"AccessPoint": "Punto de acceso",
"Phone": "Teléfono",
"Computer": "Computadora"
},
"minorDevice": {
"group": "Dispositivo menor",
"None": "No hay dispositivos menores",
"Toy": {
"Game": "Juego",
"Controller": "Controlador",
"Doll": "Muñeca",
"Vehicle": "Vehículo",
"Robot": "Robot"
},
"Wearable": {
"Watch": "Reloj",
"Pager": "Buscapersonas",
"Jacket": "Chaqueta",
"Helmet": "Casco",
"Glasses": "Lentes"
},
"Imaging": {
"Display": "Monitor",
"Camera": "Cámara",
"Scanner": "Escáner",
"Printer": "Impresora"
},
"Peripheral": {
"Keyboard": "Teclado",
"Pointer": "Puntero",
"Uncategorized": "Sin categorizar",
"Joystick": "Palanca de mando",
"Gamepad": "Gamepad",
"RemoteControl": "Control remoto",
"Sensor": "Sensor",
"Digitizer": "Digitalizador",
"CardReader": "Lector de tarjetas"
},
"AudioVideo": {
"Uncategorized": "Sin categorizar",
"Headset": "Auriculares",
"Microphone": "Micrófono",
"Loudspeaker": "Altoparlante",
"Headphones": "Auriculares",
"CarAudio": "Audio del coche",
"VideoCamera": "Camara de video",
"Monitor": "Monitor",
"Conferencing": "Conferencia"
},
"AccessPoint": {
"Available": "Disponible",
"Level1": "1-17% de uso",
"Level2": "17-33% de uso",
"Level3": "33-50% de uso",
"Level4": "50-67% de uso",
"Level5": "67-83% de uso",
"Level6": "83-99% de uso",
"Unavailable": "Indisponible"
},
"Phone": {
"Uncategorized": "Sin categorizar",
"Cell": "Célula",
"Cordless": "Sin cable"
},
"Computer": {
"Uncategorized": "Sin categorizar",
"Desktop": "Escritorio",
"Server": "Servidor",
"Laptop": "Ordenador portátil",
"PDA": "PDA",
"Watch": "Reloj"
}
}
}

0
src/assets/favicon.ico → assets/favicon.ico

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

31
firebase.json

@ -1,31 +0,0 @@
{
"hosting": {
"public": "dist",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{
"source": "**/*.@(css|js)",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=604800"
}
]
},
{
"source": "/",
"headers": [
{
"key": "Link",
"value": "</index.js>;rel=preload;as=script,</index.css>;rel=preload;as=style"
}
]
}
]
}
}

89
gulpfile.js

@ -1,89 +0,0 @@
const browserSync = require("browser-sync").create();
const cacheBuster = require("gulp-cache-bust");
const clean = require("gulp-clean");
const concat = require("gulp-concat");
const gulp = require("gulp");
const include = require("gulp-include");
const pug = require("gulp-pug");
const sass = require("gulp-sass");
const terser = require("gulp-terser");
// Creating objects for/from NPM modules
const srcDir = "src";
const outDir = "dist";
// Clean the build directory
gulp.task("clean", function () {
return gulp
.src(outDir, {allowEmpty: true})
.pipe(clean());
});
// Copy assets to the build directory
gulp.task("assets", function () {
return gulp
.src(`${srcDir}/assets/**/*`)
.pipe(gulp.dest(outDir))
.pipe(browserSync.stream());
});
// Process all of our SCSS files into the build
gulp.task("css", function () {
return gulp
.src(`${srcDir}/styles/index.scss`)
.pipe(sass({outputStyle: "compressed"}).on("error", sass.logError))
.pipe(concat("index.css"))
.pipe(gulp.dest(outDir))
.pipe(browserSync.stream());
});
// Process all of our HTML files into the build
gulp.task("html", function () {
return gulp
.src(`${srcDir}/routes/**/*.pug`)
.pipe(pug())
.pipe(gulp.dest(outDir))
.pipe(browserSync.stream());
});
// Process all of our JS files into the build
gulp.task("js", function () {
return gulp
.src(`${srcDir}/scripts/index.js`)
.pipe(include())
.pipe(terser())
.pipe(gulp.dest(outDir))
.pipe(browserSync.stream());
});
// Start a BrowserSync server that executes tasks on file changes
gulp.task("sync", function () {
browserSync.init({
server: {
baseDir: `${outDir}/`,
serveStaticOptions: {
extensions: ["html"],
},
},
});
gulp.watch(`${srcDir}/assets/**/*`, gulp.series("assets"));
gulp.watch(`${srcDir}/**/*.scss`, gulp.series("css"));
gulp.watch(`${srcDir}/**/*.js`, gulp.series("js"));
gulp.watch(`${srcDir}/**/*.pug`, gulp.series("html", "css"));
});
// Create a production build
gulp.task("cacheBuster", function () {
return gulp
.src(`${outDir}/index.html`)
.pipe(cacheBuster())
.pipe(gulp.dest(outDir))
});
gulp.task("build", gulp.series("clean", "assets", "html", "js", "css", "cacheBuster"));
// Tasks run on default `gulp` invocation
gulp.task("default", gulp.series("build", "sync"));

11114
package-lock.json

File diff suppressed because it is too large

26
package.json

@ -1,26 +0,0 @@
{
"name": "bluetooth-class",
"version": "0.0.0",
"license": "MIT",
"private": true,
"scripts": {
"start": "gulp",
"build": "gulp build"
},
"dependencies": {
"materialize-css": "^1.0.0"
},
"devDependencies": {
"browser-sync": "^2.26.3",
"firebase-tools": "^6.4.0",
"gulp": "^4.0.0",
"gulp-cache-bust": "^1.4.0",
"gulp-clean": "^0.4.0",
"gulp-concat": "^2.6.1",
"gulp-include": "^2.3.1",
"gulp-pug": "^4.0.1",
"gulp-sass": "^4.0.2",
"gulp-terser": "^1.1.6",
"gulp-uglify": "^3.0.1"
}
}

21
routes/index.pug

@ -0,0 +1,21 @@
extends ../templates/layout
block content
h1(i18n="bluetoothClass") BlueTooth class
form
.row.center-align
.input-field.col.s4.offset-s4
p#bluetoothClass.text-center.underlined(onclick="copyBluetoothClassField()")
span.helper-text(i18n="clickToCopy") Click to copy
h2(i18n="majorService.group") Major service
include ../templates/major-service
h3(i18n="majorDevice.group") Major device
include ../templates/major-device
h4(i18n="minorDevice.group") Minor device
include ../templates/minor-device

68
scripts/bluetooth.js

@ -0,0 +1,68 @@
let bluetoothClass = 0;
// Grab references to our DOM elements
const bluetoothClassField = document.getElementById("bluetoothClass");
const minorDeviceGroups = document.querySelectorAll("[class^='minor-devices'] > *");
const checkboxesAndRadios = document.querySelectorAll("input[type='checkbox'], input[type='radio']");
/**
* Write a hex string of our current Bluetooth class to the input
*/
function updateBluetoothClassField() {
let hex = bluetoothClass.toString(16);
hex = hex.toLocaleUpperCase();
hex = hex.padStart(6, "0");
hex = "0x" + hex;
bluetoothClassField.innerHTML = hex;
}
/**
* Copy the current value of the Bluetooth class field to the clipboard
*/
function copyBluetoothClassField() {
navigator.clipboard.writeText(bluetoothClassField.innerHTML);
}
/**
* Depending on our selected major device category, hide or show groups of minor devices
*/
function updateBluetoothMinorDeviceFields() {
// Determine the ID of the selected major device
const majorDevice = bluetoothClass & 0x1F00;
const majorDeviceRadio = document.querySelector(`input[type='radio'][name='majorDevice'][value='${majorDevice}']`);
const majorDeviceId = majorDeviceRadio.id.replace("majorDevice", "");
// Hide all minor device fields that don't match our major device id and uncheck them
minorDeviceGroups.forEach((element) => {
element.querySelectorAll("input").forEach((minorDevice) => minorDevice.checked = false);
element.classList.add("hidden");
if (element.classList.contains(majorDeviceId)) {
element.classList.remove("hidden");
}
});
}
// Watch the page checkboxes and radio buttons for updating the Bluetooth class
checkboxesAndRadios.forEach((element) => {
element.addEventListener("change", () => {
if (element.id.startsWith("majorService")) {
// Major services can be combined, so XOR is all we need
bluetoothClass ^= element.value;
} else if (element.id.startsWith("majorDevice")) {
// Major devices cannot conflict, so zero out any current major and minor devices
bluetoothClass = (bluetoothClass & ~0x1FFC) | element.value;
updateBluetoothMinorDeviceFields();
} else if (element.id.startsWith("minorDevice")) {
// Minor devices cannot conflict, so zero out any current minor devices
bluetoothClass = (bluetoothClass & ~0xFC) | element.value;
}
updateBluetoothClassField();
});
});
// Default the bluetooth class field and minor device fields on page load
updateBluetoothClassField();
updateBluetoothMinorDeviceFields();

14
scripts/index.js

@ -0,0 +1,14 @@
// Materialize CSS
//=require ../../../node_modules/materialize-css/js/cash.js
//=require ../../../node_modules/materialize-css/js/global.js
//=require ../../../node_modules/materialize-css/js/component.js
//=require ../../../node_modules/materialize-css/js/anime.min.js
//=require ../../../node_modules/materialize-css/js/buttons.js
//=require ../../../node_modules/materialize-css/js/forms.js
//=require ../../../node_modules/materialize-css/js/waves.js
// Internationalization script shared between sites in root of project sub-modules
//=require ../../shared/scripts/i18n.js
// Project scripts
//=require bluetooth.js

2
src/assets/robots.txt

@ -1,2 +0,0 @@
User-agent: *
Disallow:

21
src/routes/index.pug

@ -1,21 +0,0 @@
extends ../templates/layout
block content
h1(i18n="bluetoothClass") BlueTooth class
form
.row.center-align
.input-field.col.s4.offset-s4
input#bluetoothClass.text-center(type="text", readonly, onclick="copyBluetoothClassField()")
span.helper-text(i18n="clickToCopy") Click to copy
h2(i18n="majorService.group") Major service
include ../templates/major-service
h3(i18n="majorDevice.group") Major device
include ../templates/major-device
h4(i18n="minorDevice.group") Minor device
include ../templates/minor-device

68
src/scripts/bluetooth.js

@ -1,68 +0,0 @@
let bluetoothClass = 0;
// Grab references to our DOM elements
const bluetoothClassField = document.getElementById("bluetoothClass");
const minorDeviceGroups = document.querySelectorAll("[class^='minor-devices'] > *");
const checkboxesAndRadios = document.querySelectorAll("input[type='checkbox'], input[type='radio']");
/**
* Write a hex string of our current Bluetooth class to the input
*/
function updateBluetoothClassField() {
let hex = bluetoothClass.toString(16);
hex = hex.toLocaleUpperCase();
hex = hex.padStart(6, "0");
hex = "0x" + hex;
bluetoothClassField.value = hex;
}
/**
* Copy the current value of the Bluetooth class field to the clipboard
*/
function copyBluetoothClassField() {
navigator.clipboard.writeText(bluetoothClassField.value);
}
/**
* Depending on our selected major device category, hide or show groups of minor devices
*/
function updateBluetoothMinorDeviceFields() {
// Determine the ID of the selected major device
const majorDevice = bluetoothClass & 0x1F00;
const majorDeviceRadio = document.querySelector(`input[type='radio'][name='majorDevice'][value='${majorDevice}']`);
const majorDeviceId = majorDeviceRadio.id.replace("majorDevice", "");
// Hide all minor device fields that don't match our major device id and uncheck them
minorDeviceGroups.forEach((element) => {
element.querySelectorAll("input").forEach((minorDevice) => minorDevice.checked = false);
element.classList.add("hidden");
if (element.classList.contains(majorDeviceId)) {
element.classList.remove("hidden");
}
});
}
// Watch the page checkboxes and radio buttons for updating the Bluetooth class
checkboxesAndRadios.forEach((element) => {
element.addEventListener("change", () => {
if (element.id.startsWith("majorService")) {
// Major services can be combined, so XOR is all we need
bluetoothClass ^= element.value;
} else if (element.id.startsWith("majorDevice")) {
// Major devices cannot conflict, so zero out any current major and minor devices
bluetoothClass = (bluetoothClass & ~0x1FFC) | element.value;
updateBluetoothMinorDeviceFields();
} else if (element.id.startsWith("minorDevice")) {
// Minor devices cannot conflict, so zero out any current minor devices
bluetoothClass = (bluetoothClass & ~0xFC) | element.value;
}
updateBluetoothClassField();
});
});
// Default the bluetooth class field and minor device fields on page load
updateBluetoothClassField();
updateBluetoothMinorDeviceFields();

114
src/scripts/i18n.js

@ -1,114 +0,0 @@
// Specify the languages this application can use
const languageWhitelist = ["en", "es"];
// Determine the current language that should be used, with English as a fallback
let languageCurrent = [localStorage.getItem("language"), ...navigator.languages]
.filter((language) => languageWhitelist.includes(language))
.find((language) => (language == null) ? "en" : language);
// Fetch our translation files
const languageTranslations = {};
languageWhitelist.forEach((language) => {
fetch(`/${language}.json`)
.then((response) => response.json())
.then((translations) => {
languageTranslations[language] = translations;
if (languageCurrent === language) {
languageChange(languageCurrent);
}
});
});
/**
* Language change handler for updating page text
*
* @param language Two character country code for translation language
*/
function languageChange(language) {
// Save the current language choice
languageCurrent = language;
localStorage.setItem("language", language);
// Set HTML language attribute
document.querySelector("html").setAttribute("lang", language);
// Set active language selector underlines
document.querySelectorAll("footer button").forEach((element) => {
element.classList.remove("underlined");
if (element.id === `${language}I18nButton`) {
element.classList.add("underlined");
}
});
// Translate text
document.querySelectorAll("[i18n]").forEach((element) => translateElement(element));
}
/**
* Return translated text based on key-value matching above
*
* @param path Translation specifier in above translation map, like majorService.title
*/
function translateKey(path) {
let translation;
if (path != null) {
translation = languageTranslations[languageCurrent]
path.split(".").forEach((key) => {
if (translation != null) {
translation = translation[key];
}
});
}
return translation == null ? "" : translation;
}
/**
* Set inner HTML to translated text based on i18n attribute of given element
*
* @param element Root element to translate
*/
function translateElement(element) {
const attribute = element.attributes.getNamedItem("i18n");
element.innerHTML = translateKey(attribute.value);
}
/**
* Translate any element with necessary attribute qualifier, including child elements
*
* @param element Root element to check for translations
*/
function checkElementForTranslations(element) {
if (element instanceof HTMLElement) {
// Translate element if necessary
if (element.hasAttribute("i18n")) {
translateElement(element);
}
// Recurse into each child element looking for more potential languageTranslations
element.childNodes.forEach((childElement) => checkElementForTranslations(childElement));
}
}
// Call language change on page load to ensure languageTranslations are set
// Check local storage / browser for desired language and supply the closest match we have
languageChange(languageCurrent);
// Watch the page HTML for changes to ensure translations are kept accurate
const mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
checkElementForTranslations(node);
});
});
});
mutationObserver.observe(document.querySelector("body"), {
childList: true,
subtree: true,
});

12
src/scripts/index.js

@ -1,12 +0,0 @@
// Materialize CSS
//=require ../../node_modules/materialize-css/js/cash.js
//=require ../../node_modules/materialize-css/js/global.js
//=require ../../node_modules/materialize-css/js/component.js
//=require ../../node_modules/materialize-css/js/anime.min.js
//=require ../../node_modules/materialize-css/js/buttons.js
//=require ../../node_modules/materialize-css/js/forms.js
//=require ../../node_modules/materialize-css/js/waves.js
// Project scripts
//=require bluetooth.js
//=require i18n.js

39
src/styles/_material.scss

@ -1,39 +0,0 @@
// Color
@import "../../node_modules/materialize-css/sass/components/color-variables";
// @import "../../node_modules/materialize-css/sass/components/color-classes";
// Variables
@import "../../node_modules/materialize-css/sass/components/variables";
// Reset
@import "../../node_modules/materialize-css/sass/components/normalize";
// Components
@import "../../node_modules/materialize-css/sass/components/global";
// @import "../../node_modules/materialize-css/sass/components/badges";
// @import "../../node_modules/materialize-css/sass/components/icons-material-design";
@import "../../node_modules/materialize-css/sass/components/grid";
// @import "../../node_modules/materialize-css/sass/components/navbar";
@import "../../node_modules/materialize-css/sass/components/typography";
// @import "../../node_modules/materialize-css/sass/components/transitions";
// @import "../../node_modules/materialize-css/sass/components/cards";
// @import "../../node_modules/materialize-css/sass/components/toast";
// @import "../../node_modules/materialize-css/sass/components/tabs";
// @import "../../node_modules/materialize-css/sass/components/tooltip";
@import "../../node_modules/materialize-css/sass/components/buttons";
// @import "../../node_modules/materialize-css/sass/components/dropdown";
@import "../../node_modules/materialize-css/sass/components/waves";
// @import "../../node_modules/materialize-css/sass/components/modal";
// @import "../../node_modules/materialize-css/sass/components/collapsible";
// @import "../../node_modules/materialize-css/sass/components/chips";
// @import "../../node_modules/materialize-css/sass/components/materialbox";
@import "../../node_modules/materialize-css/sass/components/forms/forms";
// @import "../../node_modules/materialize-css/sass/components/table_of_contents";
// @import "../../node_modules/materialize-css/sass/components/sidenav";
// @import "../../node_modules/materialize-css/sass/components/preloader";
// @import "../../node_modules/materialize-css/sass/components/slider";
// @import "../../node_modules/materialize-css/sass/components/carousel";
// @import "../../node_modules/materialize-css/sass/components/tapTarget";
// @import "../../node_modules/materialize-css/sass/components/pulse";
// @import "../../node_modules/materialize-css/sass/components/datepicker";
// @import "../../node_modules/materialize-css/sass/components/timepicker";

21
src/styles/_wrapper.scss

@ -1,21 +0,0 @@
// Pushing footer to bottom of page
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1 0 auto;
}
// Off-white background colors
body {
background-color: whitesmoke;
}
// Content breathing room
footer {
margin: 20px 0;
}

32
src/templates/layout.pug

@ -1,32 +0,0 @@
doctype html
html(lang="en")
head
meta(charset="utf-8")
meta(name="viewport", content="width=device-width, initial-scale=1, shrink-to-fit=no")
meta(name="description", content="Builder for hcitool class codes")
title(i18n="bluetoothClass") BlueTooth Class
link(rel="shortcut icon", href="/favicon.ico")
link(rel="stylesheet", href="/index.css")
body.container
.text-center
noscript This application requires JavaScript to run!
main
block content
footer.center-align
div
button#enI18nButton.btn-flat.waves-effect.waves-light(onclick="languageChange('en')") English
button#esI18nButton.btn-flat.waves-effect.waves-light(onclick="languageChange('es')") Español
h5
span(i18n="builtBy") Built by
a(href="https://mobiusk.com") MobiusK
block scripts
script(src="/index.js")

20
src/templates/major-device.pug

@ -1,20 +0,0 @@
-
const majorDevices = {
"Uncategorized": 0x1F00,
"Toy": 0x0800,
"Wearable": 0x0700,
"Imaging": 0x0600,
"Peripheral": 0x0500,
"AudioVideo": 0x0400,
"AccessPoint": 0x0300,
"Phone": 0x0200,
"Computer": 0x0100,
"Miscellaneous": 0x0000,
}
.row
each value, key in majorDevices
.col.s12.m6.l4
label(for="majorDevice" + key)
input.with-gap(type="radio", name="majorDevice", id="majorDevice" + key, value=value)
span(i18n="majorDevice." + key) #{key}

19
src/templates/major-service.pug

@ -1,19 +0,0 @@
-
const majorServices = {
"Information": 0x800000,
"Telephony": 0x400000,
"Audio": 0x200000,
"Transfer": 0x100000,
"Capturing": 0x080000,
"Rendering": 0x040000,
"AccessPoint": 0x020000,
"Positioning": 0x010000,
"LimitedDiscovery": 0x002000
}
.row
each value, key in majorServices
.col.s12.m6.l4
label(for="majorService" + key)
input(type="checkbox", id="majorService" + key, value=value)
span(i18n="majorService." + key) #{key}

82
src/templates/minor-device.pug

@ -1,82 +0,0 @@
-
const minorDevices = {
"Toy": {
"Game": 0x14,
"Controller": 0x10,
"Doll": 0x0C,
"Vehicle": 0x08,
"Robot": 0x04,
},
"Wearable": {
"Glasses": 0x14,
"Helmet": 0x10,
"Jacket": 0x0C,
"Pager": 0x08,
"Watch": 0x04,
},
"Imaging": {
"Display": 0x10,
"Camera": 0x20,
"Scanner": 0x40,
"Printer": 0x80,
},
"Peripheral": {
"Uncategorized": 0x00,
"Keyboard": 0x40,
"Pointer": 0x80,
"Joystick": 0x04,
"Gamepad": 0x08,
"RemoteControl": 0x0C,
"Sensor": 0x10,
"Digitizer": 0x14,
"CardReader": 0x18,
},
"AudioVideo": {
"Uncategorized": 0x00,
"Headset": 0x04,
"Microphone": 0x10,
"Loudspeaker": 0x14,
"Headphones": 0x18,
"CarAudio": 0x20,
"VideoCamera": 0x30,
"Monitor": 0x38,
"Conferencing": 0x40,
},
"AccessPoint": {
"Available": 0x00,
"Level1": 0x20,
"Level2": 0x40,
"Level3": 0x60,
"Level4": 0x80,
"Level5": 0xA0,
"Level6": 0xC0,
"Unavailable": 0xE0,
},
"Phone": {
"Uncategorized": 0x00,
"Cell": 0x04,
"Cordless": 0x08,
},
"Computer": {
"Uncategorized": 0x00,
"Desktop": 0x04,
"Server": 0x08,
"Laptop": 0x0C,
"PDA": 0x14,
"Watch": 0x18,
},
"Miscellaneous": {},
"Uncategorized": {}
}
.minor-devices
each devices, groupKey in minorDevices
.row(class=groupKey)
each value, key in devices
.col.s12.m6.l4
label(for="minorDevice" + groupKey + key)
input.with-gap(type="radio", name="minorDevice" + groupKey, id="minorDevice" + groupKey + key, value=value)
span(i18n="minorDevice." + groupKey + "." + key) #{key}
else
.col.s12
p(i18n="minorDevice.None") No minor devices

39
styles/_material.scss

@ -0,0 +1,39 @@
// Color
@import "../../../node_modules/materialize-css/sass/components/color-variables";
// @import "../../../node_modules/materialize-css/sass/components/color-classes";
// Variables
@import "../../../node_modules/materialize-css/sass/components/variables";
// Reset
@import "../../../node_modules/materialize-css/sass/components/normalize";
// Components
@import "../../../node_modules/materialize-css/sass/components/global";
// @import "../../../node_modules/materialize-css/sass/components/badges";
// @import "../../../node_modules/materialize-css/sass/components/icons-material-design";
@import "../../../node_modules/materialize-css/sass/components/grid";
// @import "../../../node_modules/materialize-css/sass/components/navbar";
@import "../../../node_modules/materialize-css/sass/components/typography";
// @import "../../../node_modules/materialize-css/sass/components/transitions";
// @import "../../../node_modules/materialize-css/sass/components/cards";
// @import "../../../node_modules/materialize-css/sass/components/toast";
// @import "../../../node_modules/materialize-css/sass/components/tabs";
// @import "../../../node_modules/materialize-css/sass/components/tooltip";
@import "../../../node_modules/materialize-css/sass/components/buttons";
// @import "../../../node_modules/materialize-css/sass/components/dropdown";
@import "../../../node_modules/materialize-css/sass/components/waves";
// @import "../../../node_modules/materialize-css/sass/components/modal";
// @import "../../../node_modules/materialize-css/sass/components/collapsible";
// @import "../../../node_modules/materialize-css/sass/components/chips";
// @import "../../../node_modules/materialize-css/sass/components/materialbox";
@import "../../../node_modules/materialize-css/sass/components/forms/forms";
// @import "../../../node_modules/materialize-css/sass/components/table_of_contents";
// @import "../../../node_modules/materialize-css/sass/components/sidenav";
// @import "../../../node_modules/materialize-css/sass/components/preloader";
// @import "../../../node_modules/materialize-css/sass/components/slider";
// @import "../../../node_modules/materialize-css/sass/components/carousel";
// @import "../../../node_modules/materialize-css/sass/components/tapTarget";
// @import "../../../node_modules/materialize-css/sass/components/pulse";
// @import "../../../node_modules/materialize-css/sass/components/datepicker";
// @import "../../../node_modules/materialize-css/sass/components/timepicker";

26
src/styles/_variables.scss → styles/_variables.scss

@ -1,13 +1,13 @@
// Bluetooth blue: #3B5998
// Darker blue: #14306B
// Paletton: http://paletton.com/#uid=23K0u0klOszcgHQhAwKqLpHt8k9
$primary-color: #3B5998;
$secondary-color: #14306B;
$h1-fontsize: 1.90rem;
$h2-fontsize: 1.78rem;
$h3-fontsize: 1.46rem;
$h4-fontsize: 1.30rem;
$h5-fontsize: 1.15rem;
$h6-fontsize: 1.00rem;
// Bluetooth blue: #3B5998
// Darker blue: #14306B
// Paletton: http://paletton.com/#uid=23K0u0klOszcgHQhAwKqLpHt8k9
$primary-color: #3B5998;
$secondary-color: #14306B;
$h1-fontsize: 1.90rem;
$h2-fontsize: 1.78rem;
$h3-fontsize: 1.46rem;
$h4-fontsize: 1.30rem;
$h5-fontsize: 1.15rem;
$h6-fontsize: 1.00rem;

21
styles/_wrapper.scss

@ -0,0 +1,21 @@
// Pushing footer to bottom of page
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1 0 auto;
}
// Off-white background colors
body {
background-color: whitesmoke;
}
// Content breathing room
footer {
margin: 20px 0;
}

80
src/styles/index.scss → styles/index.scss

@ -1,40 +1,40 @@
// Global variables
@import "variables";
// Material framework
@import "material";
// Content wrap styles
@import "wrapper";
// Give anchors and top level headers our branding color
a, h1 {
color: $primary-color;
}
// Our input text fields are all either readonly
input[type='text'] {
cursor: default;
}
// Self-explanatory
.text-center {
text-align: center;
}
// Hide fields as necessary
.hidden {
display: none;
}
// Make our checkbox labels all the same width, and a more readable color
label span {
color: rgba(0,0,0,0.72);
min-width: 190px;
}
// Utility class for underlining text
.underlined {
text-decoration-color: $primary-color !important;
text-decoration: underline;
}
// Global variables
@import "variables";
// Material framework
@import "material";
// Content wrap styles
@import "wrapper";
// Give anchors and top level headers our branding color
a, h1 {
color: $primary-color;
}
// Our input text fields are all either readonly
input[type='text'] {
cursor: default;
}
// Self-explanatory
.text-center {
text-align: center;
}
// Hide fields as necessary
.hidden {
display: none;
}
// Make our checkbox labels all the same width, and a more readable color
label span {
color: rgba(0, 0, 0, 0.72);
min-width: 190px;
}
// Utility class for underlining text
.underlined {
text-decoration-color: $primary-color !important;
text-decoration: underline;
}

32
templates/layout.pug

@ -0,0 +1,32 @@
doctype html
html(lang="en")
head
meta(charset="utf-8")
meta(name="viewport", content="width=device-width, initial-scale=1, shrink-to-fit=no")
meta(name="description", content="Builder for hcitool class codes")
title(i18n="bluetoothClass") BlueTooth Class
link(rel="shortcut icon", href="/favicon.ico")
link(rel="stylesheet", href="/index.css")
body.container
.text-center
noscript This application requires JavaScript to run!
main
block content
footer.center-align
div
button#enI18nButton.btn-flat.waves-effect.waves-light(onclick="languageChange('en')") English
button#esI18nButton.btn-flat.waves-effect.waves-light(onclick="languageChange('es')") Español
h5
span(i18n="builtBy") Built by
a(href="https://mobiusk.com") MobiusK
block scripts
script(src="/index.js")

20
templates/major-device.pug

@ -0,0 +1,20 @@
-
const majorDevices = {
"Uncategorized": 0x1F00,
"Toy": 0x0800,
"Wearable": 0x0700,
"Imaging": 0x0600,
"Peripheral": 0x0500,
"AudioVideo": 0x0400,
"AccessPoint": 0x0300,
"Phone": 0x0200,
"Computer": 0x0100,
"Miscellaneous": 0x0000,
}
.row
each value, key in majorDevices
.col.s12.m6.l4
label(for="majorDevice" + key)
input.with-gap(type="radio", name="majorDevice", id="majorDevice" + key, value=value)
span(i18n="majorDevice." + key) #{key}

19
templates/major-service.pug

@ -0,0 +1,19 @@
-
const majorServices = {
"Information": 0x800000,
"Telephony": 0x400000,
"Audio": 0x200000,
"Transfer": 0x100000,
"Capturing": 0x080000,
"Rendering": 0x040000,
"AccessPoint": 0x020000,
"Positioning": 0x010000,
"LimitedDiscovery": 0x002000
}
.row
each value, key in majorServices
.col.s12.m6.l4
label(for="majorService" + key)
input(type="checkbox", id="majorService" + key, value=value)
span(i18n="majorService." + key) #{key}

82
templates/minor-device.pug

@ -0,0 +1,82 @@
-
const minorDevices = {
"Toy": {
"Game": 0x14,
"Controller": 0x10,
"Doll": 0x0C,
"Vehicle": 0x08,
"Robot": 0x04,
},
"Wearable": {
"Glasses": 0x14,
"Helmet": 0x10,
"Jacket": 0x0C,
"Pager": 0x08,
"Watch": 0x04,
},
"Imaging": {
"Display": 0x10,
"Camera": 0x20,
"Scanner": 0x40,
"Printer": 0x80,
},
"Peripheral": {
"Uncategorized": 0x00,
"Keyboard": 0x40,
"Pointer": 0x80,
"Joystick": 0x04,
"Gamepad": 0x08,
"RemoteControl": 0x0C,
"Sensor": 0x10,
"Digitizer": 0x14,
"CardReader": 0x18,
},
"AudioVideo": {
"Uncategorized": 0x00,
"Headset": 0x04,
"Microphone": 0x10,
"Loudspeaker": 0x14,
"Headphones": 0x18,
"CarAudio": 0x20,
"VideoCamera": 0x30,
"Monitor": 0x38,
"Conferencing": 0x40,
},
"AccessPoint": {
"Available": 0x00,
"Level1": 0x20,
"Level2": 0x40,
"Level3": 0x60,
"Level4": 0x80,
"Level5": 0xA0,
"Level6": 0xC0,
"Unavailable": 0xE0,
},
"Phone": {
"Uncategorized": 0x00,
"Cell": 0x04,
"Cordless": 0x08,
},
"Computer": {
"Uncategorized": 0x00,
"Desktop": 0x04,
"Server": 0x08,
"Laptop": 0x0C,
"PDA": 0x14,
"Watch": 0x18,
},
"Miscellaneous": {},
"Uncategorized": {}
}
.minor-devices
each devices, groupKey in minorDevices
.row(class=groupKey)
each value, key in devices
.col.s12.m6.l4
label(for="minorDevice" + groupKey + key)
input.with-gap(type="radio", name="minorDevice" + groupKey, id="minorDevice" + groupKey + key, value=value)
span(i18n="minorDevice." + groupKey + "." + key) #{key}
else
.col.s12
p(i18n="minorDevice.None") No minor devices
Loading…
Cancel
Save