Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/help plugin #1376

Closed
wants to merge 16 commits into from
280 changes: 280 additions & 0 deletions src/pat/tinymce/js/help.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
/**
* TinyMCE version 6.8.3 (2024-02-08)
* Extracted from tinymce/plugins/help/plugin.js
*/

import tinymce from "tinymce/tinymce";
import shortcuts from "./help/shortcuts";
import { Cell, Optional } from "./help/utils";

(function () {
"use strict";

let unique = 0;
const generate = (prefix) => {
const date = new Date();
const time = date.getTime();
const random = Math.floor(Math.random() * 1000000000);
unique++;
return prefix + "_" + random + unique + String(time);
};

const get$1 = (customTabs) => {
const addTab = (spec) => {
var _a;
const name =
(_a = spec.name) !== null && _a !== void 0 ? _a : generate("tab-name");
const currentCustomTabs = customTabs.get();
currentCustomTabs[name] = spec;
customTabs.set(currentCustomTabs);
};
return { addTab };
};

const registerCommand = (editor, dialogOpener) => {
editor.addCommand("mceHelp", dialogOpener);
};

const option = (name) => (editor) => editor.options.get(name);
const registerOption = (editor) => {
editor.options.register("help_tabs", { processor: "array" });
};
const getHelpTabs = option("help_tabs");

// Menu Item and Button
const register = (editor, dialogOpener) => {
editor.ui.registry.addButton("help", {
icon: "help",
tooltip: "Help",
shortcut: "Alt+0",
onAction: dialogOpener,
});
editor.ui.registry.addMenuItem("help", {
text: "Help",
icon: "help",
shortcut: "Alt+0",
onAction: dialogOpener,
});
};

const hasProto = (v, constructor, predicate) => {
var _a;
if (predicate(v, constructor.prototype)) {
return true;
} else {
return (
((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) ===
constructor.name
);
}
};
const typeOf = (x) => {
const t = typeof x;
if (x === null) {
return "null";
} else if (t === "object" && Array.isArray(x)) {
return "array";
} else if (
t === "object" &&
hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))

Check failure on line 79 in src/pat/tinymce/js/help.js

View workflow job for this annotation

GitHub Actions / test

Do not access Object.prototype method 'isPrototypeOf' from target object
) {
return "string";
} else {
return t;
}
};

const isType = (type) => (value) => typeOf(value) === type;
const isString = isType("string");

Optional.singletonNone = new Optional(false);

const map = (xs, f) => {
const len = xs.length;
const r = new Array(len);
for (let i = 0; i < len; i++) {
const x = xs[i];
r[i] = f(x, i);
}
return r;
};

const keys = Object.keys;
const hasOwnProperty = Object.hasOwnProperty;
const get = (obj, key) => {
return has(obj, key) ? Optional.from(obj[key]) : Optional.none();
};
const has = (obj, key) => hasOwnProperty.call(obj, key);

const cat = (arr) => {
const r = [];
const push = (x) => {
r.push(x);
};
for (let i = 0; i < arr.length; i++) {
arr[i].each(push);
}
return r;
};

// Keynav tab
var tinymce_resource = tinymce.util.Tools.resolve('tinymce.Resource');
var tinymce_i18n = tinymce.util.Tools.resolve('tinymce.util.I18n');
const pLoadHtmlByLangCode = (baseUrl, langCode) => {
Promise.resolve(import(`./help/i18n/keynav/${ langCode }.js`));
return tinymce_resource.load(`tinymce.html-i18n.help-keynav.${ langCode }`, `./help/i18n/keynav/${ langCode }.js`);
}
const pLoadI18nHtml = baseUrl => pLoadHtmlByLangCode(baseUrl, tinymce_i18n.getCode()).catch(() => pLoadHtmlByLangCode(baseUrl, 'en'));
const initI18nLoad = (editor, baseUrl) => {
editor.on('init', () => {
var lastIndex = baseUrl.lastIndexOf('/');
var resourcesUrl = baseUrl.substring(0, lastIndex);
pLoadI18nHtml(resourcesUrl);
});
};
const pTab = async pluginUrl => {

const body = {
type: "htmlpanel",
presets: "document",
html: await pLoadI18nHtml(pluginUrl)
};
return {
name: "keyboardnav",
title: "Keyboard Navigation",
items: [body],
};
};

const convertText = (source) => {
const isMac = tinymce.Env.os.isMacOS() || tinymce.Env.os.isiOS();
const mac = {
alt: "&#x2325;",
ctrl: "&#x2303;",
shift: "&#x21E7;",
meta: "&#x2318;",
access: "&#x2303;&#x2325;",
};
const other = {
meta: "Ctrl ",
access: "Shift + Alt ",
};
const replace = isMac ? mac : other;
const shortcut = source.split("+");
const updated = map(shortcut, (segment) => {
const search = segment.toLowerCase().trim();
return has(replace, search) ? replace[search] : segment;
});
return isMac ? updated.join("").replace(/\s/, "") : updated.join("+");
};

// Shortcuts Tab
const getShortcuts = () => {
const shortcutList = map(shortcuts, (shortcut) => {
const shortcutText = map(shortcut.shortcuts, convertText).join(
" " + tinymce.util.I18n.translate("or").toLocaleLowerCase() + " "
);
return [shortcut.action, shortcutText];
});
const tablePanel = {
type: "table",
header: ["Action", "Shortcut"],
cells: shortcutList,
};
return {
name: "shortcuts",
title: "Handy Shortcuts",
items: [tablePanel],
};
};

const parseHelpTabsSetting = (tabsFromSettings, tabs) => {
const newTabs = {};
const names = map(tabsFromSettings, (t) => {
var _a;
if (isString(t)) {
if (has(tabs, t)) {
newTabs[t] = tabs[t];
}
return t;
} else {
const name =
(_a = t.name) !== null && _a !== void 0 ? _a : generate("tab-name");
newTabs[name] = t;
return name;
}
});
return {
tabs: newTabs,
names,
};
};
const getNamesFromTabs = (tabs) => {
const names = keys(tabs);
const idx = names.indexOf("versions");
if (idx !== -1) {
names.splice(idx, 1);
names.push("versions");
}
return {
tabs,
names,
};
};
const pParseCustomTabs = async (editor, customTabs, pluginUrl) => {
const shortcuts = getShortcuts();
const nav = await pTab(pluginUrl);
const tabs = {
[shortcuts.name]: shortcuts,
[nav.name]: nav,
...customTabs.get(),
};
return Optional.from(getHelpTabs(editor)).fold(
() => getNamesFromTabs(tabs),
(tabsFromSettings) => parseHelpTabsSetting(tabsFromSettings, tabs)
);
};
const init = (editor, customTabs, pluginUrl) => () => {
pParseCustomTabs(editor, customTabs, pluginUrl).then(({ tabs, names }) => {
const foundTabs = map(names, (name) => get(tabs, name));
const dialogTabs = cat(foundTabs);
const body = {
type: "tabpanel",
tabs: dialogTabs,
};
editor.windowManager.open({
title: "Help",
size: "medium",
body,
buttons: [
{
type: "cancel",
name: "close",
text: "Close",
primary: true,
},
],
initialData: {},
});
});
};

var Plugin = () => {
tinymce.PluginManager.add("help", (editor, pluginUrl) => {
const customTabs = new Cell({});
const api = get$1(customTabs);
registerOption(editor);
const dialogOpener = init(editor, customTabs, pluginUrl);
register(editor, dialogOpener);
registerCommand(editor, dialogOpener);
editor.shortcuts.add("Alt+0", "Open help dialog", "mceHelp");
initI18nLoad(editor, pluginUrl);

return api;
});
};
tinymce.addI18n("es", {
"Press {0} for help": "Pulse {0} para obtener la ayuda"
})
Plugin();
})();
90 changes: 90 additions & 0 deletions src/pat/tinymce/js/help/i18n/keynav/ar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
tinymce.Resource.add('tinymce.html-i18n.help-keynav.ar',

Check failure on line 1 in src/pat/tinymce/js/help/i18n/keynav/ar.js

View workflow job for this annotation

GitHub Actions / test

'tinymce' is not defined
'<h1>بدء التنقل بواسطة لوحة المفاتيح</h1>\n' +
'\n' +
'<dl>\n' +
' <dt>التركيز على شريط القوائم</dt>\n' +
' <dd>نظاما التشغيل Windows أو Linux: Alt + F9</dd>\n' +
' <dd>نظام التشغيل macOS: &#x2325;F9</dd>\n' +
' <dt>التركيز على شريط الأدوات</dt>\n' +
' <dd>نظاما التشغيل Windows أو Linux: Alt + F10</dd>\n' +
' <dd>نظام التشغيل macOS: &#x2325;F10</dd>\n' +
' <dt>التركيز على التذييل</dt>\n' +
' <dd>نظاما التشغيل Windows أو Linux: Alt + F11</dd>\n' +
' <dd>نظام التشغيل macOS: &#x2325;F11</dd>\n' +
' <dt>التركيز على شريط أدوات السياق</dt>\n' +
' <dd>أنظمة التشغيل Windows أو Linux أو macOS: Ctrl+F9\n' +
'</dl>\n' +
'\n' +
'<p>سيبدأ التنقل عند عنصر واجهة المستخدم الأول، والذي سيتم تمييزه أو تسطيره في حالة العنصر الأول في\n' +
' مسار عنصر التذييل.</p>\n' +
'\n' +
'<h1>التنقل بين أقسام واجهة المستخدم</h1>\n' +
'\n' +
'<p>للانتقال من أحد أقسام واجهة المستخدم إلى القسم التالي، اضغط على <strong>Tab</strong>.</p>\n' +
'\n' +
'<p>للانتقال من أحد أقسام واجهة المستخدم إلى القسم السابق، اضغط على <strong>Shift+Tab</strong>.</p>\n' +
'\n' +
'<p>ترتيب علامات <strong>Tab</strong> لأقسام واجهة المستخدم هذه هو:</p>\n' +
'\n' +
'<ol>\n' +
' <li>شريط القوائم</li>\n' +
' <li>كل مجموعة شريط الأدوات</li>\n' +
' <li>الشريط الجانبي</li>\n' +
' <li>مسار العنصر في التذييل</li>\n' +
' <li>زر تبديل عدد الكلمات في التذييل</li>\n' +
' <li>رابط إدراج العلامة التجارية في التذييل</li>\n' +
' <li>مؤشر تغيير حجم المحرر في التذييل</li>\n' +
'</ol>\n' +
'\n' +
'<p>إذا لم يكن قسم واجهة المستخدم موجودًا، فسيتم تخطيه.</p>\n' +
'\n' +
'<p>إذا كان التذييل يحتوي على التركيز على ‏‫التنقل بواسطة لوحة المفاتيح، ولا يوجد شريط جانبي مرئي، فإن الضغط على <strong>Shift+Tab</strong>\n' +
' ينقل التركيز إلى مجموعة شريط الأدوات الأولى، وليس الأخيرة.</p>\n' +
'\n' +
'<h1>التنقل بين أقسام واجهة المستخدم</h1>\n' +
'\n' +
'<p>للانتقال من أحد عناصر واجهة المستخدم إلى العنصر التالي، اضغط على مفتاح <strong>السهم</strong> المناسب.</p>\n' +
'\n' +
'<p>مفتاحا السهمين <strong>اليسار‎</strong> و<strong>اليمين‎</strong></p>\n' +
'\n' +
'<ul>\n' +
' <li>التنقل بين القوائم في شريط القوائم.</li>\n' +
' <li>فتح قائمة فرعية في القائمة.</li>\n' +
' <li>التنقل بين الأزرار في مجموعة شريط الأدوات.</li>\n' +
' <li>التنقل بين العناصر في مسار عنصر التذييل.</li>\n' +
'</ul>\n' +
'\n' +
'<p>مفتاحا السهمين <strong>لأسفل‎</strong> و<strong>لأعلى‎</strong></p>\n' +
'\n' +
'<ul>\n' +
' <li>التنقل بين عناصر القائمة في القائمة.</li>\n' +
' <li>التنقل بين العناصر في قائمة شريط الأدوات المنبثقة.</li>\n' +
'</ul>\n' +
'\n' +
'<p>دورة مفاتيح <strong>الأسهم‎</strong> داخل قسم واجهة المستخدم التي تم التركيز عليها.</p>\n' +
'\n' +
'<p>لإغلاق قائمة مفتوحة أو قائمة فرعية مفتوحة أو قائمة منبثقة مفتوحة، اضغط على مفتاح <strong>Esc</strong>.</p>\n' +
'\n' +
'<p>إذا كان التركيز الحالي على "الجزء العلوي" من قسم معين لواجهة المستخدم، فإن الضغط على مفتاح <strong>Esc</strong> يؤدي أيضًا إلى الخروج\n' +
' من التنقل بواسطة لوحة المفاتيح بالكامل.</p>\n' +
'\n' +
'<h1>تنفيذ عنصر قائمة أو زر شريط أدوات</h1>\n' +
'\n' +
'<p>عندما يتم تمييز عنصر القائمة المطلوب أو زر شريط الأدوات، اضغط على زر <strong>Return</strong>، أو <strong>Enter</strong>،\n' +
' أو <strong>مفتاح المسافة</strong> لتنفيذ العنصر.</p>\n' +
'\n' +
'<h1>التنقل في مربعات الحوار غير المبوبة</h1>\n' +
'\n' +
'<p>في مربعات الحوار غير المبوبة، يتم التركيز على المكون التفاعلي الأول عند فتح مربع الحوار.</p>\n' +
'\n' +
'<p>التنقل بين مكونات الحوار التفاعلي بالضغط على زر <strong>Tab</strong> أو <strong>Shift+Tab</strong>.</p>\n' +
'\n' +
'<h1>التنقل في مربعات الحوار المبوبة</h1>\n' +
'\n' +
'<p>في مربعات الحوار المبوبة، يتم التركيز على الزر الأول في قائمة علامات التبويب عند فتح مربع الحوار.</p>\n' +
'\n' +
'<p>التنقل بين المكونات التفاعلية لعلامة التبويب لمربع الحوار هذه بالضغط على زر <strong>Tab</strong> أو\n' +
' <strong>Shift+Tab</strong>.</p>\n' +
'\n' +
'<p>التبديل إلى علامة تبويب أخرى لمربع الحوار من خلال التركيز على قائمة علامة التبويب ثم الضغط على زر <strong>السهم</strong> المناسب\n' +
' مفتاح للتنقل بين علامات التبويب المتاحة.</p>\n');
Loading
Loading