diff --git a/common/tools/sourcemap-path-remapper/build.sh b/common/tools/sourcemap-path-remapper/build.sh
index 0351211a81..55bc386eab 100755
--- a/common/tools/sourcemap-path-remapper/build.sh
+++ b/common/tools/sourcemap-path-remapper/build.sh
@@ -7,8 +7,6 @@
# Exit on command failure and when using unset variables:
set -eu
-# Include some helper functions from resources
-
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")"
diff --git a/package-lock.json b/package-lock.json
index a1f7d1dc64..2c75f7b58c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7934,7 +7934,7 @@
"@keymanapp/web-utils": "*",
"@types/node": "^10.17.21",
"chai": "^4.3.4",
- "combine-source-map": "*",
+ "combine-source-map": "^0.8.0",
"es6-shim": "^0.35.5",
"karma": "^6.4.1",
"karma-browserstack-launcher": "^1.6.0",
diff --git a/web/ci.sh b/web/ci.sh
index d9dfcbf586..629d61d1a0 100755
--- a/web/ci.sh
+++ b/web/ci.sh
@@ -39,6 +39,10 @@ builder_parse "$@"
####
+TIER=`cat ../TIER.md`
+BUILD_NUMBER=`cat ../VERSION.md`
+S_KEYMAN_COM=../../s.keyman.com
+
if builder_start_action build; then
# Build step: since CI builds start (and should start) from scratch, run the following
# three actions:
diff --git a/web/src/engine/device-detect/kmwdevice.ts b/web/src/engine/device-detect/kmwdevice.ts
index 1d55068160..7a596cd25b 100644
--- a/web/src/engine/device-detect/kmwdevice.ts
+++ b/web/src/engine/device-detect/kmwdevice.ts
@@ -1,203 +1,201 @@
-// Includes version-related functionality
-///
+import StyleConstants from 'utils/styleConstants.js';
+import { DeviceSpec, Version } from "@keymanapp/web-utils/build/obj/index.js";
// The Device object definition -------------------------------------------------
-namespace com.keyman {
- export class Device {
- touchable: boolean;
- OS: string;
- formFactor: string;
- dyPortrait: number;
- dyLandscape: number;
- version: string;
- orientation: string|number;
- browser: string;
- colorScheme: 'light' | 'dark';
+export class Device {
+ touchable: boolean;
+ OS: string;
+ formFactor: string;
+ dyPortrait: number;
+ dyLandscape: number;
+ version: string;
+ orientation: string|number;
+ browser: string;
+ colorScheme: 'light' | 'dark';
- private detected: boolean = false;
- private _styles: utils.StyleConstants;
+ private detected: boolean = false;
+ private _styles: StyleConstants;
- // Generates a default Device value.
- constructor() {
- this.touchable = !!('ontouchstart' in window);
- this.OS = '';
- this.formFactor='desktop';
- this.dyPortrait=0;
- this.dyLandscape=0;
- this.version='0';
- this.orientation=window.orientation;
- this.browser='';
- }
+ // Generates a default Device value.
+ constructor() {
+ this.touchable = !!('ontouchstart' in window);
+ this.OS = '';
+ this.formFactor='desktop';
+ this.dyPortrait=0;
+ this.dyLandscape=0;
+ this.version='0';
+ this.orientation=window.orientation;
+ this.browser='';
+ }
- /**
- * Get device horizontal DPI for touch devices, to set actual size of active regions
- * Note that the actual physical DPI may be somewhat different.
- *
- * @return {number}
- */
- getDPI(): number {
- var t=document.createElement('DIV') ,s=t.style,dpi=96;
- if(document.readyState !== 'complete') {
- return dpi;
- }
-
- t.id='calculateDPI';
- s.position='absolute'; s.display='block';s.visibility='hidden';
- s.left='10px'; s.top='10px'; s.width='1in'; s.height='10px';
- document.body.appendChild(t);
- dpi=(typeof window.devicePixelRatio == 'undefined') ? t.offsetWidth : t.offsetWidth * window.devicePixelRatio;
- document.body.removeChild(t);
+ /**
+ * Get device horizontal DPI for touch devices, to set actual size of active regions
+ * Note that the actual physical DPI may be somewhat different.
+ *
+ * @return {number}
+ */
+ getDPI(): number {
+ var t=document.createElement('DIV') ,s=t.style,dpi=96;
+ if(document.readyState !== 'complete') {
return dpi;
}
- detect() : void {
- var possMacSpoof = false;
+ t.id='calculateDPI';
+ s.position='absolute'; s.display='block';s.visibility='hidden';
+ s.left='10px'; s.top='10px'; s.width='1in'; s.height='10px';
+ document.body.appendChild(t);
+ dpi=(typeof window.devicePixelRatio == 'undefined') ? t.offsetWidth : t.offsetWidth * window.devicePixelRatio;
+ document.body.removeChild(t);
+ return dpi;
+ }
- if(navigator && navigator.userAgent) {
- var agent=navigator.userAgent;
+ detect() : void {
+ var possMacSpoof = false;
- if(agent.indexOf('iPad') >= 0) {
- this.OS='iOS';
- this.formFactor='tablet';
- this.dyPortrait=this.dyLandscape=0;
- } else if(agent.indexOf('iPhone') >= 0) {
- this.OS='iOS';
- this.formFactor='phone';
- this.dyPortrait=this.dyLandscape=25;
- } else if(agent.indexOf('Android') >= 0) {
- this.OS='Android';
- this.formFactor='phone'; // form factor may be redefined on initialization
- this.dyPortrait=75;
- this.dyLandscape=25;
- try {
- var rx=new RegExp("(?:Android\\s+)(\\d+\\.\\d+\\.\\d+)");
- this.version=agent.match(rx)[1];
- } catch(ex) {}
- } else if(agent.indexOf('Linux') >= 0) {
- this.OS='Linux';
- } else if(agent.indexOf('Macintosh') >= 0) {
- // Starting with 13.1, "Macintosh" can reflect iPads (by default) or iPhones
- // (by user setting); a new "Request Desktop Website" setting for Safari will
- // change the user agent string to match a desktop Mac.
- //
- // Firefox uses '.' between version components, while Chrome and Safari use
- // '_' instead. So, we have to check for both. Yay.
- let regex = /Intel Mac OS X (\d+(?:[_\.]\d+)+)/i;
- let results = regex.exec(agent);
+ if(navigator && navigator.userAgent) {
+ var agent=navigator.userAgent;
- // Match result: a version string with components separated by underscores.
- if(!results) {
- console.warn("KMW could not properly parse the user agent string."
- + "A suboptimal keyboard layout may result.");
- this.OS='MacOSX';
- } else if(results.length > 1 && results[1]) {
- // Convert version string into a usable form.
- let versionString = results[1].replace('_', '.');
- let version = new utils.Version(versionString);
+ if(agent.indexOf('iPad') >= 0) {
+ this.OS='iOS';
+ this.formFactor='tablet';
+ this.dyPortrait=this.dyLandscape=0;
+ } else if(agent.indexOf('iPhone') >= 0) {
+ this.OS='iOS';
+ this.formFactor='phone';
+ this.dyPortrait=this.dyLandscape=25;
+ } else if(agent.indexOf('Android') >= 0) {
+ this.OS='Android';
+ this.formFactor='phone'; // form factor may be redefined on initialization
+ this.dyPortrait=75;
+ this.dyLandscape=25;
+ try {
+ var rx=new RegExp("(?:Android\\s+)(\\d+\\.\\d+\\.\\d+)");
+ this.version=agent.match(rx)[1];
+ } catch(ex) {}
+ } else if(agent.indexOf('Linux') >= 0) {
+ this.OS='Linux';
+ } else if(agent.indexOf('Macintosh') >= 0) {
+ // Starting with 13.1, "Macintosh" can reflect iPads (by default) or iPhones
+ // (by user setting); a new "Request Desktop Website" setting for Safari will
+ // change the user agent string to match a desktop Mac.
+ //
+ // Firefox uses '.' between version components, while Chrome and Safari use
+ // '_' instead. So, we have to check for both. Yay.
+ let regex = /Intel Mac OS X (\d+(?:[_\.]\d+)+)/i;
+ let results = regex.exec(agent);
- possMacSpoof = utils.Version.MAC_POSSIBLE_IPAD_ALIAS.compareTo(version) <= 0;
- this.OS='MacOSX';
- }
- } else if(agent.indexOf('Windows NT') >= 0) {
- this.OS='Windows';
- if(agent.indexOf('Touch') >= 0) {
- this.formFactor='phone'; // will be redefined as tablet if resolution high enough
- }
+ // Match result: a version string with components separated by underscores.
+ if(!results) {
+ console.warn("KMW could not properly parse the user agent string."
+ + "A suboptimal keyboard layout may result.");
+ this.OS='MacOSX';
+ } else if(results.length > 1 && results[1]) {
+ // Convert version string into a usable form.
+ let versionString = results[1].replace('_', '.');
+ let version = new Version(versionString);
- // Windows Phone and Tablet PC
- if(typeof (navigator).msMaxTouchPoints == 'number' && (navigator).msMaxTouchPoints > 0) {
- this.touchable=true;
- }
+ possMacSpoof = Version.MAC_POSSIBLE_IPAD_ALIAS.compareTo(version) <= 0;
+ this.OS='MacOSX';
+ }
+ } else if(agent.indexOf('Windows NT') >= 0) {
+ this.OS='Windows';
+ if(agent.indexOf('Touch') >= 0) {
+ this.formFactor='phone'; // will be redefined as tablet if resolution high enough
+ }
+
+ // Windows Phone and Tablet PC
+ if(typeof (navigator).msMaxTouchPoints == 'number' && (navigator).msMaxTouchPoints > 0) {
+ this.touchable=true;
}
}
+ }
- // We look at the screen resolution for Android, because we can't tell from
- // the user agent string whether or not this is supposed to be a tablet.
- // It seems that there are a handful of older phones out there that report a
- // higher resolution than 700px*___px, but it is proving hard to test these,
- // and the majority have an aspect ratio <= 0.5625 anyway.
- // But we trust what iOS tells us for phone vs tablet.
+ // We look at the screen resolution for Android, because we can't tell from
+ // the user agent string whether or not this is supposed to be a tablet.
+ // It seems that there are a handful of older phones out there that report a
+ // higher resolution than 700px*___px, but it is proving hard to test these,
+ // and the majority have an aspect ratio <= 0.5625 anyway.
+ // But we trust what iOS tells us for phone vs tablet.
- const dimMin = Math.min(screen.width,screen.height), dimMax = Math.max(screen.width,screen.height);
- const aspect = dimMin / dimMax;
+ const dimMin = Math.min(screen.width,screen.height), dimMax = Math.max(screen.width,screen.height);
+ const aspect = dimMin / dimMax;
- if(this.OS != 'iOS' &&
- this.formFactor == 'phone' &&
- ((dimMin >= 600 && aspect > 0.5625) || // 0.5625 -> 1920x1080 is common phone res
- (aspect >= 0.625)) // all reported devices with aspect >= 0.625 are tablets per https://screensiz.es/
- ) {
- this.formFactor='tablet';
- }
+ if(this.OS != 'iOS' &&
+ this.formFactor == 'phone' &&
+ ((dimMin >= 600 && aspect > 0.5625) || // 0.5625 -> 1920x1080 is common phone res
+ (aspect >= 0.625)) // all reported devices with aspect >= 0.625 are tablets per https://screensiz.es/
+ ) {
+ this.formFactor='tablet';
+ }
- // Test for potential Chrome emulation on Windows or macOS X (used only in next if-check)
- let possibleChromeEmulation = navigator.platform == 'Win32' || navigator.platform == 'MacIntel'
+ // Test for potential Chrome emulation on Windows or macOS X (used only in next if-check)
+ let possibleChromeEmulation = navigator.platform == 'Win32' || navigator.platform == 'MacIntel'
- // alert(sxx+'->'+device.formFactor);
- // Check for phony iOS devices (but don't undo for Chrome emulation used during development)
- if(this.OS == 'iOS' && !('ongesturestart' in window) && !possibleChromeEmulation) {
- this.OS='Android';
- }
+ // alert(sxx+'->'+device.formFactor);
+ // Check for phony iOS devices (but don't undo for Chrome emulation used during development)
+ if(this.OS == 'iOS' && !('ongesturestart' in window) && !possibleChromeEmulation) {
+ this.OS='Android';
+ }
- // Determine application or browser
- this.browser='web';
- if(this.OS == 'iOS' || this.OS.toLowerCase() == 'macosx') {
+ // Determine application or browser
+ this.browser='web';
+ if(this.OS == 'iOS' || this.OS.toLowerCase() == 'macosx') {
+ this.browser='safari';
+ }
+
+ var bMatch=/Firefox|Chrome|OPR|Safari|Edge/;
+ if(bMatch.test(navigator.userAgent)) {
+ if((navigator.userAgent.indexOf('Firefox') >= 0) && ('onmozorientationchange' in screen)) {
+ this.browser='firefox';
+ } else if(navigator.userAgent.indexOf('OPR') >= 0) {
+ this.browser='opera';
+ } else if(navigator.userAgent.indexOf(' Edge/') >= 0) {
+ // Edge is too common a word, so test for Edge/ :)
+ // Must come before Chrome and Safari test because
+ // Edge pretends to be both
+ this.browser='edge';
+ } else if(navigator.userAgent.indexOf('Chrome') >= 0) {
+ // This test must come before Safari test because on macOS,
+ // Chrome also reports "Safari"
+ this.browser='chrome';
+ } else if(navigator.userAgent.indexOf('Safari') >= 0) {
this.browser='safari';
}
-
- var bMatch=/Firefox|Chrome|OPR|Safari|Edge/;
- if(bMatch.test(navigator.userAgent)) {
- if((navigator.userAgent.indexOf('Firefox') >= 0) && ('onmozorientationchange' in screen)) {
- this.browser='firefox';
- } else if(navigator.userAgent.indexOf('OPR') >= 0) {
- this.browser='opera';
- } else if(navigator.userAgent.indexOf(' Edge/') >= 0) {
- // Edge is too common a word, so test for Edge/ :)
- // Must come before Chrome and Safari test because
- // Edge pretends to be both
- this.browser='edge';
- } else if(navigator.userAgent.indexOf('Chrome') >= 0) {
- // This test must come before Safari test because on macOS,
- // Chrome also reports "Safari"
- this.browser='chrome';
- } else if(navigator.userAgent.indexOf('Safari') >= 0) {
- this.browser='safari';
- }
- }
-
- if(possMacSpoof && this.browser == 'safari') {
- // Indistinguishable user agent string! We need a different test; fortunately, true macOS
- // Safari doesn't support TouchEvents. (Chrome does, though! Hence the filter above.)
- if(window['TouchEvent']) {
- this.OS='iOS';
- this.formFactor='tablet';
- this.dyPortrait=this.dyLandscape=0;
-
- // It's currently impossible to differentiate between iPhone and iPad here
- // except for by screen dimensions.
- let aspectRatio = screen.height / screen.width;
- if(aspectRatio < 1) {
- aspectRatio = 1 / aspectRatio;
- }
-
- // iPhones usually have a ratio of 16:9 (or 1.778) or higher, while iPads use 4:3 (or 1.333)
- if(aspectRatio > 1.6) {
- // Override - we'll treat this device as an iPhone.
- this.formFactor = 'phone';
- this.dyPortrait=this.dyLandscape=25;
- }
- }
- }
-
- this.colorScheme = utils.StyleConstants.prefersDarkMode() ? 'dark' : 'light';
- this.detected = true;
}
- /**
- * Returns a slimmer, web-core compatible version of this object.
- */
- public get coreSpec(): utils.DeviceSpec {
- return new utils.DeviceSpec(this.browser, this.formFactor, this.OS, this.touchable);
+ if(possMacSpoof && this.browser == 'safari') {
+ // Indistinguishable user agent string! We need a different test; fortunately, true macOS
+ // Safari doesn't support TouchEvents. (Chrome does, though! Hence the filter above.)
+ if(window['TouchEvent']) {
+ this.OS='iOS';
+ this.formFactor='tablet';
+ this.dyPortrait=this.dyLandscape=0;
+
+ // It's currently impossible to differentiate between iPhone and iPad here
+ // except for by screen dimensions.
+ let aspectRatio = screen.height / screen.width;
+ if(aspectRatio < 1) {
+ aspectRatio = 1 / aspectRatio;
+ }
+
+ // iPhones usually have a ratio of 16:9 (or 1.778) or higher, while iPads use 4:3 (or 1.333)
+ if(aspectRatio > 1.6) {
+ // Override - we'll treat this device as an iPhone.
+ this.formFactor = 'phone';
+ this.dyPortrait=this.dyLandscape=25;
+ }
+ }
}
+
+ this.colorScheme = StyleConstants.prefersDarkMode() ? 'dark' : 'light';
+ this.detected = true;
+ }
+
+ /**
+ * Returns a slimmer, web-core compatible version of this object.
+ */
+ public get coreSpec(): DeviceSpec {
+ return new DeviceSpec(this.browser, this.formFactor, this.OS, this.touchable);
}
}
\ No newline at end of file
diff --git a/web/src/engine/device-detect/tsconfig.bundled.json b/web/src/engine/device-detect/tsconfig.bundled.json
deleted file mode 100644
index 61ac455342..0000000000
--- a/web/src/engine/device-detect/tsconfig.bundled.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "extends": "./tsconfig.json",
-
- "compilerOptions": {
- "outFile": "../../../build/engine/device-detect/obj/index.bundled.js",
- },
-
- "references": [
- { "path": "../../../../common/web/keyman-version", "prepend": true },
- { "path": "../../../../common/web/utils", "prepend": true }
- ]
-}
diff --git a/web/src/engine/device-detect/tsconfig.json b/web/src/engine/device-detect/tsconfig.json
index 471f6f9b90..b63fa2cc79 100644
--- a/web/src/engine/device-detect/tsconfig.json
+++ b/web/src/engine/device-detect/tsconfig.json
@@ -2,18 +2,19 @@
"extends": "../../../../tsconfig-base.json",
"compilerOptions": {
- "allowJs": true,
+ "allowJs": false,
"inlineSources": true,
- "module": "none",
- "outFile": "../../../build/engine/device-detect/obj/index.js",
+ "module": "es6",
+ "moduleResolution": "Node",
"sourceMap": true,
- "target": "es5"
+ "target": "es5",
+ "baseUrl": "./",
+ "outDir": "../../../build/engine/device-detect/obj/",
+ "tsBuildInfoFile": "../../../build/engine/device-detect/obj/tsconfig.tsbuildinfo",
+ "rootDir": "."
},
- "files": [
- "kmwdevice.ts",
- "utils/styleConstants.ts", // goes with kmwdevice.ts
- ],
+ "include": [ "**/*.ts" ],
"references": [
{ "path": "../../../../common/web/keyman-version" },
diff --git a/web/src/engine/device-detect/utils/styleConstants.ts b/web/src/engine/device-detect/utils/styleConstants.ts
index 0ea2e3be22..90aa2fe307 100644
--- a/web/src/engine/device-detect/utils/styleConstants.ts
+++ b/web/src/engine/device-detect/utils/styleConstants.ts
@@ -1,34 +1,31 @@
-// Includes Device definitions, which may play a role in constant logic.
-///
+import { DeviceSpec } from "@keymanapp/web-utils/build/obj/index.js";
/*
* This file is intended for CSS-styling constants that see use with the OSK.
*/
-namespace com.keyman.utils {
- /**
- * Defines device-level constants used for CSS styling.
- */
- export class StyleConstants {
- constructor(device: com.keyman.utils.DeviceSpec) {
- // popupCanvasBackgroundColor
- if(device.OS == utils.OperatingSystem.Android) {
- this.popupCanvasBackgroundColor = '#999';
- } else {
- this.popupCanvasBackgroundColor = StyleConstants.prefersDarkMode() ? '#0f1319' : '#ffffff';
- }
+/**
+ * Defines device-level constants used for CSS styling.
+ */
+export default class StyleConstants {
+ constructor(device: DeviceSpec) {
+ // popupCanvasBackgroundColor
+ if(device.OS == DeviceSpec.OperatingSystem.Android) {
+ this.popupCanvasBackgroundColor = '#999';
+ } else {
+ this.popupCanvasBackgroundColor = StyleConstants.prefersDarkMode() ? '#0f1319' : '#ffffff';
}
-
- /**
- * Checks is a user's browser is in dark mode, if the feature is supported. Returns false otherwise.
- *
- * Thanks to https://stackoverflow.com/a/57795518 for this code.
- */
- static prefersDarkMode(): boolean {
- // Ensure the detector exists (otherwise, returns false)
- return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
- }
-
- public readonly popupCanvasBackgroundColor: string;
}
+
+ /**
+ * Checks is a user's browser is in dark mode, if the feature is supported. Returns false otherwise.
+ *
+ * Thanks to https://stackoverflow.com/a/57795518 for this code.
+ */
+ static prefersDarkMode(): boolean {
+ // Ensure the detector exists (otherwise, returns false)
+ return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
+ }
+
+ public readonly popupCanvasBackgroundColor: string;
}
\ No newline at end of file