mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-28 10:37:41 +00:00
Merge branch 'fix/core/10605-app-context-copy-strip-markers' into fix/core/10615-10616-10617-core-surrogates-and-app-context-markers-and-memory-leak
This commit is contained in:
commit
fe8eba176c
79 changed files with 246174 additions and 609 deletions
7
.github/workflows/deb-packaging.yml
vendored
7
.github/workflows/deb-packaging.yml
vendored
|
|
@ -115,7 +115,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
dist: [focal, jammy, lunar, mantic]
|
||||
dist: [focal, jammy, mantic, noble]
|
||||
arch: [amd64]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -241,15 +241,12 @@ jobs:
|
|||
esac
|
||||
|
||||
echo -e "::group::${COLOR_GREEN}Upload source package"
|
||||
cd keyman-srcpkg
|
||||
|
||||
dput_retry -U llso:ubuntu/jammy${destination} *_source.changes
|
||||
echo "::endgroup::"
|
||||
|
||||
echo -e "::group::${COLOR_GREEN}Uploading binary packages"
|
||||
cd ../keyman-binarypkgs
|
||||
pattern="keyman_${{ needs.sourcepackage.outputs.VERSION }}.+\+(.*)[0-9]_[^.]+.changes"
|
||||
for f in *.changes; do
|
||||
for f in *_amd64.changes; do
|
||||
if [[ $f =~ $pattern ]]; then
|
||||
dist=${BASH_REMATCH[1]}
|
||||
dput_retry -U llso:ubuntu/${dist}${destination} $f
|
||||
|
|
|
|||
59
HISTORY.md
59
HISTORY.md
|
|
@ -1,5 +1,48 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 17.0.260 alpha 2024-02-03
|
||||
|
||||
* chore(oem/fv): Update versions in keyboards.csv (#10606)
|
||||
* fix(linux): Fix path to artifacts when uploading to llso (#10609)
|
||||
* docs(linux): Update sample settings (#10610)
|
||||
* chore(linux): Fix API check if lines got removed in .symbols file (#10612)
|
||||
|
||||
## 17.0.259 alpha 2024-02-02
|
||||
|
||||
* feat(developer): double markers once again (#10541)
|
||||
* feat(core): ldml double marker, C++ side (#10563)
|
||||
* feat(core,developer): simplify markers (#10565)
|
||||
* chore(common): cancel earlier builds when new test builds triggered (#10593)
|
||||
* chore(linux): Use correct format specifier for `g_utf8_strlen` (#10599)
|
||||
* fix(linux): Fix version comparison (#10576)
|
||||
* chore(common): Add entries from 16.0.145 HISTORY.md (#10603)
|
||||
* chore(common): history missing message fixup (#10601)
|
||||
* chore(linux): Update debian changelog (#10596)
|
||||
|
||||
## 17.0.258 alpha 2024-02-01
|
||||
|
||||
* chore(developer): add unicode-license.txt for ldml keyboards data (#10568)
|
||||
* feat(core): ldml reorder marker processing (#10539)
|
||||
* chore(developer): kmc ldml build - call c8 directly for tests (#10522)
|
||||
* fix(web): app/webview control flow for initial layer (#10571)
|
||||
* chore(linux): Upgrade artifacts to v4 (#10577)
|
||||
* chore(linux): Fix the path of downloaded artifacts (#10594)
|
||||
|
||||
## 17.0.257 alpha 2024-01-31
|
||||
|
||||
* epic: Keyman Core normalization (#10390)
|
||||
* fix(web): OSK resource-loading promise's font-wait (#10506)
|
||||
* fix(web): documentation-keyboard font size, font scaling (#10507)
|
||||
* feat(web): testing page for font-load / key-scale interactions (#10534)
|
||||
* fix(web): proper await for special-osk font, use of it in key scaling (#10535)
|
||||
* fix(web): ensure use of actual key-cap / label font in key-scale ops (#10555)
|
||||
* docs: Update websites README.md (#10566)
|
||||
* fix(developer): kmc exit needs to wait for Sentry (#10558)
|
||||
* fix(developer): make sourcePath calculation for kmc-keyboard-info more lenient (#10559)
|
||||
* chore(developer): disable .js output for LDML keyboard compiler for 17.0 (#10560)
|
||||
* chore(linux): Replace Lunar with Noble (#10561)
|
||||
* chore(linux): Adjust test expectations for ICU >= 73 (#10573)
|
||||
|
||||
## 17.0.256 alpha 2024-01-30
|
||||
|
||||
* fix(linux): packaging on Ubuntu and Debian servers should give message instead of warning because we don't have patched ibus there (#10537)
|
||||
|
|
@ -26,7 +69,7 @@
|
|||
* fix(common): ldml: fix typo in kmc error message (#10484)
|
||||
* chore(developer): `--enable-source-maps` parameter for kmc wrapper (#10496)
|
||||
* fix(android/engine): Fix OSK widths (#10442)
|
||||
* (#10508)
|
||||
* fix(web): banner suggestion resizing after device rotation (#10508)
|
||||
|
||||
## 17.0.251 alpha 2024-01-24
|
||||
|
||||
|
|
@ -38,7 +81,7 @@
|
|||
* fix(web): osk responsiveness to held modifiers on legacy kbds (#10437)
|
||||
* fix(android/engine): Skip final globe key action (#10465)
|
||||
* feat(developer): marker normalization in .ts (#10443)
|
||||
* (#10445)
|
||||
* fix(android): hardware keystrokes now include mnemonic processing (#10445)
|
||||
|
||||
## 17.0.249 alpha 2024-01-22
|
||||
|
||||
|
|
@ -1196,7 +1239,7 @@
|
|||
|
||||
## 17.0.67 alpha 2023-03-14
|
||||
|
||||
* (developer):Update copyright period in License.rtf (#8425)
|
||||
* chore(developer): Update copyright period in License.rtf (#8425)
|
||||
* chore(common): support shorthand for build.sh (#8415)
|
||||
* chore(common): build script performance improvements (#8416)
|
||||
* chore(common): TS updates to non-sync'd packages, feature-esmodule merge conflict prevention (#8429)
|
||||
|
|
@ -1573,6 +1616,16 @@
|
|||
* chore: move to 17.0-alpha (#7577)
|
||||
* chore: Move to 17.0 alpha
|
||||
|
||||
## 16.0.145 stable 2024-02-01
|
||||
|
||||
* chore(linux): Add support for loong64 architecture (#10108)
|
||||
* chore(linux): Update debian changelog (#10122)
|
||||
* fix(web): Fix null error with legacy keyboards (#10177)
|
||||
* chore(ios): FV certificate key (#10229)
|
||||
* fix(oem/fv): Add fv_hulquminum_combine and fv_kwadacha_tsekene (#10285)
|
||||
* chore(developer): upgrade ngrok to v3 (#10360)
|
||||
* chore(linux): Build with webkitgtk 4.1 instead of 4.0 (#10574)
|
||||
|
||||
## 16.0.144 stable 2023-11-30
|
||||
|
||||
* chore(linux): Update debian changelog (#10046)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
17.0.257
|
||||
17.0.261
|
||||
|
|
@ -145,95 +145,8 @@ export class MarkerParser {
|
|||
* @returns the normalized string
|
||||
*/
|
||||
public static nfd_markers(s: string, forMatch?: boolean) : string {
|
||||
// Note: parallel to ldml::normalize_nfd_markers()
|
||||
if(!s) return s;
|
||||
/** Accumulate output normalized string */
|
||||
let out = '';
|
||||
/** 's' split into code point units */
|
||||
let a = [...s];
|
||||
/** index into `a` where the current segment starts */
|
||||
let seg_start = 0;
|
||||
/** index into 'a' following end of current segment */
|
||||
let seg_end = 0;
|
||||
/** iterator over 'a' */
|
||||
let i = 0;
|
||||
/** for parallelism with C++, end of string */
|
||||
let str_end = a.length;
|
||||
|
||||
/** map of all markers in the string */
|
||||
const mall : MarkerMap = [];
|
||||
// preprocess string, also check for markers
|
||||
const all_no_markers = MarkerParser.remove_markers(s, mall, forMatch);
|
||||
if(mall.length == 0) {
|
||||
// no markers, so just normalize as a single segment
|
||||
return all_no_markers.normalize("NFD");
|
||||
}
|
||||
|
||||
// split segments on grapheme cluster boundaries for processing
|
||||
const raw_segments = Array.from(graphemeSegmenter.segment(all_no_markers))
|
||||
.map(({ segment }) => segment)
|
||||
.map((c) => c.codePointAt(0));
|
||||
/**
|
||||
* Set of all code points which have a boundary before.
|
||||
* This parallels nfd->hasBoundaryBefore().
|
||||
*/
|
||||
const hasBoundaryBefore = new Set(raw_segments);
|
||||
|
||||
// now we'll loop through looking for normalization-safe subsegments of [seg_start, seg_end)
|
||||
// For example (sentinel mode) the following would be segments:
|
||||
// - A
|
||||
// - E\u0320\u0302
|
||||
// - U\u0320\m{marker}\u0300
|
||||
//
|
||||
// The marker will typically 'look' like an NFD-safe boundary, but it's not! It's part of the
|
||||
// subsegment, we want to skip over it. This is why we use parse_next_marker to look-ahead to see
|
||||
// if there is actually a marker under the iterator.
|
||||
//
|
||||
// We don't assume that the marker sentinel or regex appears as an NFD boundary, that is why
|
||||
// the parse function and the nfd function are called in parallel.
|
||||
|
||||
do {
|
||||
/** remainder of string i..end, for match */
|
||||
const rest = a.slice(i).join('');
|
||||
const p = MarkerParser.parse_next_marker(rest, forMatch);
|
||||
const have_marker = !!(p?.match);
|
||||
|
||||
// First, categorize the current character.
|
||||
|
||||
if (i === str_end) {
|
||||
// at end of string, so this is also the end of the segment.
|
||||
seg_end = i;
|
||||
} else if (hasBoundaryBefore.has(rest.codePointAt(0)) && !have_marker) {
|
||||
// it's an NFD safe boundary, but NOT a marker
|
||||
// (if it were a marker, we would fall through to consume the marker)
|
||||
// segment ends before this code point
|
||||
seg_end = i;
|
||||
// move the index past the NFD boundary
|
||||
i++;
|
||||
} else if(have_marker) {
|
||||
// we have a marker. move the index past the marker.
|
||||
i += [...p.match].length;
|
||||
} else {
|
||||
// non boundary. just move the index forward
|
||||
i++;
|
||||
}
|
||||
|
||||
// Next, if we found a segment boundary (which includes
|
||||
// end of string) process it
|
||||
|
||||
if(seg_end !== seg_start) {
|
||||
// re-join the segment from the iterated code points
|
||||
const segment = a.slice(seg_start, seg_end).join('');
|
||||
const m : MarkerMap = [];
|
||||
// process the subsegment
|
||||
const segment_nfd = MarkerParser.nfd_markers_segment(segment, m, forMatch);
|
||||
// append the processed segment to the output buffer
|
||||
out = out + segment_nfd;
|
||||
// the end of this segment is the start of the next one.
|
||||
seg_start = seg_end;
|
||||
}
|
||||
} while (seg_end != str_end); // until the last codepoint processed
|
||||
return out;
|
||||
const m : MarkerMap = [];
|
||||
return this.nfd_markers_segment(s, m, forMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -283,8 +196,8 @@ export class MarkerParser {
|
|||
}
|
||||
/** output string */
|
||||
let out = '';
|
||||
/** for checking: the total number of markers expected */
|
||||
const max_markers = map.length;
|
||||
/** for checking: the total number of markers expected, skipping end markers */
|
||||
const max_markers = map.filter(({ end }) => !end).length;
|
||||
/** for checking: the number of markers we've written */
|
||||
let written_markers = 0;
|
||||
/** we are going to mutate the map, so copy it */
|
||||
|
|
@ -292,39 +205,36 @@ export class MarkerParser {
|
|||
// First, add back all 'MARKER_BEFORE_EOT' markers
|
||||
while (map2.length && map2[map2.length - 1].ch === MARKER_BEFORE_EOT) {
|
||||
// remove from list
|
||||
const { marker } = map2.pop();
|
||||
out = MarkerParser.prepend_marker(out, marker, forMatch);
|
||||
written_markers++;
|
||||
const { marker, end } = map2.pop();
|
||||
if (!end) {
|
||||
out = MarkerParser.prepend_marker(out, marker, forMatch);
|
||||
written_markers++;
|
||||
}
|
||||
}
|
||||
// Then, take each codepoint (from back to front)
|
||||
for (let p of [...s].reverse()) {
|
||||
// reverse order code units, prepend to out
|
||||
out = p + out;
|
||||
|
||||
// 1. we write any markers which match the output string in order,
|
||||
// and pop them off the list as dones
|
||||
while(map2.length > 0 && map2[map2.length-1].ch === p) {
|
||||
const { marker } = map2.pop();
|
||||
if (marker !== constants.marker_no_index) { // if not already written
|
||||
out = MarkerParser.prepend_marker(out, marker, forMatch);
|
||||
written_markers++;
|
||||
// no need to update .marker here, we're about to pop it
|
||||
}
|
||||
}
|
||||
// 2. now, any out of order markers. iterate with an index so we can record.
|
||||
// will skip this if map2.length < 2
|
||||
for (let i = map2.length - 2; i >= 0; i--) {
|
||||
const { ch, marker } = map2[i]; // don't pop here, as it might not be the right char
|
||||
if (ch === p && marker !== constants.marker_no_index) {
|
||||
out = MarkerParser.prepend_marker(out, marker, forMatch);
|
||||
written_markers++;
|
||||
map2[i].marker = constants.marker_no_index; // mark as written
|
||||
for (let i = map2.length - 1; i >= 0; i--) {
|
||||
const { ch, marker, processed, end } = map2[i];
|
||||
if (ch === p && !processed) {
|
||||
map2[i].processed = true; // mark as processed
|
||||
if (end) {
|
||||
break; // exit loop
|
||||
} else {
|
||||
out = MarkerParser.prepend_marker(out, marker, forMatch);
|
||||
written_markers++;
|
||||
}
|
||||
} else if (map2[map2.length-1]?.processed) {
|
||||
// keep the list as short as possible
|
||||
map2.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
// validate that we consumed all markers
|
||||
if(written_markers !== max_markers) {
|
||||
throw Error(`Internal Error: We should have written ${max_markers} markers but only wrote ${written_markers}`);
|
||||
throw Error(`Internal Error: should have written ${max_markers} markers but only wrote ${written_markers}`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -343,18 +253,31 @@ export class MarkerParser {
|
|||
let last_markers: number[] = [];
|
||||
/** input string, split into codepoint runs */
|
||||
let a: string[] = [...s];
|
||||
/** were any markers found? */
|
||||
let had_markers = false;
|
||||
|
||||
/**
|
||||
* subfunc: add all markers in the pending (last_markers) queue
|
||||
* @param l string the marker is 'glued' to, or '' for end
|
||||
*/
|
||||
function add_pending_markers(l: string): void {
|
||||
// first char, or, marker-before-eot. Must be first char of NFD sequence
|
||||
const ch = (l === '') ? MARKER_BEFORE_EOT : [...(l.normalize("NFD"))][0];
|
||||
function add_pending_markers(l: string){
|
||||
// first char, or, marker-before-eot
|
||||
const glueChars = (l === '') ? [MARKER_BEFORE_EOT] : [...(l.normalize("NFD"))];
|
||||
const glue = glueChars[0];
|
||||
// push the 'end' value
|
||||
map.push({ ch: glue, end: true });
|
||||
while(last_markers.length) {
|
||||
const marker = last_markers[0];
|
||||
last_markers = last_markers.slice(1); // pop from front
|
||||
map.push({ ch, marker });
|
||||
map.push({ ch: glue, marker });
|
||||
}
|
||||
// now, push the rest of the glue chars as an NFD sequence.
|
||||
// For example, `\m{m}\u0344` will create the following stream:
|
||||
// { ch: 0308, end: true}
|
||||
// { ch: 0308, marker: 1}
|
||||
// { ch: 0301, end: true} // added because of decomp
|
||||
for(const ch of glueChars.slice(1)) {
|
||||
map.push({ ch, end: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -369,6 +292,7 @@ export class MarkerParser {
|
|||
a = a.slice(1); // move forward 1 codepoint
|
||||
} else {
|
||||
// found a marker
|
||||
had_markers = true;
|
||||
const { marker, match } = p;
|
||||
if ((marker == constants.marker_any_index) ||
|
||||
(marker >= constants.marker_min_index && marker <= constants.marker_max_index)) {
|
||||
|
|
@ -379,8 +303,12 @@ export class MarkerParser {
|
|||
a = a.slice([...match].length); // move forward over matched marker
|
||||
}
|
||||
}
|
||||
// finally, add any remaining markers at the end of the string
|
||||
// add any remaining markers at the end of the string
|
||||
add_pending_markers('');
|
||||
if (!had_markers) {
|
||||
// no markers were found. clear out the map.
|
||||
map = [];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -422,19 +350,21 @@ export class MarkerParser {
|
|||
};
|
||||
|
||||
/** special noncharacter value denoting end of string */
|
||||
const MARKER_BEFORE_EOT = '\ufffe';
|
||||
export const MARKER_BEFORE_EOT = '\ufffe';
|
||||
/** matcher for a sentinel */
|
||||
const PARSE_SENTINEL_MARKER = new RegExp(`^${MarkerParser.ANY_MARKER_MATCH}`);
|
||||
/** matcher for a regex marker, either single or any */
|
||||
const PARSE_REGEX_MARKER = /^\\uffff\\u0008(?:(\\u[0-9a-fA-F]{4})|(\[\\u[0-9a-fA-F]{4}-\\u[0-9a-fA-F]{4}\]))/;
|
||||
/** a grapheme cluster segmenter for finding NFD boundaries */
|
||||
const graphemeSegmenter = new Intl.Segmenter(['und'], { granularity: 'grapheme' });
|
||||
|
||||
export interface MarkerEntry {
|
||||
/** code point 'glued' to, or MARKER_BEFORE_EOT */
|
||||
/** code point 'glued' to, or MARKER_BEFORE_EOT */
|
||||
ch? : string;
|
||||
/** marker number, 1-based */
|
||||
marker? : number;
|
||||
/** true if processed */
|
||||
processed? : boolean;
|
||||
/** true if the end of the entries */
|
||||
end? : boolean;
|
||||
};
|
||||
|
||||
/** list of marker entries, from remove_markers */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'mocha';
|
||||
import { assert } from 'chai';
|
||||
import { ElementParser, ElementSegment, ElementType, MarkerParser, MarkerResult, OrderedStringList, VariableParser } from '../../src/ldml-keyboard/pattern-parser.js';
|
||||
import { ElementParser, ElementSegment, ElementType, MARKER_BEFORE_EOT, MarkerMap, MarkerParser, MarkerResult, OrderedStringList, VariableParser } from '../../src/ldml-keyboard/pattern-parser.js';
|
||||
import { constants } from '@keymanapp/ldml-keyboard-constants';
|
||||
import { KMXFile } from '../../src/kmx/kmx.js';
|
||||
import Hexy from 'hexy';
|
||||
|
|
@ -339,6 +339,18 @@ describe('Test of nfd_markers()', () => {
|
|||
assert.equal(dst, <string>expect, `mapping ${src} with forMatch=${forMatch}`);
|
||||
});
|
||||
});
|
||||
it('should be able to remove_markers() with composed chars', () => {
|
||||
const src = '\uffff\u0008\u0001\u0344';
|
||||
const m : MarkerMap = [];
|
||||
const dst = MarkerParser.remove_markers(src, m, false);
|
||||
assert.equal(dst, '\u0344'); // nfc
|
||||
assert.sameDeepOrderedMembers(m, [
|
||||
{ ch: '\u0308', end: true },
|
||||
{ ch: '\u0308', marker: 1},
|
||||
{ ch: '\u0301', end: true},
|
||||
{ ch: MARKER_BEFORE_EOT, end: true },
|
||||
]);
|
||||
});
|
||||
it('should normalize as expected', () => {
|
||||
// this is a little bit simpler in structure than what's in test_transforms.cpp,
|
||||
// see there for more complicated cases and discussion
|
||||
|
|
@ -379,6 +391,30 @@ describe('Test of nfd_markers()', () => {
|
|||
["\u0995\uFFFF\u0008\u0001\u09CB", "\u0995\uFFFF\u0008\u0001\u09C7\u09BE"],
|
||||
["\u0995\u09BE\uFFFF\u0008\u0001\u09C7"],
|
||||
["\u03B5\uFFFF\u0008\u0001\u0344", "\u03B5\uFFFF\u0008\u0001\u0308\u0301"],
|
||||
|
||||
// double marker -
|
||||
["e\uffff\u0008\u0001\u0300\u0320\u0300", "e\u0320\uffff\u0008\u0001\u0300\u0300"],
|
||||
// double marker - no change
|
||||
["e\u0320\uffff\u0008\u0001\u0300\u0300", "e\u0320\uffff\u0008\u0001\u0300\u0300"],
|
||||
|
||||
// Double marker with greek!
|
||||
["\u03B5\uFFFF\u0008\u0001\u0344\uFFFF\u0008\u0002\u0344\uFFFF\u0008\u0003",
|
||||
"\u03B5\uFFFF\u0008\u0001\u0308\u0301\uFFFF\u0008\u0002\u0308\u0301\uFFFF\u0008\u0003"],
|
||||
|
||||
// double marker -
|
||||
["e\uffff\u0008\u0001\u0300\u0320\u0300", "e\u0320\uffff\u0008\u0001\u0300\u0300"],
|
||||
// double marker - no change
|
||||
["e\u0320\uffff\u0008\u0001\u0300\u0300", "e\u0320\uffff\u0008\u0001\u0300\u0300"],
|
||||
// double marker - in front of second, no change
|
||||
["e\u0320\u0300\uffff\u0008\u0001\u0300", "e\u0320\u0300\uffff\u0008\u0001\u0300"],
|
||||
// double marker - in front of second, with segment reordering
|
||||
["e\u0300\u0320\uffff\u0008\u0001\u0300", "e\u0320\u0300\uffff\u0008\u0001\u0300"],
|
||||
// double marker - alternate pattern with reordering needed
|
||||
["e\u0300\uffff\u0008\u0001\u0300\u0320", "e\u0320\u0300\uffff\u0008\u0001\u0300"],
|
||||
// triple diacritic + marker - reordering needed
|
||||
["e\u0300\uffff\u0008\u0001\u0300\u0320\u0300", "e\u0320\u0300\uffff\u0008\u0001\u0300\u0300"],
|
||||
|
||||
|
||||
];
|
||||
|
||||
for (let i = 0; i < src_expect.length; i++) {
|
||||
|
|
@ -390,7 +426,14 @@ describe('Test of nfd_markers()', () => {
|
|||
// regex
|
||||
forMatch = true;
|
||||
}
|
||||
const dst = MarkerParser.nfd_markers(src, forMatch);
|
||||
let dst;
|
||||
try {
|
||||
dst = MarkerParser.nfd_markers(src, forMatch);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
assert.fail(`#${i+1} normalizing '${src}'`);
|
||||
}
|
||||
assert.ok(dst, `#${i+1} normalizing '${src}'`);
|
||||
assert.equal(dst, exp, `#${i+1} normalizing '${src}' forMatch=${forMatch} (got \n${hex_str(dst)} expected \n${hex_str(exp)}) `);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -58,7 +58,30 @@ bool normalize_nfd(std::u16string &str) {
|
|||
return normalize(nfd, str, status);
|
||||
}
|
||||
|
||||
static void add_back_markers(std::u32string &str, const std::u32string &src, marker_map &map, marker_encoding encoding) {
|
||||
marker_entry::marker_entry(char32_t c) : ch(c), marker(LDML_MARKER_NO_INDEX), processed(false), end(true) {
|
||||
}
|
||||
|
||||
marker_entry::marker_entry(char32_t c, marker_num n) : ch(c), marker(n), processed(false), end(false) {
|
||||
|
||||
}
|
||||
|
||||
size_t count_markers(const marker_map &map) {
|
||||
size_t m = 0;
|
||||
for (auto i = map.begin(); i < map.end(); i++) {
|
||||
// add all actual markers
|
||||
if (i->marker != LDML_MARKER_NO_INDEX) {
|
||||
m++;
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void add_back_markers(std::u32string &str, const std::u32string &src, marker_map &map, marker_encoding encoding) {
|
||||
if (map.empty()) {
|
||||
// quick check, nothing to do if no markers
|
||||
str = src;
|
||||
return;
|
||||
}
|
||||
// need to reconstitute.
|
||||
marker_map map2(map); // make a copy of the map
|
||||
// clear the string
|
||||
|
|
@ -66,13 +89,16 @@ static void add_back_markers(std::u32string &str, const std::u32string &src, mar
|
|||
// iterator over the marker map
|
||||
auto marki = map2.rbegin();
|
||||
// number of markers left to process
|
||||
size_t processed_count = map2.size();
|
||||
size_t max_markers = count_markers(map);
|
||||
size_t processed_markers = 0;
|
||||
|
||||
// add any end-of-text markers
|
||||
while(marki != map2.rend() && marki->first == MARKER_BEFORE_EOT) {
|
||||
prepend_marker(str, marki->second, encoding);
|
||||
processed_count--;
|
||||
marki->second = 0; // mark as done
|
||||
while(marki != map2.rend() && marki->ch == MARKER_BEFORE_EOT) {
|
||||
if (!marki->end) {
|
||||
prepend_marker(str, marki->marker, encoding);
|
||||
processed_markers++;
|
||||
}
|
||||
marki->processed = true; // mark as done
|
||||
marki++;
|
||||
}
|
||||
|
||||
|
|
@ -81,26 +107,25 @@ static void add_back_markers(std::u32string &str, const std::u32string &src, mar
|
|||
const auto ch = *p;
|
||||
str.insert(0, 1, ch); // prepend
|
||||
|
||||
// add the markers at the end of the list first.
|
||||
for (; marki != map2.rend() && marki->first == ch; marki++) {
|
||||
if (marki->second != 0) {
|
||||
// set to '0' if already applied
|
||||
prepend_marker(str, marki->second, encoding);
|
||||
marki->second = 0; // mark as already applied
|
||||
processed_count--;
|
||||
}
|
||||
// remove all processed entries, outside of an iterator
|
||||
while (!map2.empty() && map2.back().processed) {
|
||||
map2.pop_back();
|
||||
}
|
||||
|
||||
// now, add any out of order markers.
|
||||
for (auto marki2 = marki; marki2 != map2.rend(); marki2++) {
|
||||
if (marki2->second != 0 && marki2->first == ch) {
|
||||
prepend_marker(str, marki2->second, encoding);
|
||||
marki2->second = 0; // mark as already applied
|
||||
processed_count--;
|
||||
for (auto i = map2.rbegin(); i != map2.rend(); i++) {
|
||||
if ((i->ch == ch) && !(i->processed)) {
|
||||
i->processed = true;
|
||||
if (i->end) {
|
||||
break;
|
||||
} else {
|
||||
prepend_marker(str, i->marker, encoding);
|
||||
processed_markers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(processed_count == 0); // assert that we consumed all marks
|
||||
assert(max_markers == processed_markers); // assert that we consumed all marks
|
||||
}
|
||||
|
||||
bool normalize_nfd_markers_segment(std::u32string &str, marker_map &map, marker_encoding encoding) {
|
||||
|
|
@ -110,9 +135,6 @@ bool normalize_nfd_markers_segment(std::u32string &str, marker_map &map, marker_
|
|||
std::u32string str_unmarked_nfd = str_unmarked;
|
||||
if(!normalize_nfd(str_unmarked_nfd)) {
|
||||
return false; // normalize failed.
|
||||
} else if (map.size() == 0) {
|
||||
// no markers. Return the normalized unmarked str
|
||||
str = str_unmarked_nfd;
|
||||
} else if (str_unmarked_nfd == str_unmarked) {
|
||||
// Normalization produced no change when markers were removed.
|
||||
// So, we'll call this a no-op.
|
||||
|
|
@ -198,84 +220,8 @@ bool normalize_nfd_markers(std::u32string &str, marker_encoding encoding) {
|
|||
// quick check - don't bother if the string is empty
|
||||
if(str.empty()) return true;
|
||||
|
||||
// we're going to need an NFD normalizer
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
const icu::Normalizer2 *nfd = icu::Normalizer2::getNFDInstance(status);
|
||||
if (!UASSERT_SUCCESS(status)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* output string, we'll accumulate the normalized string here
|
||||
*/
|
||||
std::u32string out;
|
||||
/**
|
||||
* this is the beginning of the current segment to process.
|
||||
* it will also be the beginning of the string OR the end of the previous segment.
|
||||
*/
|
||||
std::u32string::const_iterator seg_start = str.begin();
|
||||
/** end of the current segment. This will be == seg_start unless a new segment is identified. */
|
||||
std::u32string::const_iterator seg_end = str.begin();
|
||||
/** iterator through this loop */
|
||||
std::u32string::const_iterator i = str.begin();
|
||||
|
||||
// now we'll loop through looking for normalization-safe subsegments of [seg_start, seg_end)
|
||||
// For example (sentinel mode) the following would be segments:
|
||||
// - A
|
||||
// - E\u0320\u0302
|
||||
// - U\u0320\m{marker}\u0300
|
||||
//
|
||||
// The marker will typically 'look' like an NFD-safe boundary, but it's not! It's part of the
|
||||
// subsegment, we want to skip over it. This is why we use parse_next_marker to look-ahead to see
|
||||
// if there is actually a marker under the iterator.
|
||||
//
|
||||
// We don't assume that the marker sentinel or regex appears as an NFD boundary, that is why
|
||||
// the parse function and the nfd function are called in parallel.
|
||||
|
||||
do {
|
||||
|
||||
// First, check if there's a marker.
|
||||
|
||||
// temporary iterator so we don't move 'i' unnecessarily. points to end of marker sequence.
|
||||
std::u32string::const_iterator marker_end = i;
|
||||
// true if marker detected
|
||||
bool have_marker = parse_next_marker(marker_end, str.end(), encoding) != LDML_MARKER_NO_INDEX;
|
||||
|
||||
|
||||
// Now, categorize the string. Is there a segment boundary BEFORE 'i'?
|
||||
if (i == str.end()) {
|
||||
// end of string, mark as the end of a segment
|
||||
// this will cause the final segment to be processed and the loop exitted.
|
||||
seg_end = i;
|
||||
} else if (nfd->hasBoundaryBefore(*i) && !have_marker) {
|
||||
// 'i' is the beginning of an NFD safe boundary (such as a base character).
|
||||
// but it's also not on a marker (which would be included in the segment)
|
||||
seg_end = i;
|
||||
// we also need to advance so we can pick up the next char.
|
||||
i++;
|
||||
} else if (have_marker) {
|
||||
// it's actually a marker. so, advance over it.
|
||||
i = marker_end;
|
||||
} else {
|
||||
// some other non boundary char.
|
||||
// advance without further drama
|
||||
i++;
|
||||
}
|
||||
|
||||
// Finally, process any identified segment.
|
||||
if (seg_end != seg_start) { // is the segment non-empty?
|
||||
std::u32string segment(seg_start, seg_end);
|
||||
marker_map m;
|
||||
if (!normalize_nfd_markers_segment(segment, m, encoding)) {
|
||||
return false;
|
||||
}
|
||||
out.append(segment);
|
||||
seg_start = seg_end;
|
||||
}
|
||||
} while(seg_end != str.end()); // repeat until the last codepoint has been processed in a segment
|
||||
// update output string
|
||||
str = out;
|
||||
return true;
|
||||
marker_map m;
|
||||
return normalize_nfd_markers_segment(str, m, encoding);
|
||||
}
|
||||
|
||||
// TODO-LDML: cleanup
|
||||
|
|
@ -380,14 +326,6 @@ KMX_DWORD parse_hex_quad(const km_core_usv hex_str[]) {
|
|||
return mark_no;
|
||||
}
|
||||
|
||||
/** add the list to the map */
|
||||
inline void add_markers_to_map(marker_map &markers, char32_t marker_ch, const marker_list &list) {
|
||||
for (auto i = list.begin(); i < list.end(); i++) {
|
||||
// marker_ch is duplicate, but keeps the structure more shallow.
|
||||
markers.emplace_back(marker_ch, *i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any markers, if needed. Inlined because we need to run it twice.
|
||||
* @param markers marker map or nullptr
|
||||
|
|
@ -402,16 +340,16 @@ add_pending_markers(
|
|||
const std::u32string::const_iterator &end,
|
||||
const icu::Normalizer2 *nfd) {
|
||||
// quick check to see if there's no work to do.
|
||||
if(markers == nullptr || last_markers.empty()) {
|
||||
if(markers == nullptr) {
|
||||
return;
|
||||
}
|
||||
/** which character this marker is 'glued' to. */
|
||||
char32_t marker_ch;
|
||||
icu::UnicodeString decomposition;
|
||||
if (last == end) {
|
||||
// at end of text, so use a special value to indicate 'EOT'.
|
||||
marker_ch = MARKER_BEFORE_EOT;
|
||||
} else {
|
||||
icu::UnicodeString decomposition;
|
||||
auto ch = *last; // non-normalized character
|
||||
|
||||
// if the character is composed, we need to use the first decomposed char
|
||||
|
|
@ -419,14 +357,28 @@ add_pending_markers(
|
|||
if(!nfd->getDecomposition(ch, decomposition)) {
|
||||
// char does not have a decomposition - so it may be used for the glue
|
||||
marker_ch = ch;
|
||||
decomposition.remove(); // no other entries needed
|
||||
} else {
|
||||
// 'glue' is the first codepoint of the decomposition.
|
||||
marker_ch = decomposition.char32At(0);
|
||||
if (decomposition.countChar32() == 1) {
|
||||
decomposition.remove(); // no other entries needed
|
||||
} // else: will add the remainder below
|
||||
}
|
||||
}
|
||||
|
||||
markers->emplace_back(marker_ch);
|
||||
// now, update the map with these markers (in order) on this character.
|
||||
add_markers_to_map(*markers, marker_ch, last_markers);
|
||||
for (auto i = last_markers.begin(); i < last_markers.end(); i++) {
|
||||
// marker_ch is duplicate, but keeps the structure more shallow.
|
||||
markers->emplace_back(marker_ch, *i);
|
||||
}
|
||||
// add any further entries due to decomposition
|
||||
if (!decomposition.isEmpty()) {
|
||||
// We already added the base char above, add teh rest
|
||||
for (auto i=1; i<decomposition.countChar32(); i++) {
|
||||
markers->emplace_back(decomposition.char32At(i));
|
||||
}
|
||||
}
|
||||
// clear the list
|
||||
last_markers.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,12 +44,33 @@ typedef KMX_DWORD marker_num;
|
|||
/** list of marker numbers */
|
||||
typedef std::deque<marker_num> marker_list;
|
||||
|
||||
/** map from one char to one entry */
|
||||
typedef std::pair<char32_t, marker_num> marker_entry;
|
||||
struct marker_entry {
|
||||
/** code point glued to or MARKER_BEFORE_EOT */
|
||||
char32_t ch;
|
||||
/** marker number */
|
||||
marker_num marker;
|
||||
/** true if processed */
|
||||
bool processed;
|
||||
/** true if an end of this codepoint */
|
||||
bool end;
|
||||
|
||||
/** add an 'end' entry */
|
||||
marker_entry(char32_t ch);
|
||||
/** add a 'marker' entry */
|
||||
marker_entry(char32_t ch, marker_num marker);
|
||||
|
||||
bool operator==(const marker_entry &o) const {
|
||||
// don't test 'processed'
|
||||
return (ch == o.ch) && (marker == o.marker) && (end == o.end);
|
||||
}
|
||||
};
|
||||
|
||||
/** map from following-char to marker numbers, in front to back order */
|
||||
typedef std::deque<marker_entry> marker_map;
|
||||
|
||||
/** count number of non-end entries */
|
||||
size_t count_markers(const marker_map &map);
|
||||
|
||||
/** Normalize a u32string inplace to NFD. @return false on failure */
|
||||
bool normalize_nfd(std::u32string &str);
|
||||
/** Normalize a u16string inplace to NFD. @return false on failure */
|
||||
|
|
@ -93,6 +114,8 @@ void prepend_hex_quad(std::u32string &str, KMX_DWORD marker);
|
|||
/** parse 0001...FFFF into a KMX_DWORD. Returns 0 on failure */
|
||||
KMX_DWORD parse_hex_quad(const km_core_usv hex_str[]);
|
||||
|
||||
/** re-add markers */
|
||||
void add_back_markers(std::u32string &str, const std::u32string &src, marker_map &map, marker_encoding encoding);
|
||||
|
||||
// bool normalize_nfc_markers(std::u16string &str, marker_encoding encoding) {
|
||||
// marker_map m;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace core {
|
|||
ldml_processor::ldml_processor(path const & kb_path, const std::vector<uint8_t> &data)
|
||||
: abstract_processor(
|
||||
keyboard_attributes(kb_path.stem(), KM_CORE_LMDL_PROCESSOR_VERSION, kb_path.parent(), {})
|
||||
), _valid(false), transforms(), bksp_transforms(), keys()
|
||||
), _valid(false), transforms(), bksp_transforms(), keys(), normalization_disabled(false)
|
||||
{
|
||||
|
||||
if(data.size() <= sizeof(kmx::COMP_KEYBOARD_EX)) {
|
||||
|
|
@ -106,6 +106,11 @@ ldml_processor::ldml_processor(path const & kb_path, const std::vector<uint8_t>
|
|||
return; // failed to load
|
||||
}
|
||||
}
|
||||
if (kplus.meta != nullptr) {
|
||||
if (kplus.meta->settings & LDML_META_SETTINGS_NORMALIZATION_DISABLED) {
|
||||
normalization_disabled = true;
|
||||
}
|
||||
}
|
||||
// Only valid if we reach here
|
||||
DebugLog("_valid = true");
|
||||
_valid = true;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ class ldml_processor : public abstract_processor {
|
|||
|
||||
bool
|
||||
supports_normalization() const override {
|
||||
return true;
|
||||
return !normalization_disabled;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -111,6 +111,7 @@ class ldml_processor : public abstract_processor {
|
|||
bool _valid;
|
||||
std::unique_ptr<ldml::transforms> transforms, bksp_transforms;
|
||||
ldml::vkeys keys;
|
||||
bool normalization_disabled;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -331,15 +331,19 @@ reorder_group::apply(std::u32string &str) const {
|
|||
/** did we match anything */
|
||||
bool some_match = false;
|
||||
|
||||
// markers need to 'pass through' reorders. remove and re-add if needed
|
||||
marker_map markers;
|
||||
std::u32string out = remove_markers(str, markers, plain_sentinel);
|
||||
|
||||
// get a baseline sort key
|
||||
auto sort_keys = reorder_sort_key::from(str);
|
||||
auto sort_keys = reorder_sort_key::from(out);
|
||||
|
||||
// apply ALL reorders in the group.
|
||||
for (const auto &r : list) {
|
||||
// work backward from end of string forward
|
||||
// That is, see if "abc" matches "abc" or "ab" or "a"
|
||||
for (size_t s = str.size(); s > 0; s--) {
|
||||
size_t submatch = r.match_end(str, 0, s);
|
||||
for (size_t s = out.size(); s > 0; s--) {
|
||||
size_t submatch = r.match_end(out, 0, s);
|
||||
if (submatch != 0) {
|
||||
#if KMXPLUS_DEBUG_TRANSFORM
|
||||
DebugTran("Matched: %S (off=%d, len=%d)", str.c_str(), 0, s);
|
||||
|
|
@ -367,18 +371,6 @@ reorder_group::apply(std::u32string &str) const {
|
|||
}
|
||||
#endif
|
||||
|
||||
// TODO-LDML: for now, assume matches entire string.
|
||||
// A needed optimization here would be to detect a common substring
|
||||
// at the end of the old and new strings, and keep the match_len
|
||||
// minimal. This could reduce thrash in core's context.
|
||||
// However, the calling code does check for a common substring with mismatch()
|
||||
size_t match_len = str.size();
|
||||
|
||||
// 'prefix' is the unmatched string before the match
|
||||
// TODO-LDML: right now, this is empty, because match_len is the entire size.
|
||||
std::u32string prefix = str;
|
||||
prefix.resize(str.size() - match_len); // just the part before the matched part.
|
||||
|
||||
// Now, we need to actually do the sorting, but we must only sort
|
||||
// 'runs' beginning with 0-weight keys.
|
||||
|
||||
|
|
@ -420,7 +412,7 @@ reorder_group::apply(std::u32string &str) const {
|
|||
}
|
||||
// recombine into a string by pulling out the 'ch' value
|
||||
// that's in each sortkey element.
|
||||
std::u32string newSuffix;
|
||||
out.clear(); // will re-add all text
|
||||
signed char q = sort_keys.begin()->quaternary; // start with the first quaternary
|
||||
for (auto e = sort_keys.begin(); e < sort_keys.end(); e++, q++) {
|
||||
if (q != e->quaternary) {
|
||||
|
|
@ -428,13 +420,12 @@ reorder_group::apply(std::u32string &str) const {
|
|||
applied = true;
|
||||
}
|
||||
// collect the characters
|
||||
newSuffix.append(1, e->ch);
|
||||
out.append(1, e->ch);
|
||||
}
|
||||
if (applied) {
|
||||
str.resize(prefix.size());
|
||||
str.append(newSuffix);
|
||||
} else {
|
||||
if (!applied) {
|
||||
DebugTran("Skip: sorting caused no reordering");
|
||||
// exit early to avoid string copying and possibly marker re-adding.
|
||||
return false; // no change
|
||||
}
|
||||
#if KMXPLUS_DEBUG_TRANSFORM
|
||||
DebugTran("Sorted sortkey");
|
||||
|
|
@ -442,7 +433,8 @@ reorder_group::apply(std::u32string &str) const {
|
|||
r.dump();
|
||||
}
|
||||
#endif
|
||||
return applied;
|
||||
add_back_markers(str, out, markers, plain_sentinel);
|
||||
return true; // updated
|
||||
}
|
||||
|
||||
transform_entry::transform_entry(const transform_entry &other)
|
||||
|
|
|
|||
|
|
@ -21,4 +21,46 @@
|
|||
<check result="a\u0332\u0305xz" />
|
||||
</test>
|
||||
</tests>
|
||||
<tests name="onekey" >
|
||||
<!-- same as above, but a single key-->
|
||||
<test name="1short">
|
||||
<startContext to="" />
|
||||
<keystroke key="test0" />
|
||||
<check result="a\u0305x" />
|
||||
<keystroke key="o"/>
|
||||
<backspace />
|
||||
<!-- no change -->
|
||||
<check result="a\u0305x" />
|
||||
</test>
|
||||
<test name="2longer">
|
||||
<startContext to="" />
|
||||
<keystroke key="test1" />
|
||||
<check result="a\u0332\u0305xz" />
|
||||
<keystroke key="o"/>
|
||||
<backspace />
|
||||
<!-- no change -->
|
||||
<check result="a\u0332\u0305xz" />
|
||||
</test>
|
||||
</tests>
|
||||
<tests name="marker" >
|
||||
<!-- same as above, but a single key with markers -->
|
||||
<test name="1short">
|
||||
<startContext to="" />
|
||||
<keystroke key="markertest0" />
|
||||
<check result="a\u0305x" />
|
||||
<keystroke key="o"/>
|
||||
<backspace />
|
||||
<!-- no change -->
|
||||
<check result="a\u0305x" />
|
||||
</test>
|
||||
<test name="2longer">
|
||||
<startContext to="" />
|
||||
<keystroke key="markertest1" />
|
||||
<check result="a\u0332\u0305xz" />
|
||||
<keystroke key="o"/>
|
||||
<backspace />
|
||||
<!-- no change -->
|
||||
<check result="a\u0332\u0305xz" />
|
||||
</test>
|
||||
</tests>
|
||||
</keyboardTest3>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
Test Keyboard from Spec
|
||||
see https://keyman.com/keyboards/sil_boonkit
|
||||
-->
|
||||
|
||||
<!DOCTYPE keyboard3 SYSTEM "../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard3.dtd">
|
||||
<keyboard3 locale="en-t-k0-esk" conformsTo="techpreview">
|
||||
<info author="srl295" indicator="🙀" layout="qwerty" name="esk reorder test"/>
|
||||
|
|
@ -19,6 +14,10 @@
|
|||
<key id="overbar" output="${overbar}" />
|
||||
<key id="underbar" output="${underbar}" />
|
||||
<key id="circumflex" output="${circumflex}" />
|
||||
<key id="test0" output="ax${overbar}" />
|
||||
<key id="markertest0" output="a\m{m4}x\m{marker0}${overbar}" />
|
||||
<key id="test1" output="az${overbar}x${underbar}" />
|
||||
<key id="markertest1" output="a\m{m0}z\m{m1}${overbar}\m{m2}x\m{m3}${underbar}\m{m3}" />
|
||||
</keys>
|
||||
|
||||
<layers formId="us">
|
||||
|
|
@ -29,6 +28,12 @@
|
|||
<row keys="z x" />
|
||||
<row keys="space" />
|
||||
</layer>
|
||||
<layer modifiers="shift">
|
||||
<row keys="gap test0 test1" />
|
||||
</layer>
|
||||
<layer modifiers="altR shift">
|
||||
<row keys="gap markertest0 markertest1" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
||||
<variables>
|
||||
|
|
|
|||
|
|
@ -55,19 +55,40 @@ std::u32string
|
|||
marker_map_to_string(const marker_map &m) {
|
||||
std::u32string s;
|
||||
for (auto i = m.rbegin(); i < m.rend(); i++) {
|
||||
prepend_hex_oct(s, i->first);
|
||||
s.insert(0, U"=U+");
|
||||
if (i->end) {
|
||||
s.insert(0, U"<END>");
|
||||
}
|
||||
if (i->processed) {
|
||||
s.insert(0, U"<PROCESSED?>");
|
||||
}
|
||||
if (i->ch == MARKER_BEFORE_EOT) {
|
||||
s.insert(0, U"<EOT>");
|
||||
} else {
|
||||
prepend_hex_oct(s, i->ch);
|
||||
s.insert(0, U"=U+");
|
||||
}
|
||||
|
||||
prepend_hex_quad(s, i->second);
|
||||
s.insert(0, U" \\m0x");
|
||||
if (i->marker != LDML_MARKER_NO_INDEX) {
|
||||
prepend_hex_quad(s, i->marker);
|
||||
s.insert(0, U" \\m0x");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool
|
||||
_assert_marker_map_equal(const char *f, int l, const marker_map a, const marker_map x) {
|
||||
if (a == x)
|
||||
_assert_marker_map_equal(const char *f, int l, const marker_map orig, const marker_map x) {
|
||||
marker_map a;
|
||||
// copy everything but 'end' entries
|
||||
std::copy_if(
|
||||
orig.begin(), orig.end(), std::back_inserter(a), [](const marker_entry &m) { return m.marker != LDML_MARKER_NO_INDEX; });
|
||||
|
||||
// now check equality
|
||||
if (a == x) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not equal. Print out why.
|
||||
std::wcerr << f << ":" << l << ": " << console_color::fg(console_color::BRIGHT_RED);
|
||||
std::wcerr << "got: " << marker_map_to_string(a);
|
||||
std::wcerr << " expected: " << marker_map_to_string(x);
|
||||
|
|
@ -636,7 +657,7 @@ test_strutils() {
|
|||
const std::u32string src = U"abc";
|
||||
const std::u32string dst = remove_markers(src, map);
|
||||
zassert_string_equal(dst, src); // unchanged
|
||||
assert_equal(map.size(), 0);
|
||||
assert_equal(count_markers(map), 0);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
|
|
@ -647,7 +668,7 @@ test_strutils() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm = {{U'e', 0x1L}};
|
||||
assert_marker_map_equal(map, expm); // marker 1 @ e
|
||||
assert_equal(map.size(), 1);
|
||||
assert_equal(count_markers(map), 1);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
|
|
@ -656,7 +677,7 @@ test_strutils() {
|
|||
const std::u32string dst = remove_markers(src, map);
|
||||
const std::u32string expect = src;
|
||||
zassert_string_equal(dst, expect);
|
||||
assert_equal(map.size(), 0);
|
||||
assert_equal(count_markers(map), 0);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
|
|
@ -665,7 +686,7 @@ test_strutils() {
|
|||
const std::u32string dst = remove_markers(src, map);
|
||||
const std::u32string expect = src; // 'q' removed
|
||||
zassert_string_equal(dst, expect);
|
||||
assert_equal(map.size(), 0);
|
||||
assert_equal(count_markers(map), 0);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
|
|
@ -674,7 +695,7 @@ test_strutils() {
|
|||
const std::u32string dst = remove_markers(src, map);
|
||||
const std::u32string expect = src;
|
||||
zassert_string_equal(dst, expect);
|
||||
assert_equal(map.size(), 0);
|
||||
assert_equal(count_markers(map), 0);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
|
|
@ -683,7 +704,7 @@ test_strutils() {
|
|||
const std::u32string dst = remove_markers(src, map);
|
||||
const std::u32string expect = src;
|
||||
zassert_string_equal(dst, expect);
|
||||
assert_equal(map.size(), 0);
|
||||
assert_equal(count_markers(map), 0);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
|
|
@ -694,7 +715,7 @@ test_strutils() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{MARKER_BEFORE_EOT, 0x1L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 1);
|
||||
assert_equal(count_markers(map), 1);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
|
|
@ -707,7 +728,7 @@ test_strutils() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{U'e', 0x1L}, {0x0320, 0x2L}, {0x0300, 0x3L}, {MARKER_BEFORE_EOT, 0x4L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 4);
|
||||
assert_equal(count_markers(map), 4);
|
||||
}
|
||||
{
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " - prepend hex quad" << std::endl;
|
||||
|
|
@ -752,7 +773,7 @@ test_normalize() {
|
|||
std::u32string dst = src;
|
||||
assert(normalize_nfd_markers_segment(dst, map));
|
||||
zassert_string_equal(dst, expect);
|
||||
assert_equal(map.size(), 0);
|
||||
assert_equal(count_markers(map), 0);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
|
|
@ -762,7 +783,7 @@ test_normalize() {
|
|||
std::u32string dst = src;
|
||||
assert(normalize_nfd_markers_segment(dst, map));
|
||||
zassert_string_equal(dst, expect);
|
||||
assert_equal(map.size(), 0);
|
||||
assert_equal(count_markers(map), 0);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -777,7 +798,7 @@ test_normalize() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{U'e', 0x1L}, {0x320, 0x2L}, {0x300, 0x3L}, {MARKER_BEFORE_EOT, 0x4L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 4);
|
||||
assert_equal(count_markers(map), 4);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -792,7 +813,7 @@ test_normalize() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{U'e', 0x1L}, {0x320, 0x2L}, {0x300, 0x3L}, {MARKER_BEFORE_EOT, 0x4L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 4);
|
||||
assert_equal(count_markers(map), 4);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
|
|
@ -812,7 +833,7 @@ test_normalize() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{U'e', 0x1L}, {0x300, 0x2L}, {0x320, 0x3L}, {MARKER_BEFORE_EOT, 0x4L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 4);
|
||||
assert_equal(count_markers(map), 4);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -830,7 +851,7 @@ test_normalize() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{0x320, 0x1L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 1);
|
||||
assert_equal(count_markers(map), 1);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -848,7 +869,7 @@ test_normalize() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{0x320, 0x2L}, {MARKER_BEFORE_EOT, 0x1L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 2);
|
||||
assert_equal(count_markers(map), 2);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -868,7 +889,7 @@ test_normalize() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{0x320, 0x2L}, {MARKER_BEFORE_EOT, 0x1L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 2);
|
||||
assert_equal(count_markers(map), 2);
|
||||
}
|
||||
{
|
||||
// from tests - regex edition
|
||||
|
|
@ -885,7 +906,7 @@ test_normalize() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{0x320, LDML_MARKER_ANY_INDEX}, {MARKER_BEFORE_EOT, 0x1L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 2);
|
||||
assert_equal(count_markers(map), 2);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -903,7 +924,7 @@ test_normalize() {
|
|||
zassert_string_equal(dst, expect);
|
||||
marker_map expm({{0x320, 0x2L}, {0x320, 0x2L}});
|
||||
assert_marker_map_equal(map, expm);
|
||||
assert_equal(map.size(), 2);
|
||||
assert_equal(count_markers(map), 2);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -1030,6 +1051,87 @@ test_normalize() {
|
|||
assert_marker_map_equal(map, expm);
|
||||
}
|
||||
|
||||
// doubled markers tests
|
||||
{
|
||||
marker_map map;
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " - doubled marker1 " << std::endl;
|
||||
const std::u32string src = U"e\uffff\u0008\u0001\u0300\u0320\u0300";
|
||||
const std::u32string expect_rem = U"e\u0300\u0320\u0300";
|
||||
const std::u32string expect_nfd = U"e\u0320\uffff\u0008\u0001\u0300\u0300";
|
||||
auto dst_rem = remove_markers(src, &map);
|
||||
marker_map expm({{0x0300, 0x1L}});
|
||||
zassert_string_equal(dst_rem, expect_rem);
|
||||
std::u32string dst_nfd = src;
|
||||
assert(normalize_nfd_markers(dst_nfd));
|
||||
if (dst_nfd != expect_nfd) {
|
||||
std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl;
|
||||
std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl;
|
||||
}
|
||||
zassert_string_equal(dst_nfd, expect_nfd);
|
||||
assert_marker_map_equal(map, expm);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " - doubled unchanged marker " << std::endl;
|
||||
const std::u32string src = U"e\u0320\uffff\u0008\u0001\u0300\u0300";
|
||||
const std::u32string expect_rem = U"e\u0320\u0300\u0300";
|
||||
const std::u32string expect_nfd = src;
|
||||
auto dst_rem = remove_markers(src, &map);
|
||||
marker_map expm({{0x300, 0x1L}});
|
||||
zassert_string_equal(dst_rem, expect_rem);
|
||||
std::u32string dst_nfd = src;
|
||||
assert(normalize_nfd_markers(dst_nfd));
|
||||
if (dst_nfd != expect_nfd) {
|
||||
std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl;
|
||||
std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl;
|
||||
}
|
||||
zassert_string_equal(dst_nfd, expect_nfd);
|
||||
assert_marker_map_equal(map, expm);
|
||||
}
|
||||
{
|
||||
marker_map map;
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " - marker-before-double-greek " << std::endl;
|
||||
const std::u32string src = U"\u03B5\uFFFF\u0008\u0001\u0344\uFFFF\u0008\u0002\u0344\uFFFF\u0008\u0003";
|
||||
const std::u32string expect_rem = U"\u03B5\u0344\u0344";
|
||||
const std::u32string expect_nfd = U"\u03B5\uFFFF\u0008\u0001\u0308\u0301\uFFFF\u0008\u0002\u0308\u0301\uFFFF\u0008\u0003";
|
||||
auto dst_rem = remove_markers(src, &map);
|
||||
marker_map expm({{0x0308, 0x1L},{0x0308, 0x2L},{MARKER_BEFORE_EOT, 0x3L}});
|
||||
zassert_string_equal(dst_rem, expect_rem);
|
||||
std::u32string dst_nfd = src;
|
||||
assert(normalize_nfd_markers(dst_nfd));
|
||||
if (dst_nfd != expect_nfd) {
|
||||
std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl;
|
||||
std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl;
|
||||
}
|
||||
zassert_string_equal(dst_nfd, expect_nfd);
|
||||
assert_marker_map_equal(map, expm);
|
||||
}
|
||||
|
||||
// macro for moving tests from test-pattern-parser.ts
|
||||
#define TEST_NFD_PLAIN(x, y) \
|
||||
{ \
|
||||
marker_map map; \
|
||||
std::cout << __FILE__ << ":" << __LINE__ << ": nfd test " << std::endl; \
|
||||
const std::u32string src = x; \
|
||||
const std::u32string expect_nfd = y; \
|
||||
std::u32string dst_nfd = src; \
|
||||
assert(normalize_nfd_markers(dst_nfd)); \
|
||||
if (dst_nfd != expect_nfd) { \
|
||||
std::cout << "dst: " << Debug_UnicodeString(dst_nfd) << std::endl; \
|
||||
std::cout << "exp: " << Debug_UnicodeString(expect_nfd) << std::endl; \
|
||||
} \
|
||||
zassert_string_equal(dst_nfd, expect_nfd); \
|
||||
}
|
||||
|
||||
// double marker - in front of second, no change
|
||||
TEST_NFD_PLAIN(U"e\u0320\u0300\uffff\u0008\u0001\u0300", U"e\u0320\u0300\uffff\u0008\u0001\u0300")
|
||||
// double marker - in front of second, with segment reordering
|
||||
TEST_NFD_PLAIN(U"e\u0300\u0320\uffff\u0008\u0001\u0300", U"e\u0320\u0300\uffff\u0008\u0001\u0300")
|
||||
// double marker - alternate pattern with reordering needed
|
||||
TEST_NFD_PLAIN(U"e\u0300\uffff\u0008\u0001\u0300\u0320", U"e\u0320\u0300\uffff\u0008\u0001\u0300")
|
||||
// triple diacritic + marker - reordering needed
|
||||
TEST_NFD_PLAIN(U"e\u0300\uffff\u0008\u0001\u0300\u0320\u0300", U"e\u0320\u0300\uffff\u0008\u0001\u0300\u0300")
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ let isInit = false;
|
|||
|
||||
export class KeymanSentry {
|
||||
|
||||
static async isEnabled() {
|
||||
static isEnabled() {
|
||||
if(process.argv.includes('--no-error-reporting')) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,6 +202,9 @@
|
|||
<Component>
|
||||
<File Id="LdmlImportReadme" Name="README.md" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component>
|
||||
<File Id="LdmlImportLicense" Name="unicode-license.txt" Source="..\..\..\resources\standards-data\ldml-keyboards\unicode-license.txt" />
|
||||
</Component>
|
||||
<Component>
|
||||
<File Name="keys-Latn-implied.xml" KeyPath="yes" />
|
||||
</Component>
|
||||
|
|
|
|||
|
|
@ -74,7 +74,11 @@ fi
|
|||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
if builder_start_action test; then
|
||||
npm test
|
||||
eslint .
|
||||
cd test
|
||||
tsc -b
|
||||
cd ..
|
||||
c8 --reporter=lcov --reporter=text mocha "${builder_extra_params[@]}"
|
||||
builder_finish_action success test
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"lint": "eslint .",
|
||||
"test": "npm run lint && cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha"
|
||||
"test": "gosh ./build.sh test"
|
||||
},
|
||||
"author": "Marc Durdin <marc@keyman.com> (https://github.com/mcdurdin)",
|
||||
"license": "MIT",
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@keymanapp/developer-test-helpers": "*",
|
||||
"@keymanapp/resources-gosh": "*",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "^20.4.1",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { SectionIdent, constants } from '@keymanapp/ldml-keyboard-constants';
|
|||
import { KmnCompiler } from '@keymanapp/kmc-kmn';
|
||||
import { KMXPlusMetadataCompiler } from './metadata-compiler.js';
|
||||
import { LdmlKeyboardVisualKeyboardCompiler } from './visual-keyboard-compiler.js';
|
||||
import { LdmlKeyboardKeymanWebCompiler } from './keymanweb-compiler.js';
|
||||
//KMW17.0: import { LdmlKeyboardKeymanWebCompiler } from './keymanweb-compiler.js';
|
||||
|
||||
export const SECTION_COMPILERS = [
|
||||
// These are in dependency order.
|
||||
|
|
@ -97,10 +97,11 @@ export class LdmlKeyboardCompiler implements KeymanCompiler {
|
|||
// const tlcompiler = new kmc.TouchLayoutCompiler();
|
||||
// const tl = tlcompiler.compile(source);
|
||||
// const tlwriter = new TouchLayoutFileWriter();
|
||||
const kmwcompiler = new LdmlKeyboardKeymanWebCompiler(this.callbacks, compilerOptions);
|
||||
const kmw_string = kmwcompiler.compile(inputFilename, source);
|
||||
const encoder = new TextEncoder();
|
||||
const kmw_binary = encoder.encode(kmw_string);
|
||||
|
||||
//KMW17.0: const kmwcompiler = new LdmlKeyboardKeymanWebCompiler(this.callbacks, compilerOptions);
|
||||
//KMW17.0: const kmw_string = kmwcompiler.compile(inputFilename, source);
|
||||
//KMW17.0: const encoder = new TextEncoder();
|
||||
//KMW17.0: const kmw_binary = encoder.encode(kmw_string);
|
||||
|
||||
outputFilename = outputFilename ?? inputFilename.replace(/\.xml$/, '.kmx');
|
||||
|
||||
|
|
@ -108,7 +109,7 @@ export class LdmlKeyboardCompiler implements KeymanCompiler {
|
|||
artifacts: {
|
||||
kmx: { data: kmx_binary, filename: outputFilename },
|
||||
kvk: { data: kvk_binary, filename: outputFilename.replace(/\.kmx$/, '.kvk') },
|
||||
js: { data: kmw_binary, filename: outputFilename.replace(/\.kmx$/, '.js') },
|
||||
//KMW17.0: js: { data: kmw_binary, filename: outputFilename.replace(/\.kmx$/, '.js') },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ fi
|
|||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
if builder_start_action build; then
|
||||
npm run build
|
||||
tsc -b
|
||||
cp "$KEYMAN_ROOT/resources/standards-data/ldml-keyboards/unicode-license.txt" ./build/unicode-license.txt
|
||||
builder_finish_action success build
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@
|
|||
"supports-color": "^9.4.0"
|
||||
},
|
||||
"files": [
|
||||
"build/src/"
|
||||
"build/src/",
|
||||
"build/unicode-license.txt"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@sentry/cli": "^2.19.4",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { CompilerCallbacks, CompilerLogLevel } from '@keymanapp/common-types';
|
|||
import { AnalyzeOskCharacterUse, AnalyzeOskRewritePua } from '@keymanapp/kmc-analyze';
|
||||
import { BaseOptions } from '../util/baseOptions.js';
|
||||
import { runOnFiles } from '../util/projectRunner.js';
|
||||
import { exitProcess } from '../util/sysexits.js';
|
||||
|
||||
interface AnalysisActivityOptions /* not inheriting from CompilerBaseOptions */ {
|
||||
/**
|
||||
|
|
@ -45,7 +46,7 @@ function declareOskCharUse(command: Command) {
|
|||
|
||||
if(!await analyze(analyzeOskCharUse, filenames, options)) {
|
||||
// Once a file fails to build, we bail on subsequent builds
|
||||
process.exit(1);
|
||||
await exitProcess(1);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ function declareOskRewrite(command: Command) {
|
|||
|
||||
if(!await analyze(analyzeOskRewritePua, filenames, options)) {
|
||||
// Once a file fails to build, we bail on subsequent builds
|
||||
process.exit(1);
|
||||
await exitProcess(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { isProject } from '../util/projectLoader.js';
|
|||
import { buildTestData } from './buildTestData/index.js';
|
||||
import { buildWindowsPackageInstaller } from './buildWindowsPackageInstaller/index.js';
|
||||
import { commandOptionsToCompilerOptions } from '../util/extendedCompilerOptions.js';
|
||||
import { exitProcess } from '../util/sysexits.js';
|
||||
|
||||
export function declareBuild(program: Command) {
|
||||
// TODO: localization?
|
||||
|
|
@ -79,7 +80,7 @@ async function buildFile(filenames: string[], _options: any, commander: any) {
|
|||
const commanderOptions/*:{TODO?} CommandLineCompilerOptions*/ = commander.optsWithGlobals();
|
||||
const options = initialize(commanderOptions);
|
||||
if(!options) {
|
||||
process.exit(1);
|
||||
await exitProcess(1);
|
||||
}
|
||||
|
||||
const callbacks = new NodeCompilerCallbacks(options);
|
||||
|
|
@ -93,17 +94,17 @@ async function buildFile(filenames: string[], _options: any, commander: any) {
|
|||
if(filenames.length > 1 && commanderOptions.outFile) {
|
||||
// -o can only be specified with a single input file
|
||||
callbacks.reportMessage(InfrastructureMessages.Error_OutFileCanOnlyBeSpecifiedWithSingleInfile());
|
||||
process.exit(1);
|
||||
await exitProcess(1);
|
||||
}
|
||||
|
||||
if(!expandFileLists(filenames, callbacks)) {
|
||||
process.exit(1);
|
||||
await exitProcess(1);
|
||||
}
|
||||
|
||||
for(let filename of filenames) {
|
||||
if(!await build(filename, commanderOptions.outFile, callbacks, options)) {
|
||||
// Once a file fails to build, we bail on subsequent builds
|
||||
process.exit(1);
|
||||
await exitProcess(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { CompilerCallbacks, defaultCompilerOptions } from '@keymanapp/common-typ
|
|||
import { NodeCompilerCallbacks } from '../../util/NodeCompilerCallbacks.js';
|
||||
import { WindowsPackageInstallerCompiler, WindowsPackageInstallerSources } from '@keymanapp/kmc-package';
|
||||
import { CommandLineBaseOptions } from 'src/util/baseOptions.js';
|
||||
import { exitProcess } from '../../util/sysexits.js';
|
||||
|
||||
interface WindowsPackageInstallerCommandLineOptions extends CommandLineBaseOptions {
|
||||
msi: string;
|
||||
|
|
@ -38,7 +39,7 @@ export async function buildWindowsPackageInstaller(infile: string, _options: any
|
|||
const callbacks: CompilerCallbacks = new NodeCompilerCallbacks({...defaultCompilerOptions, ...options});
|
||||
const compiler = new WindowsPackageInstallerCompiler();
|
||||
if(!await compiler.init(callbacks, {...options, sources})) {
|
||||
process.exit(1);
|
||||
await exitProcess(1);
|
||||
}
|
||||
|
||||
const fileBaseName = outfile ?? infile;
|
||||
|
|
@ -49,10 +50,10 @@ export async function buildWindowsPackageInstaller(infile: string, _options: any
|
|||
const result = await compiler.run(infile, outFileExe);
|
||||
if(!result) {
|
||||
// errors will have been reported already
|
||||
process.exit(1);
|
||||
await exitProcess(1);
|
||||
}
|
||||
|
||||
if(!await compiler.write(result.artifacts)) {
|
||||
process.exit(1);
|
||||
await exitProcess(1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,12 @@ import { declareAnalyze } from './commands/analyze.js';
|
|||
import { KeymanSentry, loadOptions } from '@keymanapp/developer-utils';
|
||||
import KEYMAN_VERSION from "@keymanapp/keyman-version";
|
||||
import { TestKeymanSentry } from './util/TestKeymanSentry.js';
|
||||
import { exitProcess } from './util/sysexits.js';
|
||||
|
||||
await TestKeymanSentry.runTestIfCLRequested();
|
||||
KeymanSentry.init();
|
||||
if(KeymanSentry.isEnabled()) {
|
||||
KeymanSentry.init();
|
||||
}
|
||||
try {
|
||||
await run();
|
||||
} catch(e) {
|
||||
|
|
@ -20,7 +23,7 @@ try {
|
|||
|
||||
// Ensure any messages reported to Sentry have had time to be uploaded before we
|
||||
// exit. In most cases, this will be a no-op so should not affect performance.
|
||||
await KeymanSentry.close();
|
||||
await exitProcess(0);
|
||||
|
||||
async function run() {
|
||||
await loadOptions();
|
||||
|
|
@ -42,10 +45,6 @@ async function run() {
|
|||
.addOption(new Option('--no-error-reporting', 'Disable error reporting to keyman.com (overriding user settings)'))
|
||||
.addOption(new Option('--error-reporting', 'Enable error reporting to keyman.com (overriding user settings)'));
|
||||
|
||||
if(await KeymanSentry.isEnabled()) {
|
||||
KeymanSentry.init();
|
||||
}
|
||||
|
||||
declareBuild(program);
|
||||
declareAnalyze(program);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
*/
|
||||
export function calculateSourcePath(projectFilename: string): string {
|
||||
projectFilename = projectFilename.replace(/\\/g, '/');
|
||||
const result = /([^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/]+)\.kpj$/.exec(projectFilename);
|
||||
const result = /(release|legacy|experimental)\/([^\/]+)\/([^\/]+)\/([^\/]+)\.kpj$/.exec(projectFilename);
|
||||
if (!result) {
|
||||
throw new Error(`Invalid path ${projectFilename}`);
|
||||
return undefined;
|
||||
}
|
||||
return `${result[1]}/${result[2]}/${result[3]}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { KeymanSentry } from "@keymanapp/developer-utils";
|
||||
|
||||
/**
|
||||
* Exit codes defined in <sysexits.h>:
|
||||
* https://www.freebsd.org/cgi/man.cgi?query=sysexits&apropos=0&sektion=0&manpath=FreeBSD+4.3-RELEASE&format=html
|
||||
|
|
@ -6,3 +8,8 @@ export const enum SysExits {
|
|||
EX_USAGE = 64,
|
||||
EX_DATAERR = 65,
|
||||
};
|
||||
|
||||
export async function exitProcess(exitCode?: number): Promise<never> {
|
||||
await KeymanSentry.close();
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
|
@ -73,14 +73,14 @@
|
|||
},
|
||||
{
|
||||
"name": "Python: Current File",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "km-config",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "linux/keyman-config/km-config",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
},
|
||||
{
|
||||
"name": "km-package-install",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "linux/keyman-config/km-package-install",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@ Docker tends to throttle Docker image downloads, so some developer offices may w
|
|||
]
|
||||
```
|
||||
|
||||
### Using Website-Local-Proxy
|
||||
Rather than remembering localhost port values below, you can clone and run [website-local-proxy](https://github.com/keymanapp/website-local-proxy).
|
||||
|
||||
Follow https://github.com/keymanapp/website-local-proxy?tab=readme-ov-file#using-website-local-proxy and you can access the local websites at
|
||||
http://*keyman.com.localhost
|
||||
|
||||
## Builder BASH Script Actions
|
||||
|
||||
#### Stop the Docker container
|
||||
|
|
@ -40,12 +46,12 @@ This stops the Docker container for the site.
|
|||
This downloads and builds the Docker images needed for the site.
|
||||
|
||||
#### Configure
|
||||
1. Run `./build.sh configure`.
|
||||
1. Run `./build.sh configure`
|
||||
|
||||
This step typically downloads _common/ website files from [shared-sites](https://github.com/keymanapp/shared-sites/tree/main/_common).
|
||||
|
||||
#### Start the Docker container
|
||||
1. Run `./build.sh start`.
|
||||
1. Run `./build.sh start`
|
||||
|
||||
This maps the local directory to the the Docker image.
|
||||
|
||||
|
|
@ -54,10 +60,14 @@ The link file also appears locally.
|
|||
|
||||
After this, you can access the website at the following ports:
|
||||
|
||||
| Website | URL |
|
||||
|--------------|-----------------------|
|
||||
|help.keyman | http://localhost:8055 |
|
||||
|keymanweb.com | http://localhost:8057 |
|
||||
| Website | URL | with website-local-proxy running |
|
||||
|----------------|-----------------------|-----------------------------------|
|
||||
| keyman.com | http://localhost:8053 | http://keyman.com.localhost |
|
||||
| s.keyman.com | http://localhost:8054 | http://s.keyman.com.localhost |
|
||||
| help.keyman | http://localhost:8055 | http://help.keyman.com.localhost |
|
||||
| keymanweb.com | http://localhost:8057 | http://keymanweb.com.localhost |
|
||||
| | | http://web.keyman.com.localhost |
|
||||
| api.keyman.com | http://localhost:8058 | http://api.keyman.com.localhost |
|
||||
|
||||
#### Remove the Docker container and image
|
||||
1. Run `./build.sh clean`.
|
||||
|
|
@ -66,13 +76,6 @@ After this, you can access the website at the following ports:
|
|||
Checks for broken links
|
||||
1. Run `./build.sh test`
|
||||
|
||||
#### Using Website-Local-Proxy
|
||||
Rather than remembering localhost port values, you can clone and run [website-local-proxy](https://github.com/keymanapp/website-local-proxy).
|
||||
|
||||
Follow https://github.com/keymanapp/website-local-proxy?tab=readme-ov-file#using-website-local-proxy and you can access the local websites at
|
||||
http://*keyman.com.localhost
|
||||
|
||||
|
||||
---------
|
||||
|
||||
## Kubernetes Deployment
|
||||
|
|
|
|||
2
linux/.gitignore
vendored
2
linux/.gitignore
vendored
|
|
@ -6,7 +6,7 @@ ltmain.sh
|
|||
__pycache__
|
||||
*.egg-info
|
||||
build-*
|
||||
deb-*
|
||||
./deb-*
|
||||
builddebs
|
||||
keyman-config/make_deb
|
||||
tmp/
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ DEBIAN_SUITES=($UNSTABLE_CODENAME $TESTING_CODENAME $STABLE_CODENAME $STABLE_BAC
|
|||
"experimental" "unstable" "testing" "stable")
|
||||
|
||||
# List of Ubuntu suites. Update these when needed.
|
||||
UBUNTU_SUITES=("mantic" "lunar" "jammy" "focal")
|
||||
UBUNTU_SUITES=("noble" "mantic" "jammy" "focal")
|
||||
|
||||
# Mirrors to use. Update these to your preferred mirror.
|
||||
DEBIAN_MIRROR="deb.debian.org"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
keyman (16.0.144-2) UNRELEASED; urgency=medium
|
||||
keyman (16.0.145-1) unstable; urgency=medium
|
||||
|
||||
[Jeremy Bicha]
|
||||
* Build with webkitgtk 4.1 instead of 4.0 (closes: #1061396)
|
||||
* New upstream release.
|
||||
* Re-release to Debian
|
||||
|
||||
-- Eberhard Beilharz <eb1@sil.org> Wed, 24 Jan 2024 17:38:11 +0100
|
||||
-- Eberhard Beilharz <eb1@sil.org> Thu, 01 Feb 2024 12:58:00 +0100
|
||||
|
||||
keyman (16.0.144-1) unstable; urgency=medium
|
||||
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ get_context_debug(IBusEngine *engine) {
|
|||
static gchar *
|
||||
debug_utf8_with_codepoints(const gchar *utf8) {
|
||||
GString *output = g_string_new("");
|
||||
g_string_append_printf(output, "|%s| len:%zu) [", utf8, g_utf8_strlen(utf8, -1));
|
||||
g_string_append_printf(output, "|%s| len:%ld) [", utf8, g_utf8_strlen(utf8, -1));
|
||||
gunichar2 *utf16 = g_utf8_to_utf16(utf8, -1, NULL, NULL, NULL);
|
||||
for (int i = 0; utf16[i] != '\0'; i++) {
|
||||
g_string_append_printf(output, "U+%04x ", utf16[i]);
|
||||
|
|
|
|||
|
|
@ -212,6 +212,44 @@ get_engine_for_language(
|
|||
return engine_desc;
|
||||
}
|
||||
|
||||
int _get_version(const gchar **pver) {
|
||||
g_assert(pver);
|
||||
const gchar *ver = *pver;
|
||||
int version = 0;
|
||||
|
||||
while (*ver && *ver != '.') {
|
||||
if (*ver >= '0' && *ver <= '9') {
|
||||
version = version * 10 + (*ver - '0');
|
||||
ver++;
|
||||
} else {
|
||||
// stop comparison on first non-digit
|
||||
while (*ver)
|
||||
ver++;
|
||||
}
|
||||
}
|
||||
*pver = ver;
|
||||
return version;
|
||||
}
|
||||
|
||||
int
|
||||
keyman_compare_version(const gchar *ver1, const gchar *ver2) {
|
||||
for (; *ver1 || *ver2; ) {
|
||||
int version1 = _get_version(&ver1);
|
||||
int version2 = _get_version(&ver2);
|
||||
|
||||
if (version1 < version2)
|
||||
return -1;
|
||||
if (version1 > version2)
|
||||
return +1;
|
||||
|
||||
if (*ver1)
|
||||
ver1++;
|
||||
if (*ver2)
|
||||
ver2++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
gboolean
|
||||
keyman_list_contains_keyboard(
|
||||
GList *engines_list,
|
||||
|
|
@ -225,8 +263,7 @@ keyman_list_contains_keyboard(
|
|||
// If we already have an engine for this keyboard (in a different area), we
|
||||
// don't want to add it again since we wouldn't add anything new
|
||||
// if it's the same version
|
||||
// TODO: fix version comparison (#9593)
|
||||
if (g_strcmp0(kmx_file, keyboard->kmx_file) == 0 && g_strcmp0(version, keyboard->version) >= 0) {
|
||||
if (g_strcmp0(kmx_file, keyboard->kmx_file) == 0 && keyman_compare_version(version, keyboard->version) >= 0) {
|
||||
g_debug("keyboard %s already exists at version %s which is newer or same as %s", kmx_file, version, keyboard->version);
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ void keyman_set_custom_keyboards(gchar ** keyboards);
|
|||
GHashTable * keyman_get_custom_keyboard_dictionary();
|
||||
void keyman_add_keyboard(gpointer data, gpointer user_data);
|
||||
void keyman_add_keyboards_from_dir(gpointer data, gpointer user_data);
|
||||
int keyman_compare_version(const gchar *version1, const gchar *version2);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(IBusEngineDesc, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(IBusComponent, g_object_unref)
|
||||
|
|
|
|||
|
|
@ -1033,6 +1033,51 @@ test_keyman_add_keyboards_from_dir__one_language_plus_two_different_custom() {
|
|||
g_assert_cmpstr(ibus_engine_desc_get_name(desc), ==, expected_name);
|
||||
}
|
||||
|
||||
void
|
||||
test_keyman_compare_version_equal() {
|
||||
g_assert_cmpint(keyman_compare_version("1.0", "1.0"), ==, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_keyman_compare_version_equal_with_patch() {
|
||||
g_assert_cmpint(keyman_compare_version("1.0", "1.0.0"), ==, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_keyman_compare_version_equal_nondigit() {
|
||||
g_assert_cmpint(keyman_compare_version("1.1", "1.1-beta2"), ==, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_keyman_compare_version_equal_doubledot() {
|
||||
g_assert_cmpint(keyman_compare_version("1..2", "1.0.2"), ==, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_keyman_compare_version_less() {
|
||||
g_assert_cmpint(keyman_compare_version("1.0", "1.1"), <, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_keyman_compare_version_minor_less_with_patch() {
|
||||
g_assert_cmpint(keyman_compare_version("1.0.1", "1.1"), <, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_keyman_compare_version_patch_less() {
|
||||
g_assert_cmpint(keyman_compare_version("1.0", "1.0.1"), <, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_keyman_compare_version_less_twodigit() {
|
||||
g_assert_cmpint(keyman_compare_version("99.9", "100"), <, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_keyman_compare_version_greater() {
|
||||
g_assert_cmpint(keyman_compare_version("1.10", "1.9"), >, 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
void
|
||||
print_usage() {
|
||||
|
|
@ -1110,10 +1155,9 @@ int main(int argc, char* argv[]) {
|
|||
g_test_add_func(
|
||||
"/keymanutil/keyman_add_keyboard/prev_engine_adding_newer_version",
|
||||
test_keyman_add_keyboard__prev_engine_adding_newer_version);
|
||||
// #9593
|
||||
// g_test_add_func(
|
||||
// "/keymanutil/keyman_add_keyboard/prev_engine_adding_newer_version_versioncompare",
|
||||
// test_keyman_add_keyboard__prev_engine_adding_newer_version_9593);
|
||||
g_test_add_func(
|
||||
"/keymanutil/keyman_add_keyboard/prev_engine_adding_newer_version_versioncompare",
|
||||
test_keyman_add_keyboard__prev_engine_adding_newer_version_9593);
|
||||
g_test_add_func(
|
||||
"/keymanutil/keyman_add_keyboard/prev_engine_adding_older_version",
|
||||
test_keyman_add_keyboard__prev_engine_adding_older_version);
|
||||
|
|
@ -1126,6 +1170,16 @@ int main(int argc, char* argv[]) {
|
|||
"/keymanutil/keyman_add_keyboards_from_dir/one_language_plus_two_different_custom",
|
||||
test_keyman_add_keyboards_from_dir__one_language_plus_two_different_custom);
|
||||
|
||||
g_test_add_func("/keymanutil/keyman_compare_version/1.0==1.0", test_keyman_compare_version_equal);
|
||||
g_test_add_func("/keymanutil/keyman_compare_version/1.0==1.0.0", test_keyman_compare_version_equal_with_patch);
|
||||
g_test_add_func("/keymanutil/keyman_compare_version/1.1==1.1-beta", test_keyman_compare_version_equal_nondigit);
|
||||
g_test_add_func("/keymanutil/keyman_compare_version/1..2==1.0.2", test_keyman_compare_version_equal_doubledot);
|
||||
g_test_add_func("/keymanutil/keyman_compare_version/1.0<1.1", test_keyman_compare_version_less);
|
||||
g_test_add_func("/keymanutil/keyman_compare_version/1.0.1<1.1", test_keyman_compare_version_minor_less_with_patch);
|
||||
g_test_add_func("/keymanutil/keyman_compare_version/1.0<1.0.1", test_keyman_compare_version_patch_less);
|
||||
g_test_add_func("/keymanutil/keyman_compare_version/99.9<100",test_keyman_compare_version_less_twodigit);
|
||||
g_test_add_func("/keymanutil/keyman_compare_version/1.10>1.9", test_keyman_compare_version_greater);
|
||||
|
||||
// Run tests
|
||||
int retVal = g_test_run();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_size = 2
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# If needed set cowbuilder up for building Keyman Debian packages
|
||||
# Then cowbuilder update
|
||||
|
||||
distributions='focal jammy lunar mantic'
|
||||
distributions='focal jammy mantic noble'
|
||||
|
||||
if ! dpkg-query -l cowbuilder; then
|
||||
echo "installing pbuilder and cowbuilder"
|
||||
|
|
|
|||
|
|
@ -103,6 +103,13 @@ is_symbols_file_changed() {
|
|||
return 0
|
||||
}
|
||||
|
||||
get_changes() {
|
||||
local WHAT_CHANGED
|
||||
WHAT_CHANGED=$(git diff -I "^${PKG_NAME}.so" "$1".."$2" -- "debian/${PKG_NAME}.symbols" | diffstat -m -t | tail -n +2)
|
||||
|
||||
IFS=',' read -r -a CHANGES <<< "${WHAT_CHANGED:-0,0,0}"
|
||||
}
|
||||
|
||||
check_updated_version_number() {
|
||||
# Checks that the package version number got updated in the .symbols file if it got changed
|
||||
# shellcheck disable=SC2310
|
||||
|
|
@ -114,7 +121,16 @@ check_updated_version_number() {
|
|||
# is out of date when it is merged to the release branch (master/beta/stable-x.y). If this
|
||||
# is considered important, then make sure the branch is up to date, and wait for test
|
||||
# builds to complete, before merging.
|
||||
if ! git log -p -1 -- "debian/${PKG_NAME}.symbols" | grep -q "${VERSION}"; then
|
||||
get_changes "${GIT_BASE}" "${GIT_SHA}"
|
||||
INSERTED="${CHANGES[0]}"
|
||||
DELETED="${CHANGES[1]}"
|
||||
MODIFIED="${CHANGES[2]}"
|
||||
|
||||
if (( DELETED > 0 )) && (( MODIFIED == 0 )) && (( INSERTED == 0)); then
|
||||
# If only lines got removed we basically skip this test. A later check will
|
||||
# test that the API version got updated.
|
||||
output_ok "${PKG_NAME}.symbols file did change but only removed lines"
|
||||
elif ! git log -p -1 -- "debian/${PKG_NAME}.symbols" | grep -q "${VERSION}"; then
|
||||
output_error "${PKG_NAME}.symbols file got changed without changing the package version number of the symbol"
|
||||
EXIT_CODE=1
|
||||
else
|
||||
|
|
@ -135,20 +151,40 @@ get_api_version_in_symbols_file() {
|
|||
}
|
||||
|
||||
is_api_version_updated() {
|
||||
local NEW_VERSION OLD_VERSION
|
||||
local NEW_VERSION OLD_VERSION TIER
|
||||
git checkout "${GIT_BASE}" -- "debian/${PKG_NAME}.symbols"
|
||||
OLD_VERSION=$(get_api_version_in_symbols_file)
|
||||
git checkout "${GIT_SHA}" -- "debian/${PKG_NAME}.symbols"
|
||||
NEW_VERSION=$(get_api_version_in_symbols_file)
|
||||
if (( NEW_VERSION > OLD_VERSION )); then
|
||||
return 0
|
||||
echo "0"
|
||||
return
|
||||
fi
|
||||
return 1
|
||||
|
||||
# API version didn't change. However, that might be ok if we're in alpha
|
||||
# and a major change happened previously.
|
||||
TIER=$(cat "${REPO_ROOT}/TIER.md")
|
||||
case ${TIER} in
|
||||
alpha)
|
||||
local STABLE_VERSION
|
||||
STABLE_VERSION=$((${VERSION%%.*} - 1))
|
||||
git checkout "stable-${STABLE_VERSION}.0" -- "debian/${PKG_NAME}.symbols"
|
||||
STABLE_API_VERSION=$(get_api_version_in_symbols_file)
|
||||
git checkout "${GIT_SHA}" -- "debian/${PKG_NAME}.symbols"
|
||||
if (( NEW_VERSION > STABLE_API_VERSION )); then
|
||||
echo "2"
|
||||
return
|
||||
fi ;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "1"
|
||||
}
|
||||
|
||||
check_for_major_api_changes() {
|
||||
# Checks that API version number gets updated if API changes
|
||||
local WHAT_CHANGED CHANGES INSERTED DELETED MODIFIED
|
||||
local WHAT_CHANGED CHANGES INSERTED DELETED MODIFIED UPDATED
|
||||
|
||||
# shellcheck disable=2310
|
||||
if ! is_symbols_file_changed; then
|
||||
|
|
@ -156,19 +192,19 @@ check_for_major_api_changes() {
|
|||
return
|
||||
fi
|
||||
|
||||
WHAT_CHANGED=$(git diff "${GIT_BASE}".."${GIT_SHA}" -- "debian/${PKG_NAME}.symbols" | diffstat -m -t | tail -1)
|
||||
|
||||
IFS=',' read -r -a CHANGES <<< "${WHAT_CHANGED}"
|
||||
get_changes "${GIT_BASE}" "${GIT_SHA}"
|
||||
INSERTED="${CHANGES[0]}"
|
||||
DELETED="${CHANGES[1]}"
|
||||
MODIFIED="${CHANGES[2]}"
|
||||
|
||||
if (( DELETED > 0 )) || (( MODIFIED > 0 )); then
|
||||
builder_echo "Major API change: ${DELETED} lines deleted and ${MODIFIED} lines modified"
|
||||
# shellcheck disable=2310
|
||||
if ! is_api_version_updated; then
|
||||
UPDATED=$(is_api_version_updated)
|
||||
if [[ ${UPDATED} == 1 ]]; then
|
||||
output_error "Major API change without updating API version number in ${PKG_NAME}.symbols file"
|
||||
EXIT_CODE=2
|
||||
elif [[ ${UPDATED} == 2 ]]; then
|
||||
output_ok "API version number got previously updated in ${PKG_NAME}.symbols file after major API change; no change within alpha necessary"
|
||||
else
|
||||
output_ok "API version number got updated in ${PKG_NAME}.symbols file after major API change"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
set -e
|
||||
|
||||
all_distributions="focal jammy lunar mantic"
|
||||
all_distributions="focal jammy mantic noble"
|
||||
distributions=""
|
||||
echo "all_distributions: ${all_distributions}"
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ else
|
|||
fi
|
||||
echo "ppa: ${ppa}"
|
||||
|
||||
distributions="${DIST:-focal jammy lunar mantic}"
|
||||
distributions="${DIST:-focal jammy mantic noble}"
|
||||
packageversion="${PACKAGEVERSION:-1~sil1}"
|
||||
|
||||
BASEDIR=$(pwd)
|
||||
|
|
|
|||
202
linux/scripts/test/deb-packaging.tests.sh
Executable file
202
linux/scripts/test/deb-packaging.tests.sh
Executable file
|
|
@ -0,0 +1,202 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
mockDebPkgTools() {
|
||||
echo "#!/bin/bash
|
||||
" > ${tmpDir}/dpkg
|
||||
chmod +x ${tmpDir}/dpkg
|
||||
cp ${tmpDir}/dpkg ${tmpDir}/dpkg-gensymbols
|
||||
PATH=${tmpDir}:${PATH}
|
||||
}
|
||||
|
||||
createBase() {
|
||||
TIER=$1
|
||||
workDir=$(mktemp -d)
|
||||
cd ${workDir}
|
||||
git init .
|
||||
mkdir -p linux/debian
|
||||
echo "libkeymancore.so.1 libkeymancore #MINVER#
|
||||
* Build-Depends-Package: libkeymancore-dev
|
||||
|
||||
km_core_actions_dispose@Base 17.0.197
|
||||
km_core_context_clear@Base 17.0.195
|
||||
km_core_context_get@Base 17.0.195
|
||||
km_core_context_item_list_size@Base 17.0.195
|
||||
km_core_context_items_dispose@Base 17.0.195
|
||||
" > linux/debian/libkeymancore.symbols
|
||||
git add linux/debian/libkeymancore.symbols
|
||||
|
||||
mkdir -p core
|
||||
echo "1.0.0" > core/CORE_API_VERSION.md
|
||||
git add core/CORE_API_VERSION.md
|
||||
|
||||
echo "16.0.145" > VERSION.md
|
||||
git add VERSION.md
|
||||
|
||||
echo "stable" > TIER.md
|
||||
git add TIER.md
|
||||
|
||||
mkdir -p linux/scripts
|
||||
cp -r ${REPO_ROOT}/linux/scripts/* linux/scripts
|
||||
git add linux/scripts
|
||||
|
||||
mkdir -p resources/build
|
||||
cp -r ${REPO_ROOT}/resources/build/* resources/build
|
||||
cp ${REPO_ROOT}/resources/builder.inc.sh resources/
|
||||
git add resources
|
||||
|
||||
git commit -m "Initial"
|
||||
|
||||
git branch -c stable-16.0
|
||||
git tag -m "16.0.145" release-16.0.145
|
||||
|
||||
echo "${TIER}" > TIER.md
|
||||
git add TIER.md
|
||||
|
||||
echo "17.0.255" > VERSION.md
|
||||
git add VERSION.md
|
||||
git commit -m "Commit for Alpha"
|
||||
|
||||
git checkout -b chore
|
||||
|
||||
BINPKG_NAME=${tmpDir}/libkeymancore_17.0.257-1+noble1_amd64.deb
|
||||
touch "${BINPKG_NAME}"
|
||||
}
|
||||
|
||||
setup() {
|
||||
OLDPATH=${PATH}
|
||||
tmpDir=$(mktemp -d)
|
||||
mockDebPkgTools
|
||||
}
|
||||
|
||||
teardown() {
|
||||
PATH=${OLDPATH}
|
||||
rm -rf ${tmpDir}
|
||||
}
|
||||
|
||||
run_test() {
|
||||
setup
|
||||
$1 > /tmp/$1.log 2>&1 && echo -e "${COLOR_GREEN}$1: OK${COLOR_RESET}" || echo -e "${COLOR_RED}$1: FAILED${COLOR_RESET}"
|
||||
teardown
|
||||
}
|
||||
|
||||
test_check_updated_version_number_NoChange_OK() {
|
||||
createBase alpha
|
||||
|
||||
echo "readme" > README.md
|
||||
git add README.md
|
||||
git commit -m "Some change on the branch"
|
||||
|
||||
pwd
|
||||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number_LineAdded_OK() {
|
||||
createBase alpha
|
||||
|
||||
sed -i 's/ km_core_actions_dispose@Base 17.0.197/ km_core_actions_dispose@Base 17.0.197\n km_core_added@Base 17.0.255/' linux/debian/libkeymancore.symbols
|
||||
git add linux/debian/libkeymancore.symbols
|
||||
git commit -m "API method added"
|
||||
|
||||
pwd
|
||||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number_LineAddedWithoutVerUpd_ERROR() {
|
||||
createBase alpha
|
||||
|
||||
sed -i 's/ km_core_actions_dispose@Base 17.0.197/ km_core_actions_dispose@Base 17.0.197\n km_core_added@Base 17.0.197/' linux/debian/libkeymancore.symbols
|
||||
git add linux/debian/libkeymancore.symbols
|
||||
git commit -m "API method added"
|
||||
|
||||
pwd
|
||||
output=$(linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify || true)
|
||||
echo "${output[*]}" # for logging
|
||||
[[ "${output[*]}" == *"ERROR: libkeymancore.symbols file got changed without changing the package version number of the symbol"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number_LineRemovedWithAPIUpd_OK() {
|
||||
createBase alpha
|
||||
echo "2.0.0" > core/CORE_API_VERSION.md
|
||||
git add core/CORE_API_VERSION.md
|
||||
sed -i 's/libkeymancore.so.1/libkeymancore.so.2/' linux/debian/libkeymancore.symbols
|
||||
sed -i '6d' linux/debian/libkeymancore.symbols
|
||||
git add linux/debian/libkeymancore.symbols
|
||||
git commit -m "API method removed"
|
||||
|
||||
pwd
|
||||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number_LineRemoved_InAlpha_OK() {
|
||||
createBase alpha
|
||||
git checkout master
|
||||
# simulate a commit that already introduced an API version change
|
||||
echo "2.0.0" > core/CORE_API_VERSION.md
|
||||
git add core/CORE_API_VERSION.md
|
||||
sed -i 's/libkeymancore.so.1/libkeymancore.so.2/' linux/debian/libkeymancore.symbols
|
||||
git add linux/debian/libkeymancore.symbols
|
||||
git commit -m "API version change"
|
||||
git checkout chore
|
||||
git rebase master
|
||||
|
||||
sed -i '6d' linux/debian/libkeymancore.symbols
|
||||
git add linux/debian/libkeymancore.symbols
|
||||
git commit -m "API method removed"
|
||||
|
||||
pwd
|
||||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number_LineRemoved_ChangeFromStable_ERROR() {
|
||||
createBase alpha
|
||||
|
||||
sed -i '6d' linux/debian/libkeymancore.symbols
|
||||
git add linux/debian/libkeymancore.symbols
|
||||
git commit -m "API method removed"
|
||||
|
||||
pwd
|
||||
output=$(linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify || true)
|
||||
echo "${output[*]}" # for logging
|
||||
[[ "${output[*]}" == *" ERROR: Major API change without updating API version number in libkeymancore.symbols file"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number_LineRemoved_ChangeInBeta_ERROR() {
|
||||
createBase beta
|
||||
|
||||
# simulate a commit that already introduced an API version change in Beta
|
||||
git checkout -b beta
|
||||
echo "2.0.0" > core/CORE_API_VERSION.md
|
||||
git add core/CORE_API_VERSION.md
|
||||
sed -i 's/libkeymancore.so.1/libkeymancore.so.2/' linux/debian/libkeymancore.symbols
|
||||
git add linux/debian/libkeymancore.symbols
|
||||
git commit -m "API version change"
|
||||
git checkout chore
|
||||
git rebase beta
|
||||
|
||||
sed -i '6d' linux/debian/libkeymancore.symbols
|
||||
git add linux/debian/libkeymancore.symbols
|
||||
git commit -m "API method removed"
|
||||
|
||||
pwd
|
||||
output=$(linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base beta verify || true)
|
||||
echo "${output[*]}" # for logging
|
||||
[[ "${output[*]}" == *" ERROR: Major API change without updating API version number in libkeymancore.symbols file"* ]]
|
||||
}
|
||||
|
||||
echo "(test logs are in /tmp/<testname>.log)"
|
||||
run_test test_check_updated_version_number_NoChange_OK
|
||||
run_test test_check_updated_version_number_LineAdded_OK
|
||||
run_test test_check_updated_version_number_LineAddedWithoutVerUpd_ERROR
|
||||
run_test test_check_updated_version_number_LineRemovedWithAPIUpd_OK
|
||||
run_test test_check_updated_version_number_LineRemoved_InAlpha_OK
|
||||
run_test test_check_updated_version_number_LineRemoved_ChangeFromStable_ERROR
|
||||
run_test test_check_updated_version_number_LineRemoved_ChangeInBeta_ERROR
|
||||
|
||||
# TODO: still some test cases missing for the different checks
|
||||
164
oem/firstvoices/keyboards.csv
Executable file → Normal file
164
oem/firstvoices/keyboards.csv
Executable file → Normal file
|
|
@ -1,99 +1,99 @@
|
|||
Shortname,ID,Name,Region,9.0 Web Keyboard,Version,Language ID,Language Name
|
||||
fv,fv_inuvialuktun,Inuvialuktun,Arctic,fv_inuvialuktun_kmw-9.0.js,9.1,ikt-Latn,Inuinnaqtun (Latin)
|
||||
fv,fv_inuvialuktun,Inuvialuktun,Arctic,fv_inuvialuktun_kmw-9.0.js,9.1.1,ikt-Latn,Inuinnaqtun (Latin)
|
||||
i,inuktitut_pirurvik,Kiputtijjut | ᑭᐳᑦᑎᔾᔪᑦ,Arctic,inuktitut_pirurvik-1.1.js,1.4,ike-Cans,Eastern Canadian Inuktitut (Unified Canadian Aboriginal Syllabics)
|
||||
i,inuktitut_latin,Qaliujaaqpait | ᖃᓕᐅᔮᖅᐸᐃᑦ,Arctic,inuktitut_latin-1.1.js,1.4,ike-Latn,Eastern Canadian Inuktitut (Latin)
|
||||
i,inuktitut_naqittaut,Qaniujaaqpait | ᖃᓂᐅᔮᖅᐸᐃᑦ,Arctic,inuktitut_naqittaut-1.3.js,1.6,ike-Cans,Eastern Canadian Inuktitut (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_uummarmiutun,Uummarmiutun,Arctic,fv_uummarmiutun_kmw-9.0.js,9.1,ikt-Latn,Inuinnaqtun (Latin)
|
||||
fv,fv_eastern_canadian_inuktitut,ᐃᓄᒃᑎᑐᑦ (Eastern Canadian Inuktitut),Arctic,fv_eastern_canadian_inuktitut_kmw-9.0.js,9.2,ike-Cans,Eastern Canadian Inuktitut (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_migmaq,Mi'gmawi'simg / Mi'kmawi'simk,Atlantic,fv_migmaq_kmw-9.0.js,9.1.1,mic-Latn,Mi'kmaq (Latin)
|
||||
fv,fv_skicinuwatuwewakon,Skicinuwatuwewakon,Atlantic,fv_skicinuwatuwewakon_kmw-9.0.js,9.1,pqm-Latn,Malecite-Passamaquoddy (Latin)
|
||||
fv,fv_uwikala,'Uwik̓ala,BC Coast,fv_uwikala_kmw-9.0.js,9.1.1,hei-Latn,Heiltsuk (Latin)
|
||||
fv,fv_dexwlesucid,Dəxʷləšucid,BC Coast,fv_dexwlesucid_kmw-9.0.js,9.2,lut-Latn,Lushootseed (Latin)
|
||||
fv,fv_diitiidatx,Diidiitidq,BC Coast,fv_diitiidatx_kmw-9.0.js,9.1.1,nuk-Latn,Nuu-chah-nulth (Latin)
|
||||
fv,fv_uummarmiutun,Uummarmiutun,Arctic,fv_uummarmiutun_kmw-9.0.js,9.1.1,ikt-Latn,Inuinnaqtun (Latin)
|
||||
fv,fv_eastern_canadian_inuktitut,ᐃᓄᒃᑎᑐᑦ (Eastern Canadian Inuktitut),Arctic,fv_eastern_canadian_inuktitut_kmw-9.0.js,9.2.1,ike-Cans,Eastern Canadian Inuktitut (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_migmaq,Mi'gmawi'simg / Mi'kmawi'simk,Atlantic,fv_migmaq_kmw-9.0.js,9.1.2,mic-Latn,Mi'kmaq (Latin)
|
||||
fv,fv_skicinuwatuwewakon,Skicinuwatuwewakon,Atlantic,fv_skicinuwatuwewakon_kmw-9.0.js,9.1.1,pqm-Latn,Malecite-Passamaquoddy (Latin)
|
||||
fv,fv_uwikala,'Uwik̓ala,BC Coast,fv_uwikala_kmw-9.0.js,9.3,hei-Latn,Heiltsuk (Latin)
|
||||
fv,fv_dexwlesucid,Dəxʷləšucid,BC Coast,fv_dexwlesucid_kmw-9.0.js,9.2.1,lut-Latn,Lushootseed (Latin)
|
||||
fv,fv_diitiidatx,Diidiitidq,BC Coast,fv_diitiidatx_kmw-9.0.js,9.1.3,nuk-Latn,Nuu-chah-nulth (Latin)
|
||||
fv,fv_gitsenimx,Gitsenimx̱,BC Coast,fv_gitsenimx_kmw-9.0.js,9.1.1,git-Latn,Gitxsan (Latin)
|
||||
fv,fv_hailzaqvla,Haiɫzaqvla,BC Coast,fv_hailzaqvla_kmw-9.0.js,9.4,hei,Heiltsuk (Latin)
|
||||
fv,fv_hailzaqvla,Haiɫzaqvla,BC Coast,fv_hailzaqvla_kmw-9.0.js,9.4.1,hei,Heiltsuk (Latin)
|
||||
fv,fv_haisla,Haisla,BC Coast,fv_haisla.js,1.0,has-Latn,Haisla (Latin)
|
||||
fv,fv_halqemeylem,Halq'eméylem,BC Coast,fv_halqemeylem_kmw-9.0.js,9.1.1,hur-Latn,Halkomelem (Latin)
|
||||
fv,fv_henqeminem,Hǝn̓q̓ǝmin̓ǝm,BC Coast,fv_henqeminem_kmw-9.0.js,9.1.1,hur-Latn,Halkomelem (Latin)
|
||||
fv,fv_halqemeylem,Halq'eméylem,BC Coast,fv_halqemeylem_kmw-9.0.js,9.1.3,hur-Latn,Halkomelem (Latin)
|
||||
fv,fv_henqeminem,Hǝn̓q̓ǝmin̓ǝm,BC Coast,fv_henqeminem_kmw-9.0.js,9.1.3,hur-Latn,Halkomelem (Latin)
|
||||
fv,fv_klahoose,Homalco-Klahoose-Sliammon,BC Coast,fv_klahoose_kmw-9.0.js,9.2,coo-Latn,Comox (Latin)
|
||||
fv,fv_hulquminum,Hul’q’umi’num’,BC Coast,fv_hulquminum_kmw-9.0.js,9.1,hur-Latn,Halkomelem (Latin)
|
||||
fv,fv_hulquminum_combine,Hul̓q̓umin̓um̓,BC Coast,fv_hulquminum_combine_kmw-9.0.js,1.0,hur-Latn,Halkomelem (Latin)
|
||||
fv,fv_kwakwala_liqwala,Kʷak̓ʷala,BC Coast,fv_kwakwala_liqwala_kmw-9.0.js,9.2.2,kwk-Latn,Kwakiutl (Latin)
|
||||
fv,fv_kwakwala,Kwak̕wala,BC Coast,fv_kwakwala_kmw-9.0.js,9.1.1,kwk-Latn,Kwakiutl (Latin)
|
||||
fv,fv_nexwslayemucen,Nəxʷsƛ̓ay̓əmúcən,BC Coast,fv_nexwslayemucen_kmw-9.0.js,9.2,clm-Latn,Clallam (Latin)
|
||||
fv,fv_nisgaa,Nisg̱a'a,BC Coast,fv_nisgaa_kmw-9.0.js,9.1.1,ncg-Latn,Nisga'a (Latin)
|
||||
fv,fv_nuucaanul,Nuučaan̓uł,BC Coast,fv_nuucaanul_kmw-9.0.js,9.1.1,nuk-Latn,Nuu-chah-nulth (Latin)
|
||||
fv,fv_nuxalk,Nuxalk,BC Coast,fv_nuxalk_kmw-9.0.js,9.1,blc-Latn,Bella Coola (Latin)
|
||||
fv,fv_sencoten,SENĆOŦEN,BC Coast,fv_sencoten_kmw-9.0.js,9.1.1,str-Latn,Straits Salish (Latin)
|
||||
fv,fv_shashishalhem,Shashishalhem,BC Coast,fv_shashishalhem_kmw-9.0.js,9.1.1,sec-Latn,Sechelt (Latin)
|
||||
fv,fv_skwxwumesh_snichim,Sḵwx̱wúmesh sníchim,BC Coast,fv_skwxwumesh_snichim_kmw-9.0.js,9.2,squ-Latn,Squamish (Latin)
|
||||
fv,fv_smalgyax,Sm'algya̱x,BC Coast,fv_smalgyax_kmw-9.0.js,9.1.1,tsi-Latn,Tsimshian (Latin)
|
||||
fv,fv_kwakwala_liqwala,Kʷak̓ʷala,BC Coast,fv_kwakwala_liqwala_kmw-9.0.js,9.2.5,kwk-Latn,Kwakiutl (Latin)
|
||||
fv,fv_kwakwala,Kwak̕wala,BC Coast,fv_kwakwala_kmw-9.0.js,9.1.2,kwk-Latn,Kwakiutl (Latin)
|
||||
fv,fv_nexwslayemucen,Nəxʷsƛ̓ay̓əmúcən,BC Coast,fv_nexwslayemucen_kmw-9.0.js,9.2.1,clm-Latn,Clallam (Latin)
|
||||
fv,fv_nisgaa,Nisg̱a'a,BC Coast,fv_nisgaa_kmw-9.0.js,9.1.2,ncg-Latn,Nisga'a (Latin)
|
||||
fv,fv_nuucaanul,Nuučaan̓uł,BC Coast,fv_nuucaanul_kmw-9.0.js,9.1.4,nuk-Latn,Nuu-chah-nulth (Latin)
|
||||
fv,fv_nuxalk,Nuxalk,BC Coast,fv_nuxalk_kmw-9.0.js,10.0,blc-Latn,Bella Coola (Latin)
|
||||
fv,fv_sencoten,SENĆOŦEN,BC Coast,fv_sencoten_kmw-9.0.js,9.2.1,str-Latn,Straits Salish (Latin)
|
||||
fv,fv_shashishalhem,Shashishalhem,BC Coast,fv_shashishalhem_kmw-9.0.js,9.1.3,sec-Latn,Sechelt (Latin)
|
||||
fv,fv_skwxwumesh_snichim,Sḵwx̱wúmesh sníchim,BC Coast,fv_skwxwumesh_snichim_kmw-9.0.js,9.2.1,squ-Latn,Squamish (Latin)
|
||||
fv,fv_smalgyax,Sm'algya̱x,BC Coast,fv_smalgyax_kmw-9.0.js,9.1.3,tsi-Latn,Tsimshian (Latin)
|
||||
fv,fv_xaislakala,X̄a'ʼislak̓ala,BC Coast,fv_xaislakala_kmw-9.0.js,9.1.1,has-Latn,Haisla (Latin)
|
||||
fv,fv_hlgaagilda_xaayda_kil,X̱aayda-X̱aad Kil,BC Coast,fv_hlgaagilda_xaayda_kil_kmw-9.0.js,9.2,hax-Latn,Southern Haida (Latin)
|
||||
fv,fv_dakelh,Dakelh,BC Interior,fv_dakelh_kmw-9.0.js,9.1.2,caf-Latn,Southern Carrier (Latin)
|
||||
fv,fv_ktunaxa,Ktunaxa,BC Interior,fv_ktunaxa_kmw-9.0.js,9.1.1,kut-Latn,Kutenai (Latin)
|
||||
fv,fv_dakelh,Dakelh,BC Interior,fv_dakelh_kmw-9.0.js,9.1.4,caf-Latn,Southern Carrier (Latin)
|
||||
fv,fv_ktunaxa,Ktunaxa,BC Interior,fv_ktunaxa_kmw-9.0.js,9.1.3,kut-Latn,Kutenai (Latin)
|
||||
fv,fv_kwadacha_tsekene,Kwadacha Tsek’ene,BC Interior,fv_kwadacha_tsekene_kmw-9.0.js,1.0,sek-Latn,Sekani (Latin)
|
||||
fv,fv_natwits,Nedut’en-Witsuwit'en,BC Interior,fv_natwits_kmw-9.0.js,9.1.1,caf-Latn,Southern Carrier (Latin)
|
||||
fv,fv_nlekepmxcin,Nłeʔkepmxcin,BC Interior,fv_nlekepmxcin_kmw-9.0.js,9.2.1,thp-Latn,Thompson (Latin)
|
||||
fv,fv_nlha7kapmxtsin,Nlha7kapmxtsin,BC Interior,fv_nlha7kapmxtsin_kmw-9.0.js,9.1,thp-Latn,Thompson (Latin)
|
||||
fv,fv_nsilxcen,Nsilxcən,BC Interior,fv_nsilxcen_kmw-9.0.js,9.1.1,oka-Latn,Okanagan (Latin)
|
||||
fv,fv_secwepemctsin,Secwepemctsín,BC Interior,fv_secwepemctsin_kmw-9.0.js,9.1.1,shs-Latn,Shuswap (Latin)
|
||||
fv,fv_stlatlimxec,Sƛ̓aƛ̓imxəc,BC Interior,fv_stlatlimxec_kmw-9.0.js,9.2.1,lil-Latn,Lillooet (Latin)
|
||||
fv,fv_statimcets,St̓át̓imcets,BC Interior,fv_statimcets_kmw-9.0.js,9.1.1,lil-Latn,Lillooet (Latin)
|
||||
fv,fv_taltan,Tāłtān,BC Interior,fv_taltan_kmw-9.0.js,9.1.2,tht-Latn,Tahltan (Latin)
|
||||
fv,fv_tsekehne,Tsek'ehne,BC Interior,fv_tsekehne_kmw-9.0.js,9.1.1,sek-Latn,Sekani (Latin)
|
||||
fv,fv_tsilhqotin,Tŝilhqot'in,BC Interior,fv_tsilhqotin_kmw-9.0.js,9.1.1,clc-Latn,Chilcotin (Latin)
|
||||
fv,fv_southern_carrier,ᑐᑊᘁᗕᑋᗸ (Southern Carrier),BC Interior,fv_southern_carrier_kmw-9.0.js,9.2,caf-Cans,Southern Carrier (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_anicinapemi8in,Anicinapemi8in/Anishinàbemiwin,Eastern Subarctic,fv_anicinapemi8in_kmw-9.0.js,9.1,alq-Latn,Algonquin (Latin)
|
||||
fv,fv_atikamekw,Atikamekw,Eastern Subarctic,fv_atikamekw_kmw-9.0.js,9.1,atj-Latn,Atikamekw (Latin)
|
||||
fv,fv_ilnu_innu_aimun,Ilnu-Innu Aimun,Eastern Subarctic,fv_ilnu_innu_aimun_kmw-9.0.js,9.1,moe-Latn,Montagnais (Latin)
|
||||
fv,fv_swampy_cree,ᐃᓂᓂᒧᐎᐣ (Swampy Cree),Eastern Subarctic,fv_swampy_cree_kmw-9.0.js,9.2,csw-Cans,Swampy Cree (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_moose_cree,ᐃᓕᓖᒧᐎᓐ (Moose Cree),Eastern Subarctic,fv_moose_cree_kmw-9.0.js,9.2,crm-Cans,Moose Cree (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_northern_east_cree,ᐄᔨᔫ-ᐄᓅ ᐊᔨᒨᓐ (Northern East Cree),Eastern Subarctic,fv_northern_east_cree_kmw-9.0.js,9.2,crl-Cans,Northern East Cree (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_severn_ojibwa,ᐊᓂᔑᓂᓂᒧᐎᐣ (Severn Ojibwa),Eastern Subarctic,fv_severn_ojibwa_kmw-9.0.js,9.3,ojs-Cans,Severn Ojibwa (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_ojibwa,ᐊᓂᔑᓇᐯᒧᐎᓐ (Ojibwa),Eastern Subarctic,fv_ojibwa_kmw-9.0.js,9.3,ojb-Cans,Northwestern Ojibwa (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_naskapi,ᓇᔅᑲᐱ (Naskapi),Eastern Subarctic,fv_naskapi_kmw-9.0.js,9.3,nsk-Cans,Naskapi (Unified Canadian Aboriginal Syllabics)
|
||||
sil,sil_euro_latin,English,European,european2-1.6.js,1.9.5,en,English
|
||||
basic,basic_kbdcan,Français,European,canadian_french-1.0.js,1.1,fr-CA,French (Canada)
|
||||
fv,fv_anishinaabemowin,Anishinaabemowin,Great Lakes - St. Lawrence,fv_anishinaabemowin_kmw-9.0.js,9.1,oj-Cans,Ojibwa (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_bodewadminwen,Bodéwadminwen-Nishnabémwen,Great Lakes - St. Lawrence,fv_bodewadminwen_kmw-9.0.js,9.1,pot-Latn,Potawatomi (Latin)
|
||||
fv,fv_goyogohono,Goyogo̱hó:nǫ',Great Lakes - St. Lawrence,fv_goyogohono_kmw-9.0.js,9.2,cay-Latn,Cayuga (Latin)
|
||||
fv,fv_kanienkeha_e,Kanien'kéha-Kanyen'kéha,Great Lakes - St. Lawrence,fv_kanienkeha_e_kmw-9.0.js,9.1.1,moh-Latn,Mohawk (Latin)
|
||||
fv,fv_lunaapeewi_huluniixsuwaakan,Lunáapeewi-hulŭniixsuwáakan,Great Lakes - St. Lawrence,fv_lunaapeewi_huluniixsuwaakan_kmw-9.0.js,9.1,umu-Latn,Munsee (Latin)
|
||||
fv,fv_onodagega_onondagega,Onǫda'géga'-Onoñda'géga',Great Lakes - St. Lawrence,fv_onodagega_onondagega_kmw-9.0.js,9.2,ono-Latn,Onondaga (Latin)
|
||||
fv,fv_onodowaga,Onödowága,Great Lakes - St. Lawrence,fv_onodowaga_kmw-9.0.js,9.2,see-Latn,Seneca (Latin)
|
||||
fv,fv_onayotaaka,Onʌyota'a:ka,Great Lakes - St. Lawrence,fv_onayotaaka_kmw-9.0.js,9.2,one-Latn,Oneida (Latin)
|
||||
fv,fv_skaru_re,Skarù·reʔ,Great Lakes - St. Lawrence,fv_skaru_re_kmw-9.0.js,9.2,tus-Latn,Tuscarora (Latin)
|
||||
fv,fv_wendat,Wendat,Great Lakes - St. Lawrence,fv_wendat_kmw-9.0.js,9.1,wya-Latn,Wyandot (Latin)
|
||||
fv,fv_wobanakiodwawogan,Wôbanakiôdwawôgan,Great Lakes - St. Lawrence,fv_wobanakiodwawogan_kmw-9.0.js,9.1,abe-Latn,Western Abnaki (Latin)
|
||||
fv,fv_australian,Australian,Pacific,fv_australian_kmw-9.0.js,9.3,pjt-Latn,Pitjantjatjara (Latin)
|
||||
fv,fv_maori,Māori,Pacific,fv_maori_kmw-9.0.js,9.1,mi-Latn,Maori (Latin)
|
||||
fv,fv_blackfoot,Blackfoot,Prairies,fv_blackfoot_kmw-9.0.js,9.2,bla-Latn,Siksika (Latin)
|
||||
fv,fv_cree_latin,Cree - Roman Orthography,Prairies,fv_cree_latin_kmw-9.0.js,9.1,cr-Latn,Cree (Latin)
|
||||
fv,fv_dakota_mb,Dakota,Prairies,fv_dakota_mb_kmw-9.0.js,9.1,dak-Latn,Dakota (Latin)
|
||||
fv,fv_dakota_sk,Dakot̄a,Prairies,fV_dakota_sk_kmw-9.0.js,9.1,dak-Latn,Dakota (Latin)
|
||||
fv,fv_isga_iabi,Isga Iʔabi,Prairies,fv_isga_iabi_kmw-9.0.js,9.1,sto-Latn,Stoney (Latin)
|
||||
fv,fv_lakota,Lak̇ot̄a,Prairies,fv_lakota-9.0.js,9.1,lkt-Latn,Lakota (Latin)
|
||||
fv,fv_nakoda,Nakoda,Prairies,fv_nakoda_kmw-9.0.js,9.1,asb-Latn,Assiniboine (Latin)
|
||||
fv,fv_tsuutina,Tsúùt'ínà,Prairies,fv_tsuutina_kmw-9.0.js,9.1,srs-Latn,Sarsi (Latin)
|
||||
fv,fv_natwits,Nedut’en-Witsuwit'en,BC Interior,fv_natwits_kmw-9.0.js,9.1.3,caf-Latn,Southern Carrier (Latin)
|
||||
fv,fv_nlekepmxcin,Nłeʔkepmxcin,BC Interior,fv_nlekepmxcin_kmw-9.0.js,9.2.3,thp-Latn,Thompson (Latin)
|
||||
fv,fv_nlha7kapmxtsin,Nlha7kapmxtsin,BC Interior,fv_nlha7kapmxtsin_kmw-9.0.js,9.1.1,thp-Latn,Thompson (Latin)
|
||||
fv,fv_nsilxcen,Nsilxcən,BC Interior,fv_nsilxcen_kmw-9.0.js,9.2,oka-Latn,Okanagan (Latin)
|
||||
fv,fv_secwepemctsin,Secwepemctsín,BC Interior,fv_secwepemctsin_kmw-9.0.js,9.1.2,shs-Latn,Shuswap (Latin)
|
||||
fv,fv_stlatlimxec,Sƛ̓aƛ̓imxəc,BC Interior,fv_stlatlimxec_kmw-9.0.js,9.2.3,lil-Latn,Lillooet (Latin)
|
||||
fv,fv_statimcets,St̓át̓imcets,BC Interior,fv_statimcets_kmw-9.0.js,9.1.3,lil-Latn,Lillooet (Latin)
|
||||
fv,fv_taltan,Tāłtān,BC Interior,fv_taltan_kmw-9.0.js,9.1.5,tht-Latn,Tahltan (Latin)
|
||||
fv,fv_tsekehne,Tsek'ehne,BC Interior,fv_tsekehne_kmw-9.0.js,9.1.2,sek-Latn,Sekani (Latin)
|
||||
fv,fv_tsilhqotin,Tŝilhqot'in,BC Interior,fv_tsilhqotin_kmw-9.0.js,9.1.3,clc-Latn,Chilcotin (Latin)
|
||||
fv,fv_southern_carrier,ᑐᑊᘁᗕᑋᗸ (Southern Carrier),BC Interior,fv_southern_carrier_kmw-9.0.js,9.2.1,caf-Cans,Southern Carrier (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_anicinapemi8in,Anicinapemi8in/Anishinàbemiwin,Eastern Subarctic,fv_anicinapemi8in_kmw-9.0.js,9.1.1,alq-Latn,Algonquin (Latin)
|
||||
fv,fv_atikamekw,Atikamekw,Eastern Subarctic,fv_atikamekw_kmw-9.0.js,9.1.1,atj-Latn,Atikamekw (Latin)
|
||||
fv,fv_ilnu_innu_aimun,Ilnu-Innu Aimun,Eastern Subarctic,fv_ilnu_innu_aimun_kmw-9.0.js,9.1.1,moe-Latn,Montagnais (Latin)
|
||||
fv,fv_swampy_cree,ᐃᓂᓂᒧᐎᐣ (Swampy Cree),Eastern Subarctic,fv_swampy_cree_kmw-9.0.js,9.2.1,csw-Cans,Swampy Cree (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_moose_cree,ᐃᓕᓖᒧᐎᓐ (Moose Cree),Eastern Subarctic,fv_moose_cree_kmw-9.0.js,9.2.1,crm-Cans,Moose Cree (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_northern_east_cree,ᐄᔨᔫ-ᐄᓅ ᐊᔨᒨᓐ (Northern East Cree),Eastern Subarctic,fv_northern_east_cree_kmw-9.0.js,9.2.1,crl-Cans,Northern East Cree (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_severn_ojibwa,ᐊᓂᔑᓂᓂᒧᐎᐣ (Severn Ojibwa),Eastern Subarctic,fv_severn_ojibwa_kmw-9.0.js,9.3.1,ojs-Cans,Severn Ojibwa (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_ojibwa,ᐊᓂᔑᓇᐯᒧᐎᓐ (Ojibwa),Eastern Subarctic,fv_ojibwa_kmw-9.0.js,9.3.1,ojb-Cans,Northwestern Ojibwa (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_naskapi,ᓇᔅᑲᐱ (Naskapi),Eastern Subarctic,fv_naskapi_kmw-9.0.js,9.3.1,nsk-Cans,Naskapi (Unified Canadian Aboriginal Syllabics)
|
||||
sil,sil_euro_latin,English,European,european2-1.6.js,2.0.4,en,English
|
||||
basic,basic_kbdcan,Français,European,canadian_french-1.0.js,1.1.1,fr-CA,French (Canada)
|
||||
fv,fv_anishinaabemowin,Anishinaabemowin,Great Lakes - St. Lawrence,fv_anishinaabemowin_kmw-9.0.js,9.1.1,oj-Cans,Ojibwa (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_bodewadminwen,Bodéwadminwen-Nishnabémwen,Great Lakes - St. Lawrence,fv_bodewadminwen_kmw-9.0.js,9.1.1,pot-Latn,Potawatomi (Latin)
|
||||
fv,fv_goyogohono,Goyogo̱hó:nǫ',Great Lakes - St. Lawrence,fv_goyogohono_kmw-9.0.js,9.2.1,cay-Latn,Cayuga (Latin)
|
||||
fv,fv_kanienkeha_e,Kanien'kéha-Kanyen'kéha,Great Lakes - St. Lawrence,fv_kanienkeha_e_kmw-9.0.js,9.1.3,moh-Latn,Mohawk (Latin)
|
||||
fv,fv_lunaapeewi_huluniixsuwaakan,Lunáapeewi-hulŭniixsuwáakan,Great Lakes - St. Lawrence,fv_lunaapeewi_huluniixsuwaakan_kmw-9.0.js,9.1.1,umu-Latn,Munsee (Latin)
|
||||
fv,fv_onodagega_onondagega,Onǫda'géga'-Onoñda'géga',Great Lakes - St. Lawrence,fv_onodagega_onondagega_kmw-9.0.js,9.2.1,ono-Latn,Onondaga (Latin)
|
||||
fv,fv_onodowaga,Onödowága,Great Lakes - St. Lawrence,fv_onodowaga_kmw-9.0.js,9.2.1,see-Latn,Seneca (Latin)
|
||||
fv,fv_onayotaaka,Onʌyota'a:ka,Great Lakes - St. Lawrence,fv_onayotaaka_kmw-9.0.js,9.2.1,one-Latn,Oneida (Latin)
|
||||
fv,fv_skaru_re,Skarù·reʔ,Great Lakes - St. Lawrence,fv_skaru_re_kmw-9.0.js,9.2.1,tus-Latn,Tuscarora (Latin)
|
||||
fv,fv_wendat,Wendat,Great Lakes - St. Lawrence,fv_wendat_kmw-9.0.js,9.1.1,wya-Latn,Wyandot (Latin)
|
||||
fv,fv_wobanakiodwawogan,Wôbanakiôdwawôgan,Great Lakes - St. Lawrence,fv_wobanakiodwawogan_kmw-9.0.js,9.1.1,abe-Latn,Western Abnaki (Latin)
|
||||
fv,fv_australian,Australian,Pacific,fv_australian_kmw-9.0.js,9.3.1,pjt-Latn,Pitjantjatjara (Latin)
|
||||
fv,fv_maori,Māori,Pacific,fv_maori_kmw-9.0.js,9.1.1,mi-Latn,Maori (Latin)
|
||||
fv,fv_blackfoot,Blackfoot,Prairies,fv_blackfoot_kmw-9.0.js,9.2.1,bla-Latn,Siksika (Latin)
|
||||
fv,fv_cree_latin,Cree - Roman Orthography,Prairies,fv_cree_latin_kmw-9.0.js,9.1.1,cr-Latn,Cree (Latin)
|
||||
fv,fv_dakota_mb,Dakota,Prairies,fv_dakota_mb_kmw-9.0.js,9.1.1,dak-Latn,Dakota (Latin)
|
||||
fv,fv_dakota_sk,Dakot̄a,Prairies,fV_dakota_sk_kmw-9.0.js,9.1.1,dak-Latn,Dakota (Latin)
|
||||
fv,fv_isga_iabi,Isga Iʔabi,Prairies,fv_isga_iabi_kmw-9.0.js,9.1.1,sto-Latn,Stoney (Latin)
|
||||
fv,fv_lakota,Lak̇ot̄a,Prairies,fv_lakota-9.0.js,9.1.1,lkt-Latn,Lakota (Latin)
|
||||
fv,fv_nakoda,Nakoda,Prairies,fv_nakoda_kmw-9.0.js,9.1.1,asb-Latn,Assiniboine (Latin)
|
||||
fv,fv_tsuutina,Tsúùt'ínà,Prairies,fv_tsuutina_kmw-9.0.js,9.1.1,srs-Latn,Sarsi (Latin)
|
||||
fv,fv_plains_cree,ᓀᐦᐃᔭᐍᐏᐣ (Plains Cree),Prairies,fv_plains_cree_kmw-9.0.js,10.0.2,crk-Cans,ᓀᐦᐃᔭᐍᐏᐣ (Cree syllabics)
|
||||
fv,fv_dine_bizaad,Diné Bizaad,South West,fv_dine_bizaad_kmw-9.0.js,9.1,nv-Latn,Navajo (Latin)
|
||||
fv,fv_dane_zaa_zaage,Dane-Z̲aa Z̲áágéʔ,Western Subarctic,fv_dane_zaa_zaage_kmw-9.0.js,9.2,bea,Beaver
|
||||
fv,fv_dene_dzage,Dene Dzage,Western Subarctic,fv_dene_dzage_kmw-9.0.js,9.1,kkz-Latn,Kaska (Latin)
|
||||
fv,fv_dene_zhatie,Dene Zhatié,Western Subarctic,fv_dene_zhatie_kmw-9.0.js,9.1,xsl-Latn,South Slavey (Latin)
|
||||
fv,fv_denesuline_epsilon,Dënesųłıné,Western Subarctic,fv_denesuline_epsilon_kmw-9.0.js,9.1.1,chp-Latn,Chipewyan (Latin)
|
||||
fv,fv_denesuline,Dɛnɛsųłiné,Western Subarctic,fv_denesuline_kmw-9.0.js,9.1,chp-Latn,Chipewyan (Latin)
|
||||
fv,fv_dine_bizaad,Diné Bizaad,South West,fv_dine_bizaad_kmw-9.0.js,9.1.1,nv-Latn,Navajo (Latin)
|
||||
fv,fv_dane_zaa_zaage,Dane-Z̲aa Z̲áágéʔ,Western Subarctic,fv_dane_zaa_zaage_kmw-9.0.js,9.3,bea,Beaver
|
||||
fv,fv_dene_dzage,Dene Dzage,Western Subarctic,fv_dene_dzage_kmw-9.0.js,9.1.1,kkz-Latn,Kaska (Latin)
|
||||
fv,fv_dene_zhatie,Dene Zhatié,Western Subarctic,fv_dene_zhatie_kmw-9.0.js,10.0.2,xsl-Latn,South Slavey (Latin)
|
||||
fv,fv_denesuline_epsilon,Dënesųłıné,Western Subarctic,fv_denesuline_epsilon_kmw-9.0.js,9.1.4,chp-Latn,Chipewyan (Latin)
|
||||
fv,fv_denesuline,Dɛnɛsųłiné,Western Subarctic,fv_denesuline_kmw-9.0.js,9.1.1,chp-Latn,Chipewyan (Latin)
|
||||
fv,fv_gwichin,Gwich'in,Western Subarctic,fv_gwichin_kmw-9.0.js,9.2.1,gwi-Latn,Gwichʼin (Latin)
|
||||
fv,fv_han,Hän,Western Subarctic,fv_han_kmw-9.0.js,9.2,haa-Latn,Han (Latin)
|
||||
fv,fv_kashogotine_yati,K'áshogot'ı̨nę́ Yatı̨́,Western Subarctic,fv_kashogotine_yati_kmw-9.0.js,9.1,scs-Latn,North Slavey (Latin)
|
||||
fv,fv_tlingit,Łingít,Western Subarctic,fv_tlingit_kmw-9.0.js,9.2,tli-Latn,Tlingit (Latin)
|
||||
fv,fv_neeaandeg,Nee'aanděg',Western Subarctic,fv_neeaandeg_kmw-9.0.js,9.1,tcb-Latn,Tanacross (Latin)
|
||||
fv,fv_kashogotine_yati,K'áshogot'ı̨nę́ Yatı̨́,Western Subarctic,fv_kashogotine_yati_kmw-9.0.js,9.1.1,scs-Latn,North Slavey (Latin)
|
||||
fv,fv_tlingit,Łingít,Western Subarctic,fv_tlingit_kmw-9.0.js,10.0.1,tli-Latn,Tlingit (Latin)
|
||||
fv,fv_neeaandeg,Nee'aanděg',Western Subarctic,fv_neeaandeg_kmw-9.0.js,9.1.1,tcb-Latn,Tanacross (Latin)
|
||||
fv,fv_neeaaneegn,Nee'aaneegn',Western Subarctic,fv_neeaaneegn_kmw-9.0.js,9.1,tau-Latn,Upper Tanana (Latin)
|
||||
fv,fv_northern_tutchone,Northern Tutchone,Western Subarctic,fv_northern_tutchone_kmw-9.0.js,9.2,ttm-Latn,Northern Tutchone (Latin)
|
||||
fv,fv_sahugotine_yati,Sahtúgot'ı̨nę́ Yatı̨́,Western Subarctic,fv_sahugotine_yati_kmw-9.0.js,9.1,scs-Latn,North Slavey (Latin)
|
||||
fv,fv_shihgotine_yati,Shıhgot'ı̨nę́ Yatı̨́,Western Subarctic,fv_shihgotine_yati_kmw-9.0.js,9.1,scs-Latn,North Slavey (Latin)
|
||||
fv,fv_southern_tutchone,Southern Tutchone,Western Subarctic,fv_southern_tutchone_kmw-9.0.js,9.2.1,tce-Latn,Southern Tutchone (Latin)
|
||||
fv,fv_tagizi_dene,Tāgizi Dene,Western Subarctic,fv_tagizi_dene_kmw-9.0.js,9.2,tgx-Latn,Tagish (Latin)
|
||||
fv,fv_tlicho_yatii,Tłı̨chǫ Yatıı̀,Western Subarctic,fv_tlicho_yatii_kmw-9.0.js,9.1,dgr-Latn,Dogrib (Latin)
|
||||
fv,fv_dene_mb,ᑌᓀ ᔭᕠᐁ (Dene MB),Western Subarctic,fv_dene_mb_kmw-9.0.js,9.2,chp-Cans,Chipewyan (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_dene_nt,ᑌᓀ ᔭᕱᐁ (Dene NT),Western Subarctic,fv_dene_nt_kmw-9.0.js,9.2,chp-Cans,Chipewyan (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_sahugotine_yati,Sahtúgot'ı̨nę́ Yatı̨́,Western Subarctic,fv_sahugotine_yati_kmw-9.0.js,9.1.1,scs-Latn,North Slavey (Latin)
|
||||
fv,fv_shihgotine_yati,Shıhgot'ı̨nę́ Yatı̨́,Western Subarctic,fv_shihgotine_yati_kmw-9.0.js,9.1.1,scs-Latn,North Slavey (Latin)
|
||||
fv,fv_southern_tutchone,Southern Tutchone,Western Subarctic,fv_southern_tutchone_kmw-9.0.js,9.2.2,tce-Latn,Southern Tutchone (Latin)
|
||||
fv,fv_tagizi_dene,Tāgizi Dene,Western Subarctic,fv_tagizi_dene_kmw-9.0.js,9.2.1,tgx-Latn,Tagish (Latin)
|
||||
fv,fv_tlicho_yatii,Tłı̨chǫ Yatıı̀,Western Subarctic,fv_tlicho_yatii_kmw-9.0.js,9.1.1,dgr-Latn,Dogrib (Latin)
|
||||
fv,fv_dene_mb,ᑌᓀ ᔭᕠᐁ (Dene MB),Western Subarctic,fv_dene_mb_kmw-9.0.js,9.2.1,chp-Cans,Chipewyan (Unified Canadian Aboriginal Syllabics)
|
||||
fv,fv_dene_nt,ᑌᓀ ᔭᕱᐁ (Dene NT),Western Subarctic,fv_dene_nt_kmw-9.0.js,9.2.1,chp-Cans,Chipewyan (Unified Canadian Aboriginal Syllabics)
|
||||
|
|
|
|||
|
1
package-lock.json
generated
1
package-lock.json
generated
|
|
@ -1788,6 +1788,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@keymanapp/developer-test-helpers": "*",
|
||||
"@keymanapp/resources-gosh": "*",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "^20.4.1",
|
||||
|
|
|
|||
90
resources/build/ci/cancel-builds/cancel-test-builds.mjs
Normal file
90
resources/build/ci/cancel-builds/cancel-test-builds.mjs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import { testBuildConfigurations } from './trigger_definitions.mjs';
|
||||
|
||||
if(process.argv.length != 4) {
|
||||
console.error('Invalid parameters!');
|
||||
console.error('Usage: node cancel-test-builds.mjs PR|branch TEAMCITY_TOKEN');
|
||||
console.error(' PR number should be a pull request number, without preceding #');
|
||||
console.error(' branch can be one of master, beta or stable-x.y, where x.y is a stable release');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const allTestBuildConfigurations = Object.keys(testBuildConfigurations).flatMap(item => testBuildConfigurations[item]);
|
||||
const branchName = process.argv[2];
|
||||
const authorizationHeader = "Bearer "+process.argv[3];
|
||||
|
||||
console.log(`Cancelling all queued and running test builds for ${branchName}`);
|
||||
|
||||
async function getTeamCity(api) {
|
||||
const response = await fetch('https://build.palaso.org/app/rest/'+api, {
|
||||
headers: {
|
||||
"Authorization": authorizationHeader,
|
||||
"Accept": "application/json"
|
||||
}
|
||||
});
|
||||
if(!response.ok) {
|
||||
return null;
|
||||
}
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async function postTeamCity(api, body) {
|
||||
const response = await fetch('https://build.palaso.org/app/rest/'+api, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Authorization": authorizationHeader,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: body
|
||||
});
|
||||
if(!response.ok) {
|
||||
console.warn(`API ${api} failed with HTTP/${response.status}: ${response.statusText}`);
|
||||
return null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getQueuedBuilds() {
|
||||
return await getTeamCity('buildQueue?locator=count:200');
|
||||
}
|
||||
|
||||
async function cancelQueuedBuild(id, buildTypeId) {
|
||||
console.log(`Cancelling queued build ${id} on ${branchName} for ${buildTypeId}`);
|
||||
if(await postTeamCity('buildQueue/id:'+id, `{ "buildCancelRequest": { "comment": "Cancelling build due to new commits on branch.", "readdIntoQueue": "false" } }`)) {
|
||||
console.log(' Build successfully cancelled.');
|
||||
}
|
||||
}
|
||||
|
||||
async function getRunningBuilds() {
|
||||
return await getTeamCity('builds?locator=running:true,count:200');
|
||||
}
|
||||
|
||||
async function cancelRunningBuild(id, buildTypeId) {
|
||||
console.log(`Cancelling running build ${id} on ${branchName} for ${buildTypeId}`);
|
||||
if(await postTeamCity('builds/id:'+id, `{ "buildCancelRequest": { "comment": "Cancelling build due to new commits on branch.", "readdIntoQueue": "false" } }`)) {
|
||||
console.log(' Build successfully cancelled.');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const queuedBuilds = await getQueuedBuilds();
|
||||
if(queuedBuilds) {
|
||||
for(const build of queuedBuilds.build) {
|
||||
if(allTestBuildConfigurations.includes(build.buildTypeId) && build.branchName == branchName) {
|
||||
await cancelQueuedBuild(build.id, build.buildTypeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const runningBuilds = await getRunningBuilds();
|
||||
if(runningBuilds) {
|
||||
for(const build of runningBuilds.build) {
|
||||
if(allTestBuildConfigurations.includes(build.buildTypeId) && build.branchName == branchName) {
|
||||
await cancelRunningBuild(build.id, build.buildTypeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
// we don't want to fail a trigger because of a hiccup in this script
|
||||
console.trace(e);
|
||||
process.exit(0);
|
||||
}
|
||||
15
resources/build/ci/cancel-builds/trigger_definitions.mjs
Normal file
15
resources/build/ci/cancel-builds/trigger_definitions.mjs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// Maps to trigger-definitions.inc.sh and must be kept in sync
|
||||
export const testBuildConfigurations = {
|
||||
'android': ['KeymanAndroid_TestPullRequests', 'KeymanAndroid_TestSamplesAndTestProjects'],
|
||||
'ios': ['Keyman_iOS_TestPullRequests', 'Keyman_iOS_TestSamplesAndTestProjects'],
|
||||
'linux': ['KeymanLinux_TestPullRequests', 'Keyman_Linux_Test_Integration', 'Keyman_Common_KPAPI_TestPullRequests_Linux', 'deb-pr-packaging_GitHub'],
|
||||
'mac': ['Keyman_KeymanMac_PullRequests', 'Keyman_Common_KPAPI_TestPullRequests_macOS'],
|
||||
'windows': ['KeymanDesktop_TestPullRequests', 'KeymanDesktop_TestPrRenderOnScreenKeyboards', 'Keyman_Common_KPAPI_TestPullRequests_Windows'],
|
||||
'web': ['Keymanweb_TestPullRequests', 'Keyman_Common_LMLayer_TestPullRequests', 'Keyman_Common_KPAPI_TestPullRequests_WASM'],
|
||||
'developer': ['Keyman_Developer_Test'],
|
||||
|
||||
'common_web': ['Keyman_Test_Common_Web'],
|
||||
'common_windows': ['Keyman_Test_Common_Windows'],
|
||||
'common_mac': ['Keyman_Test_Common_Mac'],
|
||||
'common_linux': ['Keyman_Test_Common_Linux'],
|
||||
};
|
||||
|
|
@ -49,6 +49,9 @@ function triggerTestBuilds() {
|
|||
local branch="$2"
|
||||
local force="${3:-false}"
|
||||
|
||||
# Cancel any already running builds for this branch
|
||||
node "$THIS_SCRIPT_PATH/ci/cancel-builds/cancel-test-builds.mjs" "$branch" "$TEAMCITY_TOKEN"
|
||||
|
||||
for platform in "${platforms[@]}"; do
|
||||
echo "# $platform: checking for changes"
|
||||
eval test_builds='(${'bc_test_$platform'[@]})'
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
#
|
||||
# This file maps specific paths to build triggers
|
||||
#
|
||||
# Maps to ci/cancel-builds/trigger-definitions.mjs and must be kept in sync
|
||||
#
|
||||
|
||||
|
||||
available_platforms=(android common_web common_windows common_mac common_linux ios linux mac web windows developer)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,3 +20,7 @@ Each directory contains:
|
|||
- change /keyboard/keys/key type to "array"
|
||||
|
||||
- `imports/` - the importable data files (TODO-LDML)
|
||||
|
||||
## License
|
||||
|
||||
- [unicode-license.txt](unicode-license.txt) - Unicode license
|
||||
39
resources/standards-data/ldml-keyboards/unicode-license.txt
Normal file
39
resources/standards-data/ldml-keyboards/unicode-license.txt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
UNICODE LICENSE V3
|
||||
|
||||
COPYRIGHT AND PERMISSION NOTICE
|
||||
|
||||
Copyright © 1991-2024 Unicode, Inc.
|
||||
|
||||
NOTICE TO USER: Carefully read the following legal agreement. BY
|
||||
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
|
||||
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
|
||||
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
|
||||
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of data files and any associated documentation (the "Data Files") or
|
||||
software and any associated documentation (the "Software") to deal in the
|
||||
Data Files or Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, and/or sell
|
||||
copies of the Data Files or Software, and to permit persons to whom the
|
||||
Data Files or Software are furnished to do so, provided that either (a)
|
||||
this copyright and permission notice appear with all copies of the Data
|
||||
Files or Software, or (b) this copyright and permission notice appear in
|
||||
associated Documentation.
|
||||
|
||||
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
THIRD PARTY RIGHTS.
|
||||
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
|
||||
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
|
||||
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
|
||||
FILES OR SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of a copyright holder shall
|
||||
not be used in advertising or otherwise to promote the sale, use or other
|
||||
dealings in these Data Files or Software without prior written
|
||||
authorization of the copyright holder.
|
||||
|
|
@ -647,7 +647,7 @@ export default class KeymanEngine extends KeymanEngineBase<BrowserConfiguration,
|
|||
* @param {string} PInternalName internal name of keyboard, with or without Keyboard_ prefix
|
||||
* @param {number} Pstatic static keyboard flag (unselectable elements)
|
||||
* @param {string=} argFormFactor layout form factor, defaulting to 'desktop'
|
||||
* @param {(string|number)=} argLayerId name or index of layer to show, defaulting to 'default'
|
||||
* @param {(string)=} argLayerId name or index of layer to show, defaulting to 'default'
|
||||
* @return {Object} DIV object with filled keyboard layer content
|
||||
*
|
||||
* See https://help.keyman.com/developer/engine/web/current-version/reference/osk/BuildVisualKeyboard
|
||||
|
|
@ -656,7 +656,7 @@ export default class KeymanEngine extends KeymanEngineBase<BrowserConfiguration,
|
|||
PInternalName: string,
|
||||
Pstatic: number,
|
||||
argFormFactor?: DeviceSpec.FormFactor,
|
||||
argLayerId?: string|number
|
||||
argLayerId?: string
|
||||
): HTMLElement {
|
||||
let PKbd: Keyboard = null;
|
||||
|
||||
|
|
@ -679,7 +679,7 @@ export default class KeymanEngine extends KeymanEngineBase<BrowserConfiguration,
|
|||
return VisualKeyboard.buildDocumentationKeyboard(
|
||||
PKbd,
|
||||
Pstub,
|
||||
this.config.paths.fonts,
|
||||
this.config.paths,
|
||||
argFormFactor,
|
||||
argLayerId,
|
||||
targetHeight
|
||||
|
|
|
|||
|
|
@ -6,10 +6,15 @@ type FontFamilyStyleMap = {[family: string]: HTMLStyleElement};
|
|||
export class StylesheetManager {
|
||||
private fontStyleDefinitions: { [os: string]: FontFamilyStyleMap} = {};
|
||||
private linkedSheets: HTMLStyleElement[] = [];
|
||||
private fontPromises: Promise<FontFace>[] = [];
|
||||
private doCacheBusting: boolean;
|
||||
|
||||
public readonly linkNode: Node;
|
||||
|
||||
public get sheets(): readonly HTMLStyleElement[] {
|
||||
return this.linkedSheets;
|
||||
}
|
||||
|
||||
public constructor(linkNode?: Node, doCacheBusting?: boolean) {
|
||||
if(!linkNode) {
|
||||
let _ElemHead=document.getElementsByTagName('HEAD');
|
||||
|
|
@ -24,6 +29,10 @@ export class StylesheetManager {
|
|||
}
|
||||
|
||||
linkStylesheet(sheet: HTMLStyleElement) {
|
||||
if(!(sheet instanceof HTMLLinkElement) && !sheet.innerHTML) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.linkedSheets.push(sheet);
|
||||
this.linkNode.appendChild(sheet);
|
||||
}
|
||||
|
|
@ -32,21 +41,36 @@ export class StylesheetManager {
|
|||
* Provides a `Promise` that resolves when all currently-linked stylesheets have loaded.
|
||||
* Any change to the set of linked sheets after the initial call will be ignored.
|
||||
*/
|
||||
async allLoadedPromise() {
|
||||
async allLoadedPromise(): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
for(const sheetElem of this.linkedSheets) {
|
||||
// Based on https://stackoverflow.com/a/21147238
|
||||
if(sheetElem.sheet?.cssRules) {
|
||||
promises.push(Promise.resolve());
|
||||
} else if(sheetElem.innerHTML) {
|
||||
// NOT at the StackOverflow link, but something I found experimentally.
|
||||
// Needed for live-constructed sheets with no corresponding file.
|
||||
promises.push(Promise.resolve());
|
||||
} else {
|
||||
const promise = new ManagedPromise<void>();
|
||||
sheetElem.addEventListener('load', () => promise.resolve());
|
||||
sheetElem.addEventListener('error', () => promise.reject());
|
||||
promises.push(promise.corePromise);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
const allPromises = promises.concat(this.fontPromises as Promise<any>[]);
|
||||
if(Promise.allSettled) {
|
||||
// allSettled - Chrome 76 / Safari 13
|
||||
// Delays for settling (either then OR catch) for ALL promises.
|
||||
await Promise.allSettled(allPromises)
|
||||
} else {
|
||||
// all - Chrome 32
|
||||
// If an error happens, .all instantly resolves regardless of state of
|
||||
// other Promises.
|
||||
await Promise.all(allPromises);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -67,8 +91,9 @@ export class StylesheetManager {
|
|||
}
|
||||
|
||||
const fontKey = fd.family;
|
||||
let source: string;
|
||||
|
||||
let i, ttf='', woff='', eot='', svg='', fList=[];
|
||||
let i, ttf='', woff='', fList=[];
|
||||
|
||||
// TODO: 22 Aug 2014: check that font path passed from cloud is actually used!
|
||||
|
||||
|
|
@ -98,8 +123,6 @@ export class StylesheetManager {
|
|||
if(fList[i].toLowerCase().indexOf('.otf') > 0) ttf=fList[i];
|
||||
if(fList[i].toLowerCase().indexOf('.ttf') > 0) ttf=fList[i];
|
||||
if(fList[i].toLowerCase().indexOf('.woff') > 0) woff=fList[i];
|
||||
if(fList[i].toLowerCase().indexOf('.eot') > 0) eot=fList[i];
|
||||
if(fList[i].toLowerCase().indexOf('.svg') > 0) svg=fList[i];
|
||||
}
|
||||
|
||||
// Font path qualified to support page-relative fonts (build 347)
|
||||
|
|
@ -111,14 +134,6 @@ export class StylesheetManager {
|
|||
woff = fontPathRoot+woff;
|
||||
}
|
||||
|
||||
if(eot != '' && (eot.indexOf('/') < 0)) {
|
||||
eot = fontPathRoot+eot;
|
||||
}
|
||||
|
||||
if(svg != '' && (svg.indexOf('/') < 0)) {
|
||||
svg = fontPathRoot+svg;
|
||||
}
|
||||
|
||||
// Build the font-face definition according to the browser being used
|
||||
var s='@font-face {\nfont-family:'
|
||||
+ fd.family + ';\nfont-style:normal;\nfont-weight:normal;\n';
|
||||
|
|
@ -132,54 +147,43 @@ export class StylesheetManager {
|
|||
if(this.doCacheBusting) {
|
||||
ttf = this.cacheBust(ttf);
|
||||
}
|
||||
s=s+'src:url(\''+ttf+'\') format(\'truetype\');';
|
||||
} else {
|
||||
return null;
|
||||
source = "url('"+encodeURI(ttf)+"') format('truetype')";
|
||||
}
|
||||
} else {
|
||||
var s0 = [];
|
||||
|
||||
if(os == DeviceSpec.OperatingSystem.Android) {
|
||||
// Android 4.2 and 4.3 have bugs in their rendering for some scripts
|
||||
// with embedded ttf or woff. svg mostly works so is a better initial
|
||||
// choice on the Android browser.
|
||||
if(svg != '') {
|
||||
s0.push("url('"+svg+"') format('svg')");
|
||||
}
|
||||
|
||||
if(woff != '') {
|
||||
s0.push("url('"+woff+"') format('woff')");
|
||||
}
|
||||
|
||||
if(ttf != '') {
|
||||
s0.push("url('"+ttf+"') format('truetype')");
|
||||
}
|
||||
} else {
|
||||
if(woff != '') {
|
||||
s0.push("url('"+woff+"') format('woff')");
|
||||
}
|
||||
|
||||
if(ttf != '') {
|
||||
s0.push("url('"+ttf+"') format('truetype')");
|
||||
}
|
||||
|
||||
if(svg != '') {
|
||||
s0.push("url('"+svg+"') format('svg')");
|
||||
}
|
||||
if(woff != '') {
|
||||
source = "url('"+encodeURI(woff)+"') format('woff')";
|
||||
}
|
||||
|
||||
if(s0.length == 0) {
|
||||
return null;
|
||||
if(ttf != '') {
|
||||
source = "url('"+encodeURI(ttf)+"') format('truetype')";
|
||||
}
|
||||
|
||||
s += 'src:'+s0.join(',')+';';
|
||||
}
|
||||
|
||||
if(!source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
s += 'src:'+source+';';
|
||||
|
||||
s=s+'\n}\n';
|
||||
|
||||
const sheet = createStyleSheet(s);
|
||||
fontStyleMap[fontKey] = sheet;
|
||||
|
||||
/* https://developer.mozilla.org/en-US/docs/Web/API/CSS_Font_Loading_API
|
||||
* Compat: Chrome 35... _just_ on the unupdated-Android 5.0 threshold.
|
||||
*
|
||||
* Note: this could probably wholesale-replace the stylesheet!
|
||||
* Would need: `document.fonts.add(fontFace)` - does not have to wait for the load() Promise.
|
||||
*
|
||||
* For now, we're using this solely to detect when the font has been succesfully loaded.
|
||||
*/
|
||||
const fontFace = new FontFace(fd.family, source);
|
||||
|
||||
const clearPromise = () => this.fontPromises = this.fontPromises.filter((entry) => entry != loadPromise);
|
||||
const loadPromise = fontFace.load().then(clearPromise).catch(clearPromise);
|
||||
this.fontPromises.push(loadPromise);
|
||||
|
||||
this.linkStylesheet(sheet);
|
||||
|
||||
return sheet;
|
||||
|
|
@ -197,15 +201,15 @@ export class StylesheetManager {
|
|||
*
|
||||
* @param {string} href path to stylesheet file
|
||||
*/
|
||||
linkExternalSheet(href: string): void {
|
||||
linkExternalSheet(href: string, force?: boolean): HTMLStyleElement {
|
||||
try {
|
||||
if(document.querySelector("link[href="+JSON.stringify(href)+"]") != null) {
|
||||
if(!force && document.querySelector("link[href="+JSON.stringify(href)+"]") != null) {
|
||||
// We've already linked this stylesheet, don't do it again
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
} catch(e) {
|
||||
// We've built an invalid href, somehow?
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
const linkElement=document.createElement('link');
|
||||
|
|
@ -214,6 +218,7 @@ export class StylesheetManager {
|
|||
linkElement.href=href;
|
||||
|
||||
this.linkStylesheet(linkElement);
|
||||
return linkElement;
|
||||
}
|
||||
|
||||
public unlink(stylesheet: HTMLStyleElement) {
|
||||
|
|
|
|||
|
|
@ -156,6 +156,11 @@ export default class KeymanEngine<
|
|||
this.osk.activeKeyboard = kbd;
|
||||
this.osk.present();
|
||||
}
|
||||
|
||||
// Needed to ensure the correct layer is displayed.
|
||||
// Needs to be after the OSK has loaded for the keyboard in case the default
|
||||
// layer should be something other than "default" for the current context.
|
||||
this.core.resetContext(this.contextManager.activeTarget);
|
||||
});
|
||||
|
||||
this.contextManager.on('keyboardasyncload', (metadata) => {
|
||||
|
|
|
|||
|
|
@ -176,22 +176,28 @@ export default abstract class OSKKey {
|
|||
* @returns font size as a style string
|
||||
*/
|
||||
getIdealFontSize(vkbd: VisualKeyboard, text: string, style: {height?: string, fontFamily?: string, fontSize: string}, override?: boolean): string {
|
||||
let buttonStyle = getComputedStyle(this.btn);
|
||||
let buttonStyle: typeof style & {width?: string} = getComputedStyle(this.btn);
|
||||
let keyWidth = parseFloat(buttonStyle.width);
|
||||
let emScale = 1;
|
||||
let emScale = vkbd.getKeyEmFontSize();
|
||||
|
||||
// Among other things, ensures we use SpecialOSK styling for special key text.
|
||||
// It's set on the key-span, not on the button.
|
||||
//
|
||||
// Also helps ensure that the stub's font-family name is used for keys, should
|
||||
// that mismatch the font-family name specified within the keyboard's touch layout.
|
||||
const capFont = !this.label ? undefined: getComputedStyle(this.label).fontFamily;
|
||||
if(capFont) {
|
||||
buttonStyle = {
|
||||
fontFamily: capFont,
|
||||
fontSize: buttonStyle.fontSize,
|
||||
height: buttonStyle.height
|
||||
}
|
||||
}
|
||||
|
||||
const originalSize = getFontSizeStyle(style.fontSize || '1em');
|
||||
|
||||
// Not yet available; it'll be handled in a later layout pass.
|
||||
if(!buttonStyle.fontSize) {
|
||||
// NOTE: preserves old behavior for use in documentation keyboards, for now.
|
||||
// Once we no longer need to maintain this code block, we can drop all current
|
||||
// method parameters safely.
|
||||
//
|
||||
// Recompute the new width for use in autoscaling calculations below, just in case.
|
||||
emScale = vkbd.getKeyEmFontSize();
|
||||
keyWidth = this.getKeyWidth(vkbd);
|
||||
} else if(!override) {
|
||||
if(!override) {
|
||||
// When available, just use computedStyle instead.
|
||||
style = buttonStyle;
|
||||
}
|
||||
|
|
@ -334,11 +340,13 @@ export default abstract class OSKKey {
|
|||
// space bar may not define the text span!
|
||||
if(this.label) {
|
||||
if(!this.label.classList.contains('kmw-spacebar-caption')) {
|
||||
this.label.style.fontSize = this.getIdealFontSize(vkbd, this.keyText, this.btn.style);
|
||||
// Do not use `this.keyText` - it holds *___* codes for special keys, not the actual glyph!
|
||||
const keyCapText = this.label.textContent;
|
||||
this.label.style.fontSize = this.getIdealFontSize(vkbd, keyCapText, this.btn.style);
|
||||
} else {
|
||||
// Remove any custom setting placed on it before recomputing its inherited style info.
|
||||
this.label.style.fontSize = '';
|
||||
const fontSize = this.getIdealFontSize(vkbd, this.label.textContent, getComputedStyle(this.label), true);
|
||||
const fontSize = this.getIdealFontSize(vkbd, this.label.textContent, this.btn.style);
|
||||
|
||||
// Since the kmw-spacebar-caption version uses !important, we must specify
|
||||
// it directly on the element too; otherwise, scaling gets ignored.
|
||||
|
|
|
|||
|
|
@ -116,6 +116,14 @@ export interface EventMap {
|
|||
pointerinteraction: (promise: Promise<void>) => void;
|
||||
}
|
||||
|
||||
export function getResourcePath(config: Configuration) {
|
||||
let resourcePathExt = 'osk/';
|
||||
if(config.isEmbedded) {
|
||||
resourcePathExt = '';
|
||||
}
|
||||
return `${config.pathConfig.resources}/${resourcePathExt}`
|
||||
}
|
||||
|
||||
export default abstract class OSKView
|
||||
extends EventEmitter<EventMap>
|
||||
implements MinimalCodesInterface, KeyEventSourceInterface<EventMap> {
|
||||
|
|
@ -198,7 +206,7 @@ export default abstract class OSKView
|
|||
private mouseEnterPromise?: ManagedPromise<void>;
|
||||
private touchEventPromiseManager = new TouchEventPromiseMap();
|
||||
|
||||
private static readonly STYLESHEET_FILES = ['kmwosk.css', 'globe-hint.css'];
|
||||
static readonly STYLESHEET_FILES = ['kmwosk.css', 'globe-hint.css'];
|
||||
|
||||
constructor(configuration: Configuration) {
|
||||
super();
|
||||
|
|
@ -710,12 +718,10 @@ export default abstract class OSKView
|
|||
// Install the default OSK stylesheets - but don't have it managed by the keyboard-specific stylesheet manager.
|
||||
// We wish to maintain kmwosk.css whenever keyboard-specific styles are reset/removed.
|
||||
// Temp-hack: embedded products prefer their stylesheet, etc linkages without the /osk path component.
|
||||
let subpath = 'osk/';
|
||||
if(this.config.isEmbedded) {
|
||||
subpath = '';
|
||||
}
|
||||
const resourcePath = getResourcePath(this.config);
|
||||
|
||||
for(let sheetFile of OSKView.STYLESHEET_FILES) {
|
||||
const sheetHref = `${this.config.pathConfig.resources}/${subpath}${sheetFile}`;
|
||||
const sheetHref = `${resourcePath}${sheetFile}`;
|
||||
this.uiStyleSheetManager.linkExternalSheet(sheetHref);
|
||||
}
|
||||
|
||||
|
|
@ -801,6 +807,8 @@ export default abstract class OSKView
|
|||
private _GenerateVisualKeyboard(keyboard: Keyboard, keyboardMetadata: KeyboardProperties): VisualKeyboard {
|
||||
let device = this.targetDevice;
|
||||
|
||||
const resourcePath = getResourcePath(this.config);
|
||||
|
||||
// Root element sets its own classes, one of which is 'kmw-osk-inner-frame'.
|
||||
let vkbd = new VisualKeyboard({
|
||||
keyboard: keyboard,
|
||||
|
|
@ -811,7 +819,12 @@ export default abstract class OSKView
|
|||
styleSheetManager: this.kbdStyleSheetManager,
|
||||
pathConfig: this.config.pathConfig,
|
||||
embeddedGestureConfig: this.config.embeddedGestureConfig,
|
||||
isEmbedded: this.config.isEmbedded
|
||||
isEmbedded: this.config.isEmbedded,
|
||||
specialFont: {
|
||||
family: 'SpecialOSK',
|
||||
files: [`${resourcePath}/keymanweb-osk.ttf`],
|
||||
path: '' // Not actually used.
|
||||
}
|
||||
});
|
||||
|
||||
vkbd.on('keyevent', (keyEvent, callback) => this.emit('keyevent', keyEvent, callback));
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import KeyTip from './keytip.interface.js';
|
|||
import OSKKey from './keyboard-layout/oskKey.js';
|
||||
import OSKLayer from './keyboard-layout/oskLayer.js';
|
||||
import OSKLayerGroup from './keyboard-layout/oskLayerGroup.js';
|
||||
import OSKView from './views/oskView.js';
|
||||
import { LengthStyle, ParsedLengthStyle } from './lengthStyle.js';
|
||||
import { defaultFontSize, getFontSizeStyle } from './fontSizeUtils.js';
|
||||
import PhoneKeyTip from './input/gestures/browser/keytip.js';
|
||||
|
|
@ -59,6 +60,7 @@ import Modipress from './input/gestures/browser/modipress.js';
|
|||
import Flick, { buildFlickScroller } from './input/gestures/browser/flick.js';
|
||||
import { GesturePreviewHost } from './keyboard-layout/gesturePreviewHost.js';
|
||||
import OSKBaseKey from './keyboard-layout/oskBaseKey.js';
|
||||
import { OSKResourcePathConfiguration } from './index.js';
|
||||
|
||||
interface KeyRuleEffects {
|
||||
contextToken?: number,
|
||||
|
|
@ -98,6 +100,11 @@ export interface VisualKeyboardConfiguration extends CommonConfiguration {
|
|||
* want to remove that one when swapping keyboards.
|
||||
*/
|
||||
styleSheetManager: StylesheetManager;
|
||||
|
||||
/**
|
||||
* A promise for loading of the font used by special keys.
|
||||
*/
|
||||
specialFont?: InternalKeyboardFont;
|
||||
}
|
||||
|
||||
interface BoundingRect {
|
||||
|
|
@ -1350,6 +1357,10 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
this.styleSheetManager.addStyleSheetForFont(kfd, this.fontRootPath, this.device.OS);
|
||||
this.styleSheetManager.addStyleSheetForFont(ofd, this.fontRootPath, this.device.OS);
|
||||
|
||||
if(this.config.specialFont) {
|
||||
this.styleSheetManager.addStyleSheetForFont(this.config.specialFont, '', this.device.OS);
|
||||
}
|
||||
|
||||
// Build the style string to USE the fonts and append (or replace) the font style sheet
|
||||
// Note: Some browsers do not download the font-face font until it is applied,
|
||||
// so must apply style before testing for font availability
|
||||
|
|
@ -1358,12 +1369,13 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
if (activeKeyboard != null && typeof (activeKeyboard.oskStyling) == 'string') // KMEW-129
|
||||
customStyle = customStyle + activeKeyboard.oskStyling;
|
||||
|
||||
this.styleSheet = createStyleSheet(customStyle); //Build 360
|
||||
this.styleSheet.addEventListener('load', () => {
|
||||
// Once any related fonts are loaded, we can re-adjust key-cap scaling.
|
||||
this.refreshLayout();
|
||||
})
|
||||
this.styleSheetManager.linkStylesheet(this.styleSheet);
|
||||
if(customStyle) {
|
||||
this.styleSheet = createStyleSheet(customStyle); //Build 360
|
||||
this.styleSheetManager.linkStylesheet(this.styleSheet);
|
||||
}
|
||||
|
||||
// Once any related fonts are loaded, we can re-adjust key-cap scaling.
|
||||
this.styleSheetManager.allLoadedPromise().then(() => this.refreshLayout());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1400,14 +1412,23 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
* Create copy of the OSK that can be used for embedding in documentation or help
|
||||
* The currently active keyboard will be returned if PInternalName is null
|
||||
*
|
||||
* @param {Object} PKbd the keyboard object to be displayed
|
||||
* @param {string=} argFormFactor layout form factor, defaulting to 'desktop'
|
||||
* @param {(string|number)=} argLayerId name or index of layer to show, defaulting to 'default'
|
||||
* @param {number} height Target height for the rendered keyboard
|
||||
* @param {Keyboard} PKbd the keyboard object to be displayed
|
||||
* @param {KeyboardProperties} kbdProperties the metadata stub for the keyboard
|
||||
* @param {Object} pathConfig an OSK path-configuration instance
|
||||
* @param {string=} argFormFactor layout form factor, defaulting to 'desktop'
|
||||
* @param {(string|number)=} argLayerId name or index of layer to show, defaulting to 'default'
|
||||
* @param {number} height Target height for the rendered keyboard
|
||||
* (currently required for legacy reasons)
|
||||
* @return {Object} DIV object with filled keyboard layer content
|
||||
*/
|
||||
static buildDocumentationKeyboard(PKbd: Keyboard, kbdProperties: KeyboardProperties, fontRootPath: string, argFormFactor: DeviceSpec.FormFactor, argLayerId, height: number): HTMLElement { // I777
|
||||
static buildDocumentationKeyboard(
|
||||
PKbd: Keyboard,
|
||||
kbdProperties: KeyboardProperties,
|
||||
pathConfig: OSKResourcePathConfiguration,
|
||||
argFormFactor: DeviceSpec.FormFactor,
|
||||
argLayerId: string,
|
||||
height: number
|
||||
): HTMLElement { // I777
|
||||
if (!PKbd) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1439,12 +1460,9 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
hostDevice: deviceSpec,
|
||||
isStatic: true,
|
||||
topContainer: null,
|
||||
pathConfig: {
|
||||
fonts: fontRootPath,
|
||||
resources: '' // ignored
|
||||
},
|
||||
pathConfig: pathConfig,
|
||||
styleSheetManager: null
|
||||
}); //
|
||||
});
|
||||
|
||||
kbdObj.layerGroup.element.className = kbdObj.kbdDiv.className; // may contain multiple classes
|
||||
kbdObj.layerGroup.element.classList.add(device.formFactor + '-static');
|
||||
|
|
@ -1469,11 +1487,10 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
// the Web OSK-Core design.
|
||||
kbdObj.setSize(800, height); // Probably need something for width, too, rather than
|
||||
kbdObj.fontSize = defaultFontSize(deviceSpec, height, false);
|
||||
classWrapper.style.fontSize = kbdObj.element.style.fontSize;
|
||||
|
||||
// assuming 100%.
|
||||
kbdObj.refreshLayout(); // Necessary for the row heights to be properly set!
|
||||
// Relocates the font size definition from the main VisualKeyboard wrapper, since we don't return the whole thing.
|
||||
kbd.style.fontSize = kbdObj.kbdDiv.style.fontSize;
|
||||
kbd.style.height = kbdObj.kbdDiv.style.height;
|
||||
kbd.style.maxHeight = kbdObj.kbdDiv.style.maxHeight;
|
||||
} else {
|
||||
|
|
@ -1485,39 +1502,58 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
kbdObj.updateState(); // double-ensure that the 'default' layer is properly displayed.
|
||||
|
||||
// Once the element is inserted into the DOM, refresh the layout so that proper text scaling may apply.
|
||||
const detectAndHandleInsertion = () => {
|
||||
const detectAndHandleInsertion = async () => {
|
||||
if(document.contains(kbd)) {
|
||||
// Yay, insertion!
|
||||
|
||||
try {
|
||||
// Are there font-size attributes we may safely adjust? If so, do that!
|
||||
if(getComputedStyle(kbd) && kbd.style.fontSize) {
|
||||
kbdObj.fontSize = new ParsedLengthStyle(kbd.style.fontSize);
|
||||
// Wait for full loading/connection before manipulating stylesheet locations.
|
||||
await kbdObj.styleSheetManager.allLoadedPromise();
|
||||
|
||||
const mainSheet = kbdObj.styleSheet;
|
||||
if(mainSheet) {
|
||||
kbd.appendChild(mainSheet);
|
||||
}
|
||||
|
||||
// Make sure that the stylesheet is attached, now that the keyboard-doc's been inserted.
|
||||
// The stylesheet is currently built + constructed in the same code that attaches it to
|
||||
// the page.
|
||||
kbdObj.appendStyleSheet();
|
||||
// Unlinking sheets will mutate the original array; make a backup
|
||||
// copy of the array to iterate over.
|
||||
const sheets = [].concat(kbdObj.styleSheetManager.sheets);
|
||||
|
||||
// Grab a reference to the stylesheet.
|
||||
const stylesheet = kbdObj.styleSheet;
|
||||
const stylesheetParentElement = stylesheet.parentElement;
|
||||
/*
|
||||
* Re-attach the font stylesheets... to the <head> element.
|
||||
* They need re-attachment for the fonts to work properly for inactive keyboards.
|
||||
*
|
||||
* For future reference: as of early 2024, Chrome does not support
|
||||
* @font-face style declaration within Shadow DOM elements. The
|
||||
* declaration needs to be part of the main HTML doc.
|
||||
*
|
||||
* References:
|
||||
* - https://stackoverflow.com/q/63710162
|
||||
* - https://github.com/mdn/interactive-examples/issues/887#issuecomment-432418008
|
||||
*/
|
||||
for(let sheet of sheets) {
|
||||
if(sheet == mainSheet) {
|
||||
// Don't need to relocate the custom stylesheet.
|
||||
continue;
|
||||
} else if(sheet.href) {
|
||||
// Don't relocate kmwosk.css or similar.
|
||||
continue;
|
||||
}
|
||||
kbdObj.styleSheetManager.unlink(sheet);
|
||||
document.head.appendChild(sheet);
|
||||
}
|
||||
|
||||
// Don't reset top-level stuff; just the visible layer.
|
||||
// kbdObj.currentLayer.refreshLayout(kbdObj, kbdObj.height);
|
||||
// // Should we ever remove ALL related stylesheets during .shutdown()...
|
||||
// kbdObj.config.styleSheetManager = new StylesheetManager(kbdObj.element);
|
||||
|
||||
// We refresh the full layout so that font-size is properly detected & stored
|
||||
// on the documentation keyboard.
|
||||
kbdObj.refreshLayout();
|
||||
kbd.style.fontSize = kbdObj.kbdDiv.style.fontSize;
|
||||
|
||||
// We no longer need a reference to the constructing VisualKeyboard, so we should let
|
||||
// it clean up its <head> stylesheet links. This detaches the stylesheet, though.
|
||||
kbdObj.styleSheet = null; // is directly checked in shutdown; prevent removal.
|
||||
kbdObj.shutdown();
|
||||
|
||||
// Now that shutdown is done, re-attach the stylesheet - but to the layer group.
|
||||
kbd.appendChild(stylesheet);
|
||||
} finally {
|
||||
insertionObserver.disconnect();
|
||||
}
|
||||
|
|
@ -1530,7 +1566,22 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
|
|||
subtree: true
|
||||
});
|
||||
|
||||
// Ensure the main keyboard root is the first child element of the top-level div.
|
||||
classWrapper.append(kbd);
|
||||
|
||||
// Ensure that the OSK's style-sheet is included by the top-level div standing in for the OSKView.
|
||||
for(let sheetFile of OSKView.STYLESHEET_FILES) {
|
||||
const sheetHref = `${pathConfig.resources}/osk/${sheetFile}`;
|
||||
const sheet = kbdObj.styleSheetManager.linkExternalSheet(sheetHref, true);
|
||||
sheet.parentNode.removeChild(sheet);
|
||||
classWrapper.appendChild(sheet);
|
||||
}
|
||||
|
||||
// Make sure that the stylesheet is attached, now that the keyboard-doc's been inserted.
|
||||
// The stylesheet is currently built + constructed in the same code that attaches it to
|
||||
// the page.
|
||||
kbdObj.appendStyleSheet();
|
||||
|
||||
return classWrapper;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,6 @@
|
|||
kmwosk.css: main CSS for keymanweb on-screen keyboard and other objects.
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: SpecialOSK;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
src: url('keymanweb-osk.ttf') format('truetype');
|
||||
}
|
||||
|
||||
/* kmw-key-square applies only to OSK key elements, kmw-key-square-ex applies only to popup key elements */
|
||||
|
||||
/* Common key-layout properties (all form factor + OS combinations) */
|
||||
|
|
@ -340,7 +333,16 @@
|
|||
/* Vertical centering of text labels on keys */
|
||||
.kmw-key {text-align:center; white-space:nowrap;}
|
||||
.kmw-key::before {content:'.'; display:inline-block; height:100%; vertical-align:middle; max-width:0px; visibility:hidden;}
|
||||
.kmw-key span {display:inline-block}
|
||||
.kmw-key span {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
.desktop .kmw-osk-frame{position:absolute;width:auto;height:auto;left:0;top:0;display:none;margin:0;padding:0;
|
||||
|
|
@ -494,8 +496,8 @@
|
|||
.phone .kmw-key-shift, .phone .kmw-key-shift-on{font-size:0.7em !important;}
|
||||
|
||||
/* Set special font and style for modifier and special key images */
|
||||
body div.kmw-key-shift span.kmw-key-text {font-family:SpecialOSK !important;font-size:1em !important;}
|
||||
body div.kmw-key-shift-on span.kmw-key-text {font-family:SpecialOSK !important;font-size:1em !important;}
|
||||
body div.kmw-key-shift span.kmw-key-text {font-family:SpecialOSK !important;font-size:1em;}
|
||||
body div.kmw-key-shift-on span.kmw-key-text {font-family:SpecialOSK !important;font-size:1em;}
|
||||
#kmw-popup-keys div.kmw-key-shift span.kmw-key-text {font-family:SpecialOSK !important;}
|
||||
|
||||
#kmw-keytip {
|
||||
|
|
@ -779,7 +781,7 @@ div.android div.kmw-keytip-cap {
|
|||
.kmw-alert-text{margin:10px;white-space:default;font-family:Arial,sans-serif;}
|
||||
|
||||
/* Note: font size gets overwritten in source code! */
|
||||
.kmw-spacebar-caption{font:1em Arial !important;color:rgba(0,0,0,0.25);}
|
||||
.kmw-spacebar-caption{font-family: Arial !important;color:rgba(0,0,0,0.25);}
|
||||
|
||||
/* Probably best to make this its own CSS that can be optionally included? */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
|
|
|||
|
|
@ -52,17 +52,19 @@
|
|||
};
|
||||
|
||||
let shadowfy = function(container) {
|
||||
const docBase = container.children[0];
|
||||
container.removeChild(docBase);
|
||||
|
||||
const shadow = container.attachShadow({ mode: "open" });
|
||||
shadow.appendChild(docBase);
|
||||
const docBase = container.children[0];
|
||||
|
||||
const baseOSKStyle = document.createElement('link');
|
||||
baseOSKStyle.href = "../../../../../build/app/resources/osk/kmwosk.css";
|
||||
baseOSKStyle.rel="stylesheet";
|
||||
baseOSKStyle.type="text/css";
|
||||
shadow.appendChild(baseOSKStyle);
|
||||
while(docBase.children.length > 1) {
|
||||
const sheet = docBase.children[1];
|
||||
docBase.removeChild(docBase.children[1]);
|
||||
shadow.appendChild(sheet);
|
||||
}
|
||||
|
||||
// Do this one last - that way, all relevant sheets are in place when the
|
||||
// layout-refresh is triggered.
|
||||
container.removeChild(docBase);
|
||||
shadow.appendChild(docBase);
|
||||
}
|
||||
|
||||
let documentKeyboard = async function(kbdid, langid, formFactor, layer) {
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@
|
|||
<h2><a href="./default-subkey/index.html">Tests handling of new default-subkey feature (#9430)</a></h2>
|
||||
<h2><a href="./issue9469/index.html">Test special characters rendering with keymanweb-osk.ttf (#9469)</a></h2>
|
||||
<h2><a href="./text_selection_tests_9073/index.html">Test text selection (#9073)</a></h2>
|
||||
<h2><a href="./pr10506/index.html">Test key-cap scaling / font load interactions (#10506)</a></h2>
|
||||
<h1>Other</h1>
|
||||
<h2><a href="./regression-tests/index.html">Keystroke processing regression test engine.</a></h2>
|
||||
<hr>
|
||||
|
|
|
|||
243755
web/src/test/manual/web/pr10506/DejaVu-Dots.sfd
Normal file
243755
web/src/test/manual/web/pr10506/DejaVu-Dots.sfd
Normal file
File diff suppressed because one or more lines are too long
BIN
web/src/test/manual/web/pr10506/DejaVu-Dots.ttf
Normal file
BIN
web/src/test/manual/web/pr10506/DejaVu-Dots.ttf
Normal file
Binary file not shown.
1
web/src/test/manual/web/pr10506/dotty_keys.js
Normal file
1
web/src/test/manual/web/pr10506/dotty_keys.js
Normal file
File diff suppressed because one or more lines are too long
1
web/src/test/manual/web/pr10506/dotty_keys/.gitignore
vendored
Normal file
1
web/src/test/manual/web/pr10506/dotty_keys/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
build/
|
||||
6
web/src/test/manual/web/pr10506/dotty_keys/HISTORY.md
Normal file
6
web/src/test/manual/web/pr10506/dotty_keys/HISTORY.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
dotty_keys Change History
|
||||
====================
|
||||
|
||||
1.0 (2024-01-26)
|
||||
----------------
|
||||
* Created by SIL International
|
||||
21
web/src/test/manual/web/pr10506/dotty_keys/LICENSE.md
Normal file
21
web/src/test/manual/web/pr10506/dotty_keys/LICENSE.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2024 SIL International
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
31
web/src/test/manual/web/pr10506/dotty_keys/README.md
Normal file
31
web/src/test/manual/web/pr10506/dotty_keys/README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
dotty_keys keyboard
|
||||
==============
|
||||
|
||||
Description
|
||||
-----------
|
||||
A keyboard designed to test key-cap scaling in relation to font loading.
|
||||
|
||||
All base a-z keys have a "dot" glyph instead.
|
||||
|
||||
Links
|
||||
-----
|
||||
Keyboard Homepage: https://keyman.com/keyboards/dotty_keys
|
||||
|
||||
Copyright
|
||||
---------
|
||||
See [LICENSE.md](LICENSE.md)
|
||||
|
||||
Supported Platforms
|
||||
-------------------
|
||||
* Windows
|
||||
* macOS
|
||||
* Linux
|
||||
* Web
|
||||
* iPhone
|
||||
* iPad
|
||||
* Android phone
|
||||
* Android tablet
|
||||
* Mobile devices
|
||||
* Desktop devices
|
||||
* Tablet devices
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<KeymanDeveloperProject>
|
||||
<Options>
|
||||
<Version>2.0</Version>
|
||||
<CompilerWarningsAsErrors>True</CompilerWarningsAsErrors>
|
||||
<CheckFilenameConventions>True</CheckFilenameConventions>
|
||||
</Options>
|
||||
</KeymanDeveloperProject>
|
||||
BIN
web/src/test/manual/web/pr10506/dotty_keys/source/DejaVuDots.ttf
Normal file
BIN
web/src/test/manual/web/pr10506/dotty_keys/source/DejaVuDots.ttf
Normal file
Binary file not shown.
BIN
web/src/test/manual/web/pr10506/dotty_keys/source/dotty_keys.ico
Normal file
BIN
web/src/test/manual/web/pr10506/dotty_keys/source/dotty_keys.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,656 @@
|
|||
{
|
||||
"phone": {
|
||||
"font": "Dotty Keys",
|
||||
"fontsize": "4em",
|
||||
"layer": [
|
||||
{
|
||||
"id": "default",
|
||||
"row": [
|
||||
{
|
||||
"id": 1,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_Q",
|
||||
"text": "q"
|
||||
},
|
||||
{
|
||||
"id": "K_W",
|
||||
"text": "w"
|
||||
},
|
||||
{
|
||||
"id": "K_E",
|
||||
"text": "e"
|
||||
},
|
||||
{
|
||||
"id": "K_R",
|
||||
"text": "r"
|
||||
},
|
||||
{
|
||||
"id": "K_T",
|
||||
"text": "t"
|
||||
},
|
||||
{
|
||||
"id": "K_Y",
|
||||
"text": "y"
|
||||
},
|
||||
{
|
||||
"id": "K_U",
|
||||
"text": "u"
|
||||
},
|
||||
{
|
||||
"id": "K_I",
|
||||
"text": "i"
|
||||
},
|
||||
{
|
||||
"id": "K_O",
|
||||
"text": "o"
|
||||
},
|
||||
{
|
||||
"id": "K_P",
|
||||
"text": "p"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_A",
|
||||
"text": "a",
|
||||
"pad": "50"
|
||||
},
|
||||
{
|
||||
"id": "K_S",
|
||||
"text": "s"
|
||||
},
|
||||
{
|
||||
"id": "K_D",
|
||||
"text": "d"
|
||||
},
|
||||
{
|
||||
"id": "K_F",
|
||||
"text": "f"
|
||||
},
|
||||
{
|
||||
"id": "K_G",
|
||||
"text": "g"
|
||||
},
|
||||
{
|
||||
"id": "K_H",
|
||||
"text": "h"
|
||||
},
|
||||
{
|
||||
"id": "K_J",
|
||||
"text": "j"
|
||||
},
|
||||
{
|
||||
"id": "K_K",
|
||||
"text": "k"
|
||||
},
|
||||
{
|
||||
"id": "K_L",
|
||||
"text": "l"
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"width": "10",
|
||||
"sp": "10"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_SHIFT",
|
||||
"text": "*Shift*",
|
||||
"sp": "1",
|
||||
"nextlayer": "shift"
|
||||
},
|
||||
{
|
||||
"id": "K_Z",
|
||||
"text": "z"
|
||||
},
|
||||
{
|
||||
"id": "K_X",
|
||||
"text": "x"
|
||||
},
|
||||
{
|
||||
"id": "K_C",
|
||||
"text": "c"
|
||||
},
|
||||
{
|
||||
"id": "K_V",
|
||||
"text": "v"
|
||||
},
|
||||
{
|
||||
"id": "K_B",
|
||||
"text": "b"
|
||||
},
|
||||
{
|
||||
"id": "K_N",
|
||||
"text": "n"
|
||||
},
|
||||
{
|
||||
"id": "K_M",
|
||||
"text": "m"
|
||||
},
|
||||
{
|
||||
"id": "K_PERIOD",
|
||||
"text": ".",
|
||||
"sk": [
|
||||
{
|
||||
"text": ",",
|
||||
"id": "K_COMMA"
|
||||
},
|
||||
{
|
||||
"text": "!",
|
||||
"id": "K_1",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"text": "?",
|
||||
"id": "K_SLASH",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"text": "'",
|
||||
"id": "K_QUOTE"
|
||||
},
|
||||
{
|
||||
"text": "\"",
|
||||
"id": "K_QUOTE",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"text": "\\",
|
||||
"id": "K_BKSLASH"
|
||||
},
|
||||
{
|
||||
"text": ":",
|
||||
"id": "K_COLON",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"text": ";",
|
||||
"id": "K_COLON"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "K_BKSP",
|
||||
"text": "*BkSp*",
|
||||
"width": "100",
|
||||
"sp": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_NUMLOCK",
|
||||
"text": "*123*",
|
||||
"width": "150",
|
||||
"sp": "1",
|
||||
"nextlayer": "numeric"
|
||||
},
|
||||
{
|
||||
"id": "K_LOPT",
|
||||
"text": "*Menu*",
|
||||
"width": "120",
|
||||
"sp": "1"
|
||||
},
|
||||
{
|
||||
"id": "K_SPACE",
|
||||
"text": "",
|
||||
"width": "610",
|
||||
"sp": "0"
|
||||
},
|
||||
{
|
||||
"id": "K_ENTER",
|
||||
"text": "*Enter*",
|
||||
"width": "150",
|
||||
"sp": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "shift",
|
||||
"row": [
|
||||
{
|
||||
"id": 1,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_Q",
|
||||
"text": "Q"
|
||||
},
|
||||
{
|
||||
"id": "K_W",
|
||||
"text": "W"
|
||||
},
|
||||
{
|
||||
"id": "K_E",
|
||||
"text": "E"
|
||||
},
|
||||
{
|
||||
"id": "K_R",
|
||||
"text": "R"
|
||||
},
|
||||
{
|
||||
"id": "K_T",
|
||||
"text": "T"
|
||||
},
|
||||
{
|
||||
"id": "K_Y",
|
||||
"text": "Y"
|
||||
},
|
||||
{
|
||||
"id": "K_U",
|
||||
"text": "U"
|
||||
},
|
||||
{
|
||||
"id": "K_I",
|
||||
"text": "I"
|
||||
},
|
||||
{
|
||||
"id": "K_O",
|
||||
"text": "O"
|
||||
},
|
||||
{
|
||||
"id": "K_P",
|
||||
"text": "P"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_A",
|
||||
"text": "A",
|
||||
"pad": "50"
|
||||
},
|
||||
{
|
||||
"id": "K_S",
|
||||
"text": "S"
|
||||
},
|
||||
{
|
||||
"id": "K_D",
|
||||
"text": "D"
|
||||
},
|
||||
{
|
||||
"id": "K_F",
|
||||
"text": "F"
|
||||
},
|
||||
{
|
||||
"id": "K_G",
|
||||
"text": "G"
|
||||
},
|
||||
{
|
||||
"id": "K_H",
|
||||
"text": "H"
|
||||
},
|
||||
{
|
||||
"id": "K_J",
|
||||
"text": "J"
|
||||
},
|
||||
{
|
||||
"id": "K_K",
|
||||
"text": "K"
|
||||
},
|
||||
{
|
||||
"id": "K_L",
|
||||
"text": "L"
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"width": "10",
|
||||
"sp": "10"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_SHIFT",
|
||||
"text": "*Shift*",
|
||||
"sp": "2",
|
||||
"nextlayer": "default"
|
||||
},
|
||||
{
|
||||
"id": "K_Z",
|
||||
"text": "Z"
|
||||
},
|
||||
{
|
||||
"id": "K_X",
|
||||
"text": "X"
|
||||
},
|
||||
{
|
||||
"id": "K_C",
|
||||
"text": "C"
|
||||
},
|
||||
{
|
||||
"id": "K_V",
|
||||
"text": "V"
|
||||
},
|
||||
{
|
||||
"id": "K_B",
|
||||
"text": "B"
|
||||
},
|
||||
{
|
||||
"id": "K_N",
|
||||
"text": "N"
|
||||
},
|
||||
{
|
||||
"id": "K_M",
|
||||
"text": "M"
|
||||
},
|
||||
{
|
||||
"id": "K_PERIOD",
|
||||
"text": ".",
|
||||
"layer": "default",
|
||||
"sk": [
|
||||
{
|
||||
"text": ",",
|
||||
"id": "K_COMMA",
|
||||
"layer": "default"
|
||||
},
|
||||
{
|
||||
"text": "!",
|
||||
"id": "K_1",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"text": "?",
|
||||
"id": "K_SLASH",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"text": "'",
|
||||
"id": "K_QUOTE",
|
||||
"layer": "default"
|
||||
},
|
||||
{
|
||||
"text": "\"",
|
||||
"id": "K_QUOTE",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"text": "\\",
|
||||
"id": "K_BKSLASH",
|
||||
"layer": "default"
|
||||
},
|
||||
{
|
||||
"text": ":",
|
||||
"id": "K_COLON",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"text": ";",
|
||||
"id": "K_COLON",
|
||||
"layer": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "K_BKSP",
|
||||
"text": "*BkSp*",
|
||||
"sp": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_NUMLOCK",
|
||||
"text": "*123*",
|
||||
"width": "150",
|
||||
"sp": "1",
|
||||
"nextlayer": "numeric"
|
||||
},
|
||||
{
|
||||
"id": "K_LOPT",
|
||||
"text": "*Menu*",
|
||||
"width": "120",
|
||||
"sp": "1"
|
||||
},
|
||||
{
|
||||
"id": "K_SPACE",
|
||||
"text": "",
|
||||
"width": "610",
|
||||
"sp": "0"
|
||||
},
|
||||
{
|
||||
"id": "K_ENTER",
|
||||
"text": "*Enter*",
|
||||
"width": "150",
|
||||
"sp": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "numeric",
|
||||
"row": [
|
||||
{
|
||||
"id": 1,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_1",
|
||||
"text": "1"
|
||||
},
|
||||
{
|
||||
"id": "K_2",
|
||||
"text": "2"
|
||||
},
|
||||
{
|
||||
"id": "K_3",
|
||||
"text": "3"
|
||||
},
|
||||
{
|
||||
"id": "K_4",
|
||||
"text": "4"
|
||||
},
|
||||
{
|
||||
"id": "K_5",
|
||||
"text": "5"
|
||||
},
|
||||
{
|
||||
"id": "K_6",
|
||||
"text": "6"
|
||||
},
|
||||
{
|
||||
"id": "K_7",
|
||||
"text": "7"
|
||||
},
|
||||
{
|
||||
"id": "K_8",
|
||||
"text": "8"
|
||||
},
|
||||
{
|
||||
"id": "K_9",
|
||||
"text": "9"
|
||||
},
|
||||
{
|
||||
"id": "K_0",
|
||||
"text": "0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_4",
|
||||
"layer": "shift",
|
||||
"text": "$",
|
||||
"pad": "50"
|
||||
},
|
||||
{
|
||||
"id": "K_2",
|
||||
"layer": "shift",
|
||||
"text": "@"
|
||||
},
|
||||
{
|
||||
"id": "K_3",
|
||||
"layer": "shift",
|
||||
"text": "#"
|
||||
},
|
||||
{
|
||||
"id": "K_5",
|
||||
"layer": "shift",
|
||||
"text": "%"
|
||||
},
|
||||
{
|
||||
"id": "K_7",
|
||||
"layer": "shift",
|
||||
"text": "&"
|
||||
},
|
||||
{
|
||||
"id": "K_HYPHEN",
|
||||
"layer": "shift",
|
||||
"text": "_"
|
||||
},
|
||||
{
|
||||
"id": "K_EQUAL",
|
||||
"text": "=",
|
||||
"layer": "default"
|
||||
},
|
||||
{
|
||||
"id": "K_BKSLASH",
|
||||
"layer": "shift",
|
||||
"text": "|"
|
||||
},
|
||||
{
|
||||
"id": "K_BKSLASH",
|
||||
"text": "\\",
|
||||
"layer": "default"
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"width": "10",
|
||||
"sp": "10"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_LBRKT",
|
||||
"text": "[",
|
||||
"pad": "110",
|
||||
"sk": [
|
||||
{
|
||||
"id": "U_00AB",
|
||||
"text": "\u00AB"
|
||||
},
|
||||
{
|
||||
"id": "K_COMMA",
|
||||
"text": "<",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"id": "K_LBRKT",
|
||||
"text": "{",
|
||||
"layer": "shift"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "K_9",
|
||||
"layer": "shift",
|
||||
"text": "("
|
||||
},
|
||||
{
|
||||
"id": "K_0",
|
||||
"layer": "shift",
|
||||
"text": ")"
|
||||
},
|
||||
{
|
||||
"id": "K_RBRKT",
|
||||
"text": "]",
|
||||
"sk": [
|
||||
{
|
||||
"id": "U_00BB",
|
||||
"text": "\u00BB"
|
||||
},
|
||||
{
|
||||
"id": "K_PERIOD",
|
||||
"text": ">",
|
||||
"layer": "shift"
|
||||
},
|
||||
{
|
||||
"id": "K_RBRKT",
|
||||
"text": "}",
|
||||
"layer": "shift"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "K_EQUAL",
|
||||
"layer": "shift",
|
||||
"text": "+"
|
||||
},
|
||||
{
|
||||
"id": "K_HYPHEN",
|
||||
"text": "-"
|
||||
},
|
||||
{
|
||||
"id": "K_8",
|
||||
"layer": "shift",
|
||||
"text": "*"
|
||||
},
|
||||
{
|
||||
"id": "K_SLASH",
|
||||
"text": "/"
|
||||
},
|
||||
{
|
||||
"id": "K_BKSP",
|
||||
"text": "*BkSp*",
|
||||
"width": "100",
|
||||
"sp": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"key": [
|
||||
{
|
||||
"id": "K_LOWER",
|
||||
"text": "*abc*",
|
||||
"width": "150",
|
||||
"sp": "1",
|
||||
"nextlayer": "default"
|
||||
},
|
||||
{
|
||||
"id": "K_LOPT",
|
||||
"text": "*Menu*",
|
||||
"width": "120",
|
||||
"sp": "1"
|
||||
},
|
||||
{
|
||||
"id": "K_SPACE",
|
||||
"text": "",
|
||||
"width": "610",
|
||||
"sp": "0"
|
||||
},
|
||||
{
|
||||
"id": "K_ENTER",
|
||||
"text": "*Enter*",
|
||||
"width": "150",
|
||||
"sp": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
c dotty_keys generated from template at 2024-01-26 12:44:31
|
||||
c with name "dotty_keys"
|
||||
store(&VERSION) '10.0'
|
||||
store(&NAME) 'dotty_keys'
|
||||
store(©RIGHT) 'Copyright © SIL International'
|
||||
store(&KEYBOARDVERSION) '1.0'
|
||||
store(&TARGETS) 'any'
|
||||
store(&BITMAP) 'dotty_keys.ico'
|
||||
store(&VISUALKEYBOARD) 'dotty_keys.kvks'
|
||||
store(&LAYOUTFILE) 'dotty_keys.keyman-touch-layout'
|
||||
|
||||
begin Unicode > use(main)
|
||||
|
||||
group(main) using keys
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package>
|
||||
<System>
|
||||
<KeymanDeveloperVersion>17.0.238.0</KeymanDeveloperVersion>
|
||||
<FileVersion>7.0</FileVersion>
|
||||
</System>
|
||||
<Options>
|
||||
<ExecuteProgram></ExecuteProgram>
|
||||
<ReadMeFile>readme.htm</ReadMeFile>
|
||||
<LicenseFile>..\LICENSE.md</LicenseFile>
|
||||
<MSIFileName></MSIFileName>
|
||||
<MSIOptions></MSIOptions>
|
||||
<FollowKeyboardVersion/>
|
||||
</Options>
|
||||
<StartMenu>
|
||||
<Folder></Folder>
|
||||
<Items/>
|
||||
</StartMenu>
|
||||
<Info>
|
||||
<Name URL="">dotty_keys</Name>
|
||||
<Copyright URL="">Copyright © SIL International</Copyright>
|
||||
<Author URL="">SIL International</Author>
|
||||
<Description URL="">A keyboard designed to test key-cap scaling in relation to font loading.
|
||||
|
||||
All base a-z keys have a "dot" glyph instead.</Description>
|
||||
<Version URL=""></Version>
|
||||
</Info>
|
||||
<Files>
|
||||
<File>
|
||||
<Name>..\build\dotty_keys.kmx</Name>
|
||||
<Description></Description>
|
||||
<CopyLocation>0</CopyLocation>
|
||||
<FileType>.kmx</FileType>
|
||||
</File>
|
||||
<File>
|
||||
<Name>..\build\dotty_keys.js</Name>
|
||||
<Description></Description>
|
||||
<CopyLocation>0</CopyLocation>
|
||||
<FileType>.js</FileType>
|
||||
</File>
|
||||
<File>
|
||||
<Name>..\build\dotty_keys.kvk</Name>
|
||||
<Description></Description>
|
||||
<CopyLocation>0</CopyLocation>
|
||||
<FileType>.kvk</FileType>
|
||||
</File>
|
||||
<File>
|
||||
<Name>welcome.htm</Name>
|
||||
<Description></Description>
|
||||
<CopyLocation>0</CopyLocation>
|
||||
<FileType>.htm</FileType>
|
||||
</File>
|
||||
<File>
|
||||
<Name>readme.htm</Name>
|
||||
<Description></Description>
|
||||
<CopyLocation>0</CopyLocation>
|
||||
<FileType>.htm</FileType>
|
||||
</File>
|
||||
<File>
|
||||
<Name>..\LICENSE.md</Name>
|
||||
<Description></Description>
|
||||
<CopyLocation>0</CopyLocation>
|
||||
<FileType>.md</FileType>
|
||||
</File>
|
||||
<File>
|
||||
<Name>DejaVuDots.ttf</Name>
|
||||
<Description>Font DejaVu Sans</Description>
|
||||
<CopyLocation>0</CopyLocation>
|
||||
<FileType>.ttf</FileType>
|
||||
</File>
|
||||
</Files>
|
||||
<Keyboards>
|
||||
<Keyboard>
|
||||
<Name>dotty_keys</Name>
|
||||
<ID>dotty_keys</ID>
|
||||
<Version>1.0</Version>
|
||||
<Languages>
|
||||
<Language ID="und">Undetermined</Language>
|
||||
</Languages>
|
||||
<Examples/>
|
||||
</Keyboard>
|
||||
</Keyboards>
|
||||
<RelatedPackages/>
|
||||
<Strings/>
|
||||
</Package>
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<visualkeyboard>
|
||||
<header>
|
||||
<version>10.0</version>
|
||||
<kbdname>dotty_keys</kbdname>
|
||||
<flags/>
|
||||
</header>
|
||||
<encoding name="unicode" fontname="Arial" fontsize="48">
|
||||
<layer shift="">
|
||||
<key vkey="K_Q">q</key>
|
||||
<key vkey="K_W">w</key>
|
||||
<key vkey="K_E">e</key>
|
||||
<key vkey="K_R">r</key>
|
||||
<key vkey="K_T">t</key>
|
||||
<key vkey="K_Y">y</key>
|
||||
<key vkey="K_U">u</key>
|
||||
<key vkey="K_I">i</key>
|
||||
<key vkey="K_O">o</key>
|
||||
<key vkey="K_P">p</key>
|
||||
<key vkey="K_A">a</key>
|
||||
<key vkey="K_S">s</key>
|
||||
<key vkey="K_D">d</key>
|
||||
<key vkey="K_F">f</key>
|
||||
<key vkey="K_G">g</key>
|
||||
<key vkey="K_H">h</key>
|
||||
<key vkey="K_K">k</key>
|
||||
<key vkey="K_J">j</key>
|
||||
<key vkey="K_L">l</key>
|
||||
<key vkey="K_COLON">;</key>
|
||||
<key vkey="K_QUOTE">'</key>
|
||||
<key vkey="K_LBRKT">[</key>
|
||||
<key vkey="K_RBRKT">]</key>
|
||||
<key vkey="K_1">1</key>
|
||||
<key vkey="K_2">2</key>
|
||||
<key vkey="K_3">3</key>
|
||||
<key vkey="K_4">4</key>
|
||||
<key vkey="K_5">5</key>
|
||||
<key vkey="K_6">6</key>
|
||||
<key vkey="K_7">7</key>
|
||||
<key vkey="K_8">8</key>
|
||||
<key vkey="K_9">9</key>
|
||||
<key vkey="K_0">0</key>
|
||||
<key vkey="K_HYPHEN">-</key>
|
||||
<key vkey="K_EQUAL">=</key>
|
||||
<key vkey="K_Z">z</key>
|
||||
<key vkey="K_X">x</key>
|
||||
<key vkey="K_C">c</key>
|
||||
<key vkey="K_V">v</key>
|
||||
<key vkey="K_B">b</key>
|
||||
<key vkey="K_N">n</key>
|
||||
<key vkey="K_M">m</key>
|
||||
<key vkey="K_COMMA">,</key>
|
||||
<key vkey="K_PERIOD">.</key>
|
||||
<key vkey="K_SLASH">/</key>
|
||||
</layer>
|
||||
</encoding>
|
||||
</visualkeyboard>
|
||||
28
web/src/test/manual/web/pr10506/dotty_keys/source/readme.htm
Normal file
28
web/src/test/manual/web/pr10506/dotty_keys/source/readme.htm
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>dotty_keys</title>
|
||||
<style type="text/css">
|
||||
p { font: 10pt Tahoma; }
|
||||
h1 { font: bold 16pt Tahoma; color: #4444cc; margin-bottom: 2px }
|
||||
h2 { font: bold 12pt Tahoma; color: #4444cc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>dotty_keys</h1>
|
||||
|
||||
<p>
|
||||
A keyboard designed to test key-cap scaling in relation to font loading.
|
||||
|
||||
All base a-z keys have a "dot" glyph instead of the actual output character,
|
||||
and the touch-layout fontsize is set to be extremely large in order to force
|
||||
application of key-cap scaling.
|
||||
</p>
|
||||
|
||||
<p>Copyright © SIL International</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Start Using dotty_keys</title>
|
||||
<style type="text/css">
|
||||
p { font: 10pt Tahoma; }
|
||||
h1 { font: bold 16pt Tahoma; color: #4444cc; margin-bottom: 2px }
|
||||
h2 { font: bold 12pt Tahoma; color: #4444cc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Start Using dotty_keys</h1>
|
||||
|
||||
<p>
|
||||
A keyboard designed to test key-cap scaling in relation to font loading.
|
||||
|
||||
All base a-z keys have a "dot" glyph instead of the actual output character,
|
||||
and the touch-layout fontsize is set to be extremely large in order to force
|
||||
application of key-cap scaling.
|
||||
</p>
|
||||
|
||||
<h1>Keyboard Layout</h1>
|
||||
|
||||
<!-- Insert Keyboard Layout Images or HTML here -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
15
web/src/test/manual/web/pr10506/header.js
Normal file
15
web/src/test/manual/web/pr10506/header.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
function loadKeyboards()
|
||||
{
|
||||
var kmw=keyman;
|
||||
kmw.addKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
|
||||
filename:('../us-1.0.js')});
|
||||
|
||||
// Add a fully-specified, locally-sourced, keyboard with custom font
|
||||
kmw.addKeyboards({id:'dotty_keys',name:'Dotty Keys',
|
||||
languages:{
|
||||
id:'und',name:'Special',region:'World',
|
||||
font:{family:'DejaVu Dots',source:['./DejaVu-Dots.ttf']}
|
||||
},
|
||||
filename:'./dotty_keys.js'
|
||||
});
|
||||
}
|
||||
90
web/src/test/manual/web/pr10506/index.html
Normal file
90
web/src/test/manual/web/pr10506/index.html
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
|
||||
<!-- Set the viewport width to match phone and tablet device widths -->
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no" />
|
||||
|
||||
<!-- Allow KeymanWeb to be saved to the iPhone home screen -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
|
||||
<!-- Enable IE9 Standards mode -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
|
||||
<title>KeymanWeb PR 10506 - Key-cap scaling / font-load delay</title>
|
||||
|
||||
<!-- Your page CSS -->
|
||||
<style type='text/css'>
|
||||
body {font-family: Tahoma,helvetica;}
|
||||
h3 {font-size: 1em;font-weight:normal;color: darkred; margin-bottom: 4px}
|
||||
.test {font-size: 1.5em; width:80%; min-height:30px; border: 1px solid gray;}
|
||||
#KeymanWebControl {width:50%;min-width:600px;}
|
||||
</style>
|
||||
|
||||
<!-- Insert uncompiled KeymanWeb source scripts -->
|
||||
<script src="../../../../../build/publish/debug/keymanweb.js" type="application/javascript"></script>
|
||||
|
||||
<!--
|
||||
For desktop browsers, a script for the user interface must be inserted here.
|
||||
|
||||
Standard UIs are toggle, button, float and toolbar.
|
||||
The toolbar UI is best for any page designed to support keyboards for
|
||||
a large number of languages.
|
||||
-->
|
||||
<script src="../../../../../build/publish/debug/kmwuitoggle.js"></script>
|
||||
|
||||
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
|
||||
<script>
|
||||
var kmw=window.keyman;
|
||||
kmw.init({
|
||||
attachType: 'auto'
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Add keyboard management script for local selection of keyboards to use -->
|
||||
<script src="./header.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<!-- Sample page HTML -->
|
||||
<body onload='loadKeyboards();'>
|
||||
<h2>KeymanWeb Test Page - Key-cap scaling / font-load delay interactions</h2>
|
||||
<p>To run the test, simply change from the default (English) keyboard to the "Special" keyboard.</p>
|
||||
<p>When things are working properly, the base keys on the "Special" keyboard should contain
|
||||
evenly-sized dots. On touch devices, they should be fairly large.</p>
|
||||
<p>If the dots are small and unevenly-sized instead, key-cap scaling is occurring before the keyboard's
|
||||
font has loaded. (Pay special attention to the QWERTY-layout 'w' and 'm' keys.)</p>
|
||||
<hr/>
|
||||
<div>
|
||||
<!--
|
||||
The following DIV is used to position the Button or Toolbar User Interfaces on the page.
|
||||
If omitted, those User Interfaces will appear at the top of the document body.
|
||||
(It is ignored by other User Interfaces.)
|
||||
-->
|
||||
<div id='KeymanWebControl'>
|
||||
<h3>Type in your language in this text area:</h3>
|
||||
<textarea id='ta1' class='test' placeholder='Type here'></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<h3><a href="../index.html">Return to testing home page</a></h3>
|
||||
</body>
|
||||
|
||||
<!--
|
||||
*** DEVELOPER NOTE -- FIREFOX CONFIGURATION FOR TESTING ***
|
||||
*
|
||||
* If the URL bar starts with <b>file://</b>, Firefox may not load the font used
|
||||
* to display the special characters used in the On-Screen Keyboard.
|
||||
*
|
||||
* To work around this Firefox bug, navigate to <b>about:config</b>
|
||||
* and set <b>security.fileuri.strict_origin_policy</b> to <b>false</b>
|
||||
* while testing.
|
||||
*
|
||||
* Firefox resolves website-based CSS URI references correctly without needing
|
||||
* any configuration change, so this change should only be made for file-based testing.
|
||||
*
|
||||
***
|
||||
-->
|
||||
</html>
|
||||
3
web/src/test/manual/web/pr10506/readme.md
Normal file
3
web/src/test/manual/web/pr10506/readme.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
DejaVuSans.sfd can be edited with [FontForge](https://fontforge.org/).
|
||||
|
||||
Is based on DejaVuSans.ttf, sloppily edited for the purposes of this test.
|
||||
|
|
@ -212,6 +212,8 @@ typedef struct tagKEYMAN64THREADDATA
|
|||
BOOL TIPFUpdateable, TIPFPreserved; // I4290
|
||||
|
||||
BOOL CoreProcessEventRun; // True if core process event has been run
|
||||
// TODO: #10583 remove core_actions cache
|
||||
km_core_actions const *core_actions;
|
||||
|
||||
BOOL FInRefreshKeyboards;
|
||||
BOOL RefreshRequired;
|
||||
|
|
|
|||
|
|
@ -23,37 +23,39 @@ static void processAlert(AITIP* app) {
|
|||
app->QueueAction(QIT_BELL, 0);
|
||||
}
|
||||
|
||||
|
||||
static BOOL
|
||||
static void
|
||||
processBack(AITIP* app, const unsigned int code_points_to_delete, const km_core_usv* delete_context) {
|
||||
if (app->IsLegacy()) {
|
||||
for (unsigned int i = 0; i < code_points_to_delete; i++) {
|
||||
app->QueueAction(QIT_BACK, BK_DEFAULT);
|
||||
}
|
||||
return TRUE;
|
||||
return;
|
||||
}
|
||||
if (!app->IsLegacy()) {
|
||||
while (*delete_context) {
|
||||
if (Uni_IsSMP(*delete_context)) {
|
||||
km_core_usv const* delete_context_ptr = delete_context;
|
||||
while (*delete_context_ptr) {
|
||||
delete_context_ptr++;
|
||||
}
|
||||
delete_context_ptr--;
|
||||
for (; delete_context_ptr >= delete_context; delete_context_ptr--) {
|
||||
if (Uni_IsSMP(*delete_context_ptr)) {
|
||||
app->QueueAction(QIT_BACK, BK_DEFAULT | BK_SURROGATE);
|
||||
}
|
||||
else {
|
||||
app->QueueAction(QIT_BACK, BK_DEFAULT);
|
||||
}
|
||||
delete_context++;
|
||||
}
|
||||
return TRUE;
|
||||
return;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
processPersistOpt(km_core_actions const* actions, LPINTKEYBOARDINFO activeKeyboard
|
||||
) {
|
||||
for (auto option = actions->persist_options; option->key; option++) {
|
||||
// Put the keyboard option into Windows Registry
|
||||
// log"Saving keyboard option to registry");
|
||||
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessHook: Saving option to registry for keyboard [%s].", activeKeyboard->Name);
|
||||
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessPersistOpt: Saving option to registry for keyboard [%s].", activeKeyboard->Name);
|
||||
size_t value_length = wcslen(reinterpret_cast<LPCWSTR>(option->value));
|
||||
LPWSTR value = new WCHAR[value_length + 1];
|
||||
wcscpy_s(value, value_length + 1, reinterpret_cast<LPCWSTR>(option->value));
|
||||
|
|
@ -101,24 +103,32 @@ BOOL ProcessActions(BOOL* emitKeyStroke)
|
|||
{
|
||||
PKEYMAN64THREADDATA _td = ThreadGlobals();
|
||||
if (!_td) return FALSE;
|
||||
// TODO: #10583 Remove caching action_struct
|
||||
if (!_td->core_actions) {
|
||||
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActions: core_actions not set");
|
||||
_td->core_actions = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState);
|
||||
}
|
||||
|
||||
_td->CoreProcessEventRun = FALSE;
|
||||
// Process the action items from the core. This actions will modify the windows context (AppContext).
|
||||
// Therefore it is not required to copy the context from the core to the windows context.
|
||||
km_core_actions const* actions = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState);
|
||||
|
||||
processBack(_td->app, actions->code_points_to_delete, actions->deleted_context);
|
||||
processUnicodeChar(_td->app, actions->output);
|
||||
if (actions->persist_options != NULL) {
|
||||
processPersistOpt(actions, _td->lpActiveKeyboard);
|
||||
|
||||
|
||||
processBack(_td->app, _td->core_actions->code_points_to_delete, _td->core_actions->deleted_context);
|
||||
processUnicodeChar(_td->app, _td->core_actions->output);
|
||||
if (_td->core_actions->persist_options != NULL) {
|
||||
processPersistOpt(_td->core_actions, _td->lpActiveKeyboard);
|
||||
}
|
||||
if (actions->do_alert) {
|
||||
if (_td->core_actions->do_alert) {
|
||||
processAlert(_td->app);
|
||||
}
|
||||
if (actions->emit_keystroke) {
|
||||
if (_td->core_actions->emit_keystroke) {
|
||||
*emitKeyStroke = TRUE;
|
||||
}
|
||||
processCapsLock(actions->new_caps_lock_state, !_td->state.isDown, _td->TIPFUpdateable, FALSE);
|
||||
processCapsLock(_td->core_actions->new_caps_lock_state, !_td->state.isDown, _td->TIPFUpdateable, FALSE);
|
||||
// TODO: #10583 remove dispose
|
||||
km_core_actions_dispose(_td->core_actions);
|
||||
_td->core_actions = nullptr;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -133,10 +143,16 @@ ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke) {
|
|||
return FALSE;
|
||||
}
|
||||
_td->CoreProcessEventRun = TRUE;
|
||||
// TODO: #10583 remove dispose
|
||||
if (_td->core_actions) {
|
||||
km_core_actions_dispose(_td->core_actions);
|
||||
_td->core_actions = nullptr;
|
||||
}
|
||||
|
||||
km_core_actions const* acts = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState);
|
||||
processCapsLock(acts->new_caps_lock_state, !_td->state.isDown, _td->TIPFUpdateable, FALSE);
|
||||
if (acts->emit_keystroke) {
|
||||
_td->core_actions = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState);
|
||||
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsNonUpdatableParse: km_core_state_get_actions");
|
||||
processCapsLock(_td->core_actions->new_caps_lock_state, !_td->state.isDown, _td->TIPFUpdateable, FALSE);
|
||||
if (_td->core_actions->emit_keystroke) {
|
||||
*emitKeyStroke = TRUE;
|
||||
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsNonUpdatableParse EMIT_KEYSTROKE");
|
||||
_td->CoreProcessEventRun = FALSE; // If we emit the key stroke on this parse we don't need the second parse
|
||||
|
|
@ -150,7 +166,11 @@ ProcessActionsExternalEvent() {
|
|||
if (!_td) {
|
||||
return FALSE;
|
||||
}
|
||||
km_core_actions const* acts = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState);
|
||||
processCapsLock(acts->new_caps_lock_state, !_td->state.isDown, FALSE, TRUE);
|
||||
if (!_td->core_actions) { // when ideponent we will not need this
|
||||
return FALSE;
|
||||
}
|
||||
// TODO: #10583 remove and call get actions here directly
|
||||
//km_core_actions const* acts = km_core_state_get_actions(_td->lpActiveKeyboard->lpCoreKeyboardState);
|
||||
processCapsLock(_td->core_actions->new_caps_lock_state, !_td->state.isDown, FALSE, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue