From 8dfe50c9e5457bb72d38d8ef307a7b231418d2e3 Mon Sep 17 00:00:00 2001 From: Noureddin Date: Wed, 1 Nov 2023 10:40:45 +0300 Subject: [PATCH] support keyboard layout emulation, now url only --- README.md | 7 + b.js | 154 +++++++++++++ index.html | 208 ++++++++++++++++-- javascript.js | 9 +- ligilumi.js | 45 ++-- ...31\202\330\261\330\243\331\206\331\212.md" | 10 + 6 files changed, 391 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 5152115..12a5044 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,13 @@ These are not changeable from the UI, only from the URL parameters; they are exp - `cn`: at the end of a recitation, it appends a "phrase" from the next ayah if it's in the same sura. +- `emu=`, `emulate=`, or `emulation=`: to use a specific keyboard layout regardless of your currently activated layout, even if yours is an English layout. Currently supported layouts: + + - `ibm`: the common Arabic layout on IBM PCs. + - `mac`: the common Arabic layout on Apple devices. + - `arak`: the [Arak](https://github.com/noureddin/arak) improved layout. + - `dv`: an experimental phonetic layout based on Dvorak. + - `dt`, or `disableteacher`: to remove teacher mode selector from the UI. The Teacher mode can still be set from the URL params. Useful to force a specific value for the option (e.g. no-teacher) in an embedding web app for example. **Warning:** It's still changeable from the JavaScript console; I couldn't disable this yet. diff --git a/b.js b/b.js index ca46ba1..22a9a27 100644 --- a/b.js +++ b/b.js @@ -13,3 +13,157 @@ function insert_in_field (el, ch) { el.selectionStart = el.selectionEnd = st + ch.length } +const mappings = { + arak: { + Backquote: ['`', '~'], + Minus: ['[', '{'], + Equal: [']', '}'], + KeyQ: ['ض', '"'], + KeyW: ['ع', 'غ'], + KeyE: ['ب', 'پ'], + KeyR: ['ح', 'َ'], + KeyT: ['س', 'ً'], + KeyY: ['خ', 'ٌ'], + KeyU: ['د', 'ُ'], + KeyI: ['أ', 'آ'], + KeyO: ['ك', 'گ'], + KeyP: ['ج', 'چ'], + BracketLeft: ['/', '؟'], + BracketRight: ['=', '+'], + KeyA: ['ه', '؛'], + KeyS: ['ي', '»'], + KeyD: ['م', '«'], + KeyF: ['ن', 'ْ'], + KeyG: ['ف', 'ڤ'], + KeyH: ['ت', 'ث'], + KeyJ: ['ل', 'ّ'], + KeyK: ['ا', 'ء'], + KeyL: ['و', 'ؤ'], + Semicolon: ['ر', '>'], + Quote: ['إ', '<'], + KeyZ: ['.', ':'], + KeyX: ['،', 'ـ'], + KeyC: ['ش', '_'], + KeyV: ['ق', '-'], + KeyB: ['ص', "'"], + KeyN: ['ذ', 'ٍ'], + KeyM: ['ة', 'ِ'], + Comma: ['ى', 'ئ'], + Period: ['ز', 'ژ'], + Slash: ['ط', 'ظ'], + }, + dv: { + Backquote: ['`', '~'], + Minus: ['[', '{'], + Equal: [']', '}'], + KeyQ: ["'", '"'], + KeyW: ['،', '<'], + KeyE: ['.', '>'], + KeyR: ['ط', 'ظ'], + KeyT: ['ى', 'آ'], + KeyY: ['ف', 'ڤ'], + KeyU: ['غ', 'ـ'], + KeyI: ['ص', 'ض'], + KeyO: ['ر', '»'], + KeyP: ['ل', '«'], + BracketLeft: ['/', '؟'], + BracketRight: ['=', '+'], + KeyA: ['ا', 'أ'], + KeyS: ['ع', 'إ'], + KeyD: ['ه', 'ة'], + KeyF: ['و', 'ؤ'], + KeyG: ['ي', 'ئ'], + KeyH: ['د', 'َ'], + KeyJ: ['ح', 'ً'], + KeyK: ['ت', 'ٌ'], + KeyL: ['ن', 'ُ'], + Semicolon: ['س', 'ش'], + Quote: ['-', '_'], + KeyZ: ['؛', ':'], + KeyX: ['ق', 'ء'], + KeyC: ['ج', 'چ'], + KeyV: ['ك', 'گ'], + KeyB: ['خ', 'ْ'], + KeyN: ['ب', 'پ'], + KeyM: ['م', 'ّ'], + Comma: ['ث', 'ٍ'], + Period: ['ذ', 'ِ'], + Slash: ['ز', 'ژ'], + }, + ibm: { + Backquote: ['ذ', 'ّ'], + Minus: ['-', '_'], + Equal: ['=', '+'], + KeyQ: ['ض', 'َ'], + KeyW: ['ص', 'ً'], + KeyE: ['ث', 'ُ'], + KeyR: ['ق', 'ٌ'], + KeyT: ['ف', 'لإ'], + KeyY: ['غ', 'إ'], + KeyU: ['ع', '`'], + KeyI: ['ه', '÷'], + KeyO: ['خ', '×'], + KeyP: ['ح', '؛'], + BracketLeft: ['ج', '<'], + BracketRight: ['د', '>'], + KeyA: ['ش', 'ِ'], + KeyS: ['س', 'ٍ'], + KeyD: ['ي', ']'], + KeyF: ['ب', '['], + KeyG: ['ل', 'لأ'], + KeyH: ['ا', 'أ'], + KeyJ: ['ت', 'ـ'], + KeyK: ['ن', '،'], + KeyL: ['م', '/'], + Semicolon: ['ك', ':'], + Quote: ['ط', '"'], + KeyZ: ['ئ', '~'], + KeyX: ['ء', 'ْ'], + KeyC: ['ؤ', '}'], + KeyV: ['ر', '{'], + KeyB: ['لا', 'لآ'], + KeyN: ['ى', 'آ'], + KeyM: ['ة', "'"], + Comma: ['و', ','], + Period: ['ز', '.'], + Slash: ['ظ', '؟'], + }, + mac: { + Backquote: ['§', '±'], + Minus: ['-', '_'], + Equal: ['=', '+'], + KeyQ: ['ض', 'َ'], + KeyW: ['ص', 'ً'], + KeyE: ['ث', 'ِ'], + KeyR: ['ق', 'ٍ'], + KeyT: ['ف', 'ُ'], + KeyY: ['غ', 'ٌ'], + KeyU: ['ع', 'ْ'], + KeyI: ['ه', 'ّ'], + KeyO: ['خ', ']'], + KeyP: ['ح', '['], + BracketLeft: ['ج', '}'], + BracketRight: ['ة', '{'], + KeyA: ['ش', '»'], + KeyS: ['س', '«'], + KeyD: ['ي', 'ى'], + KeyF: ['ب', ''], + KeyG: ['ل', ''], + KeyH: ['ا', 'آ'], + KeyJ: ['ت', ''], + KeyK: ['ن', ''], + KeyL: ['م', ''], + Semicolon: ['ك', ':'], + Quote: ['؛', '"'], + KeyZ: ['ظ', ''], + KeyX: ['ط', ''], + KeyC: ['ذ', 'ئ'], + KeyV: ['د', 'ء'], + KeyB: ['ز', 'أ'], + KeyN: ['ر', 'إ'], + KeyM: ['و', 'ؤ'], + Comma: ['،', '>'], + Period: ['.', '<'], + Slash: ['/', '؟'], + }, +} diff --git a/index.html b/index.html index 4c98615..c80eb56 100644 --- a/index.html +++ b/index.html @@ -1459,6 +1459,160 @@

ملاحظات متفرقة

el.selectionStart = el.selectionEnd = st + ch.length } +const mappings = { + arak: { + Backquote: ['`', '~'], + Minus: ['[', '{'], + Equal: [']', '}'], + KeyQ: ['ض', '"'], + KeyW: ['ع', 'غ'], + KeyE: ['ب', 'پ'], + KeyR: ['ح', 'َ'], + KeyT: ['س', 'ً'], + KeyY: ['خ', 'ٌ'], + KeyU: ['د', 'ُ'], + KeyI: ['أ', 'آ'], + KeyO: ['ك', 'گ'], + KeyP: ['ج', 'چ'], + BracketLeft: ['/', '؟'], + BracketRight: ['=', '+'], + KeyA: ['ه', '؛'], + KeyS: ['ي', '»'], + KeyD: ['م', '«'], + KeyF: ['ن', 'ْ'], + KeyG: ['ف', 'ڤ'], + KeyH: ['ت', 'ث'], + KeyJ: ['ل', 'ّ'], + KeyK: ['ا', 'ء'], + KeyL: ['و', 'ؤ'], + Semicolon: ['ر', '>'], + Quote: ['إ', '<'], + KeyZ: ['.', ':'], + KeyX: ['،', 'ـ'], + KeyC: ['ش', '_'], + KeyV: ['ق', '-'], + KeyB: ['ص', "'"], + KeyN: ['ذ', 'ٍ'], + KeyM: ['ة', 'ِ'], + Comma: ['ى', 'ئ'], + Period: ['ز', 'ژ'], + Slash: ['ط', 'ظ'], + }, + dv: { + Backquote: ['`', '~'], + Minus: ['[', '{'], + Equal: [']', '}'], + KeyQ: ["'", '"'], + KeyW: ['،', '<'], + KeyE: ['.', '>'], + KeyR: ['ط', 'ظ'], + KeyT: ['ى', 'آ'], + KeyY: ['ف', 'ڤ'], + KeyU: ['غ', 'ـ'], + KeyI: ['ص', 'ض'], + KeyO: ['ر', '»'], + KeyP: ['ل', '«'], + BracketLeft: ['/', '؟'], + BracketRight: ['=', '+'], + KeyA: ['ا', 'أ'], + KeyS: ['ع', 'إ'], + KeyD: ['ه', 'ة'], + KeyF: ['و', 'ؤ'], + KeyG: ['ي', 'ئ'], + KeyH: ['د', 'َ'], + KeyJ: ['ح', 'ً'], + KeyK: ['ت', 'ٌ'], + KeyL: ['ن', 'ُ'], + Semicolon: ['س', 'ش'], + Quote: ['-', '_'], + KeyZ: ['؛', ':'], + KeyX: ['ق', 'ء'], + KeyC: ['ج', 'چ'], + KeyV: ['ك', 'گ'], + KeyB: ['خ', 'ْ'], + KeyN: ['ب', 'پ'], + KeyM: ['م', 'ّ'], + Comma: ['ث', 'ٍ'], + Period: ['ذ', 'ِ'], + Slash: ['ز', 'ژ'], + }, + ibm: { + Backquote: ['ذ', 'ّ'], + Minus: ['-', '_'], + Equal: ['=', '+'], + KeyQ: ['ض', 'َ'], + KeyW: ['ص', 'ً'], + KeyE: ['ث', 'ُ'], + KeyR: ['ق', 'ٌ'], + KeyT: ['ف', 'لإ'], + KeyY: ['غ', 'إ'], + KeyU: ['ع', '`'], + KeyI: ['ه', '÷'], + KeyO: ['خ', '×'], + KeyP: ['ح', '؛'], + BracketLeft: ['ج', '<'], + BracketRight: ['د', '>'], + KeyA: ['ش', 'ِ'], + KeyS: ['س', 'ٍ'], + KeyD: ['ي', ']'], + KeyF: ['ب', '['], + KeyG: ['ل', 'لأ'], + KeyH: ['ا', 'أ'], + KeyJ: ['ت', 'ـ'], + KeyK: ['ن', '،'], + KeyL: ['م', '/'], + Semicolon: ['ك', ':'], + Quote: ['ط', '"'], + KeyZ: ['ئ', '~'], + KeyX: ['ء', 'ْ'], + KeyC: ['ؤ', '}'], + KeyV: ['ر', '{'], + KeyB: ['لا', 'لآ'], + KeyN: ['ى', 'آ'], + KeyM: ['ة', "'"], + Comma: ['و', ','], + Period: ['ز', '.'], + Slash: ['ظ', '؟'], + }, + mac: { + Backquote: ['§', '±'], + Minus: ['-', '_'], + Equal: ['=', '+'], + KeyQ: ['ض', 'َ'], + KeyW: ['ص', 'ً'], + KeyE: ['ث', 'ِ'], + KeyR: ['ق', 'ٍ'], + KeyT: ['ف', 'ُ'], + KeyY: ['غ', 'ٌ'], + KeyU: ['ع', 'ْ'], + KeyI: ['ه', 'ّ'], + KeyO: ['خ', ']'], + KeyP: ['ح', '['], + BracketLeft: ['ج', '}'], + BracketRight: ['ة', '{'], + KeyA: ['ش', '»'], + KeyS: ['س', '«'], + KeyD: ['ي', 'ى'], + KeyF: ['ب', ''], + KeyG: ['ل', ''], + KeyH: ['ا', 'آ'], + KeyJ: ['ت', ''], + KeyK: ['ن', ''], + KeyL: ['م', ''], + Semicolon: ['ك', ':'], + Quote: ['؛', '"'], + KeyZ: ['ظ', ''], + KeyX: ['ط', ''], + KeyC: ['ذ', 'ئ'], + KeyV: ['د', 'ء'], + KeyB: ['ز', 'أ'], + KeyN: ['ر', 'إ'], + KeyM: ['و', 'ؤ'], + Comma: ['،', '>'], + Period: ['.', '<'], + Slash: ['/', '؟'], + }, +} // ligilumi: reading url parameters @@ -1699,6 +1853,7 @@

ملاحظات متفرقة

let disableteacher // remove teacher mode selector from the UI, teacher mode can still be set from the URL: dt/disableteacher let disablequizmode // remove quiz mode selector from the UI, quiz mode can still be set from the URL: dq/disablequizmode let highcontrast // high-contrast, dark colorscheme + let emulate // keyboard layout emulation; see https://noureddin.github.io/kbt (same ids, w/o '-ar') let cn // continuation; ie, append a "phrase" from the next aaya if in the same sura let zz // enable embedded integration: zz (cannot be disabled if enabled) params @@ -1708,25 +1863,26 @@

ملاحظات متفرقة

//.reduce((obj, cur, i) => { i == 0? {} : (obj[cur[0]] = cur[1], obj), {}) .forEach((e, i) => { const is_of = (...params) => params.includes(e[0]) - if (is_of('dark', 'd')) { dark = true } - else if (is_of('light', 'l')) { dark = false } - else if (is_of('color', 'c')) { color = parse_color(e[1]) } - else if (is_of('mvbtns', 'mv', 'm')) { mv = parse_mv(e[1]) } - else if (is_of('quizmode', 'qz', 'q')) { quizmode = parse_quizmode(e[1]) } - else if (is_of('txt')) { quizmode = parse_quizmode('imlaai') } - else if (is_of('byword')) { byword = true } - else if (is_of('byletter')) { byword = false } - else if (is_of( 'linebreaks')) { nolinebreaks = false } - else if (is_of('nolinebreaks')) { nolinebreaks = true } - else if (is_of('t', 'teach', 'teacher')) { teacher = true } - else if (is_of('n', 'noteach', 'noteacher')) { teacher = false } - else if (is_of('dt', 'disableteacher')) { disableteacher = true } - else if (is_of('dq', 'disablequizmode')) { disablequizmode = true } - else if (is_of('hc', 'highcontrast')) { highcontrast = true } - else if (is_of('qari')) { qari = e[1] } - else if (is_of('qariurl')) { qariurl = e[1] } - else if (is_of('cn')) { cn = true } - else if (is_of('zz')) { zz = true } + if (is_of('dark', 'd')) { dark = true } + else if (is_of('light', 'l')) { dark = false } + else if (is_of('color', 'c')) { color = parse_color(e[1]) } + else if (is_of('mvbtns', 'mv', 'm')) { mv = parse_mv(e[1]) } + else if (is_of('quizmode', 'qz', 'q')) { quizmode = parse_quizmode(e[1]) } + else if (is_of('txt')) { quizmode = parse_quizmode('imlaai') } + else if (is_of('byword')) { byword = true } + else if (is_of('byletter')) { byword = false } + else if (is_of( 'linebreaks')) { nolinebreaks = false } + else if (is_of('nolinebreaks')) { nolinebreaks = true } + else if (is_of('t', 'teach', 'teacher')) { teacher = true } + else if (is_of('n', 'noteach', 'noteacher')) { teacher = false } + else if (is_of('dt', 'disableteacher')) { disableteacher = true } + else if (is_of('dq', 'disablequizmode')) { disablequizmode = true } + else if (is_of('hc', 'highcontrast')) { highcontrast = true } + else if (is_of('emu', 'emulate', 'emulation')) { emulate = e[1] } + else if (is_of('qari')) { qari = e[1] } + else if (is_of('qariurl')) { qariurl = e[1] } + else if (is_of('cn')) { cn = true } + else if (is_of('zz')) { zz = true } else if (is_of('a')) { a = +e[1] } else if (is_of('b')) { b = +e[1] } else if (is_of('p')) { [st, en] = pages_to_ayat(...range_to_pair(e[1])) } @@ -1749,6 +1905,7 @@

ملاحظات متفرقة

disableteacher, disablequizmode, highcontrast, + emulate, qari, qariurl, cn, @@ -1819,6 +1976,10 @@

ملاحظات متفرقة

} delete opts.disablequizmode // + if (opts.emulate && mappings[opts.emulate]) { + window.emulate = opts.emulate // it's an option, but a hidden one. + } + // // if no ayat are selected, only change the provided preferences if (opts.st == null || opts.en == null) { return } recite(opts) @@ -2055,8 +2216,13 @@

ملاحظات متفرقة

el_imla_txt.onkeydown = (ev) => { if (!ev.altKey && !ev.ctrlKey && ev.key.length === 1) { ev.preventDefault() - if (ev.key.match(/^[ \nء-غف-\u0652]$/)) { - insert_in_field(el_imla_txt, ev.key) + const k = window.emulate + && mappings[window.emulate] + && mappings[window.emulate][ev.code] + ? mappings[window.emulate][ev.code][+ev.shiftKey] + : ev.key + if (k.match(/^[ \nء-غف-\u0652]$|^ل[اأإآ]$/)) { // the lam-alefs for emulated IBM kb + insert_in_field(el_imla_txt, k) txt_changed() } } diff --git a/javascript.js b/javascript.js index de6a675..9909280 100644 --- a/javascript.js +++ b/javascript.js @@ -223,8 +223,13 @@ function _recite (o) { el_imla_txt.onkeydown = (ev) => { if (!ev.altKey && !ev.ctrlKey && ev.key.length === 1) { ev.preventDefault() - if (ev.key.match(/^[ \nء-غف-\u0652]$/)) { - insert_in_field(el_imla_txt, ev.key) + const k = window.emulate + && mappings[window.emulate] + && mappings[window.emulate][ev.code] + ? mappings[window.emulate][ev.code][+ev.shiftKey] + : ev.key + if (k.match(/^[ \nء-غف-\u0652]$|^ل[اأإآ]$/)) { // the lam-alefs for emulated IBM kb + insert_in_field(el_imla_txt, k) txt_changed() } } diff --git a/ligilumi.js b/ligilumi.js index 1c66bdd..79dc847 100644 --- a/ligilumi.js +++ b/ligilumi.js @@ -237,6 +237,7 @@ function _ligilumilo (params) { let disableteacher // remove teacher mode selector from the UI, teacher mode can still be set from the URL: dt/disableteacher let disablequizmode // remove quiz mode selector from the UI, quiz mode can still be set from the URL: dq/disablequizmode let highcontrast // high-contrast, dark colorscheme + let emulate // keyboard layout emulation; see https://noureddin.github.io/kbt (same ids, w/o '-ar') let cn // continuation; ie, append a "phrase" from the next aaya if in the same sura let zz // enable embedded integration: zz (cannot be disabled if enabled) params @@ -246,25 +247,26 @@ function _ligilumilo (params) { //.reduce((obj, cur, i) => { i == 0? {} : (obj[cur[0]] = cur[1], obj), {}) .forEach((e, i) => { const is_of = (...params) => params.includes(e[0]) - if (is_of('dark', 'd')) { dark = true } - else if (is_of('light', 'l')) { dark = false } - else if (is_of('color', 'c')) { color = parse_color(e[1]) } - else if (is_of('mvbtns', 'mv', 'm')) { mv = parse_mv(e[1]) } - else if (is_of('quizmode', 'qz', 'q')) { quizmode = parse_quizmode(e[1]) } - else if (is_of('txt')) { quizmode = parse_quizmode('imlaai') } - else if (is_of('byword')) { byword = true } - else if (is_of('byletter')) { byword = false } - else if (is_of( 'linebreaks')) { nolinebreaks = false } - else if (is_of('nolinebreaks')) { nolinebreaks = true } - else if (is_of('t', 'teach', 'teacher')) { teacher = true } - else if (is_of('n', 'noteach', 'noteacher')) { teacher = false } - else if (is_of('dt', 'disableteacher')) { disableteacher = true } - else if (is_of('dq', 'disablequizmode')) { disablequizmode = true } - else if (is_of('hc', 'highcontrast')) { highcontrast = true } - else if (is_of('qari')) { qari = e[1] } - else if (is_of('qariurl')) { qariurl = e[1] } - else if (is_of('cn')) { cn = true } - else if (is_of('zz')) { zz = true } + if (is_of('dark', 'd')) { dark = true } + else if (is_of('light', 'l')) { dark = false } + else if (is_of('color', 'c')) { color = parse_color(e[1]) } + else if (is_of('mvbtns', 'mv', 'm')) { mv = parse_mv(e[1]) } + else if (is_of('quizmode', 'qz', 'q')) { quizmode = parse_quizmode(e[1]) } + else if (is_of('txt')) { quizmode = parse_quizmode('imlaai') } + else if (is_of('byword')) { byword = true } + else if (is_of('byletter')) { byword = false } + else if (is_of( 'linebreaks')) { nolinebreaks = false } + else if (is_of('nolinebreaks')) { nolinebreaks = true } + else if (is_of('t', 'teach', 'teacher')) { teacher = true } + else if (is_of('n', 'noteach', 'noteacher')) { teacher = false } + else if (is_of('dt', 'disableteacher')) { disableteacher = true } + else if (is_of('dq', 'disablequizmode')) { disablequizmode = true } + else if (is_of('hc', 'highcontrast')) { highcontrast = true } + else if (is_of('emu', 'emulate', 'emulation')) { emulate = e[1] } + else if (is_of('qari')) { qari = e[1] } + else if (is_of('qariurl')) { qariurl = e[1] } + else if (is_of('cn')) { cn = true } + else if (is_of('zz')) { zz = true } else if (is_of('a')) { a = +e[1] } else if (is_of('b')) { b = +e[1] } else if (is_of('p')) { [st, en] = pages_to_ayat(...range_to_pair(e[1])) } @@ -287,6 +289,7 @@ function _ligilumilo (params) { disableteacher, disablequizmode, highcontrast, + emulate, qari, qariurl, cn, @@ -357,6 +360,10 @@ function ligilumi () { } delete opts.disablequizmode // + if (opts.emulate && mappings[opts.emulate]) { + window.emulate = opts.emulate // it's an option, but a hidden one. + } + // // if no ayat are selected, only change the provided preferences if (opts.st == null || opts.en == null) { return } recite(opts) diff --git "a/\330\247\331\202\330\261\330\243\331\206\331\212.md" "b/\330\247\331\202\330\261\330\243\331\206\331\212.md" index 326f2cf..28f7d3d 100644 --- "a/\330\247\331\202\330\261\330\243\331\206\331\212.md" +++ "b/\330\247\331\202\330\261\330\243\331\206\331\212.md" @@ -152,6 +152,16 @@
  • cn: يضيف في نهاية التسميع «عبارة» من الآية التالية إذا كانت في نفس السورة. +
  • emu= أو emulate= أو emulation=: +لاستخدام تخطيط لوحة مفاتيح مختلف عن نظامك، حتى لو كنت تستخدم لوحة مفاتيح أجنبية. اللوحات المدعومة حاليا هي: + + +
  • dt أو disableteacher: لإزالة إمكانية تغيير الوضع المعلم من الواجهة. سيبقى الوضع المعلم قابلا للتغيير من مُعامِلات الرابط. هذا مفيد لفرض قيمة معينة له (مثلا بلا معلم) في حالة تضمينه في تطبيق ويب مثلا.

    تحذير: ستبقى قيمته قابلةً للتغيير من شاشة كونسول جافاسكربت؛ لم أستطيع تعطيل هذا بعد.