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

Added support for Unicode formats and code casing #85

Merged
merged 2 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions scripts/manifests/chromemanifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
},

"background": {
"scripts": ["browser-polyfill.js", "background/modules/InstallUpgrade.js", "background/background.js"],
"type": "module"
"page": "background.html"
},
"content_scripts": [
{
Expand Down
58 changes: 58 additions & 0 deletions src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,35 @@
"description": "An entry in the context menu. This is an entry for the case."
},

"menuCodeCaseCodeCasing": {
"message": "Change code casing",
"description": "An entry in the context menu. This is an entry for the case."
},
"menuCodeCaseCamelCase": {
"message": "Camel case",
"description": "An entry in the context menu. This is an entry for the case."
},
"menuCodeCaseUpperCamelCase": {
"message": "Upper camel case",
"description": "An entry in the context menu. This is an entry for the case."
},
"menuCodeCaseSnakeCase": {
"message": "Snake case",
"description": "An entry in the context menu. This is an entry for the case."
},
"menuCodeCaseConstantCase": {
"message": "Constant case",
"description": "An entry in the context menu. This is an entry for the case."
},
"menuCodeCaseKebabCase": {
"message": "Kebab case",
"description": "An entry in the context menu. This is an entry for the case."
},
"menuCodeCaseTrainCase": {
"message": "Train case",
"description": "An entry in the context menu. This is an entry for the case."
},

"menuFontSuperscript": {
"message": "Superscript",
"description": "An entry in the context menu. This is an entry for the Unicode font."
Expand Down Expand Up @@ -251,11 +280,40 @@
"message": "Squared (black)",
"description": "An entry in the context menu. This is an entry for the Unicode font."
},
"menuFontParenthesized": {
"message": "Parenthesized",
"description": "An entry in the context menu. This is an entry for the Unicode font."
},
"menuFontFullwidth": {
"message": "Fullwidth",
"description": "An entry in the context menu. This is an entry for the Unicode font."
},

"menuFormatFormatting": {
"message": "Change formatting",
"description": "An entry in the context menu. This is an entry for the Unicode format."
},
"menuFormatOverlined": {
"message": "Overlined",
"description": "An entry in the context menu. This is an entry for the Unicode format."
},
"menuFormatDoubleOverlined": {
"message": "Double overlined",
"description": "An entry in the context menu. This is an entry for the Unicode format."
},
"menuFormatStrikethrough": {
"message": "Strikethrough",
"description": "An entry in the context menu. This is an entry for the Unicode format."
},
"menuFormatUnderlined": {
"message": "Underlined",
"description": "An entry in the context menu. This is an entry for the Unicode format."
},
"menuFormatDoubleUnderlined": {
"message": "Double underlined",
"description": "An entry in the context menu. This is an entry for the Unicode format."
},

// options
"someSettingsAreManaged": {
"message": "Some settings are managed by your system administrator and cannot be changed.",
Expand Down
28 changes: 27 additions & 1 deletion src/background/modules/ContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,43 @@ async function buildMenu(unicodeFontSettings, exampleText = null) {
}
if (!unicodeFontSettings.nested &&
unicodeFontSettings.changeFont &&
unicodeFontSettings.changeFormat &&
!menuIsShown) {
await menus.create({
id: "seperator-format-font",
type: "separator",
contexts: ["editable"]
});
}
if (unicodeFontSettings.changeFormat) {
await addMenuItems(menuStructure[TRANSFORMATION_TYPE.FORMAT], unicodeFontSettings, exampleText);
}
if (!unicodeFontSettings.nested &&
(unicodeFontSettings.changeFont || unicodeFontSettings.changeFormat) &&
unicodeFontSettings.changeCase &&
!menuIsShown) {
await menus.create({
id: "seperator-case-font",
id: "seperator-case-format",
type: "separator",
contexts: ["editable"]
});
}
if (unicodeFontSettings.changeCase) {
await addMenuItems(menuStructure[TRANSFORMATION_TYPE.CASING], unicodeFontSettings, exampleText);
}
if (!unicodeFontSettings.nested &&
(unicodeFontSettings.changeFont || unicodeFontSettings.changeFormat || unicodeFontSettings.changeCase) &&
unicodeFontSettings.changeCodeCase &&
!menuIsShown) {
await menus.create({
id: "seperator-code-case",
type: "separator",
contexts: ["editable"]
});
}
if (unicodeFontSettings.changeCodeCase) {
await addMenuItems(menuStructure[TRANSFORMATION_TYPE.CODE_CASING], unicodeFontSettings, exampleText);
}

menuIsShown = true;
}
Expand Down
147 changes: 136 additions & 11 deletions src/common/modules/UnicodeTransformationHandler.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fontLetters, CASE_ID_PREFIX, FONT_ID_PREFIX, TRANSFORMATION_TYPE } from "/common/modules/data/Fonts.js";
import { fontLetters, formats, CASE_ID_PREFIX, CODE_CASE_ID_PREFIX, FONT_ID_PREFIX, FORMAT_ID_PREFIX, TRANSFORMATION_TYPE } from "/common/modules/data/Fonts.js";

/**
* Transforms the given text according to the given transformation.
Expand All @@ -12,11 +12,20 @@ import { fontLetters, CASE_ID_PREFIX, FONT_ID_PREFIX, TRANSFORMATION_TYPE } from
export function transformText(text, transformationId) {
let output = null;
const transformationType = getTransformationType(transformationId);
if (transformationType === TRANSFORMATION_TYPE.CASING) {
output = changeCase[transformationId](text);
} else if (transformationType === TRANSFORMATION_TYPE.FONT) {
switch (transformationType) {
case TRANSFORMATION_TYPE.CASING:
output = changeCase[transformationId.slice(CASE_ID_PREFIX.length)](text);
break;
case TRANSFORMATION_TYPE.CODE_CASING:
output = changeCodeCase[transformationId.slice(CODE_CASE_ID_PREFIX.length)](text);
break;
case TRANSFORMATION_TYPE.FONT:
output = changeFont(text, transformationId);
} else {
break;
case TRANSFORMATION_TYPE.FORMAT:
output = changeFormat(text, transformationId);
break;
default:
throw new Error(`Transformation with id=${transformationId} is unknown and could not be processed.`);
}

Expand All @@ -37,8 +46,12 @@ export function transformText(text, transformationId) {
export function getTransformationType(transformationId) {
if (transformationId.startsWith(CASE_ID_PREFIX)) {
return TRANSFORMATION_TYPE.CASING;
} else if (transformationId.startsWith(CODE_CASE_ID_PREFIX)) {
return TRANSFORMATION_TYPE.CODE_CASING;
} else if (transformationId.startsWith(FONT_ID_PREFIX)) {
return TRANSFORMATION_TYPE.FONT;
} else if (transformationId.startsWith(FORMAT_ID_PREFIX)) {
return TRANSFORMATION_TYPE.FORMAT;
}
throw new Error(`Error while getting transformation type. Transformation with id=${transformationId} is unknown.`);

Expand All @@ -55,7 +68,7 @@ function capitalizeEachWord(text) {
// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#bcd:javascript.builtins.RegExp
// Intl.Segmenter is not yet supported by Firefox/Thunderbird: https://bugzilla.mozilla.org/show_bug.cgi?id=1423593
// \p{Alphabetic}
return text.replace(/(?<=^|\P{Alpha})\p{Alpha}\S*/gu, ([h, ...t]) => h.toLocaleUpperCase() + t.join(""));
return text.replaceAll(/(?<=^|\P{Alpha})\p{Alpha}\S*/gu, ([h, ...t]) => h.toLocaleUpperCase() + t.join(""));
}

/**
Expand All @@ -69,7 +82,7 @@ function capitalizeEachWord(text) {
* @throws {Error}
*/
function changeFont(text, chosenFont) {
const font = fontLetters[chosenFont];
const font = fontLetters[chosenFont.slice(FONT_ID_PREFIX.length)];
if (!font) {
throw new Error(`Font ${chosenFont} could not be processed.`);
}
Expand Down Expand Up @@ -102,6 +115,23 @@ function changeFont(text, chosenFont) {
return output;
}

/**
* Changes the Unicode format of the given text.
*
* @param {string} text
* @param {string} chosenFormat
* @returns {string}
* @throws {Error}
*/
function changeFormat(text, chosenFormat) {
const format = formats[chosenFormat.slice(FORMAT_ID_PREFIX.length)];
if (!format) {
throw new Error(`Format ${chosenFormat} could not be processed.`);
}

return Array.from(text, (letter) => letter + format).join("");
}

/**
* Toggle Case.
*
Expand Down Expand Up @@ -133,8 +163,103 @@ function toggleCase(atext) {
* @type {Object.<string, function(string): string>}
*/
const changeCase = Object.freeze({
[`${CASE_ID_PREFIX}Lowercase`]: (str) => str.toLocaleLowerCase(),
[`${CASE_ID_PREFIX}Uppercase`]: (str) => str.toLocaleUpperCase(),
[`${CASE_ID_PREFIX}CapitalizeEachWord`]: (str) => capitalizeEachWord(str.toLocaleLowerCase()),
[`${CASE_ID_PREFIX}ToggleCase`]: (str) => toggleCase(str)
Lowercase: (str) => str.toLocaleLowerCase(),
Uppercase: (str) => str.toLocaleUpperCase(),
CapitalizeEachWord: (str) => capitalizeEachWord(str.toLocaleLowerCase()),
ToggleCase: (str) => toggleCase(str)
});

/**
* Split string to change coding case.
*
* @param {string} str
* @returns {string[]}
*/
function split(str) {
// \p{Alphabetic} \p{Mark} \p{Decimal_Number} \p{Join_Control}
const re = /[^\p{Alpha}\p{M}\p{digit}\p{Join_C}]+/gu;
let arr = str.split(/\s+/u).map((x) => x.replaceAll(re, "")).filter(Boolean);
if (!arr.length || arr.length > 1) {
return arr;
}

arr = str.split(re).filter(Boolean);
// \p{Uppercase}
return !arr.length || arr.length > 1 ? arr : arr[0].match(/\p{Upper}*\P{Upper}+|\p{Upper}+/gu);
}

/**
* Lower camel case.
*
* @param {string} atext
* @returns {string}
*/
function camelCase(atext) {
const [head, ...tail] = split(atext);
return head.toLowerCase() + tail.map(([h, ...t]) => h.toUpperCase() + t.join("").toLowerCase()).join("");
}

/**
* Upper camel case.
*
* @param {string} atext
* @returns {string}
*/
function upperCamelCase(atext) {
return split(atext).map(([h, ...t]) => h.toUpperCase() + t.join("").toLowerCase()).join("");
}

/**
* Snake case.
*
* @param {string} atext
* @returns {string}
*/
function snakeCase(atext) {
return split(atext).map((x) => x.toLowerCase()).join("_");
}

/**
* Constant case.
*
* @param {string} atext
* @returns {string}
*/
function constantCase(atext) {
return split(atext).map((x) => x.toUpperCase()).join("_");
}

/**
* Kebab case.
*
* @param {string} atext
* @returns {string}
*/
function kebabCase(atext) {
return split(atext).map((x) => x.toLowerCase()).join("-");
}

/**
* Train case.
*
* @param {string} atext
* @returns {string}
*/
function trainCase(atext) {
return split(atext).map((x) => x.toUpperCase()).join("-");
}

/**
* Change Coding Case
*
* @const
* @type {Object.<string, function(string): string>}
*/
const changeCodeCase = Object.freeze({
CamelCase: camelCase,
UpperCamelCase: upperCamelCase,
SnakeCase: snakeCase,
ConstantCase: constantCase,
KebabCase: kebabCase,
TrainCase: trainCase
});
2 changes: 2 additions & 0 deletions src/common/modules/data/DefaultSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ const defaultSettings = {
},
unicodeFont: {
changeFont: true,
changeFormat: false,
changeCase: true,
changeCodeCase: true,
showReadableText: true,
livePreview: true,
nested: true
Expand Down
Loading
Loading