Skip to content

Commit

Permalink
Merge pull request #85 from rugk/Unicode-format
Browse files Browse the repository at this point in the history
Added support for Unicode formats and code casing
  • Loading branch information
rugk authored Feb 29, 2024
2 parents c49096b + 0a8541a commit e094322
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 41 deletions.
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

0 comments on commit e094322

Please sign in to comment.